77 */
88
99import type ng from '@angular/compiler-cli' ;
10- import ts from 'typescript' ;
10+ import { createHash } from 'node:crypto' ;
11+ import nodePath from 'node:path' ;
12+ import type ts from 'typescript' ;
1113
1214export type AngularCompilerOptions = ng . CompilerOptions ;
1315export type AngularCompilerHost = ng . CompilerHost ;
@@ -24,36 +26,146 @@ export interface AngularHostOptions {
2426 processWebWorker ( workerFile : string , containingFile : string ) : string ;
2527}
2628
27- // Temporary deep import for host augmentation support.
28- // TODO: Move these to a private exports location or move the implementation into this package.
29- const {
30- augmentHostWithCaching,
31- augmentHostWithReplacements,
32- augmentProgramWithVersioning,
33- } = require ( '@ngtools/webpack/src/ivy/host' ) ;
34-
3529/**
3630 * Patches in-place the `getSourceFiles` function on an instance of a TypeScript
3731 * `Program` to ensure that all returned SourceFile instances have a `version`
3832 * field. The `version` field is required when used with a TypeScript BuilderProgram.
3933 * @param program The TypeScript Program instance to patch.
4034 */
4135export function ensureSourceFileVersions ( program : ts . Program ) : void {
42- augmentProgramWithVersioning ( program ) ;
36+ const baseGetSourceFiles = program . getSourceFiles ;
37+ // TODO: Update Angular compiler to add versions to all internal files and remove this
38+ program . getSourceFiles = function ( ...parameters ) {
39+ const files : readonly ( ts . SourceFile & { version ?: string } ) [ ] = baseGetSourceFiles (
40+ ...parameters ,
41+ ) ;
42+
43+ for ( const file of files ) {
44+ if ( file . version === undefined ) {
45+ file . version = createHash ( 'sha256' ) . update ( file . text ) . digest ( 'hex' ) ;
46+ }
47+ }
48+
49+ return files ;
50+ } ;
51+ }
52+
53+ function augmentHostWithCaching ( host : ts . CompilerHost , cache : Map < string , ts . SourceFile > ) : void {
54+ const baseGetSourceFile = host . getSourceFile ;
55+ host . getSourceFile = function (
56+ fileName ,
57+ languageVersion ,
58+ onError ,
59+ shouldCreateNewSourceFile ,
60+ ...parameters
61+ ) {
62+ if ( ! shouldCreateNewSourceFile && cache . has ( fileName ) ) {
63+ return cache . get ( fileName ) ;
64+ }
65+
66+ const file = baseGetSourceFile . call (
67+ host ,
68+ fileName ,
69+ languageVersion ,
70+ onError ,
71+ true ,
72+ ...parameters ,
73+ ) ;
74+
75+ if ( file ) {
76+ cache . set ( fileName , file ) ;
77+ }
78+
79+ return file ;
80+ } ;
81+ }
82+
83+ function augmentResolveModuleNames (
84+ typescript : typeof ts ,
85+ host : ts . CompilerHost ,
86+ resolvedModuleModifier : (
87+ resolvedModule : ts . ResolvedModule | undefined ,
88+ moduleName : string ,
89+ ) => ts . ResolvedModule | undefined ,
90+ moduleResolutionCache ?: ts . ModuleResolutionCache ,
91+ ) : void {
92+ if ( host . resolveModuleNames ) {
93+ const baseResolveModuleNames = host . resolveModuleNames ;
94+ host . resolveModuleNames = function ( moduleNames : string [ ] , ...parameters ) {
95+ return moduleNames . map ( ( name ) => {
96+ const result = baseResolveModuleNames . call ( host , [ name ] , ...parameters ) ;
97+
98+ return resolvedModuleModifier ( result [ 0 ] , name ) ;
99+ } ) ;
100+ } ;
101+ } else {
102+ host . resolveModuleNames = function (
103+ moduleNames : string [ ] ,
104+ containingFile : string ,
105+ _reusedNames : string [ ] | undefined ,
106+ redirectedReference : ts . ResolvedProjectReference | undefined ,
107+ options : ts . CompilerOptions ,
108+ ) {
109+ return moduleNames . map ( ( name ) => {
110+ const result = typescript . resolveModuleName (
111+ name ,
112+ containingFile ,
113+ options ,
114+ host ,
115+ moduleResolutionCache ,
116+ redirectedReference ,
117+ ) . resolvedModule ;
118+
119+ return resolvedModuleModifier ( result , name ) ;
120+ } ) ;
121+ } ;
122+ }
123+ }
124+
125+ function normalizePath ( path : string ) : string {
126+ return nodePath . win32 . normalize ( path ) . replace ( / \\ / g, nodePath . posix . sep ) ;
127+ }
128+
129+ function augmentHostWithReplacements (
130+ typescript : typeof ts ,
131+ host : ts . CompilerHost ,
132+ replacements : Record < string , string > ,
133+ moduleResolutionCache ?: ts . ModuleResolutionCache ,
134+ ) : void {
135+ if ( Object . keys ( replacements ) . length === 0 ) {
136+ return ;
137+ }
138+
139+ const normalizedReplacements : Record < string , string > = { } ;
140+ for ( const [ key , value ] of Object . entries ( replacements ) ) {
141+ normalizedReplacements [ normalizePath ( key ) ] = normalizePath ( value ) ;
142+ }
143+
144+ const tryReplace = ( resolvedModule : ts . ResolvedModule | undefined ) => {
145+ const replacement = resolvedModule && normalizedReplacements [ resolvedModule . resolvedFileName ] ;
146+ if ( replacement ) {
147+ return {
148+ resolvedFileName : replacement ,
149+ isExternalLibraryImport : / [ / \\ ] n o d e _ m o d u l e s [ / \\ ] / . test ( replacement ) ,
150+ } ;
151+ } else {
152+ return resolvedModule ;
153+ }
154+ } ;
155+
156+ augmentResolveModuleNames ( typescript , host , tryReplace , moduleResolutionCache ) ;
43157}
44158
45159export function createAngularCompilerHost (
160+ typescript : typeof ts ,
46161 compilerOptions : AngularCompilerOptions ,
47162 hostOptions : AngularHostOptions ,
48163) : AngularCompilerHost {
49164 // Create TypeScript compiler host
50- const host : AngularCompilerHost = ts . createIncrementalCompilerHost ( compilerOptions ) ;
51- // Set the parsing mode to the same as TS 5.3 default for tsc. This provides a parse
165+ const host : AngularCompilerHost = typescript . createIncrementalCompilerHost ( compilerOptions ) ;
166+ // Set the parsing mode to the same as TS 5.3+ default for tsc. This provides a parse
52167 // performance improvement by skipping non-type related JSDoc parsing.
53- // NOTE: The check for this enum can be removed when TS 5.3 support is the minimum.
54- if ( ts . JSDocParsingMode ) {
55- host . jsDocParsingMode = ts . JSDocParsingMode . ParseForTypeErrors ;
56- }
168+ host . jsDocParsingMode = typescript . JSDocParsingMode . ParseForTypeErrors ;
57169
58170 // The AOT compiler currently requires this hook to allow for a transformResource hook.
59171 // Once the AOT compiler allows only a transformResource hook, this can be reevaluated.
@@ -90,14 +202,14 @@ export function createAngularCompilerHost(
90202 // Augment TypeScript Host for file replacements option
91203 if ( hostOptions . fileReplacements ) {
92204 // Provide a resolution cache since overriding resolution prevents automatic creation
93- const resolutionCache = ts . createModuleResolutionCache (
205+ const resolutionCache = typescript . createModuleResolutionCache (
94206 host . getCurrentDirectory ( ) ,
95207 host . getCanonicalFileName . bind ( host ) ,
96208 compilerOptions ,
97209 ) ;
98210 host . getModuleResolutionCache = ( ) => resolutionCache ;
99211
100- augmentHostWithReplacements ( host , hostOptions . fileReplacements , resolutionCache ) ;
212+ augmentHostWithReplacements ( typescript , host , hostOptions . fileReplacements , resolutionCache ) ;
101213 }
102214
103215 // Augment TypeScript Host with source file caching if provided
0 commit comments