@atlaskit/eslint-plugin-design-system 8.23.1 → 8.23.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 +6 -0
- package/dist/cjs/ast-nodes/function-call.js +48 -0
- package/dist/cjs/ast-nodes/import.js +49 -0
- package/dist/cjs/ast-nodes/index.js +40 -0
- package/dist/cjs/ast-nodes/jsx-attribute.js +64 -0
- package/dist/cjs/ast-nodes/jsx-element.js +55 -0
- package/dist/cjs/ast-nodes/root.js +34 -0
- package/dist/cjs/rules/use-primitives/index.js +6 -76
- package/dist/cjs/rules/use-primitives/transformers/emotion-css/index.js +168 -0
- package/dist/cjs/rules/use-primitives/transformers/emotion-css/supported.js +52 -0
- package/dist/cjs/rules/use-primitives/utils/is-valid-css-properties-to-transform.js +3 -0
- package/dist/es2019/ast-nodes/function-call.js +42 -0
- package/dist/es2019/ast-nodes/import.js +42 -0
- package/dist/es2019/ast-nodes/index.js +5 -0
- package/dist/es2019/ast-nodes/jsx-attribute.js +59 -0
- package/dist/es2019/ast-nodes/jsx-element.js +50 -0
- package/dist/es2019/ast-nodes/root.js +28 -0
- package/dist/es2019/rules/use-primitives/index.js +9 -79
- package/dist/es2019/rules/use-primitives/transformers/emotion-css/index.js +159 -0
- package/dist/es2019/rules/use-primitives/transformers/emotion-css/supported.js +46 -0
- package/dist/es2019/rules/use-primitives/utils/is-valid-css-properties-to-transform.js +3 -0
- package/dist/esm/ast-nodes/function-call.js +42 -0
- package/dist/esm/ast-nodes/import.js +43 -0
- package/dist/esm/ast-nodes/index.js +5 -0
- package/dist/esm/ast-nodes/jsx-attribute.js +59 -0
- package/dist/esm/ast-nodes/jsx-element.js +50 -0
- package/dist/esm/ast-nodes/root.js +28 -0
- package/dist/esm/rules/use-primitives/index.js +9 -79
- package/dist/esm/rules/use-primitives/transformers/emotion-css/index.js +158 -0
- package/dist/esm/rules/use-primitives/transformers/emotion-css/supported.js +46 -0
- package/dist/esm/rules/use-primitives/utils/is-valid-css-properties-to-transform.js +3 -0
- package/dist/types/ast-nodes/function-call.d.ts +21 -0
- package/dist/types/ast-nodes/import.d.ts +16 -0
- package/dist/types/ast-nodes/index.d.ts +5 -0
- package/dist/types/ast-nodes/jsx-attribute.d.ts +26 -0
- package/dist/types/ast-nodes/jsx-element.d.ts +21 -0
- package/dist/types/ast-nodes/root.d.ts +19 -0
- package/dist/types/rules/use-primitives/transformers/emotion-css/index.d.ts +35 -0
- package/dist/types/rules/use-primitives/transformers/emotion-css/supported.d.ts +9 -0
- package/dist/types-ts4.5/ast-nodes/function-call.d.ts +21 -0
- package/dist/types-ts4.5/ast-nodes/import.d.ts +16 -0
- package/dist/types-ts4.5/ast-nodes/index.d.ts +5 -0
- package/dist/types-ts4.5/ast-nodes/jsx-attribute.d.ts +26 -0
- package/dist/types-ts4.5/ast-nodes/jsx-element.d.ts +21 -0
- package/dist/types-ts4.5/ast-nodes/root.d.ts +19 -0
- package/dist/types-ts4.5/rules/use-primitives/transformers/emotion-css/index.d.ts +35 -0
- package/dist/types-ts4.5/rules/use-primitives/transformers/emotion-css/supported.d.ts +9 -0
- package/package.json +1 -1
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/* eslint-disable @repo/internal/react/require-jsdoc */
|
|
2
|
+
|
|
3
|
+
import { insertImportSpecifier, isNodeOfType } from 'eslint-codemod-utils';
|
|
4
|
+
export const Import = {
|
|
5
|
+
/**
|
|
6
|
+
* Note: fixes can't overlap, which means this will fail:
|
|
7
|
+
* ```
|
|
8
|
+
* const importNode = Root.findImportByModule('@atlaskit/primitives')
|
|
9
|
+
* Import.insertNamedSpecifier(importNode, 'Box')
|
|
10
|
+
* Import.insertNamedSpecifier(importNode, 'xcss')
|
|
11
|
+
* ```
|
|
12
|
+
*
|
|
13
|
+
* For this reason `insertNamedSpecifiers` accepts a `specifiers` array, so you can group all inserts together.
|
|
14
|
+
*/
|
|
15
|
+
insertNamedSpecifiers(node, specifiers, fixer) {
|
|
16
|
+
/**
|
|
17
|
+
* `insertImportSpecifier()` has the unfortunate implementation detail of naively adding duplicate specifiers.
|
|
18
|
+
* e.g. calling
|
|
19
|
+
* `insertImportSpecifier(importDecl, 'xcss')`
|
|
20
|
+
* on
|
|
21
|
+
* `import { Inline, xcss } from '@atlaskit/primitives'`
|
|
22
|
+
* will result in:
|
|
23
|
+
* `import { Inline, xcss, xcss } from '@atlaskit/primitives'`.
|
|
24
|
+
* So, we need to filter out specifiers that are already imported.
|
|
25
|
+
*/
|
|
26
|
+
const uniqueSpecifiers = specifiers.filter(specifier => {
|
|
27
|
+
return !this.containsNamedSpecifier(node, specifier);
|
|
28
|
+
});
|
|
29
|
+
if (uniqueSpecifiers.length === 0) {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
return fixer.replaceText(node, `${insertImportSpecifier(node, uniqueSpecifiers.join(', '))};\n`);
|
|
33
|
+
},
|
|
34
|
+
containsNamedSpecifier(node, name) {
|
|
35
|
+
return node.specifiers.some(specifier => {
|
|
36
|
+
if (!isNodeOfType(specifier, 'ImportSpecifier')) {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
return specifier.imported.name === name;
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
};
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { isNodeOfType, literal } from 'eslint-codemod-utils';
|
|
2
|
+
const HelperJSXAttribute = {
|
|
3
|
+
getName(node) {
|
|
4
|
+
if (!isNodeOfType(node, 'JSXAttribute')) {
|
|
5
|
+
throw new Error('Not a JSXAttribute');
|
|
6
|
+
}
|
|
7
|
+
if (!isNodeOfType(node.name, 'JSXIdentifier')) {
|
|
8
|
+
throw new Error('name is not a JSXIdentifier');
|
|
9
|
+
}
|
|
10
|
+
return node.name.name;
|
|
11
|
+
},
|
|
12
|
+
updateName(node, name, fixer) {
|
|
13
|
+
if (!isNodeOfType(node, 'JSXAttribute')) {
|
|
14
|
+
throw new Error('Not a JSXAttribute');
|
|
15
|
+
}
|
|
16
|
+
if (!isNodeOfType(node.name, 'JSXIdentifier')) {
|
|
17
|
+
throw new Error('name is not a JSXIdentifier');
|
|
18
|
+
}
|
|
19
|
+
return fixer.replaceText(node.name, literal(name).toString());
|
|
20
|
+
},
|
|
21
|
+
/**
|
|
22
|
+
* A JSXAttribute value can be many things:
|
|
23
|
+
* - css='myStyles'
|
|
24
|
+
* - css={myStyles}
|
|
25
|
+
* - css={[styles1, styles2]}
|
|
26
|
+
* - header={<></>}
|
|
27
|
+
* - css={styleMap.header}
|
|
28
|
+
* - css={...styles}
|
|
29
|
+
*
|
|
30
|
+
* Currently, `getValue` has only implemented strategies for when the value is a string, or an ExpressionStatement
|
|
31
|
+
* If you need additional functionality add it, and set the correct `type` on the returned object
|
|
32
|
+
*/
|
|
33
|
+
getValue(node) {
|
|
34
|
+
if (!isNodeOfType(node, 'JSXAttribute')) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
if (!node.value) {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// handle `css={myStyles}`
|
|
42
|
+
if (isNodeOfType(node.value, 'JSXExpressionContainer') && isNodeOfType(node.value.expression, 'Identifier')) {
|
|
43
|
+
return {
|
|
44
|
+
type: 'ExpressionStatement',
|
|
45
|
+
value: node.value.expression.name
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// handle `css='myStyles'`
|
|
50
|
+
if (isNodeOfType(node.value, 'Literal') && node.value.value) {
|
|
51
|
+
var _node$value$value;
|
|
52
|
+
return {
|
|
53
|
+
type: 'Literal',
|
|
54
|
+
value: (_node$value$value = node.value.value) === null || _node$value$value === void 0 ? void 0 : _node$value$value.toString()
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
export { HelperJSXAttribute as JSXAttribute };
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { isNodeOfType, jsxIdentifier } from 'eslint-codemod-utils';
|
|
2
|
+
export const JSXElementHelper = {
|
|
3
|
+
/**
|
|
4
|
+
* Names of JSXElements can be any of:
|
|
5
|
+
* `<Component></Component>` - (JSXIdentifier)
|
|
6
|
+
* `<MyComponents.Component></MyComponents.Component>` - `MyComponents` is a namespace (JSXNamespacedName)
|
|
7
|
+
* `<MyComponents.Component></MyComponents.Component>` - `MyComponents` is an object (JSXMemberExpression)
|
|
8
|
+
*
|
|
9
|
+
* Getting the name of a JSXMemberExpression is difficult, because object can contain objects, which is recursively defined in the AST.
|
|
10
|
+
* e.g. getting the name of `<MyComponents.PresentationLayer.LeftSideBar.Header />` would require `getName` to recursively resolve all parts of the name.
|
|
11
|
+
* `getName` does not currently have this functionality. Add it if you need it.
|
|
12
|
+
*/
|
|
13
|
+
getName(node) {
|
|
14
|
+
if (!isNodeOfType(node.openingElement.name, 'JSXIdentifier')) {
|
|
15
|
+
// TODO: We may want to log this
|
|
16
|
+
return '';
|
|
17
|
+
}
|
|
18
|
+
return node.openingElement.name.name;
|
|
19
|
+
},
|
|
20
|
+
updateName(node, newName, fixer) {
|
|
21
|
+
const isSelfClosing = JSXElementHelper.isSelfClosing(node);
|
|
22
|
+
const openingElementFix = fixer.replaceText(node.openingElement.name, jsxIdentifier(newName).toString());
|
|
23
|
+
if (isSelfClosing || !node.closingElement) {
|
|
24
|
+
return [openingElementFix];
|
|
25
|
+
}
|
|
26
|
+
const closingElementFix = fixer.replaceText(node.closingElement.name, jsxIdentifier(newName).toString());
|
|
27
|
+
return [openingElementFix, closingElementFix];
|
|
28
|
+
},
|
|
29
|
+
isSelfClosing(node) {
|
|
30
|
+
return node.openingElement.selfClosing;
|
|
31
|
+
},
|
|
32
|
+
getAttributes(node) {
|
|
33
|
+
return node.openingElement.attributes;
|
|
34
|
+
},
|
|
35
|
+
getAttributeByName(node, name) {
|
|
36
|
+
return node.openingElement.attributes.find(attr => {
|
|
37
|
+
// Ignore anything other than JSXAttribute
|
|
38
|
+
if (!isNodeOfType(attr, 'JSXAttribute')) {
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
return attr.name.name === name;
|
|
42
|
+
});
|
|
43
|
+
},
|
|
44
|
+
containsSpreadAttributes(node) {
|
|
45
|
+
return node.openingElement.attributes.some(attr => {
|
|
46
|
+
return isNodeOfType(attr, 'JSXSpreadAttribute');
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
export { JSXElementHelper as JSXElement };
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/* eslint-disable @repo/internal/react/require-jsdoc */
|
|
2
|
+
|
|
3
|
+
import { hasImportDeclaration, insertImportDeclaration, isNodeOfType } from 'eslint-codemod-utils';
|
|
4
|
+
// Little bit unreadable, but better than duplicating the type
|
|
5
|
+
|
|
6
|
+
export const Root = {
|
|
7
|
+
/**
|
|
8
|
+
* Note: This can return multiple ImportDeclarations for cases like:
|
|
9
|
+
* ```
|
|
10
|
+
* import { Stack } from '@atlaskit/primitives'
|
|
11
|
+
* import type { StackProps } from '@atlaskit/primitives'
|
|
12
|
+
* ```
|
|
13
|
+
*/
|
|
14
|
+
findImportsByModule(root, name) {
|
|
15
|
+
return root.filter(node => {
|
|
16
|
+
if (!isNodeOfType(node, 'ImportDeclaration')) {
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
if (!hasImportDeclaration(node, name)) {
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
return true;
|
|
23
|
+
});
|
|
24
|
+
},
|
|
25
|
+
insertImport(root, data, fixer) {
|
|
26
|
+
return fixer.insertTextBefore(root[0], `${insertImportDeclaration(data.module, data.specifiers)};\n`);
|
|
27
|
+
}
|
|
28
|
+
};
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { isNodeOfType } from 'eslint-codemod-utils';
|
|
2
2
|
import { createLintRule } from '../utils/create-rule';
|
|
3
3
|
import { getConfig } from './config';
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
4
|
+
import { styledComponentToPrimitive } from './transformers';
|
|
5
|
+
import { EmotionCSS } from './transformers/emotion-css';
|
|
6
|
+
import { findValidJsxUsageToTransform, findValidStyledComponentCall, isValidCssPropertiesToTransform } from './utils';
|
|
6
7
|
const boxDocsUrl = 'https://atlassian.design/components/primitives/box';
|
|
7
8
|
const rule = createLintRule({
|
|
8
9
|
meta: {
|
|
@@ -53,84 +54,13 @@ const rule = createLintRule({
|
|
|
53
54
|
});
|
|
54
55
|
},
|
|
55
56
|
// transforms <div css={...}> usages
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
return;
|
|
62
|
-
}
|
|
63
|
-
if (!isNodeOfType(node.name, 'JSXIdentifier')) {
|
|
64
|
-
return;
|
|
65
|
-
}
|
|
66
|
-
if (!isNodeOfType(node.parent, 'JSXElement')) {
|
|
67
|
-
return;
|
|
68
|
-
}
|
|
69
|
-
const suggestBox = shouldSuggestBox(node.parent, context, config);
|
|
70
|
-
if (suggestBox) {
|
|
71
|
-
context.report({
|
|
72
|
-
node: node,
|
|
73
|
-
messageId: 'preferPrimitivesBox',
|
|
74
|
-
data: {
|
|
75
|
-
element: node.name.name
|
|
76
|
-
},
|
|
77
|
-
suggest: [{
|
|
78
|
-
desc: `Convert to Box`,
|
|
79
|
-
fix: jsxElementToBoxTransformer(node.parent, context)
|
|
80
|
-
}]
|
|
81
|
-
});
|
|
82
|
-
}
|
|
57
|
+
JSXElement(node) {
|
|
58
|
+
EmotionCSS.lint(node, {
|
|
59
|
+
context,
|
|
60
|
+
config
|
|
61
|
+
});
|
|
83
62
|
}
|
|
84
63
|
};
|
|
85
64
|
}
|
|
86
65
|
});
|
|
87
|
-
const shouldSuggestBox = (node, context, config
|
|
88
|
-
// scope: Scope.Scope,
|
|
89
|
-
) => {
|
|
90
|
-
if (!node) {
|
|
91
|
-
return false;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
// Currently we only support `div`, but possibly more in future
|
|
95
|
-
if (!isValidTagName(node)) {
|
|
96
|
-
return false;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
// Currently we only support elements that contain only a `css` attr, possibly more in future
|
|
100
|
-
if (!containsOnlySupportedAttrs(node)) {
|
|
101
|
-
return false;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
// Get the `css` attr
|
|
105
|
-
const cssAttr = getJSXAttributeByName(node.openingElement, 'css');
|
|
106
|
-
|
|
107
|
-
// Get the prop value, e.g. `myStyles` in `css={myStyles}`
|
|
108
|
-
const cssVariableName = getAttributeValueIdentifier(cssAttr);
|
|
109
|
-
|
|
110
|
-
// `cssVariableName` could be undefined if the maker has
|
|
111
|
-
// done something like `css={[styles1, styles2]}`, `css={...styles}`, etc
|
|
112
|
-
if (!cssVariableName) {
|
|
113
|
-
return false;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
/**
|
|
117
|
-
* Make sure the variable is not used in multiple JSX elements:
|
|
118
|
-
* ```
|
|
119
|
-
* <div css={paddingStyles}></div>
|
|
120
|
-
* <input css={paddingStyles}></input>
|
|
121
|
-
* ```
|
|
122
|
-
*/
|
|
123
|
-
if (getVariableUsagesCount(cssVariableName, context) !== 1) {
|
|
124
|
-
return false;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
// Find where `cssVariableName` is defined. We're looking for `const myStyles = css({...})`
|
|
128
|
-
const cssVariableDefinition = getIdentifierInParentScope(context.getScope(), cssVariableName);
|
|
129
|
-
const cssVariableValue = getVariableDefinitionValue(cssVariableDefinition);
|
|
130
|
-
// Check if `cssVariableValue` is a function called `css()`
|
|
131
|
-
if (!cssVariableValue || !isFunctionNamed(cssVariableValue, 'css')) {
|
|
132
|
-
return false;
|
|
133
|
-
}
|
|
134
|
-
return isValidCssPropertiesToTransform(cssVariableValue.node.init, config);
|
|
135
|
-
};
|
|
136
66
|
export default rule;
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
/* eslint-disable @repo/internal/react/require-jsdoc */
|
|
2
|
+
|
|
3
|
+
import { getIdentifierInParentScope, isNodeOfType } from 'eslint-codemod-utils';
|
|
4
|
+
import * as ast from '../../../../ast-nodes';
|
|
5
|
+
import { getVariableDefinitionValue, getVariableUsagesCount, isValidCssPropertiesToTransform } from '../../utils';
|
|
6
|
+
import { cssToXcssTransformer } from '../css-to-xcss';
|
|
7
|
+
import * as supported from './supported';
|
|
8
|
+
export const EmotionCSS = {
|
|
9
|
+
lint(node, {
|
|
10
|
+
context,
|
|
11
|
+
config
|
|
12
|
+
}) {
|
|
13
|
+
if (!isNodeOfType(node, 'JSXElement')) {
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// Check whether all criteria needed to make a transformation are met
|
|
18
|
+
if (!EmotionCSS._check(node, {
|
|
19
|
+
context,
|
|
20
|
+
config
|
|
21
|
+
})) {
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
context.report({
|
|
25
|
+
node: node.openingElement,
|
|
26
|
+
messageId: 'preferPrimitivesBox',
|
|
27
|
+
suggest: [{
|
|
28
|
+
desc: `Convert to Box`,
|
|
29
|
+
fix: EmotionCSS._fix(node, {
|
|
30
|
+
context
|
|
31
|
+
})
|
|
32
|
+
}]
|
|
33
|
+
});
|
|
34
|
+
},
|
|
35
|
+
_check(node, {
|
|
36
|
+
context,
|
|
37
|
+
config
|
|
38
|
+
}) {
|
|
39
|
+
var _cssVariableValue$nod, _cssVariableValue$nod2;
|
|
40
|
+
if (!config.patterns.includes('compiled-css-function')) {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
if (!isNodeOfType(node, 'JSXElement')) {
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
const elementName = ast.JSXElement.getName(node);
|
|
47
|
+
if (!elementName) {
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Currently we only support `div`. This may change in future.
|
|
52
|
+
if (!supported.elements.includes(elementName)) {
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Ignore elements that contain dangerous attributes like `id`.
|
|
57
|
+
if (!this._containsOnlySupportedAttributes(node)) {
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Currently we don't transform anything to `Box` unless it defines styles
|
|
62
|
+
const cssAttr = ast.JSXElement.getAttributeByName(node, 'css');
|
|
63
|
+
if (!cssAttr) {
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Get `myStyles` in `css={myStyles}` as a string so we can search for the `const myStyles` VariableDefinition
|
|
68
|
+
const cssAttrValue = ast.JSXAttribute.getValue(cssAttr);
|
|
69
|
+
if ((cssAttrValue === null || cssAttrValue === void 0 ? void 0 : cssAttrValue.type) !== 'ExpressionStatement') {
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// TODO: Everything below this line could be refactored to use `ast-nodes`.
|
|
74
|
+
|
|
75
|
+
// Bail if the styles are used on multiple JSXElements
|
|
76
|
+
if (getVariableUsagesCount(cssAttrValue.value, context) !== 1) {
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Find where `myStyles` is defined. We're looking for `const myStyles = css({...})`
|
|
81
|
+
const cssVariableDefinition = getIdentifierInParentScope(context.getScope(), cssAttrValue.value);
|
|
82
|
+
const cssVariableValue = getVariableDefinitionValue(cssVariableDefinition);
|
|
83
|
+
// Check if `cssVariableValue` is a function called `css()`
|
|
84
|
+
if (ast.FunctionCall.getName(cssVariableValue === null || cssVariableValue === void 0 ? void 0 : (_cssVariableValue$nod = cssVariableValue.node) === null || _cssVariableValue$nod === void 0 ? void 0 : _cssVariableValue$nod.init) !== 'css') {
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
if (!isValidCssPropertiesToTransform(cssVariableValue === null || cssVariableValue === void 0 ? void 0 : (_cssVariableValue$nod2 = cssVariableValue.node) === null || _cssVariableValue$nod2 === void 0 ? void 0 : _cssVariableValue$nod2.init, config)) {
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
const importDeclaration = ast.Root.findImportsByModule(context.getSourceCode().ast.body, '@atlaskit/primitives');
|
|
91
|
+
|
|
92
|
+
// If there is more than one `@atlaskit/primitives` import, then it becomes difficult to determine which import to transform
|
|
93
|
+
if (importDeclaration.length > 1) {
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
return true;
|
|
97
|
+
},
|
|
98
|
+
_fix(node, {
|
|
99
|
+
context
|
|
100
|
+
}) {
|
|
101
|
+
return fixer => {
|
|
102
|
+
const importFix = this._upsertImportDeclaration({
|
|
103
|
+
module: '@atlaskit/primitives',
|
|
104
|
+
specifiers: ['Box', 'xcss']
|
|
105
|
+
}, context, fixer);
|
|
106
|
+
const cssAttr = ast.JSXElement.getAttributeByName(node, 'css'); // Can strongly assert the type here, because we validated it exists in `check()`.
|
|
107
|
+
const attributeFix = ast.JSXAttribute.updateName(cssAttr, 'xcss', fixer);
|
|
108
|
+
const elementNameFixes = ast.JSXElement.updateName(node, 'Box', fixer);
|
|
109
|
+
const cssToXcssTransform = cssToXcssTransformer(node, context, fixer);
|
|
110
|
+
return [importFix, attributeFix, ...elementNameFixes, ...cssToXcssTransform].filter(fix => Boolean(fix)); // Some of the transformers can return arrays with undefined, so filter them out
|
|
111
|
+
};
|
|
112
|
+
},
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Check that every attribute in the JSXElement is something we support.
|
|
116
|
+
* We do this via a whitelist in `this.attributes`. The result is we exclude
|
|
117
|
+
* dangerous attrs like `id` and `style`.
|
|
118
|
+
*/
|
|
119
|
+
_containsOnlySupportedAttributes(node) {
|
|
120
|
+
const attrs = ast.JSXElement.getAttributes(node);
|
|
121
|
+
return attrs.every(attr => {
|
|
122
|
+
if (!isNodeOfType(attr, 'JSXAttribute')) {
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
if (!isNodeOfType(attr.name, 'JSXIdentifier')) {
|
|
126
|
+
return false;
|
|
127
|
+
}
|
|
128
|
+
return supported.attributes.includes(attr.name.name);
|
|
129
|
+
});
|
|
130
|
+
},
|
|
131
|
+
/**
|
|
132
|
+
* Currently this is defined here because it's not very general purpose.
|
|
133
|
+
* If we were to move this to `ast-nodes`, half the implementation would be in `Root`,
|
|
134
|
+
* and the other half would be in `Import`.
|
|
135
|
+
*
|
|
136
|
+
* TODO: Refactor and move to `ast-nodes`
|
|
137
|
+
*
|
|
138
|
+
* Note: It does not handle default imports, namespace imports, or aliased imports.
|
|
139
|
+
*/
|
|
140
|
+
_upsertImportDeclaration({
|
|
141
|
+
module,
|
|
142
|
+
specifiers
|
|
143
|
+
}, context, fixer) {
|
|
144
|
+
// Find any imports that match the packageName
|
|
145
|
+
const root = context.getSourceCode().ast.body;
|
|
146
|
+
const importDeclarations = ast.Root.findImportsByModule(root, module);
|
|
147
|
+
|
|
148
|
+
// The import doesn't exist yet, we can just insert a whole new one
|
|
149
|
+
if (importDeclarations.length === 0) {
|
|
150
|
+
return ast.Root.insertImport(root, {
|
|
151
|
+
module,
|
|
152
|
+
specifiers
|
|
153
|
+
}, fixer);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// The import exists so, modify the existing one
|
|
157
|
+
return ast.Import.insertNamedSpecifiers(importDeclarations[0], specifiers, fixer);
|
|
158
|
+
}
|
|
159
|
+
};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
export const elements = ['div'];
|
|
2
|
+
export const attributes = ['css'
|
|
3
|
+
// 'data-testid'
|
|
4
|
+
];
|
|
5
|
+
|
|
6
|
+
// TODO: https://product-fabric.atlassian.net/browse/DSP-16054
|
|
7
|
+
const spaceTokenMap = {
|
|
8
|
+
'0px': 'space.0',
|
|
9
|
+
'2px': 'space.025',
|
|
10
|
+
'4px': 'space.050',
|
|
11
|
+
'6px': 'space.075',
|
|
12
|
+
'8px': 'space.100',
|
|
13
|
+
'12px': 'space.150',
|
|
14
|
+
'16px': 'space.200',
|
|
15
|
+
'20px': 'space.250',
|
|
16
|
+
'24px': 'space.300',
|
|
17
|
+
'32px': 'space.400',
|
|
18
|
+
'40px': 'space.500',
|
|
19
|
+
'48px': 'space.600',
|
|
20
|
+
'64px': 'space.800',
|
|
21
|
+
'80px': 'space.1000'
|
|
22
|
+
};
|
|
23
|
+
export const styles = {
|
|
24
|
+
padding: spaceTokenMap,
|
|
25
|
+
paddingBlock: spaceTokenMap,
|
|
26
|
+
paddingBlockEnd: spaceTokenMap,
|
|
27
|
+
paddingBlockStart: spaceTokenMap,
|
|
28
|
+
paddingBottom: spaceTokenMap,
|
|
29
|
+
paddingInline: spaceTokenMap,
|
|
30
|
+
paddingInlineEnd: spaceTokenMap,
|
|
31
|
+
paddingInlineStart: spaceTokenMap,
|
|
32
|
+
paddingLeft: spaceTokenMap,
|
|
33
|
+
paddingRight: spaceTokenMap,
|
|
34
|
+
paddingTop: spaceTokenMap,
|
|
35
|
+
margin: spaceTokenMap,
|
|
36
|
+
marginBlock: spaceTokenMap,
|
|
37
|
+
marginBlockEnd: spaceTokenMap,
|
|
38
|
+
marginBlockStart: spaceTokenMap,
|
|
39
|
+
marginBottom: spaceTokenMap,
|
|
40
|
+
marginInline: spaceTokenMap,
|
|
41
|
+
marginInlineEnd: spaceTokenMap,
|
|
42
|
+
marginInlineStart: spaceTokenMap,
|
|
43
|
+
marginLeft: spaceTokenMap,
|
|
44
|
+
marginRight: spaceTokenMap,
|
|
45
|
+
marginTop: spaceTokenMap
|
|
46
|
+
};
|
|
@@ -2,6 +2,9 @@ import { isNodeOfType } from 'eslint-codemod-utils';
|
|
|
2
2
|
import { supportedStylesMap } from '../transformers/css-to-xcss';
|
|
3
3
|
import { convertASTObjectExpressionToJSObject } from './convert-ast-object-expression-to-js-object';
|
|
4
4
|
export const isValidCssPropertiesToTransform = (node, config) => {
|
|
5
|
+
if (!node) {
|
|
6
|
+
return false;
|
|
7
|
+
}
|
|
5
8
|
const cssObjectExpression = node.arguments[0];
|
|
6
9
|
// Bail on empty object calls
|
|
7
10
|
if (!cssObjectExpression || !isNodeOfType(cssObjectExpression, 'ObjectExpression')) {
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/* eslint-disable @repo/internal/react/require-jsdoc */
|
|
2
|
+
|
|
3
|
+
import { isNodeOfType } from 'eslint-codemod-utils';
|
|
4
|
+
export var FunctionCall = {
|
|
5
|
+
getName: function getName(node) {
|
|
6
|
+
if (!isNodeOfType(node, 'CallExpression')) {
|
|
7
|
+
return undefined;
|
|
8
|
+
}
|
|
9
|
+
if (!isNodeOfType(node.callee, 'Identifier')) {
|
|
10
|
+
return undefined;
|
|
11
|
+
}
|
|
12
|
+
return node.callee.name;
|
|
13
|
+
},
|
|
14
|
+
updateName: function updateName(node, newName, fixer) {
|
|
15
|
+
return fixer.replaceText(node.callee, newName);
|
|
16
|
+
},
|
|
17
|
+
/**
|
|
18
|
+
* Function arguments can be many things:
|
|
19
|
+
* `css(myStyles, () => {}, undefined, 'literal', ...rest) // etc`
|
|
20
|
+
* They all need slightly different treatment.
|
|
21
|
+
*
|
|
22
|
+
* Currently 'getArgumentAtPos' only implements strategies for Literals and ObjectExpressions.
|
|
23
|
+
* If you need to support another type of arg, add it, and update the type.
|
|
24
|
+
*/
|
|
25
|
+
getArgumentAtPos: function getArgumentAtPos(node, pos) {
|
|
26
|
+
var argument = node.arguments[pos];
|
|
27
|
+
if (isNodeOfType(argument, 'Literal') && argument.value) {
|
|
28
|
+
var _argument$value;
|
|
29
|
+
return {
|
|
30
|
+
type: 'Literal',
|
|
31
|
+
value: (_argument$value = argument.value) === null || _argument$value === void 0 ? void 0 : _argument$value.toString()
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
if (isNodeOfType(argument, 'ObjectExpression')) {
|
|
35
|
+
argument;
|
|
36
|
+
return {
|
|
37
|
+
type: 'ObjectExpression',
|
|
38
|
+
value: argument
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/* eslint-disable @repo/internal/react/require-jsdoc */
|
|
2
|
+
|
|
3
|
+
import { insertImportSpecifier, isNodeOfType } from 'eslint-codemod-utils';
|
|
4
|
+
export var Import = {
|
|
5
|
+
/**
|
|
6
|
+
* Note: fixes can't overlap, which means this will fail:
|
|
7
|
+
* ```
|
|
8
|
+
* const importNode = Root.findImportByModule('@atlaskit/primitives')
|
|
9
|
+
* Import.insertNamedSpecifier(importNode, 'Box')
|
|
10
|
+
* Import.insertNamedSpecifier(importNode, 'xcss')
|
|
11
|
+
* ```
|
|
12
|
+
*
|
|
13
|
+
* For this reason `insertNamedSpecifiers` accepts a `specifiers` array, so you can group all inserts together.
|
|
14
|
+
*/
|
|
15
|
+
insertNamedSpecifiers: function insertNamedSpecifiers(node, specifiers, fixer) {
|
|
16
|
+
var _this = this;
|
|
17
|
+
/**
|
|
18
|
+
* `insertImportSpecifier()` has the unfortunate implementation detail of naively adding duplicate specifiers.
|
|
19
|
+
* e.g. calling
|
|
20
|
+
* `insertImportSpecifier(importDecl, 'xcss')`
|
|
21
|
+
* on
|
|
22
|
+
* `import { Inline, xcss } from '@atlaskit/primitives'`
|
|
23
|
+
* will result in:
|
|
24
|
+
* `import { Inline, xcss, xcss } from '@atlaskit/primitives'`.
|
|
25
|
+
* So, we need to filter out specifiers that are already imported.
|
|
26
|
+
*/
|
|
27
|
+
var uniqueSpecifiers = specifiers.filter(function (specifier) {
|
|
28
|
+
return !_this.containsNamedSpecifier(node, specifier);
|
|
29
|
+
});
|
|
30
|
+
if (uniqueSpecifiers.length === 0) {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
return fixer.replaceText(node, "".concat(insertImportSpecifier(node, uniqueSpecifiers.join(', ')), ";\n"));
|
|
34
|
+
},
|
|
35
|
+
containsNamedSpecifier: function containsNamedSpecifier(node, name) {
|
|
36
|
+
return node.specifiers.some(function (specifier) {
|
|
37
|
+
if (!isNodeOfType(specifier, 'ImportSpecifier')) {
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
return specifier.imported.name === name;
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
};
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { isNodeOfType, literal } from 'eslint-codemod-utils';
|
|
2
|
+
var HelperJSXAttribute = {
|
|
3
|
+
getName: function getName(node) {
|
|
4
|
+
if (!isNodeOfType(node, 'JSXAttribute')) {
|
|
5
|
+
throw new Error('Not a JSXAttribute');
|
|
6
|
+
}
|
|
7
|
+
if (!isNodeOfType(node.name, 'JSXIdentifier')) {
|
|
8
|
+
throw new Error('name is not a JSXIdentifier');
|
|
9
|
+
}
|
|
10
|
+
return node.name.name;
|
|
11
|
+
},
|
|
12
|
+
updateName: function updateName(node, name, fixer) {
|
|
13
|
+
if (!isNodeOfType(node, 'JSXAttribute')) {
|
|
14
|
+
throw new Error('Not a JSXAttribute');
|
|
15
|
+
}
|
|
16
|
+
if (!isNodeOfType(node.name, 'JSXIdentifier')) {
|
|
17
|
+
throw new Error('name is not a JSXIdentifier');
|
|
18
|
+
}
|
|
19
|
+
return fixer.replaceText(node.name, literal(name).toString());
|
|
20
|
+
},
|
|
21
|
+
/**
|
|
22
|
+
* A JSXAttribute value can be many things:
|
|
23
|
+
* - css='myStyles'
|
|
24
|
+
* - css={myStyles}
|
|
25
|
+
* - css={[styles1, styles2]}
|
|
26
|
+
* - header={<></>}
|
|
27
|
+
* - css={styleMap.header}
|
|
28
|
+
* - css={...styles}
|
|
29
|
+
*
|
|
30
|
+
* Currently, `getValue` has only implemented strategies for when the value is a string, or an ExpressionStatement
|
|
31
|
+
* If you need additional functionality add it, and set the correct `type` on the returned object
|
|
32
|
+
*/
|
|
33
|
+
getValue: function getValue(node) {
|
|
34
|
+
if (!isNodeOfType(node, 'JSXAttribute')) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
if (!node.value) {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// handle `css={myStyles}`
|
|
42
|
+
if (isNodeOfType(node.value, 'JSXExpressionContainer') && isNodeOfType(node.value.expression, 'Identifier')) {
|
|
43
|
+
return {
|
|
44
|
+
type: 'ExpressionStatement',
|
|
45
|
+
value: node.value.expression.name
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// handle `css='myStyles'`
|
|
50
|
+
if (isNodeOfType(node.value, 'Literal') && node.value.value) {
|
|
51
|
+
var _node$value$value;
|
|
52
|
+
return {
|
|
53
|
+
type: 'Literal',
|
|
54
|
+
value: (_node$value$value = node.value.value) === null || _node$value$value === void 0 ? void 0 : _node$value$value.toString()
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
export { HelperJSXAttribute as JSXAttribute };
|