Compare commits
5 Commits
pr35832
...
gh/mvitous
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0544af4dcc | ||
|
|
c014aada1c | ||
|
|
bc0882dc3c | ||
|
|
b966dfa91e | ||
|
|
29019bf6ec |
@@ -155,11 +155,7 @@ function* runWithEnvironment(
|
||||
validateContextVariableLValues(hir);
|
||||
validateUseMemo(hir);
|
||||
|
||||
if (
|
||||
!env.config.enablePreserveExistingManualUseMemo &&
|
||||
!env.config.disableMemoizationForDebugging &&
|
||||
!env.config.enableChangeDetectionForDebugging
|
||||
) {
|
||||
if (!env.preserveManualMemo()) {
|
||||
dropManualMemoization(hir);
|
||||
yield log({kind: 'hir', name: 'DropManualMemoization', value: hir});
|
||||
}
|
||||
@@ -415,7 +411,7 @@ function* runWithEnvironment(
|
||||
value: reactiveFunction,
|
||||
});
|
||||
|
||||
if (env.config.enableChangeDetectionForDebugging != null) {
|
||||
if (env.config.enableChangeDetection != null) {
|
||||
pruneInitializationDependencies(reactiveFunction);
|
||||
yield log({
|
||||
kind: 'reactive',
|
||||
|
||||
@@ -538,11 +538,27 @@ export function compileProgram(
|
||||
externalFunctions.push(enableEmitHookGuards);
|
||||
}
|
||||
|
||||
if (pass.opts.environment?.enableChangeDetectionForDebugging != null) {
|
||||
const enableChangeDetectionForDebugging = tryParseExternalFunction(
|
||||
pass.opts.environment.enableChangeDetectionForDebugging,
|
||||
);
|
||||
externalFunctions.push(enableChangeDetectionForDebugging);
|
||||
if (pass.opts.environment?.enableChangeDetection != null) {
|
||||
const enableChangeDetection = tryParseExternalFunction({
|
||||
importSpecifierName:
|
||||
pass.opts.environment.enableChangeDetection.structuralCheck,
|
||||
source: pass.opts.environment.enableChangeDetection.source,
|
||||
});
|
||||
externalFunctions.push(enableChangeDetection);
|
||||
if (pass.opts.environment.enableChangeDetection.wrappers != null) {
|
||||
const store = tryParseExternalFunction({
|
||||
importSpecifierName:
|
||||
pass.opts.environment.enableChangeDetection.wrappers.store,
|
||||
source: pass.opts.environment.enableChangeDetection.source,
|
||||
});
|
||||
const restore = tryParseExternalFunction({
|
||||
importSpecifierName:
|
||||
pass.opts.environment.enableChangeDetection.wrappers.restore,
|
||||
source: pass.opts.environment.enableChangeDetection.source,
|
||||
});
|
||||
externalFunctions.push(store);
|
||||
externalFunctions.push(restore);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
handleError(err, pass, null);
|
||||
|
||||
@@ -385,7 +385,7 @@ const EnvironmentConfigSchema = z.object({
|
||||
* @enablePreserveExistingManualUseMemo, because otherwise memoization in the
|
||||
* original source will be disabled as well.
|
||||
*/
|
||||
disableMemoizationForDebugging: z.boolean().default(false),
|
||||
disableMemoization: z.boolean().default(false),
|
||||
|
||||
/**
|
||||
* When true, rather using memoized values, the compiler will always re-compute
|
||||
@@ -393,7 +393,13 @@ const EnvironmentConfigSchema = z.object({
|
||||
* computed one. This detects cases where rules of react violations may cause the
|
||||
* compiled code to behave differently than the original.
|
||||
*/
|
||||
enableChangeDetectionForDebugging: ExternalFunctionSchema.nullish(),
|
||||
enableChangeDetection: z
|
||||
.object({
|
||||
source: z.string(),
|
||||
structuralCheck: z.string(),
|
||||
wrappers: z.object({ store: z.string(), restore: z.string() }).nullish(),
|
||||
})
|
||||
.nullish(),
|
||||
|
||||
/**
|
||||
* The react native re-animated library uses custom Babel transforms that
|
||||
@@ -456,12 +462,27 @@ export function parseConfigPragma(pragma: string): EnvironmentConfig {
|
||||
}
|
||||
|
||||
if (
|
||||
key === 'enableChangeDetectionForDebugging' &&
|
||||
(val === undefined || val === 'true')
|
||||
key === "enableChangeDetection" &&
|
||||
(val === undefined || val === "true")
|
||||
) {
|
||||
maybeConfig[key] = {
|
||||
source: 'react-compiler-runtime',
|
||||
importSpecifierName: '$structuralCheck',
|
||||
structuralCheck: "$structuralCheck",
|
||||
};
|
||||
continue;
|
||||
}
|
||||
|
||||
if (
|
||||
key === "enableChangeDetectionWrappers" &&
|
||||
(val === undefined || val === "true")
|
||||
) {
|
||||
maybeConfig["enableChangeDetection"] = {
|
||||
source: "react-compiler-runtime",
|
||||
structuralCheck: '$structuralCheck',
|
||||
wrappers: {
|
||||
store: "$store",
|
||||
restore: "$restore",
|
||||
},
|
||||
};
|
||||
continue;
|
||||
}
|
||||
@@ -549,18 +570,6 @@ export class Environment {
|
||||
this.#shapes = new Map(DEFAULT_SHAPES);
|
||||
this.#globals = new Map(DEFAULT_GLOBALS);
|
||||
|
||||
if (
|
||||
config.disableMemoizationForDebugging &&
|
||||
config.enableChangeDetectionForDebugging != null
|
||||
) {
|
||||
CompilerError.throwInvalidConfig({
|
||||
reason: `Invalid environment config: the 'disableMemoizationForDebugging' and 'enableChangeDetectionForDebugging' options cannot be used together`,
|
||||
description: null,
|
||||
loc: null,
|
||||
suggestions: null,
|
||||
});
|
||||
}
|
||||
|
||||
for (const [hookName, hook] of this.config.customHooks) {
|
||||
CompilerError.invariant(!this.#globals.has(hookName), {
|
||||
reason: `[Globals] Found existing definition in global registry for custom hook ${hookName}`,
|
||||
@@ -765,6 +774,14 @@ export class Environment {
|
||||
return DefaultMutatingHook;
|
||||
}
|
||||
}
|
||||
|
||||
preserveManualMemo(): boolean {
|
||||
return (
|
||||
this.config.enablePreserveExistingManualUseMemo ||
|
||||
this.config.disableMemoization ||
|
||||
this.config.enableChangeDetection != null
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// From https://github.com/facebook/react/blob/main/packages/eslint-plugin-react-hooks/src/RulesOfHooks.js#LL18C1-L23C2
|
||||
|
||||
@@ -1168,7 +1168,7 @@ export type NonLocalBinding =
|
||||
imported: string;
|
||||
}
|
||||
// let, const, function, etc declared in the module but outside the current component/hook
|
||||
| {kind: 'ModuleLocal'; name: string}
|
||||
| {kind: 'ModuleLocal'; name: string, immutable: boolean}
|
||||
// an unresolved binding
|
||||
| {kind: 'Global'; name: string};
|
||||
|
||||
|
||||
@@ -271,9 +271,14 @@ export default class HIRBuilder {
|
||||
module: importDeclaration.node.source.value,
|
||||
};
|
||||
} else {
|
||||
const immutable =
|
||||
(path.isVariableDeclaration() && path.node.kind === "const") ||
|
||||
path.isClassDeclaration() ||
|
||||
path.isClassExpression();
|
||||
return {
|
||||
kind: 'ModuleLocal',
|
||||
name: originalName,
|
||||
immutable,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import {
|
||||
makeInstructionId,
|
||||
} from '.';
|
||||
import {getPlaceScope} from '../ReactiveScopes/BuildReactiveBlocks';
|
||||
import {isMutable} from '../ReactiveScopes/InferReactiveScopeVariables';
|
||||
import {isMutableAtInstruction} from '../ReactiveScopes/InferReactiveScopeVariables';
|
||||
import DisjointSet from '../Utils/DisjointSet';
|
||||
import {getOrInsertDefault} from '../Utils/utils';
|
||||
import {
|
||||
@@ -254,7 +254,7 @@ function visitPlace(
|
||||
* of the stack to the mutated outer scope.
|
||||
*/
|
||||
const placeScope = getPlaceScope(id, place);
|
||||
if (placeScope != null && isMutable({id} as any, place)) {
|
||||
if (placeScope != null && isMutableAtInstruction({id} as any, place)) {
|
||||
const placeScopeIdx = activeScopes.indexOf(placeScope);
|
||||
if (placeScopeIdx !== -1 && placeScopeIdx !== activeScopes.length - 1) {
|
||||
joined.union([placeScope, ...activeScopes.slice(placeScopeIdx + 1)]);
|
||||
|
||||
@@ -26,7 +26,7 @@ import {
|
||||
} from '../HIR/visitors';
|
||||
import {
|
||||
findDisjointMutableValues,
|
||||
isMutable,
|
||||
isMutableAtInstruction,
|
||||
} from '../ReactiveScopes/InferReactiveScopeVariables';
|
||||
import DisjointSet from '../Utils/DisjointSet';
|
||||
import {assertExhaustive} from '../Utils/utils';
|
||||
@@ -232,7 +232,7 @@ export function inferReactivePlaces(fn: HIRFunction): void {
|
||||
case Effect.Store:
|
||||
case Effect.ConditionallyMutate:
|
||||
case Effect.Mutate: {
|
||||
if (isMutable(instruction, operand)) {
|
||||
if (isMutableAtInstruction(instruction, operand)) {
|
||||
const resolvedId = identifierMapping.get(operand.identifier);
|
||||
if (resolvedId !== undefined) {
|
||||
reactiveIdentifiers.markReactiveIdentifier(resolvedId);
|
||||
|
||||
@@ -669,15 +669,7 @@ function codegenReactiveScope(
|
||||
);
|
||||
}
|
||||
|
||||
if (cx.env.config.disableMemoizationForDebugging) {
|
||||
CompilerError.invariant(
|
||||
cx.env.config.enableChangeDetectionForDebugging == null,
|
||||
{
|
||||
reason: `Expected to not have both change detection enabled and memoization disabled`,
|
||||
description: `Incompatible config options`,
|
||||
loc: null,
|
||||
},
|
||||
);
|
||||
if (cx.env.config.disableMemoization) {
|
||||
testCondition = t.logicalExpression(
|
||||
'||',
|
||||
testCondition,
|
||||
@@ -687,41 +679,76 @@ function codegenReactiveScope(
|
||||
let computationBlock = codegenBlock(cx, block);
|
||||
|
||||
let memoStatement;
|
||||
if (
|
||||
cx.env.config.enableChangeDetectionForDebugging != null &&
|
||||
changeExpressions.length > 0
|
||||
) {
|
||||
if (cx.env.config.enableChangeDetection != null) {
|
||||
const loc =
|
||||
typeof scope.loc === 'symbol'
|
||||
? 'unknown location'
|
||||
: `(${scope.loc.start.line}:${scope.loc.end.line})`;
|
||||
const detectionFunction =
|
||||
cx.env.config.enableChangeDetectionForDebugging.importSpecifierName;
|
||||
cx.env.config.enableChangeDetection.structuralCheck;
|
||||
const cacheLoadOldValueStatements: Array<t.Statement> = [];
|
||||
const restoreOldValueStatements: Array<t.Statement> = [];
|
||||
const changeDetectionStatements: Array<t.Statement> = [];
|
||||
const idempotenceDetectionStatements: Array<t.Statement> = [];
|
||||
|
||||
for (const {name, index, value} of cacheLoads) {
|
||||
const loadName = cx.synthesizeName(`old$${name.name}`);
|
||||
const slot = t.memberExpression(
|
||||
t.identifier(cx.synthesizeName('$')),
|
||||
for (const {
|
||||
name: { name: nameStr },
|
||||
index,
|
||||
value,
|
||||
} of cacheLoads) {
|
||||
const baseSlot = t.memberExpression(
|
||||
t.identifier(cx.synthesizeName("$")),
|
||||
t.numericLiteral(index),
|
||||
true,
|
||||
);
|
||||
|
||||
const genSlot = (): t.MemberExpression => t.cloneNode(baseSlot, true);
|
||||
|
||||
const loadNameStr = cx.synthesizeName(`old$${nameStr}`);
|
||||
|
||||
let storedValue, restoredValue, restoredRecomputed;
|
||||
if (cx.env.config.enableChangeDetection.wrappers != null) {
|
||||
storedValue = t.callExpression(
|
||||
t.identifier(cx.env.config.enableChangeDetection.wrappers.store),
|
||||
[value]
|
||||
);
|
||||
restoredValue = t.callExpression(
|
||||
t.identifier(cx.env.config.enableChangeDetection.wrappers.restore),
|
||||
[t.identifier(loadNameStr)]
|
||||
);
|
||||
restoredRecomputed = t.callExpression(
|
||||
t.identifier(cx.env.config.enableChangeDetection.wrappers.restore),
|
||||
[genSlot()]
|
||||
);
|
||||
} else {
|
||||
storedValue = value;
|
||||
restoredValue = t.identifier(loadNameStr);
|
||||
restoredRecomputed = genSlot();
|
||||
}
|
||||
|
||||
cacheStoreStatements.push(
|
||||
t.expressionStatement(t.assignmentExpression('=', slot, value)),
|
||||
t.expressionStatement(
|
||||
t.assignmentExpression("=", genSlot(), storedValue)
|
||||
)
|
||||
);
|
||||
cacheLoadOldValueStatements.push(
|
||||
t.variableDeclaration('let', [
|
||||
t.variableDeclarator(t.identifier(loadName), slot),
|
||||
t.variableDeclarator(t.identifier(loadNameStr), genSlot()),
|
||||
]),
|
||||
);
|
||||
if (!cx.env.config.disableMemoization) {
|
||||
restoreOldValueStatements.push(
|
||||
t.expressionStatement(
|
||||
t.assignmentExpression("=", t.identifier(nameStr), restoredValue)
|
||||
)
|
||||
);
|
||||
}
|
||||
changeDetectionStatements.push(
|
||||
t.expressionStatement(
|
||||
t.callExpression(t.identifier(detectionFunction), [
|
||||
t.identifier(loadName),
|
||||
t.cloneNode(name, true),
|
||||
t.stringLiteral(name.name),
|
||||
t.identifier(loadNameStr),
|
||||
t.identifier(nameStr),
|
||||
t.stringLiteral(nameStr),
|
||||
t.stringLiteral(cx.fnName),
|
||||
t.stringLiteral('cached'),
|
||||
t.stringLiteral(loc),
|
||||
@@ -731,9 +758,9 @@ function codegenReactiveScope(
|
||||
idempotenceDetectionStatements.push(
|
||||
t.expressionStatement(
|
||||
t.callExpression(t.identifier(detectionFunction), [
|
||||
t.cloneNode(slot, true),
|
||||
t.cloneNode(name, true),
|
||||
t.stringLiteral(name.name),
|
||||
genSlot(),
|
||||
t.identifier(nameStr),
|
||||
t.stringLiteral(nameStr),
|
||||
t.stringLiteral(cx.fnName),
|
||||
t.stringLiteral('recomputed'),
|
||||
t.stringLiteral(loc),
|
||||
@@ -741,7 +768,9 @@ function codegenReactiveScope(
|
||||
),
|
||||
);
|
||||
idempotenceDetectionStatements.push(
|
||||
t.expressionStatement(t.assignmentExpression('=', name, slot)),
|
||||
t.expressionStatement(
|
||||
t.assignmentExpression("=", t.identifier(nameStr), restoredRecomputed)
|
||||
)
|
||||
);
|
||||
}
|
||||
const condition = cx.synthesizeName('condition');
|
||||
|
||||
@@ -185,7 +185,10 @@ function mergeLocation(l: SourceLocation, r: SourceLocation): SourceLocation {
|
||||
}
|
||||
|
||||
// Is the operand mutable at this given instruction
|
||||
export function isMutable({id}: Instruction, place: Place): boolean {
|
||||
export function isMutableAtInstruction(
|
||||
{id}: Instruction,
|
||||
place: Place
|
||||
): boolean {
|
||||
const range = place.identifier.mutableRange;
|
||||
return id >= range.start && id < range.end;
|
||||
}
|
||||
@@ -253,6 +256,89 @@ function mayAllocate(env: Environment, instruction: Instruction): boolean {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* These instructions may pick up external changes due to rules of react violations.
|
||||
* Instructions should be included here if they may change without their inputs changing.
|
||||
* For example, PostfixUpdate is not included because it only has a changed lval if
|
||||
* it has a changed argument, but LoadProperty is included because the argument can be
|
||||
* mutated elsewhere.
|
||||
*/
|
||||
function mayHaveChanged(env: Environment, instruction: Instruction): boolean {
|
||||
if (env.config.enableChangeDetection == null) {
|
||||
return false;
|
||||
}
|
||||
switch (instruction.value.kind) {
|
||||
case "Await":
|
||||
case "ComputedLoad":
|
||||
case "Destructure":
|
||||
case "GetIterator":
|
||||
case "IteratorNext":
|
||||
case "NextPropertyOf":
|
||||
case "CallExpression":
|
||||
case "MethodCall":
|
||||
case "NewExpression": {
|
||||
return true;
|
||||
}
|
||||
case "PropertyLoad": {
|
||||
return instruction.value.property !== "current"
|
||||
}
|
||||
case "LoadGlobal": {
|
||||
return (
|
||||
instruction.value.binding.kind === "ModuleLocal" &&
|
||||
!instruction.value.binding.immutable
|
||||
);
|
||||
}
|
||||
case "PostfixUpdate":
|
||||
case "PrefixUpdate":
|
||||
case "DeclareLocal":
|
||||
case "DeclareContext":
|
||||
case "StoreLocal":
|
||||
case "MetaProperty":
|
||||
case "TypeCastExpression":
|
||||
case "LoadLocal":
|
||||
case "LoadContext":
|
||||
case "StoreContext":
|
||||
case "PropertyDelete":
|
||||
case "ComputedDelete":
|
||||
case "JSXText":
|
||||
case "TemplateLiteral":
|
||||
case "Primitive":
|
||||
case "Debugger":
|
||||
case "StartMemoize":
|
||||
case "FinishMemoize":
|
||||
case "UnaryExpression":
|
||||
case "BinaryExpression":
|
||||
case "StoreGlobal":
|
||||
case "RegExpLiteral":
|
||||
case "PropertyStore":
|
||||
case "ComputedStore":
|
||||
case "ArrayExpression":
|
||||
case "JsxExpression":
|
||||
case "JsxFragment":
|
||||
case "ObjectExpression":
|
||||
case "UnsupportedNode":
|
||||
case "ObjectMethod":
|
||||
case "FunctionExpression":
|
||||
case "TaggedTemplateExpression": {
|
||||
return false;
|
||||
}
|
||||
default: {
|
||||
assertExhaustive(
|
||||
instruction.value,
|
||||
`Unexpected value kind \`${(instruction.value as any).kind}\``
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function isIdentifierMutable(id: Identifier): boolean {
|
||||
return id.mutableRange.end > id.mutableRange.start + 1;
|
||||
}
|
||||
|
||||
function identifierHasMutableRange(id: Identifier): boolean {
|
||||
return id.mutableRange.start > 0;
|
||||
}
|
||||
|
||||
export function findDisjointMutableValues(
|
||||
fn: HIRFunction,
|
||||
): DisjointSet<Identifier> {
|
||||
@@ -265,7 +351,7 @@ export function findDisjointMutableValues(
|
||||
for (const phi of block.phis) {
|
||||
if (
|
||||
// The phi was reset because it was not mutated after creation
|
||||
phi.id.mutableRange.start + 1 !== phi.id.mutableRange.end &&
|
||||
isIdentifierMutable(phi.id) &&
|
||||
phi.id.mutableRange.end >
|
||||
(block.instructions.at(0)?.id ?? block.terminal.id)
|
||||
) {
|
||||
@@ -281,50 +367,52 @@ export function findDisjointMutableValues(
|
||||
|
||||
for (const instr of block.instructions) {
|
||||
const operands: Array<Identifier> = [];
|
||||
const range = instr.lvalue.identifier.mutableRange;
|
||||
if (range.end > range.start + 1 || mayAllocate(fn.env, instr)) {
|
||||
if (
|
||||
isIdentifierMutable(instr.lvalue.identifier) ||
|
||||
mayAllocate(fn.env, instr) ||
|
||||
mayHaveChanged(fn.env, instr)
|
||||
) {
|
||||
operands.push(instr.lvalue!.identifier);
|
||||
}
|
||||
if (
|
||||
instr.value.kind === 'StoreLocal' ||
|
||||
instr.value.kind === 'StoreContext'
|
||||
) {
|
||||
if (
|
||||
instr.value.lvalue.place.identifier.mutableRange.end >
|
||||
instr.value.lvalue.place.identifier.mutableRange.start + 1
|
||||
) {
|
||||
if (isIdentifierMutable(instr.value.lvalue.place.identifier)) {
|
||||
operands.push(instr.value.lvalue.place.identifier);
|
||||
}
|
||||
if (
|
||||
isMutable(instr, instr.value.value) &&
|
||||
instr.value.value.identifier.mutableRange.start > 0
|
||||
isMutableAtInstruction(instr, instr.value.value) &&
|
||||
identifierHasMutableRange(instr.value.value.identifier)
|
||||
) {
|
||||
operands.push(instr.value.value.identifier);
|
||||
}
|
||||
} else if (instr.value.kind === 'Destructure') {
|
||||
for (const place of eachPatternOperand(instr.value.lvalue.pattern)) {
|
||||
if (
|
||||
place.identifier.mutableRange.end >
|
||||
place.identifier.mutableRange.start + 1
|
||||
isIdentifierMutable(place.identifier) ||
|
||||
mayHaveChanged(fn.env, instr)
|
||||
) {
|
||||
operands.push(place.identifier);
|
||||
}
|
||||
}
|
||||
if (
|
||||
isMutable(instr, instr.value.value) &&
|
||||
instr.value.value.identifier.mutableRange.start > 0
|
||||
(isMutableAtInstruction(instr, instr.value.value) &&
|
||||
identifierHasMutableRange(instr.value.value.identifier)) ||
|
||||
mayHaveChanged(fn.env, instr)
|
||||
) {
|
||||
operands.push(instr.value.value.identifier);
|
||||
}
|
||||
} else if (instr.value.kind === 'MethodCall') {
|
||||
for (const operand of eachInstructionOperand(instr)) {
|
||||
if (
|
||||
isMutable(instr, operand) &&
|
||||
/*
|
||||
* exclude global variables from being added to scopes, we can't recreate them!
|
||||
* TODO: improve handling of module-scoped variables and globals
|
||||
*/
|
||||
operand.identifier.mutableRange.start > 0
|
||||
(isMutableAtInstruction(instr, operand) &&
|
||||
/*
|
||||
* exclude global variables from being added to scopes, we can't recreate them!
|
||||
* TODO: improve handling of module-scoped variables and globals
|
||||
*/
|
||||
identifierHasMutableRange(operand.identifier)) ||
|
||||
mayHaveChanged(fn.env, instr)
|
||||
) {
|
||||
operands.push(operand.identifier);
|
||||
}
|
||||
@@ -337,12 +425,13 @@ export function findDisjointMutableValues(
|
||||
} else {
|
||||
for (const operand of eachInstructionOperand(instr)) {
|
||||
if (
|
||||
isMutable(instr, operand) &&
|
||||
/*
|
||||
* exclude global variables from being added to scopes, we can't recreate them!
|
||||
* TODO: improve handling of module-scoped variables and globals
|
||||
*/
|
||||
operand.identifier.mutableRange.start > 0
|
||||
(isMutableAtInstruction(instr, operand) &&
|
||||
/*
|
||||
* exclude global variables from being added to scopes, we can't recreate them!
|
||||
* TODO: improve handling of module-scoped variables and globals
|
||||
*/
|
||||
identifierHasMutableRange(operand.identifier)) ||
|
||||
mayHaveChanged(fn.env, instr)
|
||||
) {
|
||||
operands.push(operand.identifier);
|
||||
}
|
||||
|
||||
@@ -45,6 +45,7 @@ export function memoizeFbtAndMacroOperandsInSameScope(
|
||||
const fbtMacroTags = new Set([
|
||||
...FBT_TAGS,
|
||||
...(fn.env.config.customMacros ?? []),
|
||||
...(fn.env.preserveManualMemo() ? ["useMemo", "useCallback"] : []),
|
||||
]);
|
||||
const fbtValues: Set<IdentifierId> = new Set();
|
||||
while (true) {
|
||||
|
||||
@@ -811,7 +811,9 @@ class CollectDependenciesVisitor extends ReactiveFunctionVisitor<State> {
|
||||
this.env = env;
|
||||
this.options = {
|
||||
memoizeJsxElements: !this.env.config.enableForest,
|
||||
forceMemoizePrimitives: this.env.config.enableForest,
|
||||
forceMemoizePrimitives:
|
||||
this.env.config.enableForest ||
|
||||
this.env.config.enableChangeDetection != null,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ import {
|
||||
isUseInsertionEffectHookType,
|
||||
isUseLayoutEffectHookType,
|
||||
} from '../HIR';
|
||||
import {isMutable} from '../ReactiveScopes/InferReactiveScopeVariables';
|
||||
import {isMutableAtInstruction} from '../ReactiveScopes/InferReactiveScopeVariables';
|
||||
import {
|
||||
ReactiveFunctionVisitor,
|
||||
visitReactiveFunction,
|
||||
@@ -103,7 +103,7 @@ class Visitor extends ReactiveFunctionVisitor<CompilerError> {
|
||||
* TODO: isMutable is not safe to call here as it relies on identifier mutableRange which is no longer valid at this point
|
||||
* in the pipeline
|
||||
*/
|
||||
(isMutable(instruction as Instruction, deps) ||
|
||||
(isMutableAtInstruction(instruction as Instruction, deps) ||
|
||||
isUnmemoized(deps.identifier, this.scopes))
|
||||
) {
|
||||
state.push({
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @enableChangeDetectionForDebugging
|
||||
// @enableChangeDetection
|
||||
function Component(props) {
|
||||
let x = null;
|
||||
if (props.cond) {
|
||||
@@ -18,7 +18,7 @@ function Component(props) {
|
||||
|
||||
```javascript
|
||||
import { $structuralCheck } from "react-compiler-runtime";
|
||||
import { c as _c } from "react/compiler-runtime"; // @enableChangeDetectionForDebugging
|
||||
import { c as _c } from "react/compiler-runtime"; // @enableChangeDetection
|
||||
function Component(props) {
|
||||
const $ = _c(2);
|
||||
let x = null;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// @enableChangeDetectionForDebugging
|
||||
// @enableChangeDetection
|
||||
function Component(props) {
|
||||
let x = null;
|
||||
if (props.cond) {
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @enableChangeDetectionWrappers
|
||||
function Component(props) {
|
||||
let x = null;
|
||||
if (props.cond) {
|
||||
x = [];
|
||||
x.push(props.value);
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { $structuralCheck, $store, $restore } from "react-compiler-runtime";
|
||||
import { c as _c } from "react/compiler-runtime"; // @enableChangeDetectionWrappers
|
||||
function Component(props) {
|
||||
const $ = _c(2);
|
||||
let x = null;
|
||||
if (props.cond) {
|
||||
{
|
||||
x = [];
|
||||
x.push(props.value);
|
||||
let condition = $[0] !== props.value;
|
||||
if (!condition) {
|
||||
let old$x = $[1];
|
||||
$structuralCheck(old$x, x, "x", "Component", "cached", "(3:6)");
|
||||
}
|
||||
$[0] = props.value;
|
||||
$[1] = $store(x);
|
||||
if (condition) {
|
||||
x = [];
|
||||
x.push(props.value);
|
||||
$structuralCheck($[1], x, "x", "Component", "recomputed", "(3:6)");
|
||||
x = $[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
// @enableChangeDetectionWrappers
|
||||
function Component(props) {
|
||||
let x = null;
|
||||
if (props.cond) {
|
||||
x = [];
|
||||
x.push(props.value);
|
||||
}
|
||||
return x;
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @enableChangeDetection
|
||||
let glob = 1;
|
||||
|
||||
function Component(props) {
|
||||
const a = props.x;
|
||||
const { b, ...c } = props.y;
|
||||
const d = glob;
|
||||
return (
|
||||
<div>
|
||||
{a}
|
||||
{b}
|
||||
{c.c}
|
||||
{d}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { $structuralCheck } from "react-compiler-runtime";
|
||||
import { c as _c } from "react/compiler-runtime"; // @enableChangeDetection
|
||||
let glob = 1;
|
||||
|
||||
function Component(props) {
|
||||
const $ = _c(12);
|
||||
let t0;
|
||||
{
|
||||
t0 = props.x;
|
||||
let condition = $[0] !== props.x;
|
||||
if (!condition) {
|
||||
let old$t0 = $[1];
|
||||
$structuralCheck(old$t0, t0, "t0", "Component", "cached", "(5:5)");
|
||||
}
|
||||
$[0] = props.x;
|
||||
$[1] = t0;
|
||||
if (condition) {
|
||||
t0 = props.x;
|
||||
$structuralCheck($[1], t0, "t0", "Component", "recomputed", "(5:5)");
|
||||
t0 = $[1];
|
||||
}
|
||||
}
|
||||
const a = t0;
|
||||
let c;
|
||||
let b;
|
||||
{
|
||||
({ b, ...c } = props.y);
|
||||
let condition = $[2] !== props.y;
|
||||
if (!condition) {
|
||||
let old$c = $[3];
|
||||
let old$b = $[4];
|
||||
$structuralCheck(old$c, c, "c", "Component", "cached", "(6:6)");
|
||||
$structuralCheck(old$b, b, "b", "Component", "cached", "(6:6)");
|
||||
}
|
||||
$[2] = props.y;
|
||||
$[3] = c;
|
||||
$[4] = b;
|
||||
if (condition) {
|
||||
({ b, ...c } = props.y);
|
||||
$structuralCheck($[3], c, "c", "Component", "recomputed", "(6:6)");
|
||||
c = $[3];
|
||||
$structuralCheck($[4], b, "b", "Component", "recomputed", "(6:6)");
|
||||
b = $[4];
|
||||
}
|
||||
}
|
||||
let t1;
|
||||
{
|
||||
t1 = c.c;
|
||||
let condition = $[5] !== c.c;
|
||||
if (!condition) {
|
||||
let old$t1 = $[6];
|
||||
$structuralCheck(old$t1, t1, "t1", "Component", "cached", "(12:12)");
|
||||
}
|
||||
$[5] = c.c;
|
||||
$[6] = t1;
|
||||
if (condition) {
|
||||
t1 = c.c;
|
||||
$structuralCheck($[6], t1, "t1", "Component", "recomputed", "(12:12)");
|
||||
t1 = $[6];
|
||||
}
|
||||
}
|
||||
let t2;
|
||||
{
|
||||
t2 = glob;
|
||||
let condition = $[7] === Symbol.for("react.memo_cache_sentinel");
|
||||
if (!condition) {
|
||||
let old$t2 = $[7];
|
||||
$structuralCheck(old$t2, t2, "t2", "Component", "cached", "(13:13)");
|
||||
}
|
||||
$[7] = t2;
|
||||
if (condition) {
|
||||
t2 = glob;
|
||||
$structuralCheck($[7], t2, "t2", "Component", "recomputed", "(13:13)");
|
||||
t2 = $[7];
|
||||
}
|
||||
}
|
||||
let t3;
|
||||
{
|
||||
t3 = (
|
||||
<div>
|
||||
{a}
|
||||
{b}
|
||||
{t1}
|
||||
{t2}
|
||||
</div>
|
||||
);
|
||||
let condition = $[8] !== a || $[9] !== b || $[10] !== t1;
|
||||
if (!condition) {
|
||||
let old$t3 = $[11];
|
||||
$structuralCheck(old$t3, t3, "t3", "Component", "cached", "(9:14)");
|
||||
}
|
||||
$[8] = a;
|
||||
$[9] = b;
|
||||
$[10] = t1;
|
||||
$[11] = t3;
|
||||
if (condition) {
|
||||
t3 = (
|
||||
<div>
|
||||
{a}
|
||||
{b}
|
||||
{t1}
|
||||
{t2}
|
||||
</div>
|
||||
);
|
||||
$structuralCheck($[11], t3, "t3", "Component", "recomputed", "(9:14)");
|
||||
t3 = $[11];
|
||||
}
|
||||
}
|
||||
return t3;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
// @enableChangeDetection
|
||||
let glob = 1;
|
||||
|
||||
function Component(props) {
|
||||
const a = props.x;
|
||||
const { b, ...c } = props.y;
|
||||
const d = glob;
|
||||
return (
|
||||
<div>
|
||||
{a}
|
||||
{b}
|
||||
{c.c}
|
||||
{d}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @disableMemoizationForDebugging @enableChangeDetectionForDebugging
|
||||
function Component(props) {}
|
||||
|
||||
```
|
||||
|
||||
|
||||
## Error
|
||||
|
||||
```
|
||||
InvalidConfig: Invalid environment config: the 'disableMemoizationForDebugging' and 'enableChangeDetectionForDebugging' options cannot be used together
|
||||
```
|
||||
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
// @disableMemoizationForDebugging @enableChangeDetectionForDebugging
|
||||
function Component(props) {}
|
||||
@@ -0,0 +1,107 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @disableMemoization @enableChangeDetection
|
||||
import { useMemo } from "react";
|
||||
|
||||
function Component(props) {
|
||||
const a = useMemo(() => <div>{props.a}</div>, [props]);
|
||||
const b = <div>{props.b}</div>;
|
||||
return (
|
||||
<div>
|
||||
{a}
|
||||
{b}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{ a: 1, b: 2 }],
|
||||
isComponent: true,
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { $structuralCheck } from "react-compiler-runtime";
|
||||
import { c as _c } from "react/compiler-runtime"; // @disableMemoization @enableChangeDetection
|
||||
import { useMemo } from "react";
|
||||
|
||||
function Component(props) {
|
||||
const $ = _c(7);
|
||||
const a = useMemo(() => <div>{props.a}</div>, [props]);
|
||||
let t0;
|
||||
{
|
||||
t0 = props.b;
|
||||
let condition = $[0] !== props.b || true;
|
||||
if (!condition) {
|
||||
let old$t0 = $[1];
|
||||
$structuralCheck(old$t0, t0, "t0", "Component", "cached", "(6:6)");
|
||||
}
|
||||
$[0] = props.b;
|
||||
$[1] = t0;
|
||||
if (condition) {
|
||||
t0 = props.b;
|
||||
$structuralCheck($[1], t0, "t0", "Component", "recomputed", "(6:6)");
|
||||
t0 = $[1];
|
||||
}
|
||||
}
|
||||
let t1;
|
||||
{
|
||||
t1 = <div>{t0}</div>;
|
||||
let condition = $[2] !== t0 || true;
|
||||
if (!condition) {
|
||||
let old$t1 = $[3];
|
||||
$structuralCheck(old$t1, t1, "t1", "Component", "cached", "(6:6)");
|
||||
}
|
||||
$[2] = t0;
|
||||
$[3] = t1;
|
||||
if (condition) {
|
||||
t1 = <div>{t0}</div>;
|
||||
$structuralCheck($[3], t1, "t1", "Component", "recomputed", "(6:6)");
|
||||
t1 = $[3];
|
||||
}
|
||||
}
|
||||
const b = t1;
|
||||
let t2;
|
||||
{
|
||||
t2 = (
|
||||
<div>
|
||||
{a}
|
||||
{b}
|
||||
</div>
|
||||
);
|
||||
let condition = $[4] !== a || $[5] !== b || true;
|
||||
if (!condition) {
|
||||
let old$t2 = $[6];
|
||||
$structuralCheck(old$t2, t2, "t2", "Component", "cached", "(8:11)");
|
||||
}
|
||||
$[4] = a;
|
||||
$[5] = b;
|
||||
$[6] = t2;
|
||||
if (condition) {
|
||||
t2 = (
|
||||
<div>
|
||||
{a}
|
||||
{b}
|
||||
</div>
|
||||
);
|
||||
$structuralCheck($[6], t2, "t2", "Component", "recomputed", "(8:11)");
|
||||
t2 = $[6];
|
||||
}
|
||||
}
|
||||
return t2;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{ a: 1, b: 2 }],
|
||||
isComponent: true,
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
// @disableMemoization @enableChangeDetection
|
||||
import { useMemo } from "react";
|
||||
|
||||
function Component(props) {
|
||||
const a = useMemo(() => <div>{props.a}</div>, [props]);
|
||||
const b = <div>{props.b}</div>;
|
||||
return (
|
||||
<div>
|
||||
{a}
|
||||
{b}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{ a: 1, b: 2 }],
|
||||
isComponent: true,
|
||||
};
|
||||
@@ -2,7 +2,7 @@
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @disableMemoizationForDebugging
|
||||
// @disableMemoization
|
||||
import {useMemo} from 'react';
|
||||
|
||||
function Component({a}) {
|
||||
@@ -21,37 +21,22 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime"; // @disableMemoizationForDebugging
|
||||
import { c as _c } from "react/compiler-runtime"; // @disableMemoization
|
||||
import { useMemo } from "react";
|
||||
|
||||
function Component(t0) {
|
||||
const $ = _c(5);
|
||||
const $ = _c(2);
|
||||
const { a } = t0;
|
||||
const x = useMemo(() => [a], []);
|
||||
let t1;
|
||||
if ($[0] !== a || true) {
|
||||
t1 = () => [a];
|
||||
$[0] = a;
|
||||
if ($[0] !== x || true) {
|
||||
t1 = <div>{x}</div>;
|
||||
$[0] = x;
|
||||
$[1] = t1;
|
||||
} else {
|
||||
t1 = $[1];
|
||||
}
|
||||
let t2;
|
||||
if ($[2] === Symbol.for("react.memo_cache_sentinel") || true) {
|
||||
t2 = [];
|
||||
$[2] = t2;
|
||||
} else {
|
||||
t2 = $[2];
|
||||
}
|
||||
const x = useMemo(t1, t2);
|
||||
let t3;
|
||||
if ($[3] !== x || true) {
|
||||
t3 = <div>{x}</div>;
|
||||
$[3] = x;
|
||||
$[4] = t3;
|
||||
} else {
|
||||
t3 = $[4];
|
||||
}
|
||||
return t3;
|
||||
return t1;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// @disableMemoizationForDebugging
|
||||
// @disableMemoization
|
||||
import {useMemo} from 'react';
|
||||
|
||||
function Component({a}) {
|
||||
|
||||
@@ -25,33 +25,18 @@ import { c as _c } from "react/compiler-runtime"; // @enablePreserveExistingManu
|
||||
import { useMemo } from "react";
|
||||
|
||||
function Component(t0) {
|
||||
const $ = _c(5);
|
||||
const $ = _c(2);
|
||||
const { a } = t0;
|
||||
const x = useMemo(() => [a], []);
|
||||
let t1;
|
||||
if ($[0] !== a) {
|
||||
t1 = () => [a];
|
||||
$[0] = a;
|
||||
if ($[0] !== x) {
|
||||
t1 = <div>{x}</div>;
|
||||
$[0] = x;
|
||||
$[1] = t1;
|
||||
} else {
|
||||
t1 = $[1];
|
||||
}
|
||||
let t2;
|
||||
if ($[2] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t2 = [];
|
||||
$[2] = t2;
|
||||
} else {
|
||||
t2 = $[2];
|
||||
}
|
||||
const x = useMemo(t1, t2);
|
||||
let t3;
|
||||
if ($[3] !== x) {
|
||||
t3 = <div>{x}</div>;
|
||||
$[3] = x;
|
||||
$[4] = t3;
|
||||
} else {
|
||||
t3 = $[4];
|
||||
}
|
||||
return t3;
|
||||
return t1;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
import {useState} from 'react'; // @enableChangeDetectionForDebugging
|
||||
import {useState} from 'react'; // @enableChangeDetection
|
||||
|
||||
function useOther(x) {
|
||||
return x;
|
||||
@@ -32,7 +32,7 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
```javascript
|
||||
import { $structuralCheck } from "react-compiler-runtime";
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
import { useState } from "react"; // @enableChangeDetectionForDebugging
|
||||
import { useState } from "react"; // @enableChangeDetection
|
||||
|
||||
function useOther(x) {
|
||||
return x;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import {useState} from 'react'; // @enableChangeDetectionForDebugging
|
||||
import {useState} from 'react'; // @enableChangeDetection
|
||||
|
||||
function useOther(x) {
|
||||
return x;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @enableChangeDetectionForDebugging
|
||||
// @enableChangeDetection
|
||||
import {useState} from 'react';
|
||||
|
||||
function Component(props) {
|
||||
@@ -16,36 +16,29 @@ function Component(props) {
|
||||
|
||||
```javascript
|
||||
import { $structuralCheck } from "react-compiler-runtime";
|
||||
import { c as _c } from "react/compiler-runtime"; // @enableChangeDetectionForDebugging
|
||||
import { c as _c } from "react/compiler-runtime"; // @enableChangeDetection
|
||||
import { useState } from "react";
|
||||
|
||||
function Component(props) {
|
||||
const $ = _c(3);
|
||||
const $ = _c(2);
|
||||
const [x] = useState(f(props.x));
|
||||
let t0;
|
||||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t0 = f(props.x);
|
||||
$[0] = t0;
|
||||
} else {
|
||||
t0 = $[0];
|
||||
}
|
||||
const [x] = useState(t0);
|
||||
let t1;
|
||||
{
|
||||
t1 = <div>{x}</div>;
|
||||
let condition = $[1] !== x;
|
||||
t0 = <div>{x}</div>;
|
||||
let condition = $[0] !== x;
|
||||
if (!condition) {
|
||||
let old$t1 = $[2];
|
||||
$structuralCheck(old$t1, t1, "t1", "Component", "cached", "(6:6)");
|
||||
let old$t0 = $[1];
|
||||
$structuralCheck(old$t0, t0, "t0", "Component", "cached", "(6:6)");
|
||||
}
|
||||
$[1] = x;
|
||||
$[2] = t1;
|
||||
$[0] = x;
|
||||
$[1] = t0;
|
||||
if (condition) {
|
||||
t1 = <div>{x}</div>;
|
||||
$structuralCheck($[2], t1, "t1", "Component", "recomputed", "(6:6)");
|
||||
t1 = $[2];
|
||||
t0 = <div>{x}</div>;
|
||||
$structuralCheck($[1], t0, "t0", "Component", "recomputed", "(6:6)");
|
||||
t0 = $[1];
|
||||
}
|
||||
}
|
||||
return t1;
|
||||
return t0;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// @enableChangeDetectionForDebugging
|
||||
// @enableChangeDetection
|
||||
import {useState} from 'react';
|
||||
|
||||
function Component(props) {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
import {useState} from 'react'; // @enableChangeDetectionForDebugging
|
||||
import {useState} from 'react'; // @enableChangeDetection
|
||||
|
||||
function Component(props) {
|
||||
const w = f(props.x);
|
||||
@@ -32,7 +32,7 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
```javascript
|
||||
import { $structuralCheck } from "react-compiler-runtime";
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
import { useState } from "react"; // @enableChangeDetectionForDebugging
|
||||
import { useState } from "react"; // @enableChangeDetection
|
||||
|
||||
function Component(props) {
|
||||
const $ = _c(5);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import {useState} from 'react'; // @enableChangeDetectionForDebugging
|
||||
import {useState} from 'react'; // @enableChangeDetection
|
||||
|
||||
function Component(props) {
|
||||
const w = f(props.x);
|
||||
|
||||
@@ -493,12 +493,14 @@ const skipFilter = new Set([
|
||||
|
||||
// 'react-compiler-runtime' not yet supported
|
||||
'flag-enable-emit-hook-guards',
|
||||
|
||||
'fast-refresh-refresh-on-const-changes-dev',
|
||||
'useState-pruned-dependency-change-detect',
|
||||
'useState-unpruned-dependency',
|
||||
'useState-and-other-hook-unpruned-dependency',
|
||||
'change-detect-reassign',
|
||||
"change-detect",
|
||||
"nomemo-and-change-detect",
|
||||
"change-detect-wrapper",
|
||||
|
||||
// needs to be executed as a module
|
||||
'meta-property',
|
||||
|
||||
@@ -45,7 +45,7 @@ function makePluginOptions(
|
||||
let hookPattern: string | null = null;
|
||||
// TODO(@mofeiZ) rewrite snap fixtures to @validatePreserveExistingMemo:false
|
||||
let validatePreserveExistingMemoizationGuarantees = false;
|
||||
let enableChangeDetectionForDebugging = null;
|
||||
let enableChangeDetection = null;
|
||||
let customMacros = null;
|
||||
|
||||
if (firstLine.indexOf('@compilationMode(annotation)') !== -1) {
|
||||
@@ -124,10 +124,20 @@ function makePluginOptions(
|
||||
validatePreserveExistingMemoizationGuarantees = true;
|
||||
}
|
||||
|
||||
if (firstLine.includes('@enableChangeDetectionForDebugging')) {
|
||||
enableChangeDetectionForDebugging = {
|
||||
source: 'react-compiler-runtime',
|
||||
importSpecifierName: '$structuralCheck',
|
||||
if (firstLine.includes("@enableChangeDetection")) {
|
||||
enableChangeDetection = {
|
||||
source: "react-compiler-runtime",
|
||||
structuralCheck: "$structuralCheck",
|
||||
};
|
||||
}
|
||||
if (firstLine.includes("@enableChangeDetectionWrappers")) {
|
||||
enableChangeDetection = {
|
||||
source: "react-compiler-runtime",
|
||||
structuralCheck: "$structuralCheck",
|
||||
wrappers: {
|
||||
store: "$store",
|
||||
restore: "$restore",
|
||||
},
|
||||
};
|
||||
}
|
||||
const hookPatternMatch = /@hookPattern:"([^"]+)"/.exec(firstLine);
|
||||
@@ -206,7 +216,7 @@ function makePluginOptions(
|
||||
enableSharedRuntime__testonly: true,
|
||||
hookPattern,
|
||||
validatePreserveExistingMemoizationGuarantees,
|
||||
enableChangeDetectionForDebugging,
|
||||
enableChangeDetection,
|
||||
},
|
||||
compilationMode,
|
||||
logger,
|
||||
|
||||
Reference in New Issue
Block a user