octane 0.1.23 → 0.1.24
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/dist/compiler/compile-universal.js +6 -2
- package/dist/compiler/compile.js +42 -39
- package/dist/compiler/hook-deps.js +162 -3
- package/dist/compiler/slot-hooks.js +10 -3
- package/dist/index.d.ts +1 -0
- package/dist/index.js +2 -0
- package/dist/method-dep.d.ts +1 -0
- package/dist/method-dep.js +11 -0
- package/dist/runtime.d.ts +7 -8
- package/dist/runtime.js +63 -26
- package/dist/runtime.server.d.ts +6 -2
- package/dist/runtime.server.js +21 -10
- package/dist/server/index.d.ts +1 -0
- package/dist/server/index.js +2 -0
- package/package.json +1 -1
|
@@ -3182,8 +3182,12 @@ function buildUniversalHmrBlocksAst(state, origin) {
|
|
|
3182
3182
|
return { prelude: [], tail: [] };
|
|
3183
3183
|
}
|
|
3184
3184
|
if (state.hmrDialect === 'webpack') {
|
|
3185
|
-
|
|
3186
|
-
|
|
3185
|
+
// Rspack only guarantees that the `import.meta.webpackHot` root is lowered.
|
|
3186
|
+
// Keep `.data` and the HMR methods on an ordinary local so Rsbuild's React
|
|
3187
|
+
// transform cannot turn a deeper meta-property chain into `undefined`.
|
|
3188
|
+
const hotName = allocName(state, '__octaneWebpackHot');
|
|
3189
|
+
const hot = generatedIdentifier(hotName, origin);
|
|
3190
|
+
const prelude = [generatedConst(hotName, importMetaMember('webpackHot', origin), origin)];
|
|
3187
3191
|
const tail = [];
|
|
3188
3192
|
if (disposals.length === 0) {
|
|
3189
3193
|
const data = generatedIdentifier('data', origin);
|
package/dist/compiler/compile.js
CHANGED
|
@@ -41,7 +41,12 @@ import {
|
|
|
41
41
|
import { print as esrapPrint } from 'esrap';
|
|
42
42
|
import esrapTsx from 'esrap/languages/tsx';
|
|
43
43
|
import { buildFatSegments } from './fat-segments.js';
|
|
44
|
-
import {
|
|
44
|
+
import {
|
|
45
|
+
METHOD_DEP_IMPORT,
|
|
46
|
+
analyzeHookDependencies,
|
|
47
|
+
applyHookDependencies,
|
|
48
|
+
isInvariantLiteral,
|
|
49
|
+
} from './hook-deps.js';
|
|
45
50
|
import { compileUniversal, UNIVERSAL_COMPILER_RUNTIME_IMPORTS } from './compile-universal.js';
|
|
46
51
|
import {
|
|
47
52
|
expandDomRendererRegionsAst,
|
|
@@ -6064,12 +6069,16 @@ function compileInternal(source, filename, options, analyzedAst, mode, bundlerMe
|
|
|
6064
6069
|
rendererBoundaryPreparation?.universalUnits,
|
|
6065
6070
|
),
|
|
6066
6071
|
});
|
|
6072
|
+
let hookDepHelperNeeded = false;
|
|
6067
6073
|
ast = applyHookDependencies(ast, {
|
|
6068
6074
|
filename,
|
|
6069
6075
|
hookRuntimeModules: hookRuntimeModulesForCompile(
|
|
6070
6076
|
options,
|
|
6071
6077
|
rendererBoundaryPreparation?.universalUnits,
|
|
6072
6078
|
),
|
|
6079
|
+
onRuntimeHelper: () => {
|
|
6080
|
+
hookDepHelperNeeded = true;
|
|
6081
|
+
},
|
|
6073
6082
|
});
|
|
6074
6083
|
const hmrOption = options && options.hmr;
|
|
6075
6084
|
const hmrDialect = hmrOption === true ? 'vite' : hmrOption || false;
|
|
@@ -6202,6 +6211,7 @@ function compileInternal(source, filename, options, analyzedAst, mode, bundlerMe
|
|
|
6202
6211
|
// single module print must carry a loc (OCTANE_COMPILE_ASSERT_LOC).
|
|
6203
6212
|
_moduleOrigin: ast.body.find((n) => n?.loc != null) ?? ast,
|
|
6204
6213
|
};
|
|
6214
|
+
if (hookDepHelperNeeded) ctx.runtimeNeeded.add(METHOD_DEP_IMPORT);
|
|
6205
6215
|
{
|
|
6206
6216
|
const imports = collectOctaneImportBindings(ast.body);
|
|
6207
6217
|
ctx.octaneImportLocals = imports.locals;
|
|
@@ -6822,7 +6832,18 @@ function compileInternal(source, filename, options, analyzedAst, mode, bundlerMe
|
|
|
6822
6832
|
// it. Persist that canonical identity again for the next update. This keeps
|
|
6823
6833
|
// working across any number of edits; accept callbacks in webpack are error
|
|
6824
6834
|
// handlers, not Vite-style callbacks carrying the new module namespace.
|
|
6825
|
-
|
|
6835
|
+
// Keep every access after the recognized `import.meta.webpackHot` root on a
|
|
6836
|
+
// local. Rspack's React/Rsbuild transform pipeline otherwise treats deeper
|
|
6837
|
+
// expressions such as `import.meta.webpackHot.data` as unsupported even
|
|
6838
|
+
// though it lowers the root itself to `module.hot`.
|
|
6839
|
+
const webpackHotName = allocCompilerName(ctx, '_$webpackHot');
|
|
6840
|
+
const webpackHot = () => b.id(webpackHotName);
|
|
6841
|
+
hmrNodes.push(
|
|
6842
|
+
inheritOriginLoc(
|
|
6843
|
+
b.const(webpackHotName, b.member(importMeta(), 'webpackHot')),
|
|
6844
|
+
moduleOrigin,
|
|
6845
|
+
),
|
|
6846
|
+
);
|
|
6826
6847
|
const previousComponent = (name, optional) =>
|
|
6827
6848
|
b.member(
|
|
6828
6849
|
b.member(
|
|
@@ -7122,9 +7143,13 @@ function compileServer(source, filename, options, analyzedAst = null) {
|
|
|
7122
7143
|
// Mirror the client transform exactly. Effects are server no-ops, but
|
|
7123
7144
|
// useMemo/useCallback execute during SSR and must receive the same inferred
|
|
7124
7145
|
// dependency shape as hydration's client compile.
|
|
7146
|
+
let hookDepHelperNeeded = false;
|
|
7125
7147
|
ast = applyHookDependencies(ast, {
|
|
7126
7148
|
filename,
|
|
7127
7149
|
hookRuntimeModules: hookRuntimeModulesForCompile(options),
|
|
7150
|
+
onRuntimeHelper: () => {
|
|
7151
|
+
hookDepHelperNeeded = true;
|
|
7152
|
+
},
|
|
7128
7153
|
});
|
|
7129
7154
|
const ctx = {
|
|
7130
7155
|
filename,
|
|
@@ -7168,6 +7193,7 @@ function compileServer(source, filename, options, analyzedAst = null) {
|
|
|
7168
7193
|
// Scaffolding without a more precise authored construct maps here.
|
|
7169
7194
|
_moduleOrigin: ast.body.find((n) => n?.loc != null) ?? ast,
|
|
7170
7195
|
};
|
|
7196
|
+
if (hookDepHelperNeeded) ctx.runtimeNeeded.add(METHOD_DEP_IMPORT);
|
|
7171
7197
|
{
|
|
7172
7198
|
const imports = collectOctaneImportBindings(ast.body);
|
|
7173
7199
|
ctx.octaneImportLocals = imports.locals;
|
|
@@ -16713,12 +16739,10 @@ function planJsx(
|
|
|
16713
16739
|
ctx.runtimeNeeded.add('queueRefDetach'); // unmount-detach of a spread-supplied ref
|
|
16714
16740
|
}
|
|
16715
16741
|
if (b.kind === 'ref') {
|
|
16716
|
-
ctx.runtimeNeeded.add('attachRef');
|
|
16717
16742
|
ctx.runtimeNeeded.add('queueRefAttach'); // deferred mount attach (commit-phase timing)
|
|
16718
16743
|
ctx.runtimeNeeded.add('queueRefDetach'); // deferred unmount detach (same phasing)
|
|
16719
16744
|
}
|
|
16720
16745
|
if (b.kind === 'fragmentRef') {
|
|
16721
|
-
ctx.runtimeNeeded.add('attachRef');
|
|
16722
16746
|
ctx.runtimeNeeded.add('mountFragmentRef');
|
|
16723
16747
|
ctx.runtimeNeeded.add('queueRefAttach'); // deferred update re-attach
|
|
16724
16748
|
ctx.runtimeNeeded.add('queueRefDetach'); // deferred update/unmount detach
|
|
@@ -18286,18 +18310,18 @@ function emitBindingMount(bind, elVar, bag) {
|
|
|
18286
18310
|
// bound element rides along as the cleanup target, so a callback ref
|
|
18287
18311
|
// shared across elements (ref={registerItem} on every @for row)
|
|
18288
18312
|
// releases ITS row's React-19 cleanup, not another row's.
|
|
18289
|
-
// Both deferred
|
|
18290
|
-
//
|
|
18313
|
+
// Both deferred operations retain the bound element. `_ref$` must be a LIVE
|
|
18314
|
+
// cleanup read because updates re-point it. Assigning both bag locals inside
|
|
18315
|
+
// the queue call evaluates each mount value once while avoiding throwaway
|
|
18316
|
+
// temporaries; Suspense still receives the exact ref/target pair.
|
|
18291
18317
|
return st(
|
|
18292
18318
|
b.block([
|
|
18293
|
-
b.const('_r', bind.expr),
|
|
18294
|
-
b.stmt(b.assignment('=', local(`_ref$${bind.id}`), b.id('_r'))),
|
|
18295
|
-
b.stmt(b.assignment('=', local(`_el$${bind.id}`), el())),
|
|
18296
18319
|
b.stmt(
|
|
18297
18320
|
b.call(
|
|
18298
18321
|
'_$queueRefAttach',
|
|
18299
18322
|
b.id('__s'),
|
|
18300
|
-
b.
|
|
18323
|
+
b.assignment('=', local(`_ref$${bind.id}`), bind.expr),
|
|
18324
|
+
b.assignment('=', local(`_el$${bind.id}`), el()),
|
|
18301
18325
|
),
|
|
18302
18326
|
),
|
|
18303
18327
|
cleanupsPush(
|
|
@@ -18321,22 +18345,13 @@ function emitBindingMount(bind, elVar, bag) {
|
|
|
18321
18345
|
// the user's ref, and registers a single cleanup that detaches
|
|
18322
18346
|
// the ref + destroys the instance on unmount.
|
|
18323
18347
|
return st(
|
|
18324
|
-
b.
|
|
18325
|
-
b.
|
|
18326
|
-
|
|
18327
|
-
|
|
18328
|
-
|
|
18329
|
-
local(`_fi$${bind.id}`),
|
|
18330
|
-
b.call(
|
|
18331
|
-
'_$mountFragmentRef',
|
|
18332
|
-
b.id('__s'),
|
|
18333
|
-
el(),
|
|
18334
|
-
hostVarNode(bind.endElVar),
|
|
18335
|
-
b.id('_r'),
|
|
18336
|
-
),
|
|
18337
|
-
),
|
|
18348
|
+
b.stmt(
|
|
18349
|
+
b.assignment(
|
|
18350
|
+
'=',
|
|
18351
|
+
local(`_fi$${bind.id}`),
|
|
18352
|
+
b.call('_$mountFragmentRef', b.id('__s'), el(), hostVarNode(bind.endElVar), bind.expr),
|
|
18338
18353
|
),
|
|
18339
|
-
|
|
18354
|
+
),
|
|
18340
18355
|
);
|
|
18341
18356
|
}
|
|
18342
18357
|
}
|
|
@@ -18596,13 +18611,7 @@ function emitBindingUpdate(bind, bag) {
|
|
|
18596
18611
|
),
|
|
18597
18612
|
b.if(
|
|
18598
18613
|
b.binary('!=', b.id('_r'), nullNode()),
|
|
18599
|
-
b.stmt(
|
|
18600
|
-
b.call(
|
|
18601
|
-
'_$queueRefAttach',
|
|
18602
|
-
b.id('__s'),
|
|
18603
|
-
b.arrow([], b.call('_$attachRef', b.id('_r'), F('_el'))),
|
|
18604
|
-
),
|
|
18605
|
-
),
|
|
18614
|
+
b.stmt(b.call('_$queueRefAttach', b.id('__s'), b.id('_r'), F('_el'))),
|
|
18606
18615
|
null,
|
|
18607
18616
|
),
|
|
18608
18617
|
b.stmt(b.assignment('=', F('_ref'), b.id('_r'))),
|
|
@@ -18635,13 +18644,7 @@ function emitBindingUpdate(bind, bag) {
|
|
|
18635
18644
|
),
|
|
18636
18645
|
b.if(
|
|
18637
18646
|
b.binary('!=', b.id('_r'), nullNode()),
|
|
18638
|
-
b.stmt(
|
|
18639
|
-
b.call(
|
|
18640
|
-
'_$queueRefAttach',
|
|
18641
|
-
b.id('__s'),
|
|
18642
|
-
b.arrow([], b.call('_$attachRef', b.id('_r'), fi())),
|
|
18643
|
-
),
|
|
18644
|
-
),
|
|
18647
|
+
b.stmt(b.call('_$queueRefAttach', b.id('__s'), b.id('_r'), fi())),
|
|
18645
18648
|
null,
|
|
18646
18649
|
),
|
|
18647
18650
|
b.stmt(b.assignment('=', cur(), b.id('_r'))),
|
|
@@ -819,10 +819,78 @@ function staticMemberInfo(node) {
|
|
|
819
819
|
return {
|
|
820
820
|
node: dependencyNode,
|
|
821
821
|
root,
|
|
822
|
+
name: current.property.name,
|
|
822
823
|
path: `${current.optional ? '?' : ''}.${current.property.name}`,
|
|
823
824
|
};
|
|
824
825
|
}
|
|
825
826
|
|
|
827
|
+
// Directive prologues that declare "this body executes in another context, not
|
|
828
|
+
// during render" — a nested function carrying one contributes only its ROOT
|
|
829
|
+
// captures to an inferred dependency array, because hoisting its member reads
|
|
830
|
+
// to render time would run getters in a context where they may be illegal
|
|
831
|
+
// (issue #542: TypeGPU's `.$` is only readable inside `'use gpu'` shader code)
|
|
832
|
+
// and at a moment the program never performs them.
|
|
833
|
+
//
|
|
834
|
+
// This is a deliberate ALLOWLIST, not "any directive": directives that mark
|
|
835
|
+
// same-context compiler hints (`'use strict'`, React Compiler's `'use memo'` /
|
|
836
|
+
// `'use no memo'`, `'use signals'`) or reserved module/function markers with
|
|
837
|
+
// their own semantics (`'use server'`, `'use client'`, `'use cache'`,
|
|
838
|
+
// `'use workflow'`/`'use step'`) must NOT truncate. Extend the set as more
|
|
839
|
+
// other-context directives appear in the ecosystem.
|
|
840
|
+
//
|
|
841
|
+
// 'use gpu' — TypeGPU shader functions (transpiled, run on the GPU).
|
|
842
|
+
// 'worklet' — react-native-reanimated / react-native-worklets-core bodies,
|
|
843
|
+
// serialized and executed on a separate UI-thread runtime.
|
|
844
|
+
const OPAQUE_EXECUTION_DIRECTIVES = new Set(['use gpu', 'worklet']);
|
|
845
|
+
|
|
846
|
+
// A directive prologue is the run of leading string-literal expression
|
|
847
|
+
// statements. Parsers implementing ESTree stamp `directive` on those
|
|
848
|
+
// statements; fall back to the literal value where they don't.
|
|
849
|
+
function hasOpaqueExecutionDirective(fn) {
|
|
850
|
+
if (fn.body?.type !== 'BlockStatement') return false;
|
|
851
|
+
for (const statement of fn.body.body || []) {
|
|
852
|
+
if (statement.type !== 'ExpressionStatement') return false;
|
|
853
|
+
const expression = unwrapValue(statement.expression);
|
|
854
|
+
const value =
|
|
855
|
+
statement.directive ??
|
|
856
|
+
(expression?.type === 'Literal' && typeof expression.value === 'string'
|
|
857
|
+
? expression.value
|
|
858
|
+
: null);
|
|
859
|
+
if (value === null) return false;
|
|
860
|
+
if (OPAQUE_EXECUTION_DIRECTIVES.has(value)) return true;
|
|
861
|
+
}
|
|
862
|
+
return false;
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
// The `octane` runtime export inferred method-call dependencies compile to.
|
|
866
|
+
// Both emitters alias it: the full compiler through `ctx.runtimeNeeded` (its
|
|
867
|
+
// `_$`-prefixed rtAlias convention is baked into methodDepNode below) and the
|
|
868
|
+
// surgical pass through its own helper-import allocator.
|
|
869
|
+
export const METHOD_DEP_IMPORT = '__methodDep';
|
|
870
|
+
|
|
871
|
+
// The emitted dependency expression for a one-level method call:
|
|
872
|
+
// `_$__methodDep(root, 'name')` — own property ? member value : receiver (see
|
|
873
|
+
// the runtime helper's contract in src/method-dep.ts). The call node carries
|
|
874
|
+
// the authored member's source range so source maps and the surgical pass's
|
|
875
|
+
// offset expectations stay anchored to the authored expression, while the
|
|
876
|
+
// cloned root identifier keeps its own authored position.
|
|
877
|
+
function methodDepNode(dependency) {
|
|
878
|
+
// Every synthesized node is stamped with the authored member's origin — the
|
|
879
|
+
// bundler print path asserts a loc on each printed node, including the
|
|
880
|
+
// helper's callee identifier.
|
|
881
|
+
const call = b.call(
|
|
882
|
+
b.id(`_$${METHOD_DEP_IMPORT}`, dependency.node),
|
|
883
|
+
{ ...dependency.method.root },
|
|
884
|
+
b.literal(dependency.method.name, JSON.stringify(dependency.method.name), dependency.node),
|
|
885
|
+
);
|
|
886
|
+
return {
|
|
887
|
+
...call,
|
|
888
|
+
start: dependency.node.start,
|
|
889
|
+
end: dependency.node.end,
|
|
890
|
+
loc: dependency.node.loc,
|
|
891
|
+
};
|
|
892
|
+
}
|
|
893
|
+
|
|
826
894
|
function collectDependencies(expression, callbackScope, analysis) {
|
|
827
895
|
const dependencies = [];
|
|
828
896
|
const seen = new Set();
|
|
@@ -863,6 +931,48 @@ function collectDependencies(expression, callbackScope, analysis) {
|
|
|
863
931
|
}
|
|
864
932
|
}
|
|
865
933
|
|
|
934
|
+
// A one-level member CALLED as a method. The member value alone cannot
|
|
935
|
+
// witness a changed receiver when the method is inherited (issue #542:
|
|
936
|
+
// `count.toFixed` is `Number.prototype.toFixed` on every render), and the
|
|
937
|
+
// receiver alone would defeat memoization for own function properties on
|
|
938
|
+
// per-render containers (`props.onChange(...)`). Record the pair and let the
|
|
939
|
+
// emitted `__methodDep(root, 'name')` helper pick the comparable value at
|
|
940
|
+
// runtime. Deeper callees (`a.b.c(...)`) never reach here: their receiver
|
|
941
|
+
// path is recorded by the ordinary member walk, which cannot capture the
|
|
942
|
+
// method itself, so they were never exposed to the stale-method hazard.
|
|
943
|
+
function addMethodCall(info) {
|
|
944
|
+
const scope = analysis.nodeScopes.get(info.root);
|
|
945
|
+
const binding = scope ? resolveBinding(scope, info.root.name) : null;
|
|
946
|
+
if (
|
|
947
|
+
binding === null ||
|
|
948
|
+
binding.imported ||
|
|
949
|
+
binding.dependencyInvariant ||
|
|
950
|
+
(callbackScope !== null && scopeIsWithin(binding.scope, callbackScope))
|
|
951
|
+
) {
|
|
952
|
+
return;
|
|
953
|
+
}
|
|
954
|
+
// Distinct from the plain-read key: `x.m` read as a value elsewhere in the
|
|
955
|
+
// callback still contributes its own member dependency.
|
|
956
|
+
const key = `b${binding.id}${info.path}()`;
|
|
957
|
+
if (!seen.has(key)) {
|
|
958
|
+
seen.add(key);
|
|
959
|
+
dependencies.push({
|
|
960
|
+
node: info.node,
|
|
961
|
+
key,
|
|
962
|
+
binding,
|
|
963
|
+
method: { root: info.root, name: info.name },
|
|
964
|
+
});
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
// Depth of enclosing functions whose directive prologue declares another
|
|
969
|
+
// execution context (see OPAQUE_EXECUTION_DIRECTIVES). Inside one, member
|
|
970
|
+
// chains truncate to their root bindings: the body's property reads happen
|
|
971
|
+
// in that other context, so hoisting them into a render-time dependency
|
|
972
|
+
// array would evaluate getters in a context where they may be illegal
|
|
973
|
+
// (TypeGPU's `.$`) and at a time the program never reads them.
|
|
974
|
+
let opaqueDepth = 0;
|
|
975
|
+
|
|
866
976
|
function walk(node) {
|
|
867
977
|
if (!node || typeof node !== 'object') return;
|
|
868
978
|
if (Array.isArray(node)) {
|
|
@@ -878,13 +988,44 @@ function collectDependencies(expression, callbackScope, analysis) {
|
|
|
878
988
|
case 'Identifier':
|
|
879
989
|
addIdentifier(node);
|
|
880
990
|
return;
|
|
991
|
+
case 'CallExpression': {
|
|
992
|
+
if (opaqueDepth > 0) {
|
|
993
|
+
// Roots only: the callee's receiver chain collapses through the
|
|
994
|
+
// MemberExpression case below; no method-pair dependency either,
|
|
995
|
+
// since it would read the member at render.
|
|
996
|
+
walk(node.callee);
|
|
997
|
+
walk(node.arguments);
|
|
998
|
+
return;
|
|
999
|
+
}
|
|
1000
|
+
// Optional spellings land here too: `a?.b(x)` and `a.b?.(x)` are a
|
|
1001
|
+
// CallExpression under a ChainExpression whose callee is a bare
|
|
1002
|
+
// (possibly optional) MemberExpression, which staticMemberInfo accepts.
|
|
1003
|
+
const callee = unwrapValue(node.callee);
|
|
1004
|
+
const info =
|
|
1005
|
+
callee?.type === 'MemberExpression' || callee?.type === 'ChainExpression'
|
|
1006
|
+
? staticMemberInfo(callee)
|
|
1007
|
+
: null;
|
|
1008
|
+
if (info) addMethodCall(info);
|
|
1009
|
+
else walk(node.callee);
|
|
1010
|
+
walk(node.arguments);
|
|
1011
|
+
return;
|
|
1012
|
+
}
|
|
881
1013
|
case 'ChainExpression': {
|
|
1014
|
+
if (opaqueDepth > 0) {
|
|
1015
|
+
walk(node.expression);
|
|
1016
|
+
return;
|
|
1017
|
+
}
|
|
882
1018
|
const info = staticMemberInfo(node);
|
|
883
1019
|
if (info) addStaticMember(info);
|
|
884
1020
|
else walk(node.expression);
|
|
885
1021
|
return;
|
|
886
1022
|
}
|
|
887
1023
|
case 'MemberExpression': {
|
|
1024
|
+
if (opaqueDepth > 0) {
|
|
1025
|
+
walk(node.object);
|
|
1026
|
+
if (node.computed) walk(node.property);
|
|
1027
|
+
return;
|
|
1028
|
+
}
|
|
888
1029
|
const info = staticMemberInfo(node);
|
|
889
1030
|
if (info) addStaticMember(info);
|
|
890
1031
|
else {
|
|
@@ -913,10 +1054,14 @@ function collectDependencies(expression, callbackScope, analysis) {
|
|
|
913
1054
|
return;
|
|
914
1055
|
case 'FunctionDeclaration':
|
|
915
1056
|
case 'FunctionExpression':
|
|
916
|
-
case 'ArrowFunctionExpression':
|
|
1057
|
+
case 'ArrowFunctionExpression': {
|
|
1058
|
+
const opaque = hasOpaqueExecutionDirective(node);
|
|
1059
|
+
if (opaque) opaqueDepth++;
|
|
917
1060
|
for (const param of node.params || []) walkPatternExpression(param);
|
|
918
1061
|
walk(node.body);
|
|
1062
|
+
if (opaque) opaqueDepth--;
|
|
919
1063
|
return;
|
|
1064
|
+
}
|
|
920
1065
|
case 'ImportDeclaration':
|
|
921
1066
|
case 'ExportAllDeclaration':
|
|
922
1067
|
case 'MetaProperty':
|
|
@@ -1163,7 +1308,7 @@ function rebuildWithHookMetadata(ast, analysis, inferred, insertDeps) {
|
|
|
1163
1308
|
args.splice(result.depsIndex, 0, {
|
|
1164
1309
|
...b.array(
|
|
1165
1310
|
result.dependencies.map((/** @type {any} */ dependency) =>
|
|
1166
|
-
cloneDependency(dependency.node),
|
|
1311
|
+
dependency.method ? methodDepNode(dependency) : cloneDependency(dependency.node),
|
|
1167
1312
|
),
|
|
1168
1313
|
),
|
|
1169
1314
|
start: node.start,
|
|
@@ -1197,8 +1342,22 @@ export function annotateHookCalls(ast, options = {}) {
|
|
|
1197
1342
|
* dependency arrays inserted at each candidate call. Copy-on-write — the input
|
|
1198
1343
|
* AST is never modified; callers must use the returned module.
|
|
1199
1344
|
*/
|
|
1200
|
-
/** @param {any} ast @param {{ onlyImported?: boolean, hookRuntimeModules?: readonly string[], filename?: string }} [options] */
|
|
1345
|
+
/** @param {any} ast @param {{ onlyImported?: boolean, hookRuntimeModules?: readonly string[], filename?: string, onRuntimeHelper?: (name: string) => void }} [options] */
|
|
1201
1346
|
export function applyHookDependencies(ast, options = {}) {
|
|
1202
1347
|
const { analysis, inferred } = analyzeInternal(ast, options);
|
|
1348
|
+
// The inserted `_$__methodDep(...)` calls need their aliased runtime import;
|
|
1349
|
+
// the caller owns import assembly, so report the requirement rather than
|
|
1350
|
+
// splicing an ImportDeclaration into a module whose runtime request
|
|
1351
|
+
// ('octane' vs 'octane/server') this pass cannot know.
|
|
1352
|
+
if (options.onRuntimeHelper !== undefined) {
|
|
1353
|
+
outer: for (const result of inferred.values()) {
|
|
1354
|
+
for (const dependency of result.dependencies) {
|
|
1355
|
+
if (dependency.method) {
|
|
1356
|
+
options.onRuntimeHelper(METHOD_DEP_IMPORT);
|
|
1357
|
+
break outer;
|
|
1358
|
+
}
|
|
1359
|
+
}
|
|
1360
|
+
}
|
|
1361
|
+
}
|
|
1203
1362
|
return rebuildWithHookMetadata(ast, analysis, inferred, true).ast;
|
|
1204
1363
|
}
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
|
|
17
17
|
import { parseModule } from '@tsrx/core';
|
|
18
18
|
import { HOOK_NAMES, hookSlotHash } from './compile.js';
|
|
19
|
-
import { annotateHookCalls } from './hook-deps.js';
|
|
19
|
+
import { METHOD_DEP_IMPORT, annotateHookCalls } from './hook-deps.js';
|
|
20
20
|
import { assertStrongMode } from './strong-mode.js';
|
|
21
21
|
|
|
22
22
|
// Build a cheap import-presence gate. Precise call identity is annotated by the
|
|
@@ -940,9 +940,16 @@ function walk(node, owner, st) {
|
|
|
940
940
|
// The dependency callback is already the final user argument. Insert
|
|
941
941
|
// both the generated array and slot in one edit so equal-position edit
|
|
942
942
|
// ordering cannot reverse them. Dependency nodes retain original source
|
|
943
|
-
// offsets, preserving arbitrary TS syntax byte-for-byte.
|
|
943
|
+
// offsets, preserving arbitrary TS syntax byte-for-byte. Method-call
|
|
944
|
+
// dependencies are the one synthesized form: the helper call's root is
|
|
945
|
+
// a bare identifier and its name a JSON string, so no arbitrary TS
|
|
946
|
+
// syntax needs reprinting there either.
|
|
944
947
|
const deps = inferred.dependencies
|
|
945
|
-
.map((dependency) =>
|
|
948
|
+
.map((dependency) =>
|
|
949
|
+
dependency.method
|
|
950
|
+
? `${requireParallelHelper(st, METHOD_DEP_IMPORT)}(${dependency.method.root.name}, ${JSON.stringify(dependency.method.name)})`
|
|
951
|
+
: st.source.slice(dependency.node.start, dependency.node.end),
|
|
952
|
+
)
|
|
946
953
|
.join(', ');
|
|
947
954
|
st.edits.push({
|
|
948
955
|
pos: node.arguments[node.arguments.length - 1].end,
|
package/dist/index.d.ts
CHANGED
|
@@ -3,3 +3,4 @@ export { initializeHydrationEventCapture } from './hydration/event-capture.js';
|
|
|
3
3
|
export { createRoot, hydrateRoot, flushSync, act, type Root, type RootOptions, useState, useLinkedState, type LinkedStatePrevious, type LinkedStateOptions, useReducer, useEffect, useLayoutEffect, useInsertionEffect, useMemo, useCallback, useRef, useId, useImperativeHandle, useEffectEvent, useSyncExternalStore, useDeferredValue, useTransition, useActionState, useFormStatus, useOptimistic, useDebugValue, type FormStatus, startTransition, requestFormReset, memo, lazy, preload, preinit, preconnect, prefetchDNS, createContext, use, useContext, type Context, type ForeignHostContext, Suspense, ErrorBoundary, Hydrate, Activity, ViewTransition, addTransitionType, ViewTransition as unstable_ViewTransition, addTransitionType as unstable_addTransitionType, ViewTransitionPseudoElement, type ViewTransitionProps, type ViewTransitionInstance, Fragment, createPortal, type PortalDescriptor, createElement, cloneElement, isValidElement, isChildrenBlock, Children, type ElementDescriptor, type ComponentBody, type OctaneNode, TsrxErrorBoundary, __useStateWithGetter, __useLinkedStateWithGetter, __useReducerWithGetter, __createVoidRoot, bindRendererRegionOwner, EXTERNAL_HYDRATION_PROMISE, HYDRATION_RANGE_BOUNDARY, createHostContextRequest, __vtSeen, template, clone, drainFrag, bag0, bag1, bag2, bag3, bag4, bag5, bag6, bag7, bag8, bag9, bag10, bag11, bag12, bag13, bag14, bag15, bag16, bagOf, evt0, evt0u, evt1, evt1u, evt2, evt2u, evtN, evtNu, devEventListener, htext, htextSwap, child, sibling, setText, setScriptText, setHTML, setDangerouslySetInnerHTML, setDangerouslySetInnerHTMLSources, markDangerouslySetInnerHTMLChildren, setAttribute, setStringData, setBooleanAttribute, setAriaAttribute, setClassName, setClassAttr, normalizeClass, setStyle, setSpread, snapshotSpread, setHostPropSources, queueNativeChangeDiagnostic, markNativeChangeDiagnosticStatic, setFormAction, setValue, setFormControlSources, setChecked, setCheckedCheckable, setSelectValue, setDefaultValue, setDefaultValueUncontrolled, setDefaultChecked, setAutoFocus, attachRef, queueRefAttach, queueRefDetach, injectStyle, headBlock, namespaceHead, namespaceHeadElement, delegateEvents, delegateCaptureEvents, forBlock, mapSlot, ifBlock, tryBlock, switchBlock, activityBlock, componentSlot, componentSlotVoid, componentSlotLite, compilerCacheContext, markSingleRoot, markSingleRoot as __s, markChildrenBlock, createScopedValue, createScopedElement, childSlot, positionalChildren, textSlot, textHole, childTextHole, hostComponent, renderBlock, portal, hookSlots, withSlot, useBatch, warmMemo, warmChild, puMiss, puTake0, puTake1, puTake2, puTake3, puTake4, puPub, provideContext, mountFragmentRef, FragmentInstance, hmr, HMR, hasPendingWork, type Scope, type Block, drainPassiveEffects, setIsOctaneActEnvironment, setTransitionFallbackTimeout, getTransitionFallbackTimeout, } from './runtime.js';
|
|
4
4
|
export type { HydrateOptions, HydrateProps, HydrateWhen, HydrationInteractionEvent, HydrationInteractionEvents, HydrationPrefetchContext, HydrationPrefetchFunction, HydrationPrefetchStrategy, HydrationPrefetchWaitReason, HydrationStrategy, HydrationWhen, } from './hydration/types.js';
|
|
5
5
|
export { __serverRpc } from './server-rpc-client.js';
|
|
6
|
+
export { __methodDep } from './method-dep.js';
|
package/dist/index.js
CHANGED
|
@@ -180,6 +180,7 @@ import {
|
|
|
180
180
|
getTransitionFallbackTimeout
|
|
181
181
|
} from "./runtime.js";
|
|
182
182
|
import { __serverRpc } from "./server-rpc-client.js";
|
|
183
|
+
import { __methodDep } from "./method-dep.js";
|
|
183
184
|
export {
|
|
184
185
|
Activity,
|
|
185
186
|
Children,
|
|
@@ -195,6 +196,7 @@ export {
|
|
|
195
196
|
ViewTransition,
|
|
196
197
|
ViewTransitionPseudoElement,
|
|
197
198
|
__createVoidRoot,
|
|
199
|
+
__methodDep,
|
|
198
200
|
markSingleRoot2 as __s,
|
|
199
201
|
__serverRpc,
|
|
200
202
|
__useLinkedStateWithGetter,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function __methodDep(receiver: unknown, name: string): unknown;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
const hasOwnProp = Object.prototype.hasOwnProperty;
|
|
2
|
+
function __methodDep(receiver, name) {
|
|
3
|
+
if ((typeof receiver !== "object" || receiver === null) && typeof receiver !== "function") {
|
|
4
|
+
return receiver;
|
|
5
|
+
}
|
|
6
|
+
if (hasOwnProp.call(receiver, name)) return receiver[name];
|
|
7
|
+
return name in receiver ? receiver : void 0;
|
|
8
|
+
}
|
|
9
|
+
export {
|
|
10
|
+
__methodDep
|
|
11
|
+
};
|
package/dist/runtime.d.ts
CHANGED
|
@@ -385,14 +385,13 @@ export declare function flushSync<T>(fn: () => T): T;
|
|
|
385
385
|
* Compiler-emitted on a host element's ref MOUNT. Defers the attach until commit
|
|
386
386
|
* (drainRefAttaches) so the node is connected when a callback ref fires and
|
|
387
387
|
* ref.current is set before layout effects run. Each entry records its owning
|
|
388
|
-
* `block
|
|
389
|
-
*
|
|
390
|
-
*
|
|
391
|
-
*
|
|
392
|
-
*
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
export declare function queueRefAttach(scope: Scope, fn: () => void): void;
|
|
388
|
+
* `block`; drainRefAttaches preserves enqueue order for disjoint subtrees and
|
|
389
|
+
* only reorders ancestor/descendant pairs for child-before-parent ordering.
|
|
390
|
+
* Ref identity UPDATES queue here too (paired with a queueRefDetach of the old
|
|
391
|
+
* ref), so within one commit every detach drains before every attach — a ref
|
|
392
|
+
* hopping between elements never ends null, whichever binding updates first.
|
|
393
|
+
*/
|
|
394
|
+
export declare function queueRefAttach(scope: Scope, ref: any, el: Element | FragmentInstance): void;
|
|
396
395
|
/**
|
|
397
396
|
* Queue a teardown ref detach for commit (compiled `ref` binding / spread-ref /
|
|
398
397
|
* hostComponent / fragment-ref unmount cleanups, and the de-opt teardown walk).
|
package/dist/runtime.js
CHANGED
|
@@ -1018,6 +1018,9 @@ let passiveScheduled = false;
|
|
|
1018
1018
|
let commitSeq = 0;
|
|
1019
1019
|
const storeSyncQueue = [];
|
|
1020
1020
|
const refAttachQueue = [];
|
|
1021
|
+
function attachLiveFragmentRef(instance) {
|
|
1022
|
+
attachRef(instance._currentRef, instance);
|
|
1023
|
+
}
|
|
1021
1024
|
let WIP_CAPTURE = null;
|
|
1022
1025
|
function createOffscreenCapture() {
|
|
1023
1026
|
return {
|
|
@@ -1474,10 +1477,10 @@ function flushSync(fn) {
|
|
|
1474
1477
|
}
|
|
1475
1478
|
}
|
|
1476
1479
|
const LAYOUT_CASCADE_LIMIT = 50;
|
|
1477
|
-
function queueRefAttach(scope,
|
|
1480
|
+
function queueRefAttach(scope, ref, el) {
|
|
1478
1481
|
(WIP_CAPTURE !== null ? WIP_CAPTURE.refs : refAttachQueue).push({
|
|
1479
|
-
|
|
1480
|
-
|
|
1482
|
+
ref,
|
|
1483
|
+
el,
|
|
1481
1484
|
block: scope.block
|
|
1482
1485
|
});
|
|
1483
1486
|
}
|
|
@@ -1532,13 +1535,13 @@ function drainRefDetaches() {
|
|
|
1532
1535
|
function drainRefAttaches() {
|
|
1533
1536
|
if (refAttachQueue.length === 0) return;
|
|
1534
1537
|
const q = refAttachQueue.splice(0);
|
|
1535
|
-
q.sort((a, b) => comparePostOrder(a.block,
|
|
1538
|
+
q.sort((a, b) => comparePostOrder(a.block, 0, b.block, 0));
|
|
1536
1539
|
for (const r of q) {
|
|
1537
1540
|
if (blockSubtreeDisposed(r.block)) continue;
|
|
1538
1541
|
try {
|
|
1539
1542
|
REF_CALLBACK_DEPTH++;
|
|
1540
1543
|
try {
|
|
1541
|
-
r.
|
|
1544
|
+
attachRef(r.ref, r.el);
|
|
1542
1545
|
} finally {
|
|
1543
1546
|
REF_CALLBACK_DEPTH--;
|
|
1544
1547
|
}
|
|
@@ -1558,6 +1561,30 @@ function blockSubtreeDisposed(block) {
|
|
|
1558
1561
|
}
|
|
1559
1562
|
return false;
|
|
1560
1563
|
}
|
|
1564
|
+
function refAttachWasDiscarded(uncommitted, el, ref) {
|
|
1565
|
+
return uncommitted !== null && uncommitted.get(el) === ref;
|
|
1566
|
+
}
|
|
1567
|
+
function discardSubtreeRefAttachesFrom(queue, root, uncommitted) {
|
|
1568
|
+
let write = 0;
|
|
1569
|
+
for (let read = 0; read < queue.length; read++) {
|
|
1570
|
+
const entry = queue[read];
|
|
1571
|
+
const owner = entry.block;
|
|
1572
|
+
if (owner === root || owner !== null && blockIsAncestorOf(root, owner)) {
|
|
1573
|
+
uncommitted.set(
|
|
1574
|
+
entry.el,
|
|
1575
|
+
entry.ref === attachLiveFragmentRef ? entry.el._currentRef : entry.ref
|
|
1576
|
+
);
|
|
1577
|
+
continue;
|
|
1578
|
+
}
|
|
1579
|
+
queue[write++] = entry;
|
|
1580
|
+
}
|
|
1581
|
+
queue.length = write;
|
|
1582
|
+
}
|
|
1583
|
+
function discardSubtreeRefAttaches(root, uncommitted = /* @__PURE__ */ new Map()) {
|
|
1584
|
+
discardSubtreeRefAttachesFrom(refAttachQueue, root, uncommitted);
|
|
1585
|
+
if (WIP_CAPTURE !== null) discardSubtreeRefAttachesFrom(WIP_CAPTURE.refs, root, uncommitted);
|
|
1586
|
+
return uncommitted;
|
|
1587
|
+
}
|
|
1561
1588
|
function commitEffects() {
|
|
1562
1589
|
if (effectEventQueue.length === 0 && effectEventCommitActions.length === 0 && effectQueues[INSERTION].length === 0 && effectQueues[LAYOUT].length === 0 && refDetachQueue.length === 0 && refAttachQueue.length === 0 && storeSyncQueue.length === 0 && activeFragments.size === 0 && !hasControlledSyncs()) {
|
|
1563
1590
|
if ((effectQueues[PASSIVE].length > 0 || pendingPassiveUnmounts.length > 0) && !passiveScheduled) {
|
|
@@ -6017,7 +6044,7 @@ function isFocusable(el) {
|
|
|
6017
6044
|
function mountFragmentRef(scope, startMarker, endMarker, ref) {
|
|
6018
6045
|
const fi = new FragmentInstance(scope.block, startMarker, endMarker);
|
|
6019
6046
|
fi._currentRef = ref;
|
|
6020
|
-
queueRefAttach(scope,
|
|
6047
|
+
queueRefAttach(scope, attachLiveFragmentRef, fi);
|
|
6021
6048
|
(scope.cleanups ??= []).push(() => {
|
|
6022
6049
|
queueRefDetach(fi._currentRef, fi);
|
|
6023
6050
|
fi._destroy();
|
|
@@ -6552,7 +6579,7 @@ function setSpread(el, value, prev, mountScope, skipDangerouslySetInnerHTML = fa
|
|
|
6552
6579
|
const pv = prev ? prev[k] : void 0;
|
|
6553
6580
|
if (k === "ref") {
|
|
6554
6581
|
if (v === pv) continue;
|
|
6555
|
-
if (mountScope) queueRefAttach(mountScope,
|
|
6582
|
+
if (mountScope) queueRefAttach(mountScope, v, el);
|
|
6556
6583
|
else attachRef(v, el);
|
|
6557
6584
|
continue;
|
|
6558
6585
|
}
|
|
@@ -8875,7 +8902,7 @@ function positionalChildren(children) {
|
|
|
8875
8902
|
}
|
|
8876
8903
|
function applyDeoptProp(el, name, v, ownerBlock) {
|
|
8877
8904
|
if (name === "ref") {
|
|
8878
|
-
if (v != null) queueRefAttach(ownerBlock,
|
|
8905
|
+
if (v != null) queueRefAttach(ownerBlock, v, el);
|
|
8879
8906
|
} else if (name === "className" || name === "class") {
|
|
8880
8907
|
setDeoptClass(el, v);
|
|
8881
8908
|
} else if (name === "style") {
|
|
@@ -9000,7 +9027,7 @@ function applyHostProps(el, props, scope, state) {
|
|
|
9000
9027
|
if (name === "ref") {
|
|
9001
9028
|
if (v !== state.ref) {
|
|
9002
9029
|
if (state.ref != null) queueRefDetach(state.ref, el);
|
|
9003
|
-
if (v != null) queueRefAttach(scope,
|
|
9030
|
+
if (v != null) queueRefAttach(scope, v, el);
|
|
9004
9031
|
state.ref = v;
|
|
9005
9032
|
}
|
|
9006
9033
|
} else if (name === "className" || name === "class") {
|
|
@@ -11067,17 +11094,19 @@ function hideTryContentAndMountPending(state, resumeThenable) {
|
|
|
11067
11094
|
}
|
|
11068
11095
|
if (state.tryBlock) {
|
|
11069
11096
|
const persistent = state.tryBlock;
|
|
11097
|
+
const uncommittedRefs = discardSubtreeRefAttaches(persistent);
|
|
11070
11098
|
deactivateScope(persistent, false);
|
|
11071
11099
|
if (state.parentBlock.disposed || persistent.disposed || state.tryBlock !== persistent) {
|
|
11072
11100
|
return false;
|
|
11073
11101
|
}
|
|
11074
11102
|
if (state.detachedRefs === null) {
|
|
11075
11103
|
state.detachedRefs = [];
|
|
11076
|
-
detachSubtreeRefs(persistent, state.detachedRefs, true, false);
|
|
11104
|
+
detachSubtreeRefs(persistent, state.detachedRefs, true, false, uncommittedRefs);
|
|
11077
11105
|
}
|
|
11078
11106
|
if (state.parentBlock.disposed || persistent.disposed || state.tryBlock !== persistent) {
|
|
11079
11107
|
return false;
|
|
11080
11108
|
}
|
|
11109
|
+
discardSubtreeRefAttaches(persistent, uncommittedRefs);
|
|
11081
11110
|
persistent.inactive = true;
|
|
11082
11111
|
}
|
|
11083
11112
|
if (!mountPendingBody(state)) return false;
|
|
@@ -11363,7 +11392,7 @@ function queueCurrentHiddenRefs(state) {
|
|
|
11363
11392
|
collectVisibleSubtreeRefs(state.tryBlock, refs);
|
|
11364
11393
|
for (let i = 0; i < refs.length; i++) {
|
|
11365
11394
|
const entry = refs[i];
|
|
11366
|
-
queueRefAttach(entry.scope,
|
|
11395
|
+
queueRefAttach(entry.scope, entry.ref, entry.el);
|
|
11367
11396
|
}
|
|
11368
11397
|
}
|
|
11369
11398
|
function attemptHiddenReveal(state, scheduledMode) {
|
|
@@ -11488,14 +11517,12 @@ function compareStagedRevealDomOrder(a, b) {
|
|
|
11488
11517
|
if (position & Node.DOCUMENT_POSITION_PRECEDING) return 1;
|
|
11489
11518
|
return 0;
|
|
11490
11519
|
}
|
|
11491
|
-
function
|
|
11520
|
+
function rebaseOffscreenEffectSeq(capture) {
|
|
11492
11521
|
const effects = capture.effects[INSERTION].concat(
|
|
11493
11522
|
capture.effects[LAYOUT],
|
|
11494
11523
|
capture.effects[PASSIVE]
|
|
11495
11524
|
).sort((a, b) => a.seq - b.seq);
|
|
11496
11525
|
for (let i = 0; i < effects.length; i++) effects[i].seq = commitSeq++;
|
|
11497
|
-
const refs = capture.refs.slice().sort((a, b) => a.seq - b.seq);
|
|
11498
|
-
for (let i = 0; i < refs.length; i++) refs[i].seq = commitSeq++;
|
|
11499
11526
|
}
|
|
11500
11527
|
function flushStagedReveals() {
|
|
11501
11528
|
if (flushingStagedReveals) return;
|
|
@@ -11521,7 +11548,7 @@ function flushStagedReveals() {
|
|
|
11521
11548
|
const deferEffects = batch.every((state) => state.stagedCapture !== null);
|
|
11522
11549
|
if (deferEffects) {
|
|
11523
11550
|
batch.sort(compareStagedRevealDomOrder);
|
|
11524
|
-
for (const state of batch)
|
|
11551
|
+
for (const state of batch) rebaseOffscreenEffectSeq(state.stagedCapture);
|
|
11525
11552
|
}
|
|
11526
11553
|
const previousDeferral = deferringStagedRevealEffects;
|
|
11527
11554
|
deferringStagedRevealEffects = deferEffects;
|
|
@@ -12515,9 +12542,9 @@ function forEachSubtreeChild(scope, visit, includeHiddenTry = true) {
|
|
|
12515
12542
|
}
|
|
12516
12543
|
}
|
|
12517
12544
|
}
|
|
12518
|
-
function detachSubtreeRefs(scope, out, shouldDetach = true, includeHiddenTry = true) {
|
|
12545
|
+
function detachSubtreeRefs(scope, out, shouldDetach = true, includeHiddenTry = true, uncommitted = null) {
|
|
12519
12546
|
const deoptRoot = scope.deoptNode;
|
|
12520
|
-
if (deoptRoot != null) detachDeoptTreeRefs(deoptRoot, out, shouldDetach, scope);
|
|
12547
|
+
if (deoptRoot != null) detachDeoptTreeRefs(deoptRoot, out, shouldDetach, scope, uncommitted);
|
|
12521
12548
|
const rm = scope.refFields;
|
|
12522
12549
|
if (rm !== null) {
|
|
12523
12550
|
const bag = scope.slots[0];
|
|
@@ -12529,19 +12556,25 @@ function detachSubtreeRefs(scope, out, shouldDetach = true, includeHiddenTry = t
|
|
|
12529
12556
|
if (ref == null) continue;
|
|
12530
12557
|
const el = bag[rm[j + 2]];
|
|
12531
12558
|
out.push({ ref, el, scope });
|
|
12532
|
-
if (shouldDetach
|
|
12559
|
+
if (shouldDetach && !refAttachWasDiscarded(uncommitted, el, ref)) {
|
|
12560
|
+
attachRef(ref, null, el);
|
|
12561
|
+
}
|
|
12533
12562
|
} else if (kind === "s") {
|
|
12534
12563
|
const ref = bag[rm[j + 1]]?.ref;
|
|
12535
12564
|
if (ref == null) continue;
|
|
12536
12565
|
const el = bag[rm[j + 2]];
|
|
12537
12566
|
if (el == null) continue;
|
|
12538
12567
|
out.push({ ref, el, scope });
|
|
12539
|
-
if (shouldDetach
|
|
12568
|
+
if (shouldDetach && !refAttachWasDiscarded(uncommitted, el, ref)) {
|
|
12569
|
+
attachRef(ref, null, el);
|
|
12570
|
+
}
|
|
12540
12571
|
} else {
|
|
12541
12572
|
const fi = bag[rm[j + 1]];
|
|
12542
12573
|
if (fi == null || fi._currentRef == null) continue;
|
|
12543
12574
|
out.push({ ref: fi._currentRef, el: fi, scope });
|
|
12544
|
-
if (shouldDetach
|
|
12575
|
+
if (shouldDetach && !refAttachWasDiscarded(uncommitted, fi, fi._currentRef)) {
|
|
12576
|
+
attachRef(fi._currentRef, null, fi);
|
|
12577
|
+
}
|
|
12545
12578
|
}
|
|
12546
12579
|
}
|
|
12547
12580
|
}
|
|
@@ -12552,27 +12585,31 @@ function detachSubtreeRefs(scope, out, shouldDetach = true, includeHiddenTry = t
|
|
|
12552
12585
|
if (s === null || typeof s !== "object") continue;
|
|
12553
12586
|
if (s.ref != null && s.anchor !== void 0 && s.el instanceof Element) {
|
|
12554
12587
|
out.push({ ref: s.ref, el: s.el, scope });
|
|
12555
|
-
if (shouldDetach
|
|
12588
|
+
if (shouldDetach && !refAttachWasDiscarded(uncommitted, s.el, s.ref)) {
|
|
12589
|
+
attachRef(s.ref, null, s.el);
|
|
12590
|
+
}
|
|
12556
12591
|
}
|
|
12557
12592
|
if (s.__kind === "childSlot" && s.hostNode != null) {
|
|
12558
|
-
detachDeoptTreeRefs(s.hostNode, out, shouldDetach, scope);
|
|
12593
|
+
detachDeoptTreeRefs(s.hostNode, out, shouldDetach, scope, uncommitted);
|
|
12559
12594
|
}
|
|
12560
12595
|
}
|
|
12561
12596
|
forEachSubtreeChild(
|
|
12562
12597
|
scope,
|
|
12563
|
-
(child2) => detachSubtreeRefs(child2, out, shouldDetach, includeHiddenTry),
|
|
12598
|
+
(child2) => detachSubtreeRefs(child2, out, shouldDetach, includeHiddenTry, uncommitted),
|
|
12564
12599
|
includeHiddenTry
|
|
12565
12600
|
);
|
|
12566
12601
|
}
|
|
12567
12602
|
function collectVisibleSubtreeRefs(scope, out) {
|
|
12568
12603
|
detachSubtreeRefs(scope, out, false, false);
|
|
12569
12604
|
}
|
|
12570
|
-
function detachDeoptTreeRefs(node, out, shouldDetach = true, ownerScope) {
|
|
12605
|
+
function detachDeoptTreeRefs(node, out, shouldDetach = true, ownerScope, uncommitted = null) {
|
|
12571
12606
|
const ref = getDeoptDesc(node)?.props?.ref;
|
|
12572
12607
|
if (ref != null) {
|
|
12573
12608
|
if (out !== null) {
|
|
12574
12609
|
out.push({ ref, el: node, scope: ownerScope });
|
|
12575
|
-
if (shouldDetach
|
|
12610
|
+
if (shouldDetach && !refAttachWasDiscarded(uncommitted, node, ref)) {
|
|
12611
|
+
attachRef(ref, null, node);
|
|
12612
|
+
}
|
|
12576
12613
|
} else {
|
|
12577
12614
|
queueRefDetach(ref, node);
|
|
12578
12615
|
}
|
|
@@ -12584,7 +12621,7 @@ function detachDeoptTreeRefs(node, out, shouldDetach = true, ownerScope) {
|
|
|
12584
12621
|
c = nodeAfterPortalRange(c, rangeEnd);
|
|
12585
12622
|
continue;
|
|
12586
12623
|
}
|
|
12587
|
-
detachDeoptTreeRefs(c, out, shouldDetach, ownerScope);
|
|
12624
|
+
detachDeoptTreeRefs(c, out, shouldDetach, ownerScope, uncommitted);
|
|
12588
12625
|
c = c.nextSibling;
|
|
12589
12626
|
}
|
|
12590
12627
|
}
|
package/dist/runtime.server.d.ts
CHANGED
|
@@ -551,11 +551,15 @@ export interface RenderOptions {
|
|
|
551
551
|
}
|
|
552
552
|
export declare function setSsrSuspenseTimeout(ms: number): void;
|
|
553
553
|
export declare function getSsrSuspenseTimeout(): number;
|
|
554
|
-
type
|
|
554
|
+
type SuspenseResult = {
|
|
555
555
|
value: unknown;
|
|
556
556
|
} | {
|
|
557
557
|
reason: unknown;
|
|
558
558
|
};
|
|
559
|
+
type SuspenseOutcome = SuspenseResult & {
|
|
560
|
+
/** Thenable whose settlement produced this string-keyed cached result. */
|
|
561
|
+
thenable: PromiseLike<unknown>;
|
|
562
|
+
};
|
|
559
563
|
type ResolvedMap = Map<string, SuspenseOutcome> & {
|
|
560
564
|
/** Render-local stable ids for non-primitive control/list keys. */
|
|
561
565
|
asyncIdentities: Map<unknown, number>;
|
|
@@ -571,7 +575,7 @@ type ResolvedMap = Map<string, SuspenseOutcome> & {
|
|
|
571
575
|
site: ServerHookSlot | undefined;
|
|
572
576
|
frame: Frame | null;
|
|
573
577
|
}>;
|
|
574
|
-
resolvedT: Map<PromiseLike<unknown>,
|
|
578
|
+
resolvedT: Map<PromiseLike<unknown>, SuspenseResult>;
|
|
575
579
|
warm: Map<ServerHookSlot, {
|
|
576
580
|
deps: unknown[];
|
|
577
581
|
value: unknown;
|
package/dist/runtime.server.js
CHANGED
|
@@ -2258,6 +2258,13 @@ function use(usable, siteKey) {
|
|
|
2258
2258
|
const resolved = RESOLVED;
|
|
2259
2259
|
if (resolved !== null && resolved.has(key)) {
|
|
2260
2260
|
const entry = resolved.get(key);
|
|
2261
|
+
const thenable = usable;
|
|
2262
|
+
if (entry.thenable !== thenable) {
|
|
2263
|
+
try {
|
|
2264
|
+
thenable.then(NOOP, NOOP);
|
|
2265
|
+
} catch {
|
|
2266
|
+
}
|
|
2267
|
+
}
|
|
2261
2268
|
if ("reason" in entry) {
|
|
2262
2269
|
recordHydrationRejection(serial, entry.reason);
|
|
2263
2270
|
throw entry.reason;
|
|
@@ -3124,11 +3131,11 @@ async function settleSuspended(suspended, resolved, timeoutMs, signal) {
|
|
|
3124
3131
|
if (resolved.has(key)) return;
|
|
3125
3132
|
const isPu = key.charCodeAt(0) === 124 && key.startsWith("|pu#");
|
|
3126
3133
|
try {
|
|
3127
|
-
const outcome = { value: await promise };
|
|
3134
|
+
const outcome = { value: await promise, thenable: promise };
|
|
3128
3135
|
resolved.set(key, outcome);
|
|
3129
3136
|
if (isPu) pu.resolvedT.set(promise, outcome);
|
|
3130
3137
|
} catch (reason) {
|
|
3131
|
-
const outcome = { reason };
|
|
3138
|
+
const outcome = { reason, thenable: promise };
|
|
3132
3139
|
resolved.set(key, outcome);
|
|
3133
3140
|
if (isPu) pu.resolvedT.set(promise, outcome);
|
|
3134
3141
|
}
|
|
@@ -3147,11 +3154,13 @@ async function settleFirstOfWave(suspended, resolved, timeoutMs, signal) {
|
|
|
3147
3154
|
(async () => {
|
|
3148
3155
|
try {
|
|
3149
3156
|
const value = await promise;
|
|
3150
|
-
|
|
3151
|
-
if (
|
|
3157
|
+
const outcome = { value, thenable: promise };
|
|
3158
|
+
if (!resolved.has(key)) resolved.set(key, outcome);
|
|
3159
|
+
if (isPu && !pu.resolvedT.has(promise)) pu.resolvedT.set(promise, outcome);
|
|
3152
3160
|
} catch (reason) {
|
|
3153
|
-
|
|
3154
|
-
if (
|
|
3161
|
+
const outcome = { reason, thenable: promise };
|
|
3162
|
+
if (!resolved.has(key)) resolved.set(key, outcome);
|
|
3163
|
+
if (isPu && !pu.resolvedT.has(promise)) pu.resolvedT.set(promise, outcome);
|
|
3155
3164
|
}
|
|
3156
3165
|
})()
|
|
3157
3166
|
);
|
|
@@ -3296,11 +3305,13 @@ function recordHostedStratum(suspended, resolved) {
|
|
|
3296
3305
|
const isPu = key.startsWith("|pu#");
|
|
3297
3306
|
try {
|
|
3298
3307
|
const value = await promise;
|
|
3299
|
-
|
|
3300
|
-
if (
|
|
3308
|
+
const outcome = { value, thenable: promise };
|
|
3309
|
+
if (!resolved.has(key)) resolved.set(key, outcome);
|
|
3310
|
+
if (isPu && !pu.resolvedT.has(promise)) pu.resolvedT.set(promise, outcome);
|
|
3301
3311
|
} catch (reason) {
|
|
3302
|
-
|
|
3303
|
-
if (
|
|
3312
|
+
const outcome = { reason, thenable: promise };
|
|
3313
|
+
if (!resolved.has(key)) resolved.set(key, outcome);
|
|
3314
|
+
if (isPu && !pu.resolvedT.has(promise)) pu.resolvedT.set(promise, outcome);
|
|
3304
3315
|
}
|
|
3305
3316
|
})()
|
|
3306
3317
|
);
|
package/dist/server/index.d.ts
CHANGED
|
@@ -21,4 +21,5 @@
|
|
|
21
21
|
* should call them.
|
|
22
22
|
*/
|
|
23
23
|
export { executeServerFunction } from './rpc.js';
|
|
24
|
+
export { __methodDep } from '../method-dep.js';
|
|
24
25
|
export { renderToString, renderToStaticMarkup, renderToPipeableStream, renderToReadableStream, type RenderResult, type RenderOptions, type StreamOptions, type StreamInjectionSource, setSsrSuspenseTimeout, getSsrSuspenseTimeout, EXTERNAL_HYDRATION_PROMISE, HYDRATION_RANGE_BOUNDARY, useState, useLinkedState, useReducer, __useStateWithGetter, __useLinkedStateWithGetter, __useReducerWithGetter, type LinkedStatePrevious, type LinkedStateOptions, useEffect, useLayoutEffect, useInsertionEffect, useImperativeHandle, useMemo, useCallback, useRef, useId, useEffectEvent, useTransition, useDeferredValue, useSyncExternalStore, useActionState, useFormStatus, useOptimistic, useDebugValue, memo, lazy, hookSlots, withSlot, startTransition, flushSync, isChildrenBlock, isValidElement, cloneElement, Children, createPortal, requestFormReset, preload, preinit, preconnect, prefetchDNS, Suspense, ErrorBoundary, Hydrate, Fragment, Activity, ViewTransition, ViewTransition as unstable_ViewTransition, addTransitionType, addTransitionType as unstable_addTransitionType, createContext, use, useContext, ssrIsSuspense, type Context, type FormStatus, markChildrenBlock, createElement, createScopedValue, createScopedElement, positionalChildren, escapeHtml, escapeAttr, ssrText, ssrTextPre, ssrChild, ssrChildText, ssrAttr, normalizeClass, ssrStyle, ssrClass, ssrAttrs, ssrSnapshotSpread, ssrSpread, ssrInnerHtml, ssrScriptInnerHtml, ssrChildrenSources, ssrVoidContent, ssrValueAttr, ssrCheckedAttr, ssrInputAttrs, ssrTextareaValue, ssrTextareaValueSources, ssrSelectAttrs, ssrSelectScope, ssrSelectScopeSources, ssrOptionValueSources, ssrOption, ssrElement, ssrComponent, ssrComponentNS, ssrInNamespace, ssrBlock, ssrActivity, ssrForBlock, mapSlot, ssrControl, ssrArm, ssrTry, ssrPortal, injectStyle, ssrHeadEl, namespaceHead, namespaceHeadElement, puMemo, puBatch, warmMemo, warmChild, } from '../runtime.server.js';
|
package/dist/server/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { executeServerFunction } from "./rpc.js";
|
|
2
|
+
import { __methodDep } from "../method-dep.js";
|
|
2
3
|
import {
|
|
3
4
|
renderToString,
|
|
4
5
|
renderToStaticMarkup,
|
|
@@ -122,6 +123,7 @@ export {
|
|
|
122
123
|
Hydrate,
|
|
123
124
|
Suspense,
|
|
124
125
|
ViewTransition,
|
|
126
|
+
__methodDep,
|
|
125
127
|
__useLinkedStateWithGetter,
|
|
126
128
|
__useReducerWithGetter,
|
|
127
129
|
__useStateWithGetter,
|