octane 0.1.24 → 0.1.26
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/README.md +4 -0
- package/dist/compiler/bundler.js +32 -15
- package/dist/compiler/register.js +144 -0
- package/dist/compiler/runtime-requests.js +36 -0
- package/dist/compiler/slot-hooks.js +15 -3
- package/dist/runtime.js +109 -5
- package/dist/universal-core.d.ts +1 -0
- package/dist/universal-core.js +439 -90
- package/package.json +4 -2
package/README.md
CHANGED
|
@@ -17,6 +17,10 @@ wrapper is narrower: the wrapper must be locally declared in a fully compiled
|
|
|
17
17
|
parameter to a supported hook. This package ships both the runtime and compiler,
|
|
18
18
|
with the compiler exposed at `octane/compiler`.
|
|
19
19
|
|
|
20
|
+
Direct Node or Bun server scripts can preload `octane/compiler/register` to
|
|
21
|
+
compile imported Octane components without going through Vite. See the
|
|
22
|
+
[SSR guide](https://github.com/octanejs/octane/blob/main/docs/ssr.md#run-an-ssg-script-directly).
|
|
23
|
+
|
|
20
24
|
For the full story, see the
|
|
21
25
|
[main README](https://github.com/octanejs/octane#readme).
|
|
22
26
|
|
package/dist/compiler/bundler.js
CHANGED
|
@@ -31,6 +31,7 @@ import { findLeadingJsxImportSourcePragma } from './pragma.js';
|
|
|
31
31
|
import { normalizeUniversalRuntime } from './universal-runtime.js';
|
|
32
32
|
import { formatCompileDiagnostic } from './native-change-diagnostics.js';
|
|
33
33
|
import { findVoidComponentImports, findVoidRootImports, slotHooks } from './slot-hooks.js';
|
|
34
|
+
import { rewriteServerRuntimeRequests } from './runtime-requests.js';
|
|
34
35
|
import { assertStrongMode } from './strong-mode.js';
|
|
35
36
|
import {
|
|
36
37
|
assertNoLiveClientOnlyImports,
|
|
@@ -848,6 +849,20 @@ class OctaneBundlerCompiler {
|
|
|
848
849
|
options.universalRuntime ?? this.defaults.universalRuntime,
|
|
849
850
|
);
|
|
850
851
|
const filename = this._canonicalModuleId(file);
|
|
852
|
+
const targetRuntimeRequests = (source, kind) => {
|
|
853
|
+
if (environment !== 'server' || options.explicitRuntimeRequests !== true) return null;
|
|
854
|
+
const runtimeResult = rewriteServerRuntimeRequests(source, filename);
|
|
855
|
+
if (runtimeResult === null) return null;
|
|
856
|
+
return {
|
|
857
|
+
code: runtimeResult.code,
|
|
858
|
+
map: runtimeResult.map,
|
|
859
|
+
kind,
|
|
860
|
+
...finishMetadata(collected),
|
|
861
|
+
};
|
|
862
|
+
};
|
|
863
|
+
const passThrough = () => {
|
|
864
|
+
return targetRuntimeRequests(code, 'runtime-requests') ?? this._passThrough(code, collected);
|
|
865
|
+
};
|
|
851
866
|
const clientOnlyImports =
|
|
852
867
|
environment === 'server' && Array.isArray(options.clientOnlyImports)
|
|
853
868
|
? options.clientOnlyImports
|
|
@@ -974,22 +989,22 @@ class OctaneBundlerCompiler {
|
|
|
974
989
|
if (this.requireDirective && !pragmaOwned && this._isProjectOwnedSource(file)) {
|
|
975
990
|
this._warnUnmarkedOctaneImport(code, filename);
|
|
976
991
|
}
|
|
977
|
-
return
|
|
992
|
+
return passThrough();
|
|
978
993
|
}
|
|
979
994
|
|
|
980
995
|
if (plainHelperSource) {
|
|
981
|
-
if (/\/\/\s*octane-no-slot\b/.test(code)) return
|
|
996
|
+
if (/\/\/\s*octane-no-slot\b/.test(code)) return passThrough();
|
|
982
997
|
if (this.exclude.some((path) => file.includes(path))) {
|
|
983
998
|
// Same conflict diagnostic as the full-compile gate: an ownership
|
|
984
999
|
// pragma inside an excluded path must not fail silent.
|
|
985
1000
|
if (this.requireDirective) {
|
|
986
1001
|
this._warnExcludedPragmaConflict(file, filename, pragmaOwned);
|
|
987
1002
|
}
|
|
988
|
-
return
|
|
1003
|
+
return passThrough();
|
|
989
1004
|
}
|
|
990
|
-
if (!/from\s*['"]octane['"]/.test(code)) return
|
|
1005
|
+
if (!/from\s*['"]octane['"]/.test(code)) return passThrough();
|
|
991
1006
|
if (!this._isInstalledOctaneSource(file, collected)) {
|
|
992
|
-
return
|
|
1007
|
+
return passThrough();
|
|
993
1008
|
}
|
|
994
1009
|
// Hook slotting is an Octane-ownership rewrite, so the ownership
|
|
995
1010
|
// gate applies to it exactly as to full compilation: an unmarked
|
|
@@ -997,7 +1012,7 @@ class OctaneBundlerCompiler {
|
|
|
997
1012
|
// pragma diagnostic), a pragma-marked one gets its hook slots.
|
|
998
1013
|
if (this.requireDirective && !pragmaOwned && this._isProjectOwnedSource(file)) {
|
|
999
1014
|
this._warnUnmarkedOctaneImport(code, filename);
|
|
1000
|
-
return
|
|
1015
|
+
return passThrough();
|
|
1001
1016
|
}
|
|
1002
1017
|
if (this._hasManualHookSlots(file, collected)) {
|
|
1003
1018
|
// Hand-slotted bindings still own their authored policy. Opting one
|
|
@@ -1008,7 +1023,7 @@ class OctaneBundlerCompiler {
|
|
|
1008
1023
|
strong,
|
|
1009
1024
|
});
|
|
1010
1025
|
}
|
|
1011
|
-
return
|
|
1026
|
+
return passThrough();
|
|
1012
1027
|
}
|
|
1013
1028
|
const profileFilename = profile ? this._profileModuleId(file, collected) : undefined;
|
|
1014
1029
|
const specializeVoidRoot =
|
|
@@ -1025,16 +1040,18 @@ class OctaneBundlerCompiler {
|
|
|
1025
1040
|
}
|
|
1026
1041
|
: {}),
|
|
1027
1042
|
});
|
|
1028
|
-
if (out === null) return
|
|
1029
|
-
return
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1043
|
+
if (out === null) return passThrough();
|
|
1044
|
+
return (
|
|
1045
|
+
targetRuntimeRequests(out.code, 'slots') ?? {
|
|
1046
|
+
code: out.code,
|
|
1047
|
+
map: out.map,
|
|
1048
|
+
kind: 'slots',
|
|
1049
|
+
...finishMetadata(collected),
|
|
1050
|
+
}
|
|
1051
|
+
);
|
|
1035
1052
|
}
|
|
1036
1053
|
|
|
1037
|
-
return
|
|
1054
|
+
return passThrough();
|
|
1038
1055
|
}
|
|
1039
1056
|
}
|
|
1040
1057
|
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Synchronous server-script compiler registration.
|
|
3
|
+
*
|
|
4
|
+
* Import this module before a Node or Bun entry point to compile authored
|
|
5
|
+
* `.tsrx`/`.tsx` modules and slot Octane hooks in plain `.ts`/`.js` helpers.
|
|
6
|
+
* Direct execution is a server pipeline, so authored `octane` runtime imports
|
|
7
|
+
* resolve to `octane/server`, matching the server code the compiler emits.
|
|
8
|
+
*/
|
|
9
|
+
import * as nodeFs from 'node:fs';
|
|
10
|
+
import * as nodeModule from 'node:module';
|
|
11
|
+
import * as nodePath from 'node:path';
|
|
12
|
+
import * as nodeUrl from 'node:url';
|
|
13
|
+
import { createOctaneCompiler } from './bundler.js';
|
|
14
|
+
|
|
15
|
+
const compiler = createOctaneCompiler({
|
|
16
|
+
root: process.cwd(),
|
|
17
|
+
environment: 'server',
|
|
18
|
+
hmr: false,
|
|
19
|
+
dev: false,
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
const extensionlessCandidates = ['.tsrx', '.tsx', '.ts', '.mts', '.mjs', '.js'];
|
|
23
|
+
|
|
24
|
+
function isFile(path) {
|
|
25
|
+
try {
|
|
26
|
+
return nodeFs.statSync(path).isFile();
|
|
27
|
+
} catch {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function candidateUrl(specifier, parentURL) {
|
|
33
|
+
if (parentURL === undefined || !parentURL.startsWith('file:')) return null;
|
|
34
|
+
if (!specifier.startsWith('.') && !specifier.startsWith('/') && !specifier.startsWith('file:')) {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
let requested;
|
|
39
|
+
try {
|
|
40
|
+
requested = new URL(specifier, parentURL);
|
|
41
|
+
} catch {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
if (requested.protocol !== 'file:') return null;
|
|
45
|
+
|
|
46
|
+
const requestedPath = nodeUrl.fileURLToPath(requested);
|
|
47
|
+
const extension = nodePath.extname(requestedPath);
|
|
48
|
+
const candidates = [];
|
|
49
|
+
if (extension === '') {
|
|
50
|
+
for (const candidateExtension of extensionlessCandidates) {
|
|
51
|
+
candidates.push(requestedPath + candidateExtension);
|
|
52
|
+
}
|
|
53
|
+
for (const candidateExtension of extensionlessCandidates) {
|
|
54
|
+
candidates.push(nodePath.join(requestedPath, 'index' + candidateExtension));
|
|
55
|
+
}
|
|
56
|
+
} else if (extension === '.js') {
|
|
57
|
+
const stem = requestedPath.slice(0, -extension.length);
|
|
58
|
+
candidates.push(stem + '.ts', stem + '.tsx');
|
|
59
|
+
} else if (extension === '.mjs') {
|
|
60
|
+
candidates.push(requestedPath.slice(0, -extension.length) + '.mts');
|
|
61
|
+
} else if (extension === '.cjs') {
|
|
62
|
+
candidates.push(requestedPath.slice(0, -extension.length) + '.cts');
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
for (const candidate of candidates) {
|
|
66
|
+
if (!isFile(candidate)) continue;
|
|
67
|
+
const resolved = nodeUrl.pathToFileURL(candidate);
|
|
68
|
+
resolved.search = requested.search;
|
|
69
|
+
resolved.hash = requested.hash;
|
|
70
|
+
return resolved.href;
|
|
71
|
+
}
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function sourceText(source) {
|
|
76
|
+
if (typeof source === 'string') return source;
|
|
77
|
+
if (source === undefined) return null;
|
|
78
|
+
return new TextDecoder().decode(source);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function resolve(specifier, context, nextResolve) {
|
|
82
|
+
const runtimeRequest = compiler.resolveRuntimeRequest(specifier, 'server');
|
|
83
|
+
if (runtimeRequest !== null) return nextResolve(runtimeRequest, context);
|
|
84
|
+
|
|
85
|
+
try {
|
|
86
|
+
return nextResolve(specifier, context);
|
|
87
|
+
} catch (error) {
|
|
88
|
+
const url = candidateUrl(specifier, context.parentURL);
|
|
89
|
+
if (url !== null) return { url, shortCircuit: true };
|
|
90
|
+
throw error;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function load(url, context, nextLoad) {
|
|
95
|
+
if (!url.startsWith('file:')) return nextLoad(url, context);
|
|
96
|
+
const filename = nodeUrl.fileURLToPath(url);
|
|
97
|
+
|
|
98
|
+
if (filename.endsWith('.tsrx') || filename.endsWith('.tsx')) {
|
|
99
|
+
const source = nodeFs.readFileSync(filename, 'utf8');
|
|
100
|
+
const result = compiler.transform(source, filename, { environment: 'server' });
|
|
101
|
+
if (result === null || result.kind === 'none') return nextLoad(url, context);
|
|
102
|
+
return { format: 'module', source: result.code, shortCircuit: true };
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if ((filename.endsWith('.ts') && !filename.endsWith('.d.ts')) || filename.endsWith('.js')) {
|
|
106
|
+
const loaded = nextLoad(url, context);
|
|
107
|
+
const source = sourceText(loaded.source);
|
|
108
|
+
if (source === null) return loaded;
|
|
109
|
+
const result = compiler.transform(source, filename, { environment: 'server' });
|
|
110
|
+
if (result === null || result.kind === 'none') return loaded;
|
|
111
|
+
return { ...loaded, source: result.code, shortCircuit: true };
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return nextLoad(url, context);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function registerBunPlugin() {
|
|
118
|
+
globalThis.Bun.plugin({
|
|
119
|
+
name: 'octane-compiler',
|
|
120
|
+
setup(build) {
|
|
121
|
+
build.onLoad({ filter: /\.(?:tsrx|tsx|ts|js)$/, namespace: 'file' }, ({ path }) => {
|
|
122
|
+
const source = nodeFs.readFileSync(path, 'utf8');
|
|
123
|
+
const result = compiler.transform(source, path, {
|
|
124
|
+
environment: 'server',
|
|
125
|
+
explicitRuntimeRequests: true,
|
|
126
|
+
});
|
|
127
|
+
const loader =
|
|
128
|
+
path.endsWith('.tsx') || path.endsWith('.tsrx')
|
|
129
|
+
? 'tsx'
|
|
130
|
+
: path.endsWith('.ts')
|
|
131
|
+
? 'ts'
|
|
132
|
+
: 'js';
|
|
133
|
+
if (result === null || result.kind === 'none') return { contents: source, loader };
|
|
134
|
+
return {
|
|
135
|
+
contents: result.code,
|
|
136
|
+
loader: result.kind === 'slots' || result.kind === 'runtime-requests' ? loader : 'js',
|
|
137
|
+
};
|
|
138
|
+
});
|
|
139
|
+
},
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (typeof globalThis.Bun === 'object') registerBunPlugin();
|
|
144
|
+
else nodeModule.registerHooks({ resolve, load });
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { initSync as initModuleLexer, parse as lexModule } from 'es-module-lexer';
|
|
2
|
+
|
|
3
|
+
let moduleLexerInitialized = false;
|
|
4
|
+
|
|
5
|
+
/** Target bare Octane ESM requests at the explicit server runtime. */
|
|
6
|
+
export function rewriteServerRuntimeRequests(source, id) {
|
|
7
|
+
if (!source.includes('octane')) return null;
|
|
8
|
+
if (!moduleLexerInitialized) {
|
|
9
|
+
initModuleLexer();
|
|
10
|
+
moduleLexerInitialized = true;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
let imports;
|
|
14
|
+
try {
|
|
15
|
+
[imports] = lexModule(source, id);
|
|
16
|
+
} catch {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
let code = source;
|
|
21
|
+
let changed = false;
|
|
22
|
+
for (let index = imports.length - 1; index >= 0; index--) {
|
|
23
|
+
const request = imports[index];
|
|
24
|
+
if (request.n !== 'octane') continue;
|
|
25
|
+
if (request.d === -1) {
|
|
26
|
+
code = code.slice(0, request.s) + 'octane/server' + code.slice(request.e);
|
|
27
|
+
changed = true;
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
const quote = source[request.s];
|
|
31
|
+
if (quote !== "'" && quote !== '"' && quote !== '`') continue;
|
|
32
|
+
code = code.slice(0, request.s) + `${quote}octane/server${quote}` + code.slice(request.e);
|
|
33
|
+
changed = true;
|
|
34
|
+
}
|
|
35
|
+
return changed ? { code, map: null } : null;
|
|
36
|
+
}
|
|
@@ -27,6 +27,13 @@ function octaneHookLocals(ast) {
|
|
|
27
27
|
let importsHook = false;
|
|
28
28
|
let hasOctaneImport = false;
|
|
29
29
|
for (const node of ast.body || []) {
|
|
30
|
+
if (
|
|
31
|
+
(node.type === 'ExportNamedDeclaration' || node.type === 'ExportAllDeclaration') &&
|
|
32
|
+
node.source?.value === 'octane'
|
|
33
|
+
) {
|
|
34
|
+
hasOctaneImport = true;
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
30
37
|
if (node.type !== 'ImportDeclaration' || node.source?.value !== 'octane') continue;
|
|
31
38
|
hasOctaneImport = true;
|
|
32
39
|
for (const sp of node.specifiers || []) {
|
|
@@ -1025,7 +1032,9 @@ export function slotHooks(source, id, options) {
|
|
|
1025
1032
|
!options?.profile &&
|
|
1026
1033
|
typeof options?.isVoidComponentImport === 'function' &&
|
|
1027
1034
|
importInfo.hasOctaneImport;
|
|
1028
|
-
if (!importInfo.importsHook && !canSpecializeRoot)
|
|
1035
|
+
if (!importInfo.importsHook && !canSpecializeRoot) {
|
|
1036
|
+
return null;
|
|
1037
|
+
}
|
|
1029
1038
|
// The parsed tree is never mutated: annotateHookCalls returns a COW-rebuilt
|
|
1030
1039
|
// module whose hook calls carry their `_octane*` props (start/end offsets are
|
|
1031
1040
|
// preserved, so the text edits below stay valid), with the dependency
|
|
@@ -1072,8 +1081,11 @@ export function slotHooks(source, id, options) {
|
|
|
1072
1081
|
// Apply insertions right-to-left so earlier offsets stay valid.
|
|
1073
1082
|
st.edits.sort((a, b) => b.pos - a.pos);
|
|
1074
1083
|
let code = source;
|
|
1075
|
-
for (const
|
|
1076
|
-
code =
|
|
1084
|
+
for (const edit of st.edits) {
|
|
1085
|
+
code =
|
|
1086
|
+
code.slice(0, edit.pos) +
|
|
1087
|
+
edit.text +
|
|
1088
|
+
code.slice(edit.end === undefined ? edit.pos : edit.end);
|
|
1077
1089
|
}
|
|
1078
1090
|
|
|
1079
1091
|
// APPEND the slot consts (rather than prepend) so every original line number
|
package/dist/runtime.js
CHANGED
|
@@ -100,6 +100,14 @@ function profilePortalComponent(rawBody) {
|
|
|
100
100
|
function ensureHooks(scope) {
|
|
101
101
|
return scope.hooks ?? (scope.hooks = /* @__PURE__ */ new Map());
|
|
102
102
|
}
|
|
103
|
+
function registerHookCleanup(scope, cleanup) {
|
|
104
|
+
if (process.env.NODE_ENV !== "production") {
|
|
105
|
+
if (scope.block.body[HMR] !== void 0) {
|
|
106
|
+
cleanup[HMR] = true;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
(scope.cleanups ??= []).push(cleanup);
|
|
110
|
+
}
|
|
103
111
|
let nextHookSlot = 0;
|
|
104
112
|
function hookSlots(count) {
|
|
105
113
|
const base = nextHookSlot;
|
|
@@ -2446,7 +2454,7 @@ function unmountScope(scope, detachDom = true) {
|
|
|
2446
2454
|
unmountScopeChildrenAndSlots(scope, detachDom);
|
|
2447
2455
|
});
|
|
2448
2456
|
}
|
|
2449
|
-
function
|
|
2457
|
+
function runScopeCleanups(scope) {
|
|
2450
2458
|
const c = scope.cleanups;
|
|
2451
2459
|
if (c !== null)
|
|
2452
2460
|
for (let i = c.length - 1; i >= 0; i--) {
|
|
@@ -2456,6 +2464,12 @@ function unmountScopeChildrenAndSlots(scope, detachDom) {
|
|
|
2456
2464
|
reportTeardownError(err);
|
|
2457
2465
|
}
|
|
2458
2466
|
}
|
|
2467
|
+
}
|
|
2468
|
+
function unmountScopeChildrenAndSlots(scope, detachDom) {
|
|
2469
|
+
runScopeCleanups(scope);
|
|
2470
|
+
unmountScopeChildrenAndSlotsOnly(scope, detachDom);
|
|
2471
|
+
}
|
|
2472
|
+
function unmountScopeChildrenAndSlotsOnly(scope, detachDom) {
|
|
2459
2473
|
const children = scope.children;
|
|
2460
2474
|
if (children !== null)
|
|
2461
2475
|
for (let i = 0, n = children.length; i < n; i++) unmountScope(children[i].scope, detachDom);
|
|
@@ -3123,7 +3137,7 @@ function useEffectEvent(fn, slot) {
|
|
|
3123
3137
|
s = { impl: fn, active: true };
|
|
3124
3138
|
ensureHooks(scope).set(slot, s);
|
|
3125
3139
|
const cell2 = s;
|
|
3126
|
-
(scope
|
|
3140
|
+
registerHookCleanup(scope, () => {
|
|
3127
3141
|
cell2.active = false;
|
|
3128
3142
|
});
|
|
3129
3143
|
} else {
|
|
@@ -3205,6 +3219,87 @@ function scopedChildrenAsBody(props) {
|
|
|
3205
3219
|
return (_props, scope, extra) => childrenAsBody(props.children)(void 0, scope, extra);
|
|
3206
3220
|
}
|
|
3207
3221
|
const CHILDREN_DIALECT_SLOT = /* @__PURE__ */ Symbol("octane.childrenDialect");
|
|
3222
|
+
function hasResettableHmrRange(block) {
|
|
3223
|
+
const start = block.startMarker;
|
|
3224
|
+
const end = block.endMarker;
|
|
3225
|
+
if (start === void 0 || end === void 0) return false;
|
|
3226
|
+
if (start === null || end === null) {
|
|
3227
|
+
return start === null && end === null && (block.kind === "root" || block.exclusiveMarkers === true);
|
|
3228
|
+
}
|
|
3229
|
+
if (start !== end) {
|
|
3230
|
+
return start.parentNode === block.parentNode && end.parentNode === block.parentNode;
|
|
3231
|
+
}
|
|
3232
|
+
for (let parent = block.parentBlock; parent !== null; parent = parent.parentBlock) {
|
|
3233
|
+
if (parent.startMarker === start && parent.endMarker === end) return false;
|
|
3234
|
+
}
|
|
3235
|
+
return start.parentNode === block.parentNode;
|
|
3236
|
+
}
|
|
3237
|
+
function promoteHmrBlockRange(block) {
|
|
3238
|
+
const root = block.startMarker;
|
|
3239
|
+
if (root === null || root !== block.endMarker) return;
|
|
3240
|
+
const parent = block.parentNode;
|
|
3241
|
+
const rangeStart = document.createComment("hmr");
|
|
3242
|
+
const rangeEnd = document.createComment("/hmr");
|
|
3243
|
+
parent.insertBefore(rangeStart, root);
|
|
3244
|
+
parent.insertBefore(rangeEnd, root.nextSibling);
|
|
3245
|
+
block.startMarker = rangeStart;
|
|
3246
|
+
block.endMarker = rangeEnd;
|
|
3247
|
+
block.exclusiveMarkers = false;
|
|
3248
|
+
}
|
|
3249
|
+
function resetHmrBlock(block) {
|
|
3250
|
+
if (TEARDOWN_DEPTH === 0) {
|
|
3251
|
+
TEARDOWN_HANDLER = findTryHandler(block.parentBlock) ?? rendererRegionTryHandler(block);
|
|
3252
|
+
}
|
|
3253
|
+
TEARDOWN_DEPTH++;
|
|
3254
|
+
let abortedRefs = null;
|
|
3255
|
+
if (!block.mounted) {
|
|
3256
|
+
abortedRefs = [];
|
|
3257
|
+
collectVisibleSubtreeRefs(block, abortedRefs);
|
|
3258
|
+
}
|
|
3259
|
+
try {
|
|
3260
|
+
withRefDetachSuppression(abortedRefs, () => {
|
|
3261
|
+
if (block.deoptNode !== null) detachDeoptTreeRefs(block.deoptNode, null);
|
|
3262
|
+
const cleanups = block.cleanups;
|
|
3263
|
+
let preserved = null;
|
|
3264
|
+
if (cleanups !== null) {
|
|
3265
|
+
for (let i = cleanups.length - 1; i >= 0; i--) {
|
|
3266
|
+
const cleanup = cleanups[i];
|
|
3267
|
+
if (cleanup[HMR] === true) continue;
|
|
3268
|
+
try {
|
|
3269
|
+
runEffectLifecycleCallback(cleanup);
|
|
3270
|
+
} catch (err) {
|
|
3271
|
+
reportTeardownError(err);
|
|
3272
|
+
}
|
|
3273
|
+
}
|
|
3274
|
+
for (let i = 0; i < cleanups.length; i++) {
|
|
3275
|
+
const cleanup = cleanups[i];
|
|
3276
|
+
if (cleanup[HMR] === true) (preserved ??= []).push(cleanup);
|
|
3277
|
+
}
|
|
3278
|
+
}
|
|
3279
|
+
unmountScopeChildrenAndSlotsOnly(block, true);
|
|
3280
|
+
removeRange(
|
|
3281
|
+
block.startMarker !== null ? block.startMarker.nextSibling : block.parentNode.firstChild,
|
|
3282
|
+
block.endMarker
|
|
3283
|
+
);
|
|
3284
|
+
block.children = null;
|
|
3285
|
+
block.cleanups = preserved;
|
|
3286
|
+
block._slots = null;
|
|
3287
|
+
block.refFields = null;
|
|
3288
|
+
block.slots = [];
|
|
3289
|
+
block.deoptNode = null;
|
|
3290
|
+
});
|
|
3291
|
+
const hooks = block.hooks;
|
|
3292
|
+
if (hooks !== null) {
|
|
3293
|
+
for (const value of hooks.values()) {
|
|
3294
|
+
if (value !== null && typeof value === "object" && Object.prototype.hasOwnProperty.call(value, "deps")) {
|
|
3295
|
+
value.deps = void 0;
|
|
3296
|
+
}
|
|
3297
|
+
}
|
|
3298
|
+
}
|
|
3299
|
+
} finally {
|
|
3300
|
+
if (--TEARDOWN_DEPTH === 0) dispatchTeardownErrors();
|
|
3301
|
+
}
|
|
3302
|
+
}
|
|
3208
3303
|
function resetScopeChildren(scope) {
|
|
3209
3304
|
const block = scope.block;
|
|
3210
3305
|
if (TEARDOWN_DEPTH === 0) {
|
|
@@ -10559,6 +10654,13 @@ function hmr(fn) {
|
|
|
10559
10654
|
const nextFn = incomingMeta ? incomingMeta.fn : incoming;
|
|
10560
10655
|
if (meta.fn.__octaneReturnedOutput !== nextFn.__octaneReturnedOutput)
|
|
10561
10656
|
return false;
|
|
10657
|
+
for (const b of meta.liveBlocks) {
|
|
10658
|
+
if (b.disposed) {
|
|
10659
|
+
meta.liveBlocks.delete(b);
|
|
10660
|
+
continue;
|
|
10661
|
+
}
|
|
10662
|
+
if (!hasResettableHmrRange(b)) return false;
|
|
10663
|
+
}
|
|
10562
10664
|
meta.fn = nextFn;
|
|
10563
10665
|
if (typeof __OCTANE_PROFILE_ENABLED__ !== "undefined" && __OCTANE_PROFILE_ENABLED__)
|
|
10564
10666
|
__profileComponentSource(wrapper, meta.fn);
|
|
@@ -10573,6 +10675,8 @@ function hmr(fn) {
|
|
|
10573
10675
|
b.body = wrapper;
|
|
10574
10676
|
if (typeof __OCTANE_PROFILE_ENABLED__ !== "undefined" && __OCTANE_PROFILE_ENABLED__)
|
|
10575
10677
|
__profileSchedule(b, "hmr");
|
|
10678
|
+
promoteHmrBlockRange(b);
|
|
10679
|
+
resetHmrBlock(b);
|
|
10576
10680
|
scheduleRender(b);
|
|
10577
10681
|
}
|
|
10578
10682
|
return true;
|
|
@@ -11684,7 +11788,7 @@ function useTransition(slot) {
|
|
|
11684
11788
|
}
|
|
11685
11789
|
};
|
|
11686
11790
|
TRANSITION_LISTENERS.add(listener);
|
|
11687
|
-
(scope
|
|
11791
|
+
registerHookCleanup(scope, () => TRANSITION_LISTENERS.delete(listener));
|
|
11688
11792
|
}
|
|
11689
11793
|
return [s.isPending, s.start];
|
|
11690
11794
|
}
|
|
@@ -11786,7 +11890,7 @@ function useFormStatus(slot) {
|
|
|
11786
11890
|
s = { form: null, listener: null };
|
|
11787
11891
|
const slotRef = s;
|
|
11788
11892
|
ensureHooks(scope).set(slot, slotRef);
|
|
11789
|
-
(scope
|
|
11893
|
+
registerHookCleanup(scope, () => {
|
|
11790
11894
|
if (slotRef.form && slotRef.listener)
|
|
11791
11895
|
FORM_STATUS_LISTENERS.get(slotRef.form)?.delete(slotRef.listener);
|
|
11792
11896
|
});
|
|
@@ -11861,7 +11965,7 @@ function useOptimistic(passthrough, updateFnOrSlot, slot) {
|
|
|
11861
11965
|
if (TRANSITION_PENDING_COUNT === 0 && slotRef.armed) clear();
|
|
11862
11966
|
};
|
|
11863
11967
|
TRANSITION_LISTENERS.add(listener);
|
|
11864
|
-
(scope
|
|
11968
|
+
registerHookCleanup(scope, () => TRANSITION_LISTENERS.delete(listener));
|
|
11865
11969
|
}
|
|
11866
11970
|
s.updateFn = updateFn;
|
|
11867
11971
|
let optimistic = passthrough;
|
package/dist/universal-core.d.ts
CHANGED
|
@@ -611,6 +611,7 @@ export interface ObjectHostInstance {
|
|
|
611
611
|
}
|
|
612
612
|
interface ObjectDriverState {
|
|
613
613
|
instances: Map<number, ObjectHostInstance>;
|
|
614
|
+
parents: Map<number, number | null>;
|
|
614
615
|
events: Map<number, Map<string, UniversalEventListenerDescriptor>>;
|
|
615
616
|
lifecycles: Map<number, Map<string, UniversalListenerDescriptor>>;
|
|
616
617
|
localCallbacks: Map<number, Map<string, UniversalListenerDescriptor>>;
|