@octanejs/rsbuild-plugin 0.1.6 → 0.1.7

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/README.md CHANGED
@@ -46,8 +46,10 @@ export default defineConfig({
46
46
 
47
47
  `index.html` must contain `<!--ssr-head-->` in `<head>` and
48
48
  `<!--ssr-body-->` inside `<div id="root">`. In app mode the plugin creates a
49
- `web` hydration environment and a `node` SSR environment. Override their names
50
- with `clientEnvironment` and `serverEnvironment` when composing a larger
49
+ `web` hydration environment and a `node` SSR environment. A deployment adapter
50
+ with `serverTarget: 'webworker'` changes only the production server target to
51
+ `web-worker`; development SSR remains Node-based. Override the environment
52
+ names with `clientEnvironment` and `serverEnvironment` when composing a larger
51
53
  Rsbuild setup.
52
54
 
53
55
  ```sh
@@ -58,9 +60,11 @@ pnpm octane-rsbuild-preview
58
60
 
59
61
  Production assets are written to `dist/client`; the self-contained ESM server,
60
62
  SSR template, and route asset map are written to `dist/server`. Change the
61
- shared root with `build.outDir` in `octane.config.ts`. The generated server
62
- exports `handler` and `nodeHandler`, auto-boots under Node, and invokes a
63
- configured adapter after both environments finish.
63
+ shared root with `build.outDir` in `octane.config.ts`. The default generated
64
+ server exports `handler` and `nodeHandler` and auto-boots under Node. A
65
+ webworker adapter instead receives an importable `createWebWorkerHandler`
66
+ factory for its platform wrapper. The configured adapter runs after both
67
+ environments finish.
64
68
 
65
69
  `build.target` applies to both application transforms and Rspack's generated
66
70
  runtime. Use one ES level (`es2018`, `es2022`, and so on), `modules`, `false`, or
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@octanejs/rsbuild-plugin",
3
- "version": "0.1.6",
3
+ "version": "0.1.7",
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.7",
52
- "@octanejs/rspack-plugin": "0.1.6"
51
+ "@octanejs/app-core": "0.0.8",
52
+ "@octanejs/rspack-plugin": "0.1.7"
53
53
  },
54
54
  "peerDependencies": {
55
55
  "@rsbuild/core": "^2.0.0",
56
- "octane": "0.1.11"
56
+ "octane": "0.1.12"
57
57
  },
58
58
  "devDependencies": {
59
59
  "@rsbuild/core": "^2.1.5",
@@ -62,6 +62,6 @@
62
62
  "playwright": "^1.61.0",
63
63
  "type-fest": "^5.6.0",
64
64
  "vitest": "^4.1.9",
65
- "octane": "0.1.11"
65
+ "octane": "0.1.12"
66
66
  }
67
67
  }
package/src/build.js CHANGED
@@ -45,7 +45,9 @@ export async function finalizeOctaneRsbuildOutput(options) {
45
45
  fs.renameSync(clientAssetMap, serverAssetMap);
46
46
 
47
47
  log(`Server build complete: ${path.relative(root, serverEntry)}`);
48
- log(`Start with: node ${path.relative(root, serverEntry)} (or octane-rsbuild-preview)`);
48
+ if (options.config.adapter?.serverTarget !== 'webworker') {
49
+ log(`Start with: node ${path.relative(root, serverEntry)} (or octane-rsbuild-preview)`);
50
+ }
49
51
 
50
52
  if (options.config.adapter?.adapt) {
51
53
  const adapterName = options.config.adapter.name ?? 'adapter';
package/src/index.js CHANGED
@@ -356,6 +356,8 @@ export function pluginOctane(inlineOptions = {}) {
356
356
  const octaneConfig = /** @type {import('@octanejs/app-core').ResolvedOctaneConfig} */ (
357
357
  initialConfig
358
358
  );
359
+ const webWorkerServer =
360
+ productionBuild && octaneConfig.adapter?.serverTarget === 'webworker';
359
361
  const template = path.join(root, 'index.html');
360
362
  if (!fs.existsSync(template)) {
361
363
  throw new Error(
@@ -394,7 +396,7 @@ export function pluginOctane(inlineOptions = {}) {
394
396
  },
395
397
  output: {
396
398
  ...targetOutput,
397
- target: 'node',
399
+ target: webWorkerServer ? 'web-worker' : 'node',
398
400
  autoExternal: false,
399
401
  distPath: {
400
402
  root: path.resolve(root, octaneConfig.build.outDir, 'server'),
@@ -403,7 +405,10 @@ export function pluginOctane(inlineOptions = {}) {
403
405
  },
404
406
  filename: { js: '[name].js' },
405
407
  filenameHash: false,
406
- module: productionBuild,
408
+ // Rsbuild rejects its public ESM switch for web-worker targets.
409
+ // The final Rspack config enables module output below so the
410
+ // adapter wrapper can import createWebWorkerHandler.
411
+ module: productionBuild && !webWorkerServer,
407
412
  emitAssets: false,
408
413
  ...outputMinify,
409
414
  },
@@ -415,14 +420,17 @@ export function pluginOctane(inlineOptions = {}) {
415
420
  api.modifyEnvironmentConfig((config, { mergeEnvironmentConfig }) => {
416
421
  // Octane and several framework bindings use this conventional guard for
417
422
  // diagnostics and production-only branches. Rsbuild does not define it
418
- // when a programmatic build inherits mode "none", so scope the define to
419
- // browser environments where the Node `process` global is unavailable.
420
- if (config.output.target === 'node') return config;
423
+ // when a programmatic build inherits mode "none". Browser output always
424
+ // needs a replacement because `process` is absent; production Node output
425
+ // also needs one so Rspack can remove DEV-only diagnostics without relying
426
+ // on minification. Keep the Node dev server runtime-controlled.
427
+ const productionBuild = isProductionBuild();
428
+ if (config.output.target === 'node' && !productionBuild) return config;
421
429
  return mergeEnvironmentConfig(config, {
422
430
  source: {
423
431
  define: {
424
432
  'process.env.NODE_ENV': JSON.stringify(
425
- isProductionBuild() ? 'production' : 'development',
433
+ productionBuild ? 'production' : 'development',
426
434
  ),
427
435
  },
428
436
  },
@@ -480,6 +488,8 @@ export function pluginOctane(inlineOptions = {}) {
480
488
  // programmatic `rsbuild.build()` may intentionally keep development
481
489
  // transforms while still requiring production output/finalization.
482
490
  const production = isProductionBuild();
491
+ const webWorkerServer =
492
+ production && loaded.config.adapter?.serverTarget === 'webworker';
483
493
  const generator = production ? generateServerEntry : generateServerManifestEntry;
484
494
  return generator({
485
495
  routes: loaded.config.router.routes,
@@ -487,7 +497,10 @@ export function pluginOctane(inlineOptions = {}) {
487
497
  configImportPath: loaded.configPath,
488
498
  rootBoundary: loaded.config.rootBoundary,
489
499
  rpcModulePaths: serverModules.ids,
490
- ...(production ? { clientAssetMapFile: CLIENT_ASSET_MAP } : { clientAssetMap: {} }),
500
+ ...(webWorkerServer ? { mode: 'webworker' } : null),
501
+ ...(production && !webWorkerServer
502
+ ? { clientAssetMapFile: CLIENT_ASSET_MAP }
503
+ : { clientAssetMap: {} }),
491
504
  resolveImport: (id) => resolveProjectModule(id, root),
492
505
  configModuleId: localRequire.resolve('@octanejs/app-core/config'),
493
506
  productionModuleId: localRequire.resolve('@octanejs/app-core/production'),
@@ -502,9 +515,13 @@ export function pluginOctane(inlineOptions = {}) {
502
515
  const productionBuild = isProductionBuild();
503
516
  const environment =
504
517
  utils.environment.name === serverEnvironment || utils.isServer ? 'server' : 'client';
518
+ const webWorkerServer =
519
+ productionBuild &&
520
+ environment === 'server' &&
521
+ initialConfig?.adapter?.serverTarget === 'webworker';
505
522
  if (buildTargetPlan) {
506
523
  config.target = /** @type {any} */ ([
507
- environment === 'server' ? 'node' : 'web',
524
+ environment === 'server' ? (webWorkerServer ? 'webworker' : 'node') : 'web',
508
525
  buildTargetPlan.rspackTarget,
509
526
  ]);
510
527
  }
@@ -538,6 +555,26 @@ export function pluginOctane(inlineOptions = {}) {
538
555
  );
539
556
  if (appEnabled && environment === 'server') {
540
557
  config.output ??= {};
558
+ if (webWorkerServer) {
559
+ // Rsbuild's web-worker target is script-only at its public config
560
+ // layer. The generated entry is an importable adapter input, so
561
+ // retain its named factory export in an ESM Rspack library.
562
+ config.experiments ??= {};
563
+ /** @type {any} */ (config.experiments).outputModule = true;
564
+ config.output.module = true;
565
+ config.output.chunkFilename = 'chunks/[name].js';
566
+ config.output.chunkFormat = 'module';
567
+ config.output.chunkLoading = 'import';
568
+ config.output.workerChunkLoading = 'import';
569
+ config.optimization ??= {};
570
+ config.optimization.minimize = initialConfig?.build.minify ?? true;
571
+ // Workers with compatibility modules (including Cloudflare's
572
+ // nodejs_compat) resolve `node:` imports as native ESM. Install
573
+ // this before Rspack attempts to treat the scheme as browser code.
574
+ config.plugins ??= [];
575
+ config.plugins.push(new utils.rspack.ExternalsPlugin('module', /^node:/));
576
+ config.externalsType = 'module';
577
+ }
541
578
  config.output.library = { type: productionBuild ? 'module' : 'commonjs2' };
542
579
  if (productionBuild) {
543
580
  config.module ??= {};
package/types/index.d.ts CHANGED
@@ -22,9 +22,12 @@ export interface OctaneRsbuildPluginOptions {
22
22
  */
23
23
  exclude?: string[];
24
24
  /**
25
- * Mixed-toolchain ownership gate: compile only project modules declaring
26
- * `'use octane'`; undirected project `.tsx`/`.ts` pass through to the host
27
- * framework's own pipeline. See `@octanejs/rspack-plugin` for details.
25
+ * Mixed-toolchain ownership gate: project `.tsrx` stays Octane's by
26
+ * extension; a project `.tsx` (full compile) or plain `.ts`/`.js`
27
+ * (hook slotting) is Octane's only with a leading
28
+ * `@jsxImportSource octane` pragma comment. Unmarked modules pass
29
+ * through to the host framework's own pipeline. See
30
+ * `@octanejs/rspack-plugin` for details.
28
31
  * @default false
29
32
  */
30
33
  requireDirective?: boolean;