@alloy-js/core 0.19.0-dev.0 → 0.19.0-dev.12

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 (105) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/src/components/AppendFile.d.ts +90 -0
  3. package/dist/src/components/AppendFile.d.ts.map +1 -0
  4. package/dist/src/components/AppendFile.js +226 -0
  5. package/dist/src/components/CopyFile.d.ts +12 -0
  6. package/dist/src/components/CopyFile.d.ts.map +1 -0
  7. package/dist/src/components/CopyFile.js +15 -0
  8. package/dist/src/components/TemplateFile.d.ts +84 -0
  9. package/dist/src/components/TemplateFile.d.ts.map +1 -0
  10. package/dist/src/components/TemplateFile.js +133 -0
  11. package/dist/src/components/UpdateFile.d.ts +34 -0
  12. package/dist/src/components/UpdateFile.d.ts.map +1 -0
  13. package/dist/src/components/UpdateFile.js +66 -0
  14. package/dist/src/components/index.d.ts +4 -0
  15. package/dist/src/components/index.d.ts.map +1 -1
  16. package/dist/src/components/index.js +4 -0
  17. package/dist/src/components/stc/index.d.ts +4 -0
  18. package/dist/src/components/stc/index.d.ts.map +1 -1
  19. package/dist/src/components/stc/index.js +4 -0
  20. package/dist/src/context/source-directory.d.ts +3 -3
  21. package/dist/src/context/source-directory.d.ts.map +1 -1
  22. package/dist/src/context/source-file.d.ts +4 -0
  23. package/dist/src/context/source-file.d.ts.map +1 -1
  24. package/dist/src/debug.d.ts.map +1 -1
  25. package/dist/src/debug.js +4 -1
  26. package/dist/src/host/alloy-host.browser.d.ts +11 -0
  27. package/dist/src/host/alloy-host.browser.d.ts.map +1 -0
  28. package/dist/src/host/alloy-host.browser.js +31 -0
  29. package/dist/src/host/alloy-host.d.ts +11 -0
  30. package/dist/src/host/alloy-host.d.ts.map +1 -0
  31. package/dist/src/host/alloy-host.js +143 -0
  32. package/dist/src/host/interface.d.ts +144 -0
  33. package/dist/src/host/interface.d.ts.map +1 -0
  34. package/dist/src/host/interface.js +1 -0
  35. package/dist/src/index.browser.d.ts +1 -1
  36. package/dist/src/index.browser.d.ts.map +1 -1
  37. package/dist/src/index.browser.js +2 -2
  38. package/dist/src/reactivity.js +2 -2
  39. package/dist/src/render.d.ts +10 -2
  40. package/dist/src/render.d.ts.map +1 -1
  41. package/dist/src/render.js +20 -1
  42. package/dist/src/resource.d.ts +80 -0
  43. package/dist/src/resource.d.ts.map +1 -0
  44. package/dist/src/resource.js +118 -0
  45. package/dist/src/scheduler.d.ts +6 -0
  46. package/dist/src/scheduler.d.ts.map +1 -1
  47. package/dist/src/scheduler.js +36 -0
  48. package/dist/src/write-output.d.ts +1 -1
  49. package/dist/src/write-output.d.ts.map +1 -1
  50. package/dist/src/write-output.js +40 -21
  51. package/dist/test/components/append-file.test.d.ts +2 -0
  52. package/dist/test/components/append-file.test.d.ts.map +1 -0
  53. package/dist/test/components/append-file.test.js +281 -0
  54. package/dist/test/components/copy-file.test.d.ts +2 -0
  55. package/dist/test/components/copy-file.test.d.ts.map +1 -0
  56. package/dist/test/components/copy-file.test.js +94 -0
  57. package/dist/test/components/source-file.test.d.ts.map +1 -1
  58. package/dist/test/components/template-file.test.d.ts +2 -0
  59. package/dist/test/components/template-file.test.d.ts.map +1 -0
  60. package/dist/test/components/template-file.test.js +133 -0
  61. package/dist/test/components/update-file.test.d.ts +2 -0
  62. package/dist/test/components/update-file.test.d.ts.map +1 -0
  63. package/dist/test/components/update-file.test.js +169 -0
  64. package/dist/test/rendering/formatting.test.d.ts.map +1 -1
  65. package/dist/testing/extend-expect.d.ts +0 -15
  66. package/dist/testing/extend-expect.d.ts.map +1 -1
  67. package/dist/testing/extend-expect.js +89 -12
  68. package/dist/testing/extend-expect.test.d.ts +2 -0
  69. package/dist/testing/extend-expect.test.d.ts.map +1 -0
  70. package/dist/testing/extend-expect.test.js +149 -0
  71. package/dist/tsconfig.tsbuildinfo +1 -1
  72. package/package.json +4 -4
  73. package/src/components/AppendFile.tsx +294 -0
  74. package/src/components/CopyFile.tsx +29 -0
  75. package/src/components/TemplateFile.tsx +193 -0
  76. package/src/components/UpdateFile.tsx +86 -0
  77. package/src/components/index.tsx +4 -0
  78. package/src/components/stc/index.ts +4 -0
  79. package/src/context/source-directory.ts +5 -3
  80. package/src/context/source-file.ts +5 -0
  81. package/src/debug.ts +4 -1
  82. package/src/host/alloy-host.browser.ts +56 -0
  83. package/src/host/alloy-host.ts +160 -0
  84. package/src/host/interface.ts +153 -0
  85. package/src/index.browser.ts +1 -1
  86. package/src/reactivity.ts +2 -2
  87. package/src/render.ts +44 -5
  88. package/src/resource.ts +152 -0
  89. package/src/scheduler.ts +39 -0
  90. package/src/write-output.ts +49 -19
  91. package/temp/api.json +2009 -546
  92. package/test/components/append-file.test.tsx +275 -0
  93. package/test/components/copy-file.test.tsx +98 -0
  94. package/test/components/source-file.test.tsx +5 -2
  95. package/test/components/template-file.test.tsx +127 -0
  96. package/test/components/update-file.test.tsx +214 -0
  97. package/test/rendering/formatting.test.tsx +9 -3
  98. package/testing/extend-expect.test.tsx +126 -0
  99. package/testing/extend-expect.ts +123 -23
  100. package/testing/vitest.d.ts +9 -1
  101. package/tsconfig.json +2 -1
  102. package/dist/src/write-output.browser.d.ts +0 -2
  103. package/dist/src/write-output.browser.d.ts.map +0 -1
  104. package/dist/src/write-output.browser.js +0 -4
  105. package/src/write-output.browser.ts +0 -4
package/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # Changelog - @alloy-js/core
2
2
 
3
+ ## 0.18.2
4
+
5
+ ### Bug Fixes
6
+
7
+ - Fix invalid .d.ts import
8
+
9
+
10
+ ## 0.18.1
11
+
12
+ ### Bug Fixes
13
+
14
+ - [#232](https://github.com/alloy-framework/alloy/pull/232) Improve `toRenderTo` test matcher to work better with SourceFiles
15
+
16
+
3
17
  ## 0.18.0
4
18
 
5
19
  ### Bug Fixes
@@ -0,0 +1,90 @@
1
+ import { Children } from "../runtime/component.js";
2
+ export interface AppendFileProps {
3
+ /**
4
+ * The path to the file to read and append content to.
5
+ */
6
+ path: string;
7
+ /**
8
+ * List of region IDs to append to. Defaults to ["append"] if not specified.
9
+ * Each region corresponds to an AppendRegion child component.
10
+ */
11
+ regions?: string[];
12
+ /**
13
+ * AppendRegion children components that define content to append.
14
+ */
15
+ children?: Children;
16
+ }
17
+ /**
18
+ * A component that reads a file and returns content with new content appended
19
+ * at the end or within specific regions marked by alloy-\{region name\}-start/alloy-\{region name\}-end sigils.
20
+ *
21
+ * The component can append content in two ways:
22
+ * 1. **Simple append**: Content is appended to the end of the file
23
+ * 2. **Region-based append**: Content is appended before the end sigil on its own line
24
+ *
25
+ * Region sigils are line-based - any line containing "alloy-\{region name\}-start" or "alloy-\{region name\}-end"
26
+ * is considered a sigil.
27
+ *
28
+ * @example
29
+ * Simple append to end of file:
30
+ * ```tsx
31
+ * <AppendFile path="output.txt">
32
+ * <AppendRegion id="append">New content to add</AppendRegion>
33
+ * </AppendFile>
34
+ *
35
+ * // Returns:
36
+ * // Original file content
37
+ * // New content to add
38
+ * ```
39
+ *
40
+ * @example
41
+ * Append to specific regions:
42
+ * ```tsx
43
+ * // File content before:
44
+ * // Header content
45
+ * // <!-- alloy-main-start -->
46
+ * // <!-- alloy-main-end -->
47
+ * // Footer content
48
+ *
49
+ * <AppendFile path="template.html" regions={["main"]}>
50
+ * <AppendRegion id="main">New main content</AppendRegion>
51
+ * </AppendFile>
52
+ *
53
+ * // Returns:
54
+ * // Header content
55
+ * // <!-- alloy-main-start -->
56
+ * // New main content
57
+ * // <!-- alloy-main-end -->
58
+ * // Footer content
59
+ * ```
60
+ */
61
+ export declare function AppendFile(props: AppendFileProps): Children;
62
+ export interface AppendRegionPropsWithChildren {
63
+ /**
64
+ * The ID of the region.
65
+ */
66
+ id: string;
67
+ /**
68
+ * The content to append to the region.
69
+ */
70
+ children: Children;
71
+ }
72
+ export interface AppendRegionPropsWithContent {
73
+ /**
74
+ * The ID of the region.
75
+ */
76
+ id: string;
77
+ /**
78
+ * The content to append to the region.
79
+ */
80
+ content: Children;
81
+ }
82
+ export interface AppendRegionPropsBase {
83
+ /**
84
+ * The ID of the region.
85
+ */
86
+ id: string;
87
+ }
88
+ export type AppendRegionProps = AppendRegionPropsWithChildren | AppendRegionPropsWithContent | AppendRegionPropsBase;
89
+ export declare function AppendRegion(props: AppendRegionProps): void;
90
+ //# sourceMappingURL=AppendFile.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AppendFile.d.ts","sourceRoot":"","sources":["../../../src/components/AppendFile.tsx"],"names":[],"mappings":"AAKA,OAAO,EAAE,QAAQ,EAAsB,MAAM,yBAAyB,CAAC;AAavE,MAAM,WAAW,eAAe;IAC9B;;OAEG;IACH,IAAI,EAAE,MAAM,CAAC;IAEb;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IAEnB;;OAEG;IACH,QAAQ,CAAC,EAAE,QAAQ,CAAC;CACrB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,eAAe,GAAG,QAAQ,CA0K3D;AAED,MAAM,WAAW,6BAA6B;IAC5C;;OAEG;IACH,EAAE,EAAE,MAAM,CAAC;IAEX;;OAEG;IACH,QAAQ,EAAE,QAAQ,CAAC;CACpB;AAED,MAAM,WAAW,4BAA4B;IAC3C;;OAEG;IACH,EAAE,EAAE,MAAM,CAAC;IAEX;;OAEG;IACH,OAAO,EAAE,QAAQ,CAAC;CACnB;AAED,MAAM,WAAW,qBAAqB;IACpC;;OAEG;IACH,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,MAAM,MAAM,iBAAiB,GACzB,6BAA6B,GAC7B,4BAA4B,GAC5B,qBAAqB,CAAC;AAE1B,wBAAgB,YAAY,CAAC,KAAK,EAAE,iBAAiB,QAKpD"}
@@ -0,0 +1,226 @@
1
+ import { createComponent as _$createComponent } from "@alloy-js/core/jsx-runtime";
2
+ import { computed } from "@vue/reactivity";
3
+ import { join } from "pathe";
4
+ import { useContext } from "../context.js";
5
+ import { SourceDirectoryContext } from "../context/source-directory.js";
6
+ import { createFileResource } from "../resource.js";
7
+ import { isComponentCreator } from "../runtime/component.js";
8
+ import { childrenArray } from "../utils.js";
9
+ import { SourceFile } from "./SourceFile.js";
10
+
11
+ // Regular expression for finding all sigils at once
12
+ const SIGIL_REGEX = /^(\s*)(.*alloy-(.+)-(start|end).*)$/gm;
13
+ /**
14
+ * A component that reads a file and returns content with new content appended
15
+ * at the end or within specific regions marked by alloy-\{region name\}-start/alloy-\{region name\}-end sigils.
16
+ *
17
+ * The component can append content in two ways:
18
+ * 1. **Simple append**: Content is appended to the end of the file
19
+ * 2. **Region-based append**: Content is appended before the end sigil on its own line
20
+ *
21
+ * Region sigils are line-based - any line containing "alloy-\{region name\}-start" or "alloy-\{region name\}-end"
22
+ * is considered a sigil.
23
+ *
24
+ * @example
25
+ * Simple append to end of file:
26
+ * ```tsx
27
+ * <AppendFile path="output.txt">
28
+ * <AppendRegion id="append">New content to add</AppendRegion>
29
+ * </AppendFile>
30
+ *
31
+ * // Returns:
32
+ * // Original file content
33
+ * // New content to add
34
+ * ```
35
+ *
36
+ * @example
37
+ * Append to specific regions:
38
+ * ```tsx
39
+ * // File content before:
40
+ * // Header content
41
+ * // <!-- alloy-main-start -->
42
+ * // <!-- alloy-main-end -->
43
+ * // Footer content
44
+ *
45
+ * <AppendFile path="template.html" regions={["main"]}>
46
+ * <AppendRegion id="main">New main content</AppendRegion>
47
+ * </AppendFile>
48
+ *
49
+ * // Returns:
50
+ * // Header content
51
+ * // <!-- alloy-main-start -->
52
+ * // New main content
53
+ * // <!-- alloy-main-end -->
54
+ * // Footer content
55
+ * ```
56
+ */
57
+ export function AppendFile(props) {
58
+ const regions = props.regions || ["append"];
59
+
60
+ // Get all children and filter for AppendRegion components
61
+ const children = childrenArray(() => props.children);
62
+ const appendRegions = {};
63
+
64
+ // Check if we have any AppendRegion components
65
+ let hasAppendRegions = false;
66
+ for (const child of children) {
67
+ if (isComponentCreator(child, AppendRegion)) {
68
+ hasAppendRegions = true;
69
+ const regionProps = child.props;
70
+ let content;
71
+ if ("children" in regionProps && regionProps.children !== undefined) {
72
+ content = regionProps.children;
73
+ } else if ("content" in regionProps) {
74
+ content = regionProps.content;
75
+ } else {
76
+ throw new Error(`AppendRegion "${regionProps.id}" must have either children or content`);
77
+ }
78
+ appendRegions[regionProps.id] = content;
79
+ }
80
+ }
81
+
82
+ // If no AppendRegion components found, treat all children as content for the default "append" region
83
+ if (!hasAppendRegions && children.length > 0) {
84
+ appendRegions["append"] = children;
85
+ }
86
+
87
+ // Validate that all requested regions have corresponding AppendRegion children
88
+ for (const regionId of regions) {
89
+ if (!(regionId in appendRegions)) {
90
+ throw new Error(`Region "${regionId}" specified but no corresponding AppendRegion child found`);
91
+ }
92
+ }
93
+
94
+ // Read existing file content or start with empty string
95
+ const parentDirectory = useContext(SourceDirectoryContext);
96
+ const fullPath = join(parentDirectory ? parentDirectory.path : "", props.path);
97
+ const currentContents = createFileResource(fullPath);
98
+ const newFileContent = computed(() => {
99
+ if (currentContents.loading) {
100
+ return;
101
+ }
102
+ const fileContent = currentContents.error ? "" : currentContents.data;
103
+
104
+ // Find all sigils in the file
105
+ const sigilInfo = {};
106
+ const endSigils = [];
107
+
108
+ // Reset regex and find all sigils
109
+ SIGIL_REGEX.lastIndex = 0;
110
+ let match;
111
+ while ((match = SIGIL_REGEX.exec(fileContent)) !== null) {
112
+ const indent = match[1];
113
+ const fullLine = match[0];
114
+ const regionId = match[3];
115
+ const sigilType = match[4];
116
+ const index = match.index;
117
+
118
+ // Initialize sigil info for this region if not exists
119
+ if (!sigilInfo[regionId]) {
120
+ sigilInfo[regionId] = {
121
+ id: regionId,
122
+ start: null,
123
+ end: null
124
+ };
125
+ }
126
+ if (sigilType === "start") {
127
+ sigilInfo[regionId].start = index;
128
+ } else if (sigilType === "end") {
129
+ sigilInfo[regionId].end = index;
130
+
131
+ // If this is a region we care about, track it for processing
132
+ if (regions.includes(regionId)) {
133
+ endSigils.push({
134
+ regionId,
135
+ index,
136
+ indent,
137
+ line: fullLine
138
+ });
139
+ }
140
+ }
141
+ }
142
+
143
+ // Validate regions - check for unclosed regions
144
+ for (const regionId of regions) {
145
+ const info = sigilInfo[regionId];
146
+ if (info && info.start !== null && info.end === null) {
147
+ throw new Error(`Region "${regionId}" has start sigil but no corresponding end sigil`);
148
+ }
149
+ }
150
+
151
+ // Check if we have any sigils to process
152
+ if (endSigils.length === 0) {
153
+ // No sigils found, append all regions to the end
154
+ const result = [fileContent];
155
+ for (const regionId of regions) {
156
+ if (fileContent && !fileContent.endsWith("\n")) {
157
+ result.push("\n");
158
+ }
159
+ result.push(appendRegions[regionId]);
160
+ }
161
+ return _$createComponent(SourceFile, {
162
+ get path() {
163
+ return props.path;
164
+ },
165
+ filetype: "text/plain",
166
+ children: result
167
+ });
168
+ }
169
+
170
+ // Sort end sigils by their position in the file
171
+ endSigils.sort((a, b) => a.index - b.index);
172
+
173
+ // Process content with sigils
174
+ const result = [];
175
+ let lastIndex = 0;
176
+
177
+ // Process each end sigil in order
178
+ for (const {
179
+ regionId,
180
+ index,
181
+ indent,
182
+ line
183
+ } of endSigils) {
184
+ // Add content before this sigil
185
+ if (index > lastIndex) {
186
+ const beforeContent = fileContent.substring(lastIndex, index);
187
+ if (beforeContent) {
188
+ result.push(beforeContent);
189
+ }
190
+ }
191
+
192
+ // Add the new content with proper indentation
193
+ result.push(indent);
194
+ result.push(appendRegions[regionId]);
195
+ result.push("\n");
196
+
197
+ // Add the sigil line
198
+ result.push(line);
199
+
200
+ // Update last processed index
201
+ lastIndex = index + line.length;
202
+ }
203
+
204
+ // Add any remaining content after the last sigil
205
+ if (lastIndex < fileContent.length) {
206
+ const remainingPart = fileContent.substring(lastIndex);
207
+ if (remainingPart) {
208
+ result.push(remainingPart);
209
+ }
210
+ }
211
+ return result;
212
+ });
213
+ return _$createComponent(SourceFile, {
214
+ get path() {
215
+ return props.path;
216
+ },
217
+ filetype: "text/plain",
218
+ children: newFileContent
219
+ });
220
+ }
221
+ export function AppendRegion(props) {
222
+ /**
223
+ * This component does nothing except hold props which are retrieved by
224
+ * the `AppendFile` component.
225
+ */
226
+ }
@@ -0,0 +1,12 @@
1
+ export interface CopyFileProps {
2
+ /**
3
+ * The path to write the copy to, relative to the containing directory.
4
+ */
5
+ path: string;
6
+ /**
7
+ * The path to the file to copy.
8
+ */
9
+ src: string;
10
+ }
11
+ export declare function CopyFile(props: CopyFileProps): void;
12
+ //# sourceMappingURL=CopyFile.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CopyFile.d.ts","sourceRoot":"","sources":["../../../src/components/CopyFile.tsx"],"names":[],"mappings":"AAMA,MAAM,WAAW,aAAa;IAC5B;;OAEG;IACH,IAAI,EAAE,MAAM,CAAC;IAEb;;OAEG;IACH,GAAG,EAAE,MAAM,CAAC;CACb;AAED,wBAAgB,QAAQ,CAAC,KAAK,EAAE,aAAa,QAU5C"}
@@ -0,0 +1,15 @@
1
+ import { join } from "pathe";
2
+ import { useContext } from "../context.js";
3
+ import { SourceDirectoryContext } from "../context/source-directory.js";
4
+ import { getContext } from "../reactivity.js";
5
+ export function CopyFile(props) {
6
+ const parentDirectory = useContext(SourceDirectoryContext);
7
+ const context = {
8
+ path: join(parentDirectory ? parentDirectory.path : "", props.path),
9
+ sourcePath: props.src
10
+ };
11
+ parentDirectory?.addContent(context);
12
+ const nodeContext = getContext();
13
+ nodeContext.meta ??= {};
14
+ nodeContext.meta.copyFile = context;
15
+ }
@@ -0,0 +1,84 @@
1
+ import { Children } from "../runtime/component.js";
2
+ export interface TemplateFileProps {
3
+ /**
4
+ * The path to write the compiled template to.
5
+ */
6
+ path: string;
7
+ /**
8
+ * The file path of the template.
9
+ */
10
+ src: string;
11
+ /**
12
+ * Template variable children components.
13
+ */
14
+ children?: Children;
15
+ }
16
+ /**
17
+ * A component that reads a template file and replaces variable placeholders
18
+ * with actual values.
19
+ *
20
+ * Template files can contain variable placeholders in the format
21
+ * `{{ variable_name }}` which will be replaced with values from `TemplateVariable`
22
+ * children components. Whitespace around variable names is ignored, so
23
+ * `{{ name }}`, `{{name}}`, and `{{ name }}` are all equivalent.
24
+ *
25
+ * @example
26
+ * Basic usage with template variables:
27
+ * ```tsx
28
+ * // Template file content (greeting.txt):
29
+ * // "Hello {{ name }}! You are {{ age }} years old."
30
+ *
31
+ * <TemplateFile src="greeting.txt" path="output.txt">
32
+ * <TemplateVariable name="name" value="John" />
33
+ * <TemplateVariable name="age" value="25" />
34
+ * </TemplateFile>
35
+ * ```
36
+ *
37
+ * @example
38
+ * Using children instead of value prop:
39
+ * ```tsx
40
+ * // Template file content (welcome.txt):
41
+ * // "Welcome {{ greeting }}!"
42
+ *
43
+ * <TemplateFile src="welcome.txt" path="output.txt">
44
+ * <TemplateVariable name="greeting">Hello World</TemplateVariable>
45
+ * </TemplateFile>
46
+ * ```
47
+ *
48
+ * @example
49
+ * Complex template with multiple variables:
50
+ * ```tsx
51
+ * // Template file content (profile.txt):
52
+ * // "Name: {{ name }}\nAge: {{ age }}\nLocation: {{ location }}"
53
+ *
54
+ * <TemplateFile src="profile.txt" path="profile-output.txt">
55
+ * <TemplateVariable name="name" value="Alice" />
56
+ * <TemplateVariable name="age">30</TemplateVariable>
57
+ * <TemplateVariable name="location" value="New York" />
58
+ * </TemplateFile>
59
+ * ```
60
+ */
61
+ export declare function TemplateFile(props: TemplateFileProps): Children;
62
+ export interface TemplateVariablePropsWithChildren {
63
+ /**
64
+ * The name of the variable.
65
+ */
66
+ name: string;
67
+ /**
68
+ * The value of the variable.
69
+ */
70
+ children: Children;
71
+ }
72
+ export interface TemplateVariablePropsWithValue {
73
+ /**
74
+ * The name of the variable.
75
+ */
76
+ name: string;
77
+ /**
78
+ * The value of the variable.
79
+ */
80
+ value: string;
81
+ }
82
+ export type TemplateVariableProps = TemplateVariablePropsWithChildren | TemplateVariablePropsWithValue;
83
+ export declare function TemplateVariable(props: TemplateVariableProps): void;
84
+ //# sourceMappingURL=TemplateFile.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TemplateFile.d.ts","sourceRoot":"","sources":["../../../src/components/TemplateFile.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,QAAQ,EAAsB,MAAM,yBAAyB,CAAC;AAIvE,MAAM,WAAW,iBAAiB;IAChC;;OAEG;IACH,IAAI,EAAE,MAAM,CAAC;IAEb;;OAEG;IACH,GAAG,EAAE,MAAM,CAAC;IAEZ;;OAEG;IACH,QAAQ,CAAC,EAAE,QAAQ,CAAC;CACrB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,iBAAiB,GAAG,QAAQ,CAyF/D;AAED,MAAM,WAAW,iCAAiC;IAChD;;OAEG;IACH,IAAI,EAAE,MAAM,CAAC;IAEb;;OAEG;IACH,QAAQ,EAAE,QAAQ,CAAC;CACpB;AAED,MAAM,WAAW,8BAA8B;IAC7C;;OAEG;IACH,IAAI,EAAE,MAAM,CAAC;IAEb;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,MAAM,qBAAqB,GAC7B,iCAAiC,GACjC,8BAA8B,CAAC;AAEnC,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,qBAAqB,QAK5D"}
@@ -0,0 +1,133 @@
1
+ import { createComponent as _$createComponent } from "@alloy-js/core/jsx-runtime";
2
+ import { computed } from "@vue/reactivity";
3
+ import { createFileResource } from "../resource.js";
4
+ import { isComponentCreator } from "../runtime/component.js";
5
+ import { childrenArray } from "../utils.js";
6
+ import { SourceFile } from "./SourceFile.js";
7
+ /**
8
+ * A component that reads a template file and replaces variable placeholders
9
+ * with actual values.
10
+ *
11
+ * Template files can contain variable placeholders in the format
12
+ * `{{ variable_name }}` which will be replaced with values from `TemplateVariable`
13
+ * children components. Whitespace around variable names is ignored, so
14
+ * `{{ name }}`, `{{name}}`, and `{{ name }}` are all equivalent.
15
+ *
16
+ * @example
17
+ * Basic usage with template variables:
18
+ * ```tsx
19
+ * // Template file content (greeting.txt):
20
+ * // "Hello {{ name }}! You are {{ age }} years old."
21
+ *
22
+ * <TemplateFile src="greeting.txt" path="output.txt">
23
+ * <TemplateVariable name="name" value="John" />
24
+ * <TemplateVariable name="age" value="25" />
25
+ * </TemplateFile>
26
+ * ```
27
+ *
28
+ * @example
29
+ * Using children instead of value prop:
30
+ * ```tsx
31
+ * // Template file content (welcome.txt):
32
+ * // "Welcome {{ greeting }}!"
33
+ *
34
+ * <TemplateFile src="welcome.txt" path="output.txt">
35
+ * <TemplateVariable name="greeting">Hello World</TemplateVariable>
36
+ * </TemplateFile>
37
+ * ```
38
+ *
39
+ * @example
40
+ * Complex template with multiple variables:
41
+ * ```tsx
42
+ * // Template file content (profile.txt):
43
+ * // "Name: {{ name }}\nAge: {{ age }}\nLocation: {{ location }}"
44
+ *
45
+ * <TemplateFile src="profile.txt" path="profile-output.txt">
46
+ * <TemplateVariable name="name" value="Alice" />
47
+ * <TemplateVariable name="age">30</TemplateVariable>
48
+ * <TemplateVariable name="location" value="New York" />
49
+ * </TemplateFile>
50
+ * ```
51
+ */
52
+ export function TemplateFile(props) {
53
+ // Get all children and filter for TemplateVariable components
54
+ const children = childrenArray(() => props.children);
55
+ const templateVariables = {};
56
+
57
+ // Extract variable values from TemplateVariable children
58
+ for (const child of children) {
59
+ if (!isComponentCreator(child, TemplateVariable)) {
60
+ continue;
61
+ }
62
+ const variableProps = child.props;
63
+ let value;
64
+ if ("children" in variableProps && variableProps.children !== undefined) {
65
+ value = variableProps.children;
66
+ } else if ("value" in variableProps) {
67
+ value = variableProps.value;
68
+ } else {
69
+ throw new Error(`TemplateVariable "${variableProps.name}" must have either children or value`);
70
+ }
71
+ templateVariables[variableProps.name] = value;
72
+ }
73
+ const templateResource = createFileResource(props.src);
74
+ const fileContent = computed(() => {
75
+ if (templateResource.loading) {
76
+ return;
77
+ }
78
+ if (templateResource.error) {
79
+ throw new Error(`Failed to read template file "${props.src}": ${templateResource.error}`);
80
+ }
81
+ const templateContent = templateResource.data;
82
+
83
+ // Parse template and replace variables
84
+ const result = [];
85
+ let lastIndex = 0;
86
+
87
+ // Match {{ var_name }} patterns
88
+ const variableRegex = /\{\{\s*(\w+)\s*\}\}/g;
89
+ let match;
90
+ while ((match = variableRegex.exec(templateContent)) !== null) {
91
+ const [fullMatch, variableName] = match;
92
+ const matchStart = match.index;
93
+
94
+ // Add content before the variable
95
+ if (matchStart > lastIndex) {
96
+ const beforeContent = templateContent.slice(lastIndex, matchStart);
97
+ if (beforeContent) {
98
+ result.push(beforeContent);
99
+ }
100
+ }
101
+
102
+ // Add the variable value
103
+ if (variableName in templateVariables) {
104
+ result.push(templateVariables[variableName]);
105
+ } else {
106
+ throw new Error(`Template variable "${variableName}" not found in TemplateVariable children`);
107
+ }
108
+ lastIndex = matchStart + fullMatch.length;
109
+ }
110
+
111
+ // Add remaining content after the last variable
112
+ if (lastIndex < templateContent.length) {
113
+ const remainingContent = templateContent.slice(lastIndex);
114
+ if (remainingContent) {
115
+ result.push(remainingContent);
116
+ }
117
+ }
118
+ return result;
119
+ });
120
+ return _$createComponent(SourceFile, {
121
+ get path() {
122
+ return props.path;
123
+ },
124
+ filetype: "text/plain",
125
+ children: fileContent
126
+ });
127
+ }
128
+ export function TemplateVariable(props) {
129
+ /**
130
+ * This component does nothing except hold props which are retrieved by
131
+ * the `TemplateFile` component.
132
+ */
133
+ }
@@ -0,0 +1,34 @@
1
+ import { Children } from "../runtime/component.js";
2
+ /**
3
+ * Props for the UpdateFile component.
4
+ */
5
+ export interface UpdateFileProps {
6
+ /** The relative path to the file to update or create */
7
+ path: string;
8
+ /** Optional path to a file containing default content to use when the target file doesn't exist */
9
+ defaultContentPath?: string;
10
+ /** Optional default content to use when the target file doesn't exist */
11
+ defaultContent?: Children;
12
+ /** Function that receives the current file contents and returns the new content */
13
+ children: (currentContents: string | null) => Children;
14
+ }
15
+ /**
16
+ * A component for updating existing files or initializing new files.
17
+ *
18
+ * This component allows you to read the current contents of a file and generate
19
+ * new content based on those contents. If the file doesn't exist, it can use
20
+ * default content from either a file path or inline content.
21
+ *
22
+ * @example
23
+ * ```tsx
24
+ * <UpdateFile path="config.json" defaultContent="{}">
25
+ * {(currentContents) => {
26
+ * const config = currentContents ? JSON.parse(currentContents) : {};
27
+ * config.newProperty = "value";
28
+ * return JSON.stringify(config, null, 2);
29
+ * }}
30
+ * </UpdateFile>
31
+ * ```
32
+ */
33
+ export declare function UpdateFile(props: UpdateFileProps): Children;
34
+ //# sourceMappingURL=UpdateFile.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"UpdateFile.d.ts","sourceRoot":"","sources":["../../../src/components/UpdateFile.tsx"],"names":[],"mappings":"AAKA,OAAO,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AAEnD;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,wDAAwD;IACxD,IAAI,EAAE,MAAM,CAAC;IACb,mGAAmG;IACnG,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,yEAAyE;IACzE,cAAc,CAAC,EAAE,QAAQ,CAAC;IAC1B,mFAAmF;IACnF,QAAQ,EAAE,CAAC,eAAe,EAAE,MAAM,GAAG,IAAI,KAAK,QAAQ,CAAC;CACxD;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,eAAe,YA8ChD"}