@octanejs/rsbuild-plugin 0.1.48 → 0.1.50
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 +5 -5
- package/src/build.js +42 -0
- package/src/dev-server.js +49 -6
- package/src/html.js +36 -0
- package/src/index.js +35 -19
- package/types/index.d.ts +7 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@octanejs/rsbuild-plugin",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.50",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
@@ -48,12 +48,12 @@
|
|
|
48
48
|
}
|
|
49
49
|
},
|
|
50
50
|
"dependencies": {
|
|
51
|
-
"@octanejs/app-core": "0.0.
|
|
52
|
-
"@octanejs/rspack-plugin": "0.1.
|
|
51
|
+
"@octanejs/app-core": "0.0.51",
|
|
52
|
+
"@octanejs/rspack-plugin": "0.1.50"
|
|
53
53
|
},
|
|
54
54
|
"peerDependencies": {
|
|
55
55
|
"@rsbuild/core": "^2.0.0",
|
|
56
|
-
"octane": "^0.
|
|
56
|
+
"octane": "^0.2.13"
|
|
57
57
|
},
|
|
58
58
|
"devDependencies": {
|
|
59
59
|
"@rsbuild/core": "^2.1.6",
|
|
@@ -62,6 +62,6 @@
|
|
|
62
62
|
"@types/node": "^24.13.3",
|
|
63
63
|
"playwright": "^1.61.1",
|
|
64
64
|
"vitest": "^4.1.10",
|
|
65
|
-
"octane": "0.2.
|
|
65
|
+
"octane": "0.2.13"
|
|
66
66
|
}
|
|
67
67
|
}
|
package/src/build.js
CHANGED
|
@@ -10,6 +10,8 @@ import path from 'node:path';
|
|
|
10
10
|
* root: string,
|
|
11
11
|
* config: import('@octanejs/app-core').ResolvedOctaneConfig,
|
|
12
12
|
* assetMapFilename?: string,
|
|
13
|
+
* clientBuildFilename?: string,
|
|
14
|
+
* independentHydrationManifestFilename?: string,
|
|
13
15
|
* log?: (message: string) => void,
|
|
14
16
|
* }} options
|
|
15
17
|
*/
|
|
@@ -19,11 +21,18 @@ export async function finalizeOctaneRsbuildOutput(options) {
|
|
|
19
21
|
const clientDir = path.resolve(root, outDir, 'client');
|
|
20
22
|
const serverDir = path.resolve(root, outDir, 'server');
|
|
21
23
|
const assetMapFilename = options.assetMapFilename ?? 'octane-client-assets.json';
|
|
24
|
+
const clientBuildFilename = options.clientBuildFilename ?? 'octane-client-build.json';
|
|
25
|
+
const independentHydrationManifestFilename =
|
|
26
|
+
options.independentHydrationManifestFilename ?? 'octane-independent-hydration.json';
|
|
22
27
|
const log = options.log ?? (() => {});
|
|
23
28
|
const clientHtml = path.join(clientDir, 'index.html');
|
|
24
29
|
const serverHtml = path.join(serverDir, 'index.html');
|
|
25
30
|
const clientAssetMap = path.join(clientDir, assetMapFilename);
|
|
26
31
|
const serverAssetMap = path.join(serverDir, assetMapFilename);
|
|
32
|
+
const clientBuildPath = path.join(clientDir, clientBuildFilename);
|
|
33
|
+
const serverBuildPath = path.join(serverDir, clientBuildFilename);
|
|
34
|
+
const clientIndependentHydration = path.join(clientDir, independentHydrationManifestFilename);
|
|
35
|
+
const serverIndependentHydration = path.join(serverDir, independentHydrationManifestFilename);
|
|
27
36
|
const serverEntry = path.join(serverDir, 'entry.js');
|
|
28
37
|
|
|
29
38
|
if (!fs.existsSync(clientHtml)) {
|
|
@@ -37,12 +46,45 @@ export async function finalizeOctaneRsbuildOutput(options) {
|
|
|
37
46
|
if (!fs.existsSync(serverEntry)) {
|
|
38
47
|
throw new Error(`[@octanejs/rsbuild-plugin] Server entry was not emitted at ${serverEntry}.`);
|
|
39
48
|
}
|
|
49
|
+
if (!fs.existsSync(clientBuildPath)) {
|
|
50
|
+
throw new Error(
|
|
51
|
+
`[@octanejs/rsbuild-plugin] Client build metadata was not emitted at ${clientBuildPath}.`,
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
const clientBuild = JSON.parse(fs.readFileSync(clientBuildPath, 'utf8'));
|
|
55
|
+
if (
|
|
56
|
+
clientBuild?.version !== 1 ||
|
|
57
|
+
typeof clientBuild.buildId !== 'string' ||
|
|
58
|
+
!clientBuild.buildId ||
|
|
59
|
+
clientBuild.mode !== 'production' ||
|
|
60
|
+
typeof clientBuild.capabilities?.independentHydration !== 'boolean'
|
|
61
|
+
) {
|
|
62
|
+
throw new Error('[@octanejs/rsbuild-plugin] Invalid production client build metadata.');
|
|
63
|
+
}
|
|
64
|
+
if (fs.existsSync(clientIndependentHydration)) {
|
|
65
|
+
const widgets = JSON.parse(fs.readFileSync(clientIndependentHydration, 'utf8'));
|
|
66
|
+
if (widgets.buildId !== clientBuild.buildId) {
|
|
67
|
+
throw new Error(
|
|
68
|
+
'[@octanejs/rsbuild-plugin] Independent hydration and client build metadata disagree.',
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
} else if (clientBuild.capabilities.independentHydration) {
|
|
72
|
+
throw new Error(
|
|
73
|
+
'[@octanejs/rsbuild-plugin] Client build is missing independent hydration metadata.',
|
|
74
|
+
);
|
|
75
|
+
}
|
|
40
76
|
|
|
41
77
|
fs.mkdirSync(serverDir, { recursive: true });
|
|
42
78
|
fs.rmSync(serverHtml, { force: true });
|
|
43
79
|
fs.rmSync(serverAssetMap, { force: true });
|
|
80
|
+
fs.rmSync(serverBuildPath, { force: true });
|
|
44
81
|
fs.renameSync(clientHtml, serverHtml);
|
|
45
82
|
fs.renameSync(clientAssetMap, serverAssetMap);
|
|
83
|
+
fs.renameSync(clientBuildPath, serverBuildPath);
|
|
84
|
+
fs.rmSync(serverIndependentHydration, { force: true });
|
|
85
|
+
if (fs.existsSync(clientIndependentHydration)) {
|
|
86
|
+
fs.renameSync(clientIndependentHydration, serverIndependentHydration);
|
|
87
|
+
}
|
|
46
88
|
|
|
47
89
|
log(`Server build complete: ${path.relative(root, serverEntry)}`);
|
|
48
90
|
if (options.config.adapter?.serverTarget !== 'webworker') {
|
package/src/dev-server.js
CHANGED
|
@@ -5,6 +5,8 @@ import { nodeRequestToWebRequest, sendWebResponse } from '@octanejs/app-core/nod
|
|
|
5
5
|
|
|
6
6
|
import { isRsbuildOwnedUrl } from './html.js';
|
|
7
7
|
|
|
8
|
+
const INDEPENDENT_HYDRATION_MANIFEST_FILENAME = 'octane-independent-hydration.json';
|
|
9
|
+
|
|
8
10
|
/**
|
|
9
11
|
* @typedef {{
|
|
10
12
|
* manifest: import('@octanejs/app-core/production').ServerManifest,
|
|
@@ -27,6 +29,35 @@ function collectAssetPaths(stats) {
|
|
|
27
29
|
return paths;
|
|
28
30
|
}
|
|
29
31
|
|
|
32
|
+
/** @param {import('@rspack/core').Stats} stats */
|
|
33
|
+
function readIndependentHydrationManifest(stats) {
|
|
34
|
+
const asset = stats.compilation.getAsset?.(INDEPENDENT_HYDRATION_MANIFEST_FILENAME);
|
|
35
|
+
if (!asset) return null;
|
|
36
|
+
const source = asset.source.source();
|
|
37
|
+
return JSON.parse(typeof source === 'string' ? source : Buffer.from(source).toString('utf8'));
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** @param {import('@rspack/core').Stats} stats */
|
|
41
|
+
function readClientBuild(stats) {
|
|
42
|
+
const asset = stats.compilation.getAsset?.('octane-client-build.json');
|
|
43
|
+
if (!asset) throw new Error('The Octane client build metadata is missing.');
|
|
44
|
+
const source = asset.source.source();
|
|
45
|
+
const metadata = JSON.parse(
|
|
46
|
+
typeof source === 'string' ? source : Buffer.from(source).toString('utf8'),
|
|
47
|
+
);
|
|
48
|
+
if (
|
|
49
|
+
metadata?.version !== 1 ||
|
|
50
|
+
metadata.buildId !== stats.hash ||
|
|
51
|
+
metadata.mode !== 'development' ||
|
|
52
|
+
typeof metadata.capabilities?.independentHydration !== 'boolean'
|
|
53
|
+
) {
|
|
54
|
+
throw new Error(
|
|
55
|
+
'The Octane client build metadata is incompatible with this development compilation.',
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
return metadata;
|
|
59
|
+
}
|
|
60
|
+
|
|
30
61
|
/**
|
|
31
62
|
* Create the early Connect middleware used by the Rsbuild Environment API.
|
|
32
63
|
* Asset/internal requests fall through to Rsbuild; matched app/RPC requests
|
|
@@ -54,7 +85,7 @@ export function createOctaneDevMiddleware(options) {
|
|
|
54
85
|
|
|
55
86
|
let assetHash = '';
|
|
56
87
|
let assetPaths = new Set();
|
|
57
|
-
/** @type {WeakMap<object, { html: string, handler: (request: Request) => Promise<Response> }>} */
|
|
88
|
+
/** @type {WeakMap<object, { html: string, assetHash: string, handler: (request: Request) => Promise<Response> }>} */
|
|
58
89
|
const handlerCache = new WeakMap();
|
|
59
90
|
|
|
60
91
|
return async function octaneDevMiddleware(request, response, next) {
|
|
@@ -69,6 +100,7 @@ export function createOctaneDevMiddleware(options) {
|
|
|
69
100
|
return;
|
|
70
101
|
}
|
|
71
102
|
const clientStats = await clientApi.getStats();
|
|
103
|
+
if (clientStats.hasErrors()) throw new Error('The Octane client compilation failed.');
|
|
72
104
|
if (clientStats.hash !== assetHash) {
|
|
73
105
|
assetHash = clientStats.hash ?? '';
|
|
74
106
|
assetPaths = collectAssetPaths(clientStats);
|
|
@@ -92,13 +124,24 @@ export function createOctaneDevMiddleware(options) {
|
|
|
92
124
|
|
|
93
125
|
const html = await clientApi.getTransformedHtml(options.clientEntry);
|
|
94
126
|
let cached = handlerCache.get(typedBundle.manifest);
|
|
95
|
-
if (!cached || cached.html !== html) {
|
|
127
|
+
if (!cached || cached.html !== html || cached.assetHash !== assetHash) {
|
|
128
|
+
const independentHydration = readIndependentHydrationManifest(clientStats);
|
|
129
|
+
const clientBuild = readClientBuild(clientStats);
|
|
130
|
+
if (independentHydration !== null && independentHydration.buildId !== clientBuild.buildId) {
|
|
131
|
+
throw new Error(
|
|
132
|
+
'The Octane independent widget metadata belongs to another client build.',
|
|
133
|
+
);
|
|
134
|
+
}
|
|
96
135
|
cached = {
|
|
97
136
|
html,
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
137
|
+
assetHash,
|
|
138
|
+
handler: createHandler(
|
|
139
|
+
{ ...typedBundle.manifest, independentHydration, clientBuild },
|
|
140
|
+
{
|
|
141
|
+
...typedBundle.rendererDeps,
|
|
142
|
+
htmlTemplate: html,
|
|
143
|
+
},
|
|
144
|
+
),
|
|
102
145
|
};
|
|
103
146
|
handlerCache.set(typedBundle.manifest, cached);
|
|
104
147
|
}
|
package/src/html.js
CHANGED
|
@@ -9,6 +9,42 @@ function escapeRegExp(value) {
|
|
|
9
9
|
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
+
/**
|
|
13
|
+
* A classic bootstrap can run after the streamed shell only if its native
|
|
14
|
+
* entrypoint needs no deferred prerequisite and starts no other entry module.
|
|
15
|
+
* Keep the bundler's runtime/chunk ownership intact; reject unsafe overrides.
|
|
16
|
+
*
|
|
17
|
+
* @param {import('@rspack/core').Compilation} compilation
|
|
18
|
+
* @param {string} name
|
|
19
|
+
* @param {string} clientEntry
|
|
20
|
+
*/
|
|
21
|
+
export function assertEarlyHydrationEntry(compilation, name, clientEntry) {
|
|
22
|
+
const entrypoint = compilation.entrypoints.get(name);
|
|
23
|
+
const chunk = entrypoint?.getEntrypointChunk();
|
|
24
|
+
const files = entrypoint?.getFiles().filter((file) => /\.m?js(?:\?.*)?$/.test(file)) ?? [];
|
|
25
|
+
if (!chunk || files.length !== 1 || !chunk.files.has(files[0])) {
|
|
26
|
+
throw new Error(
|
|
27
|
+
'Octane early hydration requires one self-contained entry script. ' +
|
|
28
|
+
'Do not extract its runtime or initial JavaScript dependencies into deferred chunks.',
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
const entries = [...compilation.chunkGraph.getChunkEntryModulesIterable(chunk)];
|
|
32
|
+
const entry = entries[0];
|
|
33
|
+
const root = entry && 'rootModule' in entry ? (entry.rootModule ?? entry) : entry;
|
|
34
|
+
const resource =
|
|
35
|
+
root && typeof root === 'object' && 'resource' in root ? root.resource : undefined;
|
|
36
|
+
if (
|
|
37
|
+
entries.length !== 1 ||
|
|
38
|
+
typeof resource !== 'string' ||
|
|
39
|
+
fs.realpathSync(resource.split('?', 1)[0]) !== fs.realpathSync(clientEntry)
|
|
40
|
+
) {
|
|
41
|
+
throw new Error(
|
|
42
|
+
'Octane early hydration must own its entry module exclusively. ' +
|
|
43
|
+
'Keep user entries and pre-entry scripts separate from the generated hydration entry.',
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
12
48
|
/**
|
|
13
49
|
* Rsbuild has already injected the web entry by the time `modifyHTML` runs.
|
|
14
50
|
* Mark the concrete generated tag so the shared production runtime can add a
|
package/src/index.js
CHANGED
|
@@ -25,12 +25,15 @@ import {
|
|
|
25
25
|
write_project_generated_file,
|
|
26
26
|
} from '@octanejs/app-core/codegen';
|
|
27
27
|
import { OctaneRspackPlugin } from '@octanejs/rspack-plugin';
|
|
28
|
-
import {
|
|
28
|
+
import {
|
|
29
|
+
INDEPENDENT_HYDRATION_MANIFEST_FILENAME,
|
|
30
|
+
createOctaneCompiler,
|
|
31
|
+
} from 'octane/compiler/bundler';
|
|
29
32
|
|
|
30
33
|
import { finalizeOctaneRsbuildOutput } from './build.js';
|
|
31
34
|
import { OctaneClientAssetsPlugin } from './client-assets-plugin.js';
|
|
32
35
|
import { createOctaneDevMiddleware } from './dev-server.js';
|
|
33
|
-
import { markHydrationEntry } from './html.js';
|
|
36
|
+
import { assertEarlyHydrationEntry, markHydrationEntry } from './html.js';
|
|
34
37
|
import {
|
|
35
38
|
collectClientEntries,
|
|
36
39
|
discoverServerModules,
|
|
@@ -135,9 +138,8 @@ function hasRoutes(config) {
|
|
|
135
138
|
/**
|
|
136
139
|
* @param {import('@octanejs/app-core').ResolvedOctaneConfig} config
|
|
137
140
|
* @param {boolean | undefined} strongOverride
|
|
138
|
-
* @param {boolean | undefined} nativeReadsOverride
|
|
139
141
|
*/
|
|
140
|
-
function configSignature(config, strongOverride
|
|
142
|
+
function configSignature(config, strongOverride) {
|
|
141
143
|
return JSON.stringify({
|
|
142
144
|
build: config.build,
|
|
143
145
|
routes: config.router.routes.map((route) =>
|
|
@@ -157,7 +159,6 @@ function configSignature(config, strongOverride, nativeReadsOverride) {
|
|
|
157
159
|
compiler: {
|
|
158
160
|
renderers: config.compiler.renderers.signature,
|
|
159
161
|
strong: strongOverride ?? config.compiler.strong,
|
|
160
|
-
nativeReads: nativeReadsOverride ?? config.compiler.nativeReads,
|
|
161
162
|
},
|
|
162
163
|
});
|
|
163
164
|
}
|
|
@@ -237,9 +238,9 @@ function assertRootPublicPaths(config, clientEnvironment) {
|
|
|
237
238
|
* hmr?: boolean,
|
|
238
239
|
* parallel?: boolean | { maxWorkers?: number },
|
|
239
240
|
* cssModuleConstants?: import('@octanejs/rspack-plugin').OctaneRspackPluginOptions['cssModuleConstants'],
|
|
241
|
+
* textTypes?: import('@octanejs/rspack-plugin').OctaneRspackPluginOptions['textTypes'],
|
|
240
242
|
* profile?: boolean,
|
|
241
243
|
* strong?: boolean,
|
|
242
|
-
* nativeReads?: boolean,
|
|
243
244
|
* exclude?: string[],
|
|
244
245
|
* requireDirective?: boolean,
|
|
245
246
|
* clientEnvironment?: string,
|
|
@@ -248,9 +249,6 @@ function assertRootPublicPaths(config, clientEnvironment) {
|
|
|
248
249
|
* @returns {import('@rsbuild/core').RsbuildPlugin}
|
|
249
250
|
*/
|
|
250
251
|
export function pluginOctane(inlineOptions = {}) {
|
|
251
|
-
if (inlineOptions.nativeReads !== undefined && typeof inlineOptions.nativeReads !== 'boolean') {
|
|
252
|
-
throw new TypeError('[@octanejs/rsbuild-plugin] nativeReads must be a boolean.');
|
|
253
|
-
}
|
|
254
252
|
if (inlineOptions.strong !== undefined && typeof inlineOptions.strong !== 'boolean') {
|
|
255
253
|
throw new TypeError('[@octanejs/rsbuild-plugin] `strong` must be a boolean.');
|
|
256
254
|
}
|
|
@@ -272,7 +270,6 @@ export function pluginOctane(inlineOptions = {}) {
|
|
|
272
270
|
: null;
|
|
273
271
|
const initialConfig = initialLoaded?.config ?? null;
|
|
274
272
|
const strong = inlineOptions.strong ?? initialConfig?.compiler.strong;
|
|
275
|
-
const nativeReads = inlineOptions.nativeReads ?? initialConfig?.compiler.nativeReads;
|
|
276
273
|
const appEnabled = hasRoutes(initialConfig);
|
|
277
274
|
const buildTargetPlan =
|
|
278
275
|
initialConfig?.build.target === undefined
|
|
@@ -282,12 +279,11 @@ export function pluginOctane(inlineOptions = {}) {
|
|
|
282
279
|
? createOctaneCompiler({
|
|
283
280
|
root,
|
|
284
281
|
...(strong === undefined ? null : { strong }),
|
|
285
|
-
...(nativeReads === undefined ? null : { nativeReads }),
|
|
286
282
|
})
|
|
287
283
|
: null;
|
|
288
284
|
let currentConfig = initialConfig;
|
|
289
285
|
let lastConfigSignature = initialLoaded
|
|
290
|
-
? configSignature(initialLoaded.config, inlineOptions.strong
|
|
286
|
+
? configSignature(initialLoaded.config, inlineOptions.strong)
|
|
291
287
|
: '';
|
|
292
288
|
let configNeedsReload = false;
|
|
293
289
|
/** @type {import('@rsbuild/core').RsbuildDevServer | null} */
|
|
@@ -335,11 +331,7 @@ export function pluginOctane(inlineOptions = {}) {
|
|
|
335
331
|
throw error;
|
|
336
332
|
}
|
|
337
333
|
registerConfigDependencies(context, loaded);
|
|
338
|
-
const signature = configSignature(
|
|
339
|
-
loaded.config,
|
|
340
|
-
inlineOptions.strong,
|
|
341
|
-
inlineOptions.nativeReads,
|
|
342
|
-
);
|
|
334
|
+
const signature = configSignature(loaded.config, inlineOptions.strong);
|
|
343
335
|
if (lastConfigSignature && signature !== lastConfigSignature) configNeedsReload = true;
|
|
344
336
|
lastConfigSignature = signature;
|
|
345
337
|
currentConfig = loaded.config;
|
|
@@ -408,6 +400,18 @@ export function pluginOctane(inlineOptions = {}) {
|
|
|
408
400
|
server: { htmlFallback: false, historyApiFallback: false },
|
|
409
401
|
environments: {
|
|
410
402
|
[clientEnvironment]: {
|
|
403
|
+
// Keep the generated bootstrap self-contained by default, including
|
|
404
|
+
// downlevel helpers. Explicit splitting remains subject to the
|
|
405
|
+
// emitted-entry ownership check instead of being silently replaced.
|
|
406
|
+
...(watchedConfig.splitChunks !== false &&
|
|
407
|
+
Object.keys(watchedConfig.splitChunks ?? {}).length === 0 &&
|
|
408
|
+
watchedConfig.environments?.[clientEnvironment]?.splitChunks !== false &&
|
|
409
|
+
Object.keys(watchedConfig.environments?.[clientEnvironment]?.splitChunks ?? {})
|
|
410
|
+
.length === 0 &&
|
|
411
|
+
watchedConfig.performance?.chunkSplit === undefined &&
|
|
412
|
+
watchedConfig.environments?.[clientEnvironment]?.performance?.chunkSplit === undefined
|
|
413
|
+
? { splitChunks: { chunks: 'async' } }
|
|
414
|
+
: {}),
|
|
411
415
|
...targetConfig,
|
|
412
416
|
source: { entry: { [CLIENT_ENTRY_NAME]: clientEntryFile } },
|
|
413
417
|
html: { template },
|
|
@@ -491,6 +495,7 @@ export function pluginOctane(inlineOptions = {}) {
|
|
|
491
495
|
}));
|
|
492
496
|
return create_client_entry_source({
|
|
493
497
|
staticEntries: entries,
|
|
498
|
+
clientBuildIdExpression: '__webpack_hash__',
|
|
494
499
|
generatedBy: PLUGIN_NAME,
|
|
495
500
|
});
|
|
496
501
|
},
|
|
@@ -531,7 +536,11 @@ export function pluginOctane(inlineOptions = {}) {
|
|
|
531
536
|
rpcModulePaths: serverModules.ids,
|
|
532
537
|
...(webWorkerServer ? { mode: 'webworker' } : null),
|
|
533
538
|
...(production && !webWorkerServer
|
|
534
|
-
? {
|
|
539
|
+
? {
|
|
540
|
+
clientAssetMapFile: CLIENT_ASSET_MAP,
|
|
541
|
+
clientBuildFile: 'octane-client-build.json',
|
|
542
|
+
independentHydrationManifestFile: INDEPENDENT_HYDRATION_MANIFEST_FILENAME,
|
|
543
|
+
}
|
|
535
544
|
: { clientAssetMap: {} }),
|
|
536
545
|
resolveImport: (id) => resolveProjectModule(id, root),
|
|
537
546
|
configModuleId: localRequire.resolve('@octanejs/app-core/config'),
|
|
@@ -562,13 +571,16 @@ export function pluginOctane(inlineOptions = {}) {
|
|
|
562
571
|
new OctaneRspackPlugin({
|
|
563
572
|
root,
|
|
564
573
|
environment,
|
|
574
|
+
clientBuildMode: productionBuild ? 'production' : 'development',
|
|
565
575
|
transpile: false,
|
|
566
576
|
...(inlineOptions.parallel === undefined ? null : { parallel: inlineOptions.parallel }),
|
|
567
577
|
...(inlineOptions.cssModuleConstants === undefined
|
|
568
578
|
? null
|
|
569
579
|
: { cssModuleConstants: inlineOptions.cssModuleConstants }),
|
|
580
|
+
...(inlineOptions.textTypes === undefined
|
|
581
|
+
? null
|
|
582
|
+
: { textTypes: inlineOptions.textTypes }),
|
|
570
583
|
...(strong === undefined ? null : { strong }),
|
|
571
|
-
...(nativeReads === undefined ? null : { nativeReads }),
|
|
572
584
|
...(inlineOptions.hmr === undefined ? null : { hmr: inlineOptions.hmr }),
|
|
573
585
|
...(inlineOptions.profile === undefined
|
|
574
586
|
? null
|
|
@@ -654,6 +666,9 @@ export function pluginOctane(inlineOptions = {}) {
|
|
|
654
666
|
|
|
655
667
|
api.modifyHTML((html, context) => {
|
|
656
668
|
if (context.environment.name !== clientEnvironment) return html;
|
|
669
|
+
if (isProductionBuild()) {
|
|
670
|
+
assertEarlyHydrationEntry(context.compilation, CLIENT_ENTRY_NAME, clientEntryFile);
|
|
671
|
+
}
|
|
657
672
|
const entrypoint = context.compilation.entrypoints.get(CLIENT_ENTRY_NAME);
|
|
658
673
|
const entryFiles = entrypoint?.getEntrypointChunk().files ?? [];
|
|
659
674
|
return markHydrationEntry(html, entryFiles);
|
|
@@ -694,6 +709,7 @@ export function pluginOctane(inlineOptions = {}) {
|
|
|
694
709
|
root,
|
|
695
710
|
config: loaded.config,
|
|
696
711
|
assetMapFilename: CLIENT_ASSET_MAP,
|
|
712
|
+
independentHydrationManifestFilename: INDEPENDENT_HYDRATION_MANIFEST_FILENAME,
|
|
697
713
|
log: createLog(api.logger),
|
|
698
714
|
});
|
|
699
715
|
});
|
package/types/index.d.ts
CHANGED
|
@@ -10,6 +10,13 @@ export {
|
|
|
10
10
|
} from '@octanejs/app-core/config-loader';
|
|
11
11
|
|
|
12
12
|
export interface OctaneRsbuildPluginOptions {
|
|
13
|
+
/**
|
|
14
|
+
* @experimental TypeScript text proofs in one-shot production browser and
|
|
15
|
+
* server builds. Relative tsconfig paths resolve from the project root.
|
|
16
|
+
* Watch/HMR and development builds retain syntax-only classification.
|
|
17
|
+
* Requires the optional TypeScript peer; see the Rspack option for caching.
|
|
18
|
+
*/
|
|
19
|
+
textTypes?: OctaneRspackPluginOptions['textTypes'];
|
|
13
20
|
/**
|
|
14
21
|
* @experimental Fold authenticated JavaScript CSS-module exports in one-shot
|
|
15
22
|
* production builds. Uses the Rspack plugin's exact-source provider contract;
|
|
@@ -33,8 +40,6 @@ export interface OctaneRsbuildPluginOptions {
|
|
|
33
40
|
* in with `"use strong"`.
|
|
34
41
|
*/
|
|
35
42
|
strong?: boolean;
|
|
36
|
-
/** Experimental native signal reads in DOM client/server render scopes. */
|
|
37
|
-
nativeReads?: boolean;
|
|
38
43
|
/**
|
|
39
44
|
* Ad-hoc path fragments skipped by the plain TypeScript/JavaScript
|
|
40
45
|
* hook-slot pass. With `requireDirective`, excluded paths are exempt from
|