@atlaskit/eslint-plugin-design-system 5.5.0 → 6.0.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/README.md +1 -0
- package/constellation/index/usage.mdx +95 -0
- package/dist/cjs/presets/all.codegen.js +2 -1
- package/dist/cjs/presets/recommended.codegen.js +2 -1
- package/dist/cjs/rules/consistent-css-prop-usage/index.js +182 -0
- package/dist/cjs/rules/ensure-design-token-usage-spacing/utils.js +1 -1
- package/dist/cjs/rules/index.codegen.js +3 -1
- package/dist/cjs/version.json +1 -1
- package/dist/es2019/presets/all.codegen.js +2 -1
- package/dist/es2019/presets/recommended.codegen.js +2 -1
- package/dist/es2019/rules/consistent-css-prop-usage/index.js +165 -0
- package/dist/es2019/rules/ensure-design-token-usage-spacing/utils.js +1 -1
- package/dist/es2019/rules/index.codegen.js +3 -1
- package/dist/es2019/version.json +1 -1
- package/dist/esm/presets/all.codegen.js +2 -1
- package/dist/esm/presets/recommended.codegen.js +2 -1
- package/dist/esm/rules/consistent-css-prop-usage/index.js +174 -0
- package/dist/esm/rules/ensure-design-token-usage-spacing/utils.js +1 -1
- package/dist/esm/rules/index.codegen.js +3 -1
- package/dist/esm/version.json +1 -1
- package/dist/types/index.codegen.d.ts +2 -0
- package/dist/types/presets/all.codegen.d.ts +2 -1
- package/dist/types/presets/recommended.codegen.d.ts +2 -1
- package/dist/types/rules/consistent-css-prop-usage/index.d.ts +3 -0
- package/dist/types/rules/index.codegen.d.ts +1 -0
- package/dist/types/rules/utils/create-rule.d.ts +4 -0
- package/dist/types-ts4.5/index.codegen.d.ts +2 -0
- package/dist/types-ts4.5/presets/all.codegen.d.ts +2 -1
- package/dist/types-ts4.5/presets/recommended.codegen.d.ts +2 -1
- package/dist/types-ts4.5/rules/consistent-css-prop-usage/index.d.ts +3 -0
- package/dist/types-ts4.5/rules/index.codegen.d.ts +1 -0
- package/dist/types-ts4.5/rules/utils/create-rule.d.ts +4 -0
- package/package.json +3 -3
- package/report.api.md +95 -11
- package/tmp/api-report-tmp.d.ts +103 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# @atlaskit/eslint-plugin-design-system
|
|
2
2
|
|
|
3
|
+
## 6.0.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [`fa18829653d`](https://bitbucket.org/atlassian/atlassian-frontend/commits/fa18829653d) - Updated spacing rule to ignore gridSize functions that accept arguments as these are custom and not part of the Design System.
|
|
8
|
+
|
|
9
|
+
## 6.0.0
|
|
10
|
+
|
|
11
|
+
### Major Changes
|
|
12
|
+
|
|
13
|
+
- [`c8174712a85`](https://bitbucket.org/atlassian/atlassian-frontend/commits/c8174712a85) - New rule consistent-css-prop-usage available as part of the recommended preset. For detailed docs please see: https://atlassian.design/components/eslint-plugin-design-system/usage#consistent-css-prop-usage
|
|
14
|
+
|
|
3
15
|
## 5.5.0
|
|
4
16
|
|
|
5
17
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -49,6 +49,7 @@ module.exports = {
|
|
|
49
49
|
|
|
50
50
|
| Rule | Description | Recommended | Fixable | Suggestions |
|
|
51
51
|
| ------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- | ----------- | ------- | ----------- |
|
|
52
|
+
| <a href="./src/rules/consistent-css-prop-usage/README.md">consistent-css-prop-usage</a> | Ensures consistency with CSS and xCSS prop usages | Yes | Yes | |
|
|
52
53
|
| <a href="./src/rules/ensure-design-token-usage/README.md">ensure-design-token-usage</a> | Enforces usage of design tokens. | Yes | Yes | Yes |
|
|
53
54
|
| <a href="./src/rules/ensure-design-token-usage-spacing/README.md">ensure-design-token-usage-spacing</a> | Enforces usage of spacing design tokens rather than hard-coded values. | | Yes | |
|
|
54
55
|
| <a href="./src/rules/icon-label/README.md">icon-label</a> | Enforces accessible usage of icon labels when composed with Atlassian Design System components. | Yes | Yes | |
|
|
@@ -13,6 +13,7 @@ This plugin contains rules that should be used when working with the [Atlassian
|
|
|
13
13
|
|
|
14
14
|
| Rule | Description | Recommended | Fixable | Suggestions |
|
|
15
15
|
| ---------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- | ----------- | ------- | ----------- |
|
|
16
|
+
| <a href="#consistent-css-prop-usage">consistent-css-prop-usage</a> | Ensures consistency with CSS and xCSS prop usages | Yes | Yes | |
|
|
16
17
|
| <a href="#ensure-design-token-usage">ensure-design-token-usage</a> | Enforces usage of design tokens. | Yes | Yes | Yes |
|
|
17
18
|
| <a href="#ensure-design-token-usage-spacing">ensure-design-token-usage-spacing</a> | Enforces usage of spacing design tokens rather than hard-coded values. | | Yes | |
|
|
18
19
|
| <a href="#icon-label">icon-label</a> | Enforces accessible usage of icon labels when composed with Atlassian Design System components. | Yes | Yes | |
|
|
@@ -30,6 +31,100 @@ This plugin contains rules that should be used when working with the [Atlassian
|
|
|
30
31
|
<!-- START_RULE_CONTENT_CODEGEN -->
|
|
31
32
|
<!-- @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen -->
|
|
32
33
|
|
|
34
|
+
## consistent-css-prop-usage
|
|
35
|
+
|
|
36
|
+
> Ensures consistency with CSS prop usage.
|
|
37
|
+
|
|
38
|
+
<h3>Rationale</h3>
|
|
39
|
+
|
|
40
|
+
Every product should be defining styles in the same way, using the same tools, enforced by the same linting rules, which we can then all evolve and scale together.
|
|
41
|
+
|
|
42
|
+
<h3>How the rule works</h3>
|
|
43
|
+
|
|
44
|
+
This rule checks for the following cases:
|
|
45
|
+
|
|
46
|
+
- When styles are defined inline.
|
|
47
|
+
- When styles are not using `css` object api.
|
|
48
|
+
- When styles are coming from outside of the module i.e. using imports.
|
|
49
|
+
- When styles are spread inside another styles and not using array composition.
|
|
50
|
+
|
|
51
|
+
This rule has no options.
|
|
52
|
+
|
|
53
|
+
<h3>Examples</h3>
|
|
54
|
+
|
|
55
|
+
👎 Example of **incorrect** code for this rule:
|
|
56
|
+
|
|
57
|
+
```js
|
|
58
|
+
function Button({ children }) {
|
|
59
|
+
return <div css={css({...})}>{children}</div>;
|
|
60
|
+
^^^^^^^ css functional call is used inline
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
```js
|
|
65
|
+
const container = {
|
|
66
|
+
^^^^^^^^^ should be a css function call
|
|
67
|
+
zIndex: 10,
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
function Button({ children }) {
|
|
71
|
+
return <button css={container}>{children}</button>;
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
```js
|
|
76
|
+
import { container } from './styles';
|
|
77
|
+
^^^^^^^^^ styles should be local, not shared
|
|
78
|
+
|
|
79
|
+
function Button({ children }) {
|
|
80
|
+
return <button css={container}>{children}</button>;
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
```js
|
|
85
|
+
const baseContainerStyles = css({
|
|
86
|
+
zIndex: 5,
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
const containerStyles = css({
|
|
90
|
+
...baseContainerStyles,
|
|
91
|
+
^^^^^^^^^^^^^^^^^^^^^^ compose styles with an array to the css call instead
|
|
92
|
+
zIndex: 7,
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
function Button({ children }) {
|
|
96
|
+
return <button css={containerStyles}>{children}</button>;
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
👍 Example of **correct** code for this rule:
|
|
101
|
+
|
|
102
|
+
```js
|
|
103
|
+
const containerStyles = css({
|
|
104
|
+
zIndex: 1,
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
function Button({ children }) {
|
|
108
|
+
return <button css={containerStyles}>{children}</button>;
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
```js
|
|
113
|
+
const baseContainerStyles = css({
|
|
114
|
+
zIndex: 5,
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
const containerStyles = css({
|
|
118
|
+
zIndex: 7,
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
function Button({ children }) {
|
|
122
|
+
return (
|
|
123
|
+
<button css={[baseContainerStyles, containerStyles]}>{children}</button>
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
33
128
|
## ensure-design-token-usage
|
|
34
129
|
|
|
35
130
|
Using color design tokens enables our experiences to be themed and harmonious.
|
|
@@ -6,12 +6,13 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
6
6
|
exports.default = void 0;
|
|
7
7
|
/**
|
|
8
8
|
* THIS FILE WAS CREATED VIA CODEGEN DO NOT MODIFY {@see http://go/af-codegen}
|
|
9
|
-
* @codegen <<SignedSource::
|
|
9
|
+
* @codegen <<SignedSource::42b6b42f38f30d690b6a38a2b6369b70>>
|
|
10
10
|
* @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
|
|
11
11
|
*/
|
|
12
12
|
var _default = {
|
|
13
13
|
plugins: ['@atlaskit/design-system'],
|
|
14
14
|
rules: {
|
|
15
|
+
'@atlaskit/design-system/consistent-css-prop-usage': 'error',
|
|
15
16
|
'@atlaskit/design-system/ensure-design-token-usage': 'error',
|
|
16
17
|
'@atlaskit/design-system/ensure-design-token-usage-spacing': 'error',
|
|
17
18
|
'@atlaskit/design-system/icon-label': 'warn',
|
|
@@ -6,12 +6,13 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
6
6
|
exports.default = void 0;
|
|
7
7
|
/**
|
|
8
8
|
* THIS FILE WAS CREATED VIA CODEGEN DO NOT MODIFY {@see http://go/af-codegen}
|
|
9
|
-
* @codegen <<SignedSource::
|
|
9
|
+
* @codegen <<SignedSource::5b7e085532640c14193d07c63e0f5442>>
|
|
10
10
|
* @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
|
|
11
11
|
*/
|
|
12
12
|
var _default = {
|
|
13
13
|
plugins: ['@atlaskit/design-system'],
|
|
14
14
|
rules: {
|
|
15
|
+
'@atlaskit/design-system/consistent-css-prop-usage': 'error',
|
|
15
16
|
'@atlaskit/design-system/ensure-design-token-usage': 'error',
|
|
16
17
|
'@atlaskit/design-system/icon-label': 'warn',
|
|
17
18
|
'@atlaskit/design-system/no-banned-imports': 'error',
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
4
|
+
Object.defineProperty(exports, "__esModule", {
|
|
5
|
+
value: true
|
|
6
|
+
});
|
|
7
|
+
exports.default = void 0;
|
|
8
|
+
var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
|
|
9
|
+
var _eslintCodemodUtils = require("eslint-codemod-utils");
|
|
10
|
+
var _createRule = require("../utils/create-rule");
|
|
11
|
+
var declarationSuffix = 'Styles';
|
|
12
|
+
function isCssCallExpression(node) {
|
|
13
|
+
return !!((0, _eslintCodemodUtils.isNodeOfType)(node, 'CallExpression') && node.callee && node.callee.type === 'Identifier' && (node.callee.name === 'css' || node.callee.name === 'xcss') && node.arguments.length && node.arguments[0].type === 'ObjectExpression');
|
|
14
|
+
}
|
|
15
|
+
function findSpreadProperties(node) {
|
|
16
|
+
// @ts-ignore
|
|
17
|
+
return node.properties.filter(function (property) {
|
|
18
|
+
return property.type === 'SpreadElement' ||
|
|
19
|
+
// @ts-ignore
|
|
20
|
+
property.type === 'ExperimentalSpreadProperty';
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
function analyzeIdentifier(context, sourceIdentifier) {
|
|
24
|
+
var _getIdentifierInParen;
|
|
25
|
+
var scope = context.getScope();
|
|
26
|
+
var _ref = ((_getIdentifierInParen = (0, _eslintCodemodUtils.getIdentifierInParentScope)(scope, sourceIdentifier.name)) === null || _getIdentifierInParen === void 0 ? void 0 : _getIdentifierInParen.identifiers) || [],
|
|
27
|
+
_ref2 = (0, _slicedToArray2.default)(_ref, 1),
|
|
28
|
+
identifier = _ref2[0];
|
|
29
|
+
if (!identifier || !identifier.parent) {
|
|
30
|
+
// Identifier isn't in the module, skip!
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
if (identifier.parent.type !== 'VariableDeclarator') {
|
|
34
|
+
// When variable is not in the file or coming from import
|
|
35
|
+
context.report({
|
|
36
|
+
node: sourceIdentifier,
|
|
37
|
+
messageId: 'cssInModule'
|
|
38
|
+
});
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
if (identifier.parent.parent.parent.type !== 'Program') {
|
|
42
|
+
// When variable is declared inside the component
|
|
43
|
+
context.report({
|
|
44
|
+
node: sourceIdentifier,
|
|
45
|
+
messageId: 'cssOnTopOfModule'
|
|
46
|
+
});
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
if (identifier.parent && identifier.parent.init && !isCssCallExpression(identifier.parent.init)) {
|
|
50
|
+
// When variable value is not of type css({})
|
|
51
|
+
context.report({
|
|
52
|
+
node: identifier,
|
|
53
|
+
messageId: 'cssObjectTypeOnly'
|
|
54
|
+
});
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
var spreadProperties = (0, _eslintCodemodUtils.isNodeOfType)(identifier.parent.init, 'CallExpression') && findSpreadProperties(identifier.parent.init.arguments[0]);
|
|
58
|
+
if (spreadProperties) {
|
|
59
|
+
// TODO: Recursively handle spread items in children properties.
|
|
60
|
+
spreadProperties.forEach(function (prop) {
|
|
61
|
+
context.report({
|
|
62
|
+
node: prop,
|
|
63
|
+
messageId: 'cssArrayStylesOnly'
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
function traverseExpression(context, expression) {
|
|
69
|
+
switch (expression.type) {
|
|
70
|
+
case 'Identifier':
|
|
71
|
+
// {styles}
|
|
72
|
+
// We've found an identifier - time to analyze it!
|
|
73
|
+
analyzeIdentifier(context, expression);
|
|
74
|
+
break;
|
|
75
|
+
case 'ArrayExpression':
|
|
76
|
+
// {[styles, moreStyles]}
|
|
77
|
+
// We've found an array expression - let's traverse again over each element individually.
|
|
78
|
+
expression.elements.forEach(function (element) {
|
|
79
|
+
return traverseExpression(context, element);
|
|
80
|
+
});
|
|
81
|
+
break;
|
|
82
|
+
case 'LogicalExpression':
|
|
83
|
+
// {isEnabled && styles}
|
|
84
|
+
// We've found a logical expression - we're only interested in the right expression so
|
|
85
|
+
// let's traverse that and see what it is!
|
|
86
|
+
traverseExpression(context, expression.right);
|
|
87
|
+
break;
|
|
88
|
+
case 'ConditionalExpression':
|
|
89
|
+
// {isEnabled ? styles : null}
|
|
90
|
+
// We've found a conditional expression - we're only interested in the consequent and
|
|
91
|
+
// alternate (styles : null)
|
|
92
|
+
traverseExpression(context, expression.consequent);
|
|
93
|
+
traverseExpression(context, expression.alternate);
|
|
94
|
+
break;
|
|
95
|
+
case 'CallExpression':
|
|
96
|
+
case 'ObjectExpression':
|
|
97
|
+
case 'TaggedTemplateExpression':
|
|
98
|
+
case 'TemplateLiteral':
|
|
99
|
+
// We've found elements that shouldn't be here! Report an error.
|
|
100
|
+
context.report({
|
|
101
|
+
node: expression,
|
|
102
|
+
messageId: 'cssOnTopOfModule'
|
|
103
|
+
});
|
|
104
|
+
break;
|
|
105
|
+
default:
|
|
106
|
+
// Do nothing!
|
|
107
|
+
break;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
var rule = (0, _createRule.createLintRule)({
|
|
111
|
+
meta: {
|
|
112
|
+
name: 'consistent-css-prop-usage',
|
|
113
|
+
docs: {
|
|
114
|
+
description: 'Ensures consistency with CSS and xCSS prop usages',
|
|
115
|
+
url: 'https://developer.atlassian.com/cloud/framework/atlassian-frontend/development/styling',
|
|
116
|
+
recommended: true,
|
|
117
|
+
severity: 'error'
|
|
118
|
+
},
|
|
119
|
+
fixable: 'code',
|
|
120
|
+
messages: {
|
|
121
|
+
cssOnTopOfModule: "Create styles at the top of the module scope using the css function.",
|
|
122
|
+
cssObjectTypeOnly: "Create styles using objects passed to the css function.",
|
|
123
|
+
cssInModule: "Imported styles should not be used, instead define in the module, import a component, or use a design token.",
|
|
124
|
+
cssArrayStylesOnly: "Compose styles with an array on the CSS prop instead of using object spread.",
|
|
125
|
+
shouldEndInStyles: 'Declared styles should end in "styles".'
|
|
126
|
+
}
|
|
127
|
+
},
|
|
128
|
+
create: function create(context) {
|
|
129
|
+
return {
|
|
130
|
+
'CallExpression[callee.name=css], CallExpression[callee.name=xcss]': function CallExpressionCalleeNameCssCallExpressionCalleeNameXcss(node) {
|
|
131
|
+
if (node.parent.type !== 'VariableDeclarator') {
|
|
132
|
+
// We aren't interested in these that don't have a parent.
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
var identifier = node.parent.id;
|
|
136
|
+
if (identifier.type === 'Identifier' && identifier.name.endsWith(declarationSuffix)) {
|
|
137
|
+
// Already prefixed! Nothing to do.
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
context.report({
|
|
141
|
+
node: identifier,
|
|
142
|
+
messageId: 'shouldEndInStyles',
|
|
143
|
+
fix: function fix(fixer) {
|
|
144
|
+
var _context$getScope$var;
|
|
145
|
+
var identifierName = identifier.type === 'Identifier' ? identifier.name : '';
|
|
146
|
+
var references = ((_context$getScope$var = context.getScope().variables.find(function (varb) {
|
|
147
|
+
return varb.name === identifierName;
|
|
148
|
+
})) === null || _context$getScope$var === void 0 ? void 0 : _context$getScope$var.references) || [];
|
|
149
|
+
var newIdentifierName = "".concat(identifierName
|
|
150
|
+
// Remove "Style" if it is already defined.
|
|
151
|
+
.replace(/Style$/, '')).concat(declarationSuffix);
|
|
152
|
+
return references.filter(function (ref) {
|
|
153
|
+
return ref.identifier.name === identifierName;
|
|
154
|
+
}).map(function (ref) {
|
|
155
|
+
if (ref.identifier.parent && ref.identifier.parent.shorthand) {
|
|
156
|
+
return fixer.replaceText(ref.identifier, "".concat(identifierName, ": ").concat(newIdentifierName));
|
|
157
|
+
}
|
|
158
|
+
return fixer.replaceText(ref.identifier, newIdentifierName);
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
},
|
|
163
|
+
JSXAttribute: function JSXAttribute(node) {
|
|
164
|
+
var name = node.name,
|
|
165
|
+
value = node.value;
|
|
166
|
+
if (name.type === 'JSXIdentifier' && (name.name === 'css' || name.name === 'xcss')) {
|
|
167
|
+
// When not a jsx expression. For eg. css=""
|
|
168
|
+
if ((value === null || value === void 0 ? void 0 : value.type) !== 'JSXExpressionContainer') {
|
|
169
|
+
context.report({
|
|
170
|
+
node: value,
|
|
171
|
+
messageId: 'cssOnTopOfModule'
|
|
172
|
+
});
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
traverseExpression(context, value.expression);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
});
|
|
181
|
+
var _default = rule;
|
|
182
|
+
exports.default = _default;
|
|
@@ -82,7 +82,7 @@ var getValueFromShorthand = function getValueFromShorthand(str) {
|
|
|
82
82
|
};
|
|
83
83
|
exports.getValueFromShorthand = getValueFromShorthand;
|
|
84
84
|
var isGridSize = function isGridSize(node) {
|
|
85
|
-
return (0, _eslintCodemodUtils.isNodeOfType)(node, 'CallExpression') && (0, _eslintCodemodUtils.isNodeOfType)(node.callee, 'Identifier') && (node.callee.name === 'gridSize' || node.callee.name === 'getGridSize');
|
|
85
|
+
return (0, _eslintCodemodUtils.isNodeOfType)(node, 'CallExpression') && (0, _eslintCodemodUtils.isNodeOfType)(node.callee, 'Identifier') && (node.callee.name === 'gridSize' || node.callee.name === 'getGridSize') && node.arguments.length === 0;
|
|
86
86
|
};
|
|
87
87
|
var isToken = function isToken(node) {
|
|
88
88
|
return (0, _eslintCodemodUtils.isNodeOfType)(node, 'CallExpression') && (0, _eslintCodemodUtils.isNodeOfType)(node.callee, 'Identifier') && node.callee.name === 'token';
|
|
@@ -5,6 +5,7 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
5
5
|
value: true
|
|
6
6
|
});
|
|
7
7
|
exports.default = void 0;
|
|
8
|
+
var _consistentCssPropUsage = _interopRequireDefault(require("./consistent-css-prop-usage"));
|
|
8
9
|
var _ensureDesignTokenUsage = _interopRequireDefault(require("./ensure-design-token-usage"));
|
|
9
10
|
var _ensureDesignTokenUsageSpacing = _interopRequireDefault(require("./ensure-design-token-usage-spacing"));
|
|
10
11
|
var _iconLabel = _interopRequireDefault(require("./icon-label"));
|
|
@@ -18,10 +19,11 @@ var _usePrimitives = _interopRequireDefault(require("./use-primitives"));
|
|
|
18
19
|
var _useVisuallyHidden = _interopRequireDefault(require("./use-visually-hidden"));
|
|
19
20
|
/**
|
|
20
21
|
* THIS FILE WAS CREATED VIA CODEGEN DO NOT MODIFY {@see http://go/af-codegen}
|
|
21
|
-
* @codegen <<SignedSource::
|
|
22
|
+
* @codegen <<SignedSource::a966a7ed0f0346cea4b9a41131e25f2e>>
|
|
22
23
|
* @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
|
|
23
24
|
*/
|
|
24
25
|
var _default = {
|
|
26
|
+
'consistent-css-prop-usage': _consistentCssPropUsage.default,
|
|
25
27
|
'ensure-design-token-usage': _ensureDesignTokenUsage.default,
|
|
26
28
|
'ensure-design-token-usage-spacing': _ensureDesignTokenUsageSpacing.default,
|
|
27
29
|
'icon-label': _iconLabel.default,
|
package/dist/cjs/version.json
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* THIS FILE WAS CREATED VIA CODEGEN DO NOT MODIFY {@see http://go/af-codegen}
|
|
3
|
-
* @codegen <<SignedSource::
|
|
3
|
+
* @codegen <<SignedSource::42b6b42f38f30d690b6a38a2b6369b70>>
|
|
4
4
|
* @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
|
|
5
5
|
*/
|
|
6
6
|
export default {
|
|
7
7
|
plugins: ['@atlaskit/design-system'],
|
|
8
8
|
rules: {
|
|
9
|
+
'@atlaskit/design-system/consistent-css-prop-usage': 'error',
|
|
9
10
|
'@atlaskit/design-system/ensure-design-token-usage': 'error',
|
|
10
11
|
'@atlaskit/design-system/ensure-design-token-usage-spacing': 'error',
|
|
11
12
|
'@atlaskit/design-system/icon-label': 'warn',
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* THIS FILE WAS CREATED VIA CODEGEN DO NOT MODIFY {@see http://go/af-codegen}
|
|
3
|
-
* @codegen <<SignedSource::
|
|
3
|
+
* @codegen <<SignedSource::5b7e085532640c14193d07c63e0f5442>>
|
|
4
4
|
* @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
|
|
5
5
|
*/
|
|
6
6
|
export default {
|
|
7
7
|
plugins: ['@atlaskit/design-system'],
|
|
8
8
|
rules: {
|
|
9
|
+
'@atlaskit/design-system/consistent-css-prop-usage': 'error',
|
|
9
10
|
'@atlaskit/design-system/ensure-design-token-usage': 'error',
|
|
10
11
|
'@atlaskit/design-system/icon-label': 'warn',
|
|
11
12
|
'@atlaskit/design-system/no-banned-imports': 'error',
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import { getIdentifierInParentScope, isNodeOfType } from 'eslint-codemod-utils';
|
|
2
|
+
import { createLintRule } from '../utils/create-rule';
|
|
3
|
+
const declarationSuffix = 'Styles';
|
|
4
|
+
function isCssCallExpression(node) {
|
|
5
|
+
return !!(isNodeOfType(node, 'CallExpression') && node.callee && node.callee.type === 'Identifier' && (node.callee.name === 'css' || node.callee.name === 'xcss') && node.arguments.length && node.arguments[0].type === 'ObjectExpression');
|
|
6
|
+
}
|
|
7
|
+
function findSpreadProperties(node) {
|
|
8
|
+
// @ts-ignore
|
|
9
|
+
return node.properties.filter(property => property.type === 'SpreadElement' ||
|
|
10
|
+
// @ts-ignore
|
|
11
|
+
property.type === 'ExperimentalSpreadProperty');
|
|
12
|
+
}
|
|
13
|
+
function analyzeIdentifier(context, sourceIdentifier) {
|
|
14
|
+
var _getIdentifierInParen;
|
|
15
|
+
const scope = context.getScope();
|
|
16
|
+
const [identifier] = ((_getIdentifierInParen = getIdentifierInParentScope(scope, sourceIdentifier.name)) === null || _getIdentifierInParen === void 0 ? void 0 : _getIdentifierInParen.identifiers) || [];
|
|
17
|
+
if (!identifier || !identifier.parent) {
|
|
18
|
+
// Identifier isn't in the module, skip!
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
if (identifier.parent.type !== 'VariableDeclarator') {
|
|
22
|
+
// When variable is not in the file or coming from import
|
|
23
|
+
context.report({
|
|
24
|
+
node: sourceIdentifier,
|
|
25
|
+
messageId: 'cssInModule'
|
|
26
|
+
});
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
if (identifier.parent.parent.parent.type !== 'Program') {
|
|
30
|
+
// When variable is declared inside the component
|
|
31
|
+
context.report({
|
|
32
|
+
node: sourceIdentifier,
|
|
33
|
+
messageId: 'cssOnTopOfModule'
|
|
34
|
+
});
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
if (identifier.parent && identifier.parent.init && !isCssCallExpression(identifier.parent.init)) {
|
|
38
|
+
// When variable value is not of type css({})
|
|
39
|
+
context.report({
|
|
40
|
+
node: identifier,
|
|
41
|
+
messageId: 'cssObjectTypeOnly'
|
|
42
|
+
});
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
const spreadProperties = isNodeOfType(identifier.parent.init, 'CallExpression') && findSpreadProperties(identifier.parent.init.arguments[0]);
|
|
46
|
+
if (spreadProperties) {
|
|
47
|
+
// TODO: Recursively handle spread items in children properties.
|
|
48
|
+
spreadProperties.forEach(prop => {
|
|
49
|
+
context.report({
|
|
50
|
+
node: prop,
|
|
51
|
+
messageId: 'cssArrayStylesOnly'
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
function traverseExpression(context, expression) {
|
|
57
|
+
switch (expression.type) {
|
|
58
|
+
case 'Identifier':
|
|
59
|
+
// {styles}
|
|
60
|
+
// We've found an identifier - time to analyze it!
|
|
61
|
+
analyzeIdentifier(context, expression);
|
|
62
|
+
break;
|
|
63
|
+
case 'ArrayExpression':
|
|
64
|
+
// {[styles, moreStyles]}
|
|
65
|
+
// We've found an array expression - let's traverse again over each element individually.
|
|
66
|
+
expression.elements.forEach(element => traverseExpression(context, element));
|
|
67
|
+
break;
|
|
68
|
+
case 'LogicalExpression':
|
|
69
|
+
// {isEnabled && styles}
|
|
70
|
+
// We've found a logical expression - we're only interested in the right expression so
|
|
71
|
+
// let's traverse that and see what it is!
|
|
72
|
+
traverseExpression(context, expression.right);
|
|
73
|
+
break;
|
|
74
|
+
case 'ConditionalExpression':
|
|
75
|
+
// {isEnabled ? styles : null}
|
|
76
|
+
// We've found a conditional expression - we're only interested in the consequent and
|
|
77
|
+
// alternate (styles : null)
|
|
78
|
+
traverseExpression(context, expression.consequent);
|
|
79
|
+
traverseExpression(context, expression.alternate);
|
|
80
|
+
break;
|
|
81
|
+
case 'CallExpression':
|
|
82
|
+
case 'ObjectExpression':
|
|
83
|
+
case 'TaggedTemplateExpression':
|
|
84
|
+
case 'TemplateLiteral':
|
|
85
|
+
// We've found elements that shouldn't be here! Report an error.
|
|
86
|
+
context.report({
|
|
87
|
+
node: expression,
|
|
88
|
+
messageId: 'cssOnTopOfModule'
|
|
89
|
+
});
|
|
90
|
+
break;
|
|
91
|
+
default:
|
|
92
|
+
// Do nothing!
|
|
93
|
+
break;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
const rule = createLintRule({
|
|
97
|
+
meta: {
|
|
98
|
+
name: 'consistent-css-prop-usage',
|
|
99
|
+
docs: {
|
|
100
|
+
description: 'Ensures consistency with CSS and xCSS prop usages',
|
|
101
|
+
url: 'https://developer.atlassian.com/cloud/framework/atlassian-frontend/development/styling',
|
|
102
|
+
recommended: true,
|
|
103
|
+
severity: 'error'
|
|
104
|
+
},
|
|
105
|
+
fixable: 'code',
|
|
106
|
+
messages: {
|
|
107
|
+
cssOnTopOfModule: `Create styles at the top of the module scope using the css function.`,
|
|
108
|
+
cssObjectTypeOnly: `Create styles using objects passed to the css function.`,
|
|
109
|
+
cssInModule: `Imported styles should not be used, instead define in the module, import a component, or use a design token.`,
|
|
110
|
+
cssArrayStylesOnly: `Compose styles with an array on the CSS prop instead of using object spread.`,
|
|
111
|
+
shouldEndInStyles: 'Declared styles should end in "styles".'
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
create(context) {
|
|
115
|
+
return {
|
|
116
|
+
'CallExpression[callee.name=css], CallExpression[callee.name=xcss]': node => {
|
|
117
|
+
if (node.parent.type !== 'VariableDeclarator') {
|
|
118
|
+
// We aren't interested in these that don't have a parent.
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
const identifier = node.parent.id;
|
|
122
|
+
if (identifier.type === 'Identifier' && identifier.name.endsWith(declarationSuffix)) {
|
|
123
|
+
// Already prefixed! Nothing to do.
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
context.report({
|
|
127
|
+
node: identifier,
|
|
128
|
+
messageId: 'shouldEndInStyles',
|
|
129
|
+
fix: fixer => {
|
|
130
|
+
var _context$getScope$var;
|
|
131
|
+
const identifierName = identifier.type === 'Identifier' ? identifier.name : '';
|
|
132
|
+
const references = ((_context$getScope$var = context.getScope().variables.find(varb => varb.name === identifierName)) === null || _context$getScope$var === void 0 ? void 0 : _context$getScope$var.references) || [];
|
|
133
|
+
const newIdentifierName = `${identifierName
|
|
134
|
+
// Remove "Style" if it is already defined.
|
|
135
|
+
.replace(/Style$/, '')}${declarationSuffix}`;
|
|
136
|
+
return references.filter(ref => ref.identifier.name === identifierName).map(ref => {
|
|
137
|
+
if (ref.identifier.parent && ref.identifier.parent.shorthand) {
|
|
138
|
+
return fixer.replaceText(ref.identifier, `${identifierName}: ${newIdentifierName}`);
|
|
139
|
+
}
|
|
140
|
+
return fixer.replaceText(ref.identifier, newIdentifierName);
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
},
|
|
145
|
+
JSXAttribute(node) {
|
|
146
|
+
const {
|
|
147
|
+
name,
|
|
148
|
+
value
|
|
149
|
+
} = node;
|
|
150
|
+
if (name.type === 'JSXIdentifier' && (name.name === 'css' || name.name === 'xcss')) {
|
|
151
|
+
// When not a jsx expression. For eg. css=""
|
|
152
|
+
if ((value === null || value === void 0 ? void 0 : value.type) !== 'JSXExpressionContainer') {
|
|
153
|
+
context.report({
|
|
154
|
+
node: value,
|
|
155
|
+
messageId: 'cssOnTopOfModule'
|
|
156
|
+
});
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
traverseExpression(context, value.expression);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
export default rule;
|
|
@@ -48,7 +48,7 @@ export const getValueFromShorthand = str => {
|
|
|
48
48
|
// If we want to filter out NaN just add .filter(Boolean)
|
|
49
49
|
return splitShorthandValues(String(str).trim()).map(removePixelSuffix);
|
|
50
50
|
};
|
|
51
|
-
const isGridSize = node => isNodeOfType(node, 'CallExpression') && isNodeOfType(node.callee, 'Identifier') && (node.callee.name === 'gridSize' || node.callee.name === 'getGridSize');
|
|
51
|
+
const isGridSize = node => isNodeOfType(node, 'CallExpression') && isNodeOfType(node.callee, 'Identifier') && (node.callee.name === 'gridSize' || node.callee.name === 'getGridSize') && node.arguments.length === 0;
|
|
52
52
|
const isToken = node => isNodeOfType(node, 'CallExpression') && isNodeOfType(node.callee, 'Identifier') && node.callee.name === 'token';
|
|
53
53
|
const getRawExpressionForToken = (node, context) => {
|
|
54
54
|
const args = node.arguments;
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* THIS FILE WAS CREATED VIA CODEGEN DO NOT MODIFY {@see http://go/af-codegen}
|
|
3
|
-
* @codegen <<SignedSource::
|
|
3
|
+
* @codegen <<SignedSource::a966a7ed0f0346cea4b9a41131e25f2e>>
|
|
4
4
|
* @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
|
|
5
5
|
*/
|
|
6
|
+
import consistentCssPropUsage from './consistent-css-prop-usage';
|
|
6
7
|
import ensureDesignTokenUsage from './ensure-design-token-usage';
|
|
7
8
|
import ensureDesignTokenUsageSpacing from './ensure-design-token-usage-spacing';
|
|
8
9
|
import iconLabel from './icon-label';
|
|
@@ -15,6 +16,7 @@ import noUnsafeDesignTokenUsage from './no-unsafe-design-token-usage';
|
|
|
15
16
|
import usePrimitives from './use-primitives';
|
|
16
17
|
import useVisuallyHidden from './use-visually-hidden';
|
|
17
18
|
export default {
|
|
19
|
+
'consistent-css-prop-usage': consistentCssPropUsage,
|
|
18
20
|
'ensure-design-token-usage': ensureDesignTokenUsage,
|
|
19
21
|
'ensure-design-token-usage-spacing': ensureDesignTokenUsageSpacing,
|
|
20
22
|
'icon-label': iconLabel,
|
package/dist/es2019/version.json
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* THIS FILE WAS CREATED VIA CODEGEN DO NOT MODIFY {@see http://go/af-codegen}
|
|
3
|
-
* @codegen <<SignedSource::
|
|
3
|
+
* @codegen <<SignedSource::42b6b42f38f30d690b6a38a2b6369b70>>
|
|
4
4
|
* @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
|
|
5
5
|
*/
|
|
6
6
|
export default {
|
|
7
7
|
plugins: ['@atlaskit/design-system'],
|
|
8
8
|
rules: {
|
|
9
|
+
'@atlaskit/design-system/consistent-css-prop-usage': 'error',
|
|
9
10
|
'@atlaskit/design-system/ensure-design-token-usage': 'error',
|
|
10
11
|
'@atlaskit/design-system/ensure-design-token-usage-spacing': 'error',
|
|
11
12
|
'@atlaskit/design-system/icon-label': 'warn',
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* THIS FILE WAS CREATED VIA CODEGEN DO NOT MODIFY {@see http://go/af-codegen}
|
|
3
|
-
* @codegen <<SignedSource::
|
|
3
|
+
* @codegen <<SignedSource::5b7e085532640c14193d07c63e0f5442>>
|
|
4
4
|
* @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
|
|
5
5
|
*/
|
|
6
6
|
export default {
|
|
7
7
|
plugins: ['@atlaskit/design-system'],
|
|
8
8
|
rules: {
|
|
9
|
+
'@atlaskit/design-system/consistent-css-prop-usage': 'error',
|
|
9
10
|
'@atlaskit/design-system/ensure-design-token-usage': 'error',
|
|
10
11
|
'@atlaskit/design-system/icon-label': 'warn',
|
|
11
12
|
'@atlaskit/design-system/no-banned-imports': 'error',
|