@nx/rspack 20.8.1 → 20.8.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@nx/rspack",
3
3
  "description": "The Nx Plugin for Rspack contains executors and generators that support building applications using Rspack.",
4
- "version": "20.8.1",
4
+ "version": "20.8.2",
5
5
  "type": "commonjs",
6
6
  "repository": {
7
7
  "type": "git",
@@ -24,10 +24,10 @@
24
24
  "generators": "./generators.json",
25
25
  "executors": "./executors.json",
26
26
  "dependencies": {
27
- "@nx/js": "20.8.1",
28
- "@nx/devkit": "20.8.1",
29
- "@nx/web": "20.8.1",
30
- "@nx/module-federation": "20.8.1",
27
+ "@nx/js": "20.8.2",
28
+ "@nx/devkit": "20.8.2",
29
+ "@nx/web": "20.8.2",
30
+ "@nx/module-federation": "20.8.2",
31
31
  "@phenomnomnominal/tsquery": "~5.0.1",
32
32
  "@rspack/core": "^1.1.5",
33
33
  "@rspack/dev-server": "^1.0.9",
@@ -42,6 +42,7 @@
42
42
  "less-loader": "11.1.0",
43
43
  "license-webpack-plugin": "^4.0.2",
44
44
  "loader-utils": "^2.0.3",
45
+ "parse5": "4.0.0",
45
46
  "sass": "^1.85.0",
46
47
  "sass-embedded": "^1.83.4",
47
48
  "sass-loader": "^16.0.4",
@@ -12,6 +12,87 @@ function transformCjsConfigFile(tree, configPath) {
12
12
  transformWithModuleFederation(tree, configPath, scope);
13
13
  transformWithModuleFederationSSR(tree, configPath, scope);
14
14
  });
15
+ // Add useLegacyHtmlPlugin: true to withWeb() calls
16
+ transformWithWebCalls(tree, configPath);
17
+ }
18
+ function transformWithWebCalls(tree, configPath) {
19
+ const configContents = tree.read(configPath, 'utf-8');
20
+ const ast = tsquery_1.tsquery.ast(configContents);
21
+ // Find withWeb() calls
22
+ const withWebCallNodes = (0, tsquery_1.tsquery)(ast, 'CallExpression > Identifier[name=withWeb]');
23
+ // If there are withWeb calls, update them
24
+ if (withWebCallNodes.length > 0) {
25
+ let newContents = configContents;
26
+ for (const node of withWebCallNodes) {
27
+ const callExpr = node.parent;
28
+ if (!callExpr)
29
+ continue;
30
+ const startPos = callExpr.getStart();
31
+ const endPos = callExpr.getEnd();
32
+ const callText = configContents.substring(startPos, endPos);
33
+ // Skip if useLegacyHtmlPlugin is already present
34
+ if (callText.includes('useLegacyHtmlPlugin')) {
35
+ continue;
36
+ }
37
+ // If it's already withWeb({ ... }), add useLegacyHtmlPlugin: true to the options
38
+ if (callText.includes('{') && callText.includes('}')) {
39
+ const newCallText = callText.replace(/\{\s*/, '{ useLegacyHtmlPlugin: true,\n ');
40
+ newContents =
41
+ newContents.substring(0, startPos) +
42
+ newCallText +
43
+ newContents.substring(endPos);
44
+ }
45
+ else {
46
+ // If it's just withWeb(), replace with withWeb({ useLegacyHtmlPlugin: true })
47
+ const newCallText = 'withWeb({ useLegacyHtmlPlugin: true })';
48
+ newContents =
49
+ newContents.substring(0, startPos) +
50
+ newCallText +
51
+ newContents.substring(endPos);
52
+ }
53
+ }
54
+ if (newContents !== configContents) {
55
+ tree.write(configPath, newContents);
56
+ }
57
+ return;
58
+ }
59
+ // If no withWeb calls, check for withReact calls
60
+ const withReactCallNodes = (0, tsquery_1.tsquery)(ast, 'CallExpression > Identifier[name=withReact]');
61
+ if (withReactCallNodes.length === 0) {
62
+ return;
63
+ }
64
+ let newContents = configContents;
65
+ for (const node of withReactCallNodes) {
66
+ const callExpr = node.parent;
67
+ if (!callExpr)
68
+ continue;
69
+ const startPos = callExpr.getStart();
70
+ const endPos = callExpr.getEnd();
71
+ const callText = configContents.substring(startPos, endPos);
72
+ // Skip if useLegacyHtmlPlugin is already present
73
+ if (callText.includes('useLegacyHtmlPlugin')) {
74
+ continue;
75
+ }
76
+ // If it's already withReact({ ... }), add useLegacyHtmlPlugin: true to the options
77
+ if (callText.includes('{') && callText.includes('}')) {
78
+ const newCallText = callText.replace(/\{\s*/, '{ useLegacyHtmlPlugin: true,\n ');
79
+ newContents =
80
+ newContents.substring(0, startPos) +
81
+ newCallText +
82
+ newContents.substring(endPos);
83
+ }
84
+ else {
85
+ // If it's just withReact(), replace with withReact({ useLegacyHtmlPlugin: true })
86
+ const newCallText = 'withReact({ useLegacyHtmlPlugin: true })';
87
+ newContents =
88
+ newContents.substring(0, startPos) +
89
+ newCallText +
90
+ newContents.substring(endPos);
91
+ }
92
+ }
93
+ if (newContents !== configContents) {
94
+ tree.write(configPath, newContents);
95
+ }
15
96
  }
16
97
  function transformComposePlugins(tree, configPath, scope) {
17
98
  const configContents = tree.read(configPath, 'utf-8');
@@ -12,6 +12,87 @@ function transformEsmConfigFile(tree, configPath) {
12
12
  transformWithModuleFederation(tree, configPath, scope);
13
13
  transformWithModuleFederationSSR(tree, configPath, scope);
14
14
  });
15
+ // Add useLegacyHtmlPlugin: true to withWeb() calls
16
+ transformWithWebCalls(tree, configPath);
17
+ }
18
+ function transformWithWebCalls(tree, configPath) {
19
+ const configContents = tree.read(configPath, 'utf-8');
20
+ const ast = tsquery_1.tsquery.ast(configContents);
21
+ // Find withWeb() calls
22
+ const withWebCallNodes = (0, tsquery_1.tsquery)(ast, 'CallExpression > Identifier[name=withWeb]');
23
+ // If there are withWeb calls, update them
24
+ if (withWebCallNodes.length > 0) {
25
+ let newContents = configContents;
26
+ for (const node of withWebCallNodes) {
27
+ const callExpr = node.parent;
28
+ if (!callExpr)
29
+ continue;
30
+ const startPos = callExpr.getStart();
31
+ const endPos = callExpr.getEnd();
32
+ const callText = configContents.substring(startPos, endPos);
33
+ // Skip if useLegacyHtmlPlugin is already present
34
+ if (callText.includes('useLegacyHtmlPlugin')) {
35
+ continue;
36
+ }
37
+ // If it's already withWeb({ ... }), add useLegacyHtmlPlugin: true to the options
38
+ if (callText.includes('{') && callText.includes('}')) {
39
+ const newCallText = callText.replace(/\{\s*/, '{ useLegacyHtmlPlugin: true,\n ');
40
+ newContents =
41
+ newContents.substring(0, startPos) +
42
+ newCallText +
43
+ newContents.substring(endPos);
44
+ }
45
+ else {
46
+ // If it's just withWeb(), replace with withWeb({ useLegacyHtmlPlugin: true })
47
+ const newCallText = 'withWeb({ useLegacyHtmlPlugin: true })';
48
+ newContents =
49
+ newContents.substring(0, startPos) +
50
+ newCallText +
51
+ newContents.substring(endPos);
52
+ }
53
+ }
54
+ if (newContents !== configContents) {
55
+ tree.write(configPath, newContents);
56
+ }
57
+ return;
58
+ }
59
+ // If no withWeb calls, check for withReact calls
60
+ const withReactCallNodes = (0, tsquery_1.tsquery)(ast, 'CallExpression > Identifier[name=withReact]');
61
+ if (withReactCallNodes.length === 0) {
62
+ return;
63
+ }
64
+ let newContents = configContents;
65
+ for (const node of withReactCallNodes) {
66
+ const callExpr = node.parent;
67
+ if (!callExpr)
68
+ continue;
69
+ const startPos = callExpr.getStart();
70
+ const endPos = callExpr.getEnd();
71
+ const callText = configContents.substring(startPos, endPos);
72
+ // Skip if useLegacyHtmlPlugin is already present
73
+ if (callText.includes('useLegacyHtmlPlugin')) {
74
+ continue;
75
+ }
76
+ // If it's already withReact({ ... }), add useLegacyHtmlPlugin: true to the options
77
+ if (callText.includes('{') && callText.includes('}')) {
78
+ const newCallText = callText.replace(/\{\s*/, '{ useLegacyHtmlPlugin: true,\n ');
79
+ newContents =
80
+ newContents.substring(0, startPos) +
81
+ newCallText +
82
+ newContents.substring(endPos);
83
+ }
84
+ else {
85
+ // If it's just withReact(), replace with withReact({ useLegacyHtmlPlugin: true })
86
+ const newCallText = 'withReact({ useLegacyHtmlPlugin: true })';
87
+ newContents =
88
+ newContents.substring(0, startPos) +
89
+ newCallText +
90
+ newContents.substring(endPos);
91
+ }
92
+ }
93
+ if (newContents !== configContents) {
94
+ tree.write(configPath, newContents);
95
+ }
15
96
  }
16
97
  function transformComposePlugins(tree, configPath, scope) {
17
98
  const configContents = tree.read(configPath, 'utf-8');
@@ -132,7 +132,6 @@ function applyNxIndependentConfig(options, config) {
132
132
  },
133
133
  }),
134
134
  ],
135
- runtimeChunk: false,
136
135
  concatenateModules: true,
137
136
  };
138
137
  config.stats = {
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.applyWebConfig = applyWebConfig;
4
4
  const core_1 = require("@rspack/core");
5
+ const write_index_html_plugin_1 = require("../write-index-html-plugin");
5
6
  const instantiate_script_plugins_1 = require("./instantiate-script-plugins");
6
7
  const path_1 = require("path");
7
8
  const hash_format_1 = require("./hash-format");
@@ -32,14 +33,27 @@ function applyWebConfig(options, config = {}, { useNormalizedEntry, } = {}) {
32
33
  plugins.push(...(0, instantiate_script_plugins_1.instantiateScriptPlugins)(options));
33
34
  }
34
35
  if (options.index && options.generateIndexHtml) {
35
- plugins.push(new core_1.HtmlRspackPlugin({
36
- template: options.index,
37
- sri: options.subresourceIntegrity ? 'sha256' : undefined,
38
- ...(options.baseHref ? { base: { href: options.baseHref } } : {}),
39
- ...(config.output?.scriptType === 'module'
40
- ? { scriptLoading: 'module' }
41
- : {}),
42
- }));
36
+ if (options.useLegacyHtmlPlugin) {
37
+ plugins.push(new write_index_html_plugin_1.WriteIndexHtmlPlugin({
38
+ indexPath: options.index,
39
+ outputPath: 'index.html',
40
+ baseHref: typeof options.baseHref === 'string' ? options.baseHref : undefined,
41
+ sri: options.subresourceIntegrity,
42
+ scripts: options.scripts,
43
+ styles: options.styles,
44
+ crossOrigin: config.output?.scriptType === 'module' ? 'anonymous' : undefined,
45
+ }));
46
+ }
47
+ else {
48
+ plugins.push(new core_1.HtmlRspackPlugin({
49
+ template: options.index,
50
+ sri: options.subresourceIntegrity ? 'sha256' : undefined,
51
+ ...(options.baseHref ? { base: { href: options.baseHref } } : {}),
52
+ ...(config.output?.scriptType === 'module'
53
+ ? { scriptLoading: 'module' }
54
+ : {}),
55
+ }));
56
+ }
43
57
  }
44
58
  const minimizer = [];
45
59
  if (isProd && stylesOptimization) {
@@ -233,6 +233,10 @@ export interface NxAppRspackPluginOptions {
233
233
  * Whether to rebase absolute path for assets in postcss cli resources.
234
234
  */
235
235
  rebaseRootRelative?: boolean;
236
+ /**
237
+ * Use the legacy WriteIndexHtmlPlugin instead of the built-in HtmlRspackPlugin.
238
+ */
239
+ useLegacyHtmlPlugin?: boolean;
236
240
  }
237
241
  export interface NormalizedNxAppRspackPluginOptions extends NxAppRspackPluginOptions {
238
242
  projectName: string;
@@ -244,4 +248,5 @@ export interface NormalizedNxAppRspackPluginOptions extends NxAppRspackPluginOpt
244
248
  projectGraph: ProjectGraph;
245
249
  outputFileName: string;
246
250
  assets: AssetGlobPattern[];
251
+ useLegacyHtmlPlugin: boolean;
247
252
  }
@@ -65,6 +65,7 @@ function normalizeOptions(options) {
65
65
  extractCss: combinedPluginAndMaybeExecutorOptions.extractCss ?? true,
66
66
  fileReplacements: normalizeFileReplacements(devkit_1.workspaceRoot, combinedPluginAndMaybeExecutorOptions.fileReplacements),
67
67
  generateIndexHtml: combinedPluginAndMaybeExecutorOptions.generateIndexHtml ?? true,
68
+ useLegacyHtmlPlugin: combinedPluginAndMaybeExecutorOptions.useLegacyHtmlPlugin ?? false,
68
69
  main: combinedPluginAndMaybeExecutorOptions.main,
69
70
  namedChunks: combinedPluginAndMaybeExecutorOptions.namedChunks ?? !isProd,
70
71
  optimization: combinedPluginAndMaybeExecutorOptions.optimization ?? isProd,
@@ -0,0 +1,22 @@
1
+ import { Compiler } from '@rspack/core';
2
+ import { ExtraEntryPoint } from '../utils/model';
3
+ export interface WriteIndexHtmlOptions {
4
+ indexPath: string;
5
+ outputPath: string;
6
+ baseHref?: string;
7
+ deployUrl?: string;
8
+ sri?: boolean;
9
+ scripts?: ExtraEntryPoint[];
10
+ styles?: ExtraEntryPoint[];
11
+ crossOrigin?: 'none' | 'anonymous' | 'use-credentials';
12
+ }
13
+ export declare class WriteIndexHtmlPlugin {
14
+ private readonly options;
15
+ constructor(options: WriteIndexHtmlOptions);
16
+ apply(compiler: Compiler): void;
17
+ private getEmittedFiles;
18
+ private stripBom;
19
+ private augmentIndexHtml;
20
+ private generateSriAttributes;
21
+ private filterAndMapBuildFiles;
22
+ }
@@ -0,0 +1,250 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.WriteIndexHtmlPlugin = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const rspack = tslib_1.__importStar(require("@rspack/core"));
6
+ const crypto_1 = require("crypto");
7
+ const fs_1 = require("fs");
8
+ const interpolate_env_variables_to_index_1 = require("../utils/webpack/interpolate-env-variables-to-index");
9
+ const package_chunk_sort_1 = require("../utils/webpack/package-chunk-sort");
10
+ const path_1 = require("path");
11
+ const parse5 = require('parse5');
12
+ class WriteIndexHtmlPlugin {
13
+ constructor(options) {
14
+ this.options = options;
15
+ }
16
+ apply(compiler) {
17
+ const { outputPath, indexPath, baseHref, deployUrl, sri = false, scripts = [], styles = [], crossOrigin, } = this.options;
18
+ compiler.hooks.thisCompilation.tap('WriteIndexHtmlPlugin', (compilation) => {
19
+ compilation.hooks.processAssets.tap({
20
+ name: 'WriteIndexHtmlPlugin',
21
+ // After minification and sourcemaps are done
22
+ stage: rspack.Compilation.PROCESS_ASSETS_STAGE_OPTIMIZE_INLINE,
23
+ }, () => {
24
+ const moduleFiles = this.getEmittedFiles(compilation);
25
+ const files = moduleFiles.filter((x) => x.extension === '.css');
26
+ let content = (0, fs_1.readFileSync)(indexPath).toString();
27
+ content = this.stripBom(content);
28
+ compilation.assets[outputPath] = this.augmentIndexHtml({
29
+ input: outputPath,
30
+ inputContent: (0, interpolate_env_variables_to_index_1.interpolateEnvironmentVariablesToIndex)(content, deployUrl),
31
+ baseHref,
32
+ deployUrl,
33
+ crossOrigin,
34
+ sri,
35
+ entrypoints: (0, package_chunk_sort_1.generateEntryPoints)({ scripts, styles }),
36
+ files: this.filterAndMapBuildFiles(files, ['.js', '.css']),
37
+ moduleFiles: this.filterAndMapBuildFiles(moduleFiles, ['.js']),
38
+ loadOutputFile: (filePath) => compilation.assets[filePath].source().toString(),
39
+ });
40
+ });
41
+ });
42
+ }
43
+ getEmittedFiles(compilation) {
44
+ const files = [];
45
+ // adds all chunks to the list of emitted files such as lazy loaded modules
46
+ for (const chunk of compilation.chunks) {
47
+ for (const file of chunk.files) {
48
+ files.push({
49
+ // The id is guaranteed to exist at this point in the compilation process
50
+ id: chunk.id.toString(),
51
+ name: chunk.name,
52
+ file,
53
+ extension: (0, path_1.extname)(file),
54
+ initial: chunk.isOnlyInitial(),
55
+ });
56
+ }
57
+ }
58
+ // other all files
59
+ for (const file of Object.keys(compilation.assets)) {
60
+ files.push({
61
+ file,
62
+ extension: (0, path_1.extname)(file),
63
+ initial: false,
64
+ asset: true,
65
+ });
66
+ }
67
+ // dedupe
68
+ return files.filter(({ file, name }, index) => files.findIndex((f) => f.file === file && (!name || name === f.name)) === index);
69
+ }
70
+ stripBom(data) {
71
+ return data.replace(/^\uFEFF/, '');
72
+ }
73
+ augmentIndexHtml(params) {
74
+ const { loadOutputFile, files, moduleFiles = [], entrypoints } = params;
75
+ let { crossOrigin = 'none' } = params;
76
+ if (params.sri && crossOrigin === 'none') {
77
+ crossOrigin = 'anonymous';
78
+ }
79
+ const stylesheets = new Set();
80
+ const scripts = new Set();
81
+ // Sort files in the order we want to insert them by entrypoint and dedupes duplicates
82
+ const mergedFiles = [...moduleFiles, ...files];
83
+ for (const entrypoint of entrypoints) {
84
+ for (const { extension, file, name } of mergedFiles) {
85
+ if (name !== entrypoint) {
86
+ continue;
87
+ }
88
+ switch (extension) {
89
+ case '.js':
90
+ scripts.add(file);
91
+ break;
92
+ case '.css':
93
+ stylesheets.add(file);
94
+ break;
95
+ }
96
+ }
97
+ }
98
+ // Find the head and body elements
99
+ const treeAdapter = parse5.treeAdapters.default;
100
+ const document = parse5.parse(params.inputContent, {
101
+ treeAdapter,
102
+ locationInfo: true,
103
+ });
104
+ let headElement;
105
+ let bodyElement;
106
+ for (const docChild of document.childNodes) {
107
+ if (docChild.tagName === 'html') {
108
+ for (const htmlChild of docChild.childNodes) {
109
+ if (htmlChild.tagName === 'head') {
110
+ headElement = htmlChild;
111
+ }
112
+ else if (htmlChild.tagName === 'body') {
113
+ bodyElement = htmlChild;
114
+ }
115
+ }
116
+ }
117
+ }
118
+ if (!headElement || !bodyElement) {
119
+ throw new Error('Missing head and/or body elements');
120
+ }
121
+ // Determine script insertion point
122
+ let scriptInsertionPoint;
123
+ if (bodyElement.__location && bodyElement.__location.endTag) {
124
+ scriptInsertionPoint = bodyElement.__location.endTag.startOffset;
125
+ }
126
+ else {
127
+ // Less accurate fallback
128
+ // parse5 4.x does not provide locations if malformed html is present
129
+ scriptInsertionPoint = params.inputContent.indexOf('</body>');
130
+ }
131
+ let styleInsertionPoint;
132
+ if (headElement.__location && headElement.__location.endTag) {
133
+ styleInsertionPoint = headElement.__location.endTag.startOffset;
134
+ }
135
+ else {
136
+ // Less accurate fallback
137
+ // parse5 4.x does not provide locations if malformed html is present
138
+ styleInsertionPoint = params.inputContent.indexOf('</head>');
139
+ }
140
+ // Inject into the html
141
+ const indexSource = new rspack.sources.ReplaceSource(new rspack.sources.RawSource(params.inputContent), params.input);
142
+ let scriptElements = '';
143
+ for (const script of scripts) {
144
+ const attrs = [
145
+ { name: 'src', value: (params.deployUrl || '') + script },
146
+ ];
147
+ if (crossOrigin !== 'none') {
148
+ attrs.push({ name: 'crossorigin', value: crossOrigin });
149
+ }
150
+ // We want to include nomodule or module when a file is not common amongs all
151
+ // such as runtime.js
152
+ const scriptPredictor = ({ file, }) => file === script;
153
+ if (!files.some(scriptPredictor)) {
154
+ // in some cases for differential loading file with the same name is avialable in both
155
+ // nomodule and module such as scripts.js
156
+ // we shall not add these attributes if that's the case
157
+ const isModuleType = moduleFiles.some(scriptPredictor);
158
+ if (isModuleType) {
159
+ attrs.push({ name: 'type', value: 'module' });
160
+ }
161
+ else {
162
+ attrs.push({ name: 'defer', value: null });
163
+ }
164
+ }
165
+ else {
166
+ attrs.push({ name: 'type', value: 'module' });
167
+ }
168
+ if (params.sri) {
169
+ const content = loadOutputFile(script);
170
+ attrs.push(...this.generateSriAttributes(content));
171
+ }
172
+ const attributes = attrs
173
+ .map((attr) => attr.value === null ? attr.name : `${attr.name}="${attr.value}"`)
174
+ .join(' ');
175
+ scriptElements += `<script ${attributes}></script>`;
176
+ }
177
+ indexSource.insert(scriptInsertionPoint, scriptElements);
178
+ // Adjust base href if specified
179
+ if (typeof params.baseHref == 'string') {
180
+ let baseElement;
181
+ for (const headChild of headElement.childNodes) {
182
+ if (headChild.tagName === 'base') {
183
+ baseElement = headChild;
184
+ }
185
+ }
186
+ const baseFragment = treeAdapter.createDocumentFragment();
187
+ if (!baseElement) {
188
+ baseElement = treeAdapter.createElement('base', undefined, [
189
+ { name: 'href', value: params.baseHref },
190
+ ]);
191
+ treeAdapter.appendChild(baseFragment, baseElement);
192
+ indexSource.insert(headElement.__location.startTag.endOffset, parse5.serialize(baseFragment, { treeAdapter }));
193
+ }
194
+ else {
195
+ let hrefAttribute;
196
+ for (const attribute of baseElement.attrs) {
197
+ if (attribute.name === 'href') {
198
+ hrefAttribute = attribute;
199
+ }
200
+ }
201
+ if (hrefAttribute) {
202
+ hrefAttribute.value = params.baseHref;
203
+ }
204
+ else {
205
+ baseElement.attrs.push({ name: 'href', value: params.baseHref });
206
+ }
207
+ treeAdapter.appendChild(baseFragment, baseElement);
208
+ indexSource.replace(baseElement.__location.startOffset, baseElement.__location.endOffset, parse5.serialize(baseFragment, { treeAdapter }));
209
+ }
210
+ }
211
+ const styleElements = treeAdapter.createDocumentFragment();
212
+ for (const stylesheet of stylesheets) {
213
+ const attrs = [
214
+ { name: 'rel', value: 'stylesheet' },
215
+ { name: 'href', value: (params.deployUrl || '') + stylesheet },
216
+ ];
217
+ if (crossOrigin !== 'none') {
218
+ attrs.push({ name: 'crossorigin', value: crossOrigin });
219
+ }
220
+ if (params.sri) {
221
+ const content = loadOutputFile(stylesheet);
222
+ attrs.push(...this.generateSriAttributes(content));
223
+ }
224
+ const element = treeAdapter.createElement('link', undefined, attrs);
225
+ treeAdapter.appendChild(styleElements, element);
226
+ }
227
+ indexSource.insert(styleInsertionPoint, parse5.serialize(styleElements, { treeAdapter }));
228
+ return indexSource;
229
+ }
230
+ generateSriAttributes(content) {
231
+ const algo = 'sha384';
232
+ const hash = (0, crypto_1.createHash)(algo).update(content, 'utf8').digest('base64');
233
+ return [{ name: 'integrity', value: `${algo}-${hash}` }];
234
+ }
235
+ filterAndMapBuildFiles(files, extensionFilter) {
236
+ const filteredFiles = [];
237
+ // This test excludes files generated by HMR (e.g. main.hot-update.js).
238
+ const hotUpdateAsset = /hot-update\.[cm]?js$/;
239
+ for (const { file, name, extension, initial } of files) {
240
+ if (name &&
241
+ initial &&
242
+ extensionFilter.includes(extension) &&
243
+ !hotUpdateAsset.test(file)) {
244
+ filteredFiles.push({ file, extension, name });
245
+ }
246
+ }
247
+ return filteredFiles;
248
+ }
249
+ }
250
+ exports.WriteIndexHtmlPlugin = WriteIndexHtmlPlugin;
@@ -0,0 +1 @@
1
+ export declare function interpolateEnvironmentVariablesToIndex(contents: string, deployUrl?: string): string;
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.interpolateEnvironmentVariablesToIndex = interpolateEnvironmentVariablesToIndex;
4
+ function interpolateEnvironmentVariablesToIndex(contents, deployUrl) {
5
+ const environmentVariables = getClientEnvironment(deployUrl || '');
6
+ return interpolateEnvironmentVariables(contents, environmentVariables);
7
+ }
8
+ const NX_PREFIX = /^NX_PUBLIC_/i;
9
+ function isNxEnvironmentKey(x) {
10
+ return NX_PREFIX.test(x);
11
+ }
12
+ function getClientEnvironment(deployUrl) {
13
+ return Object.keys(process.env)
14
+ .filter(isNxEnvironmentKey)
15
+ .reduce((env, key) => {
16
+ env[key] = process.env[key];
17
+ return env;
18
+ }, {
19
+ NODE_ENV: process.env.NODE_ENV || 'development',
20
+ DEPLOY_URL: deployUrl || process.env.DEPLOY_URL || '',
21
+ });
22
+ }
23
+ function interpolateEnvironmentVariables(documentContents, environmentVariables) {
24
+ let temp = documentContents;
25
+ for (const [key, value] of Object.entries(environmentVariables)) {
26
+ temp = temp.replace(new RegExp(`%${key}%`, 'g'), value);
27
+ }
28
+ return temp;
29
+ }
@@ -0,0 +1,2 @@
1
+ import { ExtraEntryPoint, NormalizedEntryPoint } from '../model';
2
+ export declare function normalizeExtraEntryPoints(extraEntryPoints: ExtraEntryPoint[], defaultBundleName: string): NormalizedEntryPoint[];
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.normalizeExtraEntryPoints = normalizeExtraEntryPoints;
4
+ function normalizeExtraEntryPoints(extraEntryPoints, defaultBundleName) {
5
+ return extraEntryPoints.map((entry) => {
6
+ let normalizedEntry;
7
+ if (typeof entry === 'string') {
8
+ normalizedEntry = {
9
+ input: entry,
10
+ inject: true,
11
+ bundleName: defaultBundleName,
12
+ };
13
+ }
14
+ else {
15
+ const { inject = true, ...newEntry } = entry;
16
+ let bundleName;
17
+ if (entry.bundleName) {
18
+ bundleName = entry.bundleName;
19
+ }
20
+ else {
21
+ bundleName = defaultBundleName;
22
+ }
23
+ normalizedEntry = { ...newEntry, bundleName };
24
+ }
25
+ return normalizedEntry;
26
+ });
27
+ }
@@ -0,0 +1,5 @@
1
+ import { ExtraEntryPoint } from '../model';
2
+ export declare function generateEntryPoints(appConfig: {
3
+ styles: ExtraEntryPoint[];
4
+ scripts: ExtraEntryPoint[];
5
+ }): string[];
@@ -0,0 +1,30 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.generateEntryPoints = generateEntryPoints;
4
+ const normalize_entry_1 = require("./normalize-entry");
5
+ function generateEntryPoints(appConfig) {
6
+ // Add all styles/scripts, except lazy-loaded ones.
7
+ const extraEntryPoints = (extraEntryPoints, defaultBundleName) => {
8
+ const entryPoints = (0, normalize_entry_1.normalizeExtraEntryPoints)(extraEntryPoints, defaultBundleName).map((entry) => entry.bundleName);
9
+ // remove duplicates
10
+ return [...new Set(entryPoints)];
11
+ };
12
+ const styleEntryPoints = appConfig.styles.filter((style) => !(typeof style !== 'string' && !style.inject));
13
+ const scriptEntryPoints = appConfig.scripts.filter((script) => !(typeof script !== 'string' && !script.inject));
14
+ const entryPoints = [
15
+ 'runtime',
16
+ 'polyfills',
17
+ 'sw-register',
18
+ ...extraEntryPoints(styleEntryPoints, 'styles'),
19
+ ...extraEntryPoints(scriptEntryPoints, 'scripts'),
20
+ 'vendor',
21
+ 'main',
22
+ ];
23
+ const duplicates = [
24
+ ...new Set(entryPoints.filter((x) => entryPoints.indexOf(x) !== entryPoints.lastIndexOf(x))),
25
+ ];
26
+ if (duplicates.length > 0) {
27
+ throw new Error(`Multiple bundles have been named the same: '${duplicates.join(`', '`)}'.`);
28
+ }
29
+ return entryPoints;
30
+ }
@@ -26,6 +26,7 @@ function withNx(pluginOptions = {}) {
26
26
  targetName: context.targetName,
27
27
  configurationName: context.configurationName,
28
28
  projectGraph: context.projectGraph,
29
+ useLegacyHtmlPlugin: pluginOptions.useLegacyHtmlPlugin ?? false,
29
30
  }, config);
30
31
  processed.add(config);
31
32
  return config;
@@ -18,5 +18,9 @@ export interface WithWebOptions {
18
18
  };
19
19
  cssModules?: boolean;
20
20
  ssr?: boolean;
21
+ /**
22
+ * Use the legacy WriteIndexHtmlPlugin instead of the built-in HtmlRspackPlugin.
23
+ */
24
+ useLegacyHtmlPlugin?: boolean;
21
25
  }
22
26
  export declare function withWeb(pluginOptions?: WithWebOptions): (config: Configuration, { options, context }: NxRspackExecutionContext) => Configuration;
@@ -16,6 +16,7 @@ function withWeb(pluginOptions = {}) {
16
16
  targetName: context.targetName,
17
17
  configurationName: context.configurationName,
18
18
  projectGraph: context.projectGraph,
19
+ useLegacyHtmlPlugin: pluginOptions.useLegacyHtmlPlugin ?? false,
19
20
  }, config);
20
21
  processed.add(config);
21
22
  return config;