@ethlete/core 5.0.0-next.6 → 5.0.0-next.60

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. package/CHANGELOG.md +686 -0
  2. package/README.md +28 -4
  3. package/fesm2022/ethlete-core.mjs +8020 -3527
  4. package/fesm2022/ethlete-core.mjs.map +1 -1
  5. package/generators/devtools-about/generator.js +122 -0
  6. package/generators/devtools-about/schema.json +25 -0
  7. package/generators/generators.json +26 -5
  8. package/generators/migrate-provider-shape/migration-scope.js +35 -0
  9. package/generators/migrate-provider-shape/migration.js +50 -0
  10. package/generators/migrate-provider-shape/provider-shape.js +284 -0
  11. package/generators/migrate-provider-shape/schema.json +26 -0
  12. package/generators/migrate-surface-interaction-swatch/interaction-swatch.js +63 -0
  13. package/generators/migrate-surface-interaction-swatch/migration.js +36 -0
  14. package/generators/migrate-surface-interaction-swatch/schema.json +26 -0
  15. package/generators/migrate-to-v5/color-naming.js +170 -0
  16. package/generators/migrate-to-v5/create-provider.js +14 -14
  17. package/generators/migrate-to-v5/migration.js +11 -1
  18. package/generators/migrate-to-v5/removed-exports.js +93 -0
  19. package/generators/migrate-to-v5/router-state-service.js +33 -8
  20. package/generators/migrate-to-v5/schema.json +10 -0
  21. package/generators/migrate-to-v5/viewport-service.js +41 -60
  22. package/generators/tailwind-4-color-theme/generator.js +608 -0
  23. package/generators/tailwind-4-color-theme/schema.json +44 -0
  24. package/generators/tailwind-4-surface-theme/generator.js +599 -0
  25. package/generators/tailwind-4-surface-theme/schema.json +48 -0
  26. package/migrations/seo-directive-removed.md +80 -0
  27. package/migrations/surface-theme-regenerate.md +20 -0
  28. package/migrations.json +43 -0
  29. package/package.json +25 -14
  30. package/types/ethlete-core.d.ts +3329 -1774
  31. package/generators/tailwind-4-theme/generator.js +0 -490
  32. package/generators/tailwind-4-theme/schema.json +0 -32
@@ -1,5 +1,4 @@
1
1
  /* eslint-disable @typescript-eslint/no-unused-vars */
2
- /* eslint-disable @typescript-eslint/no-non-null-assertion */
3
2
  import { logger } from '@nx/devkit';
4
3
  import * as ts from 'typescript';
5
4
  export default async function migrateViewportService(tree) {
@@ -12,7 +11,7 @@ export default async function migrateViewportService(tree) {
12
11
  for (const child of children) {
13
12
  const path = dir === '.' ? child : `${dir}/${child}`;
14
13
  if (tree.isFile(path)) {
15
- if (path.endsWith('.ts') && !path.includes('node_modules') && !path.includes('.spec.ts')) {
14
+ if (path.endsWith('.ts') && !path.includes('node_modules')) {
16
15
  tsFiles.push(path);
17
16
  }
18
17
  else if (path.endsWith('.css') ||
@@ -23,6 +22,8 @@ export default async function migrateViewportService(tree) {
23
22
  }
24
23
  }
25
24
  else {
25
+ if (child === 'node_modules')
26
+ continue;
26
27
  findFiles(path);
27
28
  }
28
29
  }
@@ -45,6 +46,9 @@ export default async function migrateViewportService(tree) {
45
46
  continue;
46
47
  logger.log(`Processing: ${filePath}`);
47
48
  const sourceFile = ts.createSourceFile(filePath, content, ts.ScriptTarget.Latest, true);
49
+ const viewportPackage = getViewportServicePackage(sourceFile);
50
+ if (viewportPackage !== '@ethlete/core' && !viewportPackage?.startsWith('@fifa-gg/uikit'))
51
+ continue;
48
52
  // Check if this is a component with a template
49
53
  const templatePath = findTemplateForComponent(tree, filePath, sourceFile);
50
54
  // Track signal properties for template migration
@@ -94,7 +98,7 @@ export default async function migrateViewportService(tree) {
94
98
  }
95
99
  });
96
100
  // Check if any replacements use toObservable or toSignal
97
- for (const [original, replacement] of context.replacements) {
101
+ for (const [original, replacement] of [...context.replacements].sort((a, b) => b[0].length - a[0].length)) {
98
102
  if (replacement.includes('toObservable(')) {
99
103
  allImportsNeeded['@angular/core/rxjs-interop'].add('toObservable');
100
104
  }
@@ -175,6 +179,7 @@ export default async function migrateViewportService(tree) {
175
179
  componentTemplateMigrations.set(filePath, templateMigrationInfo);
176
180
  }
177
181
  const updatedSourceFileForInline = ts.createSourceFile(filePath, updatedContent, ts.ScriptTarget.Latest, true);
182
+ const inlineTemplateEdits = [];
178
183
  updatedSourceFileForInline.forEachChild((node) => {
179
184
  if (ts.isClassDeclaration(node)) {
180
185
  const decorators =
@@ -193,25 +198,25 @@ export default async function migrateViewportService(tree) {
193
198
  let templateText = prop.initializer.getText();
194
199
  // Remove the surrounding quotes/backticks
195
200
  templateText = templateText.slice(1, -1);
196
- // For each signal property, replace usages with ()
197
- for (const propName of templateMigrationInfo.signalProperties) {
198
- const regex = new RegExp(`\\b${propName}(?!\\s*\\()\\b`, 'g');
199
- templateText = templateText.replace(regex, `${propName}()`);
200
- }
201
+ templateText = migrateTemplateText(templateText, templateMigrationInfo.signalProperties);
201
202
  // Re-wrap with original quotes/backticks
202
203
  const quote = prop.initializer.getText()[0];
203
204
  const newInitializer = quote + templateText + quote;
204
- // Replace in the file content
205
- updatedContent =
206
- updatedContent.slice(0, prop.initializer.getStart(updatedSourceFileForInline)) +
207
- newInitializer +
208
- updatedContent.slice(prop.initializer.getEnd());
205
+ inlineTemplateEdits.push({
206
+ start: prop.initializer.getStart(updatedSourceFileForInline),
207
+ end: prop.initializer.getEnd(),
208
+ replacement: newInitializer,
209
+ });
209
210
  }
210
211
  });
211
212
  }
212
213
  });
213
214
  }
214
215
  });
216
+ inlineTemplateEdits.sort((a, b) => b.start - a.start);
217
+ for (const edit of inlineTemplateEdits) {
218
+ updatedContent = updatedContent.slice(0, edit.start) + edit.replacement + updatedContent.slice(edit.end);
219
+ }
215
220
  // Write the updated content if changes were made
216
221
  if (updatedContent !== content) {
217
222
  tree.write(filePath, updatedContent);
@@ -276,28 +281,24 @@ function detectCssVariableUsage(tree, styleFiles) {
276
281
  }
277
282
  return { hasViewportVariables, hasScrollbarVariables };
278
283
  }
284
+ const migrateTemplateExpression = (expression, signalProperties) => {
285
+ let migrated = expression;
286
+ for (const propName of signalProperties) {
287
+ const regex = new RegExp(`\\b${escapeRegExp(propName)}(?!\\s*\\()\\b`, 'g');
288
+ migrated = migrated.replace(regex, `${propName}()`);
289
+ }
290
+ return migrated;
291
+ };
292
+ const migrateTemplateText = (template, signalProperties) => template
293
+ .replace(/{{([\s\S]*?)}}/g, (_, expression) => `{{${migrateTemplateExpression(expression, signalProperties)}}}`)
294
+ .replace(/((?:\[[^\]]+\]|\([^)]+\)|\*[\w.-]+)\s*=\s*)(["'])([\s\S]*?)\2/g, (_, prefix, quote, expression) => `${prefix}${quote}${migrateTemplateExpression(expression, signalProperties)}${quote}`)
295
+ .replace(/@(if|for|switch|case|defer)\s*\(([^\n{}]*)\)/g, (_, block, expression) => `@${block} (${migrateTemplateExpression(expression, signalProperties)})`);
279
296
  function migrateTemplateFile(tree, htmlFilePath, migrationInfo) {
280
297
  const content = tree.read(htmlFilePath, 'utf-8');
281
298
  if (!content)
282
299
  return false;
283
- let updatedContent = content;
284
- let hasChanges = false;
285
- // For each signal property, replace usages with () calls
286
- for (const propName of migrationInfo.signalProperties) {
287
- // Match property usage in various Angular contexts:
288
- // 1. Interpolation: {{ propName }}
289
- // 2. Property binding: [prop]="propName"
290
- // 3. Event binding: (event)="propName === value"
291
- // 4. Structural directives: *ngIf="propName"
292
- // But NOT if already called: propName()
293
- // Create a regex that matches the property but not if followed by (
294
- const regex = new RegExp(`\\b${escapeRegExp(propName)}(?!\\s*\\()\\b`, 'g');
295
- const newContent = updatedContent.replace(regex, `${propName}()`);
296
- if (newContent !== updatedContent) {
297
- updatedContent = newContent;
298
- hasChanges = true;
299
- }
300
- }
300
+ const updatedContent = migrateTemplateText(content, migrationInfo.signalProperties);
301
+ const hasChanges = updatedContent !== content;
301
302
  if (hasChanges) {
302
303
  tree.write(htmlFilePath, updatedContent);
303
304
  }
@@ -340,7 +341,7 @@ function getViewportServicePackage(sourceFile) {
340
341
  sourceFile.forEachChild((node) => {
341
342
  if (ts.isImportDeclaration(node) && node.importClause?.namedBindings) {
342
343
  if (ts.isNamedImports(node.importClause.namedBindings)) {
343
- const hasViewportService = node.importClause.namedBindings.elements.some((el) => el.name.text === 'ViewportService');
344
+ const hasViewportService = node.importClause.namedBindings.elements.some((el) => (el.propertyName?.text ?? el.name.text) === 'ViewportService');
344
345
  if (hasViewportService && ts.isStringLiteral(node.moduleSpecifier)) {
345
346
  packageName = node.moduleSpecifier.text;
346
347
  }
@@ -359,7 +360,7 @@ function getPropertyMaps(packageName) {
359
360
  isLg: isEthleteCore ? 'injectIsLg' : 'injectIsXl',
360
361
  isXl: isEthleteCore ? 'injectIsXl' : 'injectIs2Xl',
361
362
  is2Xl: 'injectIs2Xl',
362
- isBase: isEthleteCore ? 'injectIsBase' : 'injectIsLg',
363
+ isBase: 'injectIsLg',
363
364
  viewportSize: 'injectViewportDimensions',
364
365
  scrollbarSize: 'injectScrollbarDimensions',
365
366
  currentViewport: 'injectCurrentBreakpoint',
@@ -1093,7 +1094,7 @@ function findClassForViewportService(sourceFile, viewportServiceVar) {
1093
1094
  function findAvailableMemberName(baseName, existingMembers) {
1094
1095
  // baseName already has $ suffix for observables at this point
1095
1096
  // Try variations: name, _name, #name
1096
- const candidates = [baseName, `_${baseName}`, `#${baseName}`];
1097
+ const candidates = [baseName, `_${baseName}`];
1097
1098
  for (const candidate of candidates) {
1098
1099
  if (!existingMembers.has(candidate)) {
1099
1100
  return candidate;
@@ -1161,29 +1162,8 @@ function migrateMonitorViewport(sourceFile, content, viewportServiceVars, cssVar
1161
1162
  return { content, imports };
1162
1163
  }
1163
1164
  // Determine what to add to constructor
1164
- const constructorStatements = [];
1165
- if (cssVariablesUsed.hasViewportVariables) {
1166
- constructorStatements.push('writeViewportSizeToCssVariables();');
1167
- imports.push('writeViewportSizeToCssVariables');
1168
- }
1169
- if (cssVariablesUsed.hasScrollbarVariables) {
1170
- constructorStatements.push('writeScrollbarSizeToCssVariables();');
1171
- imports.push('writeScrollbarSizeToCssVariables');
1172
- }
1173
- if (constructorStatements.length === 0) {
1174
- // Just remove monitorViewport calls
1175
- const modifications = monitorViewportCalls.map((call) => ({
1176
- start: call.start,
1177
- end: call.end,
1178
- replacement: '',
1179
- }));
1180
- // Apply modifications from end to start
1181
- modifications.sort((a, b) => b.start - a.start);
1182
- for (const mod of modifications) {
1183
- updatedContent = updatedContent.slice(0, mod.start) + mod.replacement + updatedContent.slice(mod.end);
1184
- }
1185
- return { content: updatedContent, imports };
1186
- }
1165
+ const constructorStatements = ['writeViewportSizeToCssVariables();', 'writeScrollbarSizeToCssVariables();'];
1166
+ imports.push('writeViewportSizeToCssVariables', 'writeScrollbarSizeToCssVariables');
1187
1167
  // Group calls by class
1188
1168
  const callsByClass = new Map();
1189
1169
  for (const call of monitorViewportCalls) {
@@ -1278,8 +1258,10 @@ function addImportsToPackage(sourceFile, content, neededImports, packageName) {
1278
1258
  statement.moduleSpecifier.text === packageName);
1279
1259
  const importsToAdd = Array.from(neededImports).sort();
1280
1260
  if (existingImport?.importClause?.namedBindings && ts.isNamedImports(existingImport.importClause.namedBindings)) {
1281
- const existingImportNames = existingImport.importClause.namedBindings.elements.map((el) => el.name.text);
1282
- const newImports = importsToAdd.filter((imp) => !existingImportNames.includes(imp));
1261
+ const existingElements = existingImport.importClause.namedBindings.elements;
1262
+ const existingImportedNames = existingElements.map((el) => el.propertyName?.text ?? el.name.text);
1263
+ const existingImportNames = existingElements.map((el) => el.getText(sourceFile));
1264
+ const newImports = importsToAdd.filter((imp) => !existingImportedNames.includes(imp));
1283
1265
  if (newImports.length > 0) {
1284
1266
  const allImports = [...existingImportNames, ...newImports].sort();
1285
1267
  const importText = existingImport.getText(sourceFile);
@@ -1675,4 +1657,3 @@ function checkIfPropertyIsUsedElsewhere(sourceFile, classNode, propertyName, pro
1675
1657
  });
1676
1658
  return isUsed;
1677
1659
  }
1678
- //# sourceMappingURL=viewport-service.js.map