Skip to content

Commit 59eca00

Browse files
committed
feat(node): support webpackConfig as array
1 parent 2612268 commit 59eca00

File tree

9 files changed

+169
-18
lines changed

9 files changed

+169
-18
lines changed

docs/angular/api-node/executors/build.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,6 @@ Run build when files change.
158158

159159
### webpackConfig
160160

161-
Type: `string`
161+
Type: `array[] | string `
162162

163163
Path to a function which takes a webpack config, context and returns the resulting webpack config

docs/node/api-node/executors/build.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,6 @@ Run build when files change.
159159

160160
### webpackConfig
161161

162-
Type: `string`
162+
Type: `array[] | string `
163163

164164
Path to a function which takes a webpack config, context and returns the resulting webpack config

docs/react/api-node/executors/build.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,6 @@ Run build when files change.
159159

160160
### webpackConfig
161161

162-
Type: `string`
162+
Type: `array[] | string `
163163

164164
Path to a function which takes a webpack config, context and returns the resulting webpack config
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
import { ExecutorContext } from '@nrwl/devkit';
2+
import { of } from 'rxjs';
3+
import * as projectGraph from '@nrwl/workspace/src/core/project-graph';
4+
import type { ProjectGraph } from '@nrwl/workspace/src/core/project-graph';
5+
import buildExecutor from './build.impl';
6+
import { BuildNodeBuilderOptions } from '../../utils/types';
7+
8+
jest.mock('tsconfig-paths-webpack-plugin');
9+
jest.mock('@nrwl/workspace/src/utilities/run-webpack', () => ({
10+
runWebpack: jest.fn(),
11+
}));
12+
import { runWebpack } from '@nrwl/workspace/src/utilities/run-webpack';
13+
14+
describe('Node Build Executor', () => {
15+
let context: ExecutorContext;
16+
let options: BuildNodeBuilderOptions;
17+
18+
beforeEach(async () => {
19+
jest
20+
.spyOn(projectGraph, 'createProjectGraph')
21+
.mockReturnValue({} as ProjectGraph);
22+
23+
(<any>runWebpack).mockReturnValue(of([]));
24+
context = {
25+
root: '/root',
26+
cwd: '/root',
27+
projectName: 'my-app',
28+
targetName: 'build',
29+
workspace: {
30+
version: 2,
31+
projects: {
32+
'my-app': <any>{
33+
root: 'apps/wibble',
34+
sourceRoot: 'apps/wibble',
35+
},
36+
},
37+
},
38+
isVerbose: false,
39+
};
40+
options = {
41+
outputPath: 'dist/apps/wibble',
42+
externalDependencies: 'all',
43+
main: 'apps/wibble/src/main.ts',
44+
tsConfig: 'apps/wibble/tsconfig.ts',
45+
buildLibsFromSource: true,
46+
fileReplacements: [],
47+
};
48+
});
49+
50+
afterEach(() => jest.clearAllMocks());
51+
52+
it('should call webpack', async () => {
53+
await buildExecutor(options, context);
54+
55+
expect(runWebpack).toHaveBeenCalledWith(
56+
expect.objectContaining({
57+
output: {
58+
filename: 'main.js',
59+
libraryTarget: 'commonjs',
60+
path: '/root/dist/apps/wibble',
61+
},
62+
})
63+
);
64+
});
65+
66+
describe('webpackConfig', () => {
67+
it('sholud hanldle custom path', async () => {
68+
jest.mock(
69+
'/root/config.js',
70+
() => (options) => ({ ...options, prop: 'my-val' }),
71+
{ virtual: true }
72+
);
73+
await buildExecutor({ ...options, webpackConfig: 'config.js' }, context);
74+
75+
expect(runWebpack).toHaveBeenCalledWith(
76+
expect.objectContaining({
77+
output: {
78+
filename: 'main.js',
79+
libraryTarget: 'commonjs',
80+
path: '/root/dist/apps/wibble',
81+
},
82+
prop: 'my-val',
83+
})
84+
);
85+
});
86+
87+
it('sholud hanldle multiple custom paths in order', async () => {
88+
jest.mock(
89+
'/root/config1.js',
90+
() => (o) => ({ ...o, prop1: 'my-val-1' }),
91+
{ virtual: true }
92+
);
93+
jest.mock(
94+
'/root/config2.js',
95+
() => (o) => ({
96+
...o,
97+
prop1: o.prop1 + '-my-val-2',
98+
prop2: 'my-val-2',
99+
}),
100+
{ virtual: true }
101+
);
102+
await buildExecutor(
103+
{ ...options, webpackConfig: ['config1.js', 'config2.js'] },
104+
context
105+
);
106+
107+
expect(runWebpack).toHaveBeenCalledWith(
108+
expect.objectContaining({
109+
output: {
110+
filename: 'main.js',
111+
libraryTarget: 'commonjs',
112+
path: '/root/dist/apps/wibble',
113+
},
114+
prop1: 'my-val-1-my-val-2',
115+
prop2: 'my-val-2',
116+
})
117+
);
118+
});
119+
});
120+
});

packages/node/src/executors/build/build.impl.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -79,13 +79,12 @@ export function buildExecutor(
7979
if (options.generatePackageJson) {
8080
generatePackageJson(context.projectName, projGraph, options);
8181
}
82-
let config = getNodeWebpackConfig(options);
83-
if (options.webpackConfig) {
84-
config = require(options.webpackConfig)(config, {
82+
const config = options.webpackConfig.reduce((currentConfig, plugin) => {
83+
return require(plugin)(currentConfig, {
8584
options,
8685
configuration: context.configurationName,
8786
});
88-
}
87+
}, getNodeWebpackConfig(options));
8988

9089
return eachValueFrom(
9190
runWebpack(config, webpack).pipe(

packages/node/src/executors/build/schema.json

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,17 @@
112112
"default": []
113113
},
114114
"webpackConfig": {
115-
"type": "string",
115+
"oneOf": [
116+
{
117+
"type": "array",
118+
"items": {
119+
"type": "string"
120+
}
121+
},
122+
{
123+
"type": "string"
124+
}
125+
],
116126
"description": "Path to a function which takes a webpack config, context and returns the resulting webpack config"
117127
},
118128
"buildLibsFromSource": {

packages/node/src/utils/normalize.spec.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import { normalizeBuildOptions } from './normalize';
2-
import { BuildBuilderOptions } from './types';
2+
import { BuildNodeBuilderOptions } from './types';
33

44
import * as fs from 'fs';
55

66
describe('normalizeBuildOptions', () => {
7-
let testOptions: BuildBuilderOptions;
7+
let testOptions: BuildNodeBuilderOptions;
88
let root: string;
99
let sourceRoot: string;
1010
let projectRoot: string;
@@ -26,6 +26,7 @@ describe('normalizeBuildOptions', () => {
2626
],
2727
assets: [],
2828
statsJson: false,
29+
externalDependencies: 'all',
2930
};
3031
root = '/root';
3132
sourceRoot = 'apps/nodeapp/src';
@@ -76,7 +77,7 @@ describe('normalizeBuildOptions', () => {
7677
isDirectory: () => true,
7778
});
7879
const result = normalizeBuildOptions(
79-
<BuildBuilderOptions>{
80+
{
8081
...testOptions,
8182
root,
8283
assets: [

packages/node/src/utils/normalize.ts

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,21 @@
11
import { resolve, dirname, relative, basename } from 'path';
2-
import { BuildBuilderOptions } from './types';
2+
import {
3+
BuildNodeBuilderOptions,
4+
NormalizedBuildNodeBuilderOptions,
5+
} from './types';
36
import { statSync } from 'fs';
47

58
export interface FileReplacement {
69
replace: string;
710
with: string;
811
}
912

10-
export function normalizeBuildOptions<T extends BuildBuilderOptions>(
11-
options: T,
13+
export function normalizeBuildOptions(
14+
options: BuildNodeBuilderOptions,
1215
root: string,
1316
sourceRoot: string,
1417
projectRoot: string
15-
): T {
18+
): NormalizedBuildNodeBuilderOptions {
1619
return {
1720
...options,
1821
root,
@@ -24,8 +27,10 @@ export function normalizeBuildOptions<T extends BuildBuilderOptions>(
2427
fileReplacements: normalizeFileReplacements(root, options.fileReplacements),
2528
assets: normalizeAssets(options.assets, root, sourceRoot),
2629
webpackConfig: options.webpackConfig
27-
? resolve(root, options.webpackConfig)
28-
: options.webpackConfig,
30+
? []
31+
.concat(options.webpackConfig)
32+
.map((path) => normalizePluginPath(path, root))
33+
: [],
2934
};
3035
}
3136

@@ -34,6 +39,9 @@ function normalizeAssets(
3439
root: string,
3540
sourceRoot: string
3641
): any[] {
42+
if (!Array.isArray(assets)) {
43+
return [];
44+
}
3745
return assets.map((asset) => {
3846
if (typeof asset === 'string') {
3947
const resolvedAssetPath = resolve(root, asset);
@@ -83,3 +91,11 @@ function normalizeFileReplacements(
8391
with: resolve(root, fileReplacement.with),
8492
}));
8593
}
94+
95+
function normalizePluginPath(path: string, root: string) {
96+
try {
97+
return require.resolve(path);
98+
} catch {
99+
return resolve(root, path);
100+
}
101+
}

packages/node/src/utils/types.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ export interface BuildBuilderOptions {
3535
extractLicenses?: boolean;
3636
verbose?: boolean;
3737

38-
webpackConfig?: string;
38+
webpackConfig?: string | string[];
3939

4040
root?: string;
4141
sourceRoot?: string;
@@ -49,3 +49,8 @@ export interface BuildNodeBuilderOptions extends BuildBuilderOptions {
4949
buildLibsFromSource?: boolean;
5050
generatePackageJson?: boolean;
5151
}
52+
53+
export interface NormalizedBuildNodeBuilderOptions
54+
extends BuildNodeBuilderOptions {
55+
webpackConfig: string[];
56+
}

0 commit comments

Comments
 (0)