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.
@@ -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: d.children ?? d.props?.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" ' + import_constants.SUSPENSE_SCRIPT_ATTR + nonceAttr + ">" + json + "</script>";
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" ' + import_native_read_seeds.NATIVE_SIGNAL_SEED_ATTR + nonceAttr + ">" + json + "</script>";
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,
@@ -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.4";
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
@@ -1299,7 +1299,13 @@ class OctaneBundlerCompiler {
1299
1299
  return passThrough();
1300
1300
  }
1301
1301
  const manualSlots = this._hasManualHookSlots(file, collected);
1302
- if (!hasHookRuntimeImport && !manualSlots) return passThrough();
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' &&
@@ -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
- export { parseModule } from '@tsrx/core';
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';