@alloy-js/core 0.22.0-dev.1 → 0.22.0-dev.4

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 (65) hide show
  1. package/dist/src/components/Block.d.ts.map +1 -1
  2. package/dist/src/components/Block.js +24 -7
  3. package/dist/src/components/Block.js.map +1 -1
  4. package/dist/src/components/Indent.d.ts.map +1 -1
  5. package/dist/src/components/Indent.js +2 -1
  6. package/dist/src/components/Indent.js.map +1 -1
  7. package/dist/src/components/Prose.d.ts.map +1 -1
  8. package/dist/src/components/Prose.js +2 -1
  9. package/dist/src/components/Prose.js.map +1 -1
  10. package/dist/src/content-slot.d.ts +51 -0
  11. package/dist/src/content-slot.d.ts.map +1 -0
  12. package/dist/src/content-slot.js +69 -0
  13. package/dist/src/content-slot.js.map +1 -0
  14. package/dist/src/content-slot.test.d.ts +2 -0
  15. package/dist/src/content-slot.test.d.ts.map +1 -0
  16. package/dist/src/content-slot.test.js +57 -0
  17. package/dist/src/content-slot.test.js.map +1 -0
  18. package/dist/src/index.d.ts +1 -0
  19. package/dist/src/index.d.ts.map +1 -1
  20. package/dist/src/index.js +1 -0
  21. package/dist/src/index.js.map +1 -1
  22. package/dist/src/reactivity.d.ts +15 -1
  23. package/dist/src/reactivity.d.ts.map +1 -1
  24. package/dist/src/reactivity.js +20 -8
  25. package/dist/src/reactivity.js.map +1 -1
  26. package/dist/src/render.d.ts +24 -0
  27. package/dist/src/render.d.ts.map +1 -1
  28. package/dist/src/render.js +98 -2
  29. package/dist/src/render.js.map +1 -1
  30. package/dist/src/symbols/decl.d.ts +8 -0
  31. package/dist/src/symbols/decl.d.ts.map +1 -0
  32. package/dist/src/symbols/decl.js +22 -0
  33. package/dist/src/symbols/decl.js.map +1 -0
  34. package/dist/src/symbols/index.d.ts +1 -0
  35. package/dist/src/symbols/index.d.ts.map +1 -1
  36. package/dist/src/symbols/index.js +1 -0
  37. package/dist/src/symbols/index.js.map +1 -1
  38. package/dist/src/utils.d.ts.map +1 -1
  39. package/dist/src/utils.js +212 -15
  40. package/dist/src/utils.js.map +1 -1
  41. package/dist/test/components/block.test.d.ts.map +1 -1
  42. package/dist/test/components/block.test.js +18 -1
  43. package/dist/test/components/block.test.js.map +1 -1
  44. package/dist/test/components/list.test.d.ts.map +1 -1
  45. package/dist/test/components/list.test.js +80 -1
  46. package/dist/test/components/list.test.js.map +1 -1
  47. package/dist/test/control-flow/for.test.js +32 -2
  48. package/dist/test/control-flow/for.test.js.map +1 -1
  49. package/dist/tsconfig.tsbuildinfo +1 -1
  50. package/package.json +1 -1
  51. package/src/components/Block.tsx +18 -6
  52. package/src/components/Indent.tsx +4 -2
  53. package/src/components/Prose.tsx +2 -1
  54. package/src/content-slot.test.tsx +65 -0
  55. package/src/content-slot.tsx +91 -0
  56. package/src/index.ts +1 -0
  57. package/src/reactivity.ts +38 -5
  58. package/src/render.ts +112 -3
  59. package/src/symbols/decl.ts +25 -0
  60. package/src/symbols/index.ts +1 -0
  61. package/src/utils.tsx +240 -16
  62. package/temp/api.json +550 -4
  63. package/test/components/block.test.tsx +21 -1
  64. package/test/components/list.test.tsx +76 -1
  65. package/test/control-flow/for.test.tsx +43 -2
@@ -1,6 +1,6 @@
1
1
  import { computed } from "@vue/reactivity";
2
+ import { createContentSlot } from "../content-slot.jsx";
2
3
  import type { Children } from "../runtime/component.js";
3
- import { childrenArray } from "../utils.jsx";
4
4
  import { Indent } from "./Indent.jsx";
5
5
 
6
6
  export interface BlockProps {
@@ -33,17 +33,29 @@ export interface BlockProps {
33
33
  * added after the block, which defaults to `"}"`.
34
34
  */
35
35
  export function Block(props: BlockProps) {
36
- const childCount = computed(() => childrenArray(() => props.children).length);
36
+ const ContentSlot = createContentSlot();
37
+ const leadingNewline = computed(() => {
38
+ if (!props.newline) return false;
39
+
40
+ // When inline, we want a newline only when content breaks, otherwise a nothing..
41
+ if (props.inline) return <sbr />;
42
+
43
+ // When not inline, we want a hardline when the slot has contents otherwise a space.
44
+ if (ContentSlot.hasContent) return <hbr />;
45
+
46
+ return " ";
47
+ });
37
48
  return (
38
49
  <group>
39
- {props.newline && (props.inline ? <softline /> : <br />)}
50
+ {leadingNewline}
40
51
  {props.opener ?? "{"}
41
52
  <Indent
42
- line={props.inline && childCount.value > 0}
43
- softline={childCount.value === 0}
53
+ hardline={!props.inline && ContentSlot.hasContent}
54
+ line={props.inline && ContentSlot.hasContent}
55
+ softline={!props.inline || ContentSlot.isEmpty}
44
56
  trailingBreak
45
57
  >
46
- {props.children}
58
+ <ContentSlot>{props.children}</ContentSlot>
47
59
  </Indent>
48
60
  {props.closer ?? "}"}
49
61
  </group>
@@ -1,3 +1,4 @@
1
+ import { computed } from "@vue/reactivity";
1
2
  import type { Children } from "../runtime/component.js";
2
3
 
3
4
  export interface IndentProps {
@@ -40,12 +41,13 @@ export interface IndentProps {
40
41
  * break suitable for typical blocks of statements but can be configured.
41
42
  */
42
43
  export function Indent(props: IndentProps) {
43
- const breakElem =
44
+ const breakElem = computed(() =>
44
45
  props.nobreak ? ""
45
46
  : props.hardline ? <hbr />
46
47
  : props.softline ? <sbr />
47
48
  : props.line ? <br />
48
- : <hbr />;
49
+ : <hbr />,
50
+ );
49
51
 
50
52
  return (
51
53
  <>
@@ -1,5 +1,6 @@
1
- import { childrenArray, computed } from "@alloy-js/core";
1
+ import { computed } from "@vue/reactivity";
2
2
  import type { Children } from "../runtime/component.js";
3
+ import { childrenArray } from "../utils.jsx";
3
4
 
4
5
  export interface Prose {
5
6
  children: Children;
@@ -0,0 +1,65 @@
1
+ import { ref } from "@vue/reactivity";
2
+ import { expect, it } from "vitest";
3
+ import "../testing/extend-expect.js";
4
+ import { Show } from "./components/Show.jsx";
5
+ import { createContentSlot } from "./content-slot.jsx";
6
+ import { printTree, renderTree } from "./render.js";
7
+
8
+ it("knows when its empty", () => {
9
+ const ContentSlot = createContentSlot();
10
+
11
+ expect(
12
+ <>
13
+ {ContentSlot.hasContent && "{"}
14
+ <ContentSlot>hi</ContentSlot>
15
+ {ContentSlot.hasContent && "}"}
16
+ </>,
17
+ ).toRenderTo(`
18
+ {hi}
19
+ `);
20
+
21
+ expect(
22
+ <>
23
+ {ContentSlot.hasContent && "{"}
24
+ <ContentSlot>{false}</ContentSlot>
25
+ {ContentSlot.hasContent && "}"}
26
+ </>,
27
+ ).toRenderTo(``);
28
+ });
29
+
30
+ it("is reactive", () => {
31
+ const ContentSlot = createContentSlot();
32
+ const showContent = ref(false);
33
+
34
+ const tree = renderTree(
35
+ <>
36
+ {ContentSlot.isEmpty && "It's empty!"}
37
+ <ContentSlot>
38
+ <Show when={showContent.value}>Content!</Show>
39
+ </ContentSlot>
40
+ </>,
41
+ );
42
+
43
+ expect(printTree(tree)).toBe(`It's empty!`);
44
+ showContent.value = true;
45
+ expect(printTree(tree)).toBe(`Content!`);
46
+ });
47
+
48
+ it("works with WhenEmpty and WhenHasContent", () => {
49
+ const ContentSlot = createContentSlot();
50
+ const showContent = ref(false);
51
+
52
+ const tree = renderTree(
53
+ <>
54
+ <ContentSlot.WhenEmpty>It's empty!</ContentSlot.WhenEmpty>
55
+ <ContentSlot.WhenHasContent>Has content!</ContentSlot.WhenHasContent>
56
+ <ContentSlot>
57
+ <Show when={showContent.value}>Content!</Show>
58
+ </ContentSlot>
59
+ </>,
60
+ );
61
+
62
+ expect(printTree(tree)).toBe(`It's empty!`);
63
+ showContent.value = true;
64
+ expect(printTree(tree)).toBe(`Has content!Content!`);
65
+ });
@@ -0,0 +1,91 @@
1
+ import { effect, Ref, shallowRef } from "@vue/reactivity";
2
+ import { Show } from "./components/Show.jsx";
3
+ import { getContext } from "./reactivity.js";
4
+ import { Children, Component } from "./runtime/component.js";
5
+
6
+ export interface ContentSlot {
7
+ (props: { children: Children }): Children;
8
+ /**
9
+ * A ref indicating whether the slot is currently empty.
10
+ */
11
+ ref: Ref<boolean>;
12
+
13
+ /**
14
+ * Whether the slot is currently empty.
15
+ */
16
+ isEmpty: boolean;
17
+
18
+ /**
19
+ * Whether the slot has any content.
20
+ */
21
+ hasContent: boolean;
22
+
23
+ /**
24
+ * A component that will render its contents when the content slot is empty.
25
+ */
26
+ WhenEmpty: Component<{}>;
27
+
28
+ /**
29
+ * A component that will render its contents when the content slot has content.
30
+ */
31
+ WhenHasContent: Component<{}>;
32
+ }
33
+
34
+ /**
35
+ * Create a component which tracks whether any content is placed inside of it.
36
+ * The component exposes a ref `isEmpty` which indicates whether the slot is
37
+ * empty, as well as convenience accessors `isEmpty` and `hasContent`.
38
+ * Additionally, it provides two sub-components, `WhenEmpty` and
39
+ * `WhenHasContent`, which render their contents conditionally based on whether
40
+ * the slot is empty.
41
+ *
42
+ * @example
43
+ *
44
+ * ```tsx
45
+ * const ContentSlot = createContentSlot();
46
+ *
47
+ * <>
48
+ * <ContentSlot.WhenEmpty>The slot is empty!</ContentSlot.WhenEmpty>
49
+ * <ContentSlot.WhenHasContent>The slot has content!</ContentSlot.WhenHasContent>
50
+ * <ContentSlot>
51
+ * {someCondition && "Here is some content!"}
52
+ * </ContentSlot>
53
+ * </>
54
+ * ```
55
+ */
56
+ export function createContentSlot(): ContentSlot {
57
+ const isEmpty = shallowRef<boolean>(false);
58
+
59
+ function ContentSlot(props: { children: Children }) {
60
+ const context = getContext()!;
61
+ effect(() => {
62
+ isEmpty.value = context.isEmpty!.value;
63
+ });
64
+
65
+ return props.children;
66
+ }
67
+ ContentSlot.ref = isEmpty;
68
+ ContentSlot.WhenEmpty = function WhenEmpty(props: { children: Children }) {
69
+ return <Show when={isEmpty.value}>{props.children}</Show>;
70
+ };
71
+
72
+ ContentSlot.WhenHasContent = function WhenHasContent(props: {
73
+ children: Children;
74
+ }) {
75
+ return <Show when={!isEmpty.value}>{props.children}</Show>;
76
+ };
77
+
78
+ Object.defineProperty(ContentSlot, "isEmpty", {
79
+ get() {
80
+ return isEmpty.value;
81
+ },
82
+ });
83
+
84
+ Object.defineProperty(ContentSlot, "hasContent", {
85
+ get() {
86
+ return !isEmpty.value;
87
+ },
88
+ });
89
+
90
+ return ContentSlot as any;
91
+ }
package/src/index.ts CHANGED
@@ -25,6 +25,7 @@ export {
25
25
  export * from "./binder.js";
26
26
  export * from "./code.js";
27
27
  export * from "./components/index.js";
28
+ export * from "./content-slot.js";
28
29
  export * from "./context.js";
29
30
  export * from "./context/index.js";
30
31
  export * from "./library-symbol-reference.js";
package/src/reactivity.ts CHANGED
@@ -1,6 +1,9 @@
1
1
  import {
2
+ isRef,
2
3
  pauseTracking,
3
4
  ReactiveEffectRunner,
5
+ ref,
6
+ Ref,
4
7
  resetTracking,
5
8
  ShallowReactive,
6
9
  shallowRef,
@@ -66,6 +69,22 @@ export interface Context {
66
69
  * The symbol that this component has taken.
67
70
  */
68
71
  takenSymbols?: ShallowReactive<Set<OutputSymbol>>;
72
+
73
+ /**
74
+ * The number of child nodes that have content. When zero, this component is
75
+ * semantically empty.
76
+ */
77
+ childrenWithContent: number;
78
+
79
+ /**
80
+ * A ref that indicates whether the component is empty.
81
+ */
82
+ isEmpty?: Ref<boolean>;
83
+
84
+ /**
85
+ * Whether this context is a root context
86
+ */
87
+ isRoot: boolean;
69
88
  }
70
89
 
71
90
  let globalContext: Context | null = null;
@@ -86,6 +105,9 @@ export function root<T>(fn: (d: Disposable) => T, options?: RootOptions): T {
86
105
  elementCache: new Map(),
87
106
  takesSymbols: false,
88
107
  takenSymbols: undefined,
108
+ childrenWithContent: 0,
109
+ isEmpty: ref(true),
110
+ isRoot: true,
89
111
  };
90
112
 
91
113
  globalContext = context;
@@ -131,6 +153,8 @@ export function effect<T>(fn: (prev?: T) => T, current?: T) {
131
153
  elementCache: new Map(),
132
154
  takesSymbols: false,
133
155
  takenSymbols: undefined,
156
+ childrenWithContent: 0,
157
+ isRoot: false,
134
158
  };
135
159
 
136
160
  const cleanupFn = (final: boolean) => {
@@ -162,19 +186,16 @@ export function effect<T>(fn: (prev?: T) => T, current?: T) {
162
186
  scheduler: scheduler(),
163
187
  onTrack(event) {
164
188
  trace(TracePhase.effect.track, () => {
165
- return `tracking ${event.target}, ${String(event.key)}`;
189
+ return `tracking ${isRef(event.target) ? `Ref:${refId(event.target)}` : event.target}, ${String(event.key)}`;
166
190
  });
167
191
  },
168
192
  onTrigger(event) {
169
193
  trace(TracePhase.effect.trigger, () => {
170
- return `triggering ${event.target}, ${String(event.key)}`;
194
+ return `triggering ${isRef(event.target) ? `Ref:${refId(event.target)}` : event.target}, ${String(event.key)}`;
171
195
  });
172
196
  },
173
197
  },
174
198
  );
175
-
176
- // allow recursive effects (recursive option does nothing, possible bug)
177
- (runner as any).effect.flags |= 1 << 5;
178
199
  }
179
200
 
180
201
  /**
@@ -231,3 +252,15 @@ export function isCustomContext(child: Children): child is CustomContext {
231
252
  Object.hasOwn(child, CUSTOM_CONTEXT_SYM)
232
253
  );
233
254
  }
255
+
256
+ const seenRefs = new WeakMap<Ref<unknown>, number>();
257
+ let refIdCounter = 1;
258
+
259
+ export function refId(ref: Ref<unknown>): number {
260
+ let id = seenRefs.get(ref);
261
+ if (id === undefined) {
262
+ id = refIdCounter++;
263
+ seenRefs.set(ref, id);
264
+ }
265
+ return id;
266
+ }
package/src/render.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { isRef } from "@vue/reactivity";
1
+ import { isRef, ref } from "@vue/reactivity";
2
2
  import { Doc, doc } from "prettier";
3
3
  import prettier from "prettier/doc.js";
4
4
  import { useContext } from "./context.js";
@@ -186,6 +186,12 @@ export function isPrintHook(type: unknown): type is PrintHook {
186
186
 
187
187
  export type RenderedTextTree = (string | RenderedTextTree | PrintHook)[];
188
188
 
189
+ /**
190
+ * Render a component tree to source directories and files. Will ensure that
191
+ * all non-async scheduled jobs are completed before returning. If async jobs
192
+ * are found, an error will be thrown. Use `renderAsync` when asynchronous
193
+ * jobs are expected.
194
+ */
189
195
  export function render(
190
196
  children: Children,
191
197
  options?: PrintTreeOptions,
@@ -195,15 +201,37 @@ export function render(
195
201
  return sourceFilesForTree(tree, options);
196
202
  }
197
203
 
204
+ /**
205
+ * Render a component tree to source directories and files. Will ensure that all
206
+ * scheduled jobs are completed before returning.
207
+ */
198
208
  export async function renderAsync(
199
209
  children: Children,
200
210
  options?: PrintTreeOptions,
201
211
  ): Promise<OutputDirectory> {
202
212
  const tree = renderTree(children);
213
+ return sourceFilesForTreeAsync(tree, options);
214
+ }
215
+
216
+ /**
217
+ * Convert a rendered text tree to source directories and files. Will ensure that
218
+ * all scheduled jobs are completed, including async ones.
219
+ */
220
+ export async function sourceFilesForTreeAsync(
221
+ tree: RenderedTextTree,
222
+ options?: PrintTreeOptions,
223
+ ) {
224
+ // if we await here, we ensure all reactive updates are flushed.
225
+ // sourceFilesForTree will flush again, but won't find anything, because tree
226
+ // printing won't schedule anything.
203
227
  await flushJobsAsync();
204
228
  return sourceFilesForTree(tree, options);
205
229
  }
206
230
 
231
+ /**
232
+ * Convert a rendered text tree to source directories and files. Will ensure
233
+ * that all scheduled jobs are completed before returning.
234
+ */
207
235
  export function sourceFilesForTree(
208
236
  tree: RenderedTextTree,
209
237
  options?: PrintTreeOptions,
@@ -332,11 +360,74 @@ function renderWorker(node: RenderedTextTree, children: Children) {
332
360
  }
333
361
  }
334
362
 
363
+ function contentAdded() {
364
+ const context: Context = getContext()!;
365
+ context.childrenWithContent++;
366
+ }
367
+
368
+ export function notifyContentState() {
369
+ untrack(() => {
370
+ const startContext = getContext()!;
371
+
372
+ if (startContext.childrenWithContent === 0) {
373
+ if (startContext.isEmpty!.value === true) {
374
+ // it was already empty, no work to do.
375
+ return;
376
+ }
377
+
378
+ if (startContext.isEmpty) {
379
+ startContext.isEmpty.value = true;
380
+ }
381
+
382
+ // otherwise we need to decrement the content counts up the tree.
383
+ let current = startContext.owner;
384
+ while (current) {
385
+ if (current.childrenWithContent === 0) {
386
+ break;
387
+ }
388
+ current.childrenWithContent--;
389
+ if (current.isEmpty) {
390
+ current.isEmpty.value = true;
391
+ }
392
+ current = current.owner;
393
+ }
394
+ } else {
395
+ if (startContext.isEmpty!.value === false) {
396
+ // it was already non-empty, no work to do.
397
+ return;
398
+ }
399
+
400
+ if (startContext.isEmpty && startContext.isEmpty.value) {
401
+ startContext.isEmpty.value = false;
402
+ }
403
+
404
+ // otherwise we need to increment the content counts up the tree.
405
+ let current = startContext.owner;
406
+ while (current) {
407
+ current.childrenWithContent++;
408
+ if (current.childrenWithContent > 1) {
409
+ // This isn't the first content so we have no work to do
410
+ break;
411
+ }
412
+
413
+ if (current.isEmpty && current.isEmpty.value) {
414
+ current.isEmpty.value = false;
415
+ }
416
+
417
+ current = current.owner;
418
+ }
419
+ }
420
+ });
421
+ }
422
+
335
423
  function appendChild(node: RenderedTextTree, rawChild: Child) {
336
424
  trace(TracePhase.render.appendChild, () => debugPrintChild(rawChild));
337
425
  const child = normalizeChild(rawChild);
338
426
 
339
427
  if (typeof child === "string") {
428
+ if (child !== "") {
429
+ contentAdded();
430
+ }
340
431
  node.push(child);
341
432
  } else {
342
433
  const cache = getElementCache();
@@ -358,6 +449,7 @@ function appendChild(node: RenderedTextTree, rawChild: Child) {
358
449
  renderWorker(newNode, children);
359
450
  node.push(newNode);
360
451
  cache.set(child, newNode);
452
+ notifyContentState();
361
453
  });
362
454
  } else if (isIntrinsicElement(child)) {
363
455
  trace(
@@ -488,6 +580,9 @@ function appendChild(node: RenderedTextTree, rawChild: Child) {
488
580
  () => "Component: " + debugPrintChild(child),
489
581
  );
490
582
  const context = getContext();
583
+ context!.childrenWithContent = 0;
584
+ context!.isEmpty ??= ref(true);
585
+
491
586
  if (context) context.componentOwner = child;
492
587
  const componentRoot: RenderedTextTree = [];
493
588
  pushStack(child.component, child.props);
@@ -495,10 +590,14 @@ function appendChild(node: RenderedTextTree, rawChild: Child) {
495
590
  popStack();
496
591
  node.push(componentRoot);
497
592
  cache.set(child, componentRoot);
498
-
593
+ notifyContentState();
499
594
  trace(
500
595
  TracePhase.render.appendChild,
501
- () => "Component done: " + debugPrintChild(child),
596
+ () =>
597
+ "Component done: " +
598
+ debugPrintChild(child) +
599
+ ", empty: " +
600
+ context!.isEmpty!.value,
502
601
  );
503
602
  });
504
603
  } else if (typeof child === "function") {
@@ -510,10 +609,16 @@ function appendChild(node: RenderedTextTree, rawChild: Child) {
510
609
  while (typeof res === "function" && !isComponentCreator(res)) {
511
610
  res = res();
512
611
  }
612
+ const context = getContext();
613
+ context!.childrenWithContent = 0;
614
+ context!.isEmpty ??= ref(true);
615
+
513
616
  const newNodes: RenderedTextTree = [];
514
617
  renderWorker(newNodes, res);
515
618
  node[index] = newNodes;
516
619
  cache.set(child, newNodes);
620
+
621
+ notifyContentState();
517
622
  return newNodes;
518
623
  });
519
624
  } else {
@@ -618,6 +723,10 @@ const defaultPrintTreeOptions: PrintTreeOptions = {
618
723
  tabWidth: 2,
619
724
  };
620
725
 
726
+ /**
727
+ * Convert a rendered text tree to a string. Will ensure that the scheduler is
728
+ * empty before printing.
729
+ */
621
730
  export function printTree(tree: RenderedTextTree, options?: PrintTreeOptions) {
622
731
  options = {
623
732
  ...defaultPrintTreeOptions,
@@ -0,0 +1,25 @@
1
+ import { toRef } from "@vue/reactivity";
2
+ import { useScope } from "../context/scope.js";
3
+ import { Namekey } from "../refkey.js";
4
+ import { createComponent } from "../runtime/component.js";
5
+ import { BasicScope } from "./basic-scope.js";
6
+ import { BasicSymbol } from "./basic-symbol.js";
7
+
8
+ /**
9
+ * Create a declaration in the current scope with the given namekey. Only works
10
+ * with basic scopes. Import `decl` from a specific language library for
11
+ * declaring language-specific symbols.
12
+ */
13
+ export function decl(namekey: Namekey) {
14
+ return createComponent(() => {
15
+ const currentScope = useScope();
16
+ if (!(currentScope instanceof BasicScope)) {
17
+ throw new Error(
18
+ `Cannot declare symbol in non-basic scope: ${currentScope.constructor.name}. Use a language-specific 'decl' function instead.`,
19
+ );
20
+ }
21
+ const symbol = new BasicSymbol(namekey, currentScope.symbols);
22
+
23
+ return toRef(symbol, "name");
24
+ }, {});
25
+ }
@@ -1,5 +1,6 @@
1
1
  export * from "./basic-scope.js";
2
2
  export * from "./basic-symbol.js";
3
+ export * from "./decl.js";
3
4
  export * from "./output-scope.js";
4
5
  export * from "./output-space.js";
5
6
  export * from "./output-symbol.js";