@atlaskit/eslint-plugin-design-system 13.14.2 → 13.16.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 +24 -0
- package/README.md +2 -0
- package/dist/cjs/presets/all-flat.codegen.js +3 -1
- package/dist/cjs/presets/all.codegen.js +3 -1
- package/dist/cjs/presets/recommended-flat.codegen.js +2 -1
- package/dist/cjs/presets/recommended.codegen.js +2 -1
- package/dist/cjs/rules/ensure-proper-xcss-usage/index.js +157 -0
- package/dist/cjs/rules/index.codegen.js +5 -1
- package/dist/cjs/rules/no-legacy-icons/checks.js +22 -5
- package/dist/cjs/rules/no-legacy-icons/helpers.js +122 -46
- package/dist/cjs/rules/no-utility-icons/checks.js +233 -0
- package/dist/cjs/rules/no-utility-icons/index.js +40 -0
- package/dist/cjs/rules/utils/get-deprecated-config.js +1 -1
- package/dist/es2019/presets/all-flat.codegen.js +3 -1
- package/dist/es2019/presets/all.codegen.js +3 -1
- package/dist/es2019/presets/recommended-flat.codegen.js +2 -1
- package/dist/es2019/presets/recommended.codegen.js +2 -1
- package/dist/es2019/rules/ensure-proper-xcss-usage/index.js +149 -0
- package/dist/es2019/rules/index.codegen.js +5 -1
- package/dist/es2019/rules/no-legacy-icons/checks.js +22 -6
- package/dist/es2019/rules/no-legacy-icons/helpers.js +125 -44
- package/dist/es2019/rules/no-utility-icons/checks.js +164 -0
- package/dist/es2019/rules/no-utility-icons/index.js +35 -0
- package/dist/es2019/rules/utils/get-deprecated-config.js +3 -2
- package/dist/esm/presets/all-flat.codegen.js +3 -1
- package/dist/esm/presets/all.codegen.js +3 -1
- package/dist/esm/presets/recommended-flat.codegen.js +2 -1
- package/dist/esm/presets/recommended.codegen.js +2 -1
- package/dist/esm/rules/ensure-proper-xcss-usage/index.js +151 -0
- package/dist/esm/rules/index.codegen.js +5 -1
- package/dist/esm/rules/no-legacy-icons/checks.js +22 -5
- package/dist/esm/rules/no-legacy-icons/helpers.js +121 -45
- package/dist/esm/rules/no-utility-icons/checks.js +226 -0
- package/dist/esm/rules/no-utility-icons/index.js +34 -0
- package/dist/esm/rules/utils/get-deprecated-config.js +2 -2
- package/dist/types/index.codegen.d.ts +14 -0
- package/dist/types/presets/all-flat.codegen.d.ts +2 -0
- package/dist/types/presets/all.codegen.d.ts +2 -0
- package/dist/types/presets/recommended-flat.codegen.d.ts +1 -0
- package/dist/types/presets/recommended.codegen.d.ts +1 -0
- package/dist/types/rules/ensure-proper-xcss-usage/index.d.ts +3 -0
- package/dist/types/rules/index.codegen.d.ts +2 -0
- package/dist/types/rules/no-legacy-icons/helpers.d.ts +12 -4
- package/dist/types/rules/no-utility-icons/checks.d.ts +10 -0
- package/dist/types/rules/no-utility-icons/index.d.ts +3 -0
- package/dist/types/rules/use-tokens-typography/transformers/banned-properties.d.ts +1 -1
- package/dist/types/rules/use-tokens-typography/transformers/font-family.d.ts +1 -1
- package/dist/types/rules/use-tokens-typography/transformers/font-weight.d.ts +1 -1
- package/dist/types/rules/use-tokens-typography/transformers/restricted-capitalisation.d.ts +1 -1
- package/dist/types/rules/use-tokens-typography/transformers/untokenized-properties.d.ts +1 -1
- package/dist/types-ts4.5/index.codegen.d.ts +14 -0
- package/dist/types-ts4.5/presets/all-flat.codegen.d.ts +2 -0
- package/dist/types-ts4.5/presets/all.codegen.d.ts +2 -0
- package/dist/types-ts4.5/presets/recommended-flat.codegen.d.ts +1 -0
- package/dist/types-ts4.5/presets/recommended.codegen.d.ts +1 -0
- package/dist/types-ts4.5/rules/ensure-proper-xcss-usage/index.d.ts +3 -0
- package/dist/types-ts4.5/rules/index.codegen.d.ts +2 -0
- package/dist/types-ts4.5/rules/no-legacy-icons/helpers.d.ts +12 -4
- package/dist/types-ts4.5/rules/no-utility-icons/checks.d.ts +10 -0
- package/dist/types-ts4.5/rules/no-utility-icons/index.d.ts +3 -0
- package/dist/types-ts4.5/rules/use-tokens-typography/transformers/banned-properties.d.ts +1 -1
- package/dist/types-ts4.5/rules/use-tokens-typography/transformers/font-family.d.ts +1 -1
- package/dist/types-ts4.5/rules/use-tokens-typography/transformers/font-weight.d.ts +1 -1
- package/dist/types-ts4.5/rules/use-tokens-typography/transformers/restricted-capitalisation.d.ts +1 -1
- package/dist/types-ts4.5/rules/use-tokens-typography/transformers/untokenized-properties.d.ts +1 -1
- package/package.json +4 -4
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import { isNodeOfType } from 'eslint-codemod-utils';
|
|
2
|
+
const specialCases = {
|
|
3
|
+
'@atlaskit/icon/utility/cross': '@atlaskit/icon/core/close',
|
|
4
|
+
'@atlaskit/icon/utility/migration/cross--editor-close': '@atlaskit/icon/core/migration/close--editor-close'
|
|
5
|
+
};
|
|
6
|
+
const iconPropsinNewButton = ['icon', 'iconBefore', 'iconAfter'];
|
|
7
|
+
export const createChecks = context => {
|
|
8
|
+
const importStatementsUtility = {};
|
|
9
|
+
const importStatementsCore = {};
|
|
10
|
+
const newButtonImports = new Set();
|
|
11
|
+
const errors = {};
|
|
12
|
+
const checkImportDeclarations = node => {
|
|
13
|
+
const moduleSource = node.source.value;
|
|
14
|
+
if (typeof moduleSource !== 'string') {
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
if (moduleSource.startsWith('@atlaskit/icon/utility/')) {
|
|
18
|
+
for (const specifier of node.specifiers) {
|
|
19
|
+
if (specifier.type === 'ImportDefaultSpecifier') {
|
|
20
|
+
importStatementsUtility[specifier.local.name] = node;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
} else if (moduleSource.startsWith('@atlaskit/icon/core/')) {
|
|
24
|
+
for (const specifier of node.specifiers) {
|
|
25
|
+
if (specifier.type === 'ImportDefaultSpecifier') {
|
|
26
|
+
importStatementsCore[moduleSource] = {
|
|
27
|
+
node,
|
|
28
|
+
localName: specifier.local.name
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
} else if (moduleSource.startsWith('@atlaskit/button/')) {
|
|
33
|
+
for (const specifier of node.specifiers) {
|
|
34
|
+
newButtonImports.add(specifier.local.name);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
const checkJSXElement = node => {
|
|
39
|
+
if (!(isNodeOfType(node, 'JSXElement') && isNodeOfType(node.openingElement.name, 'JSXIdentifier') && importStatementsUtility.hasOwnProperty(node.openingElement.name.name) && typeof importStatementsUtility[node.openingElement.name.name].source.value === 'string')) {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
if (!errors.hasOwnProperty(node.openingElement.name.name)) {
|
|
43
|
+
errors[node.openingElement.name.name] = [];
|
|
44
|
+
}
|
|
45
|
+
errors[node.openingElement.name.name].push({
|
|
46
|
+
node,
|
|
47
|
+
messageId: 'noUtilityIconsJSXElement',
|
|
48
|
+
localName: node.openingElement.name.name,
|
|
49
|
+
fixable: true,
|
|
50
|
+
inNewButton: false
|
|
51
|
+
});
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
// Cases: As Props, In Function Calls, In Arrays, In Maps, In Exports
|
|
55
|
+
const checkIconReference = node => {
|
|
56
|
+
//if this is an import statement then exit early
|
|
57
|
+
if (node.parent && (isNodeOfType(node.parent, 'ImportSpecifier') || isNodeOfType(node.parent, 'ImportDefaultSpecifier'))) {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
//check the reference to see if it's a utility icon, if not exit early
|
|
62
|
+
if (!importStatementsUtility.hasOwnProperty(node.name)) {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// if it is in new Button then we can fix it
|
|
67
|
+
if (node.parent && node.parent.parent && node.parent.parent.parent && isNodeOfType(node.parent, 'JSXExpressionContainer') && isNodeOfType(node.parent.parent, 'JSXAttribute') && isNodeOfType(node.parent.parent.name, 'JSXIdentifier') && iconPropsinNewButton.includes(node.parent.parent.name.name) && isNodeOfType(node.parent.parent.parent, 'JSXOpeningElement') && isNodeOfType(node.parent.parent.parent.name, 'JSXIdentifier') && newButtonImports.has(node.parent.parent.parent.name.name)) {
|
|
68
|
+
// if it is in new Button then we can fix it
|
|
69
|
+
if (!errors.hasOwnProperty(node.name)) {
|
|
70
|
+
errors[node.name] = [];
|
|
71
|
+
}
|
|
72
|
+
errors[node.name].push({
|
|
73
|
+
node,
|
|
74
|
+
messageId: 'noUtilityIconsReference',
|
|
75
|
+
localName: node.name,
|
|
76
|
+
fixable: true,
|
|
77
|
+
inNewButton: true
|
|
78
|
+
});
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// manually error
|
|
83
|
+
if (!errors.hasOwnProperty(node.name)) {
|
|
84
|
+
errors[node.name] = [];
|
|
85
|
+
}
|
|
86
|
+
errors[node.name].push({
|
|
87
|
+
node,
|
|
88
|
+
messageId: 'noUtilityIconsReference',
|
|
89
|
+
localName: node.name,
|
|
90
|
+
fixable: false,
|
|
91
|
+
inNewButton: false
|
|
92
|
+
});
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Throws the relevant errors in the correct order.
|
|
97
|
+
*/
|
|
98
|
+
const throwErrors = () => {
|
|
99
|
+
for (const utilityIcon of Object.keys(errors)) {
|
|
100
|
+
const allFixable = errors[utilityIcon].every(x => x.fixable); // Check if ALL errors for a giving import are fixable
|
|
101
|
+
const originalImportNode = importStatementsUtility[utilityIcon];
|
|
102
|
+
const oldImportName = importStatementsUtility[utilityIcon].source.value;
|
|
103
|
+
const newImportName = specialCases.hasOwnProperty(oldImportName) ? specialCases[oldImportName] : oldImportName.replace('@atlaskit/icon/utility/', '@atlaskit/icon/core/');
|
|
104
|
+
const existingImport = importStatementsCore.hasOwnProperty(newImportName) ? importStatementsCore[newImportName].localName : null;
|
|
105
|
+
let replacementImportName = existingImport ? existingImport : allFixable ? utilityIcon : `${utilityIcon}Core`;
|
|
106
|
+
let importFixAdded = false;
|
|
107
|
+
for (const [index, error] of errors[utilityIcon].entries()) {
|
|
108
|
+
if (error.fixable) {
|
|
109
|
+
context.report({
|
|
110
|
+
node: error.node,
|
|
111
|
+
messageId: error.messageId,
|
|
112
|
+
fix: fixer => {
|
|
113
|
+
const fixes = [];
|
|
114
|
+
|
|
115
|
+
// Add a new import statement if it doesn't already exist
|
|
116
|
+
if (!existingImport && !importFixAdded) {
|
|
117
|
+
importFixAdded = true;
|
|
118
|
+
fixes.push(fixer.insertTextBeforeRange([0, 0], `import ${replacementImportName} from '${newImportName}';\n`));
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Handle JSX elements differently if they are in a "new Button"
|
|
122
|
+
if (error.inNewButton) {
|
|
123
|
+
// Replace the icon with a function that wraps it with iconProps and size="small"
|
|
124
|
+
const wrappedIcon = `(iconProps) => <${replacementImportName} label="" {...iconProps} size="small" />`;
|
|
125
|
+
fixes.push(fixer.replaceText(error.node, wrappedIcon));
|
|
126
|
+
} else if (isNodeOfType(error.node, 'JSXElement') && isNodeOfType(error.node.openingElement.name, 'JSXIdentifier')) {
|
|
127
|
+
// Replace the JSX element's closing tag with size="small"
|
|
128
|
+
const newOpeningElementText = context.sourceCode.getText(error.node.openingElement).replace(/\s*\/\s*>$/, ` size="small"\/>`).replace(new RegExp('<\s*' + error.node.openingElement.name.name), `<${replacementImportName}`);
|
|
129
|
+
fixes.push(fixer.replaceText(error.node.openingElement, newOpeningElementText));
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Handle the first fixable error for import removal if all fixable for this import
|
|
133
|
+
if (index === 0 && allFixable) {
|
|
134
|
+
fixes.push(fixer.remove(originalImportNode));
|
|
135
|
+
}
|
|
136
|
+
return fixes;
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
} else {
|
|
140
|
+
// Report non-fixable errors
|
|
141
|
+
context.report({
|
|
142
|
+
node: error.node,
|
|
143
|
+
messageId: error.messageId
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// If other utility icons are imported but there were no errors for them - (this should only be unused imports but good to have as a backup), report them
|
|
150
|
+
const otherUtilityImportsWithoutErrors = Object.keys(importStatementsUtility).filter(utilityIcon => !errors.hasOwnProperty(utilityIcon));
|
|
151
|
+
for (const utilityIcon of otherUtilityImportsWithoutErrors) {
|
|
152
|
+
context.report({
|
|
153
|
+
node: importStatementsUtility[utilityIcon],
|
|
154
|
+
messageId: 'noUtilityIconsImport'
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
};
|
|
158
|
+
return {
|
|
159
|
+
checkImportDeclarations,
|
|
160
|
+
checkJSXElement,
|
|
161
|
+
checkIconReference,
|
|
162
|
+
throwErrors
|
|
163
|
+
};
|
|
164
|
+
};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { createLintRule } from '../utils/create-rule';
|
|
2
|
+
import { createChecks } from './checks';
|
|
3
|
+
const rule = createLintRule({
|
|
4
|
+
meta: {
|
|
5
|
+
name: 'no-utility-icons',
|
|
6
|
+
fixable: 'code',
|
|
7
|
+
hasSuggestions: true,
|
|
8
|
+
type: 'problem',
|
|
9
|
+
docs: {
|
|
10
|
+
description: 'Disallow use of deprecated utility icons, in favor of core icons with `size="small"`.',
|
|
11
|
+
recommended: true,
|
|
12
|
+
severity: 'warn'
|
|
13
|
+
},
|
|
14
|
+
messages: {
|
|
15
|
+
noUtilityIconsJSXElement: `Utility icons are deprecated. Please use core icons instead with the size prop set to small.`,
|
|
16
|
+
noUtilityIconsImport: `Utility icons are deprecated. Please do not import them, use core icons instead.`,
|
|
17
|
+
noUtilityIconsReference: `Utility icons are deprecated. To replace them, please use core icons with the size prop set to small instead.`
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
create(context) {
|
|
21
|
+
const {
|
|
22
|
+
checkImportDeclarations,
|
|
23
|
+
checkJSXElement,
|
|
24
|
+
checkIconReference,
|
|
25
|
+
throwErrors
|
|
26
|
+
} = createChecks(context);
|
|
27
|
+
return {
|
|
28
|
+
ImportDeclaration: checkImportDeclarations,
|
|
29
|
+
JSXElement: checkJSXElement,
|
|
30
|
+
Identifier: checkIconReference,
|
|
31
|
+
'Program:exit': throwErrors
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
export default rule;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
2
|
import path from 'path';
|
|
3
|
-
import { deprecatedCore as deprecatedIconLabCore } from '@atlaskit/icon-lab/deprecated-map';
|
|
3
|
+
import { deprecatedCore as deprecatedIconLabCore, deprecatedUtility as deprecatedIconLabUtility } from '@atlaskit/icon-lab/deprecated-map';
|
|
4
4
|
import { deprecatedCore as deprecatedIconCore, deprecatedUtility as deprecatedIconUtility } from '@atlaskit/icon/deprecated-map';
|
|
5
5
|
export const getConfig = specifier => {
|
|
6
6
|
const configPath = path.resolve(__dirname, '..', '..', '..', 'configs', 'deprecated.json');
|
|
@@ -12,7 +12,8 @@ export const getConfig = specifier => {
|
|
|
12
12
|
...parsedConfig.imports,
|
|
13
13
|
...deprecatedIconCore,
|
|
14
14
|
...deprecatedIconUtility,
|
|
15
|
-
...deprecatedIconLabCore
|
|
15
|
+
...deprecatedIconLabCore,
|
|
16
|
+
...deprecatedIconLabUtility
|
|
16
17
|
}
|
|
17
18
|
};
|
|
18
19
|
return combinedConfig[specifier];
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* THIS FILE WAS CREATED VIA CODEGEN DO NOT MODIFY {@see http://go/af-codegen}
|
|
3
|
-
* @codegen <<SignedSource::
|
|
3
|
+
* @codegen <<SignedSource::5ceb45b21744434ca96a850461a3c574>>
|
|
4
4
|
* @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
|
|
5
5
|
*/
|
|
6
6
|
|
|
@@ -12,6 +12,7 @@ export default {
|
|
|
12
12
|
'@atlaskit/design-system/ensure-design-token-usage': 'error',
|
|
13
13
|
'@atlaskit/design-system/ensure-design-token-usage/preview': 'warn',
|
|
14
14
|
'@atlaskit/design-system/ensure-icon-color': 'error',
|
|
15
|
+
'@atlaskit/design-system/ensure-proper-xcss-usage': 'error',
|
|
15
16
|
'@atlaskit/design-system/icon-label': 'warn',
|
|
16
17
|
'@atlaskit/design-system/no-banned-imports': 'error',
|
|
17
18
|
'@atlaskit/design-system/no-boolean-autofocus-on-modal-dialog': 'warn',
|
|
@@ -47,6 +48,7 @@ export default {
|
|
|
47
48
|
'@atlaskit/design-system/no-unsafe-design-token-usage': 'error',
|
|
48
49
|
'@atlaskit/design-system/no-unsafe-style-overrides': 'warn',
|
|
49
50
|
'@atlaskit/design-system/no-unsupported-drag-and-drop-libraries': 'error',
|
|
51
|
+
'@atlaskit/design-system/no-utility-icons': 'warn',
|
|
50
52
|
'@atlaskit/design-system/prefer-primitives': 'warn',
|
|
51
53
|
'@atlaskit/design-system/use-button-group-label': 'warn',
|
|
52
54
|
'@atlaskit/design-system/use-cx-function-in-xcss': 'error',
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* THIS FILE WAS CREATED VIA CODEGEN DO NOT MODIFY {@see http://go/af-codegen}
|
|
3
|
-
* @codegen <<SignedSource::
|
|
3
|
+
* @codegen <<SignedSource::b96d8bb5115de6e0a230feef152a1365>>
|
|
4
4
|
* @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
|
|
5
5
|
*/
|
|
6
6
|
|
|
@@ -11,6 +11,7 @@ export default {
|
|
|
11
11
|
'@atlaskit/design-system/ensure-design-token-usage': 'error',
|
|
12
12
|
'@atlaskit/design-system/ensure-design-token-usage/preview': 'warn',
|
|
13
13
|
'@atlaskit/design-system/ensure-icon-color': 'error',
|
|
14
|
+
'@atlaskit/design-system/ensure-proper-xcss-usage': 'error',
|
|
14
15
|
'@atlaskit/design-system/icon-label': 'warn',
|
|
15
16
|
'@atlaskit/design-system/no-banned-imports': 'error',
|
|
16
17
|
'@atlaskit/design-system/no-boolean-autofocus-on-modal-dialog': 'warn',
|
|
@@ -46,6 +47,7 @@ export default {
|
|
|
46
47
|
'@atlaskit/design-system/no-unsafe-design-token-usage': 'error',
|
|
47
48
|
'@atlaskit/design-system/no-unsafe-style-overrides': 'warn',
|
|
48
49
|
'@atlaskit/design-system/no-unsupported-drag-and-drop-libraries': 'error',
|
|
50
|
+
'@atlaskit/design-system/no-utility-icons': 'warn',
|
|
49
51
|
'@atlaskit/design-system/prefer-primitives': 'warn',
|
|
50
52
|
'@atlaskit/design-system/use-button-group-label': 'warn',
|
|
51
53
|
'@atlaskit/design-system/use-cx-function-in-xcss': 'error',
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* THIS FILE WAS CREATED VIA CODEGEN DO NOT MODIFY {@see http://go/af-codegen}
|
|
3
|
-
* @codegen <<SignedSource::
|
|
3
|
+
* @codegen <<SignedSource::10f372e16c048db9bfb1bc36cf48421c>>
|
|
4
4
|
* @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
|
|
5
5
|
*/
|
|
6
6
|
|
|
@@ -36,6 +36,7 @@ export default {
|
|
|
36
36
|
'@atlaskit/design-system/no-unsafe-design-token-usage': 'error',
|
|
37
37
|
'@atlaskit/design-system/no-unsafe-style-overrides': 'warn',
|
|
38
38
|
'@atlaskit/design-system/no-unsupported-drag-and-drop-libraries': 'error',
|
|
39
|
+
'@atlaskit/design-system/no-utility-icons': 'warn',
|
|
39
40
|
'@atlaskit/design-system/use-button-group-label': 'warn',
|
|
40
41
|
'@atlaskit/design-system/use-cx-function-in-xcss': 'error',
|
|
41
42
|
'@atlaskit/design-system/use-datetime-picker-calendar-button': 'warn',
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* THIS FILE WAS CREATED VIA CODEGEN DO NOT MODIFY {@see http://go/af-codegen}
|
|
3
|
-
* @codegen <<SignedSource::
|
|
3
|
+
* @codegen <<SignedSource::608f46f9cc8653226152a8edc34849d6>>
|
|
4
4
|
* @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
|
|
5
5
|
*/
|
|
6
6
|
|
|
@@ -35,6 +35,7 @@ export default {
|
|
|
35
35
|
'@atlaskit/design-system/no-unsafe-design-token-usage': 'error',
|
|
36
36
|
'@atlaskit/design-system/no-unsafe-style-overrides': 'warn',
|
|
37
37
|
'@atlaskit/design-system/no-unsupported-drag-and-drop-libraries': 'error',
|
|
38
|
+
'@atlaskit/design-system/no-utility-icons': 'warn',
|
|
38
39
|
'@atlaskit/design-system/use-button-group-label': 'warn',
|
|
39
40
|
'@atlaskit/design-system/use-cx-function-in-xcss': 'error',
|
|
40
41
|
'@atlaskit/design-system/use-datetime-picker-calendar-button': 'warn',
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { isNodeOfType } from 'eslint-codemod-utils';
|
|
2
|
+
import { createLintRule } from '../utils/create-rule';
|
|
3
|
+
import { errorBoundary } from '../utils/error-boundary';
|
|
4
|
+
var rule = createLintRule({
|
|
5
|
+
meta: {
|
|
6
|
+
name: 'ensure-proper-xcss-usage',
|
|
7
|
+
docs: {
|
|
8
|
+
description: 'Enforces proper xcss usage: migrate from xcss() to cssMap() and use cssMap objects with specific keys.',
|
|
9
|
+
recommended: false,
|
|
10
|
+
severity: 'error'
|
|
11
|
+
},
|
|
12
|
+
messages: {
|
|
13
|
+
missingCssMapKey: 'xcss prop should use a specific key from cssMap (e.g., {{identifier}}.root) instead of the entire cssMap object.',
|
|
14
|
+
noXcssWithCompiled: 'Cannot use `xcss()` function with `@atlaskit/primitives/compiled`. Use `cssMap()` from `@atlaskit/css or `@compiled/react` instead.'
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
create: function create(context) {
|
|
18
|
+
var tracker = {
|
|
19
|
+
// Components tracking
|
|
20
|
+
compiledComponents: new Set(),
|
|
21
|
+
// Function tracking
|
|
22
|
+
cssMapFunction: new Set(),
|
|
23
|
+
xcssFunction: new Set(),
|
|
24
|
+
// Variables tracking
|
|
25
|
+
cssMapVariables: new Map(),
|
|
26
|
+
xcssVariables: new Set()
|
|
27
|
+
};
|
|
28
|
+
return errorBoundary({
|
|
29
|
+
// Track all imports in a single handler
|
|
30
|
+
ImportDeclaration: function ImportDeclaration(node) {
|
|
31
|
+
var source = node.source.value;
|
|
32
|
+
node.specifiers.forEach(function (specifier) {
|
|
33
|
+
if (specifier.type !== 'ImportSpecifier') {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Handle different import sources
|
|
38
|
+
switch (source) {
|
|
39
|
+
case '@atlaskit/primitives/compiled':
|
|
40
|
+
tracker.compiledComponents.add(specifier.imported.name);
|
|
41
|
+
break;
|
|
42
|
+
case '@atlaskit/primitives':
|
|
43
|
+
if (specifier.imported.name === 'xcss') {
|
|
44
|
+
tracker.xcssFunction.add(specifier.local.name);
|
|
45
|
+
}
|
|
46
|
+
break;
|
|
47
|
+
case '@atlaskit/css':
|
|
48
|
+
case '@compiled/react':
|
|
49
|
+
if (specifier.imported.name === 'cssMap') {
|
|
50
|
+
tracker.cssMapFunction.add(specifier.local.name);
|
|
51
|
+
}
|
|
52
|
+
break;
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
},
|
|
56
|
+
// Track variable declarations
|
|
57
|
+
VariableDeclarator: function VariableDeclarator(node) {
|
|
58
|
+
if (!node.init || node.init.type !== 'CallExpression' || node.init.callee.type !== 'Identifier' || node.id.type !== 'Identifier') {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
var calleeName = node.init.callee.name;
|
|
62
|
+
var variableName = node.id.name;
|
|
63
|
+
|
|
64
|
+
// Track cssMap variables and extract their keys
|
|
65
|
+
if (tracker.cssMapFunction.has(calleeName)) {
|
|
66
|
+
var keys = new Set();
|
|
67
|
+
|
|
68
|
+
// Extract keys from the cssMap object argument
|
|
69
|
+
if (node.init.arguments.length > 0 && node.init.arguments[0].type === 'ObjectExpression') {
|
|
70
|
+
node.init.arguments[0].properties.forEach(function (prop) {
|
|
71
|
+
if (prop.type === 'Property' && prop.key.type === 'Identifier') {
|
|
72
|
+
keys.add(prop.key.name);
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
tracker.cssMapVariables.set(variableName, keys);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Track xcss variables
|
|
80
|
+
if (tracker.xcssFunction.has(calleeName)) {
|
|
81
|
+
tracker.xcssVariables.add(variableName);
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
// Check JSX elements for xcss prop usage
|
|
85
|
+
JSXElement: function JSXElement(node) {
|
|
86
|
+
var _xcssAttribute$value;
|
|
87
|
+
if (!isNodeOfType(node, 'JSXElement')) {
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
var elementName = node.openingElement.name;
|
|
91
|
+
if (elementName.type !== 'JSXIdentifier') {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
var componentName = elementName.name;
|
|
95
|
+
if (!tracker.compiledComponents.has(componentName)) {
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Find xcss attribute
|
|
100
|
+
var xcssAttribute = node.openingElement.attributes.find(function (attr) {
|
|
101
|
+
return attr.type === 'JSXAttribute' && attr.name.name === 'xcss';
|
|
102
|
+
});
|
|
103
|
+
if ((xcssAttribute === null || xcssAttribute === void 0 || (_xcssAttribute$value = xcssAttribute.value) === null || _xcssAttribute$value === void 0 ? void 0 : _xcssAttribute$value.type) !== 'JSXExpressionContainer') {
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
var expression = xcssAttribute.value.expression;
|
|
107
|
+
|
|
108
|
+
// Check for direct xcss function calls
|
|
109
|
+
if (expression.type === 'CallExpression' && expression.callee.type === 'Identifier' && tracker.xcssFunction.has(expression.callee.name)) {
|
|
110
|
+
context.report({
|
|
111
|
+
node: expression,
|
|
112
|
+
messageId: 'noXcssWithCompiled'
|
|
113
|
+
});
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Check for variables
|
|
118
|
+
if (expression.type === 'Identifier') {
|
|
119
|
+
var identifierName = expression.name;
|
|
120
|
+
if (tracker.xcssVariables.has(identifierName)) {
|
|
121
|
+
context.report({
|
|
122
|
+
node: expression,
|
|
123
|
+
messageId: 'noXcssWithCompiled'
|
|
124
|
+
});
|
|
125
|
+
} else if (tracker.cssMapVariables.has(identifierName)) {
|
|
126
|
+
context.report({
|
|
127
|
+
node: expression,
|
|
128
|
+
messageId: 'missingCssMapKey',
|
|
129
|
+
data: {
|
|
130
|
+
identifier: identifierName
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Check member expressions (e.g., styles.root)
|
|
138
|
+
if (expression.type === 'MemberExpression' && expression.object.type === 'Identifier') {
|
|
139
|
+
var objectName = expression.object.name;
|
|
140
|
+
if (tracker.xcssVariables.has(objectName)) {
|
|
141
|
+
context.report({
|
|
142
|
+
node: expression,
|
|
143
|
+
messageId: 'noXcssWithCompiled'
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
export default rule;
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* THIS FILE WAS CREATED VIA CODEGEN DO NOT MODIFY {@see http://go/af-codegen}
|
|
3
|
-
* @codegen <<SignedSource::
|
|
3
|
+
* @codegen <<SignedSource::3de5d5b44aa01faabdb840bc7fb43d04>>
|
|
4
4
|
* @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
|
|
5
5
|
*/
|
|
6
6
|
import consistentCssPropUsage from './consistent-css-prop-usage';
|
|
7
7
|
import ensureDesignTokenUsage from './ensure-design-token-usage';
|
|
8
8
|
import ensureDesignTokenUsagePreview from './ensure-design-token-usage-preview';
|
|
9
9
|
import ensureIconColor from './ensure-icon-color';
|
|
10
|
+
import ensureProperXcssUsage from './ensure-proper-xcss-usage';
|
|
10
11
|
import iconLabel from './icon-label';
|
|
11
12
|
import noBannedImports from './no-banned-imports';
|
|
12
13
|
import noBooleanAutofocusOnModalDialog from './no-boolean-autofocus-on-modal-dialog';
|
|
@@ -43,6 +44,7 @@ import noStyledTaggedTemplateExpression from './no-styled-tagged-template-expres
|
|
|
43
44
|
import noUnsafeDesignTokenUsage from './no-unsafe-design-token-usage';
|
|
44
45
|
import noUnsafeStyleOverrides from './no-unsafe-style-overrides';
|
|
45
46
|
import noUnsupportedDragAndDropLibraries from './no-unsupported-drag-and-drop-libraries';
|
|
47
|
+
import noUtilityIcons from './no-utility-icons';
|
|
46
48
|
import preferPrimitives from './prefer-primitives';
|
|
47
49
|
import useButtonGroupLabel from './use-button-group-label';
|
|
48
50
|
import useCxFunctionInXcss from './use-cx-function-in-xcss';
|
|
@@ -69,6 +71,7 @@ export var rules = {
|
|
|
69
71
|
'ensure-design-token-usage': ensureDesignTokenUsage,
|
|
70
72
|
'ensure-design-token-usage/preview': ensureDesignTokenUsagePreview,
|
|
71
73
|
'ensure-icon-color': ensureIconColor,
|
|
74
|
+
'ensure-proper-xcss-usage': ensureProperXcssUsage,
|
|
72
75
|
'icon-label': iconLabel,
|
|
73
76
|
'no-banned-imports': noBannedImports,
|
|
74
77
|
'no-boolean-autofocus-on-modal-dialog': noBooleanAutofocusOnModalDialog,
|
|
@@ -105,6 +108,7 @@ export var rules = {
|
|
|
105
108
|
'no-unsafe-design-token-usage': noUnsafeDesignTokenUsage,
|
|
106
109
|
'no-unsafe-style-overrides': noUnsafeStyleOverrides,
|
|
107
110
|
'no-unsupported-drag-and-drop-libraries': noUnsupportedDragAndDropLibraries,
|
|
111
|
+
'no-utility-icons': noUtilityIcons,
|
|
108
112
|
'prefer-primitives': preferPrimitives,
|
|
109
113
|
'use-button-group-label': useButtonGroupLabel,
|
|
110
114
|
'use-cx-function-in-xcss': useCxFunctionInXcss,
|
|
@@ -14,6 +14,8 @@ export var createChecks = function createChecks(context) {
|
|
|
14
14
|
var legacyButtonImports = new Set();
|
|
15
15
|
var errorsManual = {};
|
|
16
16
|
var errorsAuto = {};
|
|
17
|
+
var iconSizesInfo = {}; //Import source key, locations as value
|
|
18
|
+
|
|
17
19
|
var guidance = {};
|
|
18
20
|
|
|
19
21
|
// Extract parameters
|
|
@@ -385,6 +387,7 @@ export var createChecks = function createChecks(context) {
|
|
|
385
387
|
|
|
386
388
|
// Legacy icons rendered as JSX elements
|
|
387
389
|
if (Object.keys(legacyIconImports).includes(name)) {
|
|
390
|
+
var _sizeProp$value2;
|
|
388
391
|
// Determine if inside a new button - if so:
|
|
389
392
|
// - Assume spread props are safe - still error if props explicitly set to unmigratable values
|
|
390
393
|
var insideNewButton = isInsideNewButton(node, newButtonImports);
|
|
@@ -483,7 +486,7 @@ export var createChecks = function createChecks(context) {
|
|
|
483
486
|
var isNewIconMigratable = canAutoMigrateNewIconBasedOnSize(upcomingIcon ? upcomingIcon.sizeGuidance[size !== null && size !== void 0 ? size : 'medium'] : migrationMapObject === null || migrationMapObject === void 0 ? void 0 : migrationMapObject.sizeGuidance[size !== null && size !== void 0 ? size : 'medium']);
|
|
484
487
|
|
|
485
488
|
// Add spacing if:
|
|
486
|
-
// 1. size is medium for core/utility icons or
|
|
489
|
+
// 1. size is medium for core/utility icons or not set (default is medium for core and small for utility icons)
|
|
487
490
|
// 2. not inside a new or legacy button (except for icon-only legacy buttons)
|
|
488
491
|
var sizeProp = node.openingElement.attributes.find(function (attribute) {
|
|
489
492
|
return attribute.type === 'JSXAttribute' && (attribute.name.name === 'size' || attribute.name.name === 'LEGACY_size');
|
|
@@ -494,13 +497,24 @@ export var createChecks = function createChecks(context) {
|
|
|
494
497
|
if (sizeProp && sizeProp.type === 'JSXAttribute' && ((_sizeProp$value = sizeProp.value) === null || _sizeProp$value === void 0 ? void 0 : _sizeProp$value.type) === 'Literal') {
|
|
495
498
|
if (sizeProp.value.value === 'medium') {
|
|
496
499
|
spacing = 'spacious';
|
|
497
|
-
} else if (sizeProp.value.value === 'small' && (newIcon === null || newIcon === void 0 ? void 0 : newIcon.type) === 'utility') {
|
|
498
|
-
spacing = 'compact';
|
|
499
500
|
}
|
|
500
501
|
} else if (!sizeProp) {
|
|
501
502
|
spacing = 'spacious';
|
|
502
503
|
}
|
|
503
504
|
}
|
|
505
|
+
if (!iconSizesInfo[legacyIconImports[name].packageName]) {
|
|
506
|
+
iconSizesInfo[legacyIconImports[name].packageName] = {
|
|
507
|
+
small: [],
|
|
508
|
+
usageCount: 0
|
|
509
|
+
};
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
// Do not automatically migration if size is small as we cannot determine if a core icon or a scaled down utility icon should be used
|
|
513
|
+
if (sizeProp && sizeProp.type === 'JSXAttribute' && ((_sizeProp$value2 = sizeProp.value) === null || _sizeProp$value2 === void 0 ? void 0 : _sizeProp$value2.type) === 'Literal' && sizeProp.value.value === 'small') {
|
|
514
|
+
iconSizesInfo[legacyIconImports[name].packageName].small.push(locToString(node));
|
|
515
|
+
}
|
|
516
|
+
iconSizesInfo[legacyIconImports[name].packageName].usageCount++;
|
|
517
|
+
var shouldForceSmallIcon = newIcon === null || newIcon === void 0 ? void 0 : newIcon.shouldForceSmallIcon;
|
|
504
518
|
if (shouldUseSafeMigrationMode && !hasManualMigration && (newIcon !== null && newIcon !== void 0 && newIcon.isMigrationUnsafe || size !== 'medium' || hasSecondaryColorProp)) {
|
|
505
519
|
createCantFindSuitableReplacementError(node, legacyIconImports[name].packageName, name, errorsManual, upcomingIcon ? true : migrationMapObject ? true : false);
|
|
506
520
|
} else if (!hasManualMigration && (newIcon || upcomingIcon) && isNewIconMigratable) {
|
|
@@ -510,7 +524,8 @@ export var createChecks = function createChecks(context) {
|
|
|
510
524
|
iconName: name,
|
|
511
525
|
errors: errorsAuto,
|
|
512
526
|
spacing: spacing,
|
|
513
|
-
insideNewButton: insideNewButton
|
|
527
|
+
insideNewButton: insideNewButton,
|
|
528
|
+
shouldForceSmallIcon: shouldForceSmallIcon
|
|
514
529
|
});
|
|
515
530
|
} else if ((!newIcon && !upcomingIcon || !isNewIconMigratable) && size) {
|
|
516
531
|
createCantFindSuitableReplacementError(node, legacyIconImports[name].packageName, name, errorsManual, upcomingIcon ? true : migrationMapObject ? true : false);
|
|
@@ -520,7 +535,8 @@ export var createChecks = function createChecks(context) {
|
|
|
520
535
|
iconPackage: legacyIconImports[name].packageName,
|
|
521
536
|
insideNewButton: insideNewButton,
|
|
522
537
|
size: size && isSize(size) ? size : undefined,
|
|
523
|
-
shouldUseMigrationPath: shouldUseMigrationPath
|
|
538
|
+
shouldUseMigrationPath: shouldUseMigrationPath,
|
|
539
|
+
shouldForceSmallIcon: shouldForceSmallIcon
|
|
524
540
|
});
|
|
525
541
|
}
|
|
526
542
|
};
|
|
@@ -571,6 +587,7 @@ export var createChecks = function createChecks(context) {
|
|
|
571
587
|
throwAutoErrors({
|
|
572
588
|
errorsManual: errorsManual,
|
|
573
589
|
errorsAuto: errorsAuto,
|
|
590
|
+
iconSizesInfo: iconSizesInfo,
|
|
574
591
|
legacyIconImports: legacyIconImports,
|
|
575
592
|
guidance: guidance,
|
|
576
593
|
migrationIconImports: migrationIconImports,
|