forked from angular/angular-cli
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.ts
138 lines (123 loc) · 4.88 KB
/
index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.dev/license
*/
import type { ApplicationBuilderOptions, BuildOutputAsset, BuildOutputFile } from '@angular/build';
import { buildApplicationInternal, deleteOutputDir, emitFilesToDisk } from '@angular/build/private';
import { BuilderContext, BuilderOutput, createBuilder } from '@angular-devkit/architect';
import type { Plugin } from 'esbuild';
import fs from 'node:fs/promises';
import path from 'node:path';
import { logBuilderStatusWarnings } from './builder-status-warnings';
import type { Schema as BrowserBuilderOptions } from './schema';
type OutputPathClass = Exclude<ApplicationBuilderOptions['outputPath'], string | undefined>;
/**
* Main execution function for the esbuild-based application builder.
* The options are compatible with the Webpack-based builder.
* @param userOptions The browser builder options to use when setting up the application build
* @param context The Architect builder context object
* @returns An async iterable with the builder result output
*/
export async function* buildEsbuildBrowser(
userOptions: BrowserBuilderOptions,
context: BuilderContext,
infrastructureSettings?: {
write?: boolean;
},
plugins?: Plugin[],
): AsyncIterable<
BuilderOutput & {
outputFiles?: BuildOutputFile[];
assetFiles?: { source: string; destination: string }[];
}
> {
// Inform user of status of builder and options
logBuilderStatusWarnings(userOptions, context);
const normalizedOptions = normalizeOptions(userOptions);
const { deleteOutputPath, outputPath } = normalizedOptions;
const fullOutputPath = path.join(context.workspaceRoot, outputPath.base);
if (deleteOutputPath && infrastructureSettings?.write !== false) {
await deleteOutputDir(context.workspaceRoot, outputPath.base);
}
for await (const result of buildApplicationInternal(
normalizedOptions,
context,
{
write: false,
},
plugins && { codePlugins: plugins },
)) {
if (infrastructureSettings?.write !== false && result.outputFiles) {
// Write output files
await writeResultFiles(result.outputFiles, result.assetFiles, fullOutputPath);
}
// The builder system (architect) currently attempts to treat all results as JSON and
// attempts to validate the object with a JSON schema validator. This can lead to slow
// build completion (even after the actual build is fully complete) or crashes if the
// size and/or quantity of output files is large. Architect only requires a `success`
// property so that is all that will be passed here if the infrastructure settings have
// not been explicitly set to avoid writes. Writing is only disabled when used directly
// by the dev server which bypasses the architect behavior.
const builderResult =
infrastructureSettings?.write === false ? result : { success: result.success };
yield builderResult;
}
}
function normalizeOptions(
options: BrowserBuilderOptions,
): Omit<ApplicationBuilderOptions, 'outputPath'> & { outputPath: OutputPathClass } {
const {
main: browser,
outputPath,
ngswConfigPath,
serviceWorker,
polyfills,
...otherOptions
} = options;
return {
browser,
serviceWorker: serviceWorker ? ngswConfigPath : false,
polyfills: typeof polyfills === 'string' ? [polyfills] : polyfills,
outputPath: {
base: outputPath,
browser: '',
},
...otherOptions,
};
}
// We write the file directly from this builder to maintain webpack output compatibility
// and not output browser files into '/browser'.
async function writeResultFiles(
outputFiles: BuildOutputFile[],
assetFiles: BuildOutputAsset[] | undefined,
outputPath: string,
) {
const directoryExists = new Set<string>();
const ensureDirectoryExists = async (basePath: string) => {
if (basePath && !directoryExists.has(basePath)) {
await fs.mkdir(path.join(outputPath, basePath), { recursive: true });
directoryExists.add(basePath);
}
};
// Writes the output file to disk and ensures the containing directories are present
await emitFilesToDisk(outputFiles, async (file: BuildOutputFile) => {
// Ensure output subdirectories exist
const basePath = path.dirname(file.path);
await ensureDirectoryExists(basePath);
// Write file contents
await fs.writeFile(path.join(outputPath, file.path), file.contents);
});
if (assetFiles?.length) {
await emitFilesToDisk(assetFiles, async ({ source, destination }) => {
const basePath = path.dirname(destination);
// Ensure output subdirectories exist
await ensureDirectoryExists(basePath);
// Copy file contents
await fs.copyFile(source, path.join(outputPath, destination), fs.constants.COPYFILE_FICLONE);
});
}
}
export default createBuilder(buildEsbuildBrowser);