eslint-plugin-import-next 2.3.9 → 2.3.11
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.
- package/README.md +47 -0
- package/package.json +16 -12
- package/src/files/foo.js +0 -5
- package/src/files/no-default.js +0 -5
- package/src/index.d.ts +15 -2812
- package/src/index.js +5 -147
- package/src/oxlint.d.ts +17 -20
- package/src/oxlint.js +0 -18
- package/src/rules/consistent-type-specifier-style.js +0 -19
- package/src/rules/default.js +0 -7
- package/src/rules/dynamic-import-chunkname.js +0 -8
- package/src/rules/enforce-dependency-direction.js +2 -39
- package/src/rules/enforce-import-order.js +3 -33
- package/src/rules/enforce-team-boundaries.js +0 -49
- package/src/rules/export.js +0 -9
- package/src/rules/exports-last.js +0 -10
- package/src/rules/extensions.js +2 -11
- package/src/rules/first.js +0 -6
- package/src/rules/group-exports.js +0 -13
- package/src/rules/max-dependencies.js +0 -16
- package/src/rules/named.js +1 -10
- package/src/rules/namespace.js +1 -11
- package/src/rules/newline-after-import.js +2 -13
- package/src/rules/no-absolute-path.js +0 -7
- package/src/rules/no-amd.js +1 -18
- package/src/rules/no-anonymous-default-export.js +0 -13
- package/src/rules/no-barrel-file.js +0 -32
- package/src/rules/no-barrel-import.js +0 -25
- package/src/rules/no-commonjs.js +0 -18
- package/src/rules/no-cross-domain-imports.js +2 -47
- package/src/rules/no-cycle.js +2 -135
- package/src/rules/no-default-export.js +0 -28
- package/src/rules/no-deprecated.js +0 -32
- package/src/rules/no-duplicates.js +2 -10
- package/src/rules/no-dynamic-require.js +0 -6
- package/src/rules/no-empty-named-blocks.js +1 -17
- package/src/rules/no-extraneous-dependencies.js +0 -38
- package/src/rules/no-full-package-import.js +0 -19
- package/src/rules/no-import-module-exports.js +0 -8
- package/src/rules/no-internal-modules.js +0 -40
- package/src/rules/no-legacy-imports.js +1 -11
- package/src/rules/no-mutable-exports.js +0 -14
- package/src/rules/no-named-as-default-member.js +0 -11
- package/src/rules/no-named-as-default.js +0 -17
- package/src/rules/no-named-default.js +0 -5
- package/src/rules/no-named-export.js +1 -22
- package/src/rules/no-namespace.js +0 -10
- package/src/rules/no-nodejs-modules.js +0 -15
- package/src/rules/no-relative-packages.js +0 -15
- package/src/rules/no-relative-parent-imports.js +0 -11
- package/src/rules/no-restricted-paths.js +0 -6
- package/src/rules/no-self-import.js +0 -22
- package/src/rules/no-unassigned-import.js +0 -14
- package/src/rules/no-unresolved.js +0 -16
- package/src/rules/no-unused-modules.js +0 -7
- package/src/rules/no-useless-path-segments.js +0 -15
- package/src/rules/prefer-default-export.js +0 -18
- package/src/rules/prefer-direct-import.js +1 -21
- package/src/rules/prefer-modern-api.js +0 -21
- package/src/rules/prefer-node-protocol.js +0 -15
- package/src/rules/prefer-tree-shakeable-imports.js +0 -14
- package/src/rules/require-import-approval.js +0 -21
- package/src/rules/unambiguous.js +0 -11
- package/src/types/index.js +0 -5
- package/AGENTS.md +0 -95
- package/CHANGELOG.md +0 -170
- package/src/files/foo.d.ts +0 -7
- package/src/files/no-default.d.ts +0 -6
- package/src/rules/consistent-type-specifier-style.d.ts +0 -13
- package/src/rules/default.d.ts +0 -9
- package/src/rules/dynamic-import-chunkname.d.ts +0 -26
- package/src/rules/enforce-dependency-direction.d.ts +0 -27
- package/src/rules/enforce-import-order.d.ts +0 -29
- package/src/rules/enforce-team-boundaries.d.ts +0 -47
- package/src/rules/export.d.ts +0 -17
- package/src/rules/exports-last.d.ts +0 -15
- package/src/rules/extensions.d.ts +0 -17
- package/src/rules/first.d.ts +0 -13
- package/src/rules/group-exports.d.ts +0 -15
- package/src/rules/max-dependencies.d.ts +0 -26
- package/src/rules/named.d.ts +0 -13
- package/src/rules/namespace.d.ts +0 -13
- package/src/rules/newline-after-import.d.ts +0 -13
- package/src/rules/no-absolute-path.d.ts +0 -25
- package/src/rules/no-amd.d.ts +0 -21
- package/src/rules/no-anonymous-default-export.d.ts +0 -24
- package/src/rules/no-barrel-file.d.ts +0 -55
- package/src/rules/no-barrel-import.d.ts +0 -48
- package/src/rules/no-commonjs.d.ts +0 -28
- package/src/rules/no-cross-domain-imports.d.ts +0 -41
- package/src/rules/no-cycle.d.ts +0 -102
- package/src/rules/no-default-export.d.ts +0 -24
- package/src/rules/no-deprecated.d.ts +0 -26
- package/src/rules/no-duplicates.d.ts +0 -13
- package/src/rules/no-dynamic-require.d.ts +0 -22
- package/src/rules/no-empty-named-blocks.d.ts +0 -17
- package/src/rules/no-extraneous-dependencies.d.ts +0 -45
- package/src/rules/no-full-package-import.d.ts +0 -42
- package/src/rules/no-import-module-exports.d.ts +0 -21
- package/src/rules/no-internal-modules.d.ts +0 -28
- package/src/rules/no-legacy-imports.d.ts +0 -47
- package/src/rules/no-mutable-exports.d.ts +0 -22
- package/src/rules/no-named-as-default-member.d.ts +0 -15
- package/src/rules/no-named-as-default.d.ts +0 -15
- package/src/rules/no-named-default.d.ts +0 -15
- package/src/rules/no-named-export.d.ts +0 -26
- package/src/rules/no-namespace.d.ts +0 -21
- package/src/rules/no-nodejs-modules.d.ts +0 -24
- package/src/rules/no-relative-packages.d.ts +0 -21
- package/src/rules/no-relative-parent-imports.d.ts +0 -22
- package/src/rules/no-restricted-paths.d.ts +0 -20
- package/src/rules/no-self-import.d.ts +0 -15
- package/src/rules/no-unassigned-import.d.ts +0 -20
- package/src/rules/no-unresolved.d.ts +0 -20
- package/src/rules/no-unused-modules.d.ts +0 -19
- package/src/rules/no-useless-path-segments.d.ts +0 -23
- package/src/rules/prefer-default-export.d.ts +0 -24
- package/src/rules/prefer-direct-import.d.ts +0 -51
- package/src/rules/prefer-modern-api.d.ts +0 -47
- package/src/rules/prefer-node-protocol.d.ts +0 -19
- package/src/rules/prefer-tree-shakeable-imports.d.ts +0 -49
- package/src/rules/require-import-approval.d.ts +0 -52
- package/src/rules/unambiguous.d.ts +0 -15
|
@@ -1,9 +1,4 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* Copyright (c) 2025 Ofri Peretz
|
|
4
|
-
* Licensed under the MIT License. Use of this source code is governed by the
|
|
5
|
-
* MIT license that can be found in the LICENSE file.
|
|
6
|
-
*/
|
|
7
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
3
|
exports.noDeprecated = void 0;
|
|
9
4
|
const eslint_devkit_1 = require("@interlace/eslint-devkit");
|
|
@@ -88,7 +83,6 @@ exports.noDeprecated = (0, eslint_devkit_1.createRule)({
|
|
|
88
83
|
if (!filename) {
|
|
89
84
|
return {};
|
|
90
85
|
}
|
|
91
|
-
// Track deprecated items found in the current file
|
|
92
86
|
const deprecatedItems = new Map();
|
|
93
87
|
function isAllowed(name) {
|
|
94
88
|
return allow.includes(name);
|
|
@@ -97,24 +91,19 @@ exports.noDeprecated = (0, eslint_devkit_1.createRule)({
|
|
|
97
91
|
const lines = comment.split('\n');
|
|
98
92
|
for (const line of lines) {
|
|
99
93
|
const trimmed = line.trim();
|
|
100
|
-
// Check for @deprecated tag
|
|
101
94
|
for (const marker of deprecationMarkers) {
|
|
102
95
|
if (trimmed.includes(marker)) {
|
|
103
96
|
const afterMarker = trimmed
|
|
104
97
|
.substring(trimmed.indexOf(marker) + marker.length)
|
|
105
98
|
.trim();
|
|
106
|
-
// Try to extract reason and replacement
|
|
107
99
|
let reason;
|
|
108
100
|
let replacement;
|
|
109
|
-
// Look for common patterns like "Use X instead" or "Replaced by Y"
|
|
110
101
|
const insteadMatch = afterMarker.match(/(?:use|replaced by|see)\s+([^\s]+(?:\s+[^\s]+)*?)(?:\s|$)/i);
|
|
111
102
|
if (insteadMatch) {
|
|
112
|
-
// Remove common trailing words
|
|
113
103
|
replacement = insteadMatch[1]
|
|
114
104
|
.trim()
|
|
115
105
|
.replace(/\s+instead$|\s+or$|\s+as$|\s+with$/i, '');
|
|
116
106
|
}
|
|
117
|
-
// The rest is usually the reason
|
|
118
107
|
const reasonText = afterMarker
|
|
119
108
|
.replace(/use\s+[^.]*[.]?/i, '')
|
|
120
109
|
.replace(/replaced by\s+[^.]*[.]?/i, '')
|
|
@@ -133,14 +122,12 @@ exports.noDeprecated = (0, eslint_devkit_1.createRule)({
|
|
|
133
122
|
return;
|
|
134
123
|
const sourceCode = context.sourceCode;
|
|
135
124
|
const comments = sourceCode.getCommentsBefore(node);
|
|
136
|
-
// Also check leading comments attached to the node
|
|
137
125
|
const allComments = [...comments];
|
|
138
126
|
if ('leadingComments' in node && Array.isArray(node.leadingComments)) {
|
|
139
127
|
allComments.push(...node.leadingComments);
|
|
140
128
|
}
|
|
141
129
|
for (const comment of allComments) {
|
|
142
130
|
if (comment.type === 'Block' && comment.value.includes('*')) {
|
|
143
|
-
// JSDoc comment
|
|
144
131
|
const deprecationInfo = parseJSDocDeprecated(comment.value);
|
|
145
132
|
if (deprecationInfo.deprecated) {
|
|
146
133
|
deprecatedItems.set(name, {
|
|
@@ -156,10 +143,8 @@ exports.noDeprecated = (0, eslint_devkit_1.createRule)({
|
|
|
156
143
|
function checkDecoratorsForDeprecation(node, name) {
|
|
157
144
|
if (!checkDecorators)
|
|
158
145
|
return;
|
|
159
|
-
// Check for @deprecated decorator in TypeScript
|
|
160
146
|
if ('decorators' in node &&
|
|
161
147
|
Array.isArray(node.decorators)) {
|
|
162
|
-
// The Array.isArray guard above proves decorators is an array.
|
|
163
148
|
const decorators = node.decorators;
|
|
164
149
|
for (const decorator of decorators) {
|
|
165
150
|
const dec = decorator;
|
|
@@ -214,7 +199,6 @@ exports.noDeprecated = (0, eslint_devkit_1.createRule)({
|
|
|
214
199
|
});
|
|
215
200
|
}
|
|
216
201
|
return {
|
|
217
|
-
// Check for deprecated declarations in the current file
|
|
218
202
|
VariableDeclaration(node) {
|
|
219
203
|
node.declarations.forEach((decl) => {
|
|
220
204
|
if (decl.id.type === 'Identifier') {
|
|
@@ -237,13 +221,11 @@ exports.noDeprecated = (0, eslint_devkit_1.createRule)({
|
|
|
237
221
|
},
|
|
238
222
|
MethodDefinition(node) {
|
|
239
223
|
if (node.key.type === 'Identifier') {
|
|
240
|
-
// For methods, check comments on both the method node and the key
|
|
241
224
|
checkComments(node, node.key.name);
|
|
242
225
|
checkComments(node.key, node.key.name);
|
|
243
226
|
checkDecoratorsForDeprecation(node, node.key.name);
|
|
244
227
|
}
|
|
245
228
|
},
|
|
246
|
-
// Check for deprecated exports in the current file
|
|
247
229
|
ExportNamedDeclaration(node) {
|
|
248
230
|
if (node.declaration) {
|
|
249
231
|
if (node.declaration.type === 'VariableDeclaration') {
|
|
@@ -265,7 +247,6 @@ exports.noDeprecated = (0, eslint_devkit_1.createRule)({
|
|
|
265
247
|
checkDecoratorsForDeprecation(node.declaration, node.declaration.id.name);
|
|
266
248
|
}
|
|
267
249
|
}
|
|
268
|
-
// Check exported specifiers
|
|
269
250
|
if (node.specifiers) {
|
|
270
251
|
node.specifiers.forEach((specifier) => {
|
|
271
252
|
if (specifier.exported.type === 'Identifier') {
|
|
@@ -278,16 +259,9 @@ exports.noDeprecated = (0, eslint_devkit_1.createRule)({
|
|
|
278
259
|
checkComments(node, 'default');
|
|
279
260
|
checkDecoratorsForDeprecation(node, 'default');
|
|
280
261
|
},
|
|
281
|
-
// Check for usage of deprecated imported items
|
|
282
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
283
262
|
ImportDeclaration(node) {
|
|
284
|
-
// This would need cross-file analysis to check if imported items are deprecated
|
|
285
|
-
// For now, we'll focus on deprecated items within the same file
|
|
286
263
|
},
|
|
287
|
-
// Check for usage of deprecated items
|
|
288
264
|
MemberExpression(node) {
|
|
289
|
-
// No has() pre-check: reportDeprecatedUsage bails out when the
|
|
290
|
-
// object name has no recorded deprecation info.
|
|
291
265
|
if (node.object.type === 'Identifier' &&
|
|
292
266
|
node.property.type === 'Identifier') {
|
|
293
267
|
reportDeprecatedUsage(node, node.object.name, `${node.object.name}.${node.property.name}`);
|
|
@@ -306,9 +280,7 @@ exports.noDeprecated = (0, eslint_devkit_1.createRule)({
|
|
|
306
280
|
}
|
|
307
281
|
},
|
|
308
282
|
Identifier(node) {
|
|
309
|
-
// Check if this identifier references a deprecated item
|
|
310
283
|
if (deprecatedItems.has(node.name)) {
|
|
311
|
-
// Only report if it's not part of a declaration
|
|
312
284
|
const parent = node.parent;
|
|
313
285
|
if (parent &&
|
|
314
286
|
![
|
|
@@ -319,22 +291,18 @@ exports.noDeprecated = (0, eslint_devkit_1.createRule)({
|
|
|
319
291
|
'ExportSpecifier',
|
|
320
292
|
'MethodDefinition',
|
|
321
293
|
].includes(parent.type)) {
|
|
322
|
-
// Don't report if this identifier is the callee of a CallExpression (handled separately)
|
|
323
294
|
if (parent.type === 'CallExpression' &&
|
|
324
295
|
parent.callee === node) {
|
|
325
296
|
return;
|
|
326
297
|
}
|
|
327
|
-
// Don't report if this identifier is the callee of a NewExpression (handled separately)
|
|
328
298
|
if (parent.type === 'NewExpression' &&
|
|
329
299
|
parent.callee === node) {
|
|
330
300
|
return;
|
|
331
301
|
}
|
|
332
|
-
// Don't report if this identifier is the object of a MemberExpression (handled separately)
|
|
333
302
|
if (parent.type === 'MemberExpression' &&
|
|
334
303
|
parent.object === node) {
|
|
335
304
|
return;
|
|
336
305
|
}
|
|
337
|
-
// Don't report if this identifier is the property of a MemberExpression (handled separately)
|
|
338
306
|
if (parent.type === 'MemberExpression' &&
|
|
339
307
|
parent.property === node) {
|
|
340
308
|
return;
|
|
@@ -1,9 +1,4 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* Copyright (c) 2025 Ofri Peretz
|
|
4
|
-
* Licensed under the MIT License. Use of this source code is governed by the
|
|
5
|
-
* MIT license that can be found in the LICENSE file.
|
|
6
|
-
*/
|
|
7
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
3
|
exports.noDuplicates = void 0;
|
|
9
4
|
const eslint_devkit_1 = require("@interlace/eslint-devkit");
|
|
@@ -51,22 +46,19 @@ exports.noDuplicates = (0, eslint_devkit_1.createRule)({
|
|
|
51
46
|
node: duplicate,
|
|
52
47
|
messageId: 'noDuplicates',
|
|
53
48
|
fix(fixer) {
|
|
54
|
-
// Simple fix: if both are named imports, merge them
|
|
55
49
|
const firstHasDefault = first.specifiers.some((s) => s.type === 'ImportDefaultSpecifier');
|
|
56
50
|
const duplicateHasDefault = duplicate.specifiers.some((s) => s.type === 'ImportDefaultSpecifier');
|
|
57
51
|
const firstHasNamespace = first.specifiers.some((s) => s.type === 'ImportNamespaceSpecifier');
|
|
58
52
|
const duplicateHasNamespace = duplicate.specifiers.some((s) => s.type === 'ImportNamespaceSpecifier');
|
|
59
53
|
if (firstHasNamespace || duplicateHasNamespace) {
|
|
60
|
-
return null;
|
|
54
|
+
return null;
|
|
61
55
|
}
|
|
62
56
|
if (firstHasDefault && duplicateHasDefault) {
|
|
63
|
-
return null;
|
|
57
|
+
return null;
|
|
64
58
|
}
|
|
65
|
-
// If duplicate is empty (side-effect), just remove it
|
|
66
59
|
if (duplicate.specifiers.length === 0) {
|
|
67
60
|
return fixer.remove(duplicate);
|
|
68
61
|
}
|
|
69
|
-
// If first is empty (side-effect), we can't easily merge into it without more logic
|
|
70
62
|
if (first.specifiers.length === 0) {
|
|
71
63
|
return null;
|
|
72
64
|
}
|
|
@@ -1,9 +1,4 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* Copyright (c) 2025 Ofri Peretz
|
|
4
|
-
* Licensed under the MIT License. Use of this source code is governed by the
|
|
5
|
-
* MIT license that can be found in the LICENSE file.
|
|
6
|
-
*/
|
|
7
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
3
|
exports.noDynamicRequire = void 0;
|
|
9
4
|
const eslint_devkit_1 = require("@interlace/eslint-devkit");
|
|
@@ -58,7 +53,6 @@ exports.noDynamicRequire = (0, eslint_devkit_1.createRule)({
|
|
|
58
53
|
}
|
|
59
54
|
return {
|
|
60
55
|
CallExpression(node) {
|
|
61
|
-
// Check require() calls
|
|
62
56
|
if (node.callee.type === eslint_devkit_1.AST_NODE_TYPES.Identifier &&
|
|
63
57
|
node.callee.name === 'require' &&
|
|
64
58
|
node.arguments.length >= 1) {
|
|
@@ -1,9 +1,4 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* Copyright (c) 2025 Ofri Peretz
|
|
4
|
-
* Licensed under the MIT License. Use of this source code is governed by the
|
|
5
|
-
* MIT license that can be found in the LICENSE file.
|
|
6
|
-
*/
|
|
7
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
3
|
exports.noEmptyNamedBlocks = void 0;
|
|
9
4
|
const eslint_devkit_1 = require("@interlace/eslint-devkit");
|
|
@@ -37,24 +32,18 @@ exports.noEmptyNamedBlocks = (0, eslint_devkit_1.createRule)({
|
|
|
37
32
|
create(context) {
|
|
38
33
|
return {
|
|
39
34
|
ImportDeclaration(node) {
|
|
40
|
-
// Check if this import has empty named imports: import {} from 'foo'
|
|
41
|
-
// or import Foo, {} from 'foo'
|
|
42
35
|
const hasDefaultImport = node.specifiers.some((spec) => spec.type === 'ImportDefaultSpecifier');
|
|
43
36
|
const hasNamespaceImport = node.specifiers.some((spec) => spec.type === 'ImportNamespaceSpecifier');
|
|
44
37
|
const namedImports = node.specifiers.filter((spec) => spec.type === 'ImportSpecifier');
|
|
45
|
-
// Get the source code
|
|
46
38
|
const sourceCode = context.sourceCode;
|
|
47
39
|
const importText = sourceCode.getText(node);
|
|
48
|
-
// Check for empty braces pattern: {}
|
|
49
40
|
const hasEmptyBraces = /\{\s*\}/.test(importText);
|
|
50
41
|
if (hasEmptyBraces && namedImports.length === 0) {
|
|
51
|
-
// If there's only empty braces (no default, no namespace)
|
|
52
42
|
if (!hasDefaultImport && !hasNamespaceImport) {
|
|
53
43
|
context.report({
|
|
54
44
|
node,
|
|
55
45
|
messageId: 'emptyNamedBlock',
|
|
56
46
|
fix(fixer) {
|
|
57
|
-
// Remove the entire import statement
|
|
58
47
|
return fixer.remove(node);
|
|
59
48
|
},
|
|
60
49
|
suggest: [
|
|
@@ -68,13 +57,10 @@ exports.noEmptyNamedBlocks = (0, eslint_devkit_1.createRule)({
|
|
|
68
57
|
});
|
|
69
58
|
}
|
|
70
59
|
else if (hasDefaultImport) {
|
|
71
|
-
// Has default import with empty braces: import Foo, {} from 'bar'
|
|
72
|
-
// Fix to: import Foo from 'bar'
|
|
73
60
|
context.report({
|
|
74
61
|
node,
|
|
75
62
|
messageId: 'emptyNamedBlock',
|
|
76
63
|
fix(fixer) {
|
|
77
|
-
// Remove the ", {}" part
|
|
78
64
|
const fixedText = importText.replace(/,\s*\{\s*\}/, '');
|
|
79
65
|
return fixer.replaceText(node, fixedText);
|
|
80
66
|
},
|
|
@@ -92,11 +78,9 @@ exports.noEmptyNamedBlocks = (0, eslint_devkit_1.createRule)({
|
|
|
92
78
|
}
|
|
93
79
|
},
|
|
94
80
|
ExportNamedDeclaration(node) {
|
|
95
|
-
// Check for: export {} from 'foo' or export {}
|
|
96
81
|
if (node.specifiers.length === 0 &&
|
|
97
82
|
!node.declaration &&
|
|
98
|
-
node.source
|
|
99
|
-
) {
|
|
83
|
+
node.source) {
|
|
100
84
|
context.report({
|
|
101
85
|
node,
|
|
102
86
|
messageId: 'emptyNamedBlock',
|
|
@@ -1,9 +1,4 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* Copyright (c) 2025 Ofri Peretz
|
|
4
|
-
* Licensed under the MIT License. Use of this source code is governed by the
|
|
5
|
-
* MIT license that can be found in the LICENSE file.
|
|
6
|
-
*/
|
|
7
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
3
|
exports.noExtraneousDependencies = void 0;
|
|
9
4
|
const eslint_devkit_1 = require("@interlace/eslint-devkit");
|
|
@@ -139,67 +134,49 @@ exports.noExtraneousDependencies = (0, eslint_devkit_1.createRule)({
|
|
|
139
134
|
if (!filename) {
|
|
140
135
|
return {};
|
|
141
136
|
}
|
|
142
|
-
// Use provided package.json if available (for testing)
|
|
143
137
|
let resolvedPackageJson = null;
|
|
144
138
|
if (packageJson) {
|
|
145
|
-
// Use directly provided package.json (for testing)
|
|
146
139
|
resolvedPackageJson = packageJson;
|
|
147
140
|
}
|
|
148
141
|
else if (packageJsonPath) {
|
|
149
|
-
// Use specified package.json file
|
|
150
142
|
resolvedPackageJson = (0, eslint_devkit_3.readJsonFileSync)(packageJsonPath);
|
|
151
143
|
if (!resolvedPackageJson) {
|
|
152
|
-
// Invalid package.json - skip
|
|
153
144
|
return {};
|
|
154
145
|
}
|
|
155
146
|
}
|
|
156
147
|
else {
|
|
157
|
-
// Find package.json automatically
|
|
158
148
|
const packageJsonFile = (0, eslint_devkit_3.findFileUpward)('package.json', (0, eslint_devkit_4.getDirname)(filename));
|
|
159
149
|
if (!packageJsonFile) {
|
|
160
|
-
// No package.json found - skip this rule
|
|
161
150
|
return {};
|
|
162
151
|
}
|
|
163
|
-
// Read and parse package.json
|
|
164
152
|
resolvedPackageJson = (0, eslint_devkit_3.readJsonFileSync)(packageJsonFile);
|
|
165
153
|
if (!resolvedPackageJson) {
|
|
166
|
-
// Invalid package.json - skip
|
|
167
154
|
return {};
|
|
168
155
|
}
|
|
169
156
|
}
|
|
170
|
-
// Build allowed packages map
|
|
171
157
|
const allowedPackages = new Set();
|
|
172
|
-
// Add dependencies
|
|
173
158
|
if (resolvedPackageJson?.dependencies) {
|
|
174
159
|
Object.keys(resolvedPackageJson.dependencies).forEach((pkg) => allowedPackages.add(pkg));
|
|
175
160
|
}
|
|
176
|
-
// Add dev dependencies if allowed
|
|
177
161
|
if (devDependencies && resolvedPackageJson?.devDependencies) {
|
|
178
162
|
Object.keys(resolvedPackageJson.devDependencies).forEach((pkg) => allowedPackages.add(pkg));
|
|
179
163
|
}
|
|
180
|
-
// Add optional dependencies if allowed
|
|
181
164
|
if (optionalDependencies && resolvedPackageJson?.optionalDependencies) {
|
|
182
165
|
Object.keys(resolvedPackageJson.optionalDependencies).forEach((pkg) => allowedPackages.add(pkg));
|
|
183
166
|
}
|
|
184
|
-
// Add peer dependencies if allowed
|
|
185
167
|
if (peerDependencies && resolvedPackageJson?.peerDependencies) {
|
|
186
168
|
Object.keys(resolvedPackageJson.peerDependencies).forEach((pkg) => allowedPackages.add(pkg));
|
|
187
169
|
}
|
|
188
|
-
// Add bundled dependencies if allowed
|
|
189
170
|
if (bundledDependencies && resolvedPackageJson?.bundledDependencies) {
|
|
190
171
|
resolvedPackageJson.bundledDependencies.forEach((pkg) => allowedPackages.add(pkg));
|
|
191
172
|
}
|
|
192
|
-
// oxlint-disable-next-line consistent-function-scoping
|
|
193
173
|
function isExternalPackage(importPath) {
|
|
194
|
-
// Skip relative imports
|
|
195
174
|
if (importPath.startsWith('./') || importPath.startsWith('../')) {
|
|
196
175
|
return null;
|
|
197
176
|
}
|
|
198
|
-
// Skip absolute paths
|
|
199
177
|
if (importPath.startsWith('/')) {
|
|
200
178
|
return null;
|
|
201
179
|
}
|
|
202
|
-
// Skip Node.js built-ins
|
|
203
180
|
const nodeBuiltins = [
|
|
204
181
|
'assert',
|
|
205
182
|
'buffer',
|
|
@@ -261,15 +238,12 @@ exports.noExtraneousDependencies = (0, eslint_devkit_1.createRule)({
|
|
|
261
238
|
if (nodeBuiltins.includes(importPath)) {
|
|
262
239
|
return null;
|
|
263
240
|
}
|
|
264
|
-
// Extract package name from scoped packages or regular packages
|
|
265
241
|
const packageName = importPath.startsWith('@')
|
|
266
242
|
? importPath.split('/').slice(0, 2).join('/')
|
|
267
243
|
: importPath.split('/')[0];
|
|
268
244
|
return packageName;
|
|
269
245
|
}
|
|
270
|
-
// oxlint-disable-next-line consistent-function-scoping
|
|
271
246
|
function isWorkspacePackage(packageName) {
|
|
272
|
-
// For testing purposes, consider packages with @workspace/ or @company/ as workspace packages
|
|
273
247
|
return (packageName.startsWith('@workspace/') ||
|
|
274
248
|
packageName.startsWith('@company/'));
|
|
275
249
|
}
|
|
@@ -278,7 +252,6 @@ exports.noExtraneousDependencies = (0, eslint_devkit_1.createRule)({
|
|
|
278
252
|
if (!packageName) {
|
|
279
253
|
return;
|
|
280
254
|
}
|
|
281
|
-
// Check allow patterns first
|
|
282
255
|
const isAllowedByPattern = allowPatterns.some((pattern) => {
|
|
283
256
|
try {
|
|
284
257
|
const regex = new RegExp(pattern);
|
|
@@ -291,15 +264,12 @@ exports.noExtraneousDependencies = (0, eslint_devkit_1.createRule)({
|
|
|
291
264
|
if (isAllowedByPattern) {
|
|
292
265
|
return;
|
|
293
266
|
}
|
|
294
|
-
// Apply resolution strategy
|
|
295
267
|
const shouldCheckDependency = () => {
|
|
296
268
|
switch (resolutionStrategy) {
|
|
297
269
|
case 'workspace':
|
|
298
|
-
// Allow packages that are in dependencies OR are workspace packages
|
|
299
270
|
return (!allowedPackages.has(packageName) &&
|
|
300
271
|
!isWorkspacePackage(packageName));
|
|
301
272
|
case 'monorepo':
|
|
302
|
-
// Allow cross-package imports in monorepos
|
|
303
273
|
return (!allowedPackages.has(packageName) &&
|
|
304
274
|
!isWorkspacePackage(packageName));
|
|
305
275
|
case 'strict':
|
|
@@ -308,11 +278,9 @@ exports.noExtraneousDependencies = (0, eslint_devkit_1.createRule)({
|
|
|
308
278
|
}
|
|
309
279
|
};
|
|
310
280
|
if (shouldCheckDependency()) {
|
|
311
|
-
// Check if it's in devDependencies but we're in production code
|
|
312
281
|
const isInDevDeps = resolvedPackageJson?.devDependencies &&
|
|
313
282
|
resolvedPackageJson.devDependencies[packageName];
|
|
314
283
|
if (isInDevDeps && !devDependencies) {
|
|
315
|
-
// Dev dependency used in production code
|
|
316
284
|
context.report({
|
|
317
285
|
node,
|
|
318
286
|
messageId: 'devDependencyInProduction',
|
|
@@ -325,7 +293,6 @@ exports.noExtraneousDependencies = (0, eslint_devkit_1.createRule)({
|
|
|
325
293
|
{
|
|
326
294
|
messageId: 'moveToDependencies',
|
|
327
295
|
fix(fixer) {
|
|
328
|
-
// Find the parent statement to insert before
|
|
329
296
|
let parentStatement = node;
|
|
330
297
|
while (parentStatement &&
|
|
331
298
|
parentStatement.type !== 'ImportDeclaration' &&
|
|
@@ -343,7 +310,6 @@ exports.noExtraneousDependencies = (0, eslint_devkit_1.createRule)({
|
|
|
343
310
|
});
|
|
344
311
|
}
|
|
345
312
|
else {
|
|
346
|
-
// Completely missing dependency
|
|
347
313
|
context.report({
|
|
348
314
|
node,
|
|
349
315
|
messageId: 'missingDependency',
|
|
@@ -358,7 +324,6 @@ exports.noExtraneousDependencies = (0, eslint_devkit_1.createRule)({
|
|
|
358
324
|
{
|
|
359
325
|
messageId: 'addToDependencies',
|
|
360
326
|
fix(fixer) {
|
|
361
|
-
// Find the parent statement to insert before
|
|
362
327
|
let parentStatement = node;
|
|
363
328
|
while (parentStatement &&
|
|
364
329
|
parentStatement.type !== 'ImportDeclaration' &&
|
|
@@ -375,7 +340,6 @@ exports.noExtraneousDependencies = (0, eslint_devkit_1.createRule)({
|
|
|
375
340
|
{
|
|
376
341
|
messageId: 'addToDevDependencies',
|
|
377
342
|
fix(fixer) {
|
|
378
|
-
// Find the parent statement to insert before
|
|
379
343
|
let parentStatement = node;
|
|
380
344
|
while (parentStatement &&
|
|
381
345
|
parentStatement.type !== 'ImportDeclaration' &&
|
|
@@ -401,7 +365,6 @@ exports.noExtraneousDependencies = (0, eslint_devkit_1.createRule)({
|
|
|
401
365
|
}
|
|
402
366
|
},
|
|
403
367
|
CallExpression(node) {
|
|
404
|
-
// Check require() calls
|
|
405
368
|
if (node.callee.type === 'Identifier' &&
|
|
406
369
|
node.callee.name === 'require' &&
|
|
407
370
|
node.arguments.length === 1) {
|
|
@@ -412,7 +375,6 @@ exports.noExtraneousDependencies = (0, eslint_devkit_1.createRule)({
|
|
|
412
375
|
}
|
|
413
376
|
},
|
|
414
377
|
ImportExpression(node) {
|
|
415
|
-
// Check dynamic imports
|
|
416
378
|
if (node.source.type === 'Literal' &&
|
|
417
379
|
typeof node.source.value === 'string') {
|
|
418
380
|
checkImport(node.source.value, node.source);
|
|
@@ -1,13 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* Copyright (c) 2025 Ofri Peretz
|
|
4
|
-
* Licensed under the MIT License. Use of this source code is governed by the
|
|
5
|
-
* MIT license that can be found in the LICENSE file.
|
|
6
|
-
*/
|
|
7
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
3
|
exports.noFullPackageImport = void 0;
|
|
9
4
|
const eslint_devkit_1 = require("@interlace/eslint-devkit");
|
|
10
|
-
// Known large packages that should use subpath/named imports
|
|
11
5
|
const DEFAULT_BLOCKED_PACKAGES = [
|
|
12
6
|
{
|
|
13
7
|
name: 'lodash',
|
|
@@ -55,22 +49,12 @@ const DEFAULT_BLOCKED_PACKAGES = [
|
|
|
55
49
|
example: "import { S3Client } from '@aws-sdk/client-s3';",
|
|
56
50
|
},
|
|
57
51
|
];
|
|
58
|
-
/**
|
|
59
|
-
* Check if import is a full package import (not a subpath)
|
|
60
|
-
*/
|
|
61
52
|
function isFullPackageImport(importPath, packageName) {
|
|
62
|
-
// Exact match means full package import
|
|
63
53
|
return importPath === packageName;
|
|
64
54
|
}
|
|
65
|
-
/**
|
|
66
|
-
* Check if import uses namespace pattern
|
|
67
|
-
*/
|
|
68
55
|
function hasNamespaceImport(node) {
|
|
69
56
|
return node.specifiers.some((spec) => spec.type === eslint_devkit_1.AST_NODE_TYPES.ImportNamespaceSpecifier);
|
|
70
57
|
}
|
|
71
|
-
/**
|
|
72
|
-
* Check if import uses default pattern
|
|
73
|
-
*/
|
|
74
58
|
function hasDefaultImport(node) {
|
|
75
59
|
return node.specifiers.some((spec) => spec.type === eslint_devkit_1.AST_NODE_TYPES.ImportDefaultSpecifier);
|
|
76
60
|
}
|
|
@@ -128,13 +112,10 @@ exports.noFullPackageImport = (0, eslint_devkit_1.createRule)({
|
|
|
128
112
|
const importPath = node.source.value;
|
|
129
113
|
if (typeof importPath !== 'string')
|
|
130
114
|
return;
|
|
131
|
-
// Check against blocked packages
|
|
132
115
|
for (const blocked of blockedPackages) {
|
|
133
116
|
if (!isFullPackageImport(importPath, blocked.name)) {
|
|
134
117
|
continue;
|
|
135
118
|
}
|
|
136
|
-
// Only flag namespace or default imports from full package path
|
|
137
|
-
// Named imports might be tree-shakeable depending on the package
|
|
138
119
|
if (hasNamespaceImport(node) || hasDefaultImport(node)) {
|
|
139
120
|
context.report({
|
|
140
121
|
node,
|
|
@@ -1,9 +1,4 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* Copyright (c) 2025 Ofri Peretz
|
|
4
|
-
* Licensed under the MIT License. Use of this source code is governed by the
|
|
5
|
-
* MIT license that can be found in the LICENSE file.
|
|
6
|
-
*/
|
|
7
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
3
|
exports.noImportModuleExports = void 0;
|
|
9
4
|
const eslint_devkit_1 = require("@interlace/eslint-devkit");
|
|
@@ -52,7 +47,6 @@ exports.noImportModuleExports = (0, eslint_devkit_1.createRule)({
|
|
|
52
47
|
importNodes.push(node);
|
|
53
48
|
},
|
|
54
49
|
MemberExpression(node) {
|
|
55
|
-
// Check for module.exports
|
|
56
50
|
if (node.object.type === eslint_devkit_1.AST_NODE_TYPES.Identifier &&
|
|
57
51
|
node.object.name === 'module' &&
|
|
58
52
|
node.property.type === eslint_devkit_1.AST_NODE_TYPES.Identifier &&
|
|
@@ -61,7 +55,6 @@ exports.noImportModuleExports = (0, eslint_devkit_1.createRule)({
|
|
|
61
55
|
}
|
|
62
56
|
},
|
|
63
57
|
AssignmentExpression(node) {
|
|
64
|
-
// Check for exports.xxx = ...
|
|
65
58
|
if (node.left.type === eslint_devkit_1.AST_NODE_TYPES.MemberExpression &&
|
|
66
59
|
node.left.object.type === eslint_devkit_1.AST_NODE_TYPES.Identifier &&
|
|
67
60
|
node.left.object.name === 'exports') {
|
|
@@ -69,7 +62,6 @@ exports.noImportModuleExports = (0, eslint_devkit_1.createRule)({
|
|
|
69
62
|
}
|
|
70
63
|
},
|
|
71
64
|
'Program:exit'() {
|
|
72
|
-
// If we have both imports and module.exports, report on module.exports
|
|
73
65
|
if (hasImport && moduleExportsNodes.length > 0) {
|
|
74
66
|
moduleExportsNodes.forEach((node) => {
|
|
75
67
|
context.report({
|
|
@@ -1,19 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* Copyright (c) 2025 Ofri Peretz
|
|
4
|
-
* Licensed under the MIT License. Use of this source code is governed by the
|
|
5
|
-
* MIT license that can be found in the LICENSE file.
|
|
6
|
-
*/
|
|
7
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
3
|
exports.noInternalModules = void 0;
|
|
9
4
|
const eslint_devkit_1 = require("@interlace/eslint-devkit");
|
|
10
5
|
const eslint_devkit_2 = require("@interlace/eslint-devkit");
|
|
11
|
-
/**
|
|
12
|
-
* Simple glob pattern matching
|
|
13
|
-
* Supports * (any chars) and ** (any path segments)
|
|
14
|
-
*/
|
|
15
6
|
function matchGlob(pattern, path) {
|
|
16
|
-
// Escape special regex chars except *
|
|
17
7
|
const regexPattern = pattern
|
|
18
8
|
.replace(/[.+^${}()|[\]\\]/g, '\\$&')
|
|
19
9
|
.replace(/\*\*/g, '{{DOUBLE_STAR}}')
|
|
@@ -24,35 +14,18 @@ function matchGlob(pattern, path) {
|
|
|
24
14
|
function matchesAnyPattern(path, patterns) {
|
|
25
15
|
return patterns.some((pattern) => matchGlob(pattern, path));
|
|
26
16
|
}
|
|
27
|
-
/**
|
|
28
|
-
* Calculate import depth
|
|
29
|
-
* - 'lodash' => 0
|
|
30
|
-
* - 'lodash/get' => 1
|
|
31
|
-
* - '@company/ui' => 0 (scoped package root)
|
|
32
|
-
* - '@company/ui/components' => 1
|
|
33
|
-
* - '@company/ui/components/Button' => 2
|
|
34
|
-
* - './utils' => 1
|
|
35
|
-
* - './utils/helpers' => 2
|
|
36
|
-
*/
|
|
37
17
|
function getImportDepth(importPath) {
|
|
38
|
-
// Handle relative imports
|
|
39
18
|
if (importPath.startsWith('./') || importPath.startsWith('../')) {
|
|
40
19
|
const parts = importPath.split('/').filter((p) => p !== '.' && p !== '..');
|
|
41
20
|
return parts.length;
|
|
42
21
|
}
|
|
43
|
-
// Handle scoped packages (@org/package)
|
|
44
22
|
if (importPath.startsWith('@')) {
|
|
45
23
|
const parts = importPath.split('/');
|
|
46
|
-
// @org/package = 0 depth, @org/package/sub = 1 depth
|
|
47
24
|
return Math.max(0, parts.length - 2);
|
|
48
25
|
}
|
|
49
|
-
// Handle regular packages
|
|
50
26
|
const parts = importPath.split('/');
|
|
51
27
|
return parts.length - 1;
|
|
52
28
|
}
|
|
53
|
-
/**
|
|
54
|
-
* Get the root/base import path
|
|
55
|
-
*/
|
|
56
29
|
function getRootImport(importPath) {
|
|
57
30
|
if (importPath.startsWith('./') || importPath.startsWith('../')) {
|
|
58
31
|
return '.';
|
|
@@ -63,9 +36,6 @@ function getRootImport(importPath) {
|
|
|
63
36
|
}
|
|
64
37
|
return importPath.split('/')[0];
|
|
65
38
|
}
|
|
66
|
-
/**
|
|
67
|
-
* Get import path trimmed to specific depth
|
|
68
|
-
*/
|
|
69
39
|
function getImportAtDepth(importPath, maxDepth) {
|
|
70
40
|
if (importPath.startsWith('./') || importPath.startsWith('../')) {
|
|
71
41
|
if (maxDepth === 0)
|
|
@@ -76,7 +46,6 @@ function getImportAtDepth(importPath, maxDepth) {
|
|
|
76
46
|
}
|
|
77
47
|
if (importPath.startsWith('@')) {
|
|
78
48
|
const parts = importPath.split('/');
|
|
79
|
-
// @org/package is depth 0
|
|
80
49
|
return parts.slice(0, 2 + maxDepth).join('/');
|
|
81
50
|
}
|
|
82
51
|
const parts = importPath.split('/');
|
|
@@ -170,18 +139,15 @@ exports.noInternalModules = (0, eslint_devkit_1.createRule)({
|
|
|
170
139
|
const [options] = context.options;
|
|
171
140
|
const { maxDepth = 1, allow = [], forbid = [], ignorePaths = [], strategy = 'error', } = options || {};
|
|
172
141
|
function checkImport(importPath, node) {
|
|
173
|
-
// Skip if path should be ignored
|
|
174
142
|
if (matchesAnyPattern(importPath, ignorePaths)) {
|
|
175
143
|
return;
|
|
176
144
|
}
|
|
177
|
-
// Skip if path is explicitly allowed
|
|
178
145
|
if (matchesAnyPattern(importPath, allow)) {
|
|
179
146
|
return;
|
|
180
147
|
}
|
|
181
148
|
const depth = getImportDepth(importPath);
|
|
182
149
|
const isForbidden = forbid.length > 0 && matchesAnyPattern(importPath, forbid);
|
|
183
150
|
const exceedsDepth = depth > maxDepth;
|
|
184
|
-
// No violation
|
|
185
151
|
if (!isForbidden && !exceedsDepth) {
|
|
186
152
|
return;
|
|
187
153
|
}
|
|
@@ -204,8 +170,6 @@ exports.noInternalModules = (0, eslint_devkit_1.createRule)({
|
|
|
204
170
|
messageId: 'internalModuleImport',
|
|
205
171
|
data: reportData,
|
|
206
172
|
fix(fixer) {
|
|
207
|
-
// Find the string literal node to replace
|
|
208
|
-
// Autofix always goes to the root package for safety
|
|
209
173
|
const sourceNode = node.type === 'Literal'
|
|
210
174
|
? node
|
|
211
175
|
: node.source;
|
|
@@ -229,7 +193,6 @@ exports.noInternalModules = (0, eslint_devkit_1.createRule)({
|
|
|
229
193
|
return fixer.replaceText(sourceNode, `'${rootImport}'`);
|
|
230
194
|
},
|
|
231
195
|
},
|
|
232
|
-
// Add barrel export suggestion if different from root
|
|
233
196
|
...(barrelPath !== rootImport && barrelPath !== suggestedPath
|
|
234
197
|
? [
|
|
235
198
|
{
|
|
@@ -248,7 +211,6 @@ exports.noInternalModules = (0, eslint_devkit_1.createRule)({
|
|
|
248
211
|
});
|
|
249
212
|
}
|
|
250
213
|
else {
|
|
251
|
-
// 'error' or 'warn' strategy - just report
|
|
252
214
|
context.report({
|
|
253
215
|
node,
|
|
254
216
|
messageId: 'internalModuleImport',
|
|
@@ -274,7 +236,6 @@ exports.noInternalModules = (0, eslint_devkit_1.createRule)({
|
|
|
274
236
|
}
|
|
275
237
|
},
|
|
276
238
|
CallExpression(node) {
|
|
277
|
-
// Check require() calls
|
|
278
239
|
if (node.callee.type === 'Identifier' &&
|
|
279
240
|
node.callee.name === 'require' &&
|
|
280
241
|
node.arguments.length === 1) {
|
|
@@ -284,7 +245,6 @@ exports.noInternalModules = (0, eslint_devkit_1.createRule)({
|
|
|
284
245
|
}
|
|
285
246
|
}
|
|
286
247
|
},
|
|
287
|
-
// Handle dynamic imports: import('path')
|
|
288
248
|
ImportExpression(node) {
|
|
289
249
|
if (node.source.type === 'Literal' &&
|
|
290
250
|
typeof node.source.value === 'string') {
|