octane 0.2.4 → 0.2.6
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/cjs/constants.cjs +9 -0
- package/dist/cjs/error-codes.client.generated.cjs +2 -0
- package/dist/cjs/host-property-diagnostics.cjs +2 -0
- package/dist/cjs/index.cjs +2 -0
- package/dist/cjs/runtime.cjs +482 -51
- package/dist/cjs/runtime.server.cjs +23 -6
- package/dist/cjs/version.cjs +1 -1
- package/dist/compiler/bundler.js +7 -1
- package/dist/compiler/compile.js +12 -0
- package/dist/compiler/hook-methods.js +164 -0
- package/dist/compiler/parser-output.js +37 -0
- package/dist/compiler/parser.browser.js +145 -1
- package/dist/compiler/parser.node.js +1 -35
- package/dist/compiler/plain-hook-memo.js +37 -0
- package/dist/compiler/slot-hooks.js +129 -5
- package/dist/compiler/volar.LICENSES.txt +1 -1
- package/dist/compiler/volar.js +186 -43
- package/dist/constants.d.ts +3 -0
- package/dist/constants.js +6 -0
- package/dist/error-codes.client.generated.d.ts +1 -0
- package/dist/error-codes.client.generated.js +2 -0
- package/dist/host-property-diagnostics.js +2 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +2 -0
- package/dist/runtime.d.ts +16 -12
- package/dist/runtime.js +483 -51
- package/dist/runtime.server.d.ts +2 -0
- package/dist/runtime.server.js +25 -6
- package/dist/version.js +1 -1
- package/package.json +5 -5
|
@@ -33,6 +33,7 @@ __export(runtime_server_exports, {
|
|
|
33
33
|
addTransitionType: () => addTransitionType,
|
|
34
34
|
beginNativeReadScope: () => beginNativeReadScope,
|
|
35
35
|
beginNativeReadWitness: () => beginNativeReadWitness,
|
|
36
|
+
callWithReceiver: () => callWithReceiver,
|
|
36
37
|
cloneElement: () => cloneElement,
|
|
37
38
|
createContext: () => createContext,
|
|
38
39
|
createElement: () => createElement,
|
|
@@ -967,9 +968,13 @@ function ssrComponentDescriptor(d, scope) {
|
|
|
967
968
|
if (SCOPED_ELEMENT_PROPS.has(d.props)) {
|
|
968
969
|
return ssrComponent(scope, d.type, d.props);
|
|
969
970
|
}
|
|
971
|
+
const children = d.children;
|
|
972
|
+
if (children == null || children === d.props?.children) {
|
|
973
|
+
return ssrComponent(scope, d.type, d.props);
|
|
974
|
+
}
|
|
970
975
|
return ssrComponent(scope, d.type, {
|
|
971
976
|
...d.props,
|
|
972
|
-
children
|
|
977
|
+
children
|
|
973
978
|
});
|
|
974
979
|
}
|
|
975
980
|
function ssrChild(v, scope) {
|
|
@@ -1601,7 +1606,7 @@ function ssrAttrs(sources, tag, namespace = "html", skipFormControls = false) {
|
|
|
1601
1606
|
record(name, source[name]);
|
|
1602
1607
|
}
|
|
1603
1608
|
}
|
|
1604
|
-
if (process.env.NODE_ENV === "production") {
|
|
1609
|
+
if (process.env.NODE_ENV === "production" && classMerges === null) {
|
|
1605
1610
|
let canonical = true;
|
|
1606
1611
|
for (const name of props.keys()) {
|
|
1607
1612
|
if (normalizeSsrAttributeName(name, tag, namespace) !== name || namespace === "html" && name.toLowerCase() !== name) {
|
|
@@ -3660,6 +3665,9 @@ function manualHook(fn, name) {
|
|
|
3660
3665
|
Object.defineProperty(provider, "length", { value: fn.length, configurable: true });
|
|
3661
3666
|
return provider;
|
|
3662
3667
|
}
|
|
3668
|
+
function callWithReceiver(fn, receiver, ...args) {
|
|
3669
|
+
return NATIVE_REFLECT_APPLY(fn, receiver, args);
|
|
3670
|
+
}
|
|
3663
3671
|
function withSlot(sym, fn, ...args) {
|
|
3664
3672
|
const driver = MANUAL_HOOK_DRIVER;
|
|
3665
3673
|
const pending = driver?.pending;
|
|
@@ -3844,14 +3852,14 @@ function serializeSuspenseSeedJson(values) {
|
|
|
3844
3852
|
return value;
|
|
3845
3853
|
}).replace(/</g, "\\u003c");
|
|
3846
3854
|
}
|
|
3847
|
-
function serializeSuspenseSeeds(values, nonceAttr) {
|
|
3855
|
+
function serializeSuspenseSeeds(values, nonceAttr, attr = import_constants.SUSPENSE_SCRIPT_ATTR) {
|
|
3848
3856
|
const json = serializeSuspenseSeedJson(values);
|
|
3849
3857
|
if (json === "[]") return "";
|
|
3850
|
-
return '<script type="application/json" ' +
|
|
3858
|
+
return '<script type="application/json" ' + attr + nonceAttr + ">" + json + "</script>";
|
|
3851
3859
|
}
|
|
3852
|
-
function serializeNativeSignalSeeds(signals, nonceAttr) {
|
|
3860
|
+
function serializeNativeSignalSeeds(signals, nonceAttr, attr = import_native_read_seeds.NATIVE_SIGNAL_SEED_ATTR) {
|
|
3853
3861
|
const json = JSON.stringify(signals).replace(/</g, "\\u003c");
|
|
3854
|
-
return '<script type="application/json" ' +
|
|
3862
|
+
return '<script type="application/json" ' + attr + nonceAttr + ">" + json + "</script>";
|
|
3855
3863
|
}
|
|
3856
3864
|
function newResolvedMap(resourceOptions) {
|
|
3857
3865
|
const m = /* @__PURE__ */ new Map();
|
|
@@ -4846,6 +4854,14 @@ function ssrTry(scope, siteKey, tryFn, pendFn, catchFn, namespace = FRAME?.names
|
|
|
4846
4854
|
return pendingForm();
|
|
4847
4855
|
}
|
|
4848
4856
|
appendNativeSeedReads(nativeReads);
|
|
4857
|
+
if (MARKERS && pendFn !== null) {
|
|
4858
|
+
const idCount = ID_COUNTER - outerIdCounter;
|
|
4859
|
+
const seeds = SERIAL === null ? [] : SERIAL.splice(serialStart);
|
|
4860
|
+
const native = NATIVE_READ_COLLECTOR?.serialize(nativeReads);
|
|
4861
|
+
return ssrBlock(
|
|
4862
|
+
`<!--${import_constants.SUSPENSE_RESOLVED_COMMENT}${idCount}-->` + (seeds.length === 0 ? "" : serializeSuspenseSeeds(seeds, NONCE_ATTR, import_constants.SUSPENSE_RESOLVED_SEED_ATTR)) + (native === void 0 ? "" : serializeNativeSignalSeeds(native, NONCE_ATTR, import_constants.SUSPENSE_RESOLVED_NATIVE_ATTR)) + inner
|
|
4863
|
+
);
|
|
4864
|
+
}
|
|
4849
4865
|
return ssrBlock(inner);
|
|
4850
4866
|
} catch (e) {
|
|
4851
4867
|
nativeFresh = NATIVE_SERVER_FAILURES !== nativeFailureStart;
|
|
@@ -6178,6 +6194,7 @@ function preinitModule(href, options) {
|
|
|
6178
6194
|
addTransitionType,
|
|
6179
6195
|
beginNativeReadScope,
|
|
6180
6196
|
beginNativeReadWitness,
|
|
6197
|
+
callWithReceiver,
|
|
6181
6198
|
cloneElement,
|
|
6182
6199
|
createContext,
|
|
6183
6200
|
createElement,
|
package/dist/cjs/version.cjs
CHANGED
|
@@ -21,7 +21,7 @@ __export(version_exports, {
|
|
|
21
21
|
version: () => version
|
|
22
22
|
});
|
|
23
23
|
module.exports = __toCommonJS(version_exports);
|
|
24
|
-
const version = "0.2.
|
|
24
|
+
const version = "0.2.6";
|
|
25
25
|
// Annotate the CommonJS export names for ESM import in node:
|
|
26
26
|
0 && (module.exports = {
|
|
27
27
|
version
|
package/dist/compiler/bundler.js
CHANGED
|
@@ -1299,7 +1299,13 @@ class OctaneBundlerCompiler {
|
|
|
1299
1299
|
return passThrough();
|
|
1300
1300
|
}
|
|
1301
1301
|
const manualSlots = this._hasManualHookSlots(file, collected);
|
|
1302
|
-
if (
|
|
1302
|
+
if (
|
|
1303
|
+
!hasHookRuntimeImport &&
|
|
1304
|
+
!manualSlots &&
|
|
1305
|
+
!pragmaOwned &&
|
|
1306
|
+
!this._pragmaClaimsOwnership(code)
|
|
1307
|
+
)
|
|
1308
|
+
return passThrough();
|
|
1303
1309
|
const inlinePlainMemo =
|
|
1304
1310
|
inlineHookMemo &&
|
|
1305
1311
|
environment === 'client' &&
|
package/dist/compiler/compile.js
CHANGED
|
@@ -7583,6 +7583,18 @@ function isTypeOnlyStatement(node) {
|
|
|
7583
7583
|
}
|
|
7584
7584
|
// `declare const/let/var/function/class/module/namespace …` — ambient, no emit.
|
|
7585
7585
|
if (node.declare === true) return true;
|
|
7586
|
+
// Component/type declaration merging often uses a namespace containing only
|
|
7587
|
+
// types. Like an interface, it must disappear from the emitted JavaScript.
|
|
7588
|
+
// Keep value namespaces and Octane's `module server` dialect on their own paths.
|
|
7589
|
+
if (node.type === 'TSModuleDeclaration' && node.kind === 'namespace') {
|
|
7590
|
+
if (node.body?.type === 'TSModuleDeclaration') return isTypeOnlyStatement(node.body);
|
|
7591
|
+
if (node.body?.type === 'TSModuleBlock') {
|
|
7592
|
+
return node.body.body.every(
|
|
7593
|
+
(statement) =>
|
|
7594
|
+
isTypeOnlyStatement(statement) || stripTypeOnlySpecifiers(statement) === null,
|
|
7595
|
+
);
|
|
7596
|
+
}
|
|
7597
|
+
}
|
|
7586
7598
|
// `import type { … } from …`
|
|
7587
7599
|
if (node.type === 'ImportDeclaration' && node.importKind === 'type') return true;
|
|
7588
7600
|
// `export type { … }`, `export type X = …`, `export interface I {}`
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import { builders as b } from '@tsrx/core';
|
|
2
|
+
|
|
3
|
+
function unwrapNonNullExpression(node) {
|
|
4
|
+
while (node?.type === 'TSNonNullExpression') node = node.expression;
|
|
5
|
+
return node;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
/** Static hook methods need a call boundary, never an extra positional argument. */
|
|
9
|
+
export function hookMethodName(node, hookLocals) {
|
|
10
|
+
if (
|
|
11
|
+
node?.type !== 'CallExpression' ||
|
|
12
|
+
node._octaneImportedHook ||
|
|
13
|
+
node._octaneHookRuntimeImportedHook
|
|
14
|
+
)
|
|
15
|
+
return null;
|
|
16
|
+
const callee = unwrapNonNullExpression(node.callee);
|
|
17
|
+
// Namespace calls are owned by the lexical import analysis. If it did not
|
|
18
|
+
// mark this call, the namespace is shadowed and must retain ordinary JS behavior.
|
|
19
|
+
if (callee?.object?.type === 'Identifier' && hookLocals?.get(callee.object.name) === '*')
|
|
20
|
+
return null;
|
|
21
|
+
return callee?.type === 'MemberExpression' &&
|
|
22
|
+
!callee.computed &&
|
|
23
|
+
callee.property?.type === 'Identifier' &&
|
|
24
|
+
/^use[A-Z]/.test(callee.property.name)
|
|
25
|
+
? callee.property.name
|
|
26
|
+
: null;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function hasHookMethods(node) {
|
|
30
|
+
if (!node || typeof node !== 'object') return false;
|
|
31
|
+
if (Array.isArray(node)) return node.some(hasHookMethods);
|
|
32
|
+
if (hookMethodName(node) !== null) return true;
|
|
33
|
+
return Object.keys(node).some(
|
|
34
|
+
(key) => key !== 'loc' && key !== 'metadata' && hasHookMethods(node[key]),
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// The call boundary is synchronous. Moving a suspension expression into its
|
|
39
|
+
// callback would produce invalid JavaScript; nested callbacks keep their own
|
|
40
|
+
// lexical function scope and are safe to pass as ordinary arguments.
|
|
41
|
+
export function assertSynchronousHookMethod(node) {
|
|
42
|
+
function suspends(value) {
|
|
43
|
+
if (!value || typeof value !== 'object') return false;
|
|
44
|
+
if (Array.isArray(value)) return value.some(suspends);
|
|
45
|
+
if (value.type === 'AwaitExpression' || value.type === 'YieldExpression') return true;
|
|
46
|
+
if (
|
|
47
|
+
value.type === 'ArrowFunctionExpression' ||
|
|
48
|
+
value.type === 'FunctionExpression' ||
|
|
49
|
+
value.type === 'FunctionDeclaration'
|
|
50
|
+
)
|
|
51
|
+
return false;
|
|
52
|
+
return Object.keys(value).some(
|
|
53
|
+
(key) => key !== 'loc' && key !== 'metadata' && suspends(value[key]),
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
if (suspends(node)) {
|
|
57
|
+
throw new SyntaxError(
|
|
58
|
+
'An Octane hook method cannot contain await or yield in its receiver or arguments. Evaluate the suspended expression before calling the hook method.',
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// A call boundary inside a ChainExpression would end its short-circuit region.
|
|
64
|
+
// Lower only affected chains, sharing the guard/receiver rules between emitters.
|
|
65
|
+
export function lowerHookMethodChain(chain, options, deleting = false) {
|
|
66
|
+
if (chain?.type !== 'ChainExpression') return null;
|
|
67
|
+
const links = [];
|
|
68
|
+
let base = chain.expression;
|
|
69
|
+
while (
|
|
70
|
+
base.type === 'MemberExpression' ||
|
|
71
|
+
base.type === 'CallExpression' ||
|
|
72
|
+
base.type === 'TSNonNullExpression'
|
|
73
|
+
) {
|
|
74
|
+
links.unshift(base);
|
|
75
|
+
base =
|
|
76
|
+
base.type === 'MemberExpression'
|
|
77
|
+
? base.object
|
|
78
|
+
: base.type === 'CallExpression'
|
|
79
|
+
? base.callee
|
|
80
|
+
: base.expression;
|
|
81
|
+
}
|
|
82
|
+
const firstOptional = links.findIndex((link) => link.optional);
|
|
83
|
+
if (
|
|
84
|
+
firstOptional === -1 ||
|
|
85
|
+
!links.slice(firstOptional).some((link) => hookMethodName(link, options.locals) !== null)
|
|
86
|
+
)
|
|
87
|
+
return null;
|
|
88
|
+
assertSynchronousHookMethod(chain);
|
|
89
|
+
base =
|
|
90
|
+
links[firstOptional].type === 'MemberExpression'
|
|
91
|
+
? links[firstOptional].object
|
|
92
|
+
: links[firstOptional].callee;
|
|
93
|
+
const steps = links.slice(firstOptional);
|
|
94
|
+
const skipped = deleting ? b.literal(true) : b.void0;
|
|
95
|
+
function scope(value, next) {
|
|
96
|
+
const local = b.id(options.allocateName('_oc$'));
|
|
97
|
+
return b.call(b.arrow([local], next(local)), value);
|
|
98
|
+
}
|
|
99
|
+
function guard(value, next) {
|
|
100
|
+
return b.conditional(
|
|
101
|
+
b.logical('||', b.binary('===', value, b.literal(null)), b.binary('===', value, b.void0)),
|
|
102
|
+
skipped,
|
|
103
|
+
next(value),
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
const properties = new Map();
|
|
107
|
+
function member(object, link) {
|
|
108
|
+
if (link.computed && !properties.has(link)) properties.set(link, options.visit(link.property));
|
|
109
|
+
return {
|
|
110
|
+
...link,
|
|
111
|
+
object,
|
|
112
|
+
property: link.computed ? properties.get(link) : link.property,
|
|
113
|
+
optional: false,
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
function proceed(value, index, reference = null) {
|
|
117
|
+
if (index === steps.length) return deleting ? b.unary('delete', value) : value;
|
|
118
|
+
const link = steps[index];
|
|
119
|
+
if (link.type === 'TSNonNullExpression')
|
|
120
|
+
return proceed({ ...link, expression: value }, index + 1, reference);
|
|
121
|
+
if (link.type === 'MemberExpression') {
|
|
122
|
+
const next = (object) => proceed(member(object, link), index + 1, { object, link });
|
|
123
|
+
return link.optional ? scope(value, (local) => guard(local, next)) : next(value);
|
|
124
|
+
}
|
|
125
|
+
const args = () => link.arguments.map(options.visit);
|
|
126
|
+
const method = hookMethodName(link, options.locals);
|
|
127
|
+
const invoke = (call) => (method === null ? call : options.wrap(call, link, method));
|
|
128
|
+
if (!link.optional) {
|
|
129
|
+
return proceed(
|
|
130
|
+
invoke({ ...link, callee: value, arguments: args(), optional: false }),
|
|
131
|
+
index + 1,
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
const call = (fn, receiver) =>
|
|
135
|
+
guard(fn, () =>
|
|
136
|
+
proceed(invoke(b.call(options.requireReceiver(), fn, receiver, ...args())), index + 1),
|
|
137
|
+
);
|
|
138
|
+
if (reference !== null) {
|
|
139
|
+
if (reference.object.type === 'Super') {
|
|
140
|
+
return scope(value, (fn) => call(fn, b.this));
|
|
141
|
+
}
|
|
142
|
+
return scope(reference.object, (receiver) =>
|
|
143
|
+
scope(member(receiver, reference.link), (fn) => call(fn, receiver)),
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
return scope(value, (fn) => call(fn, b.void0));
|
|
147
|
+
}
|
|
148
|
+
const referenceBase = unwrapNonNullExpression(base);
|
|
149
|
+
const reference =
|
|
150
|
+
referenceBase.type === 'MemberExpression'
|
|
151
|
+
? {
|
|
152
|
+
object:
|
|
153
|
+
referenceBase.object.type === 'Super'
|
|
154
|
+
? referenceBase.object
|
|
155
|
+
: options.visit(referenceBase.object),
|
|
156
|
+
link: referenceBase,
|
|
157
|
+
}
|
|
158
|
+
: null;
|
|
159
|
+
return proceed(
|
|
160
|
+
reference === null ? options.visit(base) : member(reference.object, referenceBase),
|
|
161
|
+
0,
|
|
162
|
+
reference,
|
|
163
|
+
);
|
|
164
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/** @import { ParseOptions } from '@tsrx/core/types' */
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Isolate caller-owned output buffers without changing parser options. An
|
|
5
|
+
* unsolicited comments array would disable native eager parsing.
|
|
6
|
+
* @param {ParseOptions} [options]
|
|
7
|
+
* @param {boolean} [captureErrors]
|
|
8
|
+
* @returns {ParseOptions | undefined}
|
|
9
|
+
*/
|
|
10
|
+
export function isolateOutputOptions(options, captureErrors = false) {
|
|
11
|
+
if (!captureErrors && !Array.isArray(options?.errors) && !Array.isArray(options?.comments)) {
|
|
12
|
+
return options;
|
|
13
|
+
}
|
|
14
|
+
const original = options ?? {};
|
|
15
|
+
const descriptors = Object.getOwnPropertyDescriptors(original);
|
|
16
|
+
if (captureErrors || Array.isArray(options?.errors)) {
|
|
17
|
+
descriptors.errors = { value: [], enumerable: true, writable: true, configurable: true };
|
|
18
|
+
}
|
|
19
|
+
if (Array.isArray(options?.comments)) {
|
|
20
|
+
descriptors.comments = { value: [], enumerable: true, writable: true, configurable: true };
|
|
21
|
+
}
|
|
22
|
+
return Object.create(Object.getPrototypeOf(original), descriptors);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* @param {ParseOptions | undefined} options
|
|
27
|
+
* @param {ParseOptions | undefined} selected
|
|
28
|
+
*/
|
|
29
|
+
export function publishOutput(options, selected) {
|
|
30
|
+
if (selected === options) return;
|
|
31
|
+
if (Array.isArray(options?.errors)) {
|
|
32
|
+
for (const error of selected?.errors ?? []) options.errors.push(error);
|
|
33
|
+
}
|
|
34
|
+
if (Array.isArray(options?.comments)) {
|
|
35
|
+
for (const comment of selected?.comments ?? []) options.comments.push(comment);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -1 +1,145 @@
|
|
|
1
|
-
|
|
1
|
+
import { acorn, tsPlugin, getCommentHandlers, parseModule as parseTsrx } from '@tsrx/core';
|
|
2
|
+
import { isolateOutputOptions, publishOutput } from './parser-output.js';
|
|
3
|
+
|
|
4
|
+
// The TypeScript parser currently enters type context after consuming `{`, so a
|
|
5
|
+
// first generic call signature is tokenized as JSX. Enter before its first token;
|
|
6
|
+
// leave before consuming `}` so the following ordinary JavaScript stays intact.
|
|
7
|
+
const TypeScriptParser = acorn.Parser.extend(
|
|
8
|
+
tsPlugin({ jsx: true }),
|
|
9
|
+
(Base) =>
|
|
10
|
+
class extends Base {
|
|
11
|
+
tsParseDeclaration(node, value, next) {
|
|
12
|
+
// `module` has additional TSRX semantics. The compatibility parser must
|
|
13
|
+
// never reinterpret a server block as an ordinary TypeScript namespace.
|
|
14
|
+
if (value === 'module') throw new SyntaxError('TSRX module requires the TSRX parser');
|
|
15
|
+
const declaration = super.tsParseDeclaration(node, value, next);
|
|
16
|
+
if (value === 'namespace') {
|
|
17
|
+
for (
|
|
18
|
+
let current = declaration;
|
|
19
|
+
current?.type === 'TSModuleDeclaration';
|
|
20
|
+
current = current.body
|
|
21
|
+
) {
|
|
22
|
+
current.kind = 'namespace';
|
|
23
|
+
current.metadata = { path: [], module_keyword: 'namespace' };
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return declaration;
|
|
27
|
+
}
|
|
28
|
+
finishNode(node, type) {
|
|
29
|
+
const finished = super.finishNode(node, type);
|
|
30
|
+
if (type === 'TSModuleDeclaration' && finished.global) {
|
|
31
|
+
finished.kind = 'global';
|
|
32
|
+
delete finished.global;
|
|
33
|
+
}
|
|
34
|
+
return finished;
|
|
35
|
+
}
|
|
36
|
+
tsParseInterfaceBody() {
|
|
37
|
+
const previous = this.inType;
|
|
38
|
+
let members;
|
|
39
|
+
this.inType = true;
|
|
40
|
+
try {
|
|
41
|
+
this.expect(acorn.tokTypes.braceL);
|
|
42
|
+
members = this.tsParseList('TypeMembers', this.tsParseTypeMember.bind(this));
|
|
43
|
+
} finally {
|
|
44
|
+
this.inType = previous;
|
|
45
|
+
}
|
|
46
|
+
this.expect(acorn.tokTypes.braceR);
|
|
47
|
+
return members;
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
function supportsTypeScriptRetry(program) {
|
|
53
|
+
let genericSyntax = false;
|
|
54
|
+
let tsrxSyntax = false;
|
|
55
|
+
function visit(node, jsxType = false) {
|
|
56
|
+
if (!node || typeof node !== 'object') return;
|
|
57
|
+
if (Array.isArray(node)) {
|
|
58
|
+
for (const child of node) visit(child, jsxType);
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
if (node.type === 'TSCallSignatureDeclaration' && node.typeParameters) genericSyntax = true;
|
|
62
|
+
if (jsxType && node.type === 'TSQualifiedName') genericSyntax = true;
|
|
63
|
+
// Raw-text elements and template directives require the TSRX lexer.
|
|
64
|
+
if (
|
|
65
|
+
node.type === 'JSXElement' &&
|
|
66
|
+
!node.openingElement.selfClosing &&
|
|
67
|
+
['style', 'script'].includes(node.openingElement.name.name)
|
|
68
|
+
)
|
|
69
|
+
tsrxSyntax = true;
|
|
70
|
+
if (node.type === 'JSXText' && node.value.includes('@')) tsrxSyntax = true;
|
|
71
|
+
for (const [key, child] of Object.entries(node)) {
|
|
72
|
+
if (key === 'loc' || key === 'metadata' || key.endsWith('Comments')) continue;
|
|
73
|
+
visit(
|
|
74
|
+
child,
|
|
75
|
+
jsxType ||
|
|
76
|
+
(node.type === 'JSXOpeningElement' &&
|
|
77
|
+
(key === 'typeParameters' || key === 'typeArguments')),
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
visit(program);
|
|
82
|
+
return genericSyntax && !tsrxSyntax;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function parseTypeScript(source, options) {
|
|
86
|
+
const comments = [];
|
|
87
|
+
const { onComment, add_comments } = getCommentHandlers(source, comments);
|
|
88
|
+
const keywordTokens = options?.keywordTokens ? [] : null;
|
|
89
|
+
const program = TypeScriptParser.parse(source, {
|
|
90
|
+
sourceType: 'module',
|
|
91
|
+
ecmaVersion: 'latest',
|
|
92
|
+
locations: true,
|
|
93
|
+
preserveParens: !!options?.preserveParens,
|
|
94
|
+
onComment,
|
|
95
|
+
onToken:
|
|
96
|
+
keywordTokens === null
|
|
97
|
+
? undefined
|
|
98
|
+
: (token) => {
|
|
99
|
+
const keyword =
|
|
100
|
+
token.type.keyword === 'function'
|
|
101
|
+
? 'function'
|
|
102
|
+
: token.type.label === 'name' && token.value === 'async'
|
|
103
|
+
? 'async'
|
|
104
|
+
: null;
|
|
105
|
+
if (keyword !== null)
|
|
106
|
+
keywordTokens.push({
|
|
107
|
+
value: keyword,
|
|
108
|
+
start: token.start,
|
|
109
|
+
end: token.end,
|
|
110
|
+
loc: token.loc,
|
|
111
|
+
});
|
|
112
|
+
},
|
|
113
|
+
});
|
|
114
|
+
if (!supportsTypeScriptRetry(program))
|
|
115
|
+
throw new SyntaxError('Unsupported TSRX compatibility retry');
|
|
116
|
+
add_comments(program);
|
|
117
|
+
if (keywordTokens !== null) program.tsrx_keyword_tokens = keywordTokens;
|
|
118
|
+
if (options?.comments) options.comments.push(...comments);
|
|
119
|
+
return program;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/** Preserve TSRX parsing and recovery; retry rejected ordinary TS/JSX modules. */
|
|
123
|
+
export function parseModule(source, filename, options) {
|
|
124
|
+
const primary = isolateOutputOptions(options);
|
|
125
|
+
let program;
|
|
126
|
+
try {
|
|
127
|
+
program = parseTsrx(source, filename, primary);
|
|
128
|
+
} catch (error) {
|
|
129
|
+
if (!(error instanceof SyntaxError)) {
|
|
130
|
+
publishOutput(options, primary);
|
|
131
|
+
throw error;
|
|
132
|
+
}
|
|
133
|
+
const alternative = isolateOutputOptions(options, true);
|
|
134
|
+
try {
|
|
135
|
+
program = parseTypeScript(source, alternative);
|
|
136
|
+
} catch (alternativeError) {
|
|
137
|
+
publishOutput(options, primary);
|
|
138
|
+
throw alternativeError instanceof SyntaxError ? error : alternativeError;
|
|
139
|
+
}
|
|
140
|
+
publishOutput(options, alternative);
|
|
141
|
+
return program;
|
|
142
|
+
}
|
|
143
|
+
publishOutput(options, primary);
|
|
144
|
+
return program;
|
|
145
|
+
}
|
|
@@ -4,41 +4,7 @@
|
|
|
4
4
|
import { parseModule as parseNativeModule } from 'oxc-tsrx/tsrx-core-compat';
|
|
5
5
|
import { parseModule as parseJavaScriptModule } from './parser.browser.js';
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
* Isolate caller-owned output buffers without changing parser options. An
|
|
9
|
-
* unsolicited comments array would disable native eager parsing.
|
|
10
|
-
* @param {ParseOptions} [options]
|
|
11
|
-
* @param {boolean} [captureErrors]
|
|
12
|
-
* @returns {ParseOptions | undefined}
|
|
13
|
-
*/
|
|
14
|
-
function isolateOutputOptions(options, captureErrors = false) {
|
|
15
|
-
if (!captureErrors && !Array.isArray(options?.errors) && !Array.isArray(options?.comments)) {
|
|
16
|
-
return options;
|
|
17
|
-
}
|
|
18
|
-
const original = options ?? {};
|
|
19
|
-
const descriptors = Object.getOwnPropertyDescriptors(original);
|
|
20
|
-
if (captureErrors || Array.isArray(options?.errors)) {
|
|
21
|
-
descriptors.errors = { value: [], enumerable: true, writable: true, configurable: true };
|
|
22
|
-
}
|
|
23
|
-
if (Array.isArray(options?.comments)) {
|
|
24
|
-
descriptors.comments = { value: [], enumerable: true, writable: true, configurable: true };
|
|
25
|
-
}
|
|
26
|
-
return Object.create(Object.getPrototypeOf(original), descriptors);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* @param {ParseOptions | undefined} options
|
|
31
|
-
* @param {ParseOptions | undefined} selected
|
|
32
|
-
*/
|
|
33
|
-
function publishOutput(options, selected) {
|
|
34
|
-
if (selected === options) return;
|
|
35
|
-
if (Array.isArray(options?.errors)) {
|
|
36
|
-
for (const error of selected?.errors ?? []) options.errors.push(error);
|
|
37
|
-
}
|
|
38
|
-
if (Array.isArray(options?.comments)) {
|
|
39
|
-
for (const comment of selected?.comments ?? []) options.comments.push(comment);
|
|
40
|
-
}
|
|
41
|
-
}
|
|
7
|
+
import { isolateOutputOptions, publishOutput } from './parser-output.js';
|
|
42
8
|
|
|
43
9
|
/**
|
|
44
10
|
* `<style>` with an expression child — `<style>{css}</style>`, an ordinary
|
|
@@ -134,6 +134,38 @@ function slotBaseHooks(ast, state, options) {
|
|
|
134
134
|
function visit(node) {
|
|
135
135
|
if (node === null || typeof node !== 'object') return node;
|
|
136
136
|
if (Array.isArray(node)) return mapChildren(node, visit);
|
|
137
|
+
if (!options.manualSlots) {
|
|
138
|
+
const deleting = node.type === 'UnaryExpression' && node.operator === 'delete';
|
|
139
|
+
const lowered = lowerHookMethodChain(
|
|
140
|
+
deleting ? node.argument : node,
|
|
141
|
+
{
|
|
142
|
+
locals: options.hookLocals,
|
|
143
|
+
allocateName: (name) => allocName(state, name),
|
|
144
|
+
visit,
|
|
145
|
+
requireReceiver: () => requireHelper(state, 'callWithReceiver'),
|
|
146
|
+
wrap: (call, origin) =>
|
|
147
|
+
inheritHookMemoOrigin(
|
|
148
|
+
b.call(
|
|
149
|
+
requireHelper(state, 'withSlot', 'octane'),
|
|
150
|
+
allocateHookSlot(state, origin),
|
|
151
|
+
b.arrow([], call),
|
|
152
|
+
),
|
|
153
|
+
origin,
|
|
154
|
+
),
|
|
155
|
+
},
|
|
156
|
+
deleting,
|
|
157
|
+
);
|
|
158
|
+
if (lowered !== null) return inheritHookMemoOrigin(lowered, node);
|
|
159
|
+
}
|
|
160
|
+
if (!options.manualSlots && hookMethodName(node, options.hookLocals) !== null) {
|
|
161
|
+
assertSynchronousHookMethod(node);
|
|
162
|
+
const slot = allocateHookSlot(state, node);
|
|
163
|
+
const mapped = mapChildren(node, visit);
|
|
164
|
+
return inheritHookMemoOrigin(
|
|
165
|
+
b.call(requireHelper(state, 'withSlot', 'octane'), slot, b.arrow([], mapped)),
|
|
166
|
+
node,
|
|
167
|
+
);
|
|
168
|
+
}
|
|
137
169
|
if (!options.manualSlots && node.type === 'CallExpression' && node._octaneCustomHookCall) {
|
|
138
170
|
const slot = allocateHookSlot(state, node);
|
|
139
171
|
const mapped = mapChildren(node, visit);
|
|
@@ -348,3 +380,8 @@ export function inlinePlainHookMemos(ast, source, id, options) {
|
|
|
348
380
|
return null;
|
|
349
381
|
}
|
|
350
382
|
}
|
|
383
|
+
import {
|
|
384
|
+
hookMethodName,
|
|
385
|
+
assertSynchronousHookMethod,
|
|
386
|
+
lowerHookMethodChain,
|
|
387
|
+
} from './hook-methods.js';
|