@atlaskit/eslint-plugin-design-system 13.41.2 → 13.42.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.
Files changed (85) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/README.md +0 -1
  3. package/dist/cjs/presets/all-flat.codegen.js +1 -2
  4. package/dist/cjs/presets/all.codegen.js +1 -2
  5. package/dist/cjs/rules/index.codegen.js +1 -3
  6. package/dist/cjs/rules/use-primitives/utils/is-valid-css-properties-to-transform.js +5 -4
  7. package/dist/es2019/presets/all-flat.codegen.js +1 -2
  8. package/dist/es2019/presets/all.codegen.js +1 -2
  9. package/dist/es2019/rules/index.codegen.js +1 -3
  10. package/dist/es2019/rules/use-primitives/utils/is-valid-css-properties-to-transform.js +5 -4
  11. package/dist/esm/presets/all-flat.codegen.js +1 -2
  12. package/dist/esm/presets/all.codegen.js +1 -2
  13. package/dist/esm/rules/index.codegen.js +1 -3
  14. package/dist/esm/rules/use-primitives/utils/is-valid-css-properties-to-transform.js +5 -4
  15. package/dist/types/presets/all-flat.codegen.d.ts +1 -1
  16. package/dist/types/presets/all.codegen.d.ts +1 -1
  17. package/dist/types/rules/ensure-design-token-usage/index.d.ts +1 -1
  18. package/dist/types/rules/ensure-design-token-usage/rule-meta.d.ts +1 -1
  19. package/dist/types/rules/ensure-design-token-usage/utils.d.ts +1 -1
  20. package/dist/types/rules/index.codegen.d.ts +1 -1
  21. package/dist/types/rules/no-deprecated-imports/handlers/icon.d.ts +1 -1
  22. package/dist/types/rules/use-heading/transformers/common.d.ts +2 -2
  23. package/dist/types/rules/use-latest-xcss-syntax-typography/linters/common.d.ts +1 -1
  24. package/dist/types/rules/use-latest-xcss-syntax-typography/linters/wrapped-token-value.d.ts +1 -1
  25. package/dist/types/rules/use-primitives/transformers/compiled-styled/index.d.ts +1 -1
  26. package/dist/types/rules/use-primitives/transformers/emotion-css/index.d.ts +1 -1
  27. package/dist/types/rules/use-primitives/utils/is-valid-css-properties-to-transform.d.ts +1 -1
  28. package/dist/types/rules/use-primitives/utils/validate-styles.d.ts +1 -1
  29. package/dist/types/rules/use-primitives-text/transformers/common.d.ts +1 -1
  30. package/dist/types/rules/use-primitives-text/transformers/unsafe-small-text.d.ts +1 -1
  31. package/dist/types/rules/use-tokens-typography/config/index.d.ts +1 -1
  32. package/dist/types/rules/use-tokens-typography/transformers/banned-properties.d.ts +1 -1
  33. package/dist/types/rules/use-tokens-typography/transformers/font-family.d.ts +1 -1
  34. package/dist/types/rules/use-tokens-typography/transformers/font-weight.d.ts +1 -1
  35. package/dist/types/rules/use-tokens-typography/transformers/restricted-capitalisation.d.ts +1 -1
  36. package/dist/types/rules/use-tokens-typography/transformers/style-object.d.ts +1 -1
  37. package/dist/types/rules/use-tokens-typography/transformers/untokenized-properties.d.ts +1 -1
  38. package/dist/types/rules/utils/create-no-exported-rule/check-if-supported-export.d.ts +1 -1
  39. package/dist/types/rules/utils/get-first-supported-import.d.ts +1 -1
  40. package/dist/types-ts4.5/presets/all-flat.codegen.d.ts +1 -1
  41. package/dist/types-ts4.5/presets/all.codegen.d.ts +1 -1
  42. package/dist/types-ts4.5/rules/ensure-design-token-usage/index.d.ts +1 -1
  43. package/dist/types-ts4.5/rules/ensure-design-token-usage/rule-meta.d.ts +1 -1
  44. package/dist/types-ts4.5/rules/ensure-design-token-usage/utils.d.ts +1 -1
  45. package/dist/types-ts4.5/rules/index.codegen.d.ts +1 -1
  46. package/dist/types-ts4.5/rules/no-deprecated-imports/handlers/icon.d.ts +1 -1
  47. package/dist/types-ts4.5/rules/use-heading/transformers/common.d.ts +2 -2
  48. package/dist/types-ts4.5/rules/use-latest-xcss-syntax-typography/linters/common.d.ts +1 -1
  49. package/dist/types-ts4.5/rules/use-latest-xcss-syntax-typography/linters/wrapped-token-value.d.ts +1 -1
  50. package/dist/types-ts4.5/rules/use-primitives/transformers/compiled-styled/index.d.ts +1 -1
  51. package/dist/types-ts4.5/rules/use-primitives/transformers/emotion-css/index.d.ts +1 -1
  52. package/dist/types-ts4.5/rules/use-primitives/utils/is-valid-css-properties-to-transform.d.ts +1 -1
  53. package/dist/types-ts4.5/rules/use-primitives/utils/validate-styles.d.ts +1 -1
  54. package/dist/types-ts4.5/rules/use-primitives-text/transformers/common.d.ts +1 -1
  55. package/dist/types-ts4.5/rules/use-primitives-text/transformers/unsafe-small-text.d.ts +1 -1
  56. package/dist/types-ts4.5/rules/use-tokens-typography/config/index.d.ts +1 -1
  57. package/dist/types-ts4.5/rules/use-tokens-typography/transformers/banned-properties.d.ts +1 -1
  58. package/dist/types-ts4.5/rules/use-tokens-typography/transformers/font-family.d.ts +1 -1
  59. package/dist/types-ts4.5/rules/use-tokens-typography/transformers/font-weight.d.ts +1 -1
  60. package/dist/types-ts4.5/rules/use-tokens-typography/transformers/restricted-capitalisation.d.ts +1 -1
  61. package/dist/types-ts4.5/rules/use-tokens-typography/transformers/style-object.d.ts +1 -1
  62. package/dist/types-ts4.5/rules/use-tokens-typography/transformers/untokenized-properties.d.ts +1 -1
  63. package/dist/types-ts4.5/rules/utils/create-no-exported-rule/check-if-supported-export.d.ts +1 -1
  64. package/dist/types-ts4.5/rules/utils/get-first-supported-import.d.ts +1 -1
  65. package/package.json +4 -4
  66. package/dist/cjs/rules/no-legacy-icons/checks.js +0 -619
  67. package/dist/cjs/rules/no-legacy-icons/helpers.js +0 -900
  68. package/dist/cjs/rules/no-legacy-icons/index.js +0 -91
  69. package/dist/cjs/rules/no-legacy-icons/upcoming-icons.js +0 -7
  70. package/dist/es2019/rules/no-legacy-icons/checks.js +0 -520
  71. package/dist/es2019/rules/no-legacy-icons/helpers.js +0 -853
  72. package/dist/es2019/rules/no-legacy-icons/index.js +0 -87
  73. package/dist/es2019/rules/no-legacy-icons/upcoming-icons.js +0 -1
  74. package/dist/esm/rules/no-legacy-icons/checks.js +0 -613
  75. package/dist/esm/rules/no-legacy-icons/helpers.js +0 -891
  76. package/dist/esm/rules/no-legacy-icons/index.js +0 -85
  77. package/dist/esm/rules/no-legacy-icons/upcoming-icons.js +0 -1
  78. package/dist/types/rules/no-legacy-icons/checks.d.ts +0 -16
  79. package/dist/types/rules/no-legacy-icons/helpers.d.ts +0 -151
  80. package/dist/types/rules/no-legacy-icons/index.d.ts +0 -2
  81. package/dist/types/rules/no-legacy-icons/upcoming-icons.d.ts +0 -1
  82. package/dist/types-ts4.5/rules/no-legacy-icons/checks.d.ts +0 -16
  83. package/dist/types-ts4.5/rules/no-legacy-icons/helpers.d.ts +0 -156
  84. package/dist/types-ts4.5/rules/no-legacy-icons/index.d.ts +0 -2
  85. package/dist/types-ts4.5/rules/no-legacy-icons/upcoming-icons.d.ts +0 -1
@@ -1,853 +0,0 @@
1
- import { isNodeOfType, literal } from 'eslint-codemod-utils';
2
- import baseMigrationMap, { migrationOutcomeDescriptionMap } from '@atlaskit/icon/migration-map';
3
- import { upcomingIcons } from './upcoming-icons';
4
- const sizes = ['small', 'medium', 'large', 'xlarge'];
5
- export const isSize = size => sizes.includes(size);
6
-
7
- /**
8
- * Returns the migration map object for a legacy icon or null if not found
9
- * @param iconPackage The name of the legacy icon package
10
- * @returns The migration map object for the legacy icon or null if not found
11
- */
12
- export const getMigrationMapObject = iconPackage => {
13
- const key = getIconKey(iconPackage);
14
- if (Object.keys(baseMigrationMap).includes(key)) {
15
- return baseMigrationMap[key];
16
- }
17
- return null;
18
- };
19
- export const getUpcomingIcons = iconPackage => {
20
- const key = getIconKey(iconPackage);
21
- if (upcomingIcons.includes(key)) {
22
- const retval = {
23
- sizeGuidance: {
24
- small: 'swap',
25
- medium: 'swap',
26
- large: 'icon-tile',
27
- xlarge: 'icon-tile'
28
- }
29
- };
30
- return retval;
31
- }
32
- return null;
33
- };
34
-
35
- /**
36
- * Returns the key of a legacy icon
37
- * @param iconPackage The name of the legacy icon package
38
- * @returns The unique identifier for the icon (the part after "@atlaskit/icon/glyph")
39
- */
40
- const getIconKey = iconPackage => {
41
- const key = iconPackage.replace(/^@atlaskit\/icon\/glyph\//, '');
42
- return key;
43
- };
44
-
45
- /**
46
- * Checks if a new icon can be auto-migrated based on guidance from the migration map
47
- */
48
- export const canAutoMigrateNewIconBasedOnSize = guidance => {
49
- return guidance ? ['swap', 'swap-slight-visual-change', 'swap-visual-change'].includes(guidance) : false;
50
- };
51
-
52
- /**
53
- *
54
- * @param iconPackage string
55
- * @returns object of new icon name and import path
56
- */
57
- const getNewIconNameAndImportPath = (iconPackage, shouldUseMigrationPath) => {
58
- const legacyIconName = getIconKey(iconPackage);
59
- const migrationMapObject = getMigrationMapObject(iconPackage);
60
- if (!migrationMapObject || !migrationMapObject.newIcon) {
61
- return {};
62
- }
63
- const {
64
- newIcon
65
- } = migrationMapObject;
66
- const migrationPath = newIcon.name === legacyIconName ? `${newIcon.package}/core/migration/${newIcon.name}` : `${newIcon.package}/core/migration/${newIcon.name}--${legacyIconName.replaceAll('/', '-')}`;
67
- return {
68
- iconName: newIcon.name,
69
- importPath: shouldUseMigrationPath ? migrationPath : `${newIcon.package}/core/${newIcon.name}`
70
- };
71
- };
72
-
73
- /**
74
- * Creates the written guidance for migrating a legacy icon to a new icon
75
- */
76
- export const createGuidance = ({
77
- iconPackage,
78
- insideNewButton,
79
- size: initialSize,
80
- shouldUseMigrationPath,
81
- shouldForceSmallIcon
82
- }) => {
83
- const size = shouldForceSmallIcon ? 'small' : initialSize;
84
- const migrationMapObject = getMigrationMapObject(iconPackage);
85
- const upcomingIcon = getUpcomingIcons(iconPackage);
86
- if (upcomingIcon) {
87
- let guidance = '';
88
- if (size) {
89
- if (upcomingIcon.sizeGuidance[size] && canAutoMigrateNewIconBasedOnSize(upcomingIcon.sizeGuidance[size])) {
90
- guidance += `Fix: An upcoming icon release is planned to migrate this legacy icon.`;
91
- } else {
92
- guidance += `No equivalent icon for this size, ${size}, in the current or upcoming set of icons.`;
93
- }
94
- guidance += `${Object.keys(migrationOutcomeDescriptionMap).includes(upcomingIcon.sizeGuidance[size]) ? ` Once the upcoming icons are released, please: ${migrationOutcomeDescriptionMap[upcomingIcon.sizeGuidance[size]]}` : ' No migration size advice given.'}\n`;
95
- } else {
96
- guidance = `Please wait for the upcoming icons released, as it will contain an alternative for this legacy icon.\nMigration suggestions, depending on the legacy icon size:\n`;
97
- for (const [size, value] of Object.entries(upcomingIcon.sizeGuidance)) {
98
- guidance += `\t- ${size}: `;
99
- if (!Object.keys(migrationOutcomeDescriptionMap).includes(value)) {
100
- guidance += 'No migration advice given.\n';
101
- } else {
102
- guidance += `${migrationOutcomeDescriptionMap[value]}.\n`;
103
- }
104
- }
105
- }
106
- return guidance;
107
- } else if (migrationMapObject) {
108
- const newIcon = migrationMapObject.newIcon;
109
- if (!newIcon) {
110
- return 'No equivalent icon in new set. An option is to contribute a custom icon into icon-labs package instead.\n';
111
- }
112
- const {
113
- iconName,
114
- importPath
115
- } = getNewIconNameAndImportPath(iconPackage, shouldUseMigrationPath);
116
- const buttonGuidanceStr = "Please set 'spacing' property of the new icon to 'none', to ensure appropriate spacing inside `@atlaskit/button`.\n";
117
- let guidance = '';
118
- if (size) {
119
- if (migrationMapObject.sizeGuidance[size] && canAutoMigrateNewIconBasedOnSize(migrationMapObject.sizeGuidance[size])) {
120
- guidance += `Fix: Use ${iconName} from ${importPath} instead.`;
121
- } else {
122
- guidance += `No equivalent icon for this size, ${size}, in new set.`;
123
- }
124
- guidance += `${Object.keys(migrationOutcomeDescriptionMap).includes(migrationMapObject.sizeGuidance[size]) ? ` Please: ${migrationOutcomeDescriptionMap[migrationMapObject.sizeGuidance[size]]}` : ' No migration size advice given.'}\n`;
125
- } else {
126
- guidance = `Use ${iconName} from ${importPath} instead.\nMigration suggestions, depending on the legacy icon size:\n`;
127
- Object.entries(migrationMapObject.sizeGuidance).forEach(([size, value]) => {
128
- guidance += `\t- ${size}: `;
129
- if (!Object.keys(migrationOutcomeDescriptionMap).includes(value)) {
130
- guidance += 'No migration advice given.\n';
131
- } else {
132
- guidance += `${migrationOutcomeDescriptionMap[value]}.\n`;
133
- }
134
- });
135
- }
136
- if (insideNewButton) {
137
- guidance += buttonGuidanceStr;
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
- }
150
- } else if (size) {
151
- guidance += "In the new icon, please use spacing='none'.\n";
152
- }
153
- return guidance;
154
- } else {
155
- return `Migration suggestions not found for "${iconPackage}".\n`;
156
- }
157
- };
158
-
159
- /**
160
- * Checks if the color can be migrated
161
- * @param color String representing the color to check
162
- * @returns True if the color can be migrated, false otherwise
163
- */
164
- export const canMigrateColor = color => {
165
- if (color.match(/^color\.icon/)) {
166
- return true;
167
- } else if (color.match(/^color\.link/)) {
168
- return true;
169
- } else if (color.match(/^color\.text/)) {
170
- return true;
171
- } else if (color === 'currentColor') {
172
- return true;
173
- } else {
174
- return false;
175
- }
176
- };
177
- export const locToString = node => {
178
- if (node.range && node.range.length >= 2) {
179
- return `${node.range[0]}:${node.range[1]}`;
180
- } else {
181
- return '';
182
- }
183
- };
184
- export const createCantMigrateReExportError = (node, packageName, exportName, errors) => {
185
- const myError = {
186
- node,
187
- messageId: 'cantMigrateReExport',
188
- data: {
189
- packageName,
190
- exportName
191
- }
192
- };
193
- pushManualError(locToString(node), errors, myError, packageName, exportName);
194
- };
195
- export const createCantMigrateIdentifierMapOrArrayError = (node, packageName, exportName, errors) => {
196
- const myError = {
197
- node,
198
- messageId: 'cantMigrateIdentifierMapOrArray',
199
- data: {
200
- packageName,
201
- exportName
202
- }
203
- };
204
- pushManualError(locToString(node), errors, myError, packageName, exportName);
205
- };
206
- export const createCantMigrateIdentifierError = (node, packageName, exportName, errors) => {
207
- const myError = {
208
- node,
209
- messageId: 'cantMigrateIdentifier',
210
- data: {
211
- iconSource: packageName,
212
- iconName: exportName
213
- }
214
- };
215
- pushManualError(locToString(node), errors, myError, packageName, exportName);
216
- };
217
- export const createCantFindSuitableReplacementError = (node, importSource, iconName, errors, sizeIssue) => {
218
- const myError = {
219
- node,
220
- messageId: 'cantFindSuitableReplacement',
221
- data: {
222
- importSource,
223
- iconName,
224
- sizeGuidance: sizeIssue ? ' at the current size' : ''
225
- }
226
- };
227
- pushManualError(locToString(node), errors, myError, importSource, iconName);
228
- };
229
- export const createCantMigrateFunctionUnknownError = (node, importSource, iconName, errors) => {
230
- const myError = {
231
- node,
232
- messageId: 'cantMigrateFunctionUnknown',
233
- data: {
234
- importSource,
235
- iconName
236
- }
237
- };
238
- pushManualError(locToString(node), errors, myError, importSource, iconName);
239
- };
240
- export const createCantMigrateColorError = (node, colorValue, errors, importSource, iconName) => {
241
- const myError = {
242
- node,
243
- messageId: 'cantMigrateColor',
244
- data: {
245
- colorValue
246
- }
247
- };
248
- pushManualError(locToString(node), errors, myError, importSource, iconName);
249
- };
250
- export const createCantMigrateSpreadPropsError = (node, missingProps, errors, importSource, iconName) => {
251
- const myError = {
252
- node,
253
- messageId: 'cantMigrateSpreadProps',
254
- data: {
255
- missingProps: missingProps.join(', ')
256
- }
257
- };
258
- pushManualError(locToString(node), errors, myError, importSource, iconName);
259
- };
260
- export const createCantMigrateSizeUnknown = (node, errors, importSource, iconName) => {
261
- const myError = {
262
- node,
263
- messageId: 'cantMigrateSizeUnknown'
264
- };
265
- pushManualError(locToString(node), errors, myError, importSource, iconName);
266
- };
267
- export const createAutoMigrationError = ({
268
- node,
269
- importSource,
270
- iconName,
271
- errors,
272
- spacing,
273
- insideNewButton,
274
- shouldForceSmallIcon
275
- }) => {
276
- const myError = {
277
- node,
278
- messageId: 'noLegacyIconsAutoMigration',
279
- data: {
280
- importSource,
281
- iconName,
282
- spacing: spacing !== null && spacing !== void 0 ? spacing : '',
283
- // value type need to be a string in Rule.ReportDescriptor
284
- insideNewButton: String(insideNewButton),
285
- shouldForceSmallIcon: String(shouldForceSmallIcon)
286
- }
287
- };
288
- errors[locToString(node)] = myError;
289
- };
290
- const pushManualError = (key, errors, myError, importSource, iconName) => {
291
- if (Object.keys(errors).includes(key)) {
292
- errors[key].errors.push(myError);
293
- } else {
294
- errors[key] = {
295
- errors: [myError],
296
- importSource,
297
- iconName
298
- };
299
- }
300
- };
301
- const getLiteralStringValue = value => {
302
- if (!value) {
303
- return;
304
- }
305
-
306
- // propName="value"
307
- if (isNodeOfType(value, 'Literal') && typeof value.value === 'string') {
308
- return value.value;
309
- }
310
-
311
- // propName={"value"}
312
- if (isNodeOfType(value, 'JSXExpressionContainer') && isNodeOfType(value.expression, 'Literal') && typeof value.expression.value === 'string') {
313
- return value.expression.value;
314
- }
315
- return;
316
- };
317
- export const createHelpers = ctx => {
318
- // TODO: JFP-2823 - this type cast was added due to Jira's ESLint v9 migration
319
- const context = ctx;
320
- /**
321
- * Extracts the token name of a token() call from a JSXExpressionContainer
322
- * @param value The JSXExpressionContainer to extract the token call from
323
- * @returns The value of the token call, or null if it could not be extracted
324
- */
325
- const getTokenCallValue = value => {
326
- /**
327
- * Previously, we used getImportName() to extract the token name from a token() call.
328
- * However, this was failing in the Issue Automat so we are now using a simpler approach.
329
- */
330
-
331
- if (isNodeOfType(value, 'JSXExpressionContainer') && isNodeOfType(value.expression, 'CallExpression') && 'name' in value.expression.callee && value.expression.callee.name === 'token') {
332
- // propName={token("color...."}
333
- return getLiteralStringValue(value.expression.arguments[0]);
334
- }
335
- return;
336
- };
337
-
338
- /**
339
- * Gets the value of a boolean configuration flag
340
- * @param key the key of the configuration flag
341
- * @param defaultValue The default value of the configuration flag
342
- * @returns defaultValue if the configuration flag is not set, the defaultValue of the configuration flag otherwise
343
- */
344
- const getConfigFlag = (key, defaultValue) => {
345
- if (context.options.length > 0 && context.options[0] && Object.keys(context.options[0]).includes(key)) {
346
- return context.options[0][key] === !defaultValue ? !defaultValue : defaultValue;
347
- }
348
- return defaultValue;
349
- };
350
- return {
351
- /**
352
- * Extracts the primaryColor value from a JSXAttribute
353
- */
354
- getPrimaryColor(attr) {
355
- var _ref, _getLiteralStringValu;
356
- const {
357
- value
358
- } = attr;
359
- return (_ref = (_getLiteralStringValu = getLiteralStringValue(value)) !== null && _getLiteralStringValu !== void 0 ? _getLiteralStringValu : getTokenCallValue(value)) !== null && _ref !== void 0 ? _ref : null;
360
- },
361
- getTokenCallValue,
362
- getConfigFlag
363
- };
364
- };
365
- export const addToListOfRanges = (node, sortedListOfRangesForErrors) => {
366
- if (node.range && node.range.length >= 2) {
367
- sortedListOfRangesForErrors.push({
368
- start: node.range[0],
369
- end: node.range[1]
370
- });
371
- }
372
- };
373
- const isInRangeList = (node, sortedListOfRangesForErrors) => {
374
- const {
375
- range
376
- } = node;
377
- if (!range || range.length < 2) {
378
- return false;
379
- }
380
- const found = sortedListOfRangesForErrors.find(currRange => range[0] >= currRange.start && range[1] <= currRange.end);
381
- return !!found;
382
- };
383
-
384
- /**
385
- *
386
- * @param node Icon JSXelement
387
- * @param newButtonImports list of new button import specifiers
388
- * @returns if Icon is inside a new button
389
- */
390
- export const isInsideNewButton = (node, newButtonImports) => {
391
- var _node$parent, _node$parent$parent, _node$parent2, _node$parent2$parent, _node$parent2$parent$;
392
- let insideNewButton = false;
393
- 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') && isNodeOfType(node.parent.parent.parent.name, 'JSXIdentifier') && (_node$parent2 = node.parent) !== null && _node$parent2 !== void 0 && (_node$parent2$parent = _node$parent2.parent) !== null && _node$parent2$parent !== void 0 && (_node$parent2$parent$ = _node$parent2$parent.parent) !== null && _node$parent2$parent$ !== void 0 && _node$parent2$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)) {
394
- insideNewButton = true;
395
- }
396
- return insideNewButton;
397
- };
398
-
399
- /**
400
- *
401
- * @param node Icon JSXelement
402
- * @param newButtonImports list of legacy button import specifiers
403
- * @returns if Icon is inside a legacy button
404
- */
405
- export const isInsideLegacyButton = (node, legacyButtonImports) => {
406
- var _node$parent3, _node$parent4, _node$parent4$parent, _node$parent5, _node$parent5$parent, _node$parent6, _node$parent6$parent;
407
- return node.parent && isNodeOfType(node.parent, 'JSXExpressionContainer') && ((_node$parent3 = node.parent) === null || _node$parent3 === void 0 ? void 0 : _node$parent3.parent) && isNodeOfType(node.parent.parent, 'JSXAttribute') && (node.parent.parent.name.name === 'iconBefore' || node.parent.parent.name.name === 'iconAfter') && isNodeOfType((_node$parent4 = node.parent) === null || _node$parent4 === void 0 ? void 0 : (_node$parent4$parent = _node$parent4.parent) === null || _node$parent4$parent === void 0 ? void 0 : _node$parent4$parent.parent, 'JSXOpeningElement') && isNodeOfType((_node$parent5 = node.parent) === null || _node$parent5 === void 0 ? void 0 : (_node$parent5$parent = _node$parent5.parent) === null || _node$parent5$parent === void 0 ? void 0 : _node$parent5$parent.parent.name, 'JSXIdentifier') && legacyButtonImports.has((_node$parent6 = node.parent) === null || _node$parent6 === void 0 ? void 0 : (_node$parent6$parent = _node$parent6.parent) === null || _node$parent6$parent === void 0 ? void 0 : _node$parent6$parent.parent.name.name);
408
- };
409
-
410
- /**
411
- *
412
- * @param node Icon JSXelement
413
- * @param newButtonImports list of legacy button import specifiers
414
- * @returns if Icon is inside a legacy button
415
- */
416
- export const isInsideIconOnlyLegacyButton = (node, legacyButtonImports) => {
417
- let insideIconOnlyLegacyButton = false;
418
- if (isInsideLegacyButton(node, legacyButtonImports)) {
419
- var _node$parent7, _node$parent8, _node$parent8$parent, _node$parent9, _node$parent9$parent, _node$parent0, _node$parent0$parent, _node$parent0$parent$, _node$parent1, _node$parent1$parent, _node$parent1$parent$, _node$parent10, _node$parent10$parent, _node$parent10$parent2;
420
- const legacyButtonAttributes = node.parent && isNodeOfType(node.parent, 'JSXExpressionContainer') && (_node$parent7 = node.parent) !== null && _node$parent7 !== void 0 && _node$parent7.parent && isNodeOfType(node.parent.parent, 'JSXAttribute') && node.parent.parent.parent && isNodeOfType((_node$parent8 = node.parent) === null || _node$parent8 === void 0 ? void 0 : (_node$parent8$parent = _node$parent8.parent) === null || _node$parent8$parent === void 0 ? void 0 : _node$parent8$parent.parent, 'JSXOpeningElement') ? (_node$parent9 = node.parent) === null || _node$parent9 === void 0 ? void 0 : (_node$parent9$parent = _node$parent9.parent) === null || _node$parent9$parent === void 0 ? void 0 : _node$parent9$parent.parent.attributes.map(attribute => {
421
- var _attribute$name;
422
- return isNodeOfType(attribute, 'JSXAttribute') && isNodeOfType(attribute.name, 'JSXIdentifier') && (attribute === null || attribute === void 0 ? void 0 : (_attribute$name = attribute.name) === null || _attribute$name === void 0 ? void 0 : _attribute$name.name);
423
- }).filter(Boolean) : [];
424
- const hasIconBefore = legacyButtonAttributes.includes('iconBefore');
425
- const hasIconAfter = legacyButtonAttributes.includes('iconAfter');
426
- const hasChildren = ((_node$parent0 = node.parent) === null || _node$parent0 === void 0 ? void 0 : (_node$parent0$parent = _node$parent0.parent) === null || _node$parent0$parent === void 0 ? void 0 : (_node$parent0$parent$ = _node$parent0$parent.parent) === null || _node$parent0$parent$ === void 0 ? void 0 : _node$parent0$parent$.parent) && isNodeOfType((_node$parent1 = node.parent) === null || _node$parent1 === void 0 ? void 0 : (_node$parent1$parent = _node$parent1.parent) === null || _node$parent1$parent === void 0 ? void 0 : (_node$parent1$parent$ = _node$parent1$parent.parent) === null || _node$parent1$parent$ === void 0 ? void 0 : _node$parent1$parent$.parent, 'JSXElement') && ((_node$parent10 = node.parent) === null || _node$parent10 === void 0 ? void 0 : (_node$parent10$parent = _node$parent10.parent) === null || _node$parent10$parent === void 0 ? void 0 : (_node$parent10$parent2 = _node$parent10$parent.parent) === null || _node$parent10$parent2 === void 0 ? void 0 : _node$parent10$parent2.parent.children.length) > 0;
427
- if (hasIconBefore && !hasIconAfter && !hasChildren || !hasIconBefore && hasIconAfter && !hasChildren || !hasIconBefore && !hasIconAfter && hasChildren) {
428
- insideIconOnlyLegacyButton = true;
429
- }
430
- }
431
- return insideIconOnlyLegacyButton;
432
- };
433
- const findProp = (attributes, propName) => attributes.find(attr => attr.type === 'JSXAttribute' && attr.name.name === propName);
434
- const getNewIconNameForRenaming = (isInManualArray, importSource, importSpecifier) => {
435
- let newIconName;
436
- if (isInManualArray) {
437
- newIconName = getNewIconNameAndImportPath(importSource).iconName;
438
- const keyToName = newIconName ? getComponentName(newIconName) : undefined;
439
- newIconName = keyToName;
440
- if (newIconName === undefined || importSpecifier === keyToName) {
441
- newIconName = `${keyToName}New`;
442
- }
443
- }
444
- return newIconName;
445
- };
446
- export const getComponentName = name => {
447
- return name.split(/\W/).map(part => `${part[0].toUpperCase()}${part.slice(1)}`).join('').concat('Icon');
448
- };
449
-
450
- /**
451
- *
452
- * Creates a list of fixers to update the icon import path
453
- * @param metadata Metadata including the import source and spacing
454
- * @param fixer The original fix function
455
- * @param legacyImportNode The import declaration node to replace
456
- * @param shouldUseMigrationPath The eslint rule config, whether to use migration entrypoint or not
457
- * @param migrationImportNode The migration import declaration node to replace, only present if shouldUseMigrationPath is false
458
- * @returns A list of fixers to migrate the icon
459
- */
460
- const createImportFix = ({
461
- fixer,
462
- legacyImportNode,
463
- metadata,
464
- shouldUseMigrationPath,
465
- migrationImportNode,
466
- newIconName
467
- }) => {
468
- const fixes = [];
469
- const {
470
- importSource
471
- } = metadata;
472
- const importPath = migrationImportNode ? importSource.replace('/migration', '').split('--')[0] : getNewIconNameAndImportPath(importSource, shouldUseMigrationPath).importPath;
473
- const useMigrationPath = legacyImportNode && importPath;
474
- const useFinalPath = migrationImportNode && !shouldUseMigrationPath && importPath;
475
- const programNode = legacyImportNode && findProgramNode(legacyImportNode);
476
- if (useMigrationPath) {
477
- if (newIconName) {
478
- const isExisting = programNode ? alreadyHasImportedLocalName(programNode, newIconName, importPath) : false;
479
- if (!isExisting) {
480
- fixes.push(fixer.insertTextBefore(legacyImportNode, `import ${newIconName} from '${importPath}';\n`));
481
- }
482
- } else {
483
- fixes.push(fixer.replaceText(legacyImportNode.source, `'${literal(importPath)}'`));
484
- }
485
- } else if (useFinalPath) {
486
- if (newIconName) {
487
- const isExisting = programNode ? alreadyHasImportedLocalName(programNode, newIconName, importPath) : false;
488
- if (!isExisting) {
489
- fixes.push(fixer.insertTextBefore(migrationImportNode, `import ${newIconName} from '${importPath}';\n`));
490
- }
491
- } else {
492
- fixes.push(fixer.replaceText(migrationImportNode.source, `'${literal(importPath)}'`));
493
- }
494
- }
495
- return fixes;
496
- };
497
-
498
- /**
499
- * Creates a list of fixers to update the icon props
500
- * @param node The Icon element to migrate
501
- * @param metadata Metadata including the import source and spacing
502
- * @param fixer The original fix function
503
- * @param legacyImportNode The import declaration node to replace
504
- * @param shouldUseMigrationPath The eslint rule config, whether to use migration entrypoint or not
505
- * @param migrationImportNode The migration import declaration node to replace, only present if shouldUseMigrationPath is false
506
- * @returns A list of fixers to migrate the icon
507
- */
508
- const createPropFixes = ({
509
- node,
510
- fixer,
511
- legacyImportNode,
512
- metadata,
513
- shouldUseMigrationPath,
514
- migrationImportNode,
515
- newIconName
516
- }) => {
517
- const fixes = [];
518
- const {
519
- spacing,
520
- size,
521
- importSource
522
- } = metadata;
523
- if (shouldUseMigrationPath && !legacyImportNode) {
524
- return fixes;
525
- }
526
- if (node.type === 'JSXElement') {
527
- const {
528
- openingElement
529
- } = node;
530
- if (newIconName) {
531
- fixes.push(fixer.replaceText(openingElement.name, newIconName));
532
- }
533
- const {
534
- attributes
535
- } = openingElement;
536
-
537
- // replace primaryColor prop with color
538
- const primaryColor = findProp(attributes, 'primaryColor');
539
- if (primaryColor && primaryColor.type === 'JSXAttribute') {
540
- fixes.push(fixer.replaceText(primaryColor.name, 'color'));
541
- }
542
- const sizeProp = findProp(attributes, 'size');
543
- const spacingProp = findProp(attributes, 'spacing');
544
- if (sizeProp && sizeProp.type === 'JSXAttribute') {
545
- if (shouldUseMigrationPath) {
546
- // Rename existing size prop to LEGACY_size and add new size prop if applicable
547
- fixes.push(fixer.replaceText(sizeProp.name, 'LEGACY_size'));
548
- if (size) {
549
- fixes.push(fixer.insertTextAfter(sizeProp, ` size="${size}"`));
550
- }
551
- } else {
552
- if (size && sizeProp.value) {
553
- // update size prop with new replacement size
554
- fixes.push(fixer.replaceText(sizeProp.value, `"${size}"`));
555
- } else if (importSource.startsWith('@atlaskit/icon/glyph/')) {
556
- // only remove size prop for glyph entry points if no new replacement size is specified
557
- fixes.push(fixer.remove(sizeProp));
558
- } else if (sizeProp.value && sizeProp.value.type === 'Literal' && typeof sizeProp.value.value === 'string' && sizeProp.value.value === 'medium') {
559
- // if size is medium, we can remove it as it is the default size
560
- fixes.push(fixer.remove(sizeProp));
561
- }
562
- }
563
- } else if (size) {
564
- fixes.push(fixer.insertTextAfter(openingElement.name, ` size="${size}"`));
565
- }
566
-
567
- // Add spacing prop if no existing spacing prop and icon is not imported from migration entrypoint
568
- if (spacing && spacing !== 'none' && !spacingProp && !migrationImportNode) {
569
- fixes.push(fixer.insertTextAfter(sizeProp || openingElement.name, ` spacing="${spacing}"`));
570
- }
571
-
572
- // rename or remove secondaryColor prop based on shouldUseMigrationPath
573
- const secondaryColorProp = findProp(attributes, 'secondaryColor');
574
- if (secondaryColorProp && secondaryColorProp.type === 'JSXAttribute') {
575
- fixes.push(shouldUseMigrationPath ?
576
- // replace secondaryColor prop with LEGACY_secondaryColor
577
- fixer.replaceText(secondaryColorProp.name, 'LEGACY_secondaryColor') :
578
- // remove secondaryColor prop if shouldUseMigrationPath is false
579
- fixer.remove(secondaryColorProp));
580
- }
581
-
582
- // remove LEGACY props
583
- if (!shouldUseMigrationPath) {
584
- ['LEGACY_size', 'LEGACY_margin', 'LEGACY_fallbackIcon', 'LEGACY_secondaryColor'].forEach(propName => {
585
- const legacyProp = findProp(attributes, propName);
586
- if (legacyProp && legacyProp.type === 'JSXAttribute') {
587
- fixes.push(fixer.remove(legacyProp));
588
- }
589
- });
590
- }
591
- } else if (node.type === 'Identifier' && newIconName) {
592
- fixes.push(fixer.replaceText(node, newIconName));
593
- }
594
- return fixes;
595
- };
596
-
597
- /**
598
- * Check if the new icon exists in the migration map
599
- */
600
- const checkIfNewIconExist = error => {
601
- var _error$data;
602
- if (!((_error$data = error.data) !== null && _error$data !== void 0 && _error$data.importSource)) {
603
- return false;
604
- }
605
- const iconKey = getIconKey(error.data.importSource);
606
- const {
607
- newIcon
608
- } = baseMigrationMap[iconKey] || {};
609
- return Boolean(newIcon);
610
- };
611
- export const throwManualErrors = ({
612
- errorsManual,
613
- errorRanges,
614
- guidance,
615
- context,
616
- isQuietMode
617
- }) => {
618
- for (const [key, errorList] of Object.entries(errorsManual)) {
619
- const node = 'node' in errorList.errors[0] ? errorList.errors[0].node : null;
620
- if (!node) {
621
- return;
622
- }
623
- const cantMigrateIdentifierError = errorList.errors.find(x => 'messageId' in x && x.messageId === 'cantMigrateIdentifier');
624
- let isInRange = false;
625
- if (cantMigrateIdentifierError && isInRangeList(node, errorRanges)) {
626
- isInRange = true;
627
- }
628
- if (isInRange && errorList.errors.length - 1 > 0 || !isInRange && errorList.errors.length > 0) {
629
- const guidanceMessage = Object.keys(guidance).includes(key) ? guidance[key] : '';
630
- context.report({
631
- node,
632
- messageId: 'noLegacyIconsManualMigration',
633
- data: {
634
- iconName: errorList.iconName,
635
- importSource: errorList.importSource,
636
- guidance: isQuietMode ? guidanceMessage : `${guidanceMessage}For more information see the below errors.\n`
637
- }
638
- });
639
- if (!isQuietMode) {
640
- for (const error of errorList.errors) {
641
- if ('messageId' in error && (error.messageId !== 'cantMigrateIdentifier' || error.messageId === 'cantMigrateIdentifier' && !isInRange)) {
642
- context.report(error);
643
- }
644
- }
645
- }
646
- }
647
- }
648
- };
649
-
650
- // Loops through automatic errors and them after adding the required suggestion/fix
651
- export const throwAutoErrors = ({
652
- errorsManual,
653
- errorsAuto,
654
- iconSizesInfo,
655
- legacyIconImports,
656
- guidance,
657
- migrationIconImports,
658
- shouldUseMigrationPath,
659
- context
660
- }) => {
661
- // Set of all the import sources that have manual errors (required later to check if a source has both manual and auto
662
- // errors in one file making it impossible to just remove the legacy import)
663
- const allManualErrorSources = Object.entries(errorsManual).reduce((result, option) => {
664
- const [key, errorInfo] = option;
665
- if (!errorsAuto.hasOwnProperty(key)) {
666
- result.add(errorInfo.importSource);
667
- }
668
- return result;
669
- }, new Set());
670
- // Group errors by import source and remove any unwanted errors
671
- const groupedErrorList = Object.entries(errorsAuto).reduce((result, option) => {
672
- const [key, error] = option;
673
- // Return early if no data
674
- if (!error.data) {
675
- return result;
676
- }
677
- if (Object.keys(errorsManual).includes(key)) {
678
- const cantMigrateIdentifierError = errorsManual[key].errors.find(x => 'messageId' in x && x.messageId === 'cantMigrateIdentifier');
679
- // If cantMigrateIdentifier is the only manual error found we still want to throw the auto error
680
- if (!(cantMigrateIdentifierError && errorsManual[key].errors.length === 1)) {
681
- return result;
682
- }
683
- }
684
- const importSource = error.data.importSource;
685
- if (!result.hasOwnProperty(importSource)) {
686
- result[importSource] = [];
687
- }
688
- result[importSource].push({
689
- key,
690
- ...error
691
- });
692
- return result;
693
- }, {});
694
- for (const [importSource, errorList] of Object.entries(groupedErrorList)) {
695
- const autoFixers = [];
696
- // appliedErrorsForImport will contain all the errors FOR A SINGLE IMPORT and will be merged into errorListForReport
697
- const appliedErrorsForImport = [];
698
- // Loop over auto errors for a single import source
699
- for (const [_, error] of errorList.entries()) {
700
- var _iconSizesInfo$import, _iconSizesInfo$import2, _iconSizesInfo$import3, _legacyIconImports$er, _error$data2, _legacyIconImports$er2, _migrationIconImports;
701
- const {
702
- key
703
- } = error;
704
- const node = 'node' in error ? error.node : null;
705
- // Check if there is a manual error for the same import source somewhere else in the same file
706
- // If that is the case we'll need to provide a suggestion instead of auto-fixing as the suggestion will
707
- // add another import without removing the old import and this needs to be validated
708
- const isInManualArray = allManualErrorSources.has(importSource);
709
-
710
- // Check if the icon has size of small, if so it cannot be automatically migrated. Two suggestions will be provided
711
- // 1. Use core icon with no spacing
712
- // 2. Use utility icon with compact spacing
713
- const isSizeSmall = (_iconSizesInfo$import = iconSizesInfo[importSource]) === null || _iconSizesInfo$import === void 0 ? void 0 : _iconSizesInfo$import.small.includes(key);
714
- 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;
715
-
716
- // Icon should be renamed
717
- // 1. If the icon is in the manual array OR
718
- // 2. If there is mixed size usages of this icon with size small
719
- const shouldRenameIcon = isInManualArray || isMixedSizeUsage;
720
-
721
- // New icon name for renaming if the icon is in the manual array
722
- 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);
723
- if (!node) {
724
- continue;
725
- }
726
- const guidanceMessage = guidance.hasOwnProperty(key) ? guidance[key] : '';
727
- if (Object.keys(error).includes('data') && error.data) {
728
- error.data.guidance = guidanceMessage;
729
- }
730
- const shouldForceSmallIcon = ((_error$data2 = error.data) === null || _error$data2 === void 0 ? void 0 : _error$data2.shouldForceSmallIcon) === 'true';
731
- const fixArguments = error.data ? {
732
- metadata: {
733
- ...error.data,
734
- spacing: error.data.isInNewButton ? 'none' : error.data.spacing,
735
- size: shouldForceSmallIcon ? 'small' : error.data.size
736
- },
737
- legacyImportNode: (_legacyIconImports$er2 = legacyIconImports[error.data.iconName]) === null || _legacyIconImports$er2 === void 0 ? void 0 : _legacyIconImports$er2.importNode,
738
- migrationImportNode: (_migrationIconImports = migrationIconImports[error.data.iconName]) === null || _migrationIconImports === void 0 ? void 0 : _migrationIconImports.importNode,
739
- shouldUseMigrationPath,
740
- newIconName: shouldRenameIcon ? newIconName : undefined
741
- } : null;
742
- if (!error.data || shouldUseMigrationPath && !checkIfNewIconExist(error) || !fixArguments) {
743
- continue;
744
- }
745
- const isInNewButton = fixArguments.metadata.insideNewButton === 'true';
746
- if (isSizeSmall && !shouldForceSmallIcon) {
747
- error.suggest = [{
748
- desc: isInNewButton ? 'Replace with medium core icon (Recommended)' : 'Replace with medium core icon and no spacing (Recommended)',
749
- fix: fixer => {
750
- return [...createPropFixes({
751
- ...fixArguments,
752
- metadata: {
753
- ...fixArguments.metadata,
754
- spacing: 'none'
755
- },
756
- node,
757
- fixer
758
- }), ...createImportFix({
759
- ...fixArguments,
760
- fixer
761
- })];
762
- }
763
- }, {
764
- desc: isInNewButton ? 'Replace with small core icon' : 'Replace with small core icon and compact spacing',
765
- fix: fixer => {
766
- return [...createPropFixes({
767
- ...fixArguments,
768
- metadata: {
769
- ...fixArguments.metadata,
770
- spacing: 'compact',
771
- size: 'small'
772
- },
773
- node,
774
- fixer
775
- }), ...createImportFix({
776
- ...fixArguments,
777
- fixer
778
- })];
779
- }
780
- }];
781
- } else {
782
- if (isInManualArray) {
783
- // provide suggestion if there is a manual error for the same import source and thus the legacy import can't be removed
784
- error.suggest = [{
785
- desc: 'Rename icon import, import from the new package, and update props.',
786
- fix: fixer => {
787
- return [...createPropFixes({
788
- ...fixArguments,
789
- node,
790
- fixer
791
- }), ...createImportFix({
792
- ...fixArguments,
793
- fixer
794
- })];
795
- }
796
- }];
797
- } else {
798
- // Update Guidance message for auto-fixing
799
- if (error.data) {
800
- 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.`;
801
- }
802
- // There should only be 1 import fix for each import source and thus only add this at the start of the list
803
- if (autoFixers.length === 0) {
804
- autoFixers.push(fixer => createImportFix({
805
- ...fixArguments,
806
- fixer
807
- }));
808
- }
809
- // Push the prop fix regardless
810
- autoFixers.push(fixer => createPropFixes({
811
- ...fixArguments,
812
- node,
813
- fixer
814
- }));
815
- }
816
- }
817
- // Add the error to the appliedErrorsForImport, ready to be thrown later
818
- appliedErrorsForImport.push(error);
819
- }
820
- // We want to have only 1 fix for each import source that is not in the manual array
821
- // NOTE: If in the manual array, suggestions have been applied above and autoFixers.length will be 0 which will mean no fix is added
822
- if (autoFixers.length > 0) {
823
- // Add the fix to only one of the errors in the list of errors from the current import source
824
- appliedErrorsForImport[0].fix = fixer => {
825
- return autoFixers.flatMap(autoFixer => autoFixer(fixer));
826
- };
827
- }
828
- // throw errors
829
- appliedErrorsForImport.forEach(error => {
830
- context.report(error);
831
- });
832
- }
833
- };
834
- function findProgramNode(node) {
835
- while (node && node.parent) {
836
- if (node.parent.type === 'Program') {
837
- return node.parent;
838
- }
839
- node = node.parent;
840
- }
841
- return null;
842
- }
843
- function alreadyHasImportedLocalName(programNode, localName, importPath) {
844
- if (!(programNode !== null && programNode !== void 0 && programNode.body)) {
845
- return false;
846
- }
847
- return programNode.body.some(stmt => {
848
- if (stmt.type === 'ImportDeclaration' && stmt.source.value === importPath) {
849
- return stmt.specifiers.some(s => s.local.name === localName);
850
- }
851
- return false;
852
- });
853
- }