|
8 | 8 |
|
9 | 9 | import { BuilderContext, BuilderOutput, createBuilder } from '@angular-devkit/architect';
|
10 | 10 | import * as assert from 'assert';
|
11 |
| -import type { OutputFile } from 'esbuild'; |
| 11 | +import type { Message, OutputFile } from 'esbuild'; |
12 | 12 | import { promises as fs } from 'fs';
|
13 | 13 | import * as path from 'path';
|
14 | 14 | import { NormalizedOptimizationOptions, deleteOutputDir } from '../../utils';
|
@@ -144,62 +144,22 @@ export async function buildEsbuildBrowser(
|
144 | 144 | }
|
145 | 145 |
|
146 | 146 | // Process global stylesheets
|
147 |
| - if (options.styles) { |
148 |
| - // resolveGlobalStyles is temporarily reused from the Webpack builder code |
149 |
| - const { entryPoints: stylesheetEntrypoints, noInjectNames } = resolveGlobalStyles( |
150 |
| - options.styles, |
151 |
| - workspaceRoot, |
152 |
| - // preserveSymlinks is always true here to allow the bundler to handle the option |
153 |
| - true, |
154 |
| - // skipResolution to leverage the bundler's more comprehensive resolution |
155 |
| - true, |
156 |
| - ); |
157 |
| - for (const [name, files] of Object.entries(stylesheetEntrypoints)) { |
158 |
| - const virtualEntryData = files |
159 |
| - .map((file) => `@import '${file.replace(/\\/g, '/')}';`) |
160 |
| - .join('\n'); |
161 |
| - const sheetResult = await bundleStylesheetText( |
162 |
| - virtualEntryData, |
163 |
| - { virtualName: `angular:style/global;${name}`, resolvePath: workspaceRoot }, |
164 |
| - { |
165 |
| - workspaceRoot, |
166 |
| - optimization: !!optimizationOptions.styles.minify, |
167 |
| - sourcemap: !!sourcemapOptions.styles && (sourcemapOptions.hidden ? 'external' : true), |
168 |
| - outputNames: noInjectNames.includes(name) ? { media: outputNames.media } : outputNames, |
169 |
| - includePaths: options.stylePreprocessorOptions?.includePaths, |
170 |
| - preserveSymlinks: options.preserveSymlinks, |
171 |
| - externalDependencies: options.externalDependencies, |
172 |
| - }, |
173 |
| - ); |
174 |
| - |
175 |
| - await logMessages(context, sheetResult); |
176 |
| - if (!sheetResult.path) { |
177 |
| - // Failed to process the stylesheet |
178 |
| - assert.ok( |
179 |
| - sheetResult.errors.length, |
180 |
| - `Global stylesheet processing for '${name}' failed with no errors.`, |
181 |
| - ); |
| 147 | + const styleResults = await bundleGlobalStylesheets( |
| 148 | + workspaceRoot, |
| 149 | + outputNames, |
| 150 | + options, |
| 151 | + optimizationOptions, |
| 152 | + sourcemapOptions, |
| 153 | + ); |
| 154 | + outputFiles.push(...styleResults.outputFiles); |
| 155 | + initialFiles.push(...styleResults.initialFiles); |
182 | 156 |
|
183 |
| - return { success: false }; |
184 |
| - } |
| 157 | + // Log all warnings and errors generated during bundling |
| 158 | + await logMessages(context, styleResults); |
185 | 159 |
|
186 |
| - // The virtual stylesheets will be named `stdin` by esbuild. This must be replaced |
187 |
| - // with the actual name of the global style and the leading directory separator must |
188 |
| - // also be removed to make the path relative. |
189 |
| - const sheetPath = sheetResult.path.replace('stdin', name); |
190 |
| - outputFiles.push(createOutputFileFromText(sheetPath, sheetResult.contents)); |
191 |
| - if (sheetResult.map) { |
192 |
| - outputFiles.push(createOutputFileFromText(sheetPath + '.map', sheetResult.map)); |
193 |
| - } |
194 |
| - if (!noInjectNames.includes(name)) { |
195 |
| - initialFiles.push({ |
196 |
| - file: sheetPath, |
197 |
| - name, |
198 |
| - extension: '.css', |
199 |
| - }); |
200 |
| - } |
201 |
| - outputFiles.push(...sheetResult.resourceFiles); |
202 |
| - } |
| 160 | + // Return if the bundling has errors |
| 161 | + if (styleResults.errors.length) { |
| 162 | + return { success: false }; |
203 | 163 | }
|
204 | 164 |
|
205 | 165 | // Generate index HTML file
|
@@ -366,4 +326,79 @@ async function bundleCode(
|
366 | 326 | });
|
367 | 327 | }
|
368 | 328 |
|
| 329 | +async function bundleGlobalStylesheets( |
| 330 | + workspaceRoot: string, |
| 331 | + outputNames: { bundles: string; media: string }, |
| 332 | + options: BrowserBuilderOptions, |
| 333 | + optimizationOptions: NormalizedOptimizationOptions, |
| 334 | + sourcemapOptions: SourceMapClass, |
| 335 | +) { |
| 336 | + const outputFiles: OutputFile[] = []; |
| 337 | + const initialFiles: FileInfo[] = []; |
| 338 | + const errors: Message[] = []; |
| 339 | + const warnings: Message[] = []; |
| 340 | + |
| 341 | + // resolveGlobalStyles is temporarily reused from the Webpack builder code |
| 342 | + const { entryPoints: stylesheetEntrypoints, noInjectNames } = resolveGlobalStyles( |
| 343 | + options.styles || [], |
| 344 | + workspaceRoot, |
| 345 | + // preserveSymlinks is always true here to allow the bundler to handle the option |
| 346 | + true, |
| 347 | + // skipResolution to leverage the bundler's more comprehensive resolution |
| 348 | + true, |
| 349 | + ); |
| 350 | + |
| 351 | + for (const [name, files] of Object.entries(stylesheetEntrypoints)) { |
| 352 | + const virtualEntryData = files |
| 353 | + .map((file) => `@import '${file.replace(/\\/g, '/')}';`) |
| 354 | + .join('\n'); |
| 355 | + const sheetResult = await bundleStylesheetText( |
| 356 | + virtualEntryData, |
| 357 | + { virtualName: `angular:style/global;${name}`, resolvePath: workspaceRoot }, |
| 358 | + { |
| 359 | + workspaceRoot, |
| 360 | + optimization: !!optimizationOptions.styles.minify, |
| 361 | + sourcemap: !!sourcemapOptions.styles && (sourcemapOptions.hidden ? 'external' : true), |
| 362 | + outputNames: noInjectNames.includes(name) ? { media: outputNames.media } : outputNames, |
| 363 | + includePaths: options.stylePreprocessorOptions?.includePaths, |
| 364 | + preserveSymlinks: options.preserveSymlinks, |
| 365 | + externalDependencies: options.externalDependencies, |
| 366 | + }, |
| 367 | + ); |
| 368 | + |
| 369 | + errors.push(...sheetResult.errors); |
| 370 | + warnings.push(...sheetResult.warnings); |
| 371 | + |
| 372 | + if (!sheetResult.path) { |
| 373 | + // Failed to process the stylesheet |
| 374 | + assert.ok( |
| 375 | + sheetResult.errors.length, |
| 376 | + `Global stylesheet processing for '${name}' failed with no errors.`, |
| 377 | + ); |
| 378 | + |
| 379 | + continue; |
| 380 | + } |
| 381 | + |
| 382 | + // The virtual stylesheets will be named `stdin` by esbuild. This must be replaced |
| 383 | + // with the actual name of the global style and the leading directory separator must |
| 384 | + // also be removed to make the path relative. |
| 385 | + const sheetPath = sheetResult.path.replace('stdin', name); |
| 386 | + outputFiles.push(createOutputFileFromText(sheetPath, sheetResult.contents)); |
| 387 | + if (sheetResult.map) { |
| 388 | + outputFiles.push(createOutputFileFromText(sheetPath + '.map', sheetResult.map)); |
| 389 | + } |
| 390 | + |
| 391 | + if (!noInjectNames.includes(name)) { |
| 392 | + initialFiles.push({ |
| 393 | + file: sheetPath, |
| 394 | + name, |
| 395 | + extension: '.css', |
| 396 | + }); |
| 397 | + } |
| 398 | + outputFiles.push(...sheetResult.resourceFiles); |
| 399 | + } |
| 400 | + |
| 401 | + return { outputFiles, initialFiles, errors, warnings }; |
| 402 | +} |
| 403 | + |
369 | 404 | export default createBuilder(buildEsbuildBrowser);
|
0 commit comments