octane 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.
@@ -65,6 +65,13 @@ export interface OctaneVitePluginOptions {
65
65
  * `@octanejs/vite-plugin`'s `devtools` option.
66
66
  */
67
67
  profile?: boolean | 'auto';
68
+ /**
69
+ * Reject state updates during render, synchronous effect state updates, and
70
+ * render-time ref writes in application-owned modules. Individual modules
71
+ * can opt in with a top-level `"use strong"` directive.
72
+ * @default false
73
+ */
74
+ strong?: boolean;
68
75
  /**
69
76
  * Path fragments excluded from Octane's plain `.ts`/`.js` hook-slot pass.
70
77
  * Prefer package manifest `octane.hookSlots.manual` declarations for bindings.
@@ -150,6 +150,9 @@ function assertProfilingDefineAvailable(definitions, enabled) {
150
150
  }
151
151
 
152
152
  export function octane(options = {}) {
153
+ if (options.strong !== undefined && typeof options.strong !== 'boolean') {
154
+ throw new TypeError('octane/compiler/vite: `strong` must be a boolean when provided.');
155
+ }
153
156
  if (options.parallelUse !== undefined) {
154
157
  // Removed 2026-07-16: the parallel-use() pipeline is unconditional compiled
155
158
  // semantics (docs/suspense-parallel-use-plan.md). Warn instead of throwing
@@ -194,6 +197,7 @@ export function octane(options = {}) {
194
197
  root: projectRoot,
195
198
  exclude: options.exclude,
196
199
  profile: profileEnabled,
200
+ strong: options.strong,
197
201
  renderers: options.renderers,
198
202
  requireDirective,
199
203
  warn,
@@ -207,6 +211,7 @@ export function octane(options = {}) {
207
211
  root: projectRoot,
208
212
  exclude: options.exclude,
209
213
  profile: profileEnabled,
214
+ strong: options.strong,
210
215
  renderers: options.renderers,
211
216
  requireDirective,
212
217
  warn,
@@ -28,10 +28,12 @@ import {
28
28
  createJsxTransform,
29
29
  createVolarMappingsResult,
30
30
  dedupeMappings,
31
+ error as collectCompileError,
31
32
  parseModule,
32
33
  } from '@tsrx/core';
33
34
  import { buildFatSegments, decodeSourceMappings } from './fat-segments.js';
34
35
  import { analyzeNativeChangeDiagnostics } from './native-change-diagnostics.js';
36
+ import { analyzeStrongMode } from './strong-mode.js';
35
37
  import { jsxImportSourcePragmaModule } from './pragma.js';
36
38
  import {
37
39
  DOM_RENDERER_MODULE,
@@ -194,7 +196,7 @@ function markNativeTemplateBodies(root) {
194
196
  * `intrinsics`; when present, the virtual TSX gets a file-local pragma so host
195
197
  * element types cannot leak into files owned by another renderer.
196
198
  *
197
- * @param {{ loose?: boolean, renderers?: unknown }} [options]
199
+ * @param {{ loose?: boolean, renderers?: unknown, strong?: boolean }} [options]
198
200
  * @returns {import('@tsrx/core/types').VolarMappingsResult & {
199
201
  * diagnostics: readonly unknown[],
200
202
  * generatedAst: import('estree').Program,
@@ -228,6 +230,11 @@ export function compileToVolarMappings(source, filename, options) {
228
230
  rendererBoundaries: rendererConfig.boundaries,
229
231
  rendererRegistry: rendererConfig.registry,
230
232
  }).diagnostics;
233
+ const strongDiagnostics =
234
+ options?.strong === true || source.includes('use strong')
235
+ ? analyzeStrongMode(ast, source, filename, options).diagnostics
236
+ : null;
237
+ if (strongDiagnostics !== null) diagnostics.push(...strongDiagnostics);
231
238
  // The renderer pragma belongs to the semantic comment set consumed by
232
239
  // @tsrx/core's type-only Program print. This keeps code and mappings in one
233
240
  // coordinate system instead of prepending text and shifting every mapping.
@@ -251,6 +258,25 @@ export function compileToVolarMappings(source, filename, options) {
251
258
  errors,
252
259
  comments: printComments,
253
260
  });
261
+ if (strongDiagnostics !== null) {
262
+ for (const diagnostic of strongDiagnostics) {
263
+ collectCompileError(
264
+ diagnostic.message,
265
+ diagnostic.filename ?? null,
266
+ {
267
+ start: diagnostic.start.offset,
268
+ end: diagnostic.end.offset,
269
+ loc: {
270
+ start: { line: diagnostic.start.line, column: diagnostic.start.column },
271
+ end: { line: diagnostic.end.line, column: diagnostic.end.column },
272
+ },
273
+ },
274
+ errors,
275
+ undefined,
276
+ diagnostic.code,
277
+ );
278
+ }
279
+ }
254
280
  // After the transform: the copy-on-write lowering must not observe the
255
281
  // marker mid-flight, and `ast` is what ships below as `sourceAst`.
256
282
  markNativeTemplateBodies(ast);
package/dist/runtime.d.ts CHANGED
@@ -629,6 +629,9 @@ export declare const ViewTransition: ComponentBody<ViewTransitionProps>;
629
629
  */
630
630
  export declare const ErrorBoundary: ComponentBody<{
631
631
  fallback?: unknown | ((error: unknown, reset: () => void) => unknown);
632
+ resetRef?: {
633
+ current: (() => void) | null;
634
+ };
632
635
  children: unknown;
633
636
  }>;
634
637
  /**
@@ -1334,7 +1337,7 @@ export declare const HMR: unique symbol;
1334
1337
  export declare function hmr<P>(fn: ComponentBody<P>): ComponentBody<P>;
1335
1338
  export declare function setTransitionFallbackTimeout(ms: number): void;
1336
1339
  export declare function getTransitionFallbackTimeout(): number;
1337
- export declare function tryBlock(parentScope: Scope, slotKey: number, domParent: Node, tryBody: ComponentBody, catchBody: ComponentBody | null, pendingBody: ComponentBody | null, anchor?: Node | null, env?: any[], propagateSuspense?: boolean): void;
1340
+ export declare function tryBlock(parentScope: Scope, slotKey: number, domParent: Node, tryBody: ComponentBody, catchBody: ComponentBody | null, pendingBody: ComponentBody | null, anchor?: Node | null, env?: any[], propagateSuspense?: boolean): () => void;
1338
1341
  export declare function startTransition(fn: () => void | Promise<unknown>): void;
1339
1342
  export declare function useTransition(slot?: symbol): [boolean, (fn: () => void | Promise<unknown>) => void];
1340
1343
  export declare function useActionState<S>(action: (prevState: S, payload: any) => S | Promise<S>, initialState: S, permalinkOrSlot?: string | symbol, slot?: symbol): [S, (payload?: any) => void, boolean];