@isentinel/rojo-utils 0.1.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 (115) hide show
  1. package/CLAUDE.md +40 -0
  2. package/coverage/base.css +224 -0
  3. package/coverage/block-navigation.js +87 -0
  4. package/coverage/clover.xml +388 -0
  5. package/coverage/coverage-final.json +7 -0
  6. package/coverage/favicon.png +0 -0
  7. package/coverage/index.html +191 -0
  8. package/coverage/loader.ts.html +220 -0
  9. package/coverage/mount-collector.ts.html +301 -0
  10. package/coverage/path-mapper.ts.html +220 -0
  11. package/coverage/prettify.css +1 -0
  12. package/coverage/prettify.js +2 -0
  13. package/coverage/rojo-resolver.ts.html +1900 -0
  14. package/coverage/rojo-tree.ts.html +559 -0
  15. package/coverage/sort-arrow-sprite.png +0 -0
  16. package/coverage/sorter.js +210 -0
  17. package/coverage/tree-mapper.ts.html +247 -0
  18. package/dist/index.d.mts +175 -0
  19. package/dist/index.d.mts.map +1 -0
  20. package/dist/index.mjs +551 -0
  21. package/dist/index.mjs.map +1 -0
  22. package/eslint.config.ts +23 -0
  23. package/out-tsc/index.d.ts +10 -0
  24. package/out-tsc/index.d.ts.map +1 -0
  25. package/out-tsc/index.js +6 -0
  26. package/out-tsc/loader.d.ts +3 -0
  27. package/out-tsc/loader.d.ts.map +1 -0
  28. package/out-tsc/loader.js +37 -0
  29. package/out-tsc/mount-collector.d.ts +10 -0
  30. package/out-tsc/mount-collector.d.ts.map +1 -0
  31. package/out-tsc/mount-collector.js +37 -0
  32. package/out-tsc/path-mapper.d.ts +4 -0
  33. package/out-tsc/path-mapper.d.ts.map +1 -0
  34. package/out-tsc/path-mapper.js +34 -0
  35. package/out-tsc/rojo-resolver.d.ts +110 -0
  36. package/out-tsc/rojo-resolver.d.ts.map +1 -0
  37. package/out-tsc/rojo-resolver.js +448 -0
  38. package/out-tsc/rojo-tree.d.ts +15 -0
  39. package/out-tsc/rojo-tree.d.ts.map +1 -0
  40. package/out-tsc/rojo-tree.js +106 -0
  41. package/out-tsc/tree-mapper.d.ts +4 -0
  42. package/out-tsc/tree-mapper.d.ts.map +1 -0
  43. package/out-tsc/tree-mapper.js +34 -0
  44. package/out-tsc/tsconfig.tsbuildinfo +1 -0
  45. package/out-tsc/types.d.ts +26 -0
  46. package/out-tsc/types.d.ts.map +1 -0
  47. package/out-tsc/types.js +1 -0
  48. package/out-tsc/vitest/eslint.config.d.ts +3 -0
  49. package/out-tsc/vitest/eslint.config.d.ts.map +1 -0
  50. package/out-tsc/vitest/eslint.shared.d.ts +13 -0
  51. package/out-tsc/vitest/eslint.shared.d.ts.map +1 -0
  52. package/out-tsc/vitest/libs/rojo-utils/eslint.config.d.ts +3 -0
  53. package/out-tsc/vitest/libs/rojo-utils/eslint.config.d.ts.map +1 -0
  54. package/out-tsc/vitest/libs/rojo-utils/src/loader.spec.d.ts +2 -0
  55. package/out-tsc/vitest/libs/rojo-utils/src/loader.spec.d.ts.map +1 -0
  56. package/out-tsc/vitest/libs/rojo-utils/src/mount-collector.spec.d.ts +2 -0
  57. package/out-tsc/vitest/libs/rojo-utils/src/mount-collector.spec.d.ts.map +1 -0
  58. package/out-tsc/vitest/libs/rojo-utils/src/path-mapper.spec.d.ts +2 -0
  59. package/out-tsc/vitest/libs/rojo-utils/src/path-mapper.spec.d.ts.map +1 -0
  60. package/out-tsc/vitest/libs/rojo-utils/src/rojo-resolver.spec.d.ts +2 -0
  61. package/out-tsc/vitest/libs/rojo-utils/src/rojo-resolver.spec.d.ts.map +1 -0
  62. package/out-tsc/vitest/libs/rojo-utils/src/rojo-tree.spec.d.ts +2 -0
  63. package/out-tsc/vitest/libs/rojo-utils/src/rojo-tree.spec.d.ts.map +1 -0
  64. package/out-tsc/vitest/libs/rojo-utils/src/tree-mapper.spec.d.ts +2 -0
  65. package/out-tsc/vitest/libs/rojo-utils/src/tree-mapper.spec.d.ts.map +1 -0
  66. package/out-tsc/vitest/libs/rojo-utils/test/setup/jest-extended.d.ts +2 -0
  67. package/out-tsc/vitest/libs/rojo-utils/test/setup/jest-extended.d.ts.map +1 -0
  68. package/out-tsc/vitest/libs/rojo-utils/tsconfig.spec.tsbuildinfo +1 -0
  69. package/out-tsc/vitest/libs/rojo-utils/tsdown.config.d.ts +3 -0
  70. package/out-tsc/vitest/libs/rojo-utils/tsdown.config.d.ts.map +1 -0
  71. package/out-tsc/vitest/libs/rojo-utils/vitest.config.d.ts +3 -0
  72. package/out-tsc/vitest/libs/rojo-utils/vitest.config.d.ts.map +1 -0
  73. package/out-tsc/vitest/src/loader.spec.d.ts +2 -0
  74. package/out-tsc/vitest/src/loader.spec.d.ts.map +1 -0
  75. package/out-tsc/vitest/src/mount-collector.spec.d.ts +2 -0
  76. package/out-tsc/vitest/src/mount-collector.spec.d.ts.map +1 -0
  77. package/out-tsc/vitest/src/path-mapper.spec.d.ts +2 -0
  78. package/out-tsc/vitest/src/path-mapper.spec.d.ts.map +1 -0
  79. package/out-tsc/vitest/src/rojo-resolver.spec.d.ts +2 -0
  80. package/out-tsc/vitest/src/rojo-resolver.spec.d.ts.map +1 -0
  81. package/out-tsc/vitest/src/rojo-tree.spec.d.ts +2 -0
  82. package/out-tsc/vitest/src/rojo-tree.spec.d.ts.map +1 -0
  83. package/out-tsc/vitest/src/tree-mapper.spec.d.ts +2 -0
  84. package/out-tsc/vitest/src/tree-mapper.spec.d.ts.map +1 -0
  85. package/out-tsc/vitest/test/setup/jest-extended.d.ts +2 -0
  86. package/out-tsc/vitest/test/setup/jest-extended.d.ts.map +1 -0
  87. package/out-tsc/vitest/tsconfig.spec.tsbuildinfo +1 -0
  88. package/out-tsc/vitest/tsdown.config.d.ts +3 -0
  89. package/out-tsc/vitest/tsdown.config.d.ts.map +1 -0
  90. package/out-tsc/vitest/vitest.config.d.ts +3 -0
  91. package/out-tsc/vitest/vitest.config.d.ts.map +1 -0
  92. package/package.json +48 -0
  93. package/project.json +25 -0
  94. package/src/index.ts +25 -0
  95. package/src/loader.spec.ts +240 -0
  96. package/src/loader.ts +45 -0
  97. package/src/mount-collector.spec.ts +272 -0
  98. package/src/mount-collector.ts +72 -0
  99. package/src/path-mapper.spec.ts +201 -0
  100. package/src/path-mapper.ts +45 -0
  101. package/src/rojo-resolver.spec.ts +956 -0
  102. package/src/rojo-resolver.ts +605 -0
  103. package/src/rojo-tree.spec.ts +622 -0
  104. package/src/rojo-tree.ts +162 -0
  105. package/src/tree-mapper.spec.ts +147 -0
  106. package/src/tree-mapper.ts +54 -0
  107. package/src/types.ts +29 -0
  108. package/test/setup/jest-extended.ts +4 -0
  109. package/test/setup/vitest-jest-extended.d.ts +1 -0
  110. package/tsconfig.json +13 -0
  111. package/tsconfig.lib.json +17 -0
  112. package/tsconfig.lib.tsbuildinfo +1 -0
  113. package/tsconfig.spec.json +26 -0
  114. package/tsdown.config.ts +15 -0
  115. package/vitest.config.ts +20 -0
@@ -0,0 +1,622 @@
1
+ import * as fs from "node:fs";
2
+ import * as os from "node:os";
3
+ import * as path from "node:path";
4
+ import { describe, expect, it } from "vitest";
5
+
6
+ import {
7
+ collectPaths,
8
+ rebaseTreePaths,
9
+ resolveNestedProjects,
10
+ resolveNestedProjectSources,
11
+ } from "./rojo-tree.ts";
12
+ import type { RojoTreeNode } from "./types.ts";
13
+
14
+ describe(collectPaths, () => {
15
+ it("should collect all $path strings from a tree", () => {
16
+ expect.assertions(1);
17
+
18
+ const tree: RojoTreeNode = {
19
+ $className: "DataModel",
20
+ ReplicatedStorage: {
21
+ client: { $path: "out/client" },
22
+ },
23
+ ServerScriptService: {
24
+ server: { $path: "out/server" },
25
+ },
26
+ };
27
+
28
+ const result: Array<string> = [];
29
+ collectPaths(tree, result);
30
+
31
+ expect(result).toStrictEqual(["out/client", "out/server"]);
32
+ });
33
+
34
+ it("should normalize backslashes", () => {
35
+ expect.assertions(1);
36
+
37
+ const tree: RojoTreeNode = {
38
+ $className: "DataModel",
39
+ Workspace: { $path: "out\\workspace" },
40
+ };
41
+
42
+ const result: Array<string> = [];
43
+ collectPaths(tree, result);
44
+
45
+ expect(result).toStrictEqual(["out/workspace"]);
46
+ });
47
+
48
+ it("should skip non-string $path values", () => {
49
+ expect.assertions(1);
50
+
51
+ const tree: RojoTreeNode = {
52
+ $className: "DataModel",
53
+ Workspace: { $path: { optional: "maybe" } },
54
+ };
55
+
56
+ const result: Array<string> = [];
57
+ collectPaths(tree, result);
58
+
59
+ expect(result).toStrictEqual([]);
60
+ });
61
+
62
+ it("should return empty for a tree with no $path entries", () => {
63
+ expect.assertions(1);
64
+
65
+ const tree: RojoTreeNode = {
66
+ $className: "DataModel",
67
+ };
68
+
69
+ const result: Array<string> = [];
70
+ collectPaths(tree, result);
71
+
72
+ expect(result).toStrictEqual([]);
73
+ });
74
+ });
75
+
76
+ describe(resolveNestedProjects, () => {
77
+ it("should return tree unchanged when no $path references .project.json", () => {
78
+ expect.assertions(1);
79
+
80
+ const tree: RojoTreeNode = {
81
+ $className: "DataModel",
82
+ ReplicatedStorage: {
83
+ client: { $path: "out/client" },
84
+ },
85
+ };
86
+
87
+ const temporaryDirectory = fs.mkdtempSync(path.join(os.tmpdir(), "rojo-tree-test-"));
88
+ const result = resolveNestedProjects(tree, temporaryDirectory);
89
+ fs.rmSync(temporaryDirectory, { force: true, recursive: true });
90
+
91
+ expect(result).toStrictEqual(tree);
92
+ });
93
+
94
+ it("should resolve $path pointing to a .project.json into its inner tree's $path", () => {
95
+ expect.assertions(1);
96
+
97
+ const temporaryDirectory = fs.mkdtempSync(path.join(os.tmpdir(), "rojo-tree-test-"));
98
+ fs.writeFileSync(
99
+ path.join(temporaryDirectory, "default.project.json"),
100
+ JSON.stringify({ name: "my-pkg", tree: { $path: "src" } }),
101
+ );
102
+
103
+ const tree: RojoTreeNode = {
104
+ $className: "DataModel",
105
+ ReplicatedStorage: {
106
+ "my-pkg": { $path: "default.project.json" },
107
+ },
108
+ };
109
+
110
+ const result = resolveNestedProjects(tree, temporaryDirectory);
111
+ fs.rmSync(temporaryDirectory, { force: true, recursive: true });
112
+
113
+ expect(result).toStrictEqual({
114
+ $className: "DataModel",
115
+ ReplicatedStorage: {
116
+ "my-pkg": { $path: "src" },
117
+ },
118
+ });
119
+ });
120
+
121
+ it("should merge children from the nested project's tree into the node", () => {
122
+ expect.assertions(1);
123
+
124
+ const temporaryDirectory = fs.mkdtempSync(path.join(os.tmpdir(), "rojo-tree-test-"));
125
+ fs.writeFileSync(
126
+ path.join(temporaryDirectory, "shared.project.json"),
127
+ JSON.stringify({
128
+ name: "Shared",
129
+ tree: {
130
+ $className: "Folder",
131
+ Components: { $className: "Folder", $path: "src/Shared/Components" },
132
+ Utils: { $className: "Folder", $path: "src/Shared/Utils" },
133
+ },
134
+ }),
135
+ );
136
+
137
+ const tree: RojoTreeNode = {
138
+ $className: "DataModel",
139
+ ReplicatedStorage: {
140
+ Shared: { $path: "shared.project.json" },
141
+ },
142
+ };
143
+
144
+ const result = resolveNestedProjects(tree, temporaryDirectory);
145
+ fs.rmSync(temporaryDirectory, { force: true, recursive: true });
146
+
147
+ expect(result).toStrictEqual({
148
+ $className: "DataModel",
149
+ ReplicatedStorage: {
150
+ Shared: {
151
+ $className: "Folder",
152
+ Components: { $className: "Folder", $path: "src/Shared/Components" },
153
+ Utils: { $className: "Folder", $path: "src/Shared/Utils" },
154
+ },
155
+ },
156
+ });
157
+ });
158
+
159
+ it("should resolve chained project references recursively", () => {
160
+ expect.assertions(1);
161
+
162
+ const temporaryDirectory = fs.mkdtempSync(path.join(os.tmpdir(), "rojo-tree-test-"));
163
+ fs.writeFileSync(
164
+ path.join(temporaryDirectory, "default.project.json"),
165
+ JSON.stringify({ name: "uuid-generator", tree: { $path: "src" } }),
166
+ );
167
+
168
+ const tree: RojoTreeNode = {
169
+ $className: "DataModel",
170
+ ReplicatedStorage: {
171
+ "uuid-generator": { $path: "default.project.json" },
172
+ },
173
+ };
174
+
175
+ const result = resolveNestedProjects(tree, temporaryDirectory);
176
+ fs.rmSync(temporaryDirectory, { force: true, recursive: true });
177
+
178
+ expect(result).toStrictEqual({
179
+ $className: "DataModel",
180
+ ReplicatedStorage: {
181
+ "uuid-generator": { $path: "src" },
182
+ },
183
+ });
184
+ });
185
+
186
+ it("should throw with file path when nested project has malformed JSON", () => {
187
+ expect.assertions(1);
188
+
189
+ const temporaryDirectory = fs.mkdtempSync(path.join(os.tmpdir(), "rojo-tree-test-"));
190
+ fs.writeFileSync(path.join(temporaryDirectory, "bad.project.json"), "not valid json {{{");
191
+
192
+ const tree: RojoTreeNode = {
193
+ $className: "DataModel",
194
+ ReplicatedStorage: {
195
+ bad: { $path: "bad.project.json" },
196
+ },
197
+ };
198
+
199
+ try {
200
+ expect(() => resolveNestedProjects(tree, temporaryDirectory)).toThrow(
201
+ "bad.project.json",
202
+ );
203
+ } finally {
204
+ fs.rmSync(temporaryDirectory, { force: true, recursive: true });
205
+ }
206
+ });
207
+
208
+ it("should throw when a referenced .project.json does not exist", () => {
209
+ expect.assertions(1);
210
+
211
+ const temporaryDirectory = fs.mkdtempSync(path.join(os.tmpdir(), "rojo-tree-test-"));
212
+ const tree: RojoTreeNode = {
213
+ $className: "DataModel",
214
+ ReplicatedStorage: {
215
+ missing: { $path: "nonexistent.project.json" },
216
+ },
217
+ };
218
+
219
+ try {
220
+ expect(() => resolveNestedProjects(tree, temporaryDirectory)).toThrow(
221
+ "Could not read nested Rojo project: nonexistent.project.json",
222
+ );
223
+ } finally {
224
+ fs.rmSync(temporaryDirectory, { force: true, recursive: true });
225
+ }
226
+ });
227
+
228
+ it("should throw on circular project references", () => {
229
+ expect.assertions(1);
230
+
231
+ const temporaryDirectory = fs.mkdtempSync(path.join(os.tmpdir(), "rojo-tree-test-"));
232
+ fs.writeFileSync(
233
+ path.join(temporaryDirectory, "a.project.json"),
234
+ JSON.stringify({ name: "A", tree: { $path: "b.project.json" } }),
235
+ );
236
+ fs.writeFileSync(
237
+ path.join(temporaryDirectory, "b.project.json"),
238
+ JSON.stringify({ name: "B", tree: { $path: "a.project.json" } }),
239
+ );
240
+
241
+ const tree: RojoTreeNode = {
242
+ $className: "DataModel",
243
+ ReplicatedStorage: {
244
+ Cycle: { $path: "a.project.json" },
245
+ },
246
+ };
247
+
248
+ try {
249
+ expect(() => resolveNestedProjects(tree, temporaryDirectory)).toThrow(
250
+ "Circular project reference",
251
+ );
252
+ } finally {
253
+ fs.rmSync(temporaryDirectory, { force: true, recursive: true });
254
+ }
255
+ });
256
+
257
+ it("should leave non-string $path values unchanged", () => {
258
+ expect.assertions(1);
259
+
260
+ const tree: RojoTreeNode = {
261
+ $className: "DataModel",
262
+ Workspace: { $path: { optional: "maybe" } },
263
+ };
264
+
265
+ const temporaryDirectory = fs.mkdtempSync(path.join(os.tmpdir(), "rojo-tree-test-"));
266
+ const result = resolveNestedProjects(tree, temporaryDirectory);
267
+ fs.rmSync(temporaryDirectory, { force: true, recursive: true });
268
+
269
+ expect(result).toStrictEqual(tree);
270
+ });
271
+
272
+ it("should allow the same project file referenced from different branches", () => {
273
+ expect.assertions(1);
274
+
275
+ const temporaryDirectory = fs.mkdtempSync(path.join(os.tmpdir(), "rojo-tree-test-"));
276
+ fs.writeFileSync(
277
+ path.join(temporaryDirectory, "loader.project.json"),
278
+ JSON.stringify({ name: "Loader", tree: { $path: "src/loader" } }),
279
+ );
280
+
281
+ const tree: RojoTreeNode = {
282
+ $className: "DataModel",
283
+ ServerScriptService: {
284
+ Loader: { $path: "loader.project.json" },
285
+ },
286
+ StarterPlayer: {
287
+ Loader: { $path: "loader.project.json" },
288
+ },
289
+ };
290
+
291
+ const result = resolveNestedProjects(tree, temporaryDirectory);
292
+ fs.rmSync(temporaryDirectory, { force: true, recursive: true });
293
+
294
+ expect(result).toStrictEqual({
295
+ $className: "DataModel",
296
+ ServerScriptService: {
297
+ Loader: { $path: "src/loader" },
298
+ },
299
+ StarterPlayer: {
300
+ Loader: { $path: "src/loader" },
301
+ },
302
+ });
303
+ });
304
+
305
+ it("should resolve nested project paths relative to the project file's directory", () => {
306
+ expect.assertions(1);
307
+
308
+ const temporaryDirectory = fs.mkdtempSync(path.join(os.tmpdir(), "rojo-tree-test-"));
309
+ const subdirectory = path.join(temporaryDirectory, "packages", "my-pkg");
310
+ fs.mkdirSync(subdirectory, { recursive: true });
311
+ fs.writeFileSync(
312
+ path.join(subdirectory, "default.project.json"),
313
+ JSON.stringify({ name: "my-pkg", tree: { $path: "src" } }),
314
+ );
315
+
316
+ const tree: RojoTreeNode = {
317
+ $className: "DataModel",
318
+ ReplicatedStorage: {
319
+ "my-pkg": { $path: "packages/my-pkg/default.project.json" },
320
+ },
321
+ };
322
+
323
+ const result = resolveNestedProjects(tree, temporaryDirectory);
324
+ fs.rmSync(temporaryDirectory, { force: true, recursive: true });
325
+
326
+ expect(result).toStrictEqual({
327
+ $className: "DataModel",
328
+ ReplicatedStorage: {
329
+ "my-pkg": { $path: "packages/my-pkg/src" },
330
+ },
331
+ });
332
+ });
333
+
334
+ it("should inline a $path directory that contains a default.project.json", () => {
335
+ expect.assertions(1);
336
+
337
+ const temporaryDirectory = fs.mkdtempSync(path.join(os.tmpdir(), "rojo-tree-test-"));
338
+ const packageDirectory = path.join(temporaryDirectory, "pkg");
339
+ fs.mkdirSync(packageDirectory, { recursive: true });
340
+ fs.writeFileSync(
341
+ path.join(packageDirectory, "default.project.json"),
342
+ JSON.stringify({ name: "pkg", tree: { $path: "src" } }),
343
+ );
344
+
345
+ const tree: RojoTreeNode = {
346
+ $className: "DataModel",
347
+ ServerScriptService: {
348
+ pkg: { $path: "pkg" },
349
+ },
350
+ };
351
+
352
+ const result = resolveNestedProjects(tree, temporaryDirectory);
353
+ fs.rmSync(temporaryDirectory, { force: true, recursive: true });
354
+
355
+ expect(result).toStrictEqual({
356
+ $className: "DataModel",
357
+ ServerScriptService: {
358
+ pkg: { $path: "pkg/src" },
359
+ },
360
+ });
361
+ });
362
+
363
+ it("should follow $path '..' into a parent directory's default.project.json", () => {
364
+ expect.assertions(1);
365
+
366
+ const temporaryDirectory = fs.mkdtempSync(path.join(os.tmpdir(), "rojo-tree-test-"));
367
+ fs.writeFileSync(
368
+ path.join(temporaryDirectory, "default.project.json"),
369
+ JSON.stringify({ name: "rx", tree: { $path: "src" } }),
370
+ );
371
+ const testDirectory = path.join(temporaryDirectory, "test");
372
+ fs.mkdirSync(testDirectory, { recursive: true });
373
+
374
+ const tree: RojoTreeNode = {
375
+ $className: "DataModel",
376
+ ServerScriptService: {
377
+ rx: { $path: ".." },
378
+ },
379
+ };
380
+
381
+ const result = resolveNestedProjects(tree, testDirectory);
382
+ fs.rmSync(temporaryDirectory, { force: true, recursive: true });
383
+
384
+ expect(result).toStrictEqual({
385
+ $className: "DataModel",
386
+ ServerScriptService: {
387
+ rx: { $path: "../src" },
388
+ },
389
+ });
390
+ });
391
+
392
+ it("should leave a $path directory without default.project.json unchanged", () => {
393
+ expect.assertions(1);
394
+
395
+ const temporaryDirectory = fs.mkdtempSync(path.join(os.tmpdir(), "rojo-tree-test-"));
396
+ fs.mkdirSync(path.join(temporaryDirectory, "src"), { recursive: true });
397
+
398
+ const tree: RojoTreeNode = {
399
+ $className: "DataModel",
400
+ ReplicatedStorage: {
401
+ Src: { $path: "src" },
402
+ },
403
+ };
404
+
405
+ const result = resolveNestedProjects(tree, temporaryDirectory);
406
+ fs.rmSync(temporaryDirectory, { force: true, recursive: true });
407
+
408
+ expect(result).toStrictEqual({
409
+ $className: "DataModel",
410
+ ReplicatedStorage: {
411
+ Src: { $path: "src" },
412
+ },
413
+ });
414
+ });
415
+ });
416
+
417
+ describe(resolveNestedProjectSources, () => {
418
+ it("should report no project files when nothing is inlined", () => {
419
+ expect.assertions(2);
420
+
421
+ const tree: RojoTreeNode = {
422
+ $className: "DataModel",
423
+ ReplicatedStorage: {
424
+ client: { $path: "out/client" },
425
+ },
426
+ };
427
+
428
+ const temporaryDirectory = fs.mkdtempSync(path.join(os.tmpdir(), "rojo-tree-test-"));
429
+ const result = resolveNestedProjectSources(tree, temporaryDirectory);
430
+ fs.rmSync(temporaryDirectory, { force: true, recursive: true });
431
+
432
+ expect(result.projectFiles).toStrictEqual([]);
433
+ expect(result.tree).toStrictEqual(tree);
434
+ });
435
+
436
+ it("should report the absolute path of each inlined nested project", () => {
437
+ expect.assertions(2);
438
+
439
+ const temporaryDirectory = fs.mkdtempSync(path.join(os.tmpdir(), "rojo-tree-test-"));
440
+ const projectFile = path.join(temporaryDirectory, "default.project.json");
441
+ fs.writeFileSync(projectFile, JSON.stringify({ name: "my-pkg", tree: { $path: "src" } }));
442
+
443
+ const tree: RojoTreeNode = {
444
+ $className: "DataModel",
445
+ ReplicatedStorage: {
446
+ "my-pkg": { $path: "default.project.json" },
447
+ },
448
+ };
449
+
450
+ const result = resolveNestedProjectSources(tree, temporaryDirectory);
451
+ fs.rmSync(temporaryDirectory, { force: true, recursive: true });
452
+
453
+ expect(result.projectFiles).toStrictEqual([projectFile]);
454
+ expect(result.tree).toStrictEqual({
455
+ $className: "DataModel",
456
+ ReplicatedStorage: {
457
+ "my-pkg": { $path: "src" },
458
+ },
459
+ });
460
+ });
461
+
462
+ it("should report every project file in a chained reference", () => {
463
+ expect.assertions(1);
464
+
465
+ const temporaryDirectory = fs.mkdtempSync(path.join(os.tmpdir(), "rojo-tree-test-"));
466
+ const outer = path.join(temporaryDirectory, "a.project.json");
467
+ const inner = path.join(temporaryDirectory, "b.project.json");
468
+ fs.writeFileSync(outer, JSON.stringify({ name: "A", tree: { $path: "b.project.json" } }));
469
+ fs.writeFileSync(inner, JSON.stringify({ name: "B", tree: { $path: "src" } }));
470
+
471
+ const tree: RojoTreeNode = {
472
+ $className: "DataModel",
473
+ ReplicatedStorage: {
474
+ Chain: { $path: "a.project.json" },
475
+ },
476
+ };
477
+
478
+ const result = resolveNestedProjectSources(tree, temporaryDirectory);
479
+ fs.rmSync(temporaryDirectory, { force: true, recursive: true });
480
+
481
+ expect(new Set(result.projectFiles)).toStrictEqual(new Set([inner, outer]));
482
+ });
483
+
484
+ it("should report a project file referenced from two branches once", () => {
485
+ expect.assertions(1);
486
+
487
+ const temporaryDirectory = fs.mkdtempSync(path.join(os.tmpdir(), "rojo-tree-test-"));
488
+ const projectFile = path.join(temporaryDirectory, "loader.project.json");
489
+ fs.writeFileSync(
490
+ projectFile,
491
+ JSON.stringify({ name: "Loader", tree: { $path: "src/loader" } }),
492
+ );
493
+
494
+ const tree: RojoTreeNode = {
495
+ $className: "DataModel",
496
+ ServerScriptService: {
497
+ Loader: { $path: "loader.project.json" },
498
+ },
499
+ StarterPlayer: {
500
+ Loader: { $path: "loader.project.json" },
501
+ },
502
+ };
503
+
504
+ const result = resolveNestedProjectSources(tree, temporaryDirectory);
505
+ fs.rmSync(temporaryDirectory, { force: true, recursive: true });
506
+
507
+ expect(result.projectFiles).toStrictEqual([projectFile]);
508
+ });
509
+
510
+ it("should report a directory mount that contains a default.project.json", () => {
511
+ expect.assertions(2);
512
+
513
+ const temporaryDirectory = fs.mkdtempSync(path.join(os.tmpdir(), "rojo-tree-test-"));
514
+ const packageDirectory = path.join(temporaryDirectory, "pkg");
515
+ fs.mkdirSync(packageDirectory, { recursive: true });
516
+ const projectFile = path.join(packageDirectory, "default.project.json");
517
+ fs.writeFileSync(projectFile, JSON.stringify({ name: "pkg", tree: { $path: "src" } }));
518
+
519
+ const tree: RojoTreeNode = {
520
+ $className: "DataModel",
521
+ ServerScriptService: {
522
+ pkg: { $path: "pkg" },
523
+ },
524
+ };
525
+
526
+ const result = resolveNestedProjectSources(tree, temporaryDirectory);
527
+ fs.rmSync(temporaryDirectory, { force: true, recursive: true });
528
+
529
+ expect(result.projectFiles).toStrictEqual([projectFile]);
530
+ expect(result.tree).toStrictEqual({
531
+ $className: "DataModel",
532
+ ServerScriptService: {
533
+ pkg: { $path: "pkg/src" },
534
+ },
535
+ });
536
+ });
537
+ });
538
+
539
+ describe(rebaseTreePaths, () => {
540
+ it("should re-express $path strings from one base directory to another", () => {
541
+ expect.assertions(1);
542
+
543
+ const packageDirectory = path.resolve("repo", "pkg");
544
+ const testDirectory = path.join(packageDirectory, "test");
545
+
546
+ const tree: RojoTreeNode = {
547
+ $className: "DataModel",
548
+ ServerScriptService: {
549
+ rx: { $path: "../src" },
550
+ },
551
+ };
552
+
553
+ const result = rebaseTreePaths(tree, testDirectory, packageDirectory);
554
+
555
+ expect(result).toStrictEqual({
556
+ $className: "DataModel",
557
+ ServerScriptService: {
558
+ rx: { $path: "src" },
559
+ },
560
+ });
561
+ });
562
+
563
+ it("should return $path unchanged when both bases are equal", () => {
564
+ expect.assertions(1);
565
+
566
+ const base = path.resolve("repo", "pkg");
567
+ const tree: RojoTreeNode = {
568
+ $className: "DataModel",
569
+ ReplicatedStorage: {
570
+ Src: { $path: "src" },
571
+ },
572
+ };
573
+
574
+ const result = rebaseTreePaths(tree, base, base);
575
+
576
+ expect(result).toStrictEqual({
577
+ $className: "DataModel",
578
+ ReplicatedStorage: {
579
+ Src: { $path: "src" },
580
+ },
581
+ });
582
+ });
583
+
584
+ it("should rebase every $path in a nested tree", () => {
585
+ expect.assertions(1);
586
+
587
+ const packageDirectory = path.resolve("repo", "pkg");
588
+ const testDirectory = path.join(packageDirectory, "test");
589
+
590
+ const tree: RojoTreeNode = {
591
+ $className: "DataModel",
592
+ ReplicatedStorage: {
593
+ Packages: { $path: "../node_modules" },
594
+ Src: { $path: "../src" },
595
+ },
596
+ };
597
+
598
+ const result = rebaseTreePaths(tree, testDirectory, packageDirectory);
599
+
600
+ expect(result).toStrictEqual({
601
+ $className: "DataModel",
602
+ ReplicatedStorage: {
603
+ Packages: { $path: "node_modules" },
604
+ Src: { $path: "src" },
605
+ },
606
+ });
607
+ });
608
+
609
+ it("should leave non-string $path values unchanged", () => {
610
+ expect.assertions(1);
611
+
612
+ const base = path.resolve("repo", "pkg");
613
+ const tree: RojoTreeNode = {
614
+ $className: "DataModel",
615
+ Workspace: { $path: { optional: "maybe" } },
616
+ };
617
+
618
+ const result = rebaseTreePaths(tree, path.join(base, "test"), base);
619
+
620
+ expect(result).toStrictEqual(tree);
621
+ });
622
+ });