@atlaskit/eslint-plugin-design-system 9.2.5 → 9.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/README.md +1 -0
  3. package/constellation/index/usage.mdx +1 -0
  4. package/constellation/use-heading/usage.mdx +28 -0
  5. package/dist/cjs/ast-nodes/root.js +32 -0
  6. package/dist/cjs/presets/all.codegen.js +2 -1
  7. package/dist/cjs/rules/index.codegen.js +3 -1
  8. package/dist/cjs/rules/use-heading/config/index.js +15 -0
  9. package/dist/cjs/rules/use-heading/index.js +39 -0
  10. package/dist/cjs/rules/use-heading/transformers/common.js +19 -0
  11. package/dist/cjs/rules/use-heading/transformers/index.js +12 -0
  12. package/dist/cjs/rules/use-heading/transformers/native-elements.js +99 -0
  13. package/dist/cjs/rules/use-primitives/transformers/compiled-styled/index.js +1 -2
  14. package/dist/cjs/rules/use-primitives/transformers/emotion-css/index.js +1 -2
  15. package/dist/cjs/rules/use-primitives-text/transformers/emphasis-elements.js +1 -2
  16. package/dist/cjs/rules/use-primitives-text/transformers/paragraph-elements.js +2 -3
  17. package/dist/cjs/rules/use-primitives-text/transformers/span-elements.js +1 -2
  18. package/dist/cjs/rules/use-primitives-text/transformers/strong-elements.js +1 -2
  19. package/dist/cjs/rules/use-tokens-space/transformers/style-property/index.js +1 -2
  20. package/dist/cjs/rules/utils/create-no-tagged-template-expression-rule/index.js +5 -7
  21. package/dist/cjs/rules/utils/create-rule.js +4 -7
  22. package/dist/es2019/ast-nodes/root.js +34 -0
  23. package/dist/es2019/presets/all.codegen.js +2 -1
  24. package/dist/es2019/rules/index.codegen.js +3 -1
  25. package/dist/es2019/rules/use-heading/config/index.js +9 -0
  26. package/dist/es2019/rules/use-heading/index.js +33 -0
  27. package/dist/es2019/rules/use-heading/transformers/common.js +9 -0
  28. package/dist/es2019/rules/use-heading/transformers/index.js +1 -0
  29. package/dist/es2019/rules/use-heading/transformers/native-elements.js +89 -0
  30. package/dist/es2019/rules/use-primitives/transformers/compiled-styled/index.js +1 -2
  31. package/dist/es2019/rules/use-primitives/transformers/emotion-css/index.js +1 -2
  32. package/dist/es2019/rules/use-primitives-text/transformers/emphasis-elements.js +1 -2
  33. package/dist/es2019/rules/use-primitives-text/transformers/paragraph-elements.js +2 -3
  34. package/dist/es2019/rules/use-primitives-text/transformers/span-elements.js +1 -2
  35. package/dist/es2019/rules/use-primitives-text/transformers/strong-elements.js +1 -2
  36. package/dist/es2019/rules/use-tokens-space/transformers/style-property/index.js +1 -2
  37. package/dist/es2019/rules/utils/create-no-tagged-template-expression-rule/index.js +21 -9
  38. package/dist/es2019/rules/utils/create-rule.js +4 -6
  39. package/dist/esm/ast-nodes/root.js +32 -0
  40. package/dist/esm/presets/all.codegen.js +2 -1
  41. package/dist/esm/rules/index.codegen.js +3 -1
  42. package/dist/esm/rules/use-heading/config/index.js +9 -0
  43. package/dist/esm/rules/use-heading/index.js +33 -0
  44. package/dist/esm/rules/use-heading/transformers/common.js +9 -0
  45. package/dist/esm/rules/use-heading/transformers/index.js +1 -0
  46. package/dist/esm/rules/use-heading/transformers/native-elements.js +89 -0
  47. package/dist/esm/rules/use-primitives/transformers/compiled-styled/index.js +1 -2
  48. package/dist/esm/rules/use-primitives/transformers/emotion-css/index.js +1 -2
  49. package/dist/esm/rules/use-primitives-text/transformers/emphasis-elements.js +1 -2
  50. package/dist/esm/rules/use-primitives-text/transformers/paragraph-elements.js +2 -3
  51. package/dist/esm/rules/use-primitives-text/transformers/span-elements.js +1 -2
  52. package/dist/esm/rules/use-primitives-text/transformers/strong-elements.js +1 -2
  53. package/dist/esm/rules/use-tokens-space/transformers/style-property/index.js +1 -2
  54. package/dist/esm/rules/utils/create-no-tagged-template-expression-rule/index.js +6 -8
  55. package/dist/esm/rules/utils/create-rule.js +4 -6
  56. package/dist/types/ast-nodes/jsx-element.d.ts +2 -2
  57. package/dist/types/ast-nodes/root.d.ts +8 -0
  58. package/dist/types/index.codegen.d.ts +1 -0
  59. package/dist/types/presets/all.codegen.d.ts +2 -1
  60. package/dist/types/rules/ensure-design-token-usage/rule-meta.d.ts +1 -1
  61. package/dist/types/rules/index.codegen.d.ts +1 -0
  62. package/dist/types/rules/use-heading/config/index.d.ts +6 -0
  63. package/dist/types/rules/use-heading/index.d.ts +3 -0
  64. package/dist/types/rules/use-heading/transformers/common.d.ts +9 -0
  65. package/dist/types/rules/use-heading/transformers/index.d.ts +1 -0
  66. package/dist/types/rules/use-heading/transformers/native-elements.d.ts +18 -0
  67. package/dist/types/rules/utils/create-rule.d.ts +1 -37
  68. package/dist/types-ts4.5/ast-nodes/jsx-element.d.ts +2 -2
  69. package/dist/types-ts4.5/ast-nodes/root.d.ts +8 -0
  70. package/dist/types-ts4.5/index.codegen.d.ts +1 -0
  71. package/dist/types-ts4.5/presets/all.codegen.d.ts +2 -1
  72. package/dist/types-ts4.5/rules/ensure-design-token-usage/rule-meta.d.ts +1 -1
  73. package/dist/types-ts4.5/rules/index.codegen.d.ts +1 -0
  74. package/dist/types-ts4.5/rules/use-heading/config/index.d.ts +6 -0
  75. package/dist/types-ts4.5/rules/use-heading/index.d.ts +3 -0
  76. package/dist/types-ts4.5/rules/use-heading/transformers/common.d.ts +9 -0
  77. package/dist/types-ts4.5/rules/use-heading/transformers/index.d.ts +1 -0
  78. package/dist/types-ts4.5/rules/use-heading/transformers/native-elements.d.ts +18 -0
  79. package/dist/types-ts4.5/rules/utils/create-rule.d.ts +1 -37
  80. package/package.json +2 -2
  81. package/dist/cjs/rules/use-primitives/transformers/compiled-styled/upsert-import-declaration.js +0 -37
  82. package/dist/cjs/rules/use-primitives/transformers/emotion-css/upsert-import-declaration.js +0 -37
  83. package/dist/cjs/rules/use-tokens-space/transformers/style-property/upsert-import-declaration.js +0 -37
  84. package/dist/es2019/rules/use-primitives/transformers/compiled-styled/upsert-import-declaration.js +0 -30
  85. package/dist/es2019/rules/use-primitives/transformers/emotion-css/upsert-import-declaration.js +0 -30
  86. package/dist/es2019/rules/use-tokens-space/transformers/style-property/upsert-import-declaration.js +0 -30
  87. package/dist/esm/rules/use-primitives/transformers/compiled-styled/upsert-import-declaration.js +0 -29
  88. package/dist/esm/rules/use-primitives/transformers/emotion-css/upsert-import-declaration.js +0 -29
  89. package/dist/esm/rules/use-tokens-space/transformers/style-property/upsert-import-declaration.js +0 -29
  90. package/dist/types/rules/use-primitives/transformers/compiled-styled/upsert-import-declaration.d.ts +0 -14
  91. package/dist/types/rules/use-primitives/transformers/emotion-css/upsert-import-declaration.d.ts +0 -14
  92. package/dist/types/rules/use-tokens-space/transformers/style-property/upsert-import-declaration.d.ts +0 -14
  93. package/dist/types-ts4.5/rules/use-primitives/transformers/compiled-styled/upsert-import-declaration.d.ts +0 -14
  94. package/dist/types-ts4.5/rules/use-primitives/transformers/emotion-css/upsert-import-declaration.d.ts +0 -14
  95. package/dist/types-ts4.5/rules/use-tokens-space/transformers/style-property/upsert-import-declaration.d.ts +0 -14
@@ -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::d1c25758089a050334359276ede0ca3a>>
3
+ * @codegen <<SignedSource::eb5c94901f711e67446ba88f0630bf76>>
4
4
  * @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
5
5
  */
6
6
  import consistentCssPropUsage from './consistent-css-prop-usage';
@@ -28,6 +28,7 @@ import noUnsupportedDragAndDropLibraries from './no-unsupported-drag-and-drop-li
28
28
  import preferPrimitives from './prefer-primitives';
29
29
  import useButtonGroupLabel from './use-button-group-label';
30
30
  import useDrawerLabel from './use-drawer-label';
31
+ import useHeading from './use-heading';
31
32
  import useHeadingLevelInSpotlightCard from './use-heading-level-in-spotlight-card';
32
33
  import useHrefInLinkItem from './use-href-in-link-item';
33
34
  import usePrimitives from './use-primitives';
@@ -61,6 +62,7 @@ export default {
61
62
  'prefer-primitives': preferPrimitives,
62
63
  'use-button-group-label': useButtonGroupLabel,
63
64
  'use-drawer-label': useDrawerLabel,
65
+ 'use-heading': useHeading,
64
66
  'use-heading-level-in-spotlight-card': useHeadingLevelInSpotlightCard,
65
67
  'use-href-in-link-item': useHrefInLinkItem,
66
68
  'use-primitives': usePrimitives,
@@ -0,0 +1,9 @@
1
+ const defaults = {
2
+ patterns: ['native-elements']
3
+ };
4
+ export const getConfig = overrides => {
5
+ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign
6
+ // start with an empty object, then merge in the defaults, then merge in overrides.
7
+ // The empty object is returned, as well as modified in place
8
+ return Object.assign({}, defaults, overrides);
9
+ };
@@ -0,0 +1,33 @@
1
+ import { createLintRule } from '../utils/create-rule';
2
+ import { getConfig } from './config';
3
+ import { NativeElements } from './transformers';
4
+ const docsUrl = 'https://atlassian.design/components/heading';
5
+ const rule = createLintRule({
6
+ meta: {
7
+ name: 'use-heading',
8
+ type: 'suggestion',
9
+ fixable: 'code',
10
+ hasSuggestions: true,
11
+ docs: {
12
+ description: 'Encourage the usage of heading components.',
13
+ recommended: false,
14
+ severity: 'warn'
15
+ },
16
+ messages: {
17
+ preferHeading: `This element can be replaced with a "Heading" component. See ${docsUrl} for additional guidance.`
18
+ }
19
+ },
20
+ create(context) {
21
+ const config = getConfig(context.options[0]);
22
+ return {
23
+ // transforms <h1>...</h1> usages
24
+ 'JSXElement[openingElement.name.name=/^h[0-6]$/]': node => {
25
+ return NativeElements.lint(node, {
26
+ context,
27
+ config
28
+ });
29
+ }
30
+ };
31
+ }
32
+ });
33
+ export default rule;
@@ -0,0 +1,9 @@
1
+ import * as ast from '../../../ast-nodes';
2
+ // Rename data-testid prop to testId if present
3
+ export function updateTestIdAttributeFix(node, fixer) {
4
+ const testIdAttr = ast.JSXElement.getAttributeByName(node, 'data-testid');
5
+ if (testIdAttr) {
6
+ return ast.JSXAttribute.updateName(testIdAttr, 'testId', fixer);
7
+ }
8
+ }
9
+ export const allowedAttrs = ['id', 'data-testid', 'key'];
@@ -0,0 +1 @@
1
+ export { NativeElements } from './native-elements';
@@ -0,0 +1,89 @@
1
+ /* eslint-disable @repo/internal/react/require-jsdoc */
2
+
3
+ import { isNodeOfType } from 'eslint-codemod-utils';
4
+ import * as ast from '../../../ast-nodes';
5
+ import { allowedAttrs, updateTestIdAttributeFix } from './common';
6
+ const tagSizeMap = {
7
+ h1: 'xlarge',
8
+ h2: 'large',
9
+ h3: 'medium',
10
+ h4: 'small',
11
+ h5: 'xsmall',
12
+ h6: 'xxsmall'
13
+ };
14
+ export const NativeElements = {
15
+ lint(node, {
16
+ context,
17
+ config
18
+ }) {
19
+ // Check whether all criteria needed to make a transformation are met
20
+ if (!NativeElements._check(node, {
21
+ context,
22
+ config
23
+ })) {
24
+ return;
25
+ }
26
+ context.report({
27
+ node,
28
+ messageId: 'preferHeading',
29
+ suggest: [{
30
+ desc: 'Convert to Heading',
31
+ fix: NativeElements._fix(node, {
32
+ context,
33
+ config
34
+ })
35
+ }]
36
+ });
37
+ },
38
+ _check(node, {
39
+ config
40
+ }) {
41
+ if (!config.patterns.includes('native-elements')) {
42
+ return false;
43
+ }
44
+ if (!isNodeOfType(node, 'JSXElement')) {
45
+ return false;
46
+ }
47
+ if (!node.parent) {
48
+ return false;
49
+ }
50
+ const elementName = ast.JSXElement.getName(node);
51
+ if (!Object.keys(tagSizeMap).includes(elementName)) {
52
+ return false;
53
+ }
54
+
55
+ // Element has to be first element of its siblings
56
+ if (!(isNodeOfType(node.parent, 'JSXElement') || isNodeOfType(node.parent, 'JSXFragment'))) {
57
+ return false;
58
+ }
59
+ const siblings = ast.JSXElement.getChildren(node.parent);
60
+ if (siblings.length > 1) {
61
+ var _siblings$0$range, _node$range, _siblings$0$range2, _node$range2;
62
+ // Only report if element is first child element
63
+ if (((_siblings$0$range = siblings[0].range) === null || _siblings$0$range === void 0 ? void 0 : _siblings$0$range[0]) !== ((_node$range = node.range) === null || _node$range === void 0 ? void 0 : _node$range[0]) || ((_siblings$0$range2 = siblings[0].range) === null || _siblings$0$range2 === void 0 ? void 0 : _siblings$0$range2[1]) !== ((_node$range2 = node.range) === null || _node$range2 === void 0 ? void 0 : _node$range2[1])) {
64
+ return false;
65
+ }
66
+ }
67
+ if (!ast.JSXElement.hasAllowedAttrsOnly(node, allowedAttrs)) {
68
+ return false;
69
+ }
70
+ return true;
71
+ },
72
+ _fix(node, {
73
+ context
74
+ }) {
75
+ return fixer => {
76
+ // change to default import
77
+ const importFix = ast.Root.upsertDefaultImportDeclaration({
78
+ module: '@atlaskit/heading',
79
+ localName: 'Heading'
80
+ }, context, fixer);
81
+ const elementName = ast.JSXElement.getName(node);
82
+ const elementNameFixes = ast.JSXElement.updateName(node, 'Heading', fixer);
83
+ const size = tagSizeMap[elementName];
84
+ const asAttributeFix = ast.JSXElement.addAttribute(node, 'size', size, fixer);
85
+ const testAttributeFix = updateTestIdAttributeFix(node, fixer);
86
+ return [importFix, ...elementNameFixes, asAttributeFix, testAttributeFix].filter(fix => Boolean(fix)); // Some of the transformers can return arrays with undefined, so filter them out
87
+ };
88
+ }
89
+ };
@@ -8,7 +8,6 @@ import { convertJsxCallSite } from './convert-jsx-call-site';
8
8
  import { convertStyledComponentToXcss } from './convert-styled-component-call-to-jsx';
9
9
  import { findValidJsxUsageToTransform } from './find-valid-jsx-usage-to-transform';
10
10
  import { findValidStyledComponentCall } from './find-valid-styled-component-call';
11
- import { upsertImportDeclaration } from './upsert-import-declaration';
12
11
  export const CompiledStyled = {
13
12
  lint(node, {
14
13
  context,
@@ -97,7 +96,7 @@ export const CompiledStyled = {
97
96
  if (!calculatedStylesVariableName) {
98
97
  return [];
99
98
  }
100
- const importFixes = upsertImportDeclaration({
99
+ const importFixes = ast.Root.upsertNamedImportDeclaration({
101
100
  module: '@atlaskit/primitives',
102
101
  specifiers: ['Box', 'xcss']
103
102
  }, context, fixer);
@@ -6,7 +6,6 @@ import { getVariableDefinitionValue, getVariableUsagesCount, isValidCssPropertie
6
6
  import { validateStyles } from '../../utils/validate-styles';
7
7
  import { cssToXcssTransformer } from '../css-to-xcss';
8
8
  import * as supported from './supported';
9
- import { upsertImportDeclaration } from './upsert-import-declaration';
10
9
  export const EmotionCSS = {
11
10
  lint(node, {
12
11
  context,
@@ -100,7 +99,7 @@ export const EmotionCSS = {
100
99
  context
101
100
  }) {
102
101
  return fixer => {
103
- const importFix = upsertImportDeclaration({
102
+ const importFix = ast.Root.upsertNamedImportDeclaration({
104
103
  module: '@atlaskit/primitives',
105
104
  specifiers: ['Box', 'xcss']
106
105
  }, context, fixer);
@@ -2,7 +2,6 @@
2
2
 
3
3
  import { isNodeOfType } from 'eslint-codemod-utils';
4
4
  import * as ast from '../../../ast-nodes';
5
- import { upsertImportDeclaration } from '../../use-primitives/transformers/emotion-css/upsert-import-declaration';
6
5
  import { addColorInheritAttributeFix, allowedAttrs, updateTestIdAttributeFix } from './common';
7
6
  export const EmphasisElements = {
8
7
  lint(node, {
@@ -61,7 +60,7 @@ export const EmphasisElements = {
61
60
  config
62
61
  }) {
63
62
  return fixer => {
64
- const importFix = upsertImportDeclaration({
63
+ const importFix = ast.Root.upsertNamedImportDeclaration({
65
64
  module: '@atlaskit/primitives',
66
65
  specifiers: ['Text']
67
66
  }, context, fixer);
@@ -2,7 +2,6 @@
2
2
 
3
3
  import { isNodeOfType } from 'eslint-codemod-utils';
4
4
  import * as ast from '../../../ast-nodes';
5
- import { upsertImportDeclaration } from '../../use-primitives/transformers/emotion-css/upsert-import-declaration';
6
5
  import { addColorInheritAttributeFix, allowedAttrs, updateTestIdAttributeFix } from './common';
7
6
  export const ParagraphElements = {
8
7
  lint(node, {
@@ -155,7 +154,7 @@ export const ParagraphElements = {
155
154
  config
156
155
  }) {
157
156
  return fixer => {
158
- const importFix = upsertImportDeclaration({
157
+ const importFix = ast.Root.upsertNamedImportDeclaration({
159
158
  module: '@atlaskit/primitives',
160
159
  specifiers: ['Text']
161
160
  }, context, fixer);
@@ -175,7 +174,7 @@ export const ParagraphElements = {
175
174
  if (!isNodeOfType(node.parent, 'JSXElement') || !node.parent.closingElement) {
176
175
  return [];
177
176
  }
178
- const importFix = upsertImportDeclaration({
177
+ const importFix = ast.Root.upsertNamedImportDeclaration({
179
178
  module: '@atlaskit/primitives',
180
179
  specifiers: ['Text', 'Stack']
181
180
  }, context, fixer);
@@ -2,7 +2,6 @@
2
2
 
3
3
  import { isNodeOfType } from 'eslint-codemod-utils';
4
4
  import * as ast from '../../../ast-nodes';
5
- import { upsertImportDeclaration } from '../../use-primitives/transformers/emotion-css/upsert-import-declaration';
6
5
  import { addColorInheritAttributeFix, allowedAttrs, hasTextChildrenOnly, updateTestIdAttributeFix } from './common';
7
6
  export const SpanElements = {
8
7
  lint(node, {
@@ -66,7 +65,7 @@ export const SpanElements = {
66
65
  config
67
66
  }) {
68
67
  return fixer => {
69
- const importFix = upsertImportDeclaration({
68
+ const importFix = ast.Root.upsertNamedImportDeclaration({
70
69
  module: '@atlaskit/primitives',
71
70
  specifiers: ['Text']
72
71
  }, context, fixer);
@@ -2,7 +2,6 @@
2
2
 
3
3
  import { isNodeOfType } from 'eslint-codemod-utils';
4
4
  import * as ast from '../../../ast-nodes';
5
- import { upsertImportDeclaration } from '../../use-primitives/transformers/emotion-css/upsert-import-declaration';
6
5
  import { addColorInheritAttributeFix, allowedAttrs, updateTestIdAttributeFix } from './common';
7
6
  export const StrongElements = {
8
7
  lint(node, {
@@ -61,7 +60,7 @@ export const StrongElements = {
61
60
  config
62
61
  }) {
63
62
  return fixer => {
64
- const importFix = upsertImportDeclaration({
63
+ const importFix = ast.Root.upsertNamedImportDeclaration({
65
64
  module: '@atlaskit/primitives',
66
65
  specifiers: ['Text']
67
66
  }, context, fixer);
@@ -5,7 +5,6 @@ import * as ast from '../../../../ast-nodes';
5
5
  import { isStringOrNumber } from '../../utils';
6
6
  import { styleMap } from './style-map';
7
7
  import supported from './supported';
8
- import { upsertImportDeclaration } from './upsert-import-declaration';
9
8
  const messageId = 'noRawSpacingValues';
10
9
  export const StyleProperty = {
11
10
  lint(node, {
@@ -109,7 +108,7 @@ export const StyleProperty = {
109
108
  */
110
109
  _fix(ref, context) {
111
110
  return fixer => {
112
- const importFix = upsertImportDeclaration({
111
+ const importFix = ast.Root.upsertNamedImportDeclaration({
113
112
  module: '@atlaskit/tokens',
114
113
  specifiers: ['token']
115
114
  }, context, fixer);
@@ -2,7 +2,7 @@
2
2
  // eslint-disable-next-line import/no-extraneous-dependencies
3
3
 
4
4
  import esquery from 'esquery';
5
- import { getImportSources, isEmotion, isStyledComponents } from '@atlaskit/eslint-utils/is-supported-import';
5
+ import { getImportSources, isEmotion } from '@atlaskit/eslint-utils/is-supported-import';
6
6
  import { generate } from './generate';
7
7
  import { getTaggedTemplateExpressionOffset } from './get-tagged-template-expression-offset';
8
8
  import { toArguments } from './to-arguments';
@@ -60,11 +60,23 @@ export const createNoTaggedTemplateExpressionRule = (isUsage, messageId) => cont
60
60
  return;
61
61
  }
62
62
  const args = toArguments(source, quasi);
63
- if (isStyledComponents(node.tag, references, importSources) && args.some(hasMixinInsideNestedSelector)) {
63
+ if (args.some(hasNestedSelectorWithMultipleArguments)) {
64
64
  /**
65
- * Styled components doesn't support arrays as style object values,
66
- * so we cannot autofix mixins as we cannot combine them with the other
67
- * properties in the object.
65
+ * We don't want to autofix if we would produce an array value, for example:
66
+ * ```
67
+ * styled.div({
68
+ * ':hover': [
69
+ * mixin,
70
+ * {
71
+ * color: 'red'
72
+ * }
73
+ * ]
74
+ * })
75
+ * ```
76
+ *
77
+ * This should only occur if there is a mixin in a nested selector.
78
+ *
79
+ * The array syntax is only supported by emotion.
68
80
  */
69
81
  return;
70
82
  }
@@ -136,17 +148,17 @@ export const createNoTaggedTemplateExpressionRule = (isUsage, messageId) => cont
136
148
  }
137
149
  };
138
150
  };
139
- function hasMixinInsideNestedSelector(arg) {
151
+ function hasNestedSelectorWithMultipleArguments(arg) {
140
152
  if (arg.type === 'literal' || arg.type === 'expression' || arg.type === 'declaration') {
141
153
  return false;
142
154
  }
143
- if (arg.type === 'rule' && arg.declarations.length > 1 && arg.declarations.some(node => node.type === 'expression')) {
155
+ if (arg.type === 'rule' && arg.declarations.length > 1) {
144
156
  return true;
145
157
  }
146
158
  if (arg.type === 'block') {
147
- return arg.blocks.some(hasMixinInsideNestedSelector);
159
+ return arg.blocks.some(hasNestedSelectorWithMultipleArguments);
148
160
  }
149
161
  if (arg.type === 'rule') {
150
- return arg.declarations.some(hasMixinInsideNestedSelector);
162
+ return arg.declarations.some(hasNestedSelectorWithMultipleArguments);
151
163
  }
152
164
  }
@@ -1,5 +1,5 @@
1
1
  import { ESLintUtils } from '@typescript-eslint/utils';
2
- // eslint-disable-next-line import/no-extraneous-dependencies
2
+ import { getCreateLintRule, getPathSafeName } from '@atlaskit/eslint-utils/create-rule';
3
3
 
4
4
  /**
5
5
  * We are moving to our own small abstraction to create a lint rule that we have the power
@@ -11,16 +11,14 @@ import { ESLintUtils } from '@typescript-eslint/utils';
11
11
  * @deprecated
12
12
  */
13
13
  export const createRule = ESLintUtils.RuleCreator(name => getRuleUrl(name));
14
+
14
15
  /**
15
16
  * Tiny wrapped over the ESLint rule module type that ensures
16
17
  * there is a docs link to our ESLint plugin documentation page,
17
18
  * as well as improving type support.
18
19
  */
19
- export const createLintRule = rule => {
20
- rule.meta.docs.url = getRuleUrl(rule.meta.name);
21
- return rule;
22
- };
20
+ export const createLintRule = getCreateLintRule(getRuleUrl);
23
21
  function getRuleUrl(ruleName) {
24
- const name = ruleName.replace('/', '-'); // If it's a nested rule, ensure the url is clean and matches codegen/gatsby
22
+ const name = getPathSafeName(ruleName);
25
23
  return `https://atlassian.design/components/eslint-plugin-design-system/${name}/usage`;
26
24
  }
@@ -1,6 +1,7 @@
1
1
  /* eslint-disable @repo/internal/react/require-jsdoc */
2
2
 
3
3
  import { hasImportDeclaration, insertImportDeclaration, isNodeOfType } from 'eslint-codemod-utils';
4
+ import { Import } from './import';
4
5
  // Little bit unreadable, but better than duplicating the type
5
6
 
6
7
  export var Root = {
@@ -24,5 +25,36 @@ export var Root = {
24
25
  },
25
26
  insertImport: function insertImport(root, data, fixer) {
26
27
  return fixer.insertTextBefore(root[0], "".concat(insertImportDeclaration(data.module, data.specifiers), ";\n"));
28
+ },
29
+ upsertNamedImportDeclaration: function upsertNamedImportDeclaration(_ref, context, fixer) {
30
+ var module = _ref.module,
31
+ specifiers = _ref.specifiers;
32
+ // Find any imports that match the packageName
33
+ var root = context.getSourceCode().ast.body;
34
+ var importDeclarations = this.findImportsByModule(root, module);
35
+
36
+ // The named import doesn't exist yet, we can just insert a whole new one
37
+ if (importDeclarations.length === 0) {
38
+ return this.insertImport(root, {
39
+ module: module,
40
+ specifiers: specifiers
41
+ }, fixer);
42
+ }
43
+
44
+ // The import exists so, modify the existing one
45
+ return Import.insertNamedSpecifiers(importDeclarations[0], specifiers, fixer);
46
+ },
47
+ upsertDefaultImportDeclaration: function upsertDefaultImportDeclaration(_ref2, context, fixer) {
48
+ var module = _ref2.module,
49
+ localName = _ref2.localName;
50
+ // Find any imports that match the packageName
51
+ var root = context.getSourceCode().ast.body;
52
+ var importDeclarations = this.findImportsByModule(root, module);
53
+
54
+ // The import already exist exist
55
+ if (importDeclarations.length > 0) {
56
+ return undefined;
57
+ }
58
+ return fixer.insertTextBefore(root[0], "import ".concat(localName, " from '").concat(module, "';\n"));
27
59
  }
28
60
  };
@@ -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::d95217b658f807294de3c81123068bf1>>
3
+ * @codegen <<SignedSource::2e2cf6c0ecfe1b01f3eb24caa223f09e>>
4
4
  * @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
5
5
  */
6
6
  export default {
@@ -31,6 +31,7 @@ export default {
31
31
  '@atlaskit/design-system/prefer-primitives': 'warn',
32
32
  '@atlaskit/design-system/use-button-group-label': 'warn',
33
33
  '@atlaskit/design-system/use-drawer-label': 'warn',
34
+ '@atlaskit/design-system/use-heading': 'warn',
34
35
  '@atlaskit/design-system/use-heading-level-in-spotlight-card': 'warn',
35
36
  '@atlaskit/design-system/use-href-in-link-item': 'warn',
36
37
  '@atlaskit/design-system/use-primitives': 'warn',
@@ -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::d1c25758089a050334359276ede0ca3a>>
3
+ * @codegen <<SignedSource::eb5c94901f711e67446ba88f0630bf76>>
4
4
  * @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
5
5
  */
6
6
  import consistentCssPropUsage from './consistent-css-prop-usage';
@@ -28,6 +28,7 @@ import noUnsupportedDragAndDropLibraries from './no-unsupported-drag-and-drop-li
28
28
  import preferPrimitives from './prefer-primitives';
29
29
  import useButtonGroupLabel from './use-button-group-label';
30
30
  import useDrawerLabel from './use-drawer-label';
31
+ import useHeading from './use-heading';
31
32
  import useHeadingLevelInSpotlightCard from './use-heading-level-in-spotlight-card';
32
33
  import useHrefInLinkItem from './use-href-in-link-item';
33
34
  import usePrimitives from './use-primitives';
@@ -61,6 +62,7 @@ export default {
61
62
  'prefer-primitives': preferPrimitives,
62
63
  'use-button-group-label': useButtonGroupLabel,
63
64
  'use-drawer-label': useDrawerLabel,
65
+ 'use-heading': useHeading,
64
66
  'use-heading-level-in-spotlight-card': useHeadingLevelInSpotlightCard,
65
67
  'use-href-in-link-item': useHrefInLinkItem,
66
68
  'use-primitives': usePrimitives,
@@ -0,0 +1,9 @@
1
+ var defaults = {
2
+ patterns: ['native-elements']
3
+ };
4
+ export var getConfig = function getConfig(overrides) {
5
+ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign
6
+ // start with an empty object, then merge in the defaults, then merge in overrides.
7
+ // The empty object is returned, as well as modified in place
8
+ return Object.assign({}, defaults, overrides);
9
+ };
@@ -0,0 +1,33 @@
1
+ import { createLintRule } from '../utils/create-rule';
2
+ import { getConfig } from './config';
3
+ import { NativeElements } from './transformers';
4
+ var docsUrl = 'https://atlassian.design/components/heading';
5
+ var rule = createLintRule({
6
+ meta: {
7
+ name: 'use-heading',
8
+ type: 'suggestion',
9
+ fixable: 'code',
10
+ hasSuggestions: true,
11
+ docs: {
12
+ description: 'Encourage the usage of heading components.',
13
+ recommended: false,
14
+ severity: 'warn'
15
+ },
16
+ messages: {
17
+ preferHeading: "This element can be replaced with a \"Heading\" component. See ".concat(docsUrl, " for additional guidance.")
18
+ }
19
+ },
20
+ create: function create(context) {
21
+ var config = getConfig(context.options[0]);
22
+ return {
23
+ // transforms <h1>...</h1> usages
24
+ 'JSXElement[openingElement.name.name=/^h[0-6]$/]': function JSXElementOpeningElementNameNameH06$(node) {
25
+ return NativeElements.lint(node, {
26
+ context: context,
27
+ config: config
28
+ });
29
+ }
30
+ };
31
+ }
32
+ });
33
+ export default rule;
@@ -0,0 +1,9 @@
1
+ import * as ast from '../../../ast-nodes';
2
+ // Rename data-testid prop to testId if present
3
+ export function updateTestIdAttributeFix(node, fixer) {
4
+ var testIdAttr = ast.JSXElement.getAttributeByName(node, 'data-testid');
5
+ if (testIdAttr) {
6
+ return ast.JSXAttribute.updateName(testIdAttr, 'testId', fixer);
7
+ }
8
+ }
9
+ export var allowedAttrs = ['id', 'data-testid', 'key'];
@@ -0,0 +1 @@
1
+ export { NativeElements } from './native-elements';
@@ -0,0 +1,89 @@
1
+ import _toConsumableArray from "@babel/runtime/helpers/toConsumableArray";
2
+ /* eslint-disable @repo/internal/react/require-jsdoc */
3
+
4
+ import { isNodeOfType } from 'eslint-codemod-utils';
5
+ import * as ast from '../../../ast-nodes';
6
+ import { allowedAttrs, updateTestIdAttributeFix } from './common';
7
+ var tagSizeMap = {
8
+ h1: 'xlarge',
9
+ h2: 'large',
10
+ h3: 'medium',
11
+ h4: 'small',
12
+ h5: 'xsmall',
13
+ h6: 'xxsmall'
14
+ };
15
+ export var NativeElements = {
16
+ lint: function lint(node, _ref) {
17
+ var context = _ref.context,
18
+ config = _ref.config;
19
+ // Check whether all criteria needed to make a transformation are met
20
+ if (!NativeElements._check(node, {
21
+ context: context,
22
+ config: config
23
+ })) {
24
+ return;
25
+ }
26
+ context.report({
27
+ node: node,
28
+ messageId: 'preferHeading',
29
+ suggest: [{
30
+ desc: 'Convert to Heading',
31
+ fix: NativeElements._fix(node, {
32
+ context: context,
33
+ config: config
34
+ })
35
+ }]
36
+ });
37
+ },
38
+ _check: function _check(node, _ref2) {
39
+ var config = _ref2.config;
40
+ if (!config.patterns.includes('native-elements')) {
41
+ return false;
42
+ }
43
+ if (!isNodeOfType(node, 'JSXElement')) {
44
+ return false;
45
+ }
46
+ if (!node.parent) {
47
+ return false;
48
+ }
49
+ var elementName = ast.JSXElement.getName(node);
50
+ if (!Object.keys(tagSizeMap).includes(elementName)) {
51
+ return false;
52
+ }
53
+
54
+ // Element has to be first element of its siblings
55
+ if (!(isNodeOfType(node.parent, 'JSXElement') || isNodeOfType(node.parent, 'JSXFragment'))) {
56
+ return false;
57
+ }
58
+ var siblings = ast.JSXElement.getChildren(node.parent);
59
+ if (siblings.length > 1) {
60
+ var _siblings$0$range, _node$range, _siblings$0$range2, _node$range2;
61
+ // Only report if element is first child element
62
+ if (((_siblings$0$range = siblings[0].range) === null || _siblings$0$range === void 0 ? void 0 : _siblings$0$range[0]) !== ((_node$range = node.range) === null || _node$range === void 0 ? void 0 : _node$range[0]) || ((_siblings$0$range2 = siblings[0].range) === null || _siblings$0$range2 === void 0 ? void 0 : _siblings$0$range2[1]) !== ((_node$range2 = node.range) === null || _node$range2 === void 0 ? void 0 : _node$range2[1])) {
63
+ return false;
64
+ }
65
+ }
66
+ if (!ast.JSXElement.hasAllowedAttrsOnly(node, allowedAttrs)) {
67
+ return false;
68
+ }
69
+ return true;
70
+ },
71
+ _fix: function _fix(node, _ref3) {
72
+ var context = _ref3.context;
73
+ return function (fixer) {
74
+ // change to default import
75
+ var importFix = ast.Root.upsertDefaultImportDeclaration({
76
+ module: '@atlaskit/heading',
77
+ localName: 'Heading'
78
+ }, context, fixer);
79
+ var elementName = ast.JSXElement.getName(node);
80
+ var elementNameFixes = ast.JSXElement.updateName(node, 'Heading', fixer);
81
+ var size = tagSizeMap[elementName];
82
+ var asAttributeFix = ast.JSXElement.addAttribute(node, 'size', size, fixer);
83
+ var testAttributeFix = updateTestIdAttributeFix(node, fixer);
84
+ return [importFix].concat(_toConsumableArray(elementNameFixes), [asAttributeFix, testAttributeFix]).filter(function (fix) {
85
+ return Boolean(fix);
86
+ }); // Some of the transformers can return arrays with undefined, so filter them out
87
+ };
88
+ }
89
+ };
@@ -9,7 +9,6 @@ import { convertJsxCallSite } from './convert-jsx-call-site';
9
9
  import { convertStyledComponentToXcss } from './convert-styled-component-call-to-jsx';
10
10
  import { findValidJsxUsageToTransform } from './find-valid-jsx-usage-to-transform';
11
11
  import { findValidStyledComponentCall } from './find-valid-styled-component-call';
12
- import { upsertImportDeclaration } from './upsert-import-declaration';
13
12
  export var CompiledStyled = {
14
13
  lint: function lint(node, _ref) {
15
14
  var context = _ref.context,
@@ -95,7 +94,7 @@ export var CompiledStyled = {
95
94
  if (!calculatedStylesVariableName) {
96
95
  return [];
97
96
  }
98
- var importFixes = upsertImportDeclaration({
97
+ var importFixes = ast.Root.upsertNamedImportDeclaration({
99
98
  module: '@atlaskit/primitives',
100
99
  specifiers: ['Box', 'xcss']
101
100
  }, context, fixer);
@@ -7,7 +7,6 @@ import { getVariableDefinitionValue, getVariableUsagesCount, isValidCssPropertie
7
7
  import { validateStyles } from '../../utils/validate-styles';
8
8
  import { cssToXcssTransformer } from '../css-to-xcss';
9
9
  import * as supported from './supported';
10
- import { upsertImportDeclaration } from './upsert-import-declaration';
11
10
  export var EmotionCSS = {
12
11
  lint: function lint(node, _ref) {
13
12
  var context = _ref.context,
@@ -98,7 +97,7 @@ export var EmotionCSS = {
98
97
  _fix: function _fix(node, _ref3) {
99
98
  var context = _ref3.context;
100
99
  return function (fixer) {
101
- var importFix = upsertImportDeclaration({
100
+ var importFix = ast.Root.upsertNamedImportDeclaration({
102
101
  module: '@atlaskit/primitives',
103
102
  specifiers: ['Box', 'xcss']
104
103
  }, context, fixer);