Skip to content

Commit 9e8d94e

Browse files
authored
feat(angular-rspack): improve createConfig public api (#35)
* feat(angular-rspack): improve `createConfig` public api * feat(angular-rspack): warn about currently unsupported options
1 parent 8a419a0 commit 9e8d94e

File tree

4 files changed

+230
-49
lines changed

4 files changed

+230
-49
lines changed

packages/angular-rspack/src/lib/config/create-config.unit.test.ts

+29-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ describe('createConfig', () => {
1818
outputHashing: 'all',
1919
scripts: [],
2020
aot: true,
21-
hasServer: false,
2221
skipTypeChecking: false,
2322
};
2423

@@ -27,6 +26,10 @@ describe('createConfig', () => {
2726
vi.stubEnv('NGRS_CONFIG', '');
2827
});
2928

29+
afterEach(() => {
30+
vi.restoreAllMocks();
31+
});
32+
3033
it('should create config for mode "production" if env variable NODE_ENV is "production"', async () => {
3134
vi.stubEnv('NODE_ENV', 'production');
3235
await expect(_createConfig(configBase)).resolves.toStrictEqual([
@@ -118,6 +121,31 @@ describe('createConfig', () => {
118121
]);
119122
});
120123

124+
it('should create config even when known unsupported options are provided and warn about them', async () => {
125+
const warnSpy = vi.spyOn(console, 'warn');
126+
127+
await expect(
128+
createConfig({
129+
options: {
130+
...configBase,
131+
clearScreen: true,
132+
devServer: { allowedHosts: ['localhost'] },
133+
},
134+
})
135+
).resolves.not.toContain([
136+
expect.objectContaining({
137+
clearScreen: true,
138+
devServer: { allowedHosts: ['localhost'] },
139+
}),
140+
]);
141+
expect(warnSpy).toHaveBeenCalledWith(
142+
`The following options are not yet supported:
143+
"clearScreen"
144+
"devServer.allowedHosts"
145+
`
146+
);
147+
});
148+
121149
it('should create config from options with a custom root', async () => {
122150
const customRoot = join(process.cwd(), 'custom-root');
123151

packages/angular-rspack/src/lib/models/angular-rspack-plugin-options.ts

+39-29
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,12 @@ import type {
33
InlineStyleLanguage,
44
StylePreprocessorOptions,
55
} from '@nx/angular-rspack-compiler';
6+
import type {
7+
DevServerUnsupportedOptions,
8+
PluginUnsupportedOptions,
9+
} from './unsupported-options';
610

7-
export interface DevServerOptions {
11+
export interface DevServerOptions extends DevServerUnsupportedOptions {
812
port?: number;
913
ssl?: boolean;
1014
sslKey?: string;
@@ -76,57 +80,63 @@ export interface SourceMap {
7680
vendor: boolean;
7781
}
7882

79-
export interface AngularRspackPluginOptions {
80-
index: IndexElement;
81-
browser: string;
83+
export interface AngularRspackPluginOptions extends PluginUnsupportedOptions {
84+
aot?: boolean;
85+
assets?: AssetElement[];
86+
browser?: string;
87+
commonChunk?: boolean;
88+
devServer?: DevServerOptions;
89+
extractLicenses?: boolean;
90+
fileReplacements?: FileReplacement[];
91+
index?: IndexElement;
92+
inlineStyleLanguage?: InlineStyleLanguage;
93+
namedChunks?: boolean;
94+
optimization?: boolean | OptimizationOptions;
95+
outputHashing?: OutputHashing;
96+
outputPath?:
97+
| string
98+
| (Required<Pick<OutputPath, 'base'>> & Partial<OutputPath>);
99+
polyfills?: string[];
100+
root?: string;
101+
scripts?: ScriptOrStyleEntry[];
82102
server?: string;
103+
skipTypeChecking?: boolean;
104+
sourceMap?: boolean | Partial<SourceMap>;
83105
ssr?:
84106
| boolean
85107
| {
86108
entry: string;
87109
experimentalPlatform?: 'node' | 'neutral';
88110
};
89-
polyfills: string[];
90-
assets?: AssetElement[];
111+
stylePreprocessorOptions?: StylePreprocessorOptions;
91112
styles?: ScriptOrStyleEntry[];
92-
scripts?: ScriptOrStyleEntry[];
93-
outputPath:
94-
| string
95-
| (Required<Pick<OutputPath, 'base'>> & Partial<OutputPath>);
96-
fileReplacements: FileReplacement[];
97-
aot: boolean;
98-
inlineStyleLanguage: InlineStyleLanguage;
99-
tsConfig: string;
100-
hasServer: boolean;
101-
sourceMap?: boolean | Partial<SourceMap>;
102-
skipTypeChecking: boolean;
113+
tsConfig?: string;
103114
useTsProjectReferences?: boolean;
104-
optimization?: boolean | OptimizationOptions;
105-
outputHashing?: OutputHashing;
106-
stylePreprocessorOptions?: StylePreprocessorOptions;
107-
namedChunks?: boolean;
108115
vendorChunk?: boolean;
109-
commonChunk?: boolean;
110-
devServer?: DevServerOptions;
111-
extractLicenses?: boolean;
112-
root?: string;
113116
}
114117

115118
export interface NormalizedAngularRspackPluginOptions
116119
extends Omit<AngularRspackPluginOptions, 'index' | 'scripts' | 'styles'> {
117120
advancedOptimizations: boolean;
121+
aot: boolean;
118122
assets: NormalizedAssetElement[];
119-
index: NormalizedIndexElement | undefined;
123+
browser: string;
124+
commonChunk: boolean;
120125
devServer: DevServerOptions & { port: number };
121126
extractLicenses: boolean;
122-
namedChunks: boolean;
123-
vendorChunk: boolean;
124-
commonChunk: boolean;
127+
fileReplacements: FileReplacement[];
125128
globalScripts: GlobalEntry[];
126129
globalStyles: GlobalEntry[];
130+
index: NormalizedIndexElement | undefined;
131+
inlineStyleLanguage: InlineStyleLanguage;
132+
hasServer: boolean;
133+
namedChunks: boolean;
127134
optimization: boolean | OptimizationOptions;
128135
outputHashing: OutputHashing;
129136
outputPath: OutputPath;
137+
polyfills: string[];
130138
root: string;
131139
sourceMap: SourceMap;
140+
tsConfig: string;
141+
vendorChunk: boolean;
132142
}

packages/angular-rspack/src/lib/models/normalize-options.ts

+51-19
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,13 @@ import type {
2727
NormalizedAssetElement,
2828
NormalizedIndexElement,
2929
OutputPath,
30-
SourceMap,
3130
ScriptOrStyleEntry,
31+
SourceMap,
3232
} from './angular-rspack-plugin-options';
33+
import {
34+
DEV_SERVER_OPTIONS_PENDING_SUPPORT,
35+
TOP_LEVEL_OPTIONS_PENDING_SUPPORT,
36+
} from './unsupported-options';
3337

3438
export const INDEX_HTML_CSR = 'index.csr.html';
3539

@@ -97,9 +101,35 @@ export function validateOptimization(
97101
);
98102
}
99103

104+
function validateGeneralUnsupportedOptions(
105+
options: AngularRspackPluginOptions
106+
) {
107+
const topLevelUnsupportedOptions = TOP_LEVEL_OPTIONS_PENDING_SUPPORT.filter(
108+
(option) => options[option] !== undefined
109+
).sort();
110+
const devServerUnsupportedOptions = DEV_SERVER_OPTIONS_PENDING_SUPPORT.filter(
111+
(option) => options.devServer?.[option] !== undefined
112+
).sort();
113+
114+
const unsupportedOptions = [
115+
...topLevelUnsupportedOptions.map((option) => `"${option}"`),
116+
...devServerUnsupportedOptions.map((option) => `"devServer.${option}"`),
117+
];
118+
119+
if (unsupportedOptions.length > 0) {
120+
console.warn(
121+
`The following options are not yet supported:\n ${unsupportedOptions.join(
122+
'\n '
123+
)}\n`
124+
);
125+
}
126+
}
127+
100128
export function normalizeOptions(
101129
options: AngularRspackPluginOptions
102130
): NormalizedAngularRspackPluginOptions {
131+
validateGeneralUnsupportedOptions(options);
132+
103133
const { fileReplacements = [], server, ssr, optimization } = options;
104134

105135
validateSsr(ssr);
@@ -146,6 +176,8 @@ export function normalizeOptions(
146176
'Disabling the "index" option is not yet supported. Defaulting to "src/index.html".'
147177
);
148178
options.index = join(root, 'src/index.html');
179+
} else if (!options.index) {
180+
options.index = join(root, 'src/index.html');
149181
}
150182

151183
let index: NormalizedIndexElement | undefined;
@@ -206,32 +238,32 @@ export function normalizeOptions(
206238
}
207239

208240
return {
209-
index,
210-
browser: options.browser ?? './src/main.ts',
211-
...(server ? { server } : {}),
212-
...(ssr ? { ssr: normalizedSsr } : {}),
213-
optimization: normalizedOptimization,
214241
advancedOptimizations,
215-
polyfills: options.polyfills ?? [],
216242
assets,
243+
aot,
244+
browser: options.browser ?? './src/main.ts',
245+
commonChunk: options.commonChunk ?? true,
246+
devServer: normalizeDevServer(options.devServer),
247+
extractLicenses: options.extractLicenses ?? true,
248+
fileReplacements: resolveFileReplacements(fileReplacements, root),
217249
globalStyles,
218250
globalScripts,
219-
outputPath: normalizeOutputPath(root, options.outputPath),
220-
fileReplacements: resolveFileReplacements(fileReplacements, root),
221-
aot,
222-
outputHashing: options.outputHashing ?? 'all',
223-
inlineStyleLanguage: options.inlineStyleLanguage ?? 'css',
224-
tsConfig,
225-
sourceMap: normalizeSourceMap(options.sourceMap),
226251
hasServer: getHasServer(root, server, normalizedSsr),
252+
index,
253+
inlineStyleLanguage: options.inlineStyleLanguage ?? 'css',
254+
namedChunks: options.namedChunks ?? false,
255+
optimization: normalizedOptimization,
256+
outputHashing: options.outputHashing ?? 'all',
257+
outputPath: normalizeOutputPath(root, options.outputPath),
258+
polyfills: options.polyfills ?? [],
259+
root,
260+
server,
227261
skipTypeChecking: options.skipTypeChecking ?? false,
262+
sourceMap: normalizeSourceMap(options.sourceMap),
263+
ssr: normalizedSsr,
264+
tsConfig,
228265
useTsProjectReferences: options.useTsProjectReferences ?? false,
229-
namedChunks: options.namedChunks ?? false,
230266
vendorChunk: options.vendorChunk ?? false,
231-
commonChunk: options.commonChunk ?? true,
232-
devServer: normalizeDevServer(options.devServer),
233-
extractLicenses: options.extractLicenses ?? true,
234-
root,
235267
};
236268
}
237269

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
export type BudgetEntry = {
2+
type:
3+
| 'all'
4+
| 'allScript'
5+
| 'any'
6+
| 'anyScript'
7+
| 'anyComponentStyle'
8+
| 'bundle'
9+
| 'initial';
10+
name?: string;
11+
baseline?: string;
12+
maximumWarning?: string;
13+
maximumError?: string;
14+
minimumWarning?: string;
15+
minimumError?: string;
16+
warning?: string;
17+
error?: string;
18+
};
19+
20+
export interface DevServerUnsupportedOptions {
21+
allowedHosts?: string[] | boolean;
22+
headers?: Record<string, string>;
23+
open?: boolean;
24+
liveReload?: boolean;
25+
servePath?: string;
26+
hmr?: boolean;
27+
inspect?: string | boolean;
28+
prebundle?:
29+
| boolean
30+
| {
31+
exclude: string[];
32+
};
33+
}
34+
35+
export interface PluginUnsupportedOptions {
36+
deployUrl?: string;
37+
security?: {
38+
autoCsp?:
39+
| boolean
40+
| {
41+
unsafeEval?: boolean;
42+
};
43+
};
44+
externalDependencies?: string[];
45+
clearScreen?: boolean;
46+
define?: Record<string, string>;
47+
baseHref?: string;
48+
verbose?: boolean;
49+
progress?: boolean;
50+
i18nMissingTranslation?: 'warning' | 'error' | 'ignore';
51+
i18nDuplicateTranslation?: 'warning' | 'error' | 'ignore';
52+
localize?: boolean | string[];
53+
watch?: boolean;
54+
poll?: number;
55+
deleteOutputPath?: boolean;
56+
preserveSymlinks?: boolean;
57+
subresourceIntegrity?: boolean;
58+
serviceWorker?: string | false;
59+
statsJson?: boolean;
60+
budgets?: BudgetEntry[];
61+
webWorkerTsConfig?: string;
62+
crossOrigin?: 'none' | 'anonymous' | 'use-credentials';
63+
allowedCommonJsDependencies?: string[];
64+
prerender?:
65+
| boolean
66+
| {
67+
routesFile?: string;
68+
discoverRoutes?: boolean;
69+
};
70+
appShell?: boolean;
71+
outputMode?: 'static' | 'server';
72+
}
73+
74+
export const TOP_LEVEL_OPTIONS_PENDING_SUPPORT = [
75+
'deployUrl',
76+
'security',
77+
'externalDependencies',
78+
'clearScreen',
79+
'define',
80+
'baseHref',
81+
'verbose',
82+
'progress',
83+
'i18nMissingTranslation',
84+
'i18nDuplicateTranslation',
85+
'localize',
86+
'watch',
87+
'poll',
88+
'deleteOutputPath',
89+
'preserveSymlinks',
90+
'subresourceIntegrity',
91+
'serviceWorker',
92+
'statsJson',
93+
'budgets',
94+
'webWorkerTsConfig',
95+
'crossOrigin',
96+
'allowedCommonJsDependencies',
97+
'prerender',
98+
'appShell',
99+
'outputMode',
100+
];
101+
102+
export const DEV_SERVER_OPTIONS_PENDING_SUPPORT = [
103+
'allowedHosts',
104+
'headers',
105+
'open',
106+
'liveReload',
107+
'servePath',
108+
'hmr',
109+
'inspect',
110+
'prebundle',
111+
];

0 commit comments

Comments
 (0)