@@ -29,6 +29,17 @@ var PluginProposalPrivateMethods = require('@babel/plugin-proposal-private-metho
2929var HermesParser = require('hermes-parser');
3030var util = require('util');
3131
32+ const SETTINGS_KEY = 'react-hooks';
33+ const SETTINGS_ADDITIONAL_EFFECT_HOOKS_KEY = 'additionalEffectHooks';
34+ function getAdditionalEffectHooksFromSettings(settings) {
35+ var _a;
36+ const additionalHooks = (_a = settings[SETTINGS_KEY]) === null || _a === void 0 ? void 0 : _a[SETTINGS_ADDITIONAL_EFFECT_HOOKS_KEY];
37+ if (additionalHooks != null && typeof additionalHooks === 'string') {
38+ return new RegExp(additionalHooks);
39+ }
40+ return undefined;
41+ }
42+
3243const rule$1 = {
3344 meta: {
3445 type: 'suggestion',
@@ -59,23 +70,24 @@ const rule$1 = {
5970 },
6071 requireExplicitEffectDeps: {
6172 type: 'boolean',
62- }
73+ },
6374 },
6475 },
6576 ],
6677 },
6778 create(context) {
6879 const rawOptions = context.options && context.options[0];
80+ const settings = context.settings || {};
6981 const additionalHooks = rawOptions && rawOptions.additionalHooks
7082 ? new RegExp(rawOptions.additionalHooks)
71- : undefined ;
83+ : getAdditionalEffectHooksFromSettings(settings) ;
7284 const enableDangerousAutofixThisMayCauseInfiniteLoops = (rawOptions &&
7385 rawOptions.enableDangerousAutofixThisMayCauseInfiniteLoops) ||
7486 false;
7587 const experimental_autoDependenciesHooks = rawOptions && Array.isArray(rawOptions.experimental_autoDependenciesHooks)
7688 ? rawOptions.experimental_autoDependenciesHooks
7789 : [];
78- const requireExplicitEffectDeps = rawOptions && rawOptions.requireExplicitEffectDeps || false;
90+ const requireExplicitEffectDeps = ( rawOptions && rawOptions.requireExplicitEffectDeps) || false;
7991 const options = {
8092 additionalHooks,
8193 experimental_autoDependenciesHooks,
@@ -944,7 +956,7 @@ const rule$1 = {
944956 reportProblem({
945957 node: reactiveHook,
946958 message: `React Hook ${reactiveHookName} always requires dependencies. ` +
947- `Please add a dependency array or an explicit \`undefined\``
959+ `Please add a dependency array or an explicit \`undefined\``,
948960 });
949961 }
950962 const isAutoDepsHook = options.experimental_autoDependenciesHooks.includes(reactiveHookName);
@@ -1446,9 +1458,7 @@ function isAncestorNodeOf(a, b) {
14461458 a.range[1] >= b.range[1]);
14471459}
14481460function isUseEffectEventIdentifier$1(node) {
1449- {
1450- return node.type === 'Identifier' && node.name === 'useEffectEvent';
1451- }
1461+ return node.type === 'Identifier' && node.name === 'useEffectEvent';
14521462}
14531463function getUnknownDependenciesMessage(reactiveHookName) {
14541464 return (`React Hook ${reactiveHookName} received a function whose dependencies ` +
@@ -32113,7 +32123,7 @@ const EnvironmentConfigSchema = zod.z.object({
3211332123 moduleTypeProvider: zod.z.nullable(zod.z.function().args(zod.z.string())).default(null),
3211432124 customMacros: zod.z.nullable(zod.z.array(MacroSchema)).default(null),
3211532125 enableResetCacheOnSourceFileChanges: zod.z.nullable(zod.z.boolean()).default(null),
32116- enablePreserveExistingMemoizationGuarantees: zod.z.boolean().default(false ),
32126+ enablePreserveExistingMemoizationGuarantees: zod.z.boolean().default(true ),
3211732127 validatePreserveExistingMemoizationGuarantees: zod.z.boolean().default(true),
3211832128 enablePreserveExistingManualUseMemo: zod.z.boolean().default(false),
3211932129 enableForest: zod.z.boolean().default(false),
@@ -45353,6 +45363,29 @@ function collectNonNullsInBlocks(fn, context) {
4535345363 }
4535445364 }
4535545365 }
45366+ else if (fn.env.config.enablePreserveExistingMemoizationGuarantees &&
45367+ instr.value.kind === 'StartMemoize' &&
45368+ instr.value.deps != null) {
45369+ for (const dep of instr.value.deps) {
45370+ if (dep.root.kind === 'NamedLocal') {
45371+ if (!isImmutableAtInstr(dep.root.value.identifier, instr.id, context)) {
45372+ continue;
45373+ }
45374+ for (let i = 0; i < dep.path.length; i++) {
45375+ const pathEntry = dep.path[i];
45376+ if (pathEntry.optional) {
45377+ break;
45378+ }
45379+ const depNode = context.registry.getOrCreateProperty({
45380+ identifier: dep.root.value.identifier,
45381+ path: dep.path.slice(0, i),
45382+ reactive: dep.root.value.reactive,
45383+ });
45384+ assumedNonNullObjects.add(depNode);
45385+ }
45386+ }
45387+ }
45388+ }
4535645389 }
4535745390 nodes.set(block.id, {
4535845391 block,
@@ -54464,7 +54497,7 @@ const allRules = LintRules.reduce((acc, rule) => {
5446454497 severity: ErrorSeverity.Error,
5446554498 },
5446654499});
54467- const recommendedRules = LintRules.filter(rule => rule.recommended).reduce((acc, rule) => {
54500+ LintRules.filter(rule => rule.recommended).reduce((acc, rule) => {
5446854501 acc[rule.name] = { rule: makeRule(rule), severity: rule.severity };
5446954502 return acc;
5447054503}, {
@@ -54473,23 +54506,6 @@ const recommendedRules = LintRules.filter(rule => rule.recommended).reduce((acc,
5447354506 severity: ErrorSeverity.Error,
5447454507 },
5447554508});
54476- function mapErrorSeverityToESlint(severity) {
54477- switch (severity) {
54478- case ErrorSeverity.Error: {
54479- return 'error';
54480- }
54481- case ErrorSeverity.Warning: {
54482- return 'warn';
54483- }
54484- case ErrorSeverity.Hint:
54485- case ErrorSeverity.Off: {
54486- return 'off';
54487- }
54488- default: {
54489- assertExhaustive(severity, `Unhandled severity: ${severity}`);
54490- }
54491- }
54492- }
5449354509
5449454510var assert_1;
5449554511var hasRequiredAssert;
@@ -57375,13 +57391,26 @@ function getNodeWithoutReactNamespace(node) {
5737557391 }
5737657392 return node;
5737757393}
57378- function isEffectIdentifier(node) {
57379- return node.type === 'Identifier' && (node.name === 'useEffect' || node.name === 'useLayoutEffect' || node.name === 'useInsertionEffect');
57394+ function isEffectIdentifier(node, additionalHooks) {
57395+ const isBuiltInEffect = node.type === 'Identifier' &&
57396+ (node.name === 'useEffect' ||
57397+ node.name === 'useLayoutEffect' ||
57398+ node.name === 'useInsertionEffect');
57399+ if (isBuiltInEffect) {
57400+ return true;
57401+ }
57402+ if (additionalHooks && node.type === 'Identifier') {
57403+ return additionalHooks.test(node.name);
57404+ }
57405+ return false;
5738057406}
5738157407function isUseEffectEventIdentifier(node) {
57382- {
57383- return node.type === 'Identifier' && node.name === 'useEffectEvent';
57384- }
57408+ return node.type === 'Identifier' && node.name === 'useEffectEvent';
57409+ }
57410+ function useEffectEventError(fn, called) {
57411+ return (`\`${fn}\` is a function created with React Hook "useEffectEvent", and can only be called from ` +
57412+ 'Effects and Effect Events in the same component.' +
57413+ (called ? '' : ' It cannot be assigned to a variable or passed down.'));
5738557414}
5738657415function isUseIdentifier(node) {
5738757416 return isReactFunction(node, 'use');
@@ -57394,8 +57423,21 @@ const rule = {
5739457423 recommended: true,
5739557424 url: 'https://react.dev/reference/rules/rules-of-hooks',
5739657425 },
57426+ schema: [
57427+ {
57428+ type: 'object',
57429+ additionalProperties: false,
57430+ properties: {
57431+ additionalHooks: {
57432+ type: 'string',
57433+ },
57434+ },
57435+ },
57436+ ],
5739757437 },
5739857438 create(context) {
57439+ const settings = context.settings || {};
57440+ const additionalEffectHooks = getAdditionalEffectHooksFromSettings(settings);
5739957441 let lastEffect = null;
5740057442 const codePathReactHooksMapStack = [];
5740157443 const codePathSegmentStack = [];
@@ -57676,19 +57718,15 @@ const rule = {
5767657718 reactHooks.push(node.callee);
5767757719 }
5767857720 const nodeWithoutNamespace = getNodeWithoutReactNamespace(node.callee);
57679- if ((isEffectIdentifier(nodeWithoutNamespace) ||
57721+ if ((isEffectIdentifier(nodeWithoutNamespace, additionalEffectHooks ) ||
5768057722 isUseEffectEventIdentifier(nodeWithoutNamespace)) &&
5768157723 node.arguments.length > 0) {
5768257724 lastEffect = node;
5768357725 }
5768457726 },
5768557727 Identifier(node) {
5768657728 if (lastEffect == null && useEffectEventFunctions.has(node)) {
57687- const message = `\`${getSourceCode().getText(node)}\` is a function created with React Hook "useEffectEvent", and can only be called from ` +
57688- 'the same component.' +
57689- (node.parent.type === 'CallExpression'
57690- ? ''
57691- : ' They cannot be assigned to variables or passed down.');
57729+ const message = useEffectEventError(getSourceCode().getText(node), node.parent.type === 'CallExpression');
5769257730 context.report({
5769357731 node,
5769457732 message,
@@ -57755,12 +57793,10 @@ function last(array) {
5775557793}
5775657794
5775757795const rules = Object.assign({ 'exhaustive-deps': rule$1, 'rules-of-hooks': rule }, Object.fromEntries(Object.entries(allRules).map(([name, config]) => [name, config.rule])));
57758- const ruleConfigs = Object.assign({ 'react-hooks/rules-of-hooks': 'error', 'react-hooks/exhaustive-deps': 'warn' }, Object.fromEntries(Object.entries(recommendedRules).map(([name, ruleConfig]) => {
57759- return [
57760- 'react-hooks/' + name,
57761- mapErrorSeverityToESlint(ruleConfig.severity),
57762- ];
57763- })));
57796+ const ruleConfigs = {
57797+ 'react-hooks/rules-of-hooks': 'error',
57798+ 'react-hooks/exhaustive-deps': 'warn',
57799+ };
5776457800const plugin = {
5776557801 meta: {
5776657802 name: 'eslint-plugin-react-hooks',
0 commit comments