nx/packages/web/src/builders/build/build.impl.ts
Daniel Ji 25432a6395
fix(react): exposed the crossOrigin option for web builder options (#2830)
ISSUES CLOSED: 2419

Co-authored-by: danielji <daniel.ji@innovid.com>
2020-05-29 11:04:08 -04:00

189 lines
5.8 KiB
TypeScript

import { BuilderContext, createBuilder } from '@angular-devkit/architect';
import {
join as devkitJoin,
JsonObject,
normalize,
} from '@angular-devkit/core';
import { BuildResult, runWebpack } from '@angular-devkit/build-webpack';
import { from, of } from 'rxjs';
import { normalizeWebBuildOptions } from '../../utils/normalize';
import { getWebConfig } from '../../utils/web.config';
import { BuildBuilderOptions } from '../../utils/types';
import { bufferCount, map, mergeScan, switchMap } from 'rxjs/operators';
import { getSourceRoot } from '../../utils/source-root';
import { writeIndexHtml } from '../../utils/third-party/cli-files/utilities/index-file/write-index-html';
import { NodeJsSyncHost } from '@angular-devkit/core/node';
import { execSync } from 'child_process';
import { Range, satisfies } from 'semver';
import { basename } from 'path';
import { createProjectGraph } from '@nrwl/workspace/src/core/project-graph';
import {
calculateProjectDependencies,
createTmpTsConfig,
} from '@nrwl/workspace/src/utils/buildable-libs-utils';
import { CrossOriginValue } from '../../utils/third-party/cli-files/utilities/index-file/augment-index-html';
export interface WebBuildBuilderOptions extends BuildBuilderOptions {
index: string;
budgets: any[];
baseHref: string;
deployUrl: string;
extractCss?: boolean;
crossOrigin?: CrossOriginValue;
polyfills?: string;
es2015Polyfills?: string;
scripts: string[];
styles: string[];
vendorChunk?: boolean;
commonChunk?: boolean;
stylePreprocessingOptions?: any;
subresourceIntegrity?: boolean;
verbose?: boolean;
buildLibsFromSource?: boolean;
}
export default createBuilder<WebBuildBuilderOptions & JsonObject>(run);
export function run(options: WebBuildBuilderOptions, context: BuilderContext) {
const host = new NodeJsSyncHost();
const isScriptOptimizeOn =
typeof options.optimization === 'boolean'
? options.optimization
: options.optimization && options.optimization.scripts
? options.optimization.scripts
: false;
// Node versions 12.2-12.8 has a bug where prod builds will hang for 2-3 minutes
// after the program exits.
const nodeVersion = execSync(`node --version`).toString('utf-8').trim();
const supportedRange = new Range('10 || >=12.9');
if (!satisfies(nodeVersion, supportedRange)) {
throw new Error(
`Node version ${nodeVersion} is not supported. Supported range is "${supportedRange.raw}".`
);
}
if (!options.buildLibsFromSource) {
const projGraph = createProjectGraph();
const { target, dependencies } = calculateProjectDependencies(
projGraph,
context
);
options.tsConfig = createTmpTsConfig(
options.tsConfig,
context.workspaceRoot,
target.data.root,
dependencies
);
}
return from(getSourceRoot(context, host))
.pipe(
map((sourceRoot) => {
options = normalizeWebBuildOptions(
options,
context.workspaceRoot,
sourceRoot
);
return [
// ESM build for modern browsers.
getWebConfig(
context.workspaceRoot,
sourceRoot,
options,
context.logger,
true,
isScriptOptimizeOn
),
// ES5 build for legacy browsers.
isScriptOptimizeOn
? getWebConfig(
context.workspaceRoot,
sourceRoot,
options,
context.logger,
false,
isScriptOptimizeOn
)
: undefined,
]
.filter(Boolean)
.map((config) =>
options.webpackConfig
? require(options.webpackConfig)(config, {
options,
configuration: context.target.configuration,
})
: config
);
})
)
.pipe(
switchMap((configs) =>
from(configs).pipe(
// Run build sequentially and bail when first one fails.
mergeScan(
(acc, config) => {
if (acc.success) {
return runWebpack(config, context, {
logging: (stats) => {
context.logger.info(stats.toString(config.stats));
},
webpackFactory: require('webpack'),
});
} else {
return of();
}
},
{ success: true } as BuildResult,
1
),
// Collect build results as an array.
bufferCount(configs.length)
)
),
switchMap(([result1, result2 = { success: true, emittedFiles: [] }]) => {
const success = [result1, result2].every((result) => result.success);
return (options.optimization
? writeIndexHtml({
crossOrigin: options.crossOrigin,
host,
outputPath: devkitJoin(
normalize(options.outputPath),
basename(options.index)
),
indexPath: devkitJoin(
normalize(context.workspaceRoot),
options.index
),
files: result1.emittedFiles.filter((x) => x.extension === '.css'),
noModuleFiles: result2.emittedFiles,
moduleFiles: result1.emittedFiles,
baseHref: options.baseHref,
deployUrl: options.deployUrl,
scripts: options.scripts,
styles: options.styles,
})
: of(null)
).pipe(
map(
() =>
({
success,
emittedFiles: [
...result1.emittedFiles,
...result2.emittedFiles,
],
} as BuildResult)
)
);
})
);
}