@@ -18928,6 +18928,9 @@ function isObjectMethodType(id) {
1892818928function isPrimitiveType(id) {
1892918929 return id.type.kind === 'Primitive';
1893018930}
18931+ function isPlainObjectType(id) {
18932+ return id.type.kind === 'Object' && id.type.shapeId === 'BuiltInObject';
18933+ }
1893118934function isArrayType(id) {
1893218935 return id.type.kind === 'Object' && id.type.shapeId === 'BuiltInArray';
1893318936}
@@ -32263,6 +32266,7 @@ const EnvironmentConfigSchema = v4.z.object({
3226332266 validateNoDynamicallyCreatedComponentsOrHooks: v4.z.boolean().default(false),
3226432267 enableAllowSetStateFromRefsInEffects: v4.z.boolean().default(true),
3226532268 enableInferEventHandlers: v4.z.boolean().default(false),
32269+ enableOptimizeForSSR: v4.z.boolean().default(false),
3226632270});
3226732271class Environment {
3226832272 constructor(scope, fnType, compilerMode, config, contextIdentifiers, parentFunction, logger, filename, code, programContext) {
@@ -34479,9 +34483,10 @@ function deadCodeElimination(fn) {
3447934483 retainWhere(fn.context, contextVar => state.isIdOrNameUsed(contextVar.identifier));
3448034484}
3448134485let State$2 = class State {
34482- constructor() {
34486+ constructor(env ) {
3448334487 this.named = new Set();
3448434488 this.identifiers = new Set();
34489+ this.env = env;
3448534490 }
3448634491 reference(identifier) {
3448734492 this.identifiers.add(identifier.id);
@@ -34503,7 +34508,7 @@ let State$2 = class State {
3450334508function findReferencedIdentifiers(fn) {
3450434509 const hasLoop = hasBackEdge(fn);
3450534510 const reversedBlocks = [...fn.body.blocks.values()].reverse();
34506- const state = new State$2();
34511+ const state = new State$2(fn.env );
3450734512 let size = state.count;
3450834513 do {
3450934514 size = state.count;
@@ -34650,12 +34655,25 @@ function pruneableValue(value, state) {
3465034655 case 'Debugger': {
3465134656 return false;
3465234657 }
34653- case 'Await':
3465434658 case 'CallExpression':
34659+ case 'MethodCall': {
34660+ if (state.env.config.enableOptimizeForSSR) {
34661+ const calleee = value.kind === 'CallExpression' ? value.callee : value.property;
34662+ const hookKind = getHookKind(state.env, calleee.identifier);
34663+ switch (hookKind) {
34664+ case 'useState':
34665+ case 'useReducer':
34666+ case 'useRef': {
34667+ return true;
34668+ }
34669+ }
34670+ }
34671+ return false;
34672+ }
34673+ case 'Await':
3465534674 case 'ComputedDelete':
3465634675 case 'ComputedStore':
3465734676 case 'PropertyDelete':
34658- case 'MethodCall':
3465934677 case 'PropertyStore':
3466034678 case 'StoreGlobal': {
3466134679 return false;
@@ -53182,6 +53200,196 @@ function nameAnonymousFunctionsImpl(fn) {
5318253200 return nodes;
5318353201}
5318453202
53203+ function optimizeForSSR(fn) {
53204+ const inlinedState = new Map();
53205+ for (const block of fn.body.blocks.values()) {
53206+ for (const instr of block.instructions) {
53207+ const { value } = instr;
53208+ switch (value.kind) {
53209+ case 'Destructure': {
53210+ if (inlinedState.has(value.value.identifier.id) &&
53211+ value.lvalue.pattern.kind === 'ArrayPattern' &&
53212+ value.lvalue.pattern.items.length >= 1 &&
53213+ value.lvalue.pattern.items[0].kind === 'Identifier') {
53214+ continue;
53215+ }
53216+ break;
53217+ }
53218+ case 'MethodCall':
53219+ case 'CallExpression': {
53220+ const calleee = value.kind === 'CallExpression' ? value.callee : value.property;
53221+ const hookKind = getHookKind(fn.env, calleee.identifier);
53222+ switch (hookKind) {
53223+ case 'useReducer': {
53224+ if (value.args.length === 2 &&
53225+ value.args[1].kind === 'Identifier') {
53226+ const arg = value.args[1];
53227+ const replace = {
53228+ kind: 'LoadLocal',
53229+ place: arg,
53230+ loc: arg.loc,
53231+ };
53232+ inlinedState.set(instr.lvalue.identifier.id, replace);
53233+ }
53234+ else if (value.args.length === 3 &&
53235+ value.args[1].kind === 'Identifier' &&
53236+ value.args[2].kind === 'Identifier') {
53237+ const arg = value.args[1];
53238+ const initializer = value.args[2];
53239+ const replace = {
53240+ kind: 'CallExpression',
53241+ callee: initializer,
53242+ args: [arg],
53243+ loc: value.loc,
53244+ };
53245+ inlinedState.set(instr.lvalue.identifier.id, replace);
53246+ }
53247+ break;
53248+ }
53249+ case 'useState': {
53250+ if (value.args.length === 1 &&
53251+ value.args[0].kind === 'Identifier') {
53252+ const arg = value.args[0];
53253+ if (isPrimitiveType(arg.identifier) ||
53254+ isPlainObjectType(arg.identifier) ||
53255+ isArrayType(arg.identifier)) {
53256+ const replace = {
53257+ kind: 'LoadLocal',
53258+ place: arg,
53259+ loc: arg.loc,
53260+ };
53261+ inlinedState.set(instr.lvalue.identifier.id, replace);
53262+ }
53263+ }
53264+ break;
53265+ }
53266+ }
53267+ }
53268+ }
53269+ if (inlinedState.size !== 0) {
53270+ for (const operand of eachInstructionValueOperand(value)) {
53271+ inlinedState.delete(operand.identifier.id);
53272+ }
53273+ }
53274+ }
53275+ if (inlinedState.size !== 0) {
53276+ for (const operand of eachTerminalOperand(block.terminal)) {
53277+ inlinedState.delete(operand.identifier.id);
53278+ }
53279+ }
53280+ }
53281+ for (const block of fn.body.blocks.values()) {
53282+ for (const instr of block.instructions) {
53283+ const { value } = instr;
53284+ switch (value.kind) {
53285+ case 'FunctionExpression': {
53286+ if (hasKnownNonRenderCall(value.loweredFunc.func)) {
53287+ instr.value = {
53288+ kind: 'Primitive',
53289+ value: undefined,
53290+ loc: value.loc,
53291+ };
53292+ }
53293+ break;
53294+ }
53295+ case 'JsxExpression': {
53296+ if (value.tag.kind === 'BuiltinTag' &&
53297+ value.tag.name.indexOf('-') === -1) {
53298+ const tag = value.tag.name;
53299+ retainWhere(value.props, prop => {
53300+ return (prop.kind === 'JsxSpreadAttribute' ||
53301+ (!isKnownEventHandler(tag, prop.name) && prop.name !== 'ref'));
53302+ });
53303+ }
53304+ break;
53305+ }
53306+ case 'Destructure': {
53307+ if (inlinedState.has(value.value.identifier.id)) {
53308+ CompilerError.invariant(value.lvalue.pattern.kind === 'ArrayPattern' &&
53309+ value.lvalue.pattern.items.length >= 1 &&
53310+ value.lvalue.pattern.items[0].kind === 'Identifier', {
53311+ reason: 'Expected a valid destructuring pattern for inlined state',
53312+ description: null,
53313+ details: [
53314+ {
53315+ kind: 'error',
53316+ message: 'Expected a valid destructuring pattern',
53317+ loc: value.loc,
53318+ },
53319+ ],
53320+ });
53321+ const store = {
53322+ kind: 'StoreLocal',
53323+ loc: value.loc,
53324+ type: null,
53325+ lvalue: {
53326+ kind: value.lvalue.kind,
53327+ place: value.lvalue.pattern.items[0],
53328+ },
53329+ value: value.value,
53330+ };
53331+ instr.value = store;
53332+ }
53333+ break;
53334+ }
53335+ case 'MethodCall':
53336+ case 'CallExpression': {
53337+ const calleee = value.kind === 'CallExpression' ? value.callee : value.property;
53338+ const hookKind = getHookKind(fn.env, calleee.identifier);
53339+ switch (hookKind) {
53340+ case 'useEffectEvent': {
53341+ if (value.args.length === 1 &&
53342+ value.args[0].kind === 'Identifier') {
53343+ const load = {
53344+ kind: 'LoadLocal',
53345+ place: value.args[0],
53346+ loc: value.loc,
53347+ };
53348+ instr.value = load;
53349+ }
53350+ break;
53351+ }
53352+ case 'useEffect':
53353+ case 'useLayoutEffect':
53354+ case 'useInsertionEffect': {
53355+ instr.value = {
53356+ kind: 'Primitive',
53357+ value: undefined,
53358+ loc: value.loc,
53359+ };
53360+ break;
53361+ }
53362+ case 'useReducer':
53363+ case 'useState': {
53364+ const replace = inlinedState.get(instr.lvalue.identifier.id);
53365+ if (replace != null) {
53366+ instr.value = replace;
53367+ }
53368+ break;
53369+ }
53370+ }
53371+ }
53372+ }
53373+ }
53374+ }
53375+ }
53376+ function hasKnownNonRenderCall(fn) {
53377+ for (const block of fn.body.blocks.values()) {
53378+ for (const instr of block.instructions) {
53379+ if (instr.value.kind === 'CallExpression' &&
53380+ (isSetStateType(instr.value.callee.identifier) ||
53381+ isStartTransitionType(instr.value.callee.identifier))) {
53382+ return true;
53383+ }
53384+ }
53385+ }
53386+ return false;
53387+ }
53388+ const EVENT_HANDLER_PATTERN = /^on[A-Z]/;
53389+ function isKnownEventHandler(_tag, prop) {
53390+ return EVENT_HANDLER_PATTERN.test(prop);
53391+ }
53392+
5318553393function run(func, config, fnType, mode, programContext, logger, filename, code) {
5318653394 var _a, _b;
5318753395 const contextIdentifiers = findContextIdentifiers(func);
@@ -53256,6 +53464,10 @@ function runWithEnvironment(func, env) {
5325653464 throw mutabilityAliasingErrors.unwrapErr();
5325753465 }
5325853466 }
53467+ if (env.config.enableOptimizeForSSR) {
53468+ optimizeForSSR(hir);
53469+ log({ kind: 'hir', name: 'OptimizeForSSR', value: hir });
53470+ }
5325953471 deadCodeElimination(hir);
5326053472 log({ kind: 'hir', name: 'DeadCodeElimination', value: hir });
5326153473 if (env.config.enableInstructionReordering) {
@@ -53313,8 +53525,10 @@ function runWithEnvironment(func, env) {
5331353525 if (env.config.validateStaticComponents) {
5331453526 env.logErrors(validateStaticComponents(hir));
5331553527 }
53316- inferReactiveScopeVariables(hir);
53317- log({ kind: 'hir', name: 'InferReactiveScopeVariables', value: hir });
53528+ if (!env.config.enableOptimizeForSSR) {
53529+ inferReactiveScopeVariables(hir);
53530+ log({ kind: 'hir', name: 'InferReactiveScopeVariables', value: hir });
53531+ }
5331853532 }
5331953533 const fbtOperands = memoizeFbtAndMacroOperandsInSameScope(hir);
5332053534 log({
0 commit comments