@caleuche/cli 0.1.2
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/README.md +103 -0
- package/dist/index.js +50 -0
- package/dist/utils.js +56 -0
- package/package.json +36 -0
- package/src/index.ts +67 -0
- package/src/utils.ts +50 -0
- package/tsconfig.json +11 -0
package/README.md
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
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
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
4
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
5
|
+
};
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
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");
|
|
12
|
+
commander_1.program
|
|
13
|
+
.name("@caleuche/cli")
|
|
14
|
+
.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
|
+
}
|
|
46
|
+
commander_1.program
|
|
47
|
+
.command("compile <sample-directory> <data-file> <output-directory>")
|
|
48
|
+
.option("-p, --project", "Generate project file")
|
|
49
|
+
.action(compile);
|
|
50
|
+
commander_1.program.parse();
|
package/dist/utils.js
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
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.parse = parse;
|
|
7
|
+
exports.resolveSampleFile = resolveSampleFile;
|
|
8
|
+
exports.isDirectory = isDirectory;
|
|
9
|
+
exports.createOutputDirectory = createOutputDirectory;
|
|
10
|
+
exports.resolveTemplate = resolveTemplate;
|
|
11
|
+
exports.isObject = isObject;
|
|
12
|
+
const yaml_1 = require("yaml");
|
|
13
|
+
const fs_1 = __importDefault(require("fs"));
|
|
14
|
+
const path_1 = __importDefault(require("path"));
|
|
15
|
+
function parse(filePath) {
|
|
16
|
+
try {
|
|
17
|
+
const fileContent = fs_1.default.readFileSync(filePath, "utf-8");
|
|
18
|
+
if (filePath.endsWith(".yaml") || filePath.endsWith(".yml")) {
|
|
19
|
+
return (0, yaml_1.parse)(fileContent);
|
|
20
|
+
}
|
|
21
|
+
return JSON.parse(fileContent);
|
|
22
|
+
}
|
|
23
|
+
catch (error) {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
function resolveSampleFile(samplePath) {
|
|
28
|
+
if (isDirectory(samplePath)) {
|
|
29
|
+
return path_1.default.join(samplePath, "sample.yaml");
|
|
30
|
+
}
|
|
31
|
+
return samplePath;
|
|
32
|
+
}
|
|
33
|
+
function isDirectory(path) {
|
|
34
|
+
return fs_1.default.existsSync(path) && fs_1.default.lstatSync(path).isDirectory();
|
|
35
|
+
}
|
|
36
|
+
function createOutputDirectory(outputPath) {
|
|
37
|
+
if (!fs_1.default.existsSync(outputPath)) {
|
|
38
|
+
fs_1.default.mkdirSync(outputPath, { recursive: true });
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
function resolveTemplate(samplePath, sample) {
|
|
42
|
+
try {
|
|
43
|
+
const templatePath = path_1.default.join(samplePath, sample.template);
|
|
44
|
+
if (!fs_1.default.existsSync(templatePath)) {
|
|
45
|
+
return sample.template;
|
|
46
|
+
}
|
|
47
|
+
return fs_1.default.readFileSync(templatePath, "utf-8");
|
|
48
|
+
}
|
|
49
|
+
catch (error) {
|
|
50
|
+
console.error("Error reading template file.");
|
|
51
|
+
process.exit(1);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
function isObject(value) {
|
|
55
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
56
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@caleuche/cli",
|
|
3
|
+
"version": "0.1.2",
|
|
4
|
+
"main": "dist/index.js",
|
|
5
|
+
"bin": {
|
|
6
|
+
"che": "dist/index.js"
|
|
7
|
+
},
|
|
8
|
+
"scripts": {
|
|
9
|
+
"build": "tsc",
|
|
10
|
+
"build:watch": "tsc --watch",
|
|
11
|
+
"build:clean": "rimraf dist && tsc",
|
|
12
|
+
"build:clean:watch": "rimraf dist && tsc --watch",
|
|
13
|
+
"clean": "rimraf dist",
|
|
14
|
+
"format": "prettier --write .",
|
|
15
|
+
"prepare": "npm run build"
|
|
16
|
+
},
|
|
17
|
+
"author": "",
|
|
18
|
+
"license": "MIT",
|
|
19
|
+
"description": "Caleuche CLI",
|
|
20
|
+
"dependencies": {
|
|
21
|
+
"@caleuche/core": "file:../caleuche",
|
|
22
|
+
"commander": "^14.0.0",
|
|
23
|
+
"yaml": "^2.8.0"
|
|
24
|
+
},
|
|
25
|
+
"devDependencies": {
|
|
26
|
+
"@types/node": "^22.15.30",
|
|
27
|
+
"prettier": "^3.5.3",
|
|
28
|
+
"rimraf": "^6.0.1",
|
|
29
|
+
"typescript": "^5.8.3"
|
|
30
|
+
},
|
|
31
|
+
"repository": {
|
|
32
|
+
"type": "git",
|
|
33
|
+
"url": "https://github.com/brandor64/caleuche"
|
|
34
|
+
},
|
|
35
|
+
"homepage": "https://github.com/brandor64/caleuche#readme"
|
|
36
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { program } from "commander";
|
|
4
|
+
import { compileSample, Sample } from "@caleuche/core";
|
|
5
|
+
import fs from "fs";
|
|
6
|
+
import path from "path";
|
|
7
|
+
import {
|
|
8
|
+
createOutputDirectory,
|
|
9
|
+
isObject,
|
|
10
|
+
parse,
|
|
11
|
+
resolveSampleFile,
|
|
12
|
+
resolveTemplate,
|
|
13
|
+
} from "./utils";
|
|
14
|
+
|
|
15
|
+
program
|
|
16
|
+
.name("@caleuche/cli")
|
|
17
|
+
.description("Caleuche CLI for compiling samples")
|
|
18
|
+
.version("1.0.0");
|
|
19
|
+
|
|
20
|
+
function compile(
|
|
21
|
+
samplePath: string,
|
|
22
|
+
dataPath: string,
|
|
23
|
+
outputPath: string,
|
|
24
|
+
options: { project?: boolean },
|
|
25
|
+
) {
|
|
26
|
+
const sampleFilePath = resolveSampleFile(samplePath);
|
|
27
|
+
if (!fs.existsSync(sampleFilePath)) {
|
|
28
|
+
console.error(`Sample file not found: ${sampleFilePath}`);
|
|
29
|
+
process.exit(1);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const sample = parse<Sample>(sampleFilePath);
|
|
33
|
+
if (!sample) {
|
|
34
|
+
console.error(`Failed to parse sample file: ${sampleFilePath}`);
|
|
35
|
+
process.exit(1);
|
|
36
|
+
}
|
|
37
|
+
sample.template = resolveTemplate(samplePath, sample);
|
|
38
|
+
|
|
39
|
+
const inputData = parse<Record<string, any>>(dataPath);
|
|
40
|
+
if (!inputData || !isObject(inputData)) {
|
|
41
|
+
console.error(`Failed to parse input data file: ${dataPath}`);
|
|
42
|
+
process.exit(1);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const output = compileSample(sample, inputData, {
|
|
46
|
+
project: options.project || false,
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
if (!isObject(inputData)) {
|
|
50
|
+
console.error("Input data must be an object.");
|
|
51
|
+
process.exit(1);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
createOutputDirectory(outputPath);
|
|
55
|
+
|
|
56
|
+
for (const { fileName, content } of output.items) {
|
|
57
|
+
const itemOutputPath = path.join(outputPath, fileName);
|
|
58
|
+
fs.writeFileSync(itemOutputPath, content);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
program
|
|
63
|
+
.command("compile <sample-directory> <data-file> <output-directory>")
|
|
64
|
+
.option("-p, --project", "Generate project file")
|
|
65
|
+
.action(compile);
|
|
66
|
+
|
|
67
|
+
program.parse();
|
package/src/utils.ts
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { parse as parseYaml } from "yaml";
|
|
2
|
+
import fs from "fs";
|
|
3
|
+
import path from "path";
|
|
4
|
+
import { Sample } from "@caleuche/core";
|
|
5
|
+
|
|
6
|
+
export function parse<T>(filePath: string): T | null {
|
|
7
|
+
try {
|
|
8
|
+
const fileContent = fs.readFileSync(filePath, "utf-8");
|
|
9
|
+
if (filePath.endsWith(".yaml") || filePath.endsWith(".yml")) {
|
|
10
|
+
return parseYaml(fileContent) as T;
|
|
11
|
+
}
|
|
12
|
+
return JSON.parse(fileContent) as T;
|
|
13
|
+
} catch (error) {
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function resolveSampleFile(samplePath: string): string {
|
|
19
|
+
if (isDirectory(samplePath)) {
|
|
20
|
+
return path.join(samplePath, "sample.yaml");
|
|
21
|
+
}
|
|
22
|
+
return samplePath;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function isDirectory(path: string): boolean {
|
|
26
|
+
return fs.existsSync(path) && fs.lstatSync(path).isDirectory();
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function createOutputDirectory(outputPath: string) {
|
|
30
|
+
if (!fs.existsSync(outputPath)) {
|
|
31
|
+
fs.mkdirSync(outputPath, { recursive: true });
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function resolveTemplate(samplePath: string, sample: Sample): string {
|
|
36
|
+
try {
|
|
37
|
+
const templatePath = path.join(samplePath, sample.template);
|
|
38
|
+
if (!fs.existsSync(templatePath)) {
|
|
39
|
+
return sample.template;
|
|
40
|
+
}
|
|
41
|
+
return fs.readFileSync(templatePath, "utf-8");
|
|
42
|
+
} catch (error) {
|
|
43
|
+
console.error("Error reading template file.");
|
|
44
|
+
process.exit(1);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function isObject(value: any): value is Record<string, any> {
|
|
49
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
50
|
+
}
|