@@ -20,6 +20,23 @@ import { Schema as BrowserBuilderOptions, OutputHashing } from './schema';
20
20
21
21
export type NormalizedBrowserOptions = Awaited < ReturnType < typeof normalizeOptions > > ;
22
22
23
+ /** Internal options hidden from builder schema but available when invoked programmatically. */
24
+ interface InternalOptions {
25
+ /**
26
+ * Entry points to use for the compilation. Incompatible with `main`, which must not be provided. May be relative or absolute paths.
27
+ * If given a relative path, it is resolved relative to the current workspace and will generate an output at the same relative location
28
+ * in the output directory. If given an absolute path, the output will be generated in the root of the output directory with the same base
29
+ * name.
30
+ */
31
+ entryPoints ?: Set < string > ;
32
+ }
33
+
34
+ /** Full set of options for `browser-esbuild` builder. */
35
+ export type BrowserEsbuildOptions = Omit < BrowserBuilderOptions & InternalOptions , 'main' > & {
36
+ // `main` can be `undefined` if `entryPoints` is used.
37
+ main ?: string ;
38
+ } ;
39
+
23
40
/**
24
41
* Normalize the user provided options by creating full paths for all path based options
25
42
* and converting multi-form options into a single form that can be directly used
@@ -33,7 +50,7 @@ export type NormalizedBrowserOptions = Awaited<ReturnType<typeof normalizeOption
33
50
export async function normalizeOptions (
34
51
context : BuilderContext ,
35
52
projectName : string ,
36
- options : BrowserBuilderOptions ,
53
+ options : BrowserEsbuildOptions ,
37
54
) {
38
55
const workspaceRoot = context . workspaceRoot ;
39
56
const projectMetadata = await context . getProjectMetadata ( projectName ) ;
@@ -46,7 +63,7 @@ export async function normalizeOptions(
46
63
47
64
const cacheOptions = normalizeCacheOptions ( projectMetadata , workspaceRoot ) ;
48
65
49
- const mainEntryPoint = path . join ( workspaceRoot , options . main ) ;
66
+ const entryPoints = normalizeEntryPoints ( workspaceRoot , options . main , options . entryPoints ) ;
50
67
const tsconfig = path . join ( workspaceRoot , options . tsConfig ) ;
51
68
const outputPath = normalizeDirectoryPath ( path . join ( workspaceRoot , options . outputPath ) ) ;
52
69
const optimizationOptions = normalizeOptimization ( options . optimization ) ;
@@ -125,11 +142,6 @@ export async function normalizeOptions(
125
142
: path . join ( projectRoot , 'ngsw-config.json' ) ;
126
143
}
127
144
128
- // Setup bundler entry points
129
- const entryPoints : Record < string , string > = {
130
- main : mainEntryPoint ,
131
- } ;
132
-
133
145
let indexHtmlOptions ;
134
146
if ( options . index ) {
135
147
indexHtmlOptions = {
@@ -204,6 +216,57 @@ export async function normalizeOptions(
204
216
} ;
205
217
}
206
218
219
+ /**
220
+ * Normalize entry point options. To maintain compatibility with the legacy browser builder, we need a single `main` option which defines a
221
+ * single entry point. However, we also want to support multiple entry points as an internal option. The two options are mutually exclusive
222
+ * and if `main` is provided it will be used as the sole entry point. If `entryPoints` are provided, they will be used as the set of entry
223
+ * points.
224
+ *
225
+ * @param workspaceRoot Path to the root of the Angular workspace.
226
+ * @param main The `main` option pointing at the application entry point. While required per the schema file, it may be omitted by
227
+ * programmatic usages of `browser-esbuild`.
228
+ * @param entryPoints Set of entry points to use if provided.
229
+ * @returns An object mapping entry point names to their file paths.
230
+ */
231
+ function normalizeEntryPoints (
232
+ workspaceRoot : string ,
233
+ main : string | undefined ,
234
+ entryPoints : Set < string > = new Set ( ) ,
235
+ ) : Record < string , string > {
236
+ if ( main === '' ) {
237
+ throw new Error ( '`main` option cannot be an empty string.' ) ;
238
+ }
239
+
240
+ // `main` and `entryPoints` are mutually exclusive.
241
+ if ( main && entryPoints . size > 0 ) {
242
+ throw new Error ( 'Only one of `main` or `entryPoints` may be provided.' ) ;
243
+ }
244
+ if ( ! main && entryPoints . size === 0 ) {
245
+ // Schema should normally reject this case, but programmatic usages of the builder might make this mistake.
246
+ throw new Error ( 'Either `main` or at least one `entryPoints` value must be provided.' ) ;
247
+ }
248
+
249
+ // Schema types force `main` to always be provided, but it may be omitted when the builder is invoked programmatically.
250
+ if ( main ) {
251
+ // Use `main` alone.
252
+ return { 'main' : path . join ( workspaceRoot , main ) } ;
253
+ } else {
254
+ // Use `entryPoints` alone.
255
+ return Object . fromEntries (
256
+ Array . from ( entryPoints ) . map ( ( entryPoint ) => {
257
+ const parsedEntryPoint = path . parse ( entryPoint ) ;
258
+
259
+ return [
260
+ // File path without extension.
261
+ path . join ( parsedEntryPoint . dir , parsedEntryPoint . name ) ,
262
+ // Full file path.
263
+ path . join ( workspaceRoot , entryPoint ) ,
264
+ ] ;
265
+ } ) ,
266
+ ) ;
267
+ }
268
+ }
269
+
207
270
/**
208
271
* Normalize a directory path string.
209
272
* Currently only removes a trailing slash if present.
0 commit comments