md-template-vars 0.1.2 → 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/README.md CHANGED
@@ -29,11 +29,12 @@ md-template-vars <input> <output> [options]
29
29
 
30
30
  ### Options
31
31
 
32
- | Option | Default | Description |
33
- | ----------- | ---------------- | -------------------------------------- |
34
- | `--vars` | `variables.yaml` | Path to the variables YAML file |
35
- | `--include` | - | Glob pattern to include specific files |
36
- | `--exclude` | - | Glob pattern to exclude specific files |
32
+ | Option | Default | Description |
33
+ | ----------- | ---------------- | ------------------------------------------------ |
34
+ | `--vars` | `variables.yaml` | Path to the variables YAML file |
35
+ | `--include` | - | Glob pattern to include specific files |
36
+ | `--exclude` | - | Glob pattern to exclude specific files |
37
+ | `--watch` | `false` | Watch for file changes and rebuild automatically |
37
38
 
38
39
  ## Examples
39
40
 
@@ -59,6 +60,18 @@ md-template-vars ./templates ./output --include "api-*.md"
59
60
  md-template-vars ./templates ./output --exclude "draft-*.md"
60
61
  ```
61
62
 
63
+ ### Watch mode
64
+
65
+ ```bash
66
+ md-template-vars ./templates ./output --watch
67
+ ```
68
+
69
+ This will watch for changes in:
70
+ - Template files in the input directory
71
+ - The variables YAML file
72
+
73
+ When changes are detected, templates are automatically rebuilt.
74
+
62
75
  ## Template Syntax
63
76
 
64
77
  Use `{{variableName}}` syntax in your Markdown files:
@@ -83,6 +96,34 @@ project: My Project
83
96
  Welcome to My Project!
84
97
  ```
85
98
 
99
+ ### Nested Variables
100
+
101
+ You can use nested objects in your variables file and access them with dot notation:
102
+
103
+ **Template:**
104
+ ```markdown
105
+ # {{app.name}}
106
+
107
+ Database: {{database.host}}:{{database.port}}
108
+ ```
109
+
110
+ **Variables (variables.yaml):**
111
+ ```yaml
112
+ app:
113
+ name: My App
114
+
115
+ database:
116
+ host: localhost
117
+ port: 5432
118
+ ```
119
+
120
+ **Output:**
121
+ ```markdown
122
+ # My App
123
+
124
+ Database: localhost:5432
125
+ ```
126
+
86
127
  ## Error Handling
87
128
 
88
129
  | Case | Behavior |
@@ -1,4 +1,4 @@
1
- const VARIABLE_PATTERN = /\{\{(\w+)\}\}/g;
1
+ const VARIABLE_PATTERN = /\{\{([\w.]+)\}\}/g;
2
2
  export function renderTemplate(content, variables) {
3
3
  const undefinedVariables = [];
4
4
  const renderedContent = content.replace(VARIABLE_PATTERN, (match, varName) => {
@@ -1,3 +1,9 @@
1
1
  import { z } from "zod";
2
- export declare const VariablesSchema: z.ZodRecord<z.ZodString, z.ZodEffects<z.ZodUnion<[z.ZodString, z.ZodNumber, z.ZodBoolean]>, string, string | number | boolean>>;
2
+ declare const PrimitiveValueSchema: z.ZodUnion<[z.ZodString, z.ZodNumber, z.ZodBoolean]>;
3
+ type NestedValue = z.infer<typeof PrimitiveValueSchema> | {
4
+ [key: string]: NestedValue;
5
+ };
6
+ export declare const VariablesSchema: z.ZodRecord<z.ZodString, z.ZodType<NestedValue, z.ZodTypeDef, NestedValue>>;
3
7
  export type Variables = Record<string, string>;
8
+ export declare function flattenVariables(obj: Record<string, NestedValue>, prefix?: string): Variables;
9
+ export {};
@@ -1,3 +1,17 @@
1
1
  import { z } from "zod";
2
- const VariableValueSchema = z.union([z.string(), z.number(), z.boolean()]).transform(String);
3
- export const VariablesSchema = z.record(z.string(), VariableValueSchema);
2
+ const PrimitiveValueSchema = z.union([z.string(), z.number(), z.boolean()]);
3
+ const NestedValueSchema = z.lazy(() => z.union([PrimitiveValueSchema, z.record(z.string(), NestedValueSchema)]));
4
+ export const VariablesSchema = z.record(z.string(), NestedValueSchema);
5
+ export function flattenVariables(obj, prefix = "") {
6
+ const result = {};
7
+ for (const [key, value] of Object.entries(obj)) {
8
+ const fullKey = prefix ? `${prefix}.${key}` : key;
9
+ if (typeof value === "object" && value !== null) {
10
+ Object.assign(result, flattenVariables(value, fullKey));
11
+ }
12
+ else {
13
+ result[fullKey] = String(value);
14
+ }
15
+ }
16
+ return result;
17
+ }
@@ -1,6 +1,6 @@
1
1
  import { readFileSync, existsSync } from "node:fs";
2
2
  import { parse } from "yaml";
3
- import { VariablesSchema } from "../../domain/value-objects/variables.js";
3
+ import { VariablesSchema, flattenVariables } from "../../domain/value-objects/variables.js";
4
4
  import { VariablesFileNotFoundError, InvalidVariablesError } from "../../shared/errors.js";
5
5
  export function loadVariables(filePath) {
6
6
  if (!existsSync(filePath)) {
@@ -12,5 +12,5 @@ export function loadVariables(filePath) {
12
12
  if (!result.success) {
13
13
  throw new InvalidVariablesError(result.error.message);
14
14
  }
15
- return result.data;
15
+ return flattenVariables(result.data);
16
16
  }
@@ -22,4 +22,9 @@ export declare const mainCommand: import("citty").CommandDef<{
22
22
  type: "string";
23
23
  description: string;
24
24
  };
25
+ watch: {
26
+ type: "boolean";
27
+ description: string;
28
+ default: false;
29
+ };
25
30
  }>;
@@ -1,6 +1,49 @@
1
1
  import { defineCommand } from "citty";
2
+ import { watch } from "node:fs";
3
+ import { resolve } from "node:path";
2
4
  import { processTemplates } from "../../../application/use-cases/process-templates.js";
3
- import { VariablesFileNotFoundError, SameInputOutputError, InvalidVariablesError, } from "../../../shared/errors.js";
5
+ import { VariablesFileNotFoundError, SameInputOutputError, InvalidVariablesError } from "../../../shared/errors.js";
6
+ async function runProcess(options) {
7
+ try {
8
+ const result = await processTemplates(options);
9
+ for (const warning of result.warnings) {
10
+ console.warn(warning);
11
+ }
12
+ console.log(`Processed ${result.processedFiles.length} file(s)`);
13
+ for (const file of result.processedFiles) {
14
+ console.log(` - ${file}`);
15
+ }
16
+ return true;
17
+ }
18
+ catch (error) {
19
+ if (error instanceof VariablesFileNotFoundError ||
20
+ error instanceof SameInputOutputError ||
21
+ error instanceof InvalidVariablesError) {
22
+ console.error(`Error: ${error.message}`);
23
+ return false;
24
+ }
25
+ throw error;
26
+ }
27
+ }
28
+ function startWatch(options) {
29
+ const inputPath = resolve(options.input);
30
+ const varsPath = resolve(options.vars);
31
+ let debounceTimer = null;
32
+ const handleChange = () => {
33
+ if (debounceTimer) {
34
+ clearTimeout(debounceTimer);
35
+ }
36
+ debounceTimer = setTimeout(async () => {
37
+ console.log("\n--- Change detected, rebuilding... ---\n");
38
+ await runProcess(options);
39
+ }, 100);
40
+ };
41
+ console.log(`\nWatching for changes...`);
42
+ console.log(` - Templates: ${inputPath}`);
43
+ console.log(` - Variables: ${varsPath}\n`);
44
+ watch(inputPath, { recursive: true }, handleChange);
45
+ watch(varsPath, handleChange);
46
+ }
4
47
  export const mainCommand = defineCommand({
5
48
  meta: {
6
49
  name: "md-template-vars",
@@ -30,32 +73,29 @@ export const mainCommand = defineCommand({
30
73
  type: "string",
31
74
  description: "Glob pattern to exclude files",
32
75
  },
76
+ watch: {
77
+ type: "boolean",
78
+ description: "Watch for file changes and rebuild automatically",
79
+ default: false,
80
+ },
33
81
  },
34
82
  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}`);
83
+ const options = {
84
+ input: args.input,
85
+ output: args.output,
86
+ vars: args.vars,
87
+ include: args.include,
88
+ exclude: args.exclude,
89
+ };
90
+ const success = await runProcess(options);
91
+ if (args.watch) {
92
+ if (!success) {
93
+ console.log("\nFix errors and save to retry...\n");
49
94
  }
95
+ startWatch(options);
50
96
  }
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;
97
+ else if (!success) {
98
+ process.exit(1);
59
99
  }
60
100
  },
61
101
  });
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "md-template-vars",
3
3
  "author": "Shunta Toda",
4
- "version": "0.1.2",
4
+ "version": "0.2.1",
5
5
  "description": "Replace {{variables}} in markdown templates with YAML values",
6
6
  "type": "module",
7
7
  "main": "./dist/application/use-cases/process-templates.js",