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,29 +1,17 @@
|
|
|
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.enforceDependencyDirection = void 0;
|
|
9
4
|
const eslint_devkit_1 = require("@interlace/eslint-devkit");
|
|
10
5
|
const eslint_devkit_2 = require("@interlace/eslint-devkit");
|
|
11
6
|
const eslint_devkit_3 = require("@interlace/eslint-devkit");
|
|
12
|
-
/**
|
|
13
|
-
* Check if two arrays are equal
|
|
14
|
-
*/
|
|
15
7
|
function arraysEqual(a, b) {
|
|
16
8
|
if (a.length !== b.length)
|
|
17
9
|
return false;
|
|
18
10
|
return a.every((val, index) => val === b[index]);
|
|
19
11
|
}
|
|
20
|
-
/**
|
|
21
|
-
* Extract layer from file path
|
|
22
|
-
*/
|
|
23
12
|
function extractLayer(filePath, layerPatterns) {
|
|
24
13
|
const normalizedPath = (0, eslint_devkit_3.normalizePath)(filePath);
|
|
25
14
|
for (const pattern of layerPatterns) {
|
|
26
|
-
// Check for layer in path segments (handles both /layer/ and layer/ at start)
|
|
27
15
|
if (normalizedPath.includes(`/${pattern}/`) ||
|
|
28
16
|
normalizedPath.includes(`\\${pattern}\\`) ||
|
|
29
17
|
normalizedPath.startsWith(`${pattern}/`) ||
|
|
@@ -33,12 +21,9 @@ function extractLayer(filePath, layerPatterns) {
|
|
|
33
21
|
}
|
|
34
22
|
return null;
|
|
35
23
|
}
|
|
36
|
-
/**
|
|
37
|
-
* Get layer order index
|
|
38
|
-
*/
|
|
39
24
|
function getLayerOrder(layer, layers) {
|
|
40
25
|
if (!layer) {
|
|
41
|
-
return -1;
|
|
26
|
+
return -1;
|
|
42
27
|
}
|
|
43
28
|
const index = layers.indexOf(layer);
|
|
44
29
|
return index >= 0 ? index : -1;
|
|
@@ -132,14 +117,12 @@ exports.enforceDependencyDirection = (0, eslint_devkit_2.createRule)({
|
|
|
132
117
|
create(context, [options = {}]) {
|
|
133
118
|
const filename = context.filename;
|
|
134
119
|
const { layers: providedLayers, layerPatterns: providedLayerPatterns, allowSameLayer = true, } = options || {};
|
|
135
|
-
// Use provided layers or defaults
|
|
136
120
|
const layers = providedLayers || [
|
|
137
121
|
'domain',
|
|
138
122
|
'application',
|
|
139
123
|
'infrastructure',
|
|
140
124
|
'presentation',
|
|
141
125
|
];
|
|
142
|
-
// Default layer patterns
|
|
143
126
|
const defaultLayerPatterns = [
|
|
144
127
|
'domain',
|
|
145
128
|
'application',
|
|
@@ -148,26 +131,19 @@ exports.enforceDependencyDirection = (0, eslint_devkit_2.createRule)({
|
|
|
148
131
|
'ui',
|
|
149
132
|
'api',
|
|
150
133
|
];
|
|
151
|
-
// Use provided layerPatterns, or if layers were provided but layerPatterns weren't (i.e., layerPatterns is still default), use layers
|
|
152
134
|
let layerPatterns;
|
|
153
135
|
if (providedLayerPatterns &&
|
|
154
136
|
!arraysEqual(providedLayerPatterns, defaultLayerPatterns)) {
|
|
155
|
-
// layerPatterns was explicitly provided and is different from default
|
|
156
137
|
layerPatterns = providedLayerPatterns;
|
|
157
138
|
}
|
|
158
139
|
else if (providedLayers) {
|
|
159
|
-
// layers were provided but layerPatterns weren't, so use layers
|
|
160
140
|
layerPatterns = providedLayers;
|
|
161
141
|
}
|
|
162
142
|
else {
|
|
163
|
-
// neither were provided, use defaults
|
|
164
143
|
layerPatterns = defaultLayerPatterns;
|
|
165
144
|
}
|
|
166
145
|
const sourceLayer = extractLayer(filename, layerPatterns);
|
|
167
146
|
const sourceOrder = getLayerOrder(sourceLayer, layers);
|
|
168
|
-
/**
|
|
169
|
-
* Check import declarations
|
|
170
|
-
*/
|
|
171
147
|
function checkImport(node) {
|
|
172
148
|
let importPath = '';
|
|
173
149
|
if (node.type === 'ImportDeclaration') {
|
|
@@ -178,19 +154,13 @@ exports.enforceDependencyDirection = (0, eslint_devkit_2.createRule)({
|
|
|
178
154
|
importPath = node.source.value;
|
|
179
155
|
}
|
|
180
156
|
else {
|
|
181
|
-
return;
|
|
157
|
+
return;
|
|
182
158
|
}
|
|
183
159
|
}
|
|
184
|
-
// Only check relative imports
|
|
185
160
|
if (!importPath.startsWith('.')) {
|
|
186
161
|
return;
|
|
187
162
|
}
|
|
188
|
-
// For relative imports, check the import path string directly for layer patterns
|
|
189
|
-
// This works for test cases where paths like '../infrastructure/repository' contain the layer
|
|
190
|
-
// The early return above guarantees importPath starts with '.' here.
|
|
191
163
|
let targetLayer = null;
|
|
192
|
-
// Check if import path contains layer patterns
|
|
193
|
-
// e.g., '../infrastructure/repository' -> extract 'infrastructure'
|
|
194
164
|
const segments = importPath.split(/[/\\]/);
|
|
195
165
|
for (const segment of segments) {
|
|
196
166
|
if (layerPatterns.includes(segment.toLowerCase())) {
|
|
@@ -198,7 +168,6 @@ exports.enforceDependencyDirection = (0, eslint_devkit_2.createRule)({
|
|
|
198
168
|
break;
|
|
199
169
|
}
|
|
200
170
|
}
|
|
201
|
-
// Fallback: try regex matching
|
|
202
171
|
if (!targetLayer) {
|
|
203
172
|
for (const pattern of layerPatterns) {
|
|
204
173
|
const regex = new RegExp(`[/\\\\]${pattern}[/\\\\]`, 'i');
|
|
@@ -209,23 +178,17 @@ exports.enforceDependencyDirection = (0, eslint_devkit_2.createRule)({
|
|
|
209
178
|
}
|
|
210
179
|
}
|
|
211
180
|
const targetOrder = getLayerOrder(targetLayer, layers);
|
|
212
|
-
// Skip if layers are unknown
|
|
213
181
|
if (sourceOrder < 0 || targetOrder < 0) {
|
|
214
182
|
return;
|
|
215
183
|
}
|
|
216
|
-
// Check if same layer (allowed by default)
|
|
217
184
|
if (allowSameLayer && sourceLayer === targetLayer) {
|
|
218
185
|
return;
|
|
219
186
|
}
|
|
220
|
-
// Check dependency direction: lower layers should not depend on higher layers
|
|
221
|
-
// Domain (0) should not depend on Application (1), etc.
|
|
222
187
|
if (targetOrder > sourceOrder) {
|
|
223
188
|
context.report({
|
|
224
189
|
node,
|
|
225
190
|
messageId: 'dependencyDirectionViolation',
|
|
226
191
|
data: {
|
|
227
|
-
// Both are guaranteed non-null here: sourceOrder/targetOrder >= 0
|
|
228
|
-
// implies the layers were resolved (see the guard above).
|
|
229
192
|
sourceLayer: sourceLayer,
|
|
230
193
|
targetLayer: targetLayer,
|
|
231
194
|
},
|
|
@@ -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.enforceImportOrder = void 0;
|
|
9
4
|
const eslint_devkit_1 = require("@interlace/eslint-devkit");
|
|
@@ -126,13 +121,9 @@ exports.enforceImportOrder = (0, eslint_devkit_1.createRule)({
|
|
|
126
121
|
const imports = [];
|
|
127
122
|
function getImportType(node) {
|
|
128
123
|
const source = node.source.value;
|
|
129
|
-
// console.log(`DEBUG: getImportType for ${source}`);
|
|
130
|
-
// Side-effect imports (no specifiers)
|
|
131
|
-
// import 'foo.css';
|
|
132
124
|
if (node.specifiers.length === 0) {
|
|
133
125
|
return 'side-effect';
|
|
134
126
|
}
|
|
135
|
-
// Built-in modules (node:)
|
|
136
127
|
if (source.startsWith('node:') ||
|
|
137
128
|
[
|
|
138
129
|
'fs',
|
|
@@ -145,10 +136,8 @@ exports.enforceImportOrder = (0, eslint_devkit_1.createRule)({
|
|
|
145
136
|
'stream',
|
|
146
137
|
'child_process',
|
|
147
138
|
].includes(source)) {
|
|
148
|
-
// console.log('DEBUG: identified as builtin');
|
|
149
139
|
return 'builtin';
|
|
150
140
|
}
|
|
151
|
-
// Relative imports
|
|
152
141
|
if (source.startsWith('.')) {
|
|
153
142
|
if (source.startsWith('../')) {
|
|
154
143
|
return 'parent';
|
|
@@ -158,7 +147,6 @@ exports.enforceImportOrder = (0, eslint_devkit_1.createRule)({
|
|
|
158
147
|
}
|
|
159
148
|
return 'sibling';
|
|
160
149
|
}
|
|
161
|
-
// Internal imports (based on patterns)
|
|
162
150
|
if (options.internalPatterns && options.internalPatterns.length > 0) {
|
|
163
151
|
for (const pattern of options.internalPatterns) {
|
|
164
152
|
if (new RegExp(pattern).test(source)) {
|
|
@@ -166,7 +154,6 @@ exports.enforceImportOrder = (0, eslint_devkit_1.createRule)({
|
|
|
166
154
|
}
|
|
167
155
|
}
|
|
168
156
|
}
|
|
169
|
-
// External imports (default)
|
|
170
157
|
return 'external';
|
|
171
158
|
}
|
|
172
159
|
function getGroupRank(importType) {
|
|
@@ -177,18 +164,13 @@ exports.enforceImportOrder = (0, eslint_devkit_1.createRule)({
|
|
|
177
164
|
let start = node.range[0];
|
|
178
165
|
let end = node.range[1];
|
|
179
166
|
const commentsBefore = sourceCode.getCommentsBefore(node);
|
|
180
|
-
// getCommentsBefore only returns comments strictly between the previous
|
|
181
|
-
// non-comment token and this node, so they always belong to this import —
|
|
182
|
-
// include them all in the range.
|
|
183
167
|
if (commentsBefore.length > 0) {
|
|
184
168
|
start = commentsBefore[0].range[0];
|
|
185
169
|
}
|
|
186
|
-
// Include semicolon if present
|
|
187
170
|
const tokenAfter = sourceCode.getTokenAfter(node);
|
|
188
171
|
if (tokenAfter && tokenAfter.value === ';') {
|
|
189
172
|
end = tokenAfter.range[1];
|
|
190
173
|
}
|
|
191
|
-
// Include trailing comment on the same line
|
|
192
174
|
const commentsAfter = sourceCode.getCommentsAfter(node);
|
|
193
175
|
const sameLineComment = commentsAfter.find((c) => c.loc.start.line === node.loc.end.line);
|
|
194
176
|
if (sameLineComment) {
|
|
@@ -208,8 +190,6 @@ exports.enforceImportOrder = (0, eslint_devkit_1.createRule)({
|
|
|
208
190
|
if (imports.length === 0) {
|
|
209
191
|
return;
|
|
210
192
|
}
|
|
211
|
-
// 1. Check for contiguousness
|
|
212
|
-
// Verify no non-whitespace code between imports
|
|
213
193
|
for (let i = 0; i < imports.length - 1; i++) {
|
|
214
194
|
const current = imports[i];
|
|
215
195
|
const next = imports[i + 1];
|
|
@@ -217,9 +197,6 @@ exports.enforceImportOrder = (0, eslint_devkit_1.createRule)({
|
|
|
217
197
|
includeComments: true,
|
|
218
198
|
});
|
|
219
199
|
if (tokensBetween.length > 0) {
|
|
220
|
-
// Found something between imports that isn't a comment?
|
|
221
|
-
// getTokensBetween includes comments if includeComments: true
|
|
222
|
-
// We want to know if there is CODE.
|
|
223
200
|
const codeTokens = sourceCode.getTokensBetween(current, next, {
|
|
224
201
|
includeComments: false,
|
|
225
202
|
});
|
|
@@ -228,12 +205,11 @@ exports.enforceImportOrder = (0, eslint_devkit_1.createRule)({
|
|
|
228
205
|
node: next,
|
|
229
206
|
messageId: 'importsNotContiguous',
|
|
230
207
|
});
|
|
231
|
-
return;
|
|
208
|
+
return;
|
|
232
209
|
}
|
|
233
210
|
}
|
|
234
211
|
}
|
|
235
212
|
const originalImports = [...imports];
|
|
236
|
-
// oxlint-disable-next-line unicorn/no-array-sort
|
|
237
213
|
const sortedImports = Array.from(imports).sort((a, b) => {
|
|
238
214
|
const typeA = getImportType(a);
|
|
239
215
|
const typeB = getImportType(b);
|
|
@@ -254,7 +230,6 @@ exports.enforceImportOrder = (0, eslint_devkit_1.createRule)({
|
|
|
254
230
|
}
|
|
255
231
|
return 0;
|
|
256
232
|
});
|
|
257
|
-
// Check if order is correct
|
|
258
233
|
let isSorted = true;
|
|
259
234
|
for (let i = 0; i < originalImports.length; i++) {
|
|
260
235
|
if (originalImports[i] !== sortedImports[i]) {
|
|
@@ -262,7 +237,6 @@ exports.enforceImportOrder = (0, eslint_devkit_1.createRule)({
|
|
|
262
237
|
break;
|
|
263
238
|
}
|
|
264
239
|
}
|
|
265
|
-
// Check for newlines if sorted
|
|
266
240
|
if (isSorted && options.newlinesBetween !== 'ignore') {
|
|
267
241
|
for (let i = 0; i < imports.length - 1; i++) {
|
|
268
242
|
const current = imports[i];
|
|
@@ -272,16 +246,13 @@ exports.enforceImportOrder = (0, eslint_devkit_1.createRule)({
|
|
|
272
246
|
const rankCurrent = getGroupRank(typeCurrent);
|
|
273
247
|
const rankNext = getGroupRank(typeNext);
|
|
274
248
|
if (rankCurrent !== rankNext) {
|
|
275
|
-
// Check lines between
|
|
276
249
|
const linesBetween = next.loc.start.line - current.loc.end.line;
|
|
277
|
-
// console.log(`DEBUG: linesBetween ${linesBetween} | options.newlinesBetween ${options.newlinesBetween}`);
|
|
278
250
|
if (options.newlinesBetween === 'always' && linesBetween < 2) {
|
|
279
|
-
|
|
280
|
-
isSorted = false; // Trigger fix
|
|
251
|
+
isSorted = false;
|
|
281
252
|
break;
|
|
282
253
|
}
|
|
283
254
|
if (options.newlinesBetween === 'never' && linesBetween > 1) {
|
|
284
|
-
isSorted = false;
|
|
255
|
+
isSorted = false;
|
|
285
256
|
break;
|
|
286
257
|
}
|
|
287
258
|
}
|
|
@@ -317,7 +288,6 @@ exports.enforceImportOrder = (0, eslint_devkit_1.createRule)({
|
|
|
317
288
|
}
|
|
318
289
|
newCode += getImportText(node) + '\n';
|
|
319
290
|
}
|
|
320
|
-
// Remove last newline
|
|
321
291
|
newCode = newCode.trimEnd();
|
|
322
292
|
return fixer.replaceTextRange([rangeStart, rangeEnd], newCode);
|
|
323
293
|
},
|
|
@@ -1,37 +1,13 @@
|
|
|
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.enforceTeamBoundaries = void 0;
|
|
9
4
|
const eslint_devkit_1 = require("@interlace/eslint-devkit");
|
|
10
|
-
/**
|
|
11
|
-
* Translate a user-supplied glob (`foo/**\/*.ts`) into a RegExp.
|
|
12
|
-
*
|
|
13
|
-
* Important: regex metacharacters in the input must be escaped BEFORE the
|
|
14
|
-
* glob tokens are translated. Without escaping, a pattern like `lib\foo` is
|
|
15
|
-
* interpreted as `\f` (form feed) and `lib.foo` matches `libXfoo` rather
|
|
16
|
-
* than the literal dot the user wrote. CodeQL flags the un-escaped backslash
|
|
17
|
-
* specifically (`js/incomplete-sanitization`).
|
|
18
|
-
*
|
|
19
|
-
* Algorithm: escape all regex metas (including `\`), then unescape and
|
|
20
|
-
* translate just the glob tokens `**` and `*` back to their regex form.
|
|
21
|
-
*/
|
|
22
5
|
function globToRegex(pattern) {
|
|
23
|
-
// Escape regex metacharacters INCLUDING `*`, so the subsequent replaces
|
|
24
|
-
// can find the now-escaped `\*\*` / `\*` and translate them back to their
|
|
25
|
-
// glob meanings. Without escaping `*`, the `**` in `src/platform/**`
|
|
26
|
-
// reaches `new RegExp()` raw and throws "Nothing to repeat".
|
|
27
6
|
const escaped = pattern.replace(/[.+?^${}()|[\]\\*]/g, '\\$&');
|
|
28
7
|
return new RegExp(escaped
|
|
29
8
|
.replace(/\\\*\\\*/g, '.*')
|
|
30
9
|
.replace(/\\\*/g, '[^/]*'));
|
|
31
10
|
}
|
|
32
|
-
/**
|
|
33
|
-
* Determine which team owns this file based on path patterns
|
|
34
|
-
*/
|
|
35
11
|
function getFileTeam(filePath, teams) {
|
|
36
12
|
for (const team of teams) {
|
|
37
13
|
for (const pattern of team.paths) {
|
|
@@ -42,9 +18,6 @@ function getFileTeam(filePath, teams) {
|
|
|
42
18
|
}
|
|
43
19
|
return null;
|
|
44
20
|
}
|
|
45
|
-
/**
|
|
46
|
-
* Determine which team owns the imported module
|
|
47
|
-
*/
|
|
48
21
|
function getImportTeam(importPath, teams) {
|
|
49
22
|
for (const team of teams) {
|
|
50
23
|
for (const pattern of team.paths) {
|
|
@@ -52,7 +25,6 @@ function getImportTeam(importPath, teams) {
|
|
|
52
25
|
return team;
|
|
53
26
|
}
|
|
54
27
|
}
|
|
55
|
-
// Check public packages
|
|
56
28
|
if (team.publicPackages) {
|
|
57
29
|
for (const pkg of team.publicPackages) {
|
|
58
30
|
if (importPath.startsWith(pkg)) {
|
|
@@ -63,31 +35,21 @@ function getImportTeam(importPath, teams) {
|
|
|
63
35
|
}
|
|
64
36
|
return null;
|
|
65
37
|
}
|
|
66
|
-
/**
|
|
67
|
-
* Check if import path is external (node_modules)
|
|
68
|
-
*/
|
|
69
38
|
function isExternalImport(importPath) {
|
|
70
|
-
// Relative imports start with . or ..
|
|
71
39
|
if (importPath.startsWith('.') || importPath.startsWith('/')) {
|
|
72
40
|
return false;
|
|
73
41
|
}
|
|
74
|
-
// Alias imports (configurable patterns)
|
|
75
42
|
if (importPath.startsWith('@/') || importPath.startsWith('~/')) {
|
|
76
43
|
return false;
|
|
77
44
|
}
|
|
78
|
-
// Internal project paths (src/, lib/, packages/, apps/)
|
|
79
45
|
if (importPath.startsWith('src/') ||
|
|
80
46
|
importPath.startsWith('lib/') ||
|
|
81
47
|
importPath.startsWith('packages/') ||
|
|
82
48
|
importPath.startsWith('apps/')) {
|
|
83
49
|
return false;
|
|
84
50
|
}
|
|
85
|
-
// Otherwise, likely external package
|
|
86
51
|
return true;
|
|
87
52
|
}
|
|
88
|
-
/**
|
|
89
|
-
* Check if path is in shared paths
|
|
90
|
-
*/
|
|
91
53
|
function isSharedPath(importPath, sharedPaths) {
|
|
92
54
|
for (const shared of sharedPaths) {
|
|
93
55
|
if (globToRegex(shared).test(importPath)) {
|
|
@@ -163,15 +125,11 @@ exports.enforceTeamBoundaries = (0, eslint_devkit_1.createRule)({
|
|
|
163
125
|
],
|
|
164
126
|
create(context, [options]) {
|
|
165
127
|
const { teams, sharedPaths = [], allowExternalPackages = true } = options;
|
|
166
|
-
// Get the file's team ownership
|
|
167
128
|
const filename = context.filename;
|
|
168
129
|
const sourceTeam = getFileTeam(filename, teams);
|
|
169
|
-
// If file doesn't belong to any team, skip checking
|
|
170
130
|
if (!sourceTeam) {
|
|
171
131
|
return {};
|
|
172
132
|
}
|
|
173
|
-
// Narrowed binding: the early return above guarantees sourceTeam is non-null,
|
|
174
|
-
// and closures cannot rely on that narrowing without re-checking.
|
|
175
133
|
const owningTeam = sourceTeam;
|
|
176
134
|
function checkImport(node) {
|
|
177
135
|
let importPath = null;
|
|
@@ -192,30 +150,23 @@ exports.enforceTeamBoundaries = (0, eslint_devkit_1.createRule)({
|
|
|
192
150
|
}
|
|
193
151
|
if (!importPath)
|
|
194
152
|
return;
|
|
195
|
-
// Allow external packages
|
|
196
153
|
if (allowExternalPackages && isExternalImport(importPath)) {
|
|
197
154
|
return;
|
|
198
155
|
}
|
|
199
|
-
// Allow shared paths
|
|
200
156
|
if (isSharedPath(importPath, sharedPaths)) {
|
|
201
157
|
return;
|
|
202
158
|
}
|
|
203
|
-
// Get target team
|
|
204
159
|
const targetTeam = getImportTeam(importPath, teams);
|
|
205
|
-
// If import doesn't belong to any team, allow it (might be shared code)
|
|
206
160
|
if (!targetTeam) {
|
|
207
161
|
return;
|
|
208
162
|
}
|
|
209
|
-
// Same team - always allowed
|
|
210
163
|
if (targetTeam.team === owningTeam.team) {
|
|
211
164
|
return;
|
|
212
165
|
}
|
|
213
|
-
// Check if target team is in allowed dependencies
|
|
214
166
|
const allowedDeps = owningTeam.allowedDependencies || [];
|
|
215
167
|
if (allowedDeps.includes(targetTeam.team)) {
|
|
216
168
|
return;
|
|
217
169
|
}
|
|
218
|
-
// Unauthorized cross-team import!
|
|
219
170
|
context.report({
|
|
220
171
|
node,
|
|
221
172
|
messageId: 'unauthorizedTeamImport',
|
package/src/rules/export.js
CHANGED
|
@@ -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.exportRule = void 0;
|
|
9
4
|
const eslint_devkit_1 = require("@interlace/eslint-devkit");
|
|
@@ -71,7 +66,6 @@ exports.exportRule = (0, eslint_devkit_1.createRule)({
|
|
|
71
66
|
checkDefaultExport(node);
|
|
72
67
|
},
|
|
73
68
|
ExportNamedDeclaration(node) {
|
|
74
|
-
// Export with declaration: export const foo = 1;
|
|
75
69
|
if (node.declaration) {
|
|
76
70
|
if (node.declaration.type === eslint_devkit_1.AST_NODE_TYPES.VariableDeclaration) {
|
|
77
71
|
node.declaration.declarations.forEach((decl) => {
|
|
@@ -94,7 +88,6 @@ exports.exportRule = (0, eslint_devkit_1.createRule)({
|
|
|
94
88
|
}
|
|
95
89
|
}
|
|
96
90
|
}
|
|
97
|
-
// Export specifiers: export { foo, bar };
|
|
98
91
|
node.specifiers.forEach((spec) => {
|
|
99
92
|
const exportedName = spec.exported.type === eslint_devkit_1.AST_NODE_TYPES.Identifier
|
|
100
93
|
? spec.exported.name
|
|
@@ -108,12 +101,10 @@ exports.exportRule = (0, eslint_devkit_1.createRule)({
|
|
|
108
101
|
});
|
|
109
102
|
},
|
|
110
103
|
ExportAllDeclaration(node) {
|
|
111
|
-
// export * as name from '...'
|
|
112
104
|
if (node.exported) {
|
|
113
105
|
const exportedName = node.exported.name;
|
|
114
106
|
checkAndAddExport(exportedName, node);
|
|
115
107
|
}
|
|
116
|
-
// Regular export * doesn't create named conflicts we can statically detect
|
|
117
108
|
},
|
|
118
109
|
};
|
|
119
110
|
},
|
|
@@ -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.exportsLast = void 0;
|
|
9
4
|
const eslint_devkit_1 = require("@interlace/eslint-devkit");
|
|
@@ -36,7 +31,6 @@ exports.exportsLast = (0, eslint_devkit_1.createRule)({
|
|
|
36
31
|
'Program:exit'(node) {
|
|
37
32
|
const body = node.body;
|
|
38
33
|
let lastNonExportIndex = -1;
|
|
39
|
-
// Find all export statements and non-export statements
|
|
40
34
|
const exportIndices = [];
|
|
41
35
|
const nonExportIndices = [];
|
|
42
36
|
body.forEach((statement, index) => {
|
|
@@ -44,11 +38,8 @@ exports.exportsLast = (0, eslint_devkit_1.createRule)({
|
|
|
44
38
|
statement.type === eslint_devkit_1.AST_NODE_TYPES.ExportNamedDeclaration ||
|
|
45
39
|
statement.type === eslint_devkit_1.AST_NODE_TYPES.ExportAllDeclaration;
|
|
46
40
|
if (isExport) {
|
|
47
|
-
// Skip export declarations that are also declarations (export const x = 1)
|
|
48
|
-
// These often need to appear where they're declared
|
|
49
41
|
if (statement.type === eslint_devkit_1.AST_NODE_TYPES.ExportNamedDeclaration &&
|
|
50
42
|
statement.declaration) {
|
|
51
|
-
// This is an inline export, treat as non-export for ordering
|
|
52
43
|
nonExportIndices.push(index);
|
|
53
44
|
}
|
|
54
45
|
else {
|
|
@@ -62,7 +53,6 @@ exports.exportsLast = (0, eslint_devkit_1.createRule)({
|
|
|
62
53
|
if (nonExportIndices.length > 0) {
|
|
63
54
|
lastNonExportIndex = Math.max(...nonExportIndices);
|
|
64
55
|
}
|
|
65
|
-
// Check if any export appears before the last non-export
|
|
66
56
|
for (const exportIndex of exportIndices) {
|
|
67
57
|
if (exportIndex < lastNonExportIndex) {
|
|
68
58
|
context.report({
|
package/src/rules/extensions.js
CHANGED
|
@@ -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.extensions = void 0;
|
|
9
4
|
const tslib_1 = require("tslib");
|
|
@@ -84,8 +79,8 @@ exports.extensions = (0, eslint_devkit_1.createRule)({
|
|
|
84
79
|
ImportDeclaration(node) {
|
|
85
80
|
const source = node.source.value;
|
|
86
81
|
if (!source.startsWith('.'))
|
|
87
|
-
return;
|
|
88
|
-
const ext = node_path_1.default.extname(source).slice(1);
|
|
82
|
+
return;
|
|
83
|
+
const ext = node_path_1.default.extname(source).slice(1);
|
|
89
84
|
const expected = pattern[ext] || defaultBehavior;
|
|
90
85
|
if (ext && expected === 'never') {
|
|
91
86
|
context.report({
|
|
@@ -97,14 +92,10 @@ exports.extensions = (0, eslint_devkit_1.createRule)({
|
|
|
97
92
|
});
|
|
98
93
|
}
|
|
99
94
|
else if (!ext) {
|
|
100
|
-
// Hard to know what the extension *should* be without checking file system
|
|
101
|
-
// But if default is 'always', we might flag it.
|
|
102
|
-
// For now, let's assume if it's missing and we expect 'always', it's a problem.
|
|
103
95
|
if (defaultBehavior === 'always') {
|
|
104
96
|
context.report({
|
|
105
97
|
node: node.source,
|
|
106
98
|
messageId: 'missingExtension',
|
|
107
|
-
// Can't fix without knowing extension
|
|
108
99
|
});
|
|
109
100
|
}
|
|
110
101
|
}
|
package/src/rules/first.js
CHANGED
|
@@ -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.first = void 0;
|
|
9
4
|
const eslint_devkit_1 = require("@interlace/eslint-devkit");
|
|
@@ -48,7 +43,6 @@ exports.first = (0, eslint_devkit_1.createRule)({
|
|
|
48
43
|
}
|
|
49
44
|
},
|
|
50
45
|
};
|
|
51
|
-
// oxlint-disable-next-line consistent-function-scoping
|
|
52
46
|
function isDirective(node) {
|
|
53
47
|
return (node.type === 'ExpressionStatement' &&
|
|
54
48
|
node.expression.type === 'Literal' &&
|
|
@@ -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.groupExports = void 0;
|
|
9
4
|
const eslint_devkit_1 = require("@interlace/eslint-devkit");
|
|
@@ -32,15 +27,12 @@ exports.groupExports = (0, eslint_devkit_1.createRule)({
|
|
|
32
27
|
},
|
|
33
28
|
defaultOptions: [],
|
|
34
29
|
create(context) {
|
|
35
|
-
// Track separate export statements (not counting inline exports)
|
|
36
30
|
const separateExports = [];
|
|
37
31
|
const reExportSources = new Map();
|
|
38
32
|
return {
|
|
39
33
|
ExportNamedDeclaration(node) {
|
|
40
|
-
// Only track export specifier lists, not inline declarations
|
|
41
34
|
if (!node.declaration && node.specifiers.length > 0) {
|
|
42
35
|
if (node.source) {
|
|
43
|
-
// Re-export from another module
|
|
44
36
|
const source = node.source.value;
|
|
45
37
|
if (!reExportSources.has(source)) {
|
|
46
38
|
reExportSources.set(source, []);
|
|
@@ -48,15 +40,12 @@ exports.groupExports = (0, eslint_devkit_1.createRule)({
|
|
|
48
40
|
reExportSources.get(source)?.push(node);
|
|
49
41
|
}
|
|
50
42
|
else {
|
|
51
|
-
// Local export
|
|
52
43
|
separateExports.push(node);
|
|
53
44
|
}
|
|
54
45
|
}
|
|
55
46
|
},
|
|
56
47
|
'Program:exit'() {
|
|
57
|
-
// Report if there are multiple separate local exports that could be combined
|
|
58
48
|
if (separateExports.length > 1) {
|
|
59
|
-
// Report all but the first one
|
|
60
49
|
for (let i = 1; i < separateExports.length; i++) {
|
|
61
50
|
context.report({
|
|
62
51
|
node: separateExports[i],
|
|
@@ -64,10 +53,8 @@ exports.groupExports = (0, eslint_devkit_1.createRule)({
|
|
|
64
53
|
});
|
|
65
54
|
}
|
|
66
55
|
}
|
|
67
|
-
// Report if there are multiple re-exports from the same source
|
|
68
56
|
for (const [, exports] of reExportSources) {
|
|
69
57
|
if (exports.length > 1) {
|
|
70
|
-
// Report all but the first one
|
|
71
58
|
for (let i = 1; i < exports.length; i++) {
|
|
72
59
|
context.report({
|
|
73
60
|
node: exports[i],
|
|
@@ -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.maxDependencies = void 0;
|
|
9
4
|
const eslint_devkit_1 = require("@interlace/eslint-devkit");
|
|
@@ -92,7 +87,6 @@ exports.maxDependencies = (0, eslint_devkit_1.createRule)({
|
|
|
92
87
|
if (!filename) {
|
|
93
88
|
return {};
|
|
94
89
|
}
|
|
95
|
-
// Check if file should be ignored
|
|
96
90
|
const shouldIgnoreFile = ignoreFiles.some((pattern) => {
|
|
97
91
|
if (pattern.includes('*')) {
|
|
98
92
|
const regex = new RegExp(pattern.replace(/\*/g, '.*'));
|
|
@@ -105,19 +99,15 @@ exports.maxDependencies = (0, eslint_devkit_1.createRule)({
|
|
|
105
99
|
}
|
|
106
100
|
const dependencies = new Set();
|
|
107
101
|
function shouldCountImport(importPath, node) {
|
|
108
|
-
// Skip ignored imports
|
|
109
102
|
if (ignoreImports.includes(importPath)) {
|
|
110
103
|
return false;
|
|
111
104
|
}
|
|
112
|
-
// Skip type-only imports if configured
|
|
113
105
|
if (ignoreTypeImports && node.importKind === 'type') {
|
|
114
106
|
return false;
|
|
115
107
|
}
|
|
116
|
-
// Skip relative imports (internal modules)
|
|
117
108
|
if (importPath.startsWith('./') || importPath.startsWith('../')) {
|
|
118
109
|
return false;
|
|
119
110
|
}
|
|
120
|
-
// Skip Node.js built-ins
|
|
121
111
|
const nodeBuiltins = [
|
|
122
112
|
'assert',
|
|
123
113
|
'buffer',
|
|
@@ -154,15 +144,12 @@ exports.maxDependencies = (0, eslint_devkit_1.createRule)({
|
|
|
154
144
|
return true;
|
|
155
145
|
}
|
|
156
146
|
function shouldCountRequire(importPath) {
|
|
157
|
-
// Skip ignored imports
|
|
158
147
|
if (ignoreImports.includes(importPath)) {
|
|
159
148
|
return false;
|
|
160
149
|
}
|
|
161
|
-
// Skip relative imports
|
|
162
150
|
if (importPath.startsWith('./') || importPath.startsWith('../')) {
|
|
163
151
|
return false;
|
|
164
152
|
}
|
|
165
|
-
// Skip Node.js built-ins
|
|
166
153
|
const nodeBuiltins = [
|
|
167
154
|
'assert',
|
|
168
155
|
'buffer',
|
|
@@ -199,7 +186,6 @@ exports.maxDependencies = (0, eslint_devkit_1.createRule)({
|
|
|
199
186
|
return true;
|
|
200
187
|
}
|
|
201
188
|
function addDependency(importPath) {
|
|
202
|
-
// Extract package name from scoped packages or regular packages
|
|
203
189
|
const packageName = importPath.startsWith('@')
|
|
204
190
|
? importPath.split('/').slice(0, 2).join('/')
|
|
205
191
|
: importPath.split('/')[0];
|
|
@@ -214,7 +200,6 @@ exports.maxDependencies = (0, eslint_devkit_1.createRule)({
|
|
|
214
200
|
}
|
|
215
201
|
},
|
|
216
202
|
CallExpression(node) {
|
|
217
|
-
// Check require() calls
|
|
218
203
|
if (node.callee.type === 'Identifier' &&
|
|
219
204
|
node.callee.name === 'require' &&
|
|
220
205
|
node.arguments.length === 1) {
|
|
@@ -226,7 +211,6 @@ exports.maxDependencies = (0, eslint_devkit_1.createRule)({
|
|
|
226
211
|
}
|
|
227
212
|
}
|
|
228
213
|
},
|
|
229
|
-
// Handle dynamic imports (import() expressions)
|
|
230
214
|
ImportExpression(node) {
|
|
231
215
|
const source = node.source;
|
|
232
216
|
if (source.type === 'Literal' && typeof source.value === 'string') {
|