octane 0.2.2 → 0.2.3

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.
Files changed (50) hide show
  1. package/dist/cjs/compatibility.cjs +35 -0
  2. package/dist/cjs/component-flags.cjs +8 -1
  3. package/dist/cjs/dom-tables.cjs +2 -0
  4. package/dist/cjs/error-codes.client.generated.cjs +15 -2
  5. package/dist/cjs/error-codes.server.generated.cjs +5 -0
  6. package/dist/cjs/event-names.cjs +39 -0
  7. package/dist/cjs/index.cjs +13 -0
  8. package/dist/cjs/runtime.cjs +1223 -332
  9. package/dist/cjs/runtime.server.cjs +362 -120
  10. package/dist/cjs/server/index.cjs +22 -0
  11. package/dist/cjs/version.cjs +1 -1
  12. package/dist/compatibility.d.ts +7 -0
  13. package/dist/compatibility.js +10 -0
  14. package/dist/compiler/bundler.js +13 -30
  15. package/dist/compiler/compile-renderer-boundaries.js +1 -0
  16. package/dist/compiler/compile-universal.js +3 -0
  17. package/dist/compiler/compile.js +619 -99
  18. package/dist/compiler/hook-deps.js +100 -3
  19. package/dist/compiler/hook-names.js +1 -0
  20. package/dist/compiler/manual-hooks.js +171 -0
  21. package/dist/compiler/plain-hook-memo.js +54 -5
  22. package/dist/compiler/slot-hooks.d.ts +43 -0
  23. package/dist/compiler/slot-hooks.js +154 -35
  24. package/dist/component-flags.d.ts +1 -1
  25. package/dist/component-flags.js +8 -1
  26. package/dist/dom-tables.js +2 -0
  27. package/dist/error-codes.client.generated.d.ts +3 -1
  28. package/dist/error-codes.client.generated.js +15 -2
  29. package/dist/error-codes.server.generated.d.ts +1 -0
  30. package/dist/error-codes.server.generated.js +5 -0
  31. package/dist/event-names.d.ts +1 -0
  32. package/dist/event-names.js +15 -0
  33. package/dist/html-tree-validation.d.ts +17 -34
  34. package/dist/index.d.ts +3 -1
  35. package/dist/index.js +12 -1
  36. package/dist/jsx-runtime.d.ts +6 -1
  37. package/dist/public-types.d.ts +188 -0
  38. package/dist/public-types.js +0 -0
  39. package/dist/runtime.d.ts +109 -66
  40. package/dist/runtime.js +1220 -332
  41. package/dist/runtime.server.d.ts +39 -14
  42. package/dist/runtime.server.js +356 -120
  43. package/dist/server/index.d.ts +4 -2
  44. package/dist/server/index.js +19 -0
  45. package/dist/static/index.d.ts +2 -1
  46. package/dist/static/index.js +3 -1
  47. package/dist/universal-core.d.ts +8 -1
  48. package/dist/universal-core.js +95 -9
  49. package/dist/version.js +1 -1
  50. package/package.json +2 -1
@@ -24,6 +24,7 @@ const STABLE_TUPLE_RESULTS = new Map([
24
24
  ['useReducer', new Set([1, 2])],
25
25
  ['useTransition', new Set([1])],
26
26
  ['useActionState', new Set([1])],
27
+ ['useFormState', new Set([1])],
27
28
  ['useOptimistic', new Set([1])],
28
29
  ]);
29
30
 
@@ -57,6 +58,7 @@ function declareName(scope, name, details = null) {
57
58
  octaneNamespace: false,
58
59
  hookRuntimeImport: null,
59
60
  hookRuntimeNamespace: false,
61
+ customHookImport: null,
60
62
  };
61
63
  scope.bindings.set(name, binding);
62
64
  }
@@ -66,6 +68,7 @@ function declareName(scope, name, details = null) {
66
68
  if (details?.octaneNamespace) binding.octaneNamespace = true;
67
69
  if (details?.hookRuntimeImport) binding.hookRuntimeImport = details.hookRuntimeImport;
68
70
  if (details?.hookRuntimeNamespace) binding.hookRuntimeNamespace = true;
71
+ if (details?.customHookImport) binding.customHookImport = details.customHookImport;
69
72
  return binding;
70
73
  }
71
74
 
@@ -133,6 +136,13 @@ function predeclareDirect(statements, scope, hookRuntimeModules, bindingsOnly =
133
136
  octaneNamespace: isOctane && specifier.type === 'ImportNamespaceSpecifier',
134
137
  hookRuntimeImport: isHookRuntime ? imported : null,
135
138
  hookRuntimeNamespace: isHookRuntime && specifier.type === 'ImportNamespaceSpecifier',
139
+ customHookImport:
140
+ !isHookRuntime &&
141
+ original.importKind !== 'type' &&
142
+ specifier.importKind !== 'type' &&
143
+ (/^use[A-Z]/.test(imported ?? '') || /^use[A-Z]/.test(specifier.local.name))
144
+ ? (imported ?? specifier.local.name)
145
+ : null,
136
146
  });
137
147
  }
138
148
  continue;
@@ -579,10 +589,20 @@ function buildScopes(ast, onlyImported, hookRuntimeModules, bindingsOnly = false
579
589
  const name = canonicalHookName(node, scope, onlyImported);
580
590
  const config = DEPENDENCY_HOOKS.get(name);
581
591
  const hookRuntimeImportedName = canonicalHookName(node, scope, true);
582
- if (octaneImportedName !== null || unboundCallee || hookRuntimeImportedName !== null) {
592
+ const customHookImport =
593
+ node.optional !== true && callee?.type === 'Identifier'
594
+ ? resolveBinding(scope, callee.name)?.customHookImport
595
+ : null;
596
+ if (
597
+ octaneImportedName !== null ||
598
+ unboundCallee ||
599
+ hookRuntimeImportedName !== null ||
600
+ customHookImport
601
+ ) {
583
602
  const props = {};
584
603
  if (octaneImportedName !== null) props._octaneImportedHook = octaneImportedName;
585
604
  if (unboundCallee) props._octaneUnboundCallee = true;
605
+ if (customHookImport) props._octaneCustomHookCall = customHookImport;
586
606
  if (octaneImportedName === null && hookRuntimeImportedName !== null) {
587
607
  props._octaneHookRuntimeImportedHook = hookRuntimeImportedName;
588
608
  }
@@ -665,8 +685,9 @@ function buildScopes(ast, onlyImported, hookRuntimeModules, bindingsOnly = false
665
685
  declarationBindings,
666
686
  };
667
687
  if (bindingsOnly) return analysis;
668
- // The surgical plain-TS pass slots base hooks only; without a custom-hook
669
- // withSlot boundary, two local wrapper calls would share their inner slots.
688
+ if (onlyImported) annotateLocalHookAliases(analysis);
689
+ // The plain-TS pass slots base hooks and imported/aliased hook values. Local
690
+ // custom-hook functions still own their authored slot boundaries.
670
691
  // Restrict custom-call inference to the full TSRX/TSX compiler, which emits
671
692
  // that boundary for every plain-identifier custom hook call.
672
693
  const customHooks = onlyImported ? new Map() : discoverCustomDependencyHooks(analysis);
@@ -687,6 +708,82 @@ function buildScopes(ast, onlyImported, hookRuntimeModules, bindingsOnly = false
687
708
  return analysis;
688
709
  }
689
710
 
711
+ // An immutable alias may select a base hook directly, or a zero-argument local
712
+ // factory may return that selection (the usual isomorphic-effect pattern).
713
+ // Follow only value aliases, never the body of an ordinary custom hook. This
714
+ // proof needs the completed lexical graph so declaration order and shadowing
715
+ // cannot change a call's identity.
716
+ function annotateLocalHookAliases(analysis) {
717
+ // Ordinary modules with only direct imported hooks need no alias tables.
718
+ if (
719
+ !analysis.calls.some(({ call, scope }) => {
720
+ const binding = directCallBinding(call, scope);
721
+ return binding !== null && !binding.imported;
722
+ })
723
+ )
724
+ return;
725
+ const values = new Map();
726
+ const factories = new Map();
727
+ for (const { decl, bindings, kind } of analysis.declarators) {
728
+ if (kind !== 'const' || decl.id.type !== 'Identifier') continue;
729
+ const binding = bindings[0]?.binding;
730
+ if (binding && !binding.reassigned) values.set(binding, unwrapValue(decl.init));
731
+ }
732
+ if (values.size === 0) return;
733
+ for (const record of analysis.functions) {
734
+ if (record.binding && record.stableDefinition && !record.binding.reassigned) {
735
+ factories.set(record.binding, record.node);
736
+ }
737
+ }
738
+ function referencesHook(node, seen) {
739
+ const value = unwrapValue(node);
740
+ if (!value) return false;
741
+ if (value.type === 'Identifier') {
742
+ const binding = resolveBinding(analysis.nodeScopes.get(value), value.name);
743
+ if (!binding || binding.reassigned || seen.has(binding)) return false;
744
+ if (/^use[A-Z]/.test(binding.hookRuntimeImport ?? '') || binding.customHookImport)
745
+ return true;
746
+ seen.add(binding);
747
+ return referencesHook(values.get(binding), seen);
748
+ }
749
+ if (value.type === 'ConditionalExpression') {
750
+ return referencesHook(value.consequent, seen) || referencesHook(value.alternate, seen);
751
+ }
752
+ if (value.type === 'LogicalExpression') {
753
+ return referencesHook(value.left, seen) || referencesHook(value.right, seen);
754
+ }
755
+ if (
756
+ value.type === 'CallExpression' &&
757
+ value.optional !== true &&
758
+ value.arguments.length === 0
759
+ ) {
760
+ const binding = directCallBinding(value, analysis.nodeScopes.get(value));
761
+ const factory = factories.get(binding);
762
+ if (!factory || factory.params.length !== 0 || seen.has(binding)) return false;
763
+ seen.add(binding);
764
+ const body = factory.body;
765
+ return referencesHook(
766
+ body.type === 'BlockStatement'
767
+ ? body.body.length === 1 && body.body[0].type === 'ReturnStatement'
768
+ ? body.body[0].argument
769
+ : null
770
+ : body,
771
+ seen,
772
+ );
773
+ }
774
+ return false;
775
+ }
776
+ for (const { call, scope } of analysis.calls) {
777
+ if (call.optional === true || call.callee.type !== 'Identifier') continue;
778
+ const binding = directCallBinding(call, scope);
779
+ if (!values.has(binding) || !referencesHook(values.get(binding), new Set([binding]))) continue;
780
+ analysis.callAnnotations.set(call, {
781
+ ...analysis.callAnnotations.get(call),
782
+ _octaneCustomHookCall: binding.name,
783
+ });
784
+ }
785
+ }
786
+
690
787
  function collectPatternBindings(pattern, scope, into) {
691
788
  if (!pattern) return;
692
789
  if (pattern.type === 'Identifier') {
@@ -17,6 +17,7 @@ export const HOOK_NAMES = new Set([
17
17
  'useSyncExternalStore',
18
18
  // React 19 Actions bundle.
19
19
  'useActionState',
20
+ 'useFormState',
20
21
  'useFormStatus',
21
22
  'useOptimistic',
22
23
  ]);
@@ -0,0 +1,171 @@
1
+ // Manual hook packages own their explicit sub-slot layout. Adapt exported or
2
+ // escaping hook definitions at their owner, so bound/forwarded imports retain
3
+ // that protocol without changing ordinary custom-hook argument lists.
4
+ import { builders as b } from '@tsrx/core';
5
+
6
+ const META = new Set([
7
+ 'loc',
8
+ 'start',
9
+ 'end',
10
+ 'range',
11
+ 'metadata',
12
+ 'parent',
13
+ 'comments',
14
+ 'leadingComments',
15
+ 'trailingComments',
16
+ 'innerComments',
17
+ ]);
18
+ const isHook = (name) =>
19
+ typeof name === 'string' && /^(?:(?:experimental|unstable|UNSTABLE)_)?use[A-Z]/.test(name);
20
+ const isFunction = (node) =>
21
+ node?.type === 'FunctionDeclaration' ||
22
+ node?.type === 'FunctionExpression' ||
23
+ node?.type === 'ArrowFunctionExpression';
24
+
25
+ function children(node, visit) {
26
+ for (const key in node) {
27
+ if (META.has(key) || key.startsWith('_octane')) continue;
28
+ const value = node[key];
29
+ if (Array.isArray(value)) for (const child of value) visit(child, node, key);
30
+ else if (value && typeof value === 'object') visit(value, node, key);
31
+ }
32
+ }
33
+
34
+ export function findManualHookProviders(ast) {
35
+ const escaping = new Set();
36
+ const definitions = new Map();
37
+ function scan(node, parent, key) {
38
+ if (!node || typeof node !== 'object') return;
39
+ if (isFunction(node) && node.body) {
40
+ const bindingName = parent?.type === 'VariableDeclarator' ? parent.id?.name : undefined;
41
+ const name = isHook(bindingName) ? bindingName : node.id?.name;
42
+ if (isHook(name)) {
43
+ // A named expression can have a private implementation name while its
44
+ // exported binding establishes the hook contract. Keep its public
45
+ // Function.name when installing the provider boundary.
46
+ definitions.set(node, { name, displayName: node.id?.name ?? name });
47
+ if (
48
+ parent?.type === 'ExportNamedDeclaration' ||
49
+ parent?.type === 'ExportDefaultDeclaration'
50
+ )
51
+ escaping.add(name);
52
+ if (parent?.type === 'ReturnStatement') escaping.add(name);
53
+ }
54
+ }
55
+ if (node.type === 'Identifier' && isHook(node.name)) {
56
+ const binding =
57
+ (isFunction(parent) && (key === 'id' || key === 'params')) ||
58
+ (parent?.type === 'VariableDeclarator' && key === 'id') ||
59
+ parent?.type === 'ImportSpecifier' ||
60
+ parent?.type === 'ImportDefaultSpecifier' ||
61
+ parent?.type === 'ImportNamespaceSpecifier';
62
+ const directCall = parent?.type === 'CallExpression' && key === 'callee';
63
+ const property =
64
+ (parent?.type === 'MemberExpression' && key === 'property' && !parent.computed) ||
65
+ (parent?.type === 'Property' && key === 'key' && !parent.computed);
66
+ if (!binding && !directCall && !property) escaping.add(node.name);
67
+ }
68
+ if (
69
+ node.type === 'ExportNamedDeclaration' &&
70
+ node.declaration?.type === 'VariableDeclaration'
71
+ ) {
72
+ for (const declaration of node.declaration.declarations) {
73
+ if (isHook(declaration.id?.name)) escaping.add(declaration.id.name);
74
+ }
75
+ }
76
+ children(node, scan);
77
+ }
78
+ scan(ast, null, null);
79
+ return new Map(
80
+ [...definitions]
81
+ .filter(([, { name }]) => escaping.has(name))
82
+ .map(([node, { displayName }]) => [node, displayName]),
83
+ );
84
+ }
85
+
86
+ function declarationOf(statement) {
87
+ return statement?.type === 'ExportNamedDeclaration' ||
88
+ statement?.type === 'ExportDefaultDeclaration'
89
+ ? statement.declaration
90
+ : statement;
91
+ }
92
+
93
+ // Only the parameters before the first default/rest contribute to Function.length.
94
+ // The public wrapper must not evaluate authored defaults or destructuring; the
95
+ // private implementation receives the original arguments after slot adaptation.
96
+ export function manualHookWrapperParameters(node) {
97
+ const parameters = [];
98
+ for (const param of node.params) {
99
+ if (param.type === 'Identifier' && param.name === 'this') continue;
100
+ if (param.type === 'AssignmentPattern' || param.type === 'RestElement') break;
101
+ parameters.push(b.id(`_$arg${parameters.length}`));
102
+ }
103
+ return parameters;
104
+ }
105
+
106
+ export function adaptManualHookProviders(ast, requireHelper, allocateName) {
107
+ const providers = findManualHookProviders(ast);
108
+ if (!providers.size) return ast;
109
+ function visit(node) {
110
+ if (!node || typeof node !== 'object') return node;
111
+ if (Array.isArray(node)) {
112
+ let changed = false;
113
+ const result = node.map((item) => {
114
+ const next = visit(item);
115
+ changed ||= next !== item;
116
+ return next;
117
+ });
118
+ return changed ? result : node;
119
+ }
120
+ let result = node;
121
+ for (const key in node) {
122
+ if (META.has(key) || key.startsWith('_octane')) continue;
123
+ const value = node[key];
124
+ if (!value || typeof value !== 'object') continue;
125
+ const next = visit(value);
126
+ if (next !== value) {
127
+ if (result === node) result = { ...node };
128
+ result[key] = next;
129
+ }
130
+ }
131
+ if (node.type === 'Program' || node.type === 'BlockStatement') {
132
+ let changed = false;
133
+ const body = result.body.flatMap((statement, index) => {
134
+ const original = declarationOf(node.body[index]);
135
+ if (original?.type !== 'FunctionDeclaration' || !providers.has(original))
136
+ return [statement];
137
+ changed = true;
138
+ const declaration = declarationOf(statement);
139
+ const implementation = allocateName(`_$manual_${original.id.name}`);
140
+ const wrapper = b.function_declaration(
141
+ original.id,
142
+ manualHookWrapperParameters(original),
143
+ b.block([
144
+ b.return(
145
+ b.call(
146
+ requireHelper('invokeManualHook'),
147
+ b.id(implementation),
148
+ b.this,
149
+ b.id('arguments'),
150
+ ),
151
+ ),
152
+ ]),
153
+ );
154
+ // Both declarations hoist. Keep the exported binding stable even for
155
+ // cyclic imports and aliases captured before the textual declaration.
156
+ return [
157
+ statement === declaration ? wrapper : { ...statement, declaration: wrapper },
158
+ { ...declaration, id: b.id(implementation) },
159
+ ];
160
+ });
161
+ if (changed) result = { ...result, body };
162
+ }
163
+ return providers.has(node) && node.type !== 'FunctionDeclaration'
164
+ ? {
165
+ ...b.call(requireHelper('manualHook'), result, b.literal(providers.get(node))),
166
+ __octanePure: true,
167
+ }
168
+ : result;
169
+ }
170
+ return visit(ast);
171
+ }
@@ -7,6 +7,7 @@ import { print as esrapPrint } from 'esrap';
7
7
  import esrapTsx from 'esrap/languages/tsx';
8
8
  import { METHOD_DEP_IMPORT } from './hook-deps.js';
9
9
  import { nativeReadActivationIndex } from './native-read-codegen.js';
10
+ import { adaptManualHookProviders } from './manual-hooks.js';
10
11
  import {
11
12
  hasInlineMemoDirectEval,
12
13
  inheritHookMemoOrigin,
@@ -126,13 +127,32 @@ function inferredDependencyArray(inferred, state, origin) {
126
127
  );
127
128
  }
128
129
 
129
- // Match the surgical pass's BASE-hook-only policy. Custom hooks remain normal
130
- // calls; their compiled caller owns withSlot. Existing explicit memo slots are
130
+ // Match the surgical pass's base-hook and imported-hook slot policy. Local
131
+ // custom helpers keep their authored boundaries. Existing explicit memo slots are
131
132
  // already the effective third argument, so no unused fourth argument is added.
132
133
  function slotBaseHooks(ast, state, options) {
133
134
  function visit(node) {
134
135
  if (node === null || typeof node !== 'object') return node;
135
136
  if (Array.isArray(node)) return mapChildren(node, visit);
137
+ if (!options.manualSlots && node.type === 'CallExpression' && node._octaneCustomHookCall) {
138
+ const slot = allocateHookSlot(state, node);
139
+ const mapped = mapChildren(node, visit);
140
+ const callee = mapped.typeArguments
141
+ ? {
142
+ type: 'TSInstantiationExpression',
143
+ expression: mapped.callee,
144
+ typeArguments: mapped.typeArguments,
145
+ }
146
+ : mapped.callee;
147
+ return {
148
+ ...mapped,
149
+ callee: b.id(requireHelper(state, 'withSlot', 'octane'), node),
150
+ typeArguments: null,
151
+ // Keep foreign/default parameters and arguments.length unchanged;
152
+ // Octane base aliases resolve their slot from this call's path.
153
+ arguments: [slot, callee, ...mapped.arguments],
154
+ };
155
+ }
136
156
  const imported =
137
157
  node.type === 'CallExpression'
138
158
  ? (node._octaneImportedHook ??
@@ -147,13 +167,12 @@ function slotBaseHooks(ast, state, options) {
147
167
  inferred === undefined &&
148
168
  node.arguments.length === 3 &&
149
169
  !node.arguments.some((argument) => argument.type === 'SpreadElement');
150
- const slot = explicitMemoSlot ? null : allocateHookSlot(state, node);
170
+ const slot = options.manualSlots || explicitMemoSlot ? null : allocateHookSlot(state, node);
151
171
  const mapped = mapChildren(node, visit);
152
172
  const args = mapped.arguments.slice();
153
173
  if (inferred !== undefined) {
154
174
  args.splice(inferred.depsIndex, 0, inferredDependencyArray(inferred, state, node));
155
175
  }
156
- if (slot !== null) args.push(slot);
157
176
  let callee = mapped.callee;
158
177
  if (options.getterCalls.has(node) && options.stateGetterHelpers[imported]) {
159
178
  callee = b.id(requireHelper(state, options.stateGetterHelpers[imported], 'octane'), node);
@@ -161,6 +180,29 @@ function slotBaseHooks(ast, state, options) {
161
180
  if (node._octaneNativeInferredMemo === true) {
162
181
  callee = b.id(requireHelper(state, 'nativePuMemo'), node);
163
182
  }
183
+ if (slot !== null) {
184
+ if (
185
+ (imported === 'useState' || imported === 'useRef') &&
186
+ args.some((arg) => arg.type === 'SpreadElement')
187
+ ) {
188
+ const fn = mapped.typeArguments
189
+ ? {
190
+ type: 'TSInstantiationExpression',
191
+ expression: callee,
192
+ typeArguments: mapped.typeArguments,
193
+ }
194
+ : callee;
195
+ return {
196
+ ...mapped,
197
+ callee: b.id(requireHelper(state, 'withSlot', 'octane'), node),
198
+ typeArguments: null,
199
+ arguments: [slot, fn, ...args],
200
+ };
201
+ }
202
+ if (args.length === 0 && (imported === 'useState' || imported === 'useRef'))
203
+ args.push(b.id('undefined', node));
204
+ args.push(slot);
205
+ }
164
206
  return { ...mapped, callee, arguments: args };
165
207
  }
166
208
  return visit(ast);
@@ -235,7 +277,7 @@ export function inlinePlainHookMemos(ast, source, id, options) {
235
277
  slotBase: null,
236
278
  slotDeclarations: [],
237
279
  };
238
- let transformed = options.manualSlots ? ast : slotBaseHooks(ast, state, options);
280
+ let transformed = slotBaseHooks(ast, state, options);
239
281
  const lowered = lowerSlotMemoFunctions(transformed, {
240
282
  allocateName: (preferred) => allocName(state, preferred),
241
283
  requireRuntime: (imported) => requireHelper(state, imported),
@@ -243,6 +285,13 @@ export function inlinePlainHookMemos(ast, source, id, options) {
243
285
  });
244
286
  if (lowered.lowered === 0) return null;
245
287
  transformed = lowered.ast;
288
+ if (options.manualSlots) {
289
+ transformed = adaptManualHookProviders(
290
+ transformed,
291
+ (name) => requireHelper(state, name, 'octane'),
292
+ (preferred) => allocName(state, preferred),
293
+ );
294
+ }
246
295
  const origin = ast.body[0] ?? ast;
247
296
  const activation = options.nativeReadActivation
248
297
  ? inheritHookMemoOrigin(
@@ -0,0 +1,43 @@
1
+ import type { CompilerProgram, CompileSourceMap } from './index.js';
2
+
3
+ /** Options for the plain-module hook transform used by compiler adapters. */
4
+ export interface SlotHooksOptions {
5
+ environment?: 'client' | 'server';
6
+ strong?: boolean;
7
+ nativeReads?: boolean;
8
+ hmr?: boolean;
9
+ dev?: boolean;
10
+ profile?: boolean;
11
+ profileFilename?: string;
12
+ inlineHookMemo?: boolean;
13
+ manualSlots?: boolean;
14
+ /** Opaque activation signal; the transform only checks its presence. */
15
+ universalRuntime?: unknown;
16
+ renderer?: { target?: string };
17
+ isVoidComponentImport?: (request: string, imported: string) => boolean;
18
+ }
19
+
20
+ export interface SlotHooksResult {
21
+ code: string;
22
+ /** The surgical transform preserves source lines and returns no map. */
23
+ map: CompileSourceMap | null;
24
+ }
25
+
26
+ export interface VoidComponentImport {
27
+ request: string;
28
+ imported: string;
29
+ }
30
+
31
+ /** Returns null when the module can pass through unchanged. */
32
+ export function slotHooks(
33
+ source: string,
34
+ id: string,
35
+ options?: SlotHooksOptions,
36
+ ): SlotHooksResult | null;
37
+
38
+ export function findVoidRootImports(source: string, id: string): VoidComponentImport[];
39
+
40
+ export function findVoidComponentImports(
41
+ source: string | CompilerProgram,
42
+ id: string,
43
+ ): VoidComponentImport[];