nx/packages/angular/src/builders/webpack-dev-server/webpack-dev-server.impl.ts
2023-03-09 16:36:50 -05:00

171 lines
6.2 KiB
TypeScript

import {
joinPathFragments,
parseTargetString,
readCachedProjectGraph,
} from '@nrwl/devkit';
import { WebpackNxBuildCoordinationPlugin } from '@nrwl/webpack/src/plugins/webpack-nx-build-coordination-plugin';
import { DependentBuildableProjectNode } from '@nrwl/js/src/utils/buildable-libs-utils';
import { readCachedProjectConfiguration } from 'nx/src/project-graph/project-graph';
import { existsSync } from 'fs';
import { isNpmProject } from 'nx/src/project-graph/operators';
import {
mergeCustomWebpackConfig,
resolveIndexHtmlTransformer,
} from '../utilities/webpack';
import { normalizeOptions } from './lib';
import type { Schema } from './schema';
import { createTmpTsConfigForBuildableLibs } from '../utilities/buildable-libs';
import { from } from 'rxjs';
import { switchMap } from 'rxjs/operators';
import { getRootTsConfigPath } from 'nx/src/utils/typescript';
export function executeWebpackDevServerBuilder(
rawOptions: Schema,
context: import('@angular-devkit/architect').BuilderContext
) {
process.env.NX_TSCONFIG_PATH = getRootTsConfigPath();
const options = normalizeOptions(rawOptions);
const parsedBrowserTarget = parseTargetString(
options.browserTarget,
readCachedProjectGraph()
);
const browserTargetProjectConfiguration = readCachedProjectConfiguration(
parsedBrowserTarget.project
);
const buildTarget =
browserTargetProjectConfiguration.targets[parsedBrowserTarget.target];
const buildTargetConfiguration = parsedBrowserTarget.configuration
? buildTarget.configurations[parsedBrowserTarget.configuration]
: buildTarget.defaultConfiguration
? buildTarget.configurations[buildTarget.defaultConfiguration]
: buildTarget.options;
const buildLibsFromSource =
options.buildLibsFromSource ??
buildTargetConfiguration?.buildLibsFromSource ??
buildTarget.options.buildLibsFromSource ??
true;
const customWebpackConfig: { path: string } =
buildTargetConfiguration?.customWebpackConfig ??
buildTarget.options.customWebpackConfig;
let pathToWebpackConfig: string;
if (customWebpackConfig && customWebpackConfig.path) {
pathToWebpackConfig = joinPathFragments(
context.workspaceRoot,
customWebpackConfig.path
);
if (pathToWebpackConfig && !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}`
);
}
}
const indexFileTransformer: string =
buildTargetConfiguration?.indexFileTransformer ??
buildTarget.options.indexFileTransformer;
let pathToIndexFileTransformer: string;
if (indexFileTransformer) {
pathToIndexFileTransformer = joinPathFragments(
context.workspaceRoot,
indexFileTransformer
);
if (pathToIndexFileTransformer && !existsSync(pathToIndexFileTransformer)) {
throw new Error(
`File containing Index File Transformer function Not Found!\n Please ensure the path to the file containing the function is correct: \n${pathToIndexFileTransformer}`
);
}
}
let dependencies: DependentBuildableProjectNode[];
if (!buildLibsFromSource) {
const buildTargetTsConfigPath =
buildTargetConfiguration?.tsConfig ?? buildTarget.options.tsConfig;
const { tsConfigPath, dependencies: foundDependencies } =
createTmpTsConfigForBuildableLibs(buildTargetTsConfigPath, context, {
target: parsedBrowserTarget.target,
});
dependencies = foundDependencies;
// 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 = tsConfigPath;
return options;
};
// The buildTargetConfiguration also needs to use the generated tsconfig path
// otherwise the build will fail if customWebpack function/file is referencing
// local libs. This synchronize the behavior with webpack-browser and
// webpack-server implementation.
buildTargetConfiguration.tsConfig = tsConfigPath;
}
return from(import('@angular-devkit/build-angular')).pipe(
switchMap(({ executeDevServerBuilder }) =>
executeDevServerBuilder(options, context, {
webpackConfiguration: async (baseWebpackConfig) => {
if (!buildLibsFromSource) {
const workspaceDependencies = dependencies
.filter((dep) => !isNpmProject(dep.node))
.map((dep) => dep.node.name);
// default for `nx run-many` is --all projects
// by passing an empty string for --projects, run-many will default to
// run the target for all projects.
// This will occur when workspaceDependencies = []
if (workspaceDependencies.length > 0) {
baseWebpackConfig.plugins.push(
new WebpackNxBuildCoordinationPlugin(
`nx run-many --target=${
parsedBrowserTarget.target
} --projects=${workspaceDependencies.join(',')}`
)
);
}
}
if (!pathToWebpackConfig) {
return baseWebpackConfig;
}
return mergeCustomWebpackConfig(
baseWebpackConfig,
pathToWebpackConfig,
buildTargetConfiguration,
context.target
);
},
...(pathToIndexFileTransformer
? {
indexHtml: resolveIndexHtmlTransformer(
pathToIndexFileTransformer,
buildTargetConfiguration.tsConfig,
context.target
),
}
: {}),
})
)
);
}
export default require('@angular-devkit/architect').createBuilder(
executeWebpackDevServerBuilder
) as any;