@atlaskit/eslint-plugin-design-system 4.11.2 → 4.12.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/constellation/index/examples.mdx +309 -1
- package/constellation/index/usage.mdx +41 -1
- package/dist/cjs/index.js +5 -4
- package/dist/cjs/rules/ensure-design-token-usage-spacing/index.js +277 -0
- package/dist/cjs/rules/{no-raw-spacing-values → ensure-design-token-usage-spacing}/utils.js +5 -3
- package/dist/cjs/rules/no-unsafe-design-token-usage/index.js +29 -3
- package/dist/cjs/rules/utils/is-node.js +1 -1
- package/dist/cjs/version.json +1 -1
- package/dist/es2019/index.js +4 -3
- package/dist/es2019/rules/ensure-design-token-usage-spacing/index.js +227 -0
- package/dist/es2019/rules/{no-raw-spacing-values → ensure-design-token-usage-spacing}/utils.js +5 -3
- package/dist/es2019/rules/no-unsafe-design-token-usage/index.js +22 -3
- package/dist/es2019/rules/utils/is-node.js +1 -1
- package/dist/es2019/version.json +1 -1
- package/dist/esm/index.js +4 -3
- package/dist/esm/rules/ensure-design-token-usage-spacing/index.js +262 -0
- package/dist/esm/rules/{no-raw-spacing-values → ensure-design-token-usage-spacing}/utils.js +5 -3
- package/dist/esm/rules/no-unsafe-design-token-usage/index.js +28 -3
- package/dist/esm/rules/utils/is-node.js +1 -1
- package/dist/esm/version.json +1 -1
- package/dist/types/index.d.ts +3 -2
- package/dist/types/rules/{no-raw-spacing-values → ensure-design-token-usage-spacing}/index.d.ts +0 -0
- package/dist/types/rules/{no-raw-spacing-values → ensure-design-token-usage-spacing}/utils.d.ts +0 -0
- package/dist/types/rules/utils/is-node.d.ts +2 -2
- package/dist/types-ts4.0/index.d.ts +3 -2
- package/dist/types-ts4.0/rules/{no-raw-spacing-values → ensure-design-token-usage-spacing}/index.d.ts +0 -0
- package/dist/types-ts4.0/rules/{no-raw-spacing-values → ensure-design-token-usage-spacing}/utils.d.ts +0 -0
- package/dist/types-ts4.0/rules/utils/is-node.d.ts +2 -2
- package/package.json +2 -3
- package/constellation/index/props.mdx +0 -0
- package/dist/cjs/rules/no-raw-spacing-values/index.js +0 -180
- package/dist/es2019/rules/no-raw-spacing-values/index.js +0 -144
- package/dist/esm/rules/no-raw-spacing-values/index.js +0 -168
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# @atlaskit/eslint-plugin-design-system
|
|
2
2
|
|
|
3
|
+
## 4.12.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [`e86c57a4a60`](https://bitbucket.org/atlassian/atlassian-frontend/commits/e86c57a4a60) - Improves the `no-raw-spacing-values` rule to include an autofixer. Spacing values that can be resolved to a token will be.
|
|
8
|
+
|
|
9
|
+
## 4.12.0
|
|
10
|
+
|
|
11
|
+
### Minor Changes
|
|
12
|
+
|
|
13
|
+
- [`109c705cd9c`](https://bitbucket.org/atlassian/atlassian-frontend/commits/109c705cd9c) - [ux] Adds a new case to the no-unsafe-design-token-usage rule to lint against uses of 'experimental' tokens and automatically replace them with their replacement (either a token or a fallback) via a fixer.
|
|
14
|
+
|
|
3
15
|
## 4.11.2
|
|
4
16
|
|
|
5
17
|
### Patch Changes
|
|
@@ -2,4 +2,312 @@
|
|
|
2
2
|
order: 0
|
|
3
3
|
---
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
## ensure-design-token-usage
|
|
6
|
+
|
|
7
|
+
Ensures that the codebase uses the global `token` function, rather than hard-coded values. This ruleset is great for codebases that are starting to adopt tokens and those that have already adopted tokens. This ruleset also prevents new contributors from accidentally adding hard-coded color values.
|
|
8
|
+
|
|
9
|
+
👎 Example of **incorrect** code for this rule:
|
|
10
|
+
|
|
11
|
+
```jsx
|
|
12
|
+
css({
|
|
13
|
+
color: 'red',
|
|
14
|
+
^^^
|
|
15
|
+
});
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
```jsx
|
|
19
|
+
css({
|
|
20
|
+
boxShadow: '0px 1px 1px #161A1D32',
|
|
21
|
+
^^^^^^^^^
|
|
22
|
+
})
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
```jsx
|
|
26
|
+
import { e100 } from '@atlaskit/theme/elevation';
|
|
27
|
+
|
|
28
|
+
css`
|
|
29
|
+
${e100};
|
|
30
|
+
^^^^
|
|
31
|
+
`;
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
```jsx
|
|
35
|
+
import { B100 } from '@atlaskit/theme/colors';
|
|
36
|
+
|
|
37
|
+
css({
|
|
38
|
+
color: B100,
|
|
39
|
+
^^^^
|
|
40
|
+
});
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
👍 Example of **correct** code for this rule:
|
|
44
|
+
|
|
45
|
+
```jsx
|
|
46
|
+
import { token } from '@atlaskit/tokens';
|
|
47
|
+
|
|
48
|
+
css({
|
|
49
|
+
boxShadow: token('elevation.shadow.card'),
|
|
50
|
+
});
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
```jsx
|
|
54
|
+
import { token } from '@atlaskit/tokens';
|
|
55
|
+
|
|
56
|
+
css`
|
|
57
|
+
color: ${token('color.text.highemphasis')};
|
|
58
|
+
`;
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## no-deprecated-design-token-usage
|
|
62
|
+
|
|
63
|
+
Will catch deprecated token usage and autofix a replacement.
|
|
64
|
+
|
|
65
|
+
It's recommended to set this rule to "warn" on error to allow for new and old tokens to exist side-by-side for the duration of the deprecation period and avoid big-bang migrations.
|
|
66
|
+
|
|
67
|
+
Once the deprecation period is over for a token, it will be moved into `deleted` state, at which point the counterpart of this rule `eslint-plugin-design-system/no-unsafe-design-token-usage` will begin to throw errors.
|
|
68
|
+
|
|
69
|
+
Run `eslint --fix` will automatically apply replacement tokens.
|
|
70
|
+
|
|
71
|
+
👎 Example of **incorrect** code for this rule:
|
|
72
|
+
|
|
73
|
+
```jsx
|
|
74
|
+
import { token } from '@atlaskit/tokens';
|
|
75
|
+
|
|
76
|
+
css({
|
|
77
|
+
color: token('i.am.deprecated'), // 👎
|
|
78
|
+
});
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
```jsx
|
|
82
|
+
css({
|
|
83
|
+
color: token('i.am.a.token'), // 👍
|
|
84
|
+
});
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## no-unsafe-design-token-usage
|
|
88
|
+
|
|
89
|
+
Ensures usages of the `token` function are done correctly, so that current token names are being used, and the resulting `var(--some-token)` statements aren't being used. This ruleset is great for codebases that have already adopted tokens.
|
|
90
|
+
|
|
91
|
+
👎 Example of **incorrect** code for this rule:
|
|
92
|
+
|
|
93
|
+
```jsx
|
|
94
|
+
const textColor = 'red';
|
|
95
|
+
|
|
96
|
+
css({
|
|
97
|
+
color: textColor,
|
|
98
|
+
^^^^^^^^^
|
|
99
|
+
});
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
```jsx
|
|
103
|
+
css({
|
|
104
|
+
boxShadow: '0px 1px 1px var(--ds-accent-subtleBlue)',
|
|
105
|
+
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
106
|
+
})
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
👍 Example of **correct** code for this rule:
|
|
110
|
+
|
|
111
|
+
```jsx
|
|
112
|
+
import { token } from '@atlaskit/tokens';
|
|
113
|
+
|
|
114
|
+
css({
|
|
115
|
+
boxShadow: token('elevation.shadow.card'),
|
|
116
|
+
});
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
```jsx
|
|
120
|
+
import { token } from '@atlaskit/tokens';
|
|
121
|
+
|
|
122
|
+
css`
|
|
123
|
+
color: ${(token('color.text.highemphasis'), N20)};
|
|
124
|
+
`;
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## icon-label
|
|
128
|
+
|
|
129
|
+
Enforces accessible usage of icon labels when composed with other Design System components.
|
|
130
|
+
|
|
131
|
+
👎 Example of **incorrect** code for this rule:
|
|
132
|
+
|
|
133
|
+
```jsx
|
|
134
|
+
import ActivityIcon from '@atlaskit/icon/glyph/activity'
|
|
135
|
+
|
|
136
|
+
<ActivityIcon>
|
|
137
|
+
^^^^^^^^^^^^^^ missing `label` prop
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
```jsx
|
|
141
|
+
import ActivityIcon from '@atlaskit/icon/glyph/activity'
|
|
142
|
+
|
|
143
|
+
<Button iconLeft={<ActivityIcon label="">} />
|
|
144
|
+
^^^^^ label should be defined
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
```jsx
|
|
148
|
+
import ActivityIcon from '@atlaskit/icon/glyph/activity'
|
|
149
|
+
|
|
150
|
+
<ButtonItem iconBefore={<ActivityIcon label="">}>
|
|
151
|
+
^^^^^ label should not be defined
|
|
152
|
+
My activity
|
|
153
|
+
</ButtonItem>
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
👍 Example of **correct** code for this rule:
|
|
157
|
+
|
|
158
|
+
```jsx
|
|
159
|
+
import ActivityIcon from '@atlaskit/icon/glyph/activity'
|
|
160
|
+
|
|
161
|
+
<ActivityIcon label="Activity">
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
```jsx
|
|
165
|
+
import ActivityIcon from '@atlaskit/icon/glyph/activity'
|
|
166
|
+
|
|
167
|
+
<Button iconLeft={<ActivityIcon label="Activity">} />
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
```jsx
|
|
171
|
+
import ActivityIcon from '@atlaskit/icon/glyph/activity'
|
|
172
|
+
|
|
173
|
+
<ButtonItem iconBefore={<ActivityIcon label="">}>
|
|
174
|
+
My activity
|
|
175
|
+
</ButtonItem>
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
## no-banned-imports
|
|
179
|
+
|
|
180
|
+
Prevents usage of private or experimental Atlassian Design System packages.
|
|
181
|
+
|
|
182
|
+
👎 Example of **incorrect** code for this rule:
|
|
183
|
+
|
|
184
|
+
```ts
|
|
185
|
+
import noop from '@atlaskit/ds-lib/noop';
|
|
186
|
+
^^^^^^^^^^^^^^^^^^^^^
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
```ts
|
|
190
|
+
import { Text } from '@atlaskit/ds-explorations';
|
|
191
|
+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
## no-deprecated-api-usage
|
|
195
|
+
|
|
196
|
+
Ensures usage of current Atlassian Design System API usage.
|
|
197
|
+
|
|
198
|
+
👎 Example of **incorrect** code for this rule:
|
|
199
|
+
|
|
200
|
+
```jsx
|
|
201
|
+
import { ButtonItem } from '@atlaskit/menu';
|
|
202
|
+
|
|
203
|
+
const Element = () => (
|
|
204
|
+
<ButtonItem cssFn={cssFn()} />
|
|
205
|
+
^^^^
|
|
206
|
+
);
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
```jsx
|
|
210
|
+
import Drawer from '@atlaskit/drawer';
|
|
211
|
+
|
|
212
|
+
const Element = () => (
|
|
213
|
+
<Drawer overrides={overrides} />
|
|
214
|
+
^^^^
|
|
215
|
+
);
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
👍 Example of **correct** code for this rule:
|
|
219
|
+
|
|
220
|
+
```jsx
|
|
221
|
+
import { SomeElement } from 'some-other-library';
|
|
222
|
+
|
|
223
|
+
const Element = () => <SomeElement cssFn={cssFn()} />;
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
```jsx
|
|
227
|
+
import { ButtonItem } from '@atlaskit/menu';
|
|
228
|
+
|
|
229
|
+
const Element = () => <ButtonItem />;
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
```jsx
|
|
233
|
+
import Drawer from '@atlaskit/drawer';
|
|
234
|
+
|
|
235
|
+
const Element = () => <Drawer />;
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
## no-deprecated-imports
|
|
239
|
+
|
|
240
|
+
Ensures usage of current Atlassian Design System dependencies.
|
|
241
|
+
|
|
242
|
+
👎 Example of **incorrect** code for this rule:
|
|
243
|
+
|
|
244
|
+
```ts
|
|
245
|
+
import Item from '@atlaskit/item';
|
|
246
|
+
^^^^^^^^^^^^^^
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
```ts
|
|
250
|
+
import GlobalNav from '@atlaskit/global-navigation';
|
|
251
|
+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
👍 Example of **correct** code for this rule:
|
|
255
|
+
|
|
256
|
+
```ts
|
|
257
|
+
import Modal from '@atlaskit/modal-dialog';
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
```ts
|
|
261
|
+
import { ButtonItem } from '@atlaskit/menu';
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
## use-visually-hidden
|
|
265
|
+
|
|
266
|
+
This rule makes Design System consumers aware of the existing solutions.
|
|
267
|
+
|
|
268
|
+
👎 Example of **incorrect** code for this rule:
|
|
269
|
+
|
|
270
|
+
```jsx
|
|
271
|
+
import { css } from '@emotion/core';
|
|
272
|
+
|
|
273
|
+
const visuallyHiddenStyles = css({
|
|
274
|
+
width: '1px',
|
|
275
|
+
height: '1px',
|
|
276
|
+
padding: '0',
|
|
277
|
+
position: 'absolute',
|
|
278
|
+
border: '0',
|
|
279
|
+
clip: 'rect(1px, 1px, 1px, 1px)',
|
|
280
|
+
overflow: 'hidden',
|
|
281
|
+
whiteSpace: 'nowrap',
|
|
282
|
+
});
|
|
283
|
+
^^^^
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
```jsx
|
|
287
|
+
import styled from '@emotion/styled';
|
|
288
|
+
|
|
289
|
+
const VisuallyHidden = styled.span`
|
|
290
|
+
width: 1px;
|
|
291
|
+
height: 1px;
|
|
292
|
+
padding: 0;
|
|
293
|
+
position: absolute;
|
|
294
|
+
border: 0;
|
|
295
|
+
clip: rect(1px, 1px, 1px, 1px);
|
|
296
|
+
overflow: hidden;
|
|
297
|
+
whiteSpace: nowrap;
|
|
298
|
+
`;
|
|
299
|
+
^^^^
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
```jsx
|
|
303
|
+
import { visuallyHidden } from '@atlaskit/theme/constants';
|
|
304
|
+
|
|
305
|
+
const VisuallyHidden = styled.span`${visuallyHidden()}`;
|
|
306
|
+
^^^^^^^^^^^^^^
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
👍 Example of **correct** code for this rule:
|
|
310
|
+
|
|
311
|
+
```jsx
|
|
312
|
+
import VisuallyHidden from '@atlaskit/visually-hidden';
|
|
313
|
+
```
|
|
@@ -2,4 +2,44 @@
|
|
|
2
2
|
order: 2
|
|
3
3
|
---
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
This plugin contains rules that should be used with the Atlassian Design System.
|
|
6
|
+
Where possible, these rules come with auto fixers.
|
|
7
|
+
|
|
8
|
+
You can read more about configuring eslint in their [documentation](https://eslint.org/docs/user-guide/configuring).
|
|
9
|
+
|
|
10
|
+
## Configuration
|
|
11
|
+
|
|
12
|
+
Add the plugin to your `.eslintrc.js` file.
|
|
13
|
+
|
|
14
|
+
```diff
|
|
15
|
+
module.exports = {
|
|
16
|
+
plugins: [
|
|
17
|
+
+ '@atlaskit/design-system',
|
|
18
|
+
],
|
|
19
|
+
};
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Extend the configuration file.
|
|
23
|
+
|
|
24
|
+
```diff
|
|
25
|
+
module.exports = {
|
|
26
|
+
extends: [
|
|
27
|
+
+ 'plugin:@atlaskit/design-system/recommended',
|
|
28
|
+
],
|
|
29
|
+
};
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Enable any desired rules. The rules shown below are strongly recommended.
|
|
33
|
+
|
|
34
|
+
```diff
|
|
35
|
+
module.exports = {
|
|
36
|
+
rules: {
|
|
37
|
+
+ '@atlaskit/design-system/ensure-design-token-usage': ['error', { 'shouldEnsureFallbackUsage': true }],
|
|
38
|
+
+ '@atlaskit/design-system/no-deprecated-design-token-usage': 'warn',
|
|
39
|
+
+ '@atlaskit/design-system/no-unsafe-design-token-usage': ['error', { 'shouldEnsureFallbackUsage': true }],
|
|
40
|
+
+ '@atlaskit/design-system/use-visually-hidden': 'error',
|
|
41
|
+
+ '@atlaskit/design-system/no-deprecated-imports': 'error',
|
|
42
|
+
+ '@atlaskit/design-system/no-deprecated-apis': 'error'
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
```
|
package/dist/cjs/index.js
CHANGED
|
@@ -9,6 +9,8 @@ exports.rules = exports.configs = void 0;
|
|
|
9
9
|
|
|
10
10
|
var _ensureDesignTokenUsage = _interopRequireDefault(require("./rules/ensure-design-token-usage"));
|
|
11
11
|
|
|
12
|
+
var _ensureDesignTokenUsageSpacing = _interopRequireDefault(require("./rules/ensure-design-token-usage-spacing"));
|
|
13
|
+
|
|
12
14
|
var _iconLabel = _interopRequireDefault(require("./rules/icon-label"));
|
|
13
15
|
|
|
14
16
|
var _noBannedImports = _interopRequireDefault(require("./rules/no-banned-imports"));
|
|
@@ -19,8 +21,6 @@ var _noDeprecatedDesignTokenUsage = _interopRequireDefault(require("./rules/no-d
|
|
|
19
21
|
|
|
20
22
|
var _noDeprecatedImports = _interopRequireDefault(require("./rules/no-deprecated-imports"));
|
|
21
23
|
|
|
22
|
-
var _noRawSpacingValues = _interopRequireDefault(require("./rules/no-raw-spacing-values"));
|
|
23
|
-
|
|
24
24
|
var _noUnsafeDesignTokenUsage = _interopRequireDefault(require("./rules/no-unsafe-design-token-usage"));
|
|
25
25
|
|
|
26
26
|
var _useVisuallyHidden = _interopRequireDefault(require("./rules/use-visually-hidden"));
|
|
@@ -34,7 +34,7 @@ var rules = {
|
|
|
34
34
|
'no-banned-imports': _noBannedImports.default,
|
|
35
35
|
'no-unsafe-design-token-usage': _noUnsafeDesignTokenUsage.default,
|
|
36
36
|
'use-visually-hidden': _useVisuallyHidden.default,
|
|
37
|
-
'
|
|
37
|
+
'ensure-design-token-usage-spacing': _ensureDesignTokenUsageSpacing.default
|
|
38
38
|
};
|
|
39
39
|
exports.rules = rules;
|
|
40
40
|
var configs = {
|
|
@@ -57,7 +57,8 @@ var configs = {
|
|
|
57
57
|
'@atlaskit/design-system/use-visually-hidden': 'error',
|
|
58
58
|
'@atlaskit/design-system/ensure-design-token-usage': 'error',
|
|
59
59
|
'@atlaskit/design-system/no-banned-imports': 'error',
|
|
60
|
-
'@atlaskit/design-system/no-
|
|
60
|
+
'@atlaskit/design-system/no-unsafe-design-token-usage': 'error',
|
|
61
|
+
'@atlaskit/design-system/ensure-design-token-usage-spacing': 'error'
|
|
61
62
|
}
|
|
62
63
|
}
|
|
63
64
|
};
|
|
@@ -0,0 +1,277 @@
|
|
|
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 _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
|
|
11
|
+
|
|
12
|
+
var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
|
|
13
|
+
|
|
14
|
+
var _eslintCodemodUtils = require("eslint-codemod-utils");
|
|
15
|
+
|
|
16
|
+
var _spacingRaw = _interopRequireDefault(require("@atlaskit/tokens/spacing-raw"));
|
|
17
|
+
|
|
18
|
+
var _isNode = require("../utils/is-node");
|
|
19
|
+
|
|
20
|
+
var _utils = require("./utils");
|
|
21
|
+
|
|
22
|
+
function _createForOfIteratorHelper(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it.return != null) it.return(); } finally { if (didErr) throw err; } } }; }
|
|
23
|
+
|
|
24
|
+
function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
|
|
25
|
+
|
|
26
|
+
function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; }
|
|
27
|
+
|
|
28
|
+
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
|
|
29
|
+
|
|
30
|
+
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { (0, _defineProperty2.default)(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
|
|
31
|
+
|
|
32
|
+
var spacingValueToToken = Object.fromEntries(_spacingRaw.default.map(function (token) {
|
|
33
|
+
return [token.value, token.name];
|
|
34
|
+
}));
|
|
35
|
+
/**
|
|
36
|
+
* @example
|
|
37
|
+
* ```
|
|
38
|
+
* '8px' => token('spacing.scale.100', '8px')
|
|
39
|
+
* ```
|
|
40
|
+
*/
|
|
41
|
+
|
|
42
|
+
function pixelValueToSpacingTokenNode(pixelValueString) {
|
|
43
|
+
var _spacingValueToToken$;
|
|
44
|
+
|
|
45
|
+
return (0, _eslintCodemodUtils.callExpression)({
|
|
46
|
+
callee: (0, _eslintCodemodUtils.identifier)({
|
|
47
|
+
name: 'token'
|
|
48
|
+
}),
|
|
49
|
+
arguments: [(0, _eslintCodemodUtils.literal)({
|
|
50
|
+
value: "'".concat((_spacingValueToToken$ = spacingValueToToken[pixelValueString]) !== null && _spacingValueToToken$ !== void 0 ? _spacingValueToToken$ : '', "'")
|
|
51
|
+
}), (0, _eslintCodemodUtils.literal)("'".concat(pixelValueString, "'"))],
|
|
52
|
+
optional: false
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
var rule = {
|
|
57
|
+
meta: {
|
|
58
|
+
type: 'problem',
|
|
59
|
+
fixable: 'code',
|
|
60
|
+
docs: {
|
|
61
|
+
description: 'Rule ensures all spacing CSS properties apply a matching spacing token',
|
|
62
|
+
recommended: true
|
|
63
|
+
},
|
|
64
|
+
messages: {
|
|
65
|
+
noMarginProperties: 'The use of margin is considered a dangerous as it breaks the component model. Prefer the application of `gap` via CSS Flexbox or Grid to achieve the same result and control the layout from the parent.',
|
|
66
|
+
noRawSpacingValues: 'The use of spacing primitives or tokens is preferred over the direct application of spacing properties.\n\n@meta <<{{payload}}>>'
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
create: function create(context) {
|
|
70
|
+
return {
|
|
71
|
+
// CSSObjectExpression
|
|
72
|
+
// const styles = css({ color: 'red', margin: '4px' })
|
|
73
|
+
'CallExpression[callee.name=css] > ObjectExpression': function CallExpressionCalleeNameCssObjectExpression(parentNode) {
|
|
74
|
+
if (!(0, _eslintCodemodUtils.isNodeOfType)(parentNode, 'ObjectExpression')) {
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* We do this in case the fontSize for a style object is declared alongside the `em` or `lineHeight` declaration
|
|
79
|
+
*/
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
var fontSizeNode = parentNode.properties.find(function (node) {
|
|
83
|
+
if (!(0, _eslintCodemodUtils.isNodeOfType)(node, 'Property')) {
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (!(0, _eslintCodemodUtils.isNodeOfType)(node.key, 'Identifier')) {
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return node.key.name === 'fontSize';
|
|
92
|
+
});
|
|
93
|
+
var fontSizeValue = (0, _utils.getValue)( // @ts-ignore
|
|
94
|
+
(0, _eslintCodemodUtils.isNodeOfType)(fontSizeNode, 'Property') && fontSizeNode.value, context);
|
|
95
|
+
var fontSize = Array.isArray(fontSizeValue) ? fontSizeValue[0] : fontSizeValue;
|
|
96
|
+
parentNode.properties.forEach(function (node) {
|
|
97
|
+
if (!(0, _eslintCodemodUtils.isNodeOfType)(node, 'Property')) {
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (!(0, _eslintCodemodUtils.isNodeOfType)(node.key, 'Identifier')) {
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (!(0, _utils.isSpacingProperty)(node.key.name)) {
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if ((0, _isNode.isDecendantOfGlobalToken)(node.value)) {
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (node.value.type === 'Literal' && !(0, _utils.isValidSpacingValue)(node.value.value, fontSize)) {
|
|
114
|
+
context.report({
|
|
115
|
+
node: node,
|
|
116
|
+
messageId: 'noRawSpacingValues',
|
|
117
|
+
data: {
|
|
118
|
+
payload: "NaN:".concat(node.value.value)
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
var value = (0, _utils.getValue)(node.value, context); // value is either NaN or it can't be resolved eg, em, 100% etc...
|
|
125
|
+
|
|
126
|
+
if (!(value && (0, _utils.isValidSpacingValue)(value, fontSize))) {
|
|
127
|
+
return context.report({
|
|
128
|
+
node: node,
|
|
129
|
+
messageId: 'noRawSpacingValues',
|
|
130
|
+
data: {
|
|
131
|
+
payload: "NaN:".concat(value)
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
var values = Array.isArray(value) ? value : [value]; // value is a single value so we can apply a more robust approach to our fix
|
|
137
|
+
|
|
138
|
+
if (values.length === 1) {
|
|
139
|
+
var _values = (0, _slicedToArray2.default)(values, 1),
|
|
140
|
+
_value = _values[0];
|
|
141
|
+
|
|
142
|
+
var pixelValue = (0, _utils.emToPixels)(_value, fontSize);
|
|
143
|
+
return context.report({
|
|
144
|
+
node: node,
|
|
145
|
+
messageId: 'noRawSpacingValues',
|
|
146
|
+
data: {
|
|
147
|
+
payload: "".concat(node.key.name, ":").concat(pixelValue)
|
|
148
|
+
},
|
|
149
|
+
fix: function fix(fixer) {
|
|
150
|
+
var _node$loc;
|
|
151
|
+
|
|
152
|
+
if (!/padding|margin|gap/.test(node.key.name)) {
|
|
153
|
+
return null;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
var pixelValueString = "".concat(pixelValue, "px");
|
|
157
|
+
var tokenName = spacingValueToToken[pixelValueString];
|
|
158
|
+
|
|
159
|
+
if (!tokenName) {
|
|
160
|
+
return null;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
return [fixer.insertTextBefore(node, "// TODO Delete this comment after verifying spacing token -> previous value `".concat((0, _eslintCodemodUtils.node)(node.value), "`\n").concat(' '.padStart(((_node$loc = node.loc) === null || _node$loc === void 0 ? void 0 : _node$loc.start.column) || 0))), fixer.replaceText(node, (0, _eslintCodemodUtils.property)(_objectSpread(_objectSpread({}, node), {}, {
|
|
164
|
+
value: pixelValueToSpacingTokenNode(pixelValueString)
|
|
165
|
+
})).toString())];
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Compound values are hard to deal with / replace because we need to find/replace strings inside an
|
|
171
|
+
* estree node.
|
|
172
|
+
*
|
|
173
|
+
* @example
|
|
174
|
+
* { padding: '8px 0px' } // two values we don't try and apply the fixer
|
|
175
|
+
*/
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
values.forEach(function (val, index) {
|
|
179
|
+
var pixelValue = (0, _utils.emToPixels)(val, fontSize);
|
|
180
|
+
context.report({
|
|
181
|
+
node: node,
|
|
182
|
+
messageId: 'noRawSpacingValues',
|
|
183
|
+
data: {
|
|
184
|
+
payload: "".concat(node.key.name, ":").concat(pixelValue)
|
|
185
|
+
},
|
|
186
|
+
fix: index === 0 ? function (fixer) {
|
|
187
|
+
var allResolvableValues = values.every(function (value) {
|
|
188
|
+
return !Number.isNaN((0, _utils.emToPixels)(value, fontSize));
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
if (!allResolvableValues) {
|
|
192
|
+
return null;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
return fixer.replaceText(node.value, "`".concat(values.map(function (value) {
|
|
196
|
+
var pixelValue = (0, _utils.emToPixels)(value, fontSize);
|
|
197
|
+
var pixelValueString = "".concat(pixelValue, "px");
|
|
198
|
+
return "${".concat(pixelValueToSpacingTokenNode(pixelValueString), "}");
|
|
199
|
+
}).join(' '), "`"));
|
|
200
|
+
} : undefined
|
|
201
|
+
});
|
|
202
|
+
});
|
|
203
|
+
});
|
|
204
|
+
},
|
|
205
|
+
// CSSTemplateLiteral and StyledTemplateLiteral
|
|
206
|
+
// const cssTemplateLiteral = css`color: red; padding: 12px`;
|
|
207
|
+
// const styledTemplateLiteral = styled.p`color: red; padding: 8px`;
|
|
208
|
+
'TaggedTemplateExpression[tag.name="css"],TaggedTemplateExpression[tag.object.name="styled"]': function TaggedTemplateExpressionTagNameCssTaggedTemplateExpressionTagObjectNameStyled(node) {
|
|
209
|
+
if (node.type !== 'TaggedTemplateExpression') {
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
var combinedString = node.quasi.quasis.map(function (q, i) {
|
|
214
|
+
return "".concat(q.value.raw).concat(node.quasi.expressions[i] ? (0, _utils.getValue)(node.quasi.expressions[i], context) : '');
|
|
215
|
+
}).join('');
|
|
216
|
+
/**
|
|
217
|
+
* Attempts to remove all non-essential words & characters from a style block.
|
|
218
|
+
* Including selectors and queries
|
|
219
|
+
* Adapted from ensure-design-token-usage
|
|
220
|
+
*/
|
|
221
|
+
|
|
222
|
+
var cssProperties = combinedString.split('\n').filter(function (line) {
|
|
223
|
+
return !line.trim().startsWith('@');
|
|
224
|
+
}).join('\n').replace(/\n/g, '').split(/;|{|}/).map(function (el) {
|
|
225
|
+
return el.trim() || '';
|
|
226
|
+
});
|
|
227
|
+
cssProperties.forEach(function (style) {
|
|
228
|
+
var _style$split = style.split(':'),
|
|
229
|
+
_style$split2 = (0, _slicedToArray2.default)(_style$split, 2),
|
|
230
|
+
rawProperty = _style$split2[0],
|
|
231
|
+
value = _style$split2[1];
|
|
232
|
+
|
|
233
|
+
var property = (0, _utils.convertHyphenatedNameToCamelCase)(rawProperty);
|
|
234
|
+
|
|
235
|
+
if (!(0, _utils.isSpacingProperty)(property)) {
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
if (!(0, _utils.isValidSpacingValue)(value)) {
|
|
240
|
+
return context.report({
|
|
241
|
+
node: node,
|
|
242
|
+
messageId: 'noRawSpacingValues',
|
|
243
|
+
data: {
|
|
244
|
+
payload: "NaN:".concat(value)
|
|
245
|
+
}
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
var values = (0, _utils.getValueFromShorthand)(value);
|
|
250
|
+
|
|
251
|
+
var _iterator = _createForOfIteratorHelper(values),
|
|
252
|
+
_step;
|
|
253
|
+
|
|
254
|
+
try {
|
|
255
|
+
for (_iterator.s(); !(_step = _iterator.n()).done;) {
|
|
256
|
+
var val = _step.value;
|
|
257
|
+
// could be array of values e.g. padding: 8px 12px 3px
|
|
258
|
+
context.report({
|
|
259
|
+
node: node,
|
|
260
|
+
messageId: 'noRawSpacingValues',
|
|
261
|
+
data: {
|
|
262
|
+
payload: "".concat(property, ":").concat(val)
|
|
263
|
+
}
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
} catch (err) {
|
|
267
|
+
_iterator.e(err);
|
|
268
|
+
} finally {
|
|
269
|
+
_iterator.f();
|
|
270
|
+
}
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
};
|
|
276
|
+
var _default = rule;
|
|
277
|
+
exports.default = _default;
|