@atlaskit/eslint-plugin-platform 2.8.0 → 2.9.1
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/CHANGELOG.md +16 -0
- package/dist/cjs/index.js +8 -1
- package/dist/cjs/rules/ensure-critical-dependency-resolutions/index.js +0 -1
- package/dist/cjs/rules/ensure-use-sync-external-store-server-snapshot/index.js +41 -0
- package/dist/cjs/rules/import/no-barrel-entry-imports/index.js +534 -74
- package/dist/cjs/rules/import/no-barrel-entry-jest-mock/index.js +428 -119
- package/dist/cjs/rules/import/no-jest-mock-barrel-files/index.js +3 -2
- package/dist/cjs/rules/import/no-relative-barrel-file-imports/index.js +7 -3
- package/dist/cjs/rules/import/shared/jest-utils.js +62 -9
- package/dist/cjs/rules/import/shared/package-resolution.js +300 -22
- package/dist/cjs/rules/no-restricted-fedramp-imports/index.js +65 -0
- package/dist/cjs/rules/visit-example-type-import-required/index.js +409 -0
- package/dist/es2019/index.js +8 -1
- package/dist/es2019/rules/ensure-critical-dependency-resolutions/index.js +0 -1
- package/dist/es2019/rules/ensure-use-sync-external-store-server-snapshot/index.js +43 -0
- package/dist/es2019/rules/import/no-barrel-entry-imports/index.js +431 -25
- package/dist/es2019/rules/import/no-barrel-entry-jest-mock/index.js +287 -25
- package/dist/es2019/rules/import/no-jest-mock-barrel-files/index.js +3 -2
- package/dist/es2019/rules/import/no-relative-barrel-file-imports/index.js +7 -3
- package/dist/es2019/rules/import/shared/jest-utils.js +44 -0
- package/dist/es2019/rules/import/shared/package-resolution.js +211 -4
- package/dist/es2019/rules/no-restricted-fedramp-imports/index.js +47 -0
- package/dist/es2019/rules/visit-example-type-import-required/index.js +375 -0
- package/dist/esm/index.js +8 -1
- package/dist/esm/rules/ensure-critical-dependency-resolutions/index.js +0 -1
- package/dist/esm/rules/ensure-use-sync-external-store-server-snapshot/index.js +35 -0
- package/dist/esm/rules/import/no-barrel-entry-imports/index.js +535 -75
- package/dist/esm/rules/import/no-barrel-entry-jest-mock/index.js +430 -121
- package/dist/esm/rules/import/no-jest-mock-barrel-files/index.js +3 -2
- package/dist/esm/rules/import/no-relative-barrel-file-imports/index.js +7 -3
- package/dist/esm/rules/import/shared/jest-utils.js +61 -9
- package/dist/esm/rules/import/shared/package-resolution.js +298 -24
- package/dist/esm/rules/no-restricted-fedramp-imports/index.js +59 -0
- package/dist/esm/rules/visit-example-type-import-required/index.js +402 -0
- package/dist/types/index.d.ts +14 -0
- package/dist/types/rules/ensure-use-sync-external-store-server-snapshot/index.d.ts +3 -0
- package/dist/types/rules/import/shared/jest-utils.d.ts +8 -0
- package/dist/types/rules/import/shared/package-resolution.d.ts +47 -2
- package/dist/types/rules/no-restricted-fedramp-imports/index.d.ts +3 -0
- package/dist/types/rules/visit-example-type-import-required/index.d.ts +4 -0
- package/dist/types-ts4.5/index.d.ts +14 -0
- package/dist/types-ts4.5/rules/ensure-use-sync-external-store-server-snapshot/index.d.ts +3 -0
- package/dist/types-ts4.5/rules/import/shared/jest-utils.d.ts +8 -0
- package/dist/types-ts4.5/rules/import/shared/package-resolution.d.ts +47 -2
- package/dist/types-ts4.5/rules/no-restricted-fedramp-imports/index.d.ts +3 -0
- package/dist/types-ts4.5/rules/visit-example-type-import-required/index.d.ts +4 -0
- package/package.json +3 -1
|
@@ -2,7 +2,7 @@ import { dirname } from 'path';
|
|
|
2
2
|
import { parseBarrelExports } from '../shared/barrel-parsing';
|
|
3
3
|
import { DEFAULT_TARGET_FOLDERS, findWorkspaceRoot, isRelativeImport } from '../shared/file-system';
|
|
4
4
|
import { findPackageInRegistry, isPackageInApplyToImportsFrom } from '../shared/package-registry';
|
|
5
|
-
import { findExportForSourceFile, parsePackageExports } from '../shared/package-resolution';
|
|
5
|
+
import { findCrossPackageBridgeExportPath, findExportForSourceFile, parsePackageExports } from '../shared/package-resolution';
|
|
6
6
|
import { realFileSystem } from '../shared/types';
|
|
7
7
|
|
|
8
8
|
/**
|
|
@@ -33,6 +33,10 @@ const ruleMeta = {
|
|
|
33
33
|
type: 'string'
|
|
34
34
|
},
|
|
35
35
|
description: 'The folder paths (relative to workspace root) containing packages whose imports will be checked and autofixed.'
|
|
36
|
+
},
|
|
37
|
+
preferImportedPackageSubpath: {
|
|
38
|
+
type: 'boolean',
|
|
39
|
+
description: 'Prefer subpaths on the imported barrel package when they bridge to the dependency (e.g. @scope/pkg/subpath instead of @scope/dependency).'
|
|
36
40
|
}
|
|
37
41
|
},
|
|
38
42
|
additionalProperties: false
|
|
@@ -110,20 +114,15 @@ function buildImportStatement({
|
|
|
110
114
|
*/
|
|
111
115
|
|
|
112
116
|
/**
|
|
113
|
-
* Resolves import context for barrel file analysis.
|
|
117
|
+
* Resolves import context for barrel file analysis from a module specifier string.
|
|
114
118
|
* Returns null if the import should not be processed (relative import, not in target folder, etc.)
|
|
115
119
|
*/
|
|
116
|
-
function
|
|
117
|
-
|
|
120
|
+
function resolveImportContextFromModulePath({
|
|
121
|
+
importPath,
|
|
118
122
|
workspaceRoot,
|
|
119
123
|
fs,
|
|
120
124
|
applyToImportsFrom
|
|
121
125
|
}) {
|
|
122
|
-
if (!node.source || typeof node.source.value !== 'string') {
|
|
123
|
-
return null;
|
|
124
|
-
}
|
|
125
|
-
const importPath = node.source.value;
|
|
126
|
-
|
|
127
126
|
// Skip relative imports - this rule is for cross-package imports
|
|
128
127
|
if (isRelativeImport(importPath)) {
|
|
129
128
|
return null;
|
|
@@ -197,6 +196,27 @@ function resolveImportContext({
|
|
|
197
196
|
};
|
|
198
197
|
}
|
|
199
198
|
|
|
199
|
+
/**
|
|
200
|
+
* Resolves import context for barrel file analysis.
|
|
201
|
+
* Returns null if the import should not be processed (relative import, not in target folder, etc.)
|
|
202
|
+
*/
|
|
203
|
+
function resolveImportContext({
|
|
204
|
+
node,
|
|
205
|
+
workspaceRoot,
|
|
206
|
+
fs,
|
|
207
|
+
applyToImportsFrom
|
|
208
|
+
}) {
|
|
209
|
+
if (!node.source || typeof node.source.value !== 'string') {
|
|
210
|
+
return null;
|
|
211
|
+
}
|
|
212
|
+
return resolveImportContextFromModulePath({
|
|
213
|
+
importPath: node.source.value,
|
|
214
|
+
workspaceRoot,
|
|
215
|
+
fs,
|
|
216
|
+
applyToImportsFrom
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
|
|
200
220
|
/**
|
|
201
221
|
* Classifies import specifiers by their target export paths.
|
|
202
222
|
* Groups specifiers that can be remapped to more specific exports.
|
|
@@ -206,12 +226,14 @@ function classifySpecifiers({
|
|
|
206
226
|
node,
|
|
207
227
|
importContext,
|
|
208
228
|
workspaceRoot,
|
|
209
|
-
fs
|
|
229
|
+
fs,
|
|
230
|
+
preferImportedPackageSubpath
|
|
210
231
|
}) {
|
|
211
232
|
const {
|
|
212
233
|
currentExportPath,
|
|
213
234
|
exportsMap,
|
|
214
|
-
exportMap
|
|
235
|
+
exportMap,
|
|
236
|
+
packageName: importedPackageName
|
|
215
237
|
} = importContext;
|
|
216
238
|
const specifiers = node.specifiers;
|
|
217
239
|
const specifiersByTarget = new Map();
|
|
@@ -229,6 +251,7 @@ function classifySpecifiers({
|
|
|
229
251
|
let kind = 'value';
|
|
230
252
|
if (spec.type === 'ImportDefaultSpecifier') {
|
|
231
253
|
nameInSource = 'default';
|
|
254
|
+
kind = node.importKind === 'type' ? 'type' : 'value';
|
|
232
255
|
} else if (spec.type === 'ImportSpecifier') {
|
|
233
256
|
nameInSource = getImportedName(spec);
|
|
234
257
|
const parentImportKind = node.importKind;
|
|
@@ -238,12 +261,43 @@ function classifySpecifiers({
|
|
|
238
261
|
}
|
|
239
262
|
const exportInfo = exportMap.get(nameInSource);
|
|
240
263
|
if (exportInfo) {
|
|
241
|
-
var _exportInfo$crossPack;
|
|
264
|
+
var _exportInfo$crossPack, _exportInfo$originalN2, _matchResult$exportPa2;
|
|
242
265
|
const effectiveKind = kind === 'type' || exportInfo.isTypeOnly ? 'type' : 'value';
|
|
243
266
|
|
|
244
267
|
// Check if this is a cross-package re-export
|
|
245
268
|
const sourcePackageName = (_exportInfo$crossPack = exportInfo.crossPackageSource) === null || _exportInfo$crossPack === void 0 ? void 0 : _exportInfo$crossPack.packageName;
|
|
246
269
|
if (sourcePackageName) {
|
|
270
|
+
let targetKey;
|
|
271
|
+
let resolvedOriginalName = exportInfo.originalName;
|
|
272
|
+
if (preferImportedPackageSubpath) {
|
|
273
|
+
const bridge = findCrossPackageBridgeExportPath({
|
|
274
|
+
exportsMap,
|
|
275
|
+
crossPackageName: sourcePackageName,
|
|
276
|
+
exportedName: nameInSource,
|
|
277
|
+
fs
|
|
278
|
+
});
|
|
279
|
+
if (bridge) {
|
|
280
|
+
targetKey = importedPackageName + bridge.exportPath.slice(1);
|
|
281
|
+
if (bridge.entryPointExportName !== undefined) {
|
|
282
|
+
resolvedOriginalName = bridge.entryPointExportName === nameInSource ? undefined : bridge.entryPointExportName;
|
|
283
|
+
}
|
|
284
|
+
if (!specifiersByTarget.has(targetKey)) {
|
|
285
|
+
specifiersByTarget.set(targetKey, []);
|
|
286
|
+
}
|
|
287
|
+
specifiersByTarget.get(targetKey).push({
|
|
288
|
+
spec: {
|
|
289
|
+
...spec,
|
|
290
|
+
importKind: effectiveKind
|
|
291
|
+
},
|
|
292
|
+
originalName: resolvedOriginalName,
|
|
293
|
+
targetExportPath: targetKey,
|
|
294
|
+
kind: effectiveKind,
|
|
295
|
+
sourcePackageName
|
|
296
|
+
});
|
|
297
|
+
continue;
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
247
301
|
// For cross-package re-exports, find the most specific subpath in the source package
|
|
248
302
|
// Note: Package resolution is not constrained by applyToImportsFrom - any package can be resolved
|
|
249
303
|
let sourcePackageExportsMap = sourcePackageExportsMaps.get(sourcePackageName);
|
|
@@ -265,14 +319,22 @@ function classifySpecifiers({
|
|
|
265
319
|
// Find the best export path in the source package
|
|
266
320
|
let targetExportPath = null;
|
|
267
321
|
if (sourcePackageExportsMap) {
|
|
268
|
-
|
|
322
|
+
var _exportInfo$originalN, _matchResult$exportPa;
|
|
323
|
+
const sourceExportName = (_exportInfo$originalN = exportInfo.originalName) !== null && _exportInfo$originalN !== void 0 ? _exportInfo$originalN : nameInSource;
|
|
324
|
+
const matchResult = findExportForSourceFile({
|
|
269
325
|
sourceFilePath: exportInfo.path,
|
|
270
|
-
exportsMap: sourcePackageExportsMap
|
|
326
|
+
exportsMap: sourcePackageExportsMap,
|
|
327
|
+
fs,
|
|
328
|
+
sourceExportName
|
|
271
329
|
});
|
|
330
|
+
targetExportPath = (_matchResult$exportPa = matchResult === null || matchResult === void 0 ? void 0 : matchResult.exportPath) !== null && _matchResult$exportPa !== void 0 ? _matchResult$exportPa : null;
|
|
331
|
+
if ((matchResult === null || matchResult === void 0 ? void 0 : matchResult.entryPointExportName) !== undefined) {
|
|
332
|
+
resolvedOriginalName = matchResult.entryPointExportName === nameInSource ? undefined : matchResult.entryPointExportName;
|
|
333
|
+
}
|
|
272
334
|
}
|
|
273
335
|
|
|
274
336
|
// Build the full import path: @package/subpath or just @package if no subpath found
|
|
275
|
-
|
|
337
|
+
targetKey = targetExportPath ? sourcePackageName + targetExportPath.slice(1) // Remove leading '.' from subpath
|
|
276
338
|
: sourcePackageName;
|
|
277
339
|
if (!specifiersByTarget.has(targetKey)) {
|
|
278
340
|
specifiersByTarget.set(targetKey, []);
|
|
@@ -282,7 +344,7 @@ function classifySpecifiers({
|
|
|
282
344
|
...spec,
|
|
283
345
|
importKind: effectiveKind
|
|
284
346
|
},
|
|
285
|
-
originalName:
|
|
347
|
+
originalName: resolvedOriginalName,
|
|
286
348
|
targetExportPath: targetKey,
|
|
287
349
|
kind: effectiveKind,
|
|
288
350
|
sourcePackageName
|
|
@@ -291,10 +353,18 @@ function classifySpecifiers({
|
|
|
291
353
|
}
|
|
292
354
|
|
|
293
355
|
// Find if there's a package.json export that points to this source file
|
|
294
|
-
const
|
|
356
|
+
const sourceExportName = (_exportInfo$originalN2 = exportInfo.originalName) !== null && _exportInfo$originalN2 !== void 0 ? _exportInfo$originalN2 : nameInSource;
|
|
357
|
+
const matchResult = findExportForSourceFile({
|
|
295
358
|
sourceFilePath: exportInfo.path,
|
|
296
|
-
exportsMap
|
|
359
|
+
exportsMap,
|
|
360
|
+
fs,
|
|
361
|
+
sourceExportName
|
|
297
362
|
});
|
|
363
|
+
const targetExportPath = (_matchResult$exportPa2 = matchResult === null || matchResult === void 0 ? void 0 : matchResult.exportPath) !== null && _matchResult$exportPa2 !== void 0 ? _matchResult$exportPa2 : null;
|
|
364
|
+
let resolvedOriginalName2 = exportInfo.originalName;
|
|
365
|
+
if ((matchResult === null || matchResult === void 0 ? void 0 : matchResult.entryPointExportName) !== undefined) {
|
|
366
|
+
resolvedOriginalName2 = matchResult.entryPointExportName === nameInSource ? undefined : matchResult.entryPointExportName;
|
|
367
|
+
}
|
|
298
368
|
|
|
299
369
|
// Get the file that the current export path resolves to
|
|
300
370
|
const currentExportResolvedFile = exportsMap.get(currentExportPath);
|
|
@@ -314,7 +384,7 @@ function classifySpecifiers({
|
|
|
314
384
|
...spec,
|
|
315
385
|
importKind: effectiveKind
|
|
316
386
|
},
|
|
317
|
-
originalName:
|
|
387
|
+
originalName: resolvedOriginalName2,
|
|
318
388
|
targetExportPath,
|
|
319
389
|
kind: effectiveKind
|
|
320
390
|
});
|
|
@@ -619,7 +689,8 @@ function createBarrelImportFix({
|
|
|
619
689
|
}
|
|
620
690
|
} else {
|
|
621
691
|
// Create new import
|
|
622
|
-
const
|
|
692
|
+
const allSpecsAreType = specsWithTarget.every(s => s.kind === 'type');
|
|
693
|
+
const isTypeImport = node.importKind === 'type' || allSpecsAreType;
|
|
623
694
|
const importStatement = buildImportStatement({
|
|
624
695
|
specs,
|
|
625
696
|
path: newImportPath,
|
|
@@ -635,7 +706,8 @@ function createBarrelImportFix({
|
|
|
635
706
|
// Handle unmapped specifiers - they stay in the original import
|
|
636
707
|
if (unmappedSpecifiers.length > 0) {
|
|
637
708
|
const unmappedSpecs = unmappedSpecifiers.map(u => u.spec);
|
|
638
|
-
const
|
|
709
|
+
const allUnmappedAreType = unmappedSpecifiers.every(u => u.kind === 'type');
|
|
710
|
+
const isTypeImport = node.importKind === 'type' || allUnmappedAreType;
|
|
639
711
|
const remainingImport = buildImportStatement({
|
|
640
712
|
specs: unmappedSpecs,
|
|
641
713
|
path: importPath,
|
|
@@ -686,6 +758,316 @@ function createBarrelImportFix({
|
|
|
686
758
|
}
|
|
687
759
|
return fixes;
|
|
688
760
|
}
|
|
761
|
+
function isPlainRequireCall(node) {
|
|
762
|
+
if (node.callee.type !== 'Identifier' || node.callee.name !== 'require') {
|
|
763
|
+
return false;
|
|
764
|
+
}
|
|
765
|
+
if (node.arguments.length !== 1) {
|
|
766
|
+
return false;
|
|
767
|
+
}
|
|
768
|
+
const arg = node.arguments[0];
|
|
769
|
+
return arg.type === 'Literal' && typeof arg.value === 'string';
|
|
770
|
+
}
|
|
771
|
+
function unwrapToRequireCall(expr) {
|
|
772
|
+
let e = expr;
|
|
773
|
+
for (;;) {
|
|
774
|
+
const wrapped = e;
|
|
775
|
+
if (wrapped.type !== 'ParenthesizedExpression' || !wrapped.expression) {
|
|
776
|
+
break;
|
|
777
|
+
}
|
|
778
|
+
e = wrapped.expression;
|
|
779
|
+
}
|
|
780
|
+
if (e.type !== 'CallExpression' || !isPlainRequireCall(e)) {
|
|
781
|
+
return null;
|
|
782
|
+
}
|
|
783
|
+
return e;
|
|
784
|
+
}
|
|
785
|
+
function buildSyntheticImportFromRequireAccess(exportPropertyName, modulePath) {
|
|
786
|
+
const specifiers = exportPropertyName === 'default' ? [{
|
|
787
|
+
type: 'ImportDefaultSpecifier',
|
|
788
|
+
local: {
|
|
789
|
+
type: 'Identifier',
|
|
790
|
+
name: '_r'
|
|
791
|
+
}
|
|
792
|
+
}] : [{
|
|
793
|
+
type: 'ImportSpecifier',
|
|
794
|
+
imported: {
|
|
795
|
+
type: 'Identifier',
|
|
796
|
+
name: exportPropertyName
|
|
797
|
+
},
|
|
798
|
+
local: {
|
|
799
|
+
type: 'Identifier',
|
|
800
|
+
name: exportPropertyName
|
|
801
|
+
}
|
|
802
|
+
}];
|
|
803
|
+
return {
|
|
804
|
+
type: 'ImportDeclaration',
|
|
805
|
+
source: {
|
|
806
|
+
type: 'Literal',
|
|
807
|
+
value: modulePath,
|
|
808
|
+
raw: `'${modulePath}'`
|
|
809
|
+
},
|
|
810
|
+
specifiers,
|
|
811
|
+
importKind: 'value'
|
|
812
|
+
};
|
|
813
|
+
}
|
|
814
|
+
function fullNewImportPathForTarget(targetKey, specsWithTarget, packageName) {
|
|
815
|
+
const isCrossPackage = specsWithTarget.some(s => s.sourcePackageName);
|
|
816
|
+
return isCrossPackage ? targetKey : packageName + targetKey.slice(1);
|
|
817
|
+
}
|
|
818
|
+
function getRhsPropertyAfterTransform(spec) {
|
|
819
|
+
if (spec.type === 'ImportDefaultSpecifier') {
|
|
820
|
+
return 'default';
|
|
821
|
+
}
|
|
822
|
+
return getImportedName(spec);
|
|
823
|
+
}
|
|
824
|
+
function appendAutomockFixesForPathMigration({
|
|
825
|
+
fixer,
|
|
826
|
+
sourceCode,
|
|
827
|
+
oldBarrelPath,
|
|
828
|
+
newPaths
|
|
829
|
+
}) {
|
|
830
|
+
const automocks = findMatchingAutomocks({
|
|
831
|
+
sourceCode,
|
|
832
|
+
importPath: oldBarrelPath
|
|
833
|
+
});
|
|
834
|
+
if (automocks.length === 0 || newPaths.length === 0) {
|
|
835
|
+
return [];
|
|
836
|
+
}
|
|
837
|
+
const fixes = [];
|
|
838
|
+
for (const automock of automocks) {
|
|
839
|
+
const newAutomockStatements = newPaths.map(path => buildAutomockStatement({
|
|
840
|
+
path,
|
|
841
|
+
quoteChar: automock.quoteChar
|
|
842
|
+
}));
|
|
843
|
+
fixes.push(fixer.replaceTextRange(automock.statementNode.range, newAutomockStatements.join('\n')));
|
|
844
|
+
}
|
|
845
|
+
return fixes;
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
/**
|
|
849
|
+
* `require('barrel').default` or `require('barrel').namedExport`
|
|
850
|
+
*/
|
|
851
|
+
function handleRequireMemberExpression({
|
|
852
|
+
node,
|
|
853
|
+
context,
|
|
854
|
+
workspaceRoot,
|
|
855
|
+
fs,
|
|
856
|
+
applyToImportsFrom,
|
|
857
|
+
preferImportedPackageSubpath
|
|
858
|
+
}) {
|
|
859
|
+
if (node.computed || node.property.type !== 'Identifier') {
|
|
860
|
+
return;
|
|
861
|
+
}
|
|
862
|
+
const reqCall = unwrapToRequireCall(node.object);
|
|
863
|
+
if (!reqCall) {
|
|
864
|
+
return;
|
|
865
|
+
}
|
|
866
|
+
const modulePath = reqCall.arguments[0].value;
|
|
867
|
+
const importContext = resolveImportContextFromModulePath({
|
|
868
|
+
importPath: modulePath,
|
|
869
|
+
workspaceRoot,
|
|
870
|
+
fs,
|
|
871
|
+
applyToImportsFrom
|
|
872
|
+
});
|
|
873
|
+
if (!importContext) {
|
|
874
|
+
return;
|
|
875
|
+
}
|
|
876
|
+
const exportPropertyName = node.property.name;
|
|
877
|
+
const synthetic = buildSyntheticImportFromRequireAccess(exportPropertyName, modulePath);
|
|
878
|
+
const {
|
|
879
|
+
specifiersByTarget,
|
|
880
|
+
hasNamespaceImport
|
|
881
|
+
} = classifySpecifiers({
|
|
882
|
+
node: synthetic,
|
|
883
|
+
importContext,
|
|
884
|
+
workspaceRoot,
|
|
885
|
+
fs,
|
|
886
|
+
preferImportedPackageSubpath
|
|
887
|
+
});
|
|
888
|
+
if (hasNamespaceImport || specifiersByTarget.size === 0) {
|
|
889
|
+
return;
|
|
890
|
+
}
|
|
891
|
+
const entries = [...specifiersByTarget.entries()];
|
|
892
|
+
if (entries.length !== 1) {
|
|
893
|
+
return;
|
|
894
|
+
}
|
|
895
|
+
const [targetKey, specsWithTarget] = entries[0];
|
|
896
|
+
if (specsWithTarget.length !== 1) {
|
|
897
|
+
return;
|
|
898
|
+
}
|
|
899
|
+
const st = specsWithTarget[0];
|
|
900
|
+
const newImportPath = fullNewImportPathForTarget(targetKey, specsWithTarget, importContext.packageName);
|
|
901
|
+
const transformed = transformSpecifierForExport({
|
|
902
|
+
spec: st.spec,
|
|
903
|
+
originalName: st.originalName,
|
|
904
|
+
kind: st.kind
|
|
905
|
+
});
|
|
906
|
+
const newRhs = getRhsPropertyAfterTransform(transformed);
|
|
907
|
+
const sourceCode = context.getSourceCode();
|
|
908
|
+
const quote = sourceCode.getText(reqCall.arguments[0])[0];
|
|
909
|
+
context.report({
|
|
910
|
+
node: node,
|
|
911
|
+
messageId: 'barrelEntryImport',
|
|
912
|
+
data: {
|
|
913
|
+
path: importContext.importPath
|
|
914
|
+
},
|
|
915
|
+
fix(fixer) {
|
|
916
|
+
const fixes = [];
|
|
917
|
+
fixes.push(fixer.replaceText(node, `require(${quote}${newImportPath}${quote}).${newRhs}`));
|
|
918
|
+
if (st.kind === 'value') {
|
|
919
|
+
fixes.push(...appendAutomockFixesForPathMigration({
|
|
920
|
+
fixer,
|
|
921
|
+
sourceCode,
|
|
922
|
+
oldBarrelPath: modulePath,
|
|
923
|
+
newPaths: [newImportPath]
|
|
924
|
+
}));
|
|
925
|
+
}
|
|
926
|
+
return fixes;
|
|
927
|
+
}
|
|
928
|
+
});
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
/**
|
|
932
|
+
* `const { a, b } = require('barrel')`
|
|
933
|
+
*/
|
|
934
|
+
function handleRequireDestructuringDeclarator({
|
|
935
|
+
node,
|
|
936
|
+
context,
|
|
937
|
+
workspaceRoot,
|
|
938
|
+
fs,
|
|
939
|
+
applyToImportsFrom,
|
|
940
|
+
preferImportedPackageSubpath
|
|
941
|
+
}) {
|
|
942
|
+
if (node.id.type !== 'ObjectPattern' || !node.init || node.init.type !== 'CallExpression') {
|
|
943
|
+
return;
|
|
944
|
+
}
|
|
945
|
+
const initCall = node.init;
|
|
946
|
+
if (!isPlainRequireCall(initCall)) {
|
|
947
|
+
return;
|
|
948
|
+
}
|
|
949
|
+
const modulePath = initCall.arguments[0].value;
|
|
950
|
+
const importContext = resolveImportContextFromModulePath({
|
|
951
|
+
importPath: modulePath,
|
|
952
|
+
workspaceRoot,
|
|
953
|
+
fs,
|
|
954
|
+
applyToImportsFrom
|
|
955
|
+
});
|
|
956
|
+
if (!importContext) {
|
|
957
|
+
return;
|
|
958
|
+
}
|
|
959
|
+
const specifiers = [];
|
|
960
|
+
for (const prop of node.id.properties) {
|
|
961
|
+
if (prop.type !== 'Property' || prop.computed) {
|
|
962
|
+
continue;
|
|
963
|
+
}
|
|
964
|
+
if (prop.key.type !== 'Identifier' || prop.value.type !== 'Identifier') {
|
|
965
|
+
continue;
|
|
966
|
+
}
|
|
967
|
+
const importedName = prop.key.name;
|
|
968
|
+
const localName = prop.value.name;
|
|
969
|
+
specifiers.push({
|
|
970
|
+
type: 'ImportSpecifier',
|
|
971
|
+
imported: {
|
|
972
|
+
type: 'Identifier',
|
|
973
|
+
name: importedName
|
|
974
|
+
},
|
|
975
|
+
local: {
|
|
976
|
+
type: 'Identifier',
|
|
977
|
+
name: localName
|
|
978
|
+
}
|
|
979
|
+
});
|
|
980
|
+
}
|
|
981
|
+
if (specifiers.length === 0) {
|
|
982
|
+
return;
|
|
983
|
+
}
|
|
984
|
+
const synthetic = {
|
|
985
|
+
type: 'ImportDeclaration',
|
|
986
|
+
source: {
|
|
987
|
+
type: 'Literal',
|
|
988
|
+
value: modulePath,
|
|
989
|
+
raw: `'${modulePath}'`
|
|
990
|
+
},
|
|
991
|
+
specifiers,
|
|
992
|
+
importKind: 'value'
|
|
993
|
+
};
|
|
994
|
+
const {
|
|
995
|
+
specifiersByTarget,
|
|
996
|
+
unmappedSpecifiers,
|
|
997
|
+
hasNamespaceImport
|
|
998
|
+
} = classifySpecifiers({
|
|
999
|
+
node: synthetic,
|
|
1000
|
+
importContext,
|
|
1001
|
+
workspaceRoot,
|
|
1002
|
+
fs,
|
|
1003
|
+
preferImportedPackageSubpath
|
|
1004
|
+
});
|
|
1005
|
+
if (hasNamespaceImport || specifiersByTarget.size === 0 || unmappedSpecifiers.length > 0) {
|
|
1006
|
+
return;
|
|
1007
|
+
}
|
|
1008
|
+
const parentDecl = node.parent;
|
|
1009
|
+
if (parentDecl.type !== 'VariableDeclaration') {
|
|
1010
|
+
return;
|
|
1011
|
+
}
|
|
1012
|
+
if (specifiersByTarget.size > 1 && parentDecl.declarations.length !== 1) {
|
|
1013
|
+
return;
|
|
1014
|
+
}
|
|
1015
|
+
const sourceCode = context.getSourceCode();
|
|
1016
|
+
const quote = sourceCode.getText(initCall.arguments[0])[0];
|
|
1017
|
+
const pkg = importContext.packageName;
|
|
1018
|
+
const buildFixes = fixer => {
|
|
1019
|
+
const fixes = [];
|
|
1020
|
+
let hasValue = false;
|
|
1021
|
+
const automockPaths = [];
|
|
1022
|
+
if (specifiersByTarget.size === 1) {
|
|
1023
|
+
const [targetKey, specsWithTarget] = [...specifiersByTarget.entries()][0];
|
|
1024
|
+
const newImportPath = fullNewImportPathForTarget(targetKey, specsWithTarget, pkg);
|
|
1025
|
+
if (specsWithTarget.some(s => s.kind === 'value')) {
|
|
1026
|
+
hasValue = true;
|
|
1027
|
+
automockPaths.push(newImportPath);
|
|
1028
|
+
}
|
|
1029
|
+
fixes.push(fixer.replaceText(initCall.arguments[0], `${quote}${newImportPath}${quote}`));
|
|
1030
|
+
} else {
|
|
1031
|
+
const lines = [];
|
|
1032
|
+
for (const [targetKey, specsWithTarget] of specifiersByTarget) {
|
|
1033
|
+
const newImportPath = fullNewImportPathForTarget(targetKey, specsWithTarget, pkg);
|
|
1034
|
+
if (specsWithTarget.some(s => s.kind === 'value')) {
|
|
1035
|
+
hasValue = true;
|
|
1036
|
+
automockPaths.push(newImportPath);
|
|
1037
|
+
}
|
|
1038
|
+
for (const st of specsWithTarget) {
|
|
1039
|
+
const transformed = transformSpecifierForExport({
|
|
1040
|
+
spec: st.spec,
|
|
1041
|
+
originalName: st.originalName,
|
|
1042
|
+
kind: st.kind
|
|
1043
|
+
});
|
|
1044
|
+
const rhs = getRhsPropertyAfterTransform(transformed);
|
|
1045
|
+
const local = st.spec.local.name;
|
|
1046
|
+
lines.push(`${local} = require(${quote}${newImportPath}${quote}).${rhs}`);
|
|
1047
|
+
}
|
|
1048
|
+
}
|
|
1049
|
+
const declText = lines.map(l => `${parentDecl.kind} ${l};`).join('\n');
|
|
1050
|
+
fixes.push(fixer.replaceText(parentDecl, declText));
|
|
1051
|
+
}
|
|
1052
|
+
if (hasValue) {
|
|
1053
|
+
fixes.push(...appendAutomockFixesForPathMigration({
|
|
1054
|
+
fixer,
|
|
1055
|
+
sourceCode,
|
|
1056
|
+
oldBarrelPath: modulePath,
|
|
1057
|
+
newPaths: [...new Set(automockPaths)]
|
|
1058
|
+
}));
|
|
1059
|
+
}
|
|
1060
|
+
return fixes;
|
|
1061
|
+
};
|
|
1062
|
+
context.report({
|
|
1063
|
+
node: initCall,
|
|
1064
|
+
messageId: 'barrelEntryImport',
|
|
1065
|
+
data: {
|
|
1066
|
+
path: importContext.importPath
|
|
1067
|
+
},
|
|
1068
|
+
fix: buildFixes
|
|
1069
|
+
});
|
|
1070
|
+
}
|
|
689
1071
|
|
|
690
1072
|
/**
|
|
691
1073
|
* Handles an ImportDeclaration node to check for barrel file imports.
|
|
@@ -696,7 +1078,8 @@ function handleImportDeclaration({
|
|
|
696
1078
|
context,
|
|
697
1079
|
workspaceRoot,
|
|
698
1080
|
fs,
|
|
699
|
-
applyToImportsFrom
|
|
1081
|
+
applyToImportsFrom,
|
|
1082
|
+
preferImportedPackageSubpath
|
|
700
1083
|
}) {
|
|
701
1084
|
// Resolve import context (validates and extracts package/export info)
|
|
702
1085
|
// applyToImportsFrom is used here to filter which packages the rule applies to
|
|
@@ -724,7 +1107,8 @@ function handleImportDeclaration({
|
|
|
724
1107
|
node,
|
|
725
1108
|
importContext,
|
|
726
1109
|
workspaceRoot,
|
|
727
|
-
fs
|
|
1110
|
+
fs,
|
|
1111
|
+
preferImportedPackageSubpath
|
|
728
1112
|
});
|
|
729
1113
|
|
|
730
1114
|
// If namespace import, report without auto-fix if there are specific exports available
|
|
@@ -774,9 +1158,10 @@ export function createRule(fs) {
|
|
|
774
1158
|
return {
|
|
775
1159
|
meta: ruleMeta,
|
|
776
1160
|
create(context) {
|
|
777
|
-
var _options$applyToImpor;
|
|
1161
|
+
var _options$applyToImpor, _options$preferImport;
|
|
778
1162
|
const options = context.options[0] || {};
|
|
779
1163
|
const applyToImportsFrom = (_options$applyToImpor = options.applyToImportsFrom) !== null && _options$applyToImpor !== void 0 ? _options$applyToImpor : DEFAULT_TARGET_FOLDERS;
|
|
1164
|
+
const preferImportedPackageSubpath = (_options$preferImport = options.preferImportedPackageSubpath) !== null && _options$preferImport !== void 0 ? _options$preferImport : false;
|
|
780
1165
|
const workspaceRoot = findWorkspaceRoot({
|
|
781
1166
|
startPath: dirname(context.filename),
|
|
782
1167
|
fs,
|
|
@@ -790,7 +1175,28 @@ export function createRule(fs) {
|
|
|
790
1175
|
context,
|
|
791
1176
|
workspaceRoot,
|
|
792
1177
|
fs,
|
|
793
|
-
applyToImportsFrom
|
|
1178
|
+
applyToImportsFrom,
|
|
1179
|
+
preferImportedPackageSubpath
|
|
1180
|
+
});
|
|
1181
|
+
},
|
|
1182
|
+
MemberExpression(rawNode) {
|
|
1183
|
+
handleRequireMemberExpression({
|
|
1184
|
+
node: rawNode,
|
|
1185
|
+
context,
|
|
1186
|
+
workspaceRoot,
|
|
1187
|
+
fs,
|
|
1188
|
+
applyToImportsFrom,
|
|
1189
|
+
preferImportedPackageSubpath
|
|
1190
|
+
});
|
|
1191
|
+
},
|
|
1192
|
+
VariableDeclarator(rawNode) {
|
|
1193
|
+
handleRequireDestructuringDeclarator({
|
|
1194
|
+
node: rawNode,
|
|
1195
|
+
context,
|
|
1196
|
+
workspaceRoot,
|
|
1197
|
+
fs,
|
|
1198
|
+
applyToImportsFrom,
|
|
1199
|
+
preferImportedPackageSubpath
|
|
794
1200
|
});
|
|
795
1201
|
}
|
|
796
1202
|
};
|