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