6
6
* found in the LICENSE file at https://angular.dev/license
7
7
*/
8
8
9
- import { OutputFile } from 'esbuild' ;
10
9
import assert from 'node:assert' ;
11
10
import { createHash } from 'node:crypto' ;
12
11
import path from 'node:path' ;
13
- import { BuildOutputFileType , BundleContextResult , BundlerContext } from '../bundler-context' ;
12
+ import {
13
+ BuildOutputFile ,
14
+ BuildOutputFileType ,
15
+ BundleContextResult ,
16
+ BundlerContext ,
17
+ } from '../bundler-context' ;
14
18
import { MemoryCache } from '../cache' ;
15
19
import {
16
20
BundleStylesheetOptions ,
@@ -37,7 +41,14 @@ export class ComponentStylesheetBundler {
37
41
private readonly incremental : boolean ,
38
42
) { }
39
43
40
- async bundleFile ( entry : string , externalId ?: string | boolean ) {
44
+ /**
45
+ * Bundle a file-based component stylesheet for use within an AOT compiled Angular application.
46
+ * @param entry The file path of the stylesheet.
47
+ * @param externalId Either an external identifier string for initial bundling or a boolean for rebuilds, if external.
48
+ * @param direct If true, the output will be used directly by the builder; false if used inside the compiler plugin.
49
+ * @returns A component bundle result object.
50
+ */
51
+ async bundleFile ( entry : string , externalId ?: string | boolean , direct ?: boolean ) {
41
52
const bundlerContext = await this . #fileContexts. getOrCreate ( entry , ( ) => {
42
53
return new BundlerContext ( this . options . workspaceRoot , this . incremental , ( loadCache ) => {
43
54
const buildOptions = createStylesheetBundleOptions ( this . options , loadCache ) ;
@@ -62,6 +73,7 @@ export class ComponentStylesheetBundler {
62
73
await bundlerContext . bundle ( ) ,
63
74
bundlerContext . watchFiles ,
64
75
! ! externalId ,
76
+ ! ! direct ,
65
77
) ;
66
78
}
67
79
@@ -127,6 +139,7 @@ export class ComponentStylesheetBundler {
127
139
await bundlerContext . bundle ( ) ,
128
140
bundlerContext . watchFiles ,
129
141
! ! externalId ,
142
+ false ,
130
143
) ;
131
144
}
132
145
@@ -156,6 +169,15 @@ export class ComponentStylesheetBundler {
156
169
return entries ;
157
170
}
158
171
172
+ collectReferencedFiles ( ) : string [ ] {
173
+ const files = [ ] ;
174
+ for ( const context of this . #fileContexts. values ( ) ) {
175
+ files . push ( ...context . watchFiles ) ;
176
+ }
177
+
178
+ return files ;
179
+ }
180
+
159
181
async dispose ( ) : Promise < void > {
160
182
const contexts = [ ...this . #fileContexts. values ( ) , ...this . #inlineContexts. values ( ) ] ;
161
183
this . #fileContexts. clear ( ) ;
@@ -168,61 +190,70 @@ export class ComponentStylesheetBundler {
168
190
result : BundleContextResult ,
169
191
referencedFiles : Set < string > | undefined ,
170
192
external : boolean ,
193
+ direct : boolean ,
171
194
) {
172
195
let contents = '' ;
173
- let metafile ;
174
- const outputFiles : OutputFile [ ] = [ ] ;
196
+ const outputFiles : BuildOutputFile [ ] = [ ] ;
175
197
176
- if ( ! result . errors ) {
177
- for ( const outputFile of result . outputFiles ) {
178
- const filename = path . basename ( outputFile . path ) ;
198
+ const { errors, warnings } = result ;
199
+ if ( errors ) {
200
+ return { errors, warnings, referencedFiles } ;
201
+ }
179
202
180
- if ( outputFile . type === BuildOutputFileType . Media || filename . endsWith ( '.css.map' ) ) {
181
- // The output files could also contain resources (images/fonts/etc.) that were referenced and the map files.
203
+ for ( const outputFile of result . outputFiles ) {
204
+ const filename = path . basename ( outputFile . path ) ;
182
205
183
- // Clone the output file to avoid amending the original path which would causes problems during rebuild.
184
- const clonedOutputFile = outputFile . clone ( ) ;
206
+ if ( outputFile . type === BuildOutputFileType . Media || filename . endsWith ( '.css.map' ) ) {
207
+ // The output files could also contain resources (images/fonts/etc.) that were referenced and the map files.
208
+
209
+ // Clone the output file to avoid amending the original path which would causes problems during rebuild.
210
+ const clonedOutputFile = outputFile . clone ( ) ;
185
211
186
- // Needed for Bazel as otherwise the files will not be written in the correct place,
187
- // this is because esbuild will resolve the output file from the outdir which is currently set to `workspaceRoot` twice,
188
- // once in the stylesheet and the other in the application code bundler.
189
- // Ex: `../../../../../app.component.css.map`.
212
+ // Needed for Bazel as otherwise the files will not be written in the correct place,
213
+ // this is because esbuild will resolve the output file from the outdir which is currently set to `workspaceRoot` twice,
214
+ // once in the stylesheet and the other in the application code bundler.
215
+ // Ex: `../../../../../app.component.css.map`.
216
+ if ( ! direct ) {
190
217
clonedOutputFile . path = path . join ( this . options . workspaceRoot , outputFile . path ) ;
218
+ }
191
219
192
- outputFiles . push ( clonedOutputFile ) ;
193
- } else if ( filename . endsWith ( '.css' ) ) {
194
- if ( external ) {
195
- const clonedOutputFile = outputFile . clone ( ) ;
220
+ outputFiles . push ( clonedOutputFile ) ;
221
+ } else if ( filename . endsWith ( '.css' ) ) {
222
+ if ( external ) {
223
+ const clonedOutputFile = outputFile . clone ( ) ;
224
+ if ( ! direct ) {
196
225
clonedOutputFile . path = path . join ( this . options . workspaceRoot , outputFile . path ) ;
197
- outputFiles . push ( clonedOutputFile ) ;
198
- contents = path . posix . join ( this . options . publicPath ?? '' , filename ) ;
199
- } else {
200
- contents = outputFile . text ;
201
226
}
227
+ outputFiles . push ( clonedOutputFile ) ;
228
+ contents = path . posix . join ( this . options . publicPath ?? '' , filename ) ;
202
229
} else {
203
- throw new Error (
204
- `Unexpected non CSS/Media file "${ filename } " outputted during component stylesheet processing.` ,
205
- ) ;
230
+ contents = outputFile . text ;
206
231
}
232
+ } else {
233
+ throw new Error (
234
+ `Unexpected non CSS/Media file "${ filename } " outputted during component stylesheet processing.` ,
235
+ ) ;
207
236
}
208
-
209
- metafile = result . metafile ;
210
- // Remove entryPoint fields from outputs to prevent the internal component styles from being
211
- // treated as initial files. Also mark the entry as a component resource for stat reporting.
212
- Object . values ( metafile . outputs ) . forEach ( ( output ) => {
213
- delete output . entryPoint ;
214
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
215
- ( output as any ) [ 'ng-component' ] = true ;
216
- } ) ;
217
237
}
218
238
239
+ const metafile = result . metafile ;
240
+ // Remove entryPoint fields from outputs to prevent the internal component styles from being
241
+ // treated as initial files. Also mark the entry as a component resource for stat reporting.
242
+ Object . values ( metafile . outputs ) . forEach ( ( output ) => {
243
+ delete output . entryPoint ;
244
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
245
+ ( output as any ) [ 'ng-component' ] = true ;
246
+ } ) ;
247
+
219
248
return {
220
- errors : result . errors ,
221
- warnings : result . warnings ,
249
+ errors,
250
+ warnings,
222
251
contents,
223
252
outputFiles,
224
253
metafile,
225
254
referencedFiles,
255
+ externalImports : result . externalImports ,
256
+ initialFiles : new Map ( ) ,
226
257
} ;
227
258
}
228
259
}
0 commit comments