@@ -10,10 +10,13 @@ import { type PartialMessage, formatMessages } from 'esbuild';
1010import { existsSync } from 'node:fs' ;
1111import {
1212 type AngularCompilation ,
13+ DiagnosticModes ,
14+ type EmitFileResult ,
1315 createAngularCompilation ,
1416} from '../../../tools/angular/compilation' ;
1517import type { ComponentStylesheetBundler } from '../../../tools/esbuild/angular/component-stylesheets' ;
1618import { useTypeChecking } from '../../../utils/environment-options' ;
19+ import { assertIsError } from '../../../utils/error' ;
1720import { toPosixPath } from '../../../utils/path' ;
1821import type { BuilderState } from '../builder-state' ;
1922import type { LibraryCompilationOutput , NormalizedLibraryOptions } from '../types' ;
@@ -70,12 +73,22 @@ export async function compileLibrary(
7073
7174 if ( effectiveModifiedFiles && effectiveModifiedFiles . size > 0 ) {
7275 await compilationInstance . update ?.( effectiveModifiedFiles ) ;
73- buildState . clearFileReferences ( effectiveModifiedFiles ) ;
7476 }
7577
7678 const stylesheetWarnings : PartialMessage [ ] = [ ] ;
7779 const stylesheetErrors : PartialMessage [ ] = [ ] ;
7880 const failedFiles = new Set < string > ( ) ;
81+ const clearedReferenceFiles = new Set < string > ( ) ;
82+ const clearPreviousReferencesOnce = ( targetFile : string ) : void => {
83+ if ( ! cached ) {
84+ return ;
85+ }
86+ const posixTarget = toPosixPath ( targetFile ) ;
87+ if ( ! clearedReferenceFiles . has ( posixTarget ) ) {
88+ clearedReferenceFiles . add ( posixTarget ) ;
89+ buildState . clearFileReferences ( [ posixTarget ] ) ;
90+ }
91+ } ;
7992
8093 const hostOptions = {
8194 modifiedFiles : effectiveModifiedFiles ,
@@ -86,20 +99,24 @@ export async function compileLibrary(
8699 ) : Promise < string > {
87100 const result = stylesheetFile
88101 ? await stylesheetBundler . bundleFile ( stylesheetFile )
89- : await stylesheetBundler . bundleInline ( data , containingFile ) ;
90-
91- if ( result . referencedFiles ) {
92- buildState . addFileReferences ( containingFile , result . referencedFiles ) ;
93- if ( stylesheetFile ) {
94- buildState . addFileReferences ( stylesheetFile , result . referencedFiles ) ;
95- }
96- }
102+ : await stylesheetBundler . bundleInline (
103+ data ,
104+ containingFile ,
105+ containingFile . endsWith ( '.html' ) ? 'css' : undefined ,
106+ ) ;
97107
98108 if ( result . warnings . length > 0 ) {
99109 stylesheetWarnings . push ( ...result . warnings ) ;
100110 }
101111
102112 if ( result . errors ?. length ) {
113+ if ( result . referencedFiles ) {
114+ buildState . addFileReferences ( containingFile , result . referencedFiles ) ;
115+ if ( stylesheetFile ) {
116+ buildState . addFileReferences ( stylesheetFile , result . referencedFiles ) ;
117+ }
118+ }
119+
103120 stylesheetErrors . push ( ...result . errors ) ;
104121 failedFiles . add ( toPosixPath ( containingFile ) ) ;
105122 if ( stylesheetFile ) {
@@ -109,12 +126,28 @@ export async function compileLibrary(
109126 return '' ;
110127 }
111128
129+ clearPreviousReferencesOnce ( containingFile ) ;
130+ if ( stylesheetFile ) {
131+ clearPreviousReferencesOnce ( stylesheetFile ) ;
132+ }
133+
134+ if ( result . referencedFiles ) {
135+ buildState . addFileReferences ( containingFile , result . referencedFiles ) ;
136+ if ( stylesheetFile ) {
137+ buildState . addFileReferences ( stylesheetFile , result . referencedFiles ) ;
138+ }
139+ }
140+
112141 return result . contents ;
113142 } ,
114143 processWebWorker : ( ) => '' ,
115144 } ;
116145
117- const { referencedFiles, compilerOptions } = await compilationInstance . initialize (
146+ const {
147+ referencedFiles,
148+ compilerOptions,
149+ warnings : initWarnings ,
150+ } = await compilationInstance . initialize (
118151 tsConfigPath ,
119152 hostOptions ,
120153 {
@@ -129,6 +162,10 @@ export async function compileLibrary(
129162 'library' ,
130163 ) ;
131164
165+ if ( initWarnings ?. length ) {
166+ stylesheetWarnings . unshift ( ...initWarnings ) ;
167+ }
168+
132169 const hasStylesheetErrors = stylesheetErrors . length > 0 ;
133170 const emittedFiles = hasStylesheetErrors ? [ ] : await compilationInstance . emitAffectedFiles ( ) ;
134171 const diagnosePromise = runDiagnosticsAndFormat (
@@ -141,49 +178,14 @@ export async function compileLibrary(
141178
142179 const esmFiles = cached ?. esmFiles ?? new Map < string , string > ( ) ;
143180 const dtsFiles = cached ?. dtsFiles ?? new Map < string , string > ( ) ;
144- const changedEsmFiles = new Set < string > ( ) ;
145- const changedDtsFiles = new Set < string > ( ) ;
146181 const posixReferencedFiles = new Set ( referencedFiles . map ( toPosixPath ) ) ;
147-
148- if ( cached && effectiveModifiedFiles ) {
149- for ( const modifiedFile of effectiveModifiedFiles ) {
150- if ( posixReferencedFiles . has ( modifiedFile ) ) {
151- continue ;
152- }
153-
154- const basePathWithoutExt = modifiedFile . replace ( / (?: \. d \. [ c m ] ? t s | \. [ c m ] ? [ j t ] s x ? ) $ / i, '' ) ;
155- if ( basePathWithoutExt === modifiedFile || existsSync ( modifiedFile ) ) {
156- continue ;
157- }
158-
159- for ( const ext of EMITTED_EXTENSIONS ) {
160- const outputPath = `${ basePathWithoutExt } ${ ext } ` ;
161- const mapPath = `${ outputPath } .map` ;
162- if ( esmFiles . delete ( outputPath ) ) {
163- changedEsmFiles . add ( outputPath ) ;
164- }
165- if ( dtsFiles . delete ( outputPath ) ) {
166- changedDtsFiles . add ( outputPath ) ;
167- }
168- esmFiles . delete ( mapPath ) ;
169- dtsFiles . delete ( mapPath ) ;
170- }
171- }
172- }
173-
174- for ( const { filename, contents } of emittedFiles ) {
175- const normalized = toPosixPath ( filename ) ;
176- const isMap = normalized . endsWith ( '.map' ) ;
177- const isDts = isMap ? isDeclarationSourceMapFile ( normalized ) : isDeclarationFile ( normalized ) ;
178- const targetFiles = isDts ? dtsFiles : esmFiles ;
179-
180- if ( targetFiles . get ( normalized ) !== contents ) {
181- targetFiles . set ( normalized , contents ) ;
182- ( isDts ? changedDtsFiles : changedEsmFiles ) . add (
183- isMap ? normalized . slice ( 0 , - 4 ) : normalized ,
184- ) ;
185- }
186- }
182+ const { changedEsmFiles, changedDtsFiles } = updateEmittedFileCaches (
183+ emittedFiles ,
184+ esmFiles ,
185+ dtsFiles ,
186+ posixReferencedFiles ,
187+ cached ? effectiveModifiedFiles : undefined ,
188+ ) ;
187189
188190 return {
189191 esmFiles,
@@ -210,13 +212,79 @@ export async function compileLibrary(
210212 }
211213}
212214
215+ /**
216+ * Updates the in-memory ESM and DTS file maps with newly emitted files and prunes deleted
217+ * source files, returning the sets of changed ESM and DTS file paths.
218+ *
219+ * @param emittedFiles Newly emitted files from the Angular compilation.
220+ * @param esmFiles In-memory map of POSIX ESM file paths to contents.
221+ * @param dtsFiles In-memory map of POSIX declaration file paths to contents.
222+ * @param posixReferencedFiles Set of POSIX file paths currently referenced by the program.
223+ * @param modifiedFiles Optional set of modified POSIX file paths in incremental builds.
224+ * @returns Sets of changed ESM and DTS file paths.
225+ */
226+ function updateEmittedFileCaches (
227+ emittedFiles : Iterable < EmitFileResult > ,
228+ esmFiles : Map < string , string > ,
229+ dtsFiles : Map < string , string > ,
230+ posixReferencedFiles : ReadonlySet < string > ,
231+ modifiedFiles ?: ReadonlySet < string > ,
232+ ) : { changedEsmFiles : Set < string > ; changedDtsFiles : Set < string > } {
233+ const changedEsmFiles = new Set < string > ( ) ;
234+ const changedDtsFiles = new Set < string > ( ) ;
235+
236+ if ( modifiedFiles ) {
237+ for ( const modifiedFile of modifiedFiles ) {
238+ if ( posixReferencedFiles . has ( modifiedFile ) ) {
239+ continue ;
240+ }
241+
242+ const basePathWithoutExt = modifiedFile . replace ( / (?: \. d \. [ c m ] ? t s | \. [ c m ] ? [ j t ] s x ? ) $ / i, '' ) ;
243+ if ( basePathWithoutExt === modifiedFile || existsSync ( modifiedFile ) ) {
244+ continue ;
245+ }
246+
247+ for ( const ext of EMITTED_EXTENSIONS ) {
248+ const outputPath = `${ basePathWithoutExt } ${ ext } ` ;
249+ const mapPath = `${ outputPath } .map` ;
250+ if ( esmFiles . delete ( outputPath ) ) {
251+ changedEsmFiles . add ( outputPath ) ;
252+ }
253+ if ( dtsFiles . delete ( outputPath ) ) {
254+ changedDtsFiles . add ( outputPath ) ;
255+ }
256+ esmFiles . delete ( mapPath ) ;
257+ dtsFiles . delete ( mapPath ) ;
258+ }
259+ }
260+ }
261+
262+ for ( const { filename, contents } of emittedFiles ) {
263+ const normalized = toPosixPath ( filename ) ;
264+ const isMap = normalized . endsWith ( '.map' ) ;
265+ const isDts = isMap ? isDeclarationSourceMapFile ( normalized ) : isDeclarationFile ( normalized ) ;
266+ const targetFiles = isDts ? dtsFiles : esmFiles ;
267+
268+ if ( targetFiles . get ( normalized ) !== contents ) {
269+ targetFiles . set ( normalized , contents ) ;
270+ if ( ! isDts ) {
271+ changedEsmFiles . add ( isMap ? normalized . slice ( 0 , - 4 ) : normalized ) ;
272+ } else if ( ! isMap ) {
273+ changedDtsFiles . add ( normalized ) ;
274+ }
275+ }
276+ }
277+
278+ return { changedEsmFiles, changedDtsFiles } ;
279+ }
280+
213281/**
214282 * Formats stylesheet bundling diagnostics and TypeScript/Angular diagnostics
215283 * and records them on the build state's execution result.
216284 *
217285 * @param compilationInstance The active Angular compilation instance.
218286 * @param stylesheetErrors List of stylesheet bundling errors.
219- * @param stylesheetWarnings List of stylesheet bundling warnings.
287+ * @param stylesheetWarnings List of initialization and stylesheet bundling warnings.
220288 * @param colors Whether diagnostic messages should be formatted with ANSI colors.
221289 * @param buildState The build state whose execution result receives the formatted diagnostics.
222290 */
@@ -231,23 +299,39 @@ async function runDiagnosticsAndFormat(
231299 const errors = [ ...stylesheetErrors ] ;
232300 const warnings : PartialMessage [ ] = [ ] ;
233301
234- if ( errors . length === 0 && useTypeChecking ) {
235- const diagnostics = await compilationInstance . diagnoseFiles ( ) ;
302+ try {
303+ const diagnostics = await compilationInstance . diagnoseFiles (
304+ useTypeChecking ? DiagnosticModes . All : DiagnosticModes . All & ~ DiagnosticModes . Semantic ,
305+ ) ;
236306 if ( diagnostics . errors ?. length ) {
237307 errors . push ( ...diagnostics . errors ) ;
238308 }
239309 if ( diagnostics . warnings ?. length ) {
240310 warnings . push ( ...diagnostics . warnings ) ;
241311 }
312+ } catch ( error ) {
313+ assertIsError ( error ) ;
314+ errors . push ( {
315+ text : 'Angular compilation diagnostics failed.' ,
316+ location : null ,
317+ notes : [ { text : error . stack ?? error . message , location : null } ] ,
318+ } ) ;
242319 }
243320
244321 warnings . push ( ...stylesheetWarnings ) ;
245322
246- if ( errors . length > 0 ) {
247- executionResult . addErrors ( await formatMessages ( errors , { kind : 'error' , color : colors } ) ) ;
248- }
323+ try {
324+ if ( errors . length > 0 ) {
325+ executionResult . addErrors ( await formatMessages ( errors , { kind : 'error' , color : colors } ) ) ;
326+ }
249327
250- if ( warnings . length > 0 ) {
251- executionResult . addWarnings ( await formatMessages ( warnings , { kind : 'warning' , color : colors } ) ) ;
328+ if ( warnings . length > 0 ) {
329+ executionResult . addWarnings (
330+ await formatMessages ( warnings , { kind : 'warning' , color : colors } ) ,
331+ ) ;
332+ }
333+ } catch ( error ) {
334+ assertIsError ( error ) ;
335+ executionResult . addError ( error . message ) ;
252336 }
253337}
0 commit comments