@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.
- package/CHANGELOG.md +14 -0
- package/dist/src/components/AppendFile.d.ts +90 -0
- package/dist/src/components/AppendFile.d.ts.map +1 -0
- package/dist/src/components/AppendFile.js +226 -0
- package/dist/src/components/CopyFile.d.ts +12 -0
- package/dist/src/components/CopyFile.d.ts.map +1 -0
- package/dist/src/components/CopyFile.js +15 -0
- package/dist/src/components/TemplateFile.d.ts +84 -0
- package/dist/src/components/TemplateFile.d.ts.map +1 -0
- package/dist/src/components/TemplateFile.js +133 -0
- package/dist/src/components/UpdateFile.d.ts +34 -0
- package/dist/src/components/UpdateFile.d.ts.map +1 -0
- package/dist/src/components/UpdateFile.js +66 -0
- package/dist/src/components/index.d.ts +4 -0
- package/dist/src/components/index.d.ts.map +1 -1
- package/dist/src/components/index.js +4 -0
- package/dist/src/components/stc/index.d.ts +4 -0
- package/dist/src/components/stc/index.d.ts.map +1 -1
- package/dist/src/components/stc/index.js +4 -0
- package/dist/src/context/source-directory.d.ts +3 -3
- package/dist/src/context/source-directory.d.ts.map +1 -1
- package/dist/src/context/source-file.d.ts +4 -0
- package/dist/src/context/source-file.d.ts.map +1 -1
- package/dist/src/debug.d.ts.map +1 -1
- package/dist/src/debug.js +4 -1
- package/dist/src/host/alloy-host.browser.d.ts +11 -0
- package/dist/src/host/alloy-host.browser.d.ts.map +1 -0
- package/dist/src/host/alloy-host.browser.js +31 -0
- package/dist/src/host/alloy-host.d.ts +11 -0
- package/dist/src/host/alloy-host.d.ts.map +1 -0
- package/dist/src/host/alloy-host.js +143 -0
- package/dist/src/host/interface.d.ts +144 -0
- package/dist/src/host/interface.d.ts.map +1 -0
- package/dist/src/host/interface.js +1 -0
- package/dist/src/index.browser.d.ts +1 -1
- package/dist/src/index.browser.d.ts.map +1 -1
- package/dist/src/index.browser.js +2 -2
- package/dist/src/reactivity.js +2 -2
- package/dist/src/render.d.ts +10 -2
- package/dist/src/render.d.ts.map +1 -1
- package/dist/src/render.js +20 -1
- package/dist/src/resource.d.ts +80 -0
- package/dist/src/resource.d.ts.map +1 -0
- package/dist/src/resource.js +118 -0
- package/dist/src/scheduler.d.ts +6 -0
- package/dist/src/scheduler.d.ts.map +1 -1
- package/dist/src/scheduler.js +36 -0
- package/dist/src/write-output.d.ts +1 -1
- package/dist/src/write-output.d.ts.map +1 -1
- package/dist/src/write-output.js +40 -21
- package/dist/test/components/append-file.test.d.ts +2 -0
- package/dist/test/components/append-file.test.d.ts.map +1 -0
- package/dist/test/components/append-file.test.js +281 -0
- package/dist/test/components/copy-file.test.d.ts +2 -0
- package/dist/test/components/copy-file.test.d.ts.map +1 -0
- package/dist/test/components/copy-file.test.js +94 -0
- package/dist/test/components/source-file.test.d.ts.map +1 -1
- package/dist/test/components/template-file.test.d.ts +2 -0
- package/dist/test/components/template-file.test.d.ts.map +1 -0
- package/dist/test/components/template-file.test.js +133 -0
- package/dist/test/components/update-file.test.d.ts +2 -0
- package/dist/test/components/update-file.test.d.ts.map +1 -0
- package/dist/test/components/update-file.test.js +169 -0
- package/dist/test/rendering/formatting.test.d.ts.map +1 -1
- package/dist/testing/extend-expect.d.ts +0 -15
- package/dist/testing/extend-expect.d.ts.map +1 -1
- package/dist/testing/extend-expect.js +89 -12
- package/dist/testing/extend-expect.test.d.ts +2 -0
- package/dist/testing/extend-expect.test.d.ts.map +1 -0
- package/dist/testing/extend-expect.test.js +149 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +4 -4
- package/src/components/AppendFile.tsx +294 -0
- package/src/components/CopyFile.tsx +29 -0
- package/src/components/TemplateFile.tsx +193 -0
- package/src/components/UpdateFile.tsx +86 -0
- package/src/components/index.tsx +4 -0
- package/src/components/stc/index.ts +4 -0
- package/src/context/source-directory.ts +5 -3
- package/src/context/source-file.ts +5 -0
- package/src/debug.ts +4 -1
- package/src/host/alloy-host.browser.ts +56 -0
- package/src/host/alloy-host.ts +160 -0
- package/src/host/interface.ts +153 -0
- package/src/index.browser.ts +1 -1
- package/src/reactivity.ts +2 -2
- package/src/render.ts +44 -5
- package/src/resource.ts +152 -0
- package/src/scheduler.ts +39 -0
- package/src/write-output.ts +49 -19
- package/temp/api.json +2009 -546
- package/test/components/append-file.test.tsx +275 -0
- package/test/components/copy-file.test.tsx +98 -0
- package/test/components/source-file.test.tsx +5 -2
- package/test/components/template-file.test.tsx +127 -0
- package/test/components/update-file.test.tsx +214 -0
- package/test/rendering/formatting.test.tsx +9 -3
- package/testing/extend-expect.test.tsx +126 -0
- package/testing/extend-expect.ts +123 -23
- package/testing/vitest.d.ts +9 -1
- package/tsconfig.json +2 -1
- package/dist/src/write-output.browser.d.ts +0 -2
- package/dist/src/write-output.browser.d.ts.map +0 -1
- package/dist/src/write-output.browser.js +0 -4
- package/src/write-output.browser.ts +0 -4
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"copy-file.test.d.ts","sourceRoot":"","sources":["../../../test/components/copy-file.test.tsx"],"names":[],"mappings":"AAQA,OAAO,gCAAgC,CAAC"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { createComponent as _$createComponent } from "@alloy-js/core/jsx-runtime";
|
|
2
|
+
import { existsSync, readFileSync, unlinkSync, writeFileSync } from "fs";
|
|
3
|
+
import { tmpdir } from "os";
|
|
4
|
+
import { join } from "path";
|
|
5
|
+
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
|
6
|
+
import { CopyFile } from "../../src/components/CopyFile.js";
|
|
7
|
+
import { SourceDirectory } from "../../src/components/SourceDirectory.js";
|
|
8
|
+
import { render } from "../../src/render.js";
|
|
9
|
+
import { writeOutput } from "../../src/write-output.js";
|
|
10
|
+
import "../../testing/extend-expect.js";
|
|
11
|
+
describe("CopyFile", () => {
|
|
12
|
+
let testDir;
|
|
13
|
+
let sourceFile;
|
|
14
|
+
let targetFile;
|
|
15
|
+
beforeEach(() => {
|
|
16
|
+
// Create unique temporary paths for each test
|
|
17
|
+
testDir = tmpdir();
|
|
18
|
+
sourceFile = join(testDir, "source.txt");
|
|
19
|
+
targetFile = join(testDir, "target.txt");
|
|
20
|
+
});
|
|
21
|
+
afterEach(() => {
|
|
22
|
+
// Clean up test files after each test
|
|
23
|
+
if (existsSync(sourceFile)) {
|
|
24
|
+
unlinkSync(sourceFile);
|
|
25
|
+
}
|
|
26
|
+
if (existsSync(targetFile)) {
|
|
27
|
+
unlinkSync(targetFile);
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
it("should create copy file context with correct paths", async () => {
|
|
31
|
+
writeFileSync(sourceFile, "Hello, World!", "utf-8");
|
|
32
|
+
const result = _$createComponent(SourceDirectory, {
|
|
33
|
+
path: testDir,
|
|
34
|
+
get children() {
|
|
35
|
+
return _$createComponent(CopyFile, {
|
|
36
|
+
src: sourceFile,
|
|
37
|
+
path: "target.txt"
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
const output = render(result);
|
|
42
|
+
await writeOutput(output);
|
|
43
|
+
expect(output.contents).toHaveLength(1);
|
|
44
|
+
const copyFileOutput = output.contents[0];
|
|
45
|
+
expect(copyFileOutput.kind).toBe("file");
|
|
46
|
+
expect(copyFileOutput.sourcePath).toBe(sourceFile);
|
|
47
|
+
expect(copyFileOutput.path).toBe(join(testDir, "target.txt"));
|
|
48
|
+
expect(existsSync(targetFile)).toBe(true);
|
|
49
|
+
const targetContent = readFileSync(targetFile, "utf-8");
|
|
50
|
+
expect(targetContent).toBe("Hello, World!");
|
|
51
|
+
});
|
|
52
|
+
it("should handle relative paths correctly", async () => {
|
|
53
|
+
// Create source file
|
|
54
|
+
writeFileSync(sourceFile, "Relative path test", "utf-8");
|
|
55
|
+
const result = _$createComponent(SourceDirectory, {
|
|
56
|
+
path: testDir,
|
|
57
|
+
get children() {
|
|
58
|
+
return _$createComponent(SourceDirectory, {
|
|
59
|
+
path: "subdir",
|
|
60
|
+
get children() {
|
|
61
|
+
return _$createComponent(CopyFile, {
|
|
62
|
+
src: sourceFile,
|
|
63
|
+
path: "nested-target.txt"
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
const output = render(result);
|
|
70
|
+
|
|
71
|
+
// Find the copy file in the nested directory
|
|
72
|
+
const subdir = output.contents.find(item => item.kind === "directory" && item.path === join(testDir, "subdir"));
|
|
73
|
+
expect(subdir).toBeDefined();
|
|
74
|
+
if (subdir && subdir.kind === "directory") {
|
|
75
|
+
expect(subdir.contents).toHaveLength(1);
|
|
76
|
+
const copyFile = subdir.contents[0];
|
|
77
|
+
expect(copyFile.kind).toBe("file");
|
|
78
|
+
if (copyFile.kind === "file" && "sourcePath" in copyFile) {
|
|
79
|
+
expect(copyFile.sourcePath).toBe(sourceFile);
|
|
80
|
+
expect(copyFile.path).toBe(join(testDir, "subdir", "nested-target.txt"));
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
it("should throw error when used without SourceDirectory context", () => {
|
|
85
|
+
// Create source file
|
|
86
|
+
writeFileSync(sourceFile, "Error test", "utf-8");
|
|
87
|
+
expect(() => {
|
|
88
|
+
render(_$createComponent(CopyFile, {
|
|
89
|
+
src: sourceFile,
|
|
90
|
+
path: "target.txt"
|
|
91
|
+
}));
|
|
92
|
+
}).toThrow("Copy file doesn't have parent directory");
|
|
93
|
+
});
|
|
94
|
+
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"source-file.test.d.ts","sourceRoot":"","sources":["../../../test/components/source-file.test.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"source-file.test.d.ts","sourceRoot":"","sources":["../../../test/components/source-file.test.tsx"],"names":[],"mappings":"AAWA,OAAO,gCAAgC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"template-file.test.d.ts","sourceRoot":"","sources":["../../../test/components/template-file.test.tsx"],"names":[],"mappings":"AASA,OAAO,gCAAgC,CAAC"}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { createComponent as _$createComponent } from "@alloy-js/core/jsx-runtime";
|
|
2
|
+
import { writeFileSync } from "fs";
|
|
3
|
+
import { tmpdir } from "os";
|
|
4
|
+
import { join } from "path";
|
|
5
|
+
import { describe, expect, it } from "vitest";
|
|
6
|
+
import { TemplateFile, TemplateVariable } from "../../src/components/TemplateFile.js";
|
|
7
|
+
import { renderAsync } from "../../src/render.js";
|
|
8
|
+
import "../../testing/extend-expect.js";
|
|
9
|
+
import { d } from "../../testing/render.js";
|
|
10
|
+
describe("TemplateFile", () => {
|
|
11
|
+
it("should replace template variables with values", async () => {
|
|
12
|
+
// Create a temporary template file
|
|
13
|
+
const templatePath = join(tmpdir(), "test-template.txt");
|
|
14
|
+
const templateContent = "Hello {{ name }}! You are {{ age }} years old.";
|
|
15
|
+
writeFileSync(templatePath, templateContent);
|
|
16
|
+
const result = _$createComponent(TemplateFile, {
|
|
17
|
+
src: templatePath,
|
|
18
|
+
path: "output.txt",
|
|
19
|
+
get children() {
|
|
20
|
+
return [_$createComponent(TemplateVariable, {
|
|
21
|
+
name: "name",
|
|
22
|
+
value: "John"
|
|
23
|
+
}), _$createComponent(TemplateVariable, {
|
|
24
|
+
name: "age",
|
|
25
|
+
value: "25"
|
|
26
|
+
})];
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
await expect(result).toRenderToAsync("Hello John! You are 25 years old.");
|
|
30
|
+
});
|
|
31
|
+
it("should handle template variables with children", async () => {
|
|
32
|
+
const templatePath = join(tmpdir(), "test-template-children.txt");
|
|
33
|
+
const templateContent = "Welcome {{ greeting }}!";
|
|
34
|
+
writeFileSync(templatePath, templateContent);
|
|
35
|
+
const result = _$createComponent(TemplateFile, {
|
|
36
|
+
src: templatePath,
|
|
37
|
+
path: "output.txt",
|
|
38
|
+
get children() {
|
|
39
|
+
return _$createComponent(TemplateVariable, {
|
|
40
|
+
name: "greeting",
|
|
41
|
+
children: "Hello World"
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
await expect(result).toRenderToAsync("Welcome Hello World!");
|
|
46
|
+
});
|
|
47
|
+
it("should handle complex templates with multiple variables", async () => {
|
|
48
|
+
const templatePath = join(tmpdir(), "test-complex-template.txt");
|
|
49
|
+
const templateContent = d`
|
|
50
|
+
Name: {{ name }}
|
|
51
|
+
Age: {{ age }}
|
|
52
|
+
Location: {{ location }}
|
|
53
|
+
Status: {{ status }}
|
|
54
|
+
`;
|
|
55
|
+
writeFileSync(templatePath, templateContent);
|
|
56
|
+
const result = _$createComponent(TemplateFile, {
|
|
57
|
+
src: templatePath,
|
|
58
|
+
path: "output.txt",
|
|
59
|
+
get children() {
|
|
60
|
+
return [_$createComponent(TemplateVariable, {
|
|
61
|
+
name: "name",
|
|
62
|
+
value: "Alice"
|
|
63
|
+
}), _$createComponent(TemplateVariable, {
|
|
64
|
+
name: "age",
|
|
65
|
+
children: "30"
|
|
66
|
+
}), _$createComponent(TemplateVariable, {
|
|
67
|
+
name: "location",
|
|
68
|
+
value: "New York"
|
|
69
|
+
}), _$createComponent(TemplateVariable, {
|
|
70
|
+
name: "status",
|
|
71
|
+
value: "Active"
|
|
72
|
+
})];
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
await expect(result).toRenderToAsync(d`
|
|
76
|
+
Name: Alice
|
|
77
|
+
Age: 30
|
|
78
|
+
Location: New York
|
|
79
|
+
Status: Active
|
|
80
|
+
`);
|
|
81
|
+
});
|
|
82
|
+
it("should handle templates with whitespace around variable names", async () => {
|
|
83
|
+
const templatePath = join(tmpdir(), "test-whitespace-template.txt");
|
|
84
|
+
const templateContent = "Hello {{ name }}!";
|
|
85
|
+
writeFileSync(templatePath, templateContent);
|
|
86
|
+
const result = _$createComponent(TemplateFile, {
|
|
87
|
+
src: templatePath,
|
|
88
|
+
path: "output.txt",
|
|
89
|
+
get children() {
|
|
90
|
+
return _$createComponent(TemplateVariable, {
|
|
91
|
+
name: "name",
|
|
92
|
+
value: "Bob"
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
await expect(result).toRenderToAsync("Hello Bob!");
|
|
97
|
+
});
|
|
98
|
+
it("should throw error for missing template variables", async () => {
|
|
99
|
+
const templatePath = join(tmpdir(), "test-missing-var-template.txt");
|
|
100
|
+
const templateContent = "Hello {{ name }}! Your age is {{ age }}.";
|
|
101
|
+
writeFileSync(templatePath, templateContent);
|
|
102
|
+
await expect(async () => await renderAsync(_$createComponent(TemplateFile, {
|
|
103
|
+
src: templatePath,
|
|
104
|
+
path: "output.txt",
|
|
105
|
+
get children() {
|
|
106
|
+
return _$createComponent(TemplateVariable, {
|
|
107
|
+
name: "name",
|
|
108
|
+
value: "Charlie"
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
}))).rejects.toThrow('Template variable "age" not found in TemplateVariable children');
|
|
112
|
+
});
|
|
113
|
+
it("should handle template with no variables", async () => {
|
|
114
|
+
const templatePath = join(tmpdir(), "test-no-vars-template.txt");
|
|
115
|
+
const templateContent = "This is just plain text with no variables.";
|
|
116
|
+
writeFileSync(templatePath, templateContent);
|
|
117
|
+
const result = _$createComponent(TemplateFile, {
|
|
118
|
+
src: templatePath,
|
|
119
|
+
path: "output.txt"
|
|
120
|
+
});
|
|
121
|
+
await expect(result).toRenderToAsync("This is just plain text with no variables.");
|
|
122
|
+
});
|
|
123
|
+
it("should handle empty template", async () => {
|
|
124
|
+
const templatePath = join(tmpdir(), "test-empty-template.txt");
|
|
125
|
+
const templateContent = "";
|
|
126
|
+
writeFileSync(templatePath, templateContent);
|
|
127
|
+
const result = _$createComponent(TemplateFile, {
|
|
128
|
+
src: templatePath,
|
|
129
|
+
path: "output.txt"
|
|
130
|
+
});
|
|
131
|
+
await expect(result).toRenderToAsync("");
|
|
132
|
+
});
|
|
133
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"update-file.test.d.ts","sourceRoot":"","sources":["../../../test/components/update-file.test.tsx"],"names":[],"mappings":"AAMA,OAAO,gCAAgC,CAAC"}
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import { createComponent as _$createComponent } from "@alloy-js/core/jsx-runtime";
|
|
2
|
+
import { existsSync, unlinkSync, writeFileSync } from "fs";
|
|
3
|
+
import { tmpdir } from "os";
|
|
4
|
+
import { join } from "pathe";
|
|
5
|
+
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
|
6
|
+
import { UpdateFile } from "../../src/components/UpdateFile.js";
|
|
7
|
+
import { render } from "../../src/render.js";
|
|
8
|
+
import "../../testing/extend-expect.js";
|
|
9
|
+
import { d } from "../../testing/render.js";
|
|
10
|
+
describe("UpdateFile", () => {
|
|
11
|
+
let testFilePath;
|
|
12
|
+
beforeEach(() => {
|
|
13
|
+
testFilePath = join(tmpdir(), "test-file.txt");
|
|
14
|
+
});
|
|
15
|
+
afterEach(() => {
|
|
16
|
+
if (existsSync(testFilePath)) {
|
|
17
|
+
unlinkSync(testFilePath);
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
describe("when file exists", () => {
|
|
21
|
+
it("should read existing file and transform content", async () => {
|
|
22
|
+
const existingContent = "Hello World";
|
|
23
|
+
writeFileSync(testFilePath, existingContent, "utf-8");
|
|
24
|
+
const result = _$createComponent(UpdateFile, {
|
|
25
|
+
path: testFilePath,
|
|
26
|
+
children: currentContents => {
|
|
27
|
+
return `${currentContents?.toUpperCase()}`;
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
await expect(result).toRenderToAsync("HELLO WORLD");
|
|
31
|
+
});
|
|
32
|
+
it("should handle JSON file updates", async () => {
|
|
33
|
+
const existingConfig = {
|
|
34
|
+
version: "1.0.0",
|
|
35
|
+
name: "test"
|
|
36
|
+
};
|
|
37
|
+
writeFileSync(testFilePath, JSON.stringify(existingConfig), "utf-8");
|
|
38
|
+
const result = _$createComponent(UpdateFile, {
|
|
39
|
+
path: testFilePath,
|
|
40
|
+
children: currentContents => {
|
|
41
|
+
const config = JSON.parse(currentContents);
|
|
42
|
+
config.version = "2.0.0";
|
|
43
|
+
config.newProperty = "added";
|
|
44
|
+
return JSON.stringify(config, null, 2);
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
await expect(result).toRenderToAsync(d`
|
|
48
|
+
{
|
|
49
|
+
"version": "2.0.0",
|
|
50
|
+
"name": "test",
|
|
51
|
+
"newProperty": "added"
|
|
52
|
+
}
|
|
53
|
+
`);
|
|
54
|
+
});
|
|
55
|
+
it("should handle multiline content transformation", async () => {
|
|
56
|
+
const existingContent = d`
|
|
57
|
+
line 1
|
|
58
|
+
line 2
|
|
59
|
+
line 3
|
|
60
|
+
`;
|
|
61
|
+
writeFileSync(testFilePath, existingContent, "utf-8");
|
|
62
|
+
const result = _$createComponent(UpdateFile, {
|
|
63
|
+
path: testFilePath,
|
|
64
|
+
children: currentContents => {
|
|
65
|
+
const lines = currentContents.split("\n");
|
|
66
|
+
const modifiedLines = lines.map(line => `// ${line}`);
|
|
67
|
+
return modifiedLines.join("\n");
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
await expect(result).toRenderToAsync(d`
|
|
71
|
+
// line 1
|
|
72
|
+
// line 2
|
|
73
|
+
// line 3
|
|
74
|
+
`);
|
|
75
|
+
});
|
|
76
|
+
it("should handle empty existing file", async () => {
|
|
77
|
+
writeFileSync(testFilePath, "", "utf-8");
|
|
78
|
+
const result = _$createComponent(UpdateFile, {
|
|
79
|
+
path: testFilePath,
|
|
80
|
+
children: currentContents => {
|
|
81
|
+
return `Content was: "${currentContents || "empty"}"`;
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
await expect(result).toRenderToAsync('Content was: "empty"');
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
describe("when file does not exist", () => {
|
|
88
|
+
it("should use defaultContent when file does not exist", async () => {
|
|
89
|
+
const result = _$createComponent(UpdateFile, {
|
|
90
|
+
path: "non-existent.txt",
|
|
91
|
+
defaultContent: "default content",
|
|
92
|
+
children: currentContents => {
|
|
93
|
+
if (currentContents === null) {
|
|
94
|
+
return `File did not exist`;
|
|
95
|
+
}
|
|
96
|
+
return `File existed with: ${currentContents}`;
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
await expect(result).toRenderToAsync("File did not exist");
|
|
100
|
+
});
|
|
101
|
+
it("should use defaultContentPath when file does not exist", async () => {
|
|
102
|
+
const defaultFilePath = join(tmpdir(), `default-content-${Date.now()}.txt`);
|
|
103
|
+
const defaultContent = "This is default content from file";
|
|
104
|
+
writeFileSync(defaultFilePath, defaultContent, "utf-8");
|
|
105
|
+
try {
|
|
106
|
+
const result = _$createComponent(UpdateFile, {
|
|
107
|
+
path: "non-existent.txt",
|
|
108
|
+
defaultContentPath: defaultFilePath,
|
|
109
|
+
children: currentContents => {
|
|
110
|
+
return `Default: ${currentContents}`;
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
await expect(result).toRenderToAsync("Default: This is default content from file");
|
|
114
|
+
} finally {
|
|
115
|
+
if (existsSync(defaultFilePath)) {
|
|
116
|
+
unlinkSync(defaultFilePath);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
it("should pass null to children when no default content provided", async () => {
|
|
121
|
+
const result = _$createComponent(UpdateFile, {
|
|
122
|
+
path: "non-existent.txt",
|
|
123
|
+
children: currentContents => {
|
|
124
|
+
if (currentContents === null) {
|
|
125
|
+
return `Creating new file`;
|
|
126
|
+
}
|
|
127
|
+
return `Updating existing file: ${currentContents}`;
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
await expect(result).toRenderToAsync("Creating new file");
|
|
131
|
+
});
|
|
132
|
+
it("should create JSON file from scratch when it doesn't exist", async () => {
|
|
133
|
+
const result = _$createComponent(UpdateFile, {
|
|
134
|
+
path: "new-config.json",
|
|
135
|
+
children: currentContents => {
|
|
136
|
+
const config = currentContents ? JSON.parse(currentContents) : {};
|
|
137
|
+
config.name = "new-project";
|
|
138
|
+
config.version = "1.0.0";
|
|
139
|
+
config.dependencies = {};
|
|
140
|
+
return JSON.stringify(config, null, 2);
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
await expect(result).toRenderToAsync(d`
|
|
144
|
+
{
|
|
145
|
+
"name": "new-project",
|
|
146
|
+
"version": "1.0.0",
|
|
147
|
+
"dependencies": {}
|
|
148
|
+
}
|
|
149
|
+
`);
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
describe("error handling", () => {
|
|
153
|
+
it("should throw error when defaultContentPath file does not exist", async () => {
|
|
154
|
+
expect(() => render(_$createComponent(UpdateFile, {
|
|
155
|
+
path: "non-existent.txt",
|
|
156
|
+
defaultContentPath: "/path/that/does/not/exist.txt",
|
|
157
|
+
children: currentContents => currentContents
|
|
158
|
+
}))).toThrow();
|
|
159
|
+
});
|
|
160
|
+
it("should handle file system errors gracefully", async () => {
|
|
161
|
+
// Try to read a directory as a file (should cause an error)
|
|
162
|
+
const dirPath = join(tmpdir(), `test-dir-${Date.now()}`);
|
|
163
|
+
expect(() => render(_$createComponent(UpdateFile, {
|
|
164
|
+
path: dirPath,
|
|
165
|
+
children: currentContents => currentContents
|
|
166
|
+
}))).toThrow();
|
|
167
|
+
});
|
|
168
|
+
});
|
|
169
|
+
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"formatting.test.d.ts","sourceRoot":"","sources":["../../../test/rendering/formatting.test.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"formatting.test.d.ts","sourceRoot":"","sources":["../../../test/rendering/formatting.test.tsx"],"names":[],"mappings":"AAQA,OAAO,gCAAgC,CAAC"}
|
|
@@ -1,17 +1,2 @@
|
|
|
1
1
|
import "vitest";
|
|
2
|
-
interface ToRenderToRenderOptions {
|
|
3
|
-
printWidth?: number;
|
|
4
|
-
tabWidth?: number;
|
|
5
|
-
useTabs?: boolean;
|
|
6
|
-
}
|
|
7
|
-
interface CustomMatchers<R = unknown> {
|
|
8
|
-
toRenderTo: (str: string, options?: ToRenderToRenderOptions) => R;
|
|
9
|
-
}
|
|
10
|
-
declare module "vitest" {
|
|
11
|
-
interface Assertion<T = any> extends CustomMatchers<T> {
|
|
12
|
-
}
|
|
13
|
-
interface AsymmetricMatchersContaining extends CustomMatchers {
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
export {};
|
|
17
2
|
//# sourceMappingURL=extend-expect.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"extend-expect.d.ts","sourceRoot":"","sources":["../../testing/extend-expect.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"extend-expect.d.ts","sourceRoot":"","sources":["../../testing/extend-expect.ts"],"names":[],"mappings":"AAOA,OAAO,QAAQ,CAAC"}
|
|
@@ -1,22 +1,99 @@
|
|
|
1
|
-
import { printTree, renderTree } from "@alloy-js/core";
|
|
1
|
+
import { getContextForRenderNode, printTree, renderTree } from "@alloy-js/core";
|
|
2
|
+
import "vitest";
|
|
2
3
|
import { expect } from "vitest";
|
|
4
|
+
import { flushJobs, flushJobsAsync } from "../src/scheduler.js";
|
|
3
5
|
import { dedent } from "./render.js";
|
|
4
6
|
expect.extend({
|
|
5
7
|
toRenderTo(received, expectedRaw, renderOptions) {
|
|
6
|
-
const {
|
|
7
|
-
isNot
|
|
8
|
-
} = this;
|
|
9
8
|
const tree = renderTree(received);
|
|
10
|
-
|
|
9
|
+
flushJobs();
|
|
10
|
+
const actual = getFilesFromTree(tree, renderOptions);
|
|
11
|
+
return validateRender(actual, expectedRaw, this.isNot);
|
|
12
|
+
},
|
|
13
|
+
async toRenderToAsync(received, expectedRaw, renderOptions) {
|
|
14
|
+
const tree = renderTree(received);
|
|
15
|
+
await flushJobsAsync();
|
|
16
|
+
const actual = getFilesFromTree(tree, renderOptions);
|
|
17
|
+
return validateRender(actual, expectedRaw, this.isNot);
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
function validateRender(actual, expectedRaw, isNot) {
|
|
21
|
+
const message = () => `Render is${isNot ? " not" : ""} incorrect`;
|
|
22
|
+
if (typeof expectedRaw === "string") {
|
|
11
23
|
const expected = dedent(expectedRaw);
|
|
24
|
+
let actualStr;
|
|
25
|
+
if (typeof actual === "string") {
|
|
26
|
+
actualStr = actual;
|
|
27
|
+
} else if (Object.keys(actual).length === 1) {
|
|
28
|
+
// If we have a single file, we can use its content directly.
|
|
29
|
+
actualStr = Object.values(actual)[0];
|
|
30
|
+
} else {
|
|
31
|
+
return {
|
|
32
|
+
pass: false,
|
|
33
|
+
message,
|
|
34
|
+
actual,
|
|
35
|
+
expected
|
|
36
|
+
};
|
|
37
|
+
}
|
|
12
38
|
return {
|
|
13
|
-
pass:
|
|
14
|
-
message
|
|
15
|
-
|
|
16
|
-
},
|
|
17
|
-
actual,
|
|
39
|
+
pass: actualStr === expected,
|
|
40
|
+
message,
|
|
41
|
+
actual: actualStr,
|
|
18
42
|
expected
|
|
19
43
|
};
|
|
44
|
+
} else if (typeof actual === "object" && typeof expectedRaw === "object") {
|
|
45
|
+
const expected = expectedRaw;
|
|
46
|
+
const dedentExpected = {};
|
|
47
|
+
for (const [key, value] of Object.entries(expected)) {
|
|
48
|
+
dedentExpected[key] = dedent(value);
|
|
49
|
+
}
|
|
50
|
+
const pass = Object.keys(actual).length === Object.keys(expected).length && Object.entries(actual).every(([key, value]) => {
|
|
51
|
+
return dedentExpected[key] === value;
|
|
52
|
+
});
|
|
53
|
+
return {
|
|
54
|
+
pass,
|
|
55
|
+
message,
|
|
56
|
+
actual,
|
|
57
|
+
expected: dedentExpected
|
|
58
|
+
};
|
|
59
|
+
} else {
|
|
60
|
+
return {
|
|
61
|
+
pass: false,
|
|
62
|
+
message,
|
|
63
|
+
actual,
|
|
64
|
+
expected: expectedRaw
|
|
65
|
+
};
|
|
20
66
|
}
|
|
21
|
-
}
|
|
22
|
-
|
|
67
|
+
}
|
|
68
|
+
function getFilesFromTree(tree, options) {
|
|
69
|
+
const files = {};
|
|
70
|
+
// when passing Output, the first render tree child is the Output component.
|
|
71
|
+
const rootRenderOptions = Array.isArray(tree) ? getContextForRenderNode(tree[0])?.meta?.printOptions ?? {} : {};
|
|
72
|
+
collectSourceFiles(tree);
|
|
73
|
+
// If we found no source files, we return the tree as a string.
|
|
74
|
+
if (Object.keys(files).length === 0) {
|
|
75
|
+
return printTree(tree, options);
|
|
76
|
+
} else {
|
|
77
|
+
return files;
|
|
78
|
+
}
|
|
79
|
+
function collectSourceFiles(root) {
|
|
80
|
+
if (!Array.isArray(root)) {
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
const context = getContextForRenderNode(root);
|
|
84
|
+
if (context?.meta?.sourceFile) {
|
|
85
|
+
files[context.meta.sourceFile.path] = printTree(root, {
|
|
86
|
+
printWidth: options?.printWidth ?? context.meta?.printOptions?.printWidth ?? rootRenderOptions.printWidth,
|
|
87
|
+
tabWidth: options?.tabWidth ?? context.meta?.printOptions?.tabWidth ?? rootRenderOptions.tabWidth,
|
|
88
|
+
useTabs: options?.useTabs ?? context.meta?.printOptions?.useTabs ?? rootRenderOptions.useTabs
|
|
89
|
+
});
|
|
90
|
+
} else {
|
|
91
|
+
visitChildren();
|
|
92
|
+
}
|
|
93
|
+
function visitChildren() {
|
|
94
|
+
for (const child of root) {
|
|
95
|
+
collectSourceFiles(child);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extend-expect.test.d.ts","sourceRoot":"","sources":["../../testing/extend-expect.test.tsx"],"names":[],"mappings":"AAEA,OAAO,oBAAoB,CAAC"}
|