eslint-plugin-import-next 2.3.14 → 2.3.16

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 (62) hide show
  1. package/package.json +9 -4
  2. package/src/files/foo.js +1 -6
  3. package/src/files/no-default.js +1 -4
  4. package/src/index.js +1 -326
  5. package/src/oxlint.js +1 -3
  6. package/src/rules/consistent-type-specifier-style.js +1 -109
  7. package/src/rules/default.js +1 -56
  8. package/src/rules/dynamic-import-chunkname.js +1 -115
  9. package/src/rules/enforce-dependency-direction.js +1 -213
  10. package/src/rules/enforce-import-order.js +1 -299
  11. package/src/rules/enforce-team-boundaries.js +1 -191
  12. package/src/rules/export.js +1 -111
  13. package/src/rules/exports-last.js +1 -67
  14. package/src/rules/extensions.js +1 -105
  15. package/src/rules/first.js +1 -53
  16. package/src/rules/group-exports.js +1 -69
  17. package/src/rules/max-dependencies.js +1 -241
  18. package/src/rules/named.js +1 -67
  19. package/src/rules/namespace.js +1 -64
  20. package/src/rules/newline-after-import.js +1 -57
  21. package/src/rules/no-absolute-path.js +1 -107
  22. package/src/rules/no-amd.js +1 -108
  23. package/src/rules/no-anonymous-default-export.js +1 -135
  24. package/src/rules/no-barrel-file.js +1 -194
  25. package/src/rules/no-barrel-import.js +1 -135
  26. package/src/rules/no-commonjs.js +1 -312
  27. package/src/rules/no-cross-domain-imports.js +1 -233
  28. package/src/rules/no-cycle.js +2 -396
  29. package/src/rules/no-default-export.js +2 -213
  30. package/src/rules/no-deprecated.js +1 -316
  31. package/src/rules/no-duplicates.js +1 -81
  32. package/src/rules/no-dynamic-require.js +1 -78
  33. package/src/rules/no-empty-named-blocks.js +1 -103
  34. package/src/rules/no-extraneous-dependencies.js +7 -385
  35. package/src/rules/no-full-package-import.js +1 -134
  36. package/src/rules/no-import-module-exports.js +1 -76
  37. package/src/rules/no-internal-modules.js +1 -256
  38. package/src/rules/no-legacy-imports.js +1 -125
  39. package/src/rules/no-mutable-exports.js +1 -146
  40. package/src/rules/no-named-as-default-member.js +1 -73
  41. package/src/rules/no-named-as-default.js +1 -90
  42. package/src/rules/no-named-default.js +1 -48
  43. package/src/rules/no-named-export.js +1 -174
  44. package/src/rules/no-namespace.js +1 -71
  45. package/src/rules/no-nodejs-modules.js +1 -229
  46. package/src/rules/no-relative-packages.js +1 -129
  47. package/src/rules/no-relative-parent-imports.js +1 -118
  48. package/src/rules/no-restricted-paths.js +1 -89
  49. package/src/rules/no-self-import.js +1 -113
  50. package/src/rules/no-unassigned-import.js +1 -115
  51. package/src/rules/no-unresolved.js +1 -189
  52. package/src/rules/no-unused-modules.js +1 -82
  53. package/src/rules/no-useless-path-segments.js +1 -107
  54. package/src/rules/prefer-default-export.js +1 -188
  55. package/src/rules/prefer-direct-import.js +1 -170
  56. package/src/rules/prefer-modern-api.js +1 -212
  57. package/src/rules/prefer-node-protocol.js +1 -174
  58. package/src/rules/prefer-tree-shakeable-imports.js +1 -134
  59. package/src/rules/require-import-approval.js +1 -174
  60. package/src/rules/unambiguous.js +1 -70
  61. package/src/types/index.js +1 -2
  62. 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}}}}});