md-template-vars 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/application/use-cases/process-templates.d.ts +2 -0
- package/dist/application/use-cases/process-templates.js +32 -0
- package/dist/domain/entities/template.d.ts +5 -0
- package/dist/domain/entities/template.js +8 -0
- package/dist/domain/services/template-renderer.d.ts +3 -0
- package/dist/domain/services/template-renderer.js +15 -0
- package/dist/domain/value-objects/variables.d.ts +3 -0
- package/dist/domain/value-objects/variables.js +3 -0
- package/dist/infrastructure/repositories/template-repository.d.ts +3 -0
- package/dist/infrastructure/repositories/template-repository.js +11 -0
- package/dist/infrastructure/repositories/variables-repository.d.ts +2 -0
- package/dist/infrastructure/repositories/variables-repository.js +16 -0
- package/dist/infrastructure/services/file-scanner.d.ts +5 -0
- package/dist/infrastructure/services/file-scanner.js +8 -0
- package/dist/presentation/cli/commands/main.d.ts +25 -0
- package/dist/presentation/cli/commands/main.js +61 -0
- package/dist/presentation/cli/index.d.ts +2 -0
- package/dist/presentation/cli/index.js +4 -0
- package/dist/shared/errors.d.ts +9 -0
- package/dist/shared/errors.js +18 -0
- package/dist/shared/types.d.ts +15 -0
- package/dist/shared/types.js +1 -0
- package/package.json +48 -0
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { resolve, relative, join } from "node:path";
|
|
2
|
+
import { SameInputOutputError } from "../../shared/errors.js";
|
|
3
|
+
import { loadVariables } from "../../infrastructure/repositories/variables-repository.js";
|
|
4
|
+
import { readTemplate, writeTemplate } from "../../infrastructure/repositories/template-repository.js";
|
|
5
|
+
import { scanTemplates } from "../../infrastructure/services/file-scanner.js";
|
|
6
|
+
import { renderTemplate } from "../../domain/services/template-renderer.js";
|
|
7
|
+
export async function processTemplates(options) {
|
|
8
|
+
const inputDir = resolve(options.input);
|
|
9
|
+
const outputDir = resolve(options.output);
|
|
10
|
+
if (inputDir === outputDir) {
|
|
11
|
+
throw new SameInputOutputError();
|
|
12
|
+
}
|
|
13
|
+
const variables = loadVariables(options.vars);
|
|
14
|
+
const files = await scanTemplates(inputDir, {
|
|
15
|
+
include: options.include,
|
|
16
|
+
exclude: options.exclude,
|
|
17
|
+
});
|
|
18
|
+
const processedFiles = [];
|
|
19
|
+
const warnings = [];
|
|
20
|
+
for (const filePath of files) {
|
|
21
|
+
const template = readTemplate(filePath);
|
|
22
|
+
const result = renderTemplate(template.content, variables);
|
|
23
|
+
const relativePath = relative(inputDir, filePath);
|
|
24
|
+
const outputPath = join(outputDir, relativePath);
|
|
25
|
+
writeTemplate(outputPath, result.content);
|
|
26
|
+
processedFiles.push(relativePath);
|
|
27
|
+
for (const varName of result.undefinedVariables) {
|
|
28
|
+
warnings.push(`Warning: undefined variable "{{${varName}}}" in ${relativePath}`);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return { processedFiles, warnings };
|
|
32
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
const VARIABLE_PATTERN = /\{\{(\w+)\}\}/g;
|
|
2
|
+
export function renderTemplate(content, variables) {
|
|
3
|
+
const undefinedVariables = [];
|
|
4
|
+
const renderedContent = content.replace(VARIABLE_PATTERN, (match, varName) => {
|
|
5
|
+
if (varName in variables) {
|
|
6
|
+
return variables[varName];
|
|
7
|
+
}
|
|
8
|
+
undefinedVariables.push(varName);
|
|
9
|
+
return match;
|
|
10
|
+
});
|
|
11
|
+
return {
|
|
12
|
+
content: renderedContent,
|
|
13
|
+
undefinedVariables,
|
|
14
|
+
};
|
|
15
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { readFileSync, writeFileSync, mkdirSync } from "node:fs";
|
|
2
|
+
import { dirname } from "node:path";
|
|
3
|
+
import { Template } from "../../domain/entities/template.js";
|
|
4
|
+
export function readTemplate(filePath) {
|
|
5
|
+
const content = readFileSync(filePath, "utf-8");
|
|
6
|
+
return new Template(filePath, content);
|
|
7
|
+
}
|
|
8
|
+
export function writeTemplate(filePath, content) {
|
|
9
|
+
mkdirSync(dirname(filePath), { recursive: true });
|
|
10
|
+
writeFileSync(filePath, content, "utf-8");
|
|
11
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { readFileSync, existsSync } from "node:fs";
|
|
2
|
+
import { parse } from "yaml";
|
|
3
|
+
import { VariablesSchema } from "../../domain/value-objects/variables.js";
|
|
4
|
+
import { VariablesFileNotFoundError, InvalidVariablesError } from "../../shared/errors.js";
|
|
5
|
+
export function loadVariables(filePath) {
|
|
6
|
+
if (!existsSync(filePath)) {
|
|
7
|
+
throw new VariablesFileNotFoundError(filePath);
|
|
8
|
+
}
|
|
9
|
+
const content = readFileSync(filePath, "utf-8");
|
|
10
|
+
const parsed = parse(content);
|
|
11
|
+
const result = VariablesSchema.safeParse(parsed);
|
|
12
|
+
if (!result.success) {
|
|
13
|
+
throw new InvalidVariablesError(result.error.message);
|
|
14
|
+
}
|
|
15
|
+
return result.data;
|
|
16
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import fg from "fast-glob";
|
|
2
|
+
export async function scanTemplates(inputDir, options = {}) {
|
|
3
|
+
const pattern = options.include
|
|
4
|
+
? `${inputDir}/${options.include}`
|
|
5
|
+
: `${inputDir}/**/*.md`;
|
|
6
|
+
const ignore = options.exclude ? [`${inputDir}/${options.exclude}`] : [];
|
|
7
|
+
return fg(pattern, { ignore });
|
|
8
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export declare const mainCommand: import("citty").CommandDef<{
|
|
2
|
+
input: {
|
|
3
|
+
type: "positional";
|
|
4
|
+
description: string;
|
|
5
|
+
required: true;
|
|
6
|
+
};
|
|
7
|
+
output: {
|
|
8
|
+
type: "positional";
|
|
9
|
+
description: string;
|
|
10
|
+
required: true;
|
|
11
|
+
};
|
|
12
|
+
vars: {
|
|
13
|
+
type: "string";
|
|
14
|
+
description: string;
|
|
15
|
+
default: string;
|
|
16
|
+
};
|
|
17
|
+
include: {
|
|
18
|
+
type: "string";
|
|
19
|
+
description: string;
|
|
20
|
+
};
|
|
21
|
+
exclude: {
|
|
22
|
+
type: "string";
|
|
23
|
+
description: string;
|
|
24
|
+
};
|
|
25
|
+
}>;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { defineCommand } from "citty";
|
|
2
|
+
import { processTemplates } from "../../../application/use-cases/process-templates.js";
|
|
3
|
+
import { VariablesFileNotFoundError, SameInputOutputError, InvalidVariablesError, } from "../../../shared/errors.js";
|
|
4
|
+
export const mainCommand = defineCommand({
|
|
5
|
+
meta: {
|
|
6
|
+
name: "md-template-vars",
|
|
7
|
+
description: "Replace {{variables}} in markdown templates with YAML values",
|
|
8
|
+
},
|
|
9
|
+
args: {
|
|
10
|
+
input: {
|
|
11
|
+
type: "positional",
|
|
12
|
+
description: "Input directory containing templates",
|
|
13
|
+
required: true,
|
|
14
|
+
},
|
|
15
|
+
output: {
|
|
16
|
+
type: "positional",
|
|
17
|
+
description: "Output directory for processed files",
|
|
18
|
+
required: true,
|
|
19
|
+
},
|
|
20
|
+
vars: {
|
|
21
|
+
type: "string",
|
|
22
|
+
description: "Path to variables YAML file",
|
|
23
|
+
default: "variables.yaml",
|
|
24
|
+
},
|
|
25
|
+
include: {
|
|
26
|
+
type: "string",
|
|
27
|
+
description: "Glob pattern to include files",
|
|
28
|
+
},
|
|
29
|
+
exclude: {
|
|
30
|
+
type: "string",
|
|
31
|
+
description: "Glob pattern to exclude files",
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
async run({ args }) {
|
|
35
|
+
try {
|
|
36
|
+
const result = await processTemplates({
|
|
37
|
+
input: args.input,
|
|
38
|
+
output: args.output,
|
|
39
|
+
vars: args.vars,
|
|
40
|
+
include: args.include,
|
|
41
|
+
exclude: args.exclude,
|
|
42
|
+
});
|
|
43
|
+
for (const warning of result.warnings) {
|
|
44
|
+
console.warn(warning);
|
|
45
|
+
}
|
|
46
|
+
console.log(`Processed ${result.processedFiles.length} file(s)`);
|
|
47
|
+
for (const file of result.processedFiles) {
|
|
48
|
+
console.log(` - ${file}`);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
catch (error) {
|
|
52
|
+
if (error instanceof VariablesFileNotFoundError ||
|
|
53
|
+
error instanceof SameInputOutputError ||
|
|
54
|
+
error instanceof InvalidVariablesError) {
|
|
55
|
+
console.error(`Error: ${error.message}`);
|
|
56
|
+
process.exit(1);
|
|
57
|
+
}
|
|
58
|
+
throw error;
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
});
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export declare class VariablesFileNotFoundError extends Error {
|
|
2
|
+
constructor(filePath: string);
|
|
3
|
+
}
|
|
4
|
+
export declare class SameInputOutputError extends Error {
|
|
5
|
+
constructor();
|
|
6
|
+
}
|
|
7
|
+
export declare class InvalidVariablesError extends Error {
|
|
8
|
+
constructor(message: string);
|
|
9
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export class VariablesFileNotFoundError extends Error {
|
|
2
|
+
constructor(filePath) {
|
|
3
|
+
super(`Variables file not found: ${filePath}`);
|
|
4
|
+
this.name = "VariablesFileNotFoundError";
|
|
5
|
+
}
|
|
6
|
+
}
|
|
7
|
+
export class SameInputOutputError extends Error {
|
|
8
|
+
constructor() {
|
|
9
|
+
super("Input and output directories cannot be the same");
|
|
10
|
+
this.name = "SameInputOutputError";
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
export class InvalidVariablesError extends Error {
|
|
14
|
+
constructor(message) {
|
|
15
|
+
super(`Invalid variables file: ${message}`);
|
|
16
|
+
this.name = "InvalidVariablesError";
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export interface CliOptions {
|
|
2
|
+
input: string;
|
|
3
|
+
output: string;
|
|
4
|
+
vars: string;
|
|
5
|
+
include?: string;
|
|
6
|
+
exclude?: string;
|
|
7
|
+
}
|
|
8
|
+
export interface ProcessResult {
|
|
9
|
+
processedFiles: string[];
|
|
10
|
+
warnings: string[];
|
|
11
|
+
}
|
|
12
|
+
export interface RenderResult {
|
|
13
|
+
content: string;
|
|
14
|
+
undefinedVariables: string[];
|
|
15
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/package.json
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "md-template-vars",
|
|
3
|
+
"author": "Shunta Toda",
|
|
4
|
+
"version": "0.1.0",
|
|
5
|
+
"description": "Replace {{variables}} in markdown templates with YAML values",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"main": "./dist/application/use-cases/process-templates.js",
|
|
8
|
+
"types": "./dist/application/use-cases/process-templates.d.ts",
|
|
9
|
+
"bin": {
|
|
10
|
+
"md-template-vars": "./dist/presentation/cli/index.js"
|
|
11
|
+
},
|
|
12
|
+
"files": [
|
|
13
|
+
"dist"
|
|
14
|
+
],
|
|
15
|
+
"scripts": {
|
|
16
|
+
"build": "tsc",
|
|
17
|
+
"start": "node ./dist/presentation/cli/index.js",
|
|
18
|
+
"dev": "tsc --watch",
|
|
19
|
+
"test": "vitest",
|
|
20
|
+
"prepublishOnly": "pnpm test run && pnpm build"
|
|
21
|
+
},
|
|
22
|
+
"keywords": [
|
|
23
|
+
"markdown",
|
|
24
|
+
"template",
|
|
25
|
+
"variables",
|
|
26
|
+
"yaml",
|
|
27
|
+
"cli"
|
|
28
|
+
],
|
|
29
|
+
"license": "MIT",
|
|
30
|
+
"repository": {
|
|
31
|
+
"type": "git",
|
|
32
|
+
"url": ""
|
|
33
|
+
},
|
|
34
|
+
"engines": {
|
|
35
|
+
"node": ">=18"
|
|
36
|
+
},
|
|
37
|
+
"dependencies": {
|
|
38
|
+
"citty": "^0.1.6",
|
|
39
|
+
"fast-glob": "^3.3.2",
|
|
40
|
+
"yaml": "^2.3.4",
|
|
41
|
+
"zod": "^3.22.4"
|
|
42
|
+
},
|
|
43
|
+
"devDependencies": {
|
|
44
|
+
"@types/node": "^20.11.0",
|
|
45
|
+
"typescript": "^5.3.3",
|
|
46
|
+
"vitest": "^1.2.0"
|
|
47
|
+
}
|
|
48
|
+
}
|