octane 0.16.6 → 0.16.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.
@@ -2293,9 +2293,45 @@ function flushSync(fn) {
2293
2293
  if (import_drivers.CONTROLLED_RESTORE_DRIVER) import_drivers.CONTROLLED_RESTORE_DRIVER.restore();
2294
2294
  return result;
2295
2295
  }
2296
+ const DETACHES_BY_TARGET = /* @__PURE__ */ new Map();
2297
+ function dropUncommittedRefPairs(detaches) {
2298
+ const byTarget = DETACHES_BY_TARGET;
2299
+ for (let i = 0; i < detaches.length; i += 4) {
2300
+ const queued = byTarget.get(detaches[i + 1]);
2301
+ if (queued === void 0) byTarget.set(detaches[i + 1], i);
2302
+ else if (typeof queued === "number") byTarget.set(detaches[i + 1], [queued, i]);
2303
+ else queued.push(i);
2304
+ }
2305
+ let write = 0;
2306
+ for (let read = 0; read < refAttachQueue.length; read++) {
2307
+ const entry = refAttachQueue[read];
2308
+ const queued = byTarget.get(entry.el);
2309
+ if (queued !== void 0) {
2310
+ const live = entry.ref === attachLiveFragmentRef && (import_drivers.activityRefState ? import_drivers.activityRefState.get(entry.el)?.connected : null) == null;
2311
+ if (typeof queued === "number") {
2312
+ if (live || detaches[queued] === entry.ref) {
2313
+ detaches[queued] = null;
2314
+ byTarget.delete(entry.el);
2315
+ continue;
2316
+ }
2317
+ } else {
2318
+ const k = live ? 0 : queued.findIndex((i) => detaches[i] === entry.ref);
2319
+ if (k !== -1 && k < queued.length) {
2320
+ detaches[queued[k]] = null;
2321
+ queued.splice(k, 1);
2322
+ continue;
2323
+ }
2324
+ }
2325
+ }
2326
+ refAttachQueue[write++] = entry;
2327
+ }
2328
+ refAttachQueue.length = write;
2329
+ byTarget.clear();
2330
+ }
2296
2331
  function drainQueuedRefDetaches() {
2297
2332
  if (import_refs.refDetachQueue.length === 0) return;
2298
2333
  const q = import_refs.refDetachQueue.splice(0);
2334
+ if (refAttachQueue.length !== 0) dropUncommittedRefPairs(q);
2299
2335
  for (let i = 0; i < q.length; i += 4) {
2300
2336
  try {
2301
2337
  REF_CALLBACK_DEPTH++;
@@ -507,7 +507,11 @@ function readReducerHook(reducer, initialArg, initOrSlot, slot) {
507
507
  function __useReducerWithGetter(reducer, initialArg, initOrSlot, slot) {
508
508
  const s = readReducerHook(reducer, initialArg, initOrSlot, slot);
509
509
  const getter = s.getter ??= () => latestHookValue(s, s.reducer);
510
- return [s.queue === null ? s.value : foldHookQueue(s, reducer), s.dispatch, getter];
510
+ return [
511
+ s.queue === null ? s.value : foldHookQueue(s, reducer),
512
+ s.dispatch,
513
+ getter
514
+ ];
511
515
  }
512
516
  function depsChanged(prev, next, hook = false) {
513
517
  if (prev === void 0 || next === void 0) return true;
@@ -330,18 +330,19 @@ function renderPortalState(prev, parentBlock, target, rawBody, rawProps, host, e
330
330
  state.start.$$portalParent = host;
331
331
  }
332
332
  state.host = host;
333
- if (state.block.body !== norm.body)
334
- (0, import_engine.journalRootProperty)(state.block, "body", state.block.body);
335
- if (state.block.props !== norm.props)
336
- (0, import_engine.journalRootProperty)(state.block, "props", state.block.props);
337
- if (state.block.extra !== env) (0, import_engine.journalRootProperty)(state.block, "extra", state.block.extra);
338
- state.block.body = norm.body;
339
- state.block.props = norm.props;
340
- state.block.extra = env;
341
- if (typeof __OCTANE_PROFILE_ENABLED__ !== "undefined" && __OCTANE_PROFILE_ENABLED__) {
342
- (0, import_profiling.__profileTrackComponent)(state.block, (0, import_profile_probes.profilePortalComponent)(rawBody, norm.body));
333
+ const block = state.block;
334
+ if (!(import_drivers.MEMO_BAIL && block.body === norm.body && (0, import_drivers.MEMO_BAIL)(block, norm.body, norm.props))) {
335
+ if (block.body !== norm.body) (0, import_engine.journalRootProperty)(block, "body", block.body);
336
+ if (block.props !== norm.props) (0, import_engine.journalRootProperty)(block, "props", block.props);
337
+ if (block.extra !== env) (0, import_engine.journalRootProperty)(block, "extra", block.extra);
338
+ block.body = norm.body;
339
+ block.props = norm.props;
340
+ block.extra = env;
341
+ if (typeof __OCTANE_PROFILE_ENABLED__ !== "undefined" && __OCTANE_PROFILE_ENABLED__) {
342
+ (0, import_profiling.__profileTrackComponent)(block, (0, import_profile_probes.profilePortalComponent)(rawBody, norm.body));
343
+ }
344
+ (0, import_signal_owners.renderBlock)(block);
343
345
  }
344
- (0, import_signal_owners.renderBlock)(state.block);
345
346
  }
346
347
  let n = (0, import_templates.getNextSibling)(state.start);
347
348
  if (state.interleavedFragment === void 0) {
@@ -21,7 +21,7 @@ __export(version_exports, {
21
21
  version: () => version
22
22
  });
23
23
  module.exports = __toCommonJS(version_exports);
24
- const version = "0.16.6";
24
+ const version = "0.16.7";
25
25
  // Annotate the CommonJS export names for ESM import in node:
26
26
  0 && (module.exports = {
27
27
  version
@@ -0,0 +1,306 @@
1
+ import type {
2
+ CompileDiagnostic,
3
+ CompileOptions,
4
+ CompileResult,
5
+ CompilerProgram,
6
+ CompileSourceMap,
7
+ OctaneCssModuleConstants,
8
+ } from './index.js';
9
+ import type {
10
+ OctaneRendererConfigOptions,
11
+ OctaneResolvedRenderer,
12
+ OctaneResolvedRendererConfig,
13
+ } from './renderers.js';
14
+
15
+ export { DOM_BINDING_COMPILER_ABI_VERSION } from './index.js';
16
+ export {
17
+ DOM_RENDERER_ID,
18
+ DOM_RENDERER_MODULE,
19
+ RENDERER_CONFIG_VERSION,
20
+ normalizeRendererConfig,
21
+ resolveRendererForFile,
22
+ } from './renderers.js';
23
+ export type {
24
+ OctaneRendererConfigOptions,
25
+ OctaneResolvedRenderer,
26
+ OctaneResolvedRendererConfig,
27
+ } from './renderers.js';
28
+ export { findVoidComponentImports, findVoidRootImports } from './slot-hooks.js';
29
+ export type { VoidComponentImport } from './slot-hooks.js';
30
+
31
+ export type OctaneEnvironment = 'client' | 'server';
32
+
33
+ /** Options for one compiler instance; `transform` may override the per-module ones. */
34
+ export interface OctaneCompilerOptions extends Pick<
35
+ CompileOptions,
36
+ 'knownAttributeSpreads' | 'domBindingFixedProps' | 'opaqueSignalHandles' | 'universalRuntime'
37
+ > {
38
+ /** Project root for canonical module IDs and package-manifest discovery. @default process.cwd() */
39
+ root?: string;
40
+ /** @default 'client' */
41
+ environment?: OctaneEnvironment;
42
+ /** Opt into a typed compiler intermediate. The host emits JavaScript and composes maps. */
43
+ output?: 'js' | 'ts';
44
+ /** HMR code generation dialect; `true` means `'vite'`. Server transforms never emit HMR. */
45
+ hmr?: boolean | 'vite' | 'webpack';
46
+ /** Client development metadata. Defaults on whenever client HMR is on. */
47
+ dev?: boolean;
48
+ /** Component profiling metadata in client transforms. */
49
+ profile?: boolean;
50
+ /** @default true */
51
+ inlineHookMemo?: boolean;
52
+ /**
53
+ * Assert pure immutable-snapshot renders in application-owned modules.
54
+ * Individual modules can opt in with a top-level `"use strong"` directive.
55
+ * @default false
56
+ */
57
+ strong?: boolean;
58
+ /** Path fragments excluded from Octane's plain `.ts`/`.js` hook-slot pass. */
59
+ exclude?: readonly string[];
60
+ /**
61
+ * Mixed-toolchain ownership gate: a project `.tsx`, `.ts`, or `.js` module is
62
+ * Octane's only with a leading `@jsxImportSource octane` pragma.
63
+ * @default false
64
+ */
65
+ requireDirective?: boolean;
66
+ /** @experimental Declarative renderer selection for this compiler instance. */
67
+ renderers?: OctaneRendererConfigOptions | OctaneResolvedRendererConfig;
68
+ /** Receives ownership warnings and nonfatal compiler diagnostics, once per watch generation. */
69
+ warn?: (message: string) => void;
70
+ }
71
+
72
+ export interface OctaneTransformOptions
73
+ extends
74
+ Pick<
75
+ OctaneCompilerOptions,
76
+ | 'environment'
77
+ | 'output'
78
+ | 'hmr'
79
+ | 'dev'
80
+ | 'profile'
81
+ | 'inlineHookMemo'
82
+ | 'strong'
83
+ | 'knownAttributeSpreads'
84
+ | 'domBindingFixedProps'
85
+ | 'opaqueSignalHandles'
86
+ | 'universalRuntime'
87
+ >,
88
+ Pick<
89
+ CompileOptions,
90
+ | 'textTypeFacts'
91
+ | 'clientOnlyImports'
92
+ | 'resolveCssModuleConstant'
93
+ | 'preserveCssModuleReferences'
94
+ | 'isVoidComponentImport'
95
+ | 'isCompilerMemoImport'
96
+ | 'isLiteComponentImport'
97
+ | 'isStrongLiteComponentImport'
98
+ | 'isAnchorlessComponentImport'
99
+ | 'isDescriptorChildrenImport'
100
+ > {
101
+ /** Rewrite server runtime requests (`octane` to `octane/server`) in the output. */
102
+ explicitRuntimeRequests?: boolean;
103
+ /** Report `voidComponentExports` for a compiled client module. */
104
+ collectVoidComponentExports?: boolean;
105
+ }
106
+
107
+ /** Manifests that decided a result; a host registers them as watch dependencies. */
108
+ export interface OctaneWatchDependencies {
109
+ dependencies: string[];
110
+ missingDependencies: string[];
111
+ }
112
+
113
+ /** Server-graph identity of a module owned by a client-only renderer. */
114
+ export interface OctaneClientReference {
115
+ readonly id: string;
116
+ readonly moduleId: string;
117
+ readonly renderer: string;
118
+ }
119
+
120
+ export interface OctaneTransformResult
121
+ extends
122
+ OctaneWatchDependencies,
123
+ Pick<
124
+ CompileResult,
125
+ 'lang' | 'streamedSignals' | 'universalRuntime' | 'compilerMemoExports' | 'bindingConstants'
126
+ > {
127
+ code: string;
128
+ map: CompileSourceMap | null;
129
+ /**
130
+ * `compile`: full Octane compilation. `slots`: hook slotting of a plain
131
+ * module. `client-only-stub`: an export-preserving server stub.
132
+ * `runtime-requests`: only runtime requests were rewritten. `none`: the
133
+ * source is unchanged and returned only to report watch dependencies.
134
+ */
135
+ kind: 'compile' | 'slots' | 'client-only-stub' | 'runtime-requests' | 'none';
136
+ diagnostics?: readonly CompileDiagnostic[];
137
+ renderer?: OctaneResolvedRenderer;
138
+ clientReference?: OctaneClientReference;
139
+ /** Export names of a client-only server stub. */
140
+ clientOnlyExports?: readonly string[];
141
+ /** The printed client-only server stub. */
142
+ ast?: CompilerProgram;
143
+ // Build-time metadata adapters publish to importers of this module.
144
+ descriptorChildrenExports?: readonly string[];
145
+ voidComponentExports?: readonly string[];
146
+ liteComponentExports?: readonly string[];
147
+ strongLiteComponentExports?: readonly string[];
148
+ anchorlessComponentExports?: readonly string[];
149
+ cssModuleConstantImports?: readonly unknown[];
150
+ independentWidgets?: readonly unknown[];
151
+ }
152
+
153
+ export interface OctaneSourceDependencies extends OctaneWatchDependencies {
154
+ /** Installed packages that ship Octane source, following their runtime dependencies. */
155
+ packages: string[];
156
+ /** Package IDs those packages ask Vite not to pre-bundle. */
157
+ viteOptimizeDepsExclusions: string[];
158
+ }
159
+
160
+ /** The bundler-neutral compiler that every Octane bundler adapter drives. */
161
+ export interface OctaneCompiler {
162
+ /**
163
+ * Compile or hook-slot one module, or return `null` when it passes through
164
+ * unchanged with no watch dependencies.
165
+ */
166
+ transform(
167
+ code: string,
168
+ id: string,
169
+ options?: OctaneTransformOptions,
170
+ ): OctaneTransformResult | null;
171
+ /** Clear cached manifest decisions after a watched path changes, or all of them. */
172
+ invalidate(path?: string | null): void;
173
+ discoverSourceDependencies(): OctaneSourceDependencies;
174
+ /** The privacy-safe source identity used by profiling metadata. */
175
+ resolveProfileModuleId(id: string): OctaneWatchDependencies & { id: string };
176
+ resolveRuntimeRequest(request: string, environment?: OctaneEnvironment): string | null;
177
+ /** Static requests an adapter resolves before a server transform. */
178
+ findServerImportRequests(code: string, id: string): string[];
179
+ /** CSS-module requests the host may prove constant, behind the same ownership gate. */
180
+ findCssModuleImportRequests(
181
+ code: string,
182
+ id: string,
183
+ environment?: OctaneEnvironment,
184
+ parsedAst?: CompilerProgram | null,
185
+ ): string[];
186
+ /** Classify a bundler-resolved module without loading or evaluating it. */
187
+ clientReferenceForFile(id: string): OctaneClientReference | null;
188
+ /** How Strong mode reaches a module: its own directive, the `strong` option, or neither. */
189
+ strongModuleStatus(
190
+ code: string,
191
+ id: string,
192
+ ): {
193
+ strong: boolean;
194
+ directive: boolean;
195
+ config: boolean;
196
+ };
197
+ /** The package owning a module by its nearest manifest, and whether `transform` may change it. */
198
+ modulePackage(
199
+ code: string,
200
+ id: string,
201
+ ): {
202
+ name: string | null;
203
+ root: string | null;
204
+ compiled: boolean;
205
+ };
206
+ }
207
+
208
+ export function createOctaneCompiler(options?: OctaneCompilerOptions): OctaneCompiler;
209
+
210
+ /** Discover raw-source Octane dependencies from the nearest owning package manifest. */
211
+ export function discoverOctaneSourceDependencies(projectRoot: string): string[];
212
+
213
+ export const OCTANE_RUNTIME_REQUESTS: Readonly<{ client: 'octane'; server: 'octane/server' }>;
214
+
215
+ /** The server or client runtime request for an Octane runtime import, or `null` for any other. */
216
+ export function resolveOctaneRuntimeRequest(
217
+ request: string,
218
+ environment: OctaneEnvironment,
219
+ ): string | null;
220
+
221
+ /** Strip bundler query/hash suffixes without changing the underlying path. */
222
+ export function cleanModuleId(id: string): string;
223
+
224
+ /** The stable module ID embedded in compiler output: root-relative POSIX inside the root. */
225
+ export function canonicalModuleId(id: string, projectRoot?: string): string;
226
+
227
+ /** Whether an installed package reaches, through non-Octane packages, one Vite serves as CommonJS. */
228
+ export function packageReachesCommonJs(root: string): boolean;
229
+
230
+ /** Read the module specifier of a leading `@jsxImportSource` pragma. */
231
+ export function findLeadingJsxImportSourcePragma(code: string): string | null;
232
+
233
+ export const HYDRATE_QUERY_PARAM: 'octane-hydrate';
234
+ export const INDEPENDENT_HYDRATION_MANIFEST_FILENAME: 'octane-independent-hydration.json';
235
+
236
+ /** Direct function exports whose authored body never returns a renderable value. */
237
+ export function findVoidComponentExports(source: string | CompilerProgram, id: string): string[];
238
+
239
+ /** Facts about a module's final JavaScript after every bundler transform, or `null` if it does not parse. */
240
+ export function analyzeCompiledModule(
241
+ source: string,
242
+ id: string,
243
+ ): {
244
+ voidComponentExports: string[];
245
+ /** `imported` is `default` or `*` for those import forms. */
246
+ importBindings: { local: string; request: string; imported: string }[];
247
+ } | null;
248
+
249
+ export function findDescriptorChildrenExports(
250
+ source: string | CompilerProgram,
251
+ id: string,
252
+ ): string[];
253
+
254
+ /** Imported components rendered as JSX or re-exported, as descriptor-children candidates. */
255
+ export function findDescriptorChildrenImports(
256
+ source: string | CompilerProgram,
257
+ id: string,
258
+ ): { request: string; imported: string; local?: string; exported?: string }[];
259
+
260
+ export function isPlainCssModuleId(id: string): boolean;
261
+
262
+ /** A CSS provider's literal string exports, read without running it. */
263
+ export interface OctaneCssModuleExports {
264
+ named: Map<string, string>;
265
+ default: Map<string, string> | null;
266
+ pure: boolean;
267
+ }
268
+
269
+ export function readCssModuleExports(
270
+ source: string,
271
+ options?: { allowPureVar?: boolean },
272
+ ): OctaneCssModuleExports | null;
273
+
274
+ /** Validate a host assertion against the exact module it describes; throws when it does not hold. */
275
+ export function validateCssModuleConstants(
276
+ provided: OctaneCssModuleConstants | null | undefined,
277
+ exports: OctaneCssModuleExports | null,
278
+ id: string,
279
+ diagnosticOwner?: string,
280
+ ): { named: Map<string, string>; default: Map<string, string> } | null;
281
+
282
+ /**
283
+ * Fail-closed check of a `hydrate: 'islands'` shell: constructs that need
284
+ * client work, and the imported components to check the same way.
285
+ */
286
+ export function analyzeIslandsShell(
287
+ source: string,
288
+ filename: string,
289
+ exports?: readonly string[] | null,
290
+ options?: { values?: boolean },
291
+ ): {
292
+ problems: { message: string; line: number; column: number }[];
293
+ components: { source: string; exportName: string; value?: true }[];
294
+ };
295
+
296
+ export const CLIENT_REFERENCE_MANIFEST_FILENAME: 'octane-client-references.json';
297
+ export const CLIENT_REFERENCE_MANIFEST_VERSION: 1;
298
+
299
+ /** Merge the chunks each adapter found for client references into one deterministic manifest. */
300
+ export function createClientReferenceManifest(
301
+ entries:
302
+ Iterable<{ reference: OctaneClientReference; chunks?: Iterable<string> }> | null | undefined,
303
+ ): {
304
+ version: typeof CLIENT_REFERENCE_MANIFEST_VERSION;
305
+ references: Record<string, { moduleId: string; renderer: string; chunks: string[] }>;
306
+ };
@@ -265,6 +265,57 @@ function realPathOrSelf(dir) {
265
265
  }
266
266
  }
267
267
 
268
+ /**
269
+ * Whether Vite would serve this package as CommonJS: its entry is a `.js` or
270
+ * `.cjs` file outside a `"type": "module"` scope, and neither a `module` field
271
+ * nor an `import` condition offers ESM.
272
+ */
273
+ function servesCommonJs(pkg) {
274
+ if (pkg.type === 'module' || typeof pkg.module === 'string') return false;
275
+ const exports = JSON.stringify(pkg.exports ?? null);
276
+ if (/"(?:import|module)":|\.mjs"/.test(exports)) return false;
277
+ return (
278
+ /\.c?js"/.test(exports) ||
279
+ (typeof pkg.main === 'string' && /(?:^|\/)[^./]+$|\.c?js$/.test(pkg.main))
280
+ );
281
+ }
282
+
283
+ /**
284
+ * Whether the installed package at `root` depends, through packages other than
285
+ * Octane source packages, on one Vite would serve as CommonJS. Reads manifests
286
+ * only: the package's dependencies and peers, then their dependencies.
287
+ */
288
+ export function packageReachesCommonJs(root) {
289
+ const unwatched = { dependencies: new Set(), missingDependencies: new Set() };
290
+ const seen = new Set();
291
+ const pending = [root];
292
+ while (pending.length > 0) {
293
+ const dir = pending.pop();
294
+ if (seen.has(dir)) continue;
295
+ seen.add(dir);
296
+ let pkg;
297
+ try {
298
+ pkg = JSON.parse(nodeFs.readFileSync(nodePath.join(dir, 'package.json'), 'utf8'));
299
+ } catch {
300
+ continue;
301
+ }
302
+ if (dir !== root) {
303
+ if (packageUsesOctane(pkg, dir)) continue;
304
+ if (servesCommonJs(pkg)) return true;
305
+ }
306
+ for (const name of Object.keys({
307
+ ...(dir === root ? pkg.peerDependencies : null),
308
+ ...pkg.dependencies,
309
+ ...pkg.optionalDependencies,
310
+ })) {
311
+ if (name.startsWith('@types/')) continue;
312
+ const manifest = resolveInstalledPackageManifest(name, dir, unwatched);
313
+ if (manifest !== null) pending.push(realPathOrSelf(nodePath.dirname(manifest)));
314
+ }
315
+ }
316
+ return false;
317
+ }
318
+
268
319
  function metadata(dependencies = [], missingDependencies = []) {
269
320
  return { dependencies, missingDependencies };
270
321
  }
@@ -998,7 +1049,7 @@ class OctaneBundlerCompiler {
998
1049
  return isPathInside(this.root, absoluteFile) || isPathInside(this.realRoot, absoluteFile);
999
1050
  }
1000
1051
 
1001
- _hasApplicationStrongPolicy(file, collected) {
1052
+ _isApplicationSource(file, collected) {
1002
1053
  if (!this._isProjectOwnedSource(file)) return false;
1003
1054
  const absoluteFile = nodePath.isAbsolute(file)
1004
1055
  ? nodePath.resolve(file)
@@ -1031,7 +1082,7 @@ class OctaneBundlerCompiler {
1031
1082
  const file = cleanModuleId(id);
1032
1083
  const config =
1033
1084
  this.defaults.strong === true &&
1034
- this._hasApplicationStrongPolicy(file, {
1085
+ this._isApplicationSource(file, {
1035
1086
  dependencies: new Set(),
1036
1087
  missingDependencies: new Set(),
1037
1088
  });
@@ -1567,7 +1618,7 @@ class OctaneBundlerCompiler {
1567
1618
  // directive, which the authored-source compiler resolves separately.
1568
1619
  const strong =
1569
1620
  (options.strong ?? this.defaults.strong) === true &&
1570
- this._hasApplicationStrongPolicy(file, collected);
1621
+ this._isApplicationSource(file, collected);
1571
1622
  const universalRuntime = normalizeUniversalRuntime(
1572
1623
  options.universalRuntime ?? this.defaults.universalRuntime,
1573
1624
  );
@@ -1816,7 +1867,6 @@ class OctaneBundlerCompiler {
1816
1867
  }
1817
1868
  return passThrough();
1818
1869
  }
1819
- const hasHookRuntimeImport = importsHookRuntime(code);
1820
1870
  // Hook slotting is an Octane-ownership rewrite, so the ownership
1821
1871
  // gate applies to it exactly as to full compilation: an unmarked
1822
1872
  // project module stays with the host pipeline (with the forgotten-
@@ -1826,13 +1876,19 @@ class OctaneBundlerCompiler {
1826
1876
  return passThrough();
1827
1877
  }
1828
1878
  const manualSlots = this._hasManualHookSlots(file, collected);
1829
- if (
1830
- !hasHookRuntimeImport &&
1831
- !manualSlots &&
1832
- !pragmaOwned &&
1833
- !this._pragmaClaimsOwnership(code)
1834
- )
1835
- return passThrough();
1879
+ // The application's own modules are Octane's, as its `.tsx` are, so they
1880
+ // slot custom-hook calls without importing Octane: a hook composed only
1881
+ // of other hooks keeps one instance per call. A package, installed or
1882
+ // nested in the root, still needs the import or pragma, since its plain
1883
+ // modules may implement hooks with authored slots, as Octane's runtime does.
1884
+ // A leading pragma decides for itself: one naming `react` hands the
1885
+ // module to that toolchain.
1886
+ const pragma = findLeadingJsxImportSourcePragma(code);
1887
+ const owned =
1888
+ pragma === null
1889
+ ? this._isApplicationSource(file, collected)
1890
+ : this.pragmaOwnedModules.has(pragma);
1891
+ if (!importsHookRuntime(code) && !manualSlots && !owned) return passThrough();
1836
1892
  if (output === 'ts' && !manualSlots) {
1837
1893
  // The cheap ownership regex also matches documentation examples. A
1838
1894
  // full compile needs a real import (or an explicit ownership pragma).
@@ -1878,7 +1934,7 @@ class OctaneBundlerCompiler {
1878
1934
  profile,
1879
1935
  profileFilename,
1880
1936
  inlineHookMemo: inlinePlainMemo,
1881
- ...(manualSlots ? { manualSlots: true } : null),
1937
+ ...(manualSlots ? { manualSlots: true } : owned ? { owned: true } : null),
1882
1938
  ...(strong ? { strong: true } : null),
1883
1939
  renderer,
1884
1940
  ...(specializeVoidRoot
@@ -10434,6 +10434,82 @@ function dropTypeOnlyStatements(body) {
10434
10434
  return typeNames.size === 0 ? result : dropTypeOnlyExports(result, typeNames);
10435
10435
  }
10436
10436
 
10437
+ const TYPESCRIPT_SOURCE = /\.(?:tsrx|[cm]?tsx?)$/;
10438
+
10439
+ // The runtime emit erases what tsc does. Without `verbatimModuleSyntax` that
10440
+ // includes an import specifier written without `type` when the module refers
10441
+ // to it only in type positions, and a declaration left with none. Such a
10442
+ // specifier usually names only a type, so keeping it imports an export that
10443
+ // does not exist, which Rolldown rejects. A specifier nothing refers to stays,
10444
+ // as in JavaScript: a pass before this one may have added an import whose reads
10445
+ // it emits later (renderer regions).
10446
+ function dropTypeOnlyCode(body, filename) {
10447
+ const runtime = dropTypeOnlyStatements(body);
10448
+ if (!TYPESCRIPT_SOURCE.test(cleanCompileFilename(filename))) return runtime;
10449
+ const reads = collectFreeIdentifiers(
10450
+ runtime.filter((statement) => statement.type !== 'ImportDeclaration'),
10451
+ [],
10452
+ );
10453
+ const unread = (specifier) => !reads.has(specifier.local.name);
10454
+ if (
10455
+ !runtime.some(
10456
+ (statement) => statement.type === 'ImportDeclaration' && statement.specifiers.some(unread),
10457
+ )
10458
+ ) {
10459
+ return runtime;
10460
+ }
10461
+ // Erased declarations refer to types too, as in `interface Props { icon: Icon }`.
10462
+ const types = collectTypeReferenceNames(body, new Set());
10463
+ const result = [];
10464
+ for (const statement of runtime) {
10465
+ if (statement.type !== 'ImportDeclaration') {
10466
+ result.push(statement);
10467
+ continue;
10468
+ }
10469
+ const specifiers = statement.specifiers.filter(
10470
+ (specifier) => !unread(specifier) || !types.has(specifier.local.name),
10471
+ );
10472
+ if (specifiers.length === statement.specifiers.length) result.push(statement);
10473
+ else if (specifiers.length > 0) result.push({ ...statement, specifiers });
10474
+ }
10475
+ return result;
10476
+ }
10477
+
10478
+ // The root name of every type reference: `T`, `ns.T`, `typeof value`, the
10479
+ // heritage of `implements` and interface `extends`, and a local exported with
10480
+ // `export type { T }` or `export { type T }`.
10481
+ function collectTypeReferenceNames(node, names) {
10482
+ if (node === null || typeof node !== 'object') return names;
10483
+ if (Array.isArray(node)) {
10484
+ for (const child of node) collectTypeReferenceNames(child, names);
10485
+ return names;
10486
+ }
10487
+ let name =
10488
+ node.type === 'TSTypeReference'
10489
+ ? node.typeName
10490
+ : node.type === 'TSTypeQuery'
10491
+ ? node.exprName
10492
+ : node.type === 'TSClassImplements' ||
10493
+ node.type === 'TSInterfaceHeritage' ||
10494
+ node.type === 'TSExpressionWithTypeArguments'
10495
+ ? node.expression
10496
+ : null;
10497
+ while (name?.type === 'TSQualifiedName') name = name.left;
10498
+ while (name?.type === 'MemberExpression') name = name.object;
10499
+ if (name?.type === 'Identifier') names.add(name.name);
10500
+ if (node.type === 'ExportNamedDeclaration' && node.source == null) {
10501
+ for (const specifier of node.specifiers) {
10502
+ if (node.exportKind === 'type' || specifier.exportKind === 'type') {
10503
+ names.add(specifier.local.name);
10504
+ }
10505
+ }
10506
+ }
10507
+ for (const key in node) {
10508
+ if (!AST_WALK_SKIP_KEYS.has(key)) collectTypeReferenceNames(node[key], names);
10509
+ }
10510
+ return names;
10511
+ }
10512
+
10437
10513
  // Local names declared only by an erased statement or specifier.
10438
10514
  function collectTypeOnlyNames(statement, names) {
10439
10515
  const node = statement.type === 'ExportNamedDeclaration' ? statement.declaration : statement;
@@ -12549,9 +12625,9 @@ function compileInternal(
12549
12625
  }
12550
12626
  // Drop type-only statements (interface / type / declare / import-export type)
12551
12627
  // and inline `type` specifiers before emit — they carry no runtime value and
12552
- // would leak invalid TS into the .js (or crash the printer). Runtime-only;
12553
- // Volar keeps them.
12554
- if (options?.output !== 'ts') ast = { ...ast, body: dropTypeOnlyStatements(ast.body) };
12628
+ // would leak invalid TS into the .js (or crash the printer), then imports
12629
+ // used only as types, as tsc elides them. Runtime-only; Volar keeps them.
12630
+ if (options?.output !== 'ts') ast = { ...ast, body: dropTypeOnlyCode(ast.body, filename) };
12555
12631
  const serverModuleInfo = analyzeServerModule(ast, filename);
12556
12632
  // Normalize arrow-function components (`const X = () => @{…}`) to
12557
12633
  // FunctionDeclaration form so the component pipeline recognizes them.
@@ -14341,9 +14417,9 @@ function compileServer(
14341
14417
  // server codegen rewrites are copy-on-write over it.
14342
14418
  let ast = parsedAst;
14343
14419
  // Drop type-only statements and inline `type` specifiers before emit (see
14344
- // isTypeOnlyStatement) — same as the client path; the server HTML-string
14345
- // output is plain JS too.
14346
- if (options?.output !== 'ts') ast = { ...ast, body: dropTypeOnlyStatements(ast.body) };
14420
+ // isTypeOnlyStatement), then imports used only as types — same as the client
14421
+ // path; the server HTML-string output is plain JS too.
14422
+ if (options?.output !== 'ts') ast = { ...ast, body: dropTypeOnlyCode(ast.body, filename) };
14347
14423
  const serverModuleInfo = analyzeServerModule(ast, filename);
14348
14424
  // Normalize arrow-function components (`const X = () => @{…}`) to
14349
14425
  // FunctionDeclaration form so the component pipeline recognizes them.
@@ -17023,7 +17099,9 @@ function ssrEmitComponent(node, ctx, name, inlinedSubs, parentNs, cssHash, compo
17023
17099
  let keyExpr = null;
17024
17100
  for (const attr of attrs) {
17025
17101
  if (attr.type === 'SpreadAttribute' || attr.type === 'JSXSpreadAttribute') {
17026
- propNodes.push(b.spread(tsrxExprNode(attr.argument, ctx, name, inlinedSubs)));
17102
+ propNodes.push(
17103
+ b.spread(tsrxExprNode(rewriteJsxValues(attr.argument, ctx), ctx, name, inlinedSubs)),
17104
+ );
17027
17105
  continue;
17028
17106
  }
17029
17107
  if (attr.type !== 'Attribute' && attr.type !== 'JSXAttribute') continue;
@@ -36604,7 +36682,11 @@ function makeCompCall(
36604
36682
  for (const attr of attrs) {
36605
36683
  if (attr.type === 'SpreadAttribute' || attr.type === 'JSXSpreadAttribute') {
36606
36684
  hasSpreadProp = true;
36607
- propNodes.push(b.spread(tsrxExprNode(attr.argument, ctx, componentName, inlinedSubs)));
36685
+ propNodes.push(
36686
+ b.spread(
36687
+ tsrxExprNode(rewriteJsxValues(attr.argument, ctx), ctx, componentName, inlinedSubs),
36688
+ ),
36689
+ );
36608
36690
  continue;
36609
36691
  }
36610
36692
  if (attr.type !== 'Attribute' && attr.type !== 'JSXAttribute') continue;