@atlaskit/eslint-plugin-design-system 8.0.2 → 8.1.0

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 (40) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/README.md +3 -2
  3. package/constellation/index/usage.mdx +51 -2
  4. package/dist/cjs/presets/all.codegen.js +2 -1
  5. package/dist/cjs/presets/recommended.codegen.js +2 -1
  6. package/dist/cjs/rules/ensure-design-token-usage/spacing-utils.js +6 -22
  7. package/dist/cjs/rules/index.codegen.js +3 -1
  8. package/dist/cjs/rules/no-nested-styles/index.js +140 -0
  9. package/dist/cjs/rules/utils/find-in-parent.js +25 -0
  10. package/dist/cjs/version.json +1 -1
  11. package/dist/es2019/presets/all.codegen.js +2 -1
  12. package/dist/es2019/presets/recommended.codegen.js +2 -1
  13. package/dist/es2019/rules/ensure-design-token-usage/spacing-utils.js +1 -14
  14. package/dist/es2019/rules/index.codegen.js +3 -1
  15. package/dist/es2019/rules/no-nested-styles/index.js +132 -0
  16. package/dist/es2019/rules/utils/find-in-parent.js +18 -0
  17. package/dist/es2019/version.json +1 -1
  18. package/dist/esm/presets/all.codegen.js +2 -1
  19. package/dist/esm/presets/recommended.codegen.js +2 -1
  20. package/dist/esm/rules/ensure-design-token-usage/spacing-utils.js +4 -18
  21. package/dist/esm/rules/index.codegen.js +3 -1
  22. package/dist/esm/rules/no-nested-styles/index.js +133 -0
  23. package/dist/esm/rules/utils/find-in-parent.js +19 -0
  24. package/dist/esm/version.json +1 -1
  25. package/dist/types/index.codegen.d.ts +2 -0
  26. package/dist/types/presets/all.codegen.d.ts +2 -1
  27. package/dist/types/presets/recommended.codegen.d.ts +2 -1
  28. package/dist/types/rules/ensure-design-token-usage/spacing-utils.d.ts +1 -5
  29. package/dist/types/rules/index.codegen.d.ts +1 -0
  30. package/dist/types/rules/no-nested-styles/index.d.ts +3 -0
  31. package/dist/types/rules/utils/find-in-parent.d.ts +9 -0
  32. package/dist/types-ts4.5/index.codegen.d.ts +2 -0
  33. package/dist/types-ts4.5/presets/all.codegen.d.ts +2 -1
  34. package/dist/types-ts4.5/presets/recommended.codegen.d.ts +2 -1
  35. package/dist/types-ts4.5/rules/ensure-design-token-usage/spacing-utils.d.ts +1 -5
  36. package/dist/types-ts4.5/rules/index.codegen.d.ts +1 -0
  37. package/dist/types-ts4.5/rules/no-nested-styles/index.d.ts +3 -0
  38. package/dist/types-ts4.5/rules/utils/find-in-parent.d.ts +9 -0
  39. package/package.json +2 -2
  40. package/report.api.md +3 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # @atlaskit/eslint-plugin-design-system
2
2
 
3
+ ## 8.1.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [`4f9c29b2f9f`](https://bitbucket.org/atlassian/atlassian-frontend/commits/4f9c29b2f9f) - Added the `no-nested-styles` rule which disallows usage of nested styles. The `no-nested-styles` rule also disallows media queries that contain min-width or max-width. The Atlassian Design System `media` object should be used instead. Other forms of media queries are still allowed.
8
+
3
9
  ## 8.0.2
4
10
 
5
11
  ### Patch Changes
package/README.md CHANGED
@@ -50,14 +50,15 @@ module.exports = {
50
50
  | Rule | Description | Recommended | Fixable | Suggestions |
51
51
  | ------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- | ----------- | ------- | ----------- |
52
52
  | <a href="./src/rules/consistent-css-prop-usage/README.md">consistent-css-prop-usage</a> | Ensures consistency with CSS and xCSS prop usages | Yes | Yes | |
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 | |
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 | |
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
+ | <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
56
  | <a href="./src/rules/no-banned-imports/README.md">no-banned-imports</a> | Disallow importing banned modules. | Yes | | |
57
57
  | <a href="./src/rules/no-deprecated-apis/README.md">no-deprecated-apis</a> | Disallow using deprecated APIs. | Yes | | |
58
58
  | <a href="./src/rules/no-deprecated-design-token-usage/README.md">no-deprecated-design-token-usage</a> | Disallow using deprecated design tokens. | Yes | Yes | |
59
59
  | <a href="./src/rules/no-deprecated-imports/README.md">no-deprecated-imports</a> | Disallow importing deprecated modules. | Yes | | |
60
60
  | <a href="./src/rules/no-margin/README.md">no-margin</a> | Disallow using the margin CSS property. | | | |
61
+ | <a href="./src/rules/no-nested-styles/README.md">no-nested-styles</a> | Disallows use of nested styles in `css` functions. | Yes | | |
61
62
  | <a href="./src/rules/no-unsafe-design-token-usage/README.md">no-unsafe-design-token-usage</a> | Enforces design token usage is statically and locally analyzable. | Yes | Yes | |
62
63
  | <a href="./src/rules/use-primitives/README.md">use-primitives</a> | Encourage the usage of primitives components. | | Yes | Yes |
63
64
  | <a href="./src/rules/use-visually-hidden/README.md">use-visually-hidden</a> | Enforce usage of the visually hidden component. | Yes | Yes | |
@@ -14,14 +14,15 @@ This plugin contains rules that should be used when working with the [Atlassian
14
14
  | Rule | Description | Recommended | Fixable | Suggestions |
15
15
  | ---------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- | ----------- | ------- | ----------- |
16
16
  | <a href="#consistent-css-prop-usage">consistent-css-prop-usage</a> | Ensures consistency with CSS and xCSS prop usages | Yes | Yes | |
17
- | <a href="#ensure-design-token-usage">ensure-design-token-usage</a> | Enforces usage of design tokens rather than hard-coded values. | Yes | Yes | |
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 | |
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
+ | <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
20
  | <a href="#no-banned-imports">no-banned-imports</a> | Disallow importing banned modules. | Yes | | |
21
21
  | <a href="#no-deprecated-apis">no-deprecated-apis</a> | Disallow using deprecated APIs. | Yes | | |
22
22
  | <a href="#no-deprecated-design-token-usage">no-deprecated-design-token-usage</a> | Disallow using deprecated design tokens. | Yes | Yes | |
23
23
  | <a href="#no-deprecated-imports">no-deprecated-imports</a> | Disallow importing deprecated modules. | Yes | | |
24
24
  | <a href="#no-margin">no-margin</a> | Disallow using the margin CSS property. | | | |
25
+ | <a href="#no-nested-styles">no-nested-styles</a> | Disallows use of nested styles in `css` functions. | Yes | | |
25
26
  | <a href="#no-unsafe-design-token-usage">no-unsafe-design-token-usage</a> | Enforces design token usage is statically and locally analyzable. | Yes | Yes | |
26
27
  | <a href="#use-primitives">use-primitives</a> | Encourage the usage of primitives components. | | Yes | Yes |
27
28
  | <a href="#use-visually-hidden">use-visually-hidden</a> | Enforce usage of the visually hidden component. | Yes | Yes | |
@@ -451,6 +452,54 @@ css({ margin: '10px' });
451
452
  css({ gap: token('spacing.100') });
452
453
  ```
453
454
 
455
+ ## no-nested-styles
456
+
457
+ Disallows using nested styles. Nested styles can change unexpectedly when child markup changes and result in duplicates when extracting to CSS.
458
+
459
+ <h3>Examples</h3>
460
+
461
+ This rule checks for nested styles inside `css` objects.
462
+ This rule has no options.
463
+
464
+ #### Incorrect
465
+
466
+ ```js
467
+ css({
468
+ div: {
469
+ color: 'red',
470
+ },
471
+ });
472
+ ```
473
+
474
+ ```js
475
+ css({
476
+ '@media (min-width: 480px)': {
477
+ color: 'red',
478
+ },
479
+ });
480
+ ```
481
+
482
+ #### Correct
483
+
484
+ ```js
485
+ css({
486
+ color: 'red',
487
+ ':hover': {
488
+ color: 'black',
489
+ },
490
+ });
491
+ ```
492
+
493
+ ```js
494
+ import { media } from '@atlaskit/primitives';
495
+
496
+ css({
497
+ [media.above.xs]: {
498
+ color: 'red',
499
+ },
500
+ });
501
+ ```
502
+
454
503
  ## no-unsafe-design-token-usage
455
504
 
456
505
  Using design tokens in an unsafe way risks the health of the system and will effect how fast your codebase can migrate between versions.
@@ -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::8f70fad50a1889d1f86b3339503a60d1>>
9
+ * @codegen <<SignedSource::43a2737550b7d1d9f26370cebe700c1d>>
10
10
  * @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
11
11
  */
12
12
  var _default = {
@@ -21,6 +21,7 @@ var _default = {
21
21
  '@atlaskit/design-system/no-deprecated-design-token-usage': 'warn',
22
22
  '@atlaskit/design-system/no-deprecated-imports': 'error',
23
23
  '@atlaskit/design-system/no-margin': 'warn',
24
+ '@atlaskit/design-system/no-nested-styles': 'error',
24
25
  '@atlaskit/design-system/no-unsafe-design-token-usage': 'error',
25
26
  '@atlaskit/design-system/use-primitives': 'warn',
26
27
  '@atlaskit/design-system/use-visually-hidden': '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::5b7e085532640c14193d07c63e0f5442>>
9
+ * @codegen <<SignedSource::4a946a1b1d14700234605a44d7b2edd3>>
10
10
  * @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
11
11
  */
12
12
  var _default = {
@@ -19,6 +19,7 @@ var _default = {
19
19
  '@atlaskit/design-system/no-deprecated-apis': 'error',
20
20
  '@atlaskit/design-system/no-deprecated-design-token-usage': 'warn',
21
21
  '@atlaskit/design-system/no-deprecated-imports': 'error',
22
+ '@atlaskit/design-system/no-nested-styles': 'error',
22
23
  '@atlaskit/design-system/no-unsafe-design-token-usage': 'error',
23
24
  '@atlaskit/design-system/use-visually-hidden': 'error'
24
25
  }
@@ -5,9 +5,7 @@ Object.defineProperty(exports, "__esModule", {
5
5
  value: true
6
6
  });
7
7
  exports.cleanComments = cleanComments;
8
- exports.emToPixels = exports.convertHyphenatedNameToCamelCase = void 0;
9
- exports.findIdentifierInParentScope = findIdentifierInParentScope;
10
- exports.findParentNodeForLine = void 0;
8
+ exports.findParentNodeForLine = exports.emToPixels = exports.convertHyphenatedNameToCamelCase = void 0;
11
9
  exports.findTokenNameByPropertyValue = findTokenNameByPropertyValue;
12
10
  exports.getDomainsForProperty = getDomainsForProperty;
13
11
  exports.getFontSizeFromNode = getFontSizeFromNode;
@@ -29,6 +27,7 @@ exports.splitShorthandValues = void 0;
29
27
  var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
30
28
  var _eslintCodemodUtils = require("eslint-codemod-utils");
31
29
  var _tokensRaw = require("@atlaskit/tokens/tokens-raw");
30
+ var _findInParent = require("../utils/find-in-parent");
32
31
  var _isColor = require("../utils/is-color");
33
32
  var _shape = require("./shape");
34
33
  var _typography = require("./typography");
@@ -36,21 +35,6 @@ var properties = ['padding', 'paddingBlock', 'paddingInline', 'paddingLeft', 'pa
36
35
  var spacingValueToToken = Object.fromEntries(_tokensRaw.spacing.map(function (token) {
37
36
  return [token.value, token.name];
38
37
  }));
39
- function findIdentifierInParentScope(_ref) {
40
- var scope = _ref.scope,
41
- identifierName = _ref.identifierName;
42
- var traversingScope = scope;
43
- while (traversingScope && traversingScope.type !== 'global') {
44
- var matchedVariable = traversingScope.variables.find(function (variable) {
45
- return variable.name === identifierName;
46
- });
47
- if (matchedVariable) {
48
- return matchedVariable;
49
- }
50
- traversingScope = traversingScope.upper;
51
- }
52
- return null;
53
- }
54
38
  function insertTokensImport(fixer) {
55
39
  return (0, _eslintCodemodUtils.insertAtStartOfFile)(fixer, "".concat((0, _eslintCodemodUtils.insertImportDeclaration)('@atlaskit/tokens', ['token']), "\n"));
56
40
  }
@@ -177,7 +161,7 @@ var getValueFromIdentifier = function getValueFromIdentifier(node, context) {
177
161
  return 8;
178
162
  }
179
163
  var scope = context.getScope();
180
- var variable = findIdentifierInParentScope({
164
+ var variable = (0, _findInParent.findIdentifierInParentScope)({
181
165
  scope: scope,
182
166
  identifierName: node.name
183
167
  });
@@ -447,9 +431,9 @@ function getTokenNodeForValue(propertyName, value) {
447
431
  });
448
432
  }
449
433
  function getFontSizeValueInScope(cssProperties) {
450
- var fontSizeNode = cssProperties.find(function (_ref2) {
451
- var _ref3 = (0, _slicedToArray2.default)(_ref2, 1),
452
- style = _ref3[0];
434
+ var fontSizeNode = cssProperties.find(function (_ref) {
435
+ var _ref2 = (0, _slicedToArray2.default)(_ref, 1),
436
+ style = _ref2[0];
453
437
  var _style$split = style.split(':'),
454
438
  _style$split2 = (0, _slicedToArray2.default)(_style$split, 2),
455
439
  rawProperty = _style$split2[0],
@@ -14,12 +14,13 @@ var _noDeprecatedApis = _interopRequireDefault(require("./no-deprecated-apis"));
14
14
  var _noDeprecatedDesignTokenUsage = _interopRequireDefault(require("./no-deprecated-design-token-usage"));
15
15
  var _noDeprecatedImports = _interopRequireDefault(require("./no-deprecated-imports"));
16
16
  var _noMargin = _interopRequireDefault(require("./no-margin"));
17
+ var _noNestedStyles = _interopRequireDefault(require("./no-nested-styles"));
17
18
  var _noUnsafeDesignTokenUsage = _interopRequireDefault(require("./no-unsafe-design-token-usage"));
18
19
  var _usePrimitives = _interopRequireDefault(require("./use-primitives"));
19
20
  var _useVisuallyHidden = _interopRequireDefault(require("./use-visually-hidden"));
20
21
  /**
21
22
  * THIS FILE WAS CREATED VIA CODEGEN DO NOT MODIFY {@see http://go/af-codegen}
22
- * @codegen <<SignedSource::4dc269134c0cd38281be0b5ad7582dc8>>
23
+ * @codegen <<SignedSource::10aa91b16d9c498373b4a22760ca7fab>>
23
24
  * @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
24
25
  */
25
26
  var _default = {
@@ -32,6 +33,7 @@ var _default = {
32
33
  'no-deprecated-design-token-usage': _noDeprecatedDesignTokenUsage.default,
33
34
  'no-deprecated-imports': _noDeprecatedImports.default,
34
35
  'no-margin': _noMargin.default,
36
+ 'no-nested-styles': _noNestedStyles.default,
35
37
  'no-unsafe-design-token-usage': _noUnsafeDesignTokenUsage.default,
36
38
  'use-primitives': _usePrimitives.default,
37
39
  'use-visually-hidden': _useVisuallyHidden.default
@@ -0,0 +1,140 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = void 0;
7
+ var _eslintCodemodUtils = require("eslint-codemod-utils");
8
+ var _createRule = require("../utils/create-rule");
9
+ var _findInParent = require("../utils/find-in-parent");
10
+ var allowedPrefix = [':', '&:'];
11
+ var allowedResponsiveImports = ['@atlaskit/primitives/responsive', '@atlaskit/primitives'];
12
+
13
+ /**
14
+ * Tests against properties using the Design System primitives media object: [media.above.md]
15
+ * @returns true if the property is a media query
16
+ */
17
+ var isMediaObject = function isMediaObject(node, context) {
18
+ if (node.type === 'MemberExpression') {
19
+ if (node.object.type === 'MemberExpression' && node.object.object.type === 'Identifier') {
20
+ var scope = context.getScope();
21
+ var variable = (0, _findInParent.findIdentifierInParentScope)({
22
+ scope: scope,
23
+ identifierName: node.object.object.name
24
+ });
25
+ if (!variable) {
26
+ return false;
27
+ }
28
+ var definition = variable.defs[0];
29
+
30
+ // Make sure it's coming from the primitives packages and isn't a bootleg media query or worse: nested styles
31
+ if ((0, _eslintCodemodUtils.isNodeOfType)(definition.node, 'ImportSpecifier') && definition.node.parent && (0, _eslintCodemodUtils.isNodeOfType)(definition.node.parent, 'ImportDeclaration') && allowedResponsiveImports.includes(definition.node.parent.source.value)) {
32
+ // This should match the name of the media object exported from packages/design-system/primitives/src/responsive/media-helper.tsx
33
+ return definition.node.imported.name === 'media';
34
+ }
35
+ }
36
+ }
37
+ return false;
38
+ };
39
+ var parseSelector = function parseSelector(rawSelector) {
40
+ if (typeof rawSelector !== 'string') {
41
+ throw new Error('expected string');
42
+ }
43
+ var selectors = rawSelector.split(',').map(function (selector) {
44
+ return selector.trim();
45
+ });
46
+ return selectors;
47
+ };
48
+ var getKeyValue = function getKeyValue(node, context) {
49
+ if (node.type === 'Identifier') {
50
+ return node.name;
51
+ }
52
+ if (node.type === 'Literal' && typeof node.value === 'string') {
53
+ return node.value;
54
+ }
55
+ if (isMediaObject(node, context)) {
56
+ return '@media';
57
+ }
58
+ return '';
59
+ };
60
+ var isWidthMediaQuery = function isWidthMediaQuery(rawSelector) {
61
+ var selectors = parseSelector(rawSelector);
62
+ if (selectors[0].startsWith('@')) {
63
+ // If the selector includes a min-width/max-width query, return false - the primitives media object should be used instead:
64
+ // https://staging.atlassian.design/components/primitives/responsive/breakpoints/examples
65
+ // Otherwise return true, non-width queries are acceptable
66
+ return selectors.some(function (selector) {
67
+ return selector.includes('min-width') || selector.includes('max-width');
68
+ });
69
+ }
70
+ return false;
71
+ };
72
+ var isAllowedNestedSelector = function isAllowedNestedSelector(rawSelector) {
73
+ if (rawSelector.trim() === '&') {
74
+ // This can be written without the nest.
75
+ return false;
76
+ }
77
+ var selectors = parseSelector(rawSelector);
78
+ if (selectors[0].startsWith('@')) {
79
+ // Bail early as it's an at selector. Width queries are handled by `isWidthMediaQuery`.
80
+ return true;
81
+ }
82
+ return selectors.every(function (selector) {
83
+ return selector === '&' || allowedPrefix.find(function (prefix) {
84
+ return selector.startsWith(prefix);
85
+ });
86
+ });
87
+ };
88
+ var isUsingDirectDataAttribute = function isUsingDirectDataAttribute(rawSelector) {
89
+ var selectors = parseSelector(rawSelector);
90
+ return selectors.some(function (selector) {
91
+ return selector.startsWith('&[');
92
+ });
93
+ };
94
+ var rule = (0, _createRule.createLintRule)({
95
+ meta: {
96
+ name: 'no-nested-styles',
97
+ docs: {
98
+ description: 'Disallows use of nested styles in `css` functions.',
99
+ recommended: true,
100
+ severity: 'error'
101
+ },
102
+ messages: {
103
+ noWidthQueries: 'Media queries that target min-width or max-width are not allowed. Use the media object provided by the Atlassian Design System instead. https://staging.atlassian.design/components/primitives/responsive/breakpoints/examples',
104
+ noNestedStyles: 'Nested styles are not allowed as they can change unexpectedly when child markup changes and result in duplicates when extracting to CSS.',
105
+ noDirectNestedStyles: "Styles applied with data attributes are not allowed, split them into discrete CSS declarations and apply them conditionally with JavaScript.\n\n```\nconst disabledStyles = css({ opacity: 0.5 });\n\n<div css={isDisabled && disabledStyles} />\n```\n"
106
+ }
107
+ },
108
+ create: function create(context) {
109
+ return {
110
+ 'CallExpression[callee.name=css] > ObjectExpression Property,CallExpression[callee.name=xcss] > ObjectExpression Property': function CallExpressionCalleeNameCssObjectExpressionPropertyCallExpressionCalleeNameXcssObjectExpressionProperty(node) {
111
+ if (node.type !== 'Property' || node.value.type !== 'ObjectExpression') {
112
+ return;
113
+ }
114
+ if (isUsingDirectDataAttribute(getKeyValue(node.key, context))) {
115
+ context.report({
116
+ node: node,
117
+ messageId: 'noDirectNestedStyles'
118
+ });
119
+ return;
120
+ }
121
+ if (isWidthMediaQuery(getKeyValue(node.key, context))) {
122
+ context.report({
123
+ node: node,
124
+ messageId: 'noWidthQueries'
125
+ });
126
+ return;
127
+ }
128
+ if (!isAllowedNestedSelector(getKeyValue(node.key, context))) {
129
+ context.report({
130
+ node: node,
131
+ messageId: 'noNestedStyles'
132
+ });
133
+ return;
134
+ }
135
+ }
136
+ };
137
+ }
138
+ });
139
+ var _default = rule;
140
+ exports.default = _default;
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.findIdentifierInParentScope = findIdentifierInParentScope;
7
+ /**
8
+ * This will search first matched identifier in same and parent scopes.
9
+ * Returns first matched identifer otherwise null.
10
+ */
11
+ function findIdentifierInParentScope(_ref) {
12
+ var scope = _ref.scope,
13
+ identifierName = _ref.identifierName;
14
+ var traversingScope = scope;
15
+ while (traversingScope && traversingScope.type !== 'global') {
16
+ var matchedVariable = traversingScope.variables.find(function (variable) {
17
+ return variable.name === identifierName;
18
+ });
19
+ if (matchedVariable) {
20
+ return matchedVariable;
21
+ }
22
+ traversingScope = traversingScope.upper;
23
+ }
24
+ return null;
25
+ }
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "@atlaskit/eslint-plugin-design-system",
3
- "version": "8.0.2",
3
+ "version": "8.1.0",
4
4
  "sideEffects": false
5
5
  }
@@ -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::8f70fad50a1889d1f86b3339503a60d1>>
3
+ * @codegen <<SignedSource::43a2737550b7d1d9f26370cebe700c1d>>
4
4
  * @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
5
5
  */
6
6
  export default {
@@ -15,6 +15,7 @@ export default {
15
15
  '@atlaskit/design-system/no-deprecated-design-token-usage': 'warn',
16
16
  '@atlaskit/design-system/no-deprecated-imports': 'error',
17
17
  '@atlaskit/design-system/no-margin': 'warn',
18
+ '@atlaskit/design-system/no-nested-styles': 'error',
18
19
  '@atlaskit/design-system/no-unsafe-design-token-usage': 'error',
19
20
  '@atlaskit/design-system/use-primitives': 'warn',
20
21
  '@atlaskit/design-system/use-visually-hidden': '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::5b7e085532640c14193d07c63e0f5442>>
3
+ * @codegen <<SignedSource::4a946a1b1d14700234605a44d7b2edd3>>
4
4
  * @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
5
5
  */
6
6
  export default {
@@ -13,6 +13,7 @@ export default {
13
13
  '@atlaskit/design-system/no-deprecated-apis': 'error',
14
14
  '@atlaskit/design-system/no-deprecated-design-token-usage': 'warn',
15
15
  '@atlaskit/design-system/no-deprecated-imports': 'error',
16
+ '@atlaskit/design-system/no-nested-styles': 'error',
16
17
  '@atlaskit/design-system/no-unsafe-design-token-usage': 'error',
17
18
  '@atlaskit/design-system/use-visually-hidden': 'error'
18
19
  }
@@ -1,24 +1,11 @@
1
1
  import { callExpression, identifier, insertAtStartOfFile, insertImportDeclaration, isNodeOfType, literal } from 'eslint-codemod-utils';
2
2
  import { spacing as spacingScale } from '@atlaskit/tokens/tokens-raw';
3
+ import { findIdentifierInParentScope } from '../utils/find-in-parent';
3
4
  import { isColorCssPropertyName } from '../utils/is-color';
4
5
  import { borderWidthValueToToken, isBorderRadius, isBorderSizeProperty, isShapeProperty, radiusValueToToken } from './shape';
5
6
  import { isCodeFontFamily, isFontFamily, isFontSize, isFontSizeSmall, isTypographyProperty, typographyValueToToken } from './typography';
6
7
  const properties = ['padding', 'paddingBlock', 'paddingInline', 'paddingLeft', 'paddingTop', 'paddingRight', 'paddingBottom', 'paddingInline', 'paddingInlineStart', 'paddingInlineEnd', 'paddingBlock', 'paddingBlockStart', 'paddingBlockEnd', 'marginLeft', 'marginTop', 'marginRight', 'marginBottom', 'marginInline', 'marginInlineStart', 'marginInlineEnd', 'marginBlock', 'marginBlockStart', 'marginBlockEnd', 'margin', 'gap', 'rowGap', 'gridRowGap', 'columnGap', 'gridColumnGap', 'top', 'left', 'right', 'bottom', 'inlineStart', 'inlineEnd', 'blockStart', 'blockEnd', 'outline-offset'];
7
8
  const spacingValueToToken = Object.fromEntries(spacingScale.map(token => [token.value, token.name]));
8
- export function findIdentifierInParentScope({
9
- scope,
10
- identifierName
11
- }) {
12
- let traversingScope = scope;
13
- while (traversingScope && traversingScope.type !== 'global') {
14
- const matchedVariable = traversingScope.variables.find(variable => variable.name === identifierName);
15
- if (matchedVariable) {
16
- return matchedVariable;
17
- }
18
- traversingScope = traversingScope.upper;
19
- }
20
- return null;
21
- }
22
9
  export function insertTokensImport(fixer) {
23
10
  return insertAtStartOfFile(fixer, `${insertImportDeclaration('@atlaskit/tokens', ['token'])}\n`);
24
11
  }
@@ -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::4dc269134c0cd38281be0b5ad7582dc8>>
3
+ * @codegen <<SignedSource::10aa91b16d9c498373b4a22760ca7fab>>
4
4
  * @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
5
5
  */
6
6
  import consistentCssPropUsage from './consistent-css-prop-usage';
@@ -12,6 +12,7 @@ import noDeprecatedApis from './no-deprecated-apis';
12
12
  import noDeprecatedDesignTokenUsage from './no-deprecated-design-token-usage';
13
13
  import noDeprecatedImports from './no-deprecated-imports';
14
14
  import noMargin from './no-margin';
15
+ import noNestedStyles from './no-nested-styles';
15
16
  import noUnsafeDesignTokenUsage from './no-unsafe-design-token-usage';
16
17
  import usePrimitives from './use-primitives';
17
18
  import useVisuallyHidden from './use-visually-hidden';
@@ -25,6 +26,7 @@ export default {
25
26
  'no-deprecated-design-token-usage': noDeprecatedDesignTokenUsage,
26
27
  'no-deprecated-imports': noDeprecatedImports,
27
28
  'no-margin': noMargin,
29
+ 'no-nested-styles': noNestedStyles,
28
30
  'no-unsafe-design-token-usage': noUnsafeDesignTokenUsage,
29
31
  'use-primitives': usePrimitives,
30
32
  'use-visually-hidden': useVisuallyHidden
@@ -0,0 +1,132 @@
1
+ import { isNodeOfType } from 'eslint-codemod-utils';
2
+ import { createLintRule } from '../utils/create-rule';
3
+ import { findIdentifierInParentScope } from '../utils/find-in-parent';
4
+ const allowedPrefix = [':', '&:'];
5
+ const allowedResponsiveImports = ['@atlaskit/primitives/responsive', '@atlaskit/primitives'];
6
+
7
+ /**
8
+ * Tests against properties using the Design System primitives media object: [media.above.md]
9
+ * @returns true if the property is a media query
10
+ */
11
+ const isMediaObject = (node, context) => {
12
+ if (node.type === 'MemberExpression') {
13
+ if (node.object.type === 'MemberExpression' && node.object.object.type === 'Identifier') {
14
+ const scope = context.getScope();
15
+ const variable = findIdentifierInParentScope({
16
+ scope,
17
+ identifierName: node.object.object.name
18
+ });
19
+ if (!variable) {
20
+ return false;
21
+ }
22
+ const definition = variable.defs[0];
23
+
24
+ // Make sure it's coming from the primitives packages and isn't a bootleg media query or worse: nested styles
25
+ if (isNodeOfType(definition.node, 'ImportSpecifier') && definition.node.parent && isNodeOfType(definition.node.parent, 'ImportDeclaration') && allowedResponsiveImports.includes(definition.node.parent.source.value)) {
26
+ // This should match the name of the media object exported from packages/design-system/primitives/src/responsive/media-helper.tsx
27
+ return definition.node.imported.name === 'media';
28
+ }
29
+ }
30
+ }
31
+ return false;
32
+ };
33
+ const parseSelector = rawSelector => {
34
+ if (typeof rawSelector !== 'string') {
35
+ throw new Error('expected string');
36
+ }
37
+ const selectors = rawSelector.split(',').map(selector => selector.trim());
38
+ return selectors;
39
+ };
40
+ const getKeyValue = (node, context) => {
41
+ if (node.type === 'Identifier') {
42
+ return node.name;
43
+ }
44
+ if (node.type === 'Literal' && typeof node.value === 'string') {
45
+ return node.value;
46
+ }
47
+ if (isMediaObject(node, context)) {
48
+ return '@media';
49
+ }
50
+ return '';
51
+ };
52
+ const isWidthMediaQuery = rawSelector => {
53
+ const selectors = parseSelector(rawSelector);
54
+ if (selectors[0].startsWith('@')) {
55
+ // If the selector includes a min-width/max-width query, return false - the primitives media object should be used instead:
56
+ // https://staging.atlassian.design/components/primitives/responsive/breakpoints/examples
57
+ // Otherwise return true, non-width queries are acceptable
58
+ return selectors.some(selector => selector.includes('min-width') || selector.includes('max-width'));
59
+ }
60
+ return false;
61
+ };
62
+ const isAllowedNestedSelector = rawSelector => {
63
+ if (rawSelector.trim() === '&') {
64
+ // This can be written without the nest.
65
+ return false;
66
+ }
67
+ const selectors = parseSelector(rawSelector);
68
+ if (selectors[0].startsWith('@')) {
69
+ // Bail early as it's an at selector. Width queries are handled by `isWidthMediaQuery`.
70
+ return true;
71
+ }
72
+ return selectors.every(selector => {
73
+ return selector === '&' || allowedPrefix.find(prefix => selector.startsWith(prefix));
74
+ });
75
+ };
76
+ const isUsingDirectDataAttribute = rawSelector => {
77
+ const selectors = parseSelector(rawSelector);
78
+ return selectors.some(selector => selector.startsWith('&['));
79
+ };
80
+ const rule = createLintRule({
81
+ meta: {
82
+ name: 'no-nested-styles',
83
+ docs: {
84
+ description: 'Disallows use of nested styles in `css` functions.',
85
+ recommended: true,
86
+ severity: 'error'
87
+ },
88
+ messages: {
89
+ noWidthQueries: 'Media queries that target min-width or max-width are not allowed. Use the media object provided by the Atlassian Design System instead. https://staging.atlassian.design/components/primitives/responsive/breakpoints/examples',
90
+ noNestedStyles: 'Nested styles are not allowed as they can change unexpectedly when child markup changes and result in duplicates when extracting to CSS.',
91
+ noDirectNestedStyles: `Styles applied with data attributes are not allowed, split them into discrete CSS declarations and apply them conditionally with JavaScript.
92
+
93
+ \`\`\`
94
+ const disabledStyles = css({ opacity: 0.5 });
95
+
96
+ <div css={isDisabled && disabledStyles} />
97
+ \`\`\`
98
+ `
99
+ }
100
+ },
101
+ create(context) {
102
+ return {
103
+ 'CallExpression[callee.name=css] > ObjectExpression Property,CallExpression[callee.name=xcss] > ObjectExpression Property': node => {
104
+ if (node.type !== 'Property' || node.value.type !== 'ObjectExpression') {
105
+ return;
106
+ }
107
+ if (isUsingDirectDataAttribute(getKeyValue(node.key, context))) {
108
+ context.report({
109
+ node,
110
+ messageId: 'noDirectNestedStyles'
111
+ });
112
+ return;
113
+ }
114
+ if (isWidthMediaQuery(getKeyValue(node.key, context))) {
115
+ context.report({
116
+ node,
117
+ messageId: 'noWidthQueries'
118
+ });
119
+ return;
120
+ }
121
+ if (!isAllowedNestedSelector(getKeyValue(node.key, context))) {
122
+ context.report({
123
+ node,
124
+ messageId: 'noNestedStyles'
125
+ });
126
+ return;
127
+ }
128
+ }
129
+ };
130
+ }
131
+ });
132
+ export default rule;
@@ -0,0 +1,18 @@
1
+ /**
2
+ * This will search first matched identifier in same and parent scopes.
3
+ * Returns first matched identifer otherwise null.
4
+ */
5
+ export function findIdentifierInParentScope({
6
+ scope,
7
+ identifierName
8
+ }) {
9
+ let traversingScope = scope;
10
+ while (traversingScope && traversingScope.type !== 'global') {
11
+ const matchedVariable = traversingScope.variables.find(variable => variable.name === identifierName);
12
+ if (matchedVariable) {
13
+ return matchedVariable;
14
+ }
15
+ traversingScope = traversingScope.upper;
16
+ }
17
+ return null;
18
+ }
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "@atlaskit/eslint-plugin-design-system",
3
- "version": "8.0.2",
3
+ "version": "8.1.0",
4
4
  "sideEffects": false
5
5
  }
@@ -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::8f70fad50a1889d1f86b3339503a60d1>>
3
+ * @codegen <<SignedSource::43a2737550b7d1d9f26370cebe700c1d>>
4
4
  * @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
5
5
  */
6
6
  export default {
@@ -15,6 +15,7 @@ export default {
15
15
  '@atlaskit/design-system/no-deprecated-design-token-usage': 'warn',
16
16
  '@atlaskit/design-system/no-deprecated-imports': 'error',
17
17
  '@atlaskit/design-system/no-margin': 'warn',
18
+ '@atlaskit/design-system/no-nested-styles': 'error',
18
19
  '@atlaskit/design-system/no-unsafe-design-token-usage': 'error',
19
20
  '@atlaskit/design-system/use-primitives': 'warn',
20
21
  '@atlaskit/design-system/use-visually-hidden': '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::5b7e085532640c14193d07c63e0f5442>>
3
+ * @codegen <<SignedSource::4a946a1b1d14700234605a44d7b2edd3>>
4
4
  * @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
5
5
  */
6
6
  export default {
@@ -13,6 +13,7 @@ export default {
13
13
  '@atlaskit/design-system/no-deprecated-apis': 'error',
14
14
  '@atlaskit/design-system/no-deprecated-design-token-usage': 'warn',
15
15
  '@atlaskit/design-system/no-deprecated-imports': 'error',
16
+ '@atlaskit/design-system/no-nested-styles': 'error',
16
17
  '@atlaskit/design-system/no-unsafe-design-token-usage': 'error',
17
18
  '@atlaskit/design-system/use-visually-hidden': 'error'
18
19
  }
@@ -1,6 +1,7 @@
1
1
  import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
2
2
  import { callExpression, identifier, insertAtStartOfFile, insertImportDeclaration, isNodeOfType, literal } from 'eslint-codemod-utils';
3
3
  import { spacing as spacingScale } from '@atlaskit/tokens/tokens-raw';
4
+ import { findIdentifierInParentScope } from '../utils/find-in-parent';
4
5
  import { isColorCssPropertyName } from '../utils/is-color';
5
6
  import { borderWidthValueToToken, isBorderRadius, isBorderSizeProperty, isShapeProperty, radiusValueToToken } from './shape';
6
7
  import { isCodeFontFamily, isFontFamily, isFontSize, isFontSizeSmall, isTypographyProperty, typographyValueToToken } from './typography';
@@ -8,21 +9,6 @@ var properties = ['padding', 'paddingBlock', 'paddingInline', 'paddingLeft', 'pa
8
9
  var spacingValueToToken = Object.fromEntries(spacingScale.map(function (token) {
9
10
  return [token.value, token.name];
10
11
  }));
11
- export function findIdentifierInParentScope(_ref) {
12
- var scope = _ref.scope,
13
- identifierName = _ref.identifierName;
14
- var traversingScope = scope;
15
- while (traversingScope && traversingScope.type !== 'global') {
16
- var matchedVariable = traversingScope.variables.find(function (variable) {
17
- return variable.name === identifierName;
18
- });
19
- if (matchedVariable) {
20
- return matchedVariable;
21
- }
22
- traversingScope = traversingScope.upper;
23
- }
24
- return null;
25
- }
26
12
  export function insertTokensImport(fixer) {
27
13
  return insertAtStartOfFile(fixer, "".concat(insertImportDeclaration('@atlaskit/tokens', ['token']), "\n"));
28
14
  }
@@ -406,9 +392,9 @@ export function getTokenNodeForValue(propertyName, value) {
406
392
  });
407
393
  }
408
394
  export function getFontSizeValueInScope(cssProperties) {
409
- var fontSizeNode = cssProperties.find(function (_ref2) {
410
- var _ref3 = _slicedToArray(_ref2, 1),
411
- style = _ref3[0];
395
+ var fontSizeNode = cssProperties.find(function (_ref) {
396
+ var _ref2 = _slicedToArray(_ref, 1),
397
+ style = _ref2[0];
412
398
  var _style$split = style.split(':'),
413
399
  _style$split2 = _slicedToArray(_style$split, 2),
414
400
  rawProperty = _style$split2[0],
@@ -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::4dc269134c0cd38281be0b5ad7582dc8>>
3
+ * @codegen <<SignedSource::10aa91b16d9c498373b4a22760ca7fab>>
4
4
  * @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
5
5
  */
6
6
  import consistentCssPropUsage from './consistent-css-prop-usage';
@@ -12,6 +12,7 @@ import noDeprecatedApis from './no-deprecated-apis';
12
12
  import noDeprecatedDesignTokenUsage from './no-deprecated-design-token-usage';
13
13
  import noDeprecatedImports from './no-deprecated-imports';
14
14
  import noMargin from './no-margin';
15
+ import noNestedStyles from './no-nested-styles';
15
16
  import noUnsafeDesignTokenUsage from './no-unsafe-design-token-usage';
16
17
  import usePrimitives from './use-primitives';
17
18
  import useVisuallyHidden from './use-visually-hidden';
@@ -25,6 +26,7 @@ export default {
25
26
  'no-deprecated-design-token-usage': noDeprecatedDesignTokenUsage,
26
27
  'no-deprecated-imports': noDeprecatedImports,
27
28
  'no-margin': noMargin,
29
+ 'no-nested-styles': noNestedStyles,
28
30
  'no-unsafe-design-token-usage': noUnsafeDesignTokenUsage,
29
31
  'use-primitives': usePrimitives,
30
32
  'use-visually-hidden': useVisuallyHidden
@@ -0,0 +1,133 @@
1
+ import { isNodeOfType } from 'eslint-codemod-utils';
2
+ import { createLintRule } from '../utils/create-rule';
3
+ import { findIdentifierInParentScope } from '../utils/find-in-parent';
4
+ var allowedPrefix = [':', '&:'];
5
+ var allowedResponsiveImports = ['@atlaskit/primitives/responsive', '@atlaskit/primitives'];
6
+
7
+ /**
8
+ * Tests against properties using the Design System primitives media object: [media.above.md]
9
+ * @returns true if the property is a media query
10
+ */
11
+ var isMediaObject = function isMediaObject(node, context) {
12
+ if (node.type === 'MemberExpression') {
13
+ if (node.object.type === 'MemberExpression' && node.object.object.type === 'Identifier') {
14
+ var scope = context.getScope();
15
+ var variable = findIdentifierInParentScope({
16
+ scope: scope,
17
+ identifierName: node.object.object.name
18
+ });
19
+ if (!variable) {
20
+ return false;
21
+ }
22
+ var definition = variable.defs[0];
23
+
24
+ // Make sure it's coming from the primitives packages and isn't a bootleg media query or worse: nested styles
25
+ if (isNodeOfType(definition.node, 'ImportSpecifier') && definition.node.parent && isNodeOfType(definition.node.parent, 'ImportDeclaration') && allowedResponsiveImports.includes(definition.node.parent.source.value)) {
26
+ // This should match the name of the media object exported from packages/design-system/primitives/src/responsive/media-helper.tsx
27
+ return definition.node.imported.name === 'media';
28
+ }
29
+ }
30
+ }
31
+ return false;
32
+ };
33
+ var parseSelector = function parseSelector(rawSelector) {
34
+ if (typeof rawSelector !== 'string') {
35
+ throw new Error('expected string');
36
+ }
37
+ var selectors = rawSelector.split(',').map(function (selector) {
38
+ return selector.trim();
39
+ });
40
+ return selectors;
41
+ };
42
+ var getKeyValue = function getKeyValue(node, context) {
43
+ if (node.type === 'Identifier') {
44
+ return node.name;
45
+ }
46
+ if (node.type === 'Literal' && typeof node.value === 'string') {
47
+ return node.value;
48
+ }
49
+ if (isMediaObject(node, context)) {
50
+ return '@media';
51
+ }
52
+ return '';
53
+ };
54
+ var isWidthMediaQuery = function isWidthMediaQuery(rawSelector) {
55
+ var selectors = parseSelector(rawSelector);
56
+ if (selectors[0].startsWith('@')) {
57
+ // If the selector includes a min-width/max-width query, return false - the primitives media object should be used instead:
58
+ // https://staging.atlassian.design/components/primitives/responsive/breakpoints/examples
59
+ // Otherwise return true, non-width queries are acceptable
60
+ return selectors.some(function (selector) {
61
+ return selector.includes('min-width') || selector.includes('max-width');
62
+ });
63
+ }
64
+ return false;
65
+ };
66
+ var isAllowedNestedSelector = function isAllowedNestedSelector(rawSelector) {
67
+ if (rawSelector.trim() === '&') {
68
+ // This can be written without the nest.
69
+ return false;
70
+ }
71
+ var selectors = parseSelector(rawSelector);
72
+ if (selectors[0].startsWith('@')) {
73
+ // Bail early as it's an at selector. Width queries are handled by `isWidthMediaQuery`.
74
+ return true;
75
+ }
76
+ return selectors.every(function (selector) {
77
+ return selector === '&' || allowedPrefix.find(function (prefix) {
78
+ return selector.startsWith(prefix);
79
+ });
80
+ });
81
+ };
82
+ var isUsingDirectDataAttribute = function isUsingDirectDataAttribute(rawSelector) {
83
+ var selectors = parseSelector(rawSelector);
84
+ return selectors.some(function (selector) {
85
+ return selector.startsWith('&[');
86
+ });
87
+ };
88
+ var rule = createLintRule({
89
+ meta: {
90
+ name: 'no-nested-styles',
91
+ docs: {
92
+ description: 'Disallows use of nested styles in `css` functions.',
93
+ recommended: true,
94
+ severity: 'error'
95
+ },
96
+ messages: {
97
+ noWidthQueries: 'Media queries that target min-width or max-width are not allowed. Use the media object provided by the Atlassian Design System instead. https://staging.atlassian.design/components/primitives/responsive/breakpoints/examples',
98
+ noNestedStyles: 'Nested styles are not allowed as they can change unexpectedly when child markup changes and result in duplicates when extracting to CSS.',
99
+ noDirectNestedStyles: "Styles applied with data attributes are not allowed, split them into discrete CSS declarations and apply them conditionally with JavaScript.\n\n```\nconst disabledStyles = css({ opacity: 0.5 });\n\n<div css={isDisabled && disabledStyles} />\n```\n"
100
+ }
101
+ },
102
+ create: function create(context) {
103
+ return {
104
+ 'CallExpression[callee.name=css] > ObjectExpression Property,CallExpression[callee.name=xcss] > ObjectExpression Property': function CallExpressionCalleeNameCssObjectExpressionPropertyCallExpressionCalleeNameXcssObjectExpressionProperty(node) {
105
+ if (node.type !== 'Property' || node.value.type !== 'ObjectExpression') {
106
+ return;
107
+ }
108
+ if (isUsingDirectDataAttribute(getKeyValue(node.key, context))) {
109
+ context.report({
110
+ node: node,
111
+ messageId: 'noDirectNestedStyles'
112
+ });
113
+ return;
114
+ }
115
+ if (isWidthMediaQuery(getKeyValue(node.key, context))) {
116
+ context.report({
117
+ node: node,
118
+ messageId: 'noWidthQueries'
119
+ });
120
+ return;
121
+ }
122
+ if (!isAllowedNestedSelector(getKeyValue(node.key, context))) {
123
+ context.report({
124
+ node: node,
125
+ messageId: 'noNestedStyles'
126
+ });
127
+ return;
128
+ }
129
+ }
130
+ };
131
+ }
132
+ });
133
+ export default rule;
@@ -0,0 +1,19 @@
1
+ /**
2
+ * This will search first matched identifier in same and parent scopes.
3
+ * Returns first matched identifer otherwise null.
4
+ */
5
+ export function findIdentifierInParentScope(_ref) {
6
+ var scope = _ref.scope,
7
+ identifierName = _ref.identifierName;
8
+ var traversingScope = scope;
9
+ while (traversingScope && traversingScope.type !== 'global') {
10
+ var matchedVariable = traversingScope.variables.find(function (variable) {
11
+ return variable.name === identifierName;
12
+ });
13
+ if (matchedVariable) {
14
+ return matchedVariable;
15
+ }
16
+ traversingScope = traversingScope.upper;
17
+ }
18
+ return null;
19
+ }
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "@atlaskit/eslint-plugin-design-system",
3
- "version": "8.0.2",
3
+ "version": "8.1.0",
4
4
  "sideEffects": false
5
5
  }
@@ -12,6 +12,7 @@ export declare const configs: {
12
12
  '@atlaskit/design-system/no-deprecated-design-token-usage': string;
13
13
  '@atlaskit/design-system/no-deprecated-imports': string;
14
14
  '@atlaskit/design-system/no-margin': string;
15
+ '@atlaskit/design-system/no-nested-styles': string;
15
16
  '@atlaskit/design-system/no-unsafe-design-token-usage': string;
16
17
  '@atlaskit/design-system/use-primitives': string;
17
18
  '@atlaskit/design-system/use-visually-hidden': string;
@@ -27,6 +28,7 @@ export declare const configs: {
27
28
  '@atlaskit/design-system/no-deprecated-apis': string;
28
29
  '@atlaskit/design-system/no-deprecated-design-token-usage': string;
29
30
  '@atlaskit/design-system/no-deprecated-imports': string;
31
+ '@atlaskit/design-system/no-nested-styles': string;
30
32
  '@atlaskit/design-system/no-unsafe-design-token-usage': string;
31
33
  '@atlaskit/design-system/use-visually-hidden': string;
32
34
  };
@@ -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::8f70fad50a1889d1f86b3339503a60d1>>
3
+ * @codegen <<SignedSource::43a2737550b7d1d9f26370cebe700c1d>>
4
4
  * @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
5
5
  */
6
6
  declare const _default: {
@@ -15,6 +15,7 @@ declare const _default: {
15
15
  '@atlaskit/design-system/no-deprecated-design-token-usage': string;
16
16
  '@atlaskit/design-system/no-deprecated-imports': string;
17
17
  '@atlaskit/design-system/no-margin': string;
18
+ '@atlaskit/design-system/no-nested-styles': string;
18
19
  '@atlaskit/design-system/no-unsafe-design-token-usage': string;
19
20
  '@atlaskit/design-system/use-primitives': string;
20
21
  '@atlaskit/design-system/use-visually-hidden': string;
@@ -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::5b7e085532640c14193d07c63e0f5442>>
3
+ * @codegen <<SignedSource::4a946a1b1d14700234605a44d7b2edd3>>
4
4
  * @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
5
5
  */
6
6
  declare const _default: {
@@ -13,6 +13,7 @@ declare const _default: {
13
13
  '@atlaskit/design-system/no-deprecated-apis': string;
14
14
  '@atlaskit/design-system/no-deprecated-design-token-usage': string;
15
15
  '@atlaskit/design-system/no-deprecated-imports': string;
16
+ '@atlaskit/design-system/no-nested-styles': string;
16
17
  '@atlaskit/design-system/no-unsafe-design-token-usage': string;
17
18
  '@atlaskit/design-system/use-visually-hidden': string;
18
19
  };
@@ -1,11 +1,7 @@
1
- import type { Rule, Scope } from 'eslint';
1
+ import type { Rule } from 'eslint';
2
2
  import { EslintNode, ObjectExpression, TaggedTemplateExpression } from 'eslint-codemod-utils';
3
3
  import { Domains } from './types';
4
4
  export type ProcessedCSSLines = [string, string][];
5
- export declare function findIdentifierInParentScope({ scope, identifierName, }: {
6
- scope: Scope.Scope;
7
- identifierName: string;
8
- }): Scope.Variable | null;
9
5
  export declare function insertTokensImport(fixer: Rule.RuleFixer): Rule.Fix;
10
6
  export declare const isSpacingProperty: (propertyName: string) => boolean;
11
7
  /**
@@ -12,6 +12,7 @@ declare const _default: {
12
12
  deprecatedConfig: import("./utils/types").DeprecatedConfig;
13
13
  }], import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleListener>;
14
14
  'no-margin': import("eslint").Rule.RuleModule;
15
+ 'no-nested-styles': import("eslint").Rule.RuleModule;
15
16
  'no-unsafe-design-token-usage': import("eslint").Rule.RuleModule;
16
17
  'use-primitives': import("eslint").Rule.RuleModule;
17
18
  'use-visually-hidden': import("eslint").Rule.RuleModule;
@@ -0,0 +1,3 @@
1
+ import type { Rule } from 'eslint';
2
+ declare const rule: Rule.RuleModule;
3
+ export default rule;
@@ -0,0 +1,9 @@
1
+ import type { Scope } from 'eslint';
2
+ /**
3
+ * This will search first matched identifier in same and parent scopes.
4
+ * Returns first matched identifer otherwise null.
5
+ */
6
+ export declare function findIdentifierInParentScope({ scope, identifierName, }: {
7
+ scope: Scope.Scope;
8
+ identifierName: string;
9
+ }): Scope.Variable | null;
@@ -12,6 +12,7 @@ export declare const configs: {
12
12
  '@atlaskit/design-system/no-deprecated-design-token-usage': string;
13
13
  '@atlaskit/design-system/no-deprecated-imports': string;
14
14
  '@atlaskit/design-system/no-margin': string;
15
+ '@atlaskit/design-system/no-nested-styles': string;
15
16
  '@atlaskit/design-system/no-unsafe-design-token-usage': string;
16
17
  '@atlaskit/design-system/use-primitives': string;
17
18
  '@atlaskit/design-system/use-visually-hidden': string;
@@ -27,6 +28,7 @@ export declare const configs: {
27
28
  '@atlaskit/design-system/no-deprecated-apis': string;
28
29
  '@atlaskit/design-system/no-deprecated-design-token-usage': string;
29
30
  '@atlaskit/design-system/no-deprecated-imports': string;
31
+ '@atlaskit/design-system/no-nested-styles': string;
30
32
  '@atlaskit/design-system/no-unsafe-design-token-usage': string;
31
33
  '@atlaskit/design-system/use-visually-hidden': string;
32
34
  };
@@ -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::8f70fad50a1889d1f86b3339503a60d1>>
3
+ * @codegen <<SignedSource::43a2737550b7d1d9f26370cebe700c1d>>
4
4
  * @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
5
5
  */
6
6
  declare const _default: {
@@ -15,6 +15,7 @@ declare const _default: {
15
15
  '@atlaskit/design-system/no-deprecated-design-token-usage': string;
16
16
  '@atlaskit/design-system/no-deprecated-imports': string;
17
17
  '@atlaskit/design-system/no-margin': string;
18
+ '@atlaskit/design-system/no-nested-styles': string;
18
19
  '@atlaskit/design-system/no-unsafe-design-token-usage': string;
19
20
  '@atlaskit/design-system/use-primitives': string;
20
21
  '@atlaskit/design-system/use-visually-hidden': string;
@@ -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::5b7e085532640c14193d07c63e0f5442>>
3
+ * @codegen <<SignedSource::4a946a1b1d14700234605a44d7b2edd3>>
4
4
  * @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
5
5
  */
6
6
  declare const _default: {
@@ -13,6 +13,7 @@ declare const _default: {
13
13
  '@atlaskit/design-system/no-deprecated-apis': string;
14
14
  '@atlaskit/design-system/no-deprecated-design-token-usage': string;
15
15
  '@atlaskit/design-system/no-deprecated-imports': string;
16
+ '@atlaskit/design-system/no-nested-styles': string;
16
17
  '@atlaskit/design-system/no-unsafe-design-token-usage': string;
17
18
  '@atlaskit/design-system/use-visually-hidden': string;
18
19
  };
@@ -1,14 +1,10 @@
1
- import type { Rule, Scope } from 'eslint';
1
+ import type { Rule } from 'eslint';
2
2
  import { EslintNode, ObjectExpression, TaggedTemplateExpression } from 'eslint-codemod-utils';
3
3
  import { Domains } from './types';
4
4
  export type ProcessedCSSLines = [
5
5
  string,
6
6
  string
7
7
  ][];
8
- export declare function findIdentifierInParentScope({ scope, identifierName, }: {
9
- scope: Scope.Scope;
10
- identifierName: string;
11
- }): Scope.Variable | null;
12
8
  export declare function insertTokensImport(fixer: Rule.RuleFixer): Rule.Fix;
13
9
  export declare const isSpacingProperty: (propertyName: string) => boolean;
14
10
  /**
@@ -16,6 +16,7 @@ declare const _default: {
16
16
  }
17
17
  ], import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleListener>;
18
18
  'no-margin': import("eslint").Rule.RuleModule;
19
+ 'no-nested-styles': import("eslint").Rule.RuleModule;
19
20
  'no-unsafe-design-token-usage': import("eslint").Rule.RuleModule;
20
21
  'use-primitives': import("eslint").Rule.RuleModule;
21
22
  'use-visually-hidden': import("eslint").Rule.RuleModule;
@@ -0,0 +1,3 @@
1
+ import type { Rule } from 'eslint';
2
+ declare const rule: Rule.RuleModule;
3
+ export default rule;
@@ -0,0 +1,9 @@
1
+ import type { Scope } from 'eslint';
2
+ /**
3
+ * This will search first matched identifier in same and parent scopes.
4
+ * Returns first matched identifer otherwise null.
5
+ */
6
+ export declare function findIdentifierInParentScope({ scope, identifierName, }: {
7
+ scope: Scope.Scope;
8
+ identifierName: string;
9
+ }): Scope.Variable | null;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@atlaskit/eslint-plugin-design-system",
3
3
  "description": "The essential plugin for use with the Atlassian Design System.",
4
- "version": "8.0.2",
4
+ "version": "8.1.0",
5
5
  "author": "Atlassian Pty Ltd",
6
6
  "publishConfig": {
7
7
  "registry": "https://registry.npmjs.org/"
@@ -33,7 +33,7 @@
33
33
  ".": "./src/index.tsx"
34
34
  },
35
35
  "dependencies": {
36
- "@atlaskit/tokens": "^1.12.0",
36
+ "@atlaskit/tokens": "^1.13.0",
37
37
  "@babel/runtime": "^7.0.0",
38
38
  "@typescript-eslint/utils": "^5.48.1",
39
39
  "ajv": "^6.12.6",
package/report.api.md CHANGED
@@ -33,6 +33,7 @@ export const configs: {
33
33
  '@atlaskit/design-system/no-deprecated-design-token-usage': string;
34
34
  '@atlaskit/design-system/no-deprecated-imports': string;
35
35
  '@atlaskit/design-system/no-margin': string;
36
+ '@atlaskit/design-system/no-nested-styles': string;
36
37
  '@atlaskit/design-system/no-unsafe-design-token-usage': string;
37
38
  '@atlaskit/design-system/use-primitives': string;
38
39
  '@atlaskit/design-system/use-visually-hidden': string;
@@ -48,6 +49,7 @@ export const configs: {
48
49
  '@atlaskit/design-system/no-deprecated-apis': string;
49
50
  '@atlaskit/design-system/no-deprecated-design-token-usage': string;
50
51
  '@atlaskit/design-system/no-deprecated-imports': string;
52
+ '@atlaskit/design-system/no-nested-styles': string;
51
53
  '@atlaskit/design-system/no-unsafe-design-token-usage': string;
52
54
  '@atlaskit/design-system/use-visually-hidden': string;
53
55
  };
@@ -116,6 +118,7 @@ export const rules: {
116
118
  RuleListener
117
119
  >;
118
120
  'no-margin': Rule.RuleModule;
121
+ 'no-nested-styles': Rule.RuleModule;
119
122
  'no-unsafe-design-token-usage': Rule.RuleModule;
120
123
  'use-primitives': Rule.RuleModule;
121
124
  'use-visually-hidden': Rule.RuleModule;