@atlaskit/eslint-plugin-platform 2.7.1 → 2.8.0

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.
Files changed (125) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/cjs/index.js +17 -9
  3. package/dist/cjs/rules/constants.js +1 -1
  4. package/dist/cjs/rules/ensure-critical-dependency-resolutions/index.js +5 -5
  5. package/dist/cjs/rules/ensure-no-private-dependencies/index.js +48 -66
  6. package/dist/cjs/rules/feature-gating/inline-usage/index.js +14 -3
  7. package/dist/cjs/rules/feature-gating/no-alias/index.js +1 -1
  8. package/dist/cjs/rules/feature-gating/no-module-level-eval/index.js +1 -1
  9. package/dist/cjs/rules/feature-gating/no-module-level-eval-nav4/index.js +1 -1
  10. package/dist/cjs/rules/feature-gating/no-preconditioning/index.js +4 -1
  11. package/dist/cjs/rules/feature-gating/prefer-fg/index.js +1 -1
  12. package/dist/cjs/rules/feature-gating/static-feature-flags/index.js +2 -2
  13. package/dist/cjs/rules/feature-gating/use-recommended-utils/index.js +1 -1
  14. package/dist/cjs/rules/feature-gating/valid-gate-name/index.js +60 -0
  15. package/dist/cjs/rules/import/no-barrel-entry-imports/index.js +871 -0
  16. package/dist/cjs/rules/import/no-barrel-entry-jest-mock/index.js +1384 -0
  17. package/dist/cjs/rules/import/no-conversation-assistant-barrel-imports/index.js +43 -0
  18. package/dist/cjs/rules/import/no-jest-mock-barrel-files/index.js +1401 -0
  19. package/dist/cjs/rules/import/no-relative-barrel-file-imports/index.js +777 -0
  20. package/dist/cjs/rules/import/shared/barrel-parsing.js +511 -0
  21. package/dist/cjs/rules/import/shared/file-system.js +186 -0
  22. package/dist/cjs/rules/import/shared/jest-utils.js +191 -0
  23. package/dist/cjs/rules/import/shared/package-registry.js +263 -0
  24. package/dist/cjs/rules/import/shared/package-resolution.js +185 -0
  25. package/dist/cjs/rules/import/shared/perf.js +89 -0
  26. package/dist/cjs/rules/import/shared/types.js +67 -0
  27. package/dist/cjs/rules/no-invalid-storybook-decorator-usage/index.js +1 -1
  28. package/dist/cjs/rules/no-sparse-checkout/index.js +1 -1
  29. package/dist/cjs/rules/prefer-crypto-random-uuid/index.js +87 -0
  30. package/dist/cjs/rules/use-entrypoints-in-examples/index.js +1 -1
  31. package/dist/es2019/index.js +17 -9
  32. package/dist/es2019/rules/constants.js +1 -1
  33. package/dist/es2019/rules/ensure-critical-dependency-resolutions/index.js +5 -5
  34. package/dist/es2019/rules/ensure-no-private-dependencies/index.js +10 -9
  35. package/dist/es2019/rules/feature-gating/inline-usage/index.js +14 -3
  36. package/dist/es2019/rules/feature-gating/no-alias/index.js +1 -1
  37. package/dist/es2019/rules/feature-gating/no-module-level-eval/index.js +1 -1
  38. package/dist/es2019/rules/feature-gating/no-module-level-eval-nav4/index.js +1 -1
  39. package/dist/es2019/rules/feature-gating/no-preconditioning/index.js +4 -1
  40. package/dist/es2019/rules/feature-gating/prefer-fg/index.js +1 -1
  41. package/dist/es2019/rules/feature-gating/static-feature-flags/index.js +2 -2
  42. package/dist/es2019/rules/feature-gating/use-recommended-utils/index.js +1 -1
  43. package/dist/es2019/rules/feature-gating/valid-gate-name/index.js +52 -0
  44. package/dist/es2019/rules/import/no-barrel-entry-imports/index.js +801 -0
  45. package/dist/es2019/rules/import/no-barrel-entry-jest-mock/index.js +1113 -0
  46. package/dist/es2019/rules/import/no-conversation-assistant-barrel-imports/index.js +37 -0
  47. package/dist/es2019/rules/import/no-jest-mock-barrel-files/index.js +1179 -0
  48. package/dist/es2019/rules/import/no-relative-barrel-file-imports/index.js +738 -0
  49. package/dist/es2019/rules/import/shared/barrel-parsing.js +433 -0
  50. package/dist/es2019/rules/import/shared/file-system.js +174 -0
  51. package/dist/es2019/rules/import/shared/jest-utils.js +159 -0
  52. package/dist/es2019/rules/import/shared/package-registry.js +240 -0
  53. package/dist/es2019/rules/import/shared/package-resolution.js +161 -0
  54. package/dist/es2019/rules/import/shared/perf.js +83 -0
  55. package/dist/es2019/rules/import/shared/types.js +57 -0
  56. package/dist/es2019/rules/no-invalid-storybook-decorator-usage/index.js +1 -1
  57. package/dist/es2019/rules/no-sparse-checkout/index.js +1 -1
  58. package/dist/es2019/rules/prefer-crypto-random-uuid/index.js +81 -0
  59. package/dist/es2019/rules/use-entrypoints-in-examples/index.js +1 -1
  60. package/dist/esm/index.js +17 -9
  61. package/dist/esm/rules/constants.js +1 -1
  62. package/dist/esm/rules/ensure-critical-dependency-resolutions/index.js +5 -5
  63. package/dist/esm/rules/ensure-no-private-dependencies/index.js +48 -65
  64. package/dist/esm/rules/feature-gating/inline-usage/index.js +14 -3
  65. package/dist/esm/rules/feature-gating/no-alias/index.js +1 -1
  66. package/dist/esm/rules/feature-gating/no-module-level-eval/index.js +1 -1
  67. package/dist/esm/rules/feature-gating/no-module-level-eval-nav4/index.js +1 -1
  68. package/dist/esm/rules/feature-gating/no-preconditioning/index.js +4 -1
  69. package/dist/esm/rules/feature-gating/prefer-fg/index.js +1 -1
  70. package/dist/esm/rules/feature-gating/static-feature-flags/index.js +2 -2
  71. package/dist/esm/rules/feature-gating/use-recommended-utils/index.js +1 -1
  72. package/dist/esm/rules/feature-gating/valid-gate-name/index.js +53 -0
  73. package/dist/esm/rules/import/no-barrel-entry-imports/index.js +864 -0
  74. package/dist/esm/rules/import/no-barrel-entry-jest-mock/index.js +1375 -0
  75. package/dist/esm/rules/import/no-conversation-assistant-barrel-imports/index.js +37 -0
  76. package/dist/esm/rules/import/no-jest-mock-barrel-files/index.js +1391 -0
  77. package/dist/esm/rules/import/no-relative-barrel-file-imports/index.js +770 -0
  78. package/dist/esm/rules/import/shared/barrel-parsing.js +500 -0
  79. package/dist/esm/rules/import/shared/file-system.js +176 -0
  80. package/dist/esm/rules/import/shared/jest-utils.js +179 -0
  81. package/dist/esm/rules/import/shared/package-registry.js +256 -0
  82. package/dist/esm/rules/import/shared/package-resolution.js +175 -0
  83. package/dist/esm/rules/import/shared/perf.js +80 -0
  84. package/dist/esm/rules/import/shared/types.js +61 -0
  85. package/dist/esm/rules/no-invalid-storybook-decorator-usage/index.js +1 -1
  86. package/dist/esm/rules/no-sparse-checkout/index.js +1 -1
  87. package/dist/esm/rules/prefer-crypto-random-uuid/index.js +81 -0
  88. package/dist/esm/rules/use-entrypoints-in-examples/index.js +1 -1
  89. package/dist/types/index.d.ts +18 -16
  90. package/dist/types/rules/import/no-barrel-entry-imports/index.d.ts +9 -0
  91. package/dist/types/rules/import/no-barrel-entry-jest-mock/index.d.ts +9 -0
  92. package/dist/types/rules/import/no-conversation-assistant-barrel-imports/index.d.ts +3 -0
  93. package/dist/types/rules/import/no-jest-mock-barrel-files/index.d.ts +22 -0
  94. package/dist/types/rules/import/no-relative-barrel-file-imports/index.d.ts +5 -0
  95. package/dist/types/rules/import/shared/barrel-parsing.d.ts +30 -0
  96. package/dist/types/rules/import/shared/file-system.d.ts +38 -0
  97. package/dist/types/rules/import/shared/jest-utils.d.ts +47 -0
  98. package/dist/types/rules/import/shared/package-registry.d.ts +26 -0
  99. package/dist/types/rules/import/shared/package-resolution.d.ts +38 -0
  100. package/dist/types/rules/import/shared/perf.d.ts +13 -0
  101. package/dist/types/rules/import/shared/types.d.ts +131 -0
  102. package/dist/types/rules/prefer-crypto-random-uuid/index.d.ts +3 -0
  103. package/dist/types-ts4.5/index.d.ts +18 -28
  104. package/dist/types-ts4.5/rules/import/no-barrel-entry-imports/index.d.ts +9 -0
  105. package/dist/types-ts4.5/rules/import/no-barrel-entry-jest-mock/index.d.ts +9 -0
  106. package/dist/types-ts4.5/rules/import/no-jest-mock-barrel-files/index.d.ts +22 -0
  107. package/dist/types-ts4.5/rules/import/no-relative-barrel-file-imports/index.d.ts +5 -0
  108. package/dist/types-ts4.5/rules/import/shared/barrel-parsing.d.ts +30 -0
  109. package/dist/types-ts4.5/rules/import/shared/file-system.d.ts +38 -0
  110. package/dist/types-ts4.5/rules/import/shared/jest-utils.d.ts +47 -0
  111. package/dist/types-ts4.5/rules/import/shared/package-registry.d.ts +26 -0
  112. package/dist/types-ts4.5/rules/import/shared/package-resolution.d.ts +38 -0
  113. package/dist/types-ts4.5/rules/import/shared/perf.d.ts +13 -0
  114. package/dist/types-ts4.5/rules/import/shared/types.d.ts +131 -0
  115. package/package.json +4 -5
  116. package/dist/cjs/rules/ensure-feature-flag-prefix/index.js +0 -75
  117. package/dist/cjs/rules/ensure-native-and-af-exports-synced/index.js +0 -158
  118. package/dist/es2019/rules/ensure-feature-flag-prefix/index.js +0 -65
  119. package/dist/es2019/rules/ensure-native-and-af-exports-synced/index.js +0 -146
  120. package/dist/esm/rules/ensure-feature-flag-prefix/index.js +0 -69
  121. package/dist/esm/rules/ensure-native-and-af-exports-synced/index.js +0 -151
  122. /package/dist/types/rules/{ensure-native-and-af-exports-synced → feature-gating/valid-gate-name}/index.d.ts +0 -0
  123. /package/dist/types-ts4.5/rules/{ensure-feature-flag-prefix → feature-gating/valid-gate-name}/index.d.ts +0 -0
  124. /package/dist/types-ts4.5/rules/{ensure-native-and-af-exports-synced → import/no-conversation-assistant-barrel-imports}/index.d.ts +0 -0
  125. /package/dist/{types/rules/ensure-feature-flag-prefix → types-ts4.5/rules/prefer-crypto-random-uuid}/index.d.ts +0 -0
@@ -0,0 +1,1391 @@
1
+ import _toConsumableArray from "@babel/runtime/helpers/toConsumableArray";
2
+ import _typeof from "@babel/runtime/helpers/typeof";
3
+ import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
4
+ function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t.return || t.return(); } finally { if (u) throw o; } } }; }
5
+ function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
6
+ function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
7
+ import { dirname, relative } from 'path';
8
+ import * as ts from 'typescript';
9
+ import { hasReExportsFromOtherFiles, parseBarrelExports } from '../shared/barrel-parsing';
10
+ import { findWorkspaceRoot, isRelativeImport, resolveImportPath } from '../shared/file-system';
11
+ import { extractImportPath, findJestRequireMockCalls, isJestMockCall, isJestRequireActual, resolveNewPathForRequireMock } from '../shared/jest-utils';
12
+ import { findPackageInRegistry } from '../shared/package-registry';
13
+ import { findExportForSourceFile, parsePackageExports } from '../shared/package-resolution';
14
+ import { realFileSystem } from '../shared/types';
15
+
16
+ // Cache per source package name to avoid repeated exports parsing during a single lint run.
17
+ // This is keyed by fs instance to avoid test pollution.
18
+ var sourcePackageExportsMapsByFs = new WeakMap();
19
+ function getSourcePackageExportsMaps(fs) {
20
+ var map = sourcePackageExportsMapsByFs.get(fs);
21
+ if (!map) {
22
+ map = new Map();
23
+ sourcePackageExportsMapsByFs.set(fs, map);
24
+ }
25
+ return map;
26
+ }
27
+
28
+ /**
29
+ * Information about a mock factory's preamble (statements before the return)
30
+ */
31
+
32
+ /**
33
+ * Extract identifiers defined by a statement (e.g., variable declarations)
34
+ * Uses TypeScript AST to find declared identifiers.
35
+ */
36
+ function extractDefinedIdentifiers(statementText) {
37
+ var identifiers = new Set();
38
+ try {
39
+ // Parse the statement as a mini source file
40
+ var sourceFile = ts.createSourceFile('temp.ts', statementText, ts.ScriptTarget.Latest, true);
41
+ var _visit = function visit(node) {
42
+ if (ts.isVariableStatement(node)) {
43
+ var _iterator = _createForOfIteratorHelper(node.declarationList.declarations),
44
+ _step;
45
+ try {
46
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
47
+ var decl = _step.value;
48
+ if (ts.isIdentifier(decl.name)) {
49
+ identifiers.add(decl.name.text);
50
+ } else if (ts.isObjectBindingPattern(decl.name)) {
51
+ // Handle destructuring: const { a, b } = ...
52
+ var _iterator2 = _createForOfIteratorHelper(decl.name.elements),
53
+ _step2;
54
+ try {
55
+ for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
56
+ var element = _step2.value;
57
+ if (ts.isBindingElement(element) && ts.isIdentifier(element.name)) {
58
+ identifiers.add(element.name.text);
59
+ }
60
+ }
61
+ } catch (err) {
62
+ _iterator2.e(err);
63
+ } finally {
64
+ _iterator2.f();
65
+ }
66
+ } else if (ts.isArrayBindingPattern(decl.name)) {
67
+ // Handle array destructuring: const [a, b] = ...
68
+ var _iterator3 = _createForOfIteratorHelper(decl.name.elements),
69
+ _step3;
70
+ try {
71
+ for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
72
+ var _element = _step3.value;
73
+ if (ts.isBindingElement(_element) && ts.isIdentifier(_element.name)) {
74
+ identifiers.add(_element.name.text);
75
+ }
76
+ }
77
+ } catch (err) {
78
+ _iterator3.e(err);
79
+ } finally {
80
+ _iterator3.f();
81
+ }
82
+ }
83
+ }
84
+ } catch (err) {
85
+ _iterator.e(err);
86
+ } finally {
87
+ _iterator.f();
88
+ }
89
+ } else if (ts.isFunctionDeclaration(node) && node.name) {
90
+ identifiers.add(node.name.text);
91
+ } else if (ts.isClassDeclaration(node) && node.name) {
92
+ identifiers.add(node.name.text);
93
+ }
94
+ ts.forEachChild(node, _visit);
95
+ };
96
+ ts.forEachChild(sourceFile, _visit);
97
+ } catch (_unused) {
98
+ // Ignore parsing errors
99
+ }
100
+ return identifiers;
101
+ }
102
+
103
+ /**
104
+ * Find all identifiers used in a given text string.
105
+ * Uses a simple regex approach to find potential identifier references.
106
+ */
107
+ function findUsedIdentifiers(text, potentialIdentifiers) {
108
+ var used = new Set();
109
+ var _iterator4 = _createForOfIteratorHelper(potentialIdentifiers),
110
+ _step4;
111
+ try {
112
+ for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
113
+ var identifier = _step4.value;
114
+ // Use word boundary matching to find identifier usage
115
+ // This matches the identifier as a whole word (not part of another word)
116
+ var regex = new RegExp("\\b".concat(escapeRegExpForIdentifier(identifier), "\\b"));
117
+ if (regex.test(text)) {
118
+ used.add(identifier);
119
+ }
120
+ }
121
+ } catch (err) {
122
+ _iterator4.e(err);
123
+ } finally {
124
+ _iterator4.f();
125
+ }
126
+ return used;
127
+ }
128
+
129
+ /**
130
+ * Escape special regex characters for identifier matching
131
+ */
132
+ function escapeRegExpForIdentifier(str) {
133
+ return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
134
+ }
135
+
136
+ /**
137
+ * Filter preamble statements to only include those whose defined identifiers
138
+ * are used in the given property texts.
139
+ */
140
+ function filterPreambleForProperties(preamble, propertyTexts) {
141
+ if (!preamble.hasPreamble || preamble.statements.length === 0) {
142
+ return preamble;
143
+ }
144
+
145
+ // Collect all identifiers defined in the preamble
146
+ var allDefinedIdentifiers = new Set();
147
+ var _iterator5 = _createForOfIteratorHelper(preamble.statements),
148
+ _step5;
149
+ try {
150
+ for (_iterator5.s(); !(_step5 = _iterator5.n()).done;) {
151
+ var stmt = _step5.value;
152
+ var _iterator7 = _createForOfIteratorHelper(stmt.definedIdentifiers),
153
+ _step7;
154
+ try {
155
+ for (_iterator7.s(); !(_step7 = _iterator7.n()).done;) {
156
+ var id = _step7.value;
157
+ allDefinedIdentifiers.add(id);
158
+ }
159
+ } catch (err) {
160
+ _iterator7.e(err);
161
+ } finally {
162
+ _iterator7.f();
163
+ }
164
+ }
165
+
166
+ // Find which identifiers are used in the property texts
167
+ } catch (err) {
168
+ _iterator5.e(err);
169
+ } finally {
170
+ _iterator5.f();
171
+ }
172
+ var combinedPropertyText = propertyTexts.join('\n');
173
+ var usedIdentifiers = findUsedIdentifiers(combinedPropertyText, allDefinedIdentifiers);
174
+
175
+ // Filter statements to only those that define used identifiers
176
+ var filteredStatements = preamble.statements.filter(function (stmt) {
177
+ // Include statement if any of its defined identifiers are used
178
+ var _iterator6 = _createForOfIteratorHelper(stmt.definedIdentifiers),
179
+ _step6;
180
+ try {
181
+ for (_iterator6.s(); !(_step6 = _iterator6.n()).done;) {
182
+ var id = _step6.value;
183
+ if (usedIdentifiers.has(id)) {
184
+ return true;
185
+ }
186
+ }
187
+ } catch (err) {
188
+ _iterator6.e(err);
189
+ } finally {
190
+ _iterator6.f();
191
+ }
192
+ return false;
193
+ });
194
+ if (filteredStatements.length === 0) {
195
+ return {
196
+ text: '',
197
+ hasPreamble: false,
198
+ statements: []
199
+ };
200
+ }
201
+ return {
202
+ text: filteredStatements.map(function (s) {
203
+ return s.text;
204
+ }).join('\n\t'),
205
+ hasPreamble: true,
206
+ statements: filteredStatements
207
+ };
208
+ }
209
+
210
+ /**
211
+ * Convert absolute file path to an import path, handling cross-package resolution.
212
+ * If the export comes from a cross-package source, returns the package path (e.g., '@atlassian/package-b/utils').
213
+ * Otherwise, returns a relative path.
214
+ */
215
+ function getImportPathForSourceFile(_ref) {
216
+ var _exportInfo$crossPack;
217
+ var sourceFilePath = _ref.sourceFilePath,
218
+ basedir = _ref.basedir,
219
+ originalImportPath = _ref.originalImportPath,
220
+ exportInfo = _ref.exportInfo,
221
+ workspaceRoot = _ref.workspaceRoot,
222
+ fs = _ref.fs;
223
+ var crossPackageName = exportInfo === null || exportInfo === void 0 || (_exportInfo$crossPack = exportInfo.crossPackageSource) === null || _exportInfo$crossPack === void 0 ? void 0 : _exportInfo$crossPack.packageName;
224
+ if (crossPackageName) {
225
+ var sourcePackageExportsMaps = getSourcePackageExportsMaps(fs);
226
+ var exportsMap = sourcePackageExportsMaps.get(crossPackageName);
227
+ if (!exportsMap) {
228
+ var pkgDir = findPackageInRegistry({
229
+ packageName: crossPackageName,
230
+ workspaceRoot: workspaceRoot,
231
+ fs: fs
232
+ });
233
+ if (pkgDir) {
234
+ exportsMap = parsePackageExports({
235
+ packageDir: pkgDir,
236
+ fs: fs
237
+ });
238
+ sourcePackageExportsMaps.set(crossPackageName, exportsMap);
239
+ }
240
+ }
241
+ var targetExportPath = exportsMap ? findExportForSourceFile({
242
+ sourceFilePath: sourceFilePath,
243
+ exportsMap: exportsMap
244
+ }) : null;
245
+ return targetExportPath ? crossPackageName + targetExportPath.slice(1) : crossPackageName;
246
+ }
247
+ return getRelativeImportPath({
248
+ basedir: basedir,
249
+ absolutePath: sourceFilePath,
250
+ originalImportPath: originalImportPath
251
+ });
252
+ }
253
+
254
+ /**
255
+ * Convert absolute file path back to relative import path
256
+ */
257
+ function getRelativeImportPath(_ref2) {
258
+ var basedir = _ref2.basedir,
259
+ absolutePath = _ref2.absolutePath,
260
+ originalImportPath = _ref2.originalImportPath;
261
+ var relativePath = relative(basedir, absolutePath);
262
+ // Normalize to use forward slashes
263
+ relativePath = relativePath.replace(/\\/g, '/');
264
+
265
+ // Check for extension in original path
266
+ var extMatch = originalImportPath.match(/\.(js|jsx|ts|tsx|mjs|cjs)$/);
267
+ var originalExt = extMatch ? extMatch[0] : '';
268
+
269
+ // Get extension from the resolved absolute path
270
+ var targetExtMatch = absolutePath.match(/\.(js|jsx|ts|tsx|mjs|cjs)$/);
271
+ var targetExt = targetExtMatch ? targetExtMatch[0] : '';
272
+
273
+ // Remove file extension from the target path
274
+ relativePath = relativePath.replace(/\.(ts|tsx|js|jsx|mjs|cjs)$/, '');
275
+
276
+ // If original had extension, append it
277
+ if (originalExt) {
278
+ // If original was a TypeScript source extension, use the actual target extension
279
+ if (['.ts', '.tsx'].includes(originalExt) && targetExt) {
280
+ relativePath += targetExt;
281
+ } else {
282
+ relativePath += originalExt;
283
+ }
284
+ } else {
285
+ // Remove /index suffix only if no extension was present
286
+ if (relativePath.endsWith('/index')) {
287
+ relativePath = relativePath.slice(0, -6);
288
+ } else if (relativePath === 'index') {
289
+ relativePath = '.';
290
+ }
291
+ }
292
+
293
+ // Ensure it starts with .. or .
294
+ if (!relativePath.startsWith('.') && !relativePath.startsWith('/')) {
295
+ relativePath = './' + relativePath;
296
+ }
297
+ return relativePath;
298
+ }
299
+
300
+ /**
301
+ * Check if a node is an Object.assign call
302
+ */
303
+ function isObjectAssignCall(node) {
304
+ if (node.type !== 'CallExpression') {
305
+ return false;
306
+ }
307
+ var callee = node.callee;
308
+ if (callee.type === 'MemberExpression') {
309
+ return callee.object.type === 'Identifier' && callee.object.name === 'Object' && callee.property.type === 'Identifier' && callee.property.name === 'assign';
310
+ }
311
+ return false;
312
+ }
313
+
314
+ /**
315
+ * Extract mock object from Object.assign pattern
316
+ * Pattern: Object.assign({}, jest.requireActual(...), { mockProps })
317
+ * Returns the properties object and whether it has requireActual
318
+ */
319
+ function extractObjectAssignMock(node) {
320
+ var args = node.arguments;
321
+
322
+ // Object.assign typically has at least 2 arguments: target and source(s)
323
+ // Pattern: Object.assign({}, jest.requireActual(...), { mockProps })
324
+ // or: Object.assign({}, jest.requireActual(...), { mockProps1 }, { mockProps2 })
325
+ if (args.length < 2) {
326
+ return {
327
+ propertiesObject: null,
328
+ hasRequireActual: false
329
+ };
330
+ }
331
+ var hasRequireActual = false;
332
+ var lastObjectExpression = null;
333
+
334
+ // Scan through arguments to find jest.requireActual and the last object literal
335
+ var _iterator8 = _createForOfIteratorHelper(args),
336
+ _step8;
337
+ try {
338
+ for (_iterator8.s(); !(_step8 = _iterator8.n()).done;) {
339
+ var arg = _step8.value;
340
+ if (isJestRequireActual(arg)) {
341
+ hasRequireActual = true;
342
+ }
343
+ if (arg.type === 'ObjectExpression') {
344
+ // Skip empty objects (the first {} in Object.assign({}, ...))
345
+ if (arg.properties.length > 0) {
346
+ lastObjectExpression = arg;
347
+ }
348
+ }
349
+ }
350
+ } catch (err) {
351
+ _iterator8.e(err);
352
+ } finally {
353
+ _iterator8.f();
354
+ }
355
+ return {
356
+ propertiesObject: lastObjectExpression,
357
+ hasRequireActual: hasRequireActual
358
+ };
359
+ }
360
+
361
+ /**
362
+ * Extract mock object properties from jest.mock call
363
+ * Returns a map of property name -> { node, text } and whether there's a jest.requireActual spread
364
+ */
365
+ function extractMockProperties(_ref3) {
366
+ var sourceCode = _ref3.sourceCode,
367
+ mockObjectNode = _ref3.mockObjectNode;
368
+ var properties = new Map();
369
+ var hasRequireActual = false;
370
+
371
+ // Handle Object.assign pattern: Object.assign({}, jest.requireActual(...), { props })
372
+ if (isObjectAssignCall(mockObjectNode)) {
373
+ var _extractObjectAssignM = extractObjectAssignMock(mockObjectNode),
374
+ propertiesObject = _extractObjectAssignM.propertiesObject,
375
+ objectAssignHasRequireActual = _extractObjectAssignM.hasRequireActual;
376
+ if (propertiesObject) {
377
+ // Recursively extract properties from the properties object
378
+ var result = extractMockProperties({
379
+ sourceCode: sourceCode,
380
+ mockObjectNode: propertiesObject
381
+ });
382
+ return {
383
+ properties: result.properties,
384
+ hasRequireActual: objectAssignHasRequireActual || result.hasRequireActual
385
+ };
386
+ }
387
+ return {
388
+ properties: properties,
389
+ hasRequireActual: objectAssignHasRequireActual
390
+ };
391
+ }
392
+ if (mockObjectNode.type === 'ObjectExpression') {
393
+ var _iterator9 = _createForOfIteratorHelper(mockObjectNode.properties),
394
+ _step9;
395
+ try {
396
+ for (_iterator9.s(); !(_step9 = _iterator9.n()).done;) {
397
+ var prop = _step9.value;
398
+ if (prop.type === 'SpreadElement') {
399
+ // Check if this is ...jest.requireActual(...)
400
+ if (isJestRequireActual(prop.argument)) {
401
+ hasRequireActual = true;
402
+ }
403
+ } else if (prop.type === 'Property') {
404
+ var keyName = void 0;
405
+ if (prop.key.type === 'Identifier') {
406
+ keyName = prop.key.name;
407
+ } else if (prop.key.type === 'Literal') {
408
+ keyName = String(prop.key.value);
409
+ } else {
410
+ continue;
411
+ }
412
+ var propText = sourceCode.getText(prop);
413
+ var valueText = sourceCode.getText(prop.value);
414
+ properties.set(keyName, {
415
+ node: prop,
416
+ text: propText,
417
+ valueText: valueText
418
+ });
419
+ }
420
+ }
421
+ } catch (err) {
422
+ _iterator9.e(err);
423
+ } finally {
424
+ _iterator9.f();
425
+ }
426
+ }
427
+ return {
428
+ properties: properties,
429
+ hasRequireActual: hasRequireActual
430
+ };
431
+ }
432
+
433
+ /**
434
+ * Validate and resolve a barrel file from an import path
435
+ * Returns null if not a valid relative barrel import
436
+ */
437
+ export function validateAndResolveBarrelFile(_ref4) {
438
+ var importPath = _ref4.importPath,
439
+ basedir = _ref4.basedir,
440
+ workspaceRoot = _ref4.workspaceRoot,
441
+ fs = _ref4.fs;
442
+ if (!isRelativeImport(importPath)) {
443
+ return null;
444
+ }
445
+ var resolvedPath = resolveImportPath({
446
+ basedir: basedir,
447
+ importPath: importPath,
448
+ fs: fs
449
+ });
450
+ if (!resolvedPath) {
451
+ return null;
452
+ }
453
+ var exportMap = parseBarrelExports({
454
+ barrelFilePath: resolvedPath,
455
+ workspaceRoot: workspaceRoot,
456
+ fs: fs
457
+ });
458
+ if (exportMap.size === 0) {
459
+ return null;
460
+ }
461
+
462
+ // A file is considered a barrel file if it has re-exports from other files.
463
+ // This is the semantic check - we don't care about the filename.
464
+ if (!hasReExportsFromOtherFiles({
465
+ exportMap: exportMap,
466
+ sourceFilePath: resolvedPath
467
+ })) {
468
+ return null;
469
+ }
470
+ return {
471
+ resolvedPath: resolvedPath,
472
+ exportMap: exportMap
473
+ };
474
+ }
475
+
476
+ /**
477
+ * Extract the mock implementation object from the jest.mock call
478
+ */
479
+ function extractMockImplementation(mockImpl) {
480
+ if (mockImpl.type === 'ArrowFunctionExpression') {
481
+ if (mockImpl.body.type === 'ObjectExpression') {
482
+ return mockImpl.body;
483
+ }
484
+ // Handle arrow functions that return a call expression directly (e.g., Object.assign)
485
+ if (mockImpl.body.type === 'CallExpression') {
486
+ return mockImpl.body;
487
+ }
488
+ if (mockImpl.body.type === 'BlockStatement') {
489
+ var returnStmt = mockImpl.body.body.find(function (s) {
490
+ return s.type === 'ReturnStatement';
491
+ });
492
+ if (returnStmt !== null && returnStmt !== void 0 && returnStmt.argument) {
493
+ return returnStmt.argument;
494
+ }
495
+ }
496
+ }
497
+ if (mockImpl.type === 'FunctionExpression' && mockImpl.body.type === 'BlockStatement') {
498
+ var _returnStmt = mockImpl.body.body.find(function (s) {
499
+ return s.type === 'ReturnStatement';
500
+ });
501
+ if (_returnStmt !== null && _returnStmt !== void 0 && _returnStmt.argument) {
502
+ return _returnStmt.argument;
503
+ }
504
+ }
505
+ return mockImpl;
506
+ }
507
+
508
+ /**
509
+ * Extract the preamble (statements before the return) from a mock factory function.
510
+ * This captures variable declarations, assignments, etc. that need to be preserved.
511
+ */
512
+ function extractMockFactoryPreamble(_ref5) {
513
+ var mockImpl = _ref5.mockImpl,
514
+ sourceCode = _ref5.sourceCode;
515
+ var emptyPreamble = {
516
+ text: '',
517
+ hasPreamble: false,
518
+ statements: []
519
+ };
520
+
521
+ // Get the block statement body from the mock factory
522
+ var blockBody = null;
523
+ if ((mockImpl.type === 'ArrowFunctionExpression' || mockImpl.type === 'FunctionExpression') && mockImpl.body.type === 'BlockStatement') {
524
+ blockBody = mockImpl.body.body;
525
+ }
526
+ if (!blockBody) {
527
+ return emptyPreamble;
528
+ }
529
+
530
+ // Find the return statement index
531
+ var returnIndex = blockBody.findIndex(function (s) {
532
+ return s.type === 'ReturnStatement';
533
+ });
534
+ if (returnIndex <= 0) {
535
+ // No preamble (return is first statement or not found)
536
+ return emptyPreamble;
537
+ }
538
+
539
+ // Extract all statements before the return
540
+ var preambleStatements = blockBody.slice(0, returnIndex);
541
+ var statementsWithIdentifiers = preambleStatements.map(function (stmt) {
542
+ var text = sourceCode.getText(stmt);
543
+ var definedIdentifiers = extractDefinedIdentifiers(text);
544
+ return {
545
+ text: text,
546
+ definedIdentifiers: definedIdentifiers
547
+ };
548
+ });
549
+ var preambleTexts = statementsWithIdentifiers.map(function (s) {
550
+ return s.text;
551
+ });
552
+ return {
553
+ text: preambleTexts.join('\n\t'),
554
+ hasPreamble: true,
555
+ statements: statementsWithIdentifiers
556
+ };
557
+ }
558
+
559
+ /**
560
+ * Rewrite jest.requireActual paths in a text string from the original barrel path to a new path.
561
+ */
562
+ function rewriteRequireActualPaths(_ref6) {
563
+ var text = _ref6.text,
564
+ originalPath = _ref6.originalPath,
565
+ newPath = _ref6.newPath,
566
+ quote = _ref6.quote;
567
+ // Match jest.requireActual('originalPath') or jest.requireActual("originalPath")
568
+ // Also handle the 'as Object' or 'as any' type assertions
569
+ var patterns = [
570
+ // With single quotes
571
+ new RegExp("jest\\.requireActual\\(\\s*'".concat(escapeRegExp(originalPath), "'\\s*\\)(?:\\s+as\\s+\\w+)?"), 'g'),
572
+ // With double quotes
573
+ new RegExp("jest\\.requireActual\\(\\s*\"".concat(escapeRegExp(originalPath), "\"\\s*\\)(?:\\s+as\\s+\\w+)?"), 'g')];
574
+ var result = text;
575
+ for (var _i = 0, _patterns = patterns; _i < _patterns.length; _i++) {
576
+ var pattern = _patterns[_i];
577
+ result = result.replace(pattern, "jest.requireActual(".concat(quote).concat(newPath).concat(quote, ")"));
578
+ }
579
+ return result;
580
+ }
581
+
582
+ /**
583
+ * Escape special regex characters in a string
584
+ */
585
+ function escapeRegExp(str) {
586
+ return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
587
+ }
588
+
589
+ /**
590
+ * Group mocked properties by their source file
591
+ */
592
+ function groupPropertiesBySource(_ref7) {
593
+ var mockProperties = _ref7.mockProperties,
594
+ exportMap = _ref7.exportMap;
595
+ var propertiesBySource = new Map();
596
+ var _iterator0 = _createForOfIteratorHelper(mockProperties),
597
+ _step0;
598
+ try {
599
+ for (_iterator0.s(); !(_step0 = _iterator0.n()).done;) {
600
+ var _step0$value = _slicedToArray(_step0.value, 1),
601
+ propName = _step0$value[0];
602
+ var exportInfo = exportMap.get(propName);
603
+ if (!exportInfo) {
604
+ continue;
605
+ }
606
+ if (!propertiesBySource.has(exportInfo.path)) {
607
+ propertiesBySource.set(exportInfo.path, []);
608
+ }
609
+ propertiesBySource.get(exportInfo.path).push(propName);
610
+ }
611
+ } catch (err) {
612
+ _iterator0.e(err);
613
+ } finally {
614
+ _iterator0.f();
615
+ }
616
+ return propertiesBySource;
617
+ }
618
+
619
+ /**
620
+ * Determine if we should report a barrel mock violation
621
+ */
622
+ function shouldReportBarrelMock(_ref8) {
623
+ var propertiesBySource = _ref8.propertiesBySource,
624
+ barrelFilePath = _ref8.barrelFilePath;
625
+ // Report if any mocked property is a re-export (comes from a different file than the barrel)
626
+ // This catches both:
627
+ // 1. Properties from multiple source files
628
+ // 2. Properties from a single source file that isn't the barrel itself
629
+ var _iterator1 = _createForOfIteratorHelper(propertiesBySource.keys()),
630
+ _step1;
631
+ try {
632
+ for (_iterator1.s(); !(_step1 = _iterator1.n()).done;) {
633
+ var sourcePath = _step1.value;
634
+ if (sourcePath !== barrelFilePath) {
635
+ return true;
636
+ }
637
+ }
638
+ } catch (err) {
639
+ _iterator1.e(err);
640
+ } finally {
641
+ _iterator1.f();
642
+ }
643
+ return false;
644
+ }
645
+
646
+ /**
647
+ * Generate auto-fix for auto-mock case (no mock implementation)
648
+ */
649
+ function generateAutoMockFix(_ref9) {
650
+ var exportMap = _ref9.exportMap,
651
+ basedir = _ref9.basedir,
652
+ importPath = _ref9.importPath,
653
+ quote = _ref9.quote,
654
+ workspaceRoot = _ref9.workspaceRoot,
655
+ fs = _ref9.fs;
656
+ // Group exports by source file, filtering out type-only source files
657
+ // Also track the ExportInfo for cross-package resolution
658
+ var sourceFilesWithInfo = new Map();
659
+ var _iterator10 = _createForOfIteratorHelper(exportMap),
660
+ _step10;
661
+ try {
662
+ for (_iterator10.s(); !(_step10 = _iterator10.n()).done;) {
663
+ var _step10$value = _slicedToArray(_step10.value, 2),
664
+ info = _step10$value[1];
665
+ if (!info.isTypeOnly && !sourceFilesWithInfo.has(info.path)) {
666
+ sourceFilesWithInfo.set(info.path, info);
667
+ }
668
+ }
669
+ } catch (err) {
670
+ _iterator10.e(err);
671
+ } finally {
672
+ _iterator10.f();
673
+ }
674
+ var sourceFileArray = Array.from(sourceFilesWithInfo.entries());
675
+ return sourceFileArray.map(function (_ref0) {
676
+ var _ref1 = _slicedToArray(_ref0, 2),
677
+ sourceFile = _ref1[0],
678
+ exportInfo = _ref1[1];
679
+ var mockPath = getImportPathForSourceFile({
680
+ sourceFilePath: sourceFile,
681
+ basedir: basedir,
682
+ originalImportPath: importPath,
683
+ exportInfo: exportInfo,
684
+ workspaceRoot: workspaceRoot,
685
+ fs: fs
686
+ });
687
+ return "jest.mock(".concat(quote).concat(mockPath).concat(quote, ")");
688
+ }).join(';\n');
689
+ }
690
+
691
+ /**
692
+ * Normalize a path for comparison (resolve to absolute path)
693
+ */
694
+ function normalizePathForComparison(_ref10) {
695
+ var basedir = _ref10.basedir,
696
+ importPath = _ref10.importPath,
697
+ fs = _ref10.fs;
698
+ if (!isRelativeImport(importPath)) {
699
+ // For non-relative imports, just return as-is for comparison
700
+ return importPath;
701
+ }
702
+ var resolved = resolveImportPath({
703
+ basedir: basedir,
704
+ importPath: importPath,
705
+ fs: fs
706
+ });
707
+ return resolved || importPath;
708
+ }
709
+
710
+ /**
711
+ * Scan the entire file for all existing jest.mock calls
712
+ * Returns a map of normalized path -> { node, properties, hasRequireActual }
713
+ */
714
+ function findAllJestMocksInFile(_ref11) {
715
+ var context = _ref11.context,
716
+ basedir = _ref11.basedir,
717
+ fs = _ref11.fs;
718
+ var allMocks = new Map();
719
+ var sourceCode = context.getSourceCode();
720
+ var ast = sourceCode.ast;
721
+
722
+ // Use a visited set to prevent infinite recursion
723
+ var visited = new Set();
724
+
725
+ // Properties to skip to avoid circular references
726
+ var skipKeys = new Set(['parent', 'loc', 'range', 'tokens', 'comments']);
727
+ function visitNode(node) {
728
+ // Prevent revisiting nodes
729
+ if (visited.has(node)) {
730
+ return;
731
+ }
732
+ visited.add(node);
733
+ if (node.type === 'CallExpression' && isJestMockCall(node)) {
734
+ var importPath = extractImportPath(node);
735
+ if (importPath) {
736
+ var normalizedPath = normalizePathForComparison({
737
+ basedir: basedir,
738
+ importPath: importPath,
739
+ fs: fs
740
+ });
741
+ var mockImpl = node.arguments[1];
742
+ if (mockImpl) {
743
+ var mockObjectNode = extractMockImplementation(mockImpl);
744
+ var _extractMockPropertie = extractMockProperties({
745
+ sourceCode: sourceCode,
746
+ mockObjectNode: mockObjectNode
747
+ }),
748
+ properties = _extractMockPropertie.properties,
749
+ hasRequireActual = _extractMockPropertie.hasRequireActual;
750
+ allMocks.set(normalizedPath, {
751
+ node: node,
752
+ importPath: importPath,
753
+ properties: properties,
754
+ hasRequireActual: hasRequireActual
755
+ });
756
+ }
757
+ }
758
+ }
759
+
760
+ // Recursively visit child nodes
761
+ for (var key in node) {
762
+ if (skipKeys.has(key)) {
763
+ continue;
764
+ }
765
+ var value = node[key];
766
+ if (value && _typeof(value) === 'object') {
767
+ if (Array.isArray(value)) {
768
+ value.forEach(function (child) {
769
+ if (child && _typeof(child) === 'object' && 'type' in child) {
770
+ visitNode(child);
771
+ }
772
+ });
773
+ } else if ('type' in value) {
774
+ visitNode(value);
775
+ }
776
+ }
777
+ }
778
+ }
779
+ visitNode(ast);
780
+ return allMocks;
781
+ }
782
+
783
+ /**
784
+ * Merge mock properties from multiple sources for the same file
785
+ */
786
+ function mergeMockProperties(_ref12) {
787
+ var existingProperties = _ref12.existingProperties,
788
+ newProperties = _ref12.newProperties;
789
+ var merged = new Map(existingProperties);
790
+ var _iterator11 = _createForOfIteratorHelper(newProperties),
791
+ _step11;
792
+ try {
793
+ for (_iterator11.s(); !(_step11 = _iterator11.n()).done;) {
794
+ var _step11$value = _slicedToArray(_step11.value, 2),
795
+ key = _step11$value[0],
796
+ value = _step11$value[1];
797
+ merged.set(key, value);
798
+ }
799
+ } catch (err) {
800
+ _iterator11.e(err);
801
+ } finally {
802
+ _iterator11.f();
803
+ }
804
+ return merged;
805
+ }
806
+
807
+ /**
808
+ * Generate mock call text for a specific file with given properties
809
+ */
810
+ function generateMockCallText(_ref13) {
811
+ var relativePath = _ref13.relativePath,
812
+ properties = _ref13.properties,
813
+ hasRequireActual = _ref13.hasRequireActual,
814
+ quote = _ref13.quote,
815
+ exportMap = _ref13.exportMap,
816
+ sourceFile = _ref13.sourceFile,
817
+ preamble = _ref13.preamble,
818
+ originalImportPath = _ref13.originalImportPath;
819
+ var propNames = Array.from(properties.keys());
820
+
821
+ // Separate props by whether they're from default exports
822
+ var defaultExportProps = [];
823
+ var namedExportProps = [];
824
+ var _loop = function _loop() {
825
+ var prop = _propNames[_i2];
826
+ var exportInfo = Array.from(exportMap.entries()).find(function (_ref14) {
827
+ var _ref15 = _slicedToArray(_ref14, 2),
828
+ exportName = _ref15[0],
829
+ info = _ref15[1];
830
+ return exportName === prop && info.path === sourceFile;
831
+ });
832
+ if (exportInfo && exportInfo[1].isDefaultExport) {
833
+ defaultExportProps.push(prop);
834
+ } else {
835
+ namedExportProps.push(prop);
836
+ }
837
+ };
838
+ for (var _i2 = 0, _propNames = propNames; _i2 < _propNames.length; _i2++) {
839
+ _loop();
840
+ }
841
+
842
+ // Collect all property texts for filtering the preamble
843
+ var allPropertyTexts = [];
844
+ for (var _i3 = 0, _namedExportProps = namedExportProps; _i3 < _namedExportProps.length; _i3++) {
845
+ var _properties$get;
846
+ var prop = _namedExportProps[_i3];
847
+ var propText = (_properties$get = properties.get(prop)) === null || _properties$get === void 0 ? void 0 : _properties$get.text;
848
+ if (propText) {
849
+ allPropertyTexts.push(propText);
850
+ }
851
+ }
852
+ for (var _i4 = 0, _defaultExportProps = defaultExportProps; _i4 < _defaultExportProps.length; _i4++) {
853
+ var _properties$get2;
854
+ var _prop = _defaultExportProps[_i4];
855
+ var _propText = (_properties$get2 = properties.get(_prop)) === null || _properties$get2 === void 0 ? void 0 : _properties$get2.valueText;
856
+ if (_propText) {
857
+ allPropertyTexts.push(_propText);
858
+ }
859
+ }
860
+
861
+ // Filter preamble to only include statements used by this mock's properties
862
+ var filteredPreamble = preamble ? filterPreambleForProperties(preamble, allPropertyTexts) : undefined;
863
+
864
+ // If we have a preamble, we need to use block body syntax with return statement
865
+ if (filteredPreamble !== null && filteredPreamble !== void 0 && filteredPreamble.hasPreamble) {
866
+ // Rewrite any jest.requireActual paths in the preamble
867
+ var preambleText = filteredPreamble.text;
868
+ if (originalImportPath) {
869
+ preambleText = rewriteRequireActualPaths({
870
+ text: preambleText,
871
+ originalPath: originalImportPath,
872
+ newPath: relativePath,
873
+ quote: quote
874
+ });
875
+ }
876
+
877
+ // Rewrite any jest.requireActual paths in property values
878
+ var rewrittenMockObjectProps = namedExportProps.map(function (p) {
879
+ var _properties$get3;
880
+ var propText = (_properties$get3 = properties.get(p)) === null || _properties$get3 === void 0 ? void 0 : _properties$get3.text;
881
+ if (propText && originalImportPath) {
882
+ propText = rewriteRequireActualPaths({
883
+ text: propText,
884
+ originalPath: originalImportPath,
885
+ newPath: relativePath,
886
+ quote: quote
887
+ });
888
+ }
889
+ return propText;
890
+ }).filter(function (p) {
891
+ return !!p;
892
+ });
893
+ var mockContentLines = [];
894
+ if (hasRequireActual) {
895
+ mockContentLines.push("...jest.requireActual(".concat(quote).concat(relativePath).concat(quote, ")"));
896
+ }
897
+ mockContentLines.push.apply(mockContentLines, _toConsumableArray(rewrittenMockObjectProps));
898
+ var formattedContent = mockContentLines.map(function (line) {
899
+ return "\t\t".concat(line, ",");
900
+ }).join('\n');
901
+ return "jest.mock(".concat(quote).concat(relativePath).concat(quote, ", () => {\n\t").concat(preambleText, "\n\treturn {\n").concat(formattedContent, "\n\t};\n})");
902
+ }
903
+
904
+ // Generate the mock (original logic for simple cases without preamble)
905
+ var mockCall;
906
+ if (defaultExportProps.length > 0 && namedExportProps.length === 0) {
907
+ // All props are from default export
908
+ if (defaultExportProps.length === 1) {
909
+ var _properties$get4;
910
+ // Single default export - use __esModule pattern
911
+ var mockText = ((_properties$get4 = properties.get(defaultExportProps[0])) === null || _properties$get4 === void 0 ? void 0 : _properties$get4.valueText) || '';
912
+ mockCall = "jest.mock(".concat(quote).concat(relativePath).concat(quote, ", () => ({\n\t__esModule: true,\n\tdefault: ").concat(mockText, "\n}))");
913
+ } else {
914
+ // Multiple props from same default - shouldn't happen, but handle it
915
+ var mockTexts = defaultExportProps.map(function (p) {
916
+ var _properties$get5;
917
+ return (_properties$get5 = properties.get(p)) === null || _properties$get5 === void 0 ? void 0 : _properties$get5.text;
918
+ }).join(',\n\t');
919
+ mockCall = "jest.mock(".concat(quote).concat(relativePath).concat(quote, ", () => ({\n\t").concat(mockTexts, "\n}))");
920
+ }
921
+ } else if (defaultExportProps.length === 0 && namedExportProps.length > 0) {
922
+ // All props are named exports
923
+ var mockObjectProps = namedExportProps.map(function (p) {
924
+ var _properties$get6;
925
+ return (_properties$get6 = properties.get(p)) === null || _properties$get6 === void 0 ? void 0 : _properties$get6.text;
926
+ }).filter(function (p) {
927
+ return !!p;
928
+ });
929
+ var _mockContentLines = [];
930
+ if (hasRequireActual) {
931
+ _mockContentLines.push("...jest.requireActual(".concat(quote).concat(relativePath).concat(quote, ")"));
932
+ }
933
+ _mockContentLines.push.apply(_mockContentLines, _toConsumableArray(mockObjectProps));
934
+ if (_mockContentLines.length === 1 && _mockContentLines[0].length < 60) {
935
+ mockCall = "jest.mock(".concat(quote).concat(relativePath).concat(quote, ", () => ({ ").concat(_mockContentLines[0], " }))");
936
+ } else {
937
+ var _formattedContent = _mockContentLines.map(function (line) {
938
+ return "\t".concat(line, ",");
939
+ }).join('\n');
940
+ mockCall = "jest.mock(".concat(quote).concat(relativePath).concat(quote, ", () => ({\n").concat(_formattedContent, "\n}))");
941
+ }
942
+ } else {
943
+ // Mixed: has both default and named exports
944
+ var defaultMock = defaultExportProps.map(function (p) {
945
+ var _properties$get7;
946
+ return (_properties$get7 = properties.get(p)) === null || _properties$get7 === void 0 ? void 0 : _properties$get7.valueText;
947
+ }).join(', ');
948
+ var namedMocks = namedExportProps.map(function (p) {
949
+ var _properties$get8;
950
+ return (_properties$get8 = properties.get(p)) === null || _properties$get8 === void 0 ? void 0 : _properties$get8.text;
951
+ }).filter(function (p) {
952
+ return !!p;
953
+ });
954
+ var _mockContentLines2 = ["__esModule: true", "default: ".concat(defaultMock)].concat(_toConsumableArray(namedMocks));
955
+ if (hasRequireActual) {
956
+ _mockContentLines2.unshift("...jest.requireActual(".concat(quote).concat(relativePath).concat(quote, ")"));
957
+ }
958
+ var _formattedContent2 = _mockContentLines2.map(function (line) {
959
+ return "\t".concat(line, ",");
960
+ }).join('\n');
961
+ mockCall = "jest.mock(".concat(quote).concat(relativePath).concat(quote, ", () => ({\n").concat(_formattedContent2, "\n}))");
962
+ }
963
+ return mockCall;
964
+ }
965
+
966
+ /**
967
+ * Generate auto-fix for mock with implementation
968
+ */
969
+ function generateMockImplementationFix(_ref16) {
970
+ var propertiesBySource = _ref16.propertiesBySource,
971
+ mockProperties = _ref16.mockProperties,
972
+ hasRequireActual = _ref16.hasRequireActual,
973
+ basedir = _ref16.basedir,
974
+ importPath = _ref16.importPath,
975
+ quote = _ref16.quote,
976
+ exportMap = _ref16.exportMap,
977
+ context = _ref16.context,
978
+ currentNode = _ref16.currentNode,
979
+ preamble = _ref16.preamble,
980
+ workspaceRoot = _ref16.workspaceRoot,
981
+ fs = _ref16.fs;
982
+ var sourceFilesToMock = Array.from(propertiesBySource.entries());
983
+
984
+ // Find all existing jest.mock calls in the file
985
+ var allExistingMocks = findAllJestMocksInFile({
986
+ context: context,
987
+ basedir: basedir,
988
+ fs: fs
989
+ });
990
+
991
+ // Track which nodes we need to remove and what mock calls to generate
992
+ var nodesToRemove = new Set();
993
+ var mergedMocks = new Map();
994
+
995
+ // Always remove the current barrel mock node
996
+ nodesToRemove.add(currentNode);
997
+
998
+ // Process each source file we're creating mocks for
999
+ var _loop2 = function _loop2() {
1000
+ var _sourceFilesToMock$_i = _slicedToArray(_sourceFilesToMock[_i5], 2),
1001
+ sourceFile = _sourceFilesToMock$_i[0],
1002
+ props = _sourceFilesToMock$_i[1];
1003
+ // Find the ExportInfo for this source file to get cross-package info
1004
+ var exportInfoForSource = Array.from(exportMap.values()).find(function (info) {
1005
+ return info.path === sourceFile;
1006
+ });
1007
+ var mockPath = getImportPathForSourceFile({
1008
+ sourceFilePath: sourceFile,
1009
+ basedir: basedir,
1010
+ originalImportPath: importPath,
1011
+ exportInfo: exportInfoForSource !== null && exportInfoForSource !== void 0 ? exportInfoForSource : null,
1012
+ workspaceRoot: workspaceRoot,
1013
+ fs: fs
1014
+ });
1015
+ var normalizedPath = normalizePathForComparison({
1016
+ basedir: basedir,
1017
+ importPath: mockPath,
1018
+ fs: fs
1019
+ });
1020
+
1021
+ // Get properties for this source file from the barrel mock
1022
+ var newProperties = new Map();
1023
+ var _iterator12 = _createForOfIteratorHelper(props),
1024
+ _step12;
1025
+ try {
1026
+ for (_iterator12.s(); !(_step12 = _iterator12.n()).done;) {
1027
+ var prop = _step12.value;
1028
+ var propInfo = mockProperties.get(prop);
1029
+ if (propInfo) {
1030
+ newProperties.set(prop, propInfo);
1031
+ }
1032
+ }
1033
+
1034
+ // Check if there's already a mock for this path
1035
+ } catch (err) {
1036
+ _iterator12.e(err);
1037
+ } finally {
1038
+ _iterator12.f();
1039
+ }
1040
+ var existingMock = allExistingMocks.get(normalizedPath);
1041
+ if (existingMock && existingMock.node !== currentNode) {
1042
+ // Merge properties from existing mock with new properties
1043
+ var mergedProperties = mergeMockProperties({
1044
+ existingProperties: existingMock.properties,
1045
+ newProperties: newProperties
1046
+ });
1047
+ mergedMocks.set(normalizedPath, {
1048
+ mockPath: mockPath,
1049
+ properties: mergedProperties,
1050
+ hasRequireActual: existingMock.hasRequireActual || hasRequireActual
1051
+ });
1052
+ // Mark the existing mock node for removal
1053
+ nodesToRemove.add(existingMock.node);
1054
+ } else {
1055
+ // No existing mock, just use the new properties
1056
+ // For newly created mocks from barrel file splits, always include jest.requireActual.
1057
+ // This ensures that any properties not explicitly mocked will still be included from the original module.
1058
+ mergedMocks.set(normalizedPath, {
1059
+ mockPath: mockPath,
1060
+ properties: newProperties,
1061
+ hasRequireActual: true
1062
+ });
1063
+ }
1064
+ };
1065
+ for (var _i5 = 0, _sourceFilesToMock = sourceFilesToMock; _i5 < _sourceFilesToMock.length; _i5++) {
1066
+ _loop2();
1067
+ }
1068
+
1069
+ // Generate mock calls for all merged mocks
1070
+ var replacementParts = [];
1071
+ var _iterator13 = _createForOfIteratorHelper(mergedMocks),
1072
+ _step13;
1073
+ try {
1074
+ for (_iterator13.s(); !(_step13 = _iterator13.n()).done;) {
1075
+ var _step13$value = _slicedToArray(_step13.value, 2),
1076
+ mockInfo = _step13$value[1];
1077
+ // Find the source file for this mock path (may be relative or cross-package)
1078
+ // For cross-package paths (starting with @), we don't need to resolve
1079
+ var isCrossPackagePath = mockInfo.mockPath.startsWith('@');
1080
+ var absolutePath = isCrossPackagePath ? null : resolveImportPath({
1081
+ basedir: basedir,
1082
+ importPath: mockInfo.mockPath,
1083
+ fs: fs
1084
+ });
1085
+ if (!isCrossPackagePath && !absolutePath) {
1086
+ continue;
1087
+ }
1088
+ var mockCall = generateMockCallText({
1089
+ relativePath: mockInfo.mockPath,
1090
+ properties: mockInfo.properties,
1091
+ hasRequireActual: mockInfo.hasRequireActual,
1092
+ quote: quote,
1093
+ exportMap: exportMap,
1094
+ sourceFile: absolutePath !== null && absolutePath !== void 0 ? absolutePath : mockInfo.mockPath,
1095
+ preamble: preamble,
1096
+ originalImportPath: importPath
1097
+ });
1098
+ replacementParts.push(mockCall);
1099
+ }
1100
+ } catch (err) {
1101
+ _iterator13.e(err);
1102
+ } finally {
1103
+ _iterator13.f();
1104
+ }
1105
+ var replacementText = replacementParts.join(';\n');
1106
+
1107
+ // Build a map of symbol name -> new mock path for jest.requireMock() rewriting
1108
+ var symbolToNewMockPath = new Map();
1109
+ var _iterator14 = _createForOfIteratorHelper(mergedMocks),
1110
+ _step14;
1111
+ try {
1112
+ for (_iterator14.s(); !(_step14 = _iterator14.n()).done;) {
1113
+ var _step14$value = _slicedToArray(_step14.value, 2),
1114
+ _mockInfo = _step14$value[1];
1115
+ var _iterator16 = _createForOfIteratorHelper(_mockInfo.properties.keys()),
1116
+ _step16;
1117
+ try {
1118
+ for (_iterator16.s(); !(_step16 = _iterator16.n()).done;) {
1119
+ var propName = _step16.value;
1120
+ symbolToNewMockPath.set(propName, _mockInfo.mockPath);
1121
+ }
1122
+ } catch (err) {
1123
+ _iterator16.e(err);
1124
+ } finally {
1125
+ _iterator16.f();
1126
+ }
1127
+ }
1128
+
1129
+ // Create fixes: remove all nodes except the first, replace the first with merged mocks
1130
+ } catch (err) {
1131
+ _iterator14.e(err);
1132
+ } finally {
1133
+ _iterator14.f();
1134
+ }
1135
+ var fixes = [];
1136
+ var sortedNodesToRemove = Array.from(nodesToRemove).sort(function (a, b) {
1137
+ var _a$range$, _a$range, _b$range$, _b$range;
1138
+ return ((_a$range$ = (_a$range = a.range) === null || _a$range === void 0 ? void 0 : _a$range[0]) !== null && _a$range$ !== void 0 ? _a$range$ : 0) - ((_b$range$ = (_b$range = b.range) === null || _b$range === void 0 ? void 0 : _b$range[0]) !== null && _b$range$ !== void 0 ? _b$range$ : 0);
1139
+ });
1140
+ if (sortedNodesToRemove.length > 0) {
1141
+ // Replace the first node with all the merged mocks
1142
+ var firstNode = sortedNodesToRemove[0];
1143
+ fixes.push({
1144
+ range: firstNode.range,
1145
+ text: replacementText
1146
+ });
1147
+
1148
+ // Remove all other nodes (subsequent duplicates)
1149
+ for (var i = 1; i < sortedNodesToRemove.length; i++) {
1150
+ var nodeToRemove = sortedNodesToRemove[i];
1151
+ // Find the statement that contains this node to remove the entire line
1152
+ var sourceCode = context.getSourceCode();
1153
+ var tokenAfter = sourceCode.getTokenAfter(nodeToRemove);
1154
+
1155
+ // Try to remove the entire statement including semicolon and newline
1156
+ var startPos = nodeToRemove.range[0];
1157
+ var endPos = nodeToRemove.range[1];
1158
+
1159
+ // Include trailing semicolon if present
1160
+ if (tokenAfter && tokenAfter.type === 'Punctuator' && tokenAfter.value === ';') {
1161
+ endPos = tokenAfter.range[1];
1162
+ }
1163
+
1164
+ // Include trailing/leading whitespace and newlines
1165
+ var text = sourceCode.getText();
1166
+ while (endPos < text.length && /[\s\n]/.test(text[endPos])) {
1167
+ endPos++;
1168
+ }
1169
+ fixes.push({
1170
+ range: [startPos, endPos],
1171
+ text: ''
1172
+ });
1173
+ }
1174
+ }
1175
+
1176
+ // Fix jest.requireMock() calls that reference the old barrel path.
1177
+ // When we split a jest.mock('./barrel') into jest.mock('./specific-file'),
1178
+ // any jest.requireMock('./barrel') calls also need to be updated.
1179
+ var ast = context.getSourceCode().ast;
1180
+ var normalizedTarget = normalizePathForComparison({
1181
+ basedir: basedir,
1182
+ importPath: importPath,
1183
+ fs: fs
1184
+ });
1185
+ var requireMockCalls = findJestRequireMockCalls({
1186
+ ast: ast,
1187
+ matchPath: function matchPath(candidatePath) {
1188
+ return normalizePathForComparison({
1189
+ basedir: basedir,
1190
+ importPath: candidatePath,
1191
+ fs: fs
1192
+ }) === normalizedTarget;
1193
+ }
1194
+ });
1195
+ var _iterator15 = _createForOfIteratorHelper(requireMockCalls),
1196
+ _step15;
1197
+ try {
1198
+ for (_iterator15.s(); !(_step15 = _iterator15.n()).done;) {
1199
+ var requireMockNode = _step15.value;
1200
+ var requireMockArg = requireMockNode.arguments[0];
1201
+ if (!requireMockArg) {
1202
+ continue;
1203
+ }
1204
+ var newPath = resolveNewPathForRequireMock({
1205
+ requireMockNode: requireMockNode,
1206
+ symbolToNewPath: symbolToNewMockPath
1207
+ });
1208
+ if (newPath) {
1209
+ fixes.push({
1210
+ range: requireMockArg.range,
1211
+ text: "".concat(quote).concat(newPath).concat(quote)
1212
+ });
1213
+ }
1214
+ }
1215
+ } catch (err) {
1216
+ _iterator15.e(err);
1217
+ } finally {
1218
+ _iterator15.f();
1219
+ }
1220
+ return fixes;
1221
+ }
1222
+
1223
+ /**
1224
+ * Metadata for the ESLint rule
1225
+ */
1226
+ var ruleMeta = {
1227
+ type: 'problem',
1228
+ docs: {
1229
+ description: 'Warn when jest.mock is used on a relative import path from a barrel file, and provide an auto-fix to split mocks by source file.',
1230
+ category: 'Best Practices',
1231
+ recommended: false
1232
+ },
1233
+ fixable: 'code',
1234
+ messages: {
1235
+ barrelMock: "jest.mock('{{path}}') is mocking a barrel file. This should be split into separate mocks for each source file to improve performance. Use auto-fix to resolve."
1236
+ }
1237
+ };
1238
+
1239
+ /**
1240
+ * Factory function to create the ESLint rule with a given file system.
1241
+ * This enables testing with mock file systems.
1242
+ */
1243
+ export function createRule(fs) {
1244
+ return {
1245
+ meta: ruleMeta,
1246
+ create: function create(context) {
1247
+ return {
1248
+ CallExpression: function CallExpression(rawNode) {
1249
+ var node = rawNode;
1250
+
1251
+ // Step 1: Validate this is a jest.mock call
1252
+ if (!isJestMockCall(node)) {
1253
+ return;
1254
+ }
1255
+
1256
+ // Step 2: Extract the import path
1257
+ var importPath = extractImportPath(node);
1258
+ if (!importPath) {
1259
+ return;
1260
+ }
1261
+
1262
+ // Step 3: Validate and resolve barrel file
1263
+ var basedir = dirname(context.filename);
1264
+ var workspaceRoot = findWorkspaceRoot({
1265
+ startPath: basedir,
1266
+ fs: fs
1267
+ });
1268
+ var barrelInfo = validateAndResolveBarrelFile({
1269
+ importPath: importPath,
1270
+ basedir: basedir,
1271
+ workspaceRoot: workspaceRoot,
1272
+ fs: fs
1273
+ });
1274
+ if (!barrelInfo) {
1275
+ return;
1276
+ }
1277
+ var exportMap = barrelInfo.exportMap,
1278
+ barrelFilePath = barrelInfo.resolvedPath;
1279
+ var sourceCode = context.getSourceCode();
1280
+ var firstArg = node.arguments[0];
1281
+
1282
+ // Step 4: Handle auto-mock case (no mock implementation)
1283
+ var mockImpl = node.arguments[1];
1284
+ if (!mockImpl) {
1285
+ // Group exports by source file, filtering out type-only source files
1286
+ var sourceFilesWithNonTypeExports = new Set();
1287
+ var _iterator17 = _createForOfIteratorHelper(exportMap),
1288
+ _step17;
1289
+ try {
1290
+ for (_iterator17.s(); !(_step17 = _iterator17.n()).done;) {
1291
+ var _step17$value = _slicedToArray(_step17.value, 2),
1292
+ info = _step17$value[1];
1293
+ if (!info.isTypeOnly) {
1294
+ sourceFilesWithNonTypeExports.add(info.path);
1295
+ }
1296
+ }
1297
+ } catch (err) {
1298
+ _iterator17.e(err);
1299
+ } finally {
1300
+ _iterator17.f();
1301
+ }
1302
+ if (sourceFilesWithNonTypeExports.size === 0) {
1303
+ return;
1304
+ }
1305
+ context.report({
1306
+ node: node,
1307
+ messageId: 'barrelMock',
1308
+ data: {
1309
+ path: importPath
1310
+ },
1311
+ fix: function fix(fixer) {
1312
+ var quote = sourceCode.getText(firstArg)[0];
1313
+ var replacement = generateAutoMockFix({
1314
+ exportMap: exportMap,
1315
+ basedir: basedir,
1316
+ importPath: importPath,
1317
+ quote: quote,
1318
+ workspaceRoot: workspaceRoot,
1319
+ fs: fs
1320
+ });
1321
+ return fixer.replaceText(node, replacement);
1322
+ }
1323
+ });
1324
+ return;
1325
+ }
1326
+
1327
+ // Step 5: Extract mock implementation and properties
1328
+ var mockObjectNode = extractMockImplementation(mockImpl);
1329
+ var _extractMockPropertie2 = extractMockProperties({
1330
+ sourceCode: sourceCode,
1331
+ mockObjectNode: mockObjectNode
1332
+ }),
1333
+ mockProperties = _extractMockPropertie2.properties,
1334
+ hasRequireActual = _extractMockPropertie2.hasRequireActual;
1335
+
1336
+ // Extract preamble (variable declarations, etc.) from the mock factory
1337
+ var preamble = extractMockFactoryPreamble({
1338
+ mockImpl: mockImpl,
1339
+ sourceCode: sourceCode
1340
+ });
1341
+ if (mockProperties.size === 0) {
1342
+ return;
1343
+ }
1344
+
1345
+ // Step 6: Group properties by their source files
1346
+ var propertiesBySource = groupPropertiesBySource({
1347
+ mockProperties: mockProperties,
1348
+ exportMap: exportMap
1349
+ });
1350
+
1351
+ // Step 7: Determine if we should report
1352
+ if (!shouldReportBarrelMock({
1353
+ propertiesBySource: propertiesBySource,
1354
+ barrelFilePath: barrelFilePath
1355
+ })) {
1356
+ return;
1357
+ }
1358
+
1359
+ // Step 8: Report with auto-fix
1360
+ context.report({
1361
+ node: node,
1362
+ messageId: 'barrelMock',
1363
+ data: {
1364
+ path: importPath
1365
+ },
1366
+ fix: function fix(_fixer) {
1367
+ var quote = sourceCode.getText(firstArg)[0];
1368
+ var fixes = generateMockImplementationFix({
1369
+ propertiesBySource: propertiesBySource,
1370
+ mockProperties: mockProperties,
1371
+ hasRequireActual: hasRequireActual,
1372
+ basedir: basedir,
1373
+ importPath: importPath,
1374
+ quote: quote,
1375
+ exportMap: exportMap,
1376
+ context: context,
1377
+ currentNode: node,
1378
+ preamble: preamble,
1379
+ workspaceRoot: workspaceRoot,
1380
+ fs: fs
1381
+ });
1382
+ return fixes;
1383
+ }
1384
+ });
1385
+ }
1386
+ };
1387
+ }
1388
+ };
1389
+ }
1390
+ var rule = createRule(realFileSystem);
1391
+ export default rule;