@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@entur/typography",
3
- "version": "1.10.0-beta.5",
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": "ba4ae77ebb013a60523baf66ff658b73fa7e0719"
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
- if (changes > 0) {
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
- report.migratedFiles++;
578
- report.totalChanges += changes;
579
- report.totalWarnings += warnings.length;
580
- report.files.push({ file, changes, warnings });
581
- report.warnings.push(...warnings.map(warning => `${file}: ${warning}`));
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
- marginWarnings.forEach(warning => console.log(` ${warning}`));
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
- semanticWarnings.forEach(warning => console.log(` ${warning}`));
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
- conflictWarnings.forEach(warning => console.log(` ${warning}`));
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