Skip to content

Commit 8d0805d

Browse files
committed
feat(@schematics/angular): update TSConfig globals during karma to vitest migration
Automates the transition of developer test configurations from Jasmine typing providers to Vitest globals definitions. The tool parses each collected tsconfig path and actively relocates 'vitest/globals' into the compiler options type list while removing the 'jasmine' package.
1 parent 0d1f298 commit 8d0805d

2 files changed

Lines changed: 76 additions & 0 deletions

File tree

packages/schematics/angular/migrations/migrate-karma-to-vitest/migration.ts

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,10 @@
88

99
import type { json } from '@angular-devkit/core';
1010
import { Rule, SchematicContext, Tree, chain } from '@angular-devkit/schematics';
11+
import { join } from 'node:path/posix';
1112
import { isDeepStrictEqual } from 'util';
1213
import { DependencyType, ExistingBehavior, addDependency } from '../../utility/dependency';
14+
import { JSONFile } from '../../utility/json-file';
1315
import { latestVersions } from '../../utility/latest-versions';
1416
import { TargetDefinition, allTargetOptions, updateWorkspace } from '../../utility/workspace';
1517
import { Builders } from '../../utility/workspace-models';
@@ -119,11 +121,45 @@ async function processTestTargetOptions(
119121
return needsCoverage;
120122
}
121123

124+
function updateTsConfigTypes(
125+
tree: Tree,
126+
tsConfigsToUpdate: Set<string>,
127+
context: SchematicContext,
128+
): void {
129+
for (const tsConfigPath of tsConfigsToUpdate) {
130+
if (tree.exists(tsConfigPath)) {
131+
try {
132+
const json = new JSONFile(tree, tsConfigPath);
133+
const typesPath = ['compilerOptions', 'types'];
134+
const existingTypes = (json.get(typesPath) as string[] | undefined) ?? [];
135+
const newTypes = existingTypes.filter((t) => t !== 'jasmine');
136+
137+
if (!newTypes.includes('vitest/globals')) {
138+
newTypes.push('vitest/globals');
139+
}
140+
141+
if (
142+
newTypes.length !== existingTypes.length ||
143+
newTypes.some((t, i) => t !== existingTypes[i])
144+
) {
145+
json.modify(typesPath, newTypes);
146+
}
147+
} catch (err) {
148+
context.logger.warn(
149+
`Failed to automatically update types in "${tsConfigPath}". ` +
150+
`Please manually remove "jasmine" and add "vitest/globals" to compilerOptions.types.`,
151+
);
152+
}
153+
}
154+
}
155+
}
156+
122157
function updateProjects(tree: Tree, context: SchematicContext): Rule {
123158
return updateWorkspace(async (workspace) => {
124159
let needsCoverage = false;
125160
const removableKarmaConfigs = new Map<string, KarmaConfigProcessingResult>();
126161
const migratedProjects: string[] = [];
162+
const tsConfigsToUpdate = new Set<string>();
127163
const skippedNonApplications: string[] = [];
128164
const skippedMissingAppBuilder: string[] = [];
129165
const manualMigrationFiles: string[] = [];
@@ -169,6 +205,21 @@ function updateProjects(tree: Tree, context: SchematicContext): Rule {
169205
continue;
170206
}
171207

208+
// Collect tsConfig paths to perform globals updates
209+
const baseTsConfig = testTarget.options?.['tsConfig'];
210+
if (typeof baseTsConfig === 'string') {
211+
tsConfigsToUpdate.add(baseTsConfig);
212+
}
213+
if (testTarget.configurations) {
214+
for (const config of Object.values(testTarget.configurations)) {
215+
if (typeof config?.['tsConfig'] === 'string') {
216+
tsConfigsToUpdate.add(config['tsConfig']);
217+
}
218+
}
219+
}
220+
// Always include fallback to the default tsconfig.spec.json path
221+
tsConfigsToUpdate.add(join(project.root, 'tsconfig.spec.json'));
222+
172223
// Store custom build options to move to a new build configuration if needed
173224
const customBuildOptions: Record<string, Record<string, json.JsonValue | undefined>> = {};
174225

@@ -242,6 +293,9 @@ function updateProjects(tree: Tree, context: SchematicContext): Rule {
242293
}
243294
}
244295

296+
// Update TSConfig files to use Vitest types instead of Jasmine
297+
updateTsConfigTypes(tree, tsConfigsToUpdate, context);
298+
245299
// Log summary
246300
context.logger.info('\n--- Karma to Vitest Migration Summary ---');
247301
context.logger.info(`Projects migrated: ${migratedProjects.length}`);

packages/schematics/angular/migrations/migrate-karma-to-vitest/migration_spec.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -420,4 +420,26 @@ module.exports = function (config) {
420420
// Assert that the deletion deferred successfully until BOTH extracted the data
421421
expect(newTree.exists('karma.conf.js')).toBeFalse();
422422
});
423+
424+
it('should automatically transition types in referenced tsconfigs from jasmine to vitest/globals', async () => {
425+
// Create a virtual tsconfig that mimics existing state
426+
tree.create(
427+
'tsconfig.spec.json',
428+
JSON.stringify({
429+
compilerOptions: {
430+
outDir: './out-tsc/spec',
431+
types: ['jasmine', 'node'],
432+
},
433+
files: ['src/test.ts'],
434+
include: ['src/**/*.spec.ts', 'src/**/*.d.ts'],
435+
}),
436+
);
437+
438+
const newTree = await schematicRunner.runSchematic('migrate-karma-to-vitest', {}, tree);
439+
const tsConfigJson = JSON.parse(newTree.readText('tsconfig.spec.json'));
440+
441+
expect(tsConfigJson.compilerOptions.types).toContain('vitest/globals');
442+
expect(tsConfigJson.compilerOptions.types).not.toContain('jasmine');
443+
expect(tsConfigJson.compilerOptions.types).toContain('node');
444+
});
423445
});

0 commit comments

Comments
 (0)