@atlaskit/eslint-plugin-platform 2.9.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 (29) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/dist/cjs/index.js +2 -0
  3. package/dist/cjs/rules/ensure-critical-dependency-resolutions/index.js +0 -1
  4. package/dist/cjs/rules/import/no-barrel-entry-imports/index.js +68 -16
  5. package/dist/cjs/rules/import/no-barrel-entry-jest-mock/index.js +42 -8
  6. package/dist/cjs/rules/import/shared/package-resolution.js +153 -8
  7. package/dist/cjs/rules/no-restricted-fedramp-imports/index.js +65 -0
  8. package/dist/cjs/rules/visit-example-type-import-required/index.js +1 -1
  9. package/dist/es2019/index.js +2 -0
  10. package/dist/es2019/rules/ensure-critical-dependency-resolutions/index.js +0 -1
  11. package/dist/es2019/rules/import/no-barrel-entry-imports/index.js +66 -17
  12. package/dist/es2019/rules/import/no-barrel-entry-jest-mock/index.js +43 -9
  13. package/dist/es2019/rules/import/shared/package-resolution.js +119 -4
  14. package/dist/es2019/rules/no-restricted-fedramp-imports/index.js +47 -0
  15. package/dist/es2019/rules/visit-example-type-import-required/index.js +1 -1
  16. package/dist/esm/index.js +2 -0
  17. package/dist/esm/rules/ensure-critical-dependency-resolutions/index.js +0 -1
  18. package/dist/esm/rules/import/no-barrel-entry-imports/index.js +69 -17
  19. package/dist/esm/rules/import/no-barrel-entry-jest-mock/index.js +43 -9
  20. package/dist/esm/rules/import/shared/package-resolution.js +151 -8
  21. package/dist/esm/rules/no-restricted-fedramp-imports/index.js +59 -0
  22. package/dist/esm/rules/visit-example-type-import-required/index.js +1 -1
  23. package/dist/types/index.d.ts +2 -0
  24. package/dist/types/rules/import/shared/package-resolution.d.ts +25 -0
  25. package/dist/types/rules/no-restricted-fedramp-imports/index.d.ts +3 -0
  26. package/dist/types-ts4.5/index.d.ts +2 -0
  27. package/dist/types-ts4.5/rules/import/shared/package-resolution.d.ts +25 -0
  28. package/dist/types-ts4.5/rules/no-restricted-fedramp-imports/index.d.ts +3 -0
  29. package/package.json +1 -1
@@ -10,7 +10,7 @@ import { dirname } from 'path';
10
10
  import { parseBarrelExports } from '../shared/barrel-parsing';
11
11
  import { DEFAULT_TARGET_FOLDERS, findWorkspaceRoot, isRelativeImport } from '../shared/file-system';
12
12
  import { findPackageInRegistry, isPackageInApplyToImportsFrom } from '../shared/package-registry';
13
- import { findExportForSourceFile, parsePackageExports } from '../shared/package-resolution';
13
+ import { findCrossPackageBridgeExportPath, findExportForSourceFile, parsePackageExports } from '../shared/package-resolution';
14
14
  import { realFileSystem } from '../shared/types';
15
15
 
16
16
  /**
@@ -41,6 +41,10 @@ var ruleMeta = {
41
41
  type: 'string'
42
42
  },
43
43
  description: 'The folder paths (relative to workspace root) containing packages whose imports will be checked and autofixed.'
44
+ },
45
+ preferImportedPackageSubpath: {
46
+ type: 'boolean',
47
+ description: 'Prefer subpaths on the imported barrel package when they bridge to the dependency (e.g. @scope/pkg/subpath instead of @scope/dependency).'
44
48
  }
45
49
  },
46
50
  additionalProperties: false
@@ -240,10 +244,12 @@ function classifySpecifiers(_ref4) {
240
244
  var node = _ref4.node,
241
245
  importContext = _ref4.importContext,
242
246
  workspaceRoot = _ref4.workspaceRoot,
243
- fs = _ref4.fs;
247
+ fs = _ref4.fs,
248
+ preferImportedPackageSubpath = _ref4.preferImportedPackageSubpath;
244
249
  var currentExportPath = importContext.currentExportPath,
245
250
  exportsMap = importContext.exportsMap,
246
- exportMap = importContext.exportMap;
251
+ exportMap = importContext.exportMap,
252
+ importedPackageName = importContext.packageName;
247
253
  var specifiers = node.specifiers;
248
254
  var specifiersByTarget = new Map();
249
255
  var unmappedSpecifiers = [];
@@ -264,6 +270,7 @@ function classifySpecifiers(_ref4) {
264
270
  var kind = 'value';
265
271
  if (spec.type === 'ImportDefaultSpecifier') {
266
272
  nameInSource = 'default';
273
+ kind = node.importKind === 'type' ? 'type' : 'value';
267
274
  } else if (spec.type === 'ImportSpecifier') {
268
275
  nameInSource = getImportedName(spec);
269
276
  var parentImportKind = node.importKind;
@@ -279,6 +286,36 @@ function classifySpecifiers(_ref4) {
279
286
  // Check if this is a cross-package re-export
280
287
  var sourcePackageName = (_exportInfo$crossPack = exportInfo.crossPackageSource) === null || _exportInfo$crossPack === void 0 ? void 0 : _exportInfo$crossPack.packageName;
281
288
  if (sourcePackageName) {
289
+ var targetKey = void 0;
290
+ var resolvedOriginalName = exportInfo.originalName;
291
+ if (preferImportedPackageSubpath) {
292
+ var bridge = findCrossPackageBridgeExportPath({
293
+ exportsMap: exportsMap,
294
+ crossPackageName: sourcePackageName,
295
+ exportedName: nameInSource,
296
+ fs: fs
297
+ });
298
+ if (bridge) {
299
+ targetKey = importedPackageName + bridge.exportPath.slice(1);
300
+ if (bridge.entryPointExportName !== undefined) {
301
+ resolvedOriginalName = bridge.entryPointExportName === nameInSource ? undefined : bridge.entryPointExportName;
302
+ }
303
+ if (!specifiersByTarget.has(targetKey)) {
304
+ specifiersByTarget.set(targetKey, []);
305
+ }
306
+ specifiersByTarget.get(targetKey).push({
307
+ spec: _objectSpread(_objectSpread({}, spec), {}, {
308
+ importKind: effectiveKind
309
+ }),
310
+ originalName: resolvedOriginalName,
311
+ targetExportPath: targetKey,
312
+ kind: effectiveKind,
313
+ sourcePackageName: sourcePackageName
314
+ });
315
+ continue;
316
+ }
317
+ }
318
+
282
319
  // For cross-package re-exports, find the most specific subpath in the source package
283
320
  // Note: Package resolution is not constrained by applyToImportsFrom - any package can be resolved
284
321
  var sourcePackageExportsMap = sourcePackageExportsMaps.get(sourcePackageName);
@@ -299,7 +336,6 @@ function classifySpecifiers(_ref4) {
299
336
 
300
337
  // Find the best export path in the source package
301
338
  var _targetExportPath = null;
302
- var resolvedOriginalName = exportInfo.originalName;
303
339
  if (sourcePackageExportsMap) {
304
340
  var _exportInfo$originalN, _matchResult$exportPa;
305
341
  var _sourceExportName = (_exportInfo$originalN = exportInfo.originalName) !== null && _exportInfo$originalN !== void 0 ? _exportInfo$originalN : nameInSource;
@@ -316,7 +352,7 @@ function classifySpecifiers(_ref4) {
316
352
  }
317
353
 
318
354
  // Build the full import path: @package/subpath or just @package if no subpath found
319
- var targetKey = _targetExportPath ? sourcePackageName + _targetExportPath.slice(1) // Remove leading '.' from subpath
355
+ targetKey = _targetExportPath ? sourcePackageName + _targetExportPath.slice(1) // Remove leading '.' from subpath
320
356
  : sourcePackageName;
321
357
  if (!specifiersByTarget.has(targetKey)) {
322
358
  specifiersByTarget.set(targetKey, []);
@@ -691,7 +727,10 @@ function createBarrelImportFix(_ref0) {
691
727
  }
692
728
  } else {
693
729
  // Create new import
694
- var _isTypeImport = node.importKind === 'type';
730
+ var allSpecsAreType = specsWithTarget.every(function (s) {
731
+ return s.kind === 'type';
732
+ });
733
+ var _isTypeImport = node.importKind === 'type' || allSpecsAreType;
695
734
  var importStatement = buildImportStatement({
696
735
  specs: specs,
697
736
  path: newImportPath,
@@ -717,7 +756,10 @@ function createBarrelImportFix(_ref0) {
717
756
  var unmappedSpecs = unmappedSpecifiers.map(function (u) {
718
757
  return u.spec;
719
758
  });
720
- var isTypeImport = node.importKind === 'type';
759
+ var allUnmappedAreType = unmappedSpecifiers.every(function (u) {
760
+ return u.kind === 'type';
761
+ });
762
+ var isTypeImport = node.importKind === 'type' || allUnmappedAreType;
721
763
  var remainingImport = buildImportStatement({
722
764
  specs: unmappedSpecs,
723
765
  path: importPath,
@@ -893,7 +935,8 @@ function handleRequireMemberExpression(_ref13) {
893
935
  context = _ref13.context,
894
936
  workspaceRoot = _ref13.workspaceRoot,
895
937
  fs = _ref13.fs,
896
- applyToImportsFrom = _ref13.applyToImportsFrom;
938
+ applyToImportsFrom = _ref13.applyToImportsFrom,
939
+ preferImportedPackageSubpath = _ref13.preferImportedPackageSubpath;
897
940
  if (node.computed || node.property.type !== 'Identifier') {
898
941
  return;
899
942
  }
@@ -917,7 +960,8 @@ function handleRequireMemberExpression(_ref13) {
917
960
  node: synthetic,
918
961
  importContext: importContext,
919
962
  workspaceRoot: workspaceRoot,
920
- fs: fs
963
+ fs: fs,
964
+ preferImportedPackageSubpath: preferImportedPackageSubpath
921
965
  }),
922
966
  specifiersByTarget = _classifySpecifiers.specifiersByTarget,
923
967
  hasNamespaceImport = _classifySpecifiers.hasNamespaceImport;
@@ -975,7 +1019,8 @@ function handleRequireDestructuringDeclarator(_ref16) {
975
1019
  context = _ref16.context,
976
1020
  workspaceRoot = _ref16.workspaceRoot,
977
1021
  fs = _ref16.fs,
978
- applyToImportsFrom = _ref16.applyToImportsFrom;
1022
+ applyToImportsFrom = _ref16.applyToImportsFrom,
1023
+ preferImportedPackageSubpath = _ref16.preferImportedPackageSubpath;
979
1024
  if (node.id.type !== 'ObjectPattern' || !node.init || node.init.type !== 'CallExpression') {
980
1025
  return;
981
1026
  }
@@ -1041,7 +1086,8 @@ function handleRequireDestructuringDeclarator(_ref16) {
1041
1086
  node: synthetic,
1042
1087
  importContext: importContext,
1043
1088
  workspaceRoot: workspaceRoot,
1044
- fs: fs
1089
+ fs: fs,
1090
+ preferImportedPackageSubpath: preferImportedPackageSubpath
1045
1091
  }),
1046
1092
  specifiersByTarget = _classifySpecifiers2.specifiersByTarget,
1047
1093
  unmappedSpecifiers = _classifySpecifiers2.unmappedSpecifiers,
@@ -1151,7 +1197,8 @@ function handleImportDeclaration(_ref19) {
1151
1197
  context = _ref19.context,
1152
1198
  workspaceRoot = _ref19.workspaceRoot,
1153
1199
  fs = _ref19.fs,
1154
- applyToImportsFrom = _ref19.applyToImportsFrom;
1200
+ applyToImportsFrom = _ref19.applyToImportsFrom,
1201
+ preferImportedPackageSubpath = _ref19.preferImportedPackageSubpath;
1155
1202
  // Resolve import context (validates and extracts package/export info)
1156
1203
  // applyToImportsFrom is used here to filter which packages the rule applies to
1157
1204
  var importContext = resolveImportContext({
@@ -1174,7 +1221,8 @@ function handleImportDeclaration(_ref19) {
1174
1221
  node: node,
1175
1222
  importContext: importContext,
1176
1223
  workspaceRoot: workspaceRoot,
1177
- fs: fs
1224
+ fs: fs,
1225
+ preferImportedPackageSubpath: preferImportedPackageSubpath
1178
1226
  }),
1179
1227
  specifiersByTarget = _classifySpecifiers3.specifiersByTarget,
1180
1228
  unmappedSpecifiers = _classifySpecifiers3.unmappedSpecifiers,
@@ -1227,9 +1275,10 @@ export function createRule(fs) {
1227
1275
  return {
1228
1276
  meta: ruleMeta,
1229
1277
  create: function create(context) {
1230
- var _options$applyToImpor;
1278
+ var _options$applyToImpor, _options$preferImport;
1231
1279
  var options = context.options[0] || {};
1232
1280
  var applyToImportsFrom = (_options$applyToImpor = options.applyToImportsFrom) !== null && _options$applyToImpor !== void 0 ? _options$applyToImpor : DEFAULT_TARGET_FOLDERS;
1281
+ var preferImportedPackageSubpath = (_options$preferImport = options.preferImportedPackageSubpath) !== null && _options$preferImport !== void 0 ? _options$preferImport : false;
1233
1282
  var workspaceRoot = findWorkspaceRoot({
1234
1283
  startPath: dirname(context.filename),
1235
1284
  fs: fs,
@@ -1243,7 +1292,8 @@ export function createRule(fs) {
1243
1292
  context: context,
1244
1293
  workspaceRoot: workspaceRoot,
1245
1294
  fs: fs,
1246
- applyToImportsFrom: applyToImportsFrom
1295
+ applyToImportsFrom: applyToImportsFrom,
1296
+ preferImportedPackageSubpath: preferImportedPackageSubpath
1247
1297
  });
1248
1298
  },
1249
1299
  MemberExpression: function MemberExpression(rawNode) {
@@ -1252,7 +1302,8 @@ export function createRule(fs) {
1252
1302
  context: context,
1253
1303
  workspaceRoot: workspaceRoot,
1254
1304
  fs: fs,
1255
- applyToImportsFrom: applyToImportsFrom
1305
+ applyToImportsFrom: applyToImportsFrom,
1306
+ preferImportedPackageSubpath: preferImportedPackageSubpath
1256
1307
  });
1257
1308
  },
1258
1309
  VariableDeclarator: function VariableDeclarator(rawNode) {
@@ -1261,7 +1312,8 @@ export function createRule(fs) {
1261
1312
  context: context,
1262
1313
  workspaceRoot: workspaceRoot,
1263
1314
  fs: fs,
1264
- applyToImportsFrom: applyToImportsFrom
1315
+ applyToImportsFrom: applyToImportsFrom,
1316
+ preferImportedPackageSubpath: preferImportedPackageSubpath
1265
1317
  });
1266
1318
  }
1267
1319
  };
@@ -10,7 +10,7 @@ import { hasReExportsFromOtherFiles, parseBarrelExports } from '../shared/barrel
10
10
  import { DEFAULT_TARGET_FOLDERS, findWorkspaceRoot, isRelativeImport, readFileContent, resolveImportPath } from '../shared/file-system';
11
11
  import { extractImportPath, findJestRequireActualCalls, findJestRequireMockCalls, isJestMockCall, isJestRequireActual, resolveNewPathForRequireMock } from '../shared/jest-utils';
12
12
  import { findPackageInRegistry, isPackageInApplyToImportsFrom } from '../shared/package-registry';
13
- import { findExportForSourceFile, parsePackageExports } from '../shared/package-resolution';
13
+ import { findCrossPackageBridgeExportPath, findExportForSourceFile, parsePackageExports } from '../shared/package-resolution';
14
14
  import { realFileSystem } from '../shared/types';
15
15
 
16
16
  /**
@@ -662,7 +662,9 @@ function traceSymbolsToExports(_ref10) {
662
662
  exportMap = _ref10.exportMap,
663
663
  exportsMap = _ref10.exportsMap,
664
664
  currentExportPath = _ref10.currentExportPath,
665
- fs = _ref10.fs;
665
+ fs = _ref10.fs,
666
+ barrelPackageName = _ref10.barrelPackageName,
667
+ preferImportedPackageSubpath = _ref10.preferImportedPackageSubpath;
666
668
  var groupedByExport = new Map();
667
669
  var crossPackageGroups = new Map();
668
670
  var unmappedSymbols = [];
@@ -680,16 +682,38 @@ function traceSymbolsToExports(_ref10) {
680
682
 
681
683
  // Check for cross-package source first
682
684
  if (exportInfo.crossPackageSource) {
683
- var key = "".concat(exportInfo.crossPackageSource.packageName).concat(exportInfo.crossPackageSource.exportPath === '.' ? '' : exportInfo.crossPackageSource.exportPath.slice(1));
685
+ var key = void 0;
686
+ var tracedOriginalName = exportInfo.originalName;
687
+ var barrelBridgeExportPath = void 0;
688
+ if (preferImportedPackageSubpath) {
689
+ var bridge = findCrossPackageBridgeExportPath({
690
+ exportsMap: exportsMap,
691
+ crossPackageName: exportInfo.crossPackageSource.packageName,
692
+ exportedName: symbolName,
693
+ fs: fs
694
+ });
695
+ if (bridge) {
696
+ key = "".concat(barrelPackageName).concat(bridge.exportPath.slice(1));
697
+ barrelBridgeExportPath = bridge.exportPath;
698
+ if (bridge.entryPointExportName !== undefined) {
699
+ tracedOriginalName = bridge.entryPointExportName === symbolName ? undefined : bridge.entryPointExportName;
700
+ }
701
+ } else {
702
+ key = "".concat(exportInfo.crossPackageSource.packageName).concat(exportInfo.crossPackageSource.exportPath === '.' ? '' : exportInfo.crossPackageSource.exportPath.slice(1));
703
+ }
704
+ } else {
705
+ key = "".concat(exportInfo.crossPackageSource.packageName).concat(exportInfo.crossPackageSource.exportPath === '.' ? '' : exportInfo.crossPackageSource.exportPath.slice(1));
706
+ }
684
707
  if (!crossPackageGroups.has(key)) {
685
708
  crossPackageGroups.set(key, []);
686
709
  }
687
710
  crossPackageGroups.get(key).push({
688
711
  symbolName: symbolName,
689
- originalName: exportInfo.originalName,
712
+ originalName: tracedOriginalName,
690
713
  sourceFilePath: exportInfo.path,
691
714
  isTypeOnly: exportInfo.isTypeOnly,
692
- crossPackageSource: exportInfo.crossPackageSource
715
+ crossPackageSource: exportInfo.crossPackageSource,
716
+ barrelBridgeExportPath: barrelBridgeExportPath
693
717
  });
694
718
  continue;
695
719
  }
@@ -1060,6 +1084,10 @@ var ruleMeta = {
1060
1084
  type: 'string'
1061
1085
  },
1062
1086
  description: 'The folder paths (relative to workspace root) containing packages whose imports will be checked and autofixed.'
1087
+ },
1088
+ preferImportedPackageSubpath: {
1089
+ type: 'boolean',
1090
+ description: 'Prefer subpaths on the mocked barrel package when they bridge to the dependency.'
1063
1091
  }
1064
1092
  },
1065
1093
  additionalProperties: false
@@ -1078,9 +1106,10 @@ export function createRule(fs) {
1078
1106
  return {
1079
1107
  meta: ruleMeta,
1080
1108
  create: function create(context) {
1081
- var _options$applyToImpor;
1109
+ var _options$applyToImpor, _options$preferImport;
1082
1110
  var options = context.options[0] || {};
1083
1111
  var applyToImportsFrom = (_options$applyToImpor = options.applyToImportsFrom) !== null && _options$applyToImpor !== void 0 ? _options$applyToImpor : DEFAULT_TARGET_FOLDERS;
1112
+ var preferImportedPackageSubpath = (_options$preferImport = options.preferImportedPackageSubpath) !== null && _options$preferImport !== void 0 ? _options$preferImport : false;
1084
1113
  var workspaceRoot = findWorkspaceRoot({
1085
1114
  startPath: dirname(context.filename),
1086
1115
  fs: fs,
@@ -1175,7 +1204,9 @@ export function createRule(fs) {
1175
1204
  exportMap: raContext.exportMap,
1176
1205
  exportsMap: raContext.exportsMap,
1177
1206
  currentExportPath: raContext.currentExportPath,
1178
- fs: fs
1207
+ fs: fs,
1208
+ barrelPackageName: raContext.packageName,
1209
+ preferImportedPackageSubpath: preferImportedPackageSubpath
1179
1210
  }),
1180
1211
  _groupedByExport = _traceSymbolsToExport.groupedByExport,
1181
1212
  _crossPackageGroups = _traceSymbolsToExport.crossPackageGroups;
@@ -1272,7 +1303,9 @@ export function createRule(fs) {
1272
1303
  exportMap: mockContext.exportMap,
1273
1304
  exportsMap: mockContext.exportsMap,
1274
1305
  currentExportPath: mockContext.currentExportPath,
1275
- fs: fs
1306
+ fs: fs,
1307
+ barrelPackageName: mockContext.packageName,
1308
+ preferImportedPackageSubpath: preferImportedPackageSubpath
1276
1309
  }),
1277
1310
  groupedByExport = _traceSymbolsToExport2.groupedByExport,
1278
1311
  crossPackageGroups = _traceSymbolsToExport2.crossPackageGroups,
@@ -1364,8 +1397,9 @@ export function createRule(fs) {
1364
1397
 
1365
1398
  // Get cross-package source info from the first symbol (all symbols in same group have same source)
1366
1399
  var crossPackageSource = _symbols[0].crossPackageSource;
1400
+ var bridgeExportPath = _symbols[0].barrelBridgeExportPath;
1367
1401
  crossPackageMockGroups.push({
1368
- exportPath: crossPackageSource.exportPath,
1402
+ exportPath: bridgeExportPath !== null && bridgeExportPath !== void 0 ? bridgeExportPath : crossPackageSource.exportPath,
1369
1403
  importPath: _importPath,
1370
1404
  propertyNames: _symbols.map(function (s) {
1371
1405
  return s.symbolName;
@@ -161,10 +161,52 @@ export function parsePackageExports(_ref2) {
161
161
  });
162
162
  return exportsMap;
163
163
  }
164
+ /**
165
+ * Check whether a subpath export key (e.g. `"./checkbox-select"`) is kebab-case.
166
+ *
167
+ * A key is considered kebab-case when the portion after the leading `"./"` prefix
168
+ * consists only of lowercase letters, digits, hyphens, dots, and forward-slash
169
+ * separators — i.e. no uppercase letters, underscores, or camelCase humps.
170
+ */
171
+ export function isKebabCaseExportKey(key) {
172
+ var body = key.replace(/^\.\//, '');
173
+ if (body.length === 0) {
174
+ return false;
175
+ }
176
+ return /^[a-z0-9][a-z0-9\-./]*$/.test(body);
177
+ }
178
+
179
+ /**
180
+ * Given a list of candidate {@link ExportMatchResult}s that all resolve to the same
181
+ * source file, pick the best one. When any candidate's export path is kebab-case
182
+ * and points to an entry-point file, prefer it over non-kebab-case alternatives.
183
+ * Falls back to the first candidate if no kebab-case entry-point candidate is found.
184
+ */
185
+ function pickBestMatch(candidates, exportsMap) {
186
+ if (candidates.length === 1) {
187
+ return candidates[0];
188
+ }
189
+
190
+ // Among candidates whose value is an entry-point file, prefer kebab-case keys.
191
+ var entryPointKebab = candidates.filter(function (c) {
192
+ var resolved = exportsMap.get(c.exportPath);
193
+ return resolved && isInEntryPointsFolder(resolved) && isKebabCaseExportKey(c.exportPath);
194
+ });
195
+ if (entryPointKebab.length > 0) {
196
+ return entryPointKebab[0];
197
+ }
198
+
199
+ // Fall back to the first candidate (preserves previous behaviour).
200
+ return candidates[0];
201
+ }
202
+
164
203
  /**
165
204
  * Find a matching export entry for a given source file path.
166
205
  * Returns the export path (e.g., "./controllers/analytics") or null if not found.
167
206
  *
207
+ * When multiple export paths resolve to the same source file **and** point to an
208
+ * entry-point file, kebab-case keys are preferred over other casing styles.
209
+ *
168
210
  * When `fs` is provided, also checks entry-point wrapper files. If an export resolves
169
211
  * to a file inside a recognized entry-points folder (entry-points, entrypoints, etc.),
170
212
  * the wrapper is parsed to see if it re-exports from `sourceFilePath`.
@@ -178,6 +220,8 @@ export function findExportForSourceFile(_ref3) {
178
220
  exportsMap = _ref3.exportsMap,
179
221
  fs = _ref3.fs,
180
222
  sourceExportName = _ref3.sourceExportName;
223
+ // --- Phase 1: direct matches (export value === sourceFilePath) ---
224
+ var directMatches = [];
181
225
  var _iterator3 = _createForOfIteratorHelper(exportsMap),
182
226
  _step3;
183
227
  try {
@@ -186,9 +230,9 @@ export function findExportForSourceFile(_ref3) {
186
230
  _exportPath = _step3$value[0],
187
231
  _resolvedPath = _step3$value[1];
188
232
  if (_resolvedPath === sourceFilePath) {
189
- return {
233
+ directMatches.push({
190
234
  exportPath: _exportPath
191
- };
235
+ });
192
236
  }
193
237
  }
194
238
  } catch (err) {
@@ -196,7 +240,13 @@ export function findExportForSourceFile(_ref3) {
196
240
  } finally {
197
241
  _iterator3.f();
198
242
  }
243
+ if (directMatches.length > 0) {
244
+ return pickBestMatch(directMatches, exportsMap);
245
+ }
246
+
247
+ // --- Phase 2: entry-point wrapper re-export matches ---
199
248
  if (fs) {
249
+ var entryPointMatches = [];
200
250
  var _iterator4 = _createForOfIteratorHelper(exportsMap),
201
251
  _step4;
202
252
  try {
@@ -219,10 +269,10 @@ export function findExportForSourceFile(_ref3) {
219
269
  if (sourceExportName !== undefined && reExport.nameMap.has(sourceExportName)) {
220
270
  entryPointExportName = reExport.nameMap.get(sourceExportName);
221
271
  }
222
- return {
272
+ entryPointMatches.push({
223
273
  exportPath: exportPath,
224
274
  entryPointExportName: entryPointExportName
225
- };
275
+ });
226
276
  }
227
277
  }
228
278
  } catch (err) {
@@ -237,6 +287,99 @@ export function findExportForSourceFile(_ref3) {
237
287
  } finally {
238
288
  _iterator4.f();
239
289
  }
290
+ if (entryPointMatches.length > 0) {
291
+ return pickBestMatch(entryPointMatches, exportsMap);
292
+ }
293
+ }
294
+ return null;
295
+ }
296
+
297
+ /**
298
+ * When a symbol reaches the consumer through a barrel package that re-exports from
299
+ * `crossPackageName`, find a `package.json` export subpath of that barrel whose entry
300
+ * file directly re-exports the symbol from `crossPackageName` (named exports only).
301
+ *
302
+ * This enables rewriting imports to `@scope/barrel/subpath` instead of
303
+ * `@scope/cross-package/...` when the barrel exposes such a subpath (e.g. `@atlaskit/select/react-select`).
304
+ */
305
+ export function findCrossPackageBridgeExportPath(_ref4) {
306
+ var exportsMap = _ref4.exportsMap,
307
+ crossPackageName = _ref4.crossPackageName,
308
+ exportedName = _ref4.exportedName,
309
+ fs = _ref4.fs;
310
+ var _iterator6 = _createForOfIteratorHelper(exportsMap),
311
+ _step6;
312
+ try {
313
+ for (_iterator6.s(); !(_step6 = _iterator6.n()).done;) {
314
+ var _step6$value = _slicedToArray(_step6.value, 2),
315
+ exportPath = _step6$value[0],
316
+ resolvedPath = _step6$value[1];
317
+ var content = readFileContent({
318
+ filePath: resolvedPath,
319
+ fs: fs
320
+ });
321
+ if (!content) {
322
+ continue;
323
+ }
324
+ try {
325
+ var sourceFile = ts.createSourceFile(resolvedPath, content, ts.ScriptTarget.Latest, true);
326
+ var _iterator7 = _createForOfIteratorHelper(sourceFile.statements),
327
+ _step7;
328
+ try {
329
+ for (_iterator7.s(); !(_step7 = _iterator7.n()).done;) {
330
+ var statement = _step7.value;
331
+ if (!ts.isExportDeclaration(statement) || statement.isTypeOnly) {
332
+ continue;
333
+ }
334
+ if (!statement.moduleSpecifier || !ts.isStringLiteral(statement.moduleSpecifier)) {
335
+ continue;
336
+ }
337
+ if (statement.moduleSpecifier.text !== crossPackageName) {
338
+ continue;
339
+ }
340
+ if (!statement.exportClause || ts.isNamespaceExport(statement.exportClause)) {
341
+ continue;
342
+ }
343
+ if (!ts.isNamedExports(statement.exportClause)) {
344
+ continue;
345
+ }
346
+ var _iterator8 = _createForOfIteratorHelper(statement.exportClause.elements),
347
+ _step8;
348
+ try {
349
+ for (_iterator8.s(); !(_step8 = _iterator8.n()).done;) {
350
+ var element = _step8.value;
351
+ if (element.isTypeOnly) {
352
+ continue;
353
+ }
354
+ var publicName = element.name.text;
355
+ if (publicName !== exportedName) {
356
+ continue;
357
+ }
358
+ var entryPointExportName = element.propertyName ? element.propertyName.text : undefined;
359
+ return {
360
+ exportPath: exportPath,
361
+ entryPointExportName: entryPointExportName
362
+ };
363
+ }
364
+ } catch (err) {
365
+ _iterator8.e(err);
366
+ } finally {
367
+ _iterator8.f();
368
+ }
369
+ }
370
+ } catch (err) {
371
+ _iterator7.e(err);
372
+ } finally {
373
+ _iterator7.f();
374
+ }
375
+ } catch (_unused4) {
376
+ // Ignore parse errors for individual export entry files
377
+ }
378
+ }
379
+ } catch (err) {
380
+ _iterator6.e(err);
381
+ } finally {
382
+ _iterator6.f();
240
383
  }
241
384
  return null;
242
385
  }
@@ -260,10 +403,10 @@ export function extractPackageNameFromImport(moduleSpecifier) {
260
403
  * Resolve a cross-package import to its package directory and export info.
261
404
  * Returns null if the package is not in the target folder or cannot be resolved.
262
405
  */
263
- export function resolveCrossPackageImport(_ref4) {
264
- var moduleSpecifier = _ref4.moduleSpecifier,
265
- workspaceRoot = _ref4.workspaceRoot,
266
- fs = _ref4.fs;
406
+ export function resolveCrossPackageImport(_ref5) {
407
+ var moduleSpecifier = _ref5.moduleSpecifier,
408
+ workspaceRoot = _ref5.workspaceRoot,
409
+ fs = _ref5.fs;
267
410
  // Only handle @atlassian/* scoped packages
268
411
  var parsed = extractPackageNameFromImport(moduleSpecifier);
269
412
  if (!parsed) {
@@ -0,0 +1,59 @@
1
+ function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t.return || t.return(); } finally { if (u) throw o; } } }; }
2
+ function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
3
+ function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
4
+ // eslint-disable-next-line import/no-extraneous-dependencies
5
+
6
+ var RESTRICTED_IMPORTS = {
7
+ '@atlassian/atl-context': ['isFedRamp', 'isIsolatedCloud'],
8
+ '@atlaskit/atlassian-context': ['isFedRamp', 'isIsolatedCloud'],
9
+ '@atlassian/teams-common': ['isFedramp']
10
+ };
11
+ var rule = {
12
+ meta: {
13
+ type: 'problem',
14
+ docs: {
15
+ description: 'Disallow importing deprecated FedRamp/IsolatedCloud context functions. Use isFeatureEnabled from AEM (Atlassian Environment Manager) instead.',
16
+ recommended: true
17
+ },
18
+ messages: {
19
+ noRestrictedFedrampImports: '{{name}} from {{source}} will be deprecated soon. Please use isFeatureEnabled from AEM (Atlassian Environment Manager) instead. See go/AEM for more details.'
20
+ },
21
+ schema: []
22
+ },
23
+ create: function create(context) {
24
+ return {
25
+ ImportDeclaration: function ImportDeclaration(node) {
26
+ var source = node.source.value;
27
+ if (typeof source !== 'string') {
28
+ return;
29
+ }
30
+ var restrictedNames = RESTRICTED_IMPORTS[source];
31
+ if (!restrictedNames) {
32
+ return;
33
+ }
34
+ var _iterator = _createForOfIteratorHelper(node.specifiers),
35
+ _step;
36
+ try {
37
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
38
+ var specifier = _step.value;
39
+ if (specifier.type === 'ImportSpecifier' && restrictedNames.includes(specifier.imported.name)) {
40
+ context.report({
41
+ node: specifier,
42
+ messageId: 'noRestrictedFedrampImports',
43
+ data: {
44
+ name: specifier.imported.name,
45
+ source: source
46
+ }
47
+ });
48
+ }
49
+ }
50
+ } catch (err) {
51
+ _iterator.e(err);
52
+ } finally {
53
+ _iterator.f();
54
+ }
55
+ }
56
+ };
57
+ }
58
+ };
59
+ export default rule;
@@ -17,7 +17,7 @@ var messages = {
17
17
  suggestFixPath: 'Update import path to match visitExample arguments'
18
18
  };
19
19
  function isTargetFile(filename) {
20
- return filename.endsWith('.spec.tsx');
20
+ return filename.endsWith('.spec.tsx') || filename.endsWith('.spec.ts');
21
21
  }
22
22
 
23
23
  /**
@@ -29,6 +29,7 @@ declare const rules: {
29
29
  'no-direct-document-usage': import("eslint").Rule.RuleModule;
30
30
  'no-set-immediate': import("eslint").Rule.RuleModule;
31
31
  'prefer-crypto-random-uuid': import("eslint").Rule.RuleModule;
32
+ 'no-restricted-fedramp-imports': import("eslint").Rule.RuleModule;
32
33
  'no-barrel-entry-imports': import("eslint").Rule.RuleModule;
33
34
  'no-barrel-entry-jest-mock': import("eslint").Rule.RuleModule;
34
35
  'no-jest-mock-barrel-files': import("eslint").Rule.RuleModule;
@@ -72,6 +73,7 @@ declare const plugin: {
72
73
  'no-direct-document-usage': import("eslint").Rule.RuleModule;
73
74
  'no-set-immediate': import("eslint").Rule.RuleModule;
74
75
  'prefer-crypto-random-uuid': import("eslint").Rule.RuleModule;
76
+ 'no-restricted-fedramp-imports': import("eslint").Rule.RuleModule;
75
77
  'no-barrel-entry-imports': import("eslint").Rule.RuleModule;
76
78
  'no-barrel-entry-jest-mock': import("eslint").Rule.RuleModule;
77
79
  'no-jest-mock-barrel-files': import("eslint").Rule.RuleModule;
@@ -16,10 +16,21 @@ export interface ExportMatchResult {
16
16
  */
17
17
  entryPointExportName?: string;
18
18
  }
19
+ /**
20
+ * Check whether a subpath export key (e.g. `"./checkbox-select"`) is kebab-case.
21
+ *
22
+ * A key is considered kebab-case when the portion after the leading `"./"` prefix
23
+ * consists only of lowercase letters, digits, hyphens, dots, and forward-slash
24
+ * separators — i.e. no uppercase letters, underscores, or camelCase humps.
25
+ */
26
+ export declare function isKebabCaseExportKey(key: string): boolean;
19
27
  /**
20
28
  * Find a matching export entry for a given source file path.
21
29
  * Returns the export path (e.g., "./controllers/analytics") or null if not found.
22
30
  *
31
+ * When multiple export paths resolve to the same source file **and** point to an
32
+ * entry-point file, kebab-case keys are preferred over other casing styles.
33
+ *
23
34
  * When `fs` is provided, also checks entry-point wrapper files. If an export resolves
24
35
  * to a file inside a recognized entry-points folder (entry-points, entrypoints, etc.),
25
36
  * the wrapper is parsed to see if it re-exports from `sourceFilePath`.
@@ -34,6 +45,20 @@ export declare function findExportForSourceFile({ sourceFilePath, exportsMap, fs
34
45
  fs?: FileSystem;
35
46
  sourceExportName?: string;
36
47
  }): ExportMatchResult | null;
48
+ /**
49
+ * When a symbol reaches the consumer through a barrel package that re-exports from
50
+ * `crossPackageName`, find a `package.json` export subpath of that barrel whose entry
51
+ * file directly re-exports the symbol from `crossPackageName` (named exports only).
52
+ *
53
+ * This enables rewriting imports to `@scope/barrel/subpath` instead of
54
+ * `@scope/cross-package/...` when the barrel exposes such a subpath (e.g. `@atlaskit/select/react-select`).
55
+ */
56
+ export declare function findCrossPackageBridgeExportPath({ exportsMap, crossPackageName, exportedName, fs, }: {
57
+ exportsMap: Map<string, string>;
58
+ crossPackageName: string;
59
+ exportedName: string;
60
+ fs: FileSystem;
61
+ }): ExportMatchResult | null;
37
62
  /**
38
63
  * Extract the package name and subpath from an import specifier.
39
64
  * Returns null if the import is not a scoped package import.