@atlaskit/eslint-plugin-design-system 8.23.0 → 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.
Files changed (58) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/cjs/ast-nodes/function-call.js +48 -0
  3. package/dist/cjs/ast-nodes/import.js +49 -0
  4. package/dist/cjs/ast-nodes/index.js +40 -0
  5. package/dist/cjs/ast-nodes/jsx-attribute.js +64 -0
  6. package/dist/cjs/ast-nodes/jsx-element.js +55 -0
  7. package/dist/cjs/ast-nodes/root.js +34 -0
  8. package/dist/cjs/rules/ensure-design-token-usage/error-boundary.js +24 -0
  9. package/dist/cjs/rules/ensure-design-token-usage/index.js +208 -177
  10. package/dist/cjs/rules/use-primitives/index.js +6 -76
  11. package/dist/cjs/rules/use-primitives/transformers/emotion-css/index.js +168 -0
  12. package/dist/cjs/rules/use-primitives/transformers/emotion-css/supported.js +52 -0
  13. package/dist/cjs/rules/use-primitives/utils/is-valid-css-properties-to-transform.js +3 -0
  14. package/dist/es2019/ast-nodes/function-call.js +42 -0
  15. package/dist/es2019/ast-nodes/import.js +42 -0
  16. package/dist/es2019/ast-nodes/index.js +5 -0
  17. package/dist/es2019/ast-nodes/jsx-attribute.js +59 -0
  18. package/dist/es2019/ast-nodes/jsx-element.js +50 -0
  19. package/dist/es2019/ast-nodes/root.js +28 -0
  20. package/dist/es2019/rules/ensure-design-token-usage/error-boundary.js +19 -0
  21. package/dist/es2019/rules/ensure-design-token-usage/index.js +33 -16
  22. package/dist/es2019/rules/use-primitives/index.js +9 -79
  23. package/dist/es2019/rules/use-primitives/transformers/emotion-css/index.js +159 -0
  24. package/dist/es2019/rules/use-primitives/transformers/emotion-css/supported.js +46 -0
  25. package/dist/es2019/rules/use-primitives/utils/is-valid-css-properties-to-transform.js +3 -0
  26. package/dist/esm/ast-nodes/function-call.js +42 -0
  27. package/dist/esm/ast-nodes/import.js +43 -0
  28. package/dist/esm/ast-nodes/index.js +5 -0
  29. package/dist/esm/ast-nodes/jsx-attribute.js +59 -0
  30. package/dist/esm/ast-nodes/jsx-element.js +50 -0
  31. package/dist/esm/ast-nodes/root.js +28 -0
  32. package/dist/esm/rules/ensure-design-token-usage/error-boundary.js +18 -0
  33. package/dist/esm/rules/ensure-design-token-usage/index.js +208 -177
  34. package/dist/esm/rules/use-primitives/index.js +9 -79
  35. package/dist/esm/rules/use-primitives/transformers/emotion-css/index.js +158 -0
  36. package/dist/esm/rules/use-primitives/transformers/emotion-css/supported.js +46 -0
  37. package/dist/esm/rules/use-primitives/utils/is-valid-css-properties-to-transform.js +3 -0
  38. package/dist/types/ast-nodes/function-call.d.ts +21 -0
  39. package/dist/types/ast-nodes/import.d.ts +16 -0
  40. package/dist/types/ast-nodes/index.d.ts +5 -0
  41. package/dist/types/ast-nodes/jsx-attribute.d.ts +26 -0
  42. package/dist/types/ast-nodes/jsx-element.d.ts +21 -0
  43. package/dist/types/ast-nodes/root.d.ts +19 -0
  44. package/dist/types/rules/ensure-design-token-usage/error-boundary.d.ts +11 -0
  45. package/dist/types/rules/ensure-design-token-usage/types.d.ts +1 -0
  46. package/dist/types/rules/use-primitives/transformers/emotion-css/index.d.ts +35 -0
  47. package/dist/types/rules/use-primitives/transformers/emotion-css/supported.d.ts +9 -0
  48. package/dist/types-ts4.5/ast-nodes/function-call.d.ts +21 -0
  49. package/dist/types-ts4.5/ast-nodes/import.d.ts +16 -0
  50. package/dist/types-ts4.5/ast-nodes/index.d.ts +5 -0
  51. package/dist/types-ts4.5/ast-nodes/jsx-attribute.d.ts +26 -0
  52. package/dist/types-ts4.5/ast-nodes/jsx-element.d.ts +21 -0
  53. package/dist/types-ts4.5/ast-nodes/root.d.ts +19 -0
  54. package/dist/types-ts4.5/rules/ensure-design-token-usage/error-boundary.d.ts +11 -0
  55. package/dist/types-ts4.5/rules/ensure-design-token-usage/types.d.ts +1 -0
  56. package/dist/types-ts4.5/rules/use-primitives/transformers/emotion-css/index.d.ts +35 -0
  57. package/dist/types-ts4.5/rules/use-primitives/transformers/emotion-css/supported.d.ts +9 -0
  58. package/package.json +1 -1
@@ -0,0 +1,168 @@
1
+ "use strict";
2
+
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
+ var _typeof = require("@babel/runtime/helpers/typeof");
5
+ Object.defineProperty(exports, "__esModule", {
6
+ value: true
7
+ });
8
+ exports.EmotionCSS = void 0;
9
+ var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
10
+ var _eslintCodemodUtils = require("eslint-codemod-utils");
11
+ var ast = _interopRequireWildcard(require("../../../../ast-nodes"));
12
+ var _utils = require("../../utils");
13
+ var _cssToXcss = require("../css-to-xcss");
14
+ var supported = _interopRequireWildcard(require("./supported"));
15
+ function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
16
+ function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || _typeof(obj) !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
17
+ /* eslint-disable @repo/internal/react/require-jsdoc */
18
+
19
+ var EmotionCSS = exports.EmotionCSS = {
20
+ lint: function lint(node, _ref) {
21
+ var context = _ref.context,
22
+ config = _ref.config;
23
+ if (!(0, _eslintCodemodUtils.isNodeOfType)(node, 'JSXElement')) {
24
+ return;
25
+ }
26
+
27
+ // Check whether all criteria needed to make a transformation are met
28
+ if (!EmotionCSS._check(node, {
29
+ context: context,
30
+ config: config
31
+ })) {
32
+ return;
33
+ }
34
+ context.report({
35
+ node: node.openingElement,
36
+ messageId: 'preferPrimitivesBox',
37
+ suggest: [{
38
+ desc: "Convert to Box",
39
+ fix: EmotionCSS._fix(node, {
40
+ context: context
41
+ })
42
+ }]
43
+ });
44
+ },
45
+ _check: function _check(node, _ref2) {
46
+ var _cssVariableValue$nod, _cssVariableValue$nod2;
47
+ var context = _ref2.context,
48
+ config = _ref2.config;
49
+ if (!config.patterns.includes('compiled-css-function')) {
50
+ return false;
51
+ }
52
+ if (!(0, _eslintCodemodUtils.isNodeOfType)(node, 'JSXElement')) {
53
+ return false;
54
+ }
55
+ var elementName = ast.JSXElement.getName(node);
56
+ if (!elementName) {
57
+ return false;
58
+ }
59
+
60
+ // Currently we only support `div`. This may change in future.
61
+ if (!supported.elements.includes(elementName)) {
62
+ return false;
63
+ }
64
+
65
+ // Ignore elements that contain dangerous attributes like `id`.
66
+ if (!this._containsOnlySupportedAttributes(node)) {
67
+ return false;
68
+ }
69
+
70
+ // Currently we don't transform anything to `Box` unless it defines styles
71
+ var cssAttr = ast.JSXElement.getAttributeByName(node, 'css');
72
+ if (!cssAttr) {
73
+ return false;
74
+ }
75
+
76
+ // Get `myStyles` in `css={myStyles}` as a string so we can search for the `const myStyles` VariableDefinition
77
+ var cssAttrValue = ast.JSXAttribute.getValue(cssAttr);
78
+ if ((cssAttrValue === null || cssAttrValue === void 0 ? void 0 : cssAttrValue.type) !== 'ExpressionStatement') {
79
+ return false;
80
+ }
81
+
82
+ // TODO: Everything below this line could be refactored to use `ast-nodes`.
83
+
84
+ // Bail if the styles are used on multiple JSXElements
85
+ if ((0, _utils.getVariableUsagesCount)(cssAttrValue.value, context) !== 1) {
86
+ return false;
87
+ }
88
+
89
+ // Find where `myStyles` is defined. We're looking for `const myStyles = css({...})`
90
+ var cssVariableDefinition = (0, _eslintCodemodUtils.getIdentifierInParentScope)(context.getScope(), cssAttrValue.value);
91
+ var cssVariableValue = (0, _utils.getVariableDefinitionValue)(cssVariableDefinition);
92
+ // Check if `cssVariableValue` is a function called `css()`
93
+ if (ast.FunctionCall.getName(cssVariableValue === null || cssVariableValue === void 0 || (_cssVariableValue$nod = cssVariableValue.node) === null || _cssVariableValue$nod === void 0 ? void 0 : _cssVariableValue$nod.init) !== 'css') {
94
+ return false;
95
+ }
96
+ if (!(0, _utils.isValidCssPropertiesToTransform)(cssVariableValue === null || cssVariableValue === void 0 || (_cssVariableValue$nod2 = cssVariableValue.node) === null || _cssVariableValue$nod2 === void 0 ? void 0 : _cssVariableValue$nod2.init, config)) {
97
+ return false;
98
+ }
99
+ var importDeclaration = ast.Root.findImportsByModule(context.getSourceCode().ast.body, '@atlaskit/primitives');
100
+
101
+ // If there is more than one `@atlaskit/primitives` import, then it becomes difficult to determine which import to transform
102
+ if (importDeclaration.length > 1) {
103
+ return false;
104
+ }
105
+ return true;
106
+ },
107
+ _fix: function _fix(node, _ref3) {
108
+ var _this = this;
109
+ var context = _ref3.context;
110
+ return function (fixer) {
111
+ var importFix = _this._upsertImportDeclaration({
112
+ module: '@atlaskit/primitives',
113
+ specifiers: ['Box', 'xcss']
114
+ }, context, fixer);
115
+ var cssAttr = ast.JSXElement.getAttributeByName(node, 'css'); // Can strongly assert the type here, because we validated it exists in `check()`.
116
+ var attributeFix = ast.JSXAttribute.updateName(cssAttr, 'xcss', fixer);
117
+ var elementNameFixes = ast.JSXElement.updateName(node, 'Box', fixer);
118
+ var cssToXcssTransform = (0, _cssToXcss.cssToXcssTransformer)(node, context, fixer);
119
+ return [importFix, attributeFix].concat((0, _toConsumableArray2.default)(elementNameFixes), (0, _toConsumableArray2.default)(cssToXcssTransform)).filter(function (fix) {
120
+ return Boolean(fix);
121
+ }); // Some of the transformers can return arrays with undefined, so filter them out
122
+ };
123
+ },
124
+ /**
125
+ * Check that every attribute in the JSXElement is something we support.
126
+ * We do this via a whitelist in `this.attributes`. The result is we exclude
127
+ * dangerous attrs like `id` and `style`.
128
+ */
129
+ _containsOnlySupportedAttributes: function _containsOnlySupportedAttributes(node) {
130
+ var attrs = ast.JSXElement.getAttributes(node);
131
+ return attrs.every(function (attr) {
132
+ if (!(0, _eslintCodemodUtils.isNodeOfType)(attr, 'JSXAttribute')) {
133
+ return false;
134
+ }
135
+ if (!(0, _eslintCodemodUtils.isNodeOfType)(attr.name, 'JSXIdentifier')) {
136
+ return false;
137
+ }
138
+ return supported.attributes.includes(attr.name.name);
139
+ });
140
+ },
141
+ /**
142
+ * Currently this is defined here because it's not very general purpose.
143
+ * If we were to move this to `ast-nodes`, half the implementation would be in `Root`,
144
+ * and the other half would be in `Import`.
145
+ *
146
+ * TODO: Refactor and move to `ast-nodes`
147
+ *
148
+ * Note: It does not handle default imports, namespace imports, or aliased imports.
149
+ */
150
+ _upsertImportDeclaration: function _upsertImportDeclaration(_ref4, context, fixer) {
151
+ var module = _ref4.module,
152
+ specifiers = _ref4.specifiers;
153
+ // Find any imports that match the packageName
154
+ var root = context.getSourceCode().ast.body;
155
+ var importDeclarations = ast.Root.findImportsByModule(root, module);
156
+
157
+ // The import doesn't exist yet, we can just insert a whole new one
158
+ if (importDeclarations.length === 0) {
159
+ return ast.Root.insertImport(root, {
160
+ module: module,
161
+ specifiers: specifiers
162
+ }, fixer);
163
+ }
164
+
165
+ // The import exists so, modify the existing one
166
+ return ast.Import.insertNamedSpecifiers(importDeclarations[0], specifiers, fixer);
167
+ }
168
+ };
@@ -0,0 +1,52 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.styles = exports.elements = exports.attributes = void 0;
7
+ var elements = exports.elements = ['div'];
8
+ var attributes = exports.attributes = ['css'
9
+ // 'data-testid'
10
+ ];
11
+
12
+ // TODO: https://product-fabric.atlassian.net/browse/DSP-16054
13
+ var spaceTokenMap = {
14
+ '0px': 'space.0',
15
+ '2px': 'space.025',
16
+ '4px': 'space.050',
17
+ '6px': 'space.075',
18
+ '8px': 'space.100',
19
+ '12px': 'space.150',
20
+ '16px': 'space.200',
21
+ '20px': 'space.250',
22
+ '24px': 'space.300',
23
+ '32px': 'space.400',
24
+ '40px': 'space.500',
25
+ '48px': 'space.600',
26
+ '64px': 'space.800',
27
+ '80px': 'space.1000'
28
+ };
29
+ var styles = exports.styles = {
30
+ padding: spaceTokenMap,
31
+ paddingBlock: spaceTokenMap,
32
+ paddingBlockEnd: spaceTokenMap,
33
+ paddingBlockStart: spaceTokenMap,
34
+ paddingBottom: spaceTokenMap,
35
+ paddingInline: spaceTokenMap,
36
+ paddingInlineEnd: spaceTokenMap,
37
+ paddingInlineStart: spaceTokenMap,
38
+ paddingLeft: spaceTokenMap,
39
+ paddingRight: spaceTokenMap,
40
+ paddingTop: spaceTokenMap,
41
+ margin: spaceTokenMap,
42
+ marginBlock: spaceTokenMap,
43
+ marginBlockEnd: spaceTokenMap,
44
+ marginBlockStart: spaceTokenMap,
45
+ marginBottom: spaceTokenMap,
46
+ marginInline: spaceTokenMap,
47
+ marginInlineEnd: spaceTokenMap,
48
+ marginInlineStart: spaceTokenMap,
49
+ marginLeft: spaceTokenMap,
50
+ marginRight: spaceTokenMap,
51
+ marginTop: spaceTokenMap
52
+ };
@@ -12,6 +12,9 @@ var _cssToXcss = require("../transformers/css-to-xcss");
12
12
  var _convertAstObjectExpressionToJsObject = require("./convert-ast-object-expression-to-js-object");
13
13
  var _excluded = ["unsupported"];
14
14
  var isValidCssPropertiesToTransform = exports.isValidCssPropertiesToTransform = function isValidCssPropertiesToTransform(node, config) {
15
+ if (!node) {
16
+ return false;
17
+ }
15
18
  var cssObjectExpression = node.arguments[0];
16
19
  // Bail on empty object calls
17
20
  if (!cssObjectExpression || !(0, _eslintCodemodUtils.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 const FunctionCall = {
5
+ 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(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(node, pos) {
26
+ const 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,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,5 @@
1
+ export { FunctionCall } from './function-call';
2
+ export { Import } from './import';
3
+ export { JSXAttribute } from './jsx-attribute';
4
+ export { JSXElement } from './jsx-element';
5
+ export { Root } from './root';
@@ -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
+ };
@@ -0,0 +1,19 @@
1
+ /**
2
+ * ESLint rules should NEVER throw exceptions, because that breaks the VSCode ESLint server
3
+ * (and probably the IntelliJ one too), which causes linting to fail in a file.
4
+ *
5
+ * It also breaks CI, which was the reason this error boundary was added. It's a final
6
+ * catch all.
7
+ */
8
+ export const errorBoundary = (func, {
9
+ config
10
+ }) => {
11
+ try {
12
+ func();
13
+ } catch (err) {
14
+ if (!config.failSilently) {
15
+ // eslint-disable-next-line no-console
16
+ console.warn(err);
17
+ }
18
+ }
19
+ };
@@ -5,13 +5,15 @@ import { createLintRule } from '../utils/create-rule';
5
5
  import { includesHardCodedColor } from '../utils/is-color';
6
6
  import { isDecendantOfGlobalToken, isDecendantOfStyleBlock, isDecendantOfType } from '../utils/is-node';
7
7
  import { lintJSXIdentifierForColor, lintJSXLiteralForColor, lintJSXMemberForColor, lintObjectForColor, lintTemplateIdentifierForColor } from './color';
8
+ import { errorBoundary } from './error-boundary';
8
9
  import ruleMeta from './rule-meta';
9
10
  import { lintObjectForSpacing } from './spacing';
10
11
  import { convertHyphenatedNameToCamelCase, emToPixels, getDomainsForProperty, getFontSizeFromNode, getFontSizeValueInScope, getTokenReplacement, getValueFromShorthand, getValueFromTemplateLiteralRaw, includesTokenString, insertTokensImport, isAuto, isCalc, isTokenValueString, isValidSpacingValue, isZero, processCssNode, splitShorthandValues } from './utils';
11
12
  const defaultConfig = {
12
13
  domains: ['color', 'spacing'],
13
14
  applyImport: true,
14
- shouldEnforceFallbacks: false
15
+ shouldEnforceFallbacks: false,
16
+ failSilently: false
15
17
  };
16
18
  const createWithConfig = initialConfig => context => {
17
19
  const userConfig = context.options[0];
@@ -20,21 +22,26 @@ const createWithConfig = initialConfig => context => {
20
22
  domains: (userConfig === null || userConfig === void 0 ? void 0 : userConfig.domains) || initialConfig.domains,
21
23
  applyImport: (userConfig === null || userConfig === void 0 ? void 0 : userConfig.applyImport) !== undefined ? userConfig.applyImport : initialConfig.applyImport,
22
24
  shouldEnforceFallbacks: (userConfig === null || userConfig === void 0 ? void 0 : userConfig.shouldEnforceFallbacks) !== undefined ? userConfig.shouldEnforceFallbacks : initialConfig.shouldEnforceFallbacks,
23
- exceptions: (userConfig === null || userConfig === void 0 ? void 0 : userConfig.exceptions) || []
25
+ exceptions: (userConfig === null || userConfig === void 0 ? void 0 : userConfig.exceptions) || [],
26
+ failSilently: (userConfig === null || userConfig === void 0 ? void 0 : userConfig.failSilently) || defaultConfig.failSilently
24
27
  };
25
28
  let tokenNode = null;
26
29
  return {
27
- ImportDeclaration(node) {
30
+ ImportDeclaration: node => errorBoundary(() => {
28
31
  if (node.source.value === '@atlaskit/tokens' && config.applyImport) {
29
32
  tokenNode = node;
30
33
  }
31
- },
34
+ }, {
35
+ config
36
+ }),
32
37
  // For expressions within template literals (e.g. `color: ${red}`) - color only
33
- 'TemplateLiteral > Identifier': node => {
38
+ 'TemplateLiteral > Identifier': node => errorBoundary(() => {
34
39
  return lintTemplateIdentifierForColor(node, context, config);
35
- },
40
+ }, {
41
+ config
42
+ }),
36
43
  // const styles = css({ color: 'red', margin: '4px' }), styled.div({ color: 'red', margin: '4px' })
37
- ObjectExpression: parentNode => {
44
+ ObjectExpression: parentNode => errorBoundary(() => {
38
45
  // To force the correct node type
39
46
  if (!isNodeOfType(parentNode, 'ObjectExpression')) {
40
47
  return;
@@ -91,11 +98,13 @@ const createWithConfig = initialConfig => context => {
91
98
  }
92
99
  }
93
100
  parentNode.properties.forEach(findObjectStyles);
94
- },
101
+ }, {
102
+ config
103
+ }),
95
104
  // CSSTemplateLiteral and StyledTemplateLiteral
96
105
  // const cssTemplateLiteral = css`color: red; padding: 12px`;
97
106
  // const styledTemplateLiteral = styled.p`color: red; padding: 8px`;
98
- 'TaggedTemplateExpression[tag.name="css"],TaggedTemplateExpression[tag.object.name="styled"],TaggedTemplateExpression[tag.callee.name="styled"]': node => {
107
+ 'TaggedTemplateExpression[tag.name="css"],TaggedTemplateExpression[tag.object.name="styled"],TaggedTemplateExpression[tag.callee.name="styled"]': node => errorBoundary(() => {
99
108
  // To force the correct node type
100
109
  if (!isNodeOfType(node, 'TaggedTemplateExpression')) {
101
110
  return;
@@ -223,19 +232,27 @@ const createWithConfig = initialConfig => context => {
223
232
  }
224
233
  });
225
234
  }
226
- },
235
+ }, {
236
+ config
237
+ }),
227
238
  // For inline JSX styles - literals (e.g. <Test color="red"/>) - color only
228
- 'JSXAttribute > Literal': node => {
239
+ 'JSXAttribute > Literal': node => errorBoundary(() => {
229
240
  return lintJSXLiteralForColor(node, context, config);
230
- },
241
+ }, {
242
+ config
243
+ }),
231
244
  // For inline JSX styles - members (e.g. <Test color={color.red}/>) - color only
232
- 'JSXExpressionContainer > MemberExpression': node => {
245
+ 'JSXExpressionContainer > MemberExpression': node => errorBoundary(() => {
233
246
  return lintJSXMemberForColor(node, context, config);
234
- },
247
+ }, {
248
+ config
249
+ }),
235
250
  // For inline JSX styles - identifiers (e.g. <Test color={red}/>) - color only
236
- 'JSXExpressionContainer > Identifier': node => {
251
+ 'JSXExpressionContainer > Identifier': node => errorBoundary(() => {
237
252
  return lintJSXIdentifierForColor(node, context, config);
238
- }
253
+ }, {
254
+ config
255
+ })
239
256
  };
240
257
  };
241
258
  const rule = createLintRule({