eslint-plugin-react-hooks 6.1.0-canary-4123f6b7-20250826 → 6.1.0-canary-8d7b5e49-20250827

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -17972,6 +17972,7 @@ var ErrorCategory;
17972
17972
  ErrorCategory["CapitalizedCalls"] = "CapitalizedCalls";
17973
17973
  ErrorCategory["StaticComponents"] = "StaticComponents";
17974
17974
  ErrorCategory["UseMemo"] = "UseMemo";
17975
+ ErrorCategory["Factories"] = "Factories";
17975
17976
  ErrorCategory["PreserveManualMemo"] = "PreserveManualMemo";
17976
17977
  ErrorCategory["Immutability"] = "Immutability";
17977
17978
  ErrorCategory["Globals"] = "Globals";
@@ -18057,6 +18058,15 @@ function getRuleForCategoryImpl(category) {
18057
18058
  recommended: true,
18058
18059
  };
18059
18060
  }
18061
+ case ErrorCategory.Factories: {
18062
+ return {
18063
+ category,
18064
+ name: 'component-hook-factories',
18065
+ description: 'Validates against higher order functions defining nested components or hooks. ' +
18066
+ 'Components and hooks should be defined at the module level',
18067
+ recommended: true,
18068
+ };
18069
+ }
18060
18070
  case ErrorCategory.FBT: {
18061
18071
  return {
18062
18072
  category,
@@ -19467,7 +19477,8 @@ function printType(type) {
19467
19477
  return `:T${type.kind}<${type.shapeId}>`;
19468
19478
  }
19469
19479
  else if (type.kind === 'Function' && type.shapeId != null) {
19470
- return `:T${type.kind}<${type.shapeId}>`;
19480
+ const returnType = printType(type.return);
19481
+ return `:T${type.kind}<${type.shapeId}>()${returnType !== '' ? `: ${returnType}` : ''}`;
19471
19482
  }
19472
19483
  else {
19473
19484
  return `:T${type.kind}`;
@@ -19499,6 +19510,7 @@ function getFunctionName$2(instrValue, defaultValue) {
19499
19510
  }
19500
19511
  }
19501
19512
  function printAliasingEffect(effect) {
19513
+ var _a;
19502
19514
  switch (effect.kind) {
19503
19515
  case 'Assign': {
19504
19516
  return `Assign ${printPlaceForAliasEffect(effect.into)} = ${printPlaceForAliasEffect(effect.from)}`;
@@ -19557,7 +19569,7 @@ function printAliasingEffect(effect) {
19557
19569
  case 'MutateConditionally':
19558
19570
  case 'MutateTransitive':
19559
19571
  case 'MutateTransitiveConditionally': {
19560
- return `${effect.kind} ${printPlaceForAliasEffect(effect.value)}`;
19572
+ return `${effect.kind} ${printPlaceForAliasEffect(effect.value)}${effect.kind === 'Mutate' && ((_a = effect.reason) === null || _a === void 0 ? void 0 : _a.kind) === 'AssignCurrentProperty' ? ' (assign `.current`)' : ''}`;
19561
19573
  }
19562
19574
  case 'MutateFrozen': {
19563
19575
  return `MutateFrozen ${printPlaceForAliasEffect(effect.place)} reason=${JSON.stringify(effect.error.reason)}`;
@@ -21069,7 +21081,7 @@ class HIRBuilder {
21069
21081
  }
21070
21082
  }
21071
21083
  resolveBinding(node) {
21072
- var _a, _b;
21084
+ var _a, _b, _c;
21073
21085
  if (node.name === 'fbt') {
21074
21086
  CompilerError.throwDiagnostic({
21075
21087
  severity: ErrorSeverity.Todo,
@@ -21085,6 +21097,21 @@ class HIRBuilder {
21085
21097
  ],
21086
21098
  });
21087
21099
  }
21100
+ if (node.name === 'this') {
21101
+ CompilerError.throwDiagnostic({
21102
+ severity: ErrorSeverity.UnsupportedJS,
21103
+ category: ErrorCategory.UnsupportedSyntax,
21104
+ reason: '`this` is not supported syntax',
21105
+ description: 'React Compiler does not support compiling functions that use `this`',
21106
+ details: [
21107
+ {
21108
+ kind: 'error',
21109
+ message: '`this` was used here',
21110
+ loc: (_b = node.loc) !== null && _b !== void 0 ? _b : GeneratedSource,
21111
+ },
21112
+ ],
21113
+ });
21114
+ }
21088
21115
  const originalName = node.name;
21089
21116
  let name = originalName;
21090
21117
  let index = 0;
@@ -21102,7 +21129,7 @@ class HIRBuilder {
21102
21129
  },
21103
21130
  scope: null,
21104
21131
  type: makeType(),
21105
- loc: (_b = node.loc) !== null && _b !== void 0 ? _b : GeneratedSource,
21132
+ loc: (_c = node.loc) !== null && _c !== void 0 ? _c : GeneratedSource,
21106
21133
  };
21107
21134
  __classPrivateFieldGet(this, _HIRBuilder_env, "f").programContext.addNewReference(name);
21108
21135
  __classPrivateFieldGet(this, _HIRBuilder_bindings, "f").set(name, { node, identifier });
@@ -31611,6 +31638,7 @@ const EnvironmentConfigSchema = zod.z.object({
31611
31638
  enableTreatRefLikeIdentifiersAsRefs: zod.z.boolean().default(true),
31612
31639
  lowerContextAccess: ExternalFunctionSchema.nullable().default(null),
31613
31640
  validateNoVoidUseMemo: zod.z.boolean().default(false),
31641
+ validateNoDynamicallyCreatedComponentsOrHooks: zod.z.boolean().default(false),
31614
31642
  });
31615
31643
  class Environment {
31616
31644
  constructor(scope, fnType, compilerMode, config, contextIdentifiers, parentFunction, logger, filename, code, programContext) {
@@ -40452,6 +40480,18 @@ function computeSignatureForInstruction(context, env, instr) {
40452
40480
  });
40453
40481
  }
40454
40482
  }
40483
+ for (const prop of value.props) {
40484
+ if (prop.kind === 'JsxAttribute' &&
40485
+ prop.place.identifier.type.kind === 'Function' &&
40486
+ (isJsxType(prop.place.identifier.type.return) ||
40487
+ (prop.place.identifier.type.return.kind === 'Phi' &&
40488
+ prop.place.identifier.type.return.operands.some(operand => isJsxType(operand))))) {
40489
+ effects.push({
40490
+ kind: 'Render',
40491
+ place: prop.place,
40492
+ });
40493
+ }
40494
+ }
40455
40495
  }
40456
40496
  break;
40457
40497
  }
@@ -42224,7 +42264,7 @@ class RewriteBlockIds extends ReactiveFunctionVisitor {
42224
42264
  }
42225
42265
 
42226
42266
  function inferMutationAliasingRanges(fn, { isFunctionExpression }) {
42227
- var _a, _b, _c, _d, _e, _f;
42267
+ var _a, _b, _c, _d, _e, _f, _g;
42228
42268
  const functionEffects = [];
42229
42269
  const state = new AliasingState();
42230
42270
  const pendingPhis = new Map();
@@ -42291,6 +42331,7 @@ function inferMutationAliasingRanges(fn, { isFunctionExpression }) {
42291
42331
  kind: effect.kind === 'MutateTransitive'
42292
42332
  ? MutationKind.Definite
42293
42333
  : MutationKind.Conditional,
42334
+ reason: null,
42294
42335
  place: effect.value,
42295
42336
  });
42296
42337
  }
@@ -42303,6 +42344,7 @@ function inferMutationAliasingRanges(fn, { isFunctionExpression }) {
42303
42344
  kind: effect.kind === 'Mutate'
42304
42345
  ? MutationKind.Definite
42305
42346
  : MutationKind.Conditional,
42347
+ reason: effect.kind === 'Mutate' ? ((_a = effect.reason) !== null && _a !== void 0 ? _a : null) : null,
42306
42348
  place: effect.value,
42307
42349
  });
42308
42350
  }
@@ -42344,7 +42386,7 @@ function inferMutationAliasingRanges(fn, { isFunctionExpression }) {
42344
42386
  }
42345
42387
  }
42346
42388
  for (const mutation of mutations) {
42347
- state.mutate(mutation.index, mutation.place.identifier, makeInstructionId(mutation.id + 1), mutation.transitive, mutation.kind, mutation.place.loc, errors);
42389
+ state.mutate(mutation.index, mutation.place.identifier, makeInstructionId(mutation.id + 1), mutation.transitive, mutation.kind, mutation.place.loc, mutation.reason, errors);
42348
42390
  }
42349
42391
  for (const render of renders) {
42350
42392
  state.render(render.index, render.place.identifier, errors);
@@ -42369,6 +42411,7 @@ function inferMutationAliasingRanges(fn, { isFunctionExpression }) {
42369
42411
  functionEffects.push({
42370
42412
  kind: 'Mutate',
42371
42413
  value: Object.assign(Object.assign({}, place), { loc: node.local.loc }),
42414
+ reason: node.mutationReason,
42372
42415
  });
42373
42416
  }
42374
42417
  }
@@ -42396,7 +42439,7 @@ function inferMutationAliasingRanges(fn, { isFunctionExpression }) {
42396
42439
  for (const phi of block.phis) {
42397
42440
  phi.place.effect = Effect.Store;
42398
42441
  const isPhiMutatedAfterCreation = phi.place.identifier.mutableRange.end >
42399
- ((_b = (_a = block.instructions.at(0)) === null || _a === void 0 ? void 0 : _a.id) !== null && _b !== void 0 ? _b : block.terminal.id);
42442
+ ((_c = (_b = block.instructions.at(0)) === null || _b === void 0 ? void 0 : _b.id) !== null && _c !== void 0 ? _c : block.terminal.id);
42400
42443
  for (const operand of phi.operands.values()) {
42401
42444
  operand.effect = isPhiMutatedAfterCreation
42402
42445
  ? Effect.Capture
@@ -42404,7 +42447,7 @@ function inferMutationAliasingRanges(fn, { isFunctionExpression }) {
42404
42447
  }
42405
42448
  if (isPhiMutatedAfterCreation &&
42406
42449
  phi.place.identifier.mutableRange.start === 0) {
42407
- const firstInstructionIdOfBlock = (_d = (_c = block.instructions.at(0)) === null || _c === void 0 ? void 0 : _c.id) !== null && _d !== void 0 ? _d : block.terminal.id;
42450
+ const firstInstructionIdOfBlock = (_e = (_d = block.instructions.at(0)) === null || _d === void 0 ? void 0 : _d.id) !== null && _e !== void 0 ? _e : block.terminal.id;
42408
42451
  phi.place.identifier.mutableRange.start = makeInstructionId(firstInstructionIdOfBlock - 1);
42409
42452
  }
42410
42453
  }
@@ -42482,7 +42525,7 @@ function inferMutationAliasingRanges(fn, { isFunctionExpression }) {
42482
42525
  }
42483
42526
  }
42484
42527
  for (const lvalue of eachInstructionLValue(instr)) {
42485
- const effect = (_e = operandEffects.get(lvalue.identifier.id)) !== null && _e !== void 0 ? _e : Effect.ConditionallyMutate;
42528
+ const effect = (_f = operandEffects.get(lvalue.identifier.id)) !== null && _f !== void 0 ? _f : Effect.ConditionallyMutate;
42486
42529
  lvalue.effect = effect;
42487
42530
  }
42488
42531
  for (const operand of eachInstructionValueOperand(instr.value)) {
@@ -42490,7 +42533,7 @@ function inferMutationAliasingRanges(fn, { isFunctionExpression }) {
42490
42533
  operand.identifier.mutableRange.start === 0) {
42491
42534
  operand.identifier.mutableRange.start = instr.id;
42492
42535
  }
42493
- const effect = (_f = operandEffects.get(operand.identifier.id)) !== null && _f !== void 0 ? _f : Effect.Read;
42536
+ const effect = (_g = operandEffects.get(operand.identifier.id)) !== null && _g !== void 0 ? _g : Effect.Read;
42494
42537
  operand.effect = effect;
42495
42538
  }
42496
42539
  if (instr.value.kind === 'StoreContext' &&
@@ -42528,7 +42571,7 @@ function inferMutationAliasingRanges(fn, { isFunctionExpression }) {
42528
42571
  }
42529
42572
  for (const into of tracked) {
42530
42573
  const mutationIndex = index++;
42531
- state.mutate(mutationIndex, into.identifier, null, true, MutationKind.Conditional, into.loc, ignoredErrors);
42574
+ state.mutate(mutationIndex, into.identifier, null, true, MutationKind.Conditional, into.loc, null, ignoredErrors);
42532
42575
  for (const from of tracked) {
42533
42576
  if (from.identifier.id === into.identifier.id ||
42534
42577
  from.identifier.id === fn.returns.identifier.id) {
@@ -42596,6 +42639,7 @@ class AliasingState {
42596
42639
  transitive: null,
42597
42640
  local: null,
42598
42641
  lastMutated: 0,
42642
+ mutationReason: null,
42599
42643
  value,
42600
42644
  });
42601
42645
  }
@@ -42680,7 +42724,8 @@ class AliasingState {
42680
42724
  }
42681
42725
  }
42682
42726
  }
42683
- mutate(index, start, end, transitive, startKind, loc, errors) {
42727
+ mutate(index, start, end, transitive, startKind, loc, reason, errors) {
42728
+ var _a;
42684
42729
  const seen = new Map();
42685
42730
  const queue = [{ place: start, transitive, direction: 'backwards', kind: startKind }];
42686
42731
  while (queue.length !== 0) {
@@ -42694,6 +42739,7 @@ class AliasingState {
42694
42739
  if (node == null) {
42695
42740
  continue;
42696
42741
  }
42742
+ (_a = node.mutationReason) !== null && _a !== void 0 ? _a : (node.mutationReason = reason);
42697
42743
  node.lastMutated = Math.max(node.lastMutated, index);
42698
42744
  if (end != null) {
42699
42745
  node.id.mutableRange.end = makeInstructionId(Math.max(node.id.mutableRange.end, end));
@@ -46745,6 +46791,14 @@ class Unifier {
46745
46791
  if (type.kind === 'Phi') {
46746
46792
  return { kind: 'Phi', operands: type.operands.map(o => this.get(o)) };
46747
46793
  }
46794
+ if (type.kind === 'Function') {
46795
+ return {
46796
+ kind: 'Function',
46797
+ isConstructor: type.isConstructor,
46798
+ shapeId: type.shapeId,
46799
+ return: this.get(type.return),
46800
+ };
46801
+ }
46748
46802
  return type;
46749
46803
  }
46750
46804
  }
@@ -50921,7 +50975,14 @@ function findFunctionsToCompile(program, pass, programContext) {
50921
50975
  var _a;
50922
50976
  const queue = [];
50923
50977
  const traverseFunction = (fn, pass) => {
50978
+ if (pass.opts.compilationMode === 'all' &&
50979
+ fn.scope.getProgramParent() !== fn.scope.parent) {
50980
+ return;
50981
+ }
50924
50982
  const fnType = getReactFunctionType(fn, pass);
50983
+ if (pass.opts.environment.validateNoDynamicallyCreatedComponentsOrHooks) {
50984
+ validateNoDynamicallyCreatedComponentsOrHooks(fn, pass, programContext);
50985
+ }
50925
50986
  if (fnType === null || programContext.alreadyCompiled.has(fn.node)) {
50926
50987
  return;
50927
50988
  }
@@ -51106,6 +51167,52 @@ function shouldSkipCompilation(program, pass) {
51106
51167
  }
51107
51168
  return false;
51108
51169
  }
51170
+ function validateNoDynamicallyCreatedComponentsOrHooks(fn, pass, programContext) {
51171
+ const parentNameExpr = getFunctionName$1(fn);
51172
+ const parentName = parentNameExpr !== null && parentNameExpr.isIdentifier()
51173
+ ? parentNameExpr.node.name
51174
+ : '<anonymous>';
51175
+ const validateNestedFunction = (nestedFn) => {
51176
+ var _a, _b, _c, _d;
51177
+ if (nestedFn.node === fn.node ||
51178
+ programContext.alreadyCompiled.has(nestedFn.node)) {
51179
+ return;
51180
+ }
51181
+ if (nestedFn.scope.getProgramParent() !== nestedFn.scope.parent) {
51182
+ const nestedFnType = getReactFunctionType(nestedFn, pass);
51183
+ const nestedFnNameExpr = getFunctionName$1(nestedFn);
51184
+ const nestedName = nestedFnNameExpr !== null && nestedFnNameExpr.isIdentifier()
51185
+ ? nestedFnNameExpr.node.name
51186
+ : '<anonymous>';
51187
+ if (nestedFnType === 'Component' || nestedFnType === 'Hook') {
51188
+ CompilerError.throwDiagnostic({
51189
+ category: ErrorCategory.Factories,
51190
+ severity: ErrorSeverity.InvalidReact,
51191
+ reason: `Components and hooks cannot be created dynamically`,
51192
+ description: `The function \`${nestedName}\` appears to be a React ${nestedFnType.toLowerCase()}, but it's defined inside \`${parentName}\`. Components and Hooks should always be declared at module scope`,
51193
+ details: [
51194
+ {
51195
+ kind: 'error',
51196
+ message: 'this function dynamically created a component/hook',
51197
+ loc: (_b = (_a = parentNameExpr === null || parentNameExpr === void 0 ? void 0 : parentNameExpr.node.loc) !== null && _a !== void 0 ? _a : fn.node.loc) !== null && _b !== void 0 ? _b : null,
51198
+ },
51199
+ {
51200
+ kind: 'error',
51201
+ message: 'the component is created here',
51202
+ loc: (_d = (_c = nestedFnNameExpr === null || nestedFnNameExpr === void 0 ? void 0 : nestedFnNameExpr.node.loc) !== null && _c !== void 0 ? _c : nestedFn.node.loc) !== null && _d !== void 0 ? _d : null,
51203
+ },
51204
+ ],
51205
+ });
51206
+ }
51207
+ }
51208
+ nestedFn.skip();
51209
+ };
51210
+ fn.traverse({
51211
+ FunctionDeclaration: validateNestedFunction,
51212
+ FunctionExpression: validateNestedFunction,
51213
+ ArrowFunctionExpression: validateNestedFunction,
51214
+ });
51215
+ }
51109
51216
  function getReactFunctionType(fn, pass) {
51110
51217
  var _a, _b;
51111
51218
  const hookPattern = pass.opts.environment.hookPattern;
@@ -51135,9 +51242,6 @@ function getReactFunctionType(fn, pass) {
51135
51242
  return componentSyntaxType;
51136
51243
  }
51137
51244
  case 'all': {
51138
- if (fn.scope.getProgramParent() !== fn.scope.parent) {
51139
- return null;
51140
- }
51141
51245
  return (_b = getComponentOrHookLike(fn, hookPattern)) !== null && _b !== void 0 ? _b : 'Other';
51142
51246
  }
51143
51247
  default: {
@@ -17963,6 +17963,7 @@ var ErrorCategory;
17963
17963
  ErrorCategory["CapitalizedCalls"] = "CapitalizedCalls";
17964
17964
  ErrorCategory["StaticComponents"] = "StaticComponents";
17965
17965
  ErrorCategory["UseMemo"] = "UseMemo";
17966
+ ErrorCategory["Factories"] = "Factories";
17966
17967
  ErrorCategory["PreserveManualMemo"] = "PreserveManualMemo";
17967
17968
  ErrorCategory["Immutability"] = "Immutability";
17968
17969
  ErrorCategory["Globals"] = "Globals";
@@ -18048,6 +18049,15 @@ function getRuleForCategoryImpl(category) {
18048
18049
  recommended: true,
18049
18050
  };
18050
18051
  }
18052
+ case ErrorCategory.Factories: {
18053
+ return {
18054
+ category,
18055
+ name: 'component-hook-factories',
18056
+ description: 'Validates against higher order functions defining nested components or hooks. ' +
18057
+ 'Components and hooks should be defined at the module level',
18058
+ recommended: true,
18059
+ };
18060
+ }
18051
18061
  case ErrorCategory.FBT: {
18052
18062
  return {
18053
18063
  category,
@@ -19458,7 +19468,8 @@ function printType(type) {
19458
19468
  return `:T${type.kind}<${type.shapeId}>`;
19459
19469
  }
19460
19470
  else if (type.kind === 'Function' && type.shapeId != null) {
19461
- return `:T${type.kind}<${type.shapeId}>`;
19471
+ const returnType = printType(type.return);
19472
+ return `:T${type.kind}<${type.shapeId}>()${returnType !== '' ? `: ${returnType}` : ''}`;
19462
19473
  }
19463
19474
  else {
19464
19475
  return `:T${type.kind}`;
@@ -19490,6 +19501,7 @@ function getFunctionName$2(instrValue, defaultValue) {
19490
19501
  }
19491
19502
  }
19492
19503
  function printAliasingEffect(effect) {
19504
+ var _a;
19493
19505
  switch (effect.kind) {
19494
19506
  case 'Assign': {
19495
19507
  return `Assign ${printPlaceForAliasEffect(effect.into)} = ${printPlaceForAliasEffect(effect.from)}`;
@@ -19548,7 +19560,7 @@ function printAliasingEffect(effect) {
19548
19560
  case 'MutateConditionally':
19549
19561
  case 'MutateTransitive':
19550
19562
  case 'MutateTransitiveConditionally': {
19551
- return `${effect.kind} ${printPlaceForAliasEffect(effect.value)}`;
19563
+ return `${effect.kind} ${printPlaceForAliasEffect(effect.value)}${effect.kind === 'Mutate' && ((_a = effect.reason) === null || _a === void 0 ? void 0 : _a.kind) === 'AssignCurrentProperty' ? ' (assign `.current`)' : ''}`;
19552
19564
  }
19553
19565
  case 'MutateFrozen': {
19554
19566
  return `MutateFrozen ${printPlaceForAliasEffect(effect.place)} reason=${JSON.stringify(effect.error.reason)}`;
@@ -21060,7 +21072,7 @@ class HIRBuilder {
21060
21072
  }
21061
21073
  }
21062
21074
  resolveBinding(node) {
21063
- var _a, _b;
21075
+ var _a, _b, _c;
21064
21076
  if (node.name === 'fbt') {
21065
21077
  CompilerError.throwDiagnostic({
21066
21078
  severity: ErrorSeverity.Todo,
@@ -21076,6 +21088,21 @@ class HIRBuilder {
21076
21088
  ],
21077
21089
  });
21078
21090
  }
21091
+ if (node.name === 'this') {
21092
+ CompilerError.throwDiagnostic({
21093
+ severity: ErrorSeverity.UnsupportedJS,
21094
+ category: ErrorCategory.UnsupportedSyntax,
21095
+ reason: '`this` is not supported syntax',
21096
+ description: 'React Compiler does not support compiling functions that use `this`',
21097
+ details: [
21098
+ {
21099
+ kind: 'error',
21100
+ message: '`this` was used here',
21101
+ loc: (_b = node.loc) !== null && _b !== void 0 ? _b : GeneratedSource,
21102
+ },
21103
+ ],
21104
+ });
21105
+ }
21079
21106
  const originalName = node.name;
21080
21107
  let name = originalName;
21081
21108
  let index = 0;
@@ -21093,7 +21120,7 @@ class HIRBuilder {
21093
21120
  },
21094
21121
  scope: null,
21095
21122
  type: makeType(),
21096
- loc: (_b = node.loc) !== null && _b !== void 0 ? _b : GeneratedSource,
21123
+ loc: (_c = node.loc) !== null && _c !== void 0 ? _c : GeneratedSource,
21097
21124
  };
21098
21125
  __classPrivateFieldGet(this, _HIRBuilder_env, "f").programContext.addNewReference(name);
21099
21126
  __classPrivateFieldGet(this, _HIRBuilder_bindings, "f").set(name, { node, identifier });
@@ -31438,6 +31465,7 @@ const EnvironmentConfigSchema = zod.z.object({
31438
31465
  enableTreatRefLikeIdentifiersAsRefs: zod.z.boolean().default(true),
31439
31466
  lowerContextAccess: ExternalFunctionSchema.nullable().default(null),
31440
31467
  validateNoVoidUseMemo: zod.z.boolean().default(false),
31468
+ validateNoDynamicallyCreatedComponentsOrHooks: zod.z.boolean().default(false),
31441
31469
  });
31442
31470
  class Environment {
31443
31471
  constructor(scope, fnType, compilerMode, config, contextIdentifiers, parentFunction, logger, filename, code, programContext) {
@@ -40279,6 +40307,18 @@ function computeSignatureForInstruction(context, env, instr) {
40279
40307
  });
40280
40308
  }
40281
40309
  }
40310
+ for (const prop of value.props) {
40311
+ if (prop.kind === 'JsxAttribute' &&
40312
+ prop.place.identifier.type.kind === 'Function' &&
40313
+ (isJsxType(prop.place.identifier.type.return) ||
40314
+ (prop.place.identifier.type.return.kind === 'Phi' &&
40315
+ prop.place.identifier.type.return.operands.some(operand => isJsxType(operand))))) {
40316
+ effects.push({
40317
+ kind: 'Render',
40318
+ place: prop.place,
40319
+ });
40320
+ }
40321
+ }
40282
40322
  }
40283
40323
  break;
40284
40324
  }
@@ -42051,7 +42091,7 @@ class RewriteBlockIds extends ReactiveFunctionVisitor {
42051
42091
  }
42052
42092
 
42053
42093
  function inferMutationAliasingRanges(fn, { isFunctionExpression }) {
42054
- var _a, _b, _c, _d, _e, _f;
42094
+ var _a, _b, _c, _d, _e, _f, _g;
42055
42095
  const functionEffects = [];
42056
42096
  const state = new AliasingState();
42057
42097
  const pendingPhis = new Map();
@@ -42118,6 +42158,7 @@ function inferMutationAliasingRanges(fn, { isFunctionExpression }) {
42118
42158
  kind: effect.kind === 'MutateTransitive'
42119
42159
  ? MutationKind.Definite
42120
42160
  : MutationKind.Conditional,
42161
+ reason: null,
42121
42162
  place: effect.value,
42122
42163
  });
42123
42164
  }
@@ -42130,6 +42171,7 @@ function inferMutationAliasingRanges(fn, { isFunctionExpression }) {
42130
42171
  kind: effect.kind === 'Mutate'
42131
42172
  ? MutationKind.Definite
42132
42173
  : MutationKind.Conditional,
42174
+ reason: effect.kind === 'Mutate' ? ((_a = effect.reason) !== null && _a !== void 0 ? _a : null) : null,
42133
42175
  place: effect.value,
42134
42176
  });
42135
42177
  }
@@ -42171,7 +42213,7 @@ function inferMutationAliasingRanges(fn, { isFunctionExpression }) {
42171
42213
  }
42172
42214
  }
42173
42215
  for (const mutation of mutations) {
42174
- state.mutate(mutation.index, mutation.place.identifier, makeInstructionId(mutation.id + 1), mutation.transitive, mutation.kind, mutation.place.loc, errors);
42216
+ state.mutate(mutation.index, mutation.place.identifier, makeInstructionId(mutation.id + 1), mutation.transitive, mutation.kind, mutation.place.loc, mutation.reason, errors);
42175
42217
  }
42176
42218
  for (const render of renders) {
42177
42219
  state.render(render.index, render.place.identifier, errors);
@@ -42196,6 +42238,7 @@ function inferMutationAliasingRanges(fn, { isFunctionExpression }) {
42196
42238
  functionEffects.push({
42197
42239
  kind: 'Mutate',
42198
42240
  value: Object.assign(Object.assign({}, place), { loc: node.local.loc }),
42241
+ reason: node.mutationReason,
42199
42242
  });
42200
42243
  }
42201
42244
  }
@@ -42223,7 +42266,7 @@ function inferMutationAliasingRanges(fn, { isFunctionExpression }) {
42223
42266
  for (const phi of block.phis) {
42224
42267
  phi.place.effect = Effect.Store;
42225
42268
  const isPhiMutatedAfterCreation = phi.place.identifier.mutableRange.end >
42226
- ((_b = (_a = block.instructions.at(0)) === null || _a === void 0 ? void 0 : _a.id) !== null && _b !== void 0 ? _b : block.terminal.id);
42269
+ ((_c = (_b = block.instructions.at(0)) === null || _b === void 0 ? void 0 : _b.id) !== null && _c !== void 0 ? _c : block.terminal.id);
42227
42270
  for (const operand of phi.operands.values()) {
42228
42271
  operand.effect = isPhiMutatedAfterCreation
42229
42272
  ? Effect.Capture
@@ -42231,7 +42274,7 @@ function inferMutationAliasingRanges(fn, { isFunctionExpression }) {
42231
42274
  }
42232
42275
  if (isPhiMutatedAfterCreation &&
42233
42276
  phi.place.identifier.mutableRange.start === 0) {
42234
- const firstInstructionIdOfBlock = (_d = (_c = block.instructions.at(0)) === null || _c === void 0 ? void 0 : _c.id) !== null && _d !== void 0 ? _d : block.terminal.id;
42277
+ const firstInstructionIdOfBlock = (_e = (_d = block.instructions.at(0)) === null || _d === void 0 ? void 0 : _d.id) !== null && _e !== void 0 ? _e : block.terminal.id;
42235
42278
  phi.place.identifier.mutableRange.start = makeInstructionId(firstInstructionIdOfBlock - 1);
42236
42279
  }
42237
42280
  }
@@ -42309,7 +42352,7 @@ function inferMutationAliasingRanges(fn, { isFunctionExpression }) {
42309
42352
  }
42310
42353
  }
42311
42354
  for (const lvalue of eachInstructionLValue(instr)) {
42312
- const effect = (_e = operandEffects.get(lvalue.identifier.id)) !== null && _e !== void 0 ? _e : Effect.ConditionallyMutate;
42355
+ const effect = (_f = operandEffects.get(lvalue.identifier.id)) !== null && _f !== void 0 ? _f : Effect.ConditionallyMutate;
42313
42356
  lvalue.effect = effect;
42314
42357
  }
42315
42358
  for (const operand of eachInstructionValueOperand(instr.value)) {
@@ -42317,7 +42360,7 @@ function inferMutationAliasingRanges(fn, { isFunctionExpression }) {
42317
42360
  operand.identifier.mutableRange.start === 0) {
42318
42361
  operand.identifier.mutableRange.start = instr.id;
42319
42362
  }
42320
- const effect = (_f = operandEffects.get(operand.identifier.id)) !== null && _f !== void 0 ? _f : Effect.Read;
42363
+ const effect = (_g = operandEffects.get(operand.identifier.id)) !== null && _g !== void 0 ? _g : Effect.Read;
42321
42364
  operand.effect = effect;
42322
42365
  }
42323
42366
  if (instr.value.kind === 'StoreContext' &&
@@ -42355,7 +42398,7 @@ function inferMutationAliasingRanges(fn, { isFunctionExpression }) {
42355
42398
  }
42356
42399
  for (const into of tracked) {
42357
42400
  const mutationIndex = index++;
42358
- state.mutate(mutationIndex, into.identifier, null, true, MutationKind.Conditional, into.loc, ignoredErrors);
42401
+ state.mutate(mutationIndex, into.identifier, null, true, MutationKind.Conditional, into.loc, null, ignoredErrors);
42359
42402
  for (const from of tracked) {
42360
42403
  if (from.identifier.id === into.identifier.id ||
42361
42404
  from.identifier.id === fn.returns.identifier.id) {
@@ -42423,6 +42466,7 @@ class AliasingState {
42423
42466
  transitive: null,
42424
42467
  local: null,
42425
42468
  lastMutated: 0,
42469
+ mutationReason: null,
42426
42470
  value,
42427
42471
  });
42428
42472
  }
@@ -42507,7 +42551,8 @@ class AliasingState {
42507
42551
  }
42508
42552
  }
42509
42553
  }
42510
- mutate(index, start, end, transitive, startKind, loc, errors) {
42554
+ mutate(index, start, end, transitive, startKind, loc, reason, errors) {
42555
+ var _a;
42511
42556
  const seen = new Map();
42512
42557
  const queue = [{ place: start, transitive, direction: 'backwards', kind: startKind }];
42513
42558
  while (queue.length !== 0) {
@@ -42521,6 +42566,7 @@ class AliasingState {
42521
42566
  if (node == null) {
42522
42567
  continue;
42523
42568
  }
42569
+ (_a = node.mutationReason) !== null && _a !== void 0 ? _a : (node.mutationReason = reason);
42524
42570
  node.lastMutated = Math.max(node.lastMutated, index);
42525
42571
  if (end != null) {
42526
42572
  node.id.mutableRange.end = makeInstructionId(Math.max(node.id.mutableRange.end, end));
@@ -46572,6 +46618,14 @@ class Unifier {
46572
46618
  if (type.kind === 'Phi') {
46573
46619
  return { kind: 'Phi', operands: type.operands.map(o => this.get(o)) };
46574
46620
  }
46621
+ if (type.kind === 'Function') {
46622
+ return {
46623
+ kind: 'Function',
46624
+ isConstructor: type.isConstructor,
46625
+ shapeId: type.shapeId,
46626
+ return: this.get(type.return),
46627
+ };
46628
+ }
46575
46629
  return type;
46576
46630
  }
46577
46631
  }
@@ -50748,7 +50802,14 @@ function findFunctionsToCompile(program, pass, programContext) {
50748
50802
  var _a;
50749
50803
  const queue = [];
50750
50804
  const traverseFunction = (fn, pass) => {
50805
+ if (pass.opts.compilationMode === 'all' &&
50806
+ fn.scope.getProgramParent() !== fn.scope.parent) {
50807
+ return;
50808
+ }
50751
50809
  const fnType = getReactFunctionType(fn, pass);
50810
+ if (pass.opts.environment.validateNoDynamicallyCreatedComponentsOrHooks) {
50811
+ validateNoDynamicallyCreatedComponentsOrHooks(fn, pass, programContext);
50812
+ }
50752
50813
  if (fnType === null || programContext.alreadyCompiled.has(fn.node)) {
50753
50814
  return;
50754
50815
  }
@@ -50933,6 +50994,52 @@ function shouldSkipCompilation(program, pass) {
50933
50994
  }
50934
50995
  return false;
50935
50996
  }
50997
+ function validateNoDynamicallyCreatedComponentsOrHooks(fn, pass, programContext) {
50998
+ const parentNameExpr = getFunctionName$1(fn);
50999
+ const parentName = parentNameExpr !== null && parentNameExpr.isIdentifier()
51000
+ ? parentNameExpr.node.name
51001
+ : '<anonymous>';
51002
+ const validateNestedFunction = (nestedFn) => {
51003
+ var _a, _b, _c, _d;
51004
+ if (nestedFn.node === fn.node ||
51005
+ programContext.alreadyCompiled.has(nestedFn.node)) {
51006
+ return;
51007
+ }
51008
+ if (nestedFn.scope.getProgramParent() !== nestedFn.scope.parent) {
51009
+ const nestedFnType = getReactFunctionType(nestedFn, pass);
51010
+ const nestedFnNameExpr = getFunctionName$1(nestedFn);
51011
+ const nestedName = nestedFnNameExpr !== null && nestedFnNameExpr.isIdentifier()
51012
+ ? nestedFnNameExpr.node.name
51013
+ : '<anonymous>';
51014
+ if (nestedFnType === 'Component' || nestedFnType === 'Hook') {
51015
+ CompilerError.throwDiagnostic({
51016
+ category: ErrorCategory.Factories,
51017
+ severity: ErrorSeverity.InvalidReact,
51018
+ reason: `Components and hooks cannot be created dynamically`,
51019
+ description: `The function \`${nestedName}\` appears to be a React ${nestedFnType.toLowerCase()}, but it's defined inside \`${parentName}\`. Components and Hooks should always be declared at module scope`,
51020
+ details: [
51021
+ {
51022
+ kind: 'error',
51023
+ message: 'this function dynamically created a component/hook',
51024
+ loc: (_b = (_a = parentNameExpr === null || parentNameExpr === void 0 ? void 0 : parentNameExpr.node.loc) !== null && _a !== void 0 ? _a : fn.node.loc) !== null && _b !== void 0 ? _b : null,
51025
+ },
51026
+ {
51027
+ kind: 'error',
51028
+ message: 'the component is created here',
51029
+ loc: (_d = (_c = nestedFnNameExpr === null || nestedFnNameExpr === void 0 ? void 0 : nestedFnNameExpr.node.loc) !== null && _c !== void 0 ? _c : nestedFn.node.loc) !== null && _d !== void 0 ? _d : null,
51030
+ },
51031
+ ],
51032
+ });
51033
+ }
51034
+ }
51035
+ nestedFn.skip();
51036
+ };
51037
+ fn.traverse({
51038
+ FunctionDeclaration: validateNestedFunction,
51039
+ FunctionExpression: validateNestedFunction,
51040
+ ArrowFunctionExpression: validateNestedFunction,
51041
+ });
51042
+ }
50936
51043
  function getReactFunctionType(fn, pass) {
50937
51044
  var _a, _b;
50938
51045
  const hookPattern = pass.opts.environment.hookPattern;
@@ -50962,9 +51069,6 @@ function getReactFunctionType(fn, pass) {
50962
51069
  return componentSyntaxType;
50963
51070
  }
50964
51071
  case 'all': {
50965
- if (fn.scope.getProgramParent() !== fn.scope.parent) {
50966
- return null;
50967
- }
50968
51072
  return (_b = getComponentOrHookLike(fn, hookPattern)) !== null && _b !== void 0 ? _b : 'Other';
50969
51073
  }
50970
51074
  default: {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "eslint-plugin-react-hooks",
3
3
  "description": "ESLint rules for React Hooks",
4
- "version": "6.1.0-canary-4123f6b7-20250826",
4
+ "version": "6.1.0-canary-8d7b5e49-20250827",
5
5
  "repository": {
6
6
  "type": "git",
7
7
  "url": "https://github.com/facebook/react.git",