@alloy-js/core 0.20.0-dev.7 → 0.20.0-dev.8

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.
@@ -4,9 +4,9 @@ import {
4
4
  SymbolCreator,
5
5
  } from "../binder.js";
6
6
  import { BinderContext } from "../context/binder.js";
7
+ import { FormatOptions } from "../context/format-options.js";
7
8
  import { NamePolicyContext } from "../context/name-policy.js";
8
9
  import { NamePolicy } from "../name-policy.js";
9
- import { getContext } from "../reactivity.js";
10
10
  import { PrintTreeOptions } from "../render.js";
11
11
  import type { Children } from "../runtime/component.js";
12
12
  import { SourceDirectory } from "./SourceDirectory.js";
@@ -50,14 +50,6 @@ export function Output(props: OutputProps) {
50
50
  nameConflictResolver: props.nameConflictResolver,
51
51
  });
52
52
 
53
- const nodeContext = getContext()!;
54
- nodeContext.meta ??= {};
55
- nodeContext.meta.printOptions = {
56
- printWidth: props.printWidth,
57
- tabWidth: props.tabWidth,
58
- useTabs: props.useTabs,
59
- };
60
-
61
53
  const dir = (
62
54
  <SourceDirectory path={basePath}>{props.children}</SourceDirectory>
63
55
  );
@@ -70,11 +62,19 @@ export function Output(props: OutputProps) {
70
62
 
71
63
  return (
72
64
  <BinderContext.Provider value={binder}>
73
- {props.namePolicy ?
74
- <NamePolicyContext.Provider value={props.namePolicy}>
75
- {dir}
76
- </NamePolicyContext.Provider>
77
- : dir}
65
+ <FormatOptions
66
+ value={{
67
+ printWidth: props.printWidth,
68
+ tabWidth: props.tabWidth,
69
+ useTabs: props.useTabs,
70
+ }}
71
+ >
72
+ {props.namePolicy ?
73
+ <NamePolicyContext.Provider value={props.namePolicy}>
74
+ {dir}
75
+ </NamePolicyContext.Provider>
76
+ : dir}
77
+ </FormatOptions>
78
78
  </BinderContext.Provider>
79
79
  );
80
80
  }
@@ -1,5 +1,6 @@
1
1
  import { join } from "pathe";
2
2
  import { useContext } from "../context.js";
3
+ import { useFormatOptions } from "../context/format-options.js";
3
4
  import { SourceDirectoryContext } from "../context/source-directory.js";
4
5
  import { SourceFileContext } from "../context/source-file.js";
5
6
  import { getContext } from "../reactivity.js";
@@ -44,11 +45,11 @@ export function SourceFile(props: SourceFileProps) {
44
45
  const nodeContext = getContext()!;
45
46
  nodeContext.meta ??= {};
46
47
  nodeContext.meta.sourceFile = context;
47
- nodeContext.meta.printOptions = {
48
+ nodeContext.meta.printOptions = useFormatOptions({
48
49
  printWidth: props.printWidth,
49
50
  tabWidth: props.tabWidth,
50
51
  useTabs: props.useTabs,
51
- };
52
+ });
52
53
 
53
54
  return (
54
55
  <SourceFileContext.Provider value={context}>
@@ -0,0 +1,45 @@
1
+ import { createNamedContext, useContext } from "../context.js";
2
+
3
+ export interface CommonFormatOptions {
4
+ /**
5
+ * The number of characters the printer will wrap on.
6
+ */
7
+ printWidth?: number;
8
+
9
+ /**
10
+ * Whether to use tabs instead of spaces for indentation.
11
+ */
12
+ useTabs?: boolean;
13
+
14
+ /**
15
+ * The number of spaces to use for indentation.
16
+ */
17
+ tabWidth?: number;
18
+ }
19
+
20
+ export const { Provider: FormatOptions, useFormatOptions } =
21
+ createFormatOptionsContextFor<CommonFormatOptions>("*");
22
+
23
+ /** Create a format options context for a specific file type */
24
+ export function createFormatOptionsContextFor<T>(
25
+ filetype: string,
26
+ defaults?: T,
27
+ ) {
28
+ const context = createNamedContext<T>(`FormatOptions.${filetype}`);
29
+ return {
30
+ Provider: context.Provider,
31
+ useFormatOptions: (overrides?: Partial<T>): T => {
32
+ const base = { ...defaults, ...useContext(context) };
33
+ if (overrides === undefined) {
34
+ return base as any;
35
+ }
36
+ const result: any = { ...base };
37
+ for (const [key, value] of Object.entries(overrides)) {
38
+ if (value !== undefined) {
39
+ result[key] = value;
40
+ }
41
+ }
42
+ return result;
43
+ },
44
+ };
45
+ }
@@ -1,6 +1,7 @@
1
1
  export * from "./assignment.js";
2
2
  export * from "./binder.js";
3
3
  export * from "./declaration.js";
4
+ export * from "./format-options.js";
4
5
  export * from "./member-declaration.js";
5
6
  export * from "./member-scope.js";
6
7
  export * from "./name-policy.js";
package/src/render.ts CHANGED
@@ -208,13 +208,6 @@ export function sourceFilesForTree(
208
208
  ): OutputDirectory {
209
209
  let rootDirectory: OutputDirectory | undefined = undefined;
210
210
 
211
- // when passing Output, the first render tree child is the Output component.
212
- const rootRenderOptions =
213
- Array.isArray(tree) ?
214
- (getContextForRenderNode(tree[0] as RenderedTextTree)?.meta
215
- ?.printOptions ?? {})
216
- : {};
217
-
218
211
  collectSourceFiles(undefined, tree);
219
212
 
220
213
  if (!rootDirectory) {
@@ -265,17 +258,9 @@ export function sourceFilesForTree(
265
258
  filetype: context.meta?.sourceFile.filetype,
266
259
  contents: printTree(root, {
267
260
  printWidth:
268
- options?.printWidth ??
269
- context.meta?.printOptions?.printWidth ??
270
- rootRenderOptions.printWidth,
271
- tabWidth:
272
- options?.tabWidth ??
273
- context.meta?.printOptions?.tabWidth ??
274
- rootRenderOptions.tabWidth,
275
- useTabs:
276
- options?.useTabs ??
277
- context.meta?.printOptions?.useTabs ??
278
- rootRenderOptions.useTabs,
261
+ options?.printWidth ?? context.meta?.printOptions?.printWidth,
262
+ tabWidth: options?.tabWidth ?? context.meta?.printOptions?.tabWidth,
263
+ useTabs: options?.useTabs ?? context.meta?.printOptions?.useTabs,
279
264
  }),
280
265
  };
281
266
 
package/temp/api.json CHANGED
@@ -2584,6 +2584,105 @@
2584
2584
  ],
2585
2585
  "name": "code"
2586
2586
  },
2587
+ {
2588
+ "kind": "Interface",
2589
+ "canonicalReference": "@alloy-js/core!CommonFormatOptions:interface",
2590
+ "docComment": "",
2591
+ "excerptTokens": [
2592
+ {
2593
+ "kind": "Content",
2594
+ "text": "export interface CommonFormatOptions "
2595
+ }
2596
+ ],
2597
+ "fileUrlPath": "src/context/format-options.ts",
2598
+ "releaseTag": "Public",
2599
+ "name": "CommonFormatOptions",
2600
+ "preserveMemberOrder": false,
2601
+ "members": [
2602
+ {
2603
+ "kind": "PropertySignature",
2604
+ "canonicalReference": "@alloy-js/core!CommonFormatOptions#printWidth:member",
2605
+ "docComment": "/**\n * The number of characters the printer will wrap on.\n */\n",
2606
+ "excerptTokens": [
2607
+ {
2608
+ "kind": "Content",
2609
+ "text": "printWidth?: "
2610
+ },
2611
+ {
2612
+ "kind": "Content",
2613
+ "text": "number"
2614
+ },
2615
+ {
2616
+ "kind": "Content",
2617
+ "text": ";"
2618
+ }
2619
+ ],
2620
+ "isReadonly": false,
2621
+ "isOptional": true,
2622
+ "releaseTag": "Public",
2623
+ "name": "printWidth",
2624
+ "propertyTypeTokenRange": {
2625
+ "startIndex": 1,
2626
+ "endIndex": 2
2627
+ }
2628
+ },
2629
+ {
2630
+ "kind": "PropertySignature",
2631
+ "canonicalReference": "@alloy-js/core!CommonFormatOptions#tabWidth:member",
2632
+ "docComment": "/**\n * The number of spaces to use for indentation.\n */\n",
2633
+ "excerptTokens": [
2634
+ {
2635
+ "kind": "Content",
2636
+ "text": "tabWidth?: "
2637
+ },
2638
+ {
2639
+ "kind": "Content",
2640
+ "text": "number"
2641
+ },
2642
+ {
2643
+ "kind": "Content",
2644
+ "text": ";"
2645
+ }
2646
+ ],
2647
+ "isReadonly": false,
2648
+ "isOptional": true,
2649
+ "releaseTag": "Public",
2650
+ "name": "tabWidth",
2651
+ "propertyTypeTokenRange": {
2652
+ "startIndex": 1,
2653
+ "endIndex": 2
2654
+ }
2655
+ },
2656
+ {
2657
+ "kind": "PropertySignature",
2658
+ "canonicalReference": "@alloy-js/core!CommonFormatOptions#useTabs:member",
2659
+ "docComment": "/**\n * Whether to use tabs instead of spaces for indentation.\n */\n",
2660
+ "excerptTokens": [
2661
+ {
2662
+ "kind": "Content",
2663
+ "text": "useTabs?: "
2664
+ },
2665
+ {
2666
+ "kind": "Content",
2667
+ "text": "boolean"
2668
+ },
2669
+ {
2670
+ "kind": "Content",
2671
+ "text": ";"
2672
+ }
2673
+ ],
2674
+ "isReadonly": false,
2675
+ "isOptional": true,
2676
+ "releaseTag": "Public",
2677
+ "name": "useTabs",
2678
+ "propertyTypeTokenRange": {
2679
+ "startIndex": 1,
2680
+ "endIndex": 2
2681
+ }
2682
+ }
2683
+ ],
2684
+ "extendsTokenRanges": []
2685
+ },
2587
2686
  {
2588
2687
  "kind": "Interface",
2589
2688
  "canonicalReference": "@alloy-js/core!Component:interface",
@@ -4302,6 +4401,107 @@
4302
4401
  ],
4303
4402
  "name": "createFileResource"
4304
4403
  },
4404
+ {
4405
+ "kind": "Function",
4406
+ "canonicalReference": "@alloy-js/core!createFormatOptionsContextFor:function(1)",
4407
+ "docComment": "/**\n * Create a format options context for a specific file type\n */\n",
4408
+ "excerptTokens": [
4409
+ {
4410
+ "kind": "Content",
4411
+ "text": "export declare function createFormatOptionsContextFor<T>(filetype: "
4412
+ },
4413
+ {
4414
+ "kind": "Content",
4415
+ "text": "string"
4416
+ },
4417
+ {
4418
+ "kind": "Content",
4419
+ "text": ", defaults?: "
4420
+ },
4421
+ {
4422
+ "kind": "Content",
4423
+ "text": "T"
4424
+ },
4425
+ {
4426
+ "kind": "Content",
4427
+ "text": "): "
4428
+ },
4429
+ {
4430
+ "kind": "Content",
4431
+ "text": "{\n Provider: import(\"../index.js\")."
4432
+ },
4433
+ {
4434
+ "kind": "Reference",
4435
+ "text": "ComponentDefinition",
4436
+ "canonicalReference": "@alloy-js/core!ComponentDefinition:interface"
4437
+ },
4438
+ {
4439
+ "kind": "Content",
4440
+ "text": "<import(\"../context.js\")."
4441
+ },
4442
+ {
4443
+ "kind": "Reference",
4444
+ "text": "ContextProviderProps",
4445
+ "canonicalReference": "@alloy-js/core!ContextProviderProps:interface"
4446
+ },
4447
+ {
4448
+ "kind": "Content",
4449
+ "text": "<T>>;\n useFormatOptions: (overrides?: "
4450
+ },
4451
+ {
4452
+ "kind": "Reference",
4453
+ "text": "Partial",
4454
+ "canonicalReference": "!Partial:type"
4455
+ },
4456
+ {
4457
+ "kind": "Content",
4458
+ "text": "<T>) => T;\n}"
4459
+ },
4460
+ {
4461
+ "kind": "Content",
4462
+ "text": ";"
4463
+ }
4464
+ ],
4465
+ "fileUrlPath": "src/context/format-options.ts",
4466
+ "returnTypeTokenRange": {
4467
+ "startIndex": 5,
4468
+ "endIndex": 12
4469
+ },
4470
+ "releaseTag": "Public",
4471
+ "overloadIndex": 1,
4472
+ "parameters": [
4473
+ {
4474
+ "parameterName": "filetype",
4475
+ "parameterTypeTokenRange": {
4476
+ "startIndex": 1,
4477
+ "endIndex": 2
4478
+ },
4479
+ "isOptional": false
4480
+ },
4481
+ {
4482
+ "parameterName": "defaults",
4483
+ "parameterTypeTokenRange": {
4484
+ "startIndex": 3,
4485
+ "endIndex": 4
4486
+ },
4487
+ "isOptional": true
4488
+ }
4489
+ ],
4490
+ "typeParameters": [
4491
+ {
4492
+ "typeParameterName": "T",
4493
+ "constraintTokenRange": {
4494
+ "startIndex": 0,
4495
+ "endIndex": 0
4496
+ },
4497
+ "defaultTypeTokenRange": {
4498
+ "startIndex": 0,
4499
+ "endIndex": 0
4500
+ }
4501
+ }
4502
+ ],
4503
+ "name": "createFormatOptionsContextFor"
4504
+ },
4305
4505
  {
4306
4506
  "kind": "Function",
4307
4507
  "canonicalReference": "@alloy-js/core!createIntrinsic:function(1)",
@@ -6581,6 +6781,56 @@
6581
6781
  "endIndex": 14
6582
6782
  }
6583
6783
  },
6784
+ {
6785
+ "kind": "Variable",
6786
+ "canonicalReference": "@alloy-js/core!FormatOptions:var",
6787
+ "docComment": "",
6788
+ "excerptTokens": [
6789
+ {
6790
+ "kind": "Content",
6791
+ "text": "FormatOptions: "
6792
+ },
6793
+ {
6794
+ "kind": "Content",
6795
+ "text": "import(\"../index.js\")."
6796
+ },
6797
+ {
6798
+ "kind": "Reference",
6799
+ "text": "ComponentDefinition",
6800
+ "canonicalReference": "@alloy-js/core!ComponentDefinition:interface"
6801
+ },
6802
+ {
6803
+ "kind": "Content",
6804
+ "text": "<import(\"../context.js\")."
6805
+ },
6806
+ {
6807
+ "kind": "Reference",
6808
+ "text": "ContextProviderProps",
6809
+ "canonicalReference": "@alloy-js/core!ContextProviderProps:interface"
6810
+ },
6811
+ {
6812
+ "kind": "Content",
6813
+ "text": "<"
6814
+ },
6815
+ {
6816
+ "kind": "Reference",
6817
+ "text": "CommonFormatOptions",
6818
+ "canonicalReference": "@alloy-js/core!CommonFormatOptions:interface"
6819
+ },
6820
+ {
6821
+ "kind": "Content",
6822
+ "text": ">>"
6823
+ }
6824
+ ],
6825
+ "fileUrlPath": "src/context/format-options.ts",
6826
+ "isReadonly": true,
6827
+ "releaseTag": "Public",
6828
+ "name": "FormatOptions",
6829
+ "variableTypeTokenRange": {
6830
+ "startIndex": 1,
6831
+ "endIndex": 8
6832
+ }
6833
+ },
6584
6834
  {
6585
6835
  "kind": "Interface",
6586
6836
  "canonicalReference": "@alloy-js/core!ForProps:interface",
@@ -23291,6 +23541,62 @@
23291
23541
  ],
23292
23542
  "name": "useContext"
23293
23543
  },
23544
+ {
23545
+ "kind": "Function",
23546
+ "canonicalReference": "@alloy-js/core!useFormatOptions:function(1)",
23547
+ "docComment": "",
23548
+ "excerptTokens": [
23549
+ {
23550
+ "kind": "Content",
23551
+ "text": "useFormatOptions: (overrides?: "
23552
+ },
23553
+ {
23554
+ "kind": "Reference",
23555
+ "text": "Partial",
23556
+ "canonicalReference": "!Partial:type"
23557
+ },
23558
+ {
23559
+ "kind": "Content",
23560
+ "text": "<"
23561
+ },
23562
+ {
23563
+ "kind": "Reference",
23564
+ "text": "CommonFormatOptions",
23565
+ "canonicalReference": "@alloy-js/core!CommonFormatOptions:interface"
23566
+ },
23567
+ {
23568
+ "kind": "Content",
23569
+ "text": "> | undefined"
23570
+ },
23571
+ {
23572
+ "kind": "Content",
23573
+ "text": ") => "
23574
+ },
23575
+ {
23576
+ "kind": "Reference",
23577
+ "text": "CommonFormatOptions",
23578
+ "canonicalReference": "@alloy-js/core!CommonFormatOptions:interface"
23579
+ }
23580
+ ],
23581
+ "fileUrlPath": "src/context/format-options.ts",
23582
+ "returnTypeTokenRange": {
23583
+ "startIndex": 6,
23584
+ "endIndex": 7
23585
+ },
23586
+ "releaseTag": "Public",
23587
+ "overloadIndex": 1,
23588
+ "parameters": [
23589
+ {
23590
+ "parameterName": "overrides",
23591
+ "parameterTypeTokenRange": {
23592
+ "startIndex": 1,
23593
+ "endIndex": 5
23594
+ },
23595
+ "isOptional": true
23596
+ }
23597
+ ],
23598
+ "name": "useFormatOptions"
23599
+ },
23294
23600
  {
23295
23601
  "kind": "Function",
23296
23602
  "canonicalReference": "@alloy-js/core!useMemberContext:function(1)",
@@ -1,13 +1,18 @@
1
1
  import {
2
+ Children,
3
+ CommonFormatOptions,
2
4
  computed,
3
5
  ContentOutputFile,
6
+ FormatOptions,
7
+ Indent,
4
8
  Output,
9
+ Prose,
5
10
  render,
6
11
  renderTree,
7
12
  SourceFile,
8
13
  useContext,
9
14
  } from "@alloy-js/core";
10
- import { expect, it } from "vitest";
15
+ import { describe, expect, it } from "vitest";
11
16
  import { SourceDirectoryContext } from "../../src/context/source-directory.js";
12
17
  import "../../testing/extend-expect.js";
13
18
  import { d } from "../../testing/render.js";
@@ -71,3 +76,67 @@ it("Includes header", () => {
71
76
  hello!
72
77
  `);
73
78
  });
79
+
80
+ describe("format options", () => {
81
+ function FileWithFormatOptions(props: {
82
+ options: CommonFormatOptions;
83
+ children: Children;
84
+ }) {
85
+ return (
86
+ <FormatOptions value={props.options}>
87
+ <SourceFile path="hi.txt" filetype="text">
88
+ {props.children}
89
+ </SourceFile>
90
+ </FormatOptions>
91
+ );
92
+ }
93
+
94
+ it("respect tabWidth", () => {
95
+ expect(
96
+ <FileWithFormatOptions options={{ tabWidth: 5 }}>
97
+ hello
98
+ <Indent>indented 5 spaces</Indent>
99
+ </FileWithFormatOptions>,
100
+ ).toRenderTo(`
101
+ hello
102
+ indented 5 spaces
103
+ `);
104
+ });
105
+
106
+ it("respect useTabs", () => {
107
+ expect(
108
+ <FileWithFormatOptions options={{ useTabs: true }}>
109
+ hello
110
+ <Indent>indented with tabs</Indent>
111
+ </FileWithFormatOptions>,
112
+ ).toRenderTo(`
113
+ hello
114
+ \tindented with tabs
115
+ `);
116
+ });
117
+
118
+ it("respect printWidth", () => {
119
+ expect(
120
+ <FileWithFormatOptions options={{ printWidth: 10 }}>
121
+ <Prose>this is too long</Prose>
122
+ </FileWithFormatOptions>,
123
+ ).toRenderTo(`
124
+ this is
125
+ too long
126
+ `);
127
+ });
128
+
129
+ it("source file overrides take precedence", () => {
130
+ expect(
131
+ <FormatOptions value={{ tabWidth: 5 }}>
132
+ <SourceFile path="hi.txt" filetype="text" tabWidth={3}>
133
+ hello
134
+ <Indent>indented 3 spaces</Indent>
135
+ </SourceFile>
136
+ </FormatOptions>,
137
+ ).toRenderTo(`
138
+ hello
139
+ indented 3 spaces
140
+ `);
141
+ });
142
+ });
@@ -98,12 +98,6 @@ function validateRender(
98
98
 
99
99
  function getFilesFromTree(tree: RenderedTextTree, options?: ToRenderToOptions) {
100
100
  const files: Record<string, string> = {};
101
- // when passing Output, the first render tree child is the Output component.
102
- const rootRenderOptions =
103
- Array.isArray(tree) ?
104
- (getContextForRenderNode(tree[0] as RenderedTextTree)?.meta
105
- ?.printOptions ?? {})
106
- : {};
107
101
 
108
102
  collectSourceFiles(tree);
109
103
  // If we found no source files, we return the tree as a string.
@@ -121,17 +115,9 @@ function getFilesFromTree(tree: RenderedTextTree, options?: ToRenderToOptions) {
121
115
  if (context?.meta?.sourceFile) {
122
116
  files[context.meta.sourceFile.path] = printTree(root, {
123
117
  printWidth:
124
- options?.printWidth ??
125
- context.meta?.printOptions?.printWidth ??
126
- rootRenderOptions.printWidth,
127
- tabWidth:
128
- options?.tabWidth ??
129
- context.meta?.printOptions?.tabWidth ??
130
- rootRenderOptions.tabWidth,
131
- useTabs:
132
- options?.useTabs ??
133
- context.meta?.printOptions?.useTabs ??
134
- rootRenderOptions.useTabs,
118
+ options?.printWidth ?? context.meta?.printOptions?.printWidth,
119
+ tabWidth: options?.tabWidth ?? context.meta?.printOptions?.tabWidth,
120
+ useTabs: options?.useTabs ?? context.meta?.printOptions?.useTabs,
135
121
  });
136
122
  } else {
137
123
  visitChildren();