@caleuche/cli 0.1.4 → 0.2.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,18 @@
1
1
  # @caleuche/cli
2
2
 
3
+ ## 0.2.1
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies [d0f1a56]
8
+ - @caleuche/core@0.2.0
9
+
10
+ ## 0.2.0
11
+
12
+ ### Minor Changes
13
+
14
+ - 15ef5f4: Adding batch compile command.
15
+
3
16
  ## 0.1.4
4
17
 
5
18
  ### Patch Changes
package/dist/batch.js ADDED
@@ -0,0 +1,98 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.batchCompile = batchCompile;
7
+ const fs_1 = __importDefault(require("fs"));
8
+ const utils_1 = require("./utils");
9
+ const common_1 = require("./common");
10
+ function loadVariantDefinition(variant) {
11
+ if ((0, utils_1.isVariantDefinition)(variant)) {
12
+ return variant;
13
+ }
14
+ else if ((0, utils_1.isVariantPath)(variant)) {
15
+ const v = (0, utils_1.parse)(variant);
16
+ if (!v) {
17
+ console.error(`Failed to parse variant at path: ${variant}`);
18
+ return null;
19
+ }
20
+ return v;
21
+ }
22
+ return null;
23
+ }
24
+ function loadVariantDefinitions(variants) {
25
+ if (!variants)
26
+ return {};
27
+ const definitions = {};
28
+ for (const [key, variant] of Object.entries(variants)) {
29
+ const v = loadVariantDefinition(variant);
30
+ if (!v) {
31
+ console.error(`Failed to load variant definition for key "${key}": ${variant}`);
32
+ return null;
33
+ }
34
+ definitions[key] = v;
35
+ }
36
+ return definitions;
37
+ }
38
+ function resolveVariantDefinition(variant, variantRegistry) {
39
+ if ((0, utils_1.isVariantPath)(variant)) {
40
+ const v = (0, utils_1.parse)(variant);
41
+ if (!v) {
42
+ console.error(`Failed to parse variant at path: ${variant}`);
43
+ return null;
44
+ }
45
+ return v;
46
+ }
47
+ else if ((0, utils_1.isVariantDefinition)(variant)) {
48
+ return variant;
49
+ }
50
+ else if ((0, utils_1.isVariantReference)(variant)) {
51
+ const v = variantRegistry[variant];
52
+ if (!v) {
53
+ console.error(`Variant reference "${variant}" not found in registry.`);
54
+ return null;
55
+ }
56
+ return v;
57
+ }
58
+ console.error(`Invalid variant type: ${JSON.stringify(variant)}`);
59
+ return null;
60
+ }
61
+ function batchCompile(batchFile) {
62
+ if (!fs_1.default.existsSync(batchFile)) {
63
+ console.error(`Batch file "${batchFile}" does not exist.`);
64
+ process.exit(1);
65
+ }
66
+ if (!fs_1.default.lstatSync(batchFile).isFile()) {
67
+ console.error(`"${batchFile}" is not a file.`);
68
+ process.exit(1);
69
+ }
70
+ const bachDefinition = (0, utils_1.parse)(batchFile);
71
+ if (!bachDefinition) {
72
+ console.error(`Failed to parse batch file: ${batchFile}`);
73
+ process.exit(1);
74
+ }
75
+ const variants = loadVariantDefinitions(bachDefinition.variants);
76
+ if (!variants) {
77
+ process.exit(1);
78
+ }
79
+ const samples = bachDefinition.samples;
80
+ for (const sampleDefinition of samples) {
81
+ const sample = (0, common_1.resolveAndParseSample)(sampleDefinition.templatePath);
82
+ if (!sample) {
83
+ process.exit(1);
84
+ }
85
+ for (const variant of sampleDefinition.variants) {
86
+ const resolvedVariant = resolveVariantDefinition(variant, variants);
87
+ if (!resolvedVariant) {
88
+ process.exit(1);
89
+ }
90
+ if (!(0, common_1.compileAndWriteOutput)(sample, resolvedVariant, variant.output, {
91
+ project: true,
92
+ })) {
93
+ console.error(`Sample: ${sampleDefinition.templatePath}, Variant: ${JSON.stringify(variant)}`);
94
+ process.exit(1);
95
+ }
96
+ }
97
+ }
98
+ }
package/dist/common.js ADDED
@@ -0,0 +1,62 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.resolveAndParseSample = resolveAndParseSample;
7
+ exports.compileAndWriteOutput = compileAndWriteOutput;
8
+ const core_1 = require("@caleuche/core");
9
+ const utils_1 = require("./utils");
10
+ const fs_1 = __importDefault(require("fs"));
11
+ const path_1 = __importDefault(require("path"));
12
+ function resolveAndParseSample(samplePath) {
13
+ const sampleFilePath = (0, utils_1.resolveSampleFile)(samplePath);
14
+ if (!fs_1.default.existsSync(sampleFilePath)) {
15
+ console.error(`Sample file not found: ${sampleFilePath}`);
16
+ return null;
17
+ }
18
+ const sample = (0, utils_1.parse)(sampleFilePath);
19
+ if (!sample) {
20
+ console.error(`Failed to parse sample file: ${sampleFilePath}`);
21
+ return null;
22
+ }
23
+ const resolvedTemplate = (0, utils_1.resolveTemplate)(samplePath, sample);
24
+ if (!resolvedTemplate) {
25
+ return null;
26
+ }
27
+ sample.template = resolvedTemplate;
28
+ return sample;
29
+ }
30
+ function compileAndWriteOutput(sample, input, outputPath, options) {
31
+ const output = (() => {
32
+ try {
33
+ return (0, core_1.compileSample)(sample, input, {
34
+ project: options.project || false,
35
+ });
36
+ }
37
+ catch (error) {
38
+ if (error instanceof Error) {
39
+ console.error(`Error during compilation: ${error.message}`);
40
+ }
41
+ else {
42
+ console.error("An unknown error occurred during compilation.");
43
+ }
44
+ return null;
45
+ }
46
+ })();
47
+ if (!output) {
48
+ return false;
49
+ }
50
+ try {
51
+ (0, utils_1.createOutputDirectory)(outputPath);
52
+ for (const { fileName, content } of output.items) {
53
+ const itemOutputPath = path_1.default.join(outputPath, fileName);
54
+ fs_1.default.writeFileSync(itemOutputPath, content);
55
+ }
56
+ }
57
+ catch {
58
+ console.error(`Failed to write output to ${outputPath}`);
59
+ return false;
60
+ }
61
+ return true;
62
+ }
package/dist/compile.js CHANGED
@@ -1,56 +1,19 @@
1
1
  "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
2
  Object.defineProperty(exports, "__esModule", { value: true });
6
3
  exports.compile = compile;
7
- const core_1 = require("@caleuche/core");
8
- const fs_1 = __importDefault(require("fs"));
9
- const path_1 = __importDefault(require("path"));
10
4
  const utils_1 = require("./utils");
5
+ const common_1 = require("./common");
11
6
  function compile(samplePath, dataPath, outputPath, options) {
12
- const sampleFilePath = (0, utils_1.resolveSampleFile)(samplePath);
13
- if (!fs_1.default.existsSync(sampleFilePath)) {
14
- console.error(`Sample file not found: ${sampleFilePath}`);
15
- process.exit(1);
16
- }
17
- const sample = (0, utils_1.parse)(sampleFilePath);
7
+ const sample = (0, common_1.resolveAndParseSample)(samplePath);
18
8
  if (!sample) {
19
- console.error(`Failed to parse sample file: ${sampleFilePath}`);
20
- process.exit(1);
9
+ return process.exit(1);
21
10
  }
22
- sample.template = (0, utils_1.resolveTemplate)(samplePath, sample);
23
11
  const inputData = (0, utils_1.parse)(dataPath);
24
12
  if (!inputData || !(0, utils_1.isObject)(inputData)) {
25
13
  console.error(`Failed to parse input data file: ${dataPath}`);
26
14
  process.exit(1);
27
15
  }
28
- const output = (() => {
29
- try {
30
- return (0, core_1.compileSample)(sample, inputData, {
31
- project: options.project || false,
32
- });
33
- }
34
- catch (error) {
35
- if (error instanceof Error) {
36
- console.error(`Error during compilation: ${error.message}`);
37
- }
38
- else {
39
- console.error("An unknown error occurred during compilation.");
40
- }
41
- return null;
42
- }
43
- })();
44
- if (!output) {
16
+ if (!(0, common_1.compileAndWriteOutput)(sample, inputData, outputPath, options)) {
45
17
  process.exit(1);
46
18
  }
47
- if (!(0, utils_1.isObject)(inputData)) {
48
- console.error("Input data must be an object.");
49
- process.exit(1);
50
- }
51
- (0, utils_1.createOutputDirectory)(outputPath);
52
- for (const { fileName, content } of output.items) {
53
- const itemOutputPath = path_1.default.join(outputPath, fileName);
54
- fs_1.default.writeFileSync(itemOutputPath, content);
55
- }
56
19
  }
package/dist/index.js CHANGED
@@ -12,4 +12,7 @@ commander_1.program
12
12
  .command("compile <sample-directory> <data-file> <output-directory>")
13
13
  .option("-p, --project", "Generate project file")
14
14
  .action(compile_1.compile);
15
+ commander_1.program.command("batch <batch-file>").action((batchFile) => {
16
+ console.log(`Batch compiling samples from ${batchFile}`);
17
+ });
15
18
  commander_1.program.parse();
@@ -0,0 +1 @@
1
+ "use strict";
package/dist/utils.js CHANGED
@@ -9,6 +9,9 @@ exports.isDirectory = isDirectory;
9
9
  exports.createOutputDirectory = createOutputDirectory;
10
10
  exports.resolveTemplate = resolveTemplate;
11
11
  exports.isObject = isObject;
12
+ exports.isVariantDefinition = isVariantDefinition;
13
+ exports.isVariantPath = isVariantPath;
14
+ exports.isVariantReference = isVariantReference;
12
15
  const yaml_1 = require("yaml");
13
16
  const fs_1 = __importDefault(require("fs"));
14
17
  const path_1 = __importDefault(require("path"));
@@ -48,9 +51,18 @@ function resolveTemplate(samplePath, sample) {
48
51
  }
49
52
  catch (error) {
50
53
  console.error("Error reading template file.");
51
- process.exit(1);
54
+ return null;
52
55
  }
53
56
  }
54
57
  function isObject(value) {
55
58
  return value !== null && typeof value === "object" && !Array.isArray(value);
56
59
  }
60
+ function isVariantDefinition(variant) {
61
+ return isObject(variant) && !Array.isArray(variant);
62
+ }
63
+ function isVariantPath(variant) {
64
+ return typeof variant === "string" && fs_1.default.existsSync(variant);
65
+ }
66
+ function isVariantReference(variant) {
67
+ return typeof variant === "string" && !fs_1.default.existsSync(variant);
68
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@caleuche/cli",
3
- "version": "0.1.4",
3
+ "version": "0.2.1",
4
4
  "main": "dist/index.js",
5
5
  "bin": {
6
6
  "che": "dist/index.js"
@@ -19,7 +19,7 @@
19
19
  "license": "MIT",
20
20
  "description": "Caleuche CLI",
21
21
  "dependencies": {
22
- "@caleuche/core": "*",
22
+ "@caleuche/core": "^0.2.0",
23
23
  "commander": "^14.0.0",
24
24
  "yaml": "^2.8.0"
25
25
  },
package/src/batch.ts ADDED
@@ -0,0 +1,112 @@
1
+ import fs from "fs";
2
+ import {
3
+ isVariantDefinition,
4
+ isVariantPath,
5
+ isVariantReference,
6
+ parse,
7
+ } from "./utils";
8
+ import { compileAndWriteOutput, resolveAndParseSample } from "./common";
9
+
10
+ function loadVariantDefinition(
11
+ variant: SampleVariantDefinition | SampleVariantPath,
12
+ ): SampleVariantDefinition | null {
13
+ if (isVariantDefinition(variant)) {
14
+ return variant;
15
+ } else if (isVariantPath(variant)) {
16
+ const v = parse<SampleVariantDefinition>(variant);
17
+ if (!v) {
18
+ console.error(`Failed to parse variant at path: ${variant}`);
19
+ return null;
20
+ }
21
+ return v;
22
+ }
23
+ return null;
24
+ }
25
+
26
+ function loadVariantDefinitions(
27
+ variants?: Record<string, SampleVariantDefinition | SampleVariantPath>,
28
+ ): Record<string, SampleVariantDefinition> | null {
29
+ if (!variants) return {};
30
+ const definitions: Record<string, SampleVariantDefinition> = {};
31
+ for (const [key, variant] of Object.entries(variants)) {
32
+ const v = loadVariantDefinition(variant);
33
+ if (!v) {
34
+ console.error(
35
+ `Failed to load variant definition for key "${key}": ${variant}`,
36
+ );
37
+ return null;
38
+ }
39
+ definitions[key] = v;
40
+ }
41
+ return definitions;
42
+ }
43
+
44
+ function resolveVariantDefinition(
45
+ variant: SampleVariant,
46
+ variantRegistry: Record<string, SampleVariantDefinition>,
47
+ ): SampleVariantDefinition | null {
48
+ if (isVariantPath(variant)) {
49
+ const v = parse<SampleVariantDefinition>(variant);
50
+ if (!v) {
51
+ console.error(`Failed to parse variant at path: ${variant}`);
52
+ return null;
53
+ }
54
+ return v;
55
+ } else if (isVariantDefinition(variant)) {
56
+ return variant;
57
+ } else if (isVariantReference(variant)) {
58
+ const v = variantRegistry[variant];
59
+ if (!v) {
60
+ console.error(`Variant reference "${variant}" not found in registry.`);
61
+ return null;
62
+ }
63
+ return v;
64
+ }
65
+ console.error(`Invalid variant type: ${JSON.stringify(variant)}`);
66
+ return null;
67
+ }
68
+
69
+ export function batchCompile(batchFile: string) {
70
+ if (!fs.existsSync(batchFile)) {
71
+ console.error(`Batch file "${batchFile}" does not exist.`);
72
+ process.exit(1);
73
+ }
74
+ if (!fs.lstatSync(batchFile).isFile()) {
75
+ console.error(`"${batchFile}" is not a file.`);
76
+ process.exit(1);
77
+ }
78
+ const bachDefinition = parse<BatchCompileOptions>(batchFile);
79
+ if (!bachDefinition) {
80
+ console.error(`Failed to parse batch file: ${batchFile}`);
81
+ process.exit(1);
82
+ }
83
+ const variants = loadVariantDefinitions(bachDefinition.variants);
84
+ if (!variants) {
85
+ process.exit(1);
86
+ }
87
+ const samples = bachDefinition.samples;
88
+ for (const sampleDefinition of samples) {
89
+ const sample = resolveAndParseSample(sampleDefinition.templatePath);
90
+ if (!sample) {
91
+ process.exit(1);
92
+ }
93
+
94
+ for (const variant of sampleDefinition.variants) {
95
+ const resolvedVariant = resolveVariantDefinition(variant, variants);
96
+ if (!resolvedVariant) {
97
+ process.exit(1);
98
+ }
99
+
100
+ if (
101
+ !compileAndWriteOutput(sample, resolvedVariant, variant.output, {
102
+ project: true,
103
+ })
104
+ ) {
105
+ console.error(
106
+ `Sample: ${sampleDefinition.templatePath}, Variant: ${JSON.stringify(variant)}`,
107
+ );
108
+ process.exit(1);
109
+ }
110
+ }
111
+ }
112
+ }
package/src/common.ts ADDED
@@ -0,0 +1,67 @@
1
+ import { compileSample, Sample } from "@caleuche/core";
2
+ import {
3
+ createOutputDirectory,
4
+ parse,
5
+ resolveSampleFile,
6
+ resolveTemplate,
7
+ } from "./utils";
8
+ import fs from "fs";
9
+ import path from "path";
10
+
11
+ export function resolveAndParseSample(samplePath: string): Sample | null {
12
+ const sampleFilePath = resolveSampleFile(samplePath);
13
+ if (!fs.existsSync(sampleFilePath)) {
14
+ console.error(`Sample file not found: ${sampleFilePath}`);
15
+ return null;
16
+ }
17
+ const sample = parse<Sample>(sampleFilePath);
18
+ if (!sample) {
19
+ console.error(`Failed to parse sample file: ${sampleFilePath}`);
20
+ return null;
21
+ }
22
+ const resolvedTemplate = resolveTemplate(samplePath, sample);
23
+ if (!resolvedTemplate) {
24
+ return null;
25
+ }
26
+ sample.template = resolvedTemplate;
27
+ return sample;
28
+ }
29
+
30
+ export function compileAndWriteOutput(
31
+ sample: Sample,
32
+ input: object,
33
+ outputPath: string,
34
+ options: { project?: boolean },
35
+ ) {
36
+ const output = (() => {
37
+ try {
38
+ return compileSample(sample, input, {
39
+ project: options.project || false,
40
+ });
41
+ } catch (error) {
42
+ if (error instanceof Error) {
43
+ console.error(`Error during compilation: ${error.message}`);
44
+ } else {
45
+ console.error("An unknown error occurred during compilation.");
46
+ }
47
+ return null;
48
+ }
49
+ })();
50
+
51
+ if (!output) {
52
+ return false;
53
+ }
54
+
55
+ try {
56
+ createOutputDirectory(outputPath);
57
+
58
+ for (const { fileName, content } of output.items) {
59
+ const itemOutputPath = path.join(outputPath, fileName);
60
+ fs.writeFileSync(itemOutputPath, content);
61
+ }
62
+ } catch {
63
+ console.error(`Failed to write output to ${outputPath}`);
64
+ return false;
65
+ }
66
+ return true;
67
+ }
package/src/compile.ts CHANGED
@@ -1,13 +1,5 @@
1
- import { compileSample, Sample } from "@caleuche/core";
2
- import fs from "fs";
3
- import path from "path";
4
- import {
5
- createOutputDirectory,
6
- isObject,
7
- parse,
8
- resolveSampleFile,
9
- resolveTemplate,
10
- } from "./utils";
1
+ import { isObject, parse } from "./utils";
2
+ import { compileAndWriteOutput, resolveAndParseSample } from "./common";
11
3
 
12
4
  export function compile(
13
5
  samplePath: string,
@@ -15,18 +7,10 @@ export function compile(
15
7
  outputPath: string,
16
8
  options: { project?: boolean },
17
9
  ) {
18
- const sampleFilePath = resolveSampleFile(samplePath);
19
- if (!fs.existsSync(sampleFilePath)) {
20
- console.error(`Sample file not found: ${sampleFilePath}`);
21
- process.exit(1);
22
- }
23
-
24
- const sample = parse<Sample>(sampleFilePath);
10
+ const sample = resolveAndParseSample(samplePath);
25
11
  if (!sample) {
26
- console.error(`Failed to parse sample file: ${sampleFilePath}`);
27
- process.exit(1);
12
+ return process.exit(1);
28
13
  }
29
- sample.template = resolveTemplate(samplePath, sample);
30
14
 
31
15
  const inputData = parse<Record<string, any>>(dataPath);
32
16
  if (!inputData || !isObject(inputData)) {
@@ -34,34 +18,7 @@ export function compile(
34
18
  process.exit(1);
35
19
  }
36
20
 
37
- const output = (() => {
38
- try {
39
- return compileSample(sample, inputData, {
40
- project: options.project || false,
41
- });
42
- } catch (error) {
43
- if (error instanceof Error) {
44
- console.error(`Error during compilation: ${error.message}`);
45
- } else {
46
- console.error("An unknown error occurred during compilation.");
47
- }
48
- return null;
49
- }
50
- })();
51
-
52
- if (!output) {
21
+ if (!compileAndWriteOutput(sample, inputData, outputPath, options)) {
53
22
  process.exit(1);
54
23
  }
55
-
56
- if (!isObject(inputData)) {
57
- console.error("Input data must be an object.");
58
- process.exit(1);
59
- }
60
-
61
- createOutputDirectory(outputPath);
62
-
63
- for (const { fileName, content } of output.items) {
64
- const itemOutputPath = path.join(outputPath, fileName);
65
- fs.writeFileSync(itemOutputPath, content);
66
- }
67
24
  }
package/src/index.ts CHANGED
@@ -14,4 +14,8 @@ program
14
14
  .option("-p, --project", "Generate project file")
15
15
  .action(compile);
16
16
 
17
+ program.command("batch <batch-file>").action((batchFile) => {
18
+ console.log(`Batch compiling samples from ${batchFile}`);
19
+ });
20
+
17
21
  program.parse();
@@ -0,0 +1,22 @@
1
+ type SampleVariantDefinition = Record<string, any>;
2
+ type SampleVariantReference = string;
3
+ type SampleVariantPath = string;
4
+ type SampleVariant =
5
+ | SampleVariantDefinition
6
+ | SampleVariantReference
7
+ | SampleVariantPath;
8
+
9
+ interface SampleVariantConfig {
10
+ output: string;
11
+ data: SampleVariant;
12
+ }
13
+
14
+ interface SampleDefinition {
15
+ templatePath: string;
16
+ variants: SampleVariantConfig[];
17
+ }
18
+
19
+ interface BatchCompileOptions {
20
+ variants?: Record<string, SampleVariantDefinition | SampleVariantPath>;
21
+ samples: SampleDefinition[];
22
+ }
package/src/utils.ts CHANGED
@@ -32,7 +32,10 @@ export function createOutputDirectory(outputPath: string) {
32
32
  }
33
33
  }
34
34
 
35
- export function resolveTemplate(samplePath: string, sample: Sample): string {
35
+ export function resolveTemplate(
36
+ samplePath: string,
37
+ sample: Sample,
38
+ ): string | null {
36
39
  try {
37
40
  const templatePath = path.join(samplePath, sample.template);
38
41
  if (!fs.existsSync(templatePath)) {
@@ -41,10 +44,28 @@ export function resolveTemplate(samplePath: string, sample: Sample): string {
41
44
  return fs.readFileSync(templatePath, "utf-8");
42
45
  } catch (error) {
43
46
  console.error("Error reading template file.");
44
- process.exit(1);
47
+ return null;
45
48
  }
46
49
  }
47
50
 
48
51
  export function isObject(value: any): value is Record<string, any> {
49
52
  return value !== null && typeof value === "object" && !Array.isArray(value);
50
53
  }
54
+
55
+ export function isVariantDefinition(
56
+ variant: SampleVariant,
57
+ ): variant is SampleVariantDefinition {
58
+ return isObject(variant) && !Array.isArray(variant);
59
+ }
60
+
61
+ export function isVariantPath(
62
+ variant: SampleVariant,
63
+ ): variant is SampleVariantPath {
64
+ return typeof variant === "string" && fs.existsSync(variant);
65
+ }
66
+
67
+ export function isVariantReference(
68
+ variant: SampleVariant,
69
+ ): variant is SampleVariantReference {
70
+ return typeof variant === "string" && !fs.existsSync(variant);
71
+ }
@@ -0,0 +1,285 @@
1
+ import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
2
+ import path from "path";
3
+
4
+ vi.mock("fs");
5
+ import fs from "fs";
6
+ const mockFs = vi.mocked(fs);
7
+
8
+ vi.mock("@caleuche/core");
9
+ import { compileSample, Sample } from "@caleuche/core";
10
+ const mockCompileSample = vi.mocked(compileSample);
11
+
12
+ vi.mock("../src/utils");
13
+ import {
14
+ parse,
15
+ resolveSampleFile,
16
+ createOutputDirectory,
17
+ resolveTemplate,
18
+ isVariantDefinition,
19
+ isVariantPath,
20
+ isVariantReference,
21
+ } from "../src/utils";
22
+ const mockParse = vi.mocked(parse);
23
+ const mockResolveSampleFile = vi.mocked(resolveSampleFile);
24
+ const mockCreateOutputDirectory = vi.mocked(createOutputDirectory);
25
+ const mockResolveTemplate = vi.mocked(resolveTemplate);
26
+ const mockIsVariantDefinition = vi.mocked(isVariantDefinition);
27
+ const mockIsVariantPath = vi.mocked(isVariantPath);
28
+ const mockIsVariantReference = vi.mocked(isVariantReference);
29
+
30
+ import { batchCompile } from "../src/batch";
31
+
32
+ describe("batchCompile", () => {
33
+ let mockExit: any;
34
+ let mockConsoleError: any;
35
+
36
+ beforeEach(() => {
37
+ vi.clearAllMocks();
38
+ mockExit = vi.spyOn(process, "exit").mockImplementation(() => {
39
+ throw new Error("process.exit");
40
+ });
41
+ mockConsoleError = vi.spyOn(console, "error").mockImplementation(() => {});
42
+ });
43
+
44
+ afterEach(() => {
45
+ vi.restoreAllMocks();
46
+ });
47
+
48
+ it("should exit if batch file does not exist", () => {
49
+ mockFs.existsSync.mockReturnValue(false);
50
+ expect(() => {
51
+ batchCompile("batch.yaml");
52
+ }).toThrow("process.exit");
53
+ expect(mockConsoleError).toHaveBeenCalledWith(
54
+ 'Batch file "batch.yaml" does not exist.',
55
+ );
56
+ expect(mockExit).toHaveBeenCalledWith(1);
57
+ });
58
+
59
+ it("should exit if batch file is not a file", () => {
60
+ mockFs.existsSync.mockReturnValue(true);
61
+ mockFs.lstatSync.mockReturnValue({ isFile: () => false } as any);
62
+ expect(() => {
63
+ batchCompile("batch.yaml");
64
+ }).toThrow("process.exit");
65
+ expect(mockConsoleError).toHaveBeenCalledWith(
66
+ '"batch.yaml" is not a file.',
67
+ );
68
+ expect(mockExit).toHaveBeenCalledWith(1);
69
+ });
70
+
71
+ it("should exit if batch file cannot be parsed", () => {
72
+ mockFs.existsSync.mockReturnValue(true);
73
+ mockFs.lstatSync.mockReturnValue({ isFile: () => true } as any);
74
+ mockParse.mockReturnValueOnce(null);
75
+ expect(() => {
76
+ batchCompile("batch.yaml");
77
+ }).toThrow("process.exit");
78
+ expect(mockConsoleError).toHaveBeenCalledWith(
79
+ "Failed to parse batch file: batch.yaml",
80
+ );
81
+ expect(mockExit).toHaveBeenCalledWith(1);
82
+ });
83
+
84
+ it("should exit if variant definitions cannot be loaded", () => {
85
+ mockFs.existsSync.mockReturnValue(true);
86
+ mockFs.lstatSync.mockReturnValue({ isFile: () => true } as any);
87
+ mockParse
88
+ .mockImplementationOnce(() => ({
89
+ variants: { foo: "badvariant.yaml" },
90
+ samples: [],
91
+ }))
92
+ .mockImplementationOnce(() => null);
93
+ expect(() => {
94
+ batchCompile("batch.yaml");
95
+ }).toThrow("process.exit");
96
+ expect(mockConsoleError).toHaveBeenCalledWith(
97
+ `Failed to load variant definition for key "foo": badvariant.yaml`,
98
+ );
99
+ expect(mockExit).toHaveBeenCalledWith(1);
100
+ });
101
+
102
+ it("should exit if sample file not found", () => {
103
+ mockFs.existsSync.mockReturnValueOnce(true);
104
+ mockFs.lstatSync.mockReturnValue({ isFile: () => true } as any);
105
+ mockParse.mockReturnValueOnce({
106
+ variants: {},
107
+ samples: [{ templatePath: "sample.yaml", variants: [], output: "out" }],
108
+ });
109
+ mockResolveSampleFile.mockReturnValue("sample.yaml");
110
+ mockFs.existsSync.mockReturnValueOnce(false);
111
+ expect(() => {
112
+ batchCompile("batch.yaml");
113
+ }).toThrow("process.exit");
114
+ expect(mockConsoleError).toHaveBeenCalledWith(
115
+ "Sample file not found: sample.yaml",
116
+ );
117
+ expect(mockExit).toHaveBeenCalledWith(1);
118
+ });
119
+
120
+ it("should exit if sample file cannot be parsed", () => {
121
+ mockFs.existsSync.mockReturnValue(true);
122
+ mockFs.lstatSync.mockReturnValue({ isFile: () => true } as any);
123
+ mockParse.mockReturnValueOnce({
124
+ variants: {},
125
+ samples: [
126
+ { templatePath: "sample.yaml", variants: ["v1"], output: "out" },
127
+ ],
128
+ });
129
+ mockResolveSampleFile.mockReturnValue("/path/to/sample.yaml");
130
+ mockParse.mockReturnValueOnce(null);
131
+ expect(() => {
132
+ batchCompile("batch.yaml");
133
+ }).toThrow("process.exit");
134
+ expect(mockConsoleError).toHaveBeenCalledWith(
135
+ "Failed to parse sample file: /path/to/sample.yaml",
136
+ );
137
+ expect(mockExit).toHaveBeenCalledWith(1);
138
+ });
139
+
140
+ it("should exit if variant cannot be resolved", () => {
141
+ mockFs.existsSync.mockReturnValue(true);
142
+ mockFs.lstatSync.mockReturnValue({ isFile: () => true } as any);
143
+ mockParse.mockReturnValueOnce({
144
+ variants: {},
145
+ samples: [
146
+ { templatePath: "sample.yaml", variants: ["v1"], output: "out" },
147
+ ],
148
+ });
149
+ mockResolveSampleFile.mockReturnValue("/path/to/sample.yaml");
150
+ mockParse.mockReturnValueOnce({
151
+ template: "t",
152
+ type: "js",
153
+ dependencies: [],
154
+ input: [],
155
+ });
156
+ mockResolveTemplate.mockReturnValue("resolved template");
157
+ // variant resolution fails
158
+ mockIsVariantPath.mockReturnValue(false);
159
+ mockIsVariantDefinition.mockReturnValue(false);
160
+ mockIsVariantReference.mockReturnValue(false);
161
+ expect(() => {
162
+ batchCompile("batch.yaml");
163
+ }).toThrow("process.exit");
164
+ expect(mockConsoleError).toHaveBeenCalledWith('Invalid variant type: "v1"');
165
+ expect(mockExit).toHaveBeenCalledWith(1);
166
+ });
167
+
168
+ it("should exit if compilation throws error", () => {
169
+ mockFs.existsSync.mockReturnValue(true);
170
+ mockFs.lstatSync.mockReturnValue({ isFile: () => true } as any);
171
+ mockParse.mockReturnValueOnce({
172
+ variants: {},
173
+ samples: [
174
+ { templatePath: "sample.yaml", variants: ["v1"], output: "out" },
175
+ ],
176
+ });
177
+ mockResolveSampleFile.mockReturnValue("/path/to/sample.yaml");
178
+ mockParse.mockReturnValueOnce({
179
+ template: "t",
180
+ type: "js",
181
+ dependencies: [],
182
+ input: [],
183
+ });
184
+ mockResolveTemplate.mockReturnValue("resolved template");
185
+ mockIsVariantPath.mockReturnValue(false);
186
+ mockIsVariantDefinition.mockReturnValue(true);
187
+ mockCompileSample.mockImplementation(() => {
188
+ throw new Error("Compilation error");
189
+ });
190
+ expect(() => {
191
+ batchCompile("batch.yaml");
192
+ }).toThrow("process.exit");
193
+ expect(mockConsoleError).toHaveBeenNthCalledWith(
194
+ 1,
195
+ "Error during compilation: Compilation error",
196
+ );
197
+ expect(mockConsoleError).toHaveBeenNthCalledWith(
198
+ 2,
199
+ 'Sample: sample.yaml, Variant: "v1"',
200
+ );
201
+ expect(mockExit).toHaveBeenCalledWith(1);
202
+ });
203
+
204
+ it("should exit if compilation throws unknown error", () => {
205
+ mockFs.existsSync.mockReturnValue(true);
206
+ mockFs.lstatSync.mockReturnValue({ isFile: () => true } as any);
207
+ mockParse.mockReturnValueOnce({
208
+ variants: {},
209
+ samples: [
210
+ { templatePath: "sample.yaml", variants: ["v1"], output: "out" },
211
+ ],
212
+ });
213
+ mockResolveSampleFile.mockReturnValue("/path/to/sample.yaml");
214
+ mockParse.mockReturnValueOnce({
215
+ template: "t",
216
+ type: "js",
217
+ dependencies: [],
218
+ input: [],
219
+ });
220
+ mockResolveTemplate.mockReturnValue("resolved template");
221
+ // variant resolution
222
+ mockIsVariantPath.mockReturnValue(false);
223
+ mockIsVariantDefinition.mockReturnValue(true);
224
+ // compileSample throws unknown error
225
+ mockCompileSample.mockImplementation(() => {
226
+ throw "Unknown error";
227
+ });
228
+ expect(() => {
229
+ batchCompile("batch.yaml");
230
+ }).toThrow("process.exit");
231
+ expect(mockConsoleError).toHaveBeenCalledWith(
232
+ "An unknown error occurred during compilation.",
233
+ );
234
+ expect(mockExit).toHaveBeenCalledWith(1);
235
+ });
236
+
237
+ it("should compile and write output files for each variant", () => {
238
+ mockFs.existsSync.mockReturnValue(true);
239
+ mockFs.lstatSync.mockReturnValue({ isFile: () => true } as any);
240
+ mockParse.mockReturnValueOnce({
241
+ variants: {},
242
+ samples: [
243
+ {
244
+ templatePath: "sample.yaml",
245
+ variants: [
246
+ { output: "v1.output", foo: "bar" },
247
+ { output: "v2.output", foo: "baz" },
248
+ ],
249
+ output: "out",
250
+ },
251
+ ],
252
+ });
253
+ mockResolveSampleFile.mockReturnValue("/path/to/sample.yaml");
254
+ mockParse.mockReturnValueOnce({
255
+ template: "t",
256
+ type: "js",
257
+ dependencies: [],
258
+ input: [],
259
+ });
260
+ mockResolveTemplate.mockReturnValue("resolved template");
261
+ // variant resolution
262
+ mockIsVariantPath.mockReturnValue(false);
263
+ mockIsVariantDefinition.mockReturnValue(true);
264
+ // compileSample returns output
265
+ mockCompileSample.mockReturnValue({
266
+ items: [
267
+ { fileName: "file1.js", content: "console.log('1');" },
268
+ { fileName: "file2.js", content: "console.log('2');" },
269
+ ],
270
+ });
271
+ mockCreateOutputDirectory.mockImplementation(() => {});
272
+ mockFs.writeFileSync.mockImplementation(() => {});
273
+ batchCompile("batch.yaml");
274
+ expect(mockCreateOutputDirectory).toHaveBeenCalledWith("v1.output");
275
+ expect(mockCreateOutputDirectory).toHaveBeenCalledWith("v2.output");
276
+ expect(mockFs.writeFileSync).toHaveBeenCalledWith(
277
+ path.join("v1.output", "file1.js"),
278
+ "console.log('1');",
279
+ );
280
+ expect(mockFs.writeFileSync).toHaveBeenCalledWith(
281
+ path.join("v1.output", "file2.js"),
282
+ "console.log('2');",
283
+ );
284
+ });
285
+ });
@@ -0,0 +1,69 @@
1
+ import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
2
+ import path from "path";
3
+
4
+ vi.mock("fs");
5
+ import fs from "fs";
6
+ const mockFs = vi.mocked(fs);
7
+
8
+ vi.mock("@caleuche/core");
9
+ import { compileSample, Sample } from "@caleuche/core";
10
+ const mockCompileSample = vi.mocked(compileSample);
11
+
12
+ vi.mock("../src/utils");
13
+ import {
14
+ parse,
15
+ resolveSampleFile,
16
+ createOutputDirectory,
17
+ resolveTemplate,
18
+ isVariantDefinition,
19
+ isVariantPath,
20
+ isVariantReference,
21
+ } from "../src/utils";
22
+ import { resolveAndParseSample } from "../src/common";
23
+ const mockParse = vi.mocked(parse);
24
+ const mockResolveSampleFile = vi.mocked(resolveSampleFile);
25
+ const mockCreateOutputDirectory = vi.mocked(createOutputDirectory);
26
+ const mockResolveTemplate = vi.mocked(resolveTemplate);
27
+ const mockIsVariantDefinition = vi.mocked(isVariantDefinition);
28
+ const mockIsVariantPath = vi.mocked(isVariantPath);
29
+ const mockIsVariantReference = vi.mocked(isVariantReference);
30
+
31
+ describe("common", () => {
32
+ describe("resolveAndParseSample", () => {
33
+ let mockConsoleError: any;
34
+
35
+ beforeEach(() => {
36
+ vi.clearAllMocks();
37
+ mockConsoleError = vi
38
+ .spyOn(console, "error")
39
+ .mockImplementation(() => {});
40
+ });
41
+
42
+ afterEach(() => {
43
+ vi.restoreAllMocks();
44
+ });
45
+
46
+ it("should return null and log an error when sample file does not exist", () => {
47
+ mockResolveSampleFile.mockReturnValue("/path/to/sample.yaml");
48
+ mockFs.existsSync.mockReturnValue(false);
49
+
50
+ const sample = resolveAndParseSample("sample");
51
+ expect(sample).toBeNull();
52
+ expect(mockConsoleError).toHaveBeenCalledWith(
53
+ "Sample file not found: /path/to/sample.yaml",
54
+ );
55
+ });
56
+
57
+ it("should return null and log an error when sample file cannot be parsed", () => {
58
+ mockResolveSampleFile.mockReturnValue("/path/to/sample.yaml");
59
+ mockFs.existsSync.mockReturnValue(true);
60
+ mockParse.mockReturnValueOnce(null);
61
+
62
+ const sample = resolveAndParseSample("sample");
63
+ expect(sample).toBeNull();
64
+ expect(mockConsoleError).toHaveBeenCalledWith(
65
+ "Failed to parse sample file: /path/to/sample.yaml",
66
+ );
67
+ });
68
+ });
69
+ });
@@ -42,35 +42,6 @@ describe("compile", () => {
42
42
  });
43
43
 
44
44
  describe("error handling", () => {
45
- it("should exit when sample file does not exist", () => {
46
- mockResolveSampleFile.mockReturnValue("/path/to/sample.yaml");
47
- mockFs.existsSync.mockReturnValue(false);
48
-
49
- expect(() => {
50
- compile("sample", "data.json", "output", {});
51
- }).toThrow("process.exit");
52
-
53
- expect(mockConsoleError).toHaveBeenCalledWith(
54
- "Sample file not found: /path/to/sample.yaml",
55
- );
56
- expect(mockExit).toHaveBeenCalledWith(1);
57
- });
58
-
59
- it("should exit when sample file cannot be parsed", () => {
60
- mockResolveSampleFile.mockReturnValue("/path/to/sample.yaml");
61
- mockFs.existsSync.mockReturnValue(true);
62
- mockParse.mockReturnValueOnce(null);
63
-
64
- expect(() => {
65
- compile("sample", "data.json", "output", {});
66
- }).toThrow("process.exit");
67
-
68
- expect(mockConsoleError).toHaveBeenCalledWith(
69
- "Failed to parse sample file: /path/to/sample.yaml",
70
- );
71
- expect(mockExit).toHaveBeenCalledWith(1);
72
- });
73
-
74
45
  it("should exit when data file cannot be parsed", () => {
75
46
  const mockSample: Sample = {
76
47
  template: "test template",