@@ -55944,14 +55944,78 @@ function suppressionsToCompilerError(suppressionRanges) {
5594455944
5594555945const OPT_IN_DIRECTIVES = new Set(['use forget', 'use memo']);
5594655946const 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+ }
5595055961}
5595155962function findDirectiveDisablingMemoization(directives) {
5595255963 var _a;
5595355964 return ((_a = directives.find(directive => OPT_OUT_DIRECTIVES.has(directive.value.value))) !== null && _a !== void 0 ? _a : null);
5595455965}
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+ }
5595556019function isCriticalError(err) {
5595656020 return !(err instanceof CompilerError) || err.isCritical();
5595756021}
@@ -56184,25 +56248,33 @@ function findFunctionsToCompile(program, pass, programContext) {
5618456248 return queue;
5618556249}
5618656250function processFn(fn, fnType, programContext) {
56187- var _a, _b, _c, _d, _e, _f, _g;
56251+ var _a, _b, _c, _d, _e, _f, _g, _h ;
5618856252 let directives;
5618956253 if (fn.node.body.type !== 'BlockStatement') {
56190- directives = { optIn: null, optOut: null };
56254+ directives = {
56255+ optIn: null,
56256+ optOut: null,
56257+ };
5619156258 }
5619256259 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+ }
5619356265 directives = {
56194- optIn: findDirectiveEnablingMemoization(fn.node.body.directives ),
56266+ optIn: optIn.unwrapOr(null ),
5619556267 optOut: findDirectiveDisablingMemoization(fn.node.body.directives),
5619656268 };
5619756269 }
5619856270 let compiledFn;
5619956271 const compileResult = tryCompileFunction(fn, fnType, programContext);
5620056272 if (compileResult.kind === 'error') {
5620156273 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);
5620356275 }
5620456276 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);
5620656278 }
5620756279 const retryResult = retryCompileFunction(fn, fnType, programContext);
5620856280 if (retryResult == null) {
@@ -56217,16 +56289,16 @@ function processFn(fn, fnType, programContext) {
5621756289 directives.optOut != null) {
5621856290 programContext.logEvent({
5621956291 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,
5622156293 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,
5622356295 });
5622456296 return null;
5622556297 }
5622656298 programContext.logEvent({
5622756299 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,
5623056302 memoSlots: compiledFn.memoSlotsUsed,
5623156303 memoBlocks: compiledFn.memoBlocks,
5623256304 memoValues: compiledFn.memoValues,
@@ -56290,19 +56362,23 @@ function retryCompileFunction(fn, fnType, programContext) {
5629056362 }
5629156363}
5629256364function 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;
5629656367 for (const result of compiledFns) {
5629756368 const { kind, originalFn, compiledFn } = result;
5629856369 const transformedFn = createNewFunctionNode(originalFn, compiledFn);
5629956370 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));
5630656382 }
5630756383 else {
5630856384 originalFn.replaceWith(transformedFn);
@@ -56338,8 +56414,10 @@ function getReactFunctionType(fn, pass) {
5633856414 var _a, _b;
5633956415 const hookPattern = pass.opts.environment.hookPattern;
5634056416 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) {
5634256419 return (_a = getComponentOrHookLike(fn, hookPattern)) !== null && _a !== void 0 ? _a : 'Other';
56420+ }
5634356421 }
5634456422 let componentSyntaxType = null;
5634556423 if (fn.isFunctionDeclaration()) {
@@ -56854,6 +56932,9 @@ zod.z.enum([
5685456932 'critical_errors',
5685556933 'none',
5685656934]);
56935+ const DynamicGatingOptionsSchema = zod.z.object({
56936+ source: zod.z.string(),
56937+ });
5685756938const CompilerReactTargetSchema = zod.z.union([
5685856939 zod.z.literal('17'),
5685956940 zod.z.literal('18'),
@@ -56876,6 +56957,7 @@ const defaultOptions = {
5687656957 logger: null,
5687756958 gating: null,
5687856959 noEmit: false,
56960+ dynamicGating: null,
5687956961 eslintSuppressionRules: null,
5688056962 flowSuppressions: true,
5688156963 ignoreUseNoForget: false,
@@ -56922,6 +57004,26 @@ function parsePluginOptions(obj) {
5692257004 }
5692357005 break;
5692457006 }
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+ }
5692557027 default: {
5692657028 parsedOptions[key] = value;
5692757029 }
0 commit comments