@octanejs/rspack-plugin 0.1.4 → 0.1.6

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,6 +1,6 @@
1
1
  {
2
2
  "name": "@octanejs/rspack-plugin",
3
- "version": "0.1.4",
3
+ "version": "0.1.6",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "engines": {
@@ -45,13 +45,13 @@
45
45
  },
46
46
  "peerDependencies": {
47
47
  "@rspack/core": "^2.0.0",
48
- "octane": "0.1.9"
48
+ "octane": "0.1.11"
49
49
  },
50
50
  "devDependencies": {
51
51
  "@rspack/core": "^2.1.3",
52
52
  "@types/node": "^24.3.0",
53
53
  "vitest": "^4.1.9",
54
- "octane": "0.1.9"
54
+ "octane": "0.1.11"
55
55
  },
56
56
  "scripts": {
57
57
  "test": "vitest run --config vitest.config.js"
package/src/loader.js CHANGED
@@ -104,6 +104,9 @@ export default function octaneLoader(source, inputSourceMap) {
104
104
  profile,
105
105
  ...(options.exclude === undefined ? null : { exclude: options.exclude }),
106
106
  ...(options.renderers === undefined ? null : { renderers: options.renderers }),
107
+ ...(options.universalRuntime === undefined
108
+ ? null
109
+ : { universalRuntime: options.universalRuntime }),
107
110
  ...(options.requireDirective === undefined
108
111
  ? null
109
112
  : { requireDirective: options.requireDirective }),
@@ -138,6 +141,9 @@ export default function octaneLoader(source, inputSourceMap) {
138
141
  result.kind === 'compile' &&
139
142
  (result.code.includes('_$__serverRpc(') ||
140
143
  result.code.includes('export const _$_server_$_')),
144
+ ...(result.universalRuntime === undefined
145
+ ? null
146
+ : { universalRuntime: result.universalRuntime }),
141
147
  ...(result.clientReference === undefined
142
148
  ? null
143
149
  : { clientReference: { ...result.clientReference } }),
package/src/plugin.js CHANGED
@@ -237,6 +237,8 @@ export class OctaneRspackPlugin {
237
237
  profile,
238
238
  exclude: this.options.exclude ?? [],
239
239
  renderers: this.options.renderers?.signature,
240
+ runtime: this.options.runtime,
241
+ universalRuntime: this.options.universalRuntime,
240
242
  // Ownership flips which modules compile vs pass through — cached
241
243
  // transform results must not survive a requireDirective toggle.
242
244
  requireDirective: this.options.requireDirective === true,
@@ -248,8 +250,12 @@ export class OctaneRspackPlugin {
248
250
  profile,
249
251
  ...(this.options.exclude === undefined ? null : { exclude: this.options.exclude }),
250
252
  ...(this.options.renderers === undefined ? null : { renderers: this.options.renderers }),
253
+ ...(this.options.universalRuntime === undefined
254
+ ? null
255
+ : { universalRuntime: this.options.universalRuntime }),
251
256
  });
252
- const runtimeRequest = neutralCompiler.resolveRuntimeRequest('octane', environment);
257
+ const runtimeRequest =
258
+ this.options.runtime ?? neutralCompiler.resolveRuntimeRequest('octane', environment);
253
259
 
254
260
  compiler.options.resolve ??= {};
255
261
  addUniqueExtensions(compiler.options.resolve);
@@ -266,6 +272,9 @@ export class OctaneRspackPlugin {
266
272
  ...(this.options.dev === undefined ? null : { dev: this.options.dev }),
267
273
  ...(this.options.exclude === undefined ? null : { exclude: this.options.exclude }),
268
274
  ...(this.options.renderers === undefined ? null : { renderers: this.options.renderers }),
275
+ ...(this.options.universalRuntime === undefined
276
+ ? null
277
+ : { universalRuntime: this.options.universalRuntime }),
269
278
  ...(this.options.requireDirective === undefined
270
279
  ? null
271
280
  : { requireDirective: this.options.requireDirective }),
package/src/shared.js CHANGED
@@ -43,8 +43,35 @@ const LOADER_OPTION_KEYS = new Set([
43
43
  'exclude',
44
44
  'renderers',
45
45
  'requireDirective',
46
+ 'universalRuntime',
46
47
  ]);
47
- const PLUGIN_OPTION_KEYS = new Set([...LOADER_OPTION_KEYS, 'transpile']);
48
+ const PLUGIN_OPTION_KEYS = new Set([...LOADER_OPTION_KEYS, 'runtime', 'transpile']);
49
+
50
+ function normalizeUniversalRuntime(value) {
51
+ if (value === undefined) return undefined;
52
+ if (value === null || typeof value !== 'object' || Array.isArray(value)) {
53
+ throw new TypeError('@octanejs/rspack-plugin: `universalRuntime` must be an object.');
54
+ }
55
+ for (const key of Object.keys(value)) {
56
+ if (key !== 'runtime' && key !== 'thread') {
57
+ throw new TypeError(`@octanejs/rspack-plugin: unknown \`universalRuntime.${key}\` option.`);
58
+ }
59
+ }
60
+ if (
61
+ typeof value.runtime !== 'string' ||
62
+ !/^[a-z][a-z0-9]*(?:[._-][a-z0-9]+)*$/.test(value.runtime)
63
+ ) {
64
+ throw new TypeError(
65
+ '@octanejs/rspack-plugin: `universalRuntime.runtime` must be a lowercase runtime ID.',
66
+ );
67
+ }
68
+ if (value.thread !== 'background' && value.thread !== 'main-thread') {
69
+ throw new TypeError(
70
+ '@octanejs/rspack-plugin: `universalRuntime.thread` must be "background" or "main-thread".',
71
+ );
72
+ }
73
+ return Object.freeze({ runtime: value.runtime, thread: value.thread });
74
+ }
48
75
 
49
76
  function assertBooleanOption(options, key) {
50
77
  if (options[key] !== undefined && typeof options[key] !== 'boolean') {
@@ -66,6 +93,17 @@ function normalizeOptions(value, plugin) {
66
93
  if (options.root !== undefined && typeof options.root !== 'string') {
67
94
  throw new TypeError('@octanejs/rspack-plugin: `root` must be a path string.');
68
95
  }
96
+ if (
97
+ plugin &&
98
+ options.runtime !== undefined &&
99
+ (typeof options.runtime !== 'string' ||
100
+ options.runtime.trim() !== options.runtime ||
101
+ !options.runtime)
102
+ ) {
103
+ throw new TypeError(
104
+ '@octanejs/rspack-plugin: `runtime` must be a non-empty module request string.',
105
+ );
106
+ }
69
107
  if (
70
108
  options.environment !== undefined &&
71
109
  options.environment !== 'client' &&
@@ -86,6 +124,7 @@ function normalizeOptions(value, plugin) {
86
124
  if (plugin) assertBooleanOption(options, 'transpile');
87
125
  const renderers =
88
126
  options.renderers === undefined ? undefined : normalizeRendererConfig(options.renderers);
127
+ const universalRuntime = normalizeUniversalRuntime(options.universalRuntime);
89
128
 
90
129
  const normalized = {
91
130
  ...(options.root === undefined ? null : { root: options.root }),
@@ -95,10 +134,12 @@ function normalizeOptions(value, plugin) {
95
134
  ...(options.profile === undefined ? null : { profile: options.profile }),
96
135
  ...(options.exclude === undefined ? null : { exclude: [...options.exclude] }),
97
136
  ...(renderers === undefined ? null : { renderers }),
137
+ ...(universalRuntime === undefined ? null : { universalRuntime }),
98
138
  ...(options.requireDirective === undefined
99
139
  ? null
100
140
  : { requireDirective: options.requireDirective }),
101
141
  ...(plugin && options.transpile !== undefined ? { transpile: options.transpile } : null),
142
+ ...(plugin && options.runtime !== undefined ? { runtime: options.runtime } : null),
102
143
  };
103
144
  if (normalized.exclude) Object.freeze(normalized.exclude);
104
145
  return Object.freeze(normalized);
@@ -121,6 +162,12 @@ export function getOctaneRspackBuildInfo(module) {
121
162
  typeof value.clientReference.id === 'string' &&
122
163
  typeof value.clientReference.moduleId === 'string' &&
123
164
  typeof value.clientReference.renderer === 'string';
165
+ const universalRuntimeValid =
166
+ value?.universalRuntime !== null &&
167
+ typeof value?.universalRuntime === 'object' &&
168
+ typeof value.universalRuntime.runtime === 'string' &&
169
+ (value.universalRuntime.thread === 'background' ||
170
+ value.universalRuntime.thread === 'main-thread');
124
171
  if (
125
172
  value &&
126
173
  typeof value === 'object' &&
@@ -129,7 +176,8 @@ export function getOctaneRspackBuildInfo(module) {
129
176
  value.transformKind === 'slots' ||
130
177
  value.transformKind === 'client-only-stub') &&
131
178
  typeof value.serverRpc === 'boolean' &&
132
- (value.clientReference === undefined || nestedReferenceValid)
179
+ (value.clientReference === undefined || nestedReferenceValid) &&
180
+ (value.universalRuntime === undefined || universalRuntimeValid)
133
181
  ) {
134
182
  return value;
135
183
  }
package/types/index.d.ts CHANGED
@@ -11,6 +11,18 @@ export interface OctaneRendererRuleOptions {
11
11
  renderer: string;
12
12
  }
13
13
 
14
+ /** @experimental Static source restrictions enforced for a renderer. */
15
+ export interface OctaneRendererValidationOptions {
16
+ /** Host elements that may directly contain authored primitive text. */
17
+ textParents?: readonly string[];
18
+ /** Unbound JavaScript globals that renderer-owned source may not reference. */
19
+ forbiddenGlobals?: readonly string[];
20
+ /** Package IDs whose static imports, subpaths, and CommonJS requires are forbidden. */
21
+ forbiddenImports?: readonly string[];
22
+ /** Allowed static JSX attributes by host name; `*` supplies shared patterns. */
23
+ hostProps?: Readonly<Record<string, readonly string[]>>;
24
+ }
25
+
14
26
  export type OctaneRendererRegistryEntry =
15
27
  | string
16
28
  | {
@@ -20,6 +32,7 @@ export type OctaneRendererRegistryEntry =
20
32
  intrinsics?: string;
21
33
  text?: 'reject' | 'ignore' | 'host';
22
34
  capabilities?: readonly string[];
35
+ validation?: OctaneRendererValidationOptions;
23
36
  };
24
37
 
25
38
  /** Static metadata for a component prop lowered for another renderer. */
@@ -51,6 +64,7 @@ export interface OctaneResolvedRendererConfig {
51
64
  readonly intrinsics?: string;
52
65
  readonly text: 'reject' | 'ignore' | 'host';
53
66
  readonly capabilities: readonly string[];
67
+ readonly validation?: Readonly<OctaneRendererValidationOptions>;
54
68
  }
55
69
  >
56
70
  >;
@@ -86,6 +100,11 @@ export interface OctaneRspackLoaderOptions {
86
100
  exclude?: string[];
87
101
  /** @experimental Renderer registry and ordered per-file selection rules. */
88
102
  renderers?: OctaneRendererConfigOptions | OctaneResolvedRendererConfig;
103
+ /** Compile-only host runtime/thread identity for a universal renderer graph. */
104
+ universalRuntime?: {
105
+ readonly runtime: string;
106
+ readonly thread: 'background' | 'main-thread';
107
+ };
89
108
  /**
90
109
  * Mixed-toolchain ownership gate: when `true`, Octane compiles only
91
110
  * project modules that declare `'use octane'` in their directive prologue.
@@ -100,6 +119,12 @@ export interface OctaneRspackLoaderOptions {
100
119
  }
101
120
 
102
121
  export interface OctaneRspackPluginOptions extends OctaneRspackLoaderOptions {
122
+ /**
123
+ * Override the exact module used for plain `octane` imports in this graph.
124
+ * Universal host integrations use this to share one hook/context runtime
125
+ * between compiled templates and plain TypeScript custom hooks.
126
+ */
127
+ runtime?: string;
103
128
  /**
104
129
  * Add Rspack's built-in SWC loader to strip TypeScript after Octane runs.
105
130
  * Disable this when another rule already owns TypeScript transpilation.
@@ -112,6 +137,11 @@ export interface OctaneRspackBuildInfo {
112
137
  canonicalId: string;
113
138
  transformKind: 'compile' | 'slots' | 'client-only-stub';
114
139
  serverRpc: boolean;
140
+ /** Universal host runtime/thread identity, when this module was specialized. */
141
+ universalRuntime?: {
142
+ readonly runtime: string;
143
+ readonly thread: 'background' | 'main-thread';
144
+ };
115
145
  /** Stable identity shared by the client compile and its inert server stub. */
116
146
  clientReference?: {
117
147
  readonly id: string;