@entur/typography 1.10.0-beta.5 ā 1.10.0-beta.7
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/package.json +2 -2
- package/scripts/migrate-typography.js +341 -9
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@entur/typography",
|
|
3
|
-
"version": "1.10.0-beta.
|
|
3
|
+
"version": "1.10.0-beta.7",
|
|
4
4
|
"license": "SEE LICENSE IN README.md",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/typography.esm.js",
|
|
@@ -42,5 +42,5 @@
|
|
|
42
42
|
"devDependencies": {
|
|
43
43
|
"dts-cli": "2.0.5"
|
|
44
44
|
},
|
|
45
|
-
"gitHead": "
|
|
45
|
+
"gitHead": "26b5f016229bb0115d5f31ce51e15eafd015a870"
|
|
46
46
|
}
|
|
@@ -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;
|
|
@@ -379,12 +551,24 @@ function updateImports(content) {
|
|
|
379
551
|
let updatedContent = content;
|
|
380
552
|
let changes = 0;
|
|
381
553
|
|
|
554
|
+
// First, update import paths
|
|
382
555
|
IMPORT_PATTERNS.forEach(pattern => {
|
|
383
556
|
const matches = content.match(pattern) || [];
|
|
384
557
|
changes += matches.length;
|
|
385
558
|
updatedContent = updatedContent.replace(pattern, `from '${BETA_IMPORT}'`);
|
|
386
559
|
});
|
|
387
560
|
|
|
561
|
+
// Then, update destructured import names
|
|
562
|
+
Object.entries(COMPONENT_MAPPING).forEach(([oldComponent, mapping]) => {
|
|
563
|
+
// Simple approach: find and replace the component name in import statements
|
|
564
|
+
const importRegex = new RegExp(`\\b${oldComponent}\\b(?=\\s*[,}])`, 'g');
|
|
565
|
+
|
|
566
|
+
if (importRegex.test(updatedContent)) {
|
|
567
|
+
changes++;
|
|
568
|
+
updatedContent = updatedContent.replace(importRegex, mapping.component);
|
|
569
|
+
}
|
|
570
|
+
});
|
|
571
|
+
|
|
388
572
|
return { content: updatedContent, changes };
|
|
389
573
|
}
|
|
390
574
|
|
|
@@ -564,21 +748,32 @@ function generateMigrationReport(files, strategy, isDryRun = false) {
|
|
|
564
748
|
files.forEach(file => {
|
|
565
749
|
try {
|
|
566
750
|
const content = fs.readFileSync(file, 'utf8');
|
|
751
|
+
|
|
752
|
+
// Analyze file for problematic patterns BEFORE migration
|
|
753
|
+
const fileAnalysis = analyzeFile(file, content);
|
|
754
|
+
|
|
567
755
|
const {
|
|
568
756
|
content: updatedContent,
|
|
569
757
|
changes,
|
|
570
758
|
warnings,
|
|
571
759
|
} = updateImportsAndComponents(content, strategy);
|
|
572
760
|
|
|
573
|
-
|
|
761
|
+
// Combine migration warnings with file analysis warnings
|
|
762
|
+
const allWarnings = [...warnings, ...fileAnalysis.warnings];
|
|
763
|
+
|
|
764
|
+
if (changes > 0 || fileAnalysis.warnings.length > 0) {
|
|
574
765
|
if (!isDryRun) {
|
|
575
766
|
fs.writeFileSync(file, updatedContent, 'utf8');
|
|
576
767
|
}
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
report.
|
|
768
|
+
if (changes > 0) {
|
|
769
|
+
report.migratedFiles++;
|
|
770
|
+
report.totalChanges += changes;
|
|
771
|
+
}
|
|
772
|
+
report.totalWarnings += allWarnings.length;
|
|
773
|
+
report.files.push({ file, changes, warnings: allWarnings });
|
|
774
|
+
report.warnings.push(
|
|
775
|
+
...allWarnings.map(warning => `${file}: ${warning}`),
|
|
776
|
+
);
|
|
582
777
|
}
|
|
583
778
|
} catch (error) {
|
|
584
779
|
report.warnings.push(`${file}: Error processing file - ${error.message}`);
|
|
@@ -620,24 +815,135 @@ function printReport(report) {
|
|
|
620
815
|
w.includes('check for conflicts'),
|
|
621
816
|
);
|
|
622
817
|
|
|
818
|
+
// New warning types from file analysis
|
|
819
|
+
const styleConflictWarnings = report.warnings.filter(
|
|
820
|
+
w => w.includes('style conflicts') || w.includes('style and margin'),
|
|
821
|
+
);
|
|
822
|
+
const nestedTypographyWarnings = report.warnings.filter(w =>
|
|
823
|
+
w.includes('nested typography'),
|
|
824
|
+
);
|
|
825
|
+
const accessibilityWarnings = report.warnings.filter(
|
|
826
|
+
w => w.includes('missing as prop') || w.includes('accessibility'),
|
|
827
|
+
);
|
|
828
|
+
const semanticMismatchWarnings = report.warnings.filter(w =>
|
|
829
|
+
w.includes('semantic mismatch'),
|
|
830
|
+
);
|
|
831
|
+
|
|
623
832
|
if (marginWarnings.length > 0) {
|
|
624
833
|
console.log(
|
|
625
834
|
`\n š Margin ā Spacing Migrations (${marginWarnings.length}):`,
|
|
626
835
|
);
|
|
627
|
-
|
|
836
|
+
// Show first 5 warnings, then summarize the rest
|
|
837
|
+
marginWarnings
|
|
838
|
+
.slice(0, 5)
|
|
839
|
+
.forEach(warning => console.log(` ${warning}`));
|
|
840
|
+
if (marginWarnings.length > 5) {
|
|
841
|
+
console.log(
|
|
842
|
+
` ... and ${marginWarnings.length - 5} more similar warnings`,
|
|
843
|
+
);
|
|
844
|
+
}
|
|
628
845
|
}
|
|
629
846
|
|
|
630
847
|
if (semanticWarnings.length > 0) {
|
|
631
848
|
console.log(`\n šÆ Semantic HTML Issues (${semanticWarnings.length}):`);
|
|
632
|
-
|
|
849
|
+
// Show first 5 warnings, then summarize the rest
|
|
850
|
+
semanticWarnings
|
|
851
|
+
.slice(0, 5)
|
|
852
|
+
.forEach(warning => console.log(` ${warning}`));
|
|
853
|
+
if (semanticWarnings.length > 5) {
|
|
854
|
+
console.log(
|
|
855
|
+
` ... and ${semanticWarnings.length - 5} more similar warnings`,
|
|
856
|
+
);
|
|
857
|
+
}
|
|
633
858
|
}
|
|
634
859
|
|
|
635
860
|
if (conflictWarnings.length > 0) {
|
|
636
861
|
console.log(`\n šØ Style Conflicts (${conflictWarnings.length}):`);
|
|
637
|
-
|
|
862
|
+
// Show first 5 warnings, then summarize the rest
|
|
863
|
+
conflictWarnings
|
|
864
|
+
.slice(0, 5)
|
|
865
|
+
.forEach(warning => console.log(` ${warning}`));
|
|
866
|
+
if (conflictWarnings.length > 5) {
|
|
867
|
+
console.log(
|
|
868
|
+
` ... and ${conflictWarnings.length - 5} more similar warnings`,
|
|
869
|
+
);
|
|
870
|
+
}
|
|
638
871
|
console.log(` ā Review these components for styling conflicts`);
|
|
639
872
|
}
|
|
640
873
|
|
|
874
|
+
// Display new warning types
|
|
875
|
+
if (styleConflictWarnings.length > 0) {
|
|
876
|
+
console.log(
|
|
877
|
+
`\n šØ Style + Margin Conflicts (${styleConflictWarnings.length}):`,
|
|
878
|
+
);
|
|
879
|
+
styleConflictWarnings
|
|
880
|
+
.slice(0, 5)
|
|
881
|
+
.forEach(warning => console.log(` ${warning}`));
|
|
882
|
+
if (styleConflictWarnings.length > 5) {
|
|
883
|
+
console.log(
|
|
884
|
+
` ... and ${
|
|
885
|
+
styleConflictWarnings.length - 5
|
|
886
|
+
} more similar warnings`,
|
|
887
|
+
);
|
|
888
|
+
}
|
|
889
|
+
console.log(` ā Remove margin prop when using inline styles`);
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
if (nestedTypographyWarnings.length > 0) {
|
|
893
|
+
console.log(
|
|
894
|
+
`\n š« Nested Typography (${nestedTypographyWarnings.length}):`,
|
|
895
|
+
);
|
|
896
|
+
nestedTypographyWarnings
|
|
897
|
+
.slice(0, 5)
|
|
898
|
+
.forEach(warning => console.log(` ${warning}`));
|
|
899
|
+
if (nestedTypographyWarnings.length > 5) {
|
|
900
|
+
console.log(
|
|
901
|
+
` ... and ${
|
|
902
|
+
nestedTypographyWarnings.length - 5
|
|
903
|
+
} more similar warnings`,
|
|
904
|
+
);
|
|
905
|
+
}
|
|
906
|
+
console.log(
|
|
907
|
+
` ā Use spans or other inline elements instead of nested Text components`,
|
|
908
|
+
);
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
if (accessibilityWarnings.length > 0) {
|
|
912
|
+
console.log(
|
|
913
|
+
`\n āæ Accessibility Issues (${accessibilityWarnings.length}):`,
|
|
914
|
+
);
|
|
915
|
+
accessibilityWarnings
|
|
916
|
+
.slice(0, 5)
|
|
917
|
+
.forEach(warning => console.log(` ${warning}`));
|
|
918
|
+
if (accessibilityWarnings.length > 5) {
|
|
919
|
+
console.log(
|
|
920
|
+
` ... and ${
|
|
921
|
+
accessibilityWarnings.length - 5
|
|
922
|
+
} more similar warnings`,
|
|
923
|
+
);
|
|
924
|
+
}
|
|
925
|
+
console.log(
|
|
926
|
+
` ā Add 'as' prop to Heading components for proper semantic HTML`,
|
|
927
|
+
);
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
if (semanticMismatchWarnings.length > 0) {
|
|
931
|
+
console.log(
|
|
932
|
+
`\n š Semantic Mismatches (${semanticMismatchWarnings.length}):`,
|
|
933
|
+
);
|
|
934
|
+
semanticMismatchWarnings
|
|
935
|
+
.slice(0, 5)
|
|
936
|
+
.forEach(warning => console.log(` ${warning}`));
|
|
937
|
+
if (semanticMismatchWarnings.length > 5) {
|
|
938
|
+
console.log(
|
|
939
|
+
` ... and ${
|
|
940
|
+
semanticMismatchWarnings.length - 5
|
|
941
|
+
} more similar warnings`,
|
|
942
|
+
);
|
|
943
|
+
}
|
|
944
|
+
console.log(` ā Review heading level and variant combinations`);
|
|
945
|
+
}
|
|
946
|
+
|
|
641
947
|
console.log('\nš Summary:');
|
|
642
948
|
if (marginWarnings.length > 0)
|
|
643
949
|
console.log(
|
|
@@ -651,6 +957,32 @@ function printReport(report) {
|
|
|
651
957
|
console.log(
|
|
652
958
|
` ⢠${conflictWarnings.length} style conflicts need manual review`,
|
|
653
959
|
);
|
|
960
|
+
if (styleConflictWarnings.length > 0)
|
|
961
|
+
console.log(
|
|
962
|
+
` ⢠${styleConflictWarnings.length} style + margin conflicts detected`,
|
|
963
|
+
);
|
|
964
|
+
if (nestedTypographyWarnings.length > 0)
|
|
965
|
+
console.log(
|
|
966
|
+
` ⢠${nestedTypographyWarnings.length} nested typography components found`,
|
|
967
|
+
);
|
|
968
|
+
if (accessibilityWarnings.length > 0)
|
|
969
|
+
console.log(
|
|
970
|
+
` ⢠${accessibilityWarnings.length} accessibility issues need attention`,
|
|
971
|
+
);
|
|
972
|
+
if (semanticMismatchWarnings.length > 0)
|
|
973
|
+
console.log(
|
|
974
|
+
` ⢠${semanticMismatchWarnings.length} semantic mismatches detected`,
|
|
975
|
+
);
|
|
976
|
+
|
|
977
|
+
// Add helpful note about warning limits
|
|
978
|
+
if (report.warnings.length > 15) {
|
|
979
|
+
console.log(
|
|
980
|
+
'\nš” Note: Only showing first 5 warnings of each type to avoid overwhelming output.',
|
|
981
|
+
);
|
|
982
|
+
console.log(
|
|
983
|
+
' All warnings are still logged in the migration report above.',
|
|
984
|
+
);
|
|
985
|
+
}
|
|
654
986
|
}
|
|
655
987
|
}
|
|
656
988
|
|