@atlaskit/eslint-plugin-design-system 4.0.0 → 4.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +31 -0
- package/dist/cjs/index.js +14 -3
- package/dist/cjs/rules/ensure-design-token-usage/index.js +3 -0
- package/dist/cjs/rules/no-deprecated-apis/index.js +91 -0
- package/dist/cjs/rules/no-deprecated-design-token-usage/index.js +76 -0
- package/dist/cjs/rules/no-deprecated-imports/index.js +45 -33
- package/dist/cjs/rules/no-deprecated-imports/paths.js +43 -15
- package/dist/cjs/rules/no-unsafe-design-token-usage/index.js +19 -5
- package/dist/cjs/rules/use-visually-hidden/constants.js +12 -0
- package/dist/cjs/rules/use-visually-hidden/fix-jsx.js +39 -0
- package/dist/cjs/rules/use-visually-hidden/fix-vanilla.js +35 -0
- package/dist/cjs/rules/use-visually-hidden/index.js +194 -0
- package/dist/cjs/rules/use-visually-hidden/utils.js +91 -0
- package/dist/cjs/rules/utils/get-import-node-by-source.js +23 -0
- package/dist/cjs/rules/utils/is-color.js +3 -2
- package/dist/cjs/rules/utils/is-node.js +33 -3
- package/dist/cjs/rules/utils/remove-named-import.js +29 -0
- package/dist/cjs/version.json +1 -1
- package/dist/es2019/index.js +10 -2
- package/dist/es2019/rules/ensure-design-token-usage/index.js +3 -0
- package/dist/es2019/rules/no-deprecated-apis/index.js +72 -0
- package/dist/es2019/rules/no-deprecated-design-token-usage/index.js +58 -0
- package/dist/es2019/rules/no-deprecated-imports/index.js +45 -32
- package/dist/es2019/rules/no-deprecated-imports/paths.js +41 -14
- package/dist/es2019/rules/no-unsafe-design-token-usage/index.js +12 -5
- package/dist/es2019/rules/use-visually-hidden/constants.js +3 -0
- package/dist/es2019/rules/use-visually-hidden/fix-jsx.js +24 -0
- package/dist/es2019/rules/use-visually-hidden/fix-vanilla.js +21 -0
- package/dist/es2019/rules/use-visually-hidden/index.js +169 -0
- package/dist/es2019/rules/use-visually-hidden/utils.js +62 -0
- package/dist/es2019/rules/utils/get-import-node-by-source.js +10 -0
- package/dist/es2019/rules/utils/is-color.js +2 -1
- package/dist/es2019/rules/utils/is-node.js +19 -2
- package/dist/es2019/rules/utils/remove-named-import.js +16 -0
- package/dist/es2019/version.json +1 -1
- package/dist/esm/index.js +10 -2
- package/dist/esm/rules/ensure-design-token-usage/index.js +3 -0
- package/dist/esm/rules/no-deprecated-apis/index.js +81 -0
- package/dist/esm/rules/no-deprecated-design-token-usage/index.js +66 -0
- package/dist/esm/rules/no-deprecated-imports/index.js +45 -32
- package/dist/esm/rules/no-deprecated-imports/paths.js +41 -14
- package/dist/esm/rules/no-unsafe-design-token-usage/index.js +20 -5
- package/dist/esm/rules/use-visually-hidden/constants.js +3 -0
- package/dist/esm/rules/use-visually-hidden/fix-jsx.js +26 -0
- package/dist/esm/rules/use-visually-hidden/fix-vanilla.js +23 -0
- package/dist/esm/rules/use-visually-hidden/index.js +180 -0
- package/dist/esm/rules/use-visually-hidden/utils.js +74 -0
- package/dist/esm/rules/utils/get-import-node-by-source.js +14 -0
- package/dist/esm/rules/utils/is-color.js +2 -1
- package/dist/esm/rules/utils/is-node.js +22 -1
- package/dist/esm/rules/utils/remove-named-import.js +20 -0
- package/dist/esm/version.json +1 -1
- package/dist/types/index.d.ts +5 -0
- package/dist/types/rules/no-deprecated-apis/index.d.ts +3 -0
- package/dist/types/rules/no-deprecated-design-token-usage/index.d.ts +3 -0
- package/dist/types/rules/no-deprecated-imports/paths.d.ts +33 -14
- package/dist/types/rules/use-visually-hidden/constants.d.ts +3 -0
- package/dist/types/rules/use-visually-hidden/fix-jsx.d.ts +3 -0
- package/dist/types/rules/use-visually-hidden/fix-vanilla.d.ts +3 -0
- package/dist/types/rules/use-visually-hidden/index.d.ts +3 -0
- package/dist/types/rules/use-visually-hidden/utils.d.ts +35 -0
- package/dist/types/rules/utils/get-import-node-by-source.d.ts +8 -0
- package/dist/types/rules/utils/is-node.d.ts +7 -0
- package/dist/types/rules/utils/remove-named-import.d.ts +8 -0
- package/package.json +6 -2
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
// eslint-disable-next-line import/no-unresolved
|
|
2
|
+
import { getImportedNodeBySource } from '../utils/get-import-node-by-source';
|
|
3
|
+
import { getClosestNodeOfType } from '../utils/is-node';
|
|
4
|
+
var unsafeOverridesConfig = {
|
|
5
|
+
cssFn: ['@atlaskit/menu', '@atlaskit/side-navigation'],
|
|
6
|
+
overrides: ['@atlaskit/drawer', '@atlaskit/menu', '@atlaskit/side-navigation']
|
|
7
|
+
};
|
|
8
|
+
var unsafeOverrides = ['cssFn', 'overrides'];
|
|
9
|
+
var rule = {
|
|
10
|
+
meta: {
|
|
11
|
+
type: 'suggestion',
|
|
12
|
+
docs: {
|
|
13
|
+
description: 'Disallow specified APIs that have been marked as deprecated and/or discouraged.',
|
|
14
|
+
recommended: true
|
|
15
|
+
},
|
|
16
|
+
messages: {
|
|
17
|
+
noDeprecatedApis: "The prop \"{{propName}}\" has been deprecated. It will be removed in the next major release."
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
create: function create(context) {
|
|
21
|
+
return {
|
|
22
|
+
// find JSX atribute - find name of attribute - get source and find relevant identifiers.
|
|
23
|
+
JSXAttribute: function (_JSXAttribute) {
|
|
24
|
+
function JSXAttribute(_x) {
|
|
25
|
+
return _JSXAttribute.apply(this, arguments);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
JSXAttribute.toString = function () {
|
|
29
|
+
return _JSXAttribute.toString();
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
return JSXAttribute;
|
|
33
|
+
}(function (node) {
|
|
34
|
+
var _node$name, _closesetJSXElement$n;
|
|
35
|
+
|
|
36
|
+
if (!unsafeOverrides.includes(node === null || node === void 0 ? void 0 : (_node$name = node.name) === null || _node$name === void 0 ? void 0 : _node$name.name)) {
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
var source = context.getSourceCode();
|
|
41
|
+
var bannedApi = node.name.name; // traverse the tree to the nearest JSX Element and get its name
|
|
42
|
+
|
|
43
|
+
var closesetJSXElement = getClosestNodeOfType(node, 'JSXOpeningElement'); // @ts-ignore
|
|
44
|
+
|
|
45
|
+
var jsxElementName = closesetJSXElement === null || closesetJSXElement === void 0 ? void 0 : (_closesetJSXElement$n = closesetJSXElement.name) === null || _closesetJSXElement$n === void 0 ? void 0 : _closesetJSXElement$n.name;
|
|
46
|
+
|
|
47
|
+
if (!jsxElementName) {
|
|
48
|
+
return;
|
|
49
|
+
} // find an import for the path of the banned api
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
unsafeOverridesConfig[bannedApi].forEach(function (path) {
|
|
53
|
+
var importNode = getImportedNodeBySource(source, path);
|
|
54
|
+
|
|
55
|
+
if (!importNode) {
|
|
56
|
+
return;
|
|
57
|
+
} // find an import that matches our JSX element
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
var hasTargetNode = importNode.specifiers.some(function (node) {
|
|
61
|
+
return node.local.name === jsxElementName;
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
if (!hasTargetNode) {
|
|
65
|
+
return;
|
|
66
|
+
} // if we're here the import exists and there is a valid lint error.
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
context.report({
|
|
70
|
+
node: node,
|
|
71
|
+
messageId: 'noDeprecatedApis',
|
|
72
|
+
data: {
|
|
73
|
+
propName: bannedApi
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
})
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
export default rule;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import renameMapping from '@atlaskit/tokens/rename-mapping';
|
|
2
|
+
|
|
3
|
+
var getCleanPathId = function getCleanPathId(path) {
|
|
4
|
+
return path.split('.').filter(function (el) {
|
|
5
|
+
return el !== '[default]';
|
|
6
|
+
}).join('.');
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
var rule = {
|
|
10
|
+
meta: {
|
|
11
|
+
docs: {
|
|
12
|
+
recommended: true
|
|
13
|
+
},
|
|
14
|
+
fixable: 'code',
|
|
15
|
+
type: 'problem',
|
|
16
|
+
messages: {
|
|
17
|
+
tokenRenamed: 'The token "{{name}}" is deprecated in favour of "{{replacement}}".'
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
create: function create(context) {
|
|
21
|
+
return {
|
|
22
|
+
'CallExpression[callee.name="token"]': function CallExpressionCalleeNameToken(node) {
|
|
23
|
+
if (node.type !== 'CallExpression') {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (node.arguments[0].type !== 'Literal') {
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
var tokenKey = node.arguments[0].value;
|
|
32
|
+
|
|
33
|
+
if (!tokenKey) {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (typeof tokenKey !== 'string') {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
var migrationMeta = renameMapping.filter(function (t) {
|
|
42
|
+
return t.state === 'deprecated';
|
|
43
|
+
}).find(function (t) {
|
|
44
|
+
return t.path === tokenKey;
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
if (migrationMeta) {
|
|
48
|
+
var cleanTokenKey = getCleanPathId(migrationMeta.replacement);
|
|
49
|
+
context.report({
|
|
50
|
+
messageId: 'tokenRenamed',
|
|
51
|
+
node: node,
|
|
52
|
+
data: {
|
|
53
|
+
name: tokenKey,
|
|
54
|
+
replacement: cleanTokenKey
|
|
55
|
+
},
|
|
56
|
+
fix: function fix(fixer) {
|
|
57
|
+
return fixer.replaceText(node.arguments[0], "'".concat(cleanTokenKey, "'"));
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
export default rule;
|
|
@@ -49,17 +49,24 @@ var rule = {
|
|
|
49
49
|
}
|
|
50
50
|
},
|
|
51
51
|
create: function create(context) {
|
|
52
|
-
var sourceCode = context.getSourceCode();
|
|
53
52
|
var restrictedPathMessages = restrictedPaths.reduce(function (memo, importSource) {
|
|
54
53
|
if (typeof importSource === 'string') {
|
|
55
54
|
memo[importSource] = {
|
|
56
55
|
message: ''
|
|
57
56
|
};
|
|
58
57
|
} else {
|
|
59
|
-
|
|
60
|
-
|
|
58
|
+
if ('message' in importSource) {
|
|
59
|
+
memo[importSource.path] = {
|
|
60
|
+
message: importSource.message
|
|
61
|
+
};
|
|
62
|
+
}
|
|
61
63
|
|
|
62
|
-
|
|
64
|
+
if ('imports' in importSource) {
|
|
65
|
+
memo[importSource.path] = {
|
|
66
|
+
// @ts-ignore
|
|
67
|
+
imports: importSource.imports
|
|
68
|
+
};
|
|
69
|
+
}
|
|
63
70
|
}
|
|
64
71
|
|
|
65
72
|
return memo;
|
|
@@ -67,26 +74,45 @@ var rule = {
|
|
|
67
74
|
/**
|
|
68
75
|
* Report a restricted path.
|
|
69
76
|
* @param {string} importSource path of the import
|
|
70
|
-
* @param {Map<string,Object[]>} importNames Map of import names that are being imported
|
|
71
77
|
* @param {node} node representing the restricted path reference
|
|
78
|
+
* @param {Map<string,Rule.Node>} importNames Map of import names that are being imported
|
|
72
79
|
* @returns {void}
|
|
73
80
|
* @private
|
|
74
81
|
*/
|
|
75
82
|
|
|
76
|
-
function checkRestrictedPathAndReport(importSource,
|
|
83
|
+
function checkRestrictedPathAndReport(importSource, node, importNames) {
|
|
77
84
|
if (!Object.prototype.hasOwnProperty.call(restrictedPathMessages, importSource)) {
|
|
78
85
|
return;
|
|
79
86
|
}
|
|
80
87
|
|
|
81
|
-
var
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
88
|
+
var config = restrictedPathMessages[importSource]; // The message will only exist if the import is completely banned,
|
|
89
|
+
// eg a deprecated package
|
|
90
|
+
|
|
91
|
+
if ('message' in config) {
|
|
92
|
+
context.report({
|
|
93
|
+
node: node,
|
|
94
|
+
messageId: config.message ? 'pathWithCustomMessage' : 'path',
|
|
95
|
+
data: {
|
|
96
|
+
importSource: importSource,
|
|
97
|
+
customMessage: config.message
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
} // if there are specific named exports that are banned,
|
|
101
|
+
// iterate through and check if they're being imported
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
if ('imports' in config) {
|
|
105
|
+
var _config$imports;
|
|
106
|
+
|
|
107
|
+
(_config$imports = config.imports) === null || _config$imports === void 0 ? void 0 : _config$imports.forEach(function (restrictedImport) {
|
|
108
|
+
if (importNames.has(restrictedImport.importName)) {
|
|
109
|
+
context.report({
|
|
110
|
+
node: importNames.get(restrictedImport.importName),
|
|
111
|
+
message: restrictedImport.message
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
}
|
|
90
116
|
}
|
|
91
117
|
/**
|
|
92
118
|
* Checks a node to see if any problems should be reported.
|
|
@@ -100,12 +126,7 @@ var rule = {
|
|
|
100
126
|
var importSource = node.source.value.trim();
|
|
101
127
|
var importNames = new Map();
|
|
102
128
|
|
|
103
|
-
if (
|
|
104
|
-
var starToken = sourceCode.getFirstToken(node, 1);
|
|
105
|
-
importNames.set('*', [{
|
|
106
|
-
loc: starToken === null || starToken === void 0 ? void 0 : starToken.loc
|
|
107
|
-
}]);
|
|
108
|
-
} else if ('specifiers' in node) {
|
|
129
|
+
if ('specifiers' in node) {
|
|
109
130
|
// @ts-ignore
|
|
110
131
|
var _iterator = _createForOfIteratorHelper(node.specifiers),
|
|
111
132
|
_step;
|
|
@@ -114,9 +135,6 @@ var rule = {
|
|
|
114
135
|
for (_iterator.s(); !(_step = _iterator.n()).done;) {
|
|
115
136
|
var specifier = _step.value;
|
|
116
137
|
var name = void 0;
|
|
117
|
-
var specifierData = {
|
|
118
|
-
loc: specifier.loc
|
|
119
|
-
};
|
|
120
138
|
|
|
121
139
|
if (specifier.type === 'ImportDefaultSpecifier') {
|
|
122
140
|
name = 'default';
|
|
@@ -129,11 +147,7 @@ var rule = {
|
|
|
129
147
|
}
|
|
130
148
|
|
|
131
149
|
if (name) {
|
|
132
|
-
|
|
133
|
-
importNames.get(name).push(specifierData);
|
|
134
|
-
} else {
|
|
135
|
-
importNames.set(name, [specifierData]);
|
|
136
|
-
}
|
|
150
|
+
importNames.set(name, specifier);
|
|
137
151
|
}
|
|
138
152
|
}
|
|
139
153
|
} catch (err) {
|
|
@@ -143,7 +157,7 @@ var rule = {
|
|
|
143
157
|
}
|
|
144
158
|
}
|
|
145
159
|
|
|
146
|
-
checkRestrictedPathAndReport(importSource,
|
|
160
|
+
checkRestrictedPathAndReport(importSource, node, importNames);
|
|
147
161
|
};
|
|
148
162
|
|
|
149
163
|
return {
|
|
@@ -152,8 +166,7 @@ var rule = {
|
|
|
152
166
|
if (node.source) {
|
|
153
167
|
checkNode(node);
|
|
154
168
|
}
|
|
155
|
-
}
|
|
156
|
-
ExportAllDeclaration: checkNode
|
|
169
|
+
}
|
|
157
170
|
};
|
|
158
171
|
}
|
|
159
172
|
};
|
|
@@ -1,43 +1,70 @@
|
|
|
1
|
+
export var namedThemeExports = [{
|
|
2
|
+
importName: 'assistive',
|
|
3
|
+
message: 'The assistive mixin is deprecated. Please use `@atlaskit/visually-hidden` instead.'
|
|
4
|
+
}, {
|
|
5
|
+
importName: 'visuallyHidden',
|
|
6
|
+
message: 'The visuallyHidden mixin is deprecated. Please use `@atlaskit/visually-hidden` instead.'
|
|
7
|
+
}, {
|
|
8
|
+
importName: 'focusRing',
|
|
9
|
+
message: 'The focusRing mixin is deprecated. Please use `@atlaskit/visually-hidden` instead.'
|
|
10
|
+
}];
|
|
1
11
|
export var restrictedPaths = [{
|
|
2
|
-
|
|
12
|
+
path: '@atlaskit/navigation-next',
|
|
3
13
|
message: "navigation-next is deprecated. Please use '@atlaskit/atlassian-navigation' instead."
|
|
4
14
|
}, {
|
|
5
|
-
|
|
15
|
+
path: '@atlaskit/field-base',
|
|
6
16
|
message: "field-base is deprecated. Please use the '@atlaskit/form' package instead."
|
|
7
17
|
}, {
|
|
8
|
-
|
|
18
|
+
path: '@atlaskit/field-radio-group',
|
|
9
19
|
message: "field-radio-group is deprecated. Please use '@atlaskit/radio' instead, and check the migration guide."
|
|
10
20
|
}, {
|
|
11
|
-
|
|
21
|
+
path: '@atlaskit/field-range',
|
|
12
22
|
message: "field-range is deprecated. Please use '@atlaskit/range' instead."
|
|
13
23
|
}, {
|
|
14
|
-
|
|
24
|
+
path: '@atlaskit/field-text',
|
|
15
25
|
message: "field-text is deprecated. Please use '@atlaskit/textfield' instead."
|
|
16
26
|
}, {
|
|
17
|
-
|
|
27
|
+
path: '@atlaskit/field-text-area',
|
|
18
28
|
message: "field-text-area is deprecated. Please use '@atlaskit/textarea' instead."
|
|
19
29
|
}, {
|
|
20
|
-
|
|
30
|
+
path: '@atlaskit/navigation',
|
|
21
31
|
message: "navigation is deprecated. Please use '@atlaskit/atlassian-navigation' instead."
|
|
22
32
|
}, {
|
|
23
|
-
|
|
33
|
+
path: '@atlaskit/global-navigation',
|
|
24
34
|
message: "global-navigation is deprecated. Please use '@atlaskit/atlassian-navigation' for the horizontal nav bar, '@atlaskit/side-navigation' for the side nav, and '@atlaskit/page-layout' to layout your application."
|
|
25
35
|
}, {
|
|
26
|
-
|
|
36
|
+
path: '@atlaskit/input',
|
|
27
37
|
message: 'input is deprecated. This was an internal component and should not be used directly.'
|
|
28
38
|
}, {
|
|
29
|
-
|
|
39
|
+
path: '@atlaskit/layer',
|
|
30
40
|
message: 'layer is deprecated. This was an internal component and should not be used directly.'
|
|
31
41
|
}, {
|
|
32
|
-
|
|
42
|
+
path: '@atlaskit/single-select',
|
|
33
43
|
message: "single-select is deprecated. Please use '@atlaskit/select' instead."
|
|
34
44
|
}, {
|
|
35
|
-
|
|
45
|
+
path: '@atlaskit/multi-select',
|
|
36
46
|
message: "multi-select is deprecated. Please use '@atlaskit/select' instead."
|
|
37
47
|
}, {
|
|
38
|
-
|
|
48
|
+
path: '@atlaskit/droplist',
|
|
39
49
|
message: "droplist is deprecated. For the pop-up behaviour please use '@atlaskit/popup' and for common menu components please use '@atlaskit/menu'."
|
|
40
50
|
}, {
|
|
41
|
-
|
|
51
|
+
path: '@atlaskit/item',
|
|
42
52
|
message: "item is deprecated. Please use '@atlaskit/menu' instead."
|
|
53
|
+
}, // TODO uncomment me when we formally deprecate typography
|
|
54
|
+
// {
|
|
55
|
+
// path: '@atlaskit/theme/typography',
|
|
56
|
+
// message: 'The typography mixins are deprecated. Please use `@atlaskit/heading` instead.',
|
|
57
|
+
// },
|
|
58
|
+
{
|
|
59
|
+
path: '@atlaskit/theme/constants',
|
|
60
|
+
imports: namedThemeExports
|
|
61
|
+
}, {
|
|
62
|
+
path: '@atlaskit/theme',
|
|
63
|
+
imports: namedThemeExports // .concat(
|
|
64
|
+
// { importName: 'typography', message: 'The typography mixins are deprecated. Please use `@atlaskit/heading` instead.',}
|
|
65
|
+
// )
|
|
66
|
+
|
|
67
|
+
}, {
|
|
68
|
+
path: '@atlaskit/icon-priority',
|
|
69
|
+
message: "icon-priority is deprecated due to limited usage in Cloud products. It will be deleted after 21 April 2022."
|
|
43
70
|
}];
|
|
@@ -2,6 +2,13 @@ import renameMapping from '@atlaskit/tokens/rename-mapping';
|
|
|
2
2
|
import tokens from '@atlaskit/tokens/token-names';
|
|
3
3
|
import { isDecendantOfStyleBlock, isDecendantOfStyleJsxAttribute } from '../utils/is-node';
|
|
4
4
|
import { isToken } from '../utils/is-token';
|
|
5
|
+
|
|
6
|
+
var getCleanPathId = function getCleanPathId(path) {
|
|
7
|
+
return path.split('.').filter(function (el) {
|
|
8
|
+
return el !== '[default]';
|
|
9
|
+
}).join('.');
|
|
10
|
+
};
|
|
11
|
+
|
|
5
12
|
var defaultConfig = {
|
|
6
13
|
shouldEnforceFallbacks: false
|
|
7
14
|
};
|
|
@@ -16,7 +23,7 @@ var rule = {
|
|
|
16
23
|
directTokenUsage: "Access the global theme using the token function.\n\n```\nimport { token } from '@atlaskit/tokens';\n\ntoken('{{tokenKey}}');\n```\n",
|
|
17
24
|
staticToken: "Token string should be inlined directly into the function call.\n\n```\ntoken('color.background.blanket');\n```\n",
|
|
18
25
|
invalidToken: 'The token "{{name}}" does not exist.',
|
|
19
|
-
|
|
26
|
+
tokenRemoved: 'The token "{{name}}" is removed in favour of "{{replacement}}".',
|
|
20
27
|
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
28
|
tokenFallbackRestricted: "Token function must not use a fallback.\n\n```\ntoken('color.background.blanket');\n```\n "
|
|
22
29
|
}
|
|
@@ -117,15 +124,23 @@ var rule = {
|
|
|
117
124
|
return;
|
|
118
125
|
}
|
|
119
126
|
|
|
120
|
-
|
|
127
|
+
var migrationMeta = renameMapping.filter(function (t) {
|
|
128
|
+
return t.state === 'deleted';
|
|
129
|
+
}).find(function (t) {
|
|
130
|
+
return t.path === tokenKey;
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
if (typeof tokenKey === 'string' && migrationMeta) {
|
|
134
|
+
var cleanTokenKey = getCleanPathId(migrationMeta.replacement);
|
|
121
135
|
context.report({
|
|
122
|
-
messageId: '
|
|
136
|
+
messageId: 'tokenRemoved',
|
|
123
137
|
node: node,
|
|
124
138
|
data: {
|
|
125
|
-
name: tokenKey
|
|
139
|
+
name: tokenKey,
|
|
140
|
+
replacement: cleanTokenKey
|
|
126
141
|
},
|
|
127
142
|
fix: function fix(fixer) {
|
|
128
|
-
return fixer.replaceText(node.arguments[0], "'".concat(
|
|
143
|
+
return fixer.replaceText(node.arguments[0], "'".concat(cleanTokenKey, "'"));
|
|
129
144
|
}
|
|
130
145
|
});
|
|
131
146
|
return;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { getImportedNodeBySource } from '../utils/get-import-node-by-source';
|
|
2
|
+
import { getClosestNodeOfType } from '../utils/is-node';
|
|
3
|
+
import { IMPORT_NAME, VISUALLY_HIDDEN_IMPORT, VISUALLY_HIDDEN_SOURCE } from './constants';
|
|
4
|
+
import { getFirstImport } from './utils';
|
|
5
|
+
export default (function (source, node) {
|
|
6
|
+
return function (fixer) {
|
|
7
|
+
var fixes = [];
|
|
8
|
+
var importedNode = getFirstImport(source);
|
|
9
|
+
var visuallyHiddenNode = getImportedNodeBySource(source, VISUALLY_HIDDEN_SOURCE);
|
|
10
|
+
|
|
11
|
+
if (!importedNode) {
|
|
12
|
+
return [];
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
var jsxOpeningElement = getClosestNodeOfType(node, 'JSXOpeningElement');
|
|
16
|
+
|
|
17
|
+
if (visuallyHiddenNode) {
|
|
18
|
+
fixes.push(fixer.replaceText(jsxOpeningElement, visuallyHiddenNode.specifiers[0].local.name));
|
|
19
|
+
} else {
|
|
20
|
+
fixes.push(fixer.insertTextBefore(importedNode, VISUALLY_HIDDEN_IMPORT));
|
|
21
|
+
fixes.push(fixer.replaceText(jsxOpeningElement, "<".concat(IMPORT_NAME, " />")));
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return fixes;
|
|
25
|
+
};
|
|
26
|
+
});
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { getImportedNodeBySource } from '../utils/get-import-node-by-source';
|
|
2
|
+
import { IMPORT_NAME, VISUALLY_HIDDEN_IMPORT, VISUALLY_HIDDEN_SOURCE } from './constants';
|
|
3
|
+
import { getFirstImport } from './utils';
|
|
4
|
+
export default (function (source, node) {
|
|
5
|
+
return function (fixer) {
|
|
6
|
+
var fixes = [];
|
|
7
|
+
var importedNode = getFirstImport(source);
|
|
8
|
+
var visuallyHiddenNode = getImportedNodeBySource(source, VISUALLY_HIDDEN_SOURCE);
|
|
9
|
+
|
|
10
|
+
if (!importedNode) {
|
|
11
|
+
return [];
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
if (visuallyHiddenNode) {
|
|
15
|
+
fixes.push(fixer.replaceText(node, visuallyHiddenNode.specifiers[0].local.name));
|
|
16
|
+
} else {
|
|
17
|
+
fixes.push(fixer.insertTextBefore(importedNode, VISUALLY_HIDDEN_IMPORT));
|
|
18
|
+
fixes.push(fixer.replaceText(node, IMPORT_NAME));
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return fixes;
|
|
22
|
+
};
|
|
23
|
+
});
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
|
|
2
|
+
|
|
3
|
+
/* eslint-disable @atlaskit/design-system/use-visually-hidden */
|
|
4
|
+
import { getClosestNodeOfType, isStyledObjectNode, isStyledTemplateNode } from '../utils/is-node';
|
|
5
|
+
import fixJsx from './fix-jsx';
|
|
6
|
+
import fixVanilla from './fix-vanilla';
|
|
7
|
+
import { countMatchingKeyValues, getObjectLikeness, makeTemplateLiteralIntoEntries } from './utils';
|
|
8
|
+
var THEME_IMPORT_NAMES = ['visuallyHidden', 'assistive'];
|
|
9
|
+
var rule = {
|
|
10
|
+
meta: {
|
|
11
|
+
type: 'suggestion',
|
|
12
|
+
fixable: 'code',
|
|
13
|
+
docs: {
|
|
14
|
+
description: 'Suggest usage of the `@atlaskit/visually-hidden` component instead of either the `@atlaskit/theme` mixins or just something the user has rolled themselves.',
|
|
15
|
+
recommended: true
|
|
16
|
+
},
|
|
17
|
+
messages: {
|
|
18
|
+
noDeprecatedUsage: 'Using the export `{{local}}` from `{{import}}` as a mixin is discouraged. Please use `@atlaskit/visually-hidden` instead.',
|
|
19
|
+
noDeprecated: 'The export `{{local}}` from `{{import}}` is deprecated. Please use `@atlaskit/visually-hidden` instead.',
|
|
20
|
+
suggestion: 'This CSS closely matches the implementation of a visually hidden element. You should consider using the `@atlaskit/visually-hidden` component instead.'
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
create: function create(context) {
|
|
24
|
+
var source = context.getSourceCode();
|
|
25
|
+
return {
|
|
26
|
+
ImportDeclaration: function ImportDeclaration(node) {
|
|
27
|
+
var isThemeNode = node.source.value === '@atlaskit/theme' || node.source.value === '@atlaskit/theme/constants';
|
|
28
|
+
|
|
29
|
+
if (!isThemeNode) {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
var visuallyHiddenOrAssistive = node.specifiers.filter(function (specifier) {
|
|
34
|
+
return specifier.type === 'ImportSpecifier';
|
|
35
|
+
}).find(function (specifier) {
|
|
36
|
+
return THEME_IMPORT_NAMES.includes(specifier.imported.name);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
if (!visuallyHiddenOrAssistive) {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
context.getDeclaredVariables(visuallyHiddenOrAssistive).forEach(function (someNode) {
|
|
44
|
+
someNode.references.map(function (innerNode) {
|
|
45
|
+
return innerNode.identifier;
|
|
46
|
+
}).forEach(function (idNode) {
|
|
47
|
+
// @ts-ignore JSX is not typed correctly in eslint
|
|
48
|
+
if ((idNode === null || idNode === void 0 ? void 0 : idNode.parent.type) === 'JSXExpressionContainer') {
|
|
49
|
+
context.report({
|
|
50
|
+
node: idNode.parent,
|
|
51
|
+
messageId: 'noDeprecatedUsage',
|
|
52
|
+
data: {
|
|
53
|
+
import: "".concat(node.source.value),
|
|
54
|
+
local: visuallyHiddenOrAssistive.local.name
|
|
55
|
+
},
|
|
56
|
+
fix: fixJsx(source, idNode)
|
|
57
|
+
}); // this is either a styled usage OR mixin usage in a styled usage
|
|
58
|
+
} else if (idNode.parent.type === 'CallExpression') {
|
|
59
|
+
if (isStyledObjectNode(idNode.parent) || isStyledTemplateNode(idNode.parent)) {
|
|
60
|
+
context.report({
|
|
61
|
+
node: idNode.parent,
|
|
62
|
+
messageId: 'noDeprecatedUsage',
|
|
63
|
+
data: {
|
|
64
|
+
import: "".concat(node.source.value),
|
|
65
|
+
local: visuallyHiddenOrAssistive.local.name
|
|
66
|
+
},
|
|
67
|
+
fix: fixVanilla(source, idNode.parent)
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (idNode.parent.callee === idNode) {
|
|
72
|
+
context.report({
|
|
73
|
+
node: idNode.parent,
|
|
74
|
+
messageId: 'noDeprecatedUsage',
|
|
75
|
+
data: {
|
|
76
|
+
import: "".concat(node.source.value),
|
|
77
|
+
local: visuallyHiddenOrAssistive.local.name
|
|
78
|
+
},
|
|
79
|
+
fix: fixVanilla(source, getClosestNodeOfType(idNode.parent, 'TaggedTemplateExpression'))
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
return context.report({
|
|
86
|
+
node: visuallyHiddenOrAssistive,
|
|
87
|
+
messageId: 'noDeprecated',
|
|
88
|
+
data: {
|
|
89
|
+
import: "".concat(node.source.value),
|
|
90
|
+
local: visuallyHiddenOrAssistive.local.name
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
},
|
|
94
|
+
CallExpression: function CallExpression(node) {
|
|
95
|
+
var _node$arguments$;
|
|
96
|
+
|
|
97
|
+
if (node.type !== 'CallExpression') {
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (!(node.callee.type === 'MemberExpression' || node.callee.type === 'Identifier')) {
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
var isStyled = isStyledObjectNode(node);
|
|
106
|
+
|
|
107
|
+
if (node.callee.type === 'MemberExpression' && node.callee.object.type === 'Identifier' && node.callee.object.name !== 'styled') {
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (node.callee.type === 'Identifier' && node.callee.name !== 'css') {
|
|
112
|
+
return;
|
|
113
|
+
} // This is an object style (probably)
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
if (node.arguments && ((_node$arguments$ = node.arguments[0]) === null || _node$arguments$ === void 0 ? void 0 : _node$arguments$.type) === 'ObjectExpression') {
|
|
117
|
+
var matchingScore = getObjectLikeness(node.arguments[0]);
|
|
118
|
+
|
|
119
|
+
if (matchingScore > 0.8) {
|
|
120
|
+
return context.report({
|
|
121
|
+
node: node.parent,
|
|
122
|
+
messageId: 'suggestion',
|
|
123
|
+
fix: isStyled ? fixVanilla(source, node) : undefined
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return null;
|
|
129
|
+
},
|
|
130
|
+
ObjectExpression: function ObjectExpression(node) {
|
|
131
|
+
if (node.parent.type === 'CallExpression') {
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
var matchingScore = getObjectLikeness(node);
|
|
136
|
+
|
|
137
|
+
if (matchingScore > 0.8) {
|
|
138
|
+
return context.report({
|
|
139
|
+
node: node,
|
|
140
|
+
messageId: 'suggestion'
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
},
|
|
144
|
+
'TaggedTemplateExpression[tag.name="css"],TaggedTemplateExpression[tag.object.name="styled"]': function TaggedTemplateExpressionTagNameCssTaggedTemplateExpressionTagObjectNameStyled(node) {
|
|
145
|
+
if (node.type !== 'TaggedTemplateExpression') {
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
var templateString = node.quasi.quasis.map(function (q) {
|
|
150
|
+
return q.value.raw;
|
|
151
|
+
}).join('');
|
|
152
|
+
var styleEntries = makeTemplateLiteralIntoEntries(templateString);
|
|
153
|
+
|
|
154
|
+
if (!styleEntries) {
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
var count = countMatchingKeyValues(styleEntries.map(function (_ref) {
|
|
159
|
+
var _ref2 = _slicedToArray(_ref, 2),
|
|
160
|
+
key = _ref2[0],
|
|
161
|
+
value = _ref2[1];
|
|
162
|
+
|
|
163
|
+
return {
|
|
164
|
+
key: key,
|
|
165
|
+
value: value
|
|
166
|
+
};
|
|
167
|
+
}));
|
|
168
|
+
|
|
169
|
+
if (count > 0.8) {
|
|
170
|
+
return context.report({
|
|
171
|
+
node: node,
|
|
172
|
+
messageId: 'suggestion',
|
|
173
|
+
fix: node.tag.type !== 'Identifier' ? fixVanilla(source, node) : undefined
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
};
|
|
180
|
+
export default rule;
|