@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
|
@@ -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,149 @@
|
|
|
1
|
+
import { isNodeOfType } from 'eslint-codemod-utils';
|
|
2
|
+
import { createLintRule } from '../utils/create-rule';
|
|
3
|
+
import { errorBoundary } from '../utils/error-boundary';
|
|
4
|
+
const 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(context) {
|
|
18
|
+
const 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(node) {
|
|
31
|
+
const source = node.source.value;
|
|
32
|
+
node.specifiers.forEach(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(node) {
|
|
58
|
+
if (!node.init || node.init.type !== 'CallExpression' || node.init.callee.type !== 'Identifier' || node.id.type !== 'Identifier') {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
const calleeName = node.init.callee.name;
|
|
62
|
+
const variableName = node.id.name;
|
|
63
|
+
|
|
64
|
+
// Track cssMap variables and extract their keys
|
|
65
|
+
if (tracker.cssMapFunction.has(calleeName)) {
|
|
66
|
+
const 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(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(node) {
|
|
86
|
+
var _xcssAttribute$value;
|
|
87
|
+
if (!isNodeOfType(node, 'JSXElement')) {
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
const elementName = node.openingElement.name;
|
|
91
|
+
if (elementName.type !== 'JSXIdentifier') {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
const componentName = elementName.name;
|
|
95
|
+
if (!tracker.compiledComponents.has(componentName)) {
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Find xcss attribute
|
|
100
|
+
const xcssAttribute = node.openingElement.attributes.find(attr => attr.type === 'JSXAttribute' && attr.name.name === 'xcss');
|
|
101
|
+
if ((xcssAttribute === null || xcssAttribute === void 0 ? void 0 : (_xcssAttribute$value = xcssAttribute.value) === null || _xcssAttribute$value === void 0 ? void 0 : _xcssAttribute$value.type) !== 'JSXExpressionContainer') {
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
const expression = xcssAttribute.value.expression;
|
|
105
|
+
|
|
106
|
+
// Check for direct xcss function calls
|
|
107
|
+
if (expression.type === 'CallExpression' && expression.callee.type === 'Identifier' && tracker.xcssFunction.has(expression.callee.name)) {
|
|
108
|
+
context.report({
|
|
109
|
+
node: expression,
|
|
110
|
+
messageId: 'noXcssWithCompiled'
|
|
111
|
+
});
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Check for variables
|
|
116
|
+
if (expression.type === 'Identifier') {
|
|
117
|
+
const identifierName = expression.name;
|
|
118
|
+
if (tracker.xcssVariables.has(identifierName)) {
|
|
119
|
+
context.report({
|
|
120
|
+
node: expression,
|
|
121
|
+
messageId: 'noXcssWithCompiled'
|
|
122
|
+
});
|
|
123
|
+
} else if (tracker.cssMapVariables.has(identifierName)) {
|
|
124
|
+
context.report({
|
|
125
|
+
node: expression,
|
|
126
|
+
messageId: 'missingCssMapKey',
|
|
127
|
+
data: {
|
|
128
|
+
identifier: identifierName
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Check member expressions (e.g., styles.root)
|
|
136
|
+
if (expression.type === 'MemberExpression' && expression.object.type === 'Identifier') {
|
|
137
|
+
const objectName = expression.object.name;
|
|
138
|
+
if (tracker.xcssVariables.has(objectName)) {
|
|
139
|
+
context.report({
|
|
140
|
+
node: expression,
|
|
141
|
+
messageId: 'noXcssWithCompiled'
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
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 const 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 const 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,
|
|
@@ -12,6 +12,8 @@ export const createChecks = context => {
|
|
|
12
12
|
const legacyButtonImports = new Set();
|
|
13
13
|
const errorsManual = {};
|
|
14
14
|
const errorsAuto = {};
|
|
15
|
+
const iconSizesInfo = {}; //Import source key, locations as value
|
|
16
|
+
|
|
15
17
|
let guidance = {};
|
|
16
18
|
|
|
17
19
|
// Extract parameters
|
|
@@ -316,7 +318,7 @@ export const createChecks = context => {
|
|
|
316
318
|
|
|
317
319
|
// Legacy icons rendered as JSX elements
|
|
318
320
|
if (Object.keys(legacyIconImports).includes(name)) {
|
|
319
|
-
var _size, _size2;
|
|
321
|
+
var _size, _size2, _sizeProp$value2;
|
|
320
322
|
// Determine if inside a new button - if so:
|
|
321
323
|
// - Assume spread props are safe - still error if props explicitly set to unmigratable values
|
|
322
324
|
const insideNewButton = isInsideNewButton(node, newButtonImports);
|
|
@@ -402,7 +404,7 @@ export const createChecks = context => {
|
|
|
402
404
|
const isNewIconMigratable = canAutoMigrateNewIconBasedOnSize(upcomingIcon ? upcomingIcon.sizeGuidance[(_size = size) !== null && _size !== void 0 ? _size : 'medium'] : migrationMapObject === null || migrationMapObject === void 0 ? void 0 : migrationMapObject.sizeGuidance[(_size2 = size) !== null && _size2 !== void 0 ? _size2 : 'medium']);
|
|
403
405
|
|
|
404
406
|
// Add spacing if:
|
|
405
|
-
// 1. size is medium for core/utility icons or
|
|
407
|
+
// 1. size is medium for core/utility icons or not set (default is medium for core and small for utility icons)
|
|
406
408
|
// 2. not inside a new or legacy button (except for icon-only legacy buttons)
|
|
407
409
|
const sizeProp = node.openingElement.attributes.find(attribute => attribute.type === 'JSXAttribute' && (attribute.name.name === 'size' || attribute.name.name === 'LEGACY_size'));
|
|
408
410
|
let spacing;
|
|
@@ -411,13 +413,24 @@ export const createChecks = context => {
|
|
|
411
413
|
if (sizeProp && sizeProp.type === 'JSXAttribute' && ((_sizeProp$value = sizeProp.value) === null || _sizeProp$value === void 0 ? void 0 : _sizeProp$value.type) === 'Literal') {
|
|
412
414
|
if (sizeProp.value.value === 'medium') {
|
|
413
415
|
spacing = 'spacious';
|
|
414
|
-
} else if (sizeProp.value.value === 'small' && (newIcon === null || newIcon === void 0 ? void 0 : newIcon.type) === 'utility') {
|
|
415
|
-
spacing = 'compact';
|
|
416
416
|
}
|
|
417
417
|
} else if (!sizeProp) {
|
|
418
418
|
spacing = 'spacious';
|
|
419
419
|
}
|
|
420
420
|
}
|
|
421
|
+
if (!iconSizesInfo[legacyIconImports[name].packageName]) {
|
|
422
|
+
iconSizesInfo[legacyIconImports[name].packageName] = {
|
|
423
|
+
small: [],
|
|
424
|
+
usageCount: 0
|
|
425
|
+
};
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
// 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
|
|
429
|
+
if (sizeProp && sizeProp.type === 'JSXAttribute' && ((_sizeProp$value2 = sizeProp.value) === null || _sizeProp$value2 === void 0 ? void 0 : _sizeProp$value2.type) === 'Literal' && sizeProp.value.value === 'small') {
|
|
430
|
+
iconSizesInfo[legacyIconImports[name].packageName].small.push(locToString(node));
|
|
431
|
+
}
|
|
432
|
+
iconSizesInfo[legacyIconImports[name].packageName].usageCount++;
|
|
433
|
+
const shouldForceSmallIcon = newIcon === null || newIcon === void 0 ? void 0 : newIcon.shouldForceSmallIcon;
|
|
421
434
|
if (shouldUseSafeMigrationMode && !hasManualMigration && (newIcon !== null && newIcon !== void 0 && newIcon.isMigrationUnsafe || size !== 'medium' || hasSecondaryColorProp)) {
|
|
422
435
|
createCantFindSuitableReplacementError(node, legacyIconImports[name].packageName, name, errorsManual, upcomingIcon ? true : migrationMapObject ? true : false);
|
|
423
436
|
} else if (!hasManualMigration && (newIcon || upcomingIcon) && isNewIconMigratable) {
|
|
@@ -427,7 +440,8 @@ export const createChecks = context => {
|
|
|
427
440
|
iconName: name,
|
|
428
441
|
errors: errorsAuto,
|
|
429
442
|
spacing,
|
|
430
|
-
insideNewButton
|
|
443
|
+
insideNewButton,
|
|
444
|
+
shouldForceSmallIcon
|
|
431
445
|
});
|
|
432
446
|
} else if ((!newIcon && !upcomingIcon || !isNewIconMigratable) && size) {
|
|
433
447
|
createCantFindSuitableReplacementError(node, legacyIconImports[name].packageName, name, errorsManual, upcomingIcon ? true : migrationMapObject ? true : false);
|
|
@@ -437,7 +451,8 @@ export const createChecks = context => {
|
|
|
437
451
|
iconPackage: legacyIconImports[name].packageName,
|
|
438
452
|
insideNewButton,
|
|
439
453
|
size: size && isSize(size) ? size : undefined,
|
|
440
|
-
shouldUseMigrationPath
|
|
454
|
+
shouldUseMigrationPath,
|
|
455
|
+
shouldForceSmallIcon
|
|
441
456
|
});
|
|
442
457
|
}
|
|
443
458
|
};
|
|
@@ -479,6 +494,7 @@ export const createChecks = context => {
|
|
|
479
494
|
throwAutoErrors({
|
|
480
495
|
errorsManual,
|
|
481
496
|
errorsAuto,
|
|
497
|
+
iconSizesInfo,
|
|
482
498
|
legacyIconImports,
|
|
483
499
|
guidance,
|
|
484
500
|
migrationIconImports,
|
|
@@ -46,7 +46,7 @@ const getIconKey = iconPackage => {
|
|
|
46
46
|
* Checks if a new icon can be auto-migrated based on guidance from the migration map
|
|
47
47
|
*/
|
|
48
48
|
export const canAutoMigrateNewIconBasedOnSize = guidance => {
|
|
49
|
-
return ['swap', 'swap-slight-visual-change', 'swap-visual-change'
|
|
49
|
+
return guidance ? ['swap', 'swap-slight-visual-change', 'swap-visual-change'].includes(guidance) : false;
|
|
50
50
|
};
|
|
51
51
|
|
|
52
52
|
/**
|
|
@@ -76,9 +76,11 @@ const getNewIconNameAndImportPath = (iconPackage, shouldUseMigrationPath) => {
|
|
|
76
76
|
export const createGuidance = ({
|
|
77
77
|
iconPackage,
|
|
78
78
|
insideNewButton,
|
|
79
|
-
size,
|
|
80
|
-
shouldUseMigrationPath
|
|
79
|
+
size: initialSize,
|
|
80
|
+
shouldUseMigrationPath,
|
|
81
|
+
shouldForceSmallIcon
|
|
81
82
|
}) => {
|
|
83
|
+
const size = shouldForceSmallIcon ? 'small' : initialSize;
|
|
82
84
|
const migrationMapObject = getMigrationMapObject(iconPackage);
|
|
83
85
|
const upcomingIcon = getUpcomingIcons(iconPackage);
|
|
84
86
|
if (upcomingIcon) {
|
|
@@ -133,8 +135,18 @@ export const createGuidance = ({
|
|
|
133
135
|
}
|
|
134
136
|
if (insideNewButton) {
|
|
135
137
|
guidance += buttonGuidanceStr;
|
|
136
|
-
} else if (size
|
|
137
|
-
guidance += "Setting the spacing
|
|
138
|
+
} else if (size === 'medium') {
|
|
139
|
+
guidance += "Setting the spacing='spacious' will maintain the icon's box dimensions - but consider setting spacing='none' as it allows for easier control of spacing by parent elements.\n";
|
|
140
|
+
} else if (size === 'small') {
|
|
141
|
+
if (initialSize !== 'small' && shouldForceSmallIcon) {
|
|
142
|
+
guidance += "For this icon, it's recommended to use a smaller size using size='small'. Alternatively, for special cases where a larger version is needed size='medium' can be used, but it is generally discouraged for this icon.\n";
|
|
143
|
+
} else if (initialSize === 'small') {
|
|
144
|
+
if (shouldForceSmallIcon) {
|
|
145
|
+
guidance += "Setting spacing='compact' will maintain the icon's box dimensions - but consider setting spacing='none' as it allows for easier control of spacing by parent elements.\n";
|
|
146
|
+
} else {
|
|
147
|
+
guidance += "It's recommended to upscale to a medium icon with no spacing. Alternatively for special cases where smaller icons are required, the original icon size and dimensions can be maintained by using size='small' and spacing='compact'.\n";
|
|
148
|
+
}
|
|
149
|
+
}
|
|
138
150
|
} else if (size) {
|
|
139
151
|
guidance += "In the new icon, please use spacing='none'.\n";
|
|
140
152
|
}
|
|
@@ -258,7 +270,8 @@ export const createAutoMigrationError = ({
|
|
|
258
270
|
iconName,
|
|
259
271
|
errors,
|
|
260
272
|
spacing,
|
|
261
|
-
insideNewButton
|
|
273
|
+
insideNewButton,
|
|
274
|
+
shouldForceSmallIcon
|
|
262
275
|
}) => {
|
|
263
276
|
const myError = {
|
|
264
277
|
node,
|
|
@@ -268,7 +281,8 @@ export const createAutoMigrationError = ({
|
|
|
268
281
|
iconName,
|
|
269
282
|
spacing: spacing !== null && spacing !== void 0 ? spacing : '',
|
|
270
283
|
// value type need to be a string in Rule.ReportDescriptor
|
|
271
|
-
insideNewButton: String(insideNewButton)
|
|
284
|
+
insideNewButton: String(insideNewButton),
|
|
285
|
+
shouldForceSmallIcon: String(shouldForceSmallIcon)
|
|
272
286
|
}
|
|
273
287
|
};
|
|
274
288
|
errors[locToString(node)] = myError;
|
|
@@ -421,7 +435,7 @@ const getNewIconNameForRenaming = (isInManualArray, importSource, importSpecifie
|
|
|
421
435
|
let newIconName;
|
|
422
436
|
if (isInManualArray) {
|
|
423
437
|
newIconName = getNewIconNameAndImportPath(importSource).iconName;
|
|
424
|
-
const keyToName = newIconName ?
|
|
438
|
+
const keyToName = newIconName ? getComponentName(newIconName) : undefined;
|
|
425
439
|
newIconName = keyToName;
|
|
426
440
|
if (newIconName === undefined || importSpecifier === keyToName) {
|
|
427
441
|
newIconName = `${keyToName}New`;
|
|
@@ -429,6 +443,9 @@ const getNewIconNameForRenaming = (isInManualArray, importSource, importSpecifie
|
|
|
429
443
|
}
|
|
430
444
|
return newIconName;
|
|
431
445
|
};
|
|
446
|
+
export const getComponentName = name => {
|
|
447
|
+
return name.split(/\W/).map(part => `${part[0].toUpperCase()}${part.slice(1)}`).join('').concat('Icon');
|
|
448
|
+
};
|
|
432
449
|
|
|
433
450
|
/**
|
|
434
451
|
*
|
|
@@ -499,7 +516,8 @@ const createPropFixes = ({
|
|
|
499
516
|
}) => {
|
|
500
517
|
const fixes = [];
|
|
501
518
|
const {
|
|
502
|
-
spacing
|
|
519
|
+
spacing,
|
|
520
|
+
size
|
|
503
521
|
} = metadata;
|
|
504
522
|
if (shouldUseMigrationPath && !legacyImportNode) {
|
|
505
523
|
return fixes;
|
|
@@ -520,24 +538,31 @@ const createPropFixes = ({
|
|
|
520
538
|
if (primaryColor && primaryColor.type === 'JSXAttribute') {
|
|
521
539
|
fixes.push(fixer.replaceText(primaryColor.name, 'color'));
|
|
522
540
|
}
|
|
523
|
-
|
|
524
|
-
// rename or remove size prop based on shouldUseMigrationPath,
|
|
525
|
-
// add spacing="spacious" if
|
|
526
|
-
// 1. it's in error metadata, which means size is medium
|
|
527
|
-
// 2. no existing spacing prop
|
|
528
|
-
// 3. iconType is "core"
|
|
529
|
-
// 4. icon is not imported from migration entrypoint
|
|
530
541
|
const sizeProp = findProp(attributes, 'size');
|
|
531
542
|
const spacingProp = findProp(attributes, 'spacing');
|
|
532
|
-
if (spacing && !spacingProp && !migrationImportNode) {
|
|
533
|
-
fixes.push(fixer.insertTextAfter(sizeProp || openingElement.name, ` spacing="${spacing}"`));
|
|
534
|
-
}
|
|
535
543
|
if (sizeProp && sizeProp.type === 'JSXAttribute') {
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
544
|
+
if (shouldUseMigrationPath) {
|
|
545
|
+
// Rename existing size prop to LEGACY_size and add new size prop if applicable
|
|
546
|
+
fixes.push(fixer.replaceText(sizeProp.name, 'LEGACY_size'));
|
|
547
|
+
if (size) {
|
|
548
|
+
fixes.push(fixer.insertTextAfter(sizeProp, ` size="${size}"`));
|
|
549
|
+
}
|
|
550
|
+
} else {
|
|
551
|
+
if (size && sizeProp.value) {
|
|
552
|
+
// update size prop with new replacement size
|
|
553
|
+
fixes.push(fixer.replaceText(sizeProp.value, `"${size}"`));
|
|
554
|
+
} else {
|
|
555
|
+
// remove size prop if no new replacement size is specified
|
|
556
|
+
fixes.push(fixer.remove(sizeProp));
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
} else if (size) {
|
|
560
|
+
fixes.push(fixer.insertTextAfter(openingElement.name, ` size="${size}"`));
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
// Add spacing prop if no existing spacing prop and icon is not imported from migration entrypoint
|
|
564
|
+
if (spacing && spacing !== 'none' && !spacingProp && !migrationImportNode) {
|
|
565
|
+
fixes.push(fixer.insertTextAfter(sizeProp || openingElement.name, ` spacing="${spacing}"`));
|
|
541
566
|
}
|
|
542
567
|
|
|
543
568
|
// rename or remove secondaryColor prop based on shouldUseMigrationPath
|
|
@@ -622,6 +647,7 @@ export const throwManualErrors = ({
|
|
|
622
647
|
export const throwAutoErrors = ({
|
|
623
648
|
errorsManual,
|
|
624
649
|
errorsAuto,
|
|
650
|
+
iconSizesInfo,
|
|
625
651
|
legacyIconImports,
|
|
626
652
|
guidance,
|
|
627
653
|
migrationIconImports,
|
|
@@ -667,7 +693,7 @@ export const throwAutoErrors = ({
|
|
|
667
693
|
const appliedErrorsForImport = [];
|
|
668
694
|
// Loop over auto errors for a single import source
|
|
669
695
|
for (const [_, error] of errorList.entries()) {
|
|
670
|
-
var _legacyIconImports$er, _legacyIconImports$er2, _migrationIconImports;
|
|
696
|
+
var _iconSizesInfo$import, _iconSizesInfo$import2, _iconSizesInfo$import3, _legacyIconImports$er, _error$data2, _legacyIconImports$er2, _migrationIconImports;
|
|
671
697
|
const {
|
|
672
698
|
key
|
|
673
699
|
} = error;
|
|
@@ -676,8 +702,20 @@ export const throwAutoErrors = ({
|
|
|
676
702
|
// If that is the case we'll need to provide a suggestion instead of auto-fixing as the suggestion will
|
|
677
703
|
// add another import without removing the old import and this needs to be validated
|
|
678
704
|
const isInManualArray = allManualErrorSources.has(importSource);
|
|
705
|
+
|
|
706
|
+
// Check if the icon has size of small, if so it cannot be automatically migrated. Two suggestions will be provided
|
|
707
|
+
// 1. Use core icon with no spacing
|
|
708
|
+
// 2. Use utility icon with compact spacing
|
|
709
|
+
const isSizeSmall = (_iconSizesInfo$import = iconSizesInfo[importSource]) === null || _iconSizesInfo$import === void 0 ? void 0 : _iconSizesInfo$import.small.includes(key);
|
|
710
|
+
const isMixedSizeUsage = ((_iconSizesInfo$import2 = iconSizesInfo[importSource]) === null || _iconSizesInfo$import2 === void 0 ? void 0 : _iconSizesInfo$import2.small.length) > 0 && ((_iconSizesInfo$import3 = iconSizesInfo[importSource]) === null || _iconSizesInfo$import3 === void 0 ? void 0 : _iconSizesInfo$import3.small.length) < iconSizesInfo[importSource].usageCount;
|
|
711
|
+
|
|
712
|
+
// Icon should be renamed
|
|
713
|
+
// 1. If the icon is in the manual array OR
|
|
714
|
+
// 2. If there is mixed size usages of this icon with size small
|
|
715
|
+
const shouldRenameIcon = isInManualArray || isMixedSizeUsage;
|
|
716
|
+
|
|
679
717
|
// New icon name for renaming if the icon is in the manual array
|
|
680
|
-
const newIconName = getNewIconNameForRenaming(
|
|
718
|
+
const newIconName = getNewIconNameForRenaming(shouldRenameIcon, importSource, errorList[0].data ? (_legacyIconImports$er = legacyIconImports[errorList[0].data.iconName]) === null || _legacyIconImports$er === void 0 ? void 0 : _legacyIconImports$er.importSpecifier : undefined);
|
|
681
719
|
if (!node) {
|
|
682
720
|
continue;
|
|
683
721
|
}
|
|
@@ -685,23 +723,49 @@ export const throwAutoErrors = ({
|
|
|
685
723
|
if (Object.keys(error).includes('data') && error.data) {
|
|
686
724
|
error.data.guidance = guidanceMessage;
|
|
687
725
|
}
|
|
726
|
+
const shouldForceSmallIcon = ((_error$data2 = error.data) === null || _error$data2 === void 0 ? void 0 : _error$data2.shouldForceSmallIcon) === 'true';
|
|
688
727
|
const fixArguments = error.data ? {
|
|
689
|
-
metadata:
|
|
728
|
+
metadata: {
|
|
729
|
+
...error.data,
|
|
730
|
+
spacing: error.data.isInNewButton ? 'none' : error.data.spacing,
|
|
731
|
+
size: shouldForceSmallIcon ? 'small' : error.data.size
|
|
732
|
+
},
|
|
690
733
|
legacyImportNode: (_legacyIconImports$er2 = legacyIconImports[error.data.iconName]) === null || _legacyIconImports$er2 === void 0 ? void 0 : _legacyIconImports$er2.importNode,
|
|
691
734
|
migrationImportNode: (_migrationIconImports = migrationIconImports[error.data.iconName]) === null || _migrationIconImports === void 0 ? void 0 : _migrationIconImports.importNode,
|
|
692
735
|
shouldUseMigrationPath,
|
|
693
|
-
newIconName:
|
|
736
|
+
newIconName: shouldRenameIcon ? newIconName : undefined
|
|
694
737
|
} : null;
|
|
695
738
|
if (!error.data || shouldUseMigrationPath && !checkIfNewIconExist(error) || !fixArguments) {
|
|
696
739
|
continue;
|
|
697
740
|
}
|
|
698
|
-
|
|
699
|
-
|
|
741
|
+
const isInNewButton = fixArguments.metadata.insideNewButton === 'true';
|
|
742
|
+
if (isSizeSmall && !shouldForceSmallIcon) {
|
|
700
743
|
error.suggest = [{
|
|
701
|
-
desc: '
|
|
744
|
+
desc: isInNewButton ? 'Replace with medium core icon (Recommended)' : 'Replace with medium core icon and no spacing (Recommended)',
|
|
702
745
|
fix: fixer => {
|
|
703
746
|
return [...createPropFixes({
|
|
704
747
|
...fixArguments,
|
|
748
|
+
metadata: {
|
|
749
|
+
...fixArguments.metadata,
|
|
750
|
+
spacing: 'none'
|
|
751
|
+
},
|
|
752
|
+
node,
|
|
753
|
+
fixer
|
|
754
|
+
}), ...createImportFix({
|
|
755
|
+
...fixArguments,
|
|
756
|
+
fixer
|
|
757
|
+
})];
|
|
758
|
+
}
|
|
759
|
+
}, {
|
|
760
|
+
desc: isInNewButton ? 'Replace with small core icon' : 'Replace with small core icon and compact spacing',
|
|
761
|
+
fix: fixer => {
|
|
762
|
+
return [...createPropFixes({
|
|
763
|
+
...fixArguments,
|
|
764
|
+
metadata: {
|
|
765
|
+
...fixArguments.metadata,
|
|
766
|
+
spacing: 'compact',
|
|
767
|
+
size: 'small'
|
|
768
|
+
},
|
|
705
769
|
node,
|
|
706
770
|
fixer
|
|
707
771
|
}), ...createImportFix({
|
|
@@ -711,23 +775,40 @@ export const throwAutoErrors = ({
|
|
|
711
775
|
}
|
|
712
776
|
}];
|
|
713
777
|
} else {
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
error.
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
778
|
+
if (isInManualArray) {
|
|
779
|
+
// provide suggestion if there is a manual error for the same import source and thus the legacy import can't be removed
|
|
780
|
+
error.suggest = [{
|
|
781
|
+
desc: 'Rename icon import, import from the new package, and update props.',
|
|
782
|
+
fix: fixer => {
|
|
783
|
+
return [...createPropFixes({
|
|
784
|
+
...fixArguments,
|
|
785
|
+
node,
|
|
786
|
+
fixer
|
|
787
|
+
}), ...createImportFix({
|
|
788
|
+
...fixArguments,
|
|
789
|
+
fixer
|
|
790
|
+
})];
|
|
791
|
+
}
|
|
792
|
+
}];
|
|
793
|
+
} else {
|
|
794
|
+
// Update Guidance message for auto-fixing
|
|
795
|
+
if (error.data) {
|
|
796
|
+
error.data.guidance = error.data.guidance + `\nTo automatically fix this icon, run the auto-fixer attached to the first use of ${importSource} in this file - either manually, or by saving this file.`;
|
|
797
|
+
}
|
|
798
|
+
// There should only be 1 import fix for each import source and thus only add this at the start of the list
|
|
799
|
+
if (autoFixers.length === 0) {
|
|
800
|
+
autoFixers.push(fixer => createImportFix({
|
|
801
|
+
...fixArguments,
|
|
802
|
+
fixer
|
|
803
|
+
}));
|
|
804
|
+
}
|
|
805
|
+
// Push the prop fix regardless
|
|
806
|
+
autoFixers.push(fixer => createPropFixes({
|
|
721
807
|
...fixArguments,
|
|
808
|
+
node,
|
|
722
809
|
fixer
|
|
723
810
|
}));
|
|
724
811
|
}
|
|
725
|
-
// Push the prop fix regardless
|
|
726
|
-
autoFixers.push(fixer => createPropFixes({
|
|
727
|
-
...fixArguments,
|
|
728
|
-
node,
|
|
729
|
-
fixer
|
|
730
|
-
}));
|
|
731
812
|
}
|
|
732
813
|
// Add the error to the appliedErrorsForImport, ready to be thrown later
|
|
733
814
|
appliedErrorsForImport.push(error);
|