@alloy-js/core 0.4.0 → 0.5.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 (61) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/dist/src/binder.d.ts +15 -13
  3. package/dist/src/binder.d.ts.map +1 -1
  4. package/dist/src/binder.js +19 -15
  5. package/dist/src/binder.js.map +1 -1
  6. package/dist/src/components/Declaration.d.ts +1 -1
  7. package/dist/src/components/Declaration.d.ts.map +1 -1
  8. package/dist/src/components/Indent.d.ts +1 -1
  9. package/dist/src/components/Indent.d.ts.map +1 -1
  10. package/dist/src/components/MemberDeclaration.d.ts +1 -1
  11. package/dist/src/components/MemberDeclaration.d.ts.map +1 -1
  12. package/dist/src/components/MemberName.d.ts +1 -1
  13. package/dist/src/components/MemberName.d.ts.map +1 -1
  14. package/dist/src/components/MemberScope.d.ts +1 -1
  15. package/dist/src/components/MemberScope.d.ts.map +1 -1
  16. package/dist/src/components/Name.d.ts +1 -1
  17. package/dist/src/components/Name.d.ts.map +1 -1
  18. package/dist/src/components/Output.d.ts +1 -1
  19. package/dist/src/components/Output.d.ts.map +1 -1
  20. package/dist/src/components/Output.js +3 -1
  21. package/dist/src/components/Output.js.map +1 -1
  22. package/dist/src/components/Scope.d.ts +1 -1
  23. package/dist/src/components/Scope.d.ts.map +1 -1
  24. package/dist/src/components/SourceDirectory.d.ts +2 -2
  25. package/dist/src/components/SourceDirectory.d.ts.map +1 -1
  26. package/dist/src/components/SourceFile.d.ts +2 -2
  27. package/dist/src/components/SourceFile.d.ts.map +1 -1
  28. package/dist/src/components/stc/index.d.ts +18 -18
  29. package/dist/src/context/member-declaration.d.ts +1 -0
  30. package/dist/src/context/member-declaration.d.ts.map +1 -1
  31. package/dist/src/context/member-declaration.js +4 -1
  32. package/dist/src/context/member-declaration.js.map +1 -1
  33. package/dist/src/index.d.ts +1 -0
  34. package/dist/src/index.d.ts.map +1 -1
  35. package/dist/src/index.js +1 -0
  36. package/dist/src/index.js.map +1 -1
  37. package/dist/src/jsx-runtime.d.ts +21 -2
  38. package/dist/src/jsx-runtime.d.ts.map +1 -1
  39. package/dist/src/jsx-runtime.js +12 -1
  40. package/dist/src/jsx-runtime.js.map +1 -1
  41. package/dist/src/render.js +12 -10
  42. package/dist/src/render.js.map +1 -1
  43. package/dist/src/tap.d.ts +19 -0
  44. package/dist/src/tap.d.ts.map +1 -0
  45. package/dist/src/tap.js +39 -0
  46. package/dist/src/tap.js.map +1 -0
  47. package/dist/src/utils.d.ts +1 -1
  48. package/dist/src/utils.d.ts.map +1 -1
  49. package/dist/tsconfig.tsbuildinfo +1 -1
  50. package/package.json +3 -2
  51. package/src/binder.ts +39 -50
  52. package/src/components/Output.tsx +2 -1
  53. package/src/components/SourceDirectory.tsx +1 -1
  54. package/src/components/SourceFile.tsx +1 -1
  55. package/src/context/member-declaration.ts +9 -1
  56. package/src/index.ts +1 -0
  57. package/src/jsx-runtime.ts +27 -3
  58. package/src/render.ts +13 -11
  59. package/src/tap.ts +69 -0
  60. package/temp/api.json +1364 -225
  61. package/test/symbols.test.ts +59 -3
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alloy-js/core",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "",
5
5
  "main": "./dist/src/index.js",
6
6
  "exports": {
@@ -9,6 +9,7 @@
9
9
  "import": "./dist/src/index.js"
10
10
  },
11
11
  "./jsx-runtime": {
12
+ "types": "./dist/src/jsx-runtime.d.ts",
12
13
  "development": "./src/jsx-runtime.ts",
13
14
  "import": "./dist/src/jsx-runtime.js"
14
15
  },
@@ -47,7 +48,7 @@
47
48
  "@rollup/plugin-babel": "^6.0.4",
48
49
  "@rollup/plugin-typescript": "^11.1.6",
49
50
  "concurrently": "^8.2.2",
50
- "typescript": "^5.5.4",
51
+ "typescript": "^5.7.3",
51
52
  "vitest": "^3.0.4"
52
53
  },
53
54
  "type": "module",
package/src/binder.ts CHANGED
@@ -7,6 +7,7 @@ import {
7
7
  ShallowRef,
8
8
  shallowRef,
9
9
  } from "@vue/reactivity";
10
+ import { useBinder } from "./context/binder.js";
10
11
  import { useMemberScope } from "./context/member-scope.js";
11
12
  import { useScope } from "./context/scope.js";
12
13
  import { memo, untrack } from "./jsx-runtime.js";
@@ -223,6 +224,21 @@ export interface OutputScope {
223
224
  getSymbolNames(): Set<string>;
224
225
  }
225
226
 
227
+ export type CreateSymbolOptions<T extends OutputSymbol = OutputSymbol> = {
228
+ name: string;
229
+ scope?: OutputScope;
230
+ refkey?: Refkey;
231
+ flags?: OutputSymbolFlags;
232
+ } & Omit<T, keyof OutputSymbol>;
233
+
234
+ export type CreateScopeOptions<T extends OutputScope = OutputScope> = {
235
+ kind: T["kind"];
236
+ name: string;
237
+ parent?: OutputScope | undefined;
238
+ flags?: OutputScopeFlags;
239
+ owner?: OutputSymbol;
240
+ } & Omit<T, keyof OutputScope>;
241
+
226
242
  /**
227
243
  * The binder tracks all output scopes and symbols. Scopes are nested containers
228
244
  * for symbols.
@@ -241,28 +257,13 @@ export interface Binder {
241
257
  * Create a new scope. The scope will be added to the parent scope's children.
242
258
  * The returned scope object is reactive.
243
259
  */
244
- createScope<T extends OutputScope>(
245
- args: {
246
- kind: T["kind"];
247
- name: string;
248
- parent?: OutputScope | undefined;
249
- flags?: OutputScopeFlags;
250
- owner?: OutputSymbol;
251
- } & Omit<T, keyof OutputScope>,
252
- ): T;
260
+ createScope<T extends OutputScope>(args: CreateScopeOptions<T>): T;
253
261
 
254
262
  /**
255
263
  * Create a new symbol. The symbol will be added to the parent scope's symbols.
256
264
  * The returned symbol object is reactive.
257
265
  */
258
- createSymbol<T extends OutputSymbol>(
259
- args: {
260
- name: string;
261
- scope?: OutputScope;
262
- refkey?: unknown;
263
- flags?: OutputSymbolFlags;
264
- } & Omit<T, keyof OutputSymbol>,
265
- ): T;
266
+ createSymbol<T extends OutputSymbol>(args: CreateSymbolOptions<T>): T;
266
267
 
267
268
  /**
268
269
  * Instantiate the static members of a symbol into the instance members of
@@ -451,15 +452,7 @@ export function createOutputBinder(options: BinderOptions = {}): Binder {
451
452
 
452
453
  return binder;
453
454
 
454
- function createScope<T extends OutputScope>(
455
- args: {
456
- kind: string;
457
- name: string;
458
- parent?: OutputScope;
459
- flags?: OutputScopeFlags;
460
- owner?: OutputSymbol;
461
- } & Omit<T, keyof OutputScope>,
462
- ): T {
455
+ function createScope<T extends OutputScope>(args: CreateScopeOptions<T>): T {
463
456
  const {
464
457
  kind,
465
458
  name,
@@ -518,12 +511,7 @@ export function createOutputBinder(options: BinderOptions = {}): Binder {
518
511
  }
519
512
 
520
513
  function createSymbol<T extends OutputSymbol>(
521
- args: {
522
- name: string;
523
- scope?: OutputScope;
524
- refkey?: Refkey;
525
- flags?: OutputSymbolFlags;
526
- } & Omit<T, keyof OutputSymbol>,
514
+ args: CreateSymbolOptions<T>,
527
515
  ): T {
528
516
  const {
529
517
  name,
@@ -591,7 +579,7 @@ export function createOutputBinder(options: BinderOptions = {}): Binder {
591
579
  scope.symbolsByRefkey.set(symbol.refkey, symbol);
592
580
 
593
581
  deconflict(symbol);
594
- notifyRefkey(refkey, symbol);
582
+ notifyRefkey(symbol);
595
583
 
596
584
  return symbol;
597
585
  }
@@ -807,24 +795,25 @@ export function createOutputBinder(options: BinderOptions = {}): Binder {
807
795
  return computed(() => cb(declSignal.value));
808
796
  }
809
797
 
810
- function notifyRefkey(
811
- refkey: Refkey | undefined,
812
- symbol: OutputSymbol,
813
- ): void {
814
- if (!refkey) return;
815
- knownDeclarations.set(refkey, symbol);
816
- if (waitingDeclarations.has(refkey)) {
817
- const signal = waitingDeclarations.get(refkey)!;
818
- signal.value = symbol;
819
- }
798
+ function notifyRefkey(symbol: OutputSymbol): void {
799
+ effect(() => {
800
+ const refkey = symbol.refkey;
801
+ if (!refkey) return;
820
802
 
821
- const waitingScope = waitingSymbolNames.get(symbol.scope);
822
- if (waitingScope) {
823
- const waitingName = waitingScope.get(symbol.name);
824
- if (waitingName) {
825
- waitingName.value = symbol;
803
+ knownDeclarations.set(refkey, symbol);
804
+ if (waitingDeclarations.has(refkey)) {
805
+ const signal = waitingDeclarations.get(refkey)!;
806
+ signal.value = symbol;
826
807
  }
827
- }
808
+
809
+ const waitingScope = waitingSymbolNames.get(symbol.scope);
810
+ if (waitingScope) {
811
+ const waitingName = waitingScope.get(symbol.name);
812
+ if (waitingName) {
813
+ waitingName.value = symbol;
814
+ }
815
+ }
816
+ });
828
817
  }
829
818
 
830
819
  function findSymbolName<TSymbol extends OutputSymbol = OutputSymbol>(
@@ -949,7 +938,7 @@ export function resolve<
949
938
  TScope extends OutputScope,
950
939
  TSymbol extends OutputSymbol,
951
940
  >(refkey: Refkey): Ref<ResolutionResult<TScope, TSymbol>> {
952
- const scope = useScope();
941
+ const scope = useScope() ?? useBinder().globalScope;
953
942
  const memberScope = useMemberScope();
954
943
  const binder = scope.binder;
955
944
 
@@ -7,9 +7,10 @@ import { BinderContext } from "../context/binder.js";
7
7
  import { NamePolicyContext } from "../context/name-policy.js";
8
8
  import { Children } from "../jsx-runtime.js";
9
9
  import { NamePolicy } from "../name-policy.js";
10
+ import { extensionEffects } from "../slot.js";
10
11
  import { SourceDirectory } from "./SourceDirectory.js";
12
+
11
13
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
12
- import { extensionEffects } from "../slot.js";
13
14
  import { SourceFile } from "./SourceFile.js";
14
15
 
15
16
  export interface OutputProps {
@@ -6,7 +6,7 @@ import { Children, getContext } from "../jsx-runtime.js";
6
6
 
7
7
  export interface SourceDirectoryProps {
8
8
  path: string;
9
- children?: Children[];
9
+ children?: Children;
10
10
  }
11
11
 
12
12
  export function SourceDirectory(props: SourceDirectoryProps) {
@@ -17,7 +17,7 @@ export interface SourceFileProps {
17
17
  */
18
18
  filetype: string;
19
19
 
20
- children?: Children[];
20
+ children?: Children;
21
21
 
22
22
  /**
23
23
  * The component to use to render refkeys references within the file's
@@ -1,5 +1,9 @@
1
1
  import { OutputSymbol } from "../binder.js";
2
- import { ComponentContext, createNamedContext } from "../context.js";
2
+ import {
3
+ ComponentContext,
4
+ createNamedContext,
5
+ useContext,
6
+ } from "../context.js";
3
7
 
4
8
  /**
5
9
  * Provides the symbol for the member currently being declared.
@@ -8,3 +12,7 @@ import { ComponentContext, createNamedContext } from "../context.js";
8
12
  */
9
13
  export const MemberDeclarationContext: ComponentContext<OutputSymbol> =
10
14
  createNamedContext("MemberDeclaration");
15
+
16
+ export function useMemberDeclaration() {
17
+ return useContext(MemberDeclarationContext);
18
+ }
package/src/index.ts CHANGED
@@ -17,6 +17,7 @@ export * from "./jsx-runtime.js";
17
17
  export * from "./name-policy.js";
18
18
  export * from "./refkey.js";
19
19
  export * from "./render.js";
20
+ export * from "./tap.js";
20
21
  export * from "./utils.js";
21
22
  export * from "./write-output.js";
22
23
  import "./debug.js";
@@ -117,7 +117,7 @@ export function effect<T>(fn: (prev?: T) => T, current?: T) {
117
117
  cleanup(() => cleanupFn(true));
118
118
  }
119
119
 
120
- function cleanup(fn: Disposable) {
120
+ export function cleanup(fn: Disposable) {
121
121
  if (globalContext != null) {
122
122
  globalContext.disposables.push(fn);
123
123
  }
@@ -129,13 +129,14 @@ export type Child =
129
129
  | number
130
130
  | undefined
131
131
  | null
132
- | (() => Child | Children)
132
+ | void
133
+ | (() => Children)
133
134
  | Child[]
134
135
  | Ref
135
136
  | Refkey;
136
137
 
137
138
  export type Children = Child | Child[];
138
- export type Props = Record<string, unknown>;
139
+ export type Props = Record<string, any>;
139
140
 
140
141
  export interface ComponentDefinition<TProps = Props> {
141
142
  (props: TProps): Child | Children;
@@ -212,6 +213,29 @@ export function isComponentCreator(item: unknown): item is ComponentCreator {
212
213
  return typeof item === "function" && (item as any).component;
213
214
  }
214
215
 
216
+ /**
217
+ * This namespace is predominantly for interop with React tooling in VSCode
218
+ * and controls the type of JSX elements, components, and the like.
219
+ */
220
+ // eslint-disable-next-line @typescript-eslint/no-namespace
221
+ export namespace JSX {
222
+ export interface IntrinsicElements {}
223
+ export type ElementType = ComponentDefinition<any>;
224
+ export type Element = Children;
225
+ export interface ElementChildrenAttribute {
226
+ children: {};
227
+ }
228
+ export interface ElementAttributesProperty {
229
+ props: {};
230
+ }
231
+ }
232
+
233
+ export function jsx(type: Component<any>, props: Record<string, unknown>) {
234
+ return createComponent(type, props);
235
+ }
236
+
237
+ export const jsxs = jsx;
238
+
215
239
  export function createComponent<TProps extends Props = Props>(
216
240
  C: Component<TProps>,
217
241
  props: TProps,
package/src/render.ts CHANGED
@@ -171,7 +171,7 @@ export type RenderStructure = {};
171
171
 
172
172
  export type RenderTextTree = (string | RenderTextTree)[];
173
173
 
174
- function traceRender(phase: string, message: string) {
174
+ function traceRender(phase: string, message: () => string) {
175
175
  return false;
176
176
  // console.log(`[\x1b[34m${phase}\x1b[0m]: ${message}`);
177
177
  }
@@ -268,7 +268,7 @@ function renderWorker(
268
268
  children: Children,
269
269
  state: RenderState,
270
270
  ) {
271
- traceRender("render", dumpChildren(children));
271
+ traceRender("render", () => dumpChildren(children));
272
272
 
273
273
  if (Array.isArray(node)) {
274
274
  nodesToContext.set(node, getContext()!);
@@ -290,7 +290,7 @@ function appendChild(
290
290
  indentState: IndentState,
291
291
  state: RenderState,
292
292
  ) {
293
- traceRender("appendChild", printChild(rawChild));
293
+ traceRender("appendChild", () => printChild(rawChild));
294
294
  const child = normalizeChild(rawChild);
295
295
 
296
296
  if (typeof child === "string") {
@@ -300,11 +300,11 @@ function appendChild(
300
300
  state.newline = false;
301
301
  }
302
302
  const reindented = reindent(child, indentState.indentString);
303
- traceRender("appendChild:string", JSON.stringify(reindented));
303
+ traceRender("appendChild:string", () => JSON.stringify(reindented));
304
304
  node.push(reindented);
305
305
  } else if (isComponentCreator(child)) {
306
306
  root(() => {
307
- traceRender("appendChild:component", printChild(child));
307
+ traceRender("appendChild:component", () => printChild(child));
308
308
  if (child.component === Indent && state.newline) {
309
309
  node.push(indentState.indent);
310
310
  }
@@ -313,13 +313,13 @@ function appendChild(
313
313
  renderWorker(componentRoot, untrack(child), state);
314
314
  popStack();
315
315
  node.push(componentRoot);
316
- traceRender("appendChild:component-done", printChild(child));
316
+ traceRender("appendChild:component-done", () => printChild(child));
317
317
  }, child);
318
318
  } else if (typeof child === "function") {
319
- traceRender("appendChild:memo", child.toString());
319
+ traceRender("appendChild:memo", () => child.toString());
320
320
  const index = node.length;
321
321
  effect((prev: any) => {
322
- traceRender("memoEffect:run", "");
322
+ traceRender("memoEffect:run", () => "");
323
323
  let res = child();
324
324
  while (typeof res === "function" && !isComponentCreator(res)) {
325
325
  res = res();
@@ -330,11 +330,11 @@ function appendChild(
330
330
  node[index] = newNodes;
331
331
  return newNodes;
332
332
  });
333
- traceRender("appendChild:memo-done", "");
333
+ traceRender("appendChild:memo-done", () => "");
334
334
  } else {
335
- traceRender("appendChild:array", dumpChildren(child));
335
+ traceRender("appendChild:array", () => dumpChildren(child));
336
336
  renderWorker(node, child, state);
337
- traceRender("appendChild:array-done", dumpChildren(child));
337
+ traceRender("appendChild:array-done", () => dumpChildren(child));
338
338
  }
339
339
  }
340
340
 
@@ -383,6 +383,8 @@ function printChild(child: Child): string {
383
383
  return "<" + child.component.name + ">";
384
384
  } else if (typeof child === "function") {
385
385
  return "$memo";
386
+ } else if (isRef(child)) {
387
+ return "$ref";
386
388
  } else {
387
389
  return JSON.stringify(child);
388
390
  }
package/src/tap.ts ADDED
@@ -0,0 +1,69 @@
1
+ import { ShallowRef, shallowRef } from "@vue/reactivity";
2
+ import { OutputScope, OutputSymbol } from "./binder.js";
3
+ import { useContext } from "./context.js";
4
+ import { DeclarationContext } from "./context/declaration.js";
5
+ import { MemberDeclarationContext } from "./context/member-declaration.js";
6
+ import { useScope } from "./context/scope.js";
7
+ import { SourceFileContext } from "./context/source-file.js";
8
+ import { ComponentDefinition } from "./jsx-runtime.js";
9
+
10
+ export interface Tap<T> extends ComponentDefinition {
11
+ ref: ShallowRef<T | undefined>;
12
+ }
13
+
14
+ export interface Tapper<T> {
15
+ (): T | undefined;
16
+ }
17
+
18
+ export interface Handler<T> {
19
+ (value: T): void;
20
+ }
21
+
22
+ export function createTap<T = unknown>(
23
+ tapper: Tapper<T>,
24
+ handler?: Handler<T>,
25
+ ): Tap<T> {
26
+ const signal = shallowRef<T | undefined>(undefined);
27
+ const fn = () => {
28
+ signal.value = tapper();
29
+ if (handler && signal.value !== undefined) {
30
+ handler(signal.value);
31
+ }
32
+
33
+ return "";
34
+ };
35
+
36
+ fn.ref = signal;
37
+
38
+ return fn;
39
+ }
40
+
41
+ export function createDeclarationTap<
42
+ TSymbol extends OutputSymbol = OutputSymbol,
43
+ >(handler?: Handler<TSymbol>) {
44
+ return createTap<TSymbol>(() => {
45
+ return useContext(DeclarationContext) as any;
46
+ }, handler);
47
+ }
48
+
49
+ export function createMemberTap<TSymbol extends OutputSymbol = OutputSymbol>(
50
+ handler?: Handler<TSymbol>,
51
+ ) {
52
+ return createTap<TSymbol>(() => {
53
+ return useContext(MemberDeclarationContext) as any;
54
+ }, handler);
55
+ }
56
+
57
+ export function createScopeTap<TScope extends OutputScope = OutputScope>(
58
+ handler?: Handler<TScope>,
59
+ ) {
60
+ return createTap<TScope>(() => {
61
+ return useScope() as any;
62
+ }, handler);
63
+ }
64
+
65
+ export function createSourceFileTap(handler?: Handler<SourceFileContext>) {
66
+ return createTap(() => {
67
+ return useContext(SourceFileContext);
68
+ }, handler);
69
+ }