@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.
- package/CHANGELOG.md +10 -0
- package/dist/cjs/index.js +6 -2
- package/dist/cjs/rules/compiled/no-css-prop-in-object-spread/index.js +162 -0
- package/dist/cjs/rules/no-xcss-in-cx/index.js +221 -0
- package/dist/cjs/rules/visit-example-type-import-required/index.js +23 -13
- package/dist/es2019/index.js +6 -2
- package/dist/es2019/rules/compiled/no-css-prop-in-object-spread/index.js +136 -0
- package/dist/es2019/rules/no-xcss-in-cx/index.js +187 -0
- package/dist/es2019/rules/visit-example-type-import-required/index.js +23 -14
- package/dist/esm/index.js +6 -2
- package/dist/esm/rules/compiled/no-css-prop-in-object-spread/index.js +156 -0
- package/dist/esm/rules/no-xcss-in-cx/index.js +216 -0
- package/dist/esm/rules/visit-example-type-import-required/index.js +23 -13
- package/dist/types/index.d.ts +278 -243
- package/dist/types/rules/compiled/no-css-prop-in-object-spread/index.d.ts +3 -0
- package/dist/types/rules/no-xcss-in-cx/index.d.ts +31 -0
- package/dist/types-ts4.5/index.d.ts +222 -211
- package/dist/types-ts4.5/rules/compiled/no-css-prop-in-object-spread/index.d.ts +3 -0
- package/dist/types-ts4.5/rules/no-xcss-in-cx/index.d.ts +31 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# @atlaskit/eslint-plugin-platform
|
|
2
2
|
|
|
3
|
+
## 2.9.2
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [`48fa19b8ba41a`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/48fa19b8ba41a) -
|
|
8
|
+
Add `no-xcss-in-cx` ESLint rule to prevent `xcss()` from `@atlaskit/primitives` being called
|
|
9
|
+
inline inside `cx()` in an `xcss` prop. Calling `xcss()` inline produces a StyleRule opaque object
|
|
10
|
+
rather than a Compiled atomic class string, which causes runtime errors. The rule is import-aware
|
|
11
|
+
and recurses into logical (`&&`) and conditional (`?:`) expressions.
|
|
12
|
+
|
|
3
13
|
## 2.9.1
|
|
4
14
|
|
|
5
15
|
### Patch Changes
|
package/dist/cjs/index.js
CHANGED
|
@@ -32,6 +32,7 @@ var _useRecommendedUtils = _interopRequireDefault(require("./rules/feature-gatin
|
|
|
32
32
|
var _validGateName = _interopRequireDefault(require("./rules/feature-gating/valid-gate-name"));
|
|
33
33
|
var _expandBackgroundShorthand = _interopRequireDefault(require("./rules/compiled/expand-background-shorthand"));
|
|
34
34
|
var _expandSpacingShorthand = _interopRequireDefault(require("./rules/compiled/expand-spacing-shorthand"));
|
|
35
|
+
var _noCssPropInObjectSpread = _interopRequireDefault(require("./rules/compiled/no-css-prop-in-object-spread"));
|
|
35
36
|
var _noSparseCheckout = _interopRequireDefault(require("./rules/no-sparse-checkout"));
|
|
36
37
|
var _noDirectDocumentUsage = _interopRequireDefault(require("./rules/no-direct-document-usage"));
|
|
37
38
|
var _noSetImmediate = _interopRequireDefault(require("./rules/no-set-immediate"));
|
|
@@ -44,6 +45,7 @@ var _noRelativeBarrelFileImports = _interopRequireDefault(require("./rules/impor
|
|
|
44
45
|
var _noConversationAssistantBarrelImports = _interopRequireDefault(require("./rules/import/no-conversation-assistant-barrel-imports"));
|
|
45
46
|
var _visitExampleTypeImportRequired = _interopRequireDefault(require("./rules/visit-example-type-import-required"));
|
|
46
47
|
var _ensureUseSyncExternalStoreServerSnapshot = _interopRequireDefault(require("./rules/ensure-use-sync-external-store-server-snapshot"));
|
|
48
|
+
var _noXcssInCx = _interopRequireDefault(require("./rules/no-xcss-in-cx"));
|
|
47
49
|
var _nodePath = require("node:path");
|
|
48
50
|
var _nodeFs = require("node:fs");
|
|
49
51
|
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; }
|
|
@@ -76,6 +78,7 @@ var rules = exports.rules = {
|
|
|
76
78
|
'expand-border-shorthand': _expandBorderShorthand.default,
|
|
77
79
|
'expand-background-shorthand': _expandBackgroundShorthand.default,
|
|
78
80
|
'expand-spacing-shorthand': _expandSpacingShorthand.default,
|
|
81
|
+
'no-css-prop-in-object-spread': _noCssPropInObjectSpread.default,
|
|
79
82
|
'no-duplicate-dependencies': _noDuplicateDependencies.default,
|
|
80
83
|
'no-invalid-feature-flag-usage': _noInvalidFeatureFlagUsage.default,
|
|
81
84
|
'no-pre-post-install-scripts': _noPrePostInstalls.default,
|
|
@@ -102,6 +105,7 @@ var rules = exports.rules = {
|
|
|
102
105
|
'no-relative-barrel-file-imports': _noRelativeBarrelFileImports.default,
|
|
103
106
|
'no-conversation-assistant-barrel-imports': _noConversationAssistantBarrelImports.default,
|
|
104
107
|
'visit-example-type-import-required': _visitExampleTypeImportRequired.default,
|
|
108
|
+
'no-xcss-in-cx': _noXcssInCx.default,
|
|
105
109
|
'ensure-use-sync-external-store-server-snapshot': _ensureUseSyncExternalStoreServerSnapshot.default
|
|
106
110
|
};
|
|
107
111
|
var commonConfig = {
|
|
@@ -114,10 +118,12 @@ var commonConfig = {
|
|
|
114
118
|
'@atlaskit/platform/no-module-level-eval-nav4': 'error',
|
|
115
119
|
'@atlaskit/platform/no-direct-document-usage': 'warn',
|
|
116
120
|
'@atlaskit/platform/no-set-immediate': 'error',
|
|
121
|
+
'@atlaskit/platform/no-xcss-in-cx': 'error',
|
|
117
122
|
// Compiled: rules that are not included via `@compiled/recommended
|
|
118
123
|
'@atlaskit/platform/expand-border-shorthand': 'error',
|
|
119
124
|
'@atlaskit/platform/expand-background-shorthand': 'error',
|
|
120
125
|
'@atlaskit/platform/expand-spacing-shorthand': 'error',
|
|
126
|
+
'@atlaskit/platform/no-css-prop-in-object-spread': 'error',
|
|
121
127
|
'@compiled/jsx-pragma': ['error', {
|
|
122
128
|
importSources: ['@atlaskit/css'],
|
|
123
129
|
onlyRunIfImportingCompiled: true,
|
|
@@ -159,7 +165,6 @@ var plugin = exports.plugin = {
|
|
|
159
165
|
get '@atlaskit/platform'() {
|
|
160
166
|
return plugin;
|
|
161
167
|
},
|
|
162
|
-
// @ts-expect-error there's an issue with the types for @compiled/eslint-plugin ('no-css-prop-without-css-function' specifically)
|
|
163
168
|
'@compiled': {
|
|
164
169
|
meta: _eslintPlugin.default.meta,
|
|
165
170
|
rules: _eslintPlugin.default.rules
|
|
@@ -176,7 +181,6 @@ var plugin = exports.plugin = {
|
|
|
176
181
|
get '@atlaskit/platform'() {
|
|
177
182
|
return plugin;
|
|
178
183
|
},
|
|
179
|
-
// @ts-expect-error there's an issue with the types for @compiled/eslint-plugin ('no-css-prop-without-css-function' specifically)
|
|
180
184
|
'@compiled': {
|
|
181
185
|
meta: _eslintPlugin.default.meta,
|
|
182
186
|
rules: _eslintPlugin.default.rules
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
4
|
+
Object.defineProperty(exports, "__esModule", {
|
|
5
|
+
value: true
|
|
6
|
+
});
|
|
7
|
+
exports.noCssPropInObjectSpread = exports.default = void 0;
|
|
8
|
+
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
|
|
9
|
+
var _contextCompat = require("../../util/context-compat");
|
|
10
|
+
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; }
|
|
11
|
+
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) { (0, _defineProperty2.default)(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; }
|
|
12
|
+
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; } } }; }
|
|
13
|
+
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; } }
|
|
14
|
+
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; }
|
|
15
|
+
/**
|
|
16
|
+
* Returns the `css` Property node from an ObjectExpression, or null if not found.
|
|
17
|
+
*/
|
|
18
|
+
function getCssProperty(objectExpression) {
|
|
19
|
+
var _iterator = _createForOfIteratorHelper(objectExpression.properties),
|
|
20
|
+
_step;
|
|
21
|
+
try {
|
|
22
|
+
for (_iterator.s(); !(_step = _iterator.n()).done;) {
|
|
23
|
+
var prop = _step.value;
|
|
24
|
+
if (prop.type !== 'Property') {
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
var key = prop.key;
|
|
28
|
+
if (key.type === 'Identifier' && key.name === 'css' || key.type === 'Literal' && key.value === 'css') {
|
|
29
|
+
return prop;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
} catch (err) {
|
|
33
|
+
_iterator.e(err);
|
|
34
|
+
} finally {
|
|
35
|
+
_iterator.f();
|
|
36
|
+
}
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
var noCssPropInObjectSpread = exports.noCssPropInObjectSpread = {
|
|
40
|
+
meta: {
|
|
41
|
+
docs: {
|
|
42
|
+
url: 'https://bitbucket.org/atlassian/atlassian-frontend-monorepo/src/master/platform/packages/platform/eslint-plugin/src/rules/compiled/no-css-prop-in-object-spread/',
|
|
43
|
+
description: 'Disallows `css` property inside objects spread into JSX — the Compiled JSX pragma ignores it'
|
|
44
|
+
},
|
|
45
|
+
fixable: 'code',
|
|
46
|
+
messages: {
|
|
47
|
+
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={...} />'
|
|
48
|
+
},
|
|
49
|
+
type: 'problem'
|
|
50
|
+
},
|
|
51
|
+
create: function create(context) {
|
|
52
|
+
return {
|
|
53
|
+
JSXSpreadAttribute: function JSXSpreadAttribute(node) {
|
|
54
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
55
|
+
var spreadNode = node;
|
|
56
|
+
var arg = spreadNode.argument;
|
|
57
|
+
|
|
58
|
+
// Case 1: inline object literal — <div {...{ css: styles, id: 'foo' }} />
|
|
59
|
+
if (arg.type === 'ObjectExpression') {
|
|
60
|
+
var objectArg = arg;
|
|
61
|
+
var cssProp = getCssProperty(objectArg);
|
|
62
|
+
if (!cssProp) {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
context.report({
|
|
66
|
+
node: node,
|
|
67
|
+
messageId: 'noCssPropInObjectSpread',
|
|
68
|
+
fix: function fix(fixer) {
|
|
69
|
+
var sourceCode = (0, _contextCompat.getSourceCode)(context);
|
|
70
|
+
var cssValueText = sourceCode.getText(cssProp.value);
|
|
71
|
+
var remainingProps = objectArg.properties.filter(function (p) {
|
|
72
|
+
return p !== cssProp;
|
|
73
|
+
});
|
|
74
|
+
var directCssProp = "css={".concat(cssValueText, "}");
|
|
75
|
+
if (remainingProps.length === 0) {
|
|
76
|
+
return fixer.replaceText(node, directCssProp);
|
|
77
|
+
}
|
|
78
|
+
var remainingText = remainingProps.map(function (p) {
|
|
79
|
+
return sourceCode.getText(p);
|
|
80
|
+
}).join(', ');
|
|
81
|
+
return fixer.replaceText(node, "".concat(directCssProp, " {...{ ").concat(remainingText, " }}"));
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Case 2: variable reference — <div {...props} />
|
|
88
|
+
if (arg.type === 'Identifier') {
|
|
89
|
+
var scope = (0, _contextCompat.getScope)(context, arg);
|
|
90
|
+
var currentScope = scope;
|
|
91
|
+
var variable = null;
|
|
92
|
+
while (currentScope) {
|
|
93
|
+
var found = currentScope.variables.find(function (v) {
|
|
94
|
+
return v.name === arg.name;
|
|
95
|
+
});
|
|
96
|
+
if (found) {
|
|
97
|
+
variable = found;
|
|
98
|
+
break;
|
|
99
|
+
}
|
|
100
|
+
currentScope = currentScope.upper;
|
|
101
|
+
}
|
|
102
|
+
if (!variable || variable.defs.length === 0) {
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
var def = variable.defs[0];
|
|
106
|
+
if (def.type !== 'Variable' || !def.node.init || def.node.init.type !== 'ObjectExpression') {
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
var initObject = def.node.init;
|
|
110
|
+
var _cssProp = getCssProperty(initObject);
|
|
111
|
+
if (!_cssProp) {
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Only auto-fix when there is exactly one JSX spread site for this variable
|
|
116
|
+
var spreadCount = variable.references.filter(function (ref) {
|
|
117
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
118
|
+
var refParent = ref.identifier.parent;
|
|
119
|
+
return (refParent === null || refParent === void 0 ? void 0 : refParent.type) === 'JSXSpreadAttribute';
|
|
120
|
+
}).length;
|
|
121
|
+
context.report(_objectSpread({
|
|
122
|
+
node: node,
|
|
123
|
+
messageId: 'noCssPropInObjectSpread'
|
|
124
|
+
}, spreadCount === 1 ? {
|
|
125
|
+
fix: function fix(fixer) {
|
|
126
|
+
var sourceCode = (0, _contextCompat.getSourceCode)(context);
|
|
127
|
+
var cssValueText = sourceCode.getText(_cssProp.value);
|
|
128
|
+
var fixes = [];
|
|
129
|
+
var remainingProps = initObject.properties.filter(function (p) {
|
|
130
|
+
return p !== _cssProp;
|
|
131
|
+
});
|
|
132
|
+
if (remainingProps.length === 0) {
|
|
133
|
+
fixes.push(fixer.replaceText(initObject, '{}'));
|
|
134
|
+
} else {
|
|
135
|
+
var propIndex = initObject.properties.indexOf(_cssProp);
|
|
136
|
+
var isLast = propIndex === initObject.properties.length - 1;
|
|
137
|
+
var tokenBefore = sourceCode.getTokenBefore(_cssProp);
|
|
138
|
+
var tokenAfter = sourceCode.getTokenAfter(_cssProp);
|
|
139
|
+
if (!isLast && tokenAfter && tokenAfter.value === ',') {
|
|
140
|
+
var src = sourceCode.getText();
|
|
141
|
+
var afterEnd = tokenAfter.range[1];
|
|
142
|
+
var end = afterEnd;
|
|
143
|
+
while (end < src.length && src[end] === ' ') {
|
|
144
|
+
end++;
|
|
145
|
+
}
|
|
146
|
+
fixes.push(fixer.removeRange([_cssProp.range[0], end]));
|
|
147
|
+
} else if (tokenBefore && tokenBefore.value === ',') {
|
|
148
|
+
fixes.push(fixer.removeRange([tokenBefore.range[0], _cssProp.range[1]]));
|
|
149
|
+
} else {
|
|
150
|
+
fixes.push(fixer.remove(_cssProp));
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
fixes.push(fixer.insertTextBefore(node, "css={".concat(cssValueText, "} ")));
|
|
154
|
+
return fixes;
|
|
155
|
+
}
|
|
156
|
+
} : {}));
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
var _default = exports.default = noCssPropInObjectSpread;
|
|
@@ -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;
|
|
@@ -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
|
-
|
|
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[
|
|
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
|
-
//
|
|
184
|
-
|
|
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
|
-
|
|
283
|
-
|
|
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 =
|
|
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;
|
package/dist/es2019/index.js
CHANGED
|
@@ -25,6 +25,7 @@ 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';
|
|
@@ -37,6 +38,7 @@ import noRelativeBarrelFileImports from './rules/import/no-relative-barrel-file-
|
|
|
37
38
|
import noConversationAssistantBarrelImports from './rules/import/no-conversation-assistant-barrel-imports';
|
|
38
39
|
import visitExampleTypeImportRequired from './rules/visit-example-type-import-required';
|
|
39
40
|
import ensureUseSyncExternalStoreServerSnapshot from './rules/ensure-use-sync-external-store-server-snapshot';
|
|
41
|
+
import noXcssInCx from './rules/no-xcss-in-cx';
|
|
40
42
|
import { join, normalize } from 'node:path';
|
|
41
43
|
import { readFileSync } from 'node:fs';
|
|
42
44
|
let jiraRoot;
|
|
@@ -67,6 +69,7 @@ const rules = {
|
|
|
67
69
|
'expand-border-shorthand': expandBorderShorthand,
|
|
68
70
|
'expand-background-shorthand': expandBackgroundShorthand,
|
|
69
71
|
'expand-spacing-shorthand': expandSpacingShorthand,
|
|
72
|
+
'no-css-prop-in-object-spread': noCssPropInObjectSpread,
|
|
70
73
|
'no-duplicate-dependencies': noDuplicateDependencies,
|
|
71
74
|
'no-invalid-feature-flag-usage': noInvalidFeatureFlagUsage,
|
|
72
75
|
'no-pre-post-install-scripts': noPreAndPostInstallScripts,
|
|
@@ -93,6 +96,7 @@ const rules = {
|
|
|
93
96
|
'no-relative-barrel-file-imports': noRelativeBarrelFileImports,
|
|
94
97
|
'no-conversation-assistant-barrel-imports': noConversationAssistantBarrelImports,
|
|
95
98
|
'visit-example-type-import-required': visitExampleTypeImportRequired,
|
|
99
|
+
'no-xcss-in-cx': noXcssInCx,
|
|
96
100
|
'ensure-use-sync-external-store-server-snapshot': ensureUseSyncExternalStoreServerSnapshot
|
|
97
101
|
};
|
|
98
102
|
const commonConfig = {
|
|
@@ -105,10 +109,12 @@ const commonConfig = {
|
|
|
105
109
|
'@atlaskit/platform/no-module-level-eval-nav4': 'error',
|
|
106
110
|
'@atlaskit/platform/no-direct-document-usage': 'warn',
|
|
107
111
|
'@atlaskit/platform/no-set-immediate': 'error',
|
|
112
|
+
'@atlaskit/platform/no-xcss-in-cx': 'error',
|
|
108
113
|
// Compiled: rules that are not included via `@compiled/recommended
|
|
109
114
|
'@atlaskit/platform/expand-border-shorthand': 'error',
|
|
110
115
|
'@atlaskit/platform/expand-background-shorthand': 'error',
|
|
111
116
|
'@atlaskit/platform/expand-spacing-shorthand': 'error',
|
|
117
|
+
'@atlaskit/platform/no-css-prop-in-object-spread': 'error',
|
|
112
118
|
'@compiled/jsx-pragma': ['error', {
|
|
113
119
|
importSources: ['@atlaskit/css'],
|
|
114
120
|
onlyRunIfImportingCompiled: true,
|
|
@@ -153,7 +159,6 @@ const 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 @@ const 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
|