Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,16 @@ import {
import { basename, extname, relative } from 'node:path';
import { FileInfo } from '../../utils/index-file/augment-index-html';

export type BundleContextResult =
| { errors: Message[]; warnings: Message[] }
| {
errors: undefined;
warnings: Message[];
metafile: Metafile;
outputFiles: OutputFile[];
initialFiles: FileInfo[];
};

/**
* Determines if an unknown value is an esbuild BuildFailure error object thrown by esbuild.
* @param value A potential esbuild BuildFailure error object.
Expand All @@ -43,6 +53,50 @@ export class BundlerContext {
};
}

static async bundleAll(contexts: Iterable<BundlerContext>): Promise<BundleContextResult> {
const individualResults = await Promise.all([...contexts].map((context) => context.bundle()));

// Return directly if only one result
if (individualResults.length === 1) {
return individualResults[0];
}

let errors: Message[] | undefined;
const warnings: Message[] = [];
const metafile: Metafile = { inputs: {}, outputs: {} };
const initialFiles = [];
const outputFiles = [];
for (const result of individualResults) {
warnings.push(...result.warnings);
if (result.errors) {
errors ??= [];
errors.push(...result.errors);
continue;
}

// Combine metafiles used for the stats option as well as bundle budgets and console output
if (result.metafile) {
metafile.inputs = { ...metafile.inputs, ...result.metafile.inputs };
metafile.outputs = { ...metafile.outputs, ...result.metafile.outputs };
}

initialFiles.push(...result.initialFiles);
outputFiles.push(...result.outputFiles);
}

if (errors !== undefined) {
return { errors, warnings };
}

return {
errors,
warnings,
metafile,
initialFiles,
outputFiles,
};
}

/**
* Executes the esbuild build function and normalizes the build result in the event of a
* build failure that results in no output being generated.
Expand All @@ -52,16 +106,7 @@ export class BundlerContext {
* @returns If output files are generated, the full esbuild BuildResult; if not, the
* warnings and errors for the attempted build.
*/
async bundle(): Promise<
| { errors: Message[]; warnings: Message[] }
| {
errors: undefined;
warnings: Message[];
metafile: Metafile;
outputFiles: OutputFile[];
initialFiles: FileInfo[];
}
> {
async bundle(): Promise<BundleContextResult> {
let result;
try {
if (this.#esbuildContext) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,9 @@ async function execute(
const target = transformSupportedBrowsersToTargets(browsers);

// Reuse rebuild state or create new bundle contexts for code and global stylesheets
const bundlerContexts = [];

// Application code
const codeBundleCache = options.watch
? rebuildState?.codeBundleCache ?? new SourceFileCache()
: undefined;
Expand All @@ -118,37 +121,38 @@ async function execute(
!!options.watch,
createCodeBundleOptions(options, target, browsers, codeBundleCache),
);
const globalStylesBundleContext =
rebuildState?.globalStylesRebuild ??
new BundlerContext(
bundlerContexts.push(codeBundleContext);
// Global Stylesheets
let globalStylesBundleContext;
if (options.globalStyles.length > 0) {
globalStylesBundleContext =
rebuildState?.globalStylesRebuild ??
new BundlerContext(
workspaceRoot,
!!options.watch,
createGlobalStylesBundleOptions(
options,
target,
browsers,
codeBundleCache?.loadResultCache,
),
);
bundlerContexts.push(globalStylesBundleContext);
}
// Global Scripts
if (options.globalScripts.length > 0) {
const globalScriptsBundleContext = new BundlerContext(
workspaceRoot,
!!options.watch,
createGlobalStylesBundleOptions(options, target, browsers, codeBundleCache?.loadResultCache),
createGlobalScriptsBundleOptions(options),
);
bundlerContexts.push(globalScriptsBundleContext);
}

const globalScriptsBundleContext = new BundlerContext(
workspaceRoot,
!!options.watch,
createGlobalScriptsBundleOptions(options),
);

const [codeResults, styleResults, scriptResults] = await Promise.all([
// Execute esbuild to bundle the application code
codeBundleContext.bundle(),
// Execute esbuild to bundle the global stylesheets
globalStylesBundleContext.bundle(),
globalScriptsBundleContext.bundle(),
]);
const bundlingResult = await BundlerContext.bundleAll(bundlerContexts);

// Log all warnings and errors generated during bundling
await logMessages(context, {
errors: [
...(codeResults.errors || []),
...(styleResults.errors || []),
...(scriptResults.errors || []),
],
warnings: [...codeResults.warnings, ...styleResults.warnings, ...scriptResults.warnings],
});
await logMessages(context, bundlingResult);

const executionResult = new ExecutionResult(
codeBundleContext,
Expand All @@ -157,40 +161,22 @@ async function execute(
);

// Return if the bundling has errors
if (codeResults.errors || styleResults.errors || scriptResults.errors) {
if (bundlingResult.errors) {
return executionResult;
}

// Filter global stylesheet initial files
styleResults.initialFiles = styleResults.initialFiles.filter(
({ name }) => options.globalStyles.find((style) => style.name === name)?.initial,
);
// Filter global stylesheet initial files. Currently all initial CSS files are from the global styles option.
if (options.globalScripts.length > 0) {
bundlingResult.initialFiles = bundlingResult.initialFiles.filter(
({ file, name }) =>
!file.endsWith('.css') ||
options.globalStyles.find((style) => style.name === name)?.initial,
);
}

// Combine the bundling output files
const initialFiles: FileInfo[] = [
...codeResults.initialFiles,
...styleResults.initialFiles,
...scriptResults.initialFiles,
];
executionResult.outputFiles.push(
...codeResults.outputFiles,
...styleResults.outputFiles,
...scriptResults.outputFiles,
);
const { metafile, initialFiles, outputFiles } = bundlingResult;

// Combine metafiles used for the stats option as well as bundle budgets and console output
const metafile = {
inputs: {
...codeResults.metafile?.inputs,
...styleResults.metafile?.inputs,
...scriptResults.metafile?.inputs,
},
outputs: {
...codeResults.metafile?.outputs,
...styleResults.metafile?.outputs,
...scriptResults.metafile?.outputs,
},
};
executionResult.outputFiles.push(...outputFiles);

// Check metafile for CommonJS module usage if optimizing scripts
if (optimizationOptions.scripts) {
Expand Down