@@ -55944,14 +55944,78 @@ function suppressionsToCompilerError(suppressionRanges) {
55944
55944
55945
55945
const OPT_IN_DIRECTIVES = new Set(['use forget', 'use memo']);
55946
55946
const OPT_OUT_DIRECTIVES = new Set(['use no forget', 'use no memo']);
55947
- function findDirectiveEnablingMemoization(directives) {
55948
- var _a;
55949
- return ((_a = directives.find(directive => OPT_IN_DIRECTIVES.has(directive.value.value))) !== null && _a !== void 0 ? _a : null);
55947
+ const DYNAMIC_GATING_DIRECTIVE = new RegExp('^use memo if\\(([^\\)]*)\\)$');
55948
+ function tryFindDirectiveEnablingMemoization(directives, opts) {
55949
+ var _a, _b;
55950
+ const optIn = directives.find(directive => OPT_IN_DIRECTIVES.has(directive.value.value));
55951
+ if (optIn != null) {
55952
+ return Ok(optIn);
55953
+ }
55954
+ const dynamicGating = findDirectivesDynamicGating(directives, opts);
55955
+ if (dynamicGating.isOk()) {
55956
+ return Ok((_b = (_a = dynamicGating.unwrap()) === null || _a === void 0 ? void 0 : _a.directive) !== null && _b !== void 0 ? _b : null);
55957
+ }
55958
+ else {
55959
+ return Err(dynamicGating.unwrapErr());
55960
+ }
55950
55961
}
55951
55962
function findDirectiveDisablingMemoization(directives) {
55952
55963
var _a;
55953
55964
return ((_a = directives.find(directive => OPT_OUT_DIRECTIVES.has(directive.value.value))) !== null && _a !== void 0 ? _a : null);
55954
55965
}
55966
+ function findDirectivesDynamicGating(directives, opts) {
55967
+ var _a, _b;
55968
+ if (opts.dynamicGating === null) {
55969
+ return Ok(null);
55970
+ }
55971
+ const errors = new CompilerError();
55972
+ const result = [];
55973
+ for (const directive of directives) {
55974
+ const maybeMatch = DYNAMIC_GATING_DIRECTIVE.exec(directive.value.value);
55975
+ if (maybeMatch != null && maybeMatch[1] != null) {
55976
+ if (libExports$1.isValidIdentifier(maybeMatch[1])) {
55977
+ result.push({ directive, match: maybeMatch[1] });
55978
+ }
55979
+ else {
55980
+ errors.push({
55981
+ reason: `Dynamic gating directive is not a valid JavaScript identifier`,
55982
+ description: `Found '${directive.value.value}'`,
55983
+ severity: ErrorSeverity.InvalidReact,
55984
+ loc: (_a = directive.loc) !== null && _a !== void 0 ? _a : null,
55985
+ suggestions: null,
55986
+ });
55987
+ }
55988
+ }
55989
+ }
55990
+ if (errors.hasErrors()) {
55991
+ return Err(errors);
55992
+ }
55993
+ else if (result.length > 1) {
55994
+ const error = new CompilerError();
55995
+ error.push({
55996
+ reason: `Multiple dynamic gating directives found`,
55997
+ description: `Expected a single directive but found [${result
55998
+ .map(r => r.directive.value.value)
55999
+ .join(', ')}]`,
56000
+ severity: ErrorSeverity.InvalidReact,
56001
+ loc: (_b = result[0].directive.loc) !== null && _b !== void 0 ? _b : null,
56002
+ suggestions: null,
56003
+ });
56004
+ return Err(error);
56005
+ }
56006
+ else if (result.length === 1) {
56007
+ return Ok({
56008
+ gating: {
56009
+ source: opts.dynamicGating.source,
56010
+ importSpecifierName: result[0].match,
56011
+ },
56012
+ directive: result[0].directive,
56013
+ });
56014
+ }
56015
+ else {
56016
+ return Ok(null);
56017
+ }
56018
+ }
55955
56019
function isCriticalError(err) {
55956
56020
return !(err instanceof CompilerError) || err.isCritical();
55957
56021
}
@@ -56184,25 +56248,33 @@ function findFunctionsToCompile(program, pass, programContext) {
56184
56248
return queue;
56185
56249
}
56186
56250
function processFn(fn, fnType, programContext) {
56187
- var _a, _b, _c, _d, _e, _f, _g;
56251
+ var _a, _b, _c, _d, _e, _f, _g, _h ;
56188
56252
let directives;
56189
56253
if (fn.node.body.type !== 'BlockStatement') {
56190
- directives = { optIn: null, optOut: null };
56254
+ directives = {
56255
+ optIn: null,
56256
+ optOut: null,
56257
+ };
56191
56258
}
56192
56259
else {
56260
+ const optIn = tryFindDirectiveEnablingMemoization(fn.node.body.directives, programContext.opts);
56261
+ if (optIn.isErr()) {
56262
+ handleError(optIn.unwrapErr(), programContext, (_a = fn.node.loc) !== null && _a !== void 0 ? _a : null);
56263
+ return null;
56264
+ }
56193
56265
directives = {
56194
- optIn: findDirectiveEnablingMemoization(fn.node.body.directives ),
56266
+ optIn: optIn.unwrapOr(null ),
56195
56267
optOut: findDirectiveDisablingMemoization(fn.node.body.directives),
56196
56268
};
56197
56269
}
56198
56270
let compiledFn;
56199
56271
const compileResult = tryCompileFunction(fn, fnType, programContext);
56200
56272
if (compileResult.kind === 'error') {
56201
56273
if (directives.optOut != null) {
56202
- logError(compileResult.error, programContext, (_a = fn.node.loc) !== null && _a !== void 0 ? _a : null);
56274
+ logError(compileResult.error, programContext, (_b = fn.node.loc) !== null && _b !== void 0 ? _b : null);
56203
56275
}
56204
56276
else {
56205
- handleError(compileResult.error, programContext, (_b = fn.node.loc) !== null && _b !== void 0 ? _b : null);
56277
+ handleError(compileResult.error, programContext, (_c = fn.node.loc) !== null && _c !== void 0 ? _c : null);
56206
56278
}
56207
56279
const retryResult = retryCompileFunction(fn, fnType, programContext);
56208
56280
if (retryResult == null) {
@@ -56217,16 +56289,16 @@ function processFn(fn, fnType, programContext) {
56217
56289
directives.optOut != null) {
56218
56290
programContext.logEvent({
56219
56291
kind: 'CompileSkip',
56220
- fnLoc: (_c = fn.node.body.loc) !== null && _c !== void 0 ? _c : null,
56292
+ fnLoc: (_d = fn.node.body.loc) !== null && _d !== void 0 ? _d : null,
56221
56293
reason: `Skipped due to '${directives.optOut.value}' directive.`,
56222
- loc: (_d = directives.optOut.loc) !== null && _d !== void 0 ? _d : null,
56294
+ loc: (_e = directives.optOut.loc) !== null && _e !== void 0 ? _e : null,
56223
56295
});
56224
56296
return null;
56225
56297
}
56226
56298
programContext.logEvent({
56227
56299
kind: 'CompileSuccess',
56228
- fnLoc: (_e = fn.node.loc) !== null && _e !== void 0 ? _e : null,
56229
- fnName: (_g = (_f = compiledFn.id) === null || _f === void 0 ? void 0 : _f .name) !== null && _g !== void 0 ? _g : null,
56300
+ fnLoc: (_f = fn.node.loc) !== null && _f !== void 0 ? _f : null,
56301
+ fnName: (_h = (_g = compiledFn.id) === null || _g === void 0 ? void 0 : _g .name) !== null && _h !== void 0 ? _h : null,
56230
56302
memoSlots: compiledFn.memoSlotsUsed,
56231
56303
memoBlocks: compiledFn.memoBlocks,
56232
56304
memoValues: compiledFn.memoValues,
@@ -56290,19 +56362,23 @@ function retryCompileFunction(fn, fnType, programContext) {
56290
56362
}
56291
56363
}
56292
56364
function applyCompiledFunctions(program, compiledFns, pass, programContext) {
56293
- const referencedBeforeDeclared = pass.opts.gating != null
56294
- ? getFunctionReferencedBeforeDeclarationAtTopLevel(program, compiledFns)
56295
- : null;
56365
+ var _a, _b;
56366
+ let referencedBeforeDeclared = null;
56296
56367
for (const result of compiledFns) {
56297
56368
const { kind, originalFn, compiledFn } = result;
56298
56369
const transformedFn = createNewFunctionNode(originalFn, compiledFn);
56299
56370
programContext.alreadyCompiled.add(transformedFn);
56300
- if (referencedBeforeDeclared != null && kind === 'original') {
56301
- CompilerError.invariant(pass.opts.gating != null, {
56302
- reason: "Expected 'gating' import to be present",
56303
- loc: null,
56304
- });
56305
- insertGatedFunctionDeclaration(originalFn, transformedFn, programContext, pass.opts.gating, referencedBeforeDeclared.has(result));
56371
+ let dynamicGating = null;
56372
+ if (originalFn.node.body.type === 'BlockStatement') {
56373
+ const result = findDirectivesDynamicGating(originalFn.node.body.directives, pass.opts);
56374
+ if (result.isOk()) {
56375
+ dynamicGating = (_b = (_a = result.unwrap()) === null || _a === void 0 ? void 0 : _a.gating) !== null && _b !== void 0 ? _b : null;
56376
+ }
56377
+ }
56378
+ const functionGating = dynamicGating !== null && dynamicGating !== void 0 ? dynamicGating : pass.opts.gating;
56379
+ if (kind === 'original' && functionGating != null) {
56380
+ referencedBeforeDeclared !== null && referencedBeforeDeclared !== void 0 ? referencedBeforeDeclared : (referencedBeforeDeclared = getFunctionReferencedBeforeDeclarationAtTopLevel(program, compiledFns));
56381
+ insertGatedFunctionDeclaration(originalFn, transformedFn, programContext, functionGating, referencedBeforeDeclared.has(result));
56306
56382
}
56307
56383
else {
56308
56384
originalFn.replaceWith(transformedFn);
@@ -56338,8 +56414,10 @@ function getReactFunctionType(fn, pass) {
56338
56414
var _a, _b;
56339
56415
const hookPattern = pass.opts.environment.hookPattern;
56340
56416
if (fn.node.body.type === 'BlockStatement') {
56341
- if (findDirectiveEnablingMemoization(fn.node.body.directives) != null)
56417
+ const optInDirectives = tryFindDirectiveEnablingMemoization(fn.node.body.directives, pass.opts);
56418
+ if (optInDirectives.unwrapOr(null) != null) {
56342
56419
return (_a = getComponentOrHookLike(fn, hookPattern)) !== null && _a !== void 0 ? _a : 'Other';
56420
+ }
56343
56421
}
56344
56422
let componentSyntaxType = null;
56345
56423
if (fn.isFunctionDeclaration()) {
@@ -56854,6 +56932,9 @@ zod.z.enum([
56854
56932
'critical_errors',
56855
56933
'none',
56856
56934
]);
56935
+ const DynamicGatingOptionsSchema = zod.z.object({
56936
+ source: zod.z.string(),
56937
+ });
56857
56938
const CompilerReactTargetSchema = zod.z.union([
56858
56939
zod.z.literal('17'),
56859
56940
zod.z.literal('18'),
@@ -56876,6 +56957,7 @@ const defaultOptions = {
56876
56957
logger: null,
56877
56958
gating: null,
56878
56959
noEmit: false,
56960
+ dynamicGating: null,
56879
56961
eslintSuppressionRules: null,
56880
56962
flowSuppressions: true,
56881
56963
ignoreUseNoForget: false,
@@ -56922,6 +57004,26 @@ function parsePluginOptions(obj) {
56922
57004
}
56923
57005
break;
56924
57006
}
57007
+ case 'dynamicGating': {
57008
+ if (value == null) {
57009
+ parsedOptions[key] = null;
57010
+ }
57011
+ else {
57012
+ const result = DynamicGatingOptionsSchema.safeParse(value);
57013
+ if (result.success) {
57014
+ parsedOptions[key] = result.data;
57015
+ }
57016
+ else {
57017
+ CompilerError.throwInvalidConfig({
57018
+ reason: 'Could not parse dynamic gating. Update React Compiler config to fix the error',
57019
+ description: `${zodValidationError.fromZodError(result.error)}`,
57020
+ loc: null,
57021
+ suggestions: null,
57022
+ });
57023
+ }
57024
+ }
57025
+ break;
57026
+ }
56925
57027
default: {
56926
57028
parsedOptions[key] = value;
56927
57029
}
0 commit comments