Skip to content

Commit

Permalink
feat(angular): add support for incremental builds to the webpack-serv…
Browse files Browse the repository at this point in the history
…er executor
  • Loading branch information
leosvelperez committed Jun 16, 2022
1 parent 9900ceb commit bdbed2a
Show file tree
Hide file tree
Showing 6 changed files with 106 additions and 47 deletions.
5 changes: 5 additions & 0 deletions docs/generated/packages/angular.json
Original file line number Diff line number Diff line change
Expand Up @@ -2999,6 +2999,11 @@
"poll": {
"type": "number",
"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.",
"default": true
}
},
"additionalProperties": false,
Expand Down
1 change: 1 addition & 0 deletions packages/angular/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
"@nrwl/jest": "file:../jest",
"@nrwl/linter": "file:../linter",
"@nrwl/storybook": "file:../storybook",
"@nrwl/web": "file:../web",
"@nrwl/workspace": "file:../workspace",
"@phenomnomnominal/tsquery": "4.1.1",
"@schematics/angular": "~14.0.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export function normalizeOptions(schema: Schema): Schema {
liveReload: true,
open: false,
ssl: false,
buildLibsFromSource: true,
...schema,
};
}
1 change: 1 addition & 0 deletions packages/angular/src/builders/webpack-server/schema.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,5 @@ export interface Schema {
hmr?: boolean;
watch?: boolean;
poll?: number;
buildLibsFromSource?: boolean;
}
5 changes: 5 additions & 0 deletions packages/angular/src/builders/webpack-server/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,11 @@
"poll": {
"type": "number",
"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.",
"default": true
}
},
"additionalProperties": false,
Expand Down
140 changes: 93 additions & 47 deletions packages/angular/src/builders/webpack-server/webpack-server.impl.ts
Original file line number Diff line number Diff line change
@@ -1,33 +1,40 @@
import { BuilderContext, createBuilder } from '@angular-devkit/architect';
import {
BuilderContext,
BuilderOutput,
createBuilder,
} from '@angular-devkit/architect';
import {
DevServerBuilderOptions,
serveWebpackBrowser,
} from '@angular-devkit/build-angular/src/builders/dev-server';
executeDevServerBuilder,
} from '@angular-devkit/build-angular';
import { JsonObject } from '@angular-devkit/core';
import {
joinPathFragments,
parseTargetString,
readAllWorkspaceConfiguration,
Workspaces,
readCachedProjectGraph,
} 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 { isNpmProject } from 'nx/src/project-graph/operators';
import { Observable } from 'rxjs';
import { merge } from 'webpack-merge';
import { resolveCustomWebpackConfig } from '../utilities/webpack';
import { normalizeOptions } from './lib';
import type { Schema } from './schema';

export function executeWebpackServerBuilder(
schema: Schema,
rawOptions: Schema,
context: BuilderContext
) {
process.env.NX_TSCONFIG_PATH = joinPathFragments(
context.workspaceRoot,
'tsconfig.base.json'
);

const options = normalizeOptions(schema);
): Observable<BuilderOutput> {
const workspaceConfig = readAllWorkspaceConfiguration();

const options = normalizeOptions(rawOptions);
const parsedBrowserTarget = parseTargetString(options.browserTarget);
const buildTarget =
workspaceConfig.projects[parsedBrowserTarget.project].targets[
Expand All @@ -44,52 +51,91 @@ export function executeWebpackServerBuilder(
selectedConfiguration.customWebpackConfig ??
buildTarget.options.customWebpackConfig;

let pathToWebpackConfig: string;
if (customWebpackConfig && customWebpackConfig.path) {
const pathToWebpackConfig = joinPathFragments(
pathToWebpackConfig = joinPathFragments(
context.workspaceRoot,
customWebpackConfig.path
);

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 {
if (!existsSync(pathToWebpackConfig)) {
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}`
);
}
}

return serveWebpackBrowser(
options as DevServerBuilderOptions,
context as any
);
let dependencies: DependentBuildableProjectNode[];
if (!options.buildLibsFromSource) {
const buildTargetTsConfigPath =
selectedConfiguration.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 (!options.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, selectedConfiguration, context.target);
}

return merge(baseWebpackConfig, config);
},
});
}

export default createBuilder<JsonObject & Schema>(
Expand Down

0 comments on commit bdbed2a

Please sign in to comment.