eslint-plugin-import-next 2.3.15 → 2.3.17

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 (63) hide show
  1. package/README.md +1 -1
  2. package/package.json +3 -3
  3. package/src/files/foo.js +1 -6
  4. package/src/files/no-default.js +1 -4
  5. package/src/index.js +1 -273
  6. package/src/oxlint.js +1 -3
  7. package/src/rules/consistent-type-specifier-style.js +1 -109
  8. package/src/rules/default.js +1 -56
  9. package/src/rules/dynamic-import-chunkname.js +1 -115
  10. package/src/rules/enforce-dependency-direction.js +1 -213
  11. package/src/rules/enforce-import-order.js +1 -299
  12. package/src/rules/enforce-team-boundaries.js +1 -191
  13. package/src/rules/export.js +1 -111
  14. package/src/rules/exports-last.js +1 -67
  15. package/src/rules/extensions.js +1 -107
  16. package/src/rules/first.js +1 -53
  17. package/src/rules/group-exports.js +1 -69
  18. package/src/rules/max-dependencies.js +1 -241
  19. package/src/rules/named.js +1 -67
  20. package/src/rules/namespace.js +1 -64
  21. package/src/rules/newline-after-import.js +1 -57
  22. package/src/rules/no-absolute-path.js +1 -139
  23. package/src/rules/no-amd.js +1 -108
  24. package/src/rules/no-anonymous-default-export.js +1 -135
  25. package/src/rules/no-barrel-file.js +1 -194
  26. package/src/rules/no-barrel-import.js +1 -135
  27. package/src/rules/no-commonjs.js +1 -312
  28. package/src/rules/no-cross-domain-imports.js +1 -233
  29. package/src/rules/no-cycle.js +2 -396
  30. package/src/rules/no-default-export.js +2 -213
  31. package/src/rules/no-deprecated.js +1 -316
  32. package/src/rules/no-duplicates.js +1 -81
  33. package/src/rules/no-dynamic-require.js +1 -78
  34. package/src/rules/no-empty-named-blocks.js +1 -103
  35. package/src/rules/no-extraneous-dependencies.js +7 -385
  36. package/src/rules/no-full-package-import.js +1 -134
  37. package/src/rules/no-import-module-exports.js +1 -76
  38. package/src/rules/no-internal-modules.js +1 -256
  39. package/src/rules/no-legacy-imports.js +1 -125
  40. package/src/rules/no-mutable-exports.js +1 -146
  41. package/src/rules/no-named-as-default-member.js +1 -73
  42. package/src/rules/no-named-as-default.js +1 -90
  43. package/src/rules/no-named-default.js +1 -48
  44. package/src/rules/no-named-export.js +1 -174
  45. package/src/rules/no-namespace.js +1 -71
  46. package/src/rules/no-nodejs-modules.js +1 -229
  47. package/src/rules/no-relative-packages.js +1 -161
  48. package/src/rules/no-relative-parent-imports.js +1 -118
  49. package/src/rules/no-restricted-paths.js +1 -89
  50. package/src/rules/no-self-import.js +1 -145
  51. package/src/rules/no-unassigned-import.js +1 -115
  52. package/src/rules/no-unresolved.js +1 -189
  53. package/src/rules/no-unused-modules.js +1 -82
  54. package/src/rules/no-useless-path-segments.js +1 -139
  55. package/src/rules/prefer-default-export.js +1 -188
  56. package/src/rules/prefer-direct-import.js +1 -170
  57. package/src/rules/prefer-modern-api.js +1 -212
  58. package/src/rules/prefer-node-protocol.js +1 -174
  59. package/src/rules/prefer-tree-shakeable-imports.js +1 -134
  60. package/src/rules/require-import-approval.js +1 -174
  61. package/src/rules/unambiguous.js +1 -70
  62. package/src/types/index.js +1 -2
  63. package/src/utils/typescript-peer.js +1 -23
@@ -1,396 +1,2 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.noCycle = void 0;
4
- exports.clearCircularDependencyCache = clearCircularDependencyCache;
5
- const eslint_devkit_1 = require("@interlace/eslint-devkit");
6
- const eslint_devkit_2 = require("@interlace/eslint-devkit");
7
- const eslint_devkit_3 = require("@interlace/eslint-devkit");
8
- const node_fs_1 = require("node:fs");
9
- const eslint_devkit_4 = require("@interlace/eslint-devkit");
10
- const sharedCache = (0, eslint_devkit_4.createFileSystemCache)();
11
- function clearCircularDependencyCache() {
12
- (0, eslint_devkit_4.clearCache)(sharedCache);
13
- }
14
- exports.noCycle = (0, eslint_devkit_1.createRule)({
15
- name: 'no-cycle',
16
- meta: {
17
- type: 'problem',
18
- docs: {
19
- url: 'https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-import-next/docs/rules/no-cycle.md',
20
- description: 'Detect circular dependencies that cause bundle memory bloat and initialization issues',
21
- cwe: 'CWE-407',
22
- cvss: 9.5,
23
- confidence: 'high',
24
- },
25
- messages: {
26
- moduleSplit: (0, eslint_devkit_2.formatLLMMessage)({
27
- icon: eslint_devkit_2.MessageIcons.ARCHITECTURE,
28
- issueName: 'Circular dependency',
29
- cwe: 'CWE-407',
30
- description: 'Circular dependency detected',
31
- severity: 'CRITICAL',
32
- fix: 'Split {{moduleToSplit}} into .{{coreFile}} and .{{extendedFile}}',
33
- documentationLink: 'https://en.wikipedia.org/wiki/Circular_dependency',
34
- }),
35
- directImport: (0, eslint_devkit_2.formatLLMMessage)({
36
- icon: eslint_devkit_2.MessageIcons.ARCHITECTURE,
37
- issueName: 'Circular dependency',
38
- cwe: 'CWE-407',
39
- description: 'Circular dependency detected',
40
- severity: 'MEDIUM',
41
- fix: '{{newImport}} (direct imports preferred over barrel exports)',
42
- documentationLink: 'https://en.wikipedia.org/wiki/Circular_dependency',
43
- }),
44
- extractShared: (0, eslint_devkit_2.formatLLMMessage)({
45
- icon: eslint_devkit_2.MessageIcons.ARCHITECTURE,
46
- issueName: 'Circular dependency',
47
- cwe: 'CWE-407',
48
- description: 'Circular dependency detected',
49
- severity: 'MEDIUM',
50
- fix: 'Extract shared types to {{exports}} file',
51
- documentationLink: 'https://en.wikipedia.org/wiki/Dependency_inversion_principle',
52
- }),
53
- dependencyInjection: (0, eslint_devkit_2.formatLLMMessage)({
54
- icon: eslint_devkit_2.MessageIcons.ARCHITECTURE,
55
- issueName: 'Circular dependency',
56
- cwe: 'CWE-407',
57
- description: 'Circular dependency detected',
58
- severity: 'MEDIUM',
59
- fix: 'Use dependency injection pattern to break cycle',
60
- documentationLink: 'https://en.wikipedia.org/wiki/Dependency_injection',
61
- }),
62
- },
63
- schema: [
64
- {
65
- type: 'object',
66
- properties: {
67
- maxDepth: {
68
- type: 'number',
69
- default: Number.MAX_SAFE_INTEGER,
70
- description: 'Maximum depth to traverse when detecting cycles. Default: unlimited (matches eslint-plugin-import and oxlint). Lower values are a performance escape hatch — but with our nonCyclicFiles cache, traversal cost is amortized, and a low cap silently misses cycles deeper than the limit. Set to a finite number only on huge graphs where the bench latency hurts you.',
71
- },
72
- ignorePatterns: {
73
- type: 'array',
74
- items: { type: 'string' },
75
- default: [
76
- '**/*.test.ts',
77
- '**/*.test.tsx',
78
- '**/*.spec.ts',
79
- '**/*.spec.tsx',
80
- '**/*.stories.tsx',
81
- '**/__tests__/**',
82
- '**/__mocks__/**',
83
- ],
84
- description: 'File patterns to ignore (glob patterns)',
85
- },
86
- barrelExports: {
87
- type: 'array',
88
- items: { type: 'string' },
89
- default: ['index.ts', 'index.tsx', 'index.js', 'index.jsx'],
90
- description: 'Files considered barrel exports',
91
- },
92
- reportAllCycles: {
93
- type: 'boolean',
94
- default: true,
95
- description: 'Report all circular dependencies found (not just the first one)',
96
- },
97
- fixStrategy: {
98
- type: 'string',
99
- enum: [
100
- 'module-split',
101
- 'direct-import',
102
- 'extract-shared',
103
- 'dependency-injection',
104
- 'auto',
105
- ],
106
- default: 'auto',
107
- description: 'Strategy for fixing circular dependencies (auto = smart detection)',
108
- },
109
- moduleNamingConvention: {
110
- type: 'string',
111
- enum: ['semantic', 'numbered'],
112
- default: 'semantic',
113
- description: 'Naming convention for split modules (semantic: .core, .api | numbered: .1, .2)',
114
- },
115
- coreModuleSuffix: {
116
- type: 'string',
117
- default: 'core',
118
- description: 'Suffix for core module when splitting (e.g., "core", "base", "main")',
119
- },
120
- extendedModuleSuffix: {
121
- type: 'string',
122
- default: 'extended',
123
- description: 'Suffix for extended module when splitting (e.g., "extended", "api", "helpers")',
124
- },
125
- },
126
- additionalProperties: false,
127
- },
128
- ],
129
- fixable: undefined,
130
- },
131
- defaultOptions: [
132
- {
133
- maxDepth: Infinity,
134
- ignorePatterns: [],
135
- barrelExports: ['index.ts', 'index.tsx', 'index.js', 'index.jsx'],
136
- reportAllCycles: true,
137
- fixStrategy: 'auto',
138
- moduleNamingConvention: 'semantic',
139
- coreModuleSuffix: 'core',
140
- extendedModuleSuffix: 'extended',
141
- },
142
- ],
143
- create(context) {
144
- const options = context.options[0] || {};
145
- const maxDepth = options.maxDepth ?? Infinity;
146
- const ignorePatterns = options.ignorePatterns ?? [
147
- '**/*.test.ts',
148
- '**/*.test.tsx',
149
- '**/*.spec.ts',
150
- '**/*.spec.tsx',
151
- '**/*.stories.tsx',
152
- '**/__tests__/**',
153
- '**/__mocks__/**',
154
- ];
155
- const barrelExports = options.barrelExports ?? [
156
- 'index.ts',
157
- 'index.tsx',
158
- 'index.js',
159
- 'index.jsx',
160
- ];
161
- const _reportAllCycles = options.reportAllCycles ?? true;
162
- const fixStrategy = options.fixStrategy ?? 'auto';
163
- const moduleNamingConvention = options.moduleNamingConvention ?? 'semantic';
164
- const coreModuleSuffix = options.coreModuleSuffix ?? 'core';
165
- const extendedModuleSuffix = options.extendedModuleSuffix ?? 'extended';
166
- const filename = context.filename;
167
- const normalizedFilename = (() => {
168
- try {
169
- return (0, node_fs_1.realpathSync)(filename);
170
- }
171
- catch {
172
- return filename;
173
- }
174
- })();
175
- const workspaceRoot = context.cwd;
176
- const settings = context.settings;
177
- const resolverSettings = settings?.['import/resolver'] ||
178
- settings?.['import-next/resolver'];
179
- if ((0, eslint_devkit_3.shouldIgnoreFile)(filename, ignorePatterns, sharedCache.compiledPatterns)) {
180
- return {};
181
- }
182
- sharedCache.pendingCycleReports.clear();
183
- const reportedImportNodes = new Set();
184
- function selectFixStrategy(cycle, userStrategy) {
185
- if (userStrategy !== 'auto') {
186
- return userStrategy;
187
- }
188
- const hasBarrel = cycle.some((file) => (0, eslint_devkit_3.isBarrelExport)(file, barrelExports));
189
- const typesOnly = (0, eslint_devkit_4.hasOnlyTypeImports)(cycle, sharedCache);
190
- const cycleSize = cycle.length;
191
- if (hasBarrel && cycleSize === 2) {
192
- return 'direct-import';
193
- }
194
- if (typesOnly) {
195
- return 'extract-shared';
196
- }
197
- return 'module-split';
198
- }
199
- function generateModuleSplitMessage(cycle) {
200
- const moduleNames = (0, eslint_devkit_3.getModuleNames)(cycle, workspaceRoot);
201
- const [module1, module2] = moduleNames;
202
- const moduleToSplit = module1;
203
- const suffix1 = moduleNamingConvention === 'semantic' ? `.${coreModuleSuffix}` : '.1';
204
- const suffix2 = moduleNamingConvention === 'semantic'
205
- ? `.${extendedModuleSuffix}`
206
- : '.2';
207
- return {
208
- cycle: (0, eslint_devkit_3.formatCycleDisplay)(cycle, workspaceRoot),
209
- moduleToSplit,
210
- coreFile: coreModuleSuffix,
211
- extendedFile: extendedModuleSuffix,
212
- splitCount: '2',
213
- fileStructure: `├─ ${moduleToSplit}/${moduleToSplit}${suffix1}.ts (→ ${module2} ✓)\n` +
214
- `└─ ${moduleToSplit}/${moduleToSplit}${suffix2}.ts (→ ${suffix1} + ${module2} ✓)`,
215
- result: `${suffix1} → ${module2} → ${suffix2} (no cycle)`,
216
- };
217
- }
218
- function generateDirectImportMessage(cycle, sourceImport) {
219
- const currentFile = (0, eslint_devkit_3.getBasename)(context.filename);
220
- const targetFile = cycle[1];
221
- const relativeImport = (0, eslint_devkit_3.getRelativeImportPath)(context.filename, targetFile);
222
- return {
223
- cycle: (0, eslint_devkit_3.formatCycleDisplay)(cycle, workspaceRoot),
224
- currentFile,
225
- oldImport: `import { ... } from '${sourceImport.source}'`,
226
- newImport: `import { ... } from '${relativeImport}'`,
227
- };
228
- }
229
- function generateExtractSharedMessage(cycle) {
230
- const moduleNames = (0, eslint_devkit_3.getModuleNames)(cycle, workspaceRoot);
231
- const [module1, module2] = moduleNames;
232
- const exports = [
233
- `- export type ${module1}Id, ${module2}Id`,
234
- `- export interface ${module1}Summary, ${module2}Summary`,
235
- ].join('\n');
236
- return {
237
- cycle: (0, eslint_devkit_3.formatCycleDisplay)(cycle, workspaceRoot),
238
- exports,
239
- result: `shared/types ← ${module1}, ${module2} (no cycle)`,
240
- };
241
- }
242
- function generateDependencyInjectionMessage(cycle) {
243
- const moduleNames = (0, eslint_devkit_3.getModuleNames)(cycle, workspaceRoot);
244
- const [service1, service2] = moduleNames;
245
- const steps = [
246
- `1. Create interfaces/I${service1}.ts, interfaces/I${service2}.ts`,
247
- `2. Both services implement their interface`,
248
- `3. Inject via constructor: constructor(private dep?: IDep)`,
249
- `4. Wire in container.ts`,
250
- ].join('\n');
251
- return {
252
- cycle: (0, eslint_devkit_3.formatCycleDisplay)(cycle, workspaceRoot),
253
- steps,
254
- };
255
- }
256
- function generateMessageData(cycle, strategy, sourceImport) {
257
- switch (strategy) {
258
- case 'direct-import':
259
- return {
260
- messageId: 'directImport',
261
- data: generateDirectImportMessage(cycle, sourceImport),
262
- };
263
- case 'extract-shared':
264
- return {
265
- messageId: 'extractShared',
266
- data: generateExtractSharedMessage(cycle),
267
- };
268
- case 'dependency-injection':
269
- return {
270
- messageId: 'dependencyInjection',
271
- data: generateDependencyInjectionMessage(cycle),
272
- };
273
- default:
274
- return {
275
- messageId: 'moduleSplit',
276
- data: generateModuleSplitMessage(cycle),
277
- };
278
- }
279
- }
280
- return {
281
- 'Program'() {
282
- },
283
- ImportDeclaration(node) {
284
- if (node.importKind === 'type')
285
- return;
286
- if (reportedImportNodes.has(node))
287
- return;
288
- const importSource = node.source.value;
289
- const resolved = (0, eslint_devkit_4.resolveImportPath)(importSource, {
290
- fromFile: normalizedFilename,
291
- workspaceRoot,
292
- barrelExports,
293
- cache: sharedCache,
294
- resolverSettings,
295
- });
296
- if (!resolved)
297
- return;
298
- if (sharedCache.nonCyclicFiles.has(resolved))
299
- return;
300
- let tgtSCC = sharedCache.sccIndex.get(resolved);
301
- if (tgtSCC === undefined) {
302
- (0, eslint_devkit_4.computeSCCsFromFile)(resolved, {
303
- maxDepth: Infinity,
304
- workspaceRoot,
305
- barrelExports,
306
- cache: sharedCache,
307
- resolverSettings,
308
- });
309
- tgtSCC = sharedCache.sccIndex.get(resolved);
310
- }
311
- let srcSCC = sharedCache.sccIndex.get(normalizedFilename);
312
- if (srcSCC === undefined) {
313
- (0, eslint_devkit_4.computeSCCsFromFile)(normalizedFilename, {
314
- maxDepth: Infinity,
315
- workspaceRoot,
316
- barrelExports,
317
- cache: sharedCache,
318
- resolverSettings,
319
- });
320
- srcSCC = sharedCache.sccIndex.get(normalizedFilename);
321
- }
322
- if (srcSCC === undefined || tgtSCC === undefined || srcSCC !== tgtSCC) {
323
- return;
324
- }
325
- const cyclePath = (0, eslint_devkit_4.findShortestCyclePath)(normalizedFilename, resolved, {
326
- maxDepth,
327
- workspaceRoot,
328
- barrelExports,
329
- cache: sharedCache,
330
- resolverSettings,
331
- });
332
- if (!cyclePath)
333
- return;
334
- reportedImportNodes.add(node);
335
- const cycleStart = cyclePath.indexOf(normalizedFilename);
336
- const relevantCycle = cycleStart >= 0
337
- ? [...cyclePath.slice(cycleStart), ...cyclePath.slice(0, cycleStart)]
338
- : cyclePath;
339
- const strategy = selectFixStrategy(relevantCycle, fixStrategy);
340
- const sourceImport = { path: resolved, source: importSource };
341
- const { messageId, data } = generateMessageData(relevantCycle, strategy, sourceImport);
342
- context.report({ node, messageId, data });
343
- },
344
- ExportNamedDeclaration(node) {
345
- if (!node.source)
346
- return;
347
- if (reportedImportNodes.has(node))
348
- return;
349
- const importSource = node.source.value;
350
- const resolved = (0, eslint_devkit_4.resolveImportPath)(importSource, {
351
- fromFile: normalizedFilename,
352
- workspaceRoot,
353
- barrelExports,
354
- cache: sharedCache,
355
- resolverSettings,
356
- });
357
- if (!resolved)
358
- return;
359
- if (sharedCache.nonCyclicFiles.has(resolved))
360
- return;
361
- let tgtSCC = sharedCache.sccIndex.get(resolved);
362
- if (tgtSCC === undefined) {
363
- (0, eslint_devkit_4.computeSCCsFromFile)(resolved, {
364
- maxDepth: Infinity,
365
- workspaceRoot,
366
- barrelExports,
367
- cache: sharedCache,
368
- resolverSettings,
369
- });
370
- tgtSCC = sharedCache.sccIndex.get(resolved);
371
- }
372
- const srcSCC = sharedCache.sccIndex.get(normalizedFilename);
373
- if (srcSCC === undefined || tgtSCC === undefined || srcSCC !== tgtSCC)
374
- return;
375
- const cyclePath = (0, eslint_devkit_4.findShortestCyclePath)(normalizedFilename, resolved, {
376
- maxDepth,
377
- workspaceRoot,
378
- barrelExports,
379
- cache: sharedCache,
380
- resolverSettings,
381
- });
382
- if (!cyclePath)
383
- return;
384
- reportedImportNodes.add(node);
385
- const cycleStart = cyclePath.indexOf(normalizedFilename);
386
- const relevantCycle = cycleStart >= 0
387
- ? [...cyclePath.slice(cycleStart), ...cyclePath.slice(0, cycleStart)]
388
- : cyclePath;
389
- const strategy = selectFixStrategy(relevantCycle, fixStrategy);
390
- const sourceImport = { path: resolved, source: importSource };
391
- const { messageId, data } = generateMessageData(relevantCycle, strategy, sourceImport);
392
- context.report({ node, messageId, data });
393
- },
394
- };
395
- },
396
- });
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.noCycle=void 0;exports.clearCircularDependencyCache=clearCircularDependencyCache;const eslint_devkit_1=require("@interlace/eslint-devkit");const eslint_devkit_2=require("@interlace/eslint-devkit");const eslint_devkit_3=require("@interlace/eslint-devkit");const node_fs_1=require("node:fs");const eslint_devkit_4=require("@interlace/eslint-devkit");const sharedCache=(0,eslint_devkit_4.createFileSystemCache)();function clearCircularDependencyCache(){(0,eslint_devkit_4.clearCache)(sharedCache)}exports.noCycle=(0,eslint_devkit_1.createRule)({name:"no-cycle",meta:{type:"problem",docs:{url:"https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-import-next/docs/rules/no-cycle.md",description:"Detect circular dependencies that cause bundle memory bloat and initialization issues",cwe:"CWE-407",cvss:9.5,confidence:"high"},messages:{moduleSplit:(0,eslint_devkit_2.formatLLMMessage)({icon:eslint_devkit_2.MessageIcons.ARCHITECTURE,issueName:"Circular dependency",cwe:"CWE-407",description:"Circular dependency detected",severity:"CRITICAL",fix:"Split {{moduleToSplit}} into .{{coreFile}} and .{{extendedFile}}",documentationLink:"https://en.wikipedia.org/wiki/Circular_dependency"}),directImport:(0,eslint_devkit_2.formatLLMMessage)({icon:eslint_devkit_2.MessageIcons.ARCHITECTURE,issueName:"Circular dependency",cwe:"CWE-407",description:"Circular dependency detected",severity:"MEDIUM",fix:"{{newImport}} (direct imports preferred over barrel exports)",documentationLink:"https://en.wikipedia.org/wiki/Circular_dependency"}),extractShared:(0,eslint_devkit_2.formatLLMMessage)({icon:eslint_devkit_2.MessageIcons.ARCHITECTURE,issueName:"Circular dependency",cwe:"CWE-407",description:"Circular dependency detected",severity:"MEDIUM",fix:"Extract shared types to {{exports}} file",documentationLink:"https://en.wikipedia.org/wiki/Dependency_inversion_principle"}),dependencyInjection:(0,eslint_devkit_2.formatLLMMessage)({icon:eslint_devkit_2.MessageIcons.ARCHITECTURE,issueName:"Circular dependency",cwe:"CWE-407",description:"Circular dependency detected",severity:"MEDIUM",fix:"Use dependency injection pattern to break cycle",documentationLink:"https://en.wikipedia.org/wiki/Dependency_injection"})},schema:[{type:"object",properties:{maxDepth:{type:"number",default:Number.MAX_SAFE_INTEGER,description:"Maximum depth to traverse when detecting cycles. Default: unlimited (matches eslint-plugin-import and oxlint). Lower values are a performance escape hatch \u2014 but with our nonCyclicFiles cache, traversal cost is amortized, and a low cap silently misses cycles deeper than the limit. Set to a finite number only on huge graphs where the bench latency hurts you."},ignorePatterns:{type:"array",items:{type:"string"},default:["**/*.test.ts","**/*.test.tsx","**/*.spec.ts","**/*.spec.tsx","**/*.stories.tsx","**/__tests__/**","**/__mocks__/**"],description:"File patterns to ignore (glob patterns)"},barrelExports:{type:"array",items:{type:"string"},default:["index.ts","index.tsx","index.js","index.jsx"],description:"Files considered barrel exports"},reportAllCycles:{type:"boolean",default:true,description:"Report all circular dependencies found (not just the first one)"},fixStrategy:{type:"string",enum:["module-split","direct-import","extract-shared","dependency-injection","auto"],default:"auto",description:"Strategy for fixing circular dependencies (auto = smart detection)"},moduleNamingConvention:{type:"string",enum:["semantic","numbered"],default:"semantic",description:"Naming convention for split modules (semantic: .core, .api | numbered: .1, .2)"},coreModuleSuffix:{type:"string",default:"core",description:'Suffix for core module when splitting (e.g., "core", "base", "main")'},extendedModuleSuffix:{type:"string",default:"extended",description:'Suffix for extended module when splitting (e.g., "extended", "api", "helpers")'}},additionalProperties:false}],fixable:void 0},defaultOptions:[{maxDepth:Infinity,ignorePatterns:[],barrelExports:["index.ts","index.tsx","index.js","index.jsx"],reportAllCycles:true,fixStrategy:"auto",moduleNamingConvention:"semantic",coreModuleSuffix:"core",extendedModuleSuffix:"extended"}],create(context){const options=context.options[0]||{};const maxDepth=options.maxDepth??Infinity;const ignorePatterns=options.ignorePatterns??["**/*.test.ts","**/*.test.tsx","**/*.spec.ts","**/*.spec.tsx","**/*.stories.tsx","**/__tests__/**","**/__mocks__/**"];const barrelExports=options.barrelExports??["index.ts","index.tsx","index.js","index.jsx"];const _reportAllCycles=options.reportAllCycles??true;const fixStrategy=options.fixStrategy??"auto";const moduleNamingConvention=options.moduleNamingConvention??"semantic";const coreModuleSuffix=options.coreModuleSuffix??"core";const extendedModuleSuffix=options.extendedModuleSuffix??"extended";const filename=context.filename;const normalizedFilename=(()=>{try{return(0,node_fs_1.realpathSync)(filename)}catch{return filename}})();const workspaceRoot=context.cwd;const settings=context.settings;const resolverSettings=settings?.["import/resolver"]||settings?.["import-next/resolver"];if((0,eslint_devkit_3.shouldIgnoreFile)(filename,ignorePatterns,sharedCache.compiledPatterns)){return{}}sharedCache.pendingCycleReports.clear();const reportedImportNodes=new Set;function selectFixStrategy(cycle,userStrategy){if(userStrategy!=="auto"){return userStrategy}const hasBarrel=cycle.some(file=>(0,eslint_devkit_3.isBarrelExport)(file,barrelExports));const typesOnly=(0,eslint_devkit_4.hasOnlyTypeImports)(cycle,sharedCache);const cycleSize=cycle.length;if(hasBarrel&&cycleSize===2){return"direct-import"}if(typesOnly){return"extract-shared"}return"module-split"}function generateModuleSplitMessage(cycle){const moduleNames=(0,eslint_devkit_3.getModuleNames)(cycle,workspaceRoot);const[module1,module2]=moduleNames;const moduleToSplit=module1;const suffix1=moduleNamingConvention==="semantic"?`.${coreModuleSuffix}`:".1";const suffix2=moduleNamingConvention==="semantic"?`.${extendedModuleSuffix}`:".2";return{cycle:(0,eslint_devkit_3.formatCycleDisplay)(cycle,workspaceRoot),moduleToSplit,coreFile:coreModuleSuffix,extendedFile:extendedModuleSuffix,splitCount:"2",fileStructure:`\u251C\u2500 ${moduleToSplit}/${moduleToSplit}${suffix1}.ts (\u2192 ${module2} \u2713)
2
+ \u2514\u2500 ${moduleToSplit}/${moduleToSplit}${suffix2}.ts (\u2192 ${suffix1} + ${module2} \u2713)`,result:`${suffix1} \u2192 ${module2} \u2192 ${suffix2} (no cycle)`}}function generateDirectImportMessage(cycle,sourceImport){const currentFile=(0,eslint_devkit_3.getBasename)(context.filename);const targetFile=cycle[1];const relativeImport=(0,eslint_devkit_3.getRelativeImportPath)(context.filename,targetFile);return{cycle:(0,eslint_devkit_3.formatCycleDisplay)(cycle,workspaceRoot),currentFile,oldImport:`import { ... } from '${sourceImport.source}'`,newImport:`import { ... } from '${relativeImport}'`}}function generateExtractSharedMessage(cycle){const moduleNames=(0,eslint_devkit_3.getModuleNames)(cycle,workspaceRoot);const[module1,module2]=moduleNames;const exports2=[`- export type ${module1}Id, ${module2}Id`,`- export interface ${module1}Summary, ${module2}Summary`].join("\n");return{cycle:(0,eslint_devkit_3.formatCycleDisplay)(cycle,workspaceRoot),exports:exports2,result:`shared/types \u2190 ${module1}, ${module2} (no cycle)`}}function generateDependencyInjectionMessage(cycle){const moduleNames=(0,eslint_devkit_3.getModuleNames)(cycle,workspaceRoot);const[service1,service2]=moduleNames;const steps=[`1. Create interfaces/I${service1}.ts, interfaces/I${service2}.ts`,`2. Both services implement their interface`,`3. Inject via constructor: constructor(private dep?: IDep)`,`4. Wire in container.ts`].join("\n");return{cycle:(0,eslint_devkit_3.formatCycleDisplay)(cycle,workspaceRoot),steps}}function generateMessageData(cycle,strategy,sourceImport){switch(strategy){case"direct-import":return{messageId:"directImport",data:generateDirectImportMessage(cycle,sourceImport)};case"extract-shared":return{messageId:"extractShared",data:generateExtractSharedMessage(cycle)};case"dependency-injection":return{messageId:"dependencyInjection",data:generateDependencyInjectionMessage(cycle)};default:return{messageId:"moduleSplit",data:generateModuleSplitMessage(cycle)}}}return{"Program"(){},ImportDeclaration(node){if(node.importKind==="type")return;if(reportedImportNodes.has(node))return;const importSource=node.source.value;const resolved=(0,eslint_devkit_4.resolveImportPath)(importSource,{fromFile:normalizedFilename,workspaceRoot,barrelExports,cache:sharedCache,resolverSettings});if(!resolved)return;if(sharedCache.nonCyclicFiles.has(resolved))return;let tgtSCC=sharedCache.sccIndex.get(resolved);if(tgtSCC===void 0){(0,eslint_devkit_4.computeSCCsFromFile)(resolved,{maxDepth:Infinity,workspaceRoot,barrelExports,cache:sharedCache,resolverSettings});tgtSCC=sharedCache.sccIndex.get(resolved)}let srcSCC=sharedCache.sccIndex.get(normalizedFilename);if(srcSCC===void 0){(0,eslint_devkit_4.computeSCCsFromFile)(normalizedFilename,{maxDepth:Infinity,workspaceRoot,barrelExports,cache:sharedCache,resolverSettings});srcSCC=sharedCache.sccIndex.get(normalizedFilename)}if(srcSCC===void 0||tgtSCC===void 0||srcSCC!==tgtSCC){return}const cyclePath=(0,eslint_devkit_4.findShortestCyclePath)(normalizedFilename,resolved,{maxDepth,workspaceRoot,barrelExports,cache:sharedCache,resolverSettings});if(!cyclePath)return;reportedImportNodes.add(node);const cycleStart=cyclePath.indexOf(normalizedFilename);const relevantCycle=cycleStart>=0?[...cyclePath.slice(cycleStart),...cyclePath.slice(0,cycleStart)]:cyclePath;const strategy=selectFixStrategy(relevantCycle,fixStrategy);const sourceImport={path:resolved,source:importSource};const{messageId,data}=generateMessageData(relevantCycle,strategy,sourceImport);context.report({node,messageId,data})},ExportNamedDeclaration(node){if(!node.source)return;if(reportedImportNodes.has(node))return;const importSource=node.source.value;const resolved=(0,eslint_devkit_4.resolveImportPath)(importSource,{fromFile:normalizedFilename,workspaceRoot,barrelExports,cache:sharedCache,resolverSettings});if(!resolved)return;if(sharedCache.nonCyclicFiles.has(resolved))return;let tgtSCC=sharedCache.sccIndex.get(resolved);if(tgtSCC===void 0){(0,eslint_devkit_4.computeSCCsFromFile)(resolved,{maxDepth:Infinity,workspaceRoot,barrelExports,cache:sharedCache,resolverSettings});tgtSCC=sharedCache.sccIndex.get(resolved)}const srcSCC=sharedCache.sccIndex.get(normalizedFilename);if(srcSCC===void 0||tgtSCC===void 0||srcSCC!==tgtSCC)return;const cyclePath=(0,eslint_devkit_4.findShortestCyclePath)(normalizedFilename,resolved,{maxDepth,workspaceRoot,barrelExports,cache:sharedCache,resolverSettings});if(!cyclePath)return;reportedImportNodes.add(node);const cycleStart=cyclePath.indexOf(normalizedFilename);const relevantCycle=cycleStart>=0?[...cyclePath.slice(cycleStart),...cyclePath.slice(0,cycleStart)]:cyclePath;const strategy=selectFixStrategy(relevantCycle,fixStrategy);const sourceImport={path:resolved,source:importSource};const{messageId,data}=generateMessageData(relevantCycle,strategy,sourceImport);context.report({node,messageId,data})}}}});
@@ -1,213 +1,2 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.noDefaultExport = void 0;
4
- const eslint_devkit_1 = require("@interlace/eslint-devkit");
5
- const eslint_devkit_2 = require("@interlace/eslint-devkit");
6
- exports.noDefaultExport = (0, eslint_devkit_1.createRule)({
7
- name: 'no-default-export',
8
- meta: {
9
- type: 'problem',
10
- docs: {
11
- url: 'https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-import-next/docs/rules/no-default-export.md',
12
- description: 'Prevents default exports',
13
- },
14
- fixable: 'code',
15
- messages: {
16
- defaultExport: (0, eslint_devkit_2.formatLLMMessage)({
17
- icon: eslint_devkit_2.MessageIcons.ARCHITECTURE,
18
- issueName: 'Default Export Detected',
19
- description: 'Default export violates import style guidelines',
20
- severity: 'MEDIUM',
21
- fix: 'Use named export instead of default export',
22
- documentationLink: 'https://github.com/import-js/eslint-plugin-import/blob/main/docs/rules/no-default-export.md',
23
- }),
24
- preferNamedExport: (0, eslint_devkit_2.formatLLMMessage)({
25
- icon: eslint_devkit_2.MessageIcons.ARCHITECTURE,
26
- issueName: 'Prefer Named Exports',
27
- description: 'Named exports provide better tree-shaking and refactoring',
28
- severity: 'LOW',
29
- fix: 'Convert default export to named export',
30
- documentationLink: 'https://github.com/import-js/eslint-plugin-import/blob/main/docs/rules/no-default-export.md',
31
- }),
32
- defaultExportRefactor: (0, eslint_devkit_2.formatLLMMessage)({
33
- icon: eslint_devkit_2.MessageIcons.ARCHITECTURE,
34
- issueName: 'Export Style Violation',
35
- description: 'Default export pattern not allowed in this codebase',
36
- severity: 'MEDIUM',
37
- fix: 'Refactor to use named exports consistently',
38
- documentationLink: 'https://github.com/import-js/eslint-plugin-import/blob/main/docs/rules/no-default-export.md',
39
- }),
40
- },
41
- schema: [
42
- {
43
- type: 'object',
44
- properties: {
45
- allowInFiles: {
46
- type: 'array',
47
- items: {
48
- type: 'string',
49
- },
50
- description: 'Allow default exports in specific files.',
51
- },
52
- allowPatterns: {
53
- type: 'array',
54
- items: {
55
- type: 'string',
56
- },
57
- description: 'Allow default exports for specific patterns.',
58
- },
59
- suggestNamed: {
60
- type: 'boolean',
61
- default: true,
62
- description: 'Suggest named export alternative.',
63
- },
64
- },
65
- additionalProperties: false,
66
- },
67
- ],
68
- },
69
- defaultOptions: [
70
- {
71
- allowInFiles: [],
72
- allowPatterns: [],
73
- suggestNamed: true,
74
- },
75
- ],
76
- create(context) {
77
- const [options] = context.options;
78
- const { allowInFiles = [], allowPatterns = [], suggestNamed = true, } = options || {};
79
- const filename = context.filename;
80
- function shouldAllow() {
81
- if (!filename) {
82
- return false;
83
- }
84
- const allowedByFile = allowInFiles.some((pattern) => {
85
- if (pattern.includes('*')) {
86
- const regex = new RegExp(pattern.replace(/\*/g, '.*'));
87
- return regex.test(filename);
88
- }
89
- return filename.includes(pattern);
90
- });
91
- if (allowedByFile) {
92
- return true;
93
- }
94
- return allowPatterns.some((pattern) => {
95
- if (pattern.includes('*')) {
96
- const regex = new RegExp(pattern.replace(/\*/g, '.*'));
97
- return regex.test(filename);
98
- }
99
- return filename.includes(pattern);
100
- });
101
- }
102
- function generateNamedExportSuggestion(node) {
103
- if (!suggestNamed) {
104
- return '';
105
- }
106
- const declaration = node.declaration;
107
- let suggestedName = 'exportedValue';
108
- if (declaration.type === 'Identifier') {
109
- suggestedName = declaration.name;
110
- }
111
- else if (declaration.type === 'FunctionDeclaration' && declaration.id) {
112
- suggestedName = declaration.id.name;
113
- }
114
- else if (declaration.type === 'ClassDeclaration' && declaration.id) {
115
- suggestedName = declaration.id.name;
116
- }
117
- else if (declaration.type === 'FunctionDeclaration' &&
118
- !declaration.id) {
119
- suggestedName = 'defaultFunction';
120
- }
121
- else if (declaration.type === 'ClassDeclaration' && !declaration.id) {
122
- suggestedName = 'DefaultClass';
123
- }
124
- return `// Convert to: export { ${suggestedName} };`;
125
- }
126
- return {
127
- ExportDefaultDeclaration(node) {
128
- if (shouldAllow()) {
129
- return;
130
- }
131
- const suggestion = generateNamedExportSuggestion(node);
132
- context.report({
133
- node,
134
- messageId: 'defaultExport',
135
- data: {
136
- currentFile: filename,
137
- suggestion: 'Use named export for better tree-shaking',
138
- example: suggestion,
139
- },
140
- fix(fixer) {
141
- const declaration = node.declaration;
142
- const sourceCode = context.sourceCode;
143
- if (declaration.type === 'Identifier') {
144
- return fixer.replaceText(node, `export { ${declaration.name} };`);
145
- }
146
- else if (declaration.type === 'Literal' ||
147
- declaration.type === 'ObjectExpression' ||
148
- declaration.type === 'ArrayExpression' ||
149
- declaration.type === 'ArrowFunctionExpression' ||
150
- declaration.type === 'CallExpression' ||
151
- declaration.type === 'NewExpression') {
152
- const exprText = sourceCode.getText(declaration);
153
- const namedExport = `const defaultExport = ${exprText};\nexport { defaultExport };`;
154
- return fixer.replaceText(node, namedExport);
155
- }
156
- else {
157
- const nodeText = sourceCode.getText(node);
158
- const namedExport = nodeText.replace(/^export default/, 'export');
159
- return fixer.replaceText(node, namedExport);
160
- }
161
- },
162
- });
163
- },
164
- ExportNamedDeclaration(node) {
165
- if (shouldAllow()) {
166
- return;
167
- }
168
- if (node.specifiers) {
169
- const hasDefaultSpecifier = node.specifiers.some((spec) => (spec.exported.type === 'Identifier' &&
170
- spec.exported.name === 'default') ||
171
- (spec.local &&
172
- spec.local.type === 'Identifier' &&
173
- spec.local.name === 'default'));
174
- if (hasDefaultSpecifier) {
175
- context.report({
176
- node,
177
- messageId: 'preferNamedExport',
178
- data: {
179
- pattern: 'export { default }',
180
- currentFile: filename,
181
- suggestion: 'Use named re-export instead',
182
- },
183
- });
184
- }
185
- }
186
- },
187
- TSExportAssignment(node) {
188
- if (shouldAllow()) {
189
- return;
190
- }
191
- context.report({
192
- node,
193
- messageId: 'defaultExportRefactor',
194
- data: {
195
- pattern: 'export = value',
196
- currentFile: filename,
197
- suggestion: 'Use ES6 named exports',
198
- },
199
- });
200
- },
201
- TSInterfaceDeclaration(node) {
202
- if (node.parent && node.parent.type === 'ExportDefaultDeclaration') {
203
- return;
204
- }
205
- },
206
- TSTypeAliasDeclaration(node) {
207
- if (node.parent && node.parent.type === eslint_devkit_1.AST_NODE_TYPES.ExportDefaultDeclaration) {
208
- return;
209
- }
210
- },
211
- };
212
- },
213
- });
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.noDefaultExport=void 0;const eslint_devkit_1=require("@interlace/eslint-devkit");const eslint_devkit_2=require("@interlace/eslint-devkit");exports.noDefaultExport=(0,eslint_devkit_1.createRule)({name:"no-default-export",meta:{type:"problem",docs:{url:"https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-import-next/docs/rules/no-default-export.md",description:"Prevents default exports"},fixable:"code",messages:{defaultExport:(0,eslint_devkit_2.formatLLMMessage)({icon:eslint_devkit_2.MessageIcons.ARCHITECTURE,issueName:"Default Export Detected",description:"Default export violates import style guidelines",severity:"MEDIUM",fix:"Use named export instead of default export",documentationLink:"https://github.com/import-js/eslint-plugin-import/blob/main/docs/rules/no-default-export.md"}),preferNamedExport:(0,eslint_devkit_2.formatLLMMessage)({icon:eslint_devkit_2.MessageIcons.ARCHITECTURE,issueName:"Prefer Named Exports",description:"Named exports provide better tree-shaking and refactoring",severity:"LOW",fix:"Convert default export to named export",documentationLink:"https://github.com/import-js/eslint-plugin-import/blob/main/docs/rules/no-default-export.md"}),defaultExportRefactor:(0,eslint_devkit_2.formatLLMMessage)({icon:eslint_devkit_2.MessageIcons.ARCHITECTURE,issueName:"Export Style Violation",description:"Default export pattern not allowed in this codebase",severity:"MEDIUM",fix:"Refactor to use named exports consistently",documentationLink:"https://github.com/import-js/eslint-plugin-import/blob/main/docs/rules/no-default-export.md"})},schema:[{type:"object",properties:{allowInFiles:{type:"array",items:{type:"string"},description:"Allow default exports in specific files."},allowPatterns:{type:"array",items:{type:"string"},description:"Allow default exports for specific patterns."},suggestNamed:{type:"boolean",default:true,description:"Suggest named export alternative."}},additionalProperties:false}]},defaultOptions:[{allowInFiles:[],allowPatterns:[],suggestNamed:true}],create(context){const[options]=context.options;const{allowInFiles=[],allowPatterns=[],suggestNamed=true}=options||{};const filename=context.filename;function shouldAllow(){if(!filename){return false}const allowedByFile=allowInFiles.some(pattern=>{if(pattern.includes("*")){const regex=new RegExp(pattern.replace(/\*/g,".*"));return regex.test(filename)}return filename.includes(pattern)});if(allowedByFile){return true}return allowPatterns.some(pattern=>{if(pattern.includes("*")){const regex=new RegExp(pattern.replace(/\*/g,".*"));return regex.test(filename)}return filename.includes(pattern)})}function generateNamedExportSuggestion(node){if(!suggestNamed){return""}const declaration=node.declaration;let suggestedName="exportedValue";if(declaration.type==="Identifier"){suggestedName=declaration.name}else if(declaration.type==="FunctionDeclaration"&&declaration.id){suggestedName=declaration.id.name}else if(declaration.type==="ClassDeclaration"&&declaration.id){suggestedName=declaration.id.name}else if(declaration.type==="FunctionDeclaration"&&!declaration.id){suggestedName="defaultFunction"}else if(declaration.type==="ClassDeclaration"&&!declaration.id){suggestedName="DefaultClass"}return`// Convert to: export { ${suggestedName} };`}return{ExportDefaultDeclaration(node){if(shouldAllow()){return}const suggestion=generateNamedExportSuggestion(node);context.report({node,messageId:"defaultExport",data:{currentFile:filename,suggestion:"Use named export for better tree-shaking",example:suggestion},fix(fixer){const declaration=node.declaration;const sourceCode=context.sourceCode;if(declaration.type==="Identifier"){return fixer.replaceText(node,`export { ${declaration.name} };`)}else if(declaration.type==="Literal"||declaration.type==="ObjectExpression"||declaration.type==="ArrayExpression"||declaration.type==="ArrowFunctionExpression"||declaration.type==="CallExpression"||declaration.type==="NewExpression"){const exprText=sourceCode.getText(declaration);const namedExport=`const defaultExport = ${exprText};
2
+ export { defaultExport };`;return fixer.replaceText(node,namedExport)}else{const nodeText=sourceCode.getText(node);const namedExport=nodeText.replace(/^export default/,"export");return fixer.replaceText(node,namedExport)}}})},ExportNamedDeclaration(node){if(shouldAllow()){return}if(node.specifiers){const hasDefaultSpecifier=node.specifiers.some(spec=>spec.exported.type==="Identifier"&&spec.exported.name==="default"||spec.local&&spec.local.type==="Identifier"&&spec.local.name==="default");if(hasDefaultSpecifier){context.report({node,messageId:"preferNamedExport",data:{pattern:"export { default }",currentFile:filename,suggestion:"Use named re-export instead"}})}}},TSExportAssignment(node){if(shouldAllow()){return}context.report({node,messageId:"defaultExportRefactor",data:{pattern:"export = value",currentFile:filename,suggestion:"Use ES6 named exports"}})},TSInterfaceDeclaration(node){if(node.parent&&node.parent.type==="ExportDefaultDeclaration"){return}},TSTypeAliasDeclaration(node){if(node.parent&&node.parent.type===eslint_devkit_1.AST_NODE_TYPES.ExportDefaultDeclaration){return}}}}});