@octanejs/rsbuild-plugin 0.1.42 → 0.1.44

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
@@ -108,8 +108,17 @@ export default defineConfig({
108
108
  ```
109
109
 
110
110
  You can also pass `pluginOctane({ strong: true })`. The plugin option takes
111
- priority over the app config. Dependencies are unaffected unless they begin a
112
- module with `"use strong"`.
111
+ priority over the app config. Strong mode opts application code into immutable
112
+ render snapshots and pure render projections. The compiler rejects detectable
113
+ state, ref, Effect Event, snapshot-mutation, and nondeterministic-render
114
+ violations; production client builds condition memoization on the author's
115
+ assertion that every user-authored render operation is pure for its witnessed
116
+ inputs. Callee shape and `use*` spelling do not disable that optimization.
117
+
118
+ Dependencies retain compatibility behavior unless they begin a module with
119
+ `"use strong"`. Strong analysis is bounded: an unknown call is assumed pure, so
120
+ imported live accessors do not become immutable merely because their caller opts
121
+ in. Keep such consumers in compatibility mode or pass an actual snapshot.
113
122
 
114
123
  App mode currently serves from the root path and uses Rsbuild's default asset
115
124
  prefix. Keep `server.base` at `/` and `output.assetPrefix` at `auto` or `/`; for
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@octanejs/rsbuild-plugin",
3
- "version": "0.1.42",
3
+ "version": "0.1.44",
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.43",
52
- "@octanejs/rspack-plugin": "0.1.42"
51
+ "@octanejs/app-core": "0.0.45",
52
+ "@octanejs/rspack-plugin": "0.1.44"
53
53
  },
54
54
  "peerDependencies": {
55
55
  "@rsbuild/core": "^2.0.0",
56
- "octane": "0.1.47"
56
+ "octane": "0.1.49"
57
57
  },
58
58
  "devDependencies": {
59
59
  "@rsbuild/core": "^2.1.6",
@@ -63,6 +63,6 @@
63
63
  "playwright": "^1.61.1",
64
64
  "type-fest": "^5.8.0",
65
65
  "vitest": "^4.1.10",
66
- "octane": "0.1.47"
66
+ "octane": "0.1.49"
67
67
  }
68
68
  }
package/src/index.js CHANGED
@@ -135,8 +135,9 @@ function hasRoutes(config) {
135
135
  /**
136
136
  * @param {import('@octanejs/app-core').ResolvedOctaneConfig} config
137
137
  * @param {boolean | undefined} strongOverride
138
+ * @param {boolean | undefined} nativeReadsOverride
138
139
  */
139
- function configSignature(config, strongOverride) {
140
+ function configSignature(config, strongOverride, nativeReadsOverride) {
140
141
  return JSON.stringify({
141
142
  build: config.build,
142
143
  routes: config.router.routes.map((route) =>
@@ -156,6 +157,7 @@ function configSignature(config, strongOverride) {
156
157
  compiler: {
157
158
  renderers: config.compiler.renderers.signature,
158
159
  strong: strongOverride ?? config.compiler.strong,
160
+ nativeReads: nativeReadsOverride ?? config.compiler.nativeReads,
159
161
  },
160
162
  });
161
163
  }
@@ -237,6 +239,7 @@ function assertRootPublicPaths(config, clientEnvironment) {
237
239
  * cssModuleConstants?: import('@octanejs/rspack-plugin').OctaneRspackPluginOptions['cssModuleConstants'],
238
240
  * profile?: boolean,
239
241
  * strong?: boolean,
242
+ * nativeReads?: boolean,
240
243
  * exclude?: string[],
241
244
  * requireDirective?: boolean,
242
245
  * clientEnvironment?: string,
@@ -245,6 +248,9 @@ function assertRootPublicPaths(config, clientEnvironment) {
245
248
  * @returns {import('@rsbuild/core').RsbuildPlugin}
246
249
  */
247
250
  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
+ }
248
254
  if (inlineOptions.strong !== undefined && typeof inlineOptions.strong !== 'boolean') {
249
255
  throw new TypeError('[@octanejs/rsbuild-plugin] `strong` must be a boolean.');
250
256
  }
@@ -266,17 +272,22 @@ export function pluginOctane(inlineOptions = {}) {
266
272
  : null;
267
273
  const initialConfig = initialLoaded?.config ?? null;
268
274
  const strong = inlineOptions.strong ?? initialConfig?.compiler.strong;
275
+ const nativeReads = inlineOptions.nativeReads ?? initialConfig?.compiler.nativeReads;
269
276
  const appEnabled = hasRoutes(initialConfig);
270
277
  const buildTargetPlan =
271
278
  initialConfig?.build.target === undefined
272
279
  ? null
273
280
  : createBuildTargetPlan(initialConfig.build.target);
274
281
  const neutralCompiler = appEnabled
275
- ? createOctaneCompiler({ root, ...(strong === undefined ? null : { strong }) })
282
+ ? createOctaneCompiler({
283
+ root,
284
+ ...(strong === undefined ? null : { strong }),
285
+ ...(nativeReads === undefined ? null : { nativeReads }),
286
+ })
276
287
  : null;
277
288
  let currentConfig = initialConfig;
278
289
  let lastConfigSignature = initialLoaded
279
- ? configSignature(initialLoaded.config, inlineOptions.strong)
290
+ ? configSignature(initialLoaded.config, inlineOptions.strong, inlineOptions.nativeReads)
280
291
  : '';
281
292
  let configNeedsReload = false;
282
293
  /** @type {import('@rsbuild/core').RsbuildDevServer | null} */
@@ -324,7 +335,11 @@ export function pluginOctane(inlineOptions = {}) {
324
335
  throw error;
325
336
  }
326
337
  registerConfigDependencies(context, loaded);
327
- const signature = configSignature(loaded.config, inlineOptions.strong);
338
+ const signature = configSignature(
339
+ loaded.config,
340
+ inlineOptions.strong,
341
+ inlineOptions.nativeReads,
342
+ );
328
343
  if (lastConfigSignature && signature !== lastConfigSignature) configNeedsReload = true;
329
344
  lastConfigSignature = signature;
330
345
  currentConfig = loaded.config;
@@ -553,6 +568,7 @@ export function pluginOctane(inlineOptions = {}) {
553
568
  ? null
554
569
  : { cssModuleConstants: inlineOptions.cssModuleConstants }),
555
570
  ...(strong === undefined ? null : { strong }),
571
+ ...(nativeReads === undefined ? null : { nativeReads }),
556
572
  ...(inlineOptions.hmr === undefined ? null : { hmr: inlineOptions.hmr }),
557
573
  ...(inlineOptions.profile === undefined
558
574
  ? null
package/src/project.js CHANGED
@@ -106,6 +106,7 @@ function maskNonCode(source) {
106
106
  * @param {string} id
107
107
  */
108
108
  function ownsServerModule(source, id) {
109
+ if (!source.includes('module') || !source.includes('server')) return false;
109
110
  if (!SERVER_MODULE_CANDIDATE.test(maskNonCode(source))) return false;
110
111
  const compiled = compile(source, id, { mode: 'server' });
111
112
  return COMPILED_SERVER_NAMESPACE.test(maskNonCode(compiled.code));
package/types/index.d.ts CHANGED
@@ -28,10 +28,13 @@ export interface OctaneRsbuildPluginOptions {
28
28
  /** Enable component profiling in the browser environment. */
29
29
  profile?: boolean;
30
30
  /**
31
- * Override `compiler.strong` for this integration. Strong mode applies to
32
- * project-owned modules; dependencies can opt in with `"use strong"`.
31
+ * Override `compiler.strong` for this integration. Strong asserts pure
32
+ * immutable-snapshot renders in project-owned modules; dependencies can opt
33
+ * in with `"use strong"`.
33
34
  */
34
35
  strong?: boolean;
36
+ /** Experimental native signal reads in DOM client/server render scopes. */
37
+ nativeReads?: boolean;
35
38
  /**
36
39
  * Ad-hoc path fragments skipped by the plain TypeScript/JavaScript
37
40
  * hook-slot pass. With `requireDirective`, excluded paths are exempt from