@ethlete/core 5.0.0-next.7 ā 5.0.0-next.71
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +849 -0
- package/README.md +28 -4
- package/design/interactive-styles.ts +5 -0
- package/design/theme-css.js +392 -0
- package/design/theme-css.ts +547 -0
- package/fesm2022/ethlete-core.mjs +9357 -5667
- package/fesm2022/ethlete-core.mjs.map +1 -1
- package/generators/devtools-about/generator.js +122 -0
- package/generators/devtools-about/schema.json +25 -0
- package/generators/generators.json +41 -5
- package/generators/migrate-color-theme-variants/color-theme-variants.js +136 -0
- package/generators/migrate-color-theme-variants/migration.js +46 -0
- package/generators/migrate-color-theme-variants/schema.json +26 -0
- package/generators/migrate-create-destroy/create-destroy.js +317 -0
- package/generators/migrate-create-destroy/migration.js +46 -0
- package/generators/migrate-create-destroy/schema.json +26 -0
- package/generators/migrate-legacy-color-themes/legacy-color-themes.js +123 -0
- package/generators/migrate-legacy-color-themes/migration.js +93 -0
- package/generators/migrate-legacy-color-themes/schema.json +26 -0
- package/generators/migrate-provider-shape/migration-scope.js +35 -0
- package/generators/migrate-provider-shape/migration.js +50 -0
- package/generators/migrate-provider-shape/provider-shape.js +284 -0
- package/generators/migrate-provider-shape/schema.json +26 -0
- package/generators/migrate-surface-interaction-swatch/interaction-swatch.js +63 -0
- package/generators/migrate-surface-interaction-swatch/migration.js +36 -0
- package/generators/migrate-surface-interaction-swatch/schema.json +26 -0
- package/generators/migrate-to-v5/apply-replacements.js +14 -0
- package/generators/migrate-to-v5/color-naming.js +124 -0
- package/generators/migrate-to-v5/create-provider.js +67 -55
- package/generators/migrate-to-v5/migration-files.js +13 -0
- package/generators/migrate-to-v5/migration.js +33 -19
- package/generators/migrate-to-v5/removed-exports.js +161 -0
- package/generators/migrate-to-v5/router-state-service.js +125 -330
- package/generators/migrate-to-v5/schema.json +22 -0
- package/generators/migrate-to-v5/service-classes.js +108 -0
- package/generators/migrate-to-v5/sync-observable-consumers.js +80 -0
- package/generators/migrate-to-v5/viewport-service.js +226 -491
- package/generators/tailwind-4-color-theme/generator.js +411 -0
- package/generators/tailwind-4-color-theme/schema.json +44 -0
- package/generators/tailwind-4-surface-theme/generator.js +379 -0
- package/generators/tailwind-4-surface-theme/schema.json +48 -0
- package/migrations/legacy-color-themes.md +38 -0
- package/migrations/seo-directive-removed.md +81 -0
- package/migrations/surface-theme-regenerate.md +20 -0
- package/migrations.json +59 -0
- package/package.json +38 -21
- package/src/lib/animations/easing-tokens-styles.component.css +155 -0
- package/src/lib/providers/visually-hidden-styles.component.css +13 -0
- package/src/lib/resize-handles/resize-handles.component.css +137 -0
- package/src/lib/theming/color-interactive-styles.component.css +147 -0
- package/src/lib/theming/surface-interactive-styles.component.css +42 -0
- package/types/ethlete-core.d.ts +3842 -2205
- package/generators/tailwind-4-theme/generator.js +0 -514
- package/generators/tailwind-4-theme/schema.json +0 -32
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { formatFiles, joinPathFragments, logger, readProjectConfiguration, updateProjectConfiguration, visitNotIgnoredFiles, } from '@nx/devkit';
|
|
2
|
+
const SCRIPT_PATH = 'tools/generate-build-info.cjs';
|
|
3
|
+
const SCRIPT = `const { execSync } = require('child_process');
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
|
|
7
|
+
const [, , outputPath, packageJsonPath] = process.argv;
|
|
8
|
+
|
|
9
|
+
const read = (command) => {
|
|
10
|
+
try {
|
|
11
|
+
return execSync(command, { encoding: 'utf-8', stdio: ['ignore', 'pipe', 'ignore'] }).trim();
|
|
12
|
+
} catch {
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const version = fs.existsSync(packageJsonPath)
|
|
18
|
+
? JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8')).version
|
|
19
|
+
: null;
|
|
20
|
+
|
|
21
|
+
const info = {
|
|
22
|
+
version: version ?? 'unknown',
|
|
23
|
+
sha: read('git rev-parse --short HEAD') ?? 'unknown',
|
|
24
|
+
branch: read('git rev-parse --abbrev-ref HEAD') ?? 'unknown',
|
|
25
|
+
builtAt: new Date().toISOString(),
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const contents =
|
|
29
|
+
'// Generated by ' +
|
|
30
|
+
${JSON.stringify(SCRIPT_PATH)} +
|
|
31
|
+
' - do not edit.\\n\\nexport const APP_BUILD_INFO = ' +
|
|
32
|
+
JSON.stringify(info, null, 2) +
|
|
33
|
+
' as const;\\n';
|
|
34
|
+
|
|
35
|
+
fs.mkdirSync(path.dirname(outputPath), { recursive: true });
|
|
36
|
+
fs.writeFileSync(outputPath, contents);
|
|
37
|
+
`;
|
|
38
|
+
const PLACEHOLDER = `// Generated by ${SCRIPT_PATH} - do not edit.
|
|
39
|
+
|
|
40
|
+
export const APP_BUILD_INFO = {
|
|
41
|
+
version: 'unknown',
|
|
42
|
+
sha: 'unknown',
|
|
43
|
+
branch: 'unknown',
|
|
44
|
+
builtAt: '1970-01-01T00:00:00.000Z',
|
|
45
|
+
} as const;
|
|
46
|
+
`;
|
|
47
|
+
const relativeImport = (from, to) => {
|
|
48
|
+
const fromParts = from.split('/').slice(0, -1);
|
|
49
|
+
const toParts = to.replace(/\.ts$/, '').split('/');
|
|
50
|
+
while (fromParts.length && toParts.length && fromParts[0] === toParts[0]) {
|
|
51
|
+
fromParts.shift();
|
|
52
|
+
toParts.shift();
|
|
53
|
+
}
|
|
54
|
+
const up = fromParts.length ? fromParts.map(() => '..') : ['.'];
|
|
55
|
+
return [...up, ...toParts].join('/');
|
|
56
|
+
};
|
|
57
|
+
/** Wires the `provideQueryDevtools({ about })` call up to the generated constant, if it can be found. */
|
|
58
|
+
const wireProvider = (tree, projectRoot, outputPath) => {
|
|
59
|
+
const candidates = [];
|
|
60
|
+
visitNotIgnoredFiles(tree, projectRoot, (file) => {
|
|
61
|
+
if (!file.endsWith('.ts') ||
|
|
62
|
+
file.endsWith('.spec.ts') ||
|
|
63
|
+
file.endsWith('.test.ts') ||
|
|
64
|
+
file.endsWith('.stories.ts')) {
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
const contents = tree.read(file, 'utf-8');
|
|
68
|
+
if (!contents?.includes('provideQueryDevtools('))
|
|
69
|
+
return;
|
|
70
|
+
candidates.push({ file, contents });
|
|
71
|
+
});
|
|
72
|
+
const alreadyWired = candidates.some(({ contents }) => /provideQueryDevtools\(\s*{[\s\S]*?about\s*:\s*APP_BUILD_INFO/.test(contents));
|
|
73
|
+
if (alreadyWired)
|
|
74
|
+
return true;
|
|
75
|
+
const candidate = candidates.find(({ contents }) => contents.includes('provideQueryDevtools()'));
|
|
76
|
+
if (!candidate) {
|
|
77
|
+
for (const { file } of candidates) {
|
|
78
|
+
logger.warn(`${file} already passes options to provideQueryDevtools() - add \`about: APP_BUILD_INFO\` to them yourself.`);
|
|
79
|
+
}
|
|
80
|
+
return candidates.length > 0;
|
|
81
|
+
}
|
|
82
|
+
const importPath = relativeImport(candidate.file, outputPath);
|
|
83
|
+
tree.write(candidate.file, `import { APP_BUILD_INFO } from '${importPath}';\n` +
|
|
84
|
+
candidate.contents.replace('provideQueryDevtools()', 'provideQueryDevtools({ about: APP_BUILD_INFO })'));
|
|
85
|
+
return true;
|
|
86
|
+
};
|
|
87
|
+
export const devtoolsAboutGenerator = async (tree, options) => {
|
|
88
|
+
const project = readProjectConfiguration(tree, options.project);
|
|
89
|
+
const outputPath = options.outputPath ?? joinPathFragments(project.sourceRoot ?? project.root, 'build-info.ts');
|
|
90
|
+
if (!tree.exists(SCRIPT_PATH))
|
|
91
|
+
tree.write(SCRIPT_PATH, SCRIPT);
|
|
92
|
+
if (!tree.exists(outputPath))
|
|
93
|
+
tree.write(outputPath, PLACEHOLDER);
|
|
94
|
+
const gitignore = tree.read('.gitignore', 'utf-8') ?? '';
|
|
95
|
+
// Regenerated on every build, and its SHA changes with every commit - committing it would put a
|
|
96
|
+
// one-line diff in every commit that says nothing about the change.
|
|
97
|
+
if (!gitignore.includes(outputPath)) {
|
|
98
|
+
tree.write('.gitignore', `${gitignore.trimEnd()}${gitignore.trim() ? '\n' : ''}${outputPath}\n`);
|
|
99
|
+
}
|
|
100
|
+
project.targets ??= {};
|
|
101
|
+
project.targets['build-info'] = {
|
|
102
|
+
executor: 'nx:run-commands',
|
|
103
|
+
options: {
|
|
104
|
+
commands: [`node ${SCRIPT_PATH} ${outputPath} ${joinPathFragments(project.root, 'package.json')}`],
|
|
105
|
+
},
|
|
106
|
+
};
|
|
107
|
+
for (const target of ['build', 'serve']) {
|
|
108
|
+
const config = project.targets[target];
|
|
109
|
+
if (!config)
|
|
110
|
+
continue;
|
|
111
|
+
config.dependsOn ??= [];
|
|
112
|
+
if (!config.dependsOn.includes('build-info'))
|
|
113
|
+
config.dependsOn.push('build-info');
|
|
114
|
+
}
|
|
115
|
+
updateProjectConfiguration(tree, options.project, project);
|
|
116
|
+
if (!wireProvider(tree, project.root, outputPath)) {
|
|
117
|
+
logger.warn(`No provideQueryDevtools() call found in ${project.root} - pass \`about: APP_BUILD_INFO\` to it yourself.`);
|
|
118
|
+
}
|
|
119
|
+
if (!options.skipFormat)
|
|
120
|
+
await formatFiles(tree);
|
|
121
|
+
};
|
|
122
|
+
export default devtoolsAboutGenerator;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/schema",
|
|
3
|
+
"$id": "devtools-about",
|
|
4
|
+
"title": "Query devtools About generator",
|
|
5
|
+
"description": "Generates the application's build info (version, commit SHA, branch, build time) and wires it into provideQueryDevtools({ about })",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"properties": {
|
|
8
|
+
"project": {
|
|
9
|
+
"type": "string",
|
|
10
|
+
"description": "The application to wire up",
|
|
11
|
+
"$default": { "$source": "argv", "index": 0 },
|
|
12
|
+
"x-prompt": "Which application should report its build info?"
|
|
13
|
+
},
|
|
14
|
+
"outputPath": {
|
|
15
|
+
"type": "string",
|
|
16
|
+
"description": "Where to write the generated build-info file. Defaults to `<sourceRoot>/build-info.ts`."
|
|
17
|
+
},
|
|
18
|
+
"skipFormat": {
|
|
19
|
+
"type": "boolean",
|
|
20
|
+
"description": "Skip formatting files after generation",
|
|
21
|
+
"default": false
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
"required": ["project"]
|
|
25
|
+
}
|
|
@@ -1,14 +1,50 @@
|
|
|
1
1
|
{
|
|
2
|
+
"$schema": "https://json.schemastore.org/nx-generators",
|
|
2
3
|
"generators": {
|
|
3
4
|
"migrate-to-v5": {
|
|
4
|
-
"
|
|
5
|
+
"implementation": "./migrate-to-v5/migration",
|
|
5
6
|
"schema": "./migrate-to-v5/schema.json",
|
|
6
7
|
"description": "Migrate to v5"
|
|
7
8
|
},
|
|
8
|
-
"
|
|
9
|
-
"
|
|
10
|
-
"schema": "./
|
|
11
|
-
"description": "
|
|
9
|
+
"migrate-provider-shape": {
|
|
10
|
+
"implementation": "./migrate-provider-shape/migration",
|
|
11
|
+
"schema": "./migrate-provider-shape/schema.json",
|
|
12
|
+
"description": "Rewrite destructured provider tuples into the tree-shakeable definition shape"
|
|
13
|
+
},
|
|
14
|
+
"migrate-create-destroy": {
|
|
15
|
+
"implementation": "./migrate-create-destroy/migration",
|
|
16
|
+
"schema": "./migrate-create-destroy/schema.json",
|
|
17
|
+
"description": "Rewrite takeUntil(this.destroy$) on a createDestroy() field into takeUntilDestroyed()"
|
|
18
|
+
},
|
|
19
|
+
"migrate-surface-interaction-swatch": {
|
|
20
|
+
"implementation": "./migrate-surface-interaction-swatch/migration",
|
|
21
|
+
"schema": "./migrate-surface-interaction-swatch/schema.json",
|
|
22
|
+
"description": "Rewrite surface theme interactionColor maps into the { color, onColor, inkColor } swatch shape"
|
|
23
|
+
},
|
|
24
|
+
"migrate-color-theme-variants": {
|
|
25
|
+
"implementation": "./migrate-color-theme-variants/migration",
|
|
26
|
+
"schema": "./migrate-color-theme-variants/schema.json",
|
|
27
|
+
"description": "Rewrite surface theme colorTheme and semanticColorThemes into colorThemeVariants"
|
|
28
|
+
},
|
|
29
|
+
"migrate-legacy-color-themes": {
|
|
30
|
+
"implementation": "./migrate-legacy-color-themes/migration",
|
|
31
|
+
"schema": "./migrate-legacy-color-themes/schema.json",
|
|
32
|
+
"description": "Rewrite provideColorThemes into provideColorThemesWithTailwind4 and list the Tailwind 4 setup that is left"
|
|
33
|
+
},
|
|
34
|
+
"tailwind-4-color-theme": {
|
|
35
|
+
"implementation": "./tailwind-4-color-theme/generator",
|
|
36
|
+
"schema": "./tailwind-4-color-theme/schema.json",
|
|
37
|
+
"description": "Generate color theming for Tailwind CSS v4"
|
|
38
|
+
},
|
|
39
|
+
"tailwind-4-surface-theme": {
|
|
40
|
+
"implementation": "./tailwind-4-surface-theme/generator",
|
|
41
|
+
"schema": "./tailwind-4-surface-theme/schema.json",
|
|
42
|
+
"description": "Generate surface theming for Tailwind CSS v4"
|
|
43
|
+
},
|
|
44
|
+
"devtools-about": {
|
|
45
|
+
"implementation": "./devtools-about/generator",
|
|
46
|
+
"schema": "./devtools-about/schema.json",
|
|
47
|
+
"description": "Generate the app's build info and wire it into provideQueryDevtools({ about })"
|
|
12
48
|
}
|
|
13
49
|
}
|
|
14
50
|
}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { Project, SyntaxKind } from 'ts-morph';
|
|
2
|
+
const createProject = () => new Project({
|
|
3
|
+
useInMemoryFileSystem: true,
|
|
4
|
+
compilerOptions: {
|
|
5
|
+
target: 99,
|
|
6
|
+
module: 99,
|
|
7
|
+
},
|
|
8
|
+
});
|
|
9
|
+
const findProperty = (literal, name) => {
|
|
10
|
+
const property = literal.getProperty(name);
|
|
11
|
+
return property?.isKind(SyntaxKind.PropertyAssignment) ? property : undefined;
|
|
12
|
+
};
|
|
13
|
+
const stringValue = (property) => {
|
|
14
|
+
const initializer = property?.getInitializer();
|
|
15
|
+
return initializer?.isKind(SyntaxKind.StringLiteral) || initializer?.isKind(SyntaxKind.NoSubstitutionTemplateLiteral)
|
|
16
|
+
? initializer.getLiteralText()
|
|
17
|
+
: undefined;
|
|
18
|
+
};
|
|
19
|
+
const literalsOf = (sourceFile) => sourceFile.getDescendantsOfKind(SyntaxKind.ObjectLiteralExpression);
|
|
20
|
+
export const collectColorThemesInFile = (filePath, content) => {
|
|
21
|
+
if (!content.includes('primary'))
|
|
22
|
+
return [];
|
|
23
|
+
return literalsOf(createProject().createSourceFile(filePath, content)).flatMap((literal) => {
|
|
24
|
+
const name = stringValue(findProperty(literal, 'name'));
|
|
25
|
+
if (!name || !literal.getProperty('primary'))
|
|
26
|
+
return [];
|
|
27
|
+
return [
|
|
28
|
+
{
|
|
29
|
+
name,
|
|
30
|
+
type: stringValue(findProperty(literal, 'type')),
|
|
31
|
+
isDefault: findProperty(literal, 'isDefault')?.getInitializer()?.getText() === 'true',
|
|
32
|
+
},
|
|
33
|
+
];
|
|
34
|
+
});
|
|
35
|
+
};
|
|
36
|
+
const uniqueName = (names) => {
|
|
37
|
+
const unique = [...new Set(names)];
|
|
38
|
+
return unique.length === 1 ? unique[0] : undefined;
|
|
39
|
+
};
|
|
40
|
+
const quote = (value) => (/^[A-Za-z_$][\w$]*$/.test(value) ? value : `'${value}'`);
|
|
41
|
+
/**
|
|
42
|
+
* Rewrites a surface theme's deprecated `colorTheme` and `semanticColorThemes` into `colorThemeVariants`, keyed by
|
|
43
|
+
* the `isDefault` theme and the theme of each `type` in `themes`. Existing `colorThemeVariants` entries win, then
|
|
44
|
+
* `semanticColorThemes`, then `colorTheme`, the same order the runtime resolves them in.
|
|
45
|
+
*/
|
|
46
|
+
export const migrateColorThemeVariantsInFile = (filePath, content, themes) => {
|
|
47
|
+
if (!content.includes('colorTheme') && !content.includes('semanticColorThemes')) {
|
|
48
|
+
return { changed: false, content, unresolved: [] };
|
|
49
|
+
}
|
|
50
|
+
const defaultName = uniqueName(themes.filter((theme) => theme.isDefault).map((theme) => theme.name));
|
|
51
|
+
const nameOfType = (type) => uniqueName(themes.filter((theme) => theme.type === type).map((theme) => theme.name));
|
|
52
|
+
const sourceFile = createProject().createSourceFile(filePath, content);
|
|
53
|
+
const unresolved = [];
|
|
54
|
+
let changed = false;
|
|
55
|
+
const surfaces = literalsOf(sourceFile).filter((literal) => literal.getProperty('elevation'));
|
|
56
|
+
for (const literal of surfaces) {
|
|
57
|
+
const colorTheme = findProperty(literal, 'colorTheme');
|
|
58
|
+
const semantic = findProperty(literal, 'semanticColorThemes');
|
|
59
|
+
if (!colorTheme && !semantic)
|
|
60
|
+
continue;
|
|
61
|
+
const surfaceName = stringValue(findProperty(literal, 'name')) ?? '<unnamed surface>';
|
|
62
|
+
const variants = findProperty(literal, 'colorThemeVariants');
|
|
63
|
+
const variantsLiteral = variants?.getInitializer();
|
|
64
|
+
if (variants && !variantsLiteral?.isKind(SyntaxKind.ObjectLiteralExpression)) {
|
|
65
|
+
unresolved.push(`${surfaceName}: colorThemeVariants is not an object literal`);
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
const existingKeys = new Set(variantsLiteral?.isKind(SyntaxKind.ObjectLiteralExpression)
|
|
69
|
+
? variantsLiteral
|
|
70
|
+
.getProperties()
|
|
71
|
+
.flatMap((property) => property.isKind(SyntaxKind.PropertyAssignment) ? [property.getName().replace(/['"]/g, '')] : [])
|
|
72
|
+
: []);
|
|
73
|
+
const additions = new Map();
|
|
74
|
+
const toRemove = [];
|
|
75
|
+
const add = (key, value) => {
|
|
76
|
+
if (!existingKeys.has(key) && !additions.has(key))
|
|
77
|
+
additions.set(key, value);
|
|
78
|
+
};
|
|
79
|
+
let semanticResolved = true;
|
|
80
|
+
if (semantic) {
|
|
81
|
+
const semanticLiteral = semantic.getInitializer();
|
|
82
|
+
if (!semanticLiteral?.isKind(SyntaxKind.ObjectLiteralExpression)) {
|
|
83
|
+
semanticResolved = false;
|
|
84
|
+
unresolved.push(`${surfaceName}: semanticColorThemes is not an object literal`);
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
const pending = [];
|
|
88
|
+
for (const property of semanticLiteral.getProperties()) {
|
|
89
|
+
const type = property.isKind(SyntaxKind.PropertyAssignment) ? property.getName().replace(/['"]/g, '') : '';
|
|
90
|
+
const value = property.isKind(SyntaxKind.PropertyAssignment) ? stringValue(property) : undefined;
|
|
91
|
+
const key = type ? nameOfType(type) : undefined;
|
|
92
|
+
if (!value || !key) {
|
|
93
|
+
semanticResolved = false;
|
|
94
|
+
unresolved.push(`${surfaceName}: semanticColorThemes entry ${property.getText()} has no single color theme of that type or no string value`);
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
pending.push([key, value]);
|
|
98
|
+
}
|
|
99
|
+
if (semanticResolved)
|
|
100
|
+
pending.forEach(([key, value]) => add(key, value));
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
let colorThemeResolved = true;
|
|
104
|
+
if (colorTheme) {
|
|
105
|
+
const value = stringValue(colorTheme);
|
|
106
|
+
if (!value || !defaultName) {
|
|
107
|
+
colorThemeResolved = false;
|
|
108
|
+
unresolved.push(`${surfaceName}: colorTheme ${colorTheme.getInitializer()?.getText()} needs a string value and exactly one isDefault color theme`);
|
|
109
|
+
}
|
|
110
|
+
else if (value !== defaultName) {
|
|
111
|
+
add(defaultName, value);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
const added = [...additions];
|
|
115
|
+
if (semantic && semanticResolved) {
|
|
116
|
+
toRemove.push(semantic);
|
|
117
|
+
}
|
|
118
|
+
if (colorTheme && colorThemeResolved) {
|
|
119
|
+
toRemove.push(colorTheme);
|
|
120
|
+
}
|
|
121
|
+
if (toRemove.length === 0)
|
|
122
|
+
continue;
|
|
123
|
+
if (added.length > 0) {
|
|
124
|
+
const text = added.map(([key, value]) => `${quote(key)}: '${value}'`).join(', ');
|
|
125
|
+
if (variantsLiteral?.isKind(SyntaxKind.ObjectLiteralExpression)) {
|
|
126
|
+
variantsLiteral.addPropertyAssignments(added.map(([key, value]) => ({ name: quote(key), initializer: `'${value}'` })));
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
toRemove.shift()?.replaceWithText(`colorThemeVariants: { ${text} }`);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
toRemove.forEach((property) => property.remove());
|
|
133
|
+
changed = true;
|
|
134
|
+
}
|
|
135
|
+
return { changed, content: changed ? sourceFile.getFullText() : content, unresolved };
|
|
136
|
+
};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { formatFiles, visitNotIgnoredFiles } from '@nx/devkit';
|
|
2
|
+
import { createMigrationScope } from '../migrate-provider-shape/migration-scope.js';
|
|
3
|
+
import { collectColorThemesInFile, migrateColorThemeVariantsInFile } from './color-theme-variants.js';
|
|
4
|
+
const isSource = (filePath) => filePath.endsWith('.ts') && !filePath.endsWith('.d.ts');
|
|
5
|
+
export default async function migrateColorThemeVariants(tree, schema) {
|
|
6
|
+
console.log('\nš Migrating surface colorTheme and semanticColorThemes to colorThemeVariants...');
|
|
7
|
+
const themes = [];
|
|
8
|
+
visitNotIgnoredFiles(tree, '', (filePath) => {
|
|
9
|
+
if (!isSource(filePath))
|
|
10
|
+
return;
|
|
11
|
+
const content = tree.read(filePath, 'utf-8');
|
|
12
|
+
if (content)
|
|
13
|
+
themes.push(...collectColorThemesInFile(filePath, content));
|
|
14
|
+
});
|
|
15
|
+
const defaults = themes.filter((theme) => theme.isDefault).map((theme) => theme.name);
|
|
16
|
+
console.log(` Color themes found: ${themes.map((theme) => theme.name).join(', ') || 'none'}`);
|
|
17
|
+
console.log(` Default color theme: ${defaults.join(', ') || 'none'}`);
|
|
18
|
+
const scope = createMigrationScope(tree, schema);
|
|
19
|
+
console.log(` Scope: ${scope.describe()}`);
|
|
20
|
+
let filesChanged = 0;
|
|
21
|
+
let unresolvedCount = 0;
|
|
22
|
+
scope.visit(tree, (filePath) => {
|
|
23
|
+
if (!isSource(filePath))
|
|
24
|
+
return;
|
|
25
|
+
const before = tree.read(filePath, 'utf-8');
|
|
26
|
+
if (!before)
|
|
27
|
+
return;
|
|
28
|
+
const result = migrateColorThemeVariantsInFile(filePath, before, themes);
|
|
29
|
+
for (const message of result.unresolved) {
|
|
30
|
+
unresolvedCount++;
|
|
31
|
+
console.warn(` ā ${filePath}: ${message}`);
|
|
32
|
+
}
|
|
33
|
+
if (!result.changed)
|
|
34
|
+
return;
|
|
35
|
+
tree.write(filePath, result.content);
|
|
36
|
+
filesChanged++;
|
|
37
|
+
console.log(` ā ${filePath}`);
|
|
38
|
+
});
|
|
39
|
+
console.log(filesChanged === 0 ? ' Nothing to migrate.\n' : `\nā
Migrated ${filesChanged} file(s).\n`);
|
|
40
|
+
if (unresolvedCount > 0) {
|
|
41
|
+
console.warn(` Review ${unresolvedCount} unresolved value(s) manually. They keep working until the next major.\n`);
|
|
42
|
+
}
|
|
43
|
+
if (!schema.skipFormat) {
|
|
44
|
+
await formatFiles(tree);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/schema",
|
|
3
|
+
"$id": "migrate-color-theme-variants",
|
|
4
|
+
"title": "Migrate surface colorTheme and semanticColorThemes to colorThemeVariants",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"properties": {
|
|
7
|
+
"projects": {
|
|
8
|
+
"type": "array",
|
|
9
|
+
"items": { "type": "string" },
|
|
10
|
+
"description": "Nx projects to migrate. Defaults to every project in the workspace.",
|
|
11
|
+
"alias": "project"
|
|
12
|
+
},
|
|
13
|
+
"include": {
|
|
14
|
+
"type": "array",
|
|
15
|
+
"items": { "type": "string" },
|
|
16
|
+
"description": "Path prefixes to migrate, relative to the workspace root (e.g. 'apps/web'). Unioned with `projects`. Defaults to the whole workspace.",
|
|
17
|
+
"alias": "includePath"
|
|
18
|
+
},
|
|
19
|
+
"skipFormat": {
|
|
20
|
+
"type": "boolean",
|
|
21
|
+
"description": "Skip formatting files after migration",
|
|
22
|
+
"default": false
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
"required": []
|
|
26
|
+
}
|