feat(angular): add support for incremental builds to the webpack-server executor (#10754)
This commit is contained in:
parent
9900ceb7c4
commit
f3a9f55fc7
@ -2999,6 +2999,10 @@
|
|||||||
"poll": {
|
"poll": {
|
||||||
"type": "number",
|
"type": "number",
|
||||||
"description": "Enable and define the file watching poll time period in milliseconds."
|
"description": "Enable and define the file watching poll time period in milliseconds."
|
||||||
|
},
|
||||||
|
"buildLibsFromSource": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Read buildable libraries from source instead of building them separately. If not set, it will take the value specified in the `browserTarget` options, or it will default to `true` if it's also not set in the `browserTarget` options."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
|
|||||||
@ -43,6 +43,7 @@
|
|||||||
"@nrwl/jest": "file:../jest",
|
"@nrwl/jest": "file:../jest",
|
||||||
"@nrwl/linter": "file:../linter",
|
"@nrwl/linter": "file:../linter",
|
||||||
"@nrwl/storybook": "file:../storybook",
|
"@nrwl/storybook": "file:../storybook",
|
||||||
|
"@nrwl/web": "file:../web",
|
||||||
"@nrwl/workspace": "file:../workspace",
|
"@nrwl/workspace": "file:../workspace",
|
||||||
"@phenomnomnominal/tsquery": "4.1.1",
|
"@phenomnomnominal/tsquery": "4.1.1",
|
||||||
"@schematics/angular": "~14.0.0",
|
"@schematics/angular": "~14.0.0",
|
||||||
|
|||||||
@ -17,4 +17,5 @@ export interface Schema {
|
|||||||
hmr?: boolean;
|
hmr?: boolean;
|
||||||
watch?: boolean;
|
watch?: boolean;
|
||||||
poll?: number;
|
poll?: number;
|
||||||
|
buildLibsFromSource?: boolean;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -102,6 +102,10 @@
|
|||||||
"poll": {
|
"poll": {
|
||||||
"type": "number",
|
"type": "number",
|
||||||
"description": "Enable and define the file watching poll time period in milliseconds."
|
"description": "Enable and define the file watching poll time period in milliseconds."
|
||||||
|
},
|
||||||
|
"buildLibsFromSource": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Read buildable libraries from source instead of building them separately. If not set, it will take the value specified in the `browserTarget` options, or it will default to `true` if it's also not set in the `browserTarget` options."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
|
|||||||
@ -1,31 +1,38 @@
|
|||||||
import { BuilderContext, createBuilder } from '@angular-devkit/architect';
|
import {
|
||||||
|
BuilderContext,
|
||||||
|
BuilderOutput,
|
||||||
|
createBuilder,
|
||||||
|
} from '@angular-devkit/architect';
|
||||||
import {
|
import {
|
||||||
DevServerBuilderOptions,
|
DevServerBuilderOptions,
|
||||||
serveWebpackBrowser,
|
executeDevServerBuilder,
|
||||||
} from '@angular-devkit/build-angular/src/builders/dev-server';
|
} from '@angular-devkit/build-angular';
|
||||||
import { JsonObject } from '@angular-devkit/core';
|
import { JsonObject } from '@angular-devkit/core';
|
||||||
import {
|
import {
|
||||||
joinPathFragments,
|
joinPathFragments,
|
||||||
parseTargetString,
|
parseTargetString,
|
||||||
readAllWorkspaceConfiguration,
|
readAllWorkspaceConfiguration,
|
||||||
Workspaces,
|
readCachedProjectGraph,
|
||||||
} from '@nrwl/devkit';
|
} from '@nrwl/devkit';
|
||||||
|
import { WebpackNxBuildCoordinationPlugin } from '@nrwl/web/src/plugins/webpack-nx-build-coordination-plugin';
|
||||||
|
import {
|
||||||
|
calculateProjectDependencies,
|
||||||
|
createTmpTsConfig,
|
||||||
|
DependentBuildableProjectNode,
|
||||||
|
} from '@nrwl/workspace/src/utilities/buildable-libs-utils';
|
||||||
import { existsSync } from 'fs';
|
import { existsSync } from 'fs';
|
||||||
|
import { isNpmProject } from 'nx/src/project-graph/operators';
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
import { merge } from 'webpack-merge';
|
import { merge } from 'webpack-merge';
|
||||||
import { resolveCustomWebpackConfig } from '../utilities/webpack';
|
import { resolveCustomWebpackConfig } from '../utilities/webpack';
|
||||||
import { normalizeOptions } from './lib';
|
import { normalizeOptions } from './lib';
|
||||||
import type { Schema } from './schema';
|
import type { Schema } from './schema';
|
||||||
|
|
||||||
export function executeWebpackServerBuilder(
|
export function executeWebpackServerBuilder(
|
||||||
schema: Schema,
|
rawOptions: Schema,
|
||||||
context: BuilderContext
|
context: BuilderContext
|
||||||
) {
|
): Observable<BuilderOutput> {
|
||||||
process.env.NX_TSCONFIG_PATH = joinPathFragments(
|
const options = normalizeOptions(rawOptions);
|
||||||
context.workspaceRoot,
|
|
||||||
'tsconfig.base.json'
|
|
||||||
);
|
|
||||||
|
|
||||||
const options = normalizeOptions(schema);
|
|
||||||
const workspaceConfig = readAllWorkspaceConfiguration();
|
const workspaceConfig = readAllWorkspaceConfiguration();
|
||||||
|
|
||||||
const parsedBrowserTarget = parseTargetString(options.browserTarget);
|
const parsedBrowserTarget = parseTargetString(options.browserTarget);
|
||||||
@ -34,62 +41,111 @@ export function executeWebpackServerBuilder(
|
|||||||
parsedBrowserTarget.target
|
parsedBrowserTarget.target
|
||||||
];
|
];
|
||||||
|
|
||||||
const selectedConfiguration = parsedBrowserTarget.configuration
|
const buildTargetConfiguration = parsedBrowserTarget.configuration
|
||||||
? buildTarget.configurations[parsedBrowserTarget.configuration]
|
? buildTarget.configurations[parsedBrowserTarget.configuration]
|
||||||
: buildTarget.defaultConfiguration
|
: buildTarget.defaultConfiguration
|
||||||
? buildTarget.configurations[buildTarget.defaultConfiguration]
|
? buildTarget.configurations[buildTarget.defaultConfiguration]
|
||||||
: buildTarget.options;
|
: undefined;
|
||||||
|
|
||||||
|
const buildLibsFromSource =
|
||||||
|
options.buildLibsFromSource ??
|
||||||
|
buildTargetConfiguration?.buildLibsFromSource ??
|
||||||
|
buildTarget.options.buildLibsFromSource ??
|
||||||
|
true;
|
||||||
|
|
||||||
const customWebpackConfig: { path: string } =
|
const customWebpackConfig: { path: string } =
|
||||||
selectedConfiguration.customWebpackConfig ??
|
buildTargetConfiguration?.customWebpackConfig ??
|
||||||
buildTarget.options.customWebpackConfig;
|
buildTarget.options.customWebpackConfig;
|
||||||
|
|
||||||
|
let pathToWebpackConfig: string;
|
||||||
if (customWebpackConfig && customWebpackConfig.path) {
|
if (customWebpackConfig && customWebpackConfig.path) {
|
||||||
const pathToWebpackConfig = joinPathFragments(
|
pathToWebpackConfig = joinPathFragments(
|
||||||
context.workspaceRoot,
|
context.workspaceRoot,
|
||||||
customWebpackConfig.path
|
customWebpackConfig.path
|
||||||
);
|
);
|
||||||
|
|
||||||
if (existsSync(pathToWebpackConfig)) {
|
if (!existsSync(pathToWebpackConfig)) {
|
||||||
return serveWebpackBrowser(
|
|
||||||
options as DevServerBuilderOptions,
|
|
||||||
context as any,
|
|
||||||
{
|
|
||||||
webpackConfiguration: async (baseWebpackConfig) => {
|
|
||||||
const customWebpackConfiguration = resolveCustomWebpackConfig(
|
|
||||||
pathToWebpackConfig,
|
|
||||||
buildTarget.options.tsConfig
|
|
||||||
);
|
|
||||||
// The extra Webpack configuration file can also export a Promise, for instance:
|
|
||||||
// `module.exports = new Promise(...)`. If it exports a single object, but not a Promise,
|
|
||||||
// then await will just resolve that object.
|
|
||||||
const config = await customWebpackConfiguration;
|
|
||||||
|
|
||||||
// The extra Webpack configuration file can export a synchronous or asynchronous function,
|
|
||||||
// for instance: `module.exports = async config => { ... }`.
|
|
||||||
if (typeof config === 'function') {
|
|
||||||
return config(
|
|
||||||
baseWebpackConfig,
|
|
||||||
selectedConfiguration,
|
|
||||||
context.target
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return merge(baseWebpackConfig, config);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Custom Webpack Config File Not Found!\nTo use a custom webpack config, please ensure the path to the custom webpack file is correct: \n${pathToWebpackConfig}`
|
`Custom Webpack Config File Not Found!\nTo use a custom webpack config, please ensure the path to the custom webpack file is correct: \n${pathToWebpackConfig}`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return serveWebpackBrowser(
|
let dependencies: DependentBuildableProjectNode[];
|
||||||
options as DevServerBuilderOptions,
|
if (!buildLibsFromSource) {
|
||||||
context as any
|
const buildTargetTsConfigPath =
|
||||||
);
|
buildTargetConfiguration?.tsConfig ?? buildTarget.options.tsConfig;
|
||||||
|
const result = calculateProjectDependencies(
|
||||||
|
readCachedProjectGraph(),
|
||||||
|
context.workspaceRoot,
|
||||||
|
context.target.project,
|
||||||
|
parsedBrowserTarget.target,
|
||||||
|
context.target.configuration
|
||||||
|
);
|
||||||
|
dependencies = result.dependencies;
|
||||||
|
const updatedTsConfig = createTmpTsConfig(
|
||||||
|
joinPathFragments(context.workspaceRoot, buildTargetTsConfigPath),
|
||||||
|
context.workspaceRoot,
|
||||||
|
result.target.data.root,
|
||||||
|
dependencies
|
||||||
|
);
|
||||||
|
process.env.NX_TSCONFIG_PATH = updatedTsConfig;
|
||||||
|
|
||||||
|
// We can't just pass the tsconfig path in memory to the angular builder
|
||||||
|
// function because we can't pass the build target options to it, the build
|
||||||
|
// targets options will be retrieved by the builder from the project
|
||||||
|
// configuration. Therefore, we patch the method in the context to retrieve
|
||||||
|
// the target options to overwrite the tsconfig path to use the generated
|
||||||
|
// one with the updated path mappings.
|
||||||
|
const originalGetTargetOptions = context.getTargetOptions;
|
||||||
|
context.getTargetOptions = async (target) => {
|
||||||
|
const options = await originalGetTargetOptions(target);
|
||||||
|
options.tsConfig = updatedTsConfig;
|
||||||
|
return options;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return executeDevServerBuilder(options as DevServerBuilderOptions, context, {
|
||||||
|
webpackConfiguration: async (baseWebpackConfig) => {
|
||||||
|
if (!buildLibsFromSource) {
|
||||||
|
const workspaceDependencies = dependencies
|
||||||
|
.filter((dep) => !isNpmProject(dep.node))
|
||||||
|
.map((dep) => dep.node.name);
|
||||||
|
baseWebpackConfig.plugins.push(
|
||||||
|
new WebpackNxBuildCoordinationPlugin(
|
||||||
|
`nx run-many --target=${
|
||||||
|
parsedBrowserTarget.target
|
||||||
|
} --projects=${workspaceDependencies.join(',')}`
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!pathToWebpackConfig) {
|
||||||
|
return baseWebpackConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
const customWebpackConfiguration = resolveCustomWebpackConfig(
|
||||||
|
pathToWebpackConfig,
|
||||||
|
buildTarget.options.tsConfig
|
||||||
|
);
|
||||||
|
// The extra Webpack configuration file can also export a Promise, for instance:
|
||||||
|
// `module.exports = new Promise(...)`. If it exports a single object, but not a Promise,
|
||||||
|
// then await will just resolve that object.
|
||||||
|
const config = await customWebpackConfiguration;
|
||||||
|
|
||||||
|
// The extra Webpack configuration file can export a synchronous or asynchronous function,
|
||||||
|
// for instance: `module.exports = async config => { ... }`.
|
||||||
|
if (typeof config === 'function') {
|
||||||
|
return config(
|
||||||
|
baseWebpackConfig,
|
||||||
|
buildTargetConfiguration,
|
||||||
|
context.target
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return merge(baseWebpackConfig, config);
|
||||||
|
},
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export default createBuilder<JsonObject & Schema>(
|
export default createBuilder<JsonObject & Schema>(
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user