@atlaskit/eslint-plugin-design-system 8.29.1 → 8.31.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.
- package/CHANGELOG.md +12 -0
- package/README.md +1 -1
- package/configs/deprecated.json +0 -5
- package/constellation/index/usage.mdx +1 -1
- package/constellation/use-primitives-text/usage.mdx +21 -0
- package/dist/cjs/ast-nodes/jsx-element.js +33 -2
- package/dist/cjs/presets/all.codegen.js +2 -2
- package/dist/cjs/presets/recommended.codegen.js +1 -2
- package/dist/cjs/rules/index.codegen.js +3 -3
- package/dist/cjs/rules/use-primitives/transformers/emotion-css/index.js +1 -2
- package/dist/cjs/rules/use-primitives-text/config/index.js +15 -0
- package/dist/cjs/rules/use-primitives-text/index.js +57 -0
- package/dist/cjs/rules/use-primitives-text/transformers/common.js +43 -0
- package/dist/cjs/rules/use-primitives-text/transformers/emphasis-elements.js +82 -0
- package/dist/cjs/rules/use-primitives-text/transformers/index.js +33 -0
- package/dist/cjs/rules/use-primitives-text/transformers/paragraph-elements.js +196 -0
- package/dist/cjs/rules/use-primitives-text/transformers/span-elements.js +87 -0
- package/dist/cjs/rules/use-primitives-text/transformers/strong-elements.js +82 -0
- package/dist/configs/deprecated.json +0 -5
- package/dist/es2019/ast-nodes/jsx-element.js +33 -2
- package/dist/es2019/presets/all.codegen.js +2 -2
- package/dist/es2019/presets/recommended.codegen.js +1 -2
- package/dist/es2019/rules/index.codegen.js +3 -3
- package/dist/es2019/rules/use-primitives/transformers/emotion-css/index.js +1 -2
- package/dist/es2019/rules/use-primitives-text/config/index.js +9 -0
- package/dist/es2019/rules/use-primitives-text/index.js +51 -0
- package/dist/es2019/rules/use-primitives-text/transformers/common.js +33 -0
- package/dist/es2019/rules/use-primitives-text/transformers/emphasis-elements.js +72 -0
- package/dist/es2019/rules/use-primitives-text/transformers/index.js +4 -0
- package/dist/es2019/rules/use-primitives-text/transformers/paragraph-elements.js +187 -0
- package/dist/es2019/rules/use-primitives-text/transformers/span-elements.js +77 -0
- package/dist/es2019/rules/use-primitives-text/transformers/strong-elements.js +72 -0
- package/dist/esm/ast-nodes/jsx-element.js +33 -2
- package/dist/esm/presets/all.codegen.js +2 -2
- package/dist/esm/presets/recommended.codegen.js +1 -2
- package/dist/esm/rules/index.codegen.js +3 -3
- package/dist/esm/rules/use-primitives/transformers/emotion-css/index.js +1 -2
- package/dist/esm/rules/use-primitives-text/config/index.js +9 -0
- package/dist/esm/rules/use-primitives-text/index.js +51 -0
- package/dist/esm/rules/use-primitives-text/transformers/common.js +33 -0
- package/dist/esm/rules/use-primitives-text/transformers/emphasis-elements.js +72 -0
- package/dist/esm/rules/use-primitives-text/transformers/index.js +4 -0
- package/dist/esm/rules/use-primitives-text/transformers/paragraph-elements.js +186 -0
- package/dist/esm/rules/use-primitives-text/transformers/span-elements.js +77 -0
- package/dist/esm/rules/use-primitives-text/transformers/strong-elements.js +72 -0
- package/dist/types/ast-nodes/jsx-element.d.ts +5 -2
- package/dist/types/index.codegen.d.ts +1 -2
- package/dist/types/presets/all.codegen.d.ts +2 -2
- package/dist/types/presets/recommended.codegen.d.ts +1 -2
- package/dist/types/rules/index.codegen.d.ts +1 -1
- package/dist/types/rules/use-primitives-text/config/index.d.ts +6 -0
- package/dist/types/rules/{local-cx-xcss → use-primitives-text}/index.d.ts +1 -1
- package/dist/types/rules/use-primitives-text/transformers/common.d.ts +5 -0
- package/dist/types/rules/use-primitives-text/transformers/emphasis-elements.d.ts +16 -0
- package/dist/types/rules/use-primitives-text/transformers/index.d.ts +4 -0
- package/dist/types/rules/use-primitives-text/transformers/paragraph-elements.d.ts +32 -0
- package/dist/types/rules/use-primitives-text/transformers/span-elements.d.ts +16 -0
- package/dist/types/rules/use-primitives-text/transformers/strong-elements.d.ts +16 -0
- package/dist/types-ts4.5/ast-nodes/jsx-element.d.ts +5 -2
- package/dist/types-ts4.5/index.codegen.d.ts +1 -2
- package/dist/types-ts4.5/presets/all.codegen.d.ts +2 -2
- package/dist/types-ts4.5/presets/recommended.codegen.d.ts +1 -2
- package/dist/types-ts4.5/rules/index.codegen.d.ts +1 -1
- package/dist/types-ts4.5/rules/use-primitives-text/config/index.d.ts +6 -0
- package/dist/types-ts4.5/rules/{local-cx-xcss → use-primitives-text}/index.d.ts +1 -1
- package/dist/types-ts4.5/rules/use-primitives-text/transformers/common.d.ts +5 -0
- package/dist/types-ts4.5/rules/use-primitives-text/transformers/emphasis-elements.d.ts +16 -0
- package/dist/types-ts4.5/rules/use-primitives-text/transformers/index.d.ts +4 -0
- package/dist/types-ts4.5/rules/use-primitives-text/transformers/paragraph-elements.d.ts +32 -0
- package/dist/types-ts4.5/rules/use-primitives-text/transformers/span-elements.d.ts +16 -0
- package/dist/types-ts4.5/rules/use-primitives-text/transformers/strong-elements.d.ts +16 -0
- package/package.json +1 -1
- package/constellation/local-cx-xcss/usage.mdx +0 -37
- package/dist/cjs/rules/local-cx-xcss/index.js +0 -51
- package/dist/cjs/rules/use-primitives/transformers/emotion-css/contains-only-supported-attrs.js +0 -27
- package/dist/es2019/rules/local-cx-xcss/index.js +0 -45
- package/dist/es2019/rules/use-primitives/transformers/emotion-css/contains-only-supported-attrs.js +0 -19
- package/dist/esm/rules/local-cx-xcss/index.js +0 -45
- package/dist/esm/rules/use-primitives/transformers/emotion-css/contains-only-supported-attrs.js +0 -19
- package/dist/types/rules/use-primitives/transformers/emotion-css/contains-only-supported-attrs.d.ts +0 -7
- package/dist/types-ts4.5/rules/use-primitives/transformers/emotion-css/contains-only-supported-attrs.d.ts +0 -7
|
@@ -0,0 +1,72 @@
|
|
|
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 { upsertImportDeclaration } from '../../use-primitives/transformers/emotion-css/upsert-import-declaration';
|
|
6
|
+
import { allowedAttrs, updateTestIdAttributeFix } from './common';
|
|
7
|
+
export const EmphasisElements = {
|
|
8
|
+
lint(node, {
|
|
9
|
+
context,
|
|
10
|
+
config
|
|
11
|
+
}) {
|
|
12
|
+
if (!isNodeOfType(node, 'JSXElement')) {
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// Check whether all criteria needed to make a transformation are met
|
|
17
|
+
if (!EmphasisElements._check(node, {
|
|
18
|
+
context,
|
|
19
|
+
config
|
|
20
|
+
})) {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
context.report({
|
|
24
|
+
node: node.openingElement,
|
|
25
|
+
messageId: 'preferPrimitivesText',
|
|
26
|
+
suggest: [{
|
|
27
|
+
desc: `Convert to Text`,
|
|
28
|
+
fix: EmphasisElements._fix(node, {
|
|
29
|
+
context
|
|
30
|
+
})
|
|
31
|
+
}]
|
|
32
|
+
});
|
|
33
|
+
},
|
|
34
|
+
_check(node, {
|
|
35
|
+
context,
|
|
36
|
+
config
|
|
37
|
+
}) {
|
|
38
|
+
if (!config.patterns.includes('emphasis-elements')) {
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
const elementName = ast.JSXElement.getName(node);
|
|
42
|
+
if (elementName !== 'em') {
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Element has no unallowed props
|
|
47
|
+
if (!ast.JSXElement.hasAllowedAttrsOnly(node, allowedAttrs)) {
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// If there is more than one `@atlaskit/primitives` import, then it becomes difficult to determine which import to transform
|
|
52
|
+
const importDeclaration = ast.Root.findImportsByModule(context.getSourceCode().ast.body, '@atlaskit/primitives');
|
|
53
|
+
if (importDeclaration.length > 1) {
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
return true;
|
|
57
|
+
},
|
|
58
|
+
_fix(node, {
|
|
59
|
+
context
|
|
60
|
+
}) {
|
|
61
|
+
return fixer => {
|
|
62
|
+
const importFix = upsertImportDeclaration({
|
|
63
|
+
module: '@atlaskit/primitives',
|
|
64
|
+
specifiers: ['Text']
|
|
65
|
+
}, context, fixer);
|
|
66
|
+
const elementNameFixes = ast.JSXElement.updateName(node, 'Text', fixer);
|
|
67
|
+
const asAttributeFix = ast.JSXElement.addAttribute(node, 'as', 'em', fixer);
|
|
68
|
+
const testAttributeFix = updateTestIdAttributeFix(node, fixer);
|
|
69
|
+
return [importFix, ...elementNameFixes, asAttributeFix, testAttributeFix].filter(fix => Boolean(fix)); // Some of the transformers can return arrays with undefined, so filter them out
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
};
|
|
@@ -0,0 +1,187 @@
|
|
|
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 { upsertImportDeclaration } from '../../use-primitives/transformers/emotion-css/upsert-import-declaration';
|
|
6
|
+
import { allowedAttrs, updateTestIdAttributeFix } from './common';
|
|
7
|
+
export const ParagraphElements = {
|
|
8
|
+
lint(node, {
|
|
9
|
+
context,
|
|
10
|
+
config
|
|
11
|
+
}) {
|
|
12
|
+
if (!isNodeOfType(node, 'JSXElement')) {
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// Check whether all criteria needed to make a transformation are met
|
|
17
|
+
const {
|
|
18
|
+
success,
|
|
19
|
+
refs
|
|
20
|
+
} = ParagraphElements._check(node, {
|
|
21
|
+
context,
|
|
22
|
+
config
|
|
23
|
+
});
|
|
24
|
+
if (!success) {
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
if (refs.siblings.length > 1) {
|
|
28
|
+
var _refs$siblings$0$loc, _refs$siblings$loc;
|
|
29
|
+
/**
|
|
30
|
+
* Highlighting from first opening element to last closing element
|
|
31
|
+
* to indicate fix will change all p elements and wrap them in a Stack,
|
|
32
|
+
* falls back to first opening element.
|
|
33
|
+
*/
|
|
34
|
+
const startLoc = (_refs$siblings$0$loc = refs.siblings[0].loc) === null || _refs$siblings$0$loc === void 0 ? void 0 : _refs$siblings$0$loc.start;
|
|
35
|
+
const endLoc = (_refs$siblings$loc = refs.siblings[refs.siblings.length - 1].loc) === null || _refs$siblings$loc === void 0 ? void 0 : _refs$siblings$loc.end;
|
|
36
|
+
context.report({
|
|
37
|
+
loc: startLoc && endLoc && {
|
|
38
|
+
start: startLoc,
|
|
39
|
+
end: endLoc
|
|
40
|
+
},
|
|
41
|
+
node: node.openingElement,
|
|
42
|
+
messageId: 'preferPrimitivesStackedText',
|
|
43
|
+
suggest: [{
|
|
44
|
+
desc: 'Convert to Text and Stack',
|
|
45
|
+
fix: ParagraphElements._fixMultiple(node, {
|
|
46
|
+
context,
|
|
47
|
+
refs
|
|
48
|
+
})
|
|
49
|
+
}]
|
|
50
|
+
});
|
|
51
|
+
} else {
|
|
52
|
+
context.report({
|
|
53
|
+
node,
|
|
54
|
+
messageId: 'preferPrimitivesText',
|
|
55
|
+
suggest: [{
|
|
56
|
+
desc: 'Convert to Text',
|
|
57
|
+
fix: ParagraphElements._fixSingle(node, {
|
|
58
|
+
context
|
|
59
|
+
})
|
|
60
|
+
}]
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
_check(node, {
|
|
65
|
+
context,
|
|
66
|
+
config
|
|
67
|
+
}) {
|
|
68
|
+
if (!config.patterns.includes('paragraph-elements')) {
|
|
69
|
+
return {
|
|
70
|
+
success: false,
|
|
71
|
+
refs: {
|
|
72
|
+
siblings: []
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
const elementName = ast.JSXElement.getName(node);
|
|
77
|
+
if (elementName !== 'p') {
|
|
78
|
+
return {
|
|
79
|
+
success: false,
|
|
80
|
+
refs: {
|
|
81
|
+
siblings: []
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// All siblings have to be paragraph elements with no unallowed props
|
|
87
|
+
if (!isNodeOfType(node.parent, 'JSXElement')) {
|
|
88
|
+
return {
|
|
89
|
+
success: false,
|
|
90
|
+
refs: {
|
|
91
|
+
siblings: []
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
const siblings = ast.JSXElement.getChildren(node.parent);
|
|
96
|
+
if (siblings.length > 1) {
|
|
97
|
+
var _siblings$0$range, _node$range, _siblings$0$range2, _node$range2;
|
|
98
|
+
// Only report for the first p element by comparing node location
|
|
99
|
+
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])) {
|
|
100
|
+
return {
|
|
101
|
+
success: false,
|
|
102
|
+
refs: {
|
|
103
|
+
siblings
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
// Only report when every sibling is a p element
|
|
108
|
+
const siblingsMatch = siblings.every(child => {
|
|
109
|
+
if (!isNodeOfType(child, 'JSXElement')) {
|
|
110
|
+
return false;
|
|
111
|
+
}
|
|
112
|
+
if (ast.JSXElement.getName(child) !== 'p') {
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
115
|
+
return ast.JSXElement.hasAllowedAttrsOnly(child, allowedAttrs);
|
|
116
|
+
});
|
|
117
|
+
if (!siblingsMatch) {
|
|
118
|
+
return {
|
|
119
|
+
success: false,
|
|
120
|
+
refs: {
|
|
121
|
+
siblings
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
const importDeclaration = ast.Root.findImportsByModule(context.getSourceCode().ast.body, '@atlaskit/primitives');
|
|
127
|
+
|
|
128
|
+
// If there is more than one `@atlaskit/primitives` import, then it becomes difficult to determine which import to transform
|
|
129
|
+
if (importDeclaration.length > 1) {
|
|
130
|
+
return {
|
|
131
|
+
success: false,
|
|
132
|
+
refs: {
|
|
133
|
+
siblings
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
return {
|
|
138
|
+
success: true,
|
|
139
|
+
refs: {
|
|
140
|
+
siblings
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
},
|
|
144
|
+
_fixSingle(node, {
|
|
145
|
+
context
|
|
146
|
+
}) {
|
|
147
|
+
return fixer => {
|
|
148
|
+
const importFix = upsertImportDeclaration({
|
|
149
|
+
module: '@atlaskit/primitives',
|
|
150
|
+
specifiers: ['Text']
|
|
151
|
+
}, context, fixer);
|
|
152
|
+
const elementNameFixes = ast.JSXElement.updateName(node, 'Text', fixer);
|
|
153
|
+
const testAttributeFix = updateTestIdAttributeFix(node, fixer);
|
|
154
|
+
return [importFix, ...elementNameFixes, testAttributeFix].filter(fix => Boolean(fix)); // Some of the transformers can return arrays with undefined, so filter them out
|
|
155
|
+
};
|
|
156
|
+
},
|
|
157
|
+
|
|
158
|
+
_fixMultiple(node, {
|
|
159
|
+
context,
|
|
160
|
+
refs
|
|
161
|
+
}) {
|
|
162
|
+
return fixer => {
|
|
163
|
+
if (!isNodeOfType(node.parent, 'JSXElement') || !node.parent.closingElement) {
|
|
164
|
+
return [];
|
|
165
|
+
}
|
|
166
|
+
const importFix = upsertImportDeclaration({
|
|
167
|
+
module: '@atlaskit/primitives',
|
|
168
|
+
specifiers: ['Text', 'Stack']
|
|
169
|
+
}, context, fixer);
|
|
170
|
+
|
|
171
|
+
// Update all siblings elements and their attributes
|
|
172
|
+
const siblingFixes = refs.siblings.map(sibling => {
|
|
173
|
+
if (isNodeOfType(sibling, 'JSXElement')) {
|
|
174
|
+
const elementNameFixes = ast.JSXElement.updateName(sibling, 'Text', fixer);
|
|
175
|
+
const testAttributeFix = updateTestIdAttributeFix(sibling, fixer);
|
|
176
|
+
return [...elementNameFixes, testAttributeFix];
|
|
177
|
+
}
|
|
178
|
+
return undefined;
|
|
179
|
+
}).flat();
|
|
180
|
+
|
|
181
|
+
// Wrap in <Stack /> when more than 1 sibling
|
|
182
|
+
const wrapperOpenElementFix = fixer.insertTextAfter(node.parent.openingElement, `<Stack space='space.150'>`);
|
|
183
|
+
const wrapperCloseElementFix = fixer.insertTextBefore(node.parent.closingElement, '</Stack>');
|
|
184
|
+
return [importFix, ...siblingFixes, wrapperOpenElementFix, wrapperCloseElementFix].filter(fix => Boolean(fix)); // Some of the transformers can return arrays with undefined, so filter them out
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
};
|
|
@@ -0,0 +1,77 @@
|
|
|
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 { upsertImportDeclaration } from '../../use-primitives/transformers/emotion-css/upsert-import-declaration';
|
|
6
|
+
import { allowedAttrs, hasTextChildrenOnly, updateTestIdAttributeFix } from './common';
|
|
7
|
+
export const SpanElements = {
|
|
8
|
+
lint(node, {
|
|
9
|
+
context,
|
|
10
|
+
config
|
|
11
|
+
}) {
|
|
12
|
+
if (!isNodeOfType(node, 'JSXElement')) {
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// Check whether all criteria needed to make a transformation are met
|
|
17
|
+
if (!SpanElements._check(node, {
|
|
18
|
+
context,
|
|
19
|
+
config
|
|
20
|
+
})) {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
context.report({
|
|
24
|
+
node: node.openingElement,
|
|
25
|
+
messageId: 'preferPrimitivesText',
|
|
26
|
+
suggest: [{
|
|
27
|
+
desc: `Convert to Text`,
|
|
28
|
+
fix: SpanElements._fix(node, {
|
|
29
|
+
context
|
|
30
|
+
})
|
|
31
|
+
}]
|
|
32
|
+
});
|
|
33
|
+
},
|
|
34
|
+
_check(node, {
|
|
35
|
+
context,
|
|
36
|
+
config
|
|
37
|
+
}) {
|
|
38
|
+
if (!config.patterns.includes('span-elements')) {
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
const elementName = ast.JSXElement.getName(node);
|
|
42
|
+
if (elementName !== 'span') {
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Element has no unallowed props
|
|
47
|
+
if (!ast.JSXElement.hasAllowedAttrsOnly(node, allowedAttrs)) {
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Only allow elements with strings as children
|
|
52
|
+
if (!hasTextChildrenOnly(node)) {
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
const importDeclaration = ast.Root.findImportsByModule(context.getSourceCode().ast.body, '@atlaskit/primitives');
|
|
56
|
+
|
|
57
|
+
// If there is more than one `@atlaskit/primitives` import, then it becomes difficult to determine which import to transform
|
|
58
|
+
if (importDeclaration.length > 1) {
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
return true;
|
|
62
|
+
},
|
|
63
|
+
_fix(node, {
|
|
64
|
+
context
|
|
65
|
+
}) {
|
|
66
|
+
return fixer => {
|
|
67
|
+
const importFix = upsertImportDeclaration({
|
|
68
|
+
module: '@atlaskit/primitives',
|
|
69
|
+
specifiers: ['Text']
|
|
70
|
+
}, context, fixer);
|
|
71
|
+
const elementNameFixes = ast.JSXElement.updateName(node, 'Text', fixer);
|
|
72
|
+
const variantAttributeFix = ast.JSXElement.addAttribute(node, 'variant', 'ui', fixer);
|
|
73
|
+
const testAttributeFix = updateTestIdAttributeFix(node, fixer);
|
|
74
|
+
return [importFix, ...elementNameFixes, variantAttributeFix, testAttributeFix].filter(fix => Boolean(fix)); // Some of the transformers can return arrays with undefined, so filter them out
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
};
|
|
@@ -0,0 +1,72 @@
|
|
|
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 { upsertImportDeclaration } from '../../use-primitives/transformers/emotion-css/upsert-import-declaration';
|
|
6
|
+
import { allowedAttrs, updateTestIdAttributeFix } from './common';
|
|
7
|
+
export const StrongElements = {
|
|
8
|
+
lint(node, {
|
|
9
|
+
context,
|
|
10
|
+
config
|
|
11
|
+
}) {
|
|
12
|
+
if (!isNodeOfType(node, 'JSXElement')) {
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// Check whether all criteria needed to make a transformation are met
|
|
17
|
+
if (!StrongElements._check(node, {
|
|
18
|
+
context,
|
|
19
|
+
config
|
|
20
|
+
})) {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
context.report({
|
|
24
|
+
node: node.openingElement,
|
|
25
|
+
messageId: 'preferPrimitivesText',
|
|
26
|
+
suggest: [{
|
|
27
|
+
desc: `Convert to Text`,
|
|
28
|
+
fix: StrongElements._fix(node, {
|
|
29
|
+
context
|
|
30
|
+
})
|
|
31
|
+
}]
|
|
32
|
+
});
|
|
33
|
+
},
|
|
34
|
+
_check(node, {
|
|
35
|
+
context,
|
|
36
|
+
config
|
|
37
|
+
}) {
|
|
38
|
+
if (!config.patterns.includes('strong-elements')) {
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
const elementName = ast.JSXElement.getName(node);
|
|
42
|
+
if (elementName !== 'strong') {
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Element has no unallowed props
|
|
47
|
+
if (!ast.JSXElement.hasAllowedAttrsOnly(node, allowedAttrs)) {
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// If there is more than one `@atlaskit/primitives` import, then it becomes difficult to determine which import to transform
|
|
52
|
+
const importDeclaration = ast.Root.findImportsByModule(context.getSourceCode().ast.body, '@atlaskit/primitives');
|
|
53
|
+
if (importDeclaration.length > 1) {
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
return true;
|
|
57
|
+
},
|
|
58
|
+
_fix(node, {
|
|
59
|
+
context
|
|
60
|
+
}) {
|
|
61
|
+
return fixer => {
|
|
62
|
+
const importFix = upsertImportDeclaration({
|
|
63
|
+
module: '@atlaskit/primitives',
|
|
64
|
+
specifiers: ['Text']
|
|
65
|
+
}, context, fixer);
|
|
66
|
+
const elementNameFixes = ast.JSXElement.updateName(node, 'Text', fixer);
|
|
67
|
+
const asAttributeFix = ast.JSXElement.addAttribute(node, 'as', 'strong', fixer);
|
|
68
|
+
const testAttributeFix = updateTestIdAttributeFix(node, fixer);
|
|
69
|
+
return [importFix, ...elementNameFixes, asAttributeFix, testAttributeFix].filter(fix => Boolean(fix)); // Some of the transformers can return arrays with undefined, so filter them out
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { isNodeOfType, jsxIdentifier } from 'eslint-codemod-utils';
|
|
2
|
+
import { JSXAttribute as HelperJSXAttribute } from './jsx-attribute';
|
|
2
3
|
export var JSXElementHelper = {
|
|
3
4
|
/**
|
|
4
5
|
* Names of JSXElements can be any of:
|
|
@@ -6,8 +7,8 @@ export var JSXElementHelper = {
|
|
|
6
7
|
* `<MyComponents.Component></MyComponents.Component>` - `MyComponents` is a namespace (JSXNamespacedName)
|
|
7
8
|
* `<MyComponents.Component></MyComponents.Component>` - `MyComponents` is an object (JSXMemberExpression)
|
|
8
9
|
*
|
|
9
|
-
*
|
|
10
|
-
* e.g.
|
|
10
|
+
* getting the name of a JSXMemberExpression is difficult, because object can contain objects, which is recursively defined in the AST.
|
|
11
|
+
* e.g. Getting the name of `<MyComponents.PresentationLayer.LeftSideBar.Header />` would require `getName` to recursively resolve all parts of the name.
|
|
11
12
|
* `getName` does not currently have this functionality. Add it if you need it.
|
|
12
13
|
*/
|
|
13
14
|
getName: function getName(node) {
|
|
@@ -45,6 +46,36 @@ export var JSXElementHelper = {
|
|
|
45
46
|
return node.openingElement.attributes.some(function (attr) {
|
|
46
47
|
return isNodeOfType(attr, 'JSXSpreadAttribute');
|
|
47
48
|
});
|
|
49
|
+
},
|
|
50
|
+
addAttribute: function addAttribute(node, name, value, fixer) {
|
|
51
|
+
var attributeString = " ".concat(name, "='").concat(value, "'");
|
|
52
|
+
var isSelfClosing = JSXElementHelper.isSelfClosing(node);
|
|
53
|
+
var start = node.openingElement.range ? node.openingElement.range[0] : 0;
|
|
54
|
+
var end = node.openingElement.range ? node.openingElement.range[1] - (isSelfClosing ? 3 : 1) : 0;
|
|
55
|
+
var range = [start, end];
|
|
56
|
+
var fix = fixer.insertTextAfterRange(range, attributeString);
|
|
57
|
+
return fix;
|
|
58
|
+
},
|
|
59
|
+
getChildren: function getChildren(node) {
|
|
60
|
+
// Filter out text children with whitespace characters only as JSX removes whitespace used for intendation
|
|
61
|
+
var filteredChildren = node.children.filter(function (child) {
|
|
62
|
+
if (isNodeOfType(child, 'JSXText')) {
|
|
63
|
+
var whiteSpaceChars = new RegExp('\\s', 'g');
|
|
64
|
+
return !whiteSpaceChars.test(child.value);
|
|
65
|
+
}
|
|
66
|
+
return true;
|
|
67
|
+
});
|
|
68
|
+
return filteredChildren;
|
|
69
|
+
},
|
|
70
|
+
hasAllowedAttrsOnly: function hasAllowedAttrsOnly(node, allowedProps) {
|
|
71
|
+
var attrs = JSXElementHelper.getAttributes(node);
|
|
72
|
+
return attrs.every(function (attr) {
|
|
73
|
+
if (!isNodeOfType(attr, 'JSXAttribute')) {
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
var name = HelperJSXAttribute.getName(attr);
|
|
77
|
+
return allowedProps.includes(name);
|
|
78
|
+
});
|
|
48
79
|
}
|
|
49
80
|
};
|
|
50
81
|
export { JSXElementHelper as JSXElement };
|
|
@@ -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::
|
|
3
|
+
* @codegen <<SignedSource::aa64b4343b8763cfa5aa66230faea408>>
|
|
4
4
|
* @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
|
|
5
5
|
*/
|
|
6
6
|
export default {
|
|
@@ -10,7 +10,6 @@ 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',
|
|
14
13
|
'@atlaskit/design-system/no-banned-imports': 'error',
|
|
15
14
|
'@atlaskit/design-system/no-css-tagged-template-expression': 'error',
|
|
16
15
|
'@atlaskit/design-system/no-deprecated-apis': 'error',
|
|
@@ -34,6 +33,7 @@ export default {
|
|
|
34
33
|
'@atlaskit/design-system/use-heading-level-in-spotlight-card': 'warn',
|
|
35
34
|
'@atlaskit/design-system/use-href-in-link-item': 'warn',
|
|
36
35
|
'@atlaskit/design-system/use-primitives': 'warn',
|
|
36
|
+
'@atlaskit/design-system/use-primitives-text': 'warn',
|
|
37
37
|
'@atlaskit/design-system/use-visually-hidden': 'error'
|
|
38
38
|
}
|
|
39
39
|
};
|
|
@@ -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::
|
|
3
|
+
* @codegen <<SignedSource::aebc778f0b10032cbb2941b55189d766>>
|
|
4
4
|
* @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
|
|
5
5
|
*/
|
|
6
6
|
export default {
|
|
@@ -9,7 +9,6 @@ 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',
|
|
13
12
|
'@atlaskit/design-system/no-banned-imports': 'error',
|
|
14
13
|
'@atlaskit/design-system/no-deprecated-apis': 'error',
|
|
15
14
|
'@atlaskit/design-system/no-deprecated-design-token-usage': 'warn',
|
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* THIS FILE WAS CREATED VIA CODEGEN DO NOT MODIFY {@see http://go/af-codegen}
|
|
3
|
-
* @codegen <<SignedSource::
|
|
3
|
+
* @codegen <<SignedSource::7d840a5791dae4a9dfc4dda8ec00dcf9>>
|
|
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';
|
|
11
10
|
import noBannedImports from './no-banned-imports';
|
|
12
11
|
import noCssTaggedTemplateExpression from './no-css-tagged-template-expression';
|
|
13
12
|
import noDeprecatedApis from './no-deprecated-apis';
|
|
@@ -31,13 +30,13 @@ import useDrawerLabel from './use-drawer-label';
|
|
|
31
30
|
import useHeadingLevelInSpotlightCard from './use-heading-level-in-spotlight-card';
|
|
32
31
|
import useHrefInLinkItem from './use-href-in-link-item';
|
|
33
32
|
import usePrimitives from './use-primitives';
|
|
33
|
+
import usePrimitivesText from './use-primitives-text';
|
|
34
34
|
import useVisuallyHidden from './use-visually-hidden';
|
|
35
35
|
export default {
|
|
36
36
|
'consistent-css-prop-usage': consistentCssPropUsage,
|
|
37
37
|
'ensure-design-token-usage': ensureDesignTokenUsage,
|
|
38
38
|
'ensure-design-token-usage/preview': ensureDesignTokenUsagePreview,
|
|
39
39
|
'icon-label': iconLabel,
|
|
40
|
-
'local-cx-xcss': localCxXcss,
|
|
41
40
|
'no-banned-imports': noBannedImports,
|
|
42
41
|
'no-css-tagged-template-expression': noCssTaggedTemplateExpression,
|
|
43
42
|
'no-deprecated-apis': noDeprecatedApis,
|
|
@@ -61,5 +60,6 @@ export default {
|
|
|
61
60
|
'use-heading-level-in-spotlight-card': useHeadingLevelInSpotlightCard,
|
|
62
61
|
'use-href-in-link-item': useHrefInLinkItem,
|
|
63
62
|
'use-primitives': usePrimitives,
|
|
63
|
+
'use-primitives-text': usePrimitivesText,
|
|
64
64
|
'use-visually-hidden': useVisuallyHidden
|
|
65
65
|
};
|
|
@@ -5,7 +5,6 @@ import { getIdentifierInParentScope, isNodeOfType } from 'eslint-codemod-utils';
|
|
|
5
5
|
import * as ast from '../../../../ast-nodes';
|
|
6
6
|
import { getVariableDefinitionValue, getVariableUsagesCount, isValidCssPropertiesToTransform } from '../../utils';
|
|
7
7
|
import { cssToXcssTransformer } from '../css-to-xcss';
|
|
8
|
-
import { containsOnlySupportedAttrs } from './contains-only-supported-attrs';
|
|
9
8
|
import * as supported from './supported';
|
|
10
9
|
import { upsertImportDeclaration } from './upsert-import-declaration';
|
|
11
10
|
export var EmotionCSS = {
|
|
@@ -55,7 +54,7 @@ export var EmotionCSS = {
|
|
|
55
54
|
}
|
|
56
55
|
|
|
57
56
|
// Ignore elements that contain dangerous attributes like `id`.
|
|
58
|
-
if (!
|
|
57
|
+
if (!ast.JSXElement.hasAllowedAttrsOnly(node, supported.attributes)) {
|
|
59
58
|
return false;
|
|
60
59
|
}
|
|
61
60
|
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
var defaults = {
|
|
2
|
+
patterns: ['paragraph-elements', 'span-elements', 'strong-elements', 'emphasis-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,51 @@
|
|
|
1
|
+
import { createLintRule } from '../utils/create-rule';
|
|
2
|
+
import { getConfig } from './config';
|
|
3
|
+
import { EmphasisElements, ParagraphElements, SpanElements, StrongElements } from './transformers';
|
|
4
|
+
var textDocsUrl = 'https://atlassian.design/components/primitives/text';
|
|
5
|
+
var rule = createLintRule({
|
|
6
|
+
meta: {
|
|
7
|
+
name: 'use-primitives-text',
|
|
8
|
+
type: 'suggestion',
|
|
9
|
+
fixable: 'code',
|
|
10
|
+
hasSuggestions: true,
|
|
11
|
+
docs: {
|
|
12
|
+
description: 'Encourage the usage of text components.',
|
|
13
|
+
recommended: false,
|
|
14
|
+
severity: 'warn'
|
|
15
|
+
},
|
|
16
|
+
messages: {
|
|
17
|
+
preferPrimitivesText: "This element can be replaced with a \"Text\" primitive. See ".concat(textDocsUrl, " for additional guidance."),
|
|
18
|
+
preferPrimitivesStackedText: "These paragraphs can be replaced with a \"Text\" and \"Stack\" primitives. See ".concat(textDocsUrl, " for additional guidance.")
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
create: function create(context) {
|
|
22
|
+
var config = getConfig(context.options[0]);
|
|
23
|
+
return {
|
|
24
|
+
'JSXElement[openingElement.name.name=span]': function JSXElementOpeningElementNameNameSpan(node) {
|
|
25
|
+
return SpanElements.lint(node, {
|
|
26
|
+
context: context,
|
|
27
|
+
config: config
|
|
28
|
+
});
|
|
29
|
+
},
|
|
30
|
+
'JSXElement[openingElement.name.name=p]': function JSXElementOpeningElementNameNameP(node) {
|
|
31
|
+
return ParagraphElements.lint(node, {
|
|
32
|
+
context: context,
|
|
33
|
+
config: config
|
|
34
|
+
});
|
|
35
|
+
},
|
|
36
|
+
'JSXElement[openingElement.name.name=strong]': function JSXElementOpeningElementNameNameStrong(node) {
|
|
37
|
+
return StrongElements.lint(node, {
|
|
38
|
+
context: context,
|
|
39
|
+
config: config
|
|
40
|
+
});
|
|
41
|
+
},
|
|
42
|
+
'JSXElement[openingElement.name.name=em]': function JSXElementOpeningElementNameNameEm(node) {
|
|
43
|
+
return EmphasisElements.lint(node, {
|
|
44
|
+
context: context,
|
|
45
|
+
config: config
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
export default rule;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { isNodeOfType } from 'eslint-codemod-utils';
|
|
2
|
+
import * as ast from '../../../ast-nodes';
|
|
3
|
+
|
|
4
|
+
// Rename data-testid prop to testId if present
|
|
5
|
+
export function updateTestIdAttributeFix(node, fixer) {
|
|
6
|
+
var testIdAttr = ast.JSXElement.getAttributeByName(node, 'data-testid');
|
|
7
|
+
if (testIdAttr) {
|
|
8
|
+
return ast.JSXAttribute.updateName(testIdAttr, 'testId', fixer);
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
export var allowedAttrs = ['id', 'data-testid', 'key'];
|
|
12
|
+
|
|
13
|
+
// Only allow elements with strings as children
|
|
14
|
+
// The use of `FormattedMessage` component and `formatMessage` are allowed as these are used for i18n
|
|
15
|
+
export function hasTextChildrenOnly(node) {
|
|
16
|
+
var _node$children;
|
|
17
|
+
return (_node$children = node.children) === null || _node$children === void 0 ? void 0 : _node$children.every(function (child) {
|
|
18
|
+
if (isNodeOfType(child, 'JSXText')) {
|
|
19
|
+
return true;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// JSX child element <span><FormattedMessage /></span>
|
|
23
|
+
if (isNodeOfType(child, 'JSXElement') && ast.JSXElement.getName(child) === 'FormattedMessage') {
|
|
24
|
+
return true;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// JSX expression <span>{formatMessage(...)}</span>
|
|
28
|
+
if (isNodeOfType(child, 'JSXExpressionContainer') && isNodeOfType(child.expression, 'CallExpression')) {
|
|
29
|
+
return ast.FunctionCall.getName(child.expression) === 'formatMessage';
|
|
30
|
+
}
|
|
31
|
+
return false;
|
|
32
|
+
});
|
|
33
|
+
}
|