@caleuche/cli 0.1.3 → 0.2.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # @caleuche/cli
2
2
 
3
+ ## 0.2.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 15ef5f4: Adding batch compile command.
8
+
9
+ ## 0.1.4
10
+
11
+ ### Patch Changes
12
+
13
+ - a471981: CLI now properly handles errors.
14
+
3
15
  ## 0.1.3
4
16
 
5
17
  ### Patch Changes
package/README.md CHANGED
@@ -1,103 +1,102 @@
1
- # Caleuche CLI
2
-
3
- Caleuche CLI is a command-line tool for compiling code samples and generating project files from templates. It supports multiple languages and flexible sample definitions, including inline templates and external template files.
4
-
5
- ## Installation
6
-
7
- ```sh
8
- npm install @caleuche/cli
9
- ```
10
-
11
- ## Usage
12
-
13
- ```
14
- che compile <sample-directory|sample-file> <data-file> <output-directory> [options]
15
- ```
16
-
17
- - `<sample-directory|sample-file>`: Path to a directory containing a `sample.yaml` or a direct path to a sample YAML file.
18
- - `<data-file>`: Path to the data file (JSON or YAML).
19
- - `<output-directory>`: Directory where the generated project will be placed.
20
-
21
- ### Options
22
-
23
- - `-p, --project` Generate project file (e.g., csproj, go.mod, etc.)
24
-
25
- ## Examples
26
-
27
- ### 1. Sample with Inline Template
28
-
29
- **sample.yaml**
30
-
31
- ```yaml
32
- template: |
33
- Hello, <%= name %>!
34
- type: python
35
- dependencies: []
36
- input:
37
- - name: name
38
- type: string
39
- required: true
40
- ```
41
-
42
- **data.yaml**
43
-
44
- ```yaml
45
- name: World
46
- ```
47
-
48
- **Command:**
49
-
50
- ```sh
51
- che compile ./my-sample ./data.yaml ./output
52
- ```
53
-
54
- ### 2. Sample with Template File Reference
55
-
56
- **Directory structure:**
57
-
58
- ```
59
- my-sample/
60
- sample.yaml
61
- main.py.tmpl
62
- ```
63
-
64
- **sample.yaml**
65
-
66
- ```yaml
67
- template: main.py.tmpl
68
- type: python
69
- dependencies: []
70
- input:
71
- - name: name
72
- type: string
73
- required: true
74
- ```
75
-
76
- **main.py.tmpl**
77
-
78
- ```python
79
- print("Hello, <%= name %>!")
80
- ```
81
-
82
- **data.yaml**
83
-
84
- ```yaml
85
- name: Alice
86
- ```
87
-
88
- **Command:**
89
-
90
- ```sh
91
- che compile ./my-sample ./data.yaml ./output
92
- ```
93
-
94
- ## Sample and Data File Structure
95
-
96
- - **Sample file**: YAML describing the sample, including the template (inline or file reference), language, dependencies, and input fields.
97
-
98
- - **Data file**: JSON or YAML with the data to inject into the sample.
99
-
100
-
101
- ## License
102
-
103
- MIT
1
+ # Caleuche CLI
2
+
3
+ Caleuche CLI is a command-line tool for compiling code samples and generating project files from templates. It supports multiple languages and flexible sample definitions, including inline templates and external template files.
4
+
5
+ ## Installation
6
+
7
+ ```sh
8
+ npm install @caleuche/cli
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ```
14
+ che compile <sample-directory|sample-file> <data-file> <output-directory> [options]
15
+ ```
16
+
17
+ - `<sample-directory|sample-file>`: Path to a directory containing a `sample.yaml` or a direct path to a sample YAML file.
18
+ - `<data-file>`: Path to the data file (JSON or YAML).
19
+ - `<output-directory>`: Directory where the generated project will be placed.
20
+
21
+ ### Options
22
+
23
+ - `-p, --project` Generate project file (e.g., csproj, go.mod, etc.)
24
+
25
+ ## Examples
26
+
27
+ ### 1. Sample with Inline Template
28
+
29
+ **sample.yaml**
30
+
31
+ ```yaml
32
+ template: |
33
+ Hello, <%= name %>!
34
+ type: python
35
+ dependencies: []
36
+ input:
37
+ - name: name
38
+ type: string
39
+ required: true
40
+ ```
41
+
42
+ **data.yaml**
43
+
44
+ ```yaml
45
+ name: World
46
+ ```
47
+
48
+ **Command:**
49
+
50
+ ```sh
51
+ che compile ./my-sample ./data.yaml ./output
52
+ ```
53
+
54
+ ### 2. Sample with Template File Reference
55
+
56
+ **Directory structure:**
57
+
58
+ ```
59
+ my-sample/
60
+ sample.yaml
61
+ main.py.tmpl
62
+ ```
63
+
64
+ **sample.yaml**
65
+
66
+ ```yaml
67
+ template: main.py.tmpl
68
+ type: python
69
+ dependencies: []
70
+ input:
71
+ - name: name
72
+ type: string
73
+ required: true
74
+ ```
75
+
76
+ **main.py.tmpl**
77
+
78
+ ```python
79
+ print("Hello, <%= name %>!")
80
+ ```
81
+
82
+ **data.yaml**
83
+
84
+ ```yaml
85
+ name: Alice
86
+ ```
87
+
88
+ **Command:**
89
+
90
+ ```sh
91
+ che compile ./my-sample ./data.yaml ./output
92
+ ```
93
+
94
+ ## Sample and Data File Structure
95
+
96
+ - **Sample file**: YAML describing the sample, including the template (inline or file reference), language, dependencies, and input fields.
97
+
98
+ - **Data file**: JSON or YAML with the data to inject into the sample.
99
+
100
+ ## License
101
+
102
+ MIT
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
+ }
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.compile = compile;
4
+ const utils_1 = require("./utils");
5
+ const common_1 = require("./common");
6
+ function compile(samplePath, dataPath, outputPath, options) {
7
+ const sample = (0, common_1.resolveAndParseSample)(samplePath);
8
+ if (!sample) {
9
+ return process.exit(1);
10
+ }
11
+ const inputData = (0, utils_1.parse)(dataPath);
12
+ if (!inputData || !(0, utils_1.isObject)(inputData)) {
13
+ console.error(`Failed to parse input data file: ${dataPath}`);
14
+ process.exit(1);
15
+ }
16
+ if (!(0, common_1.compileAndWriteOutput)(sample, inputData, outputPath, options)) {
17
+ process.exit(1);
18
+ }
19
+ }
package/dist/index.js CHANGED
@@ -1,50 +1,18 @@
1
1
  #!/usr/bin/env node
2
2
  "use strict";
3
- var __importDefault = (this && this.__importDefault) || function (mod) {
4
- return (mod && mod.__esModule) ? mod : { "default": mod };
5
- };
6
3
  Object.defineProperty(exports, "__esModule", { value: true });
7
4
  const commander_1 = require("commander");
8
- const core_1 = require("@caleuche/core");
9
- const fs_1 = __importDefault(require("fs"));
10
- const path_1 = __importDefault(require("path"));
11
- const utils_1 = require("./utils");
5
+ const compile_1 = require("./compile");
6
+ const package_json_1 = require("../package.json");
12
7
  commander_1.program
13
8
  .name("@caleuche/cli")
14
9
  .description("Caleuche CLI for compiling samples")
15
- .version("1.0.0");
16
- function compile(samplePath, dataPath, outputPath, options) {
17
- const sampleFilePath = (0, utils_1.resolveSampleFile)(samplePath);
18
- if (!fs_1.default.existsSync(sampleFilePath)) {
19
- console.error(`Sample file not found: ${sampleFilePath}`);
20
- process.exit(1);
21
- }
22
- const sample = (0, utils_1.parse)(sampleFilePath);
23
- if (!sample) {
24
- console.error(`Failed to parse sample file: ${sampleFilePath}`);
25
- process.exit(1);
26
- }
27
- sample.template = (0, utils_1.resolveTemplate)(samplePath, sample);
28
- const inputData = (0, utils_1.parse)(dataPath);
29
- if (!inputData || !(0, utils_1.isObject)(inputData)) {
30
- console.error(`Failed to parse input data file: ${dataPath}`);
31
- process.exit(1);
32
- }
33
- const output = (0, core_1.compileSample)(sample, inputData, {
34
- project: options.project || false,
35
- });
36
- if (!(0, utils_1.isObject)(inputData)) {
37
- console.error("Input data must be an object.");
38
- process.exit(1);
39
- }
40
- (0, utils_1.createOutputDirectory)(outputPath);
41
- for (const { fileName, content } of output.items) {
42
- const itemOutputPath = path_1.default.join(outputPath, fileName);
43
- fs_1.default.writeFileSync(itemOutputPath, content);
44
- }
45
- }
10
+ .version(package_json_1.version);
46
11
  commander_1.program
47
12
  .command("compile <sample-directory> <data-file> <output-directory>")
48
13
  .option("-p, --project", "Generate project file")
49
- .action(compile);
14
+ .action(compile_1.compile);
15
+ commander_1.program.command("batch <batch-file>").action((batchFile) => {
16
+ console.log(`Batch compiling samples from ${batchFile}`);
17
+ });
50
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.3",
3
+ "version": "0.2.0",
4
4
  "main": "dist/index.js",
5
5
  "bin": {
6
6
  "che": "dist/index.js"
@@ -11,7 +11,9 @@
11
11
  "build:clean": "rimraf dist && tsc",
12
12
  "build:clean:watch": "rimraf dist && tsc --watch",
13
13
  "clean": "rimraf dist",
14
- "format": "prettier --write ."
14
+ "format": "prettier --write .",
15
+ "test": "vitest run",
16
+ "test:watch": "vitest"
15
17
  },
16
18
  "author": "",
17
19
  "license": "MIT",
@@ -25,10 +27,8 @@
25
27
  "@types/node": "^24.0.0",
26
28
  "prettier": "^3.5.3",
27
29
  "rimraf": "^6.0.1",
28
- "typescript": "^5.8.3"
29
- },
30
- "publishConfig": {
31
- "access": "public"
30
+ "typescript": "^5.8.3",
31
+ "vitest": "^3.2.3"
32
32
  },
33
33
  "repository": {
34
34
  "type": "git",
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
+ }