@atlaskit/eslint-plugin-platform 2.9.0 → 2.9.2

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 (39) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/dist/cjs/index.js +8 -2
  3. package/dist/cjs/rules/compiled/no-css-prop-in-object-spread/index.js +162 -0
  4. package/dist/cjs/rules/ensure-critical-dependency-resolutions/index.js +0 -1
  5. package/dist/cjs/rules/import/no-barrel-entry-imports/index.js +68 -16
  6. package/dist/cjs/rules/import/no-barrel-entry-jest-mock/index.js +42 -8
  7. package/dist/cjs/rules/import/shared/package-resolution.js +153 -8
  8. package/dist/cjs/rules/no-restricted-fedramp-imports/index.js +65 -0
  9. package/dist/cjs/rules/no-xcss-in-cx/index.js +221 -0
  10. package/dist/cjs/rules/visit-example-type-import-required/index.js +24 -14
  11. package/dist/es2019/index.js +8 -2
  12. package/dist/es2019/rules/compiled/no-css-prop-in-object-spread/index.js +136 -0
  13. package/dist/es2019/rules/ensure-critical-dependency-resolutions/index.js +0 -1
  14. package/dist/es2019/rules/import/no-barrel-entry-imports/index.js +66 -17
  15. package/dist/es2019/rules/import/no-barrel-entry-jest-mock/index.js +43 -9
  16. package/dist/es2019/rules/import/shared/package-resolution.js +119 -4
  17. package/dist/es2019/rules/no-restricted-fedramp-imports/index.js +47 -0
  18. package/dist/es2019/rules/no-xcss-in-cx/index.js +187 -0
  19. package/dist/es2019/rules/visit-example-type-import-required/index.js +24 -15
  20. package/dist/esm/index.js +8 -2
  21. package/dist/esm/rules/compiled/no-css-prop-in-object-spread/index.js +156 -0
  22. package/dist/esm/rules/ensure-critical-dependency-resolutions/index.js +0 -1
  23. package/dist/esm/rules/import/no-barrel-entry-imports/index.js +69 -17
  24. package/dist/esm/rules/import/no-barrel-entry-jest-mock/index.js +43 -9
  25. package/dist/esm/rules/import/shared/package-resolution.js +151 -8
  26. package/dist/esm/rules/no-restricted-fedramp-imports/index.js +59 -0
  27. package/dist/esm/rules/no-xcss-in-cx/index.js +216 -0
  28. package/dist/esm/rules/visit-example-type-import-required/index.js +24 -14
  29. package/dist/types/index.d.ts +278 -241
  30. package/dist/types/rules/compiled/no-css-prop-in-object-spread/index.d.ts +3 -0
  31. package/dist/types/rules/import/shared/package-resolution.d.ts +25 -0
  32. package/dist/types/rules/no-restricted-fedramp-imports/index.d.ts +3 -0
  33. package/dist/types/rules/no-xcss-in-cx/index.d.ts +31 -0
  34. package/dist/types-ts4.5/index.d.ts +222 -209
  35. package/dist/types-ts4.5/rules/compiled/no-css-prop-in-object-spread/index.d.ts +3 -0
  36. package/dist/types-ts4.5/rules/import/shared/package-resolution.d.ts +25 -0
  37. package/dist/types-ts4.5/rules/no-restricted-fedramp-imports/index.d.ts +3 -0
  38. package/dist/types-ts4.5/rules/no-xcss-in-cx/index.d.ts +31 -0
  39. package/package.json +1 -1
@@ -6,7 +6,9 @@ Object.defineProperty(exports, "__esModule", {
6
6
  value: true
7
7
  });
8
8
  exports.extractPackageNameFromImport = extractPackageNameFromImport;
9
+ exports.findCrossPackageBridgeExportPath = findCrossPackageBridgeExportPath;
9
10
  exports.findExportForSourceFile = findExportForSourceFile;
11
+ exports.isKebabCaseExportKey = isKebabCaseExportKey;
10
12
  exports.parsePackageExports = parsePackageExports;
11
13
  exports.resolveCrossPackageImport = resolveCrossPackageImport;
12
14
  var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
@@ -173,10 +175,52 @@ function parsePackageExports(_ref2) {
173
175
  });
174
176
  return exportsMap;
175
177
  }
178
+ /**
179
+ * Check whether a subpath export key (e.g. `"./checkbox-select"`) is kebab-case.
180
+ *
181
+ * A key is considered kebab-case when the portion after the leading `"./"` prefix
182
+ * consists only of lowercase letters, digits, hyphens, dots, and forward-slash
183
+ * separators — i.e. no uppercase letters, underscores, or camelCase humps.
184
+ */
185
+ function isKebabCaseExportKey(key) {
186
+ var body = key.replace(/^\.\//, '');
187
+ if (body.length === 0) {
188
+ return false;
189
+ }
190
+ return /^[a-z0-9][a-z0-9\-./]*$/.test(body);
191
+ }
192
+
193
+ /**
194
+ * Given a list of candidate {@link ExportMatchResult}s that all resolve to the same
195
+ * source file, pick the best one. When any candidate's export path is kebab-case
196
+ * and points to an entry-point file, prefer it over non-kebab-case alternatives.
197
+ * Falls back to the first candidate if no kebab-case entry-point candidate is found.
198
+ */
199
+ function pickBestMatch(candidates, exportsMap) {
200
+ if (candidates.length === 1) {
201
+ return candidates[0];
202
+ }
203
+
204
+ // Among candidates whose value is an entry-point file, prefer kebab-case keys.
205
+ var entryPointKebab = candidates.filter(function (c) {
206
+ var resolved = exportsMap.get(c.exportPath);
207
+ return resolved && isInEntryPointsFolder(resolved) && isKebabCaseExportKey(c.exportPath);
208
+ });
209
+ if (entryPointKebab.length > 0) {
210
+ return entryPointKebab[0];
211
+ }
212
+
213
+ // Fall back to the first candidate (preserves previous behaviour).
214
+ return candidates[0];
215
+ }
216
+
176
217
  /**
177
218
  * Find a matching export entry for a given source file path.
178
219
  * Returns the export path (e.g., "./controllers/analytics") or null if not found.
179
220
  *
221
+ * When multiple export paths resolve to the same source file **and** point to an
222
+ * entry-point file, kebab-case keys are preferred over other casing styles.
223
+ *
180
224
  * When `fs` is provided, also checks entry-point wrapper files. If an export resolves
181
225
  * to a file inside a recognized entry-points folder (entry-points, entrypoints, etc.),
182
226
  * the wrapper is parsed to see if it re-exports from `sourceFilePath`.
@@ -190,6 +234,8 @@ function findExportForSourceFile(_ref3) {
190
234
  exportsMap = _ref3.exportsMap,
191
235
  fs = _ref3.fs,
192
236
  sourceExportName = _ref3.sourceExportName;
237
+ // --- Phase 1: direct matches (export value === sourceFilePath) ---
238
+ var directMatches = [];
193
239
  var _iterator3 = _createForOfIteratorHelper(exportsMap),
194
240
  _step3;
195
241
  try {
@@ -198,9 +244,9 @@ function findExportForSourceFile(_ref3) {
198
244
  _exportPath = _step3$value[0],
199
245
  _resolvedPath = _step3$value[1];
200
246
  if (_resolvedPath === sourceFilePath) {
201
- return {
247
+ directMatches.push({
202
248
  exportPath: _exportPath
203
- };
249
+ });
204
250
  }
205
251
  }
206
252
  } catch (err) {
@@ -208,7 +254,13 @@ function findExportForSourceFile(_ref3) {
208
254
  } finally {
209
255
  _iterator3.f();
210
256
  }
257
+ if (directMatches.length > 0) {
258
+ return pickBestMatch(directMatches, exportsMap);
259
+ }
260
+
261
+ // --- Phase 2: entry-point wrapper re-export matches ---
211
262
  if (fs) {
263
+ var entryPointMatches = [];
212
264
  var _iterator4 = _createForOfIteratorHelper(exportsMap),
213
265
  _step4;
214
266
  try {
@@ -231,10 +283,10 @@ function findExportForSourceFile(_ref3) {
231
283
  if (sourceExportName !== undefined && reExport.nameMap.has(sourceExportName)) {
232
284
  entryPointExportName = reExport.nameMap.get(sourceExportName);
233
285
  }
234
- return {
286
+ entryPointMatches.push({
235
287
  exportPath: exportPath,
236
288
  entryPointExportName: entryPointExportName
237
- };
289
+ });
238
290
  }
239
291
  }
240
292
  } catch (err) {
@@ -249,6 +301,99 @@ function findExportForSourceFile(_ref3) {
249
301
  } finally {
250
302
  _iterator4.f();
251
303
  }
304
+ if (entryPointMatches.length > 0) {
305
+ return pickBestMatch(entryPointMatches, exportsMap);
306
+ }
307
+ }
308
+ return null;
309
+ }
310
+
311
+ /**
312
+ * When a symbol reaches the consumer through a barrel package that re-exports from
313
+ * `crossPackageName`, find a `package.json` export subpath of that barrel whose entry
314
+ * file directly re-exports the symbol from `crossPackageName` (named exports only).
315
+ *
316
+ * This enables rewriting imports to `@scope/barrel/subpath` instead of
317
+ * `@scope/cross-package/...` when the barrel exposes such a subpath (e.g. `@atlaskit/select/react-select`).
318
+ */
319
+ function findCrossPackageBridgeExportPath(_ref4) {
320
+ var exportsMap = _ref4.exportsMap,
321
+ crossPackageName = _ref4.crossPackageName,
322
+ exportedName = _ref4.exportedName,
323
+ fs = _ref4.fs;
324
+ var _iterator6 = _createForOfIteratorHelper(exportsMap),
325
+ _step6;
326
+ try {
327
+ for (_iterator6.s(); !(_step6 = _iterator6.n()).done;) {
328
+ var _step6$value = (0, _slicedToArray2.default)(_step6.value, 2),
329
+ exportPath = _step6$value[0],
330
+ resolvedPath = _step6$value[1];
331
+ var content = (0, _fileSystem.readFileContent)({
332
+ filePath: resolvedPath,
333
+ fs: fs
334
+ });
335
+ if (!content) {
336
+ continue;
337
+ }
338
+ try {
339
+ var sourceFile = ts.createSourceFile(resolvedPath, content, ts.ScriptTarget.Latest, true);
340
+ var _iterator7 = _createForOfIteratorHelper(sourceFile.statements),
341
+ _step7;
342
+ try {
343
+ for (_iterator7.s(); !(_step7 = _iterator7.n()).done;) {
344
+ var statement = _step7.value;
345
+ if (!ts.isExportDeclaration(statement) || statement.isTypeOnly) {
346
+ continue;
347
+ }
348
+ if (!statement.moduleSpecifier || !ts.isStringLiteral(statement.moduleSpecifier)) {
349
+ continue;
350
+ }
351
+ if (statement.moduleSpecifier.text !== crossPackageName) {
352
+ continue;
353
+ }
354
+ if (!statement.exportClause || ts.isNamespaceExport(statement.exportClause)) {
355
+ continue;
356
+ }
357
+ if (!ts.isNamedExports(statement.exportClause)) {
358
+ continue;
359
+ }
360
+ var _iterator8 = _createForOfIteratorHelper(statement.exportClause.elements),
361
+ _step8;
362
+ try {
363
+ for (_iterator8.s(); !(_step8 = _iterator8.n()).done;) {
364
+ var element = _step8.value;
365
+ if (element.isTypeOnly) {
366
+ continue;
367
+ }
368
+ var publicName = element.name.text;
369
+ if (publicName !== exportedName) {
370
+ continue;
371
+ }
372
+ var entryPointExportName = element.propertyName ? element.propertyName.text : undefined;
373
+ return {
374
+ exportPath: exportPath,
375
+ entryPointExportName: entryPointExportName
376
+ };
377
+ }
378
+ } catch (err) {
379
+ _iterator8.e(err);
380
+ } finally {
381
+ _iterator8.f();
382
+ }
383
+ }
384
+ } catch (err) {
385
+ _iterator7.e(err);
386
+ } finally {
387
+ _iterator7.f();
388
+ }
389
+ } catch (_unused4) {
390
+ // Ignore parse errors for individual export entry files
391
+ }
392
+ }
393
+ } catch (err) {
394
+ _iterator6.e(err);
395
+ } finally {
396
+ _iterator6.f();
252
397
  }
253
398
  return null;
254
399
  }
@@ -272,10 +417,10 @@ function extractPackageNameFromImport(moduleSpecifier) {
272
417
  * Resolve a cross-package import to its package directory and export info.
273
418
  * Returns null if the package is not in the target folder or cannot be resolved.
274
419
  */
275
- function resolveCrossPackageImport(_ref4) {
276
- var moduleSpecifier = _ref4.moduleSpecifier,
277
- workspaceRoot = _ref4.workspaceRoot,
278
- fs = _ref4.fs;
420
+ function resolveCrossPackageImport(_ref5) {
421
+ var moduleSpecifier = _ref5.moduleSpecifier,
422
+ workspaceRoot = _ref5.workspaceRoot,
423
+ fs = _ref5.fs;
279
424
  // Only handle @atlassian/* scoped packages
280
425
  var parsed = extractPackageNameFromImport(moduleSpecifier);
281
426
  if (!parsed) {
@@ -0,0 +1,65 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = void 0;
7
+ 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; } } }; }
8
+ 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; } }
9
+ 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; }
10
+ // eslint-disable-next-line import/no-extraneous-dependencies
11
+
12
+ var RESTRICTED_IMPORTS = {
13
+ '@atlassian/atl-context': ['isFedRamp', 'isIsolatedCloud'],
14
+ '@atlaskit/atlassian-context': ['isFedRamp', 'isIsolatedCloud'],
15
+ '@atlassian/teams-common': ['isFedramp']
16
+ };
17
+ var rule = {
18
+ meta: {
19
+ type: 'problem',
20
+ docs: {
21
+ description: 'Disallow importing deprecated FedRamp/IsolatedCloud context functions. Use isFeatureEnabled from AEM (Atlassian Environment Manager) instead.',
22
+ recommended: true
23
+ },
24
+ messages: {
25
+ noRestrictedFedrampImports: '{{name}} from {{source}} will be deprecated soon. Please use isFeatureEnabled from AEM (Atlassian Environment Manager) instead. See go/AEM for more details.'
26
+ },
27
+ schema: []
28
+ },
29
+ create: function create(context) {
30
+ return {
31
+ ImportDeclaration: function ImportDeclaration(node) {
32
+ var source = node.source.value;
33
+ if (typeof source !== 'string') {
34
+ return;
35
+ }
36
+ var restrictedNames = RESTRICTED_IMPORTS[source];
37
+ if (!restrictedNames) {
38
+ return;
39
+ }
40
+ var _iterator = _createForOfIteratorHelper(node.specifiers),
41
+ _step;
42
+ try {
43
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
44
+ var specifier = _step.value;
45
+ if (specifier.type === 'ImportSpecifier' && restrictedNames.includes(specifier.imported.name)) {
46
+ context.report({
47
+ node: specifier,
48
+ messageId: 'noRestrictedFedrampImports',
49
+ data: {
50
+ name: specifier.imported.name,
51
+ source: source
52
+ }
53
+ });
54
+ }
55
+ }
56
+ } catch (err) {
57
+ _iterator.e(err);
58
+ } finally {
59
+ _iterator.f();
60
+ }
61
+ }
62
+ };
63
+ }
64
+ };
65
+ var _default = exports.default = rule;
@@ -0,0 +1,221 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = void 0;
7
+ var _isSupportedImport = require("@atlaskit/eslint-utils/is-supported-import");
8
+ var _contextCompat = require("../util/context-compat");
9
+ 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; } } }; }
10
+ 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; } }
11
+ 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; }
12
+ /**
13
+ * Disallows passing xcss() results into cx() when used in an xcss prop.
14
+ *
15
+ * xcss() from @atlaskit/primitives and cx() from @atlaskit/css / @compiled/react
16
+ * are incompatible — xcss() produces an opaque StyleRule object, while cx()
17
+ * expects Compiled atomic class name strings. Mixing them causes runtime errors.
18
+ * xcss() results must never be passed to cx(), whether inline or pre-defined.
19
+ *
20
+ * ❌ Wrong — xcss() called inline inside cx():
21
+ * xcss={cx(xcss({ color: 'red' }), xcss({ fontWeight: 'bold' }))}
22
+ *
23
+ * ❌ Also wrong — xcss() results pre-defined but still passed into cx():
24
+ * const baseStyles = xcss({ color: 'red' });
25
+ * const boldStyles = xcss({ fontWeight: 'bold' });
26
+ * xcss={cx(baseStyles, boldStyles)}
27
+ *
28
+ * ✅ Correct — pass xcss() results directly to the xcss prop (no cx()):
29
+ * const baseStyles = xcss({ color: 'red' });
30
+ * xcss={baseStyles}
31
+ *
32
+ * ✅ Correct — use cssMap() + cx() (cssMap is compatible with cx()):
33
+ * const styles = cssMap({ base: { color: 'red' } });
34
+ * xcss={cx(styles.base, condition && styles.focused)}
35
+ *
36
+ * This rule is import-aware: it only flags xcss() calls (inline or via variable)
37
+ * imported from @atlaskit/primitives inside cx() calls imported from @atlaskit/css
38
+ * or @compiled/react that appear inside an xcss prop.
39
+ */
40
+ var rule = {
41
+ meta: {
42
+ type: 'problem',
43
+ docs: {
44
+ description: 'Disallow calling xcss() inline inside cx() in an xcss prop. Define styles at module level instead.'
45
+ },
46
+ messages: {
47
+ noXcssInCx: 'Do not pass xcss() results into cx(). ' + 'xcss() produces a StyleRule object that is incompatible with cx(), which expects Compiled atomic class names. ' + 'Pass xcss() results directly to the xcss prop instead: xcss={myStyles}. ' + 'To conditionally combine styles, use cssMap() + cx(): const styles = cssMap({...}); xcss={cx(styles.base, cond && styles.active)}'
48
+ },
49
+ schema: []
50
+ },
51
+ create: function create(context) {
52
+ return {
53
+ JSXAttribute: function JSXAttribute(node) {
54
+ // Narrow Rule.Node to JSXAttribute (estree-jsx augments the `estree` Node
55
+ // union with JSX members, so this discriminated narrowing is safe).
56
+ if (node.type !== 'JSXAttribute') {
57
+ return;
58
+ }
59
+
60
+ // Only check `xcss` props
61
+ if (node.name.type !== 'JSXIdentifier' || node.name.name !== 'xcss') {
62
+ return;
63
+ }
64
+ if (!node.value || node.value.type !== 'JSXExpressionContainer') {
65
+ return;
66
+ }
67
+ var expression = node.value.expression;
68
+ if (expression.type === 'JSXEmptyExpression') {
69
+ return;
70
+ }
71
+
72
+ // Early-return if the expression cannot possibly contain a cx() call —
73
+ // avoids the cost of getImportSources/getScope on simple references like xcss={baseStyles}.
74
+ var isCallOrArray = expression.type === 'CallExpression' || expression.type === 'ArrayExpression';
75
+ if (!isCallOrArray) {
76
+ return;
77
+ }
78
+ var importSources = (0, _isSupportedImport.getImportSources)(context);
79
+ var _getScope = (0, _contextCompat.getScope)(context, node),
80
+ references = _getScope.references;
81
+
82
+ /**
83
+ * Returns true if an Identifier node resolves to a variable whose
84
+ * initializer is a call to xcss() imported from @atlaskit/primitives.
85
+ *
86
+ * Walks up the scope chain from the current JSXAttribute scope to find
87
+ * the variable definition, since module-level variables are not in the
88
+ * local scope's references list.
89
+ *
90
+ * e.g. `const baseStyles = xcss({ color: 'red' })` — passing `baseStyles`
91
+ * here returns true.
92
+ */
93
+ var isXcssVariable = function isXcssVariable(identNode) {
94
+ if (identNode.type !== 'Identifier') {
95
+ return false;
96
+ }
97
+ var name = identNode.name;
98
+ // Walk up the scope chain to find the variable definition
99
+ var currentScope = (0, _contextCompat.getScope)(context, node);
100
+ while (currentScope) {
101
+ var variable = currentScope.set.get(name);
102
+ if (variable) {
103
+ var _iterator = _createForOfIteratorHelper(variable.defs),
104
+ _step;
105
+ try {
106
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
107
+ var _def$node$init;
108
+ var def = _step.value;
109
+ if (def.type === 'Variable' && def.node.type === 'VariableDeclarator' && ((_def$node$init = def.node.init) === null || _def$node$init === void 0 ? void 0 : _def$node$init.type) === 'CallExpression') {
110
+ // isXcss checks the callee identifier against referencesInScope to
111
+ // find the import binding. The callee lives in the same scope as the
112
+ // variable definition, so use all references from that scope.
113
+ var defScopeRefs = currentScope.references;
114
+ if ((0, _isSupportedImport.isXcss)(def.node.init.callee, defScopeRefs, importSources)) {
115
+ return true;
116
+ }
117
+ }
118
+ }
119
+ // Found the variable but it's not an xcss() call
120
+ } catch (err) {
121
+ _iterator.e(err);
122
+ } finally {
123
+ _iterator.f();
124
+ }
125
+ return false;
126
+ }
127
+ currentScope = currentScope.upper;
128
+ }
129
+ return false;
130
+ };
131
+
132
+ /**
133
+ * Recursively check a node that is an argument to cx() for xcss() results —
134
+ * both inline calls and references to variables initialised with xcss().
135
+ * Recurses into LogicalExpression (&&, ||) and ConditionalExpression (? :) so that
136
+ * patterns like cx(cond && xcss({...})) and cx(cond ? baseStyles : a) are caught.
137
+ */
138
+ var _checkArgForXcss = function checkArgForXcss(argNode) {
139
+ if (!argNode) {
140
+ return;
141
+ }
142
+ // Inline: cx(xcss({ color: 'red' }))
143
+ if (argNode.type === 'CallExpression' && (0, _isSupportedImport.isXcss)(argNode.callee, references, importSources)) {
144
+ context.report({
145
+ node: argNode,
146
+ messageId: 'noXcssInCx'
147
+ });
148
+ return;
149
+ }
150
+ // Variable reference: cx(baseStyles) where baseStyles = xcss({...})
151
+ if (isXcssVariable(argNode)) {
152
+ context.report({
153
+ node: argNode,
154
+ messageId: 'noXcssInCx'
155
+ });
156
+ return;
157
+ }
158
+ // Recurse into `cond && xcss({...})` or `cond || xcss({...})`
159
+ if (argNode.type === 'LogicalExpression') {
160
+ _checkArgForXcss(argNode.left);
161
+ _checkArgForXcss(argNode.right);
162
+ }
163
+ // Recurse into `cond ? xcss({...}) : fallback`
164
+ if (argNode.type === 'ConditionalExpression') {
165
+ _checkArgForXcss(argNode.consequent);
166
+ _checkArgForXcss(argNode.alternate);
167
+ }
168
+ };
169
+
170
+ /**
171
+ * Check all arguments of a cx() call for inline xcss() calls.
172
+ * Reports each xcss() call found as a violation.
173
+ */
174
+ var checkCxArgs = function checkCxArgs(callNode) {
175
+ if (callNode.type !== 'CallExpression') {
176
+ return;
177
+ }
178
+ if (!(0, _isSupportedImport.isCxFunction)(callNode.callee, references, importSources)) {
179
+ return;
180
+ }
181
+ var _iterator2 = _createForOfIteratorHelper(callNode.arguments),
182
+ _step2;
183
+ try {
184
+ for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
185
+ var arg = _step2.value;
186
+ if (arg) {
187
+ _checkArgForXcss(arg.type === 'SpreadElement' ? arg.argument : arg);
188
+ }
189
+ }
190
+ } catch (err) {
191
+ _iterator2.e(err);
192
+ } finally {
193
+ _iterator2.f();
194
+ }
195
+ };
196
+
197
+ // Case 1: xcss={cx(...)} — cx() directly as the xcss value
198
+ checkCxArgs(expression);
199
+
200
+ // Case 2: xcss={[..., cx(...), ...]} — cx() inside an xcss array
201
+ if (expression.type === 'ArrayExpression') {
202
+ var _iterator3 = _createForOfIteratorHelper(expression.elements),
203
+ _step3;
204
+ try {
205
+ for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
206
+ var element = _step3.value;
207
+ if (element) {
208
+ checkCxArgs(element.type === 'SpreadElement' ? element.argument : element);
209
+ }
210
+ }
211
+ } catch (err) {
212
+ _iterator3.e(err);
213
+ } finally {
214
+ _iterator3.f();
215
+ }
216
+ }
217
+ }
218
+ };
219
+ }
220
+ };
221
+ var _default = exports.default = rule;
@@ -24,7 +24,7 @@ var messages = {
24
24
  suggestFixPath: 'Update import path to match visitExample arguments'
25
25
  };
26
26
  function isTargetFile(filename) {
27
- return filename.endsWith('.spec.tsx');
27
+ return filename.endsWith('.spec.tsx') || filename.endsWith('.spec.ts');
28
28
  }
29
29
 
30
30
  /**
@@ -118,7 +118,8 @@ function resolveVariableToConstant(programBody, variableName, cache) {
118
118
  * Returns null for any argument that can't be statically resolved.
119
119
  */
120
120
  function extractCallArgs(node, programBody, variableCache) {
121
- if (node.arguments.length < 3) {
121
+ var argOffset = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 0;
122
+ if (node.arguments.length < argOffset + 3) {
122
123
  return null;
123
124
  }
124
125
  function resolveArg(arg) {
@@ -130,9 +131,9 @@ function extractCallArgs(node, programBody, variableCache) {
130
131
  }
131
132
  return null;
132
133
  }
133
- var groupId = resolveArg(node.arguments[0]);
134
- var packageId = resolveArg(node.arguments[1]);
135
- var exampleId = resolveArg(node.arguments[2]);
134
+ var groupId = resolveArg(node.arguments[argOffset]);
135
+ var packageId = resolveArg(node.arguments[argOffset + 1]);
136
+ var exampleId = resolveArg(node.arguments[argOffset + 2]);
136
137
  if (!groupId || !packageId || !exampleId) {
137
138
  return null;
138
139
  }
@@ -180,8 +181,13 @@ function resolveExamplePathFromArgs(groupId, packageId, exampleId, testFilePath)
180
181
  var examplesDir = _path.default.resolve(packagesBase.basePath, groupId, packageId, 'examples');
181
182
  var fallback = _path.default.resolve(examplesDir, "".concat(exampleId, ".tsx"));
182
183
 
183
- // Match: exact name OR numeric-prefixed variant, with optional `.examples` infix
184
- var candidateRe = new RegExp("^(?:\\d+-)?".concat(exampleId, "(?:\\.examples?)?\\.tsx$"));
184
+ // Phase 4: loosen candidateRe to match both pre- and post-rename filename shapes.
185
+ // Pre-rename: ^(\d+-)?<id>(\.(examples?))?\.tsx$
186
+ // Post-rename: ^(\d+-)?<id>(\.<ident>){0,3}\.tsx$ (Volt prefix, optional .dup<N>, optional role)
187
+ // The {0,3} cap prevents matching arbitrary strings (e.g. 4-component names).
188
+ // Escape regex metacharacters in exampleId (ids are kebab-case today, but defensive).
189
+ var escapedId = exampleId.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
190
+ var candidateRe = new RegExp("^(?:\\d+-)?".concat(escapedId, "(?:\\.[A-Za-z][A-Za-z0-9_]*){0,3}\\.tsx$"));
185
191
  try {
186
192
  var match = _fs.default.readdirSync(examplesDir).find(function (f) {
187
193
  return candidateRe.test(f);
@@ -279,20 +285,24 @@ var rule = {
279
285
  return;
280
286
  }
281
287
  var node = estreeNode;
282
- // Only handle `<anything>.visitExample(...)` calls
283
- if (node.callee.type !== _utils.AST_NODE_TYPES.MemberExpression || node.callee.property.type !== _utils.AST_NODE_TYPES.Identifier || node.callee.property.name !== 'visitExample') {
288
+ var calleeIdentifier = null;
289
+ var argOffset = 0;
290
+ if (node.callee.type === _utils.AST_NODE_TYPES.MemberExpression && node.callee.property.type === _utils.AST_NODE_TYPES.Identifier && node.callee.property.name === 'visitExample') {
291
+ calleeIdentifier = node.callee.property;
292
+ } else if (node.callee.type === _utils.AST_NODE_TYPES.Identifier && node.callee.name === 'visitMockedExample') {
293
+ calleeIdentifier = node.callee;
294
+ argOffset = 1; // first arg is `page`
295
+ } else {
284
296
  return;
285
297
  }
286
298
 
287
- // Narrow callee — we've confirmed property is an Identifier above
288
- var callee = node.callee;
289
299
  // reportCallee is typed as estree.Node for context.report compatibility
290
300
  var reportCallee = estreeNode.callee;
291
301
  var genericType = extractGenericType(node);
292
302
 
293
303
  // ── Case 1: No generic type parameter ────────────────────────────────
294
304
  if (genericType === null) {
295
- var _args = extractCallArgs(node, programBody, variableCache);
305
+ var _args = extractCallArgs(node, programBody, variableCache, argOffset);
296
306
  context.report({
297
307
  node: reportCallee,
298
308
  messageId: 'missingTypeofImport',
@@ -305,7 +315,7 @@ var rule = {
305
315
  return null;
306
316
  }
307
317
  var importPath = computeRelativeImportPath(filename, examplePath);
308
- var _ref2 = callee.property.range,
318
+ var _ref2 = calleeIdentifier.range,
309
319
  _ref3 = (0, _slicedToArray2.default)(_ref2, 2),
310
320
  start = _ref3[0],
311
321
  end = _ref3[1];
@@ -367,7 +377,7 @@ var rule = {
367
377
  }
368
378
 
369
379
  // Validate that the import path matches the arguments
370
- var args = extractCallArgs(node, programBody, variableCache);
380
+ var args = extractCallArgs(node, programBody, variableCache, argOffset);
371
381
  if (!args) {
372
382
  // Dynamic arguments — can't validate statically
373
383
  return;
@@ -25,10 +25,12 @@ import useRecommendedUtils from './rules/feature-gating/use-recommended-utils';
25
25
  import validGateName from './rules/feature-gating/valid-gate-name';
26
26
  import expandBackgroundShorthand from './rules/compiled/expand-background-shorthand';
27
27
  import expandSpacingShorthand from './rules/compiled/expand-spacing-shorthand';
28
+ import noCssPropInObjectSpread from './rules/compiled/no-css-prop-in-object-spread';
28
29
  import noSparseCheckout from './rules/no-sparse-checkout';
29
30
  import noDirectDocumentUsage from './rules/no-direct-document-usage';
30
31
  import noSetImmediate from './rules/no-set-immediate';
31
32
  import preferCryptoRandomUuid from './rules/prefer-crypto-random-uuid';
33
+ import noRestrictedFedrampImports from './rules/no-restricted-fedramp-imports';
32
34
  import noBarrelEntryImports from './rules/import/no-barrel-entry-imports';
33
35
  import noBarrelEntryJestMock from './rules/import/no-barrel-entry-jest-mock';
34
36
  import noJestMockBarrelFiles from './rules/import/no-jest-mock-barrel-files';
@@ -36,6 +38,7 @@ import noRelativeBarrelFileImports from './rules/import/no-relative-barrel-file-
36
38
  import noConversationAssistantBarrelImports from './rules/import/no-conversation-assistant-barrel-imports';
37
39
  import visitExampleTypeImportRequired from './rules/visit-example-type-import-required';
38
40
  import ensureUseSyncExternalStoreServerSnapshot from './rules/ensure-use-sync-external-store-server-snapshot';
41
+ import noXcssInCx from './rules/no-xcss-in-cx';
39
42
  import { join, normalize } from 'node:path';
40
43
  import { readFileSync } from 'node:fs';
41
44
  let jiraRoot;
@@ -66,6 +69,7 @@ const rules = {
66
69
  'expand-border-shorthand': expandBorderShorthand,
67
70
  'expand-background-shorthand': expandBackgroundShorthand,
68
71
  'expand-spacing-shorthand': expandSpacingShorthand,
72
+ 'no-css-prop-in-object-spread': noCssPropInObjectSpread,
69
73
  'no-duplicate-dependencies': noDuplicateDependencies,
70
74
  'no-invalid-feature-flag-usage': noInvalidFeatureFlagUsage,
71
75
  'no-pre-post-install-scripts': noPreAndPostInstallScripts,
@@ -85,12 +89,14 @@ const rules = {
85
89
  'no-direct-document-usage': noDirectDocumentUsage,
86
90
  'no-set-immediate': noSetImmediate,
87
91
  'prefer-crypto-random-uuid': preferCryptoRandomUuid,
92
+ 'no-restricted-fedramp-imports': noRestrictedFedrampImports,
88
93
  'no-barrel-entry-imports': noBarrelEntryImports,
89
94
  'no-barrel-entry-jest-mock': noBarrelEntryJestMock,
90
95
  'no-jest-mock-barrel-files': noJestMockBarrelFiles,
91
96
  'no-relative-barrel-file-imports': noRelativeBarrelFileImports,
92
97
  'no-conversation-assistant-barrel-imports': noConversationAssistantBarrelImports,
93
98
  'visit-example-type-import-required': visitExampleTypeImportRequired,
99
+ 'no-xcss-in-cx': noXcssInCx,
94
100
  'ensure-use-sync-external-store-server-snapshot': ensureUseSyncExternalStoreServerSnapshot
95
101
  };
96
102
  const commonConfig = {
@@ -103,10 +109,12 @@ const commonConfig = {
103
109
  '@atlaskit/platform/no-module-level-eval-nav4': 'error',
104
110
  '@atlaskit/platform/no-direct-document-usage': 'warn',
105
111
  '@atlaskit/platform/no-set-immediate': 'error',
112
+ '@atlaskit/platform/no-xcss-in-cx': 'error',
106
113
  // Compiled: rules that are not included via `@compiled/recommended
107
114
  '@atlaskit/platform/expand-border-shorthand': 'error',
108
115
  '@atlaskit/platform/expand-background-shorthand': 'error',
109
116
  '@atlaskit/platform/expand-spacing-shorthand': 'error',
117
+ '@atlaskit/platform/no-css-prop-in-object-spread': 'error',
110
118
  '@compiled/jsx-pragma': ['error', {
111
119
  importSources: ['@atlaskit/css'],
112
120
  onlyRunIfImportingCompiled: true,
@@ -151,7 +159,6 @@ const plugin = {
151
159
  get '@atlaskit/platform'() {
152
160
  return plugin;
153
161
  },
154
- // @ts-expect-error there's an issue with the types for @compiled/eslint-plugin ('no-css-prop-without-css-function' specifically)
155
162
  '@compiled': {
156
163
  meta: compiledPlugin.meta,
157
164
  rules: compiledPlugin.rules
@@ -168,7 +175,6 @@ const plugin = {
168
175
  get '@atlaskit/platform'() {
169
176
  return plugin;
170
177
  },
171
- // @ts-expect-error there's an issue with the types for @compiled/eslint-plugin ('no-css-prop-without-css-function' specifically)
172
178
  '@compiled': {
173
179
  meta: compiledPlugin.meta,
174
180
  rules: compiledPlugin.rules