@alloy-js/core 0.7.0 → 0.8.0

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 (130) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/babel.config.cjs +1 -4
  3. package/dist/src/binder.d.ts +13 -12
  4. package/dist/src/binder.d.ts.map +1 -1
  5. package/dist/src/binder.js +11 -23
  6. package/dist/src/binder.js.map +1 -1
  7. package/dist/src/code.d.ts +11 -2
  8. package/dist/src/code.d.ts.map +1 -1
  9. package/dist/src/code.js +27 -2
  10. package/dist/src/code.js.map +1 -1
  11. package/dist/src/components/Block.d.ts +2 -2
  12. package/dist/src/components/Block.d.ts.map +1 -1
  13. package/dist/src/components/Block.js +6 -5
  14. package/dist/src/components/Block.js.map +1 -1
  15. package/dist/src/components/Declaration.d.ts +31 -7
  16. package/dist/src/components/Declaration.d.ts.map +1 -1
  17. package/dist/src/components/Declaration.js +15 -7
  18. package/dist/src/components/Declaration.js.map +1 -1
  19. package/dist/src/components/For.d.ts +6 -0
  20. package/dist/src/components/For.d.ts.map +1 -1
  21. package/dist/src/components/For.js +2 -3
  22. package/dist/src/components/For.js.map +1 -1
  23. package/dist/src/components/Indent.d.ts +29 -1
  24. package/dist/src/components/Indent.d.ts.map +1 -1
  25. package/dist/src/components/Indent.js +7 -2
  26. package/dist/src/components/Indent.js.map +1 -1
  27. package/dist/src/components/List.d.ts +7 -3
  28. package/dist/src/components/List.d.ts.map +1 -1
  29. package/dist/src/components/List.js +1 -16
  30. package/dist/src/components/List.js.map +1 -1
  31. package/dist/src/components/MemberDeclaration.d.ts +35 -5
  32. package/dist/src/components/MemberDeclaration.d.ts.map +1 -1
  33. package/dist/src/components/MemberDeclaration.js +18 -7
  34. package/dist/src/components/MemberDeclaration.js.map +1 -1
  35. package/dist/src/components/MemberScope.d.ts +2 -0
  36. package/dist/src/components/MemberScope.d.ts.map +1 -1
  37. package/dist/src/components/MemberScope.js +2 -0
  38. package/dist/src/components/MemberScope.js.map +1 -1
  39. package/dist/src/components/Prose.d.ts +10 -0
  40. package/dist/src/components/Prose.d.ts.map +1 -0
  41. package/dist/src/components/Prose.js +23 -0
  42. package/dist/src/components/Prose.js.map +1 -0
  43. package/dist/src/components/Scope.d.ts +33 -2
  44. package/dist/src/components/Scope.d.ts.map +1 -1
  45. package/dist/src/components/Scope.js +20 -4
  46. package/dist/src/components/Scope.js.map +1 -1
  47. package/dist/src/components/SourceFile.d.ts +5 -0
  48. package/dist/src/components/SourceFile.d.ts.map +1 -1
  49. package/dist/src/components/SourceFile.js +10 -1
  50. package/dist/src/components/SourceFile.js.map +1 -1
  51. package/dist/src/components/index.d.ts +1 -0
  52. package/dist/src/components/index.d.ts.map +1 -1
  53. package/dist/src/components/index.js +1 -0
  54. package/dist/src/components/index.js.map +1 -1
  55. package/dist/src/components/stc/index.d.ts +19 -95
  56. package/dist/src/components/stc/index.d.ts.map +1 -1
  57. package/dist/src/components/stc/index.js +3 -6
  58. package/dist/src/components/stc/index.js.map +1 -1
  59. package/dist/src/components/stc/sti.d.ts +9 -0
  60. package/dist/src/components/stc/sti.d.ts.map +1 -0
  61. package/dist/src/components/stc/sti.js +10 -0
  62. package/dist/src/components/stc/sti.js.map +1 -0
  63. package/dist/src/context/assignment.d.ts +6 -0
  64. package/dist/src/context/assignment.d.ts.map +1 -1
  65. package/dist/src/context/assignment.js +7 -0
  66. package/dist/src/context/assignment.js.map +1 -1
  67. package/dist/src/context.d.ts +2 -0
  68. package/dist/src/context.d.ts.map +1 -1
  69. package/dist/src/context.js +12 -9
  70. package/dist/src/context.js.map +1 -1
  71. package/dist/src/index.d.ts +1 -0
  72. package/dist/src/index.d.ts.map +1 -1
  73. package/dist/src/index.js +1 -0
  74. package/dist/src/index.js.map +1 -1
  75. package/dist/src/jsx-runtime.d.ts +91 -0
  76. package/dist/src/jsx-runtime.d.ts.map +1 -1
  77. package/dist/src/jsx-runtime.js +46 -1
  78. package/dist/src/jsx-runtime.js.map +1 -1
  79. package/dist/src/stc.d.ts +5 -7
  80. package/dist/src/stc.d.ts.map +1 -1
  81. package/dist/src/stc.js +11 -23
  82. package/dist/src/stc.js.map +1 -1
  83. package/dist/src/sti.d.ts +11 -0
  84. package/dist/src/sti.d.ts.map +1 -0
  85. package/dist/src/sti.js +31 -0
  86. package/dist/src/sti.js.map +1 -0
  87. package/dist/src/tap.d.ts +69 -6
  88. package/dist/src/tap.d.ts.map +1 -1
  89. package/dist/src/tap.js +70 -0
  90. package/dist/src/tap.js.map +1 -1
  91. package/dist/src/utils.d.ts +5 -0
  92. package/dist/src/utils.d.ts.map +1 -1
  93. package/dist/src/utils.js +20 -0
  94. package/dist/src/utils.js.map +1 -1
  95. package/dist/test/components/prose.test.d.ts +2 -0
  96. package/dist/test/components/prose.test.d.ts.map +1 -0
  97. package/dist/test/props-with-defaults.test.d.ts +2 -0
  98. package/dist/test/props-with-defaults.test.d.ts.map +1 -0
  99. package/dist/tsconfig.tsbuildinfo +1 -1
  100. package/package.json +3 -3
  101. package/src/binder.ts +20 -29
  102. package/src/code.ts +37 -3
  103. package/src/components/Block.tsx +3 -6
  104. package/src/components/Declaration.tsx +43 -11
  105. package/src/components/For.tsx +10 -3
  106. package/src/components/Indent.tsx +38 -5
  107. package/src/components/List.tsx +14 -40
  108. package/src/components/MemberDeclaration.tsx +51 -12
  109. package/src/components/MemberScope.tsx +2 -0
  110. package/src/components/Prose.tsx +35 -0
  111. package/src/components/Scope.tsx +45 -5
  112. package/src/components/SourceFile.tsx +10 -0
  113. package/src/components/index.tsx +1 -0
  114. package/src/components/stc/index.ts +3 -6
  115. package/src/components/stc/sti.ts +10 -0
  116. package/src/context/assignment.ts +7 -1
  117. package/src/context.ts +15 -11
  118. package/src/index.ts +1 -0
  119. package/src/jsx-runtime.ts +158 -0
  120. package/src/stc.ts +35 -56
  121. package/src/sti.ts +63 -0
  122. package/src/tap.ts +69 -6
  123. package/src/{utils.ts → utils.tsx} +45 -0
  124. package/temp/api.json +1509 -428
  125. package/test/components/declaration.test.tsx +1 -1
  126. package/test/components/prose.test.tsx +36 -0
  127. package/test/components/source-file.test.tsx +17 -0
  128. package/test/props-with-defaults.test.ts +97 -0
  129. package/test/symbols.test.ts +0 -25
  130. package/vitest.config.ts +2 -10
package/src/binder.ts CHANGED
@@ -127,6 +127,11 @@ export interface OutputSymbol {
127
127
  * one static member symbol in the output (i.e., the symbol is unique).
128
128
  */
129
129
  staticMemberScope?: OutputScope;
130
+
131
+ /**
132
+ * Additional custom metadata about this symbol.
133
+ */
134
+ metadata: Record<string, unknown>;
130
135
  }
131
136
 
132
137
  /**
@@ -175,7 +180,7 @@ export interface OutputScope {
175
180
  * The kind of scope. Subtypes will likely provide a set of known scope kinds.
176
181
  * The kind is not used by the binder itself.
177
182
  */
178
- kind: string;
183
+ kind?: string;
179
184
 
180
185
  /**
181
186
  * The name of the scope.
@@ -227,9 +232,9 @@ export interface OutputScope {
227
232
  export type CreateSymbolOptions<T extends OutputSymbol = OutputSymbol> = {
228
233
  name: string;
229
234
  scope?: OutputScope;
230
- refkey?: Refkey;
231
- refkeys?: Refkey[];
235
+ refkey?: Refkey | Refkey[];
232
236
  flags?: OutputSymbolFlags;
237
+ metadata?: Record<string, unknown>;
233
238
  } & Omit<T, keyof OutputSymbol>;
234
239
 
235
240
  export type CreateScopeOptions<T extends OutputScope = OutputScope> = {
@@ -238,6 +243,7 @@ export type CreateScopeOptions<T extends OutputScope = OutputScope> = {
238
243
  parent?: OutputScope | undefined;
239
244
  flags?: OutputScopeFlags;
240
245
  owner?: OutputSymbol;
246
+ metadata?: Record<string, unknown>;
241
247
  } & Omit<T, keyof OutputScope>;
242
248
 
243
249
  /**
@@ -370,15 +376,11 @@ export interface Binder {
370
376
  * When we resolve the refkey for `bar` from within `namespace scope 2`, we will get the following
371
377
  * resolution result:
372
378
  *
373
- * **targetDeclaration**: symbol bar, the symbol we resolved.
374
- *
375
- * **commonScope**: global scope, because this is the most specific scope that contains both the declaration and the reference.
376
- *
377
- * **pathUp**: [namespace scope 2], because this is the scope between the reference and the common scope.
378
- *
379
- * **pathDown**: [namespace scope 1], because this is the scope between the common scope and the declaration
380
- *
381
- * **memberPath**: [foo, bar], because we resolved a member symbol and these are the symbols that lead from the base declaration to the member symbol.
379
+ * * **targetDeclaration**: symbol bar, the symbol we resolved.
380
+ * * **commonScope**: global scope, because this is the most specific scope that contains both the declaration and the reference.
381
+ * * **pathUp**: [namespace scope 2], because this is the scope between the reference and the common scope.
382
+ * * **pathDown**: [namespace scope 1], because this is the scope between the common scope and the declaration
383
+ * * **memberPath**: [foo, bar], because we resolved a member symbol and these are the symbols that lead from the base declaration to the member symbol.
382
384
  */
383
385
  export interface ResolutionResult<
384
386
  TScope extends OutputScope,
@@ -467,6 +469,7 @@ export function createOutputBinder(options: BinderOptions = {}): Binder {
467
469
  parent,
468
470
  owner,
469
471
  flags = OutputScopeFlags.None,
472
+ metadata = {},
470
473
  ...rest
471
474
  } = args;
472
475
 
@@ -499,6 +502,7 @@ export function createOutputBinder(options: BinderOptions = {}): Binder {
499
502
  flags,
500
503
  owner,
501
504
  binder,
505
+ metadata,
502
506
  ...rest,
503
507
  getSymbolNames: symbolNames(symbols),
504
508
  }) as T;
@@ -525,19 +529,12 @@ export function createOutputBinder(options: BinderOptions = {}): Binder {
525
529
  name,
526
530
  scope = useDefaultScope(args.flags),
527
531
  refkey,
528
- refkeys,
529
532
  flags = OutputSymbolFlags.None,
533
+ metadata = {},
530
534
  ...rest
531
535
  } = args;
532
536
 
533
- const allRefkeys = [];
534
- if (refkey) {
535
- allRefkeys.push(refkey);
536
- }
537
-
538
- if (refkeys) {
539
- allRefkeys.push(...refkeys);
540
- }
537
+ const allRefkeys = [refkey ?? []].flat();
541
538
 
542
539
  if (!scope) {
543
540
  throw new Error(
@@ -570,6 +567,7 @@ export function createOutputBinder(options: BinderOptions = {}): Binder {
570
567
  refkeys: allRefkeys,
571
568
  binder,
572
569
  flags,
570
+ metadata,
573
571
  ...rest,
574
572
  }) as T;
575
573
 
@@ -634,8 +632,7 @@ export function createOutputBinder(options: BinderOptions = {}): Binder {
634
632
  createSymbol({
635
633
  name: sym.name,
636
634
  scope: target.instanceMemberScope!,
637
- // todo: fix this or change this or something??
638
- refkeys: [refkey(target.refkeys[0], sym.refkeys[0])],
635
+ refkey: [refkey(target.refkeys[0], sym.refkeys[0])],
639
636
  flags: sym.flags | OutputSymbolFlags.InstanceMember,
640
637
  });
641
638
  }
@@ -709,12 +706,6 @@ export function createOutputBinder(options: BinderOptions = {}): Binder {
709
706
  targetDeclarationBase: TSymbol,
710
707
  ): ResolutionResult<TScope, TSymbol> {
711
708
  if (targetDeclarationBase.flags & OutputSymbolFlags.InstanceMember) {
712
- if (targetDeclarationBase.scope !== currentMemberScope) {
713
- throw new Error(
714
- "Cannot resolve member symbols from a different member scope",
715
- );
716
- }
717
-
718
709
  // todo: handle referencing nested objects by refkey
719
710
  return {
720
711
  pathUp: [],
package/src/code.ts CHANGED
@@ -1,17 +1,51 @@
1
1
  // this code is split into a tokenizer and a parser of sorts because I feel like
2
- // it should be psosible to share logic between this and the babel transform, but
2
+ // it should be possible to share logic between this and the babel transform, but
3
3
  // this is an exercise for the future.
4
- import { hbr, indent } from "./components/stc/index.js";
4
+ import { hbr, indent } from "./components/stc/sti.js";
5
5
  import { Child, Children } from "./jsx-runtime.js";
6
+
7
+ export function text(
8
+ template: TemplateStringsArray,
9
+ ...substitutions: Children[]
10
+ ): Children {
11
+ const children = [];
12
+ // push the literal parts and the substitutions into the children array. The
13
+ // first part has all leading whitespace removed, the last part has all
14
+ // trailing whitespace removed, and each part in the middle replaces any
15
+ // amount of whitespace with a single space.
16
+
17
+ for (let i = 0; i < template.length; i++) {
18
+ let part = template[i];
19
+ part = part
20
+ .replace(/(^(\s*\r?\n\s*)+)|((\s*\r?\n\s*)+$)/g, "")
21
+ .replace(/(\s*\r?\n\s*)+/g, " ");
22
+ children.push(part);
23
+ if (i < substitutions.length) {
24
+ children.push(substitutions[i]);
25
+ }
26
+ }
27
+
28
+ return children;
29
+ }
30
+
6
31
  interface IndentLevelData {
7
32
  kind: "indent";
8
33
  children: (string | Children | IndentLevelData)[];
9
34
  pendingLines: string[];
10
35
  }
36
+
37
+ /**
38
+ * Turn the provided string template into Children by replacing literal line
39
+ * breaks with hardlines and automatically indenting indented content. Similar
40
+ * in spirit to the `<code>` element in HTML.
41
+ *
42
+ * @see {@link text} for a similar function which treats whitespace similar to
43
+ * JSX template bodies.
44
+ */
11
45
  export function code(
12
46
  template: TemplateStringsArray,
13
47
  ...substitutions: Children[]
14
- ) {
48
+ ): Children {
15
49
  const indentNodes: IndentLevelData[] = [
16
50
  {
17
51
  kind: "indent",
@@ -23,8 +23,8 @@ export interface BlockProps {
23
23
 
24
24
  /**
25
25
  * Create an indented block of source text. The block has `opener` text which is
26
- * added prior to the block, which defaults to "\{", and `closer` text which is
27
- * added after the block, which defaults to "\}".
26
+ * added prior to the block, which defaults to `"{"`, and `closer` text which is
27
+ * added after the block, which defaults to `"}"`.
28
28
  */
29
29
  export function Block(props: BlockProps) {
30
30
  const childCount = computed(() => childrenArray(() => props.children).length);
@@ -32,12 +32,9 @@ export function Block(props: BlockProps) {
32
32
  <group>
33
33
  {props.newline && <br />}
34
34
  {props.opener ?? "{"}
35
- <Indent break={childCount.value > 0 ? "hard" : "soft"}>
35
+ <Indent softline={childCount.value === 0} trailingBreak>
36
36
  {props.children}
37
37
  </Indent>
38
- {childCount.value > 0 ?
39
- <hbr />
40
- : <sbr />}
41
38
  {props.closer ?? "}"}
42
39
  </group>
43
40
  );
@@ -3,15 +3,48 @@ import { useContext } from "../context.js";
3
3
  import { BinderContext } from "../context/binder.js";
4
4
  import { DeclarationContext } from "../context/declaration.js";
5
5
  import { Children, onCleanup } from "../jsx-runtime.js";
6
- import { Refkey, refkey } from "../refkey.js";
6
+ import { Refkey } from "../refkey.js";
7
+
8
+ /**
9
+ * Create a declaration by providing an already created symbol. The symbol is
10
+ * merely exposed via {@link DeclarationContext}.
11
+ */
12
+ export interface DeclarationPropsWithSymbol {
13
+ /**
14
+ * The symbol being declared. When provided, the name, refkey, and metadata
15
+ * props are ignored.
16
+ */
17
+ symbol: OutputSymbol;
18
+
19
+ children?: Children;
20
+ }
21
+
22
+ /**
23
+ * Create a declaration by providing a symbol name and optional symbol metadata.
24
+ */
25
+ export interface DeclarationPropsWithInfo {
26
+ /**
27
+ * The name of this declaration.
28
+ */
29
+ name: string;
30
+
31
+ /**
32
+ * The unique key or array of unique keys for this declaration.
33
+ */
34
+ refkey?: Refkey | Refkey[];
35
+
36
+ /**
37
+ * Additional metadata for the declared symbol.
38
+ */
39
+ metadata?: Record<string, unknown>;
7
40
 
8
- export interface DeclarationProps {
9
- name?: string;
10
- refkey?: Refkey;
11
- symbol?: OutputSymbol;
12
41
  children?: Children;
13
42
  }
14
43
 
44
+ export type DeclarationProps =
45
+ | DeclarationPropsWithSymbol
46
+ | DeclarationPropsWithInfo;
47
+
15
48
  /**
16
49
  * Declares a symbol in the current scope for this component's children.
17
50
  *
@@ -20,11 +53,10 @@ export interface DeclarationProps {
20
53
  * This component must be called in one of two ways: with a name and an optional
21
54
  * refkey, or else passing in the symbol. When called with a name and refkey, a
22
55
  * symbol will be created in the current scope (provided by
23
- * {@link ScopeContext}) with that name and refkey. If a refkey is not provided,
24
- * `refkey(props.name)` is used.
56
+ * {@link ScopeContext}) with that name and refkey.
25
57
  *
26
58
  * When called with a symbol, that symbol is merely exposed via
27
- * {@link DeclarationContext }. It is assumed that the caller of this component
59
+ * {@link DeclarationContext}. It is assumed that the caller of this component
28
60
  * has created the symbol with the `createSymbol` API on the
29
61
  * {@link BinderContext }.
30
62
  *
@@ -37,13 +69,13 @@ export function Declaration(props: DeclarationProps) {
37
69
  }
38
70
 
39
71
  let declaration;
40
- if (props.symbol) {
72
+ if ("symbol" in props) {
41
73
  declaration = props.symbol;
42
74
  } else {
43
- const rk = props.refkey ? props.refkey : refkey(props.name);
44
75
  declaration = binder.createSymbol({
45
76
  name: props.name!,
46
- refkey: rk,
77
+ refkey: props.refkey,
78
+ metadata: props.metadata,
47
79
  });
48
80
 
49
81
  onCleanup(() => {
@@ -1,7 +1,7 @@
1
1
  import { Children, memo } from "@alloy-js/core/jsx-runtime";
2
2
  import { isRef, Ref } from "@vue/reactivity";
3
- import { mapJoin } from "../utils.js";
4
- import { BaseListProps, baseListPropsToMapJoinArgs } from "./List.jsx";
3
+ import { baseListPropsToMapJoinArgs, mapJoin } from "../utils.js";
4
+ import { BaseListProps } from "./List.jsx";
5
5
 
6
6
  export type ForCallbackArgs<T> =
7
7
  T extends Ref<infer U> ? ForCallbackArgs<U>
@@ -27,6 +27,13 @@ export interface ForProps<
27
27
  * A function to call for each item.
28
28
  */
29
29
  children: (...args: [...ForCallbackArgs<T>, index: number]) => U;
30
+
31
+ /**
32
+ * Whether to skip falsy values. By default, falsy values are mapped. However,
33
+ * when mapping children, it is useful to skip falsy values, as it enables
34
+ * omitting list elements via patterns like `{condition && <ListItem />}`.
35
+ */
36
+ skipFalsy?: boolean;
30
37
  }
31
38
 
32
39
  export type ForSupportedCollections = any[] | Map<any, any> | Set<any>;
@@ -66,7 +73,7 @@ export function For<
66
73
  >(props: ForProps<T, U>) {
67
74
  const cb = props.children;
68
75
  const options = baseListPropsToMapJoinArgs(props);
69
- options.skipFalsy = true;
76
+ options.skipFalsy = props.skipFalsy;
70
77
  return memo(() => {
71
78
  const maybeRef = props.each;
72
79
 
@@ -2,17 +2,50 @@ import { Children } from "@alloy-js/core/jsx-runtime";
2
2
 
3
3
  export interface IndentProps {
4
4
  children: Children;
5
+ /**
6
+ * Don't include a line break. The new indentation level will start after the
7
+ * first linebreak within the children.
8
+ */
5
9
  nobreak?: boolean;
6
- break?: "space" | "soft" | "hard";
10
+
11
+ /**
12
+ * Use a regular line (`<br />`) to start (and optionally end with
13
+ * `trailingBreak`) the new indentation level.
14
+ */
15
+ line?: boolean;
16
+
17
+ /**
18
+ * Use a soft line (`<sbr />`) to start (and optionally end with
19
+ * `trailingBreak`) the new indentation level.
20
+ */
21
+ softline?: boolean;
22
+
23
+ /**
24
+ * Use a hard line (`<hbr />`) to start (and optionally end with
25
+ * `trailingBreak`) the new indentation level.
26
+ */
27
+ hardline?: boolean;
28
+
29
+ /**
30
+ * Place the configured line break at the end of the block after restoring the
31
+ * indentation level.
32
+ */
7
33
  trailingBreak?: boolean;
8
34
  }
35
+
36
+ /**
37
+ * Create an indented block of source text. The indented block starts a new line
38
+ * at the new indentation level and, with `trailingBreak`, ends with a new line
39
+ * after restoring the indentation level. The default line break is a hard line
40
+ * break suitable for typical blocks of statements but can be configured.
41
+ */
9
42
  export function Indent(props: IndentProps) {
10
- const breakStyle = props.break ?? "hard";
11
43
  const breakElem =
12
44
  props.nobreak ? ""
13
- : breakStyle === "hard" ? <hbr />
14
- : breakStyle === "soft" ? <sbr />
15
- : <br />;
45
+ : props.hardline ? <hbr />
46
+ : props.softline ? <sbr />
47
+ : props.line ? <br />
48
+ : <hbr />;
16
49
 
17
50
  return (
18
51
  <>
@@ -1,9 +1,7 @@
1
1
  import { Children, memo, splitProps } from "@alloy-js/core/jsx-runtime";
2
- import { childrenArray, JoinOptions } from "../utils.js";
2
+ import { childrenArray } from "../utils.js";
3
3
  import { For } from "./For.jsx";
4
4
 
5
- export type BreakKind = "none" | "space" | "soft" | "hard" | "literal";
6
-
7
5
  export interface BaseListProps {
8
6
  /** Text to place between each element */
9
7
  joiner?: Children;
@@ -14,10 +12,22 @@ export interface BaseListProps {
14
12
  /** Place a semicolon between each element */
15
13
  semicolon?: boolean;
16
14
 
15
+ /** Place a regular line (`<br />`) between each element */
17
16
  line?: boolean;
17
+
18
+ /** Place a softline (`<sbr />`) between each element */
18
19
  softline?: boolean;
20
+
21
+ /** Place a hardline (`<hbr />`) between each element */
19
22
  hardline?: boolean;
23
+
24
+ /** Place two hardlines between each element */
25
+ doubleHardline?: boolean;
26
+
27
+ /** Place a literal line (`<lbr />`) between each element */
20
28
  literalline?: boolean;
29
+
30
+ /** Place a space between each element */
21
31
  space?: boolean;
22
32
 
23
33
  /**
@@ -32,42 +42,6 @@ export interface BaseListProps {
32
42
  enderPunctuation?: boolean;
33
43
  }
34
44
 
35
- export function baseListPropsToMapJoinArgs(props: BaseListProps): JoinOptions {
36
- let joiner, punctuation;
37
- if ("joiner" in props) {
38
- joiner = props.joiner;
39
- } else {
40
- punctuation =
41
- props.comma ? ","
42
- : props.semicolon ? ";"
43
- : "";
44
-
45
- joiner = (
46
- <>
47
- {punctuation}
48
- {props.softline ?
49
- <sbr />
50
- : props.hardline ?
51
- <hbr />
52
- : props.literalline ?
53
- <lbr />
54
- : props.line ?
55
- <br />
56
- : props.space ?
57
- <> </>
58
- : <hbr />}
59
- </>
60
- );
61
- }
62
-
63
- const ender =
64
- "ender" in props ? props.ender
65
- : props.enderPunctuation ? punctuation
66
- : undefined;
67
-
68
- return { joiner, ender };
69
- }
70
-
71
45
  export interface ListProps extends BaseListProps {
72
46
  children?: Children;
73
47
  }
@@ -87,7 +61,7 @@ export function List(props: ListProps) {
87
61
  }),
88
62
  );
89
63
  return (
90
- <For each={resolvedChildren} {...forProps}>
64
+ <For each={resolvedChildren} {...forProps} skipFalsy>
91
65
  {(child) => child}
92
66
  </For>
93
67
  );
@@ -3,16 +3,54 @@ import { useContext } from "../context.js";
3
3
  import { BinderContext } from "../context/binder.js";
4
4
  import { MemberDeclarationContext } from "../context/member-declaration.js";
5
5
  import { Children } from "../jsx-runtime.js";
6
- import { Refkey, refkey } from "../refkey.js";
6
+ import { Refkey } from "../refkey.js";
7
7
 
8
- export interface MemberDeclarationProps {
9
- name?: string;
10
- refkey?: Refkey;
11
- symbol?: OutputSymbol;
12
- children?: Children;
8
+ /**
9
+ * Create a member declaration by providing a symbol name and optional symbol
10
+ * metadata.
11
+ */
12
+ export interface MemberDeclarationPropsWithInfo {
13
+ /**
14
+ * The name of this declaration.
15
+ */
16
+ name: string;
17
+
18
+ /**
19
+ * The refkey or array refkeys for this declaration.
20
+ */
21
+ refkey?: Refkey | Refkey[];
22
+
23
+ /**
24
+ * Additional metadata for the declared symbol.
25
+ */
26
+ metadata?: Record<string, unknown>;
27
+
28
+ /**
29
+ * Whether this is a static member. If not provided, the member is an instance
30
+ * member.
31
+ */
13
32
  static?: boolean;
33
+ children?: Children;
14
34
  }
15
35
 
36
+ /**
37
+ * Create a declaration by providing an already created symbol. The symbol is
38
+ * merely exposed via {@link DeclarationContext}.
39
+ */
40
+ export interface MemberDeclarationPropsWithSymbol {
41
+ /**
42
+ * The symbol being declared. When provided, the name, refkey, and metadata
43
+ * props are ignored.
44
+ */
45
+ symbol: OutputSymbol;
46
+
47
+ children?: Children;
48
+ }
49
+
50
+ export type MemberDeclarationProps =
51
+ | MemberDeclarationPropsWithInfo
52
+ | MemberDeclarationPropsWithSymbol;
53
+
16
54
  /**
17
55
  * Declares a symbol in the current member scope for this component's children.
18
56
  *
@@ -38,20 +76,21 @@ export function MemberDeclaration(props: MemberDeclarationProps) {
38
76
  }
39
77
 
40
78
  let declaration;
41
- if (props.symbol) {
79
+ if ("symbol" in props && props.symbol) {
42
80
  declaration = props.symbol;
43
81
  } else {
44
- if (!props.name) {
82
+ const infoProps = props as MemberDeclarationPropsWithInfo;
83
+ if (!infoProps.name) {
45
84
  throw new Error(
46
85
  "Must provide a member name, or else provide a member symbol",
47
86
  );
48
87
  }
49
- const rk = props.refkey ? props.refkey : refkey(props.name);
50
88
  declaration = binder.createSymbol({
51
- name: props.name!,
52
- refkey: rk,
89
+ name: infoProps.name!,
90
+ refkey: infoProps.refkey,
91
+ metadata: infoProps.metadata,
53
92
  flags:
54
- props.static ?
93
+ infoProps.static ?
55
94
  OutputSymbolFlags.StaticMember
56
95
  : OutputSymbolFlags.InstanceMember,
57
96
  });
@@ -28,6 +28,8 @@ export interface MemberScopeProps {
28
28
  *
29
29
  * The member scope contains scopes for both instance and static members.
30
30
  * However, it does not affect the resolution of static members.
31
+ *
32
+ * @see {@link (MemberScopeContext:variable)}
31
33
  */
32
34
  export function MemberScope(props: MemberScopeProps) {
33
35
  const context: MemberScopeContext = {
@@ -0,0 +1,35 @@
1
+ import { childrenArray, computed } from "@alloy-js/core";
2
+ import { Children } from "@alloy-js/core/jsx-runtime";
3
+
4
+ export interface Prose {
5
+ children: Children;
6
+ }
7
+
8
+ /**
9
+ * Create a block of text which will break once a word exceeds the configured line width.
10
+ * The children are expected to be strings, and a <br /> is added between each word.
11
+ */
12
+ export function Prose(props: Prose) {
13
+ const brokenChildren = computed(() => {
14
+ const children = childrenArray(() => props.children);
15
+ return children
16
+ .map((child) => {
17
+ if (typeof child === "string") {
18
+ return child
19
+ .trim()
20
+ .split(/\s+/)
21
+ .map((word) => (
22
+ <>
23
+ {word}
24
+ <br />
25
+ </>
26
+ ));
27
+ }
28
+
29
+ return child;
30
+ })
31
+ .flat(2);
32
+ });
33
+
34
+ return <fill>{brokenChildren.value}</fill>;
35
+ }
@@ -4,21 +4,61 @@ import { BinderContext } from "../context/binder.js";
4
4
  import { ScopeContext } from "../context/scope.js";
5
5
  import { Children } from "../jsx-runtime.js";
6
6
 
7
- export interface ScopeProps {
7
+ /**
8
+ * Declare a scope by providing an already created scope. The scope is merely
9
+ * exposed via {@link ScopeContext}.
10
+ */
11
+ export interface ScopePropsWithValue {
12
+ /**
13
+ * The scope to use. If not provided, a new scope will be created.
14
+ */
15
+ value: OutputScope;
16
+
17
+ children?: Children;
18
+ }
19
+
20
+ /**
21
+ * Create a scope by providing a name and optional metadata.
22
+ */
23
+ export interface ScopePropsWithInfo {
24
+ /**
25
+ * The kind of scope. This may be used by application code to determine how
26
+ * to handle symbols in this scope. It is not used by the core framework.
27
+ */
8
28
  kind?: string;
29
+
30
+ /**
31
+ * The name of this scope.
32
+ */
9
33
  name?: string;
10
- value?: OutputScope;
34
+
35
+ /**
36
+ * Additional metadata for the scope.
37
+ */
38
+ metadata?: Record<string, unknown>;
39
+
11
40
  children?: Children;
12
41
  }
13
42
 
43
+ export type ScopeProps = ScopePropsWithValue | ScopePropsWithInfo;
44
+
45
+ /**
46
+ * Declare a scope for this component's children. Any symbols and scopes
47
+ * declared in the children of this component will be in this scope.
48
+ *
49
+ * @see {@link ScopeContext}
50
+ */
14
51
  export function Scope(props: ScopeProps) {
15
52
  let scope: OutputScope;
16
- if (props.value) {
53
+ if ("value" in props) {
17
54
  scope = props.value;
18
55
  } else {
19
- const kind = props.kind ?? "file";
20
56
  const binder = useContext(BinderContext)!;
21
- scope = binder.createScope({ kind, name: props.name! });
57
+ scope = binder.createScope({
58
+ kind: props.kind,
59
+ metadata: props.metadata,
60
+ name: props.name ?? "",
61
+ });
22
62
  }
23
63
 
24
64
  return (