@atlaskit/eslint-plugin-platform 2.9.1 → 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.
@@ -0,0 +1,136 @@
1
+ import { getScope, getSourceCode } from '../../util/context-compat';
2
+
3
+ /**
4
+ * Returns the `css` Property node from an ObjectExpression, or null if not found.
5
+ */
6
+ function getCssProperty(objectExpression) {
7
+ for (const prop of objectExpression.properties) {
8
+ if (prop.type !== 'Property') {
9
+ continue;
10
+ }
11
+ const {
12
+ key
13
+ } = prop;
14
+ if (key.type === 'Identifier' && key.name === 'css' || key.type === 'Literal' && key.value === 'css') {
15
+ return prop;
16
+ }
17
+ }
18
+ return null;
19
+ }
20
+ export const noCssPropInObjectSpread = {
21
+ meta: {
22
+ docs: {
23
+ url: 'https://bitbucket.org/atlassian/atlassian-frontend-monorepo/src/master/platform/packages/platform/eslint-plugin/src/rules/compiled/no-css-prop-in-object-spread/',
24
+ description: 'Disallows `css` property inside objects spread into JSX — the Compiled JSX pragma ignores it'
25
+ },
26
+ fixable: 'code',
27
+ messages: {
28
+ noCssPropInObjectSpread: 'The `css` property inside an object spread into JSX is a no-op. The Compiled JSX pragma only processes `css` as a direct JSX attribute. Move `css` out of the spread: <El css={...} />'
29
+ },
30
+ type: 'problem'
31
+ },
32
+ create(context) {
33
+ return {
34
+ JSXSpreadAttribute(node) {
35
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
36
+ const spreadNode = node;
37
+ const arg = spreadNode.argument;
38
+
39
+ // Case 1: inline object literal — <div {...{ css: styles, id: 'foo' }} />
40
+ if (arg.type === 'ObjectExpression') {
41
+ const objectArg = arg;
42
+ const cssProp = getCssProperty(objectArg);
43
+ if (!cssProp) {
44
+ return;
45
+ }
46
+ context.report({
47
+ node,
48
+ messageId: 'noCssPropInObjectSpread',
49
+ fix(fixer) {
50
+ const sourceCode = getSourceCode(context);
51
+ const cssValueText = sourceCode.getText(cssProp.value);
52
+ const remainingProps = objectArg.properties.filter(p => p !== cssProp);
53
+ const directCssProp = `css={${cssValueText}}`;
54
+ if (remainingProps.length === 0) {
55
+ return fixer.replaceText(node, directCssProp);
56
+ }
57
+ const remainingText = remainingProps.map(p => sourceCode.getText(p)).join(', ');
58
+ return fixer.replaceText(node, `${directCssProp} {...{ ${remainingText} }}`);
59
+ }
60
+ });
61
+ return;
62
+ }
63
+
64
+ // Case 2: variable reference — <div {...props} />
65
+ if (arg.type === 'Identifier') {
66
+ const scope = getScope(context, arg);
67
+ let currentScope = scope;
68
+ let variable = null;
69
+ while (currentScope) {
70
+ const found = currentScope.variables.find(v => v.name === arg.name);
71
+ if (found) {
72
+ variable = found;
73
+ break;
74
+ }
75
+ currentScope = currentScope.upper;
76
+ }
77
+ if (!variable || variable.defs.length === 0) {
78
+ return;
79
+ }
80
+ const def = variable.defs[0];
81
+ if (def.type !== 'Variable' || !def.node.init || def.node.init.type !== 'ObjectExpression') {
82
+ return;
83
+ }
84
+ const initObject = def.node.init;
85
+ const cssProp = getCssProperty(initObject);
86
+ if (!cssProp) {
87
+ return;
88
+ }
89
+
90
+ // Only auto-fix when there is exactly one JSX spread site for this variable
91
+ const spreadCount = variable.references.filter(ref => {
92
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
93
+ const refParent = ref.identifier.parent;
94
+ return (refParent === null || refParent === void 0 ? void 0 : refParent.type) === 'JSXSpreadAttribute';
95
+ }).length;
96
+ context.report({
97
+ node,
98
+ messageId: 'noCssPropInObjectSpread',
99
+ ...(spreadCount === 1 ? {
100
+ fix(fixer) {
101
+ const sourceCode = getSourceCode(context);
102
+ const cssValueText = sourceCode.getText(cssProp.value);
103
+ const fixes = [];
104
+ const remainingProps = initObject.properties.filter(p => p !== cssProp);
105
+ if (remainingProps.length === 0) {
106
+ fixes.push(fixer.replaceText(initObject, '{}'));
107
+ } else {
108
+ const propIndex = initObject.properties.indexOf(cssProp);
109
+ const isLast = propIndex === initObject.properties.length - 1;
110
+ const tokenBefore = sourceCode.getTokenBefore(cssProp);
111
+ const tokenAfter = sourceCode.getTokenAfter(cssProp);
112
+ if (!isLast && tokenAfter && tokenAfter.value === ',') {
113
+ const src = sourceCode.getText();
114
+ const afterEnd = tokenAfter.range[1];
115
+ let end = afterEnd;
116
+ while (end < src.length && src[end] === ' ') {
117
+ end++;
118
+ }
119
+ fixes.push(fixer.removeRange([cssProp.range[0], end]));
120
+ } else if (tokenBefore && tokenBefore.value === ',') {
121
+ fixes.push(fixer.removeRange([tokenBefore.range[0], cssProp.range[1]]));
122
+ } else {
123
+ fixes.push(fixer.remove(cssProp));
124
+ }
125
+ }
126
+ fixes.push(fixer.insertTextBefore(node, `css={${cssValueText}} `));
127
+ return fixes;
128
+ }
129
+ } : {})
130
+ });
131
+ }
132
+ }
133
+ };
134
+ }
135
+ };
136
+ export default noCssPropInObjectSpread;
@@ -0,0 +1,187 @@
1
+ import { getImportSources, isCxFunction, isXcss } from '@atlaskit/eslint-utils/is-supported-import';
2
+ import { getScope } from '../util/context-compat';
3
+
4
+ /**
5
+ * Disallows passing xcss() results into cx() when used in an xcss prop.
6
+ *
7
+ * xcss() from @atlaskit/primitives and cx() from @atlaskit/css / @compiled/react
8
+ * are incompatible — xcss() produces an opaque StyleRule object, while cx()
9
+ * expects Compiled atomic class name strings. Mixing them causes runtime errors.
10
+ * xcss() results must never be passed to cx(), whether inline or pre-defined.
11
+ *
12
+ * ❌ Wrong — xcss() called inline inside cx():
13
+ * xcss={cx(xcss({ color: 'red' }), xcss({ fontWeight: 'bold' }))}
14
+ *
15
+ * ❌ Also wrong — xcss() results pre-defined but still passed into cx():
16
+ * const baseStyles = xcss({ color: 'red' });
17
+ * const boldStyles = xcss({ fontWeight: 'bold' });
18
+ * xcss={cx(baseStyles, boldStyles)}
19
+ *
20
+ * ✅ Correct — pass xcss() results directly to the xcss prop (no cx()):
21
+ * const baseStyles = xcss({ color: 'red' });
22
+ * xcss={baseStyles}
23
+ *
24
+ * ✅ Correct — use cssMap() + cx() (cssMap is compatible with cx()):
25
+ * const styles = cssMap({ base: { color: 'red' } });
26
+ * xcss={cx(styles.base, condition && styles.focused)}
27
+ *
28
+ * This rule is import-aware: it only flags xcss() calls (inline or via variable)
29
+ * imported from @atlaskit/primitives inside cx() calls imported from @atlaskit/css
30
+ * or @compiled/react that appear inside an xcss prop.
31
+ */
32
+ const rule = {
33
+ meta: {
34
+ type: 'problem',
35
+ docs: {
36
+ description: 'Disallow calling xcss() inline inside cx() in an xcss prop. Define styles at module level instead.'
37
+ },
38
+ messages: {
39
+ 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)}'
40
+ },
41
+ schema: []
42
+ },
43
+ create(context) {
44
+ return {
45
+ JSXAttribute(node) {
46
+ // Narrow Rule.Node to JSXAttribute (estree-jsx augments the `estree` Node
47
+ // union with JSX members, so this discriminated narrowing is safe).
48
+ if (node.type !== 'JSXAttribute') {
49
+ return;
50
+ }
51
+
52
+ // Only check `xcss` props
53
+ if (node.name.type !== 'JSXIdentifier' || node.name.name !== 'xcss') {
54
+ return;
55
+ }
56
+ if (!node.value || node.value.type !== 'JSXExpressionContainer') {
57
+ return;
58
+ }
59
+ const expression = node.value.expression;
60
+ if (expression.type === 'JSXEmptyExpression') {
61
+ return;
62
+ }
63
+
64
+ // Early-return if the expression cannot possibly contain a cx() call —
65
+ // avoids the cost of getImportSources/getScope on simple references like xcss={baseStyles}.
66
+ const isCallOrArray = expression.type === 'CallExpression' || expression.type === 'ArrayExpression';
67
+ if (!isCallOrArray) {
68
+ return;
69
+ }
70
+ const importSources = getImportSources(context);
71
+ const {
72
+ references
73
+ } = getScope(context, node);
74
+
75
+ /**
76
+ * Returns true if an Identifier node resolves to a variable whose
77
+ * initializer is a call to xcss() imported from @atlaskit/primitives.
78
+ *
79
+ * Walks up the scope chain from the current JSXAttribute scope to find
80
+ * the variable definition, since module-level variables are not in the
81
+ * local scope's references list.
82
+ *
83
+ * e.g. `const baseStyles = xcss({ color: 'red' })` — passing `baseStyles`
84
+ * here returns true.
85
+ */
86
+ const isXcssVariable = identNode => {
87
+ if (identNode.type !== 'Identifier') {
88
+ return false;
89
+ }
90
+ const name = identNode.name;
91
+ // Walk up the scope chain to find the variable definition
92
+ let currentScope = getScope(context, node);
93
+ while (currentScope) {
94
+ const variable = currentScope.set.get(name);
95
+ if (variable) {
96
+ for (const def of variable.defs) {
97
+ var _def$node$init;
98
+ 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') {
99
+ // isXcss checks the callee identifier against referencesInScope to
100
+ // find the import binding. The callee lives in the same scope as the
101
+ // variable definition, so use all references from that scope.
102
+ const defScopeRefs = currentScope.references;
103
+ if (isXcss(def.node.init.callee, defScopeRefs, importSources)) {
104
+ return true;
105
+ }
106
+ }
107
+ }
108
+ // Found the variable but it's not an xcss() call
109
+ return false;
110
+ }
111
+ currentScope = currentScope.upper;
112
+ }
113
+ return false;
114
+ };
115
+
116
+ /**
117
+ * Recursively check a node that is an argument to cx() for xcss() results —
118
+ * both inline calls and references to variables initialised with xcss().
119
+ * Recurses into LogicalExpression (&&, ||) and ConditionalExpression (? :) so that
120
+ * patterns like cx(cond && xcss({...})) and cx(cond ? baseStyles : a) are caught.
121
+ */
122
+ const checkArgForXcss = argNode => {
123
+ if (!argNode) {
124
+ return;
125
+ }
126
+ // Inline: cx(xcss({ color: 'red' }))
127
+ if (argNode.type === 'CallExpression' && isXcss(argNode.callee, references, importSources)) {
128
+ context.report({
129
+ node: argNode,
130
+ messageId: 'noXcssInCx'
131
+ });
132
+ return;
133
+ }
134
+ // Variable reference: cx(baseStyles) where baseStyles = xcss({...})
135
+ if (isXcssVariable(argNode)) {
136
+ context.report({
137
+ node: argNode,
138
+ messageId: 'noXcssInCx'
139
+ });
140
+ return;
141
+ }
142
+ // Recurse into `cond && xcss({...})` or `cond || xcss({...})`
143
+ if (argNode.type === 'LogicalExpression') {
144
+ checkArgForXcss(argNode.left);
145
+ checkArgForXcss(argNode.right);
146
+ }
147
+ // Recurse into `cond ? xcss({...}) : fallback`
148
+ if (argNode.type === 'ConditionalExpression') {
149
+ checkArgForXcss(argNode.consequent);
150
+ checkArgForXcss(argNode.alternate);
151
+ }
152
+ };
153
+
154
+ /**
155
+ * Check all arguments of a cx() call for inline xcss() calls.
156
+ * Reports each xcss() call found as a violation.
157
+ */
158
+ const checkCxArgs = callNode => {
159
+ if (callNode.type !== 'CallExpression') {
160
+ return;
161
+ }
162
+ if (!isCxFunction(callNode.callee, references, importSources)) {
163
+ return;
164
+ }
165
+ for (const arg of callNode.arguments) {
166
+ if (arg) {
167
+ checkArgForXcss(arg.type === 'SpreadElement' ? arg.argument : arg);
168
+ }
169
+ }
170
+ };
171
+
172
+ // Case 1: xcss={cx(...)} — cx() directly as the xcss value
173
+ checkCxArgs(expression);
174
+
175
+ // Case 2: xcss={[..., cx(...), ...]} — cx() inside an xcss array
176
+ if (expression.type === 'ArrayExpression') {
177
+ for (const element of expression.elements) {
178
+ if (element) {
179
+ checkCxArgs(element.type === 'SpreadElement' ? element.argument : element);
180
+ }
181
+ }
182
+ }
183
+ }
184
+ };
185
+ }
186
+ };
187
+ export default rule;
@@ -91,8 +91,8 @@ function resolveVariableToConstant(programBody, variableName, cache) {
91
91
  * Each argument may be a string literal or a reference to a top-level const string variable.
92
92
  * Returns null for any argument that can't be statically resolved.
93
93
  */
94
- function extractCallArgs(node, programBody, variableCache) {
95
- if (node.arguments.length < 3) {
94
+ function extractCallArgs(node, programBody, variableCache, argOffset = 0) {
95
+ if (node.arguments.length < argOffset + 3) {
96
96
  return null;
97
97
  }
98
98
  function resolveArg(arg) {
@@ -104,9 +104,9 @@ function extractCallArgs(node, programBody, variableCache) {
104
104
  }
105
105
  return null;
106
106
  }
107
- const groupId = resolveArg(node.arguments[0]);
108
- const packageId = resolveArg(node.arguments[1]);
109
- const exampleId = resolveArg(node.arguments[2]);
107
+ const groupId = resolveArg(node.arguments[argOffset]);
108
+ const packageId = resolveArg(node.arguments[argOffset + 1]);
109
+ const exampleId = resolveArg(node.arguments[argOffset + 2]);
110
110
  if (!groupId || !packageId || !exampleId) {
111
111
  return null;
112
112
  }
@@ -152,8 +152,13 @@ function resolveExamplePathFromArgs(groupId, packageId, exampleId, testFilePath)
152
152
  const examplesDir = path.resolve(packagesBase.basePath, groupId, packageId, 'examples');
153
153
  const fallback = path.resolve(examplesDir, `${exampleId}.tsx`);
154
154
 
155
- // Match: exact name OR numeric-prefixed variant, with optional `.examples` infix
156
- const candidateRe = new RegExp(`^(?:\\d+-)?${exampleId}(?:\\.examples?)?\\.tsx$`);
155
+ // Phase 4: loosen candidateRe to match both pre- and post-rename filename shapes.
156
+ // Pre-rename: ^(\d+-)?<id>(\.(examples?))?\.tsx$
157
+ // Post-rename: ^(\d+-)?<id>(\.<ident>){0,3}\.tsx$ (Volt prefix, optional .dup<N>, optional role)
158
+ // The {0,3} cap prevents matching arbitrary strings (e.g. 4-component names).
159
+ // Escape regex metacharacters in exampleId (ids are kebab-case today, but defensive).
160
+ const escapedId = exampleId.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
161
+ const candidateRe = new RegExp(`^(?:\\d+-)?${escapedId}(?:\\.[A-Za-z][A-Za-z0-9_]*){0,3}\\.tsx$`);
157
162
  try {
158
163
  const match = fs.readdirSync(examplesDir).find(f => candidateRe.test(f));
159
164
  if (match) {
@@ -248,20 +253,24 @@ const rule = {
248
253
  return;
249
254
  }
250
255
  const node = estreeNode;
251
- // Only handle `<anything>.visitExample(...)` calls
252
- if (node.callee.type !== AST_NODE_TYPES.MemberExpression || node.callee.property.type !== AST_NODE_TYPES.Identifier || node.callee.property.name !== 'visitExample') {
256
+ let calleeIdentifier = null;
257
+ let argOffset = 0;
258
+ if (node.callee.type === AST_NODE_TYPES.MemberExpression && node.callee.property.type === AST_NODE_TYPES.Identifier && node.callee.property.name === 'visitExample') {
259
+ calleeIdentifier = node.callee.property;
260
+ } else if (node.callee.type === AST_NODE_TYPES.Identifier && node.callee.name === 'visitMockedExample') {
261
+ calleeIdentifier = node.callee;
262
+ argOffset = 1; // first arg is `page`
263
+ } else {
253
264
  return;
254
265
  }
255
266
 
256
- // Narrow callee — we've confirmed property is an Identifier above
257
- const callee = node.callee;
258
267
  // reportCallee is typed as estree.Node for context.report compatibility
259
268
  const reportCallee = estreeNode.callee;
260
269
  const genericType = extractGenericType(node);
261
270
 
262
271
  // ── Case 1: No generic type parameter ────────────────────────────────
263
272
  if (genericType === null) {
264
- const args = extractCallArgs(node, programBody, variableCache);
273
+ const args = extractCallArgs(node, programBody, variableCache, argOffset);
265
274
  context.report({
266
275
  node: reportCallee,
267
276
  messageId: 'missingTypeofImport',
@@ -274,7 +283,7 @@ const rule = {
274
283
  return null;
275
284
  }
276
285
  const importPath = computeRelativeImportPath(filename, examplePath);
277
- const [start, end] = callee.property.range;
286
+ const [start, end] = calleeIdentifier.range;
278
287
  return fixer.insertTextAfterRange([start, end], `<typeof import('${importPath}')>`);
279
288
  }
280
289
  });
@@ -333,7 +342,7 @@ const rule = {
333
342
  }
334
343
 
335
344
  // Validate that the import path matches the arguments
336
- const args = extractCallArgs(node, programBody, variableCache);
345
+ const args = extractCallArgs(node, programBody, variableCache, argOffset);
337
346
  if (!args) {
338
347
  // Dynamic arguments — can't validate statically
339
348
  return;
package/dist/esm/index.js CHANGED
@@ -28,6 +28,7 @@ import useRecommendedUtils from './rules/feature-gating/use-recommended-utils';
28
28
  import validGateName from './rules/feature-gating/valid-gate-name';
29
29
  import expandBackgroundShorthand from './rules/compiled/expand-background-shorthand';
30
30
  import expandSpacingShorthand from './rules/compiled/expand-spacing-shorthand';
31
+ import noCssPropInObjectSpread from './rules/compiled/no-css-prop-in-object-spread';
31
32
  import noSparseCheckout from './rules/no-sparse-checkout';
32
33
  import noDirectDocumentUsage from './rules/no-direct-document-usage';
33
34
  import noSetImmediate from './rules/no-set-immediate';
@@ -40,6 +41,7 @@ import noRelativeBarrelFileImports from './rules/import/no-relative-barrel-file-
40
41
  import noConversationAssistantBarrelImports from './rules/import/no-conversation-assistant-barrel-imports';
41
42
  import visitExampleTypeImportRequired from './rules/visit-example-type-import-required';
42
43
  import ensureUseSyncExternalStoreServerSnapshot from './rules/ensure-use-sync-external-store-server-snapshot';
44
+ import noXcssInCx from './rules/no-xcss-in-cx';
43
45
  import { join, normalize } from 'node:path';
44
46
  import { readFileSync } from 'node:fs';
45
47
  var jiraRoot;
@@ -70,6 +72,7 @@ var rules = {
70
72
  'expand-border-shorthand': expandBorderShorthand,
71
73
  'expand-background-shorthand': expandBackgroundShorthand,
72
74
  'expand-spacing-shorthand': expandSpacingShorthand,
75
+ 'no-css-prop-in-object-spread': noCssPropInObjectSpread,
73
76
  'no-duplicate-dependencies': noDuplicateDependencies,
74
77
  'no-invalid-feature-flag-usage': noInvalidFeatureFlagUsage,
75
78
  'no-pre-post-install-scripts': noPreAndPostInstallScripts,
@@ -96,6 +99,7 @@ var rules = {
96
99
  'no-relative-barrel-file-imports': noRelativeBarrelFileImports,
97
100
  'no-conversation-assistant-barrel-imports': noConversationAssistantBarrelImports,
98
101
  'visit-example-type-import-required': visitExampleTypeImportRequired,
102
+ 'no-xcss-in-cx': noXcssInCx,
99
103
  'ensure-use-sync-external-store-server-snapshot': ensureUseSyncExternalStoreServerSnapshot
100
104
  };
101
105
  var commonConfig = {
@@ -108,10 +112,12 @@ var commonConfig = {
108
112
  '@atlaskit/platform/no-module-level-eval-nav4': 'error',
109
113
  '@atlaskit/platform/no-direct-document-usage': 'warn',
110
114
  '@atlaskit/platform/no-set-immediate': 'error',
115
+ '@atlaskit/platform/no-xcss-in-cx': 'error',
111
116
  // Compiled: rules that are not included via `@compiled/recommended
112
117
  '@atlaskit/platform/expand-border-shorthand': 'error',
113
118
  '@atlaskit/platform/expand-background-shorthand': 'error',
114
119
  '@atlaskit/platform/expand-spacing-shorthand': 'error',
120
+ '@atlaskit/platform/no-css-prop-in-object-spread': 'error',
115
121
  '@compiled/jsx-pragma': ['error', {
116
122
  importSources: ['@atlaskit/css'],
117
123
  onlyRunIfImportingCompiled: true,
@@ -153,7 +159,6 @@ var plugin = {
153
159
  get '@atlaskit/platform'() {
154
160
  return plugin;
155
161
  },
156
- // @ts-expect-error there's an issue with the types for @compiled/eslint-plugin ('no-css-prop-without-css-function' specifically)
157
162
  '@compiled': {
158
163
  meta: compiledPlugin.meta,
159
164
  rules: compiledPlugin.rules
@@ -170,7 +175,6 @@ var plugin = {
170
175
  get '@atlaskit/platform'() {
171
176
  return plugin;
172
177
  },
173
- // @ts-expect-error there's an issue with the types for @compiled/eslint-plugin ('no-css-prop-without-css-function' specifically)
174
178
  '@compiled': {
175
179
  meta: compiledPlugin.meta,
176
180
  rules: compiledPlugin.rules
@@ -0,0 +1,156 @@
1
+ import _defineProperty from "@babel/runtime/helpers/defineProperty";
2
+ function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
3
+ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
4
+ 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; } } }; }
5
+ 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; } }
6
+ 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; }
7
+ import { getScope, getSourceCode } from '../../util/context-compat';
8
+
9
+ /**
10
+ * Returns the `css` Property node from an ObjectExpression, or null if not found.
11
+ */
12
+ function getCssProperty(objectExpression) {
13
+ var _iterator = _createForOfIteratorHelper(objectExpression.properties),
14
+ _step;
15
+ try {
16
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
17
+ var prop = _step.value;
18
+ if (prop.type !== 'Property') {
19
+ continue;
20
+ }
21
+ var key = prop.key;
22
+ if (key.type === 'Identifier' && key.name === 'css' || key.type === 'Literal' && key.value === 'css') {
23
+ return prop;
24
+ }
25
+ }
26
+ } catch (err) {
27
+ _iterator.e(err);
28
+ } finally {
29
+ _iterator.f();
30
+ }
31
+ return null;
32
+ }
33
+ export var noCssPropInObjectSpread = {
34
+ meta: {
35
+ docs: {
36
+ url: 'https://bitbucket.org/atlassian/atlassian-frontend-monorepo/src/master/platform/packages/platform/eslint-plugin/src/rules/compiled/no-css-prop-in-object-spread/',
37
+ description: 'Disallows `css` property inside objects spread into JSX — the Compiled JSX pragma ignores it'
38
+ },
39
+ fixable: 'code',
40
+ messages: {
41
+ noCssPropInObjectSpread: 'The `css` property inside an object spread into JSX is a no-op. The Compiled JSX pragma only processes `css` as a direct JSX attribute. Move `css` out of the spread: <El css={...} />'
42
+ },
43
+ type: 'problem'
44
+ },
45
+ create: function create(context) {
46
+ return {
47
+ JSXSpreadAttribute: function JSXSpreadAttribute(node) {
48
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
49
+ var spreadNode = node;
50
+ var arg = spreadNode.argument;
51
+
52
+ // Case 1: inline object literal — <div {...{ css: styles, id: 'foo' }} />
53
+ if (arg.type === 'ObjectExpression') {
54
+ var objectArg = arg;
55
+ var cssProp = getCssProperty(objectArg);
56
+ if (!cssProp) {
57
+ return;
58
+ }
59
+ context.report({
60
+ node: node,
61
+ messageId: 'noCssPropInObjectSpread',
62
+ fix: function fix(fixer) {
63
+ var sourceCode = getSourceCode(context);
64
+ var cssValueText = sourceCode.getText(cssProp.value);
65
+ var remainingProps = objectArg.properties.filter(function (p) {
66
+ return p !== cssProp;
67
+ });
68
+ var directCssProp = "css={".concat(cssValueText, "}");
69
+ if (remainingProps.length === 0) {
70
+ return fixer.replaceText(node, directCssProp);
71
+ }
72
+ var remainingText = remainingProps.map(function (p) {
73
+ return sourceCode.getText(p);
74
+ }).join(', ');
75
+ return fixer.replaceText(node, "".concat(directCssProp, " {...{ ").concat(remainingText, " }}"));
76
+ }
77
+ });
78
+ return;
79
+ }
80
+
81
+ // Case 2: variable reference — <div {...props} />
82
+ if (arg.type === 'Identifier') {
83
+ var scope = getScope(context, arg);
84
+ var currentScope = scope;
85
+ var variable = null;
86
+ while (currentScope) {
87
+ var found = currentScope.variables.find(function (v) {
88
+ return v.name === arg.name;
89
+ });
90
+ if (found) {
91
+ variable = found;
92
+ break;
93
+ }
94
+ currentScope = currentScope.upper;
95
+ }
96
+ if (!variable || variable.defs.length === 0) {
97
+ return;
98
+ }
99
+ var def = variable.defs[0];
100
+ if (def.type !== 'Variable' || !def.node.init || def.node.init.type !== 'ObjectExpression') {
101
+ return;
102
+ }
103
+ var initObject = def.node.init;
104
+ var _cssProp = getCssProperty(initObject);
105
+ if (!_cssProp) {
106
+ return;
107
+ }
108
+
109
+ // Only auto-fix when there is exactly one JSX spread site for this variable
110
+ var spreadCount = variable.references.filter(function (ref) {
111
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
112
+ var refParent = ref.identifier.parent;
113
+ return (refParent === null || refParent === void 0 ? void 0 : refParent.type) === 'JSXSpreadAttribute';
114
+ }).length;
115
+ context.report(_objectSpread({
116
+ node: node,
117
+ messageId: 'noCssPropInObjectSpread'
118
+ }, spreadCount === 1 ? {
119
+ fix: function fix(fixer) {
120
+ var sourceCode = getSourceCode(context);
121
+ var cssValueText = sourceCode.getText(_cssProp.value);
122
+ var fixes = [];
123
+ var remainingProps = initObject.properties.filter(function (p) {
124
+ return p !== _cssProp;
125
+ });
126
+ if (remainingProps.length === 0) {
127
+ fixes.push(fixer.replaceText(initObject, '{}'));
128
+ } else {
129
+ var propIndex = initObject.properties.indexOf(_cssProp);
130
+ var isLast = propIndex === initObject.properties.length - 1;
131
+ var tokenBefore = sourceCode.getTokenBefore(_cssProp);
132
+ var tokenAfter = sourceCode.getTokenAfter(_cssProp);
133
+ if (!isLast && tokenAfter && tokenAfter.value === ',') {
134
+ var src = sourceCode.getText();
135
+ var afterEnd = tokenAfter.range[1];
136
+ var end = afterEnd;
137
+ while (end < src.length && src[end] === ' ') {
138
+ end++;
139
+ }
140
+ fixes.push(fixer.removeRange([_cssProp.range[0], end]));
141
+ } else if (tokenBefore && tokenBefore.value === ',') {
142
+ fixes.push(fixer.removeRange([tokenBefore.range[0], _cssProp.range[1]]));
143
+ } else {
144
+ fixes.push(fixer.remove(_cssProp));
145
+ }
146
+ }
147
+ fixes.push(fixer.insertTextBefore(node, "css={".concat(cssValueText, "} ")));
148
+ return fixes;
149
+ }
150
+ } : {}));
151
+ }
152
+ }
153
+ };
154
+ }
155
+ };
156
+ export default noCssPropInObjectSpread;