@atlaskit/eslint-plugin-design-system 3.2.0 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/dist/cjs/index.js +3 -0
  3. package/dist/cjs/rules/ensure-design-token-usage/index.js +4 -115
  4. package/dist/cjs/rules/no-unsafe-design-token-usage/index.js +162 -0
  5. package/dist/cjs/rules/{ensure-design-token-usage/utils → utils}/is-color.js +0 -0
  6. package/dist/cjs/rules/{ensure-design-token-usage/utils → utils}/is-elevation.js +0 -0
  7. package/dist/cjs/rules/{ensure-design-token-usage/utils → utils}/is-node.js +0 -0
  8. package/dist/cjs/rules/{ensure-design-token-usage/utils → utils}/is-token.js +0 -0
  9. package/dist/cjs/version.json +1 -1
  10. package/dist/es2019/index.js +2 -0
  11. package/dist/es2019/rules/ensure-design-token-usage/index.js +4 -129
  12. package/dist/es2019/rules/no-unsafe-design-token-usage/index.js +166 -0
  13. package/dist/es2019/rules/{ensure-design-token-usage/utils → utils}/is-color.js +0 -0
  14. package/dist/es2019/rules/{ensure-design-token-usage/utils → utils}/is-elevation.js +0 -0
  15. package/dist/es2019/rules/{ensure-design-token-usage/utils → utils}/is-node.js +0 -0
  16. package/dist/es2019/rules/{ensure-design-token-usage/utils → utils}/is-token.js +0 -0
  17. package/dist/es2019/version.json +1 -1
  18. package/dist/esm/index.js +2 -0
  19. package/dist/esm/rules/ensure-design-token-usage/index.js +4 -110
  20. package/dist/esm/rules/no-unsafe-design-token-usage/index.js +148 -0
  21. package/dist/esm/rules/{ensure-design-token-usage/utils → utils}/is-color.js +0 -0
  22. package/dist/esm/rules/{ensure-design-token-usage/utils → utils}/is-elevation.js +0 -0
  23. package/dist/esm/rules/{ensure-design-token-usage/utils → utils}/is-node.js +0 -0
  24. package/dist/esm/rules/{ensure-design-token-usage/utils → utils}/is-token.js +0 -0
  25. package/dist/esm/version.json +1 -1
  26. package/dist/types/index.d.ts +1 -0
  27. package/dist/types/rules/no-unsafe-design-token-usage/index.d.ts +3 -0
  28. package/dist/types/rules/{ensure-design-token-usage/utils → utils}/is-color.d.ts +0 -0
  29. package/dist/types/rules/{ensure-design-token-usage/utils → utils}/is-elevation.d.ts +0 -0
  30. package/dist/types/rules/{ensure-design-token-usage/utils → utils}/is-node.d.ts +0 -0
  31. package/dist/types/rules/{ensure-design-token-usage/utils → utils}/is-token.d.ts +0 -0
  32. package/package.json +6 -6
package/CHANGELOG.md CHANGED
@@ -1,5 +1,38 @@
1
1
  # @atlaskit/eslint-plugin-design-system
2
2
 
3
+ ## 4.0.0
4
+
5
+ ### Major Changes
6
+
7
+ - [`a2f953f3814`](https://bitbucket.org/atlassian/atlassian-frontend/commits/a2f953f3814) - Previously the `ensure-design-token-usage` eslint rule contained all checks relating to token use. This has now been split up into two separate rules:
8
+
9
+ `ensure-design-token-usage` now covers:
10
+
11
+ - `legacyElevation` — warns about old usages of the elevation mixins or styles, which instead should use the `card` or `overlay` tokens.
12
+ - `hardCodedColor` — warns about use of hard-coded colors such as `color: colors.B100`, which instead should be wrapped in a `token()` call. This covers the majority of cases in existing codebases when first adopting tokens.
13
+
14
+ `no-unsafe-design-token-usage` (new) covers the remaining rules:
15
+
16
+ - `directTokenUsage` — warns against using the CSS Custom Property name that is output in the browser by the `token()` call.
17
+ Eg. directly using `var(--ds-accent-subtleBlue)` is bad.
18
+ - `staticToken` — warns when tokens aren't used inline. Inlining the token usages helps with static analysis, which unlocks future improvements.
19
+ Eg. pulling the token out into a const like `css={ color: token(primaryButtonText) }` is bad.
20
+ - `invalidToken` — warns when using a token that doesn't exist (not one that's been renamed, see the next point).
21
+ - `tokenRenamed` — warns when using a token that's been renamed in a subsequent release.
22
+ - `tokenFallbackEnforced` — warns if a fallback for the token call is not provided.
23
+ Eg. call with the fallback like this `token('color.background.disabled', N10)` instead of `token('color.background.disabled')`.
24
+ - `tokenFallbackRestricted` — the opposite of `tokenFallbackEnforced`.
25
+ Eg. do not pass in a fallback like this `token('color.background.disabled', N10)` and instead only include the token `token('color.background.disabled')`.
26
+
27
+ Upgrading — some instances of `\\eslint-disable` may need to be changed to the new rule. If you have failing lint rules after only bumping this package then switch those ignores to use `no-unsafe-design-token-usage` instead.
28
+
29
+ ### Patch Changes
30
+
31
+ - [`f460cc7c411`](https://bitbucket.org/atlassian/atlassian-frontend/commits/f460cc7c411) - Builds for this package now pass through a tokens babel plugin, removing runtime invocations of the tokens() function and improving bundle size.
32
+ - [`26719f5b7b0`](https://bitbucket.org/atlassian/atlassian-frontend/commits/26719f5b7b0) - Update @atlaskit tokens dependency from a devDependency to a regular dependency
33
+ - [`a66711cd58c`](https://bitbucket.org/atlassian/atlassian-frontend/commits/a66711cd58c) - Remove `@atlaskit/tokens` from peer dependency.
34
+ - Updated dependencies
35
+
3
36
  ## 3.2.0
4
37
 
5
38
  ### Minor Changes
package/dist/cjs/index.js CHANGED
@@ -11,8 +11,11 @@ var _ensureDesignTokenUsage = _interopRequireDefault(require("./rules/ensure-des
11
11
 
12
12
  var _noDeprecatedImports = _interopRequireDefault(require("./rules/no-deprecated-imports"));
13
13
 
14
+ var _noUnsafeDesignTokenUsage = _interopRequireDefault(require("./rules/no-unsafe-design-token-usage"));
15
+
14
16
  var rules = {
15
17
  'ensure-design-token-usage': _ensureDesignTokenUsage.default,
18
+ 'no-unsafe-design-token-usage': _noUnsafeDesignTokenUsage.default,
16
19
  'no-deprecated-imports': _noDeprecatedImports.default
17
20
  };
18
21
  exports.rules = rules;
@@ -1,23 +1,15 @@
1
1
  "use strict";
2
2
 
3
- var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
-
5
3
  Object.defineProperty(exports, "__esModule", {
6
4
  value: true
7
5
  });
8
6
  exports.default = void 0;
9
7
 
10
- var _renameMapping = _interopRequireDefault(require("@atlaskit/tokens/rename-mapping"));
11
-
12
- var _tokenNames = _interopRequireDefault(require("@atlaskit/tokens/token-names"));
13
-
14
- var _isColor = require("./utils/is-color");
15
-
16
- var _isElevation = require("./utils/is-elevation");
8
+ var _isColor = require("../utils/is-color");
17
9
 
18
- var _isNode = require("./utils/is-node");
10
+ var _isElevation = require("../utils/is-elevation");
19
11
 
20
- var _isToken = require("./utils/is-token");
12
+ var _isNode = require("../utils/is-node");
21
13
 
22
14
  var defaultConfig = {
23
15
  shouldEnforceFallbacks: false
@@ -58,13 +50,7 @@ var rule = {
58
50
  type: 'problem',
59
51
  messages: {
60
52
  legacyElevation: "Elevations can be sourced from the global theme using the token function made of both a background and shadow. Use \"card\" for card elevations, and \"overlay\" for anything else that should appear elevated.\n\n{{example}}\n",
61
- directTokenUsage: "Access the global theme using the token function.\n\n```\nimport { token } from '@atlaskit/tokens';\n\ntoken('{{tokenKey}}');\n```\n",
62
- hardCodedColor: "Colors can be sourced from the global theme using the token function.\n\n```\nimport { token } from '@atlaskit/tokens';\n\ntoken('color.background.blanket');\n```\n",
63
- staticToken: "Token string should be inlined directly into the function call.\n\n```\ntoken('color.background.blanket');\n```\n",
64
- invalidToken: 'The token "{{name}}" does not exist.',
65
- tokenRenamed: 'The token "{{name}}" has been renamed.',
66
- tokenFallbackEnforced: "Token function requires a fallback, preferably something that best matches the light/default theme in case tokens aren't present.\n\n```\ntoken('color.background.blanket', N500A);\n```\n ",
67
- tokenFallbackRestricted: "Token function must not use a fallback.\n\n```\ntoken('color.background.blanket');\n```\n "
53
+ hardCodedColor: "Colors can be sourced from the global theme using the token function.\n\n```\nimport { token } from '@atlaskit/tokens';\n\ntoken('color.background.blanket');\n```\n"
68
54
  }
69
55
  },
70
56
  create: function create(context) {
@@ -134,25 +120,12 @@ var rule = {
134
120
  var value = node.quasi.quasis.map(function (q) {
135
121
  return q.value.raw;
136
122
  }).join('');
137
- var tokenKey = (0, _isToken.isToken)(value, _tokenNames.default);
138
-
139
- if (tokenKey) {
140
- context.report({
141
- messageId: 'directTokenUsage',
142
- node: node,
143
- data: {
144
- tokenKey: tokenKey
145
- }
146
- });
147
- return;
148
- }
149
123
  /**
150
124
  * Attempts to remove all non-essential words & characters from a style block.
151
125
  * Including selectors, queries and property names, leaving only values
152
126
  * This is necessary to avoid cases where a property might have a color in its name ie. "white-space"
153
127
  */
154
128
 
155
-
156
129
  var cssProperties = value.replace(/\n/g, '').split(/;|{|}/).filter(function (el) {
157
130
  return !el.match(/\.|\@|\(|\)/);
158
131
  }).map(function (el) {
@@ -181,23 +154,6 @@ var rule = {
181
154
  return;
182
155
  }
183
156
 
184
- var tokenKey = (0, _isToken.isToken)(node.value, _tokenNames.default);
185
- var isCSSVar = node.value.startsWith('var(');
186
-
187
- if (tokenKey) {
188
- context.report({
189
- messageId: 'directTokenUsage',
190
- node: node,
191
- data: {
192
- tokenKey: tokenKey
193
- },
194
- fix: function fix(fixer) {
195
- return isCSSVar ? fixer.replaceText(node, "token('".concat(tokenKey, "')")) : null;
196
- }
197
- });
198
- return;
199
- }
200
-
201
157
  if ((0, _isColor.isHardCodedColor)(node.value) || (0, _isColor.includesHardCodedColor)(node.value)) {
202
158
  context.report({
203
159
  messageId: 'hardCodedColor',
@@ -226,73 +182,6 @@ var rule = {
226
182
  suggest: getTokenSuggestion(node, "".concat(node.callee.name, "()"), config)
227
183
  });
228
184
  },
229
- 'CallExpression[callee.name="token"]': function CallExpressionCalleeNameToken(node) {
230
- if (node.type !== 'CallExpression') {
231
- return;
232
- }
233
-
234
- if (node.arguments.length < 2 && config.shouldEnforceFallbacks === true) {
235
- context.report({
236
- messageId: 'tokenFallbackEnforced',
237
- node: node
238
- });
239
- } else if (node.arguments.length > 1 && config.shouldEnforceFallbacks === false) {
240
- if (node.arguments[0].type === 'Literal') {
241
- var value = node.arguments[0].value;
242
- context.report({
243
- messageId: 'tokenFallbackRestricted',
244
- node: node.arguments[1],
245
- fix: function fix(fixer) {
246
- return fixer.replaceText(node, "token('".concat(value, "')"));
247
- }
248
- });
249
- } else {
250
- context.report({
251
- messageId: 'tokenFallbackRestricted',
252
- node: node.arguments[1]
253
- });
254
- }
255
- }
256
-
257
- if (node.arguments[0] && node.arguments[0].type !== 'Literal') {
258
- context.report({
259
- messageId: 'staticToken',
260
- node: node
261
- });
262
- return;
263
- }
264
-
265
- var tokenKey = node.arguments[0].value;
266
-
267
- if (!tokenKey) {
268
- return;
269
- }
270
-
271
- if (typeof tokenKey === 'string' && tokenKey in _renameMapping.default) {
272
- context.report({
273
- messageId: 'tokenRenamed',
274
- node: node,
275
- data: {
276
- name: tokenKey
277
- },
278
- fix: function fix(fixer) {
279
- return fixer.replaceText(node.arguments[0], "'".concat(_renameMapping.default[tokenKey], "'"));
280
- }
281
- });
282
- return;
283
- }
284
-
285
- if (typeof tokenKey !== 'string' || typeof tokenKey === 'string' && !_tokenNames.default[tokenKey]) {
286
- context.report({
287
- messageId: 'invalidToken',
288
- node: node,
289
- data: {
290
- name: tokenKey.toString()
291
- }
292
- });
293
- return;
294
- }
295
- },
296
185
  JSXAttribute: function JSXAttribute(node) {
297
186
  if (!node.value) {
298
187
  return;
@@ -0,0 +1,162 @@
1
+ "use strict";
2
+
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
+
5
+ Object.defineProperty(exports, "__esModule", {
6
+ value: true
7
+ });
8
+ exports.default = void 0;
9
+
10
+ var _renameMapping = _interopRequireDefault(require("@atlaskit/tokens/rename-mapping"));
11
+
12
+ var _tokenNames = _interopRequireDefault(require("@atlaskit/tokens/token-names"));
13
+
14
+ var _isNode = require("../utils/is-node");
15
+
16
+ var _isToken = require("../utils/is-token");
17
+
18
+ var defaultConfig = {
19
+ shouldEnforceFallbacks: false
20
+ };
21
+ var rule = {
22
+ meta: {
23
+ docs: {
24
+ recommended: true
25
+ },
26
+ fixable: 'code',
27
+ type: 'problem',
28
+ messages: {
29
+ directTokenUsage: "Access the global theme using the token function.\n\n```\nimport { token } from '@atlaskit/tokens';\n\ntoken('{{tokenKey}}');\n```\n",
30
+ staticToken: "Token string should be inlined directly into the function call.\n\n```\ntoken('color.background.blanket');\n```\n",
31
+ invalidToken: 'The token "{{name}}" does not exist.',
32
+ tokenRenamed: 'The token "{{name}}" has been renamed.',
33
+ tokenFallbackEnforced: "Token function requires a fallback, preferably something that best matches the light/default theme in case tokens aren't present.\n\n```\ntoken('color.background.blanket', N500A);\n```\n ",
34
+ tokenFallbackRestricted: "Token function must not use a fallback.\n\n```\ntoken('color.background.blanket');\n```\n "
35
+ }
36
+ },
37
+ create: function create(context) {
38
+ var config = context.options[0] || defaultConfig;
39
+ return {
40
+ 'TaggedTemplateExpression[tag.name="css"],TaggedTemplateExpression[tag.object.name="styled"]': function TaggedTemplateExpressionTagNameCssTaggedTemplateExpressionTagObjectNameStyled(node) {
41
+ if (node.type !== 'TaggedTemplateExpression') {
42
+ return;
43
+ }
44
+
45
+ var value = node.quasi.quasis.map(function (q) {
46
+ return q.value.raw;
47
+ }).join('');
48
+ var tokenKey = (0, _isToken.isToken)(value, _tokenNames.default);
49
+
50
+ if (tokenKey) {
51
+ context.report({
52
+ messageId: 'directTokenUsage',
53
+ node: node,
54
+ data: {
55
+ tokenKey: tokenKey
56
+ }
57
+ });
58
+ return;
59
+ }
60
+ },
61
+ 'ObjectExpression > Property > Literal': function ObjectExpressionPropertyLiteral(node) {
62
+ if (node.type !== 'Literal') {
63
+ return;
64
+ }
65
+
66
+ if (typeof node.value !== 'string') {
67
+ return;
68
+ }
69
+
70
+ if (!(0, _isNode.isDecendantOfStyleBlock)(node) && !(0, _isNode.isDecendantOfStyleJsxAttribute)(node)) {
71
+ return;
72
+ }
73
+
74
+ var tokenKey = (0, _isToken.isToken)(node.value, _tokenNames.default);
75
+ var isCSSVar = node.value.startsWith('var(');
76
+
77
+ if (tokenKey) {
78
+ context.report({
79
+ messageId: 'directTokenUsage',
80
+ node: node,
81
+ data: {
82
+ tokenKey: tokenKey
83
+ },
84
+ fix: function fix(fixer) {
85
+ return isCSSVar ? fixer.replaceText(node, "token('".concat(tokenKey, "')")) : null;
86
+ }
87
+ });
88
+ return;
89
+ }
90
+ },
91
+ 'CallExpression[callee.name="token"]': function CallExpressionCalleeNameToken(node) {
92
+ if (node.type !== 'CallExpression') {
93
+ return;
94
+ }
95
+
96
+ if (node.arguments.length < 2 && config.shouldEnforceFallbacks === true) {
97
+ context.report({
98
+ messageId: 'tokenFallbackEnforced',
99
+ node: node
100
+ });
101
+ } else if (node.arguments.length > 1 && config.shouldEnforceFallbacks === false) {
102
+ if (node.arguments[0].type === 'Literal') {
103
+ var value = node.arguments[0].value;
104
+ context.report({
105
+ messageId: 'tokenFallbackRestricted',
106
+ node: node.arguments[1],
107
+ fix: function fix(fixer) {
108
+ return fixer.replaceText(node, "token('".concat(value, "')"));
109
+ }
110
+ });
111
+ } else {
112
+ context.report({
113
+ messageId: 'tokenFallbackRestricted',
114
+ node: node.arguments[1]
115
+ });
116
+ }
117
+ }
118
+
119
+ if (node.arguments[0] && node.arguments[0].type !== 'Literal') {
120
+ context.report({
121
+ messageId: 'staticToken',
122
+ node: node
123
+ });
124
+ return;
125
+ }
126
+
127
+ var tokenKey = node.arguments[0].value;
128
+
129
+ if (!tokenKey) {
130
+ return;
131
+ }
132
+
133
+ if (typeof tokenKey === 'string' && tokenKey in _renameMapping.default) {
134
+ context.report({
135
+ messageId: 'tokenRenamed',
136
+ node: node,
137
+ data: {
138
+ name: tokenKey
139
+ },
140
+ fix: function fix(fixer) {
141
+ return fixer.replaceText(node.arguments[0], "'".concat(_renameMapping.default[tokenKey], "'"));
142
+ }
143
+ });
144
+ return;
145
+ }
146
+
147
+ if (typeof tokenKey !== 'string' || typeof tokenKey === 'string' && !_tokenNames.default[tokenKey]) {
148
+ context.report({
149
+ messageId: 'invalidToken',
150
+ node: node,
151
+ data: {
152
+ name: tokenKey.toString()
153
+ }
154
+ });
155
+ return;
156
+ }
157
+ }
158
+ };
159
+ }
160
+ };
161
+ var _default = rule;
162
+ exports.default = _default;
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "@atlaskit/eslint-plugin-design-system",
3
- "version": "3.2.0",
3
+ "version": "4.0.0",
4
4
  "sideEffects": false
5
5
  }
@@ -1,7 +1,9 @@
1
1
  import ensureTokenUsage from './rules/ensure-design-token-usage';
2
2
  import noDeprecatedImports from './rules/no-deprecated-imports';
3
+ import noUnsafeUsage from './rules/no-unsafe-design-token-usage';
3
4
  export const rules = {
4
5
  'ensure-design-token-usage': ensureTokenUsage,
6
+ 'no-unsafe-design-token-usage': noUnsafeUsage,
5
7
  'no-deprecated-imports': noDeprecatedImports
6
8
  };
7
9
  export const configs = {
@@ -1,9 +1,6 @@
1
- import renameMapping from '@atlaskit/tokens/rename-mapping';
2
- import tokens from '@atlaskit/tokens/token-names';
3
- import { includesHardCodedColor, isHardCodedColor, isLegacyColor, isLegacyNamedColor } from './utils/is-color';
4
- import { isLegacyElevation } from './utils/is-elevation';
5
- import { isChildOfType, isDecendantOfGlobalToken, isDecendantOfStyleBlock, isDecendantOfStyleJsxAttribute, isDecendantOfType } from './utils/is-node';
6
- import { isToken } from './utils/is-token';
1
+ import { includesHardCodedColor, isHardCodedColor, isLegacyColor, isLegacyNamedColor } from '../utils/is-color';
2
+ import { isLegacyElevation } from '../utils/is-elevation';
3
+ import { isChildOfType, isDecendantOfGlobalToken, isDecendantOfStyleBlock, isDecendantOfStyleJsxAttribute, isDecendantOfType } from '../utils/is-node';
7
4
  const defaultConfig = {
8
5
  shouldEnforceFallbacks: false
9
6
  };
@@ -36,14 +33,6 @@ const rule = {
36
33
  legacyElevation: `Elevations can be sourced from the global theme using the token function made of both a background and shadow. Use "card" for card elevations, and "overlay" for anything else that should appear elevated.
37
34
 
38
35
  {{example}}
39
- `,
40
- directTokenUsage: `Access the global theme using the token function.
41
-
42
- \`\`\`
43
- import { token } from '@atlaskit/tokens';
44
-
45
- token('{{tokenKey}}');
46
- \`\`\`
47
36
  `,
48
37
  hardCodedColor: `Colors can be sourced from the global theme using the token function.
49
38
 
@@ -52,27 +41,7 @@ import { token } from '@atlaskit/tokens';
52
41
 
53
42
  token('color.background.blanket');
54
43
  \`\`\`
55
- `,
56
- staticToken: `Token string should be inlined directly into the function call.
57
-
58
- \`\`\`
59
- token('color.background.blanket');
60
- \`\`\`
61
- `,
62
- invalidToken: 'The token "{{name}}" does not exist.',
63
- tokenRenamed: 'The token "{{name}}" has been renamed.',
64
- tokenFallbackEnforced: `Token function requires a fallback, preferably something that best matches the light/default theme in case tokens aren't present.
65
-
66
- \`\`\`
67
- token('color.background.blanket', N500A);
68
- \`\`\`
69
- `,
70
- tokenFallbackRestricted: `Token function must not use a fallback.
71
-
72
- \`\`\`
73
- token('color.background.blanket');
74
- \`\`\`
75
- `
44
+ `
76
45
  }
77
46
  },
78
47
 
@@ -151,25 +120,12 @@ ${' '.repeat(getNodeColumn(node) - 2)}box-shadow: \${token('${elevation.shadow}'
151
120
  }
152
121
 
153
122
  const value = node.quasi.quasis.map(q => q.value.raw).join('');
154
- const tokenKey = isToken(value, tokens);
155
-
156
- if (tokenKey) {
157
- context.report({
158
- messageId: 'directTokenUsage',
159
- node,
160
- data: {
161
- tokenKey
162
- }
163
- });
164
- return;
165
- }
166
123
  /**
167
124
  * Attempts to remove all non-essential words & characters from a style block.
168
125
  * Including selectors, queries and property names, leaving only values
169
126
  * This is necessary to avoid cases where a property might have a color in its name ie. "white-space"
170
127
  */
171
128
 
172
-
173
129
  const cssProperties = value.replace(/\n/g, '').split(/;|{|}/).filter(el => !el.match(/\.|\@|\(|\)/)).map(el => el.trim().split(':').pop() || '');
174
130
  cssProperties.forEach(property => {
175
131
  if (includesHardCodedColor(property)) {
@@ -194,21 +150,6 @@ ${' '.repeat(getNodeColumn(node) - 2)}box-shadow: \${token('${elevation.shadow}'
194
150
  return;
195
151
  }
196
152
 
197
- const tokenKey = isToken(node.value, tokens);
198
- const isCSSVar = node.value.startsWith('var(');
199
-
200
- if (tokenKey) {
201
- context.report({
202
- messageId: 'directTokenUsage',
203
- node,
204
- data: {
205
- tokenKey
206
- },
207
- fix: fixer => isCSSVar ? fixer.replaceText(node, `token('${tokenKey}')`) : null
208
- });
209
- return;
210
- }
211
-
212
153
  if (isHardCodedColor(node.value) || includesHardCodedColor(node.value)) {
213
154
  context.report({
214
155
  messageId: 'hardCodedColor',
@@ -239,72 +180,6 @@ ${' '.repeat(getNodeColumn(node) - 2)}box-shadow: \${token('${elevation.shadow}'
239
180
  });
240
181
  },
241
182
 
242
- 'CallExpression[callee.name="token"]': node => {
243
- if (node.type !== 'CallExpression') {
244
- return;
245
- }
246
-
247
- if (node.arguments.length < 2 && config.shouldEnforceFallbacks === true) {
248
- context.report({
249
- messageId: 'tokenFallbackEnforced',
250
- node
251
- });
252
- } else if (node.arguments.length > 1 && config.shouldEnforceFallbacks === false) {
253
- if (node.arguments[0].type === 'Literal') {
254
- const {
255
- value
256
- } = node.arguments[0];
257
- context.report({
258
- messageId: 'tokenFallbackRestricted',
259
- node: node.arguments[1],
260
- fix: fixer => fixer.replaceText(node, `token('${value}')`)
261
- });
262
- } else {
263
- context.report({
264
- messageId: 'tokenFallbackRestricted',
265
- node: node.arguments[1]
266
- });
267
- }
268
- }
269
-
270
- if (node.arguments[0] && node.arguments[0].type !== 'Literal') {
271
- context.report({
272
- messageId: 'staticToken',
273
- node
274
- });
275
- return;
276
- }
277
-
278
- const tokenKey = node.arguments[0].value;
279
-
280
- if (!tokenKey) {
281
- return;
282
- }
283
-
284
- if (typeof tokenKey === 'string' && tokenKey in renameMapping) {
285
- context.report({
286
- messageId: 'tokenRenamed',
287
- node,
288
- data: {
289
- name: tokenKey
290
- },
291
- fix: fixer => fixer.replaceText(node.arguments[0], `'${renameMapping[tokenKey]}'`)
292
- });
293
- return;
294
- }
295
-
296
- if (typeof tokenKey !== 'string' || typeof tokenKey === 'string' && !tokens[tokenKey]) {
297
- context.report({
298
- messageId: 'invalidToken',
299
- node,
300
- data: {
301
- name: tokenKey.toString()
302
- }
303
- });
304
- return;
305
- }
306
- },
307
-
308
183
  JSXAttribute(node) {
309
184
  if (!node.value) {
310
185
  return;
@@ -0,0 +1,166 @@
1
+ import renameMapping from '@atlaskit/tokens/rename-mapping';
2
+ import tokens from '@atlaskit/tokens/token-names';
3
+ import { isDecendantOfStyleBlock, isDecendantOfStyleJsxAttribute } from '../utils/is-node';
4
+ import { isToken } from '../utils/is-token';
5
+ const defaultConfig = {
6
+ shouldEnforceFallbacks: false
7
+ };
8
+ const rule = {
9
+ meta: {
10
+ docs: {
11
+ recommended: true
12
+ },
13
+ fixable: 'code',
14
+ type: 'problem',
15
+ messages: {
16
+ directTokenUsage: `Access the global theme using the token function.
17
+
18
+ \`\`\`
19
+ import { token } from '@atlaskit/tokens';
20
+
21
+ token('{{tokenKey}}');
22
+ \`\`\`
23
+ `,
24
+ staticToken: `Token string should be inlined directly into the function call.
25
+
26
+ \`\`\`
27
+ token('color.background.blanket');
28
+ \`\`\`
29
+ `,
30
+ invalidToken: 'The token "{{name}}" does not exist.',
31
+ tokenRenamed: 'The token "{{name}}" has been renamed.',
32
+ tokenFallbackEnforced: `Token function requires a fallback, preferably something that best matches the light/default theme in case tokens aren't present.
33
+
34
+ \`\`\`
35
+ token('color.background.blanket', N500A);
36
+ \`\`\`
37
+ `,
38
+ tokenFallbackRestricted: `Token function must not use a fallback.
39
+
40
+ \`\`\`
41
+ token('color.background.blanket');
42
+ \`\`\`
43
+ `
44
+ }
45
+ },
46
+
47
+ create(context) {
48
+ const config = context.options[0] || defaultConfig;
49
+ return {
50
+ 'TaggedTemplateExpression[tag.name="css"],TaggedTemplateExpression[tag.object.name="styled"]': node => {
51
+ if (node.type !== 'TaggedTemplateExpression') {
52
+ return;
53
+ }
54
+
55
+ const value = node.quasi.quasis.map(q => q.value.raw).join('');
56
+ const tokenKey = isToken(value, tokens);
57
+
58
+ if (tokenKey) {
59
+ context.report({
60
+ messageId: 'directTokenUsage',
61
+ node,
62
+ data: {
63
+ tokenKey
64
+ }
65
+ });
66
+ return;
67
+ }
68
+ },
69
+ 'ObjectExpression > Property > Literal': node => {
70
+ if (node.type !== 'Literal') {
71
+ return;
72
+ }
73
+
74
+ if (typeof node.value !== 'string') {
75
+ return;
76
+ }
77
+
78
+ if (!isDecendantOfStyleBlock(node) && !isDecendantOfStyleJsxAttribute(node)) {
79
+ return;
80
+ }
81
+
82
+ const tokenKey = isToken(node.value, tokens);
83
+ const isCSSVar = node.value.startsWith('var(');
84
+
85
+ if (tokenKey) {
86
+ context.report({
87
+ messageId: 'directTokenUsage',
88
+ node,
89
+ data: {
90
+ tokenKey
91
+ },
92
+ fix: fixer => isCSSVar ? fixer.replaceText(node, `token('${tokenKey}')`) : null
93
+ });
94
+ return;
95
+ }
96
+ },
97
+ 'CallExpression[callee.name="token"]': node => {
98
+ if (node.type !== 'CallExpression') {
99
+ return;
100
+ }
101
+
102
+ if (node.arguments.length < 2 && config.shouldEnforceFallbacks === true) {
103
+ context.report({
104
+ messageId: 'tokenFallbackEnforced',
105
+ node
106
+ });
107
+ } else if (node.arguments.length > 1 && config.shouldEnforceFallbacks === false) {
108
+ if (node.arguments[0].type === 'Literal') {
109
+ const {
110
+ value
111
+ } = node.arguments[0];
112
+ context.report({
113
+ messageId: 'tokenFallbackRestricted',
114
+ node: node.arguments[1],
115
+ fix: fixer => fixer.replaceText(node, `token('${value}')`)
116
+ });
117
+ } else {
118
+ context.report({
119
+ messageId: 'tokenFallbackRestricted',
120
+ node: node.arguments[1]
121
+ });
122
+ }
123
+ }
124
+
125
+ if (node.arguments[0] && node.arguments[0].type !== 'Literal') {
126
+ context.report({
127
+ messageId: 'staticToken',
128
+ node
129
+ });
130
+ return;
131
+ }
132
+
133
+ const tokenKey = node.arguments[0].value;
134
+
135
+ if (!tokenKey) {
136
+ return;
137
+ }
138
+
139
+ if (typeof tokenKey === 'string' && tokenKey in renameMapping) {
140
+ context.report({
141
+ messageId: 'tokenRenamed',
142
+ node,
143
+ data: {
144
+ name: tokenKey
145
+ },
146
+ fix: fixer => fixer.replaceText(node.arguments[0], `'${renameMapping[tokenKey]}'`)
147
+ });
148
+ return;
149
+ }
150
+
151
+ if (typeof tokenKey !== 'string' || typeof tokenKey === 'string' && !tokens[tokenKey]) {
152
+ context.report({
153
+ messageId: 'invalidToken',
154
+ node,
155
+ data: {
156
+ name: tokenKey.toString()
157
+ }
158
+ });
159
+ return;
160
+ }
161
+ }
162
+ };
163
+ }
164
+
165
+ };
166
+ export default rule;
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "@atlaskit/eslint-plugin-design-system",
3
- "version": "3.2.0",
3
+ "version": "4.0.0",
4
4
  "sideEffects": false
5
5
  }
package/dist/esm/index.js CHANGED
@@ -1,7 +1,9 @@
1
1
  import ensureTokenUsage from './rules/ensure-design-token-usage';
2
2
  import noDeprecatedImports from './rules/no-deprecated-imports';
3
+ import noUnsafeUsage from './rules/no-unsafe-design-token-usage';
3
4
  export var rules = {
4
5
  'ensure-design-token-usage': ensureTokenUsage,
6
+ 'no-unsafe-design-token-usage': noUnsafeUsage,
5
7
  'no-deprecated-imports': noDeprecatedImports
6
8
  };
7
9
  export var configs = {
@@ -1,9 +1,6 @@
1
- import renameMapping from '@atlaskit/tokens/rename-mapping';
2
- import tokens from '@atlaskit/tokens/token-names';
3
- import { includesHardCodedColor, isHardCodedColor, isLegacyColor, isLegacyNamedColor } from './utils/is-color';
4
- import { isLegacyElevation } from './utils/is-elevation';
5
- import { isChildOfType, isDecendantOfGlobalToken, isDecendantOfStyleBlock, isDecendantOfStyleJsxAttribute, isDecendantOfType } from './utils/is-node';
6
- import { isToken } from './utils/is-token';
1
+ import { includesHardCodedColor, isHardCodedColor, isLegacyColor, isLegacyNamedColor } from '../utils/is-color';
2
+ import { isLegacyElevation } from '../utils/is-elevation';
3
+ import { isChildOfType, isDecendantOfGlobalToken, isDecendantOfStyleBlock, isDecendantOfStyleJsxAttribute, isDecendantOfType } from '../utils/is-node';
7
4
  var defaultConfig = {
8
5
  shouldEnforceFallbacks: false
9
6
  };
@@ -43,13 +40,7 @@ var rule = {
43
40
  type: 'problem',
44
41
  messages: {
45
42
  legacyElevation: "Elevations can be sourced from the global theme using the token function made of both a background and shadow. Use \"card\" for card elevations, and \"overlay\" for anything else that should appear elevated.\n\n{{example}}\n",
46
- directTokenUsage: "Access the global theme using the token function.\n\n```\nimport { token } from '@atlaskit/tokens';\n\ntoken('{{tokenKey}}');\n```\n",
47
- hardCodedColor: "Colors can be sourced from the global theme using the token function.\n\n```\nimport { token } from '@atlaskit/tokens';\n\ntoken('color.background.blanket');\n```\n",
48
- staticToken: "Token string should be inlined directly into the function call.\n\n```\ntoken('color.background.blanket');\n```\n",
49
- invalidToken: 'The token "{{name}}" does not exist.',
50
- tokenRenamed: 'The token "{{name}}" has been renamed.',
51
- tokenFallbackEnforced: "Token function requires a fallback, preferably something that best matches the light/default theme in case tokens aren't present.\n\n```\ntoken('color.background.blanket', N500A);\n```\n ",
52
- tokenFallbackRestricted: "Token function must not use a fallback.\n\n```\ntoken('color.background.blanket');\n```\n "
43
+ hardCodedColor: "Colors can be sourced from the global theme using the token function.\n\n```\nimport { token } from '@atlaskit/tokens';\n\ntoken('color.background.blanket');\n```\n"
53
44
  }
54
45
  },
55
46
  create: function create(context) {
@@ -119,25 +110,12 @@ var rule = {
119
110
  var value = node.quasi.quasis.map(function (q) {
120
111
  return q.value.raw;
121
112
  }).join('');
122
- var tokenKey = isToken(value, tokens);
123
-
124
- if (tokenKey) {
125
- context.report({
126
- messageId: 'directTokenUsage',
127
- node: node,
128
- data: {
129
- tokenKey: tokenKey
130
- }
131
- });
132
- return;
133
- }
134
113
  /**
135
114
  * Attempts to remove all non-essential words & characters from a style block.
136
115
  * Including selectors, queries and property names, leaving only values
137
116
  * This is necessary to avoid cases where a property might have a color in its name ie. "white-space"
138
117
  */
139
118
 
140
-
141
119
  var cssProperties = value.replace(/\n/g, '').split(/;|{|}/).filter(function (el) {
142
120
  return !el.match(/\.|\@|\(|\)/);
143
121
  }).map(function (el) {
@@ -166,23 +144,6 @@ var rule = {
166
144
  return;
167
145
  }
168
146
 
169
- var tokenKey = isToken(node.value, tokens);
170
- var isCSSVar = node.value.startsWith('var(');
171
-
172
- if (tokenKey) {
173
- context.report({
174
- messageId: 'directTokenUsage',
175
- node: node,
176
- data: {
177
- tokenKey: tokenKey
178
- },
179
- fix: function fix(fixer) {
180
- return isCSSVar ? fixer.replaceText(node, "token('".concat(tokenKey, "')")) : null;
181
- }
182
- });
183
- return;
184
- }
185
-
186
147
  if (isHardCodedColor(node.value) || includesHardCodedColor(node.value)) {
187
148
  context.report({
188
149
  messageId: 'hardCodedColor',
@@ -211,73 +172,6 @@ var rule = {
211
172
  suggest: getTokenSuggestion(node, "".concat(node.callee.name, "()"), config)
212
173
  });
213
174
  },
214
- 'CallExpression[callee.name="token"]': function CallExpressionCalleeNameToken(node) {
215
- if (node.type !== 'CallExpression') {
216
- return;
217
- }
218
-
219
- if (node.arguments.length < 2 && config.shouldEnforceFallbacks === true) {
220
- context.report({
221
- messageId: 'tokenFallbackEnforced',
222
- node: node
223
- });
224
- } else if (node.arguments.length > 1 && config.shouldEnforceFallbacks === false) {
225
- if (node.arguments[0].type === 'Literal') {
226
- var value = node.arguments[0].value;
227
- context.report({
228
- messageId: 'tokenFallbackRestricted',
229
- node: node.arguments[1],
230
- fix: function fix(fixer) {
231
- return fixer.replaceText(node, "token('".concat(value, "')"));
232
- }
233
- });
234
- } else {
235
- context.report({
236
- messageId: 'tokenFallbackRestricted',
237
- node: node.arguments[1]
238
- });
239
- }
240
- }
241
-
242
- if (node.arguments[0] && node.arguments[0].type !== 'Literal') {
243
- context.report({
244
- messageId: 'staticToken',
245
- node: node
246
- });
247
- return;
248
- }
249
-
250
- var tokenKey = node.arguments[0].value;
251
-
252
- if (!tokenKey) {
253
- return;
254
- }
255
-
256
- if (typeof tokenKey === 'string' && tokenKey in renameMapping) {
257
- context.report({
258
- messageId: 'tokenRenamed',
259
- node: node,
260
- data: {
261
- name: tokenKey
262
- },
263
- fix: function fix(fixer) {
264
- return fixer.replaceText(node.arguments[0], "'".concat(renameMapping[tokenKey], "'"));
265
- }
266
- });
267
- return;
268
- }
269
-
270
- if (typeof tokenKey !== 'string' || typeof tokenKey === 'string' && !tokens[tokenKey]) {
271
- context.report({
272
- messageId: 'invalidToken',
273
- node: node,
274
- data: {
275
- name: tokenKey.toString()
276
- }
277
- });
278
- return;
279
- }
280
- },
281
175
  JSXAttribute: function JSXAttribute(node) {
282
176
  if (!node.value) {
283
177
  return;
@@ -0,0 +1,148 @@
1
+ import renameMapping from '@atlaskit/tokens/rename-mapping';
2
+ import tokens from '@atlaskit/tokens/token-names';
3
+ import { isDecendantOfStyleBlock, isDecendantOfStyleJsxAttribute } from '../utils/is-node';
4
+ import { isToken } from '../utils/is-token';
5
+ var defaultConfig = {
6
+ shouldEnforceFallbacks: false
7
+ };
8
+ var rule = {
9
+ meta: {
10
+ docs: {
11
+ recommended: true
12
+ },
13
+ fixable: 'code',
14
+ type: 'problem',
15
+ messages: {
16
+ directTokenUsage: "Access the global theme using the token function.\n\n```\nimport { token } from '@atlaskit/tokens';\n\ntoken('{{tokenKey}}');\n```\n",
17
+ staticToken: "Token string should be inlined directly into the function call.\n\n```\ntoken('color.background.blanket');\n```\n",
18
+ invalidToken: 'The token "{{name}}" does not exist.',
19
+ tokenRenamed: 'The token "{{name}}" has been renamed.',
20
+ tokenFallbackEnforced: "Token function requires a fallback, preferably something that best matches the light/default theme in case tokens aren't present.\n\n```\ntoken('color.background.blanket', N500A);\n```\n ",
21
+ tokenFallbackRestricted: "Token function must not use a fallback.\n\n```\ntoken('color.background.blanket');\n```\n "
22
+ }
23
+ },
24
+ create: function create(context) {
25
+ var config = context.options[0] || defaultConfig;
26
+ return {
27
+ 'TaggedTemplateExpression[tag.name="css"],TaggedTemplateExpression[tag.object.name="styled"]': function TaggedTemplateExpressionTagNameCssTaggedTemplateExpressionTagObjectNameStyled(node) {
28
+ if (node.type !== 'TaggedTemplateExpression') {
29
+ return;
30
+ }
31
+
32
+ var value = node.quasi.quasis.map(function (q) {
33
+ return q.value.raw;
34
+ }).join('');
35
+ var tokenKey = isToken(value, tokens);
36
+
37
+ if (tokenKey) {
38
+ context.report({
39
+ messageId: 'directTokenUsage',
40
+ node: node,
41
+ data: {
42
+ tokenKey: tokenKey
43
+ }
44
+ });
45
+ return;
46
+ }
47
+ },
48
+ 'ObjectExpression > Property > Literal': function ObjectExpressionPropertyLiteral(node) {
49
+ if (node.type !== 'Literal') {
50
+ return;
51
+ }
52
+
53
+ if (typeof node.value !== 'string') {
54
+ return;
55
+ }
56
+
57
+ if (!isDecendantOfStyleBlock(node) && !isDecendantOfStyleJsxAttribute(node)) {
58
+ return;
59
+ }
60
+
61
+ var tokenKey = isToken(node.value, tokens);
62
+ var isCSSVar = node.value.startsWith('var(');
63
+
64
+ if (tokenKey) {
65
+ context.report({
66
+ messageId: 'directTokenUsage',
67
+ node: node,
68
+ data: {
69
+ tokenKey: tokenKey
70
+ },
71
+ fix: function fix(fixer) {
72
+ return isCSSVar ? fixer.replaceText(node, "token('".concat(tokenKey, "')")) : null;
73
+ }
74
+ });
75
+ return;
76
+ }
77
+ },
78
+ 'CallExpression[callee.name="token"]': function CallExpressionCalleeNameToken(node) {
79
+ if (node.type !== 'CallExpression') {
80
+ return;
81
+ }
82
+
83
+ if (node.arguments.length < 2 && config.shouldEnforceFallbacks === true) {
84
+ context.report({
85
+ messageId: 'tokenFallbackEnforced',
86
+ node: node
87
+ });
88
+ } else if (node.arguments.length > 1 && config.shouldEnforceFallbacks === false) {
89
+ if (node.arguments[0].type === 'Literal') {
90
+ var value = node.arguments[0].value;
91
+ context.report({
92
+ messageId: 'tokenFallbackRestricted',
93
+ node: node.arguments[1],
94
+ fix: function fix(fixer) {
95
+ return fixer.replaceText(node, "token('".concat(value, "')"));
96
+ }
97
+ });
98
+ } else {
99
+ context.report({
100
+ messageId: 'tokenFallbackRestricted',
101
+ node: node.arguments[1]
102
+ });
103
+ }
104
+ }
105
+
106
+ if (node.arguments[0] && node.arguments[0].type !== 'Literal') {
107
+ context.report({
108
+ messageId: 'staticToken',
109
+ node: node
110
+ });
111
+ return;
112
+ }
113
+
114
+ var tokenKey = node.arguments[0].value;
115
+
116
+ if (!tokenKey) {
117
+ return;
118
+ }
119
+
120
+ if (typeof tokenKey === 'string' && tokenKey in renameMapping) {
121
+ context.report({
122
+ messageId: 'tokenRenamed',
123
+ node: node,
124
+ data: {
125
+ name: tokenKey
126
+ },
127
+ fix: function fix(fixer) {
128
+ return fixer.replaceText(node.arguments[0], "'".concat(renameMapping[tokenKey], "'"));
129
+ }
130
+ });
131
+ return;
132
+ }
133
+
134
+ if (typeof tokenKey !== 'string' || typeof tokenKey === 'string' && !tokens[tokenKey]) {
135
+ context.report({
136
+ messageId: 'invalidToken',
137
+ node: node,
138
+ data: {
139
+ name: tokenKey.toString()
140
+ }
141
+ });
142
+ return;
143
+ }
144
+ }
145
+ };
146
+ }
147
+ };
148
+ export default rule;
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "@atlaskit/eslint-plugin-design-system",
3
- "version": "3.2.0",
3
+ "version": "4.0.0",
4
4
  "sideEffects": false
5
5
  }
@@ -1,5 +1,6 @@
1
1
  export declare const rules: {
2
2
  'ensure-design-token-usage': import("eslint").Rule.RuleModule;
3
+ 'no-unsafe-design-token-usage': import("eslint").Rule.RuleModule;
3
4
  'no-deprecated-imports': import("eslint").Rule.RuleModule;
4
5
  };
5
6
  export declare const configs: {
@@ -0,0 +1,3 @@
1
+ import type { Rule } from 'eslint';
2
+ declare const rule: Rule.RuleModule;
3
+ export default rule;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/eslint-plugin-design-system",
3
- "version": "3.2.0",
3
+ "version": "4.0.0",
4
4
  "author": "Atlassian Pty Ltd",
5
5
  "publishConfig": {
6
6
  "registry": "https://registry.npmjs.org/"
@@ -20,19 +20,16 @@
20
20
  ".": "./src/index.tsx"
21
21
  },
22
22
  "dependencies": {
23
+ "@atlaskit/tokens": "^0.4.0",
23
24
  "@babel/runtime": "^7.0.0"
24
25
  },
25
26
  "devDependencies": {
26
- "@atlaskit/tokens": "*",
27
27
  "@atlassian/atlassian-frontend-prettier-config-1.0.1": "npm:@atlassian/atlassian-frontend-prettier-config@1.0.1",
28
28
  "eslint": "^7.7.0",
29
29
  "ts-node": "^10.0.0",
30
30
  "tsconfig-paths": "^3.9.0",
31
31
  "typescript": "3.9.6"
32
32
  },
33
- "peerDependencies": {
34
- "@atlaskit/tokens": "*"
35
- },
36
33
  "techstack": {
37
34
  "@atlassian/frontend": {
38
35
  "import-structure": "atlassian-conventions",
@@ -40,7 +37,10 @@
40
37
  },
41
38
  "@repo/internal": {
42
39
  "design-system": "v1",
43
- "deprecation": "no-deprecated-imports"
40
+ "deprecation": "no-deprecated-imports",
41
+ "styling": [
42
+ "emotion"
43
+ ]
44
44
  }
45
45
  },
46
46
  "prettier": "@atlassian/atlassian-frontend-prettier-config-1.0.1"