@atlaskit/eslint-plugin-design-system 8.32.0 → 8.32.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -7,6 +7,8 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
7
7
|
exports.default = void 0;
|
|
8
8
|
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
|
|
9
9
|
var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
|
|
10
|
+
var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
|
|
11
|
+
var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
|
|
10
12
|
var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
|
|
11
13
|
var _eslintCodemodUtils = require("eslint-codemod-utils");
|
|
12
14
|
var _estraverse = _interopRequireDefault(require("estraverse"));
|
|
@@ -18,8 +20,6 @@ var _getImportNodeBySource = require("../utils/get-import-node-by-source");
|
|
|
18
20
|
var _isSupportedImport = require("../utils/is-supported-import");
|
|
19
21
|
// eslint-disable-next-line import/no-extraneous-dependencies
|
|
20
22
|
|
|
21
|
-
// File-level tracking of styles hoisted from the cssAtTopOfModule/cssAtBottomOfModule fixers
|
|
22
|
-
var hoistedCss = [];
|
|
23
23
|
var isDOMElementName = function isDOMElementName(elementName) {
|
|
24
24
|
return elementName.charAt(0) !== elementName.charAt(0).toUpperCase() && elementName.charAt(0) === elementName.charAt(0).toLowerCase();
|
|
25
25
|
};
|
|
@@ -40,365 +40,408 @@ var getProgramNode = function getProgramNode(expression) {
|
|
|
40
40
|
}
|
|
41
41
|
return expression.parent;
|
|
42
42
|
};
|
|
43
|
+
var JSXExpressionLinter = /*#__PURE__*/function () {
|
|
44
|
+
// File-level tracking of styles hoisted from the cssAtTopOfModule/cssAtBottomOfModule fixers.
|
|
43
45
|
|
|
44
|
-
/**
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
46
|
+
/**
|
|
47
|
+
* Traverses and lints a expression found in a JSX css or xcss prop, e.g.
|
|
48
|
+
* <div css={expressionToLint} />
|
|
49
|
+
*
|
|
50
|
+
* @param context The context of the rule. Used to find the current scope and the source code of the file.
|
|
51
|
+
* @param cssAttributeName Used to encapsulate ObjectExpressions when cssAtTopOfModule/cssAtBottomOfModule violations are triggered.
|
|
52
|
+
* @param configuration What css-related functions to account for (eg. css, xcss, cssMap), and whether to detect bottom vs top expressions.
|
|
53
|
+
* @param expression The expression to traverse and lint.
|
|
54
|
+
*/
|
|
55
|
+
function JSXExpressionLinter(context, cssAttributeName, configuration, expression) {
|
|
56
|
+
(0, _classCallCheck2.default)(this, JSXExpressionLinter);
|
|
57
|
+
this.context = context;
|
|
58
|
+
this.cssAttributeName = cssAttributeName;
|
|
59
|
+
this.configuration = configuration;
|
|
60
|
+
this.expression = expression;
|
|
61
|
+
this.hoistedCss = [];
|
|
56
62
|
}
|
|
57
|
-
var variables = scope.variables.map(function (variable) {
|
|
58
|
-
return variable.name;
|
|
59
|
-
}).concat(hoistedCss);
|
|
60
|
-
var count = 2;
|
|
61
|
-
var declaratorName = 'styles';
|
|
62
63
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
64
|
+
/**
|
|
65
|
+
* Generates the declarator string when fixing the cssAtTopOfModule/cssAtBottomOfModule cases.
|
|
66
|
+
* When `styles` already exists, `styles_1, styles_2, ..., styles_X` are incrementally created for each unhoisted style.
|
|
67
|
+
*
|
|
68
|
+
* The generated `styles` varibale declaration names must be manually modified to be more informative at the discretion of owning teams.
|
|
69
|
+
*/
|
|
70
|
+
(0, _createClass2.default)(JSXExpressionLinter, [{
|
|
71
|
+
key: "getDeclaratorString",
|
|
72
|
+
value: function getDeclaratorString() {
|
|
73
|
+
var scope = this.context.getScope();
|
|
72
74
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
messageId: 'cssInModule'
|
|
92
|
-
});
|
|
93
|
-
return;
|
|
94
|
-
}
|
|
95
|
-
if (identifier.parent.parent.parent.type !== 'Program') {
|
|
96
|
-
// When variable is declared inside the component
|
|
97
|
-
context.report({
|
|
98
|
-
node: sourceIdentifier,
|
|
99
|
-
messageId: configuration.stylesPlacement === 'bottom' ? 'cssAtBottomOfModule' : 'cssAtTopOfModule',
|
|
100
|
-
fix: function fix(fixer) {
|
|
101
|
-
if (configuration.fixNamesOnly) {
|
|
102
|
-
return [];
|
|
75
|
+
// Get to ModuleScope
|
|
76
|
+
while (scope && scope.upper && scope.upper.type !== 'global') {
|
|
77
|
+
var _scope;
|
|
78
|
+
scope = (_scope = scope) === null || _scope === void 0 ? void 0 : _scope.upper;
|
|
79
|
+
}
|
|
80
|
+
var variables = scope.variables.map(function (variable) {
|
|
81
|
+
return variable.name;
|
|
82
|
+
}).concat(this.hoistedCss);
|
|
83
|
+
var count = 2;
|
|
84
|
+
var declaratorName = 'styles';
|
|
85
|
+
|
|
86
|
+
// Base case
|
|
87
|
+
if (!variables.includes(declaratorName)) {
|
|
88
|
+
return declaratorName;
|
|
89
|
+
} else {
|
|
90
|
+
// If styles already exists, increment the number
|
|
91
|
+
while (variables.includes("".concat(declaratorName).concat(count))) {
|
|
92
|
+
count++;
|
|
103
93
|
}
|
|
104
|
-
return fixCssNotInModuleScope(fixer, context, configuration, identifier);
|
|
105
94
|
}
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
// When variable value is not of type css({})
|
|
111
|
-
var value = identifier.parent.init;
|
|
112
|
-
if (!value) {
|
|
113
|
-
return;
|
|
95
|
+
|
|
96
|
+
// Keep track of it by adding it to the hoistedCss global array
|
|
97
|
+
this.hoistedCss = [].concat((0, _toConsumableArray2.default)(this.hoistedCss), ["".concat(declaratorName).concat(count)]);
|
|
98
|
+
return "".concat(declaratorName).concat(count);
|
|
114
99
|
}
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
value
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
100
|
+
}, {
|
|
101
|
+
key: "analyzeIdentifier",
|
|
102
|
+
value: function analyzeIdentifier(sourceIdentifier) {
|
|
103
|
+
var _getIdentifierInParen,
|
|
104
|
+
_getIdentifierInParen2,
|
|
105
|
+
_this = this;
|
|
106
|
+
var scope = this.context.getScope();
|
|
107
|
+
var _ref = (_getIdentifierInParen = (_getIdentifierInParen2 = (0, _eslintCodemodUtils.getIdentifierInParentScope)(scope, sourceIdentifier.name)) === null || _getIdentifierInParen2 === void 0 ? void 0 : _getIdentifierInParen2.identifiers) !== null && _getIdentifierInParen !== void 0 ? _getIdentifierInParen : [],
|
|
108
|
+
_ref2 = (0, _slicedToArray2.default)(_ref, 1),
|
|
109
|
+
identifier = _ref2[0];
|
|
110
|
+
if (!identifier || !identifier.parent) {
|
|
111
|
+
// Identifier isn't in the module, skip!
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
if (identifier.parent.type !== 'VariableDeclarator') {
|
|
115
|
+
// When variable is not in the file or coming from import
|
|
116
|
+
this.context.report({
|
|
117
|
+
node: sourceIdentifier,
|
|
118
|
+
messageId: 'cssInModule'
|
|
119
|
+
});
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
if (identifier.parent.parent.parent.type !== 'Program') {
|
|
123
|
+
// When variable is declared inside the component
|
|
124
|
+
this.context.report({
|
|
125
|
+
node: sourceIdentifier,
|
|
126
|
+
messageId: this.configuration.stylesPlacement === 'bottom' ? 'cssAtBottomOfModule' : 'cssAtTopOfModule',
|
|
127
|
+
fix: function fix(fixer) {
|
|
128
|
+
if (_this.configuration.fixNamesOnly) {
|
|
129
|
+
return [];
|
|
130
|
+
}
|
|
131
|
+
return _this.fixCssNotInModuleScope(fixer, identifier, false);
|
|
125
132
|
}
|
|
126
|
-
|
|
133
|
+
});
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
if (identifier.parent && identifier.parent.init && !isCssCallExpression(identifier.parent.init, this.configuration.cssFunctions)) {
|
|
137
|
+
// When variable value is not of type css({})
|
|
138
|
+
var value = identifier.parent.init;
|
|
139
|
+
if (!value) {
|
|
140
|
+
return;
|
|
127
141
|
}
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
142
|
+
var valueExpression =
|
|
143
|
+
// @ts-expect-error remove once eslint types are switched to @typescript-eslint
|
|
144
|
+
value.type === 'TSAsExpression' ? value.expression : value;
|
|
145
|
+
if (['ObjectExpression', 'TemplateLiteral'].includes(valueExpression.type)) {
|
|
146
|
+
this.context.report({
|
|
147
|
+
node: identifier,
|
|
148
|
+
messageId: 'cssObjectTypeOnly',
|
|
149
|
+
fix: function fix(fixer) {
|
|
150
|
+
if (_this.configuration.fixNamesOnly) {
|
|
151
|
+
return [];
|
|
152
|
+
}
|
|
153
|
+
return _this.addCssFunctionCall(fixer, identifier.parent);
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
} else {
|
|
157
|
+
this.context.report({
|
|
158
|
+
node: identifier,
|
|
159
|
+
messageId: 'cssObjectTypeOnly'
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
var spreadProperties = (0, _eslintCodemodUtils.isNodeOfType)(identifier.parent.init, 'CallExpression') && findSpreadProperties(identifier.parent.init.arguments[0]);
|
|
165
|
+
if (spreadProperties) {
|
|
166
|
+
// TODO: Recursively handle spread items in children properties.
|
|
167
|
+
spreadProperties.forEach(function (prop) {
|
|
168
|
+
_this.context.report({
|
|
169
|
+
node: prop,
|
|
170
|
+
messageId: 'cssArrayStylesOnly'
|
|
171
|
+
});
|
|
172
|
+
});
|
|
173
|
+
}
|
|
134
174
|
}
|
|
135
|
-
return;
|
|
136
|
-
}
|
|
137
|
-
var spreadProperties = (0, _eslintCodemodUtils.isNodeOfType)(identifier.parent.init, 'CallExpression') && findSpreadProperties(identifier.parent.init.arguments[0]);
|
|
138
|
-
if (spreadProperties) {
|
|
139
|
-
// TODO: Recursively handle spread items in children properties.
|
|
140
|
-
spreadProperties.forEach(function (prop) {
|
|
141
|
-
context.report({
|
|
142
|
-
node: prop,
|
|
143
|
-
messageId: 'cssArrayStylesOnly'
|
|
144
|
-
});
|
|
145
|
-
});
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
175
|
|
|
149
|
-
/**
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
176
|
+
/**
|
|
177
|
+
* Returns a fixer that adds `import { css } from 'import-source'` or
|
|
178
|
+
* `import { xcss } from 'import-source'` to the start of the file, depending
|
|
179
|
+
* on the value of cssAttributeName and importSource.
|
|
180
|
+
*/
|
|
181
|
+
}, {
|
|
182
|
+
key: "addImportSource",
|
|
183
|
+
value: function addImportSource(fixer) {
|
|
184
|
+
var importSource = this.cssAttributeName === 'xcss' ? this.configuration.xcssImportSource : this.configuration.cssImportSource;
|
|
156
185
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
186
|
+
// Add the `import { css } from 'my-css-in-js-library';` statement
|
|
187
|
+
var packageImport = (0, _getFirstSupportedImport.getFirstSupportedImport)(this.context, [importSource]);
|
|
188
|
+
if (packageImport) {
|
|
189
|
+
var addCssImport = _astNodes.Import.insertNamedSpecifiers(packageImport, [this.cssAttributeName], fixer);
|
|
190
|
+
if (addCssImport) {
|
|
191
|
+
return addCssImport;
|
|
192
|
+
}
|
|
193
|
+
} else {
|
|
194
|
+
return (0, _eslintCodemodUtils.insertAtStartOfFile)(fixer, "".concat((0, _eslintCodemodUtils.insertImportDeclaration)(importSource, [this.cssAttributeName]), ";\n"));
|
|
195
|
+
}
|
|
163
196
|
}
|
|
164
|
-
} else {
|
|
165
|
-
return (0, _eslintCodemodUtils.insertAtStartOfFile)(fixer, "".concat((0, _eslintCodemodUtils.insertImportDeclaration)(importSource, [cssAttributeName]), ";\n"));
|
|
166
|
-
}
|
|
167
|
-
};
|
|
168
|
-
|
|
169
|
-
/**
|
|
170
|
-
* Returns a list of fixes that:
|
|
171
|
-
* - add the `css` or `xcss` function call around the current node.
|
|
172
|
-
* - add an import statement for the package from which `css` is imported
|
|
173
|
-
*/
|
|
174
|
-
var addCssFunctionCall = function addCssFunctionCall(fixer, context, node, configuration, cssAttributeName) {
|
|
175
|
-
var fixes = [];
|
|
176
|
-
var sourceCode = context.getSourceCode();
|
|
177
|
-
if (node.type !== 'VariableDeclarator' || !node.init || !cssAttributeName) {
|
|
178
|
-
return [];
|
|
179
|
-
}
|
|
180
|
-
var compiledImportFix = addImportSource(context, fixer, configuration, cssAttributeName);
|
|
181
|
-
if (compiledImportFix) {
|
|
182
|
-
fixes.push(compiledImportFix);
|
|
183
|
-
}
|
|
184
|
-
var init = node.init;
|
|
185
|
-
var initString = sourceCode.getText(init);
|
|
186
|
-
if (node.init.type === 'TemplateLiteral') {
|
|
187
|
-
fixes.push(fixer.replaceText(init, "".concat(cssAttributeName).concat(initString)));
|
|
188
|
-
} else {
|
|
189
|
-
fixes.push(fixer.replaceText(init, "".concat(cssAttributeName, "(").concat(initString, ")")));
|
|
190
|
-
}
|
|
191
|
-
return fixes;
|
|
192
|
-
};
|
|
193
197
|
|
|
194
|
-
/**
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
*/
|
|
207
|
-
var potentiallyHasLocalVariable = function potentiallyHasLocalVariable(context, node) {
|
|
208
|
-
var hasPotentiallyLocalVariable = false;
|
|
209
|
-
var isImportedVariable = function isImportedVariable(identifier) {
|
|
210
|
-
return !!(0, _getImportNodeBySource.getModuleOfIdentifier)(context.getSourceCode(), identifier);
|
|
211
|
-
};
|
|
212
|
-
_estraverse.default.traverse(node, {
|
|
213
|
-
enter: function enter(node, _parent) {
|
|
214
|
-
if ((0, _eslintCodemodUtils.isNodeOfType)(node, 'SpreadElement') ||
|
|
215
|
-
// @ts-expect-error remove once we can be sure that no parser interprets
|
|
216
|
-
// the spread operator as ExperimentalSpreadProperty anymore
|
|
217
|
-
(0, _eslintCodemodUtils.isNodeOfType)(node, 'ExperimentalSpreadProperty')) {
|
|
218
|
-
// Spread elements could contain anything... so we don't bother.
|
|
219
|
-
//
|
|
220
|
-
// e.g. <div css={css({ ...(!height && { visibility: 'hidden' })} />
|
|
221
|
-
hasPotentiallyLocalVariable = true;
|
|
222
|
-
this.break();
|
|
198
|
+
/**
|
|
199
|
+
* Returns a list of fixes that:
|
|
200
|
+
* - add the `css` or `xcss` function call around the current node.
|
|
201
|
+
* - add an import statement for the package from which `css` is imported
|
|
202
|
+
*/
|
|
203
|
+
}, {
|
|
204
|
+
key: "addCssFunctionCall",
|
|
205
|
+
value: function addCssFunctionCall(fixer, node) {
|
|
206
|
+
var fixes = [];
|
|
207
|
+
var sourceCode = this.context.getSourceCode();
|
|
208
|
+
if (node.type !== 'VariableDeclarator' || !node.init || !this.cssAttributeName) {
|
|
209
|
+
return [];
|
|
223
210
|
}
|
|
224
|
-
|
|
225
|
-
|
|
211
|
+
var compiledImportFix = this.addImportSource(fixer);
|
|
212
|
+
if (compiledImportFix) {
|
|
213
|
+
fixes.push(compiledImportFix);
|
|
226
214
|
}
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
215
|
+
var init = node.init;
|
|
216
|
+
var initString = sourceCode.getText(init);
|
|
217
|
+
if (node.init.type === 'TemplateLiteral') {
|
|
218
|
+
fixes.push(fixer.replaceText(init, "".concat(this.cssAttributeName).concat(initString)));
|
|
219
|
+
} else {
|
|
220
|
+
fixes.push(fixer.replaceText(init, "".concat(this.cssAttributeName, "(").concat(initString, ")")));
|
|
221
|
+
}
|
|
222
|
+
return fixes;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Check if the expression has or potentially has a local variable
|
|
227
|
+
* (as opposed to an imported one), erring on the side ot "yes"
|
|
228
|
+
* when an expression is too complicated to analyse.
|
|
229
|
+
*
|
|
230
|
+
* This is useful because expressions containing local variables
|
|
231
|
+
* cannot be easily hoisted, whereas this is not a problem with imported
|
|
232
|
+
* variables.
|
|
233
|
+
*
|
|
234
|
+
* @param context Context of the rule.
|
|
235
|
+
* @param node Any node that is potentially hoistable.
|
|
236
|
+
* @returns Whether the node potentially has a local variable (and thus is not safe to hoist).
|
|
237
|
+
*/
|
|
238
|
+
}, {
|
|
239
|
+
key: "potentiallyHasLocalVariable",
|
|
240
|
+
value: function potentiallyHasLocalVariable(node) {
|
|
241
|
+
var _this2 = this;
|
|
242
|
+
var hasPotentiallyLocalVariable = false;
|
|
243
|
+
var isImportedVariable = function isImportedVariable(identifier) {
|
|
244
|
+
return !!(0, _getImportNodeBySource.getModuleOfIdentifier)(_this2.context.getSourceCode(), identifier);
|
|
245
|
+
};
|
|
246
|
+
_estraverse.default.traverse(node, {
|
|
247
|
+
enter: function enter(node, _parent) {
|
|
248
|
+
if ((0, _eslintCodemodUtils.isNodeOfType)(node, 'SpreadElement') ||
|
|
249
|
+
// @ts-expect-error remove once we can be sure that no parser interprets
|
|
250
|
+
// the spread operator as ExperimentalSpreadProperty anymore
|
|
251
|
+
(0, _eslintCodemodUtils.isNodeOfType)(node, 'ExperimentalSpreadProperty')) {
|
|
252
|
+
// Spread elements could contain anything... so we don't bother.
|
|
253
|
+
//
|
|
254
|
+
// e.g. <div css={css({ ...(!height && { visibility: 'hidden' })} />
|
|
233
255
|
hasPotentiallyLocalVariable = true;
|
|
256
|
+
this.break();
|
|
234
257
|
}
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
case 'MemberExpression':
|
|
238
|
-
// e.g. css({ margin: props.color })
|
|
239
|
-
// css({ margin: props.media.color })
|
|
240
|
-
if (node.value.object.type === 'Identifier' && isImportedVariable(node.value.object.name)) {
|
|
241
|
-
// We found an imported variable, don't do anything.
|
|
242
|
-
} else {
|
|
243
|
-
// e.g. css({ margin: [some complicated expression].media.color })
|
|
244
|
-
// This can potentially get too complex, so we assume there's a local
|
|
245
|
-
// variable in there somewhere.
|
|
246
|
-
hasPotentiallyLocalVariable = true;
|
|
258
|
+
if (!(0, _eslintCodemodUtils.isNodeOfType)(node, 'Property')) {
|
|
259
|
+
return;
|
|
247
260
|
}
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
261
|
+
switch (node.value.type) {
|
|
262
|
+
case 'Literal':
|
|
263
|
+
break;
|
|
264
|
+
case 'Identifier':
|
|
265
|
+
// e.g. css({ margin: myVariable })
|
|
266
|
+
if (!isImportedVariable(node.value.name)) {
|
|
267
|
+
hasPotentiallyLocalVariable = true;
|
|
268
|
+
}
|
|
269
|
+
this.break();
|
|
270
|
+
break;
|
|
271
|
+
case 'MemberExpression':
|
|
272
|
+
// e.g. css({ margin: props.color })
|
|
273
|
+
// css({ margin: props.media.color })
|
|
274
|
+
if (node.value.object.type === 'Identifier' && isImportedVariable(node.value.object.name)) {
|
|
275
|
+
// We found an imported variable, don't do anything.
|
|
276
|
+
} else {
|
|
277
|
+
// e.g. css({ margin: [some complicated expression].media.color })
|
|
278
|
+
// This can potentially get too complex, so we assume there's a local
|
|
279
|
+
// variable in there somewhere.
|
|
280
|
+
hasPotentiallyLocalVariable = true;
|
|
281
|
+
}
|
|
282
|
+
this.break();
|
|
283
|
+
break;
|
|
284
|
+
case 'TemplateLiteral':
|
|
285
|
+
if (!!node.value.expressions.length) {
|
|
286
|
+
// Too many edge cases here, don't bother...
|
|
287
|
+
// e.g. css({ animation: `${expandStyles(right, rightExpanded, isExpanded)} 0.2s ease-in-out` });
|
|
288
|
+
hasPotentiallyLocalVariable = true;
|
|
289
|
+
this.break();
|
|
290
|
+
}
|
|
291
|
+
break;
|
|
292
|
+
default:
|
|
293
|
+
// Catch-all for values such as "A && B", "A ? B : C"
|
|
294
|
+
hasPotentiallyLocalVariable = true;
|
|
295
|
+
this.break();
|
|
296
|
+
break;
|
|
256
297
|
}
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
hasPotentiallyLocalVariable = true;
|
|
261
|
-
this.break();
|
|
262
|
-
break;
|
|
263
|
-
}
|
|
298
|
+
}
|
|
299
|
+
});
|
|
300
|
+
return hasPotentiallyLocalVariable;
|
|
264
301
|
}
|
|
265
|
-
});
|
|
266
|
-
return hasPotentiallyLocalVariable;
|
|
267
|
-
};
|
|
268
302
|
|
|
269
|
-
/**
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
303
|
+
/**
|
|
304
|
+
* Fixer for the cssAtTopOfModule/cssAtBottomOfModule violation cases.
|
|
305
|
+
*
|
|
306
|
+
* This deals with Identifiers and Expressions passed from the traverseExpressionWithConfig() function.
|
|
307
|
+
*
|
|
308
|
+
* @param fixer The ESLint RuleFixer object
|
|
309
|
+
* @param context The context of the rule
|
|
310
|
+
* @param configuration The configuration of the rule, determining whether the fix is implmeneted at the top or bottom of the module
|
|
311
|
+
* @param node Any potentially hoistable node, or an identifier.
|
|
312
|
+
* @param cssAttributeName An optional parameter only added when we fix an ObjectExpression
|
|
313
|
+
*/
|
|
314
|
+
}, {
|
|
315
|
+
key: "fixCssNotInModuleScope",
|
|
316
|
+
value: function fixCssNotInModuleScope(fixer, node, isObjectExpression) {
|
|
317
|
+
var sourceCode = this.context.getSourceCode();
|
|
281
318
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
319
|
+
// Get the program node in order to properly position the hoisted styles
|
|
320
|
+
var programNode = getProgramNode(node);
|
|
321
|
+
var fixerNodePlacement = programNode;
|
|
322
|
+
if (this.configuration.stylesPlacement === 'bottom') {
|
|
323
|
+
// The last value is the bottom of the file
|
|
324
|
+
fixerNodePlacement = programNode.body[programNode.body.length - 1];
|
|
325
|
+
} else {
|
|
326
|
+
// Place after the last ImportDeclaration
|
|
327
|
+
fixerNodePlacement = programNode.body.length === 1 ? programNode.body[0] : programNode.body.find(function (node) {
|
|
328
|
+
return node.type !== 'ImportDeclaration';
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
var moduleString;
|
|
332
|
+
var fixes = [];
|
|
333
|
+
if (node.type === 'Identifier') {
|
|
334
|
+
var identifier = node;
|
|
335
|
+
var declarator = identifier.parent.parent;
|
|
336
|
+
moduleString = sourceCode.getText(declarator);
|
|
337
|
+
fixes.push(fixer.remove(declarator));
|
|
338
|
+
} else {
|
|
339
|
+
if (this.potentiallyHasLocalVariable(node)) {
|
|
340
|
+
return [];
|
|
341
|
+
}
|
|
342
|
+
var _declarator = this.getDeclaratorString();
|
|
343
|
+
var text = sourceCode.getText(node);
|
|
307
344
|
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
345
|
+
// If this has been passed, then we know it's an ObjectExpression
|
|
346
|
+
if (isObjectExpression) {
|
|
347
|
+
moduleString = "const ".concat(_declarator, " = ").concat(this.cssAttributeName, "(").concat(text, ");");
|
|
348
|
+
var compiledImportFix = this.addImportSource(fixer);
|
|
349
|
+
if (compiledImportFix) {
|
|
350
|
+
fixes.push(compiledImportFix);
|
|
351
|
+
}
|
|
352
|
+
} else {
|
|
353
|
+
moduleString = "const ".concat(_declarator, " = ").concat(text, ";");
|
|
354
|
+
}
|
|
355
|
+
fixes.push(fixer.replaceText(node, _declarator));
|
|
314
356
|
}
|
|
315
|
-
|
|
316
|
-
|
|
357
|
+
return [].concat(fixes, [
|
|
358
|
+
// Insert the node either before or after, depending on the rule configuration
|
|
359
|
+
this.configuration.stylesPlacement === 'bottom' ? fixer.insertTextAfter(fixerNodePlacement, '\n' + moduleString) : fixer.insertTextBefore(fixerNodePlacement, moduleString + '\n')]);
|
|
317
360
|
}
|
|
318
|
-
fixes.push(fixer.replaceText(node, _declarator));
|
|
319
|
-
}
|
|
320
|
-
return [].concat(fixes, [
|
|
321
|
-
// Insert the node either before or after, depending on the rule configuration
|
|
322
|
-
configuration.stylesPlacement === 'bottom' ? fixer.insertTextAfter(fixerNodePlacement, '\n' + moduleString) : fixer.insertTextBefore(fixerNodePlacement, moduleString + '\n')]);
|
|
323
|
-
};
|
|
324
361
|
|
|
325
|
-
/**
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
362
|
+
/**
|
|
363
|
+
* Handle different cases based on what's been passed in the css-related JSXAttribute.
|
|
364
|
+
*
|
|
365
|
+
* @param expression the expression of the JSXAttribute value.
|
|
366
|
+
*/
|
|
367
|
+
}, {
|
|
368
|
+
key: "traverseExpression",
|
|
369
|
+
value: function traverseExpression(expression) {
|
|
370
|
+
var _this3 = this;
|
|
371
|
+
switch (expression.type) {
|
|
372
|
+
case 'Identifier':
|
|
373
|
+
// {styles}
|
|
374
|
+
// We've found an identifier - time to analyze it!
|
|
375
|
+
this.analyzeIdentifier(expression);
|
|
376
|
+
break;
|
|
377
|
+
case 'ArrayExpression':
|
|
378
|
+
// {[styles, moreStyles]}
|
|
379
|
+
// We've found an array expression - let's traverse again over each element individually.
|
|
380
|
+
expression.elements.forEach(function (element) {
|
|
381
|
+
return _this3.traverseExpression(element);
|
|
382
|
+
});
|
|
383
|
+
break;
|
|
384
|
+
case 'LogicalExpression':
|
|
385
|
+
// {isEnabled && styles}
|
|
386
|
+
// We've found a logical expression - we're only interested in the right expression so
|
|
387
|
+
// let's traverse that and see what it is!
|
|
388
|
+
this.traverseExpression(expression.right);
|
|
389
|
+
break;
|
|
390
|
+
case 'ConditionalExpression':
|
|
391
|
+
// {isEnabled ? styles : null}
|
|
392
|
+
// We've found a conditional expression - we're only interested in the consequent and
|
|
393
|
+
// alternate (styles : null)
|
|
394
|
+
this.traverseExpression(expression.consequent);
|
|
395
|
+
this.traverseExpression(expression.alternate);
|
|
396
|
+
break;
|
|
397
|
+
case 'ObjectExpression':
|
|
398
|
+
case 'CallExpression':
|
|
399
|
+
case 'TaggedTemplateExpression':
|
|
400
|
+
case 'TemplateLiteral':
|
|
401
|
+
// We've found elements that shouldn't be here! Report an error.
|
|
402
|
+
this.context.report({
|
|
403
|
+
node: expression,
|
|
404
|
+
messageId: this.configuration.stylesPlacement === 'bottom' ? 'cssAtBottomOfModule' : 'cssAtTopOfModule',
|
|
405
|
+
fix: function fix(fixer) {
|
|
406
|
+
if (_this3.configuration.fixNamesOnly) {
|
|
407
|
+
return [];
|
|
408
|
+
}
|
|
372
409
|
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
410
|
+
// Don't fix CallExpressions unless they're from cssFunctions or cssMap
|
|
411
|
+
if (expression.type === 'CallExpression' && !isCssCallExpression(expression, _this3.configuration.cssFunctions)) {
|
|
412
|
+
return [];
|
|
413
|
+
}
|
|
414
|
+
if (expression.type === 'ObjectExpression') {
|
|
415
|
+
return _this3.fixCssNotInModuleScope(fixer, expression, true);
|
|
416
|
+
}
|
|
417
|
+
return _this3.fixCssNotInModuleScope(fixer, expression, false);
|
|
376
418
|
}
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
}
|
|
380
|
-
return fixCssNotInModuleScope(fixer, context, configuration, expression);
|
|
381
|
-
}
|
|
382
|
-
});
|
|
383
|
-
break;
|
|
419
|
+
});
|
|
420
|
+
break;
|
|
384
421
|
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
422
|
+
// @ts-expect-error - our ESLint-related types assume vanilla JS, when in fact
|
|
423
|
+
// it is running @typescript-eslint
|
|
424
|
+
//
|
|
425
|
+
// Switching to the more accurate @typescript-eslint types would break
|
|
426
|
+
// eslint-codemod-utils and all ESLint rules in packages/design-system,
|
|
427
|
+
// so we just leave this as-is.
|
|
428
|
+
case 'TSAsExpression':
|
|
429
|
+
// @ts-expect-error
|
|
430
|
+
this.traverseExpression(expression.expression);
|
|
431
|
+
break;
|
|
432
|
+
default:
|
|
433
|
+
// Do nothing!
|
|
434
|
+
break;
|
|
435
|
+
}
|
|
398
436
|
}
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
|
|
437
|
+
}, {
|
|
438
|
+
key: "run",
|
|
439
|
+
value: function run() {
|
|
440
|
+
return this.traverseExpression(this.expression);
|
|
441
|
+
}
|
|
442
|
+
}]);
|
|
443
|
+
return JSXExpressionLinter;
|
|
444
|
+
}();
|
|
402
445
|
var defaultConfig = {
|
|
403
446
|
cssFunctions: ['css', 'xcss'],
|
|
404
447
|
stylesPlacement: 'top',
|
|
@@ -510,9 +553,6 @@ var rule = (0, _createRule.createLintRule)({
|
|
|
510
553
|
return;
|
|
511
554
|
}
|
|
512
555
|
}
|
|
513
|
-
|
|
514
|
-
// Always reset to empty array
|
|
515
|
-
hoistedCss = [];
|
|
516
556
|
if (name.type === 'JSXIdentifier' && mergedConfig.cssFunctions.includes(name.name)) {
|
|
517
557
|
// When not a jsx expression. For eg. css=""
|
|
518
558
|
if ((value === null || value === void 0 ? void 0 : value.type) !== 'JSXExpressionContainer') {
|
|
@@ -527,7 +567,8 @@ var rule = (0, _createRule.createLintRule)({
|
|
|
527
567
|
// <div css={/* Hello there */} />
|
|
528
568
|
return;
|
|
529
569
|
}
|
|
530
|
-
|
|
570
|
+
var linter = new JSXExpressionLinter(context, name.name, mergedConfig, value.expression);
|
|
571
|
+
linter.run();
|
|
531
572
|
}
|
|
532
573
|
}), _ref3;
|
|
533
574
|
}
|