@alloy-js/core 0.19.0-dev.3 → 0.19.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 (96) hide show
  1. package/CHANGELOG.md +22 -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/render.d.ts +10 -2
  39. package/dist/src/render.d.ts.map +1 -1
  40. package/dist/src/render.js +20 -1
  41. package/dist/src/resource.d.ts +80 -0
  42. package/dist/src/resource.d.ts.map +1 -0
  43. package/dist/src/resource.js +118 -0
  44. package/dist/src/scheduler.d.ts +6 -0
  45. package/dist/src/scheduler.d.ts.map +1 -1
  46. package/dist/src/scheduler.js +36 -0
  47. package/dist/src/write-output.d.ts +1 -1
  48. package/dist/src/write-output.d.ts.map +1 -1
  49. package/dist/src/write-output.js +40 -21
  50. package/dist/test/components/append-file.test.d.ts +2 -0
  51. package/dist/test/components/append-file.test.d.ts.map +1 -0
  52. package/dist/test/components/append-file.test.js +281 -0
  53. package/dist/test/components/copy-file.test.d.ts +2 -0
  54. package/dist/test/components/copy-file.test.d.ts.map +1 -0
  55. package/dist/test/components/copy-file.test.js +94 -0
  56. package/dist/test/components/source-file.test.d.ts.map +1 -1
  57. package/dist/test/components/template-file.test.d.ts +2 -0
  58. package/dist/test/components/template-file.test.d.ts.map +1 -0
  59. package/dist/test/components/template-file.test.js +133 -0
  60. package/dist/test/components/update-file.test.d.ts +2 -0
  61. package/dist/test/components/update-file.test.d.ts.map +1 -0
  62. package/dist/test/components/update-file.test.js +169 -0
  63. package/dist/test/rendering/formatting.test.d.ts.map +1 -1
  64. package/dist/testing/extend-expect.js +60 -54
  65. package/dist/tsconfig.tsbuildinfo +1 -1
  66. package/package.json +6 -6
  67. package/src/components/AppendFile.tsx +294 -0
  68. package/src/components/CopyFile.tsx +29 -0
  69. package/src/components/TemplateFile.tsx +193 -0
  70. package/src/components/UpdateFile.tsx +86 -0
  71. package/src/components/index.tsx +4 -0
  72. package/src/components/stc/index.ts +4 -0
  73. package/src/context/source-directory.ts +5 -3
  74. package/src/context/source-file.ts +5 -0
  75. package/src/debug.ts +4 -1
  76. package/src/host/alloy-host.browser.ts +56 -0
  77. package/src/host/alloy-host.ts +160 -0
  78. package/src/host/interface.ts +153 -0
  79. package/src/index.browser.ts +1 -1
  80. package/src/render.ts +44 -5
  81. package/src/resource.ts +152 -0
  82. package/src/scheduler.ts +39 -0
  83. package/src/write-output.ts +49 -19
  84. package/temp/api.json +2009 -546
  85. package/test/components/append-file.test.tsx +275 -0
  86. package/test/components/copy-file.test.tsx +98 -0
  87. package/test/components/source-file.test.tsx +5 -2
  88. package/test/components/template-file.test.tsx +127 -0
  89. package/test/components/update-file.test.tsx +214 -0
  90. package/test/rendering/formatting.test.tsx +9 -3
  91. package/testing/extend-expect.ts +74 -58
  92. package/testing/vitest.d.ts +4 -0
  93. package/dist/src/write-output.browser.d.ts +0 -2
  94. package/dist/src/write-output.browser.d.ts.map +0 -1
  95. package/dist/src/write-output.browser.js +0 -4
  96. package/src/write-output.browser.ts +0 -4
@@ -0,0 +1,2 @@
1
+ import "../../testing/extend-expect.js";
2
+ //# sourceMappingURL=copy-file.test.d.ts.map
@@ -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":"AAUA,OAAO,gCAAgC,CAAC"}
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,2 @@
1
+ import "../../testing/extend-expect.js";
2
+ //# sourceMappingURL=template-file.test.d.ts.map
@@ -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,2 @@
1
+ import "../../testing/extend-expect.js";
2
+ //# sourceMappingURL=update-file.test.d.ts.map
@@ -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":"AAEA,OAAO,gCAAgC,CAAC"}
1
+ {"version":3,"file":"formatting.test.d.ts","sourceRoot":"","sources":["../../../test/rendering/formatting.test.tsx"],"names":[],"mappings":"AAQA,OAAO,gCAAgC,CAAC"}
@@ -1,68 +1,81 @@
1
1
  import { getContextForRenderNode, printTree, renderTree } from "@alloy-js/core";
2
2
  import "vitest";
3
3
  import { expect } from "vitest";
4
+ import { flushJobs, flushJobsAsync } from "../src/scheduler.js";
4
5
  import { dedent } from "./render.js";
5
6
  expect.extend({
6
7
  toRenderTo(received, expectedRaw, renderOptions) {
7
- const message = () => `Render is${isNot ? " not" : ""} incorrect`;
8
- const {
9
- isNot
10
- } = this;
11
- const actual = renderAsFiles(received, renderOptions);
12
- if (typeof expectedRaw === "string") {
13
- const expected = dedent(expectedRaw);
14
- let actualStr;
15
- if (typeof actual === "string") {
16
- actualStr = actual;
17
- } else if (Object.keys(actual).length === 1) {
18
- // If we have a single file, we can use its content directly.
19
- actualStr = Object.values(actual)[0];
20
- } else {
21
- return {
22
- pass: false,
23
- message,
24
- actual,
25
- expected
26
- };
27
- }
28
- return {
29
- pass: actualStr === expected,
30
- message,
31
- actual: actualStr,
32
- expected
33
- };
34
- } else if (typeof actual === "object" && typeof expectedRaw === "object") {
35
- const expected = expectedRaw;
36
- const dedentExpected = {};
37
- for (const [key, value] of Object.entries(expected)) {
38
- dedentExpected[key] = dedent(value);
39
- }
40
- const pass = Object.keys(actual).length === Object.keys(expected).length && Object.entries(actual).every(([key, value]) => {
41
- return dedentExpected[key] === value;
42
- });
43
- return {
44
- pass,
45
- message,
46
- actual,
47
- expected: dedentExpected
48
- };
8
+ const tree = renderTree(received);
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") {
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];
49
30
  } else {
50
31
  return {
51
32
  pass: false,
52
33
  message,
53
34
  actual,
54
- expected: expectedRaw
35
+ expected
55
36
  };
56
37
  }
38
+ return {
39
+ pass: actualStr === expected,
40
+ message,
41
+ actual: actualStr,
42
+ expected
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
+ };
57
66
  }
58
- });
59
- function renderAsFiles(received, options) {
67
+ }
68
+ function getFilesFromTree(tree, options) {
60
69
  const files = {};
61
- const tree = renderTree(received);
62
-
63
70
  // when passing Output, the first render tree child is the Output component.
64
71
  const rootRenderOptions = Array.isArray(tree) ? getContextForRenderNode(tree[0])?.meta?.printOptions ?? {} : {};
65
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
+ }
66
79
  function collectSourceFiles(root) {
67
80
  if (!Array.isArray(root)) {
68
81
  return;
@@ -83,11 +96,4 @@ function renderAsFiles(received, options) {
83
96
  }
84
97
  }
85
98
  }
86
-
87
- // If we found no source files, we return the tree as a string.
88
- if (Object.keys(files).length === 0) {
89
- return printTree(tree, options);
90
- } else {
91
- return files;
92
- }
93
99
  }