@octanejs/vite-plugin 0.1.47 → 0.1.49

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.47",
3
+ "version": "0.1.49",
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.125",
50
- "@octanejs/app-core": "0.0.43"
50
+ "@octanejs/app-core": "0.0.45"
51
51
  },
52
52
  "peerDependencies": {
53
53
  "vite": "^8.0.16",
54
- "octane": "0.1.47"
54
+ "octane": "0.1.49"
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.47"
61
+ "octane": "0.1.49"
62
62
  }
63
63
  }
@@ -39,6 +39,14 @@ export function createClientAssetMap(manifest, moduleIds, entryFiles = {}) {
39
39
  const manifestKeysByFile = new Map(
40
40
  Object.entries(manifest).map(([key, entry]) => [entry.file, key]),
41
41
  );
42
+ // Route entries usually converge on the same shared chunks. Cache only small,
43
+ // complete transitive CSS results for this immutable manifest build so a large
44
+ // shared JavaScript graph is walked once without retaining quadratic CSS lists.
45
+ const MAX_CACHED_CSS_FILES = 64;
46
+ /** @type {Map<string, string[]>} */
47
+ const cssCache = new Map();
48
+ let traversalVersion = 0;
49
+ let traversalDepth = 0;
42
50
 
43
51
  /**
44
52
  * @param {string} key
@@ -48,11 +56,22 @@ export function createClientAssetMap(manifest, moduleIds, entryFiles = {}) {
48
56
  */
49
57
  function collectCss(key, deferredHydrationBranch, visited) {
50
58
  const visitKey = `${deferredHydrationBranch ? 'deferred' : 'eager'}:${key}`;
51
- if (visited.has(visitKey)) return [];
59
+ if (visited.has(visitKey)) {
60
+ // This result is complete for the current route because the earlier path
61
+ // already supplied the subtree. It is not safe to reuse as a standalone
62
+ // result for another route; the version change prevents its active callers
63
+ // from caching their now-context-dependent results.
64
+ traversalVersion++;
65
+ return [];
66
+ }
52
67
  visited.add(visitKey);
68
+ const cached = cssCache.get(visitKey);
69
+ if (cached !== undefined) return cached;
53
70
  const entry = manifest[key];
54
71
  if (!entry) return [];
55
72
 
73
+ const version = traversalVersion;
74
+ traversalDepth++;
56
75
  const css = [...(entry.css || [])];
57
76
  for (const imported of entry.imports || []) {
58
77
  css.push(...collectCss(imported, deferredHydrationBranch, visited));
@@ -67,6 +86,11 @@ export function createClientAssetMap(manifest, moduleIds, entryFiles = {}) {
67
86
  css.push(...collectCss(imported, true, visited));
68
87
  }
69
88
  }
89
+ traversalDepth--;
90
+ // Top-level route entries are consumed once; caching them only grows the map.
91
+ if (traversalDepth > 0 && traversalVersion === version && css.length <= MAX_CACHED_CSS_FILES) {
92
+ cssCache.set(visitKey, css);
93
+ }
70
94
  return css;
71
95
  }
72
96
 
@@ -83,9 +107,11 @@ export function createClientAssetMap(manifest, moduleIds, entryFiles = {}) {
83
107
  : manifestKeysByFile.get(entryFiles[moduleId]);
84
108
  if (!manifestKey) continue;
85
109
  const entry = manifest[manifestKey];
110
+ const visited = new Set();
111
+ traversalVersion = 0;
86
112
  assets[moduleId] = {
87
113
  js: entry.file,
88
- css: [...new Set(collectCss(manifestKey, false, new Set()))],
114
+ css: [...new Set(collectCss(manifestKey, false, visited))],
89
115
  };
90
116
  }
91
117
  return assets;
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, strong?: boolean, exclude?: string[], requireDirective?: boolean, renderers?: import('@octanejs/app-core').ExperimentalRendererConfigOptions, cssModuleConstants?: import('octane/compiler/vite').OctaneVitePluginOptions['cssModuleConstants'] }} [inlineOptions]
224
+ * @param {{ hmr?: boolean, profile?: boolean, devtools?: boolean, strong?: boolean, nativeReads?: boolean, exclude?: string[], requireDirective?: boolean, renderers?: import('@octanejs/app-core').ExperimentalRendererConfigOptions, cssModuleConstants?: import('octane/compiler/vite').OctaneVitePluginOptions['cssModuleConstants'] }} [inlineOptions]
225
225
  * @returns {Plugin[]}
226
226
  */
227
227
  export function octane(inlineOptions = {}) {
@@ -881,6 +881,7 @@ export function octane(inlineOptions = {}) {
881
881
  * hmr?: boolean,
882
882
  * profile?: boolean | 'auto',
883
883
  * strong?: boolean,
884
+ * nativeReads?: boolean,
884
885
  * exclude?: string[],
885
886
  * requireDirective?: boolean,
886
887
  * renderers?: import('@octanejs/app-core').ExperimentalRendererConfigOptions,
@@ -894,6 +895,8 @@ export function octane(inlineOptions = {}) {
894
895
  if (inlineOptions.profile !== undefined) compilerOptions.profile = inlineOptions.profile;
895
896
  else if (inlineOptions.devtools === true) compilerOptions.profile = 'auto';
896
897
  if (inlineOptions.strong !== undefined) compilerOptions.strong = inlineOptions.strong;
898
+ if (inlineOptions.nativeReads !== undefined)
899
+ compilerOptions.nativeReads = inlineOptions.nativeReads;
897
900
  if (inlineOptions.exclude !== undefined) compilerOptions.exclude = inlineOptions.exclude;
898
901
  if (inlineOptions.requireDirective !== undefined) {
899
902
  compilerOptions.requireDirective = inlineOptions.requireDirective;
@@ -909,7 +912,11 @@ export function octane(inlineOptions = {}) {
909
912
  const projectRoot = userConfig.root ? path.resolve(userConfig.root) : process.cwd();
910
913
  // Both inline compiler options override app config independently. Preserve
911
914
  // the synchronous path when neither needs to read octane.config.ts.
912
- if (inlineOptions.renderers !== undefined && inlineOptions.strong !== undefined) {
915
+ if (
916
+ inlineOptions.renderers !== undefined &&
917
+ inlineOptions.strong !== undefined &&
918
+ inlineOptions.nativeReads !== undefined
919
+ ) {
913
920
  compilerConfigWatchFiles.clear();
914
921
  return compilerConfigHook.call(this, userConfig, env);
915
922
  }
@@ -918,6 +925,7 @@ export function octane(inlineOptions = {}) {
918
925
  if (!octaneConfigExists(projectRoot)) {
919
926
  if (inlineOptions.renderers === undefined) delete compilerOptions.renderers;
920
927
  if (inlineOptions.strong === undefined) delete compilerOptions.strong;
928
+ if (inlineOptions.nativeReads === undefined) delete compilerOptions.nativeReads;
921
929
  compilerConfigWatchFiles.clear();
922
930
  // A new app config can introduce Strong mode or renderer rules.
923
931
  compilerConfigWatchFiles.add(path.resolve(configPath));
@@ -932,6 +940,9 @@ export function octane(inlineOptions = {}) {
932
940
  if (inlineOptions.strong === undefined) {
933
941
  compilerOptions.strong = config.config.compiler.strong;
934
942
  }
943
+ if (inlineOptions.nativeReads === undefined) {
944
+ compilerOptions.nativeReads = config.config.compiler.nativeReads;
945
+ }
935
946
  compilerConfigWatchFiles.clear();
936
947
  for (const file of [...config.dependencies, ...config.missingDependencies]) {
937
948
  compilerConfigWatchFiles.add(path.resolve(file));
package/types/index.d.ts CHANGED
@@ -15,8 +15,10 @@ export interface OctanePluginOptions {
15
15
  hmr?: boolean;
16
16
  /** Enable component profiling in client transforms. */
17
17
  profile?: boolean;
18
- /** Enable Strong mode for app code, overriding `compiler.strong` in `octane.config.ts`. */
18
+ /** Assert Strong mode's pure immutable-snapshot render contract for app code. */
19
19
  strong?: boolean;
20
+ /** Experimental native signal reads in DOM client/server render scopes. */
21
+ nativeReads?: boolean;
20
22
  /**
21
23
  * Path fragments the compiler's plain `.ts`/`.js` hook-slotting pass must
22
24
  * skip. Prefer package manifest `octane.hookSlots.manual` declarations.