@octanejs/vite-plugin 0.1.19 → 0.1.21

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/vite-plugin",
3
- "version": "0.1.19",
3
+ "version": "0.1.21",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "engines": {
@@ -47,17 +47,17 @@
47
47
  },
48
48
  "dependencies": {
49
49
  "@ripple-ts/adapter": "^0.3.118",
50
- "@octanejs/app-core": "0.0.15"
50
+ "@octanejs/app-core": "0.0.17"
51
51
  },
52
52
  "peerDependencies": {
53
53
  "vite": "^8.0.16",
54
- "octane": "0.1.19"
54
+ "octane": "0.1.21"
55
55
  },
56
56
  "devDependencies": {
57
57
  "@types/node": "^24.13.3",
58
58
  "playwright": "^1.61.1",
59
59
  "type-fest": "^5.8.0",
60
60
  "vite": "^8.1.5",
61
- "octane": "0.1.19"
61
+ "octane": "0.1.21"
62
62
  }
63
63
  }
package/src/index.js CHANGED
@@ -221,7 +221,7 @@ function collect_hydrate_module_paths(config) {
221
221
  * it). An explicit `profile` (true or false) always takes precedence over
222
222
  * `devtools`.
223
223
  *
224
- * @param {{ hmr?: boolean, profile?: boolean, devtools?: boolean, exclude?: string[], requireDirective?: boolean, renderers?: import('@octanejs/app-core').ExperimentalRendererConfigOptions }} [inlineOptions]
224
+ * @param {{ hmr?: boolean, profile?: boolean, devtools?: boolean, strong?: boolean, exclude?: string[], requireDirective?: boolean, renderers?: import('@octanejs/app-core').ExperimentalRendererConfigOptions }} [inlineOptions]
225
225
  * @returns {Plugin[]}
226
226
  */
227
227
  export function octane(inlineOptions = {}) {
@@ -238,12 +238,12 @@ export function octane(inlineOptions = {}) {
238
238
  /** @type {boolean} Is this the SSR sub-build closeBundle launches? */
239
239
  let isSSRBuild = false;
240
240
  /**
241
- * Config dependencies that select compiler renderers. A change requires a
242
- * server restart because the neutral compiler snapshots normalized renderer
243
- * metadata before the first module transform.
241
+ * Config dependencies that select Strong mode or compiler renderers. A
242
+ * change requires a server restart because the neutral compiler snapshots
243
+ * its configuration before the first module transform.
244
244
  * @type {Set<string>}
245
245
  */
246
- const rendererConfigWatchFiles = new Set();
246
+ const compilerConfigWatchFiles = new Set();
247
247
  /** @type {Map<string, Promise<LoadedOctaneConfig | null>>} */
248
248
  const startupConfigLoads = new Map();
249
249
  /** @type {ResolvedOctaneConfig | null} Config loaded for the build (config hook, reused in closeBundle) */
@@ -494,8 +494,8 @@ export function octane(inlineOptions = {}) {
494
494
  * @param {ViteDevServer} vite
495
495
  */
496
496
  configureServer(vite) {
497
- if (rendererConfigWatchFiles.size > 0) {
498
- vite.watcher.add([...rendererConfigWatchFiles]);
497
+ if (compilerConfigWatchFiles.size > 0) {
498
+ vite.watcher.add([...compilerConfigWatchFiles]);
499
499
  }
500
500
  /** @type {Promise<void> | null} */
501
501
  let initPromise = null;
@@ -669,10 +669,9 @@ export function octane(inlineOptions = {}) {
669
669
  order: 'pre',
670
670
  async handler({ file, modules, server }) {
671
671
  if (this.environment.name !== 'client') return;
672
- if (rendererConfigWatchFiles.has(path.resolve(file))) {
673
- // Renderer rules and boundary metadata are immutable inputs to every
674
- // compiler environment. Rebuild the plugin/compiler snapshot instead
675
- // of letting later transforms observe a mixture of old and new config.
672
+ if (compilerConfigWatchFiles.has(path.resolve(file))) {
673
+ // Strong mode and renderer metadata are immutable compiler inputs.
674
+ // Restart so later transforms cannot mix old and new configuration.
676
675
  await server.restart();
677
676
  return [];
678
677
  }
@@ -881,6 +880,7 @@ export function octane(inlineOptions = {}) {
881
880
  * @type {{
882
881
  * hmr?: boolean,
883
882
  * profile?: boolean | 'auto',
883
+ * strong?: boolean,
884
884
  * exclude?: string[],
885
885
  * requireDirective?: boolean,
886
886
  * renderers?: import('@octanejs/app-core').ExperimentalRendererConfigOptions,
@@ -892,6 +892,7 @@ export function octane(inlineOptions = {}) {
892
892
  // command-aware `'auto'` signal the compiler resolves to DEV-only profiling.
893
893
  if (inlineOptions.profile !== undefined) compilerOptions.profile = inlineOptions.profile;
894
894
  else if (inlineOptions.devtools === true) compilerOptions.profile = 'auto';
895
+ if (inlineOptions.strong !== undefined) compilerOptions.strong = inlineOptions.strong;
895
896
  if (inlineOptions.exclude !== undefined) compilerOptions.exclude = inlineOptions.exclude;
896
897
  if (inlineOptions.requireDirective !== undefined) {
897
898
  compilerOptions.requireDirective = inlineOptions.requireDirective;
@@ -900,31 +901,36 @@ export function octane(inlineOptions = {}) {
900
901
  const compilerPlugin = /** @type {Plugin} */ (octaneCompiler(compilerOptions));
901
902
  const compilerConfigHook = compilerPlugin.config;
902
903
  if (typeof compilerConfigHook === 'function') {
903
- compilerPlugin.config = function compilerConfigWithAppRenderers(userConfig, env) {
904
+ compilerPlugin.config = function compilerConfigWithAppOptions(userConfig, env) {
904
905
  const projectRoot = userConfig.root ? path.resolve(userConfig.root) : process.cwd();
905
- // Inline renderer metadata is an explicit full override. Preserve the
906
- // synchronous no-config/inline path used by compiler-only SPA projects.
907
- if (inlineOptions.renderers !== undefined) {
908
- rendererConfigWatchFiles.clear();
906
+ // Both inline compiler options override app config independently. Preserve
907
+ // the synchronous path when neither needs to read octane.config.ts.
908
+ if (inlineOptions.renderers !== undefined && inlineOptions.strong !== undefined) {
909
+ compilerConfigWatchFiles.clear();
909
910
  return compilerConfigHook.call(this, userConfig, env);
910
911
  }
911
912
 
912
913
  const configPath = getOctaneConfigPath(projectRoot);
913
914
  if (!octaneConfigExists(projectRoot)) {
914
- delete compilerOptions.renderers;
915
- rendererConfigWatchFiles.clear();
916
- // A newly-created octane.config.ts can introduce renderer rules. Watch
917
- // the missing path so dev restarts into the configured compiler.
918
- rendererConfigWatchFiles.add(path.resolve(configPath));
915
+ if (inlineOptions.renderers === undefined) delete compilerOptions.renderers;
916
+ if (inlineOptions.strong === undefined) delete compilerOptions.strong;
917
+ compilerConfigWatchFiles.clear();
918
+ // A new app config can introduce Strong mode or renderer rules.
919
+ compilerConfigWatchFiles.add(path.resolve(configPath));
919
920
  return compilerConfigHook.call(this, userConfig, env);
920
921
  }
921
922
 
922
923
  return loadStartupConfig(projectRoot).then((loaded) => {
923
924
  const config = /** @type {LoadedOctaneConfig} */ (loaded);
924
- compilerOptions.renderers = config.config.compiler.renderers;
925
- rendererConfigWatchFiles.clear();
925
+ if (inlineOptions.renderers === undefined) {
926
+ compilerOptions.renderers = config.config.compiler.renderers;
927
+ }
928
+ if (inlineOptions.strong === undefined) {
929
+ compilerOptions.strong = config.config.compiler.strong;
930
+ }
931
+ compilerConfigWatchFiles.clear();
926
932
  for (const file of [...config.dependencies, ...config.missingDependencies]) {
927
- rendererConfigWatchFiles.add(path.resolve(file));
933
+ compilerConfigWatchFiles.add(path.resolve(file));
928
934
  }
929
935
  return compilerConfigHook.call(this, userConfig, env);
930
936
  });
@@ -970,6 +976,13 @@ async function handleRpcRequest(req, res, vite, trustProxy, config) {
970
976
  if (!server || !server[funcName]) return null;
971
977
  return server[funcName];
972
978
  },
979
+ describeFunction(hash) {
980
+ const rpcModules = /** @type {any} */ (globalThis).rpc_modules;
981
+ const moduleInfo = rpcModules?.get(hash);
982
+ if (!moduleInfo) return null;
983
+ const [filePath, funcName] = moduleInfo;
984
+ return { module: filePath, export: funcName };
985
+ },
973
986
  async executeServerFunction(fn, body) {
974
987
  const { executeServerFunction } = await vite.ssrLoadModule('octane/server');
975
988
  return executeServerFunction(fn, body);
@@ -1,6 +1,7 @@
1
1
  // @ts-check
2
2
  import { readFile } from 'node:fs/promises';
3
3
  import { join } from 'node:path';
4
+ import { createRpcRegistry } from '@octanejs/app-core';
4
5
  import { composeHtmlStream } from './html-stream.js';
5
6
  import {
6
7
  getContextNonce,
@@ -61,7 +62,7 @@ export async function handleRenderRoute(route, context, vite, octaneConfig) {
61
62
  // declarations during SSR module loading (renderer-agnostic; harmless when
62
63
  // the app uses no RPC).
63
64
  if (!(/** @type {any} */ (globalThis).rpc_modules)) {
64
- /** @type {any} */ (globalThis).rpc_modules = new Map();
65
+ /** @type {any} */ (globalThis).rpc_modules = createRpcRegistry();
65
66
  }
66
67
 
67
68
  // Load the octane streaming renderer. The wrappers call components
package/types/index.d.ts CHANGED
@@ -14,6 +14,8 @@ export interface OctanePluginOptions {
14
14
  hmr?: boolean;
15
15
  /** Enable component profiling in client transforms. */
16
16
  profile?: boolean;
17
+ /** Enable Strong mode for app code, overriding `compiler.strong` in `octane.config.ts`. */
18
+ strong?: boolean;
17
19
  /**
18
20
  * Path fragments the compiler's plain `.ts`/`.js` hook-slotting pass must
19
21
  * skip. Prefer package manifest `octane.hookSlots.manual` declarations.