@atlaskit/eslint-plugin-platform 2.7.2 → 2.9.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 (85) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/dist/cjs/index.js +19 -3
  3. package/dist/cjs/rules/ensure-use-sync-external-store-server-snapshot/index.js +41 -0
  4. package/dist/cjs/rules/feature-gating/valid-gate-name/index.js +60 -0
  5. package/dist/cjs/rules/import/no-barrel-entry-imports/index.js +1279 -0
  6. package/dist/cjs/rules/import/no-barrel-entry-jest-mock/index.js +1659 -0
  7. package/dist/cjs/rules/import/no-conversation-assistant-barrel-imports/index.js +43 -0
  8. package/dist/cjs/rules/import/no-jest-mock-barrel-files/index.js +1402 -0
  9. package/dist/cjs/rules/import/no-relative-barrel-file-imports/index.js +781 -0
  10. package/dist/cjs/rules/import/shared/barrel-parsing.js +511 -0
  11. package/dist/cjs/rules/import/shared/file-system.js +186 -0
  12. package/dist/cjs/rules/import/shared/jest-utils.js +244 -0
  13. package/dist/cjs/rules/import/shared/package-registry.js +263 -0
  14. package/dist/cjs/rules/import/shared/package-resolution.js +318 -0
  15. package/dist/cjs/rules/import/shared/perf.js +89 -0
  16. package/dist/cjs/rules/import/shared/types.js +67 -0
  17. package/dist/cjs/rules/visit-example-type-import-required/index.js +409 -0
  18. package/dist/es2019/index.js +19 -3
  19. package/dist/es2019/rules/ensure-use-sync-external-store-server-snapshot/index.js +43 -0
  20. package/dist/es2019/rules/feature-gating/valid-gate-name/index.js +52 -0
  21. package/dist/es2019/rules/import/no-barrel-entry-imports/index.js +1158 -0
  22. package/dist/es2019/rules/import/no-barrel-entry-jest-mock/index.js +1341 -0
  23. package/dist/es2019/rules/import/no-conversation-assistant-barrel-imports/index.js +37 -0
  24. package/dist/es2019/rules/import/no-jest-mock-barrel-files/index.js +1180 -0
  25. package/dist/es2019/rules/import/no-relative-barrel-file-imports/index.js +742 -0
  26. package/dist/es2019/rules/import/shared/barrel-parsing.js +433 -0
  27. package/dist/es2019/rules/import/shared/file-system.js +174 -0
  28. package/dist/es2019/rules/import/shared/jest-utils.js +203 -0
  29. package/dist/es2019/rules/import/shared/package-registry.js +240 -0
  30. package/dist/es2019/rules/import/shared/package-resolution.js +253 -0
  31. package/dist/es2019/rules/import/shared/perf.js +83 -0
  32. package/dist/es2019/rules/import/shared/types.js +57 -0
  33. package/dist/es2019/rules/visit-example-type-import-required/index.js +375 -0
  34. package/dist/esm/index.js +19 -3
  35. package/dist/esm/rules/ensure-use-sync-external-store-server-snapshot/index.js +35 -0
  36. package/dist/esm/rules/feature-gating/valid-gate-name/index.js +53 -0
  37. package/dist/esm/rules/import/no-barrel-entry-imports/index.js +1272 -0
  38. package/dist/esm/rules/import/no-barrel-entry-jest-mock/index.js +1650 -0
  39. package/dist/esm/rules/import/no-conversation-assistant-barrel-imports/index.js +37 -0
  40. package/dist/esm/rules/import/no-jest-mock-barrel-files/index.js +1392 -0
  41. package/dist/esm/rules/import/no-relative-barrel-file-imports/index.js +774 -0
  42. package/dist/esm/rules/import/shared/barrel-parsing.js +500 -0
  43. package/dist/esm/rules/import/shared/file-system.js +176 -0
  44. package/dist/esm/rules/import/shared/jest-utils.js +231 -0
  45. package/dist/esm/rules/import/shared/package-registry.js +256 -0
  46. package/dist/esm/rules/import/shared/package-resolution.js +306 -0
  47. package/dist/esm/rules/import/shared/perf.js +80 -0
  48. package/dist/esm/rules/import/shared/types.js +61 -0
  49. package/dist/esm/rules/visit-example-type-import-required/index.js +402 -0
  50. package/dist/types/index.d.ts +28 -2
  51. package/dist/types/rules/ensure-use-sync-external-store-server-snapshot/index.d.ts +3 -0
  52. package/dist/types/rules/feature-gating/valid-gate-name/index.d.ts +3 -0
  53. package/dist/types/rules/import/no-barrel-entry-imports/index.d.ts +9 -0
  54. package/dist/types/rules/import/no-barrel-entry-jest-mock/index.d.ts +9 -0
  55. package/dist/types/rules/import/no-conversation-assistant-barrel-imports/index.d.ts +3 -0
  56. package/dist/types/rules/import/no-jest-mock-barrel-files/index.d.ts +22 -0
  57. package/dist/types/rules/import/no-relative-barrel-file-imports/index.d.ts +5 -0
  58. package/dist/types/rules/import/shared/barrel-parsing.d.ts +30 -0
  59. package/dist/types/rules/import/shared/file-system.d.ts +38 -0
  60. package/dist/types/rules/import/shared/jest-utils.d.ts +55 -0
  61. package/dist/types/rules/import/shared/package-registry.d.ts +26 -0
  62. package/dist/types/rules/import/shared/package-resolution.d.ts +58 -0
  63. package/dist/types/rules/import/shared/perf.d.ts +13 -0
  64. package/dist/types/rules/import/shared/types.d.ts +131 -0
  65. package/dist/types/rules/visit-example-type-import-required/index.d.ts +4 -0
  66. package/dist/types-ts4.5/index.d.ts +28 -2
  67. package/dist/types-ts4.5/rules/import/no-barrel-entry-imports/index.d.ts +9 -0
  68. package/dist/types-ts4.5/rules/import/no-barrel-entry-jest-mock/index.d.ts +9 -0
  69. package/dist/types-ts4.5/rules/import/no-conversation-assistant-barrel-imports/index.d.ts +3 -0
  70. package/dist/types-ts4.5/rules/import/no-jest-mock-barrel-files/index.d.ts +22 -0
  71. package/dist/types-ts4.5/rules/import/no-relative-barrel-file-imports/index.d.ts +5 -0
  72. package/dist/types-ts4.5/rules/import/shared/barrel-parsing.d.ts +30 -0
  73. package/dist/types-ts4.5/rules/import/shared/file-system.d.ts +38 -0
  74. package/dist/types-ts4.5/rules/import/shared/jest-utils.d.ts +55 -0
  75. package/dist/types-ts4.5/rules/import/shared/package-registry.d.ts +26 -0
  76. package/dist/types-ts4.5/rules/import/shared/package-resolution.d.ts +58 -0
  77. package/dist/types-ts4.5/rules/import/shared/perf.d.ts +13 -0
  78. package/dist/types-ts4.5/rules/import/shared/types.d.ts +131 -0
  79. package/dist/types-ts4.5/rules/visit-example-type-import-required/index.d.ts +4 -0
  80. package/package.json +6 -2
  81. package/dist/cjs/rules/ensure-native-and-af-exports-synced/index.js +0 -158
  82. package/dist/es2019/rules/ensure-native-and-af-exports-synced/index.js +0 -146
  83. package/dist/esm/rules/ensure-native-and-af-exports-synced/index.js +0 -151
  84. /package/dist/types-ts4.5/rules/{ensure-native-and-af-exports-synced → ensure-use-sync-external-store-server-snapshot}/index.d.ts +0 -0
  85. /package/dist/{types/rules/ensure-native-and-af-exports-synced → types-ts4.5/rules/feature-gating/valid-gate-name}/index.d.ts +0 -0
@@ -0,0 +1,1158 @@
1
+ import { dirname } from 'path';
2
+ import { parseBarrelExports } from '../shared/barrel-parsing';
3
+ import { DEFAULT_TARGET_FOLDERS, findWorkspaceRoot, isRelativeImport } from '../shared/file-system';
4
+ import { findPackageInRegistry, isPackageInApplyToImportsFrom } from '../shared/package-registry';
5
+ import { findExportForSourceFile, parsePackageExports } from '../shared/package-resolution';
6
+ import { realFileSystem } from '../shared/types';
7
+
8
+ /**
9
+ * Options for the no-barrel-entry-imports rule.
10
+ */
11
+
12
+ /**
13
+ * Represents a Jest automock call: jest.mock('path') with no additional arguments
14
+ */
15
+
16
+ /**
17
+ * Metadata for the ESLint rule
18
+ */
19
+ const ruleMeta = {
20
+ type: 'problem',
21
+ docs: {
22
+ description: 'Disallow importing from barrel files in entry points.',
23
+ category: 'Best Practices',
24
+ recommended: false
25
+ },
26
+ fixable: 'code',
27
+ schema: [{
28
+ type: 'object',
29
+ properties: {
30
+ applyToImportsFrom: {
31
+ type: 'array',
32
+ items: {
33
+ type: 'string'
34
+ },
35
+ description: 'The folder paths (relative to workspace root) containing packages whose imports will be checked and autofixed.'
36
+ }
37
+ },
38
+ additionalProperties: false
39
+ }],
40
+ messages: {
41
+ barrelEntryImport: "Importing from barrel file '{{path}}' is not allowed. Import directly from the source file using a more specific package.json export instead."
42
+ }
43
+ };
44
+
45
+ /**
46
+ * Get the imported name from an ImportSpecifier, handling both Identifier and Literal
47
+ */
48
+ function getImportedName(spec) {
49
+ const imported = spec.imported;
50
+ return imported.type === 'Identifier' ? imported.name : String(imported.value);
51
+ }
52
+
53
+ /**
54
+ * Build an import statement for a set of specifiers
55
+ */
56
+ function buildImportStatement({
57
+ specs,
58
+ path,
59
+ quoteChar,
60
+ isTypeImport = false
61
+ }) {
62
+ const importNames = specs.map(spec => {
63
+ if (spec.type === 'ImportDefaultSpecifier') {
64
+ return spec.local.name;
65
+ } else if (spec.type === 'ImportSpecifier') {
66
+ const imported = getImportedName(spec);
67
+ const local = spec.local.name;
68
+ const isInlineType = spec.importKind === 'type' && !isTypeImport;
69
+ const prefix = isInlineType ? 'type ' : '';
70
+ return imported === local ? `${prefix}${imported}` : `${prefix}${imported} as ${local}`;
71
+ }
72
+ return '';
73
+ }).filter(name => name.length > 0);
74
+ if (importNames.length === 0) {
75
+ return '';
76
+ }
77
+ const typeKeyword = isTypeImport ? 'type ' : '';
78
+ const hasDefault = specs.some(spec => spec.type === 'ImportDefaultSpecifier');
79
+ const hasNamed = specs.some(spec => spec.type === 'ImportSpecifier');
80
+ if (hasDefault && hasNamed) {
81
+ var _specs$find;
82
+ const defaultName = (_specs$find = specs.find(spec => spec.type === 'ImportDefaultSpecifier')) === null || _specs$find === void 0 ? void 0 : _specs$find.local.name;
83
+ const namedImports = specs.filter(spec => spec.type === 'ImportSpecifier').map(spec => {
84
+ const imported = getImportedName(spec);
85
+ const local = spec.local.name;
86
+ const isInlineType = spec.importKind === 'type' && !isTypeImport;
87
+ const prefix = isInlineType ? 'type ' : '';
88
+ return imported === local ? `${prefix}${imported}` : `${prefix}${imported} as ${local}`;
89
+ }).join(', ');
90
+ return `import ${typeKeyword}${defaultName}, { ${namedImports} } from ${quoteChar}${path}${quoteChar};`;
91
+ } else if (hasDefault) {
92
+ var _specs$find2;
93
+ const defaultName = (_specs$find2 = specs.find(spec => spec.type === 'ImportDefaultSpecifier')) === null || _specs$find2 === void 0 ? void 0 : _specs$find2.local.name;
94
+ return `import ${typeKeyword}${defaultName} from ${quoteChar}${path}${quoteChar};`;
95
+ } else {
96
+ return `import ${typeKeyword}{ ${importNames.join(', ')} } from ${quoteChar}${path}${quoteChar};`;
97
+ }
98
+ }
99
+
100
+ /**
101
+ * Represents a specifier with its resolved target export path information.
102
+ */
103
+
104
+ /**
105
+ * Context resolved for an import that may be a barrel import.
106
+ */
107
+
108
+ /**
109
+ * Result of classifying specifiers by their target export paths.
110
+ */
111
+
112
+ /**
113
+ * Resolves import context for barrel file analysis from a module specifier string.
114
+ * Returns null if the import should not be processed (relative import, not in target folder, etc.)
115
+ */
116
+ function resolveImportContextFromModulePath({
117
+ importPath,
118
+ workspaceRoot,
119
+ fs,
120
+ applyToImportsFrom
121
+ }) {
122
+ // Skip relative imports - this rule is for cross-package imports
123
+ if (isRelativeImport(importPath)) {
124
+ return null;
125
+ }
126
+
127
+ // Extract the base package name (without subpath)
128
+ // e.g., "@atlassian/conversation-assistant-instrumentation" from
129
+ // "@atlassian/conversation-assistant-instrumentation" or
130
+ // "@atlassian/conversation-assistant-instrumentation/controllers/analytics"
131
+ const packageNameMatch = importPath.match(/^(@[^/]+\/[^/]+)/);
132
+ if (!packageNameMatch) {
133
+ return null;
134
+ }
135
+ const packageName = packageNameMatch[1];
136
+ const subPath = importPath.slice(packageName.length); // e.g., "" or "/controllers/analytics"
137
+
138
+ // Find the package (resolution is not constrained by applyToImportsFrom)
139
+ const packageDir = findPackageInRegistry({
140
+ packageName,
141
+ workspaceRoot,
142
+ fs
143
+ });
144
+ if (!packageDir) {
145
+ return null;
146
+ }
147
+
148
+ // Only check imports from packages in our applyToImportsFrom folders
149
+ if (!isPackageInApplyToImportsFrom({
150
+ packageDir,
151
+ workspaceRoot,
152
+ applyToImportsFrom
153
+ })) {
154
+ return null;
155
+ }
156
+
157
+ // Get the exports map for this package
158
+ const exportsMap = parsePackageExports({
159
+ packageDir,
160
+ fs
161
+ });
162
+ if (exportsMap.size === 0) {
163
+ return null;
164
+ }
165
+
166
+ // Determine which export path we're importing from
167
+ // For bare package imports, it's ".", for subpath imports it's "./" + subPath
168
+ const currentExportPath = subPath ? '.' + subPath : '.';
169
+
170
+ // Get the resolved path for the current export (the entry point file for this import)
171
+ const entryFilePath = exportsMap.get(currentExportPath);
172
+ if (!entryFilePath) {
173
+ return null;
174
+ }
175
+
176
+ // Parse the entry file to find where each export originates
177
+ // Pass workspaceRoot to enable cross-package re-export resolution
178
+ const exportMap = parseBarrelExports({
179
+ barrelFilePath: entryFilePath,
180
+ fs,
181
+ workspaceRoot
182
+ });
183
+ if (exportMap.size === 0) {
184
+ return null;
185
+ }
186
+ return {
187
+ importPath,
188
+ packageName,
189
+ currentExportPath,
190
+ exportsMap,
191
+ exportMap
192
+ };
193
+ }
194
+
195
+ /**
196
+ * Resolves import context for barrel file analysis.
197
+ * Returns null if the import should not be processed (relative import, not in target folder, etc.)
198
+ */
199
+ function resolveImportContext({
200
+ node,
201
+ workspaceRoot,
202
+ fs,
203
+ applyToImportsFrom
204
+ }) {
205
+ if (!node.source || typeof node.source.value !== 'string') {
206
+ return null;
207
+ }
208
+ return resolveImportContextFromModulePath({
209
+ importPath: node.source.value,
210
+ workspaceRoot,
211
+ fs,
212
+ applyToImportsFrom
213
+ });
214
+ }
215
+
216
+ /**
217
+ * Classifies import specifiers by their target export paths.
218
+ * Groups specifiers that can be remapped to more specific exports.
219
+ * For cross-package re-exports, suggests importing from the source package's most specific subpath.
220
+ */
221
+ function classifySpecifiers({
222
+ node,
223
+ importContext,
224
+ workspaceRoot,
225
+ fs
226
+ }) {
227
+ const {
228
+ currentExportPath,
229
+ exportsMap,
230
+ exportMap
231
+ } = importContext;
232
+ const specifiers = node.specifiers;
233
+ const specifiersByTarget = new Map();
234
+ const unmappedSpecifiers = [];
235
+ let hasNamespaceImport = false;
236
+
237
+ // Cache for source package exports maps to avoid redundant parsing
238
+ const sourcePackageExportsMaps = new Map();
239
+ for (const spec of specifiers) {
240
+ if (spec.type === 'ImportNamespaceSpecifier') {
241
+ hasNamespaceImport = true;
242
+ continue;
243
+ }
244
+ let nameInSource;
245
+ let kind = 'value';
246
+ if (spec.type === 'ImportDefaultSpecifier') {
247
+ nameInSource = 'default';
248
+ } else if (spec.type === 'ImportSpecifier') {
249
+ nameInSource = getImportedName(spec);
250
+ const parentImportKind = node.importKind;
251
+ kind = parentImportKind === 'type' || spec.importKind === 'type' ? 'type' : 'value';
252
+ } else {
253
+ continue;
254
+ }
255
+ const exportInfo = exportMap.get(nameInSource);
256
+ if (exportInfo) {
257
+ var _exportInfo$crossPack, _exportInfo$originalN2, _matchResult$exportPa2;
258
+ const effectiveKind = kind === 'type' || exportInfo.isTypeOnly ? 'type' : 'value';
259
+
260
+ // Check if this is a cross-package re-export
261
+ const sourcePackageName = (_exportInfo$crossPack = exportInfo.crossPackageSource) === null || _exportInfo$crossPack === void 0 ? void 0 : _exportInfo$crossPack.packageName;
262
+ if (sourcePackageName) {
263
+ // For cross-package re-exports, find the most specific subpath in the source package
264
+ // Note: Package resolution is not constrained by applyToImportsFrom - any package can be resolved
265
+ let sourcePackageExportsMap = sourcePackageExportsMaps.get(sourcePackageName);
266
+ if (!sourcePackageExportsMap) {
267
+ const sourcePackageDir = findPackageInRegistry({
268
+ packageName: sourcePackageName,
269
+ workspaceRoot,
270
+ fs
271
+ });
272
+ if (sourcePackageDir) {
273
+ sourcePackageExportsMap = parsePackageExports({
274
+ packageDir: sourcePackageDir,
275
+ fs
276
+ });
277
+ sourcePackageExportsMaps.set(sourcePackageName, sourcePackageExportsMap);
278
+ }
279
+ }
280
+
281
+ // Find the best export path in the source package
282
+ let targetExportPath = null;
283
+ let resolvedOriginalName = exportInfo.originalName;
284
+ if (sourcePackageExportsMap) {
285
+ var _exportInfo$originalN, _matchResult$exportPa;
286
+ const sourceExportName = (_exportInfo$originalN = exportInfo.originalName) !== null && _exportInfo$originalN !== void 0 ? _exportInfo$originalN : nameInSource;
287
+ const matchResult = findExportForSourceFile({
288
+ sourceFilePath: exportInfo.path,
289
+ exportsMap: sourcePackageExportsMap,
290
+ fs,
291
+ sourceExportName
292
+ });
293
+ targetExportPath = (_matchResult$exportPa = matchResult === null || matchResult === void 0 ? void 0 : matchResult.exportPath) !== null && _matchResult$exportPa !== void 0 ? _matchResult$exportPa : null;
294
+ if ((matchResult === null || matchResult === void 0 ? void 0 : matchResult.entryPointExportName) !== undefined) {
295
+ resolvedOriginalName = matchResult.entryPointExportName === nameInSource ? undefined : matchResult.entryPointExportName;
296
+ }
297
+ }
298
+
299
+ // Build the full import path: @package/subpath or just @package if no subpath found
300
+ const targetKey = targetExportPath ? sourcePackageName + targetExportPath.slice(1) // Remove leading '.' from subpath
301
+ : sourcePackageName;
302
+ if (!specifiersByTarget.has(targetKey)) {
303
+ specifiersByTarget.set(targetKey, []);
304
+ }
305
+ specifiersByTarget.get(targetKey).push({
306
+ spec: {
307
+ ...spec,
308
+ importKind: effectiveKind
309
+ },
310
+ originalName: resolvedOriginalName,
311
+ targetExportPath: targetKey,
312
+ kind: effectiveKind,
313
+ sourcePackageName
314
+ });
315
+ continue;
316
+ }
317
+
318
+ // Find if there's a package.json export that points to this source file
319
+ const sourceExportName = (_exportInfo$originalN2 = exportInfo.originalName) !== null && _exportInfo$originalN2 !== void 0 ? _exportInfo$originalN2 : nameInSource;
320
+ const matchResult = findExportForSourceFile({
321
+ sourceFilePath: exportInfo.path,
322
+ exportsMap,
323
+ fs,
324
+ sourceExportName
325
+ });
326
+ const targetExportPath = (_matchResult$exportPa2 = matchResult === null || matchResult === void 0 ? void 0 : matchResult.exportPath) !== null && _matchResult$exportPa2 !== void 0 ? _matchResult$exportPa2 : null;
327
+ let resolvedOriginalName2 = exportInfo.originalName;
328
+ if ((matchResult === null || matchResult === void 0 ? void 0 : matchResult.entryPointExportName) !== undefined) {
329
+ resolvedOriginalName2 = matchResult.entryPointExportName === nameInSource ? undefined : matchResult.entryPointExportName;
330
+ }
331
+
332
+ // Get the file that the current export path resolves to
333
+ const currentExportResolvedFile = exportsMap.get(currentExportPath);
334
+
335
+ // Skip if:
336
+ // 1. No target export path found
337
+ // 2. Target is same as current (no change needed)
338
+ // 3. Current export path already resolves to the same file as the source
339
+ // (handles multiple exports pointing to same file - avoid no-op changes)
340
+ const currentExportAlreadyPointsToSourceFile = currentExportResolvedFile !== undefined && currentExportResolvedFile === exportInfo.path;
341
+ if (targetExportPath && targetExportPath !== currentExportPath && !currentExportAlreadyPointsToSourceFile) {
342
+ if (!specifiersByTarget.has(targetExportPath)) {
343
+ specifiersByTarget.set(targetExportPath, []);
344
+ }
345
+ specifiersByTarget.get(targetExportPath).push({
346
+ spec: {
347
+ ...spec,
348
+ importKind: effectiveKind
349
+ },
350
+ originalName: resolvedOriginalName2,
351
+ targetExportPath,
352
+ kind: effectiveKind
353
+ });
354
+ } else {
355
+ // No more specific export available
356
+ unmappedSpecifiers.push({
357
+ spec: spec,
358
+ targetExportPath: null,
359
+ kind
360
+ });
361
+ }
362
+ } else {
363
+ unmappedSpecifiers.push({
364
+ spec: spec,
365
+ targetExportPath: null,
366
+ kind
367
+ });
368
+ }
369
+ }
370
+ return {
371
+ specifiersByTarget,
372
+ unmappedSpecifiers,
373
+ hasNamespaceImport
374
+ };
375
+ }
376
+
377
+ /**
378
+ * Transforms a specifier to use the original export name (handling aliasing).
379
+ * Converts named imports of default exports to ImportDefaultSpecifier.
380
+ */
381
+ function transformSpecifierForExport({
382
+ spec,
383
+ originalName,
384
+ kind
385
+ }) {
386
+ if (!originalName) {
387
+ return spec;
388
+ }
389
+ if (originalName === 'default') {
390
+ // Should be ImportDefaultSpecifier
391
+ if (spec.type === 'ImportDefaultSpecifier') {
392
+ return spec;
393
+ }
394
+ // Convert ImportSpecifier to ImportDefaultSpecifier
395
+ return {
396
+ type: 'ImportDefaultSpecifier',
397
+ local: spec.local,
398
+ range: spec.range,
399
+ loc: spec.loc,
400
+ parent: spec.parent
401
+ };
402
+ } else {
403
+ // Create synthetic ImportSpecifier with correct importKind
404
+ return {
405
+ type: 'ImportSpecifier',
406
+ local: spec.local,
407
+ imported: {
408
+ type: 'Identifier',
409
+ name: originalName,
410
+ range: [0, 0],
411
+ loc: {
412
+ start: {
413
+ line: 0,
414
+ column: 0
415
+ },
416
+ end: {
417
+ line: 0,
418
+ column: 0
419
+ }
420
+ }
421
+ },
422
+ importKind: kind,
423
+ range: spec.range,
424
+ loc: spec.loc,
425
+ parent: spec.parent
426
+ };
427
+ }
428
+ }
429
+
430
+ /**
431
+ * Merges new specifiers with an existing import declaration.
432
+ * Returns the new import statement string.
433
+ */
434
+ function buildMergedImportStatement({
435
+ existingImport,
436
+ newSpecs,
437
+ newImportPath,
438
+ nodeImportKind,
439
+ quoteChar
440
+ }) {
441
+ const existingSpecs = existingImport.specifiers.map(s => {
442
+ if (existingImport.importKind === 'type') {
443
+ return {
444
+ ...s,
445
+ importKind: 'type'
446
+ };
447
+ }
448
+ return s;
449
+ });
450
+ const augmentedNewSpecs = newSpecs.map(s => {
451
+ if (nodeImportKind === 'type') {
452
+ return {
453
+ ...s,
454
+ importKind: 'type'
455
+ };
456
+ }
457
+ return s;
458
+ });
459
+ const mergedSpecs = [...existingSpecs, ...augmentedNewSpecs];
460
+
461
+ // Determine if we should use 'import type'
462
+ const allType = mergedSpecs.every(s => s.importKind === 'type');
463
+ return buildImportStatement({
464
+ specs: mergedSpecs,
465
+ path: newImportPath,
466
+ quoteChar,
467
+ isTypeImport: allType
468
+ });
469
+ }
470
+
471
+ /**
472
+ * Check if an ExpressionStatement is a Jest automock: jest.mock('path') with exactly one string argument.
473
+ * Returns the JestAutomock info if it is, null otherwise.
474
+ */
475
+ function getJestAutomock(node) {
476
+ if (node.type !== 'ExpressionStatement') {
477
+ return null;
478
+ }
479
+ const statement = node;
480
+ const expr = statement.expression;
481
+ if (expr.type !== 'CallExpression') {
482
+ return null;
483
+ }
484
+
485
+ // Check for jest.mock(...)
486
+ const callee = expr.callee;
487
+ if (callee.type !== 'MemberExpression' || callee.object.type !== 'Identifier' || callee.object.name !== 'jest' || callee.property.type !== 'Identifier' || callee.property.name !== 'mock') {
488
+ return null;
489
+ }
490
+
491
+ // Must have exactly one argument (automock = no factory function)
492
+ if (expr.arguments.length !== 1) {
493
+ return null;
494
+ }
495
+ const arg = expr.arguments[0];
496
+ if (arg.type !== 'Literal' || typeof arg.value !== 'string') {
497
+ return null;
498
+ }
499
+
500
+ // Get the quote character from the raw value
501
+ const raw = arg.raw || `'${arg.value}'`;
502
+ const quoteChar = raw[0];
503
+ return {
504
+ statementNode: statement,
505
+ path: arg.value,
506
+ quoteChar
507
+ };
508
+ }
509
+
510
+ /**
511
+ * Find all Jest automocks in the AST that match the given import path.
512
+ */
513
+ function findMatchingAutomocks({
514
+ sourceCode,
515
+ importPath
516
+ }) {
517
+ const automocks = [];
518
+ const ast = sourceCode.ast;
519
+ for (const statement of ast.body) {
520
+ const automock = getJestAutomock(statement);
521
+ if (automock && automock.path === importPath) {
522
+ automocks.push(automock);
523
+ }
524
+ }
525
+ return automocks;
526
+ }
527
+
528
+ /**
529
+ * Build a jest.mock() statement string
530
+ */
531
+ function buildAutomockStatement({
532
+ path,
533
+ quoteChar
534
+ }) {
535
+ return `jest.mock(${quoteChar}${path}${quoteChar});`;
536
+ }
537
+
538
+ /**
539
+ * Creates a fix to remove a node with proper whitespace handling.
540
+ * Removes surrounding newlines to avoid leaving blank lines.
541
+ */
542
+ function createNodeRemovalFix({
543
+ fixer,
544
+ node,
545
+ sourceCode
546
+ }) {
547
+ const nodeStart = node.range[0];
548
+ const nodeEnd = node.range[1];
549
+
550
+ // Check for leading newline (prefer removing the line separator before the node)
551
+ const textBeforeNode = sourceCode.text.slice(0, nodeStart);
552
+ const leadingNewlineMatch = textBeforeNode.match(/(\r?\n)$/);
553
+ if (leadingNewlineMatch) {
554
+ // Remove the leading newline plus the node
555
+ return fixer.removeRange([nodeStart - leadingNewlineMatch[1].length, nodeEnd]);
556
+ }
557
+
558
+ // No leading newline - check for trailing newline
559
+ const textAfterNode = sourceCode.text.slice(nodeEnd);
560
+ const trailingNewlineMatch = textAfterNode.match(/^(\r?\n)/);
561
+ if (trailingNewlineMatch) {
562
+ return fixer.removeRange([nodeStart, nodeEnd + trailingNewlineMatch[1].length]);
563
+ }
564
+ return fixer.remove(node);
565
+ }
566
+
567
+ /**
568
+ * Creates the auto-fix for barrel import violations.
569
+ * Generates new import statements and handles merging with existing imports.
570
+ * Also updates Jest automocks (jest.mock calls with only a path) when present.
571
+ */
572
+ function createBarrelImportFix({
573
+ fixer,
574
+ node,
575
+ context,
576
+ importContext,
577
+ specifiersByTarget,
578
+ unmappedSpecifiers
579
+ }) {
580
+ const {
581
+ importPath,
582
+ packageName
583
+ } = importContext;
584
+ const sourceCode = context.sourceCode;
585
+ const quote = sourceCode.getText(node.source)[0]; // Get quote character
586
+
587
+ const fixes = [];
588
+ const newStatements = [];
589
+
590
+ // Find any Jest automocks that match this import path
591
+ const automocks = findMatchingAutomocks({
592
+ sourceCode,
593
+ importPath
594
+ });
595
+
596
+ // Track which new import paths need automocks (only value imports, not type-only)
597
+ const automockPaths = [];
598
+
599
+ // Track if we have any value imports at all (to determine if automocks should be updated)
600
+ let hasAnyValueImports = false;
601
+
602
+ // Get all existing imports to check for merging
603
+ const allImports = sourceCode.ast.body.filter(n => n.type === 'ImportDeclaration' && n !== node);
604
+
605
+ // Generate new import statements for each target export path
606
+ for (const [targetExportPath, specsWithTarget] of specifiersByTarget) {
607
+ // Check if this is a cross-package re-export (sourcePackageName is set)
608
+ const isCrossPackage = specsWithTarget.some(s => s.sourcePackageName);
609
+ const newImportPath = isCrossPackage ? targetExportPath // For cross-package, targetExportPath is already the full import path (e.g., @package/subpath)
610
+ : packageName + targetExportPath.slice(1); // Remove leading '.' for same-package imports
611
+
612
+ // Transform specifiers if needed (handle aliasing)
613
+ const specs = specsWithTarget.map(({
614
+ spec,
615
+ originalName,
616
+ kind
617
+ }) => transformSpecifierForExport({
618
+ spec,
619
+ originalName,
620
+ kind
621
+ }));
622
+
623
+ // Check if any specifier in this group is a value import (not type-only)
624
+ // Only add automock paths for value imports (types don't need mocking at runtime)
625
+ if (automocks.length > 0) {
626
+ const hasValueImport = specsWithTarget.some(({
627
+ kind,
628
+ spec
629
+ }) => kind === 'value' && (spec.type !== 'ImportSpecifier' || spec.importKind !== 'type'));
630
+ if (hasValueImport) {
631
+ hasAnyValueImports = true;
632
+ automockPaths.push(newImportPath);
633
+ }
634
+ }
635
+
636
+ // Check for existing import from the same path
637
+ const existingImport = allImports.find(n => n.source.value === newImportPath);
638
+
639
+ // Skip merging if existing is namespace import
640
+ const isNamespace = existingImport === null || existingImport === void 0 ? void 0 : existingImport.specifiers.some(s => s.type === 'ImportNamespaceSpecifier');
641
+ if (existingImport && !isNamespace) {
642
+ // Merge with existing import
643
+ const newImportStatement = buildMergedImportStatement({
644
+ existingImport,
645
+ newSpecs: specs,
646
+ newImportPath,
647
+ nodeImportKind: node.importKind,
648
+ quoteChar: quote
649
+ });
650
+ if (newImportStatement.length > 0) {
651
+ fixes.push(fixer.replaceText(existingImport, newImportStatement));
652
+ }
653
+ } else {
654
+ // Create new import
655
+ const isTypeImport = node.importKind === 'type';
656
+ const importStatement = buildImportStatement({
657
+ specs,
658
+ path: newImportPath,
659
+ quoteChar: quote,
660
+ isTypeImport
661
+ });
662
+ if (importStatement.length > 0) {
663
+ newStatements.push(importStatement);
664
+ }
665
+ }
666
+ }
667
+
668
+ // Handle unmapped specifiers - they stay in the original import
669
+ if (unmappedSpecifiers.length > 0) {
670
+ const unmappedSpecs = unmappedSpecifiers.map(u => u.spec);
671
+ const isTypeImport = node.importKind === 'type';
672
+ const remainingImport = buildImportStatement({
673
+ specs: unmappedSpecs,
674
+ path: importPath,
675
+ quoteChar: quote,
676
+ isTypeImport
677
+ });
678
+ if (remainingImport.length > 0) {
679
+ newStatements.push(remainingImport);
680
+ }
681
+
682
+ // If there are unmapped value specifiers and automocks, keep the original automock path too
683
+ if (automocks.length > 0) {
684
+ const hasUnmappedValueImport = unmappedSpecifiers.some(({
685
+ kind,
686
+ spec
687
+ }) => kind === 'value' && (spec.type !== 'ImportSpecifier' || spec.importKind !== 'type'));
688
+ if (hasUnmappedValueImport) {
689
+ hasAnyValueImports = true;
690
+ automockPaths.push(importPath);
691
+ }
692
+ }
693
+ }
694
+ if (newStatements.length > 0) {
695
+ fixes.push(fixer.replaceText(node, newStatements.join('\n')));
696
+ } else {
697
+ // If all were merged, remove the node including surrounding whitespace/newlines
698
+ fixes.push(createNodeRemovalFix({
699
+ fixer,
700
+ node,
701
+ sourceCode
702
+ }));
703
+ }
704
+
705
+ // Handle automock updates
706
+ // Only modify automocks if there are value imports being fixed
707
+ // Type-only imports don't need runtime mocking, so we preserve existing automocks
708
+ if (automocks.length > 0 && hasAnyValueImports && automockPaths.length > 0) {
709
+ for (const automock of automocks) {
710
+ // Build new automock statements for all new paths
711
+ const newAutomockStatements = automockPaths.map(path => buildAutomockStatement({
712
+ path,
713
+ quoteChar: automock.quoteChar
714
+ }));
715
+
716
+ // Replace the original automock statement with the new automock(s)
717
+ fixes.push(fixer.replaceTextRange(automock.statementNode.range, newAutomockStatements.join('\n')));
718
+ }
719
+ }
720
+ return fixes;
721
+ }
722
+ function isPlainRequireCall(node) {
723
+ if (node.callee.type !== 'Identifier' || node.callee.name !== 'require') {
724
+ return false;
725
+ }
726
+ if (node.arguments.length !== 1) {
727
+ return false;
728
+ }
729
+ const arg = node.arguments[0];
730
+ return arg.type === 'Literal' && typeof arg.value === 'string';
731
+ }
732
+ function unwrapToRequireCall(expr) {
733
+ let e = expr;
734
+ for (;;) {
735
+ const wrapped = e;
736
+ if (wrapped.type !== 'ParenthesizedExpression' || !wrapped.expression) {
737
+ break;
738
+ }
739
+ e = wrapped.expression;
740
+ }
741
+ if (e.type !== 'CallExpression' || !isPlainRequireCall(e)) {
742
+ return null;
743
+ }
744
+ return e;
745
+ }
746
+ function buildSyntheticImportFromRequireAccess(exportPropertyName, modulePath) {
747
+ const specifiers = exportPropertyName === 'default' ? [{
748
+ type: 'ImportDefaultSpecifier',
749
+ local: {
750
+ type: 'Identifier',
751
+ name: '_r'
752
+ }
753
+ }] : [{
754
+ type: 'ImportSpecifier',
755
+ imported: {
756
+ type: 'Identifier',
757
+ name: exportPropertyName
758
+ },
759
+ local: {
760
+ type: 'Identifier',
761
+ name: exportPropertyName
762
+ }
763
+ }];
764
+ return {
765
+ type: 'ImportDeclaration',
766
+ source: {
767
+ type: 'Literal',
768
+ value: modulePath,
769
+ raw: `'${modulePath}'`
770
+ },
771
+ specifiers,
772
+ importKind: 'value'
773
+ };
774
+ }
775
+ function fullNewImportPathForTarget(targetKey, specsWithTarget, packageName) {
776
+ const isCrossPackage = specsWithTarget.some(s => s.sourcePackageName);
777
+ return isCrossPackage ? targetKey : packageName + targetKey.slice(1);
778
+ }
779
+ function getRhsPropertyAfterTransform(spec) {
780
+ if (spec.type === 'ImportDefaultSpecifier') {
781
+ return 'default';
782
+ }
783
+ return getImportedName(spec);
784
+ }
785
+ function appendAutomockFixesForPathMigration({
786
+ fixer,
787
+ sourceCode,
788
+ oldBarrelPath,
789
+ newPaths
790
+ }) {
791
+ const automocks = findMatchingAutomocks({
792
+ sourceCode,
793
+ importPath: oldBarrelPath
794
+ });
795
+ if (automocks.length === 0 || newPaths.length === 0) {
796
+ return [];
797
+ }
798
+ const fixes = [];
799
+ for (const automock of automocks) {
800
+ const newAutomockStatements = newPaths.map(path => buildAutomockStatement({
801
+ path,
802
+ quoteChar: automock.quoteChar
803
+ }));
804
+ fixes.push(fixer.replaceTextRange(automock.statementNode.range, newAutomockStatements.join('\n')));
805
+ }
806
+ return fixes;
807
+ }
808
+
809
+ /**
810
+ * `require('barrel').default` or `require('barrel').namedExport`
811
+ */
812
+ function handleRequireMemberExpression({
813
+ node,
814
+ context,
815
+ workspaceRoot,
816
+ fs,
817
+ applyToImportsFrom
818
+ }) {
819
+ if (node.computed || node.property.type !== 'Identifier') {
820
+ return;
821
+ }
822
+ const reqCall = unwrapToRequireCall(node.object);
823
+ if (!reqCall) {
824
+ return;
825
+ }
826
+ const modulePath = reqCall.arguments[0].value;
827
+ const importContext = resolveImportContextFromModulePath({
828
+ importPath: modulePath,
829
+ workspaceRoot,
830
+ fs,
831
+ applyToImportsFrom
832
+ });
833
+ if (!importContext) {
834
+ return;
835
+ }
836
+ const exportPropertyName = node.property.name;
837
+ const synthetic = buildSyntheticImportFromRequireAccess(exportPropertyName, modulePath);
838
+ const {
839
+ specifiersByTarget,
840
+ hasNamespaceImport
841
+ } = classifySpecifiers({
842
+ node: synthetic,
843
+ importContext,
844
+ workspaceRoot,
845
+ fs
846
+ });
847
+ if (hasNamespaceImport || specifiersByTarget.size === 0) {
848
+ return;
849
+ }
850
+ const entries = [...specifiersByTarget.entries()];
851
+ if (entries.length !== 1) {
852
+ return;
853
+ }
854
+ const [targetKey, specsWithTarget] = entries[0];
855
+ if (specsWithTarget.length !== 1) {
856
+ return;
857
+ }
858
+ const st = specsWithTarget[0];
859
+ const newImportPath = fullNewImportPathForTarget(targetKey, specsWithTarget, importContext.packageName);
860
+ const transformed = transformSpecifierForExport({
861
+ spec: st.spec,
862
+ originalName: st.originalName,
863
+ kind: st.kind
864
+ });
865
+ const newRhs = getRhsPropertyAfterTransform(transformed);
866
+ const sourceCode = context.getSourceCode();
867
+ const quote = sourceCode.getText(reqCall.arguments[0])[0];
868
+ context.report({
869
+ node: node,
870
+ messageId: 'barrelEntryImport',
871
+ data: {
872
+ path: importContext.importPath
873
+ },
874
+ fix(fixer) {
875
+ const fixes = [];
876
+ fixes.push(fixer.replaceText(node, `require(${quote}${newImportPath}${quote}).${newRhs}`));
877
+ if (st.kind === 'value') {
878
+ fixes.push(...appendAutomockFixesForPathMigration({
879
+ fixer,
880
+ sourceCode,
881
+ oldBarrelPath: modulePath,
882
+ newPaths: [newImportPath]
883
+ }));
884
+ }
885
+ return fixes;
886
+ }
887
+ });
888
+ }
889
+
890
+ /**
891
+ * `const { a, b } = require('barrel')`
892
+ */
893
+ function handleRequireDestructuringDeclarator({
894
+ node,
895
+ context,
896
+ workspaceRoot,
897
+ fs,
898
+ applyToImportsFrom
899
+ }) {
900
+ if (node.id.type !== 'ObjectPattern' || !node.init || node.init.type !== 'CallExpression') {
901
+ return;
902
+ }
903
+ const initCall = node.init;
904
+ if (!isPlainRequireCall(initCall)) {
905
+ return;
906
+ }
907
+ const modulePath = initCall.arguments[0].value;
908
+ const importContext = resolveImportContextFromModulePath({
909
+ importPath: modulePath,
910
+ workspaceRoot,
911
+ fs,
912
+ applyToImportsFrom
913
+ });
914
+ if (!importContext) {
915
+ return;
916
+ }
917
+ const specifiers = [];
918
+ for (const prop of node.id.properties) {
919
+ if (prop.type !== 'Property' || prop.computed) {
920
+ continue;
921
+ }
922
+ if (prop.key.type !== 'Identifier' || prop.value.type !== 'Identifier') {
923
+ continue;
924
+ }
925
+ const importedName = prop.key.name;
926
+ const localName = prop.value.name;
927
+ specifiers.push({
928
+ type: 'ImportSpecifier',
929
+ imported: {
930
+ type: 'Identifier',
931
+ name: importedName
932
+ },
933
+ local: {
934
+ type: 'Identifier',
935
+ name: localName
936
+ }
937
+ });
938
+ }
939
+ if (specifiers.length === 0) {
940
+ return;
941
+ }
942
+ const synthetic = {
943
+ type: 'ImportDeclaration',
944
+ source: {
945
+ type: 'Literal',
946
+ value: modulePath,
947
+ raw: `'${modulePath}'`
948
+ },
949
+ specifiers,
950
+ importKind: 'value'
951
+ };
952
+ const {
953
+ specifiersByTarget,
954
+ unmappedSpecifiers,
955
+ hasNamespaceImport
956
+ } = classifySpecifiers({
957
+ node: synthetic,
958
+ importContext,
959
+ workspaceRoot,
960
+ fs
961
+ });
962
+ if (hasNamespaceImport || specifiersByTarget.size === 0 || unmappedSpecifiers.length > 0) {
963
+ return;
964
+ }
965
+ const parentDecl = node.parent;
966
+ if (parentDecl.type !== 'VariableDeclaration') {
967
+ return;
968
+ }
969
+ if (specifiersByTarget.size > 1 && parentDecl.declarations.length !== 1) {
970
+ return;
971
+ }
972
+ const sourceCode = context.getSourceCode();
973
+ const quote = sourceCode.getText(initCall.arguments[0])[0];
974
+ const pkg = importContext.packageName;
975
+ const buildFixes = fixer => {
976
+ const fixes = [];
977
+ let hasValue = false;
978
+ const automockPaths = [];
979
+ if (specifiersByTarget.size === 1) {
980
+ const [targetKey, specsWithTarget] = [...specifiersByTarget.entries()][0];
981
+ const newImportPath = fullNewImportPathForTarget(targetKey, specsWithTarget, pkg);
982
+ if (specsWithTarget.some(s => s.kind === 'value')) {
983
+ hasValue = true;
984
+ automockPaths.push(newImportPath);
985
+ }
986
+ fixes.push(fixer.replaceText(initCall.arguments[0], `${quote}${newImportPath}${quote}`));
987
+ } else {
988
+ const lines = [];
989
+ for (const [targetKey, specsWithTarget] of specifiersByTarget) {
990
+ const newImportPath = fullNewImportPathForTarget(targetKey, specsWithTarget, pkg);
991
+ if (specsWithTarget.some(s => s.kind === 'value')) {
992
+ hasValue = true;
993
+ automockPaths.push(newImportPath);
994
+ }
995
+ for (const st of specsWithTarget) {
996
+ const transformed = transformSpecifierForExport({
997
+ spec: st.spec,
998
+ originalName: st.originalName,
999
+ kind: st.kind
1000
+ });
1001
+ const rhs = getRhsPropertyAfterTransform(transformed);
1002
+ const local = st.spec.local.name;
1003
+ lines.push(`${local} = require(${quote}${newImportPath}${quote}).${rhs}`);
1004
+ }
1005
+ }
1006
+ const declText = lines.map(l => `${parentDecl.kind} ${l};`).join('\n');
1007
+ fixes.push(fixer.replaceText(parentDecl, declText));
1008
+ }
1009
+ if (hasValue) {
1010
+ fixes.push(...appendAutomockFixesForPathMigration({
1011
+ fixer,
1012
+ sourceCode,
1013
+ oldBarrelPath: modulePath,
1014
+ newPaths: [...new Set(automockPaths)]
1015
+ }));
1016
+ }
1017
+ return fixes;
1018
+ };
1019
+ context.report({
1020
+ node: initCall,
1021
+ messageId: 'barrelEntryImport',
1022
+ data: {
1023
+ path: importContext.importPath
1024
+ },
1025
+ fix: buildFixes
1026
+ });
1027
+ }
1028
+
1029
+ /**
1030
+ * Handles an ImportDeclaration node to check for barrel file imports.
1031
+ * Reports and auto-fixes imports that could use more specific export paths.
1032
+ */
1033
+ function handleImportDeclaration({
1034
+ node,
1035
+ context,
1036
+ workspaceRoot,
1037
+ fs,
1038
+ applyToImportsFrom
1039
+ }) {
1040
+ // Resolve import context (validates and extracts package/export info)
1041
+ // applyToImportsFrom is used here to filter which packages the rule applies to
1042
+ const importContext = resolveImportContext({
1043
+ node,
1044
+ workspaceRoot,
1045
+ fs,
1046
+ applyToImportsFrom
1047
+ });
1048
+ if (!importContext) {
1049
+ return;
1050
+ }
1051
+
1052
+ // Check each imported specifier to see if we can find a more specific export
1053
+ if (node.specifiers.length === 0) {
1054
+ return;
1055
+ }
1056
+
1057
+ // Classify specifiers by their target export paths
1058
+ const {
1059
+ specifiersByTarget,
1060
+ unmappedSpecifiers,
1061
+ hasNamespaceImport
1062
+ } = classifySpecifiers({
1063
+ node,
1064
+ importContext,
1065
+ workspaceRoot,
1066
+ fs
1067
+ });
1068
+
1069
+ // If namespace import, report without auto-fix if there are specific exports available
1070
+ if (hasNamespaceImport) {
1071
+ if (specifiersByTarget.size > 0) {
1072
+ context.report({
1073
+ node,
1074
+ messageId: 'barrelEntryImport',
1075
+ data: {
1076
+ path: importContext.importPath
1077
+ }
1078
+ });
1079
+ }
1080
+ return;
1081
+ }
1082
+
1083
+ // If no specifiers can be remapped to more specific imports, don't report
1084
+ if (specifiersByTarget.size === 0) {
1085
+ return;
1086
+ }
1087
+
1088
+ // Report with auto-fix
1089
+ context.report({
1090
+ node,
1091
+ messageId: 'barrelEntryImport',
1092
+ data: {
1093
+ path: importContext.importPath
1094
+ },
1095
+ fix(fixer) {
1096
+ return createBarrelImportFix({
1097
+ fixer,
1098
+ node,
1099
+ context,
1100
+ importContext,
1101
+ specifiersByTarget,
1102
+ unmappedSpecifiers
1103
+ });
1104
+ }
1105
+ });
1106
+ }
1107
+
1108
+ /**
1109
+ * Factory function to create the ESLint rule with a given file system.
1110
+ * This enables testing with mock file systems.
1111
+ */
1112
+ export function createRule(fs) {
1113
+ return {
1114
+ meta: ruleMeta,
1115
+ create(context) {
1116
+ var _options$applyToImpor;
1117
+ const options = context.options[0] || {};
1118
+ const applyToImportsFrom = (_options$applyToImpor = options.applyToImportsFrom) !== null && _options$applyToImpor !== void 0 ? _options$applyToImpor : DEFAULT_TARGET_FOLDERS;
1119
+ const workspaceRoot = findWorkspaceRoot({
1120
+ startPath: dirname(context.filename),
1121
+ fs,
1122
+ applyToImportsFrom
1123
+ });
1124
+ return {
1125
+ ImportDeclaration(rawNode) {
1126
+ const node = rawNode;
1127
+ handleImportDeclaration({
1128
+ node,
1129
+ context,
1130
+ workspaceRoot,
1131
+ fs,
1132
+ applyToImportsFrom
1133
+ });
1134
+ },
1135
+ MemberExpression(rawNode) {
1136
+ handleRequireMemberExpression({
1137
+ node: rawNode,
1138
+ context,
1139
+ workspaceRoot,
1140
+ fs,
1141
+ applyToImportsFrom
1142
+ });
1143
+ },
1144
+ VariableDeclarator(rawNode) {
1145
+ handleRequireDestructuringDeclarator({
1146
+ node: rawNode,
1147
+ context,
1148
+ workspaceRoot,
1149
+ fs,
1150
+ applyToImportsFrom
1151
+ });
1152
+ }
1153
+ };
1154
+ }
1155
+ };
1156
+ }
1157
+ const rule = createRule(realFileSystem);
1158
+ export default rule;