@atlaskit/eslint-plugin-design-system 9.0.0 → 9.2.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 +13 -0
- package/dist/cjs/rules/ensure-design-token-usage-preview/index.js +13 -7
- package/dist/cjs/rules/prefer-primitives/index.js +12 -82
- package/dist/cjs/rules/use-tokens-typography/transformers/style-object.js +47 -6
- package/dist/cjs/rules/use-tokens-typography/utils.js +39 -3
- package/dist/es2019/rules/ensure-design-token-usage-preview/index.js +13 -7
- package/dist/es2019/rules/prefer-primitives/index.js +12 -83
- package/dist/es2019/rules/use-tokens-typography/transformers/style-object.js +49 -8
- package/dist/es2019/rules/use-tokens-typography/utils.js +37 -5
- package/dist/esm/rules/ensure-design-token-usage-preview/index.js +13 -7
- package/dist/esm/rules/prefer-primitives/index.js +12 -82
- package/dist/esm/rules/use-tokens-typography/transformers/style-object.js +49 -8
- package/dist/esm/rules/use-tokens-typography/utils.js +37 -4
- package/dist/types/rules/prefer-primitives/index.d.ts +1 -2
- package/dist/types/rules/use-tokens-typography/transformers/style-object.d.ts +4 -0
- package/dist/types/rules/use-tokens-typography/utils.d.ts +8 -4
- package/dist/types-ts4.5/rules/prefer-primitives/index.d.ts +1 -2
- package/dist/types-ts4.5/rules/use-tokens-typography/transformers/style-object.d.ts +4 -0
- package/dist/types-ts4.5/rules/use-tokens-typography/utils.d.ts +8 -4
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# @atlaskit/eslint-plugin-design-system
|
|
2
2
|
|
|
3
|
+
## 9.2.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [#84330](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/pull-requests/84330) [`391be0d8e414`](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/commits/391be0d8e414) - - `prefer-primitives`: This rule is now deprecated. Please use `use-primitives` instead.
|
|
8
|
+
- `ensure-design-token-usage/preview`: This rule is now deprecated. Please use `use-tokens-space` instead.
|
|
9
|
+
|
|
10
|
+
## 9.1.0
|
|
11
|
+
|
|
12
|
+
### Minor Changes
|
|
13
|
+
|
|
14
|
+
- [#84334](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/pull-requests/84334) [`b2134858ba58`](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/commits/b2134858ba58) - The `use-tokens-typography` rule now applies token fallbacks that reference the constants exported via `@atlaskit/theme/typography`, rather than applying the fallback string inline.
|
|
15
|
+
|
|
3
16
|
## 9.0.0
|
|
4
17
|
|
|
5
18
|
### Major Changes
|
|
@@ -6,25 +6,31 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
6
6
|
});
|
|
7
7
|
exports.default = void 0;
|
|
8
8
|
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
|
|
9
|
-
var _ensureDesignTokenUsage = require("../ensure-design-token-usage");
|
|
10
9
|
var _ruleMeta = _interopRequireDefault(require("../ensure-design-token-usage/rule-meta"));
|
|
11
10
|
var _createRule = require("../utils/create-rule");
|
|
12
11
|
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
|
13
12
|
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty2.default)(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
|
|
14
|
-
var defaultConfig = {
|
|
15
|
-
domains: ['spacing'],
|
|
16
|
-
applyImport: true,
|
|
17
|
-
shouldEnforceFallbacks: false
|
|
18
|
-
};
|
|
19
13
|
var rule = (0, _createRule.createLintRule)({
|
|
20
14
|
meta: _objectSpread(_objectSpread({}, _ruleMeta.default), {}, {
|
|
21
15
|
name: 'ensure-design-token-usage/preview',
|
|
16
|
+
deprecated: true,
|
|
17
|
+
replacedBy: ['@atlaskit/design-system/use-tokens-space'],
|
|
22
18
|
docs: _objectSpread(_objectSpread({}, _ruleMeta.default.docs), {}, {
|
|
23
19
|
description: 'Enforces usage of pre-release design tokens rather than hard-coded values.',
|
|
24
20
|
recommended: false,
|
|
25
21
|
severity: 'warn'
|
|
26
22
|
})
|
|
27
23
|
}),
|
|
28
|
-
create:
|
|
24
|
+
create: function create() {
|
|
25
|
+
/**
|
|
26
|
+
* We can't just outright delete the ESLint rule, since:
|
|
27
|
+
* ```
|
|
28
|
+
* // eslint-disable @eslint-plugin/design-system/ensure-design-token-usage/preview
|
|
29
|
+
* ```
|
|
30
|
+
* will cause CI to fail if the rule definition doesn't exist. So, instead
|
|
31
|
+
* we can change the implementation of the rule so that it never reports.
|
|
32
|
+
*/
|
|
33
|
+
return {};
|
|
34
|
+
}
|
|
29
35
|
});
|
|
30
36
|
var _default = exports.default = rule;
|
|
@@ -4,17 +4,15 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
6
|
exports.default = void 0;
|
|
7
|
-
var _eslintCodemodUtils = require("eslint-codemod-utils");
|
|
8
7
|
var _createRule = require("../utils/create-rule");
|
|
9
|
-
var _utils = require("./utils");
|
|
10
|
-
// eslint-disable-next-line import/no-extraneous-dependencies
|
|
11
|
-
|
|
12
8
|
var primitiveDocsUrl = 'https://go.atlassian.com/dst-prefer-primitives';
|
|
13
9
|
var rule = (0, _createRule.createLintRule)({
|
|
14
10
|
meta: {
|
|
15
11
|
name: 'prefer-primitives',
|
|
16
12
|
type: 'suggestion',
|
|
17
13
|
hasSuggestions: false,
|
|
14
|
+
deprecated: true,
|
|
15
|
+
replacedBy: ['@atlaskit/design-system/use-primitives'],
|
|
18
16
|
docs: {
|
|
19
17
|
description: 'Increase awareness of primitive components via code hints. Strictly used for education purposes and discoverability. To enforce usage please refer to the `use-primitives` rule.',
|
|
20
18
|
recommended: false,
|
|
@@ -24,84 +22,16 @@ var rule = (0, _createRule.createLintRule)({
|
|
|
24
22
|
preferPrimitives: "This \"{{element}}\" may be able to be replaced with a primitive component. See ".concat(primitiveDocsUrl, " for guidance.")
|
|
25
23
|
}
|
|
26
24
|
},
|
|
27
|
-
create: function create(
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
}
|
|
38
|
-
var suggest = (0, _utils.shouldSuggest)(node === null || node === void 0 ? void 0 : node.parent);
|
|
39
|
-
if (suggest) {
|
|
40
|
-
context.report({
|
|
41
|
-
node: node,
|
|
42
|
-
messageId: 'preferPrimitives',
|
|
43
|
-
data: {
|
|
44
|
-
element: node.name.name
|
|
45
|
-
}
|
|
46
|
-
});
|
|
47
|
-
}
|
|
48
|
-
},
|
|
49
|
-
// styled.x`` | styled2.x`` | styled.div()
|
|
50
|
-
'MemberExpression[object.name="styled"],MemberExpression[object.name="styled2"]': function MemberExpressionObjectNameStyledMemberExpressionObjectNameStyled2(node) {
|
|
51
|
-
if (!(0, _eslintCodemodUtils.isNodeOfType)(node, 'MemberExpression')) {
|
|
52
|
-
return;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
// styled.div``
|
|
56
|
-
if ((0, _eslintCodemodUtils.isNodeOfType)(node.property, 'Identifier')) {
|
|
57
|
-
if (_utils.validPrimitiveElements.has(node.property.name)) {
|
|
58
|
-
var styledIdentifier = node.object.name;
|
|
59
|
-
var elementName = node.property.name;
|
|
60
|
-
|
|
61
|
-
// Including the `styled.` portion in the message to help makers understand it's not just the `div` element that should be replaced
|
|
62
|
-
var reportName = "".concat(styledIdentifier, ".").concat(elementName); // styled.div
|
|
63
|
-
|
|
64
|
-
context.report({
|
|
65
|
-
node: node,
|
|
66
|
-
messageId: 'preferPrimitives',
|
|
67
|
-
data: {
|
|
68
|
-
element: reportName
|
|
69
|
-
}
|
|
70
|
-
});
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
},
|
|
74
|
-
// styled(X)``
|
|
75
|
-
'CallExpression[callee.name="styled"]': function CallExpressionCalleeNameStyled(node) {
|
|
76
|
-
if (!(0, _eslintCodemodUtils.isNodeOfType)(node, 'CallExpression')) {
|
|
77
|
-
return;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
// styled('div')`` - We only care about 'div'/'span', ignore extending other components
|
|
81
|
-
if ((0, _eslintCodemodUtils.isNodeOfType)(node.arguments[0], 'Literal')) {
|
|
82
|
-
var argValue = node.arguments[0].raw;
|
|
83
|
-
if (typeof argValue === 'string') {
|
|
84
|
-
var suggest = _utils.validPrimitiveElements.has(argValue.replaceAll("'", '') // argValue will have '' around the element name, strip it out for this test
|
|
85
|
-
);
|
|
86
|
-
if (suggest) {
|
|
87
|
-
var styledIdentifier = node.callee.name;
|
|
88
|
-
var elementName = argValue;
|
|
89
|
-
|
|
90
|
-
// Including the `styled()` portion in the message to help makers understand it's not just the `div` element that should be replaced
|
|
91
|
-
var reportName = "".concat(styledIdentifier, "(").concat(elementName, ")"); // styled('div')
|
|
92
|
-
|
|
93
|
-
context.report({
|
|
94
|
-
node: node,
|
|
95
|
-
messageId: 'preferPrimitives',
|
|
96
|
-
data: {
|
|
97
|
-
element: reportName
|
|
98
|
-
}
|
|
99
|
-
});
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
};
|
|
25
|
+
create: function create() {
|
|
26
|
+
/**
|
|
27
|
+
* We can't just outright delete the ESLint rule, since:
|
|
28
|
+
* ```
|
|
29
|
+
* // eslint-disable @eslint-plugin/design-system/prefer-primitives
|
|
30
|
+
* ```
|
|
31
|
+
* will cause CI to fail if the rule definition doesn't exist. So, instead
|
|
32
|
+
* we can change the implementation of the rule so that it never reports.
|
|
33
|
+
*/
|
|
34
|
+
return {};
|
|
105
35
|
}
|
|
106
36
|
});
|
|
107
37
|
var _default = exports.default = rule;
|
|
@@ -32,7 +32,9 @@ var StyleObject = exports.StyleObject = {
|
|
|
32
32
|
}
|
|
33
33
|
var fontSizeNode = refs.fontSizeNode,
|
|
34
34
|
fontSizeRaw = refs.fontSizeRaw,
|
|
35
|
-
tokensImportNode = refs.tokensImportNode
|
|
35
|
+
tokensImportNode = refs.tokensImportNode,
|
|
36
|
+
themeImportNode = refs.themeImportNode,
|
|
37
|
+
shouldAddFallbackImport = refs.shouldAddFallbackImport;
|
|
36
38
|
var fontSizeValue = (0, _utils.normaliseValue)('fontSize', fontSizeRaw);
|
|
37
39
|
|
|
38
40
|
// -- Font weight --
|
|
@@ -123,6 +125,8 @@ var StyleObject = exports.StyleObject = {
|
|
|
123
125
|
matchingToken: matchingToken,
|
|
124
126
|
nodesToReplace: nodesToReplace,
|
|
125
127
|
tokensImportNode: tokensImportNode,
|
|
128
|
+
themeImportNode: themeImportNode,
|
|
129
|
+
shouldAddFallbackImport: shouldAddFallbackImport,
|
|
126
130
|
fontWeightReplacement: fontWeightReplacement,
|
|
127
131
|
fontFamilyReplacement: fontFamilyReplacement,
|
|
128
132
|
fontStyleReplacement: fontStyleReplacement
|
|
@@ -169,20 +173,47 @@ var StyleObject = exports.StyleObject = {
|
|
|
169
173
|
success: false
|
|
170
174
|
};
|
|
171
175
|
}
|
|
172
|
-
var
|
|
176
|
+
var tokensImportDeclaration = _astNodes.Root.findImportsByModule(context.getSourceCode().ast.body, '@atlaskit/tokens');
|
|
173
177
|
|
|
174
178
|
// If there is more than one `@atlaskit/tokens` import, then it becomes difficult to determine which import to transform
|
|
175
|
-
if (
|
|
179
|
+
if (tokensImportDeclaration.length > 1) {
|
|
176
180
|
return {
|
|
177
181
|
success: false
|
|
178
182
|
};
|
|
179
183
|
}
|
|
184
|
+
|
|
185
|
+
// This exists purely because we're not inlining the fallback values
|
|
186
|
+
// and instead referencing a `fontFallback` object that exists in @atlaskit/theme/typography.
|
|
187
|
+
// This is a temporary measure until fallbacks are no longer required
|
|
188
|
+
var shouldAddFallbackImport = 'full';
|
|
189
|
+
var themeImportDeclaration = _astNodes.Root.findImportsByModule(context.getSourceCode().ast.body, '@atlaskit/theme/typography');
|
|
190
|
+
if (themeImportDeclaration.length) {
|
|
191
|
+
// Import exists, check if specifier exists
|
|
192
|
+
shouldAddFallbackImport = 'specifier';
|
|
193
|
+
var fallbackImport = themeImportDeclaration[0].specifiers.find(function (specifier) {
|
|
194
|
+
// @atlaskit/theme/typography has no default export so we can safely narrow this type
|
|
195
|
+
if (!(0, _eslintCodemodUtils.isNodeOfType)(specifier, 'ImportSpecifier')) {
|
|
196
|
+
return false;
|
|
197
|
+
}
|
|
198
|
+
if (specifier.imported.name === 'fontFallback') {
|
|
199
|
+
return true;
|
|
200
|
+
}
|
|
201
|
+
return false;
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
// Exact import already exists, no need to add
|
|
205
|
+
if (fallbackImport) {
|
|
206
|
+
shouldAddFallbackImport = false;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
180
209
|
return {
|
|
181
210
|
success: true,
|
|
182
211
|
refs: {
|
|
183
212
|
fontSizeNode: fontSizeNode,
|
|
184
213
|
fontSizeRaw: fontSizeRaw,
|
|
185
|
-
tokensImportNode:
|
|
214
|
+
tokensImportNode: tokensImportDeclaration[0],
|
|
215
|
+
themeImportNode: themeImportDeclaration[0],
|
|
216
|
+
shouldAddFallbackImport: shouldAddFallbackImport
|
|
186
217
|
}
|
|
187
218
|
};
|
|
188
219
|
},
|
|
@@ -191,14 +222,24 @@ var StyleObject = exports.StyleObject = {
|
|
|
191
222
|
var matchingToken = refs.matchingToken,
|
|
192
223
|
nodesToReplace = refs.nodesToReplace,
|
|
193
224
|
tokensImportNode = refs.tokensImportNode,
|
|
225
|
+
themeImportNode = refs.themeImportNode,
|
|
226
|
+
shouldAddFallbackImport = refs.shouldAddFallbackImport,
|
|
194
227
|
fontWeightReplacement = refs.fontWeightReplacement,
|
|
195
228
|
fontFamilyReplacement = refs.fontFamilyReplacement,
|
|
196
229
|
fontStyleReplacement = refs.fontStyleReplacement;
|
|
197
230
|
var fontSizeNode = nodesToReplace[0];
|
|
198
|
-
|
|
231
|
+
var root = context.getSourceCode().ast.body;
|
|
232
|
+
var fallbackImport;
|
|
233
|
+
if (shouldAddFallbackImport === 'full') {
|
|
234
|
+
fallbackImport = (0, _utils2.insertFallbackImportFull)(root, fixer);
|
|
235
|
+
} else if (shouldAddFallbackImport === 'specifier') {
|
|
236
|
+
fallbackImport = (0, _utils2.insertFallbackImportSpecifier)(fixer, themeImportNode);
|
|
237
|
+
}
|
|
238
|
+
var fallbackName = (matchingToken.tokenName === 'font.body' ? 'font.body.medium' : matchingToken.tokenName).replace('font', 'fontFallback');
|
|
239
|
+
return (!tokensImportNode ? [(0, _utils2.insertTokensImport)(root, fixer)] : []).concat(fallbackImport ? [fallbackImport] : [], nodesToReplace.map(function (node, index) {
|
|
199
240
|
// Replace first node with token, delete remaining nodes. Guaranteed to be fontSize
|
|
200
241
|
if (index === 0) {
|
|
201
|
-
return fixer.replaceText(node, "".concat((0, _utils2.getTokenProperty)('font', matchingToken.tokenName,
|
|
242
|
+
return fixer.replaceText(node, "".concat((0, _utils2.getTokenProperty)('font', matchingToken.tokenName, fallbackName, true)));
|
|
202
243
|
}
|
|
203
244
|
|
|
204
245
|
// We don't replace fontWeight/fontFamily/fontStyle here in case it occurs before the font property.
|
|
@@ -11,6 +11,9 @@ exports.findTypographyTokenForValues = findTypographyTokenForValues;
|
|
|
11
11
|
exports.fontWeightTokens = exports.fontWeightMap = exports.fontFamilyTokens = void 0;
|
|
12
12
|
exports.getLiteralProperty = getLiteralProperty;
|
|
13
13
|
exports.getTokenProperty = getTokenProperty;
|
|
14
|
+
exports.insertFallbackImportFull = insertFallbackImportFull;
|
|
15
|
+
exports.insertFallbackImportSpecifier = insertFallbackImportSpecifier;
|
|
16
|
+
exports.insertTokensImport = insertTokensImport;
|
|
14
17
|
exports.isTypographyProperty = exports.isFontSizeSmall = exports.isFontSize = exports.isFontFamily = exports.isCodeFontFamily = void 0;
|
|
15
18
|
exports.isValidPropertyNode = isValidPropertyNode;
|
|
16
19
|
exports.notUndefined = notUndefined;
|
|
@@ -18,6 +21,7 @@ exports.typographyValueToToken = exports.typographyProperties = void 0;
|
|
|
18
21
|
var _eslintCodemodUtils = require("eslint-codemod-utils");
|
|
19
22
|
var _palettesRaw = require("@atlaskit/tokens/palettes-raw");
|
|
20
23
|
var _tokensRaw = require("@atlaskit/tokens/tokens-raw");
|
|
24
|
+
var _astNodes = require("../../ast-nodes");
|
|
21
25
|
var typographyProperties = exports.typographyProperties = ['fontSize', 'fontWeight', 'fontFamily', 'lineHeight'];
|
|
22
26
|
var isTypographyProperty = exports.isTypographyProperty = function isTypographyProperty(propertyName) {
|
|
23
27
|
return typographyProperties.includes(propertyName);
|
|
@@ -115,21 +119,38 @@ function isValidPropertyNode(node) {
|
|
|
115
119
|
}
|
|
116
120
|
return true;
|
|
117
121
|
}
|
|
118
|
-
function getTokenNode(tokenName, tokenValue) {
|
|
122
|
+
function getTokenNode(tokenName, tokenValue, isFallbackMember) {
|
|
123
|
+
var fallback;
|
|
124
|
+
if (isFallbackMember) {
|
|
125
|
+
fallback = createMemberExpressionFromArray(tokenValue.split('.'));
|
|
126
|
+
} else {
|
|
127
|
+
fallback = (0, _eslintCodemodUtils.literal)(tokenValue);
|
|
128
|
+
}
|
|
119
129
|
return (0, _eslintCodemodUtils.callExpression)({
|
|
120
130
|
callee: (0, _eslintCodemodUtils.identifier)({
|
|
121
131
|
name: 'token'
|
|
122
132
|
}),
|
|
123
133
|
arguments: [(0, _eslintCodemodUtils.literal)({
|
|
124
134
|
value: "'".concat(tokenName, "'")
|
|
125
|
-
}),
|
|
135
|
+
}), fallback],
|
|
126
136
|
optional: false
|
|
127
137
|
});
|
|
128
138
|
}
|
|
139
|
+
function createMemberExpressionFromArray(array) {
|
|
140
|
+
if (array.length === 1) {
|
|
141
|
+
return (0, _eslintCodemodUtils.identifier)(array[0]);
|
|
142
|
+
}
|
|
143
|
+
var property = array.pop();
|
|
144
|
+
return (0, _eslintCodemodUtils.memberExpression)({
|
|
145
|
+
object: createMemberExpressionFromArray(array),
|
|
146
|
+
property: (0, _eslintCodemodUtils.identifier)(property)
|
|
147
|
+
});
|
|
148
|
+
}
|
|
129
149
|
function getTokenProperty(propertyName, tokenName, tokenFallback) {
|
|
150
|
+
var isFallbackMember = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;
|
|
130
151
|
return (0, _eslintCodemodUtils.property)({
|
|
131
152
|
key: (0, _eslintCodemodUtils.identifier)(propertyName),
|
|
132
|
-
value: getTokenNode(tokenName, tokenFallback)
|
|
153
|
+
value: getTokenNode(tokenName, tokenFallback, isFallbackMember)
|
|
133
154
|
});
|
|
134
155
|
}
|
|
135
156
|
function getLiteralProperty(propertyName, propertyValue) {
|
|
@@ -143,4 +164,19 @@ function convertPropertyNodeToStringableNode(node) {
|
|
|
143
164
|
key: node.key,
|
|
144
165
|
value: node.value
|
|
145
166
|
});
|
|
167
|
+
}
|
|
168
|
+
function insertTokensImport(root, fixer) {
|
|
169
|
+
return _astNodes.Root.insertImport(root, {
|
|
170
|
+
module: '@atlaskit/tokens',
|
|
171
|
+
specifiers: ['token']
|
|
172
|
+
}, fixer);
|
|
173
|
+
}
|
|
174
|
+
function insertFallbackImportFull(root, fixer) {
|
|
175
|
+
return _astNodes.Root.insertImport(root, {
|
|
176
|
+
module: '@atlaskit/theme/typography',
|
|
177
|
+
specifiers: ['fontFallback']
|
|
178
|
+
}, fixer);
|
|
179
|
+
}
|
|
180
|
+
function insertFallbackImportSpecifier(fixer, themeImportNode) {
|
|
181
|
+
return _astNodes.Import.insertNamedSpecifiers(themeImportNode, ['fontFallback'], fixer);
|
|
146
182
|
}
|
|
@@ -1,15 +1,11 @@
|
|
|
1
|
-
import { createWithConfig } from '../ensure-design-token-usage';
|
|
2
1
|
import ruleMeta from '../ensure-design-token-usage/rule-meta';
|
|
3
2
|
import { createLintRule } from '../utils/create-rule';
|
|
4
|
-
const defaultConfig = {
|
|
5
|
-
domains: ['spacing'],
|
|
6
|
-
applyImport: true,
|
|
7
|
-
shouldEnforceFallbacks: false
|
|
8
|
-
};
|
|
9
3
|
const rule = createLintRule({
|
|
10
4
|
meta: {
|
|
11
5
|
...ruleMeta,
|
|
12
6
|
name: 'ensure-design-token-usage/preview',
|
|
7
|
+
deprecated: true,
|
|
8
|
+
replacedBy: ['@atlaskit/design-system/use-tokens-space'],
|
|
13
9
|
docs: {
|
|
14
10
|
...ruleMeta.docs,
|
|
15
11
|
description: 'Enforces usage of pre-release design tokens rather than hard-coded values.',
|
|
@@ -17,6 +13,16 @@ const rule = createLintRule({
|
|
|
17
13
|
severity: 'warn'
|
|
18
14
|
}
|
|
19
15
|
},
|
|
20
|
-
create
|
|
16
|
+
create() {
|
|
17
|
+
/**
|
|
18
|
+
* We can't just outright delete the ESLint rule, since:
|
|
19
|
+
* ```
|
|
20
|
+
* // eslint-disable @eslint-plugin/design-system/ensure-design-token-usage/preview
|
|
21
|
+
* ```
|
|
22
|
+
* will cause CI to fail if the rule definition doesn't exist. So, instead
|
|
23
|
+
* we can change the implementation of the rule so that it never reports.
|
|
24
|
+
*/
|
|
25
|
+
return {};
|
|
26
|
+
}
|
|
21
27
|
});
|
|
22
28
|
export default rule;
|
|
@@ -1,14 +1,12 @@
|
|
|
1
|
-
// eslint-disable-next-line import/no-extraneous-dependencies
|
|
2
|
-
|
|
3
|
-
import { isNodeOfType } from 'eslint-codemod-utils';
|
|
4
1
|
import { createLintRule } from '../utils/create-rule';
|
|
5
|
-
import { shouldSuggest, validPrimitiveElements } from './utils';
|
|
6
2
|
const primitiveDocsUrl = 'https://go.atlassian.com/dst-prefer-primitives';
|
|
7
3
|
const rule = createLintRule({
|
|
8
4
|
meta: {
|
|
9
5
|
name: 'prefer-primitives',
|
|
10
6
|
type: 'suggestion',
|
|
11
7
|
hasSuggestions: false,
|
|
8
|
+
deprecated: true,
|
|
9
|
+
replacedBy: ['@atlaskit/design-system/use-primitives'],
|
|
12
10
|
docs: {
|
|
13
11
|
description: 'Increase awareness of primitive components via code hints. Strictly used for education purposes and discoverability. To enforce usage please refer to the `use-primitives` rule.',
|
|
14
12
|
recommended: false,
|
|
@@ -18,85 +16,16 @@ const rule = createLintRule({
|
|
|
18
16
|
preferPrimitives: `This "{{element}}" may be able to be replaced with a primitive component. See ${primitiveDocsUrl} for guidance.`
|
|
19
17
|
}
|
|
20
18
|
},
|
|
21
|
-
create(
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
return;
|
|
32
|
-
}
|
|
33
|
-
const suggest = shouldSuggest(node === null || node === void 0 ? void 0 : node.parent);
|
|
34
|
-
if (suggest) {
|
|
35
|
-
context.report({
|
|
36
|
-
node: node,
|
|
37
|
-
messageId: 'preferPrimitives',
|
|
38
|
-
data: {
|
|
39
|
-
element: node.name.name
|
|
40
|
-
}
|
|
41
|
-
});
|
|
42
|
-
}
|
|
43
|
-
},
|
|
44
|
-
// styled.x`` | styled2.x`` | styled.div()
|
|
45
|
-
'MemberExpression[object.name="styled"],MemberExpression[object.name="styled2"]': node => {
|
|
46
|
-
if (!isNodeOfType(node, 'MemberExpression')) {
|
|
47
|
-
return;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
// styled.div``
|
|
51
|
-
if (isNodeOfType(node.property, 'Identifier')) {
|
|
52
|
-
if (validPrimitiveElements.has(node.property.name)) {
|
|
53
|
-
const styledIdentifier = node.object.name;
|
|
54
|
-
const elementName = node.property.name;
|
|
55
|
-
|
|
56
|
-
// Including the `styled.` portion in the message to help makers understand it's not just the `div` element that should be replaced
|
|
57
|
-
const reportName = `${styledIdentifier}.${elementName}`; // styled.div
|
|
58
|
-
|
|
59
|
-
context.report({
|
|
60
|
-
node: node,
|
|
61
|
-
messageId: 'preferPrimitives',
|
|
62
|
-
data: {
|
|
63
|
-
element: reportName
|
|
64
|
-
}
|
|
65
|
-
});
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
},
|
|
69
|
-
// styled(X)``
|
|
70
|
-
'CallExpression[callee.name="styled"]': node => {
|
|
71
|
-
if (!isNodeOfType(node, 'CallExpression')) {
|
|
72
|
-
return;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
// styled('div')`` - We only care about 'div'/'span', ignore extending other components
|
|
76
|
-
if (isNodeOfType(node.arguments[0], 'Literal')) {
|
|
77
|
-
const argValue = node.arguments[0].raw;
|
|
78
|
-
if (typeof argValue === 'string') {
|
|
79
|
-
const suggest = validPrimitiveElements.has(argValue.replaceAll(`'`, '') // argValue will have '' around the element name, strip it out for this test
|
|
80
|
-
);
|
|
81
|
-
if (suggest) {
|
|
82
|
-
const styledIdentifier = node.callee.name;
|
|
83
|
-
const elementName = argValue;
|
|
84
|
-
|
|
85
|
-
// Including the `styled()` portion in the message to help makers understand it's not just the `div` element that should be replaced
|
|
86
|
-
const reportName = `${styledIdentifier}(${elementName})`; // styled('div')
|
|
87
|
-
|
|
88
|
-
context.report({
|
|
89
|
-
node: node,
|
|
90
|
-
messageId: 'preferPrimitives',
|
|
91
|
-
data: {
|
|
92
|
-
element: reportName
|
|
93
|
-
}
|
|
94
|
-
});
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
};
|
|
19
|
+
create() {
|
|
20
|
+
/**
|
|
21
|
+
* We can't just outright delete the ESLint rule, since:
|
|
22
|
+
* ```
|
|
23
|
+
* // eslint-disable @eslint-plugin/design-system/prefer-primitives
|
|
24
|
+
* ```
|
|
25
|
+
* will cause CI to fail if the rule definition doesn't exist. So, instead
|
|
26
|
+
* we can change the implementation of the rule so that it never reports.
|
|
27
|
+
*/
|
|
28
|
+
return {};
|
|
100
29
|
}
|
|
101
30
|
});
|
|
102
31
|
export default rule;
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
import { isNodeOfType } from 'eslint-codemod-utils';
|
|
4
4
|
import { Object as ASTObject, ObjectEntry, Root } from '../../../ast-nodes';
|
|
5
|
-
import { getValueForPropertyNode,
|
|
5
|
+
import { getValueForPropertyNode, normaliseValue } from '../../ensure-design-token-usage/utils';
|
|
6
6
|
import { isDecendantOfGlobalToken, isDecendantOfStyleBlock, isDecendantOfType } from '../../utils/is-node';
|
|
7
|
-
import { convertPropertyNodeToStringableNode, defaultFontWeight, findFontFamilyValueForToken, findFontWeightTokenForValue, findTypographyTokenForValues, fontWeightMap, getLiteralProperty, getTokenProperty, isValidPropertyNode, notUndefined } from '../utils';
|
|
7
|
+
import { convertPropertyNodeToStringableNode, defaultFontWeight, findFontFamilyValueForToken, findFontWeightTokenForValue, findTypographyTokenForValues, fontWeightMap, getLiteralProperty, getTokenProperty, insertFallbackImportFull, insertFallbackImportSpecifier, insertTokensImport, isValidPropertyNode, notUndefined } from '../utils';
|
|
8
8
|
export const StyleObject = {
|
|
9
9
|
lint(node, {
|
|
10
10
|
context
|
|
@@ -29,7 +29,9 @@ export const StyleObject = {
|
|
|
29
29
|
const {
|
|
30
30
|
fontSizeNode,
|
|
31
31
|
fontSizeRaw,
|
|
32
|
-
tokensImportNode
|
|
32
|
+
tokensImportNode,
|
|
33
|
+
themeImportNode,
|
|
34
|
+
shouldAddFallbackImport
|
|
33
35
|
} = refs;
|
|
34
36
|
const fontSizeValue = normaliseValue('fontSize', fontSizeRaw);
|
|
35
37
|
|
|
@@ -117,6 +119,8 @@ export const StyleObject = {
|
|
|
117
119
|
matchingToken,
|
|
118
120
|
nodesToReplace,
|
|
119
121
|
tokensImportNode,
|
|
122
|
+
themeImportNode,
|
|
123
|
+
shouldAddFallbackImport,
|
|
120
124
|
fontWeightReplacement,
|
|
121
125
|
fontFamilyReplacement,
|
|
122
126
|
fontStyleReplacement
|
|
@@ -164,20 +168,47 @@ export const StyleObject = {
|
|
|
164
168
|
success: false
|
|
165
169
|
};
|
|
166
170
|
}
|
|
167
|
-
const
|
|
171
|
+
const tokensImportDeclaration = Root.findImportsByModule(context.getSourceCode().ast.body, '@atlaskit/tokens');
|
|
168
172
|
|
|
169
173
|
// If there is more than one `@atlaskit/tokens` import, then it becomes difficult to determine which import to transform
|
|
170
|
-
if (
|
|
174
|
+
if (tokensImportDeclaration.length > 1) {
|
|
171
175
|
return {
|
|
172
176
|
success: false
|
|
173
177
|
};
|
|
174
178
|
}
|
|
179
|
+
|
|
180
|
+
// This exists purely because we're not inlining the fallback values
|
|
181
|
+
// and instead referencing a `fontFallback` object that exists in @atlaskit/theme/typography.
|
|
182
|
+
// This is a temporary measure until fallbacks are no longer required
|
|
183
|
+
let shouldAddFallbackImport = 'full';
|
|
184
|
+
const themeImportDeclaration = Root.findImportsByModule(context.getSourceCode().ast.body, '@atlaskit/theme/typography');
|
|
185
|
+
if (themeImportDeclaration.length) {
|
|
186
|
+
// Import exists, check if specifier exists
|
|
187
|
+
shouldAddFallbackImport = 'specifier';
|
|
188
|
+
const fallbackImport = themeImportDeclaration[0].specifiers.find(specifier => {
|
|
189
|
+
// @atlaskit/theme/typography has no default export so we can safely narrow this type
|
|
190
|
+
if (!isNodeOfType(specifier, 'ImportSpecifier')) {
|
|
191
|
+
return false;
|
|
192
|
+
}
|
|
193
|
+
if (specifier.imported.name === 'fontFallback') {
|
|
194
|
+
return true;
|
|
195
|
+
}
|
|
196
|
+
return false;
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
// Exact import already exists, no need to add
|
|
200
|
+
if (fallbackImport) {
|
|
201
|
+
shouldAddFallbackImport = false;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
175
204
|
return {
|
|
176
205
|
success: true,
|
|
177
206
|
refs: {
|
|
178
207
|
fontSizeNode,
|
|
179
208
|
fontSizeRaw,
|
|
180
|
-
tokensImportNode:
|
|
209
|
+
tokensImportNode: tokensImportDeclaration[0],
|
|
210
|
+
themeImportNode: themeImportDeclaration[0],
|
|
211
|
+
shouldAddFallbackImport
|
|
181
212
|
}
|
|
182
213
|
};
|
|
183
214
|
},
|
|
@@ -187,15 +218,25 @@ export const StyleObject = {
|
|
|
187
218
|
matchingToken,
|
|
188
219
|
nodesToReplace,
|
|
189
220
|
tokensImportNode,
|
|
221
|
+
themeImportNode,
|
|
222
|
+
shouldAddFallbackImport,
|
|
190
223
|
fontWeightReplacement,
|
|
191
224
|
fontFamilyReplacement,
|
|
192
225
|
fontStyleReplacement
|
|
193
226
|
} = refs;
|
|
194
227
|
const fontSizeNode = nodesToReplace[0];
|
|
195
|
-
|
|
228
|
+
const root = context.getSourceCode().ast.body;
|
|
229
|
+
let fallbackImport;
|
|
230
|
+
if (shouldAddFallbackImport === 'full') {
|
|
231
|
+
fallbackImport = insertFallbackImportFull(root, fixer);
|
|
232
|
+
} else if (shouldAddFallbackImport === 'specifier') {
|
|
233
|
+
fallbackImport = insertFallbackImportSpecifier(fixer, themeImportNode);
|
|
234
|
+
}
|
|
235
|
+
const fallbackName = (matchingToken.tokenName === 'font.body' ? 'font.body.medium' : matchingToken.tokenName).replace('font', 'fontFallback');
|
|
236
|
+
return (!tokensImportNode ? [insertTokensImport(root, fixer)] : []).concat(fallbackImport ? [fallbackImport] : [], nodesToReplace.map((node, index) => {
|
|
196
237
|
// Replace first node with token, delete remaining nodes. Guaranteed to be fontSize
|
|
197
238
|
if (index === 0) {
|
|
198
|
-
return fixer.replaceText(node, `${getTokenProperty('font', matchingToken.tokenName,
|
|
239
|
+
return fixer.replaceText(node, `${getTokenProperty('font', matchingToken.tokenName, fallbackName, true)}`);
|
|
199
240
|
}
|
|
200
241
|
|
|
201
242
|
// We don't replace fontWeight/fontFamily/fontStyle here in case it occurs before the font property.
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { callExpression, identifier, isNodeOfType, literal, property } from 'eslint-codemod-utils';
|
|
1
|
+
import { callExpression, identifier, isNodeOfType, literal, memberExpression, property } from 'eslint-codemod-utils';
|
|
2
2
|
import { typographyPalette } from '@atlaskit/tokens/palettes-raw';
|
|
3
3
|
import { typographyAdg3 as typographyTokens } from '@atlaskit/tokens/tokens-raw';
|
|
4
|
+
import { Import, Root } from '../../ast-nodes';
|
|
4
5
|
export const typographyProperties = ['fontSize', 'fontWeight', 'fontFamily', 'lineHeight'];
|
|
5
6
|
export const isTypographyProperty = propertyName => {
|
|
6
7
|
return typographyProperties.includes(propertyName);
|
|
@@ -68,21 +69,37 @@ export function isValidPropertyNode(node) {
|
|
|
68
69
|
}
|
|
69
70
|
return true;
|
|
70
71
|
}
|
|
71
|
-
function getTokenNode(tokenName, tokenValue) {
|
|
72
|
+
function getTokenNode(tokenName, tokenValue, isFallbackMember) {
|
|
73
|
+
let fallback;
|
|
74
|
+
if (isFallbackMember) {
|
|
75
|
+
fallback = createMemberExpressionFromArray(tokenValue.split('.'));
|
|
76
|
+
} else {
|
|
77
|
+
fallback = literal(tokenValue);
|
|
78
|
+
}
|
|
72
79
|
return callExpression({
|
|
73
80
|
callee: identifier({
|
|
74
81
|
name: 'token'
|
|
75
82
|
}),
|
|
76
83
|
arguments: [literal({
|
|
77
84
|
value: `'${tokenName}'`
|
|
78
|
-
}),
|
|
85
|
+
}), fallback],
|
|
79
86
|
optional: false
|
|
80
87
|
});
|
|
81
88
|
}
|
|
82
|
-
|
|
89
|
+
function createMemberExpressionFromArray(array) {
|
|
90
|
+
if (array.length === 1) {
|
|
91
|
+
return identifier(array[0]);
|
|
92
|
+
}
|
|
93
|
+
const property = array.pop();
|
|
94
|
+
return memberExpression({
|
|
95
|
+
object: createMemberExpressionFromArray(array),
|
|
96
|
+
property: identifier(property)
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
export function getTokenProperty(propertyName, tokenName, tokenFallback, isFallbackMember = false) {
|
|
83
100
|
return property({
|
|
84
101
|
key: identifier(propertyName),
|
|
85
|
-
value: getTokenNode(tokenName, tokenFallback)
|
|
102
|
+
value: getTokenNode(tokenName, tokenFallback, isFallbackMember)
|
|
86
103
|
});
|
|
87
104
|
}
|
|
88
105
|
export function getLiteralProperty(propertyName, propertyValue) {
|
|
@@ -96,4 +113,19 @@ export function convertPropertyNodeToStringableNode(node) {
|
|
|
96
113
|
key: node.key,
|
|
97
114
|
value: node.value
|
|
98
115
|
});
|
|
116
|
+
}
|
|
117
|
+
export function insertTokensImport(root, fixer) {
|
|
118
|
+
return Root.insertImport(root, {
|
|
119
|
+
module: '@atlaskit/tokens',
|
|
120
|
+
specifiers: ['token']
|
|
121
|
+
}, fixer);
|
|
122
|
+
}
|
|
123
|
+
export function insertFallbackImportFull(root, fixer) {
|
|
124
|
+
return Root.insertImport(root, {
|
|
125
|
+
module: '@atlaskit/theme/typography',
|
|
126
|
+
specifiers: ['fontFallback']
|
|
127
|
+
}, fixer);
|
|
128
|
+
}
|
|
129
|
+
export function insertFallbackImportSpecifier(fixer, themeImportNode) {
|
|
130
|
+
return Import.insertNamedSpecifiers(themeImportNode, ['fontFallback'], fixer);
|
|
99
131
|
}
|
|
@@ -1,23 +1,29 @@
|
|
|
1
1
|
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
2
2
|
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
|
3
3
|
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
|
|
4
|
-
import { createWithConfig } from '../ensure-design-token-usage';
|
|
5
4
|
import ruleMeta from '../ensure-design-token-usage/rule-meta';
|
|
6
5
|
import { createLintRule } from '../utils/create-rule';
|
|
7
|
-
var defaultConfig = {
|
|
8
|
-
domains: ['spacing'],
|
|
9
|
-
applyImport: true,
|
|
10
|
-
shouldEnforceFallbacks: false
|
|
11
|
-
};
|
|
12
6
|
var rule = createLintRule({
|
|
13
7
|
meta: _objectSpread(_objectSpread({}, ruleMeta), {}, {
|
|
14
8
|
name: 'ensure-design-token-usage/preview',
|
|
9
|
+
deprecated: true,
|
|
10
|
+
replacedBy: ['@atlaskit/design-system/use-tokens-space'],
|
|
15
11
|
docs: _objectSpread(_objectSpread({}, ruleMeta.docs), {}, {
|
|
16
12
|
description: 'Enforces usage of pre-release design tokens rather than hard-coded values.',
|
|
17
13
|
recommended: false,
|
|
18
14
|
severity: 'warn'
|
|
19
15
|
})
|
|
20
16
|
}),
|
|
21
|
-
create:
|
|
17
|
+
create: function create() {
|
|
18
|
+
/**
|
|
19
|
+
* We can't just outright delete the ESLint rule, since:
|
|
20
|
+
* ```
|
|
21
|
+
* // eslint-disable @eslint-plugin/design-system/ensure-design-token-usage/preview
|
|
22
|
+
* ```
|
|
23
|
+
* will cause CI to fail if the rule definition doesn't exist. So, instead
|
|
24
|
+
* we can change the implementation of the rule so that it never reports.
|
|
25
|
+
*/
|
|
26
|
+
return {};
|
|
27
|
+
}
|
|
22
28
|
});
|
|
23
29
|
export default rule;
|
|
@@ -1,14 +1,12 @@
|
|
|
1
|
-
// eslint-disable-next-line import/no-extraneous-dependencies
|
|
2
|
-
|
|
3
|
-
import { isNodeOfType } from 'eslint-codemod-utils';
|
|
4
1
|
import { createLintRule } from '../utils/create-rule';
|
|
5
|
-
import { shouldSuggest, validPrimitiveElements } from './utils';
|
|
6
2
|
var primitiveDocsUrl = 'https://go.atlassian.com/dst-prefer-primitives';
|
|
7
3
|
var rule = createLintRule({
|
|
8
4
|
meta: {
|
|
9
5
|
name: 'prefer-primitives',
|
|
10
6
|
type: 'suggestion',
|
|
11
7
|
hasSuggestions: false,
|
|
8
|
+
deprecated: true,
|
|
9
|
+
replacedBy: ['@atlaskit/design-system/use-primitives'],
|
|
12
10
|
docs: {
|
|
13
11
|
description: 'Increase awareness of primitive components via code hints. Strictly used for education purposes and discoverability. To enforce usage please refer to the `use-primitives` rule.',
|
|
14
12
|
recommended: false,
|
|
@@ -18,84 +16,16 @@ var rule = createLintRule({
|
|
|
18
16
|
preferPrimitives: "This \"{{element}}\" may be able to be replaced with a primitive component. See ".concat(primitiveDocsUrl, " for guidance.")
|
|
19
17
|
}
|
|
20
18
|
},
|
|
21
|
-
create: function create(
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
}
|
|
32
|
-
var suggest = shouldSuggest(node === null || node === void 0 ? void 0 : node.parent);
|
|
33
|
-
if (suggest) {
|
|
34
|
-
context.report({
|
|
35
|
-
node: node,
|
|
36
|
-
messageId: 'preferPrimitives',
|
|
37
|
-
data: {
|
|
38
|
-
element: node.name.name
|
|
39
|
-
}
|
|
40
|
-
});
|
|
41
|
-
}
|
|
42
|
-
},
|
|
43
|
-
// styled.x`` | styled2.x`` | styled.div()
|
|
44
|
-
'MemberExpression[object.name="styled"],MemberExpression[object.name="styled2"]': function MemberExpressionObjectNameStyledMemberExpressionObjectNameStyled2(node) {
|
|
45
|
-
if (!isNodeOfType(node, 'MemberExpression')) {
|
|
46
|
-
return;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
// styled.div``
|
|
50
|
-
if (isNodeOfType(node.property, 'Identifier')) {
|
|
51
|
-
if (validPrimitiveElements.has(node.property.name)) {
|
|
52
|
-
var styledIdentifier = node.object.name;
|
|
53
|
-
var elementName = node.property.name;
|
|
54
|
-
|
|
55
|
-
// Including the `styled.` portion in the message to help makers understand it's not just the `div` element that should be replaced
|
|
56
|
-
var reportName = "".concat(styledIdentifier, ".").concat(elementName); // styled.div
|
|
57
|
-
|
|
58
|
-
context.report({
|
|
59
|
-
node: node,
|
|
60
|
-
messageId: 'preferPrimitives',
|
|
61
|
-
data: {
|
|
62
|
-
element: reportName
|
|
63
|
-
}
|
|
64
|
-
});
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
},
|
|
68
|
-
// styled(X)``
|
|
69
|
-
'CallExpression[callee.name="styled"]': function CallExpressionCalleeNameStyled(node) {
|
|
70
|
-
if (!isNodeOfType(node, 'CallExpression')) {
|
|
71
|
-
return;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
// styled('div')`` - We only care about 'div'/'span', ignore extending other components
|
|
75
|
-
if (isNodeOfType(node.arguments[0], 'Literal')) {
|
|
76
|
-
var argValue = node.arguments[0].raw;
|
|
77
|
-
if (typeof argValue === 'string') {
|
|
78
|
-
var suggest = validPrimitiveElements.has(argValue.replaceAll("'", '') // argValue will have '' around the element name, strip it out for this test
|
|
79
|
-
);
|
|
80
|
-
if (suggest) {
|
|
81
|
-
var styledIdentifier = node.callee.name;
|
|
82
|
-
var elementName = argValue;
|
|
83
|
-
|
|
84
|
-
// Including the `styled()` portion in the message to help makers understand it's not just the `div` element that should be replaced
|
|
85
|
-
var reportName = "".concat(styledIdentifier, "(").concat(elementName, ")"); // styled('div')
|
|
86
|
-
|
|
87
|
-
context.report({
|
|
88
|
-
node: node,
|
|
89
|
-
messageId: 'preferPrimitives',
|
|
90
|
-
data: {
|
|
91
|
-
element: reportName
|
|
92
|
-
}
|
|
93
|
-
});
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
};
|
|
19
|
+
create: function create() {
|
|
20
|
+
/**
|
|
21
|
+
* We can't just outright delete the ESLint rule, since:
|
|
22
|
+
* ```
|
|
23
|
+
* // eslint-disable @eslint-plugin/design-system/prefer-primitives
|
|
24
|
+
* ```
|
|
25
|
+
* will cause CI to fail if the rule definition doesn't exist. So, instead
|
|
26
|
+
* we can change the implementation of the rule so that it never reports.
|
|
27
|
+
*/
|
|
28
|
+
return {};
|
|
99
29
|
}
|
|
100
30
|
});
|
|
101
31
|
export default rule;
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
import { isNodeOfType } from 'eslint-codemod-utils';
|
|
4
4
|
import { Object as ASTObject, ObjectEntry, Root } from '../../../ast-nodes';
|
|
5
|
-
import { getValueForPropertyNode,
|
|
5
|
+
import { getValueForPropertyNode, normaliseValue } from '../../ensure-design-token-usage/utils';
|
|
6
6
|
import { isDecendantOfGlobalToken, isDecendantOfStyleBlock, isDecendantOfType } from '../../utils/is-node';
|
|
7
|
-
import { convertPropertyNodeToStringableNode, defaultFontWeight, findFontFamilyValueForToken, findFontWeightTokenForValue, findTypographyTokenForValues, fontWeightMap, getLiteralProperty, getTokenProperty, isValidPropertyNode, notUndefined } from '../utils';
|
|
7
|
+
import { convertPropertyNodeToStringableNode, defaultFontWeight, findFontFamilyValueForToken, findFontWeightTokenForValue, findTypographyTokenForValues, fontWeightMap, getLiteralProperty, getTokenProperty, insertFallbackImportFull, insertFallbackImportSpecifier, insertTokensImport, isValidPropertyNode, notUndefined } from '../utils';
|
|
8
8
|
export var StyleObject = {
|
|
9
9
|
lint: function lint(node, _ref) {
|
|
10
10
|
var context = _ref.context;
|
|
@@ -26,7 +26,9 @@ export var StyleObject = {
|
|
|
26
26
|
}
|
|
27
27
|
var fontSizeNode = refs.fontSizeNode,
|
|
28
28
|
fontSizeRaw = refs.fontSizeRaw,
|
|
29
|
-
tokensImportNode = refs.tokensImportNode
|
|
29
|
+
tokensImportNode = refs.tokensImportNode,
|
|
30
|
+
themeImportNode = refs.themeImportNode,
|
|
31
|
+
shouldAddFallbackImport = refs.shouldAddFallbackImport;
|
|
30
32
|
var fontSizeValue = normaliseValue('fontSize', fontSizeRaw);
|
|
31
33
|
|
|
32
34
|
// -- Font weight --
|
|
@@ -117,6 +119,8 @@ export var StyleObject = {
|
|
|
117
119
|
matchingToken: matchingToken,
|
|
118
120
|
nodesToReplace: nodesToReplace,
|
|
119
121
|
tokensImportNode: tokensImportNode,
|
|
122
|
+
themeImportNode: themeImportNode,
|
|
123
|
+
shouldAddFallbackImport: shouldAddFallbackImport,
|
|
120
124
|
fontWeightReplacement: fontWeightReplacement,
|
|
121
125
|
fontFamilyReplacement: fontFamilyReplacement,
|
|
122
126
|
fontStyleReplacement: fontStyleReplacement
|
|
@@ -163,20 +167,47 @@ export var StyleObject = {
|
|
|
163
167
|
success: false
|
|
164
168
|
};
|
|
165
169
|
}
|
|
166
|
-
var
|
|
170
|
+
var tokensImportDeclaration = Root.findImportsByModule(context.getSourceCode().ast.body, '@atlaskit/tokens');
|
|
167
171
|
|
|
168
172
|
// If there is more than one `@atlaskit/tokens` import, then it becomes difficult to determine which import to transform
|
|
169
|
-
if (
|
|
173
|
+
if (tokensImportDeclaration.length > 1) {
|
|
170
174
|
return {
|
|
171
175
|
success: false
|
|
172
176
|
};
|
|
173
177
|
}
|
|
178
|
+
|
|
179
|
+
// This exists purely because we're not inlining the fallback values
|
|
180
|
+
// and instead referencing a `fontFallback` object that exists in @atlaskit/theme/typography.
|
|
181
|
+
// This is a temporary measure until fallbacks are no longer required
|
|
182
|
+
var shouldAddFallbackImport = 'full';
|
|
183
|
+
var themeImportDeclaration = Root.findImportsByModule(context.getSourceCode().ast.body, '@atlaskit/theme/typography');
|
|
184
|
+
if (themeImportDeclaration.length) {
|
|
185
|
+
// Import exists, check if specifier exists
|
|
186
|
+
shouldAddFallbackImport = 'specifier';
|
|
187
|
+
var fallbackImport = themeImportDeclaration[0].specifiers.find(function (specifier) {
|
|
188
|
+
// @atlaskit/theme/typography has no default export so we can safely narrow this type
|
|
189
|
+
if (!isNodeOfType(specifier, 'ImportSpecifier')) {
|
|
190
|
+
return false;
|
|
191
|
+
}
|
|
192
|
+
if (specifier.imported.name === 'fontFallback') {
|
|
193
|
+
return true;
|
|
194
|
+
}
|
|
195
|
+
return false;
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
// Exact import already exists, no need to add
|
|
199
|
+
if (fallbackImport) {
|
|
200
|
+
shouldAddFallbackImport = false;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
174
203
|
return {
|
|
175
204
|
success: true,
|
|
176
205
|
refs: {
|
|
177
206
|
fontSizeNode: fontSizeNode,
|
|
178
207
|
fontSizeRaw: fontSizeRaw,
|
|
179
|
-
tokensImportNode:
|
|
208
|
+
tokensImportNode: tokensImportDeclaration[0],
|
|
209
|
+
themeImportNode: themeImportDeclaration[0],
|
|
210
|
+
shouldAddFallbackImport: shouldAddFallbackImport
|
|
180
211
|
}
|
|
181
212
|
};
|
|
182
213
|
},
|
|
@@ -185,14 +216,24 @@ export var StyleObject = {
|
|
|
185
216
|
var matchingToken = refs.matchingToken,
|
|
186
217
|
nodesToReplace = refs.nodesToReplace,
|
|
187
218
|
tokensImportNode = refs.tokensImportNode,
|
|
219
|
+
themeImportNode = refs.themeImportNode,
|
|
220
|
+
shouldAddFallbackImport = refs.shouldAddFallbackImport,
|
|
188
221
|
fontWeightReplacement = refs.fontWeightReplacement,
|
|
189
222
|
fontFamilyReplacement = refs.fontFamilyReplacement,
|
|
190
223
|
fontStyleReplacement = refs.fontStyleReplacement;
|
|
191
224
|
var fontSizeNode = nodesToReplace[0];
|
|
192
|
-
|
|
225
|
+
var root = context.getSourceCode().ast.body;
|
|
226
|
+
var fallbackImport;
|
|
227
|
+
if (shouldAddFallbackImport === 'full') {
|
|
228
|
+
fallbackImport = insertFallbackImportFull(root, fixer);
|
|
229
|
+
} else if (shouldAddFallbackImport === 'specifier') {
|
|
230
|
+
fallbackImport = insertFallbackImportSpecifier(fixer, themeImportNode);
|
|
231
|
+
}
|
|
232
|
+
var fallbackName = (matchingToken.tokenName === 'font.body' ? 'font.body.medium' : matchingToken.tokenName).replace('font', 'fontFallback');
|
|
233
|
+
return (!tokensImportNode ? [insertTokensImport(root, fixer)] : []).concat(fallbackImport ? [fallbackImport] : [], nodesToReplace.map(function (node, index) {
|
|
193
234
|
// Replace first node with token, delete remaining nodes. Guaranteed to be fontSize
|
|
194
235
|
if (index === 0) {
|
|
195
|
-
return fixer.replaceText(node, "".concat(getTokenProperty('font', matchingToken.tokenName,
|
|
236
|
+
return fixer.replaceText(node, "".concat(getTokenProperty('font', matchingToken.tokenName, fallbackName, true)));
|
|
196
237
|
}
|
|
197
238
|
|
|
198
239
|
// We don't replace fontWeight/fontFamily/fontStyle here in case it occurs before the font property.
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { callExpression, identifier, isNodeOfType, literal, property } from 'eslint-codemod-utils';
|
|
1
|
+
import { callExpression, identifier, isNodeOfType, literal, memberExpression, property } from 'eslint-codemod-utils';
|
|
2
2
|
import { typographyPalette } from '@atlaskit/tokens/palettes-raw';
|
|
3
3
|
import { typographyAdg3 as typographyTokens } from '@atlaskit/tokens/tokens-raw';
|
|
4
|
+
import { Import, Root } from '../../ast-nodes';
|
|
4
5
|
export var typographyProperties = ['fontSize', 'fontWeight', 'fontFamily', 'lineHeight'];
|
|
5
6
|
export var isTypographyProperty = function isTypographyProperty(propertyName) {
|
|
6
7
|
return typographyProperties.includes(propertyName);
|
|
@@ -98,21 +99,38 @@ export function isValidPropertyNode(node) {
|
|
|
98
99
|
}
|
|
99
100
|
return true;
|
|
100
101
|
}
|
|
101
|
-
function getTokenNode(tokenName, tokenValue) {
|
|
102
|
+
function getTokenNode(tokenName, tokenValue, isFallbackMember) {
|
|
103
|
+
var fallback;
|
|
104
|
+
if (isFallbackMember) {
|
|
105
|
+
fallback = createMemberExpressionFromArray(tokenValue.split('.'));
|
|
106
|
+
} else {
|
|
107
|
+
fallback = literal(tokenValue);
|
|
108
|
+
}
|
|
102
109
|
return callExpression({
|
|
103
110
|
callee: identifier({
|
|
104
111
|
name: 'token'
|
|
105
112
|
}),
|
|
106
113
|
arguments: [literal({
|
|
107
114
|
value: "'".concat(tokenName, "'")
|
|
108
|
-
}),
|
|
115
|
+
}), fallback],
|
|
109
116
|
optional: false
|
|
110
117
|
});
|
|
111
118
|
}
|
|
119
|
+
function createMemberExpressionFromArray(array) {
|
|
120
|
+
if (array.length === 1) {
|
|
121
|
+
return identifier(array[0]);
|
|
122
|
+
}
|
|
123
|
+
var property = array.pop();
|
|
124
|
+
return memberExpression({
|
|
125
|
+
object: createMemberExpressionFromArray(array),
|
|
126
|
+
property: identifier(property)
|
|
127
|
+
});
|
|
128
|
+
}
|
|
112
129
|
export function getTokenProperty(propertyName, tokenName, tokenFallback) {
|
|
130
|
+
var isFallbackMember = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;
|
|
113
131
|
return property({
|
|
114
132
|
key: identifier(propertyName),
|
|
115
|
-
value: getTokenNode(tokenName, tokenFallback)
|
|
133
|
+
value: getTokenNode(tokenName, tokenFallback, isFallbackMember)
|
|
116
134
|
});
|
|
117
135
|
}
|
|
118
136
|
export function getLiteralProperty(propertyName, propertyValue) {
|
|
@@ -126,4 +144,19 @@ export function convertPropertyNodeToStringableNode(node) {
|
|
|
126
144
|
key: node.key,
|
|
127
145
|
value: node.value
|
|
128
146
|
});
|
|
147
|
+
}
|
|
148
|
+
export function insertTokensImport(root, fixer) {
|
|
149
|
+
return Root.insertImport(root, {
|
|
150
|
+
module: '@atlaskit/tokens',
|
|
151
|
+
specifiers: ['token']
|
|
152
|
+
}, fixer);
|
|
153
|
+
}
|
|
154
|
+
export function insertFallbackImportFull(root, fixer) {
|
|
155
|
+
return Root.insertImport(root, {
|
|
156
|
+
module: '@atlaskit/theme/typography',
|
|
157
|
+
specifiers: ['fontFallback']
|
|
158
|
+
}, fixer);
|
|
159
|
+
}
|
|
160
|
+
export function insertFallbackImportSpecifier(fixer, themeImportNode) {
|
|
161
|
+
return Import.insertNamedSpecifiers(themeImportNode, ['fontFallback'], fixer);
|
|
129
162
|
}
|
|
@@ -8,6 +8,8 @@ interface Refs {
|
|
|
8
8
|
fontSizeNode: Property;
|
|
9
9
|
fontSizeRaw: string | number;
|
|
10
10
|
tokensImportNode: ImportDeclaration | undefined;
|
|
11
|
+
themeImportNode: ImportDeclaration | undefined;
|
|
12
|
+
shouldAddFallbackImport: 'full' | 'specifier' | false;
|
|
11
13
|
}
|
|
12
14
|
type Check = {
|
|
13
15
|
success: boolean;
|
|
@@ -17,6 +19,8 @@ interface FixerRefs {
|
|
|
17
19
|
matchingToken: TokenValueMap;
|
|
18
20
|
nodesToReplace: Property[];
|
|
19
21
|
tokensImportNode: ImportDeclaration | undefined;
|
|
22
|
+
themeImportNode: ImportDeclaration | undefined;
|
|
23
|
+
shouldAddFallbackImport: Refs['shouldAddFallbackImport'];
|
|
20
24
|
fontWeightReplacement: StringableASTNode<Property> | undefined;
|
|
21
25
|
fontFamilyReplacement: StringableASTNode<Property> | undefined;
|
|
22
26
|
fontStyleReplacement: StringableASTNode<Property> | undefined;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { Rule } from 'eslint';
|
|
2
|
+
import { CallExpression, Directive, EslintNode, ImportDeclaration, ModuleDeclaration, Property, Statement, StringableASTNode } from 'eslint-codemod-utils';
|
|
2
3
|
export declare const typographyProperties: string[];
|
|
3
4
|
export declare const isTypographyProperty: (propertyName: string) => boolean;
|
|
4
5
|
export declare const isFontSize: (node: EslintNode) => node is CallExpression;
|
|
@@ -189,6 +190,9 @@ export declare const fontFamilyTokens: ({
|
|
|
189
190
|
export declare function findFontFamilyValueForToken(tokenName: string): string;
|
|
190
191
|
export declare function notUndefined<V>(value: V | undefined): value is V;
|
|
191
192
|
export declare function isValidPropertyNode(node: Property): boolean;
|
|
192
|
-
export declare function getTokenProperty(propertyName: string, tokenName: string, tokenFallback: string):
|
|
193
|
-
export declare function getLiteralProperty(propertyName: string, propertyValue: string):
|
|
194
|
-
export declare function convertPropertyNodeToStringableNode(node: Property):
|
|
193
|
+
export declare function getTokenProperty(propertyName: string, tokenName: string, tokenFallback: string, isFallbackMember?: boolean): StringableASTNode<Property>;
|
|
194
|
+
export declare function getLiteralProperty(propertyName: string, propertyValue: string): StringableASTNode<Property>;
|
|
195
|
+
export declare function convertPropertyNodeToStringableNode(node: Property): StringableASTNode<Property>;
|
|
196
|
+
export declare function insertTokensImport(root: (Directive | Statement | ModuleDeclaration)[], fixer: Rule.RuleFixer): Rule.Fix;
|
|
197
|
+
export declare function insertFallbackImportFull(root: (Directive | Statement | ModuleDeclaration)[], fixer: Rule.RuleFixer): Rule.Fix;
|
|
198
|
+
export declare function insertFallbackImportSpecifier(fixer: Rule.RuleFixer, themeImportNode: ImportDeclaration): Rule.Fix | undefined;
|
|
@@ -8,6 +8,8 @@ interface Refs {
|
|
|
8
8
|
fontSizeNode: Property;
|
|
9
9
|
fontSizeRaw: string | number;
|
|
10
10
|
tokensImportNode: ImportDeclaration | undefined;
|
|
11
|
+
themeImportNode: ImportDeclaration | undefined;
|
|
12
|
+
shouldAddFallbackImport: 'full' | 'specifier' | false;
|
|
11
13
|
}
|
|
12
14
|
type Check = {
|
|
13
15
|
success: boolean;
|
|
@@ -17,6 +19,8 @@ interface FixerRefs {
|
|
|
17
19
|
matchingToken: TokenValueMap;
|
|
18
20
|
nodesToReplace: Property[];
|
|
19
21
|
tokensImportNode: ImportDeclaration | undefined;
|
|
22
|
+
themeImportNode: ImportDeclaration | undefined;
|
|
23
|
+
shouldAddFallbackImport: Refs['shouldAddFallbackImport'];
|
|
20
24
|
fontWeightReplacement: StringableASTNode<Property> | undefined;
|
|
21
25
|
fontFamilyReplacement: StringableASTNode<Property> | undefined;
|
|
22
26
|
fontStyleReplacement: StringableASTNode<Property> | undefined;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { Rule } from 'eslint';
|
|
2
|
+
import { CallExpression, Directive, EslintNode, ImportDeclaration, ModuleDeclaration, Property, Statement, StringableASTNode } from 'eslint-codemod-utils';
|
|
2
3
|
export declare const typographyProperties: string[];
|
|
3
4
|
export declare const isTypographyProperty: (propertyName: string) => boolean;
|
|
4
5
|
export declare const isFontSize: (node: EslintNode) => node is CallExpression;
|
|
@@ -189,6 +190,9 @@ export declare const fontFamilyTokens: ({
|
|
|
189
190
|
export declare function findFontFamilyValueForToken(tokenName: string): string;
|
|
190
191
|
export declare function notUndefined<V>(value: V | undefined): value is V;
|
|
191
192
|
export declare function isValidPropertyNode(node: Property): boolean;
|
|
192
|
-
export declare function getTokenProperty(propertyName: string, tokenName: string, tokenFallback: string):
|
|
193
|
-
export declare function getLiteralProperty(propertyName: string, propertyValue: string):
|
|
194
|
-
export declare function convertPropertyNodeToStringableNode(node: Property):
|
|
193
|
+
export declare function getTokenProperty(propertyName: string, tokenName: string, tokenFallback: string, isFallbackMember?: boolean): StringableASTNode<Property>;
|
|
194
|
+
export declare function getLiteralProperty(propertyName: string, propertyValue: string): StringableASTNode<Property>;
|
|
195
|
+
export declare function convertPropertyNodeToStringableNode(node: Property): StringableASTNode<Property>;
|
|
196
|
+
export declare function insertTokensImport(root: (Directive | Statement | ModuleDeclaration)[], fixer: Rule.RuleFixer): Rule.Fix;
|
|
197
|
+
export declare function insertFallbackImportFull(root: (Directive | Statement | ModuleDeclaration)[], fixer: Rule.RuleFixer): Rule.Fix;
|
|
198
|
+
export declare function insertFallbackImportSpecifier(fixer: Rule.RuleFixer, themeImportNode: ImportDeclaration): Rule.Fix | undefined;
|
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": "9.
|
|
4
|
+
"version": "9.2.0",
|
|
5
5
|
"author": "Atlassian Pty Ltd",
|
|
6
6
|
"license": "Apache-2.0",
|
|
7
7
|
"publishConfig": {
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
},
|
|
49
49
|
"devDependencies": {
|
|
50
50
|
"@atlaskit/ds-lib": "^2.2.0",
|
|
51
|
-
"@atlaskit/theme": "^12.
|
|
51
|
+
"@atlaskit/theme": "^12.7.0",
|
|
52
52
|
"@atlassian/atlassian-frontend-prettier-config-1.0.1": "npm:@atlassian/atlassian-frontend-prettier-config@1.0.1",
|
|
53
53
|
"@atlassian/codegen": "*",
|
|
54
54
|
"@atlassian/eslint-utils": "^0.4.0",
|