@atlaskit/eslint-plugin-design-system 8.23.3 → 8.23.4

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 (54) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/README.md +1 -0
  3. package/constellation/index/usage.mdx +39 -0
  4. package/dist/cjs/ast-nodes/index.js +7 -0
  5. package/dist/cjs/ast-nodes/object.js +84 -0
  6. package/dist/cjs/presets/all.codegen.js +2 -1
  7. package/dist/cjs/presets/recommended.codegen.js +2 -1
  8. package/dist/cjs/rules/index.codegen.js +3 -1
  9. package/dist/cjs/rules/local-cx-xcss/index.js +51 -0
  10. package/dist/cjs/rules/no-css-tagged-template-expression/create-no-tagged-template-expression-rule/index.js +3 -1
  11. package/dist/cjs/rules/no-css-tagged-template-expression/index.js +2 -2
  12. package/dist/cjs/rules/utils/is-supported-import.js +61 -0
  13. package/dist/es2019/ast-nodes/index.js +1 -0
  14. package/dist/es2019/ast-nodes/object.js +79 -0
  15. package/dist/es2019/presets/all.codegen.js +2 -1
  16. package/dist/es2019/presets/recommended.codegen.js +2 -1
  17. package/dist/es2019/rules/index.codegen.js +3 -1
  18. package/dist/es2019/rules/local-cx-xcss/index.js +45 -0
  19. package/dist/es2019/rules/no-css-tagged-template-expression/create-no-tagged-template-expression-rule/index.js +3 -1
  20. package/dist/es2019/rules/no-css-tagged-template-expression/index.js +2 -2
  21. package/dist/es2019/rules/utils/is-supported-import.js +52 -0
  22. package/dist/esm/ast-nodes/index.js +1 -0
  23. package/dist/esm/ast-nodes/object.js +79 -0
  24. package/dist/esm/presets/all.codegen.js +2 -1
  25. package/dist/esm/presets/recommended.codegen.js +2 -1
  26. package/dist/esm/rules/index.codegen.js +3 -1
  27. package/dist/esm/rules/local-cx-xcss/index.js +45 -0
  28. package/dist/esm/rules/no-css-tagged-template-expression/create-no-tagged-template-expression-rule/index.js +3 -1
  29. package/dist/esm/rules/no-css-tagged-template-expression/index.js +2 -2
  30. package/dist/esm/rules/utils/is-supported-import.js +54 -0
  31. package/dist/types/ast-nodes/index.d.ts +1 -0
  32. package/dist/types/ast-nodes/object.d.ts +39 -0
  33. package/dist/types/index.codegen.d.ts +2 -0
  34. package/dist/types/presets/all.codegen.d.ts +2 -1
  35. package/dist/types/presets/recommended.codegen.d.ts +2 -1
  36. package/dist/types/rules/index.codegen.d.ts +1 -0
  37. package/dist/types/rules/local-cx-xcss/index.d.ts +3 -0
  38. package/dist/types/rules/no-css-tagged-template-expression/create-no-tagged-template-expression-rule/index.d.ts +3 -4
  39. package/dist/types/rules/utils/is-supported-import.d.ts +16 -0
  40. package/dist/types-ts4.5/ast-nodes/index.d.ts +1 -0
  41. package/dist/types-ts4.5/ast-nodes/object.d.ts +39 -0
  42. package/dist/types-ts4.5/index.codegen.d.ts +2 -0
  43. package/dist/types-ts4.5/presets/all.codegen.d.ts +2 -1
  44. package/dist/types-ts4.5/presets/recommended.codegen.d.ts +2 -1
  45. package/dist/types-ts4.5/rules/index.codegen.d.ts +1 -0
  46. package/dist/types-ts4.5/rules/local-cx-xcss/index.d.ts +3 -0
  47. package/dist/types-ts4.5/rules/no-css-tagged-template-expression/create-no-tagged-template-expression-rule/index.d.ts +3 -4
  48. package/dist/types-ts4.5/rules/utils/is-supported-import.d.ts +16 -0
  49. package/package.json +1 -1
  50. package/dist/cjs/rules/no-css-tagged-template-expression/is-supported-import.js +0 -29
  51. package/dist/es2019/rules/no-css-tagged-template-expression/is-supported-import.js +0 -21
  52. package/dist/esm/rules/no-css-tagged-template-expression/is-supported-import.js +0 -23
  53. package/dist/types/rules/no-css-tagged-template-expression/is-supported-import.d.ts +0 -12
  54. package/dist/types-ts4.5/rules/no-css-tagged-template-expression/is-supported-import.d.ts +0 -12
package/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # @atlaskit/eslint-plugin-design-system
2
2
 
3
+ ## 8.23.4
4
+
5
+ ### Patch Changes
6
+
7
+ - [#69370](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/pull-requests/69370) [`e14e79732cd4`](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/commits/e14e79732cd4) - Add local-cx-xcss rule, which ensures that the `cx()` function (if imported from `@compiled/react` or `@atlaskit/css`) is only used within the `xcss` prop.
8
+
9
+ Internal changes: Also refactored the utility functions used by `no-css-tagged-template-expression`.
10
+
3
11
  ## 8.23.3
4
12
 
5
13
  ### Patch Changes
package/README.md CHANGED
@@ -53,6 +53,7 @@ module.exports = {
53
53
  | <a href="./src/rules/ensure-design-token-usage/README.md">ensure-design-token-usage</a> | Enforces usage of design tokens rather than hard-coded values. | Yes | Yes | Yes |
54
54
  | <a href="./src/rules/ensure-design-token-usage-preview/README.md">ensure-design-token-usage/preview</a> | Enforces usage of pre-release design tokens rather than hard-coded values. | | Yes | Yes |
55
55
  | <a href="./src/rules/icon-label/README.md">icon-label</a> | Enforces accessible usage of icon labels when composed with Atlassian Design System components. | Yes | Yes | |
56
+ | <a href="./src/rules/local-cx-xcss/README.md">local-cx-xcss</a> | Ensures the cx() function, which is part of the XCSS API, is only used within the xcss prop. This aids tracking what styles are applied to a jsx element. | Yes | | |
56
57
  | <a href="./src/rules/no-banned-imports/README.md">no-banned-imports</a> | Disallow importing banned modules. | Yes | | |
57
58
  | <a href="./src/rules/no-css-tagged-template-expression/README.md">no-css-tagged-template-expression</a> | Disallows any `css` tagged template expressions that originate from Emotion, Styled Components or Compiled | | Yes | |
58
59
  | <a href="./src/rules/no-deprecated-apis/README.md">no-deprecated-apis</a> | Disallow using deprecated APIs. | Yes | | |
@@ -17,6 +17,7 @@ This plugin contains rules that should be used when working with the [Atlassian
17
17
  | <a href="#ensure-design-token-usage">ensure-design-token-usage</a> | Enforces usage of design tokens rather than hard-coded values. | Yes | Yes | Yes |
18
18
  | <a href="#ensure-design-token-usage-preview">ensure-design-token-usage/preview</a> | Enforces usage of pre-release design tokens rather than hard-coded values. | | Yes | Yes |
19
19
  | <a href="#icon-label">icon-label</a> | Enforces accessible usage of icon labels when composed with Atlassian Design System components. | Yes | Yes | |
20
+ | <a href="#local-cx-xcss">local-cx-xcss</a> | Ensures the cx() function, which is part of the XCSS API, is only used within the xcss prop. This aids tracking what styles are applied to a jsx element. | Yes | | |
20
21
  | <a href="#no-banned-imports">no-banned-imports</a> | Disallow importing banned modules. | Yes | | |
21
22
  | <a href="#no-css-tagged-template-expression">no-css-tagged-template-expression</a> | Disallows any `css` tagged template expressions that originate from Emotion, Styled Components or Compiled | | Yes | |
22
23
  | <a href="#no-deprecated-apis">no-deprecated-apis</a> | Disallow using deprecated APIs. | Yes | | |
@@ -318,6 +319,44 @@ import ActivityIcon from '@atlaskit/icon/glyph/activity'
318
319
  </ButtonItem>
319
320
  ```
320
321
 
322
+ ## local-cx-xcss
323
+
324
+ This rule ensures the `cx()` function is only used within the `xcss` prop. This aids tracking what styles are applied to a jsx element.
325
+
326
+ The `cx` function is checked only if it is imported from `@compiled/react` or `@atlaskit/css`.
327
+
328
+ Passing arguments to the `cx()` function is how you compose styles (combine more than one set of styles together) with XCSS. This is a workaround for the more conventional array syntax (e.g. [in Emotion](https://emotion.sh/docs/composition)) `<div xcss={[style1, style2]} />` not giving robust enough type checking.
329
+
330
+ <h3>Examples</h3>
331
+
332
+ #### Incorrect
333
+
334
+ ```js
335
+ import { cx, cssMap } from '@compiled/react';
336
+
337
+ const styles = cssMap({
338
+ text: { color: 'red' },
339
+ bg: { background: 'blue' },
340
+ });
341
+
342
+ const joinedStyles = cx(styles.text, styles.bg);
343
+
344
+ <Button xcss={joinedStyles} />;
345
+ ```
346
+
347
+ #### Correct
348
+
349
+ ```js
350
+ import { cx, cssMap } from '@compiled/react';
351
+
352
+ const styles = cssMap({
353
+ text: { color: 'red' },
354
+ bg: { background: 'blue' },
355
+ });
356
+
357
+ <Button xcss={cx(styles.text, styles.bg)} />;
358
+ ```
359
+
321
360
  ## no-banned-imports
322
361
 
323
362
  Using private or experimental packages is dangerous as they are not supported across major versions meaning you will not be able to migrate easily causing friction for yourself and the Atlassian Design System team.
@@ -27,6 +27,12 @@ Object.defineProperty(exports, "JSXElement", {
27
27
  return _jsxElement.JSXElement;
28
28
  }
29
29
  });
30
+ Object.defineProperty(exports, "Object", {
31
+ enumerable: true,
32
+ get: function get() {
33
+ return _object.Object;
34
+ }
35
+ });
30
36
  Object.defineProperty(exports, "Root", {
31
37
  enumerable: true,
32
38
  get: function get() {
@@ -37,4 +43,5 @@ var _functionCall = require("./function-call");
37
43
  var _import = require("./import");
38
44
  var _jsxAttribute = require("./jsx-attribute");
39
45
  var _jsxElement = require("./jsx-element");
46
+ var _object = require("./object");
40
47
  var _root = require("./root");
@@ -0,0 +1,84 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.Object = void 0;
7
+ var _eslintCodemodUtils = require("eslint-codemod-utils");
8
+ /* eslint-disable @repo/internal/react/require-jsdoc */
9
+
10
+ var ASTObjectExpression = exports.Object = {
11
+ /**
12
+ * Returns `true` if an object contains a property with the specified name, `false` otherwise.
13
+ */
14
+ hasProperty: function hasProperty(node, name) {
15
+ return !!ASTObjectExpression.getProperty(node, name);
16
+ },
17
+ /**
18
+ * Returns a key-value pair like: `padding: '8px'` from: `{ padding: '8px' }`
19
+ */
20
+ getEntryByPropertyName: function getEntryByPropertyName(node, name) {
21
+ return node.properties.find(function (property) {
22
+ if (!(0, _eslintCodemodUtils.isNodeOfType)(property, 'Property')) {
23
+ return false;
24
+ }
25
+ if (!(0, _eslintCodemodUtils.isNodeOfType)(property.key, 'Identifier')) {
26
+ return false;
27
+ }
28
+ return property.key.name === name;
29
+ });
30
+ },
31
+ deleteEntry: function deleteEntry(node, name, fixer) {
32
+ var entry = ASTObjectExpression.getEntryByPropertyName(node, name);
33
+ if (!entry) {
34
+ return [];
35
+ }
36
+ return [fixer.remove(entry)];
37
+ },
38
+ /**
39
+ * Returns a only the property @type {Property['key']} like: `padding` from: `{ padding: '8px' }`
40
+ */
41
+ getProperty: function getProperty(node, name) {
42
+ var _ASTObjectExpression$;
43
+ return (_ASTObjectExpression$ = ASTObjectExpression.getEntryByPropertyName(node, name)) === null || _ASTObjectExpression$ === void 0 ? void 0 : _ASTObjectExpression$.key;
44
+ },
45
+ /**
46
+ * Returns a only the property @type {Property['value']} like: `'8px` from: `{ padding: '8px' }`
47
+ *
48
+ * Values can be basically anything, so be careful with this
49
+ */
50
+ getValueByPropertyName: function getValueByPropertyName(node, name) {
51
+ var _ASTObjectExpression$2;
52
+ return (_ASTObjectExpression$2 = ASTObjectExpression.getEntryByPropertyName(node, name)) === null || _ASTObjectExpression$2 === void 0 ? void 0 : _ASTObjectExpression$2.value;
53
+ },
54
+ containsSpreadProps: function containsSpreadProps(node) {
55
+ return node.properties.some(function (property) {
56
+ return (0, _eslintCodemodUtils.isNodeOfType)(property, 'SpreadElement');
57
+ });
58
+ },
59
+ updateValue: function updateValue(node, propertyName, newValue, fixer) {
60
+ var value = ASTObjectExpression.getValueByPropertyName(node, propertyName);
61
+ if (value === undefined) {
62
+ throw new Error("Object.updateValue: Could not get value of property ".concat(propertyName));
63
+ }
64
+ return fixer.replaceText(value, newValue);
65
+ },
66
+ /**
67
+ * Appends a key-value pair to the end of an object. For example:
68
+ * ```
69
+ * ast.Object.appendEntry(
70
+ * node, // { padding: 'space.100' }
71
+ * key, // 'margin',
72
+ * value, // 'space.200'
73
+ * fixer,
74
+ * )
75
+ * ```
76
+ * Will result in `{ padding: 'space.100', margin: 'space.200'}`
77
+ */
78
+ appendEntry: function appendEntry(node, key, value, fixer) {
79
+ return fixer.insertTextAfter(node.properties[node.properties.length - 1], "".concat((0, _eslintCodemodUtils.property)({
80
+ key: (0, _eslintCodemodUtils.identifier)(key),
81
+ value: (0, _eslintCodemodUtils.literal)(value)
82
+ }).toString(), ", "));
83
+ }
84
+ };
@@ -6,7 +6,7 @@ Object.defineProperty(exports, "__esModule", {
6
6
  exports.default = void 0;
7
7
  /**
8
8
  * THIS FILE WAS CREATED VIA CODEGEN DO NOT MODIFY {@see http://go/af-codegen}
9
- * @codegen <<SignedSource::d1a459e1ea71650f65b2890dc86cc398>>
9
+ * @codegen <<SignedSource::5026ba2cb55b3c1bcacbfe7fb7728a6c>>
10
10
  * @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
11
11
  */
12
12
  var _default = exports.default = {
@@ -16,6 +16,7 @@ var _default = exports.default = {
16
16
  '@atlaskit/design-system/ensure-design-token-usage': 'error',
17
17
  '@atlaskit/design-system/ensure-design-token-usage/preview': 'warn',
18
18
  '@atlaskit/design-system/icon-label': 'warn',
19
+ '@atlaskit/design-system/local-cx-xcss': 'error',
19
20
  '@atlaskit/design-system/no-banned-imports': 'error',
20
21
  '@atlaskit/design-system/no-css-tagged-template-expression': 'error',
21
22
  '@atlaskit/design-system/no-deprecated-apis': 'error',
@@ -6,7 +6,7 @@ Object.defineProperty(exports, "__esModule", {
6
6
  exports.default = void 0;
7
7
  /**
8
8
  * THIS FILE WAS CREATED VIA CODEGEN DO NOT MODIFY {@see http://go/af-codegen}
9
- * @codegen <<SignedSource::3b93cfbbe0ea14514b9600509632394b>>
9
+ * @codegen <<SignedSource::839224bfab98c1ddf6018dec5320968e>>
10
10
  * @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
11
11
  */
12
12
  var _default = exports.default = {
@@ -15,6 +15,7 @@ var _default = exports.default = {
15
15
  '@atlaskit/design-system/consistent-css-prop-usage': 'error',
16
16
  '@atlaskit/design-system/ensure-design-token-usage': 'error',
17
17
  '@atlaskit/design-system/icon-label': 'warn',
18
+ '@atlaskit/design-system/local-cx-xcss': 'error',
18
19
  '@atlaskit/design-system/no-banned-imports': 'error',
19
20
  '@atlaskit/design-system/no-deprecated-apis': 'error',
20
21
  '@atlaskit/design-system/no-deprecated-design-token-usage': 'warn',
@@ -9,6 +9,7 @@ var _consistentCssPropUsage = _interopRequireDefault(require("./consistent-css-p
9
9
  var _ensureDesignTokenUsage = _interopRequireDefault(require("./ensure-design-token-usage"));
10
10
  var _ensureDesignTokenUsagePreview = _interopRequireDefault(require("./ensure-design-token-usage-preview"));
11
11
  var _iconLabel = _interopRequireDefault(require("./icon-label"));
12
+ var _localCxXcss = _interopRequireDefault(require("./local-cx-xcss"));
12
13
  var _noBannedImports = _interopRequireDefault(require("./no-banned-imports"));
13
14
  var _noCssTaggedTemplateExpression = _interopRequireDefault(require("./no-css-tagged-template-expression"));
14
15
  var _noDeprecatedApis = _interopRequireDefault(require("./no-deprecated-apis"));
@@ -29,7 +30,7 @@ var _usePrimitives = _interopRequireDefault(require("./use-primitives"));
29
30
  var _useVisuallyHidden = _interopRequireDefault(require("./use-visually-hidden"));
30
31
  /**
31
32
  * THIS FILE WAS CREATED VIA CODEGEN DO NOT MODIFY {@see http://go/af-codegen}
32
- * @codegen <<SignedSource::14cdfdcbd8b999ee097a1a5b245d7117>>
33
+ * @codegen <<SignedSource::a67afc33adf50db651edb1cf12e16ef3>>
33
34
  * @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
34
35
  */
35
36
  var _default = exports.default = {
@@ -37,6 +38,7 @@ var _default = exports.default = {
37
38
  'ensure-design-token-usage': _ensureDesignTokenUsage.default,
38
39
  'ensure-design-token-usage/preview': _ensureDesignTokenUsagePreview.default,
39
40
  'icon-label': _iconLabel.default,
41
+ 'local-cx-xcss': _localCxXcss.default,
40
42
  'no-banned-imports': _noBannedImports.default,
41
43
  'no-css-tagged-template-expression': _noCssTaggedTemplateExpression.default,
42
44
  'no-deprecated-apis': _noDeprecatedApis.default,
@@ -0,0 +1,51 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.rule = exports.default = void 0;
7
+ var _createRule = require("../utils/create-rule");
8
+ var _isSupportedImport = require("../utils/is-supported-import");
9
+ var IMPORT_SOURCES = [_isSupportedImport.CSS_IN_JS_IMPORTS.compiled, _isSupportedImport.CSS_IN_JS_IMPORTS.atlaskitCss];
10
+ function getParentJSXAttribute(node) {
11
+ var parent = node.parent;
12
+ while (parent && parent.type !== 'JSXAttribute') {
13
+ parent = parent.parent;
14
+ }
15
+ if (parent && parent.type === 'JSXAttribute') {
16
+ return parent;
17
+ }
18
+ return null;
19
+ }
20
+ var rule = exports.rule = (0, _createRule.createLintRule)({
21
+ meta: {
22
+ name: 'local-cx-xcss',
23
+ docs: {
24
+ description: 'Ensures the cx() function, which is part of the XCSS API, is only used within the xcss prop. This aids tracking what styles are applied to a jsx element.',
25
+ recommended: true,
26
+ severity: 'error'
27
+ },
28
+ messages: {
29
+ 'local-cx-xcss': 'The cx function should only be declared inside the xcss prop to simplify tracking styles that are applied to a jsx element.'
30
+ },
31
+ type: 'problem'
32
+ },
33
+ create: function create(context) {
34
+ return {
35
+ 'CallExpression[callee.name="cx"]': function CallExpressionCalleeNameCx(node) {
36
+ if (node.type === 'CallExpression' && (0, _isSupportedImport.isCxFunction)(node.callee, context.getScope().references, IMPORT_SOURCES)) {
37
+ var parentJSXAttribute = getParentJSXAttribute(node);
38
+ var propName = parentJSXAttribute === null || parentJSXAttribute === void 0 ? void 0 : parentJSXAttribute.name.name.toString();
39
+ if (propName && /[xX]css$/.test(propName)) {
40
+ return;
41
+ }
42
+ context.report({
43
+ node: node,
44
+ messageId: 'local-cx-xcss'
45
+ });
46
+ }
47
+ }
48
+ };
49
+ }
50
+ });
51
+ var _default = exports.default = rule;
@@ -6,19 +6,21 @@ Object.defineProperty(exports, "__esModule", {
6
6
  });
7
7
  exports.createNoTaggedTemplateExpressionRule = void 0;
8
8
  var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"));
9
+ var _isSupportedImport = require("../../utils/is-supported-import");
9
10
  var _generate = require("./generate");
10
11
  var _getTaggedTemplateExpressionOffset = require("./get-tagged-template-expression-offset");
11
12
  var _toArguments = require("./to-arguments");
12
13
  // Original source from Compiled https://github.com/atlassian-labs/compiled/blob/master/packages/eslint-plugin/src/utils/create-no-tagged-template-expression-rule/index.ts
13
14
  // eslint-disable-next-line import/no-extraneous-dependencies
14
15
 
16
+ var IMPORT_SOURCES = [_isSupportedImport.CSS_IN_JS_IMPORTS.compiled, _isSupportedImport.CSS_IN_JS_IMPORTS.emotionReact, _isSupportedImport.CSS_IN_JS_IMPORTS.emotionCore, _isSupportedImport.CSS_IN_JS_IMPORTS.styledComponents];
15
17
  var createNoTaggedTemplateExpressionRule = exports.createNoTaggedTemplateExpressionRule = function createNoTaggedTemplateExpressionRule(isUsage, messageId) {
16
18
  return function (context) {
17
19
  return {
18
20
  TaggedTemplateExpression: function TaggedTemplateExpression(node) {
19
21
  var _context$getScope = context.getScope(),
20
22
  references = _context$getScope.references;
21
- if (!isUsage(node.tag, references)) {
23
+ if (!isUsage(node.tag, references, IMPORT_SOURCES)) {
22
24
  return;
23
25
  }
24
26
  context.report({
@@ -5,8 +5,8 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.default = void 0;
7
7
  var _createRule = require("../utils/create-rule");
8
+ var _isSupportedImport = require("../utils/is-supported-import");
8
9
  var _createNoTaggedTemplateExpressionRule = require("./create-no-tagged-template-expression-rule");
9
- var _isSupportedImport = require("./is-supported-import");
10
10
  var rule = (0, _createRule.createLintRule)({
11
11
  meta: {
12
12
  name: 'no-css-tagged-template-expression',
@@ -21,6 +21,6 @@ var rule = (0, _createRule.createLintRule)({
21
21
  unexpectedTaggedTemplate: 'Unexpected `css` tagged template expression'
22
22
  }
23
23
  },
24
- create: (0, _createNoTaggedTemplateExpressionRule.createNoTaggedTemplateExpressionRule)((0, _isSupportedImport.isSupportedImport)('css'), 'unexpectedTaggedTemplate')
24
+ create: (0, _createNoTaggedTemplateExpressionRule.createNoTaggedTemplateExpressionRule)(_isSupportedImport.isCss, 'unexpectedTaggedTemplate')
25
25
  });
26
26
  var _default = exports.default = rule;
@@ -0,0 +1,61 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.isCxFunction = exports.isCss = exports.CSS_IN_JS_IMPORTS = void 0;
7
+ // eslint-disable-next-line import/no-extraneous-dependencies
8
+
9
+ var CSS_IN_JS_IMPORTS = exports.CSS_IN_JS_IMPORTS = {
10
+ compiled: '@compiled/react',
11
+ emotionReact: '@emotion/react',
12
+ emotionCore: '@emotion/core',
13
+ styledComponents: 'styled-components',
14
+ atlaskitCss: '@atlaskit/css'
15
+ };
16
+
17
+ // List of CSS-in-JS libraries an import of a valid css, cx, cssMap, etc.
18
+ // function might originate from, e.g. @compiled/react, @emotion/core.
19
+
20
+ var isSupportedImportWrapper = function isSupportedImportWrapper(functionName) {
21
+ // This will need to be extended to support default imports once we start
22
+ // checking cases like `import css from '@emotion/css'`
23
+ var checkDefinitionHasImport = function checkDefinitionHasImport(def, importSources) {
24
+ var _def$parent, _def$parent2;
25
+ return def.node.type === 'ImportSpecifier' && def.node.imported.type === 'Identifier' && def.node.imported.name === functionName && ((_def$parent = def.parent) === null || _def$parent === void 0 ? void 0 : _def$parent.type) === 'ImportDeclaration' && importSources.includes((_def$parent2 = def.parent) === null || _def$parent2 === void 0 ? void 0 : _def$parent2.source.value);
26
+ };
27
+
28
+ /**
29
+ * Checks whether:
30
+ *
31
+ * 1. a function name `nodeToCheck` matches the name of the function we
32
+ * want to check for (e.g. `cx`, `css`, `cssMap`, or `keyframes`), and
33
+ * 2. whether `nodeToCheck` originates from one of the libraries listed
34
+ * in `importSources`.
35
+ *
36
+ * @param nodeToCheck the function callee we are checking (e.g. the `css` in `css()`)
37
+ * @param referencesInScope list of references that are in scope. We'll use this
38
+ * to check where the function callee is imported from.
39
+ * @param importSources list of libraries that we want to ensure `nodeToCheck`
40
+ * comes from
41
+ *
42
+ * @returns whether the above conditions are true
43
+ */
44
+ var isSupportedImport = function isSupportedImport(nodeToCheck, referencesInScope, importSources) {
45
+ return nodeToCheck.type === 'Identifier' && referencesInScope.some(function (reference) {
46
+ var _reference$resolved;
47
+ return reference.identifier === nodeToCheck && ((_reference$resolved = reference.resolved) === null || _reference$resolved === void 0 ? void 0 : _reference$resolved.defs.some(function (def) {
48
+ return checkDefinitionHasImport(def, importSources);
49
+ }));
50
+ });
51
+ };
52
+ return isSupportedImport;
53
+ };
54
+
55
+ // Unused functions have been commented out until we implement corresponding
56
+ // eslint rules which use them
57
+ //
58
+ var isCss = exports.isCss = isSupportedImportWrapper('css');
59
+ var isCxFunction = exports.isCxFunction = isSupportedImportWrapper('cx');
60
+ // export const isCssMap = isLibraryImportWrapper('cssMap');
61
+ // export const isKeyframes = isLibraryImportWrapper('keyframes');
@@ -2,4 +2,5 @@ export { FunctionCall } from './function-call';
2
2
  export { Import } from './import';
3
3
  export { JSXAttribute } from './jsx-attribute';
4
4
  export { JSXElement } from './jsx-element';
5
+ export { Object } from './object';
5
6
  export { Root } from './root';
@@ -0,0 +1,79 @@
1
+ /* eslint-disable @repo/internal/react/require-jsdoc */
2
+
3
+ import { identifier, isNodeOfType, literal, property } from 'eslint-codemod-utils';
4
+ const ASTObjectExpression = {
5
+ /**
6
+ * Returns `true` if an object contains a property with the specified name, `false` otherwise.
7
+ */
8
+ hasProperty(node, name) {
9
+ return !!ASTObjectExpression.getProperty(node, name);
10
+ },
11
+ /**
12
+ * Returns a key-value pair like: `padding: '8px'` from: `{ padding: '8px' }`
13
+ */
14
+ getEntryByPropertyName(node, name) {
15
+ return node.properties.find(property => {
16
+ if (!isNodeOfType(property, 'Property')) {
17
+ return false;
18
+ }
19
+ if (!isNodeOfType(property.key, 'Identifier')) {
20
+ return false;
21
+ }
22
+ return property.key.name === name;
23
+ });
24
+ },
25
+ deleteEntry(node, name, fixer) {
26
+ const entry = ASTObjectExpression.getEntryByPropertyName(node, name);
27
+ if (!entry) {
28
+ return [];
29
+ }
30
+ return [fixer.remove(entry)];
31
+ },
32
+ /**
33
+ * Returns a only the property @type {Property['key']} like: `padding` from: `{ padding: '8px' }`
34
+ */
35
+ getProperty(node, name) {
36
+ var _ASTObjectExpression$;
37
+ return (_ASTObjectExpression$ = ASTObjectExpression.getEntryByPropertyName(node, name)) === null || _ASTObjectExpression$ === void 0 ? void 0 : _ASTObjectExpression$.key;
38
+ },
39
+ /**
40
+ * Returns a only the property @type {Property['value']} like: `'8px` from: `{ padding: '8px' }`
41
+ *
42
+ * Values can be basically anything, so be careful with this
43
+ */
44
+ getValueByPropertyName(node, name) {
45
+ var _ASTObjectExpression$2;
46
+ return (_ASTObjectExpression$2 = ASTObjectExpression.getEntryByPropertyName(node, name)) === null || _ASTObjectExpression$2 === void 0 ? void 0 : _ASTObjectExpression$2.value;
47
+ },
48
+ containsSpreadProps(node) {
49
+ return node.properties.some(property => {
50
+ return isNodeOfType(property, 'SpreadElement');
51
+ });
52
+ },
53
+ updateValue(node, propertyName, newValue, fixer) {
54
+ const value = ASTObjectExpression.getValueByPropertyName(node, propertyName);
55
+ if (value === undefined) {
56
+ throw new Error(`Object.updateValue: Could not get value of property ${propertyName}`);
57
+ }
58
+ return fixer.replaceText(value, newValue);
59
+ },
60
+ /**
61
+ * Appends a key-value pair to the end of an object. For example:
62
+ * ```
63
+ * ast.Object.appendEntry(
64
+ * node, // { padding: 'space.100' }
65
+ * key, // 'margin',
66
+ * value, // 'space.200'
67
+ * fixer,
68
+ * )
69
+ * ```
70
+ * Will result in `{ padding: 'space.100', margin: 'space.200'}`
71
+ */
72
+ appendEntry(node, key, value, fixer) {
73
+ return fixer.insertTextAfter(node.properties[node.properties.length - 1], `${property({
74
+ key: identifier(key),
75
+ value: literal(value)
76
+ }).toString()}, `);
77
+ }
78
+ };
79
+ export { ASTObjectExpression as Object };
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * THIS FILE WAS CREATED VIA CODEGEN DO NOT MODIFY {@see http://go/af-codegen}
3
- * @codegen <<SignedSource::d1a459e1ea71650f65b2890dc86cc398>>
3
+ * @codegen <<SignedSource::5026ba2cb55b3c1bcacbfe7fb7728a6c>>
4
4
  * @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
5
5
  */
6
6
  export default {
@@ -10,6 +10,7 @@ export default {
10
10
  '@atlaskit/design-system/ensure-design-token-usage': 'error',
11
11
  '@atlaskit/design-system/ensure-design-token-usage/preview': 'warn',
12
12
  '@atlaskit/design-system/icon-label': 'warn',
13
+ '@atlaskit/design-system/local-cx-xcss': 'error',
13
14
  '@atlaskit/design-system/no-banned-imports': 'error',
14
15
  '@atlaskit/design-system/no-css-tagged-template-expression': 'error',
15
16
  '@atlaskit/design-system/no-deprecated-apis': 'error',
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * THIS FILE WAS CREATED VIA CODEGEN DO NOT MODIFY {@see http://go/af-codegen}
3
- * @codegen <<SignedSource::3b93cfbbe0ea14514b9600509632394b>>
3
+ * @codegen <<SignedSource::839224bfab98c1ddf6018dec5320968e>>
4
4
  * @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
5
5
  */
6
6
  export default {
@@ -9,6 +9,7 @@ export default {
9
9
  '@atlaskit/design-system/consistent-css-prop-usage': 'error',
10
10
  '@atlaskit/design-system/ensure-design-token-usage': 'error',
11
11
  '@atlaskit/design-system/icon-label': 'warn',
12
+ '@atlaskit/design-system/local-cx-xcss': 'error',
12
13
  '@atlaskit/design-system/no-banned-imports': 'error',
13
14
  '@atlaskit/design-system/no-deprecated-apis': 'error',
14
15
  '@atlaskit/design-system/no-deprecated-design-token-usage': 'warn',
@@ -1,12 +1,13 @@
1
1
  /**
2
2
  * THIS FILE WAS CREATED VIA CODEGEN DO NOT MODIFY {@see http://go/af-codegen}
3
- * @codegen <<SignedSource::14cdfdcbd8b999ee097a1a5b245d7117>>
3
+ * @codegen <<SignedSource::a67afc33adf50db651edb1cf12e16ef3>>
4
4
  * @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
5
5
  */
6
6
  import consistentCssPropUsage from './consistent-css-prop-usage';
7
7
  import ensureDesignTokenUsage from './ensure-design-token-usage';
8
8
  import ensureDesignTokenUsagePreview from './ensure-design-token-usage-preview';
9
9
  import iconLabel from './icon-label';
10
+ import localCxXcss from './local-cx-xcss';
10
11
  import noBannedImports from './no-banned-imports';
11
12
  import noCssTaggedTemplateExpression from './no-css-tagged-template-expression';
12
13
  import noDeprecatedApis from './no-deprecated-apis';
@@ -30,6 +31,7 @@ export default {
30
31
  'ensure-design-token-usage': ensureDesignTokenUsage,
31
32
  'ensure-design-token-usage/preview': ensureDesignTokenUsagePreview,
32
33
  'icon-label': iconLabel,
34
+ 'local-cx-xcss': localCxXcss,
33
35
  'no-banned-imports': noBannedImports,
34
36
  'no-css-tagged-template-expression': noCssTaggedTemplateExpression,
35
37
  'no-deprecated-apis': noDeprecatedApis,
@@ -0,0 +1,45 @@
1
+ import { createLintRule } from '../utils/create-rule';
2
+ import { CSS_IN_JS_IMPORTS, isCxFunction } from '../utils/is-supported-import';
3
+ const IMPORT_SOURCES = [CSS_IN_JS_IMPORTS.compiled, CSS_IN_JS_IMPORTS.atlaskitCss];
4
+ function getParentJSXAttribute(node) {
5
+ let parent = node.parent;
6
+ while (parent && parent.type !== 'JSXAttribute') {
7
+ parent = parent.parent;
8
+ }
9
+ if (parent && parent.type === 'JSXAttribute') {
10
+ return parent;
11
+ }
12
+ return null;
13
+ }
14
+ export const rule = createLintRule({
15
+ meta: {
16
+ name: 'local-cx-xcss',
17
+ docs: {
18
+ description: 'Ensures the cx() function, which is part of the XCSS API, is only used within the xcss prop. This aids tracking what styles are applied to a jsx element.',
19
+ recommended: true,
20
+ severity: 'error'
21
+ },
22
+ messages: {
23
+ 'local-cx-xcss': 'The cx function should only be declared inside the xcss prop to simplify tracking styles that are applied to a jsx element.'
24
+ },
25
+ type: 'problem'
26
+ },
27
+ create(context) {
28
+ return {
29
+ 'CallExpression[callee.name="cx"]': node => {
30
+ if (node.type === 'CallExpression' && isCxFunction(node.callee, context.getScope().references, IMPORT_SOURCES)) {
31
+ const parentJSXAttribute = getParentJSXAttribute(node);
32
+ const propName = parentJSXAttribute === null || parentJSXAttribute === void 0 ? void 0 : parentJSXAttribute.name.name.toString();
33
+ if (propName && /[xX]css$/.test(propName)) {
34
+ return;
35
+ }
36
+ context.report({
37
+ node,
38
+ messageId: 'local-cx-xcss'
39
+ });
40
+ }
41
+ }
42
+ };
43
+ }
44
+ });
45
+ export default rule;
@@ -1,15 +1,17 @@
1
1
  // Original source from Compiled https://github.com/atlassian-labs/compiled/blob/master/packages/eslint-plugin/src/utils/create-no-tagged-template-expression-rule/index.ts
2
2
  // eslint-disable-next-line import/no-extraneous-dependencies
3
3
 
4
+ import { CSS_IN_JS_IMPORTS } from '../../utils/is-supported-import';
4
5
  import { generate } from './generate';
5
6
  import { getTaggedTemplateExpressionOffset } from './get-tagged-template-expression-offset';
6
7
  import { toArguments } from './to-arguments';
8
+ const IMPORT_SOURCES = [CSS_IN_JS_IMPORTS.compiled, CSS_IN_JS_IMPORTS.emotionReact, CSS_IN_JS_IMPORTS.emotionCore, CSS_IN_JS_IMPORTS.styledComponents];
7
9
  export const createNoTaggedTemplateExpressionRule = (isUsage, messageId) => context => ({
8
10
  TaggedTemplateExpression(node) {
9
11
  const {
10
12
  references
11
13
  } = context.getScope();
12
- if (!isUsage(node.tag, references)) {
14
+ if (!isUsage(node.tag, references, IMPORT_SOURCES)) {
13
15
  return;
14
16
  }
15
17
  context.report({
@@ -1,6 +1,6 @@
1
1
  import { createLintRule } from '../utils/create-rule';
2
+ import { isCss } from '../utils/is-supported-import';
2
3
  import { createNoTaggedTemplateExpressionRule } from './create-no-tagged-template-expression-rule';
3
- import { isSupportedImport } from './is-supported-import';
4
4
  const rule = createLintRule({
5
5
  meta: {
6
6
  name: 'no-css-tagged-template-expression',
@@ -15,6 +15,6 @@ const rule = createLintRule({
15
15
  unexpectedTaggedTemplate: 'Unexpected `css` tagged template expression'
16
16
  }
17
17
  },
18
- create: createNoTaggedTemplateExpressionRule(isSupportedImport('css'), 'unexpectedTaggedTemplate')
18
+ create: createNoTaggedTemplateExpressionRule(isCss, 'unexpectedTaggedTemplate')
19
19
  });
20
20
  export default rule;