Skip to content

Commit dc4c6b7

Browse files
committed
feat(angular-rspack): use development config for serve
1 parent a9adbbd commit dc4c6b7

File tree

15 files changed

+671
-594
lines changed

15 files changed

+671
-594
lines changed

e2e/fixtures/rspack-csr-css/package.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,16 @@
99
"nx": {
1010
"targets": {
1111
"serve-api": {
12+
"continuous": true,
1213
"command": "node ./src/api.mjs",
1314
"options": {
1415
"cwd": "e2e/fixtures/rspack-csr-css"
1516
}
17+
},
18+
"serve": {
19+
"dependsOn": [
20+
"serve-api"
21+
]
1622
}
1723
}
1824
}
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
import type { Configuration, DevServer } from '@rspack/core';
2+
import { resolve } from 'path';
3+
import { getPolyfillsEntry, toRspackEntries } from './entry-points';
4+
import {
5+
getAllowedHostsConfig,
6+
getProxyConfig,
7+
} from './dev-server-config-utils';
8+
import { getOptimization } from './optimization-config';
9+
import { NgRspackPlugin } from '../../plugins/ng-rspack';
10+
import {
11+
HashFormat,
12+
I18nOptions,
13+
NormalizedAngularRspackPluginOptions,
14+
} from '../../models';
15+
16+
export async function getBrowserConfig(
17+
root: string,
18+
normalizedOptions: NormalizedAngularRspackPluginOptions,
19+
i18n: I18nOptions,
20+
hashFormat: HashFormat,
21+
defaultConfig: Configuration
22+
): Promise<Configuration> {
23+
const isProduction = process.env['NODE_ENV'] === 'production';
24+
const isDevServer = !!process.env['WEBPACK_SERVE'];
25+
26+
return {
27+
...defaultConfig,
28+
name: 'browser',
29+
...(normalizedOptions.hasServer && isDevServer
30+
? { dependencies: ['server'] }
31+
: {}),
32+
target: 'web',
33+
entry: {
34+
main: {
35+
import: [
36+
...(i18n.shouldInline ? ['@angular/localize/init'] : []),
37+
normalizedOptions.browser,
38+
],
39+
},
40+
...getPolyfillsEntry(normalizedOptions.polyfills, normalizedOptions.aot),
41+
...toRspackEntries(
42+
normalizedOptions.globalStyles,
43+
normalizedOptions.root,
44+
'ngGlobalStyles'
45+
),
46+
...toRspackEntries(
47+
normalizedOptions.globalScripts,
48+
normalizedOptions.root
49+
),
50+
},
51+
devServer: {
52+
headers: {
53+
'Access-Control-Allow-Origin': '*',
54+
},
55+
allowedHosts: getAllowedHostsConfig(
56+
normalizedOptions.devServer.allowedHosts,
57+
normalizedOptions.devServer.disableHostCheck
58+
),
59+
client: {
60+
webSocketURL: {
61+
hostname: normalizedOptions.devServer.host,
62+
port: normalizedOptions.devServer.port,
63+
},
64+
overlay: {
65+
errors: true,
66+
warnings: false,
67+
runtimeErrors: true,
68+
},
69+
reconnect: true,
70+
},
71+
hot: false,
72+
liveReload: true,
73+
watchFiles: ['./src/**/*.*', './public/**/*.*'],
74+
historyApiFallback: {
75+
index: '/index.html',
76+
rewrites: [{ from: /^\/$/, to: 'index.html' }],
77+
},
78+
devMiddleware: {
79+
writeToDisk: (file) => !file.includes('.hot-update.'),
80+
},
81+
host: normalizedOptions.devServer.host,
82+
port: normalizedOptions.devServer.port,
83+
server: {
84+
options:
85+
normalizedOptions.devServer.sslKey &&
86+
normalizedOptions.devServer.sslCert
87+
? {
88+
key: resolve(root, normalizedOptions.devServer.sslKey),
89+
cert: resolve(root, normalizedOptions.devServer.sslCert),
90+
}
91+
: {},
92+
type: normalizedOptions.devServer.ssl ? 'https' : 'http',
93+
},
94+
proxy: await getProxyConfig(
95+
root,
96+
normalizedOptions.devServer.proxyConfig
97+
),
98+
onListening: (devServer) => {
99+
if (!devServer) {
100+
throw new Error('@rspack/dev-server is not defined');
101+
}
102+
103+
const port =
104+
(devServer.server?.address() as { port: number })?.port ??
105+
normalizedOptions.devServer.port;
106+
console.log('Listening on port:', port);
107+
},
108+
} as DevServer,
109+
110+
output: {
111+
...defaultConfig.output,
112+
hashFunction: isProduction ? 'xxhash64' : undefined,
113+
path: normalizedOptions.outputPath.browser,
114+
cssFilename: `[name]${hashFormat.file}.css`,
115+
filename: `[name]${hashFormat.chunk}.js`,
116+
chunkFilename: `[name]${hashFormat.chunk}.js`,
117+
scriptType: 'module',
118+
module: true,
119+
},
120+
optimization: getOptimization(normalizedOptions, 'browser'),
121+
plugins: [
122+
...(defaultConfig.plugins ?? []),
123+
new NgRspackPlugin(normalizedOptions, {
124+
i18nOptions: i18n,
125+
platform: 'browser',
126+
}),
127+
],
128+
};
129+
}
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
import {
2+
type Configuration,
3+
CssExtractRspackPlugin,
4+
javascript,
5+
} from '@rspack/core';
6+
import {
7+
JS_ALL_EXT_REGEX,
8+
TS_ALL_EXT_REGEX,
9+
} from '@nx/angular-rspack-compiler';
10+
import {
11+
HashFormat,
12+
I18nOptions,
13+
NormalizedAngularRspackPluginOptions,
14+
} from '../../models';
15+
import { getStyleLoaders } from './style-config-utils';
16+
import { getCrossOriginLoading } from './helpers';
17+
import { configureSourceMap } from './sourcemap-utils';
18+
19+
export async function getCommonConfig(
20+
root: string,
21+
normalizedOptions: NormalizedAngularRspackPluginOptions,
22+
i18n: I18nOptions,
23+
i18nHash: string | (() => void),
24+
hashFormat: HashFormat
25+
) {
26+
const isProduction = process.env['NODE_ENV'] === 'production';
27+
const crossOriginLoading = getCrossOriginLoading(normalizedOptions);
28+
const sourceMapOptions = configureSourceMap(normalizedOptions.sourceMap);
29+
30+
const defaultConfig: Configuration = {
31+
context: root,
32+
mode: isProduction ? 'production' : 'development',
33+
devtool: normalizedOptions.sourceMap.scripts ? 'source-map' : undefined,
34+
output: {
35+
uniqueName: normalizedOptions.projectName ?? 'rspack-angular',
36+
publicPath: normalizedOptions.deployUrl ?? '',
37+
clean: normalizedOptions.deleteOutputPath,
38+
crossOriginLoading,
39+
trustedTypes: { policyName: 'angular#bundler' },
40+
sourceMapFilename: normalizedOptions.sourceMap.scripts
41+
? '[file].map'
42+
: undefined,
43+
scriptType: 'module',
44+
},
45+
resolve: {
46+
extensions: ['.ts', '.tsx', '.mjs', '.js'],
47+
symlinks: !normalizedOptions.preserveSymlinks,
48+
modules: ['node_modules'],
49+
mainFields: ['es2020', 'es2015', 'browser', 'module', 'main'],
50+
conditionNames: ['es2020', 'es2015', '...'],
51+
tsConfig: {
52+
configFile: normalizedOptions.tsConfig,
53+
},
54+
...(i18n.shouldInline && normalizedOptions.aot
55+
? { alias: { '@angular/localize/init': false } }
56+
: {}),
57+
},
58+
resolveLoader: {
59+
symlinks: !normalizedOptions.preserveSymlinks,
60+
},
61+
watchOptions: {
62+
followSymlinks: normalizedOptions.preserveSymlinks,
63+
},
64+
ignoreWarnings: [
65+
// https://github.com/webpack-contrib/source-map-loader/blob/b2de4249c7431dd8432da607e08f0f65e9d64219/src/index.js#L83
66+
/Failed to parse source map from/,
67+
// https://github.com/webpack-contrib/postcss-loader/blob/bd261875fdf9c596af4ffb3a1a73fe3c549befda/src/index.js#L153-L158
68+
/Add postcss as project dependency/,
69+
// esbuild will issue a warning, while still hoists the @charset at the very top.
70+
// This is caused by a bug in css-loader https://github.com/webpack-contrib/css-loader/issues/1212
71+
/"@charset" must be the first rule in the file/,
72+
],
73+
module: {
74+
parser: {
75+
javascript: {
76+
requireContext: false,
77+
url: false,
78+
},
79+
},
80+
rules: [
81+
{
82+
test: /\.?(svg|html)$/,
83+
// Only process HTML and SVG which are known Angular component resources.
84+
resourceQuery: /\?ngResource/,
85+
type: 'asset/source',
86+
},
87+
...(await getStyleLoaders(normalizedOptions)),
88+
...sourceMapOptions.sourceMapRules,
89+
{ test: /[/\\]rxjs[/\\]add[/\\].+\.js$/, sideEffects: true },
90+
{
91+
test: TS_ALL_EXT_REGEX,
92+
use: [
93+
{
94+
loader: 'builtin:swc-loader',
95+
options: {
96+
jsc: {
97+
parser: {
98+
syntax: 'typescript',
99+
},
100+
target: 'es2022',
101+
},
102+
},
103+
},
104+
{
105+
loader: require.resolve(
106+
'@nx/angular-rspack/loaders/angular-loader'
107+
),
108+
},
109+
],
110+
},
111+
{
112+
test: JS_ALL_EXT_REGEX,
113+
use: [
114+
{
115+
loader: require.resolve(
116+
'@nx/angular-rspack/loaders/angular-partial-transform-loader'
117+
),
118+
},
119+
],
120+
},
121+
],
122+
},
123+
plugins: [
124+
...sourceMapOptions.sourceMapPlugins,
125+
...(i18n.shouldInline
126+
? [
127+
{
128+
apply(compiler) {
129+
compiler.hooks.compilation.tap(
130+
'AngularRspackPlugin',
131+
(compilation) => {
132+
javascript.JavascriptModulesPlugin.getCompilationHooks(
133+
compilation
134+
).chunkHash.tap('AngularRspackPlugin', (_, hash) => {
135+
hash.update(Buffer.from('$localize' + i18nHash));
136+
});
137+
}
138+
);
139+
},
140+
},
141+
]
142+
: []),
143+
new CssExtractRspackPlugin({
144+
filename: `[name]${hashFormat.extract}.css`,
145+
}),
146+
],
147+
};
148+
return defaultConfig;
149+
}

packages/angular-rspack/src/lib/config/dev-server-config-utils.ts renamed to packages/angular-rspack/src/lib/config/config-utils/dev-server-config-utils.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import assert from 'node:assert';
33
import { existsSync, promises as fsPromises } from 'node:fs';
44
import { extname, resolve } from 'node:path';
55
import { pathToFileURL } from 'node:url';
6-
import { loadEsmModule } from '../utils/misc-helpers';
6+
import { loadEsmModule } from '../../utils/misc-helpers';
77

88
export function getAllowedHostsConfig(
99
allowedHosts: string[] | boolean | undefined,

packages/angular-rspack/src/lib/config/entry-points.ts renamed to packages/angular-rspack/src/lib/config/config-utils/entry-points.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { join } from 'node:path';
2-
import type { GlobalEntry } from '../models';
2+
import type { GlobalEntry } from '../../models';
33

44
export function getEntryPoints(
55
globalStyles: GlobalEntry[],

packages/angular-rspack/src/lib/config/helpers.ts renamed to packages/angular-rspack/src/lib/config/config-utils/helpers.ts

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,17 @@
55
* Use of this source code is governed by an MIT-style license that can be
66
* found in the LICENSE file at https://angular.dev/license
77
*/
8-
import { OutputHashing, HashFormat } from '../models';
98
import { readdir, rm } from 'node:fs/promises';
109
import { join, resolve } from 'node:path';
10+
import {
11+
AngularRspackPluginOptions,
12+
HashFormat,
13+
NormalizedAngularRspackPluginOptions,
14+
normalizeOptions,
15+
OutputHashing,
16+
} from '../../models';
17+
import { configureI18n } from '../i18n/create-i18n-options';
18+
import type { Configuration } from '@rspack/core';
1119

1220
/**
1321
* Delete an output directory, but error out if it's the root of the project.
@@ -93,3 +101,42 @@ export function getOutputHashFormat(
93101
};
94102
}
95103
}
104+
105+
export async function normalizeOptionWithI18n(
106+
options: AngularRspackPluginOptions
107+
) {
108+
const { options: _options, i18n } = await configureI18n(
109+
options.root ?? process.cwd(),
110+
options
111+
);
112+
// Update file hashes to include translation file content
113+
const i18nHash = i18n.shouldInline
114+
? Object.values(i18n.locales).reduce(
115+
(data, locale) =>
116+
data + locale.files.map((file) => file.integrity || '').join('|'),
117+
''
118+
)
119+
: () => {
120+
// no-op as i18n is not inlined
121+
};
122+
123+
const normalizedOptions = await normalizeOptions(_options);
124+
return { i18n, i18nHash, normalizedOptions };
125+
}
126+
127+
export function getCrossOriginLoading(
128+
normalizedOptions: NormalizedAngularRspackPluginOptions
129+
) {
130+
let crossOriginLoading: NonNullable<
131+
Configuration['output']
132+
>['crossOriginLoading'] = false;
133+
if (
134+
normalizedOptions.subresourceIntegrity &&
135+
normalizedOptions.crossOrigin === 'none'
136+
) {
137+
crossOriginLoading = 'anonymous';
138+
} else if (normalizedOptions.crossOrigin !== 'none') {
139+
crossOriginLoading = normalizedOptions.crossOrigin;
140+
}
141+
return crossOriginLoading;
142+
}

0 commit comments

Comments
 (0)