@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.
Files changed (47) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/dist/cjs/index.js +8 -1
  3. package/dist/cjs/rules/ensure-critical-dependency-resolutions/index.js +0 -1
  4. package/dist/cjs/rules/ensure-use-sync-external-store-server-snapshot/index.js +41 -0
  5. package/dist/cjs/rules/import/no-barrel-entry-imports/index.js +534 -74
  6. package/dist/cjs/rules/import/no-barrel-entry-jest-mock/index.js +428 -119
  7. package/dist/cjs/rules/import/no-jest-mock-barrel-files/index.js +3 -2
  8. package/dist/cjs/rules/import/no-relative-barrel-file-imports/index.js +7 -3
  9. package/dist/cjs/rules/import/shared/jest-utils.js +62 -9
  10. package/dist/cjs/rules/import/shared/package-resolution.js +300 -22
  11. package/dist/cjs/rules/no-restricted-fedramp-imports/index.js +65 -0
  12. package/dist/cjs/rules/visit-example-type-import-required/index.js +409 -0
  13. package/dist/es2019/index.js +8 -1
  14. package/dist/es2019/rules/ensure-critical-dependency-resolutions/index.js +0 -1
  15. package/dist/es2019/rules/ensure-use-sync-external-store-server-snapshot/index.js +43 -0
  16. package/dist/es2019/rules/import/no-barrel-entry-imports/index.js +431 -25
  17. package/dist/es2019/rules/import/no-barrel-entry-jest-mock/index.js +287 -25
  18. package/dist/es2019/rules/import/no-jest-mock-barrel-files/index.js +3 -2
  19. package/dist/es2019/rules/import/no-relative-barrel-file-imports/index.js +7 -3
  20. package/dist/es2019/rules/import/shared/jest-utils.js +44 -0
  21. package/dist/es2019/rules/import/shared/package-resolution.js +211 -4
  22. package/dist/es2019/rules/no-restricted-fedramp-imports/index.js +47 -0
  23. package/dist/es2019/rules/visit-example-type-import-required/index.js +375 -0
  24. package/dist/esm/index.js +8 -1
  25. package/dist/esm/rules/ensure-critical-dependency-resolutions/index.js +0 -1
  26. package/dist/esm/rules/ensure-use-sync-external-store-server-snapshot/index.js +35 -0
  27. package/dist/esm/rules/import/no-barrel-entry-imports/index.js +535 -75
  28. package/dist/esm/rules/import/no-barrel-entry-jest-mock/index.js +430 -121
  29. package/dist/esm/rules/import/no-jest-mock-barrel-files/index.js +3 -2
  30. package/dist/esm/rules/import/no-relative-barrel-file-imports/index.js +7 -3
  31. package/dist/esm/rules/import/shared/jest-utils.js +61 -9
  32. package/dist/esm/rules/import/shared/package-resolution.js +298 -24
  33. package/dist/esm/rules/no-restricted-fedramp-imports/index.js +59 -0
  34. package/dist/esm/rules/visit-example-type-import-required/index.js +402 -0
  35. package/dist/types/index.d.ts +14 -0
  36. package/dist/types/rules/ensure-use-sync-external-store-server-snapshot/index.d.ts +3 -0
  37. package/dist/types/rules/import/shared/jest-utils.d.ts +8 -0
  38. package/dist/types/rules/import/shared/package-resolution.d.ts +47 -2
  39. package/dist/types/rules/no-restricted-fedramp-imports/index.d.ts +3 -0
  40. package/dist/types/rules/visit-example-type-import-required/index.d.ts +4 -0
  41. package/dist/types-ts4.5/index.d.ts +14 -0
  42. package/dist/types-ts4.5/rules/ensure-use-sync-external-store-server-snapshot/index.d.ts +3 -0
  43. package/dist/types-ts4.5/rules/import/shared/jest-utils.d.ts +8 -0
  44. package/dist/types-ts4.5/rules/import/shared/package-resolution.d.ts +47 -2
  45. package/dist/types-ts4.5/rules/no-restricted-fedramp-imports/index.d.ts +3 -0
  46. package/dist/types-ts4.5/rules/visit-example-type-import-required/index.d.ts +4 -0
  47. 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 resolveImportContext({
117
- node,
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
- targetExportPath = findExportForSourceFile({
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
- const targetKey = targetExportPath ? sourcePackageName + targetExportPath.slice(1) // Remove leading '.' from subpath
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: exportInfo.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 targetExportPath = findExportForSourceFile({
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: exportInfo.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 isTypeImport = node.importKind === 'type';
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 isTypeImport = node.importKind === 'type';
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
  };