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,108 +1 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.noAmd = void 0;
4
- const eslint_devkit_1 = require("@interlace/eslint-devkit");
5
- const eslint_devkit_2 = require("@interlace/eslint-devkit");
6
- exports.noAmd = (0, eslint_devkit_1.createRule)({
7
- name: 'no-amd',
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-amd.md',
12
- description: 'Prevents AMD require/define calls',
13
- },
14
- hasSuggestions: false,
15
- messages: {
16
- preferES6: (0, eslint_devkit_2.formatLLMMessage)({
17
- icon: eslint_devkit_2.MessageIcons.DEVELOPMENT,
18
- issueName: 'AMD Module',
19
- description: 'AMD module detected',
20
- severity: 'MEDIUM',
21
- fix: 'Use ES6 import/export syntax instead of AMD',
22
- documentationLink: 'https://github.com/import-js/eslint-plugin-import/blob/main/docs/rules/no-amd.md',
23
- }),
24
- },
25
- schema: [
26
- {
27
- type: 'object',
28
- properties: {
29
- allow: {
30
- type: 'array',
31
- items: {
32
- type: 'string',
33
- },
34
- description: 'AMD patterns to allow.',
35
- },
36
- suggestES6: {
37
- type: 'boolean',
38
- default: true,
39
- description: 'Suggest ES6 imports instead.',
40
- },
41
- },
42
- additionalProperties: false,
43
- },
44
- ],
45
- },
46
- defaultOptions: [
47
- {
48
- allow: [],
49
- suggestES6: true,
50
- },
51
- ],
52
- create(context) {
53
- const [options] = context.options;
54
- const { allow = [], } = options || {};
55
- const filename = context.filename;
56
- function shouldAllow() {
57
- return allow.some((pattern) => {
58
- if (!filename)
59
- return false;
60
- if (pattern.includes('**')) {
61
- const regexPattern = pattern
62
- .replace(/\*\*/g, '.*')
63
- .replace(/\*/g, '[^/]*');
64
- const regex = new RegExp(`^${regexPattern}$`);
65
- return regex.test(filename);
66
- }
67
- return filename.includes(pattern);
68
- });
69
- }
70
- return {
71
- CallExpression(node) {
72
- if (shouldAllow()) {
73
- return;
74
- }
75
- if (node.callee.type === 'Identifier' &&
76
- node.callee.name === 'define') {
77
- context.report({
78
- node: node.callee,
79
- messageId: 'preferES6',
80
- data: {
81
- functionName: 'define',
82
- currentFile: filename,
83
- suggestion: 'Use ES6 export default or export {} syntax',
84
- },
85
- });
86
- }
87
- else if (node.callee.type === 'Identifier' &&
88
- node.callee.name === 'require' &&
89
- node.arguments.length > 0) {
90
- const firstArg = node.arguments[0];
91
- const hasCallback = node.arguments.length > 1 &&
92
- node.arguments[1]?.type === 'FunctionExpression';
93
- if (firstArg.type === 'ArrayExpression' && hasCallback) {
94
- context.report({
95
- node: node.callee,
96
- messageId: 'preferES6',
97
- data: {
98
- functionName: 'require',
99
- currentFile: filename,
100
- suggestion: 'Use ES6 import statements at the top of the file',
101
- },
102
- });
103
- }
104
- }
105
- },
106
- };
107
- },
108
- });
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.noAmd=void 0;const eslint_devkit_1=require("@interlace/eslint-devkit");const eslint_devkit_2=require("@interlace/eslint-devkit");exports.noAmd=(0,eslint_devkit_1.createRule)({name:"no-amd",meta:{type:"problem",docs:{url:"https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-import-next/docs/rules/no-amd.md",description:"Prevents AMD require/define calls"},hasSuggestions:false,messages:{preferES6:(0,eslint_devkit_2.formatLLMMessage)({icon:eslint_devkit_2.MessageIcons.DEVELOPMENT,issueName:"AMD Module",description:"AMD module detected",severity:"MEDIUM",fix:"Use ES6 import/export syntax instead of AMD",documentationLink:"https://github.com/import-js/eslint-plugin-import/blob/main/docs/rules/no-amd.md"})},schema:[{type:"object",properties:{allow:{type:"array",items:{type:"string"},description:"AMD patterns to allow."},suggestES6:{type:"boolean",default:true,description:"Suggest ES6 imports instead."}},additionalProperties:false}]},defaultOptions:[{allow:[],suggestES6:true}],create(context){const[options]=context.options;const{allow=[]}=options||{};const filename=context.filename;function shouldAllow(){return allow.some(pattern=>{if(!filename)return false;if(pattern.includes("**")){const regexPattern=pattern.replace(/\*\*/g,".*").replace(/\*/g,"[^/]*");const regex=new RegExp(`^${regexPattern}$`);return regex.test(filename)}return filename.includes(pattern)})}return{CallExpression(node){if(shouldAllow()){return}if(node.callee.type==="Identifier"&&node.callee.name==="define"){context.report({node:node.callee,messageId:"preferES6",data:{functionName:"define",currentFile:filename,suggestion:"Use ES6 export default or export {} syntax"}})}else if(node.callee.type==="Identifier"&&node.callee.name==="require"&&node.arguments.length>0){const firstArg=node.arguments[0];const hasCallback=node.arguments.length>1&&node.arguments[1]?.type==="FunctionExpression";if(firstArg.type==="ArrayExpression"&&hasCallback){context.report({node:node.callee,messageId:"preferES6",data:{functionName:"require",currentFile:filename,suggestion:"Use ES6 import statements at the top of the file"}})}}}}}});
@@ -1,135 +1 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.noAnonymousDefaultExport = void 0;
4
- const eslint_devkit_1 = require("@interlace/eslint-devkit");
5
- const eslint_devkit_2 = require("@interlace/eslint-devkit");
6
- exports.noAnonymousDefaultExport = (0, eslint_devkit_1.createRule)({
7
- name: 'no-anonymous-default-export',
8
- meta: {
9
- type: 'suggestion',
10
- docs: {
11
- url: 'https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-import-next/docs/rules/no-anonymous-default-export.md',
12
- description: 'Forbid anonymous values as default exports',
13
- },
14
- messages: {
15
- anonymousDefaultExport: (0, eslint_devkit_2.formatLLMMessage)({
16
- icon: eslint_devkit_2.MessageIcons.WARNING,
17
- issueName: 'Anonymous Default Export',
18
- description: 'Default export should be named for better debugging',
19
- severity: 'MEDIUM',
20
- fix: "Choose a meaningful name based on the function's purpose (e.g., handleSubmit, processData, validateInput)",
21
- documentationLink: 'https://github.com/import-js/eslint-plugin-import/blob/main/docs/rules/no-anonymous-default-export.md',
22
- }),
23
- suggestNamedExport: (0, eslint_devkit_2.formatLLMMessage)({
24
- icon: eslint_devkit_2.MessageIcons.ARCHITECTURE,
25
- issueName: 'Name Anonymous Function',
26
- description: 'Choose a descriptive name for this anonymous function',
27
- severity: 'MEDIUM',
28
- fix: 'Add a meaningful name like handleData, processInput, or validateForm',
29
- documentationLink: 'https://github.com/import-js/eslint-plugin-import/blob/main/docs/rules/no-anonymous-default-export.md',
30
- }),
31
- useNamedExport: (0, eslint_devkit_2.formatLLMMessage)({
32
- icon: eslint_devkit_2.MessageIcons.ARCHITECTURE,
33
- issueName: 'Use Named Export Pattern',
34
- description: 'Convert anonymous default export to named export',
35
- severity: 'MEDIUM',
36
- fix: 'Use named export: export function componentName() {}',
37
- documentationLink: 'https://github.com/import-js/eslint-plugin-import/blob/main/docs/rules/no-anonymous-default-export.md',
38
- }),
39
- arrowFunctionExport: (0, eslint_devkit_2.formatLLMMessage)({
40
- icon: eslint_devkit_2.MessageIcons.ARCHITECTURE,
41
- issueName: 'Arrow Function Export',
42
- description: 'Arrow function should be named for better debugging',
43
- severity: 'MEDIUM',
44
- fix: "Choose a meaningful name based on the function's purpose (e.g., handleSubmit, processData, validateInput)",
45
- documentationLink: 'https://github.com/import-js/eslint-plugin-import/blob/main/docs/rules/no-anonymous-default-export.md',
46
- }),
47
- },
48
- schema: [
49
- {
50
- type: 'object',
51
- properties: {
52
- allowArrowFunction: {
53
- type: 'boolean',
54
- default: false,
55
- description: 'Allow anonymous arrow function default exports.',
56
- },
57
- allowClassExpression: {
58
- type: 'boolean',
59
- default: false,
60
- description: 'Allow anonymous class expression default exports.',
61
- },
62
- allowFunctionExpression: {
63
- type: 'boolean',
64
- default: false,
65
- description: 'Allow anonymous function expression default exports.',
66
- },
67
- },
68
- additionalProperties: false,
69
- },
70
- ],
71
- },
72
- defaultOptions: [
73
- {
74
- allowArrowFunction: false,
75
- allowClassExpression: false,
76
- allowFunctionExpression: false,
77
- },
78
- ],
79
- create(context) {
80
- const [options] = context.options;
81
- const { allowArrowFunction = false, allowClassExpression = false, allowFunctionExpression = false, } = options || {};
82
- function isAnonymous(node) {
83
- const declaration = node.declaration;
84
- switch (declaration.type) {
85
- case 'ArrowFunctionExpression':
86
- return !allowArrowFunction;
87
- case 'ClassExpression':
88
- return !allowClassExpression;
89
- case 'FunctionExpression':
90
- return !allowFunctionExpression;
91
- case 'FunctionDeclaration':
92
- return declaration.id ? false : !allowFunctionExpression;
93
- case 'ClassDeclaration':
94
- return declaration.id ? false : !allowClassExpression;
95
- case 'CallExpression':
96
- return false;
97
- case 'Identifier':
98
- case 'Literal':
99
- case 'ObjectExpression':
100
- case 'ArrayExpression':
101
- return false;
102
- default:
103
- return true;
104
- }
105
- }
106
- function getSuggestionText(node) {
107
- const declaration = node.declaration;
108
- switch (declaration.type) {
109
- case 'ArrowFunctionExpression':
110
- return 'Convert to named arrow function: export default function componentName() {}';
111
- case 'FunctionExpression':
112
- return 'Add name to function: export default function componentName() {}';
113
- case 'ClassExpression':
114
- return 'Add name to class: export default class ComponentName {}';
115
- default:
116
- return 'Use named export instead';
117
- }
118
- }
119
- return {
120
- ExportDefaultDeclaration(node) {
121
- if (isAnonymous(node)) {
122
- context.report({
123
- node,
124
- messageId: 'anonymousDefaultExport',
125
- data: {
126
- exportType: node.declaration.type,
127
- suggestion: getSuggestionText(node),
128
- benefit: 'Named exports improve debugging, tree-shaking, and code navigation',
129
- },
130
- });
131
- }
132
- },
133
- };
134
- },
135
- });
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.noAnonymousDefaultExport=void 0;const eslint_devkit_1=require("@interlace/eslint-devkit");const eslint_devkit_2=require("@interlace/eslint-devkit");exports.noAnonymousDefaultExport=(0,eslint_devkit_1.createRule)({name:"no-anonymous-default-export",meta:{type:"suggestion",docs:{url:"https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-import-next/docs/rules/no-anonymous-default-export.md",description:"Forbid anonymous values as default exports"},messages:{anonymousDefaultExport:(0,eslint_devkit_2.formatLLMMessage)({icon:eslint_devkit_2.MessageIcons.WARNING,issueName:"Anonymous Default Export",description:"Default export should be named for better debugging",severity:"MEDIUM",fix:"Choose a meaningful name based on the function's purpose (e.g., handleSubmit, processData, validateInput)",documentationLink:"https://github.com/import-js/eslint-plugin-import/blob/main/docs/rules/no-anonymous-default-export.md"}),suggestNamedExport:(0,eslint_devkit_2.formatLLMMessage)({icon:eslint_devkit_2.MessageIcons.ARCHITECTURE,issueName:"Name Anonymous Function",description:"Choose a descriptive name for this anonymous function",severity:"MEDIUM",fix:"Add a meaningful name like handleData, processInput, or validateForm",documentationLink:"https://github.com/import-js/eslint-plugin-import/blob/main/docs/rules/no-anonymous-default-export.md"}),useNamedExport:(0,eslint_devkit_2.formatLLMMessage)({icon:eslint_devkit_2.MessageIcons.ARCHITECTURE,issueName:"Use Named Export Pattern",description:"Convert anonymous default export to named export",severity:"MEDIUM",fix:"Use named export: export function componentName() {}",documentationLink:"https://github.com/import-js/eslint-plugin-import/blob/main/docs/rules/no-anonymous-default-export.md"}),arrowFunctionExport:(0,eslint_devkit_2.formatLLMMessage)({icon:eslint_devkit_2.MessageIcons.ARCHITECTURE,issueName:"Arrow Function Export",description:"Arrow function should be named for better debugging",severity:"MEDIUM",fix:"Choose a meaningful name based on the function's purpose (e.g., handleSubmit, processData, validateInput)",documentationLink:"https://github.com/import-js/eslint-plugin-import/blob/main/docs/rules/no-anonymous-default-export.md"})},schema:[{type:"object",properties:{allowArrowFunction:{type:"boolean",default:false,description:"Allow anonymous arrow function default exports."},allowClassExpression:{type:"boolean",default:false,description:"Allow anonymous class expression default exports."},allowFunctionExpression:{type:"boolean",default:false,description:"Allow anonymous function expression default exports."}},additionalProperties:false}]},defaultOptions:[{allowArrowFunction:false,allowClassExpression:false,allowFunctionExpression:false}],create(context){const[options]=context.options;const{allowArrowFunction=false,allowClassExpression=false,allowFunctionExpression=false}=options||{};function isAnonymous(node){const declaration=node.declaration;switch(declaration.type){case"ArrowFunctionExpression":return!allowArrowFunction;case"ClassExpression":return!allowClassExpression;case"FunctionExpression":return!allowFunctionExpression;case"FunctionDeclaration":return declaration.id?false:!allowFunctionExpression;case"ClassDeclaration":return declaration.id?false:!allowClassExpression;case"CallExpression":return false;case"Identifier":case"Literal":case"ObjectExpression":case"ArrayExpression":return false;default:return true}}function getSuggestionText(node){const declaration=node.declaration;switch(declaration.type){case"ArrowFunctionExpression":return"Convert to named arrow function: export default function componentName() {}";case"FunctionExpression":return"Add name to function: export default function componentName() {}";case"ClassExpression":return"Add name to class: export default class ComponentName {}";default:return"Use named export instead"}}return{ExportDefaultDeclaration(node){if(isAnonymous(node)){context.report({node,messageId:"anonymousDefaultExport",data:{exportType:node.declaration.type,suggestion:getSuggestionText(node),benefit:"Named exports improve debugging, tree-shaking, and code navigation"}})}}}}});
@@ -1,194 +1 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.noBarrelFile = void 0;
4
- const eslint_devkit_1 = require("@interlace/eslint-devkit");
5
- const DEFAULT_BARREL_PATTERNS = [
6
- '.*[/\\\\]index\\.(ts|tsx|js|jsx|mts|mjs)$',
7
- ];
8
- function matchesPattern(filePath, patterns) {
9
- return patterns.some((pattern) => {
10
- try {
11
- const regex = new RegExp(pattern);
12
- return regex.test(filePath);
13
- }
14
- catch {
15
- return filePath.includes(pattern);
16
- }
17
- });
18
- }
19
- function isReexport(node) {
20
- if (node.type === eslint_devkit_1.AST_NODE_TYPES.ExportAllDeclaration) {
21
- return true;
22
- }
23
- if (node.type === eslint_devkit_1.AST_NODE_TYPES.ExportNamedDeclaration &&
24
- node.source !== null) {
25
- return true;
26
- }
27
- return false;
28
- }
29
- function isLocalExport(node) {
30
- if (node.type === eslint_devkit_1.AST_NODE_TYPES.ExportNamedDeclaration) {
31
- return node.source === null && node.declaration !== null;
32
- }
33
- if (node.type === eslint_devkit_1.AST_NODE_TYPES.ExportDefaultDeclaration) {
34
- return true;
35
- }
36
- return false;
37
- }
38
- function countReexportSources(reexports, namedReexports) {
39
- const sources = new Set();
40
- for (const node of reexports) {
41
- if (node.source?.value) {
42
- sources.add(String(node.source.value));
43
- }
44
- }
45
- for (const node of namedReexports) {
46
- if (node.source?.value) {
47
- sources.add(String(node.source.value));
48
- }
49
- }
50
- return sources.size;
51
- }
52
- exports.noBarrelFile = (0, eslint_devkit_1.createRule)({
53
- name: 'no-barrel-file',
54
- meta: {
55
- type: 'problem',
56
- docs: {
57
- url: 'https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-import-next/docs/rules/no-barrel-file.md',
58
- description: 'Disallow barrel files that harm build performance and tree-shaking efficiency',
59
- },
60
- hasSuggestions: true,
61
- messages: {
62
- barrelFileDetected: (0, eslint_devkit_1.formatLLMMessage)({
63
- icon: eslint_devkit_1.MessageIcons.SECURITY,
64
- issueName: 'Barrel File Detected',
65
- description: 'This file re-exports {{reexportCount}} modules without adding local logic. ' +
66
- 'Barrel files severely impact build performance and prevent effective tree-shaking.',
67
- severity: 'MEDIUM',
68
- fix: 'Replace barrel pattern with direct imports. Instead of: ' +
69
- "import { Button } from '@/components'; " +
70
- "Use: import { Button } from '@/components/Button';",
71
- documentationLink: 'https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-import-next/docs/rules/no-barrel-file.md',
72
- }),
73
- considerDirectExports: (0, eslint_devkit_1.formatLLMMessage)({
74
- icon: eslint_devkit_1.MessageIcons.INFO,
75
- issueName: 'Consider Direct Exports',
76
- description: 'This file has a high ratio of re-exports ({{ratio}}%). Consider using direct imports for better tree-shaking.',
77
- severity: 'LOW',
78
- fix: 'Evaluate if consumers can import directly from source modules instead of through this barrel file.',
79
- documentationLink: 'https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-import-next/docs/rules/no-barrel-file.md',
80
- }),
81
- },
82
- schema: [
83
- {
84
- type: 'object',
85
- properties: {
86
- threshold: {
87
- type: 'number',
88
- minimum: 1,
89
- default: 3,
90
- description: 'Minimum number of re-exports to consider a file a barrel',
91
- },
92
- allowedPaths: {
93
- type: 'array',
94
- items: { type: 'string' },
95
- default: [],
96
- description: 'Regex patterns for paths where barrel files are allowed',
97
- },
98
- barrelPatterns: {
99
- type: 'array',
100
- items: { type: 'string' },
101
- default: DEFAULT_BARREL_PATTERNS,
102
- description: 'File patterns considered barrel candidates',
103
- },
104
- allowWithLocalExports: {
105
- type: 'boolean',
106
- default: false,
107
- description: 'Allow barrel files that also export local declarations',
108
- },
109
- reexportRatio: {
110
- type: 'number',
111
- minimum: 0,
112
- maximum: 1,
113
- default: 0.8,
114
- description: 'Maximum allowed ratio of re-exports to total exports',
115
- },
116
- },
117
- additionalProperties: false,
118
- },
119
- ],
120
- },
121
- defaultOptions: [
122
- {
123
- threshold: 3,
124
- allowedPaths: [],
125
- barrelPatterns: DEFAULT_BARREL_PATTERNS,
126
- allowWithLocalExports: false,
127
- reexportRatio: 0.8,
128
- },
129
- ],
130
- create(context, [options = {}]) {
131
- const { threshold = 3, allowedPaths = [], barrelPatterns = DEFAULT_BARREL_PATTERNS, allowWithLocalExports = false, reexportRatio = 0.8, } = options || {};
132
- const filename = context.filename;
133
- if (!matchesPattern(filename, barrelPatterns)) {
134
- return {};
135
- }
136
- if (allowedPaths.length > 0 && matchesPattern(filename, allowedPaths)) {
137
- return {};
138
- }
139
- const exportAllDeclarations = [];
140
- const namedReexports = [];
141
- const localExports = [];
142
- return {
143
- ExportAllDeclaration(node) {
144
- exportAllDeclarations.push(node);
145
- },
146
- ExportNamedDeclaration(node) {
147
- if (isReexport(node)) {
148
- namedReexports.push(node);
149
- }
150
- else if (isLocalExport(node)) {
151
- localExports.push(node);
152
- }
153
- },
154
- ExportDefaultDeclaration(node) {
155
- if (isLocalExport(node)) {
156
- localExports.push(node);
157
- }
158
- },
159
- 'Program:exit'(node) {
160
- const totalReexports = exportAllDeclarations.length + namedReexports.length;
161
- const totalExports = totalReexports + localExports.length;
162
- if (totalExports === 0) {
163
- return;
164
- }
165
- const sourceCount = countReexportSources(exportAllDeclarations, namedReexports);
166
- const hasLocalExports = localExports.length > 0;
167
- if (allowWithLocalExports && hasLocalExports) {
168
- return;
169
- }
170
- const ratio = totalReexports / totalExports;
171
- if (sourceCount >= threshold && !hasLocalExports) {
172
- context.report({
173
- node,
174
- messageId: 'barrelFileDetected',
175
- data: {
176
- reexportCount: String(sourceCount),
177
- },
178
- });
179
- }
180
- else if (hasLocalExports &&
181
- sourceCount >= threshold &&
182
- ratio >= reexportRatio) {
183
- context.report({
184
- node,
185
- messageId: 'considerDirectExports',
186
- data: {
187
- ratio: String(Math.round(ratio * 100)),
188
- },
189
- });
190
- }
191
- },
192
- };
193
- },
194
- });
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.noBarrelFile=void 0;const eslint_devkit_1=require("@interlace/eslint-devkit");const DEFAULT_BARREL_PATTERNS=[".*[/\\\\]index\\.(ts|tsx|js|jsx|mts|mjs)$"];function matchesPattern(filePath,patterns){return patterns.some(pattern=>{try{const regex=new RegExp(pattern);return regex.test(filePath)}catch{return filePath.includes(pattern)}})}function isReexport(node){if(node.type===eslint_devkit_1.AST_NODE_TYPES.ExportAllDeclaration){return true}if(node.type===eslint_devkit_1.AST_NODE_TYPES.ExportNamedDeclaration&&node.source!==null){return true}return false}function isLocalExport(node){if(node.type===eslint_devkit_1.AST_NODE_TYPES.ExportNamedDeclaration){return node.source===null&&node.declaration!==null}if(node.type===eslint_devkit_1.AST_NODE_TYPES.ExportDefaultDeclaration){return true}return false}function countReexportSources(reexports,namedReexports){const sources=new Set;for(const node of reexports){if(node.source?.value){sources.add(String(node.source.value))}}for(const node of namedReexports){if(node.source?.value){sources.add(String(node.source.value))}}return sources.size}exports.noBarrelFile=(0,eslint_devkit_1.createRule)({name:"no-barrel-file",meta:{type:"problem",docs:{url:"https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-import-next/docs/rules/no-barrel-file.md",description:"Disallow barrel files that harm build performance and tree-shaking efficiency"},hasSuggestions:true,messages:{barrelFileDetected:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.SECURITY,issueName:"Barrel File Detected",description:"This file re-exports {{reexportCount}} modules without adding local logic. Barrel files severely impact build performance and prevent effective tree-shaking.",severity:"MEDIUM",fix:"Replace barrel pattern with direct imports. Instead of: import { Button } from '@/components'; Use: import { Button } from '@/components/Button';",documentationLink:"https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-import-next/docs/rules/no-barrel-file.md"}),considerDirectExports:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.INFO,issueName:"Consider Direct Exports",description:"This file has a high ratio of re-exports ({{ratio}}%). Consider using direct imports for better tree-shaking.",severity:"LOW",fix:"Evaluate if consumers can import directly from source modules instead of through this barrel file.",documentationLink:"https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-import-next/docs/rules/no-barrel-file.md"})},schema:[{type:"object",properties:{threshold:{type:"number",minimum:1,default:3,description:"Minimum number of re-exports to consider a file a barrel"},allowedPaths:{type:"array",items:{type:"string"},default:[],description:"Regex patterns for paths where barrel files are allowed"},barrelPatterns:{type:"array",items:{type:"string"},default:DEFAULT_BARREL_PATTERNS,description:"File patterns considered barrel candidates"},allowWithLocalExports:{type:"boolean",default:false,description:"Allow barrel files that also export local declarations"},reexportRatio:{type:"number",minimum:0,maximum:1,default:.8,description:"Maximum allowed ratio of re-exports to total exports"}},additionalProperties:false}]},defaultOptions:[{threshold:3,allowedPaths:[],barrelPatterns:DEFAULT_BARREL_PATTERNS,allowWithLocalExports:false,reexportRatio:.8}],create(context,[options={}]){const{threshold=3,allowedPaths=[],barrelPatterns=DEFAULT_BARREL_PATTERNS,allowWithLocalExports=false,reexportRatio=.8}=options||{};const filename=context.filename;if(!matchesPattern(filename,barrelPatterns)){return{}}if(allowedPaths.length>0&&matchesPattern(filename,allowedPaths)){return{}}const exportAllDeclarations=[];const namedReexports=[];const localExports=[];return{ExportAllDeclaration(node){exportAllDeclarations.push(node)},ExportNamedDeclaration(node){if(isReexport(node)){namedReexports.push(node)}else if(isLocalExport(node)){localExports.push(node)}},ExportDefaultDeclaration(node){if(isLocalExport(node)){localExports.push(node)}},"Program:exit"(node){const totalReexports=exportAllDeclarations.length+namedReexports.length;const totalExports=totalReexports+localExports.length;if(totalExports===0){return}const sourceCount=countReexportSources(exportAllDeclarations,namedReexports);const hasLocalExports=localExports.length>0;if(allowWithLocalExports&&hasLocalExports){return}const ratio=totalReexports/totalExports;if(sourceCount>=threshold&&!hasLocalExports){context.report({node,messageId:"barrelFileDetected",data:{reexportCount:String(sourceCount)}})}else if(hasLocalExports&&sourceCount>=threshold&&ratio>=reexportRatio){context.report({node,messageId:"considerDirectExports",data:{ratio:String(Math.round(ratio*100))}})}}}}});
@@ -1,135 +1 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.noBarrelImport = void 0;
4
- const eslint_devkit_1 = require("@interlace/eslint-devkit");
5
- function matchesPattern(importPath, patterns) {
6
- return patterns.some((pattern) => {
7
- try {
8
- const regex = new RegExp(pattern);
9
- return regex.test(importPath);
10
- }
11
- catch {
12
- return importPath === pattern;
13
- }
14
- });
15
- }
16
- function isNodeModulesImport(importPath) {
17
- if (importPath.startsWith('.') || importPath.startsWith('/')) {
18
- return false;
19
- }
20
- return true;
21
- }
22
- function endsWithIndex(importPath) {
23
- return /\/index(\.[mc]?[jt]sx?)?$/.test(importPath);
24
- }
25
- exports.noBarrelImport = (0, eslint_devkit_1.createRule)({
26
- name: 'no-barrel-import',
27
- meta: {
28
- type: 'suggestion',
29
- docs: {
30
- url: 'https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-import-next/docs/rules/no-barrel-import.md',
31
- description: 'Disallow imports from barrel files to improve build performance and tree-shaking',
32
- },
33
- hasSuggestions: true,
34
- messages: {
35
- barrelImportDetected: (0, eslint_devkit_1.formatLLMMessage)({
36
- icon: eslint_devkit_1.MessageIcons.INFO,
37
- issueName: 'Barrel Import Detected',
38
- description: "Importing from '{{importPath}}' loads an index file that may include unused modules. " +
39
- 'Barrel imports harm build performance and prevent effective tree-shaking.',
40
- severity: 'LOW',
41
- fix: 'Import directly from the source module instead. For example:\n' +
42
- " ❌ import { Button } from '@/components';\n" +
43
- " ❌ import { Button } from '@/components/index';\n" +
44
- " ✅ import { Button } from '@/components/Button';",
45
- documentationLink: 'https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-import-next/docs/rules/no-barrel-import.md',
46
- }),
47
- },
48
- schema: [
49
- {
50
- type: 'object',
51
- properties: {
52
- knownBarrels: {
53
- type: 'array',
54
- items: { type: 'string' },
55
- default: [],
56
- description: 'Regex patterns for paths known to be barrels',
57
- },
58
- ignoreNodeModules: {
59
- type: 'boolean',
60
- default: true,
61
- description: 'Ignore imports from node_modules',
62
- },
63
- ignoredPackages: {
64
- type: 'array',
65
- items: { type: 'string' },
66
- default: [],
67
- description: 'Packages to ignore (good tree-shaking support)',
68
- },
69
- },
70
- additionalProperties: false,
71
- },
72
- ],
73
- },
74
- defaultOptions: [
75
- {
76
- knownBarrels: [],
77
- ignoreNodeModules: true,
78
- ignoredPackages: [],
79
- },
80
- ],
81
- create(context, [options = {}]) {
82
- const { knownBarrels = [], ignoreNodeModules = true, ignoredPackages = [], } = options || {};
83
- function checkImportPath(node, importPath) {
84
- if (ignoreNodeModules && isNodeModulesImport(importPath)) {
85
- if (ignoredPackages.length > 0 && matchesPattern(importPath, ignoredPackages)) {
86
- return;
87
- }
88
- if (knownBarrels.length === 0) {
89
- return;
90
- }
91
- }
92
- if (knownBarrels.length > 0 && matchesPattern(importPath, knownBarrels)) {
93
- context.report({
94
- node,
95
- messageId: 'barrelImportDetected',
96
- data: {
97
- importPath,
98
- },
99
- });
100
- return;
101
- }
102
- if (endsWithIndex(importPath)) {
103
- context.report({
104
- node,
105
- messageId: 'barrelImportDetected',
106
- data: {
107
- importPath,
108
- },
109
- });
110
- }
111
- }
112
- return {
113
- ImportDeclaration(node) {
114
- const importPath = node.source.value;
115
- if (typeof importPath === 'string') {
116
- checkImportPath(node, importPath);
117
- }
118
- },
119
- ExportAllDeclaration(node) {
120
- const importPath = node.source.value;
121
- if (typeof importPath === 'string') {
122
- checkImportPath(node, importPath);
123
- }
124
- },
125
- ExportNamedDeclaration(node) {
126
- if (node.source) {
127
- const importPath = node.source.value;
128
- if (typeof importPath === 'string') {
129
- checkImportPath(node, importPath);
130
- }
131
- }
132
- },
133
- };
134
- },
135
- });
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.noBarrelImport=void 0;const eslint_devkit_1=require("@interlace/eslint-devkit");function matchesPattern(importPath,patterns){return patterns.some(pattern=>{try{const regex=new RegExp(pattern);return regex.test(importPath)}catch{return importPath===pattern}})}function isNodeModulesImport(importPath){if(importPath.startsWith(".")||importPath.startsWith("/")){return false}return true}function endsWithIndex(importPath){return/\/index(\.[mc]?[jt]sx?)?$/.test(importPath)}exports.noBarrelImport=(0,eslint_devkit_1.createRule)({name:"no-barrel-import",meta:{type:"suggestion",docs:{url:"https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-import-next/docs/rules/no-barrel-import.md",description:"Disallow imports from barrel files to improve build performance and tree-shaking"},hasSuggestions:true,messages:{barrelImportDetected:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.INFO,issueName:"Barrel Import Detected",description:"Importing from '{{importPath}}' loads an index file that may include unused modules. Barrel imports harm build performance and prevent effective tree-shaking.",severity:"LOW",fix:"Import directly from the source module instead. For example:\n \u274C import { Button } from '@/components';\n \u274C import { Button } from '@/components/index';\n \u2705 import { Button } from '@/components/Button';",documentationLink:"https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-import-next/docs/rules/no-barrel-import.md"})},schema:[{type:"object",properties:{knownBarrels:{type:"array",items:{type:"string"},default:[],description:"Regex patterns for paths known to be barrels"},ignoreNodeModules:{type:"boolean",default:true,description:"Ignore imports from node_modules"},ignoredPackages:{type:"array",items:{type:"string"},default:[],description:"Packages to ignore (good tree-shaking support)"}},additionalProperties:false}]},defaultOptions:[{knownBarrels:[],ignoreNodeModules:true,ignoredPackages:[]}],create(context,[options={}]){const{knownBarrels=[],ignoreNodeModules=true,ignoredPackages=[]}=options||{};function checkImportPath(node,importPath){if(ignoreNodeModules&&isNodeModulesImport(importPath)){if(ignoredPackages.length>0&&matchesPattern(importPath,ignoredPackages)){return}if(knownBarrels.length===0){return}}if(knownBarrels.length>0&&matchesPattern(importPath,knownBarrels)){context.report({node,messageId:"barrelImportDetected",data:{importPath}});return}if(endsWithIndex(importPath)){context.report({node,messageId:"barrelImportDetected",data:{importPath}})}}return{ImportDeclaration(node){const importPath=node.source.value;if(typeof importPath==="string"){checkImportPath(node,importPath)}},ExportAllDeclaration(node){const importPath=node.source.value;if(typeof importPath==="string"){checkImportPath(node,importPath)}},ExportNamedDeclaration(node){if(node.source){const importPath=node.source.value;if(typeof importPath==="string"){checkImportPath(node,importPath)}}}}}});