@octanejs/rspack-plugin 0.1.49 → 0.1.51
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 +3 -3
- package/src/loader.js +8 -0
- package/src/plugin.js +210 -4
- package/src/shared.js +20 -0
- package/src/streamed-signals-hmr.js +59 -0
- package/types/index.d.ts +22 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@octanejs/rspack-plugin",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.51",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
},
|
|
46
46
|
"peerDependencies": {
|
|
47
47
|
"@rspack/core": "^2.0.0",
|
|
48
|
-
"octane": "^0.
|
|
48
|
+
"octane": "^0.3.0"
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|
|
51
51
|
"@rspack/core": "^2.1.4",
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
"css-loader": "6.11.0",
|
|
55
55
|
"jsdom": "^30.0.1",
|
|
56
56
|
"vitest": "^4.1.10",
|
|
57
|
-
"octane": "0.
|
|
57
|
+
"octane": "0.3.0"
|
|
58
58
|
},
|
|
59
59
|
"scripts": {
|
|
60
60
|
"test": "vitest run --config vitest.config.js"
|
package/src/loader.js
CHANGED
|
@@ -121,6 +121,9 @@ export default function octaneLoader(source, inputSourceMap) {
|
|
|
121
121
|
root,
|
|
122
122
|
profile,
|
|
123
123
|
...(options.strong === undefined ? null : { strong: options.strong }),
|
|
124
|
+
...(options.knownAttributeSpreads === undefined
|
|
125
|
+
? null
|
|
126
|
+
: { knownAttributeSpreads: options.knownAttributeSpreads }),
|
|
124
127
|
...(options.exclude === undefined ? null : { exclude: options.exclude }),
|
|
125
128
|
...(compilerOptions.renderers === undefined
|
|
126
129
|
? null
|
|
@@ -205,11 +208,16 @@ export default function octaneLoader(source, inputSourceMap) {
|
|
|
205
208
|
}
|
|
206
209
|
setBuildInfo(this._module, {
|
|
207
210
|
canonicalId: canonicalModuleId(id, root),
|
|
211
|
+
resourceQuery: id.slice(cleanModuleId(id).length),
|
|
208
212
|
transformKind: result.kind,
|
|
213
|
+
...(result.streamedSignals === true ? { streamedSignals: true } : null),
|
|
209
214
|
serverRpc:
|
|
210
215
|
result.kind === 'compile' &&
|
|
211
216
|
(result.code.includes('_$__serverRpc(') ||
|
|
212
217
|
result.code.includes('export const _$_server_$_')),
|
|
218
|
+
...(Array.isArray(result.independentWidgets) && result.independentWidgets.length > 0
|
|
219
|
+
? { independentWidgets: result.independentWidgets }
|
|
220
|
+
: null),
|
|
213
221
|
...(result.universalRuntime === undefined
|
|
214
222
|
? null
|
|
215
223
|
: { universalRuntime: result.universalRuntime }),
|
package/src/plugin.js
CHANGED
|
@@ -5,10 +5,12 @@ import { dirname, isAbsolute, join, resolve } from 'node:path';
|
|
|
5
5
|
import { fileURLToPath } from 'node:url';
|
|
6
6
|
import {
|
|
7
7
|
CLIENT_REFERENCE_MANIFEST_FILENAME,
|
|
8
|
+
INDEPENDENT_HYDRATION_MANIFEST_FILENAME,
|
|
8
9
|
createClientReferenceManifest,
|
|
9
10
|
createOctaneCompiler,
|
|
10
11
|
} from 'octane/compiler/bundler';
|
|
11
12
|
import { installCssModuleConstants } from './css-module-constants.js';
|
|
13
|
+
import { createStreamedSignalHmrRuntimeModule } from './streamed-signals-hmr.js';
|
|
12
14
|
import {
|
|
13
15
|
getOctaneRspackBuildInfo,
|
|
14
16
|
inferRspackEnvironment,
|
|
@@ -182,6 +184,9 @@ function createDiscoveryCompiler(options, root, profile, specialization) {
|
|
|
182
184
|
root,
|
|
183
185
|
profile,
|
|
184
186
|
...(options.strong === undefined ? null : { strong: options.strong }),
|
|
187
|
+
...(options.knownAttributeSpreads === undefined
|
|
188
|
+
? null
|
|
189
|
+
: { knownAttributeSpreads: options.knownAttributeSpreads }),
|
|
185
190
|
...(options.exclude === undefined ? null : { exclude: options.exclude }),
|
|
186
191
|
...(renderers === undefined ? null : { renderers }),
|
|
187
192
|
...(universalRuntime === undefined ? null : { universalRuntime }),
|
|
@@ -234,16 +239,17 @@ function visitClientReferenceModules(
|
|
|
234
239
|
inheritedChunks,
|
|
235
240
|
visit,
|
|
236
241
|
seen = new Set(),
|
|
242
|
+
executableModule = module,
|
|
237
243
|
) {
|
|
238
244
|
if (!module || seen.has(module)) return;
|
|
239
245
|
seen.add(module);
|
|
240
246
|
const chunks = moduleChunks(compilation, module, inheritedChunks);
|
|
241
|
-
visit(module, chunks);
|
|
247
|
+
visit(module, chunks, executableModule);
|
|
242
248
|
for (const child of iterable(module.modules)) {
|
|
243
|
-
visitClientReferenceModules(compilation, child, chunks, visit, seen);
|
|
249
|
+
visitClientReferenceModules(compilation, child, chunks, visit, seen, module);
|
|
244
250
|
}
|
|
245
251
|
if (module.rootModule) {
|
|
246
|
-
visitClientReferenceModules(compilation, module.rootModule, chunks, visit, seen);
|
|
252
|
+
visitClientReferenceModules(compilation, module.rootModule, chunks, visit, seen, module);
|
|
247
253
|
}
|
|
248
254
|
}
|
|
249
255
|
|
|
@@ -279,6 +285,158 @@ function emitClientReferenceManifest(compiler, compilation) {
|
|
|
279
285
|
);
|
|
280
286
|
}
|
|
281
287
|
|
|
288
|
+
function emitIndependentHydrationManifest(compiler, compilation, mode) {
|
|
289
|
+
const modules = collectIndependentHydrationModules(compilation);
|
|
290
|
+
const widgets = {};
|
|
291
|
+
for (const { template, target } of independentHydrationTargets(modules)) {
|
|
292
|
+
const activationChunks = new Set(target.chunks);
|
|
293
|
+
for (const chunk of target.chunks) {
|
|
294
|
+
for (const referenced of iterable(chunk?.getAllReferencedChunks?.())) {
|
|
295
|
+
activationChunks.add(referenced);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
const files = [...activationChunks]
|
|
299
|
+
.flatMap((chunk) => [...iterable(chunk.files), ...iterable(chunk.auxiliaryFiles)])
|
|
300
|
+
.map(String);
|
|
301
|
+
widgets[template.boundaryId] = {
|
|
302
|
+
version: 1,
|
|
303
|
+
boundaryId: template.boundaryId,
|
|
304
|
+
// Rspack chunks are runtime payloads, not native module namespaces. The
|
|
305
|
+
// initial runtime module below owns the actual chunk-load + require closure.
|
|
306
|
+
moduleId: template.boundaryId,
|
|
307
|
+
exportName: template.exportName,
|
|
308
|
+
captureSchema: template.captureSchema,
|
|
309
|
+
hookSeed: template.hookSeed,
|
|
310
|
+
idSeed: template.idSeed,
|
|
311
|
+
signalSites: template.signalSites,
|
|
312
|
+
styles: [...new Set(files.filter((file) => /\.css(?:\?|$)/.test(file)))].sort(),
|
|
313
|
+
parentDependencies: false,
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
const records = Object.fromEntries(
|
|
317
|
+
Object.entries(widgets).sort(([left], [right]) => left.localeCompare(right)),
|
|
318
|
+
);
|
|
319
|
+
// Match the native __webpack_hash__ value captured by the executing entry.
|
|
320
|
+
// A widget-schema hash does not fence changes elsewhere in its client graph.
|
|
321
|
+
const buildId = compilation.hash;
|
|
322
|
+
if (typeof buildId !== 'string' || !buildId) {
|
|
323
|
+
throw new Error('Octane client build has no completed compilation hash.');
|
|
324
|
+
}
|
|
325
|
+
const independentHydration = Object.keys(records).length !== 0;
|
|
326
|
+
compilation.emitAsset(
|
|
327
|
+
'octane-client-build.json',
|
|
328
|
+
new compiler.webpack.sources.RawSource(
|
|
329
|
+
JSON.stringify(
|
|
330
|
+
{ version: 1, buildId, mode, capabilities: { independentHydration } },
|
|
331
|
+
null,
|
|
332
|
+
2,
|
|
333
|
+
) + '\n',
|
|
334
|
+
),
|
|
335
|
+
);
|
|
336
|
+
if (!independentHydration) return;
|
|
337
|
+
compilation.emitAsset(
|
|
338
|
+
INDEPENDENT_HYDRATION_MANIFEST_FILENAME,
|
|
339
|
+
new compiler.webpack.sources.RawSource(
|
|
340
|
+
JSON.stringify({ version: 1, buildId, widgets: records }, null, 2) + '\n',
|
|
341
|
+
),
|
|
342
|
+
);
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
function collectIndependentHydrationModules(compilation) {
|
|
346
|
+
const modules = [];
|
|
347
|
+
for (const topLevelModule of iterable(compilation.modules)) {
|
|
348
|
+
visitClientReferenceModules(
|
|
349
|
+
compilation,
|
|
350
|
+
topLevelModule,
|
|
351
|
+
[],
|
|
352
|
+
(module, chunks, executableModule) => {
|
|
353
|
+
const info = getOctaneRspackBuildInfo(module);
|
|
354
|
+
if (info !== null) modules.push({ module, executableModule, info, chunks });
|
|
355
|
+
},
|
|
356
|
+
new Set(),
|
|
357
|
+
);
|
|
358
|
+
}
|
|
359
|
+
return modules;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
function independentHydrationTargets(modules) {
|
|
363
|
+
const targets = [];
|
|
364
|
+
for (const { info } of modules) {
|
|
365
|
+
for (const template of info.independentWidgets ?? []) {
|
|
366
|
+
const query = template.request.slice(template.request.indexOf('?'));
|
|
367
|
+
const candidates = modules.filter(
|
|
368
|
+
(candidate) =>
|
|
369
|
+
candidate.info.canonicalId === template.moduleId &&
|
|
370
|
+
candidate.info.resourceQuery?.includes(query),
|
|
371
|
+
);
|
|
372
|
+
const target =
|
|
373
|
+
candidates.find(
|
|
374
|
+
(candidate) =>
|
|
375
|
+
candidate.executableModule != null && candidate.executableModule !== candidate.module,
|
|
376
|
+
) ?? candidates[0];
|
|
377
|
+
if (target === undefined) {
|
|
378
|
+
throw new Error(
|
|
379
|
+
`Octane independent Hydrate ${JSON.stringify(template.boundaryId)} has no emitted activation chunk.`,
|
|
380
|
+
);
|
|
381
|
+
}
|
|
382
|
+
targets.push({ template, target });
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
return targets;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
function independentHydrationRuntimeModule(compiler, compilation) {
|
|
389
|
+
const { RuntimeGlobals, RuntimeModule } = compiler.webpack;
|
|
390
|
+
return class OctaneIndependentHydrationRuntimeModule extends RuntimeModule {
|
|
391
|
+
constructor() {
|
|
392
|
+
super('octane independent hydration loaders');
|
|
393
|
+
this.fullHash = true;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
generate() {
|
|
397
|
+
const registrations = [];
|
|
398
|
+
const seen = new Set();
|
|
399
|
+
for (const { template, target } of independentHydrationTargets(
|
|
400
|
+
collectIndependentHydrationModules(compilation),
|
|
401
|
+
)) {
|
|
402
|
+
if (seen.has(template.boundaryId)) continue;
|
|
403
|
+
seen.add(template.boundaryId);
|
|
404
|
+
const candidates = [target.executableModule, target.module, target.module?.rootModule];
|
|
405
|
+
const executable = candidates.find(
|
|
406
|
+
(module) => module != null && compilation.chunkGraph.getModuleId(module) != null,
|
|
407
|
+
);
|
|
408
|
+
if (executable === undefined) {
|
|
409
|
+
throw new Error(
|
|
410
|
+
`Octane independent Hydrate ${JSON.stringify(template.boundaryId)} has no executable Rspack module.`,
|
|
411
|
+
);
|
|
412
|
+
}
|
|
413
|
+
const moduleId = compilation.chunkGraph.getModuleId(executable);
|
|
414
|
+
const activationChunks = new Set(target.chunks);
|
|
415
|
+
for (const chunk of target.chunks) {
|
|
416
|
+
for (const referenced of iterable(chunk?.getAllReferencedChunks?.())) {
|
|
417
|
+
activationChunks.add(referenced);
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
const chunkIds = [...activationChunks]
|
|
421
|
+
.filter((chunk) => chunk !== this.chunk && chunk?.id != null)
|
|
422
|
+
.map((chunk) => chunk.id)
|
|
423
|
+
.sort((left, right) => String(left).localeCompare(String(right)));
|
|
424
|
+
const load =
|
|
425
|
+
chunkIds.length === 0
|
|
426
|
+
? 'Promise.resolve()'
|
|
427
|
+
: `Promise.all(${JSON.stringify(chunkIds)}.map(${RuntimeGlobals.ensureChunk}))`;
|
|
428
|
+
registrations.push(
|
|
429
|
+
`${RuntimeGlobals.global}.__OCTANE_INDEPENDENT_MODULES__[${JSON.stringify(template.boundaryId)}] = function() { return ${load}.then(function() { return ${RuntimeGlobals.require}(${JSON.stringify(moduleId)}); }); };`,
|
|
430
|
+
);
|
|
431
|
+
}
|
|
432
|
+
return [
|
|
433
|
+
`${RuntimeGlobals.global}.__OCTANE_INDEPENDENT_MODULES__ ||= Object.create(null);`,
|
|
434
|
+
...registrations,
|
|
435
|
+
].join('\n');
|
|
436
|
+
}
|
|
437
|
+
};
|
|
438
|
+
}
|
|
439
|
+
|
|
282
440
|
function defineMatchesBoolean(value, expected) {
|
|
283
441
|
return value === expected || value === JSON.stringify(expected);
|
|
284
442
|
}
|
|
@@ -382,6 +540,7 @@ export class OctaneRspackPlugin {
|
|
|
382
540
|
dev,
|
|
383
541
|
profile,
|
|
384
542
|
strong: this.options.strong === true,
|
|
543
|
+
knownAttributeSpreads: this.options.knownAttributeSpreads,
|
|
385
544
|
exclude: this.options.exclude ?? [],
|
|
386
545
|
renderers: this.options.renderers?.signature,
|
|
387
546
|
runtime: this.options.runtime,
|
|
@@ -429,6 +588,9 @@ export class OctaneRspackPlugin {
|
|
|
429
588
|
environment,
|
|
430
589
|
profile,
|
|
431
590
|
...(this.options.strong === undefined ? null : { strong: this.options.strong }),
|
|
591
|
+
...(this.options.knownAttributeSpreads === undefined
|
|
592
|
+
? null
|
|
593
|
+
: { knownAttributeSpreads: this.options.knownAttributeSpreads }),
|
|
432
594
|
...(this.options.hmr === undefined ? null : { hmr: this.options.hmr }),
|
|
433
595
|
...(this.options.dev === undefined ? null : { dev: this.options.dev }),
|
|
434
596
|
...(this.options.exclude === undefined ? null : { exclude: this.options.exclude }),
|
|
@@ -515,12 +677,56 @@ export class OctaneRspackPlugin {
|
|
|
515
677
|
addDependencies(compilation.fileDependencies, current.dependencies);
|
|
516
678
|
addDependencies(compilation.missingDependencies, current.missingDependencies);
|
|
517
679
|
if (environment === 'client') {
|
|
680
|
+
const IndependentHydrationRuntimeModule = independentHydrationRuntimeModule(
|
|
681
|
+
compiler,
|
|
682
|
+
compilation,
|
|
683
|
+
);
|
|
684
|
+
compilation.hooks.additionalTreeRuntimeRequirements?.tap(
|
|
685
|
+
PLUGIN_NAME,
|
|
686
|
+
(chunk, runtimeRequirements) => {
|
|
687
|
+
if (!chunk.hasRuntime?.()) return;
|
|
688
|
+
const modules = collectIndependentHydrationModules(compilation);
|
|
689
|
+
const featureModules = modules.filter(
|
|
690
|
+
({ info }) =>
|
|
691
|
+
info.streamedSignals === true || (info.independentWidgets?.length ?? 0) > 0,
|
|
692
|
+
);
|
|
693
|
+
if (
|
|
694
|
+
featureModules.length > 0 &&
|
|
695
|
+
compiler.options.plugins.some(
|
|
696
|
+
(plugin) => plugin instanceof compiler.webpack.HotModuleReplacementPlugin,
|
|
697
|
+
)
|
|
698
|
+
) {
|
|
699
|
+
runtimeRequirements.add(compiler.webpack.RuntimeGlobals.interceptModuleExecution);
|
|
700
|
+
runtimeRequirements.add(compiler.webpack.RuntimeGlobals.moduleCache);
|
|
701
|
+
runtimeRequirements.add(compiler.webpack.RuntimeGlobals.global);
|
|
702
|
+
compilation.addRuntimeModule(
|
|
703
|
+
chunk,
|
|
704
|
+
createStreamedSignalHmrRuntimeModule(compiler, compilation, featureModules),
|
|
705
|
+
);
|
|
706
|
+
}
|
|
707
|
+
if (independentHydrationTargets(modules).length === 0) {
|
|
708
|
+
return;
|
|
709
|
+
}
|
|
710
|
+
runtimeRequirements.add(compiler.webpack.RuntimeGlobals.ensureChunk);
|
|
711
|
+
runtimeRequirements.add(compiler.webpack.RuntimeGlobals.global);
|
|
712
|
+
runtimeRequirements.add(compiler.webpack.RuntimeGlobals.require);
|
|
713
|
+
compilation.addRuntimeModule(chunk, new IndependentHydrationRuntimeModule());
|
|
714
|
+
},
|
|
715
|
+
);
|
|
518
716
|
compilation.hooks.processAssets.tap(
|
|
519
717
|
{
|
|
520
718
|
name: PLUGIN_NAME,
|
|
521
719
|
stage: compiler.webpack.Compilation.PROCESS_ASSETS_STAGE_REPORT,
|
|
522
720
|
},
|
|
523
|
-
() =>
|
|
721
|
+
() => {
|
|
722
|
+
emitClientReferenceManifest(compiler, compilation);
|
|
723
|
+
emitIndependentHydrationManifest(
|
|
724
|
+
compiler,
|
|
725
|
+
compilation,
|
|
726
|
+
this.options.clientBuildMode ??
|
|
727
|
+
(compiler.options.mode === 'development' ? 'development' : 'production'),
|
|
728
|
+
);
|
|
729
|
+
},
|
|
524
730
|
);
|
|
525
731
|
}
|
|
526
732
|
});
|
package/src/shared.js
CHANGED
|
@@ -41,6 +41,7 @@ const LOADER_OPTION_KEYS = new Set([
|
|
|
41
41
|
'dev',
|
|
42
42
|
'profile',
|
|
43
43
|
'strong',
|
|
44
|
+
'knownAttributeSpreads',
|
|
44
45
|
'exclude',
|
|
45
46
|
'renderers',
|
|
46
47
|
'requireDirective',
|
|
@@ -54,6 +55,7 @@ const PLUGIN_OPTION_KEYS = new Set([
|
|
|
54
55
|
'runtime',
|
|
55
56
|
'transpile',
|
|
56
57
|
'cssModuleConstants',
|
|
58
|
+
'clientBuildMode',
|
|
57
59
|
]);
|
|
58
60
|
const LAYER_SPECIALIZATION_KEYS = new Set(['runtime', 'renderers', 'universalRuntime']);
|
|
59
61
|
|
|
@@ -198,6 +200,16 @@ function normalizeOptions(value, plugin) {
|
|
|
198
200
|
throw new TypeError('@octanejs/rspack-plugin: `root` must be a path string.');
|
|
199
201
|
}
|
|
200
202
|
if (plugin) normalizeRuntimeRequest(options.runtime);
|
|
203
|
+
if (
|
|
204
|
+
plugin &&
|
|
205
|
+
options.clientBuildMode !== undefined &&
|
|
206
|
+
options.clientBuildMode !== 'production' &&
|
|
207
|
+
options.clientBuildMode !== 'development'
|
|
208
|
+
) {
|
|
209
|
+
throw new TypeError(
|
|
210
|
+
'@octanejs/rspack-plugin: `clientBuildMode` must be "production" or "development".',
|
|
211
|
+
);
|
|
212
|
+
}
|
|
201
213
|
if (
|
|
202
214
|
options.environment !== undefined &&
|
|
203
215
|
options.environment !== 'client' &&
|
|
@@ -255,6 +267,9 @@ function normalizeOptions(value, plugin) {
|
|
|
255
267
|
...(options.dev === undefined ? null : { dev: options.dev }),
|
|
256
268
|
...(options.profile === undefined ? null : { profile: options.profile }),
|
|
257
269
|
...(options.strong === undefined ? null : { strong: options.strong }),
|
|
270
|
+
...(options.knownAttributeSpreads === undefined
|
|
271
|
+
? null
|
|
272
|
+
: { knownAttributeSpreads: options.knownAttributeSpreads }),
|
|
258
273
|
...(options.exclude === undefined ? null : { exclude: [...options.exclude] }),
|
|
259
274
|
...(renderers === undefined ? null : { renderers }),
|
|
260
275
|
...(universalRuntime === undefined ? null : { universalRuntime }),
|
|
@@ -266,6 +281,9 @@ function normalizeOptions(value, plugin) {
|
|
|
266
281
|
? null
|
|
267
282
|
: { textTypes: Object.freeze({ tsconfig: options.textTypes.tsconfig }) }),
|
|
268
283
|
...(plugin && parallel !== undefined ? { parallel } : null),
|
|
284
|
+
...(plugin && options.clientBuildMode !== undefined
|
|
285
|
+
? { clientBuildMode: options.clientBuildMode }
|
|
286
|
+
: null),
|
|
269
287
|
...(plugin && options.transpile !== undefined ? { transpile: options.transpile } : null),
|
|
270
288
|
...(plugin && options.runtime !== undefined ? { runtime: options.runtime } : null),
|
|
271
289
|
...(plugin && options.cssModuleConstants !== undefined
|
|
@@ -307,6 +325,8 @@ export function getOctaneRspackBuildInfo(module) {
|
|
|
307
325
|
value.transformKind === 'slots' ||
|
|
308
326
|
value.transformKind === 'client-only-stub') &&
|
|
309
327
|
typeof value.serverRpc === 'boolean' &&
|
|
328
|
+
(value.resourceQuery === undefined || typeof value.resourceQuery === 'string') &&
|
|
329
|
+
(value.independentWidgets === undefined || Array.isArray(value.independentWidgets)) &&
|
|
310
330
|
(value.clientReference === undefined || nestedReferenceValid) &&
|
|
311
331
|
(value.universalRuntime === undefined || universalRuntimeValid)
|
|
312
332
|
) {
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fence feature-bearing documents through Rspack's native module-execution
|
|
3
|
+
* interceptor, before authored factories run. No compiled source is rewritten.
|
|
4
|
+
* @param {import('@rspack/core').Compiler} compiler
|
|
5
|
+
* @param {import('@rspack/core').Compilation} compilation
|
|
6
|
+
* @param {Array<{ module: import('@rspack/core').Module, executableModule?: import('@rspack/core').Module }>} modules
|
|
7
|
+
*/
|
|
8
|
+
export function createStreamedSignalHmrRuntimeModule(compiler, compilation, modules) {
|
|
9
|
+
const { RuntimeGlobals, RuntimeModule } = compiler.webpack;
|
|
10
|
+
return new (class extends RuntimeModule {
|
|
11
|
+
constructor() {
|
|
12
|
+
super('octane streamed signal HMR fence', RuntimeModule.STAGE_TRIGGER);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
generate() {
|
|
16
|
+
const ids = new Set();
|
|
17
|
+
for (const { module, executableModule } of modules) {
|
|
18
|
+
for (const candidate of [executableModule, module, module?.rootModule]) {
|
|
19
|
+
const id = candidate == null ? null : compilation.chunkGraph.getModuleId(candidate);
|
|
20
|
+
if (id != null) {
|
|
21
|
+
ids.add(String(id));
|
|
22
|
+
break;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return `
|
|
27
|
+
var octaneSignalModules = new Set(${JSON.stringify([...ids].sort())});
|
|
28
|
+
var octaneSignalFenceInstalled = false;
|
|
29
|
+
var octaneSignalGenerationChanged = false;
|
|
30
|
+
var octaneSignalModuleExecuted = false;
|
|
31
|
+
function octaneReloadSignalDocument() {
|
|
32
|
+
${RuntimeGlobals.global}.location?.reload?.();
|
|
33
|
+
throw new Error('[octane] The streamed signal client build changed. Reload this document.');
|
|
34
|
+
}
|
|
35
|
+
function octaneTrackSignalGeneration(hot) {
|
|
36
|
+
if (!hot || octaneSignalFenceInstalled) return;
|
|
37
|
+
octaneSignalFenceInstalled = true;
|
|
38
|
+
if (hot.status() !== 'idle') octaneSignalGenerationChanged = true;
|
|
39
|
+
hot.addStatusHandler(function(status) {
|
|
40
|
+
if (status === 'apply') octaneSignalGenerationChanged = true;
|
|
41
|
+
if (octaneSignalModuleExecuted && (status === 'check' || status === 'dispose' || status === 'apply')) octaneReloadSignalDocument();
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
// A newly introduced runtime may itself arrive during hot application. The
|
|
45
|
+
// entry's cached hot module supplies that state before any feature executes.
|
|
46
|
+
for (var octaneCachedId in ${RuntimeGlobals.moduleCache}) {
|
|
47
|
+
octaneTrackSignalGeneration(${RuntimeGlobals.moduleCache}[octaneCachedId].hot);
|
|
48
|
+
if (octaneSignalFenceInstalled) break;
|
|
49
|
+
}
|
|
50
|
+
${RuntimeGlobals.interceptModuleExecution}.push(function(options) {
|
|
51
|
+
var hot = options.module.hot;
|
|
52
|
+
octaneTrackSignalGeneration(hot);
|
|
53
|
+
if (!hot || !octaneSignalModules.has(String(options.id))) return;
|
|
54
|
+
if (octaneSignalGenerationChanged || hot.status() !== 'idle') octaneReloadSignalDocument();
|
|
55
|
+
octaneSignalModuleExecuted = true;
|
|
56
|
+
});`;
|
|
57
|
+
}
|
|
58
|
+
})();
|
|
59
|
+
}
|
package/types/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Compiler, RspackPluginInstance } from '@rspack/core';
|
|
2
|
-
import type { OctaneCssModuleConstants } from 'octane/compiler';
|
|
2
|
+
import type { KnownAttributeSpread, OctaneCssModuleConstants } from 'octane/compiler';
|
|
3
3
|
|
|
4
4
|
export type { OctaneCssModuleConstants } from 'octane/compiler';
|
|
5
5
|
|
|
@@ -144,6 +144,8 @@ export interface OctaneRspackLoaderOptions {
|
|
|
144
144
|
* @default false
|
|
145
145
|
*/
|
|
146
146
|
strong?: boolean;
|
|
147
|
+
/** Fixed native-attribute shapes for trusted imported spread helpers. */
|
|
148
|
+
knownAttributeSpreads?: readonly KnownAttributeSpread[];
|
|
147
149
|
/**
|
|
148
150
|
* Path fragments excluded from the plain `.ts`/`.js` hook-slot pass. With
|
|
149
151
|
* `requireDirective`, excluded paths are exempt from Octane ownership
|
|
@@ -175,6 +177,8 @@ export interface OctaneRspackLoaderOptions {
|
|
|
175
177
|
}
|
|
176
178
|
|
|
177
179
|
export interface OctaneRspackPluginOptions extends OctaneRspackLoaderOptions {
|
|
180
|
+
/** Build intent for SSR metadata; independent of optimization/HMR settings. */
|
|
181
|
+
clientBuildMode?: 'production' | 'development';
|
|
178
182
|
/**
|
|
179
183
|
* @experimental Prove primitive DOM text children from the TypeScript project
|
|
180
184
|
* in one-shot production builds. Relative paths resolve from the plugin root.
|
|
@@ -226,8 +230,25 @@ export interface OctaneRspackPluginOptions extends OctaneRspackLoaderOptions {
|
|
|
226
230
|
|
|
227
231
|
export interface OctaneRspackBuildInfo {
|
|
228
232
|
canonicalId: string;
|
|
233
|
+
/** Resource query retained separately from the canonical source identity. */
|
|
234
|
+
resourceQuery?: string;
|
|
229
235
|
transformKind: 'compile' | 'slots' | 'client-only-stub';
|
|
236
|
+
streamedSignals?: true;
|
|
230
237
|
serverRpc: boolean;
|
|
238
|
+
/** Strict-independent Hydrate templates completed into the emitted client manifest. */
|
|
239
|
+
independentWidgets?: readonly {
|
|
240
|
+
readonly version: 1;
|
|
241
|
+
readonly boundaryId: string;
|
|
242
|
+
readonly moduleId: string;
|
|
243
|
+
readonly exportName: string;
|
|
244
|
+
readonly request: string;
|
|
245
|
+
readonly captureSchema: readonly { readonly name: string; readonly type: 'json' }[];
|
|
246
|
+
readonly hookSeed: number;
|
|
247
|
+
readonly idSeed: number;
|
|
248
|
+
readonly signalSites: readonly string[];
|
|
249
|
+
readonly styles: readonly string[];
|
|
250
|
+
readonly parentDependencies: false;
|
|
251
|
+
}[];
|
|
231
252
|
/** Universal host runtime/thread identity, when this module was specialized. */
|
|
232
253
|
universalRuntime?: OctaneUniversalRuntimeOptions;
|
|
233
254
|
/** Stable identity shared by the client compile and its inert server stub. */
|