apify-schema-tools 2.0.1

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 (83) hide show
  1. package/.cspell/custom-dictionary.txt +4 -0
  2. package/.husky/pre-commit +33 -0
  3. package/.node-version +1 -0
  4. package/CHANGELOG.md +88 -0
  5. package/LICENSE +201 -0
  6. package/README.md +312 -0
  7. package/biome.json +31 -0
  8. package/dist/apify-schema-tools.d.ts +3 -0
  9. package/dist/apify-schema-tools.d.ts.map +1 -0
  10. package/dist/apify-schema-tools.js +197 -0
  11. package/dist/apify-schema-tools.js.map +1 -0
  12. package/dist/apify.d.ts +11 -0
  13. package/dist/apify.d.ts.map +1 -0
  14. package/dist/apify.js +107 -0
  15. package/dist/apify.js.map +1 -0
  16. package/dist/configuration.d.ts +43 -0
  17. package/dist/configuration.d.ts.map +1 -0
  18. package/dist/configuration.js +87 -0
  19. package/dist/configuration.js.map +1 -0
  20. package/dist/filesystem.d.ts +8 -0
  21. package/dist/filesystem.d.ts.map +1 -0
  22. package/dist/filesystem.js +16 -0
  23. package/dist/filesystem.js.map +1 -0
  24. package/dist/json-schemas.d.ts +34 -0
  25. package/dist/json-schemas.d.ts.map +1 -0
  26. package/dist/json-schemas.js +185 -0
  27. package/dist/json-schemas.js.map +1 -0
  28. package/dist/typescript.d.ts +26 -0
  29. package/dist/typescript.d.ts.map +1 -0
  30. package/dist/typescript.js +316 -0
  31. package/dist/typescript.js.map +1 -0
  32. package/package.json +60 -0
  33. package/samples/all-defaults/.actor/actor.json +15 -0
  34. package/samples/all-defaults/.actor/dataset_schema.json +32 -0
  35. package/samples/all-defaults/.actor/input_schema.json +53 -0
  36. package/samples/all-defaults/src/generated/dataset.ts +24 -0
  37. package/samples/all-defaults/src/generated/input-utils.ts +60 -0
  38. package/samples/all-defaults/src/generated/input.ts +42 -0
  39. package/samples/all-defaults/src-schemas/dataset-item.json +28 -0
  40. package/samples/all-defaults/src-schemas/input.json +73 -0
  41. package/samples/deep-merged-schemas/.actor/actor.json +15 -0
  42. package/samples/deep-merged-schemas/.actor/dataset_schema.json +37 -0
  43. package/samples/deep-merged-schemas/.actor/input_schema.json +61 -0
  44. package/samples/deep-merged-schemas/add-schemas/dataset-item.json +10 -0
  45. package/samples/deep-merged-schemas/add-schemas/input.json +33 -0
  46. package/samples/deep-merged-schemas/src/generated/dataset.ts +28 -0
  47. package/samples/deep-merged-schemas/src/generated/input-utils.ts +66 -0
  48. package/samples/deep-merged-schemas/src/generated/input.ts +47 -0
  49. package/samples/deep-merged-schemas/src-schemas/dataset-item.json +28 -0
  50. package/samples/deep-merged-schemas/src-schemas/input.json +73 -0
  51. package/samples/merged-schemas/.actor/actor.json +15 -0
  52. package/samples/merged-schemas/.actor/dataset_schema.json +37 -0
  53. package/samples/merged-schemas/.actor/input_schema.json +58 -0
  54. package/samples/merged-schemas/add-schemas/dataset-item.json +10 -0
  55. package/samples/merged-schemas/add-schemas/input.json +33 -0
  56. package/samples/merged-schemas/src/generated/dataset.ts +28 -0
  57. package/samples/merged-schemas/src/generated/input-utils.ts +57 -0
  58. package/samples/merged-schemas/src/generated/input.ts +42 -0
  59. package/samples/merged-schemas/src-schemas/dataset-item.json +28 -0
  60. package/samples/merged-schemas/src-schemas/input.json +73 -0
  61. package/samples/package-json-config/.actor/actor.json +15 -0
  62. package/samples/package-json-config/.actor/dataset_schema.json +32 -0
  63. package/samples/package-json-config/.actor/input_schema.json +53 -0
  64. package/samples/package-json-config/custom-src-schemas/dataset-item.json +28 -0
  65. package/samples/package-json-config/custom-src-schemas/input.json +73 -0
  66. package/samples/package-json-config/package.json +11 -0
  67. package/samples/package-json-config/src/custom-generated/dataset.ts +24 -0
  68. package/samples/package-json-config/src/custom-generated/input-utils.ts +60 -0
  69. package/samples/package-json-config/src/custom-generated/input.ts +42 -0
  70. package/src/apify-schema-tools.ts +302 -0
  71. package/src/apify.ts +124 -0
  72. package/src/configuration.ts +110 -0
  73. package/src/filesystem.ts +18 -0
  74. package/src/json-schemas.ts +252 -0
  75. package/src/typescript.ts +381 -0
  76. package/test/apify-schema-tools.test.ts +2064 -0
  77. package/test/apify.test.ts +28 -0
  78. package/test/common.ts +19 -0
  79. package/test/configuration.test.ts +642 -0
  80. package/test/json-schemas.test.ts +587 -0
  81. package/test/typescript.test.ts +817 -0
  82. package/tsconfig.json +18 -0
  83. package/update-samples.sh +27 -0
@@ -0,0 +1,28 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import type { ObjectSchema } from "../src/json-schemas";
3
+
4
+ describe("Apify utilities", () => {
5
+ describe("generateInputDefaultsTSFileContent", () => {
6
+ it("should generate TypeScript content with default values", async () => {
7
+ const inputSchema: ObjectSchema = {
8
+ type: "object",
9
+ properties: {
10
+ name: { type: "string", default: "John Doe" },
11
+ age: { type: "number", default: 30 },
12
+ email: { type: "string" },
13
+ },
14
+ required: ["name"],
15
+ };
16
+
17
+ const tsContent = await import("../src/apify.js").then((mod) =>
18
+ mod.generateInputDefaultsTSFileContent(inputSchema),
19
+ );
20
+
21
+ expect(tsContent).toContain("export type InputWithDefaults = Input & {");
22
+ expect(tsContent).toContain("export const DEFAULT_INPUT_VALUES = {");
23
+ expect(tsContent).toContain('"name": "John Doe"');
24
+ expect(tsContent).toContain('"age": 30');
25
+ expect(tsContent).toContain("age: number;");
26
+ });
27
+ });
28
+ });
package/test/common.ts ADDED
@@ -0,0 +1,19 @@
1
+ import { existsSync, mkdirSync, rmSync } from "node:fs";
2
+ import { join } from "node:path";
3
+
4
+ export function getTestDir(suffix: string): string {
5
+ return join(__dirname, `test-temp-${suffix}`);
6
+ }
7
+
8
+ export function setupTestDirectory(dirPath: string) {
9
+ // Create directory if it doesn't exist, do nothing if it already exists
10
+ if (!existsSync(dirPath)) {
11
+ mkdirSync(dirPath, { recursive: true });
12
+ }
13
+ }
14
+
15
+ export function cleanupTestDirectory(dirPath: string) {
16
+ if (existsSync(dirPath)) {
17
+ rmSync(dirPath, { recursive: true, force: true });
18
+ }
19
+ }
@@ -0,0 +1,642 @@
1
+ import { existsSync } from "node:fs";
2
+ import type { ArgumentParser } from "argparse";
3
+ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
4
+ import {
5
+ type CommonCLIArgs,
6
+ Configuration,
7
+ addCommonCLIArgs,
8
+ parseConfigurationFromFileOrDefault,
9
+ writeConfigurationToPackageJson,
10
+ } from "../src/configuration.js";
11
+
12
+ // Mock filesystem
13
+ vi.mock("node:fs", () => ({
14
+ existsSync: vi.fn(),
15
+ writeFileSync: vi.fn(),
16
+ rmSync: vi.fn(),
17
+ }));
18
+
19
+ vi.mock("../src/filesystem.js", () => ({
20
+ readFile: vi.fn(),
21
+ writeFile: vi.fn(),
22
+ }));
23
+
24
+ const mockExistsSync = vi.mocked(existsSync);
25
+ const mockReadFile = vi.mocked(await import("../src/filesystem.js")).readFile;
26
+ const mockWriteFile = vi.mocked(await import("../src/filesystem.js")).writeFile;
27
+
28
+ describe("parseConfigurationFromFileOrDefault", () => {
29
+ beforeEach(() => {
30
+ vi.clearAllMocks();
31
+ });
32
+
33
+ it("should return default configuration when package.json does not exist", () => {
34
+ mockExistsSync.mockReturnValue(false);
35
+
36
+ const result = parseConfigurationFromFileOrDefault();
37
+
38
+ expect(result).toEqual({
39
+ input: ["input", "dataset"],
40
+ output: ["json-schemas", "ts-types"],
41
+ srcInput: "src-schemas/input.json",
42
+ srcDataset: "src-schemas/dataset-item.json",
43
+ addInput: undefined,
44
+ addDataset: undefined,
45
+ inputSchema: ".actor/input_schema.json",
46
+ datasetSchema: ".actor/dataset_schema.json",
47
+ outputTSDir: "src/generated",
48
+ });
49
+ expect(mockExistsSync).toHaveBeenCalledWith("package.json");
50
+ expect(mockReadFile).not.toHaveBeenCalled();
51
+ });
52
+
53
+ it("should return default configuration when package.json exists but has no apify-schema-tools config", () => {
54
+ mockExistsSync.mockReturnValue(true);
55
+ mockReadFile.mockReturnValue('{"name": "test-package"}');
56
+
57
+ const result = parseConfigurationFromFileOrDefault();
58
+
59
+ expect(result).toEqual({
60
+ input: ["input", "dataset"],
61
+ output: ["json-schemas", "ts-types"],
62
+ srcInput: "src-schemas/input.json",
63
+ srcDataset: "src-schemas/dataset-item.json",
64
+ addInput: undefined,
65
+ addDataset: undefined,
66
+ inputSchema: ".actor/input_schema.json",
67
+ datasetSchema: ".actor/dataset_schema.json",
68
+ outputTSDir: "src/generated",
69
+ });
70
+ expect(mockExistsSync).toHaveBeenCalledWith("package.json");
71
+ expect(mockReadFile).toHaveBeenCalledWith("package.json");
72
+ });
73
+
74
+ it("should return default configuration when package.json has empty apify-schema-tools config", () => {
75
+ mockExistsSync.mockReturnValue(true);
76
+ mockReadFile.mockReturnValue(
77
+ JSON.stringify({
78
+ name: "test-package",
79
+ "apify-schema-tools": {},
80
+ }),
81
+ );
82
+
83
+ const result = parseConfigurationFromFileOrDefault();
84
+
85
+ expect(result).toEqual({
86
+ input: ["input", "dataset"],
87
+ output: ["json-schemas", "ts-types"],
88
+ srcInput: "src-schemas/input.json",
89
+ srcDataset: "src-schemas/dataset-item.json",
90
+ addInput: undefined,
91
+ addDataset: undefined,
92
+ inputSchema: ".actor/input_schema.json",
93
+ datasetSchema: ".actor/dataset_schema.json",
94
+ outputTSDir: "src/generated",
95
+ });
96
+ expect(mockExistsSync).toHaveBeenCalledWith("package.json");
97
+ expect(mockReadFile).toHaveBeenCalledWith("package.json");
98
+ });
99
+
100
+ it("should merge custom configuration from package.json with defaults", () => {
101
+ mockExistsSync.mockReturnValue(true);
102
+ mockReadFile.mockReturnValue(
103
+ JSON.stringify({
104
+ name: "test-package",
105
+ "apify-schema-tools": {
106
+ input: ["input"],
107
+ srcInput: "custom-schemas/input.json",
108
+ outputTSDir: "custom/generated",
109
+ },
110
+ }),
111
+ );
112
+
113
+ const result = parseConfigurationFromFileOrDefault();
114
+
115
+ expect(result).toEqual({
116
+ input: ["input"],
117
+ output: ["json-schemas", "ts-types"], // default
118
+ srcInput: "custom-schemas/input.json",
119
+ srcDataset: "src-schemas/dataset-item.json", // default
120
+ addInput: undefined,
121
+ addDataset: undefined,
122
+ inputSchema: ".actor/input_schema.json", // default
123
+ datasetSchema: ".actor/dataset_schema.json", // default
124
+ outputTSDir: "custom/generated",
125
+ });
126
+ });
127
+
128
+ it("should handle complete custom configuration", () => {
129
+ mockExistsSync.mockReturnValue(true);
130
+ mockReadFile.mockReturnValue(
131
+ JSON.stringify({
132
+ name: "test-package",
133
+ "apify-schema-tools": {
134
+ input: ["dataset"],
135
+ output: ["ts-types"],
136
+ srcInput: "custom-input.json",
137
+ srcDataset: "custom-dataset.json",
138
+ addInput: "additional-input.json",
139
+ addDataset: "additional-dataset.json",
140
+ inputSchema: "custom-input-schema.json",
141
+ datasetSchema: "custom-dataset-schema.json",
142
+ outputTSDir: "custom-output",
143
+ },
144
+ }),
145
+ );
146
+
147
+ const result = parseConfigurationFromFileOrDefault();
148
+
149
+ expect(result).toEqual({
150
+ input: ["dataset"],
151
+ output: ["ts-types"],
152
+ srcInput: "custom-input.json",
153
+ srcDataset: "custom-dataset.json",
154
+ addInput: "additional-input.json",
155
+ addDataset: "additional-dataset.json",
156
+ inputSchema: "custom-input-schema.json",
157
+ datasetSchema: "custom-dataset-schema.json",
158
+ outputTSDir: "custom-output",
159
+ });
160
+ });
161
+
162
+ it("should throw error for invalid configuration", () => {
163
+ mockExistsSync.mockReturnValue(true);
164
+ mockReadFile.mockReturnValue(
165
+ JSON.stringify({
166
+ name: "test-package",
167
+ "apify-schema-tools": {
168
+ input: ["invalid-input"], // invalid enum value
169
+ },
170
+ }),
171
+ );
172
+
173
+ expect(() => parseConfigurationFromFileOrDefault()).toThrow();
174
+ });
175
+
176
+ it("should throw error for malformed JSON", () => {
177
+ mockExistsSync.mockReturnValue(true);
178
+ mockReadFile.mockReturnValue("invalid json");
179
+
180
+ expect(() => parseConfigurationFromFileOrDefault()).toThrow();
181
+ });
182
+ });
183
+
184
+ describe("addCommonCLIArgs", () => {
185
+ let parser: ArgumentParser;
186
+ let mockAddArgument: ReturnType<typeof vi.fn>;
187
+
188
+ beforeEach(() => {
189
+ mockAddArgument = vi.fn();
190
+ parser = {
191
+ add_argument: mockAddArgument,
192
+ } as unknown as ArgumentParser;
193
+ });
194
+
195
+ afterEach(() => {
196
+ vi.clearAllMocks();
197
+ });
198
+
199
+ it("should add all CLI arguments with default configuration", () => {
200
+ const defaultConfig = Configuration.parse({});
201
+
202
+ addCommonCLIArgs(parser, defaultConfig);
203
+
204
+ expect(mockAddArgument).toHaveBeenCalledTimes(9);
205
+
206
+ // Check input argument
207
+ expect(mockAddArgument).toHaveBeenCalledWith("-i", "--input", {
208
+ help: "specify which sources to use for generation",
209
+ choices: ["input", "dataset"],
210
+ default: ["input", "dataset"],
211
+ nargs: "*",
212
+ });
213
+
214
+ // Check output argument
215
+ expect(mockAddArgument).toHaveBeenCalledWith("-o", "--output", {
216
+ help: "specify what to generate",
217
+ choices: ["json-schemas", "ts-types"],
218
+ default: ["json-schemas", "ts-types"],
219
+ nargs: "*",
220
+ });
221
+
222
+ // Check src-input argument
223
+ expect(mockAddArgument).toHaveBeenCalledWith("--src-input", {
224
+ help: "path to the input schema source file",
225
+ default: "src-schemas/input.json",
226
+ });
227
+
228
+ // Check src-dataset argument
229
+ expect(mockAddArgument).toHaveBeenCalledWith("--src-dataset", {
230
+ help: "path to the dataset schema source file",
231
+ default: "src-schemas/dataset-item.json",
232
+ });
233
+
234
+ // Check add-input argument
235
+ expect(mockAddArgument).toHaveBeenCalledWith("--add-input", {
236
+ help: "path to an additional schema to merge into the input schema",
237
+ default: undefined,
238
+ });
239
+
240
+ // Check add-dataset argument
241
+ expect(mockAddArgument).toHaveBeenCalledWith("--add-dataset", {
242
+ help: "path to an additional schema to merge into the dataset schema",
243
+ default: undefined,
244
+ });
245
+
246
+ // Check input-schema argument
247
+ expect(mockAddArgument).toHaveBeenCalledWith("--input-schema", {
248
+ help: "the path of the destination input schema file",
249
+ default: ".actor/input_schema.json",
250
+ });
251
+
252
+ // Check dataset-schema argument
253
+ expect(mockAddArgument).toHaveBeenCalledWith("--dataset-schema", {
254
+ help: "the path of the destination dataset schema file",
255
+ default: ".actor/dataset_schema.json",
256
+ });
257
+
258
+ // Check output-ts-dir argument
259
+ expect(mockAddArgument).toHaveBeenCalledWith("--output-ts-dir", {
260
+ help: "path where to save generated TypeScript files",
261
+ default: "src/generated",
262
+ });
263
+ });
264
+
265
+ it("should add CLI arguments with custom configuration", () => {
266
+ const customConfig = Configuration.parse({
267
+ input: ["input"],
268
+ output: ["ts-types"],
269
+ srcInput: "custom-input.json",
270
+ srcDataset: "custom-dataset.json",
271
+ addInput: "additional.json",
272
+ addDataset: "additional-dataset.json",
273
+ inputSchema: "custom-input-schema.json",
274
+ datasetSchema: "custom-dataset-schema.json",
275
+ outputTSDir: "custom-output",
276
+ });
277
+
278
+ addCommonCLIArgs(parser, customConfig);
279
+
280
+ expect(mockAddArgument).toHaveBeenCalledTimes(9);
281
+
282
+ // Check that custom defaults are used
283
+ expect(mockAddArgument).toHaveBeenCalledWith("-i", "--input", {
284
+ help: "specify which sources to use for generation",
285
+ choices: ["input", "dataset"],
286
+ default: ["input"],
287
+ nargs: "*",
288
+ });
289
+
290
+ expect(mockAddArgument).toHaveBeenCalledWith("-o", "--output", {
291
+ help: "specify what to generate",
292
+ choices: ["json-schemas", "ts-types"],
293
+ default: ["ts-types"],
294
+ nargs: "*",
295
+ });
296
+
297
+ expect(mockAddArgument).toHaveBeenCalledWith("--src-input", {
298
+ help: "path to the input schema source file",
299
+ default: "custom-input.json",
300
+ });
301
+
302
+ expect(mockAddArgument).toHaveBeenCalledWith("--add-input", {
303
+ help: "path to an additional schema to merge into the input schema",
304
+ default: "additional.json",
305
+ });
306
+
307
+ expect(mockAddArgument).toHaveBeenCalledWith("--output-ts-dir", {
308
+ help: "path where to save generated TypeScript files",
309
+ default: "custom-output",
310
+ });
311
+ });
312
+
313
+ it("should preserve argument order", () => {
314
+ const config = Configuration.parse({});
315
+
316
+ addCommonCLIArgs(parser, config);
317
+
318
+ const calls = mockAddArgument.mock.calls;
319
+ expect(calls[0]).toEqual(["-i", "--input", expect.any(Object)]);
320
+ expect(calls[1]).toEqual(["-o", "--output", expect.any(Object)]);
321
+ expect(calls[2]).toEqual(["--src-input", expect.any(Object)]);
322
+ expect(calls[3]).toEqual(["--src-dataset", expect.any(Object)]);
323
+ expect(calls[4]).toEqual(["--add-input", expect.any(Object)]);
324
+ expect(calls[5]).toEqual(["--add-dataset", expect.any(Object)]);
325
+ expect(calls[6]).toEqual(["--input-schema", expect.any(Object)]);
326
+ expect(calls[7]).toEqual(["--dataset-schema", expect.any(Object)]);
327
+ expect(calls[8]).toEqual(["--output-ts-dir", expect.any(Object)]);
328
+ });
329
+
330
+ it("should handle empty arrays in configuration", () => {
331
+ const configWithEmptyArrays = Configuration.parse({
332
+ input: [],
333
+ output: [],
334
+ });
335
+
336
+ addCommonCLIArgs(parser, configWithEmptyArrays);
337
+
338
+ expect(mockAddArgument).toHaveBeenCalledWith("-i", "--input", {
339
+ help: "specify which sources to use for generation",
340
+ choices: ["input", "dataset"],
341
+ default: [],
342
+ nargs: "*",
343
+ });
344
+
345
+ expect(mockAddArgument).toHaveBeenCalledWith("-o", "--output", {
346
+ help: "specify what to generate",
347
+ choices: ["json-schemas", "ts-types"],
348
+ default: [],
349
+ nargs: "*",
350
+ });
351
+ });
352
+ });
353
+
354
+ describe("writeConfigurationToPackageJson", () => {
355
+ beforeEach(() => {
356
+ vi.clearAllMocks();
357
+ });
358
+
359
+ it("should write configuration to package.json when file exists", () => {
360
+ mockExistsSync.mockReturnValue(true);
361
+ mockReadFile.mockReturnValue(JSON.stringify({ name: "test-package", version: "1.0.0" }));
362
+
363
+ const args: CommonCLIArgs = {
364
+ input: ["input"],
365
+ output: ["ts-types"],
366
+ src_input: "custom-input.json",
367
+ src_dataset: "custom-dataset.json",
368
+ add_input: "additional-input.json",
369
+ add_dataset: "additional-dataset.json",
370
+ input_schema: "custom-input-schema.json",
371
+ dataset_schema: "custom-dataset-schema.json",
372
+ output_ts_dir: "custom-output",
373
+ };
374
+
375
+ writeConfigurationToPackageJson(args);
376
+
377
+ expect(mockExistsSync).toHaveBeenCalledWith("package.json");
378
+ expect(mockReadFile).toHaveBeenCalledWith("package.json");
379
+ expect(mockWriteFile).toHaveBeenCalledWith(
380
+ "package.json",
381
+ JSON.stringify(
382
+ {
383
+ name: "test-package",
384
+ version: "1.0.0",
385
+ "apify-schema-tools": {
386
+ input: ["input"],
387
+ output: ["ts-types"],
388
+ srcInput: "custom-input.json",
389
+ srcDataset: "custom-dataset.json",
390
+ addInput: "additional-input.json",
391
+ addDataset: "additional-dataset.json",
392
+ inputSchema: "custom-input-schema.json",
393
+ datasetSchema: "custom-dataset-schema.json",
394
+ outputTSDir: "custom-output",
395
+ },
396
+ },
397
+ null,
398
+ 2,
399
+ ),
400
+ );
401
+ });
402
+
403
+ it("should overwrite existing apify-schema-tools configuration", () => {
404
+ mockExistsSync.mockReturnValue(true);
405
+ mockReadFile.mockReturnValue(
406
+ JSON.stringify({
407
+ name: "test-package",
408
+ version: "1.0.0",
409
+ "apify-schema-tools": {
410
+ input: ["dataset"],
411
+ output: ["json-schemas"],
412
+ srcInput: "old-input.json",
413
+ },
414
+ }),
415
+ );
416
+
417
+ const args: CommonCLIArgs = {
418
+ input: ["input", "dataset"],
419
+ output: ["json-schemas", "ts-types"],
420
+ src_input: "new-input.json",
421
+ src_dataset: "new-dataset.json",
422
+ input_schema: "new-input-schema.json",
423
+ dataset_schema: "new-dataset-schema.json",
424
+ output_ts_dir: "new-output",
425
+ };
426
+
427
+ writeConfigurationToPackageJson(args);
428
+
429
+ expect(mockWriteFile).toHaveBeenCalledWith(
430
+ "package.json",
431
+ JSON.stringify(
432
+ {
433
+ name: "test-package",
434
+ version: "1.0.0",
435
+ "apify-schema-tools": {
436
+ input: ["input", "dataset"],
437
+ output: ["json-schemas", "ts-types"],
438
+ srcInput: "new-input.json",
439
+ srcDataset: "new-dataset.json",
440
+ addInput: undefined,
441
+ addDataset: undefined,
442
+ inputSchema: "new-input-schema.json",
443
+ datasetSchema: "new-dataset-schema.json",
444
+ outputTSDir: "new-output",
445
+ },
446
+ },
447
+ null,
448
+ 2,
449
+ ),
450
+ );
451
+ });
452
+
453
+ it("should handle optional fields correctly when undefined", () => {
454
+ mockExistsSync.mockReturnValue(true);
455
+ mockReadFile.mockReturnValue(JSON.stringify({ name: "test-package" }));
456
+
457
+ const args: CommonCLIArgs = {
458
+ input: ["input"],
459
+ output: ["ts-types"],
460
+ src_input: "input.json",
461
+ src_dataset: "dataset.json",
462
+ add_input: undefined,
463
+ add_dataset: undefined,
464
+ input_schema: "input-schema.json",
465
+ dataset_schema: "dataset-schema.json",
466
+ output_ts_dir: "output",
467
+ };
468
+
469
+ writeConfigurationToPackageJson(args);
470
+
471
+ expect(mockWriteFile).toHaveBeenCalledWith(
472
+ "package.json",
473
+ JSON.stringify(
474
+ {
475
+ name: "test-package",
476
+ "apify-schema-tools": {
477
+ input: ["input"],
478
+ output: ["ts-types"],
479
+ srcInput: "input.json",
480
+ srcDataset: "dataset.json",
481
+ addInput: undefined,
482
+ addDataset: undefined,
483
+ inputSchema: "input-schema.json",
484
+ datasetSchema: "dataset-schema.json",
485
+ outputTSDir: "output",
486
+ },
487
+ },
488
+ null,
489
+ 2,
490
+ ),
491
+ );
492
+ });
493
+
494
+ it("should handle optional fields correctly when defined", () => {
495
+ mockExistsSync.mockReturnValue(true);
496
+ mockReadFile.mockReturnValue(JSON.stringify({ name: "test-package" }));
497
+
498
+ const args: CommonCLIArgs = {
499
+ input: ["input", "dataset"],
500
+ output: ["json-schemas", "ts-types"],
501
+ src_input: "input.json",
502
+ src_dataset: "dataset.json",
503
+ add_input: "additional.json",
504
+ add_dataset: "additional-dataset.json",
505
+ input_schema: "input-schema.json",
506
+ dataset_schema: "dataset-schema.json",
507
+ output_ts_dir: "output",
508
+ };
509
+
510
+ writeConfigurationToPackageJson(args);
511
+
512
+ expect(mockWriteFile).toHaveBeenCalledWith(
513
+ "package.json",
514
+ JSON.stringify(
515
+ {
516
+ name: "test-package",
517
+ "apify-schema-tools": {
518
+ input: ["input", "dataset"],
519
+ output: ["json-schemas", "ts-types"],
520
+ srcInput: "input.json",
521
+ srcDataset: "dataset.json",
522
+ addInput: "additional.json",
523
+ addDataset: "additional-dataset.json",
524
+ inputSchema: "input-schema.json",
525
+ datasetSchema: "dataset-schema.json",
526
+ outputTSDir: "output",
527
+ },
528
+ },
529
+ null,
530
+ 2,
531
+ ),
532
+ );
533
+ });
534
+
535
+ it("should throw error when package.json does not exist", () => {
536
+ mockExistsSync.mockReturnValue(false);
537
+
538
+ const args: CommonCLIArgs = {
539
+ input: ["input"],
540
+ output: ["ts-types"],
541
+ src_input: "input.json",
542
+ src_dataset: "dataset.json",
543
+ input_schema: "input-schema.json",
544
+ dataset_schema: "dataset-schema.json",
545
+ output_ts_dir: "output",
546
+ };
547
+
548
+ expect(() => writeConfigurationToPackageJson(args)).toThrow(
549
+ "package.json does not exist. Please run this command in the root of your project.",
550
+ );
551
+ expect(mockExistsSync).toHaveBeenCalledWith("package.json");
552
+ expect(mockReadFile).not.toHaveBeenCalled();
553
+ expect(mockWriteFile).not.toHaveBeenCalled();
554
+ });
555
+
556
+ it("should preserve existing package.json structure and add apify-schema-tools", () => {
557
+ mockExistsSync.mockReturnValue(true);
558
+ mockReadFile.mockReturnValue(
559
+ JSON.stringify({
560
+ name: "test-package",
561
+ version: "1.0.0",
562
+ description: "Test package",
563
+ scripts: {
564
+ build: "tsc",
565
+ test: "vitest",
566
+ },
567
+ dependencies: {
568
+ "some-package": "^1.0.0",
569
+ },
570
+ devDependencies: {
571
+ typescript: "^5.0.0",
572
+ },
573
+ }),
574
+ );
575
+
576
+ const args: CommonCLIArgs = {
577
+ input: ["input"],
578
+ output: ["ts-types"],
579
+ src_input: "input.json",
580
+ src_dataset: "dataset.json",
581
+ input_schema: "input-schema.json",
582
+ dataset_schema: "dataset-schema.json",
583
+ output_ts_dir: "output",
584
+ };
585
+
586
+ writeConfigurationToPackageJson(args);
587
+
588
+ expect(mockWriteFile).toHaveBeenCalledWith(
589
+ "package.json",
590
+ JSON.stringify(
591
+ {
592
+ name: "test-package",
593
+ version: "1.0.0",
594
+ description: "Test package",
595
+ scripts: {
596
+ build: "tsc",
597
+ test: "vitest",
598
+ },
599
+ dependencies: {
600
+ "some-package": "^1.0.0",
601
+ },
602
+ devDependencies: {
603
+ typescript: "^5.0.0",
604
+ },
605
+ "apify-schema-tools": {
606
+ input: ["input"],
607
+ output: ["ts-types"],
608
+ srcInput: "input.json",
609
+ srcDataset: "dataset.json",
610
+ addInput: undefined,
611
+ addDataset: undefined,
612
+ inputSchema: "input-schema.json",
613
+ datasetSchema: "dataset-schema.json",
614
+ outputTSDir: "output",
615
+ },
616
+ },
617
+ null,
618
+ 2,
619
+ ),
620
+ );
621
+ });
622
+
623
+ it("should handle malformed package.json", () => {
624
+ mockExistsSync.mockReturnValue(true);
625
+ mockReadFile.mockReturnValue("invalid json");
626
+
627
+ const args: CommonCLIArgs = {
628
+ input: ["input"],
629
+ output: ["ts-types"],
630
+ src_input: "input.json",
631
+ src_dataset: "dataset.json",
632
+ input_schema: "input-schema.json",
633
+ dataset_schema: "dataset-schema.json",
634
+ output_ts_dir: "output",
635
+ };
636
+
637
+ expect(() => writeConfigurationToPackageJson(args)).toThrow();
638
+ expect(mockExistsSync).toHaveBeenCalledWith("package.json");
639
+ expect(mockReadFile).toHaveBeenCalledWith("package.json");
640
+ expect(mockWriteFile).not.toHaveBeenCalled();
641
+ });
642
+ });