@atlaskit/eslint-plugin-design-system 10.8.1 → 10.9.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 +18 -0
- package/constellation/consistent-css-prop-usage/usage.mdx +9 -0
- package/dist/cjs/rules/consistent-css-prop-usage/index.js +19 -2
- package/dist/cjs/rules/ensure-design-token-usage/index.js +1 -1
- package/dist/cjs/rules/no-legacy-icons/checks.js +484 -0
- package/dist/cjs/rules/no-legacy-icons/helpers.js +289 -0
- package/dist/cjs/rules/no-legacy-icons/index.js +111 -131
- package/dist/cjs/rules/no-legacy-icons/migration-map-temp.js +4090 -0
- package/dist/cjs/rules/use-tokens-typography/index.js +1 -1
- package/dist/es2019/rules/consistent-css-prop-usage/index.js +19 -2
- package/dist/es2019/rules/ensure-design-token-usage/index.js +1 -1
- package/dist/es2019/rules/no-legacy-icons/checks.js +388 -0
- package/dist/es2019/rules/no-legacy-icons/helpers.js +275 -0
- package/dist/es2019/rules/no-legacy-icons/index.js +96 -97
- package/dist/es2019/rules/no-legacy-icons/migration-map-temp.js +4082 -0
- package/dist/es2019/rules/use-tokens-typography/index.js +1 -1
- package/dist/esm/rules/consistent-css-prop-usage/index.js +19 -2
- package/dist/esm/rules/ensure-design-token-usage/index.js +1 -1
- package/dist/esm/rules/no-legacy-icons/checks.js +477 -0
- package/dist/esm/rules/no-legacy-icons/helpers.js +279 -0
- package/dist/esm/rules/no-legacy-icons/index.js +111 -131
- package/dist/esm/rules/no-legacy-icons/migration-map-temp.js +4084 -0
- package/dist/esm/rules/use-tokens-typography/index.js +1 -1
- package/dist/types/rules/consistent-css-prop-usage/types.d.ts +1 -0
- package/dist/types/rules/no-legacy-icons/checks.d.ts +13 -0
- package/dist/types/rules/no-legacy-icons/helpers.d.ts +82 -0
- package/dist/types/rules/no-legacy-icons/index.d.ts +2 -1
- package/dist/types/rules/no-legacy-icons/migration-map-temp.d.ts +22 -0
- package/dist/{types-ts4.5/rules/use-tokens-typography → types/rules/utils}/error-boundary.d.ts +5 -1
- package/dist/types-ts4.5/rules/consistent-css-prop-usage/types.d.ts +1 -0
- package/dist/types-ts4.5/rules/no-legacy-icons/checks.d.ts +13 -0
- package/dist/types-ts4.5/rules/no-legacy-icons/helpers.d.ts +82 -0
- package/dist/types-ts4.5/rules/no-legacy-icons/index.d.ts +2 -1
- package/dist/types-ts4.5/rules/no-legacy-icons/migration-map-temp.d.ts +27 -0
- package/dist/types-ts4.5/rules/{ensure-design-token-usage → utils}/error-boundary.d.ts +5 -1
- package/package.json +1 -1
- package/dist/cjs/rules/use-tokens-typography/error-boundary.js +0 -24
- package/dist/es2019/rules/use-tokens-typography/error-boundary.js +0 -19
- package/dist/esm/rules/use-tokens-typography/error-boundary.js +0 -18
- package/dist/types/rules/ensure-design-token-usage/error-boundary.d.ts +0 -11
- package/dist/types/rules/use-tokens-typography/error-boundary.d.ts +0 -11
- /package/dist/cjs/rules/{ensure-design-token-usage → utils}/error-boundary.js +0 -0
- /package/dist/es2019/rules/{ensure-design-token-usage → utils}/error-boundary.js +0 -0
- /package/dist/esm/rules/{ensure-design-token-usage → utils}/error-boundary.js +0 -0
|
@@ -5,8 +5,8 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
5
5
|
});
|
|
6
6
|
exports.default = void 0;
|
|
7
7
|
var _createRule = require("../utils/create-rule");
|
|
8
|
+
var _errorBoundary = require("../utils/error-boundary");
|
|
8
9
|
var _config = require("./config");
|
|
9
|
-
var _errorBoundary = require("./error-boundary");
|
|
10
10
|
var _styleObject = require("./transformers/style-object");
|
|
11
11
|
var create = function create(context) {
|
|
12
12
|
var config = (0, _config.getConfig)(context.options[0]);
|
|
@@ -449,7 +449,8 @@ const defaultConfig = {
|
|
|
449
449
|
cssImportSource: CSS_IN_JS_IMPORTS.compiled,
|
|
450
450
|
xcssImportSource: CSS_IN_JS_IMPORTS.atlaskitPrimitives,
|
|
451
451
|
excludeReactComponents: false,
|
|
452
|
-
autoFix: true
|
|
452
|
+
autoFix: true,
|
|
453
|
+
shouldAlwaysCheckXcss: false
|
|
453
454
|
};
|
|
454
455
|
const rule = createLintRule({
|
|
455
456
|
meta: {
|
|
@@ -492,6 +493,9 @@ const rule = createLintRule({
|
|
|
492
493
|
excludeReactComponents: {
|
|
493
494
|
type: 'boolean'
|
|
494
495
|
},
|
|
496
|
+
shouldAlwaysCheckXcss: {
|
|
497
|
+
type: 'boolean'
|
|
498
|
+
},
|
|
495
499
|
autoFix: {
|
|
496
500
|
type: 'boolean'
|
|
497
501
|
}
|
|
@@ -508,7 +512,20 @@ const rule = createLintRule({
|
|
|
508
512
|
name,
|
|
509
513
|
value
|
|
510
514
|
} = node;
|
|
511
|
-
|
|
515
|
+
|
|
516
|
+
/**
|
|
517
|
+
* We skip linting `xcss` attributes if:
|
|
518
|
+
*
|
|
519
|
+
* - excludeReactComponents === true
|
|
520
|
+
* - shouldAlwaysCheckXcss === false
|
|
521
|
+
*
|
|
522
|
+
* In the future we may want to remove `shouldAlwaysCheckXcss`
|
|
523
|
+
* and just always lint `xcss`, regardless of `excludeReactComponents`
|
|
524
|
+
*/
|
|
525
|
+
if (mergedConfig.excludeReactComponents && name.name === 'xcss' && !mergedConfig.shouldAlwaysCheckXcss) {
|
|
526
|
+
return;
|
|
527
|
+
}
|
|
528
|
+
if (mergedConfig.excludeReactComponents && node.parent.type === 'JSXOpeningElement' && name.name === 'css') {
|
|
512
529
|
// e.g. <item.before />
|
|
513
530
|
if (node.parent.name.type === 'JSXMemberExpression') {
|
|
514
531
|
return;
|
|
@@ -3,10 +3,10 @@
|
|
|
3
3
|
import { isNodeOfType } from 'eslint-codemod-utils';
|
|
4
4
|
import { getImportSources } from '@atlaskit/eslint-utils/is-supported-import';
|
|
5
5
|
import { createLintRule } from '../utils/create-rule';
|
|
6
|
+
import { errorBoundary } from '../utils/error-boundary';
|
|
6
7
|
import { includesHardCodedColor } from '../utils/is-color';
|
|
7
8
|
import { isDecendantOfGlobalToken, isDecendantOfStyleBlock, isDecendantOfType, isDecendantOfXcssBlock } from '../utils/is-node';
|
|
8
9
|
import { lintJSXIdentifierForColor, lintJSXLiteralForColor, lintJSXMemberForColor, lintObjectForColor, lintTemplateIdentifierForColor } from './color';
|
|
9
|
-
import { errorBoundary } from './error-boundary';
|
|
10
10
|
import ruleMeta from './rule-meta';
|
|
11
11
|
import { lintObjectForSpacing } from './spacing';
|
|
12
12
|
import { convertHyphenatedNameToCamelCase, emToPixels, getDomainsForProperty, getFontSizeValueInScope, getPropertyNodeFromParent, getTokenReplacement, getValueForPropertyNode, getValueFromShorthand, getValueFromTemplateLiteralRaw, includesTokenString, insertTokensImport, isAuto, isCalc, isTokenValueString, isValidSpacingValue, isZero, processCssNode, splitShorthandValues } from './utils';
|
|
@@ -0,0 +1,388 @@
|
|
|
1
|
+
import { isNodeOfType } from 'eslint-codemod-utils';
|
|
2
|
+
import { canAutoMigrateNewIconBasedOnSize, canMigrateColor, createAutoMigrationError, createCantFindSuitableReplacementError, createCantMigrateColorError, createCantMigrateFunctionUnknownError, createCantMigrateIdentifierError, createCantMigrateReExportError, createCantMigrateSizeUnknown, createCantMigrateSpreadPropsError, createCantMigrateUnsafeProp, createGuidance, createHelpers, findUNSAFEProp, getIconKey, getMigrationMapObject, locToString } from './helpers';
|
|
3
|
+
import { isSize } from './migration-map-temp';
|
|
4
|
+
export const createChecks = context => {
|
|
5
|
+
//create global variables to be shared by the checks
|
|
6
|
+
const {
|
|
7
|
+
getPrimaryColor,
|
|
8
|
+
getConfigFlag
|
|
9
|
+
} = createHelpers(context);
|
|
10
|
+
const legacyIconImports = {};
|
|
11
|
+
const newButtonImports = new Set();
|
|
12
|
+
const errorsManual = {};
|
|
13
|
+
const errorsAuto = {};
|
|
14
|
+
let guidance = {};
|
|
15
|
+
|
|
16
|
+
// Extract parameters
|
|
17
|
+
const shouldErrorForManualMigration = getConfigFlag('shouldErrorForManualMigration', true);
|
|
18
|
+
const shouldErrorForAutoMigration = getConfigFlag('shouldErrorForAutoMigration', true);
|
|
19
|
+
const isQuietMode = getConfigFlag('quiet', false);
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Adds the legacy Icon and new button imports to the correct global arrays to be used by the other checks
|
|
23
|
+
* @param node The import node found by ESLint
|
|
24
|
+
*/
|
|
25
|
+
const checkImportDeclarations = node => {
|
|
26
|
+
const moduleSource = node.source.value;
|
|
27
|
+
|
|
28
|
+
// Find the imports for legacy icons
|
|
29
|
+
if (moduleSource && typeof moduleSource === 'string' && ['@atlaskit/icon/glyph/', '@atlaskit/icon-object/glyph/'].find(val => moduleSource.startsWith(val)) && node.specifiers.length > 0) {
|
|
30
|
+
for (const spec of node.specifiers) {
|
|
31
|
+
if (spec.local.name) {
|
|
32
|
+
legacyIconImports[spec.local.name] = {
|
|
33
|
+
packageName: moduleSource,
|
|
34
|
+
exported: false
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Find the imports for new button and IconButton
|
|
41
|
+
if (typeof moduleSource === 'string' && moduleSource.startsWith('@atlaskit/button/new') && node.specifiers.length) {
|
|
42
|
+
for (const spec of node.specifiers) {
|
|
43
|
+
if (spec.type === 'ImportDefaultSpecifier') {
|
|
44
|
+
newButtonImports.add(spec.local.name);
|
|
45
|
+
} else if (spec.type === 'ImportSpecifier' && spec.imported.name === 'IconButton') {
|
|
46
|
+
newButtonImports.add(spec.local.name);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Adds the legacy Icon and new button variable reassignments to the correct global arrays to be used by the other checks
|
|
54
|
+
* @param node The variable reassignment node found by ESLint
|
|
55
|
+
*/
|
|
56
|
+
const checkVariableDeclarations = node => {
|
|
57
|
+
if (isNodeOfType(node, 'VariableDeclaration')) {
|
|
58
|
+
const isExported = node.parent && isNodeOfType(node.parent, 'ExportNamedDeclaration');
|
|
59
|
+
for (const decl of node.declarations) {
|
|
60
|
+
if (isNodeOfType(decl, 'VariableDeclarator') && 'init' in decl && 'id' in decl && decl.init && decl.id && 'name' in decl.id && decl.id.name && isNodeOfType(decl.init, 'Identifier')) {
|
|
61
|
+
if (decl.init.name in legacyIconImports) {
|
|
62
|
+
legacyIconImports[decl.id.name] = {
|
|
63
|
+
packageName: legacyIconImports[decl.init.name].packageName,
|
|
64
|
+
exported: legacyIconImports[decl.init.name].exported || isExported
|
|
65
|
+
};
|
|
66
|
+
} else if (newButtonImports.has(decl.init.name)) {
|
|
67
|
+
newButtonImports.add(decl.id.name);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Checks if the default export is a re-export of a legacy icon and stores the errors in the global array
|
|
76
|
+
* @param node The default export node found by ESLint
|
|
77
|
+
*/
|
|
78
|
+
const checkExportDefaultDeclaration = node => {
|
|
79
|
+
let exportName = '';
|
|
80
|
+
let packageName = '';
|
|
81
|
+
if ('declaration' in node && node.declaration && isNodeOfType(node.declaration, 'Identifier') && node.declaration.name in legacyIconImports) {
|
|
82
|
+
packageName = legacyIconImports[node.declaration.name].packageName;
|
|
83
|
+
exportName = 'Default export';
|
|
84
|
+
} else if ('declaration' in node && node.declaration && isNodeOfType(node.declaration, 'AssignmentExpression') && isNodeOfType(node.declaration.left, 'Identifier') && isNodeOfType(node.declaration.right, 'Identifier') && node.declaration.right.name in legacyIconImports) {
|
|
85
|
+
packageName = legacyIconImports[node.declaration.right.name].packageName;
|
|
86
|
+
exportName = node.declaration.left.name;
|
|
87
|
+
} else {
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
createCantMigrateReExportError(node, packageName, exportName, errorsManual);
|
|
91
|
+
guidance[locToString(node)] = createGuidance(packageName);
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Checks if the named exports are re-exports of a legacy icon and stores the errors in the global array
|
|
96
|
+
* @param node The named export node found by ESLint
|
|
97
|
+
*/
|
|
98
|
+
const checkExportNamedVariables = node => {
|
|
99
|
+
// export {default as AddIcon} from '@atlaskit/icon/glyph/add';
|
|
100
|
+
if (node.source && isNodeOfType(node.source, 'Literal') && 'value' in node.source) {
|
|
101
|
+
const moduleSource = node.source.value;
|
|
102
|
+
if (typeof moduleSource === 'string' && ['@atlaskit/icon/glyph/', '@atlaskit/icon-object/glyph/'].find(val => moduleSource.startsWith(val)) && node.specifiers.length) {
|
|
103
|
+
createCantMigrateReExportError(node, moduleSource, node.specifiers[0].exported.name, errorsManual);
|
|
104
|
+
guidance[locToString(node)] = createGuidance(moduleSource);
|
|
105
|
+
}
|
|
106
|
+
} else if (node.declaration && isNodeOfType(node.declaration, 'VariableDeclaration')) {
|
|
107
|
+
// export const Icon = AddIcon;
|
|
108
|
+
for (const decl of node.declaration.declarations) {
|
|
109
|
+
if (isNodeOfType(decl, 'VariableDeclarator') && 'init' in decl && decl.init && isNodeOfType(decl.init, 'Identifier') && decl.init.name in legacyIconImports) {
|
|
110
|
+
createCantMigrateReExportError(node, legacyIconImports[decl.init.name].packageName, decl.init.name, errorsManual);
|
|
111
|
+
guidance[locToString(node)] = createGuidance(createGuidance(legacyIconImports[decl.init.name].packageName));
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
} else if (!node.source && node.specifiers && node.specifiers.length > 0) {
|
|
115
|
+
/**
|
|
116
|
+
* case where multiple consts are re-exported:
|
|
117
|
+
* const AddIcon = LegacyIcon;
|
|
118
|
+
* const crossIcon = LegacyIcon2;
|
|
119
|
+
* export { AddIcon, CrossIcon as default }
|
|
120
|
+
*/
|
|
121
|
+
for (const spec of node.specifiers) {
|
|
122
|
+
if (spec.local.name in legacyIconImports) {
|
|
123
|
+
//update legacy imports to be exported
|
|
124
|
+
legacyIconImports[spec.local.name] = {
|
|
125
|
+
packageName: legacyIconImports[spec.local.name].packageName,
|
|
126
|
+
exported: true
|
|
127
|
+
};
|
|
128
|
+
createCantMigrateReExportError(spec, legacyIconImports[spec.local.name].packageName, spec.exported.name, errorsManual);
|
|
129
|
+
guidance[locToString(spec)] = createGuidance(legacyIconImports[spec.local.name].packageName);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Checks if a legacy icon is referenced in an array or map and stores the errors in the global array
|
|
137
|
+
* @param node The array/map node found by ESLint
|
|
138
|
+
*/
|
|
139
|
+
const checkArrayOrMap = node => {
|
|
140
|
+
if (!isNodeOfType(node, 'Identifier')) {
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
if (node.name && node.name in legacyIconImports && legacyIconImports[node.name].packageName) {
|
|
144
|
+
createCantMigrateIdentifierError(node, legacyIconImports[node.name].packageName, node.name, errorsManual);
|
|
145
|
+
guidance[locToString(node)] = createGuidance(legacyIconImports[node.name].packageName);
|
|
146
|
+
}
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Checks if a legacy icon is referenced as a prop to a component and stores the errors in the global array
|
|
151
|
+
* @param node The property node found by ESLint
|
|
152
|
+
*/
|
|
153
|
+
const checkIconAsProp = node => {
|
|
154
|
+
if (!isNodeOfType(node, 'Identifier')) {
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
if (!node.parent || !node.parent.parent || !node.parent.parent.parent) {
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
if (!isNodeOfType(node.parent, 'JSXExpressionContainer') || !isNodeOfType(node.parent.parent, 'JSXAttribute') || !isNodeOfType(node.parent.parent.parent, 'JSXOpeningElement')) {
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
if (node.name in legacyIconImports && isNodeOfType(node.parent.parent.name, 'JSXIdentifier') && node.parent.parent.name.name !== 'LEGACY_fallbackIcon') {
|
|
164
|
+
var _migrationMapObject$s;
|
|
165
|
+
const migrationMapObject = getMigrationMapObject(legacyIconImports[node.name].packageName);
|
|
166
|
+
const newIcon = migrationMapObject === null || migrationMapObject === void 0 ? void 0 : migrationMapObject.newIcon;
|
|
167
|
+
const isNewIconMigratable = canAutoMigrateNewIconBasedOnSize(migrationMapObject === null || migrationMapObject === void 0 ? void 0 : (_migrationMapObject$s = migrationMapObject.sizeGuidance) === null || _migrationMapObject$s === void 0 ? void 0 : _migrationMapObject$s.medium);
|
|
168
|
+
const isInNewButton = isNodeOfType(node.parent.parent.parent.name, 'JSXIdentifier') && newButtonImports.has(node.parent.parent.parent.name.name);
|
|
169
|
+
let UNSAFE_size = null;
|
|
170
|
+
let UNSAFE_propName = null;
|
|
171
|
+
if (isInNewButton) {
|
|
172
|
+
const result = findUNSAFEProp(node.parent.parent, node.parent.parent.parent);
|
|
173
|
+
UNSAFE_size = result.UNSAFE_size;
|
|
174
|
+
UNSAFE_propName = result.UNSAFE_propName;
|
|
175
|
+
}
|
|
176
|
+
if (newIcon && isInNewButton && isNewIconMigratable && UNSAFE_size !== 'large' && UNSAFE_size !== 'xlarge') {
|
|
177
|
+
const iconKey = getIconKey(legacyIconImports[node.name].packageName);
|
|
178
|
+
createAutoMigrationError(node, legacyIconImports[node.name].packageName, node.name, newIcon, iconKey, errorsAuto);
|
|
179
|
+
guidance[locToString(node)] = createGuidance(legacyIconImports[node.name].packageName, 'medium');
|
|
180
|
+
} else if ((!newIcon || !isNewIconMigratable) && !UNSAFE_size) {
|
|
181
|
+
createCantFindSuitableReplacementError(node, legacyIconImports[node.name].packageName, node.name, errorsManual);
|
|
182
|
+
guidance[locToString(node)] = createGuidance(legacyIconImports[node.name].packageName);
|
|
183
|
+
} else if ((UNSAFE_size === 'large' || UNSAFE_size === 'xlarge') && UNSAFE_propName) {
|
|
184
|
+
createCantMigrateUnsafeProp(node, UNSAFE_propName, UNSAFE_size, legacyIconImports[node.name].packageName, node.name, errorsManual);
|
|
185
|
+
guidance[locToString(node)] = createGuidance(legacyIconImports[node.name].packageName, UNSAFE_size);
|
|
186
|
+
} else if (!isInNewButton) {
|
|
187
|
+
createCantMigrateFunctionUnknownError(node, legacyIconImports[node.name].packageName, node.name, errorsManual);
|
|
188
|
+
guidance[locToString(node)] = createGuidance(legacyIconImports[node.name].packageName);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Checks if a legacy icon is being rendered and stores the errors in the global array
|
|
195
|
+
* @param node The JSX node found by ESLint
|
|
196
|
+
*/
|
|
197
|
+
const checkJSXElement = node => {
|
|
198
|
+
var _node$parent, _node$parent$parent, _node$parent$parent$p;
|
|
199
|
+
if (!('openingElement' in node) || !isNodeOfType(node.openingElement.name, 'JSXIdentifier')) {
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Determine if element is rendered inside LEGACY_fallbackIcon prop - if so, don't perform any checks
|
|
204
|
+
if (node.parent && isNodeOfType(node.parent, 'ArrowFunctionExpression') && (_node$parent = node.parent) !== null && _node$parent !== void 0 && (_node$parent$parent = _node$parent.parent) !== null && _node$parent$parent !== void 0 && _node$parent$parent.parent && isNodeOfType(node.parent.parent.parent, 'JSXAttribute') && ((_node$parent$parent$p = node.parent.parent.parent.name) === null || _node$parent$parent$p === void 0 ? void 0 : _node$parent$parent$p.name) === 'LEGACY_fallbackIcon') {
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
const name = node.openingElement.name.name;
|
|
208
|
+
// Legacy icons rendered as JSX elements
|
|
209
|
+
if (name in legacyIconImports) {
|
|
210
|
+
var _node$parent2, _node$parent2$parent, _node$parent3, _node$parent3$parent, _node$parent3$parent$, _size;
|
|
211
|
+
// Determine if inside a new button - if so:
|
|
212
|
+
// - Assume spread props are safe - still error if props explicitly set to unmigratable values
|
|
213
|
+
// - eventually: look for UNSAFE_iconBefore_size props on the parent button - if it's large/xlarge, fail
|
|
214
|
+
let insideNewButton = false;
|
|
215
|
+
let UNSAFE_propName = null;
|
|
216
|
+
let UNSAFE_size = null;
|
|
217
|
+
if (node.parent && isNodeOfType(node.parent, 'ArrowFunctionExpression') && (_node$parent2 = node.parent) !== null && _node$parent2 !== void 0 && (_node$parent2$parent = _node$parent2.parent) !== null && _node$parent2$parent !== void 0 && _node$parent2$parent.parent && isNodeOfType(node.parent.parent.parent, 'JSXAttribute') && isNodeOfType(node.parent.parent.parent.name, 'JSXIdentifier') && (_node$parent3 = node.parent) !== null && _node$parent3 !== void 0 && (_node$parent3$parent = _node$parent3.parent) !== null && _node$parent3$parent !== void 0 && (_node$parent3$parent$ = _node$parent3$parent.parent) !== null && _node$parent3$parent$ !== void 0 && _node$parent3$parent$.parent && isNodeOfType(node.parent.parent.parent.parent, 'JSXOpeningElement') && isNodeOfType(node.parent.parent.parent.parent.name, 'JSXIdentifier') && newButtonImports.has(node.parent.parent.parent.parent.name.name)) {
|
|
218
|
+
insideNewButton = true;
|
|
219
|
+
const result = findUNSAFEProp(node.parent.parent.parent, node.parent.parent.parent.parent);
|
|
220
|
+
UNSAFE_size = result.UNSAFE_size;
|
|
221
|
+
UNSAFE_propName = result.UNSAFE_propName;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// Find size prop on node
|
|
225
|
+
let size = 'medium';
|
|
226
|
+
let primaryColor = null;
|
|
227
|
+
let afterSpreadSet = new Set();
|
|
228
|
+
let requiredAttributesAfterSpread = new Set(['size', 'primaryColor', 'secondaryColor']);
|
|
229
|
+
let hasSpread = false;
|
|
230
|
+
let hasPrimaryColorProp = false;
|
|
231
|
+
for (const attr of node.openingElement.attributes) {
|
|
232
|
+
// Detect spread props
|
|
233
|
+
if (isNodeOfType(attr, 'JSXSpreadAttribute')) {
|
|
234
|
+
// In case there are more spread props
|
|
235
|
+
afterSpreadSet.clear();
|
|
236
|
+
hasSpread = true;
|
|
237
|
+
continue;
|
|
238
|
+
}
|
|
239
|
+
if (!isNodeOfType(attr, 'JSXAttribute') || !isNodeOfType(attr.name, 'JSXIdentifier') || !attr.value) {
|
|
240
|
+
continue;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// Register props that aren't being spread
|
|
244
|
+
afterSpreadSet.add(attr.name.name);
|
|
245
|
+
|
|
246
|
+
// Extract values of props
|
|
247
|
+
switch (attr.name.name) {
|
|
248
|
+
case 'size':
|
|
249
|
+
if (isNodeOfType(attr.value, 'Literal') && isSize(attr.value.value)) {
|
|
250
|
+
size = attr.value.value;
|
|
251
|
+
} else if (isNodeOfType(attr.value, 'JSXExpressionContainer') && isNodeOfType(attr.value.expression, 'Literal') && isSize(attr.value.expression.value)) {
|
|
252
|
+
size = attr.value.expression.value;
|
|
253
|
+
} else {
|
|
254
|
+
size = null;
|
|
255
|
+
}
|
|
256
|
+
break;
|
|
257
|
+
case 'primaryColor':
|
|
258
|
+
primaryColor = getPrimaryColor(attr);
|
|
259
|
+
hasPrimaryColorProp = true;
|
|
260
|
+
break;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
let hasManualMigration = false;
|
|
264
|
+
if (primaryColor && !canMigrateColor(primaryColor) || hasPrimaryColorProp && !primaryColor) {
|
|
265
|
+
createCantMigrateColorError(node, primaryColor ? `the value of '${primaryColor}'` : 'a statically unknown value', errorsManual, legacyIconImports[name].packageName, name);
|
|
266
|
+
hasManualMigration = true;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// If size can't be determined (i.e. size is a variable or function call, etc)
|
|
270
|
+
// then we need to error; icon can't be auto-migrated safely
|
|
271
|
+
if (size === null) {
|
|
272
|
+
createCantMigrateSizeUnknown(node, errorsManual, legacyIconImports[name].packageName, name);
|
|
273
|
+
hasManualMigration = true;
|
|
274
|
+
}
|
|
275
|
+
guidance[locToString(node)] = createGuidance(legacyIconImports[name].packageName, size ? size : undefined);
|
|
276
|
+
// Check for unsafe size
|
|
277
|
+
if ((UNSAFE_size === 'large' || UNSAFE_size === 'xlarge') && (UNSAFE_propName === 'UNSAFE_iconAfter_size' || UNSAFE_propName === 'UNSAFE_iconBefore_size' || UNSAFE_propName === 'UNSAFE_size')) {
|
|
278
|
+
createCantMigrateUnsafeProp(node, UNSAFE_propName, UNSAFE_size, legacyIconImports[name].packageName, name, errorsManual);
|
|
279
|
+
hasManualMigration = true;
|
|
280
|
+
}
|
|
281
|
+
// Do a set comparison - is requiredAttributesAfterSpread a subset of afterSpreadSet?
|
|
282
|
+
if (hasSpread === true && !Array.from(requiredAttributesAfterSpread).every(val => afterSpreadSet.has(val)) && !insideNewButton) {
|
|
283
|
+
const missingProps = Array.from(requiredAttributesAfterSpread).filter(val => !afterSpreadSet.has(val));
|
|
284
|
+
createCantMigrateSpreadPropsError(node, missingProps, errorsManual, legacyIconImports[name].packageName, name);
|
|
285
|
+
hasManualMigration = true;
|
|
286
|
+
}
|
|
287
|
+
// Check if it is an exported component?
|
|
288
|
+
if (legacyIconImports[name].exported) {
|
|
289
|
+
createCantMigrateReExportError(node, legacyIconImports[name].packageName, name, errorsManual);
|
|
290
|
+
hasManualMigration = true;
|
|
291
|
+
}
|
|
292
|
+
const migrationMapObject = getMigrationMapObject(legacyIconImports[name].packageName);
|
|
293
|
+
const iconKey = getIconKey(legacyIconImports[name].packageName);
|
|
294
|
+
const newIcon = migrationMapObject === null || migrationMapObject === void 0 ? void 0 : migrationMapObject.newIcon;
|
|
295
|
+
const isNewIconMigratable = canAutoMigrateNewIconBasedOnSize(migrationMapObject === null || migrationMapObject === void 0 ? void 0 : migrationMapObject.sizeGuidance[(_size = size) !== null && _size !== void 0 ? _size : 'medium']);
|
|
296
|
+
if (!hasManualMigration && newIcon && isNewIconMigratable) {
|
|
297
|
+
createAutoMigrationError(node, legacyIconImports[name].packageName, name, newIcon, iconKey, errorsAuto);
|
|
298
|
+
} else if ((!newIcon || !isNewIconMigratable) && size) {
|
|
299
|
+
createCantFindSuitableReplacementError(node, legacyIconImports[name].packageName, name, errorsManual);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
};
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Checks if a legacy icon is being passed into a function call and stores the errors in the global array
|
|
306
|
+
* @param node The function call node found by ESLint
|
|
307
|
+
*/
|
|
308
|
+
const checkCallExpression = node => {
|
|
309
|
+
if ('arguments' in node && node.arguments.length) {
|
|
310
|
+
for (const arg of node.arguments) {
|
|
311
|
+
if (isNodeOfType(arg, 'Identifier') && arg.name in legacyIconImports && legacyIconImports[arg.name].packageName) {
|
|
312
|
+
createCantMigrateFunctionUnknownError(node, legacyIconImports[arg.name].packageName, arg.name, errorsManual);
|
|
313
|
+
guidance[locToString(node)] = createGuidance(legacyIconImports[arg.name].packageName);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
};
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* Throws the relevant errors in the correct order based on configs.
|
|
321
|
+
*/
|
|
322
|
+
const throwErrors = () => {
|
|
323
|
+
if (shouldErrorForManualMigration) {
|
|
324
|
+
for (const [key, errorList] of Object.entries(errorsManual)) {
|
|
325
|
+
const node = 'node' in errorList.errors[0] ? errorList.errors[0].node : null;
|
|
326
|
+
if (node) {
|
|
327
|
+
context.report({
|
|
328
|
+
node,
|
|
329
|
+
messageId: 'noLegacyIconsManualMigration',
|
|
330
|
+
data: {
|
|
331
|
+
iconName: errorList.iconName,
|
|
332
|
+
importSource: errorList.importSource,
|
|
333
|
+
quietModeGuidance: isQuietMode ? 'For more information see the below errors:' : ''
|
|
334
|
+
}
|
|
335
|
+
});
|
|
336
|
+
if (!isQuietMode) {
|
|
337
|
+
for (const error of errorList.errors) {
|
|
338
|
+
context.report(error);
|
|
339
|
+
}
|
|
340
|
+
if (key in guidance) {
|
|
341
|
+
context.report({
|
|
342
|
+
node,
|
|
343
|
+
messageId: 'guidance',
|
|
344
|
+
data: {
|
|
345
|
+
guidance: guidance[key]
|
|
346
|
+
}
|
|
347
|
+
});
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
if (shouldErrorForAutoMigration) {
|
|
354
|
+
for (const [key, error] of Object.entries(errorsAuto)) {
|
|
355
|
+
// If there's a manual error that exists for this same component,
|
|
356
|
+
// don't throw the auto error
|
|
357
|
+
if (key in errorsManual) {
|
|
358
|
+
delete errorsAuto[key];
|
|
359
|
+
continue;
|
|
360
|
+
}
|
|
361
|
+
const node = 'node' in error ? error.node : null;
|
|
362
|
+
if (node) {
|
|
363
|
+
context.report(error);
|
|
364
|
+
if (key in guidance && !isQuietMode) {
|
|
365
|
+
context.report({
|
|
366
|
+
node,
|
|
367
|
+
messageId: 'guidance',
|
|
368
|
+
data: {
|
|
369
|
+
guidance: guidance[key]
|
|
370
|
+
}
|
|
371
|
+
});
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
};
|
|
377
|
+
return {
|
|
378
|
+
checkImportDeclarations,
|
|
379
|
+
checkVariableDeclarations,
|
|
380
|
+
checkExportDefaultDeclaration,
|
|
381
|
+
checkExportNamedVariables,
|
|
382
|
+
checkArrayOrMap,
|
|
383
|
+
checkIconAsProp,
|
|
384
|
+
checkJSXElement,
|
|
385
|
+
checkCallExpression,
|
|
386
|
+
throwErrors
|
|
387
|
+
};
|
|
388
|
+
};
|