|
8 | 8 |
|
9 | 9 | import type { json } from '@angular-devkit/core'; |
10 | 10 | import { Rule, SchematicContext, Tree, chain } from '@angular-devkit/schematics'; |
| 11 | +import { join } from 'node:path/posix'; |
11 | 12 | import { isDeepStrictEqual } from 'util'; |
12 | 13 | import { DependencyType, ExistingBehavior, addDependency } from '../../utility/dependency'; |
| 14 | +import { JSONFile } from '../../utility/json-file'; |
13 | 15 | import { latestVersions } from '../../utility/latest-versions'; |
14 | 16 | import { TargetDefinition, allTargetOptions, updateWorkspace } from '../../utility/workspace'; |
15 | 17 | import { Builders } from '../../utility/workspace-models'; |
@@ -119,11 +121,45 @@ async function processTestTargetOptions( |
119 | 121 | return needsCoverage; |
120 | 122 | } |
121 | 123 |
|
| 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 | + |
122 | 157 | function updateProjects(tree: Tree, context: SchematicContext): Rule { |
123 | 158 | return updateWorkspace(async (workspace) => { |
124 | 159 | let needsCoverage = false; |
125 | 160 | const removableKarmaConfigs = new Map<string, KarmaConfigProcessingResult>(); |
126 | 161 | const migratedProjects: string[] = []; |
| 162 | + const tsConfigsToUpdate = new Set<string>(); |
127 | 163 | const skippedNonApplications: string[] = []; |
128 | 164 | const skippedMissingAppBuilder: string[] = []; |
129 | 165 | const manualMigrationFiles: string[] = []; |
@@ -169,6 +205,21 @@ function updateProjects(tree: Tree, context: SchematicContext): Rule { |
169 | 205 | continue; |
170 | 206 | } |
171 | 207 |
|
| 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 | + |
172 | 223 | // Store custom build options to move to a new build configuration if needed |
173 | 224 | const customBuildOptions: Record<string, Record<string, json.JsonValue | undefined>> = {}; |
174 | 225 |
|
@@ -242,6 +293,9 @@ function updateProjects(tree: Tree, context: SchematicContext): Rule { |
242 | 293 | } |
243 | 294 | } |
244 | 295 |
|
| 296 | + // Update TSConfig files to use Vitest types instead of Jasmine |
| 297 | + updateTsConfigTypes(tree, tsConfigsToUpdate, context); |
| 298 | + |
245 | 299 | // Log summary |
246 | 300 | context.logger.info('\n--- Karma to Vitest Migration Summary ---'); |
247 | 301 | context.logger.info(`Projects migrated: ${migratedProjects.length}`); |
|
0 commit comments