@atlaskit/eslint-plugin-design-system 13.30.0 → 13.31.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 +17 -0
- package/README.md +75 -75
- package/dist/cjs/presets/all-flat.codegen.js +2 -2
- package/dist/cjs/presets/all.codegen.js +2 -2
- package/dist/cjs/rules/index.codegen.js +3 -3
- package/dist/cjs/rules/lozenge-badge-tag-labelling-system-migration/index.js +712 -0
- package/dist/es2019/presets/all-flat.codegen.js +2 -2
- package/dist/es2019/presets/all.codegen.js +2 -2
- package/dist/es2019/rules/index.codegen.js +3 -3
- package/dist/es2019/rules/lozenge-badge-tag-labelling-system-migration/index.js +702 -0
- package/dist/esm/presets/all-flat.codegen.js +2 -2
- package/dist/esm/presets/all.codegen.js +2 -2
- package/dist/esm/rules/index.codegen.js +3 -3
- package/dist/esm/rules/lozenge-badge-tag-labelling-system-migration/index.js +705 -0
- package/dist/types/presets/all-flat.codegen.d.ts +1 -1
- package/dist/types/presets/all.codegen.d.ts +1 -1
- package/dist/types/rules/index.codegen.d.ts +1 -1
- package/dist/types-ts4.5/presets/all-flat.codegen.d.ts +1 -1
- package/dist/types-ts4.5/presets/all.codegen.d.ts +1 -1
- package/dist/types-ts4.5/rules/index.codegen.d.ts +1 -1
- package/package.json +2 -2
- package/dist/cjs/rules/lozenge-isBold-and-lozenge-badge-appearance-migration/index.js +0 -332
- package/dist/es2019/rules/lozenge-isBold-and-lozenge-badge-appearance-migration/index.js +0 -324
- package/dist/esm/rules/lozenge-isBold-and-lozenge-badge-appearance-migration/index.js +0 -326
- /package/dist/types/rules/{lozenge-isBold-and-lozenge-badge-appearance-migration → lozenge-badge-tag-labelling-system-migration}/index.d.ts +0 -0
- /package/dist/types-ts4.5/rules/{lozenge-isBold-and-lozenge-badge-appearance-migration → lozenge-badge-tag-labelling-system-migration}/index.d.ts +0 -0
|
@@ -0,0 +1,705 @@
|
|
|
1
|
+
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
2
|
+
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
|
3
|
+
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
|
|
4
|
+
import { isNodeOfType } from 'eslint-codemod-utils';
|
|
5
|
+
import { createLintRule } from '../utils/create-rule';
|
|
6
|
+
var rule = createLintRule({
|
|
7
|
+
meta: {
|
|
8
|
+
name: 'lozenge-badge-tag-labelling-system-migration',
|
|
9
|
+
fixable: 'code',
|
|
10
|
+
type: 'suggestion',
|
|
11
|
+
docs: {
|
|
12
|
+
description: 'Helps migrate Lozenge isBold prop, Badge appearance values, and SimpleTag/RemovableTag components as part of the Labelling System Phase 1 migration.',
|
|
13
|
+
recommended: false,
|
|
14
|
+
severity: 'warn'
|
|
15
|
+
},
|
|
16
|
+
messages: {
|
|
17
|
+
updateAppearance: 'Update appearance value to new semantic value.',
|
|
18
|
+
migrateTag: 'Non-bold <Lozenge> variants should migrate to <Tag> component.',
|
|
19
|
+
manualReview: "Dynamic 'isBold' props require manual review before migration.",
|
|
20
|
+
dynamicLozengeAppearance: "Dynamic 'appearance' prop values require manual review before migrating to Tag. Please verify the appearance value and manually convert it to the appropriate color prop value.",
|
|
21
|
+
updateBadgeAppearance: 'Update Badge appearance value "{{oldValue}}" to new semantic value "{{newValue}}".',
|
|
22
|
+
dynamicBadgeAppearance: 'Dynamic appearance prop values require manual review to ensure they use the new semantic values: neutral, information, inverse, danger, success.'
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
create: function create(context) {
|
|
26
|
+
/**
|
|
27
|
+
* Contains a map of imported Lozenge components.
|
|
28
|
+
*/
|
|
29
|
+
var lozengeImports = {}; // local name -> import source
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Contains a map of imported Badge components.
|
|
33
|
+
*/
|
|
34
|
+
var badgeImports = {}; // local name -> import source
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Contains a map of imported Tag components (SimpleTag, RemovableTag, or default Tag imports).
|
|
38
|
+
* Maps local name to { type: 'SimpleTag' | 'RemovableTag' | 'Tag', source: string, node: ImportNode }
|
|
39
|
+
*/
|
|
40
|
+
var tagImports = {};
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Tracks which tag imports need to migrate to Tag (default) or AvatarTag (named)
|
|
44
|
+
* Maps local name to migration target: 'Tag' | 'AvatarTag'
|
|
45
|
+
*/
|
|
46
|
+
var tagMigrationTargets = {};
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Tracks import declaration nodes that need to be updated
|
|
50
|
+
*/
|
|
51
|
+
var importDeclarationsToUpdate = new Set();
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Contains a map of imported Avatar components from @atlaskit/avatar.
|
|
55
|
+
* Maps local name to import source
|
|
56
|
+
*/
|
|
57
|
+
var avatarImports = {};
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Contains a map of imported Tag and AvatarTag components from @atlaskit/tag.
|
|
61
|
+
* These are the new components that should not be migrated.
|
|
62
|
+
* Maps local name to import source
|
|
63
|
+
*/
|
|
64
|
+
var newTagImports = {};
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Check if a JSX attribute value is a literal false
|
|
68
|
+
*/
|
|
69
|
+
function isLiteralFalse(node) {
|
|
70
|
+
return node && node.type === 'JSXExpressionContainer' && node.expression && node.expression.type === 'Literal' && node.expression.value === false;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Check if a JSX attribute value is dynamic (not a static literal value)
|
|
75
|
+
* Can be used for any prop type (boolean, string, etc.)
|
|
76
|
+
*/
|
|
77
|
+
function isDynamicExpression(node) {
|
|
78
|
+
if (!node) {
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// If it's a plain literal (e.g., appearance="value"), it's not dynamic
|
|
83
|
+
if (node.type === 'Literal') {
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// If it's an expression container with a non-literal expression, it's dynamic
|
|
88
|
+
if (node.type === 'JSXExpressionContainer') {
|
|
89
|
+
var expr = node.expression;
|
|
90
|
+
return expr && expr.type !== 'Literal';
|
|
91
|
+
}
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Get all attributes as an object for easier manipulation
|
|
97
|
+
*/
|
|
98
|
+
function getAttributesMap(attributes) {
|
|
99
|
+
var map = {};
|
|
100
|
+
attributes.forEach(function (attr) {
|
|
101
|
+
if (attr.type === 'JSXAttribute' && attr.name.type === 'JSXIdentifier') {
|
|
102
|
+
map[attr.name.name] = attr;
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
return map;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Map old appearance values to new semantic appearance values
|
|
110
|
+
* Both Lozenge and Tag now use the same appearance prop with new semantic values
|
|
111
|
+
*/
|
|
112
|
+
function mapToNewAppearanceValue(oldValue) {
|
|
113
|
+
var mapping = {
|
|
114
|
+
success: 'success',
|
|
115
|
+
default: 'default',
|
|
116
|
+
removed: 'removed',
|
|
117
|
+
inprogress: 'inprogress',
|
|
118
|
+
new: 'new',
|
|
119
|
+
moved: 'moved'
|
|
120
|
+
};
|
|
121
|
+
return mapping[oldValue] || oldValue;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Map Lozenge appearance values to Tag color values
|
|
126
|
+
* Used when migrating Lozenge to Tag component
|
|
127
|
+
*/
|
|
128
|
+
function mapLozengeAppearanceToTagColor(appearanceValue) {
|
|
129
|
+
var mapping = {
|
|
130
|
+
success: 'lime',
|
|
131
|
+
default: 'gray',
|
|
132
|
+
removed: 'red',
|
|
133
|
+
inprogress: 'blue',
|
|
134
|
+
new: 'purple',
|
|
135
|
+
moved: 'yellow'
|
|
136
|
+
};
|
|
137
|
+
return mapping[appearanceValue] || appearanceValue;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Map Badge old appearance values to new semantic appearance values
|
|
142
|
+
*/
|
|
143
|
+
function mapBadgeToNewAppearanceValue(oldValue) {
|
|
144
|
+
var mapping = {
|
|
145
|
+
added: 'success',
|
|
146
|
+
removed: 'danger',
|
|
147
|
+
default: 'neutral',
|
|
148
|
+
primary: 'information',
|
|
149
|
+
primaryInverted: 'inverse',
|
|
150
|
+
important: 'danger'
|
|
151
|
+
};
|
|
152
|
+
return mapping[oldValue] || oldValue;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Map Tag color light variants to semantic color values
|
|
157
|
+
*/
|
|
158
|
+
function mapTagColorValue(oldValue) {
|
|
159
|
+
var mapping = {
|
|
160
|
+
limeLight: 'lime',
|
|
161
|
+
orangeLight: 'orange',
|
|
162
|
+
magentaLight: 'magenta',
|
|
163
|
+
greenLight: 'green',
|
|
164
|
+
blueLight: 'blue',
|
|
165
|
+
redLight: 'red',
|
|
166
|
+
purpleLight: 'purple',
|
|
167
|
+
greyLight: 'gray',
|
|
168
|
+
tealLight: 'teal',
|
|
169
|
+
yellowLight: 'yellow',
|
|
170
|
+
grey: 'gray'
|
|
171
|
+
};
|
|
172
|
+
return mapping[oldValue] || oldValue;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Check if elemBefore prop contains only an Avatar component from @atlaskit/avatar
|
|
177
|
+
* Returns the Avatar component name if it's from the avatar package, null otherwise
|
|
178
|
+
*/
|
|
179
|
+
function getAvatarComponentName(elemBeforeProp) {
|
|
180
|
+
if (!elemBeforeProp || !elemBeforeProp.value) {
|
|
181
|
+
return null;
|
|
182
|
+
}
|
|
183
|
+
var value = elemBeforeProp.value;
|
|
184
|
+
|
|
185
|
+
// Check for JSX element: <Avatar ... />
|
|
186
|
+
if (value.type === 'JSXElement' && value.openingElement.name.name === 'Avatar') {
|
|
187
|
+
var avatarName = value.openingElement.name.name;
|
|
188
|
+
if (avatarImports[avatarName]) {
|
|
189
|
+
return avatarName;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// Check for JSX expression container: {<Avatar ... />}
|
|
194
|
+
if (value.type === 'JSXExpressionContainer' && value.expression) {
|
|
195
|
+
// Direct JSX element: {<Avatar ... />}
|
|
196
|
+
if (value.expression.type === 'JSXElement' && value.expression.openingElement.name.name === 'Avatar') {
|
|
197
|
+
var _avatarName = value.expression.openingElement.name.name;
|
|
198
|
+
if (avatarImports[_avatarName]) {
|
|
199
|
+
return _avatarName;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Arrow function: {() => <Avatar ... />}
|
|
204
|
+
if (value.expression.type === 'ArrowFunctionExpression') {
|
|
205
|
+
var body = value.expression.body;
|
|
206
|
+
if (body.type === 'JSXElement' && body.openingElement.name.name === 'Avatar') {
|
|
207
|
+
var _avatarName2 = body.openingElement.name.name;
|
|
208
|
+
if (avatarImports[_avatarName2]) {
|
|
209
|
+
return _avatarName2;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
return null;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Check if color prop value needs mapping
|
|
219
|
+
*/
|
|
220
|
+
function colorNeedsMapping(colorProp) {
|
|
221
|
+
if (!(colorProp !== null && colorProp !== void 0 && colorProp.value)) {
|
|
222
|
+
return false;
|
|
223
|
+
}
|
|
224
|
+
var stringValue = extractStringValue(colorProp.value);
|
|
225
|
+
return stringValue !== null && typeof stringValue === 'string' && mapTagColorValue(stringValue) !== stringValue;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Extract the string value from a JSX attribute value
|
|
230
|
+
*/
|
|
231
|
+
function extractStringValue(attrValue) {
|
|
232
|
+
if (!attrValue) {
|
|
233
|
+
return null;
|
|
234
|
+
}
|
|
235
|
+
if (attrValue.type === 'Literal') {
|
|
236
|
+
return attrValue.value;
|
|
237
|
+
}
|
|
238
|
+
if (attrValue.type === 'JSXExpressionContainer' && attrValue.expression && attrValue.expression.type === 'Literal') {
|
|
239
|
+
return attrValue.expression.value;
|
|
240
|
+
}
|
|
241
|
+
return null;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Create a fixer function to replace an appearance prop value
|
|
246
|
+
* Handles both Literal and JSXExpressionContainer with Literal
|
|
247
|
+
*/
|
|
248
|
+
function createAppearanceFixer(attrValue, newValue) {
|
|
249
|
+
return function (fixer) {
|
|
250
|
+
if (!attrValue) {
|
|
251
|
+
return null;
|
|
252
|
+
}
|
|
253
|
+
if (attrValue.type === 'Literal') {
|
|
254
|
+
return fixer.replaceText(attrValue, "\"".concat(newValue, "\""));
|
|
255
|
+
}
|
|
256
|
+
if (attrValue.type === 'JSXExpressionContainer' && 'expression' in attrValue && attrValue.expression && attrValue.expression.type === 'Literal') {
|
|
257
|
+
return fixer.replaceText(attrValue.expression, "\"".concat(newValue, "\""));
|
|
258
|
+
}
|
|
259
|
+
return null;
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Generate the replacement JSX element text for Tag migration
|
|
265
|
+
* Handles both regular Tag and avatarTag migrations
|
|
266
|
+
*/
|
|
267
|
+
function generateTagReplacement(node) {
|
|
268
|
+
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
|
269
|
+
var sourceCode = context.getSourceCode();
|
|
270
|
+
var attributes = node.openingElement.attributes;
|
|
271
|
+
|
|
272
|
+
// Build new attributes array
|
|
273
|
+
var newAttributes = [];
|
|
274
|
+
attributes.forEach(function (attr) {
|
|
275
|
+
if (attr.type === 'JSXAttribute' && attr.name.type === 'JSXIdentifier') {
|
|
276
|
+
var attrName = attr.name.name;
|
|
277
|
+
if (attrName === 'isBold') {
|
|
278
|
+
// Skip isBold attribute
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
if (attrName === 'appearance') {
|
|
282
|
+
// For Lozenge migrations, convert appearance to color prop
|
|
283
|
+
// For SimpleTag/RemovableTag migrations, delete appearance prop
|
|
284
|
+
if (options.isLozengeMigration) {
|
|
285
|
+
// Map Lozenge appearance value to Tag color value and change prop name from appearance to color
|
|
286
|
+
var stringValue = extractStringValue(attr.value);
|
|
287
|
+
if (stringValue && typeof stringValue === 'string') {
|
|
288
|
+
var mappedColor = mapLozengeAppearanceToTagColor(stringValue);
|
|
289
|
+
newAttributes.push("color=\"".concat(mappedColor, "\""));
|
|
290
|
+
}
|
|
291
|
+
// If we can't extract the string value (dynamic expression), skip it
|
|
292
|
+
// Dynamic expressions should be caught earlier and require manual review
|
|
293
|
+
// This code path shouldn't be reached, but we skip to be safe
|
|
294
|
+
}
|
|
295
|
+
// For SimpleTag/RemovableTag migrations, skip appearance prop (delete it)
|
|
296
|
+
return;
|
|
297
|
+
}
|
|
298
|
+
if (attrName === 'color') {
|
|
299
|
+
// For avatar tag, skip color prop; for regular tag, map color value
|
|
300
|
+
// Note: Lozenge doesn't have a color prop, but Tag/SimpleTag/RemovableTag do
|
|
301
|
+
if (options.isAvatarTag) {
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
304
|
+
var _stringValue = extractStringValue(attr.value);
|
|
305
|
+
if (_stringValue && typeof _stringValue === 'string') {
|
|
306
|
+
var _mappedColor = mapTagColorValue(_stringValue);
|
|
307
|
+
newAttributes.push("color=\"".concat(_mappedColor, "\""));
|
|
308
|
+
} else {
|
|
309
|
+
// If we can't extract the string value, keep as-is
|
|
310
|
+
var value = attr.value ? sourceCode.getText(attr.value) : '';
|
|
311
|
+
newAttributes.push("color".concat(value ? "=".concat(value) : ''));
|
|
312
|
+
}
|
|
313
|
+
return;
|
|
314
|
+
}
|
|
315
|
+
if (attrName === 'elemBefore') {
|
|
316
|
+
// For avatar tag, rename elemBefore to avatar and use render props
|
|
317
|
+
if (options.isAvatarTag) {
|
|
318
|
+
var elemBeforeValue = attr.value;
|
|
319
|
+
var avatarElement = null;
|
|
320
|
+
|
|
321
|
+
// Extract Avatar element from various formats
|
|
322
|
+
if (elemBeforeValue.type === 'JSXElement') {
|
|
323
|
+
avatarElement = elemBeforeValue;
|
|
324
|
+
} else if (elemBeforeValue.type === 'JSXExpressionContainer') {
|
|
325
|
+
var expr = elemBeforeValue.expression;
|
|
326
|
+
// Direct JSX element: {<Avatar ... />}
|
|
327
|
+
if (expr.type === 'JSXElement') {
|
|
328
|
+
avatarElement = expr;
|
|
329
|
+
}
|
|
330
|
+
// Arrow function: {() => <Avatar ... />}
|
|
331
|
+
else if (expr.type === 'ArrowFunctionExpression' && expr.body.type === 'JSXElement') {
|
|
332
|
+
avatarElement = expr.body;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
if (avatarElement) {
|
|
336
|
+
// Generate render props: avatar={(props) => <Avatar {...props} ... />}
|
|
337
|
+
var avatarElementText = sourceCode.getText(avatarElement);
|
|
338
|
+
// Add {...props} spread to the Avatar element attributes
|
|
339
|
+
var avatarWithProps = avatarElementText.replace(/<Avatar\s/, '<Avatar {...props} ');
|
|
340
|
+
newAttributes.push("avatar={(props) => ".concat(avatarWithProps, "}"));
|
|
341
|
+
}
|
|
342
|
+
return;
|
|
343
|
+
}
|
|
344
|
+
// For regular tag, keep elemBefore as-is
|
|
345
|
+
newAttributes.push(sourceCode.getText(attr));
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
// Keep all other attributes
|
|
350
|
+
newAttributes.push(sourceCode.getText(attr));
|
|
351
|
+
} else if (attr.type === 'JSXSpreadAttribute') {
|
|
352
|
+
// Keep spread attributes
|
|
353
|
+
newAttributes.push(sourceCode.getText(attr));
|
|
354
|
+
}
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
// Add isRemovable={false} for SimpleTag migrations and Lozenge migrations
|
|
358
|
+
if (options.isSimpleTag || options.isLozengeMigration) {
|
|
359
|
+
newAttributes.push('isRemovable={false}');
|
|
360
|
+
}
|
|
361
|
+
var attributesText = newAttributes.length > 0 ? " ".concat(newAttributes.join(' ')) : '';
|
|
362
|
+
var children = node.children.length > 0 ? sourceCode.getText().slice(node.openingElement.range[1], node.closingElement ? node.closingElement.range[0] : node.range[1]) : '';
|
|
363
|
+
var componentName = options.preserveComponentName ? node.openingElement.name.name : options.isAvatarTag ? 'AvatarTag' : 'Tag';
|
|
364
|
+
if (node.closingElement) {
|
|
365
|
+
return "<".concat(componentName).concat(attributesText, ">").concat(children, "</").concat(componentName, ">");
|
|
366
|
+
} else {
|
|
367
|
+
return "<".concat(componentName).concat(attributesText, " />");
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
return {
|
|
371
|
+
ImportDeclaration: function ImportDeclaration(node) {
|
|
372
|
+
var moduleSource = node.source.value;
|
|
373
|
+
if (typeof moduleSource === 'string') {
|
|
374
|
+
// Track Lozenge imports
|
|
375
|
+
if (moduleSource === '@atlaskit/lozenge' || moduleSource.startsWith('@atlaskit/lozenge')) {
|
|
376
|
+
node.specifiers.forEach(function (spec) {
|
|
377
|
+
if (spec.type === 'ImportDefaultSpecifier') {
|
|
378
|
+
lozengeImports[spec.local.name] = moduleSource;
|
|
379
|
+
} else if (spec.type === 'ImportSpecifier' && spec.imported.type === 'Identifier') {
|
|
380
|
+
if (spec.imported.name === 'Lozenge') {
|
|
381
|
+
lozengeImports[spec.local.name] = moduleSource;
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
});
|
|
385
|
+
}
|
|
386
|
+
// Track Badge imports
|
|
387
|
+
if (moduleSource === '@atlaskit/badge' || moduleSource.startsWith('@atlaskit/badge')) {
|
|
388
|
+
node.specifiers.forEach(function (spec) {
|
|
389
|
+
if (spec.type === 'ImportDefaultSpecifier') {
|
|
390
|
+
badgeImports[spec.local.name] = moduleSource;
|
|
391
|
+
} else if (spec.type === 'ImportSpecifier' && spec.imported.type === 'Identifier') {
|
|
392
|
+
if (spec.imported.name === 'Badge' || spec.imported.name === 'default') {
|
|
393
|
+
badgeImports[spec.local.name] = moduleSource;
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
});
|
|
397
|
+
}
|
|
398
|
+
// Track Tag imports (SimpleTag, RemovableTag only - not the new Tag component)
|
|
399
|
+
if (moduleSource === '@atlaskit/tag' || moduleSource.startsWith('@atlaskit/tag')) {
|
|
400
|
+
node.specifiers.forEach(function (spec) {
|
|
401
|
+
if (spec.type === 'ImportDefaultSpecifier') {
|
|
402
|
+
// Check for default imports from subpaths and main package
|
|
403
|
+
if (moduleSource === '@atlaskit/tag/simple-tag') {
|
|
404
|
+
// Default import from @atlaskit/tag/simple-tag is a SimpleTag
|
|
405
|
+
tagImports[spec.local.name] = {
|
|
406
|
+
type: 'SimpleTag',
|
|
407
|
+
source: moduleSource,
|
|
408
|
+
node: _objectSpread(_objectSpread({}, spec), {}, {
|
|
409
|
+
parent: node
|
|
410
|
+
})
|
|
411
|
+
};
|
|
412
|
+
importDeclarationsToUpdate.add(node);
|
|
413
|
+
} else if (moduleSource === '@atlaskit/tag/removable-tag' || moduleSource === '@atlaskit/tag') {
|
|
414
|
+
// Default import from @atlaskit/tag/removable-tag or @atlaskit/tag is a RemovableTag
|
|
415
|
+
tagImports[spec.local.name] = {
|
|
416
|
+
type: 'RemovableTag',
|
|
417
|
+
source: moduleSource,
|
|
418
|
+
node: _objectSpread(_objectSpread({}, spec), {}, {
|
|
419
|
+
parent: node
|
|
420
|
+
})
|
|
421
|
+
};
|
|
422
|
+
importDeclarationsToUpdate.add(node);
|
|
423
|
+
}
|
|
424
|
+
} else if (spec.type === 'ImportSpecifier' && spec.imported.type === 'Identifier') {
|
|
425
|
+
var importName = spec.imported.name;
|
|
426
|
+
if (importName === 'SimpleTag' || importName === 'RemovableTag') {
|
|
427
|
+
tagImports[spec.local.name] = {
|
|
428
|
+
type: importName,
|
|
429
|
+
source: moduleSource,
|
|
430
|
+
node: _objectSpread(_objectSpread({}, spec), {}, {
|
|
431
|
+
parent: node
|
|
432
|
+
})
|
|
433
|
+
};
|
|
434
|
+
// Mark this import declaration for potential updates
|
|
435
|
+
importDeclarationsToUpdate.add(node);
|
|
436
|
+
} else if (importName === 'AvatarTag') {
|
|
437
|
+
// Track new AvatarTag component - it should not be migrated
|
|
438
|
+
newTagImports[spec.local.name] = moduleSource;
|
|
439
|
+
}
|
|
440
|
+
// Note: Tag from named imports is not skipped - it may still need migration
|
|
441
|
+
// (e.g., if it has appearance prop or other old props)
|
|
442
|
+
}
|
|
443
|
+
});
|
|
444
|
+
}
|
|
445
|
+
// Track Avatar imports
|
|
446
|
+
if (moduleSource === '@atlaskit/avatar' || moduleSource.startsWith('@atlaskit/avatar')) {
|
|
447
|
+
node.specifiers.forEach(function (spec) {
|
|
448
|
+
if (spec.type === 'ImportDefaultSpecifier') {
|
|
449
|
+
avatarImports[spec.local.name] = moduleSource;
|
|
450
|
+
} else if (spec.type === 'ImportSpecifier' && spec.imported.type === 'Identifier') {
|
|
451
|
+
if (spec.imported.name === 'Avatar') {
|
|
452
|
+
avatarImports[spec.local.name] = moduleSource;
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
});
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
},
|
|
459
|
+
JSXElement: function JSXElement(node) {
|
|
460
|
+
if (!isNodeOfType(node, 'JSXElement')) {
|
|
461
|
+
return;
|
|
462
|
+
}
|
|
463
|
+
if (!isNodeOfType(node.openingElement.name, 'JSXIdentifier')) {
|
|
464
|
+
return;
|
|
465
|
+
}
|
|
466
|
+
var elementName = node.openingElement.name.name;
|
|
467
|
+
|
|
468
|
+
// Skip new AvatarTag component - it should not be migrated
|
|
469
|
+
if (newTagImports[elementName]) {
|
|
470
|
+
return;
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
// Handle SimpleTag, RemovableTag, and Tag migrations
|
|
474
|
+
if (tagImports[elementName]) {
|
|
475
|
+
var tagImportInfo = tagImports[elementName];
|
|
476
|
+
var _attributesMap = getAttributesMap(node.openingElement.attributes);
|
|
477
|
+
var elemBeforeProp = _attributesMap.elemBefore,
|
|
478
|
+
avatarProp = _attributesMap.avatar,
|
|
479
|
+
_appearanceProp = _attributesMap.appearance,
|
|
480
|
+
colorProp = _attributesMap.color;
|
|
481
|
+
|
|
482
|
+
// For default import from @atlaskit/tag, check if it's already the new Tag
|
|
483
|
+
if (tagImportInfo.type === 'RemovableTag' && tagImportInfo.source === '@atlaskit/tag') {
|
|
484
|
+
// If using avatar prop, it's already the new Tag
|
|
485
|
+
if (avatarProp) {
|
|
486
|
+
return;
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
// Check if component name is already correct and nothing needs migration
|
|
490
|
+
if (elementName === 'Tag' || elementName === 'AvatarTag') {
|
|
491
|
+
var needsNameChange = false;
|
|
492
|
+
var needsMigration = needsNameChange || _appearanceProp || colorNeedsMapping(colorProp);
|
|
493
|
+
if (!needsMigration) {
|
|
494
|
+
// Still need to check elemBefore for Avatar
|
|
495
|
+
if (elemBeforeProp) {
|
|
496
|
+
var _hasAvatarInElemBefore = getAvatarComponentName(elemBeforeProp) !== null;
|
|
497
|
+
if (_hasAvatarInElemBefore) {
|
|
498
|
+
// Has Avatar in elemBefore, needs migration to AvatarTag
|
|
499
|
+
} else {
|
|
500
|
+
// No Avatar, nothing to migrate
|
|
501
|
+
return;
|
|
502
|
+
}
|
|
503
|
+
} else {
|
|
504
|
+
// No elemBefore, nothing to migrate
|
|
505
|
+
return;
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
// If we get here, something needs migration
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
// Determine migration target based on elemBefore containing Avatar
|
|
513
|
+
var hasAvatarInElemBefore = elemBeforeProp ? getAvatarComponentName(elemBeforeProp) !== null : false;
|
|
514
|
+
var migrationTarget = hasAvatarInElemBefore ? 'AvatarTag' : 'Tag';
|
|
515
|
+
|
|
516
|
+
// Record the migration target for this import
|
|
517
|
+
tagMigrationTargets[elementName] = migrationTarget;
|
|
518
|
+
|
|
519
|
+
// Migrate the JSX element
|
|
520
|
+
context.report({
|
|
521
|
+
node: node,
|
|
522
|
+
messageId: 'migrateTag',
|
|
523
|
+
fix: function fix(fixer) {
|
|
524
|
+
var _tagImportInfo$node, _tagImportInfo$node2, _tagImportInfo$node3, _tagImportInfo$node4, _tagImportInfo$node5, _tagImportInfo$node6;
|
|
525
|
+
var fixes = [];
|
|
526
|
+
|
|
527
|
+
// Fix the JSX element
|
|
528
|
+
var replacement = generateTagReplacement(node, {
|
|
529
|
+
isAvatarTag: hasAvatarInElemBefore,
|
|
530
|
+
isSimpleTag: !hasAvatarInElemBefore && tagImportInfo.type === 'SimpleTag'
|
|
531
|
+
});
|
|
532
|
+
fixes.push(fixer.replaceText(node, replacement));
|
|
533
|
+
|
|
534
|
+
// Fix the import statement for named imports, subpath default imports, and main package default imports
|
|
535
|
+
var isSubpathImport = ((_tagImportInfo$node = tagImportInfo.node) === null || _tagImportInfo$node === void 0 || (_tagImportInfo$node = _tagImportInfo$node.parent) === null || _tagImportInfo$node === void 0 || (_tagImportInfo$node = _tagImportInfo$node.source) === null || _tagImportInfo$node === void 0 ? void 0 : _tagImportInfo$node.value) === '@atlaskit/tag/simple-tag' || ((_tagImportInfo$node2 = tagImportInfo.node) === null || _tagImportInfo$node2 === void 0 || (_tagImportInfo$node2 = _tagImportInfo$node2.parent) === null || _tagImportInfo$node2 === void 0 || (_tagImportInfo$node2 = _tagImportInfo$node2.source) === null || _tagImportInfo$node2 === void 0 ? void 0 : _tagImportInfo$node2.value) === '@atlaskit/tag/removable-tag';
|
|
536
|
+
var isMainPackageDefaultImport = ((_tagImportInfo$node3 = tagImportInfo.node) === null || _tagImportInfo$node3 === void 0 || (_tagImportInfo$node3 = _tagImportInfo$node3.parent) === null || _tagImportInfo$node3 === void 0 || (_tagImportInfo$node3 = _tagImportInfo$node3.source) === null || _tagImportInfo$node3 === void 0 ? void 0 : _tagImportInfo$node3.value) === '@atlaskit/tag' && ((_tagImportInfo$node4 = tagImportInfo.node) === null || _tagImportInfo$node4 === void 0 ? void 0 : _tagImportInfo$node4.type) === 'ImportDefaultSpecifier';
|
|
537
|
+
if (isSubpathImport || isMainPackageDefaultImport || ((_tagImportInfo$node5 = tagImportInfo.node) === null || _tagImportInfo$node5 === void 0 || (_tagImportInfo$node5 = _tagImportInfo$node5.parent) === null || _tagImportInfo$node5 === void 0 || (_tagImportInfo$node5 = _tagImportInfo$node5.source) === null || _tagImportInfo$node5 === void 0 ? void 0 : _tagImportInfo$node5.value) === '@atlaskit/tag' && ((_tagImportInfo$node6 = tagImportInfo.node) === null || _tagImportInfo$node6 === void 0 ? void 0 : _tagImportInfo$node6.type) === 'ImportSpecifier') {
|
|
538
|
+
var _tagImportInfo$node7;
|
|
539
|
+
var importNode = (_tagImportInfo$node7 = tagImportInfo.node) === null || _tagImportInfo$node7 === void 0 ? void 0 : _tagImportInfo$node7.parent;
|
|
540
|
+
if (importNode) {
|
|
541
|
+
var sourceCode = context.getSourceCode();
|
|
542
|
+
var mainModuleSource = '@atlaskit/tag';
|
|
543
|
+
|
|
544
|
+
// Get all other specifiers that are not SimpleTag or RemovableTag
|
|
545
|
+
// For subpath imports and main package default imports, exclude the default specifier itself
|
|
546
|
+
var otherSpecifiers = importNode.specifiers.filter(function (spec) {
|
|
547
|
+
// Skip default specifiers from subpath imports and main package - they're being replaced
|
|
548
|
+
if (spec.type === 'ImportDefaultSpecifier' && (isSubpathImport || isMainPackageDefaultImport)) {
|
|
549
|
+
return false;
|
|
550
|
+
}
|
|
551
|
+
if (spec.type === 'ImportSpecifier' && spec.imported.type === 'Identifier') {
|
|
552
|
+
var importName = spec.imported.name;
|
|
553
|
+
return importName !== 'SimpleTag' && importName !== 'RemovableTag';
|
|
554
|
+
}
|
|
555
|
+
return false;
|
|
556
|
+
}).map(function (spec) {
|
|
557
|
+
return sourceCode.getText(spec);
|
|
558
|
+
});
|
|
559
|
+
var newImportText = '';
|
|
560
|
+
if (migrationTarget === 'Tag') {
|
|
561
|
+
if (otherSpecifiers.length > 0) {
|
|
562
|
+
newImportText = "import Tag, { ".concat(otherSpecifiers.join(', '), " } from '").concat(mainModuleSource, "';");
|
|
563
|
+
} else {
|
|
564
|
+
newImportText = "import Tag from '".concat(mainModuleSource, "';");
|
|
565
|
+
}
|
|
566
|
+
} else if (migrationTarget === 'AvatarTag') {
|
|
567
|
+
if (otherSpecifiers.length > 0) {
|
|
568
|
+
newImportText = "import { AvatarTag, ".concat(otherSpecifiers.join(', '), " } from '").concat(mainModuleSource, "';");
|
|
569
|
+
} else {
|
|
570
|
+
newImportText = "import { AvatarTag } from '".concat(mainModuleSource, "';");
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
if (newImportText) {
|
|
574
|
+
fixes.push(fixer.replaceText(importNode, newImportText));
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
return fixes.length === 1 ? fixes[0] : fixes;
|
|
579
|
+
}
|
|
580
|
+
});
|
|
581
|
+
return;
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
// Handle Badge components
|
|
585
|
+
if (badgeImports[elementName]) {
|
|
586
|
+
// Find the appearance prop
|
|
587
|
+
var _appearanceProp2 = node.openingElement.attributes.find(function (attr) {
|
|
588
|
+
return attr.type === 'JSXAttribute' && attr.name.type === 'JSXIdentifier' && attr.name.name === 'appearance';
|
|
589
|
+
});
|
|
590
|
+
if (!_appearanceProp2 || _appearanceProp2.type !== 'JSXAttribute') {
|
|
591
|
+
// No appearance prop or it's a spread attribute, nothing to migrate
|
|
592
|
+
return;
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
// Check if it's a dynamic expression
|
|
596
|
+
if (isDynamicExpression(_appearanceProp2.value)) {
|
|
597
|
+
context.report({
|
|
598
|
+
node: _appearanceProp2,
|
|
599
|
+
messageId: 'dynamicBadgeAppearance'
|
|
600
|
+
});
|
|
601
|
+
return;
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
// Extract the string value
|
|
605
|
+
var stringValue = extractStringValue(_appearanceProp2.value);
|
|
606
|
+
if (stringValue && typeof stringValue === 'string') {
|
|
607
|
+
var mappedValue = mapBadgeToNewAppearanceValue(stringValue);
|
|
608
|
+
if (mappedValue !== stringValue) {
|
|
609
|
+
context.report({
|
|
610
|
+
node: _appearanceProp2,
|
|
611
|
+
messageId: 'updateBadgeAppearance',
|
|
612
|
+
data: {
|
|
613
|
+
oldValue: stringValue,
|
|
614
|
+
newValue: mappedValue
|
|
615
|
+
},
|
|
616
|
+
fix: createAppearanceFixer(_appearanceProp2.value, mappedValue)
|
|
617
|
+
});
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
return;
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
// Only process if this is a Lozenge component we've imported
|
|
624
|
+
if (!lozengeImports[elementName]) {
|
|
625
|
+
return;
|
|
626
|
+
}
|
|
627
|
+
var attributesMap = getAttributesMap(node.openingElement.attributes);
|
|
628
|
+
var appearanceProp = attributesMap.appearance;
|
|
629
|
+
var isBoldProp = attributesMap.isBold;
|
|
630
|
+
|
|
631
|
+
// Handle appearance prop value migration
|
|
632
|
+
if (appearanceProp) {
|
|
633
|
+
var shouldMigrateToTag = !isBoldProp || isLiteralFalse(isBoldProp.value);
|
|
634
|
+
if (!shouldMigrateToTag) {
|
|
635
|
+
// Only update appearance values for Lozenge components that stay as Lozenge
|
|
636
|
+
var _stringValue2 = extractStringValue(appearanceProp.value);
|
|
637
|
+
if (_stringValue2 && typeof _stringValue2 === 'string') {
|
|
638
|
+
var _mappedValue = mapToNewAppearanceValue(_stringValue2);
|
|
639
|
+
if (_mappedValue !== _stringValue2) {
|
|
640
|
+
context.report({
|
|
641
|
+
node: appearanceProp,
|
|
642
|
+
messageId: 'updateAppearance',
|
|
643
|
+
fix: createAppearanceFixer(appearanceProp.value, _mappedValue)
|
|
644
|
+
});
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
// Handle isBold prop and Tag migration
|
|
651
|
+
if (isBoldProp) {
|
|
652
|
+
if (isLiteralFalse(isBoldProp.value)) {
|
|
653
|
+
// isBold={false} should migrate to Tag
|
|
654
|
+
// Check if appearance is dynamic - if so, require manual review
|
|
655
|
+
if (appearanceProp && isDynamicExpression(appearanceProp.value)) {
|
|
656
|
+
context.report({
|
|
657
|
+
node: appearanceProp,
|
|
658
|
+
messageId: 'dynamicLozengeAppearance'
|
|
659
|
+
});
|
|
660
|
+
return;
|
|
661
|
+
}
|
|
662
|
+
context.report({
|
|
663
|
+
node: node,
|
|
664
|
+
messageId: 'migrateTag',
|
|
665
|
+
fix: function fix(fixer) {
|
|
666
|
+
var replacement = generateTagReplacement(node, {
|
|
667
|
+
isLozengeMigration: true
|
|
668
|
+
});
|
|
669
|
+
return fixer.replaceText(node, replacement);
|
|
670
|
+
}
|
|
671
|
+
});
|
|
672
|
+
} else if (isDynamicExpression(isBoldProp.value)) {
|
|
673
|
+
// Dynamic isBold requires manual review
|
|
674
|
+
context.report({
|
|
675
|
+
node: isBoldProp,
|
|
676
|
+
messageId: 'manualReview'
|
|
677
|
+
});
|
|
678
|
+
}
|
|
679
|
+
// isBold={true} or isBold (implicit true) - no action needed
|
|
680
|
+
} else {
|
|
681
|
+
// No isBold prop means implicit false, should migrate to Tag
|
|
682
|
+
// Check if appearance is dynamic - if so, require manual review
|
|
683
|
+
if (appearanceProp && isDynamicExpression(appearanceProp.value)) {
|
|
684
|
+
context.report({
|
|
685
|
+
node: appearanceProp,
|
|
686
|
+
messageId: 'dynamicLozengeAppearance'
|
|
687
|
+
});
|
|
688
|
+
return;
|
|
689
|
+
}
|
|
690
|
+
context.report({
|
|
691
|
+
node: node,
|
|
692
|
+
messageId: 'migrateTag',
|
|
693
|
+
fix: function fix(fixer) {
|
|
694
|
+
var replacement = generateTagReplacement(node, {
|
|
695
|
+
isLozengeMigration: true
|
|
696
|
+
});
|
|
697
|
+
return fixer.replaceText(node, replacement);
|
|
698
|
+
}
|
|
699
|
+
});
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
};
|
|
703
|
+
}
|
|
704
|
+
});
|
|
705
|
+
export default rule;
|