@entur/typography 1.10.0-beta.1 โ 1.10.0-beta.11
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/README.md +12 -11
- package/dist/beta/ListItemBeta.d.ts +16 -0
- package/dist/beta/NumberedListBeta.d.ts +16 -0
- package/dist/beta/UnorderedListBeta.d.ts +14 -0
- package/dist/beta/index.d.ts +3 -0
- package/dist/beta/utils.d.ts +2 -1
- package/dist/index.d.ts +3 -0
- package/dist/styles.css +78 -0
- package/dist/typography.cjs.development.js +125 -54
- package/dist/typography.cjs.development.js.map +1 -1
- package/dist/typography.cjs.production.min.js +1 -1
- package/dist/typography.cjs.production.min.js.map +1 -1
- package/dist/typography.esm.js +123 -55
- package/dist/typography.esm.js.map +1 -1
- package/package.json +6 -2
- package/scripts/migrate-typography.js +593 -155
|
@@ -14,25 +14,25 @@
|
|
|
14
14
|
* * <Heading1> becomes <Heading as="h1" variant="title-1">
|
|
15
15
|
* * <Paragraph> becomes <Text variant="paragraph">
|
|
16
16
|
* * <Link> becomes <LinkBeta>
|
|
17
|
+
* * <Blockquote> becomes <BlockquoteBeta>
|
|
18
|
+
* * <BlockquoteFooter> becomes <BlockquoteFooterBeta>
|
|
19
|
+
* * <UnorderedList> becomes <UnorderedListBeta>
|
|
20
|
+
* * <NumberedList> becomes <NumberedListBeta>
|
|
21
|
+
* * <ListItem> becomes <ListItemBeta>
|
|
17
22
|
* * Props may need updates (e.g., different prop names)
|
|
18
23
|
* * Styling classes may change
|
|
19
24
|
* * Test thoroughly after migration!
|
|
20
25
|
*
|
|
21
|
-
|
|
22
|
-
* - Only updates import paths from '@entur/typography' to '@entur/typography'
|
|
23
|
-
* - Keeps your existing component usage unchanged
|
|
24
|
-
* - Minimal risk, allows gradual migration
|
|
25
|
-
* - You can manually update components later
|
|
26
|
+
|
|
26
27
|
*
|
|
27
28
|
* Usage:
|
|
28
29
|
* 1. Run this script in your project root
|
|
29
|
-
* 2. Choose your migration mode (complete
|
|
30
|
+
* 2. Choose your migration mode (complete)
|
|
30
31
|
* 3. Update your styles as needed
|
|
31
32
|
* 4. Test your application thoroughly
|
|
32
33
|
*
|
|
33
34
|
* Options:
|
|
34
35
|
* --dry-run Show what would be changed without modifying files
|
|
35
|
-
* --import-only Import-only migration: update import paths only
|
|
36
36
|
*
|
|
37
37
|
* Environment Variables:
|
|
38
38
|
* TYPOGRAPHY_MIGRATION_DIRS Comma-separated list of directories to scan
|
|
@@ -72,6 +72,31 @@ try {
|
|
|
72
72
|
const OLD_IMPORT = '@entur/typography';
|
|
73
73
|
const BETA_IMPORT = '@entur/typography';
|
|
74
74
|
|
|
75
|
+
// Enhanced warning detection patterns - only truly problematic patterns
|
|
76
|
+
const PROBLEMATIC_PATTERNS = {
|
|
77
|
+
// Style conflicts that will cause issues
|
|
78
|
+
styleMarginConflict: /style=.*margin=/g,
|
|
79
|
+
styleSpacingConflict: /style=.*spacing=/g,
|
|
80
|
+
|
|
81
|
+
// Invalid HTML structure
|
|
82
|
+
nestedTypography: /<Text[^>]*>.*<Text[^>]*>/g,
|
|
83
|
+
|
|
84
|
+
// Accessibility issues
|
|
85
|
+
missingAsProps: /<Heading[^>]*>(?!.*\bas=)/g,
|
|
86
|
+
|
|
87
|
+
// Semantic HTML mismatches
|
|
88
|
+
semanticMismatch: /<Heading[^>]*as="([^"]*)"[^>]*variant="([^"]*)"/g,
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
// Warning severity levels
|
|
92
|
+
const WARNING_CATEGORIES = {
|
|
93
|
+
CRITICAL: 'critical', // Will break functionality
|
|
94
|
+
HIGH: 'high', // Likely to cause issues
|
|
95
|
+
MEDIUM: 'medium', // May cause styling issues
|
|
96
|
+
LOW: 'low', // Best practice suggestions
|
|
97
|
+
INFO: 'info', // Informational only
|
|
98
|
+
};
|
|
99
|
+
|
|
75
100
|
// =============================================================================
|
|
76
101
|
// ๐ฏ MIGRATION FOLDERS CONFIGURATION
|
|
77
102
|
// =============================================================================
|
|
@@ -114,6 +139,153 @@ function validateDirectoryPath(dir) {
|
|
|
114
139
|
return !path.isAbsolute(dir) && !dir.includes('..') && !dir.includes('~');
|
|
115
140
|
}
|
|
116
141
|
|
|
142
|
+
// Enhanced file analysis for better warning detection - only truly problematic patterns
|
|
143
|
+
function analyzeFile(filePath, content) {
|
|
144
|
+
const analysis = {
|
|
145
|
+
hasStyleConflicts: false,
|
|
146
|
+
hasNestedTypography: false,
|
|
147
|
+
hasAccessibilityIssues: false,
|
|
148
|
+
hasSemanticMismatches: false,
|
|
149
|
+
lineNumbers: {},
|
|
150
|
+
suggestions: [],
|
|
151
|
+
warnings: [],
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
// Line-by-line analysis for better context
|
|
155
|
+
content.split('\n').forEach((line, index) => {
|
|
156
|
+
const lineNum = index + 1;
|
|
157
|
+
|
|
158
|
+
// Check for style conflicts (style + margin/spacing)
|
|
159
|
+
if (
|
|
160
|
+
line.match(PROBLEMATIC_PATTERNS.styleMarginConflict) ||
|
|
161
|
+
line.match(PROBLEMATIC_PATTERNS.styleSpacingConflict)
|
|
162
|
+
) {
|
|
163
|
+
analysis.hasStyleConflicts = true;
|
|
164
|
+
analysis.lineNumbers.styleConflicts = (
|
|
165
|
+
analysis.lineNumbers.styleConflicts || []
|
|
166
|
+
).concat(lineNum);
|
|
167
|
+
|
|
168
|
+
// Generate warning message
|
|
169
|
+
analysis.warnings.push(
|
|
170
|
+
`Line ${lineNum}: Style conflicts detected - component has both style and margin/spacing props`,
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// Check for nested typography components (invalid HTML)
|
|
175
|
+
if (line.match(PROBLEMATIC_PATTERNS.nestedTypography)) {
|
|
176
|
+
analysis.hasNestedTypography = true;
|
|
177
|
+
analysis.lineNumbers.nestedTypography = (
|
|
178
|
+
analysis.lineNumbers.nestedTypography || []
|
|
179
|
+
).concat(lineNum);
|
|
180
|
+
|
|
181
|
+
// Generate warning message
|
|
182
|
+
analysis.warnings.push(
|
|
183
|
+
`Line ${lineNum}: Nested typography components detected - invalid HTML structure`,
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Check for missing as props (accessibility issue)
|
|
188
|
+
if (line.match(PROBLEMATIC_PATTERNS.missingAsProps)) {
|
|
189
|
+
analysis.hasAccessibilityIssues = true;
|
|
190
|
+
analysis.lineNumbers.missingAsProps = (
|
|
191
|
+
analysis.lineNumbers.missingAsProps || []
|
|
192
|
+
).concat(lineNum);
|
|
193
|
+
|
|
194
|
+
// Generate warning message
|
|
195
|
+
analysis.warnings.push(
|
|
196
|
+
`Line ${lineNum}: Missing 'as' prop - accessibility issue for Heading component`,
|
|
197
|
+
);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// Check for semantic mismatches (e.g., h1 with subtitle variant)
|
|
201
|
+
if (line.match(PROBLEMATIC_PATTERNS.semanticMismatch)) {
|
|
202
|
+
analysis.hasSemanticMismatches = true;
|
|
203
|
+
analysis.lineNumbers.semanticMismatches = (
|
|
204
|
+
analysis.lineNumbers.semanticMismatches || []
|
|
205
|
+
).concat(lineNum);
|
|
206
|
+
|
|
207
|
+
// Generate warning message
|
|
208
|
+
analysis.warnings.push(
|
|
209
|
+
`Line ${lineNum}: Semantic mismatch detected - heading level and variant combination may be incorrect`,
|
|
210
|
+
);
|
|
211
|
+
}
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
return analysis;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// Generate enhanced warnings with context and solutions
|
|
218
|
+
function generateWarningWithSolution(warning, context, filePath, lineNumber) {
|
|
219
|
+
const severity = determineSeverity(warning);
|
|
220
|
+
const suggestion = generateSuggestion(warning, context);
|
|
221
|
+
const codeExample = generateCodeExample(warning);
|
|
222
|
+
|
|
223
|
+
return {
|
|
224
|
+
message: warning,
|
|
225
|
+
severity,
|
|
226
|
+
suggestion,
|
|
227
|
+
codeExample,
|
|
228
|
+
file: filePath,
|
|
229
|
+
line: lineNumber,
|
|
230
|
+
documentation: getRelevantDocs(warning),
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// Determine warning severity based on content
|
|
235
|
+
function determineSeverity(warning) {
|
|
236
|
+
if (warning.includes('will break') || warning.includes('fatal'))
|
|
237
|
+
return WARNING_CATEGORIES.CRITICAL;
|
|
238
|
+
if (warning.includes('conflict') || warning.includes('override'))
|
|
239
|
+
return WARNING_CATEGORIES.HIGH;
|
|
240
|
+
if (warning.includes('may cause') || warning.includes('styling'))
|
|
241
|
+
return WARNING_CATEGORIES.MEDIUM;
|
|
242
|
+
if (warning.includes('best practice') || warning.includes('consider'))
|
|
243
|
+
return WARNING_CATEGORIES.LOW;
|
|
244
|
+
return WARNING_CATEGORIES.INFO;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// Generate actionable suggestions
|
|
248
|
+
function generateSuggestion(warning, context) {
|
|
249
|
+
if (warning.includes('style and margin')) {
|
|
250
|
+
return 'Remove the margin prop as it will be overridden by inline styles. Use spacing prop instead.';
|
|
251
|
+
}
|
|
252
|
+
if (warning.includes('missing variant')) {
|
|
253
|
+
return 'Add a variant prop to ensure consistent styling. Example: variant="title-1"';
|
|
254
|
+
}
|
|
255
|
+
if (warning.includes('nested typography')) {
|
|
256
|
+
return 'Avoid nesting Text components. Use spans or other inline elements for emphasis.';
|
|
257
|
+
}
|
|
258
|
+
if (warning.includes('deprecated margin')) {
|
|
259
|
+
return 'Replace margin prop with spacing prop for better consistency.';
|
|
260
|
+
}
|
|
261
|
+
return 'Review the component for potential styling conflicts.';
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// Generate code examples for fixes
|
|
265
|
+
function generateCodeExample(warning) {
|
|
266
|
+
if (warning.includes('style and margin')) {
|
|
267
|
+
return '// Before: <Text style={{color: "red"}} margin="bottom">\n// After: <Text style={{color: "red"}} spacing="bottom">';
|
|
268
|
+
}
|
|
269
|
+
if (warning.includes('missing variant')) {
|
|
270
|
+
return '// Before: <Heading as="h1">Title</Heading>\n// After: <Heading as="h1" variant="title-1">Title</Heading>';
|
|
271
|
+
}
|
|
272
|
+
if (warning.includes('nested typography')) {
|
|
273
|
+
return '// Before: <Text>Hello <Text>World</Text></Text>\n// After: <Text>Hello <span>World</span></Text>';
|
|
274
|
+
}
|
|
275
|
+
return '';
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// Get relevant documentation links
|
|
279
|
+
function getRelevantDocs(warning) {
|
|
280
|
+
if (warning.includes('variant'))
|
|
281
|
+
return 'https://linje.entur.no/komponenter/ressurser/typography-beta#heading-variants';
|
|
282
|
+
if (warning.includes('spacing'))
|
|
283
|
+
return 'https://linje.entur.no/komponenter/ressurser/typography-beta#spacing';
|
|
284
|
+
if (warning.includes('semantic'))
|
|
285
|
+
return 'https://linje.entur.no/komponenter/ressurser/typography-beta#semantic-html';
|
|
286
|
+
return 'https://linje.entur.no/komponenter/ressurser/typography-beta';
|
|
287
|
+
}
|
|
288
|
+
|
|
117
289
|
let ALLOWED_DIRECTORIES = process.env.TYPOGRAPHY_MIGRATION_DIRS
|
|
118
290
|
? process.env.TYPOGRAPHY_MIGRATION_DIRS.split(',')
|
|
119
291
|
: MIGRATION_FOLDERS;
|
|
@@ -170,15 +342,19 @@ const COMPONENT_MAPPING = {
|
|
|
170
342
|
Heading6: { component: 'Heading', as: 'h6', variant: 'section-2' },
|
|
171
343
|
Paragraph: { component: 'Text', variant: 'paragraph' },
|
|
172
344
|
LeadParagraph: { component: 'Text', variant: 'leading' },
|
|
173
|
-
SmallText: { component: 'Text', variant: 'subparagraph'
|
|
174
|
-
StrongText: { component: 'Text',
|
|
175
|
-
SubLabel: { component: 'Text', variant: 'sublabel'
|
|
345
|
+
SmallText: { component: 'Text', variant: 'subparagraph' },
|
|
346
|
+
StrongText: { component: 'Text', as: 'strong', weight: 'bold' },
|
|
347
|
+
SubLabel: { component: 'Text', variant: 'sublabel' },
|
|
176
348
|
SubParagraph: { component: 'Text', variant: 'subparagraph' },
|
|
177
349
|
Label: { component: 'Text', variant: 'label' },
|
|
178
350
|
EmphasizedText: { component: 'Text', variant: 'emphasized' },
|
|
179
351
|
CodeText: { component: 'Text', variant: 'code-text' },
|
|
180
|
-
Link: { component: 'LinkBeta' },
|
|
181
|
-
Blockquote: { component: 'BlockquoteBeta' },
|
|
352
|
+
Link: { component: 'LinkBeta' }, // Convert Link to LinkBeta
|
|
353
|
+
Blockquote: { component: 'BlockquoteBeta' }, // Convert Blockquote to BlockquoteBeta
|
|
354
|
+
BlockquoteFooter: { component: 'BlockquoteFooterBeta' }, // Convert BlockquoteFooter to BlockquoteFooterBeta
|
|
355
|
+
UnorderedList: { component: 'UnorderedListBeta' },
|
|
356
|
+
NumberedList: { component: 'NumberedListBeta' },
|
|
357
|
+
ListItem: { component: 'ListItemBeta' },
|
|
182
358
|
};
|
|
183
359
|
|
|
184
360
|
// Props mapping for migration
|
|
@@ -187,17 +363,43 @@ const PROPS_MAPPING = {
|
|
|
187
363
|
};
|
|
188
364
|
|
|
189
365
|
// Spacing value mapping from old margin to new spacing
|
|
366
|
+
// Based on the actual CSS classes in src/beta/styles.scss
|
|
367
|
+
// and the old margin prop values: "top" | "bottom" | "both" | "none"
|
|
190
368
|
const SPACING_MAPPING = {
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
369
|
+
// Old margin values mapped to new spacing values
|
|
370
|
+
none: 'none', // No spacing
|
|
371
|
+
top: 'md-top', // Top margin only (medium size)
|
|
372
|
+
bottom: 'md-bottom', // Bottom margin only (medium size)
|
|
373
|
+
both: 'md', // Both top and bottom margins (medium size)
|
|
374
|
+
|
|
375
|
+
// Additional spacing values for more granular control
|
|
376
|
+
// These weren't in the old margin prop but are available in new spacing
|
|
377
|
+
left: 'md-left', // Left margin (medium size)
|
|
378
|
+
right: 'md-right', // Right margin (medium size)
|
|
379
|
+
|
|
380
|
+
// Size-based spacing (applies to both top and bottom)
|
|
196
381
|
xs: 'xs',
|
|
197
382
|
sm: 'sm',
|
|
198
383
|
md: 'md',
|
|
199
384
|
lg: 'lg',
|
|
200
385
|
xl: 'xl',
|
|
386
|
+
|
|
387
|
+
// Specific directional spacing with sizes
|
|
388
|
+
'xs-top': 'xs-top',
|
|
389
|
+
'xs-bottom': 'xs-bottom',
|
|
390
|
+
'sm-top': 'sm-top',
|
|
391
|
+
'sm-bottom': 'sm-bottom',
|
|
392
|
+
'md-top': 'md-top',
|
|
393
|
+
'md-bottom': 'md-bottom',
|
|
394
|
+
'lg-top': 'lg-top',
|
|
395
|
+
'lg-bottom': 'lg-bottom',
|
|
396
|
+
'xl-top': 'xl-top',
|
|
397
|
+
'xl-bottom': 'xl-bottom',
|
|
398
|
+
|
|
399
|
+
// Extra small variants
|
|
400
|
+
xs2: 'xs2',
|
|
401
|
+
'xs2-top': 'xs2-top',
|
|
402
|
+
'xs2-bottom': 'xs2-bottom',
|
|
201
403
|
};
|
|
202
404
|
|
|
203
405
|
// Import patterns to handle
|
|
@@ -211,30 +413,34 @@ const IMPORT_PATTERNS = [
|
|
|
211
413
|
// Parse JSX props more robustly
|
|
212
414
|
function parseJSXProps(propsString) {
|
|
213
415
|
if (!propsString || !propsString.trim()) {
|
|
214
|
-
return { props: {}, warnings: [] };
|
|
416
|
+
return { props: {}, warnings: [], spreadProps: [] };
|
|
215
417
|
}
|
|
216
418
|
|
|
217
419
|
const props = {};
|
|
218
420
|
const warnings = [];
|
|
219
|
-
const
|
|
220
|
-
|
|
421
|
+
const spreadProps = []; // Track spread props separately
|
|
422
|
+
const originalSyntax = {}; // Track original JSX syntax for each prop
|
|
221
423
|
|
|
222
424
|
try {
|
|
223
425
|
// Parse props manually to handle complex cases
|
|
224
426
|
let remaining = propsString.trim();
|
|
225
|
-
let lastRemainingLength = remaining.length;
|
|
226
427
|
|
|
227
|
-
|
|
228
|
-
|
|
428
|
+
// First, extract all spread props
|
|
429
|
+
const spreadRegex = /\.\.\.\{?(\w+)\}?/g;
|
|
430
|
+
let spreadMatch;
|
|
431
|
+
while ((spreadMatch = spreadRegex.exec(remaining)) !== null) {
|
|
432
|
+
spreadProps.push(spreadMatch[1]);
|
|
433
|
+
}
|
|
229
434
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
435
|
+
// Remove spread props from the string to parse regular props
|
|
436
|
+
remaining = remaining.replace(/\.\.\.\{?(\w+)\}?/g, '');
|
|
437
|
+
|
|
438
|
+
// Now parse regular props
|
|
439
|
+
while (remaining.trim().length > 0) {
|
|
440
|
+
// Skip whitespace
|
|
441
|
+
remaining = remaining.replace(/^\s+/, '');
|
|
236
442
|
|
|
237
|
-
// Match prop name
|
|
443
|
+
// Match prop name
|
|
238
444
|
const nameMatch = remaining.match(/^(\w+)=/);
|
|
239
445
|
if (!nameMatch) break;
|
|
240
446
|
|
|
@@ -244,7 +450,7 @@ function parseJSXProps(propsString) {
|
|
|
244
450
|
|
|
245
451
|
// Match prop value
|
|
246
452
|
if (remaining.startsWith('"') || remaining.startsWith("'")) {
|
|
247
|
-
// String value
|
|
453
|
+
// String value
|
|
248
454
|
const quote = remaining[0];
|
|
249
455
|
const endQuoteIndex = remaining.indexOf(quote, 1);
|
|
250
456
|
if (endQuoteIndex === -1) {
|
|
@@ -254,14 +460,14 @@ function parseJSXProps(propsString) {
|
|
|
254
460
|
|
|
255
461
|
const propValue = remaining.substring(1, endQuoteIndex);
|
|
256
462
|
props[propName] = propValue;
|
|
463
|
+
originalSyntax[propName] = 'string'; // Mark as string literal
|
|
257
464
|
remaining = remaining.substring(endQuoteIndex + 1);
|
|
258
465
|
} else if (remaining.startsWith('{')) {
|
|
259
|
-
// Object value - find matching closing brace
|
|
466
|
+
// Object value - find matching closing brace
|
|
260
467
|
let braceCount = 0;
|
|
261
468
|
let endIndex = -1;
|
|
262
|
-
const maxSearchLength = Math.min(remaining.length, 1000); // Limit search length
|
|
263
469
|
|
|
264
|
-
for (let i = 0; i <
|
|
470
|
+
for (let i = 0; i < remaining.length; i++) {
|
|
265
471
|
if (remaining[i] === '{') braceCount++;
|
|
266
472
|
if (remaining[i] === '}') {
|
|
267
473
|
braceCount--;
|
|
@@ -279,25 +485,23 @@ function parseJSXProps(propsString) {
|
|
|
279
485
|
|
|
280
486
|
const propValue = remaining.substring(1, endIndex);
|
|
281
487
|
props[propName] = propValue;
|
|
488
|
+
originalSyntax[propName] = 'jsx'; // Mark as JSX expression
|
|
282
489
|
remaining = remaining.substring(endIndex + 1);
|
|
283
490
|
} else {
|
|
284
|
-
// Boolean prop
|
|
491
|
+
// Boolean prop
|
|
285
492
|
props[propName] = true;
|
|
493
|
+
originalSyntax[propName] = 'boolean'; // Mark as boolean
|
|
286
494
|
break;
|
|
287
495
|
}
|
|
288
496
|
|
|
289
|
-
// Skip whitespace
|
|
497
|
+
// Skip whitespace
|
|
290
498
|
remaining = remaining.replace(/^\s+/, '');
|
|
291
499
|
}
|
|
292
|
-
|
|
293
|
-
if (iterationCount >= MAX_ITERATIONS) {
|
|
294
|
-
warnings.push(`Maximum parsing iterations (${MAX_ITERATIONS}) reached`);
|
|
295
|
-
}
|
|
296
500
|
} catch (error) {
|
|
297
501
|
warnings.push(`Failed to parse props: ${error.message}`);
|
|
298
502
|
}
|
|
299
503
|
|
|
300
|
-
return { props, warnings };
|
|
504
|
+
return { props, warnings, spreadProps, originalSyntax };
|
|
301
505
|
}
|
|
302
506
|
|
|
303
507
|
// Migrate props from old to new format
|
|
@@ -315,11 +519,12 @@ function migrateProps(props, oldComponent) {
|
|
|
315
519
|
`Migrated 'margin="${props.margin}"' to 'spacing="${newSpacing}"'`,
|
|
316
520
|
);
|
|
317
521
|
} else {
|
|
318
|
-
// Unknown margin value -
|
|
319
|
-
|
|
522
|
+
// Unknown margin value - suggest alternatives
|
|
523
|
+
const suggestions = getSpacingSuggestions(props.margin);
|
|
524
|
+
migratedProps.spacing = props.margin; // Keep original value for now
|
|
320
525
|
delete migratedProps.margin;
|
|
321
526
|
warnings.push(
|
|
322
|
-
`Migrated 'margin="${props.margin}"' to 'spacing="${props.margin}"' (unknown value
|
|
527
|
+
`Migrated 'margin="${props.margin}"' to 'spacing="${props.margin}"' (unknown value). ${suggestions}`,
|
|
323
528
|
);
|
|
324
529
|
}
|
|
325
530
|
}
|
|
@@ -346,8 +551,55 @@ function migrateProps(props, oldComponent) {
|
|
|
346
551
|
return { props: migratedProps, warnings };
|
|
347
552
|
}
|
|
348
553
|
|
|
554
|
+
// Helper function to suggest spacing alternatives for unknown margin values
|
|
555
|
+
function getSpacingSuggestions(unknownMargin) {
|
|
556
|
+
const suggestions = [];
|
|
557
|
+
|
|
558
|
+
// Check if it might be one of the old margin values
|
|
559
|
+
if (['top', 'bottom', 'both', 'none'].includes(unknownMargin)) {
|
|
560
|
+
suggestions.push(
|
|
561
|
+
`"${unknownMargin}" is a valid old margin value and will be migrated correctly.`,
|
|
562
|
+
);
|
|
563
|
+
return suggestions.join(' ');
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
// Check if it might be a directional value
|
|
567
|
+
if (
|
|
568
|
+
unknownMargin.includes('top') ||
|
|
569
|
+
unknownMargin.includes('bottom') ||
|
|
570
|
+
unknownMargin.includes('left') ||
|
|
571
|
+
unknownMargin.includes('right')
|
|
572
|
+
) {
|
|
573
|
+
suggestions.push(
|
|
574
|
+
'Consider using directional spacing like "md-top", "sm-bottom", etc.',
|
|
575
|
+
);
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
// Check if it might be a size value
|
|
579
|
+
if (['xs', 'sm', 'md', 'lg', 'xl'].includes(unknownMargin)) {
|
|
580
|
+
suggestions.push(
|
|
581
|
+
'Consider using size-based spacing like "xs", "sm", "md", "lg", "xl".',
|
|
582
|
+
);
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
// Check if it might be a specific variant
|
|
586
|
+
if (unknownMargin.includes('xs2')) {
|
|
587
|
+
suggestions.push(
|
|
588
|
+
'Consider using "xs2", "xs2-top", or "xs2-bottom" for extra small spacing.',
|
|
589
|
+
);
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
if (suggestions.length === 0) {
|
|
593
|
+
suggestions.push(
|
|
594
|
+
'Old margin values: "none", "top", "bottom", "both". New spacing values: "xs", "sm", "md", "lg", "xl", and directional variants.',
|
|
595
|
+
);
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
return suggestions.join(' ');
|
|
599
|
+
}
|
|
600
|
+
|
|
349
601
|
// Convert props object back to JSX string
|
|
350
|
-
function propsToString(props) {
|
|
602
|
+
function propsToString(props, originalSyntax = {}) {
|
|
351
603
|
if (!props || Object.keys(props).length === 0) {
|
|
352
604
|
return '';
|
|
353
605
|
}
|
|
@@ -356,20 +608,30 @@ function propsToString(props) {
|
|
|
356
608
|
' ' +
|
|
357
609
|
Object.entries(props)
|
|
358
610
|
.map(([key, value]) => {
|
|
359
|
-
//
|
|
360
|
-
if (
|
|
611
|
+
// Use original syntax information if available
|
|
612
|
+
if (originalSyntax[key] === 'string') {
|
|
361
613
|
return `${key}="${value}"`;
|
|
362
|
-
} else if (
|
|
363
|
-
typeof value === 'string' &&
|
|
364
|
-
value.startsWith('{') &&
|
|
365
|
-
value.endsWith('}')
|
|
366
|
-
) {
|
|
367
|
-
// Already a JSX object, don't add extra braces
|
|
614
|
+
} else if (originalSyntax[key] === 'jsx') {
|
|
368
615
|
return `${key}={${value}}`;
|
|
616
|
+
} else if (originalSyntax[key] === 'boolean') {
|
|
617
|
+
return value ? key : '';
|
|
369
618
|
} else {
|
|
370
|
-
|
|
619
|
+
// Fallback logic for when originalSyntax is not available
|
|
620
|
+
if (typeof value === 'string' && !value.includes('{')) {
|
|
621
|
+
return `${key}="${value}"`;
|
|
622
|
+
} else if (
|
|
623
|
+
typeof value === 'string' &&
|
|
624
|
+
value.startsWith('{') &&
|
|
625
|
+
value.endsWith('}')
|
|
626
|
+
) {
|
|
627
|
+
// Already a JSX object, don't add extra braces
|
|
628
|
+
return `${key}={${value}}`;
|
|
629
|
+
} else {
|
|
630
|
+
return `${key}={${value}}`;
|
|
631
|
+
}
|
|
371
632
|
}
|
|
372
633
|
})
|
|
634
|
+
.filter(prop => prop.length > 0) // Remove empty props (like false booleans)
|
|
373
635
|
.join(' ')
|
|
374
636
|
);
|
|
375
637
|
}
|
|
@@ -379,12 +641,46 @@ function updateImports(content) {
|
|
|
379
641
|
let updatedContent = content;
|
|
380
642
|
let changes = 0;
|
|
381
643
|
|
|
644
|
+
// First, update import paths
|
|
382
645
|
IMPORT_PATTERNS.forEach(pattern => {
|
|
383
646
|
const matches = content.match(pattern) || [];
|
|
384
647
|
changes += matches.length;
|
|
385
648
|
updatedContent = updatedContent.replace(pattern, `from '${BETA_IMPORT}'`);
|
|
386
649
|
});
|
|
387
650
|
|
|
651
|
+
// Then, update destructured import names - only within @entur/typography imports
|
|
652
|
+
// Find all import statements from @entur/typography and update component names
|
|
653
|
+
const importRegex =
|
|
654
|
+
/import\s*{([^}]+)}\s*from\s*['"']@entur\/typography['"']/g;
|
|
655
|
+
|
|
656
|
+
updatedContent = updatedContent.replace(importRegex, (match, importList) => {
|
|
657
|
+
let updatedImportList = importList;
|
|
658
|
+
let hasChanges = false;
|
|
659
|
+
const uniqueComponents = new Set();
|
|
660
|
+
|
|
661
|
+
// Check each component in the import list
|
|
662
|
+
Object.entries(COMPONENT_MAPPING).forEach(([oldComponent, mapping]) => {
|
|
663
|
+
const componentRegex = new RegExp(`\\b${oldComponent}\\b`, 'g');
|
|
664
|
+
if (componentRegex.test(updatedImportList)) {
|
|
665
|
+
updatedImportList = updatedImportList.replace(
|
|
666
|
+
componentRegex,
|
|
667
|
+
mapping.component,
|
|
668
|
+
);
|
|
669
|
+
uniqueComponents.add(mapping.component);
|
|
670
|
+
hasChanges = true;
|
|
671
|
+
}
|
|
672
|
+
});
|
|
673
|
+
|
|
674
|
+
if (hasChanges) {
|
|
675
|
+
changes++;
|
|
676
|
+
// Deduplicate components and create clean import statement
|
|
677
|
+
const finalImportList = Array.from(uniqueComponents).join(', ');
|
|
678
|
+
return `import {${finalImportList}} from '${BETA_IMPORT}'`;
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
return match;
|
|
682
|
+
});
|
|
683
|
+
|
|
388
684
|
return { content: updatedContent, changes };
|
|
389
685
|
}
|
|
390
686
|
|
|
@@ -404,8 +700,12 @@ function updateComponents(content) {
|
|
|
404
700
|
changes++;
|
|
405
701
|
|
|
406
702
|
// Parse existing props
|
|
407
|
-
const {
|
|
408
|
-
|
|
703
|
+
const {
|
|
704
|
+
props: existingProps,
|
|
705
|
+
warnings: parseWarnings,
|
|
706
|
+
spreadProps,
|
|
707
|
+
originalSyntax,
|
|
708
|
+
} = parseJSXProps(propsString);
|
|
409
709
|
warnings.push(...parseWarnings);
|
|
410
710
|
|
|
411
711
|
// Migrate props
|
|
@@ -425,8 +725,10 @@ function updateComponents(content) {
|
|
|
425
725
|
|
|
426
726
|
// Handle Heading components
|
|
427
727
|
if (mapping.component === 'Heading') {
|
|
428
|
-
|
|
429
|
-
const
|
|
728
|
+
// Preserve existing 'as' prop if it exists, otherwise use mapping default
|
|
729
|
+
const asValue = existingProps.as || mapping.as;
|
|
730
|
+
// Preserve existing 'variant' prop if it exists, otherwise use mapping default
|
|
731
|
+
const variantValue = existingProps.variant || mapping.variant;
|
|
430
732
|
|
|
431
733
|
// Remove as and variant from props since we'll add them separately
|
|
432
734
|
delete newProps.as;
|
|
@@ -440,8 +742,10 @@ function updateComponents(content) {
|
|
|
440
742
|
}
|
|
441
743
|
Object.assign(orderedProps, newProps);
|
|
442
744
|
|
|
443
|
-
const propsString = propsToString(orderedProps);
|
|
444
|
-
|
|
745
|
+
const propsString = propsToString(orderedProps, originalSyntax);
|
|
746
|
+
const spreadPropsString =
|
|
747
|
+
spreadProps.length > 0 ? ` {...${spreadProps.join(', ...')}}` : '';
|
|
748
|
+
return `<Heading as="${asValue}" variant="${variantValue}"${propsString}${spreadPropsString}>`;
|
|
445
749
|
}
|
|
446
750
|
|
|
447
751
|
// Handle other components
|
|
@@ -463,8 +767,10 @@ function updateComponents(content) {
|
|
|
463
767
|
});
|
|
464
768
|
Object.assign(finalProps, newProps);
|
|
465
769
|
|
|
466
|
-
const otherPropsString = propsToString(finalProps);
|
|
467
|
-
|
|
770
|
+
const otherPropsString = propsToString(finalProps, originalSyntax);
|
|
771
|
+
const spreadPropsString =
|
|
772
|
+
spreadProps.length > 0 ? ` {...${spreadProps.join(', ...')}}` : '';
|
|
773
|
+
return `<${componentName}${otherPropsString}${spreadPropsString}>`;
|
|
468
774
|
},
|
|
469
775
|
);
|
|
470
776
|
|
|
@@ -492,15 +798,33 @@ function updateComponents(content) {
|
|
|
492
798
|
* @returns {string[]} Array of matching file paths
|
|
493
799
|
*/
|
|
494
800
|
function findFiles(pattern) {
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
801
|
+
const allFiles = [];
|
|
802
|
+
|
|
803
|
+
// Process directory patterns
|
|
804
|
+
const directoryPatterns = ALLOWED_DIRECTORIES.filter(dir =>
|
|
805
|
+
dir.includes('**'),
|
|
806
|
+
);
|
|
807
|
+
const filePatterns = ALLOWED_DIRECTORIES.filter(dir => !dir.includes('**'));
|
|
808
|
+
|
|
809
|
+
// Handle directory patterns (e.g., src/**, app/**)
|
|
810
|
+
if (directoryPatterns.length > 0) {
|
|
811
|
+
const combinedDirPattern = `{${directoryPatterns.join(',')}}/${pattern}`;
|
|
812
|
+
const dirFiles = glob.sync(combinedDirPattern, {
|
|
813
|
+
ignore: BLOCKED_DIRECTORIES,
|
|
814
|
+
nodir: true,
|
|
815
|
+
absolute: false,
|
|
816
|
+
});
|
|
817
|
+
allFiles.push(...dirFiles);
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
// Handle file patterns (e.g., *.jsx, *.tsx)
|
|
821
|
+
filePatterns.forEach(filePattern => {
|
|
822
|
+
const files = glob.sync(filePattern, {
|
|
823
|
+
ignore: BLOCKED_DIRECTORIES,
|
|
824
|
+
nodir: true,
|
|
825
|
+
absolute: false,
|
|
826
|
+
});
|
|
827
|
+
allFiles.push(...files);
|
|
504
828
|
});
|
|
505
829
|
|
|
506
830
|
// Use Set for efficient deduplication and filtering
|
|
@@ -521,37 +845,29 @@ function findFiles(pattern) {
|
|
|
521
845
|
return uniqueFiles;
|
|
522
846
|
}
|
|
523
847
|
|
|
524
|
-
function updateImportsAndComponents(content
|
|
848
|
+
function updateImportsAndComponents(content) {
|
|
525
849
|
let updatedContent = content;
|
|
526
850
|
let changes = 0;
|
|
527
851
|
let warnings = [];
|
|
528
852
|
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
changes
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
content: finalContent,
|
|
541
|
-
changes: componentChanges,
|
|
542
|
-
warnings: componentWarnings,
|
|
543
|
-
} = updateComponents(newContent);
|
|
544
|
-
updatedContent = finalContent;
|
|
545
|
-
changes = importChanges + componentChanges;
|
|
546
|
-
warnings = componentWarnings;
|
|
547
|
-
}
|
|
853
|
+
// Update both imports and components
|
|
854
|
+
const { content: newContent, changes: importChanges } =
|
|
855
|
+
updateImports(content);
|
|
856
|
+
const {
|
|
857
|
+
content: finalContent,
|
|
858
|
+
changes: componentChanges,
|
|
859
|
+
warnings: componentWarnings,
|
|
860
|
+
} = updateComponents(newContent);
|
|
861
|
+
updatedContent = finalContent;
|
|
862
|
+
changes = importChanges + componentChanges;
|
|
863
|
+
warnings = componentWarnings;
|
|
548
864
|
|
|
549
865
|
return { content: updatedContent, changes, warnings };
|
|
550
866
|
}
|
|
551
867
|
|
|
552
|
-
function generateMigrationReport(files,
|
|
868
|
+
function generateMigrationReport(files, isDryRun = false) {
|
|
553
869
|
const report = {
|
|
554
|
-
strategy,
|
|
870
|
+
strategy: 'complete',
|
|
555
871
|
totalFiles: files.length,
|
|
556
872
|
migratedFiles: 0,
|
|
557
873
|
totalChanges: 0,
|
|
@@ -564,21 +880,32 @@ function generateMigrationReport(files, strategy, isDryRun = false) {
|
|
|
564
880
|
files.forEach(file => {
|
|
565
881
|
try {
|
|
566
882
|
const content = fs.readFileSync(file, 'utf8');
|
|
883
|
+
|
|
884
|
+
// Analyze file for problematic patterns BEFORE migration
|
|
885
|
+
const fileAnalysis = analyzeFile(file, content);
|
|
886
|
+
|
|
567
887
|
const {
|
|
568
888
|
content: updatedContent,
|
|
569
889
|
changes,
|
|
570
890
|
warnings,
|
|
571
|
-
} = updateImportsAndComponents(content
|
|
891
|
+
} = updateImportsAndComponents(content);
|
|
892
|
+
|
|
893
|
+
// Combine migration warnings with file analysis warnings
|
|
894
|
+
const allWarnings = [...warnings, ...fileAnalysis.warnings];
|
|
572
895
|
|
|
573
|
-
if (changes > 0) {
|
|
896
|
+
if (changes > 0 || fileAnalysis.warnings.length > 0) {
|
|
574
897
|
if (!isDryRun) {
|
|
575
898
|
fs.writeFileSync(file, updatedContent, 'utf8');
|
|
576
899
|
}
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
report.
|
|
900
|
+
if (changes > 0) {
|
|
901
|
+
report.migratedFiles++;
|
|
902
|
+
report.totalChanges += changes;
|
|
903
|
+
}
|
|
904
|
+
report.totalWarnings += allWarnings.length;
|
|
905
|
+
report.files.push({ file, changes, warnings: allWarnings });
|
|
906
|
+
report.warnings.push(
|
|
907
|
+
...allWarnings.map(warning => `${file}: ${warning}`),
|
|
908
|
+
);
|
|
582
909
|
}
|
|
583
910
|
} catch (error) {
|
|
584
911
|
report.warnings.push(`${file}: Error processing file - ${error.message}`);
|
|
@@ -620,24 +947,135 @@ function printReport(report) {
|
|
|
620
947
|
w.includes('check for conflicts'),
|
|
621
948
|
);
|
|
622
949
|
|
|
950
|
+
// New warning types from file analysis
|
|
951
|
+
const styleConflictWarnings = report.warnings.filter(
|
|
952
|
+
w => w.includes('style conflicts') || w.includes('style and margin'),
|
|
953
|
+
);
|
|
954
|
+
const nestedTypographyWarnings = report.warnings.filter(w =>
|
|
955
|
+
w.includes('nested typography'),
|
|
956
|
+
);
|
|
957
|
+
const accessibilityWarnings = report.warnings.filter(
|
|
958
|
+
w => w.includes('missing as prop') || w.includes('accessibility'),
|
|
959
|
+
);
|
|
960
|
+
const semanticMismatchWarnings = report.warnings.filter(w =>
|
|
961
|
+
w.includes('semantic mismatch'),
|
|
962
|
+
);
|
|
963
|
+
|
|
623
964
|
if (marginWarnings.length > 0) {
|
|
624
965
|
console.log(
|
|
625
966
|
`\n ๐ Margin โ Spacing Migrations (${marginWarnings.length}):`,
|
|
626
967
|
);
|
|
627
|
-
|
|
968
|
+
// Show first 5 warnings, then summarize the rest
|
|
969
|
+
marginWarnings
|
|
970
|
+
.slice(0, 5)
|
|
971
|
+
.forEach(warning => console.log(` ${warning}`));
|
|
972
|
+
if (marginWarnings.length > 5) {
|
|
973
|
+
console.log(
|
|
974
|
+
` ... and ${marginWarnings.length - 5} more similar warnings`,
|
|
975
|
+
);
|
|
976
|
+
}
|
|
628
977
|
}
|
|
629
978
|
|
|
630
979
|
if (semanticWarnings.length > 0) {
|
|
631
980
|
console.log(`\n ๐ฏ Semantic HTML Issues (${semanticWarnings.length}):`);
|
|
632
|
-
|
|
981
|
+
// Show first 5 warnings, then summarize the rest
|
|
982
|
+
semanticWarnings
|
|
983
|
+
.slice(0, 5)
|
|
984
|
+
.forEach(warning => console.log(` ${warning}`));
|
|
985
|
+
if (semanticWarnings.length > 5) {
|
|
986
|
+
console.log(
|
|
987
|
+
` ... and ${semanticWarnings.length - 5} more similar warnings`,
|
|
988
|
+
);
|
|
989
|
+
}
|
|
633
990
|
}
|
|
634
991
|
|
|
635
992
|
if (conflictWarnings.length > 0) {
|
|
636
993
|
console.log(`\n ๐จ Style Conflicts (${conflictWarnings.length}):`);
|
|
637
|
-
|
|
994
|
+
// Show first 5 warnings, then summarize the rest
|
|
995
|
+
conflictWarnings
|
|
996
|
+
.slice(0, 5)
|
|
997
|
+
.forEach(warning => console.log(` ${warning}`));
|
|
998
|
+
if (conflictWarnings.length > 5) {
|
|
999
|
+
console.log(
|
|
1000
|
+
` ... and ${conflictWarnings.length - 5} more similar warnings`,
|
|
1001
|
+
);
|
|
1002
|
+
}
|
|
638
1003
|
console.log(` โ Review these components for styling conflicts`);
|
|
639
1004
|
}
|
|
640
1005
|
|
|
1006
|
+
// Display new warning types
|
|
1007
|
+
if (styleConflictWarnings.length > 0) {
|
|
1008
|
+
console.log(
|
|
1009
|
+
`\n ๐จ Style + Margin Conflicts (${styleConflictWarnings.length}):`,
|
|
1010
|
+
);
|
|
1011
|
+
styleConflictWarnings
|
|
1012
|
+
.slice(0, 5)
|
|
1013
|
+
.forEach(warning => console.log(` ${warning}`));
|
|
1014
|
+
if (styleConflictWarnings.length > 5) {
|
|
1015
|
+
console.log(
|
|
1016
|
+
` ... and ${
|
|
1017
|
+
styleConflictWarnings.length - 5
|
|
1018
|
+
} more similar warnings`,
|
|
1019
|
+
);
|
|
1020
|
+
}
|
|
1021
|
+
console.log(` โ Remove margin prop when using inline styles`);
|
|
1022
|
+
}
|
|
1023
|
+
|
|
1024
|
+
if (nestedTypographyWarnings.length > 0) {
|
|
1025
|
+
console.log(
|
|
1026
|
+
`\n ๐ซ Nested Typography (${nestedTypographyWarnings.length}):`,
|
|
1027
|
+
);
|
|
1028
|
+
nestedTypographyWarnings
|
|
1029
|
+
.slice(0, 5)
|
|
1030
|
+
.forEach(warning => console.log(` ${warning}`));
|
|
1031
|
+
if (nestedTypographyWarnings.length > 5) {
|
|
1032
|
+
console.log(
|
|
1033
|
+
` ... and ${
|
|
1034
|
+
nestedTypographyWarnings.length - 5
|
|
1035
|
+
} more similar warnings`,
|
|
1036
|
+
);
|
|
1037
|
+
}
|
|
1038
|
+
console.log(
|
|
1039
|
+
` โ Use spans or other inline elements instead of nested Text components`,
|
|
1040
|
+
);
|
|
1041
|
+
}
|
|
1042
|
+
|
|
1043
|
+
if (accessibilityWarnings.length > 0) {
|
|
1044
|
+
console.log(
|
|
1045
|
+
`\n โฟ Accessibility Issues (${accessibilityWarnings.length}):`,
|
|
1046
|
+
);
|
|
1047
|
+
accessibilityWarnings
|
|
1048
|
+
.slice(0, 5)
|
|
1049
|
+
.forEach(warning => console.log(` ${warning}`));
|
|
1050
|
+
if (accessibilityWarnings.length > 5) {
|
|
1051
|
+
console.log(
|
|
1052
|
+
` ... and ${
|
|
1053
|
+
accessibilityWarnings.length - 5
|
|
1054
|
+
} more similar warnings`,
|
|
1055
|
+
);
|
|
1056
|
+
}
|
|
1057
|
+
console.log(
|
|
1058
|
+
` โ Add 'as' prop to Heading components for proper semantic HTML`,
|
|
1059
|
+
);
|
|
1060
|
+
}
|
|
1061
|
+
|
|
1062
|
+
if (semanticMismatchWarnings.length > 0) {
|
|
1063
|
+
console.log(
|
|
1064
|
+
`\n ๐ Semantic Mismatches (${semanticMismatchWarnings.length}):`,
|
|
1065
|
+
);
|
|
1066
|
+
semanticMismatchWarnings
|
|
1067
|
+
.slice(0, 5)
|
|
1068
|
+
.forEach(warning => console.log(` ${warning}`));
|
|
1069
|
+
if (semanticMismatchWarnings.length > 5) {
|
|
1070
|
+
console.log(
|
|
1071
|
+
` ... and ${
|
|
1072
|
+
semanticMismatchWarnings.length - 5
|
|
1073
|
+
} more similar warnings`,
|
|
1074
|
+
);
|
|
1075
|
+
}
|
|
1076
|
+
console.log(` โ Review heading level and variant combinations`);
|
|
1077
|
+
}
|
|
1078
|
+
|
|
641
1079
|
console.log('\n๐ Summary:');
|
|
642
1080
|
if (marginWarnings.length > 0)
|
|
643
1081
|
console.log(
|
|
@@ -651,26 +1089,42 @@ function printReport(report) {
|
|
|
651
1089
|
console.log(
|
|
652
1090
|
` โข ${conflictWarnings.length} style conflicts need manual review`,
|
|
653
1091
|
);
|
|
1092
|
+
if (styleConflictWarnings.length > 0)
|
|
1093
|
+
console.log(
|
|
1094
|
+
` โข ${styleConflictWarnings.length} style + margin conflicts detected`,
|
|
1095
|
+
);
|
|
1096
|
+
if (nestedTypographyWarnings.length > 0)
|
|
1097
|
+
console.log(
|
|
1098
|
+
` โข ${nestedTypographyWarnings.length} nested typography components found`,
|
|
1099
|
+
);
|
|
1100
|
+
if (accessibilityWarnings.length > 0)
|
|
1101
|
+
console.log(
|
|
1102
|
+
` โข ${accessibilityWarnings.length} accessibility issues need attention`,
|
|
1103
|
+
);
|
|
1104
|
+
if (semanticMismatchWarnings.length > 0)
|
|
1105
|
+
console.log(
|
|
1106
|
+
` โข ${semanticMismatchWarnings.length} semantic mismatches detected`,
|
|
1107
|
+
);
|
|
1108
|
+
|
|
1109
|
+
// Add helpful note about warning limits
|
|
1110
|
+
if (report.warnings.length > 15) {
|
|
1111
|
+
console.log(
|
|
1112
|
+
'\n๐ก Note: Only showing first 5 warnings of each type to avoid overwhelming output.',
|
|
1113
|
+
);
|
|
1114
|
+
console.log(
|
|
1115
|
+
' All warnings are still logged in the migration report above.',
|
|
1116
|
+
);
|
|
1117
|
+
}
|
|
654
1118
|
}
|
|
655
1119
|
}
|
|
656
1120
|
|
|
657
|
-
function showNextSteps(
|
|
1121
|
+
function showNextSteps() {
|
|
658
1122
|
console.log('\n๐ Next Steps');
|
|
659
1123
|
console.log('=============');
|
|
660
1124
|
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
Object.entries(COMPONENT_MAPPING).forEach(([old, new_]) => {
|
|
665
|
-
console.log(` ${old} โ ${new_}`);
|
|
666
|
-
});
|
|
667
|
-
console.log('3. ๐งช Test your application');
|
|
668
|
-
console.log('4. ๐ Read the migration guide on our website');
|
|
669
|
-
} else if (strategy === 'complete') {
|
|
670
|
-
console.log('1. ๐งช Test your application thoroughly');
|
|
671
|
-
console.log('2. ๐ Review and adjust any component props if needed');
|
|
672
|
-
console.log('3. ๐ Read the migration guide on our website');
|
|
673
|
-
}
|
|
1125
|
+
console.log('1. ๐งช Test your application thoroughly');
|
|
1126
|
+
console.log('2. ๐ Review and adjust any component props if needed');
|
|
1127
|
+
console.log('3. ๐ Read the migration guide on our website');
|
|
674
1128
|
|
|
675
1129
|
console.log('\nโ ๏ธ Important Notes:');
|
|
676
1130
|
console.log('- Check warnings above for potential issues');
|
|
@@ -699,23 +1153,19 @@ function main() {
|
|
|
699
1153
|
console.log(
|
|
700
1154
|
' --dry-run Show what would be changed without modifying files',
|
|
701
1155
|
);
|
|
702
|
-
console.log(
|
|
703
|
-
' --import-only Import-only migration: update import paths only',
|
|
704
|
-
);
|
|
705
|
-
|
|
706
1156
|
console.log(' --help, -h Show this help message');
|
|
707
1157
|
console.log('');
|
|
708
|
-
console.log('Migration
|
|
709
|
-
console.log(' ๐ Complete Mode
|
|
1158
|
+
console.log('Migration Mode:');
|
|
1159
|
+
console.log(' ๐ Complete Mode: Updates everything');
|
|
710
1160
|
console.log(' - Replaces old components with beta components');
|
|
711
|
-
console.log(' -
|
|
712
|
-
console.log(' -
|
|
713
|
-
console.log('');
|
|
1161
|
+
console.log(' - Heading1-6 โ Heading with as/variant props');
|
|
1162
|
+
console.log(' - Text components โ Text with variant props');
|
|
1163
|
+
console.log(' - Link โ LinkBeta, Blockquote โ BlockquoteBeta');
|
|
714
1164
|
console.log(
|
|
715
|
-
'
|
|
1165
|
+
' - Lists โ UnorderedListBeta, NumberedListBeta, ListItemBeta',
|
|
716
1166
|
);
|
|
717
|
-
console.log(' -
|
|
718
|
-
console.log(' -
|
|
1167
|
+
console.log(' - May require prop/styling updates');
|
|
1168
|
+
console.log(' - Test thoroughly after migration');
|
|
719
1169
|
console.log('');
|
|
720
1170
|
console.log('Examples:');
|
|
721
1171
|
console.log(' # See what would be changed');
|
|
@@ -723,12 +1173,6 @@ function main() {
|
|
|
723
1173
|
console.log('');
|
|
724
1174
|
console.log(' # Complete migration: update everything (default)');
|
|
725
1175
|
console.log(' npx @entur/typography@latest migrate');
|
|
726
|
-
console.log('');
|
|
727
|
-
console.log(' # Import-only migration: update import paths only');
|
|
728
|
-
console.log(' npx @entur/typography@latest migrate --import-only');
|
|
729
|
-
console.log('');
|
|
730
|
-
|
|
731
|
-
console.log('');
|
|
732
1176
|
|
|
733
1177
|
console.log('Environment Variables:');
|
|
734
1178
|
console.log(
|
|
@@ -744,7 +1188,7 @@ function main() {
|
|
|
744
1188
|
console.log(' Add/remove folder patterns between the ๐ and ๐ markers');
|
|
745
1189
|
console.log(' Examples: "src/**", "app/**", "packages/my-app/**"');
|
|
746
1190
|
console.log('');
|
|
747
|
-
console.log(' Option 2: Set environment variable
|
|
1191
|
+
console.log(' Option 2: Set environment variable');
|
|
748
1192
|
console.log(
|
|
749
1193
|
' export TYPOGRAPHY_MIGRATION_DIRS="src/**,app/**,components/**"',
|
|
750
1194
|
);
|
|
@@ -812,36 +1256,30 @@ function main() {
|
|
|
812
1256
|
|
|
813
1257
|
// Parse command line options
|
|
814
1258
|
const isDryRun = process.argv.includes('--dry-run');
|
|
815
|
-
const isImportOnly = process.argv.includes('--import-only');
|
|
816
1259
|
|
|
817
1260
|
if (isDryRun) {
|
|
818
1261
|
console.log('๐ DRY RUN MODE: No files will be modified');
|
|
819
1262
|
console.log('');
|
|
820
1263
|
}
|
|
821
1264
|
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
}
|
|
1265
|
+
console.log('๐ COMPLETE MIGRATION: Updating imports + component usage');
|
|
1266
|
+
console.log('โ ๏ธ WARNING: This will modify your component usage!');
|
|
1267
|
+
console.log(' - Old components will be replaced with beta components');
|
|
1268
|
+
console.log(
|
|
1269
|
+
' - Link โ LinkBeta, Blockquote โ BlockquoteBeta, Lists โ ListBeta components',
|
|
1270
|
+
);
|
|
1271
|
+
console.log(
|
|
1272
|
+
' - List components โ UnorderedListBeta, NumberedListBeta, ListItemBeta',
|
|
1273
|
+
);
|
|
1274
|
+
console.log(' - You may need to update props and styling');
|
|
1275
|
+
console.log(' - Test thoroughly after migration');
|
|
834
1276
|
|
|
835
1277
|
console.log('');
|
|
836
1278
|
|
|
837
1279
|
// Perform migration
|
|
838
|
-
const report = generateMigrationReport(
|
|
839
|
-
allFiles,
|
|
840
|
-
isImportOnly ? 'import-only' : 'complete',
|
|
841
|
-
isDryRun,
|
|
842
|
-
);
|
|
1280
|
+
const report = generateMigrationReport(allFiles, isDryRun);
|
|
843
1281
|
printReport(report);
|
|
844
|
-
showNextSteps(
|
|
1282
|
+
showNextSteps();
|
|
845
1283
|
|
|
846
1284
|
console.log('\n๐ฏ Migration complete!');
|
|
847
1285
|
}
|