@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
@@ -5,6 +5,7 @@ import { SourceFileContext } from "../context/source-file.js";
5
5
  import { Children, ComponentDefinition, getContext } from "../jsx-runtime.js";
6
6
  import { Refkey } from "../refkey.js";
7
7
  import { PrintTreeOptions } from "../render.js";
8
+ import { Show } from "./Show.jsx";
8
9
 
9
10
  export interface SourceFileProps extends PrintTreeOptions {
10
11
  /**
@@ -24,6 +25,11 @@ export interface SourceFileProps extends PrintTreeOptions {
24
25
  * contents.
25
26
  */
26
27
  reference?: ComponentDefinition<{ refkey: Refkey }>;
28
+ /**
29
+ * The header of the file. This is rendered before the contents of the file.
30
+ * This is useful for adding license headers or other metadata to the file.
31
+ */
32
+ header?: Children;
27
33
  }
28
34
 
29
35
  export function SourceFile(props: SourceFileProps) {
@@ -45,6 +51,10 @@ export function SourceFile(props: SourceFileProps) {
45
51
 
46
52
  return (
47
53
  <SourceFileContext.Provider value={context}>
54
+ <Show when={props.header !== undefined}>
55
+ {props.header}
56
+ <hbr />
57
+ </Show>
48
58
  {props.children}
49
59
  </SourceFileContext.Provider>
50
60
  );
@@ -8,6 +8,7 @@ export * from "./MemberName.jsx";
8
8
  export * from "./MemberScope.jsx";
9
9
  export * from "./Name.jsx";
10
10
  export * from "./Output.js";
11
+ export * from "./Prose.jsx";
11
12
  export * from "./Scope.js";
12
13
  export * from "./Show.jsx";
13
14
  export * from "./SourceDirectory.js";
@@ -1,4 +1,4 @@
1
- import { stc, sti } from "../../stc.js";
1
+ import { stc } from "../../stc.js";
2
2
  import * as base from "../index.js";
3
3
 
4
4
  export const Block = stc(base.Block);
@@ -11,6 +11,7 @@ export const MemberName = stc(base.MemberName);
11
11
  export const MemberScope = stc(base.MemberScope);
12
12
  export const Name = stc(base.Name);
13
13
  export const Output = stc(base.Output);
14
+ export const Prose = stc(base.Prose);
14
15
  export const Scope = stc(base.Scope);
15
16
  export const Show = stc(base.Show);
16
17
  export const StatementList = stc(base.StatementList);
@@ -19,8 +20,4 @@ export const SourceFile = stc(base.SourceFile);
19
20
  export const Switch = stc(base.Switch);
20
21
  export const Wrap = stc(base.Wrap);
21
22
 
22
- export const indent = sti("indent");
23
- export const hbr = sti("hbr");
24
- export const sbr = sti("sbr");
25
- export const lbr = sti("lbr");
26
- export const br = sti("br");
23
+ export * from "./sti.js";
@@ -0,0 +1,10 @@
1
+ import { sti } from "../../sti.js";
2
+
3
+ export const indent = sti("indent");
4
+ export const group = sti("group");
5
+ export const ifBreak = sti("ifBreak");
6
+ export const hbr = sti("hbr");
7
+ export const sbr = sti("sbr");
8
+ export const lbr = sti("lbr");
9
+ export const br = sti("br");
10
+ export const dedentToRoot = sti("dedentToRoot");
@@ -52,7 +52,13 @@ export function createAssignmentContext(
52
52
  };
53
53
  }
54
54
 
55
- export function getAssignmentSymbol() {
55
+ /**
56
+ * Get the symbol being defined.
57
+ *
58
+ * @returns The symbol currently being defined, or `undefined` if no symbol is
59
+ * being defined.
60
+ */
61
+ export function getAssignmentSymbol(): OutputSymbol | undefined {
56
62
  const assignmentContext = useContext(AssignmentContext);
57
63
  if (assignmentContext) {
58
64
  return assignmentContext.target;
package/src/context.ts CHANGED
@@ -5,11 +5,13 @@ import {
5
5
  effect,
6
6
  getContext,
7
7
  } from "./jsx-runtime.js";
8
+ import { stc, StcSignature } from "./stc.js";
8
9
 
9
10
  export interface ComponentContext<T> {
10
11
  id: symbol;
11
12
  default: T | undefined;
12
13
  Provider: ComponentDefinition<ContextProviderProps<T>>;
14
+ ProviderStc: StcSignature<ContextProviderProps<T>>;
13
15
  name?: string;
14
16
  }
15
17
 
@@ -38,21 +40,23 @@ export function createContext<T = unknown>(
38
40
  name?: string,
39
41
  ): ComponentContext<T> {
40
42
  const id = Symbol(name ?? "context");
43
+ function Provider(props: ContextProviderProps<T>) {
44
+ const context = getContext();
45
+
46
+ const rendered = shallowRef();
47
+ effect(() => {
48
+ context!.context![id] = props.value;
49
+ rendered.value = () => props.children;
50
+ }, undefined);
51
+
52
+ return rendered.value;
53
+ }
41
54
  const ctx = {
42
55
  id,
43
56
  default: defaultValue,
44
57
  name,
45
- Provider(props: ContextProviderProps<T>) {
46
- const context = getContext();
47
-
48
- const rendered = shallowRef();
49
- effect(() => {
50
- context!.context![id] = props.value;
51
- rendered.value = () => props.children;
52
- }, undefined);
53
-
54
- return rendered.value;
55
- },
58
+ Provider,
59
+ ProviderStc: stc(Provider),
56
60
  };
57
61
  contextsByKey.set(id, ctx);
58
62
  return ctx;
package/src/index.ts CHANGED
@@ -18,6 +18,7 @@ export * from "./name-policy.js";
18
18
  export * from "./refkey.js";
19
19
  export * from "./render.js";
20
20
  export * from "./stc.js";
21
+ export * from "./sti.js";
21
22
  export * from "./tap.js";
22
23
  export * from "./utils.js";
23
24
  export * from "./write-output.js";
@@ -1,6 +1,7 @@
1
1
  // Much of the implementations in this file are inspired by vuerx-js
2
2
  // See: https://github.com/ryansolid/vuerx-jsx.
3
3
  import {
4
+ computed,
4
5
  isReactive,
5
6
  pauseTracking,
6
7
  proxyRefs,
@@ -294,27 +295,131 @@ export function isComponentCreator(item: unknown): item is ComponentCreator {
294
295
  // eslint-disable-next-line @typescript-eslint/no-namespace
295
296
  export namespace JSX {
296
297
  export interface IntrinsicElements {
298
+ /**
299
+ * Attempt to render the children on a single line if possible. If a group
300
+ * contains `<breakParent />` or a hard line, or if the group exceeds the
301
+ * print width, all linebreaks in the group will be broken.
302
+ */
297
303
  group: { shouldBreak?: boolean; id?: symbol; children: Children };
304
+
305
+ /**
306
+ * A regular line break. This will break if the line exceeds the print
307
+ * width, otherwise it will be a space.
308
+ */
298
309
  line: {};
310
+
311
+ /**
312
+ * A regular line break. This will break if the line exceeds the print
313
+ * width, otherwise it will be a space.
314
+ */
299
315
  br: {};
316
+
317
+ /**
318
+ * A hard line break. This is a line that will always break, even if the
319
+ * group does not exceed print width.
320
+ */
300
321
  hardline: {};
322
+
323
+ /**
324
+ * A hard line break. This is a line that will always break, even if the
325
+ * group does not exceed print width.
326
+ */
301
327
  hbr: {};
328
+
329
+ /**
330
+ * A soft line break. This will break if the line exceeds the print width,
331
+ * otherwise it will be be nothing.
332
+ */
302
333
  softline: {};
334
+
335
+ /**
336
+ * A soft line break. This will break if the line exceeds the print width,
337
+ * otherwise it will be be nothing.
338
+ */
303
339
  sbr: {};
340
+
341
+ /**
342
+ * A literal line break. This will always break, even if the group does not
343
+ * exceed print width. The new line will ignore indentation.
344
+ */
304
345
  literalline: {};
346
+
347
+ /**
348
+ * A literal line break. This will always break, even if the group does not
349
+ * exceed print width. The new line will ignore indentation.
350
+ */
305
351
  lbr: {};
352
+
353
+ /**
354
+ * Increase the indentation level of the children of this component.
355
+ * Indentation is determined by the print options provided to the Output
356
+ * component or source file.
357
+ */
306
358
  indent: { children: Children };
359
+
360
+ /**
361
+ * Indent the children of this component if the group specified by `groupId`
362
+ * is broken (or not broken if `negate` is passed). The specified group must
363
+ * already be printed.
364
+ */
307
365
  indentIfBreak: { children: Children; groupId: symbol; negate?: boolean };
366
+
367
+ /**
368
+ * Similar to `group`, but will only place a line break before the last
369
+ * segment to exceed the print width. This is useful for formatting
370
+ * paragraphs of text where breaks are inserted prior to words which would
371
+ * otherwise exceed the print width.
372
+ */
308
373
  fill: { children: Children };
374
+
375
+ /**
376
+ * Force the parent group to break.
377
+ */
309
378
  breakParent: {};
379
+
380
+ /**
381
+ * Print children if the current group or already printed group specified by
382
+ * `groupId` is broken. Otherwise, `flatContents` is printed instead.
383
+ */
310
384
  ifBreak: { children: Children; flatContents?: Children; groupId?: symbol };
385
+
386
+ /**
387
+ * Print this content at the end of the line. Useful for things like line
388
+ * comments.
389
+ */
311
390
  lineSuffix: { children: Children };
391
+
392
+ /**
393
+ * Force any line suffixes to print at this point.
394
+ */
312
395
  lineSuffixBoundary: {};
396
+
397
+ /**
398
+ * Decrease the indentation level of the children of this component.
399
+ * Indentation is determined by the print options provided to the Output
400
+ * component or source file.
401
+ */
313
402
  dedent: { children: Children };
403
+
404
+ /**
405
+ * Indent the children of this component by either the number of characters
406
+ * indicated by the `width` prop, or by the provided string indicated by the
407
+ * `string` prop.
408
+ */
314
409
  align:
315
410
  | { children: Children; width: number }
316
411
  | { children: Children; string: string };
412
+
413
+ /**
414
+ * Mark the current indentation level as "root" for the purposes of literal
415
+ * line breaks and `dedentToRoot`.
416
+ */
317
417
  markAsRoot: { children: Children };
418
+
419
+ /**
420
+ * Decrease the indentation level to the root level specified by
421
+ * `<markAsRoot />`, or else to no indentation.
422
+ */
318
423
  dedentToRoot: { children: Children };
319
424
  }
320
425
  export type ElementType = string | ComponentDefinition<any>;
@@ -518,6 +623,59 @@ export function splitProps<
518
623
  return [...result, remaining] as any;
519
624
  }
520
625
 
626
+ /**
627
+ * Applies default values to a props object. Reactive props are handled properly
628
+ * by ensuring that their value is not accessed by `defaultProps`, avoiding any
629
+ * unintended side effects.
630
+ */
631
+ export function defaultProps<T extends {}>(props: T, defaults: Partial<T>): T {
632
+ if (isReactive(props)) {
633
+ const refs = untrack(() => toRefs(props));
634
+ for (const key in defaults) {
635
+ const originalRef = refs[key];
636
+ refs[key] = computed(() =>
637
+ originalRef.value === undefined ? defaults[key] : originalRef.value,
638
+ ) as any;
639
+ }
640
+
641
+ return proxyRefs(refs);
642
+ }
643
+ const withDefaults = {} as T;
644
+ const copied = new Set<string>();
645
+ for (const key in defaults) {
646
+ copied.add(key);
647
+ const desc = Object.getOwnPropertyDescriptor(props, key);
648
+ if (!desc) {
649
+ withDefaults[key] = defaults[key]!;
650
+ continue;
651
+ }
652
+
653
+ if (desc.get) {
654
+ const originalGet = desc.get;
655
+ desc.get = function () {
656
+ const value = originalGet.call(this);
657
+ return value === undefined ? defaults[key as keyof T] : value;
658
+ };
659
+ Object.defineProperty(withDefaults, key, desc);
660
+ } else {
661
+ desc.value =
662
+ desc.value === undefined ? defaults[key as keyof T] : desc.value;
663
+ Object.defineProperty(withDefaults, key, desc);
664
+ }
665
+ }
666
+
667
+ const descriptors = Object.getOwnPropertyDescriptors(props);
668
+ for (const key in descriptors) {
669
+ if (copied.has(key)) {
670
+ continue;
671
+ }
672
+
673
+ Object.defineProperty(withDefaults, key, descriptors[key]);
674
+ }
675
+
676
+ return withDefaults;
677
+ }
678
+
521
679
  function shouldDebug() {
522
680
  return typeof process !== "undefined" && !!process.env?.ALLOY_DEBUG;
523
681
  }
package/src/stc.ts CHANGED
@@ -2,71 +2,40 @@ import {
2
2
  Children,
3
3
  ComponentCreator,
4
4
  ComponentDefinition,
5
- createIntrinsic,
6
- IntrinsicElementBase,
7
- JSX,
8
5
  } from "@alloy-js/core/jsx-runtime";
9
- import { code } from "./code.js";
10
-
11
- export function sti<T extends keyof JSX.IntrinsicElements>(name: T) {
12
- return (
13
- ...args: unknown extends T ? []
14
- : {} extends Omit<JSX.IntrinsicElements[T], "children"> ?
15
- [props?: JSX.IntrinsicElements[T]]
16
- : [props: JSX.IntrinsicElements[T]]
17
- ) => {
18
- const props: JSX.IntrinsicElements[T] | undefined = args[0];
19
- const fn = () => createIntrinsic(name, props!);
20
- fn.children = (
21
- ...children: Children[]
22
- ): (() => IntrinsicElementBase<T>) => {
23
- const propsWithChildren = {
24
- ...(props ?? {}),
25
- children,
26
- };
27
-
28
- return () => createIntrinsic(name, propsWithChildren as any);
29
- };
30
-
31
- fn.code = (
32
- template: TemplateStringsArray,
33
- ...substitutions: Children[]
34
- ): (() => IntrinsicElementBase<T>) => {
35
- const propsWithChildren = {
36
- ...(args[0] ?? {}),
37
- children: code(template, ...substitutions),
38
- };
39
-
40
- return () => createIntrinsic(name, propsWithChildren as any);
41
- };
42
- return fn;
43
- };
44
- }
6
+ import { code, text } from "./code.js";
45
7
 
46
8
  export type MakeChildrenOptional<T extends object> =
47
9
  T extends { children?: any } ?
48
10
  Omit<T, "children"> & Partial<Pick<T, "children">>
49
11
  : T;
50
12
 
51
- export function stc<T extends {}>(Component: ComponentDefinition<T>) {
52
- return (
53
- ...args: unknown extends T ? []
54
- : {} extends Omit<T, "children"> ? [props?: MakeChildrenOptional<T>]
55
- : [props: MakeChildrenOptional<T>]
56
- ) => {
57
- const fn: ComponentCreator<T> & {
58
- code(
59
- template: TemplateStringsArray,
60
- ...substitutions: Children[]
61
- ): ComponentCreator<T>;
62
- children(...children: Children[]): ComponentCreator<T>;
63
- } = () => Component(args[0] as any);
13
+ export type StcSignature<T extends {}> = (
14
+ ...args: unknown extends T ? []
15
+ : {} extends Omit<T, "children"> ? [props?: MakeChildrenOptional<T>]
16
+ : [props: MakeChildrenOptional<T>]
17
+ ) => StcComponentCreator<T>;
18
+
19
+ export type StcComponentCreator<T> = ComponentCreator<T> & {
20
+ code(
21
+ template: TemplateStringsArray,
22
+ ...substitutions: Children[]
23
+ ): ComponentCreator<T>;
24
+ text(
25
+ template: TemplateStringsArray,
26
+ ...substitutions: Children[]
27
+ ): ComponentCreator<T>;
28
+ children(...children: Children[]): ComponentCreator<T>;
29
+ };
30
+
31
+ export function stc<T extends {}>(
32
+ Component: ComponentDefinition<T>,
33
+ ): StcSignature<T> {
34
+ return (...args) => {
35
+ const fn: StcComponentCreator<T> = () => Component(args[0] as any);
64
36
  fn.component = Component;
65
37
  fn.props = args[0]!;
66
- fn.code = (
67
- template: TemplateStringsArray,
68
- ...substitutions: Children[]
69
- ): ComponentCreator<T> => {
38
+ fn.code = (template, ...substitutions): ComponentCreator<T> => {
70
39
  const propsWithChildren = {
71
40
  ...(args[0] ?? {}),
72
41
  children: code(template, ...substitutions),
@@ -77,7 +46,17 @@ export function stc<T extends {}>(Component: ComponentDefinition<T>) {
77
46
  fn.props = args[0]!;
78
47
  return fn;
79
48
  };
49
+ fn.text = (template, ...substitutions) => {
50
+ const propsWithChildren = {
51
+ ...(args[0] ?? {}),
52
+ children: text(template, ...substitutions),
53
+ };
80
54
 
55
+ const fn = () => Component(propsWithChildren as any);
56
+ fn.component = Component;
57
+ fn.props = args[0]!;
58
+ return fn;
59
+ };
81
60
  fn.children = (...children: Children[]): ComponentCreator<T> => {
82
61
  const propsWithChildren = {
83
62
  ...(args[0] ?? {}),
package/src/sti.ts ADDED
@@ -0,0 +1,63 @@
1
+ import {
2
+ Children,
3
+ createIntrinsic,
4
+ IntrinsicElementBase,
5
+ JSX,
6
+ } from "@alloy-js/core/jsx-runtime";
7
+ import { code, text } from "./code.js";
8
+
9
+ export type StiSignature<T extends keyof JSX.IntrinsicElements> = (
10
+ ...args: unknown extends T ? []
11
+ : {} extends Omit<JSX.IntrinsicElements[T], "children"> ?
12
+ [props?: JSX.IntrinsicElements[T]]
13
+ : [props: JSX.IntrinsicElements[T]]
14
+ ) => StiComponentCreator<T>;
15
+
16
+ export type StiComponentCreator<T extends keyof JSX.IntrinsicElements> =
17
+ (() => IntrinsicElementBase<T>) & {
18
+ code(
19
+ template: TemplateStringsArray,
20
+ ...substitutions: Children[]
21
+ ): () => IntrinsicElementBase<T>;
22
+ text(
23
+ template: TemplateStringsArray,
24
+ ...substitutions: Children[]
25
+ ): () => IntrinsicElementBase<T>;
26
+ children(...children: Children[]): () => IntrinsicElementBase<T>;
27
+ };
28
+
29
+ export function sti<T extends keyof JSX.IntrinsicElements>(
30
+ name: T,
31
+ ): StiSignature<T> {
32
+ return (...args) => {
33
+ const props: JSX.IntrinsicElements[T] | undefined = args[0];
34
+ const fn: StiComponentCreator<T> = () => createIntrinsic(name, props!);
35
+ fn.children = (...children: Children[]) => {
36
+ const propsWithChildren = {
37
+ ...(props ?? {}),
38
+ children,
39
+ };
40
+
41
+ return () => createIntrinsic(name, propsWithChildren as any);
42
+ };
43
+
44
+ fn.code = (template, ...substitutions) => {
45
+ const propsWithChildren = {
46
+ ...(args[0] ?? {}),
47
+ children: code(template, ...substitutions),
48
+ };
49
+
50
+ return () => createIntrinsic(name, propsWithChildren as any);
51
+ };
52
+
53
+ fn.text = (template, ...substitutions) => {
54
+ const propsWithChildren = {
55
+ ...(args[0] ?? {}),
56
+ children: text(template, ...substitutions),
57
+ };
58
+
59
+ return () => createIntrinsic(name, propsWithChildren as any);
60
+ };
61
+ return fn;
62
+ };
63
+ }
package/src/tap.ts CHANGED
@@ -7,21 +7,72 @@ import { useScope } from "./context/scope.js";
7
7
  import { SourceFileContext } from "./context/source-file.js";
8
8
  import { ComponentDefinition } from "./jsx-runtime.js";
9
9
 
10
+ /**
11
+ * The return value of {@link createTap}, this holds a reference to the tapped
12
+ * value. It will be undefined until the tapped value is initialized.
13
+ */
10
14
  export interface Tap<T> extends ComponentDefinition {
15
+ /** Ref for the tapped value */
11
16
  ref: ShallowRef<T | undefined>;
12
17
  }
13
18
 
19
+ /**
20
+ * A function called when the Tap is rendered.
21
+ *
22
+ * @returns The tapped value.
23
+ */
14
24
  export interface Tapper<T> {
15
25
  (): T | undefined;
16
26
  }
17
27
 
18
- export interface Handler<T> {
28
+ /**
29
+ * A function that is called when the tapped value is available.
30
+ */
31
+ export interface TapHandler<T> {
19
32
  (value: T): void;
20
33
  }
21
34
 
35
+ /**
36
+ * Create a component that when rendered, initializes the tapped value
37
+ * with the provided callback. This is useful for accessing context
38
+ * provided by child components inside a parent component.
39
+ *
40
+ * @example
41
+ * ```tsx
42
+ * import { type Children, computed, createTap } from "@alloy-js/core";
43
+ *
44
+ * // context we will tap into
45
+ * const SomeContext = createContext<string>();
46
+ *
47
+ * // a component which provides some specific context
48
+ * function MyDeclaration(props: { children: Children }) {
49
+ * return <SomeContext.Provider value="Hello World">
50
+ * {props.children}
51
+ * </SomeContext.Provider>;
52
+ * }
53
+ *
54
+ * // a parent component which wants to know about the context set
55
+ * // by its children
56
+ * function MySpecialDeclaration() {
57
+ * const SomeContextTap = createTap(() => useContext(SomeContext));
58
+ *
59
+ * return <>
60
+ * The declaration context is: {SomeContextTap.ref}
61
+ * <MyDeclaration>
62
+ * <SomeContextTap />
63
+ * </MyDeclaration>
64
+ * </>
65
+ * }
66
+ *
67
+ * @see {@link createDeclarationTap} for tapping {@link DeclarationContext}.
68
+ * @see {@link createMemberTap} for tapping {@link MemberDeclarationContext}.
69
+ * @see {@link createScopeTap} for tapping {@link OutputScope}.
70
+ * @see {@link createSourceFileTap} for tapping {@link SourceFileContext}.
71
+ * ```
72
+ */
22
73
  export function createTap<T = unknown>(
23
74
  tapper: Tapper<T>,
24
- handler?: Handler<T>,
75
+ handler?: TapHandler<T>,
25
76
  ): Tap<T> {
26
77
  const signal = shallowRef<T | undefined>(undefined);
27
78
  const fn = () => {
@@ -38,31 +89,43 @@ export function createTap<T = unknown>(
38
89
  return fn;
39
90
  }
40
91
 
92
+ /**
93
+ * Create a tap for {@link DeclarationContext}.
94
+ */
41
95
  export function createDeclarationTap<
42
96
  TSymbol extends OutputSymbol = OutputSymbol,
43
- >(handler?: Handler<TSymbol>) {
97
+ >(handler?: TapHandler<TSymbol>) {
44
98
  return createTap<TSymbol>(() => {
45
99
  return useContext(DeclarationContext) as any;
46
100
  }, handler);
47
101
  }
48
102
 
103
+ /**
104
+ * Create a tap for {@link MemberDeclarationContext}.
105
+ */
49
106
  export function createMemberTap<TSymbol extends OutputSymbol = OutputSymbol>(
50
- handler?: Handler<TSymbol>,
107
+ handler?: TapHandler<TSymbol>,
51
108
  ) {
52
109
  return createTap<TSymbol>(() => {
53
110
  return useContext(MemberDeclarationContext) as any;
54
111
  }, handler);
55
112
  }
56
113
 
114
+ /**
115
+ * Create a tap for {@link OutputScope}.
116
+ */
57
117
  export function createScopeTap<TScope extends OutputScope = OutputScope>(
58
- handler?: Handler<TScope>,
118
+ handler?: TapHandler<TScope>,
59
119
  ) {
60
120
  return createTap<TScope>(() => {
61
121
  return useScope() as any;
62
122
  }, handler);
63
123
  }
64
124
 
65
- export function createSourceFileTap(handler?: Handler<SourceFileContext>) {
125
+ /**
126
+ * Create a tap for {@link (SourceFileContext:interface)}.
127
+ */
128
+ export function createSourceFileTap(handler?: TapHandler<SourceFileContext>) {
66
129
  return createTap(() => {
67
130
  return useContext(SourceFileContext);
68
131
  }, handler);