@@ -14,7 +14,6 @@ import type {
14
14
Plugin ,
15
15
PluginBuild ,
16
16
} from 'esbuild' ;
17
- import * as assert from 'node:assert' ;
18
17
import { realpath } from 'node:fs/promises' ;
19
18
import { platform } from 'node:os' ;
20
19
import * as path from 'node:path' ;
@@ -30,7 +29,7 @@ import {
30
29
resetCumulativeDurations ,
31
30
} from '../profiling' ;
32
31
import { BundleStylesheetOptions , bundleComponentStylesheet } from '../stylesheets/bundle-options' ;
33
- import { AngularCompilation , FileEmitter } from './angular-compilation' ;
32
+ import { AngularCompilation } from './angular-compilation' ;
34
33
import { AngularHostOptions } from './angular-host' ;
35
34
import { AotCompilation } from './aot-compilation' ;
36
35
import { convertTypeScriptDiagnostic } from './diagnostics' ;
@@ -43,7 +42,7 @@ const WINDOWS_SEP_REGEXP = new RegExp(`\\${path.win32.sep}`, 'g');
43
42
export class SourceFileCache extends Map < string , ts . SourceFile > {
44
43
readonly modifiedFiles = new Set < string > ( ) ;
45
44
readonly babelFileCache = new Map < string , Uint8Array > ( ) ;
46
- readonly typeScriptFileCache = new Map < string , Uint8Array > ( ) ;
45
+ readonly typeScriptFileCache = new Map < string , string | Uint8Array > ( ) ;
47
46
readonly loadResultCache = new MemoryLoadResultCache ( ) ;
48
47
49
48
invalidate ( files : Iterable < string > ) : void {
@@ -116,8 +115,11 @@ export function createCompilerPlugin(
116
115
build . initialOptions . define [ key ] = value . toString ( ) ;
117
116
}
118
117
119
- // The file emitter created during `onStart` that will be used during the build in `onLoad` callbacks for TS files
120
- let fileEmitter : FileEmitter | undefined ;
118
+ // The in-memory cache of TypeScript file outputs will be used during the build in `onLoad` callbacks for TS files.
119
+ // A string value indicates direct TS/NG output and a Uint8Array indicates fully transformed code.
120
+ const typeScriptFileCache =
121
+ pluginOptions . sourceFileCache ?. typeScriptFileCache ??
122
+ new Map < string , string | Uint8Array > ( ) ;
121
123
122
124
// The stylesheet resources from component stylesheets that will be added to the build results output files
123
125
let stylesheetResourceFiles : OutputFile [ ] = [ ] ;
@@ -178,7 +180,6 @@ export function createCompilerPlugin(
178
180
// Initialize the Angular compilation for the current build.
179
181
// In watch mode, previous build state will be reused.
180
182
const {
181
- affectedFiles,
182
183
compilerOptions : { allowJs } ,
183
184
} = await compilation . initialize ( tsconfigPath , hostOptions , ( compilerOptions ) => {
184
185
if (
@@ -219,15 +220,6 @@ export function createCompilerPlugin(
219
220
} ) ;
220
221
shouldTsIgnoreJs = ! allowJs ;
221
222
222
- // Clear affected files from the cache (if present)
223
- if ( pluginOptions . sourceFileCache ) {
224
- for ( const affected of affectedFiles ) {
225
- pluginOptions . sourceFileCache . typeScriptFileCache . delete (
226
- pathToFileURL ( affected . fileName ) . href ,
227
- ) ;
228
- }
229
- }
230
-
231
223
profileSync ( 'NG_DIAGNOSTICS_TOTAL' , ( ) => {
232
224
for ( const diagnostic of compilation . collectDiagnostics ( ) ) {
233
225
const message = convertTypeScriptDiagnostic ( diagnostic ) ;
@@ -239,7 +231,10 @@ export function createCompilerPlugin(
239
231
}
240
232
} ) ;
241
233
242
- fileEmitter = compilation . createFileEmitter ( ) ;
234
+ // Update TypeScript file output cache for all affected files
235
+ for ( const { filename, contents } of compilation . emitAffectedFiles ( ) ) {
236
+ typeScriptFileCache . set ( pathToFileURL ( filename ) . href , contents ) ;
237
+ }
243
238
244
239
// Reset the setup warnings so that they are only shown during the first build.
245
240
setupWarnings = undefined ;
@@ -251,8 +246,6 @@ export function createCompilerPlugin(
251
246
profileAsync (
252
247
'NG_EMIT_TS*' ,
253
248
async ( ) => {
254
- assert . ok ( fileEmitter , 'Invalid plugin execution order' ) ;
255
-
256
249
const request = pluginOptions . fileReplacements ?. [ args . path ] ?? args . path ;
257
250
258
251
// Skip TS load attempt if JS TypeScript compilation not enabled and file is JS
@@ -264,41 +257,35 @@ export function createCompilerPlugin(
264
257
// the options cannot change and do not need to be represented in the key. If the
265
258
// cache is later stored to disk, then the options that affect transform output
266
259
// would need to be added to the key as well as a check for any change of content.
267
- let contents = pluginOptions . sourceFileCache ?. typeScriptFileCache . get (
268
- pathToFileURL ( request ) . href ,
269
- ) ;
260
+ let contents = typeScriptFileCache . get ( pathToFileURL ( request ) . href ) ;
270
261
271
262
if ( contents === undefined ) {
272
- const typescriptResult = await fileEmitter ( request ) ;
273
- if ( ! typescriptResult ?. content ) {
274
- // No TS result indicates the file is not part of the TypeScript program.
275
- // If allowJs is enabled and the file is JS then defer to the next load hook.
276
- if ( ! shouldTsIgnoreJs && / \. [ c m ] ? j s $ / . test ( request ) ) {
277
- return undefined ;
278
- }
279
-
280
- // Otherwise return an error
281
- return {
282
- errors : [
283
- createMissingFileError (
284
- request ,
285
- args . path ,
286
- build . initialOptions . absWorkingDir ?? '' ,
287
- ) ,
288
- ] ,
289
- } ;
263
+ // No TS result indicates the file is not part of the TypeScript program.
264
+ // If allowJs is enabled and the file is JS then defer to the next load hook.
265
+ if ( ! shouldTsIgnoreJs && / \. [ c m ] ? j s $ / . test ( request ) ) {
266
+ return undefined ;
290
267
}
291
268
269
+ // Otherwise return an error
270
+ return {
271
+ errors : [
272
+ createMissingFileError (
273
+ request ,
274
+ args . path ,
275
+ build . initialOptions . absWorkingDir ?? '' ,
276
+ ) ,
277
+ ] ,
278
+ } ;
279
+ } else if ( typeof contents === 'string' ) {
280
+ // A string indicates untransformed output from the TS/NG compiler
292
281
contents = await javascriptTransformer . transformData (
293
282
request ,
294
- typescriptResult . content ,
283
+ contents ,
295
284
true /* skipLinker */ ,
296
285
) ;
297
286
298
- pluginOptions . sourceFileCache ?. typeScriptFileCache . set (
299
- pathToFileURL ( request ) . href ,
300
- contents ,
301
- ) ;
287
+ // Store as the returned Uint8Array to allow caching the fully transformed code
288
+ typeScriptFileCache . set ( pathToFileURL ( request ) . href , contents ) ;
302
289
}
303
290
304
291
return {
0 commit comments