mikel-cli 0.33.0 → 0.34.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.
Files changed (4) hide show
  1. package/README.md +117 -15
  2. package/cli.js +306 -0
  3. package/index.js +11 -233
  4. package/package.json +12 -4
package/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
  ![npm version](https://badgen.net/npm/v/mikel-cli?labelColor=1d2734&color=21bf81)
4
4
  ![license](https://badgen.net/github/license/jmjuanes/mikel?labelColor=1d2734&color=21bf81)
5
5
 
6
- A command-line interface for the [Mikel](https://github.com/jmjuanes/mikel) templating engine. This CLI tool allows you to render Mikel templates from the command line with support for data files, partials, helpers, and functions.
6
+ A command-line interface for the [Mikel](https://github.com/jmjuanes/mikel) templating engine. This CLI tool allows you to render Mikel templates from the command line with support for data files, partials, helpers, functions, and plugins.
7
7
 
8
8
  ## Installation
9
9
 
@@ -31,6 +31,7 @@ $ mikel <template> [options]
31
31
 
32
32
  | Option | Short | Description |
33
33
  |--------|-------|-------------|
34
+ | `--config <file>` | `-c` | Path to configuration file |
34
35
  | `--help` | `-h` | Display help information |
35
36
  | `--data <file>` | `-D` | Path to JSON data file |
36
37
  | `--output <file>` | `-o` | Output file path |
@@ -99,20 +100,6 @@ mikel template.html --partial 'components/**/*.html' --output dist/index.html
99
100
  mikel template.html --partial header.html --partial 'components/*.html' --output dist/index.html
100
101
  ```
101
102
 
102
- #### Complex Example
103
-
104
- A complete example combining all features:
105
-
106
- ```bash
107
- mikel src/template.html \
108
- --data src/data.json \
109
- --partial 'src/partials/*.html' \
110
- --partial 'src/components/**/*.html' \
111
- --helper 'src/helpers/*.js' \
112
- --function 'src/utils/*.js' \
113
- --output dist/index.html
114
- ```
115
-
116
103
  ### Glob Pattern Support
117
104
 
118
105
  The `--partial`, `--helper`, and `--function` options support glob patterns for loading multiple files at once:
@@ -126,6 +113,121 @@ The `--partial`, `--helper`, and `--function` options support glob patterns for
126
113
 
127
114
  **Note:** Glob patterns should be quoted to prevent shell expansion.
128
115
 
116
+ ## Configuration File
117
+
118
+ For more complex use cases — multiple input files, output renaming, or plugins — you can use a configuration file instead of CLI arguments. Create a `mikel.config.js` file in your project root:
119
+
120
+ ```js
121
+ export default {
122
+ input: "src/**/*.mustache",
123
+ output: {
124
+ dir: "dist/",
125
+ rename: {
126
+ "^src/(.+)\\.mustache$": "$1.html",
127
+ },
128
+ },
129
+ data: "./data/site.json",
130
+ plugins: [],
131
+ };
132
+ ```
133
+
134
+ Then run mikel pointing to the config file:
135
+
136
+ ```bash
137
+ mikel --config mikel.config.js
138
+ ```
139
+
140
+ CLI arguments take precedence over the configuration file when both are provided. However, when using a configuration file, `input` can be a glob or an array of globs — something not possible via CLI arguments alone.
141
+
142
+ ### Configuration Fields
143
+
144
+ #### `input`
145
+
146
+ The template file(s) to process. Accepts a string (file path or glob) or an array of strings:
147
+
148
+ ```js
149
+ // single file
150
+ input: "src/index.mustache"
151
+
152
+ // array of files and globs
153
+ input: ["src/index.mustache", "src/pages/**/*.mustache"]
154
+ ```
155
+
156
+ #### `output`
157
+
158
+ Where to write the rendered files. Accepts a string (directory path) or an object:
159
+
160
+ ```js
161
+ // simple directory
162
+ output: "dist/"
163
+
164
+ // with rename rules
165
+ output: {
166
+ dir: "dist/",
167
+ rename: {
168
+ "^src/(.+)\\.mustache$": "$1.html",
169
+ },
170
+ }
171
+ ```
172
+
173
+ The `rename` field works like Jest's `moduleNameMapper` — keys are regular expressions and values are replacement strings. The first matching pattern wins. If no pattern matches, the basename of the input file is used as the output filename.
174
+
175
+ ```js
176
+ // src/docs/guide/index.mustache → dist/docs/guide/index.html
177
+ rename: {
178
+ "^src/(.+)\\.mustache$": "$1.html",
179
+ }
180
+ ```
181
+
182
+ #### `data`
183
+
184
+ Data to pass to the templates. Accepts a path to a JSON file or a plain object:
185
+
186
+ ```js
187
+ // path to JSON file
188
+ data: "./data/site.json"
189
+
190
+ // inline object
191
+ data: {
192
+ site: {
193
+ title: "My Site",
194
+ },
195
+ }
196
+ ```
197
+
198
+ #### `plugins`
199
+
200
+ An array of Mikel plugins to load. See the [Plugins](#plugins) section for details.
201
+
202
+ ## Plugins
203
+
204
+ Plugins extend Mikel's functionality by registering additional helpers, functions, or partials. They can be loaded both via the `--plugin` CLI flag and the `plugins` configuration field.
205
+
206
+ ### Loading Plugins via CLI
207
+
208
+ Use the `--plugin` flag to load a plugin from a JavaScript module:
209
+
210
+ ```bash
211
+ mikel template.html --plugin mikel-markdown --output dist/index.html
212
+ ```
213
+
214
+ ### Loading Plugins via Configuration
215
+
216
+ Use the `plugins` field in your configuration file. Each entry can be a module name (string) or a tuple of `[moduleName, options]` when the plugin requires configuration:
217
+
218
+ ```js
219
+ export default {
220
+ plugins: [
221
+ // plugin without options
222
+ "mikel-frontmatter",
223
+ // plugin with options
224
+ ["mikel-markdown", {
225
+ classNames: { ... },
226
+ }],
227
+ ],
228
+ };
229
+ ```
230
+
129
231
  ## License
130
232
 
131
233
  Licensed under the [MIT License](../../LICENSE).
package/cli.js ADDED
@@ -0,0 +1,306 @@
1
+ #!/usr/bin/env node
2
+
3
+ import fs from "node:fs/promises";
4
+ import { existsSync } from "node:fs";
5
+ import path from "node:path";
6
+ import { parseArgs } from "node:util";
7
+ import mikel from "mikel";
8
+ import { expandGlobPatterns, applyRename } from "./index.js";
9
+
10
+ // load configuration file
11
+ const loadConfiguration = async (configurationFile) => {
12
+ if (!configurationFile) {
13
+ return {};
14
+ }
15
+ const configurationPath = path.resolve(process.cwd(), configurationFile);
16
+ if (!existsSync(configurationPath)) {
17
+ throw new Error(`Configuration file '${configurationPath}' was not found.`);
18
+ }
19
+ // check the extension of the file
20
+ const configurationExtension = path.extname(configurationFile);
21
+ if (configurationExtension === ".js") {
22
+ return await import(configurationPath);
23
+ }
24
+ else if (configurationExtension === ".json") {
25
+ const content = await fs.readFile(configurationPath, "utf8");
26
+ return JSON.parse(content);
27
+ }
28
+ else {
29
+ throw new Error(`Unknown extension for configuration file '${configurationFile}'`);
30
+ }
31
+ };
32
+
33
+ // @description loading input files
34
+ const loadInput = async (inputFiles) => {
35
+ if (!inputFiles || inputFiles?.length === 0) {
36
+ throw new Error(`No input templates provided.`);
37
+ }
38
+ // expand glob patterns
39
+ return expandGlobPatterns([inputFiles].flat());
40
+ };
41
+
42
+ // @description resolve output
43
+ const resolveOutput = (inputFile, outputFile, outputConfig) => {
44
+ // 1. output file is provided via cli arguments
45
+ if (outputFile) {
46
+ return path.resolve(process.cwd(), outputFile);
47
+ }
48
+ // 2. output configuration is provided and it is a string
49
+ else if (outputConfig && typeof outputConfig === "string") {
50
+ return path.resolve(process.cwd(), path.join(outputConfig, inputFile));
51
+ }
52
+ // 3. output configuration is provided and it is an object
53
+ else if (outputConfig && typeof outputConfig === "object") {
54
+ const renamedOutputFile = applyRename(inputFile, outputConfig?.rename || {});
55
+ return path.resolve(process.cwd(), path.join(outputConfig?.dir || ".", renamedOutputFile));
56
+ }
57
+ // 4. other case???
58
+ throw new Error(`Unknown error resolving output for template '${inputFile}'`);
59
+ };
60
+
61
+ // @description load JSON data from the provided path
62
+ const loadData = async (fileOrObject = null) => {
63
+ if (!fileOrObject) {
64
+ return {};
65
+ }
66
+ // 1. check for object containing data (from config.data)
67
+ if (typeof fileOrObject === "object") {
68
+ return fileOrObject;
69
+ }
70
+ // 2. build the full data file path and check if exists
71
+ const dataPath = path.resolve(process.cwd(), fileOrObject);
72
+ if (!existsSync(dataPath)) {
73
+ throw new Error(`Data file '${dataPath}' was not found.`);
74
+ }
75
+ // 3. read the file and parse it as JSON
76
+ try {
77
+ const content = await fs.readFile(dataPath, "utf8");
78
+ return JSON.parse(content);
79
+ } catch (error) {
80
+ if (error instanceof SyntaxError) {
81
+ throw new Error(`Invalid JSON in data file '${dataPath}': ${error.message}`);
82
+ }
83
+ throw new Error(`Failed to read data file '${dataPath}': ${error.message}`);
84
+ }
85
+ };
86
+
87
+ // @description load javascript modules
88
+ const loadModules = async (patterns = [], callback) => {
89
+ const files = await expandGlobPatterns(patterns);
90
+
91
+ for (let i = 0; i < files.length; i++) {
92
+ const file = files[i]; // path.resolve(process.cwd(), uniqueFiles[i]);
93
+ if (!existsSync(file)) {
94
+ throw new Error(`File '${file}' was not found.`);
95
+ }
96
+
97
+ // Check if it's a file (not a directory)
98
+ const stats = await fs.stat(file);
99
+ if (!stats.isFile()) {
100
+ continue; // Skip directories
101
+ }
102
+
103
+ // import the module
104
+ try {
105
+ await callback(file);
106
+ } catch (error) {
107
+ throw new Error(`Failed to load '${file}': ${error.message}`);
108
+ }
109
+ }
110
+ };
111
+
112
+ // print the help of the tool
113
+ const printHelp = () => {
114
+ console.log("Usage: ");
115
+ console.log(" mikel --help");
116
+ console.log(" mikel <template> [...options]");
117
+ console.log("");
118
+ console.log("Options:");
119
+ console.log(" -h, --help Prints the usage information");
120
+ console.log(" -P, --partial <file> Register a partial (supports glob patterns, can be used multiple times)");
121
+ console.log(" -H, --helper <file> Register a helper (supports glob patterns, can be used multiple times)");
122
+ console.log(" -F, --function <file> Register a function (supports glob patterns, can be used multiple times)");
123
+ console.log(" -L, --plugin <file> Load a plugin from node_modules (can be used multiple times)");
124
+ console.log(" -D, --data <file> Path to the data file to use (JSON)");
125
+ console.log(" -o, --output <file> Output file");
126
+ console.log("");
127
+ console.log("Examples:");
128
+ console.log(" mikel template.html --data data.json --output www/index.html");
129
+ console.log(" mikel template.html --data data.json --partial header.html --partial footer.html --output www/index.html");
130
+ console.log(" mikel template.html --helper helpers.js --function utils.js --output dist/index.html");
131
+ console.log(" mikel template.html --partial 'partials/*.html' --helper 'helpers/*.js' --output dist/index.html");
132
+ console.log(" mikel template.html --partial 'components/**/*.html' --output dist/index.html");
133
+ console.log("");
134
+ process.exit(0);
135
+ };
136
+
137
+ // @description main function
138
+ const main = async (inputOption = "", options = {}) => {
139
+ // check to print help
140
+ if (options.help) {
141
+ return printHelp();
142
+ }
143
+
144
+ // load configuration file and inputs
145
+ const config = await loadConfiguration(options.config);
146
+ const inputs = await loadInput(inputOption || config.input || null);
147
+ const data = await loadData(options.data || config.data || null);
148
+ const mikelInstance = mikel.create({});
149
+
150
+ // if no input files were provided, throw an error and stop processing
151
+ if (!inputs || inputs?.length === 0) {
152
+ throw new Error(`No input templates found`);
153
+ }
154
+ // load plugins
155
+ const plugins = options.plugin || config.plugins || [];
156
+ for (let i = 0; i < plugins.length; i++) {
157
+ const pluginName = Array.isArray(plugins[i]) ? plugins[i][0] : plugins[i];
158
+ const pluginOptions = Array.isArray(plugins[i]) && plugins[i].length === 2 ? plugins[i][1] : null;
159
+ let pluginModule;
160
+ try {
161
+ // try to import the plugin from node_modules
162
+ pluginModule = (await import(pluginName))?.default;
163
+ if (typeof pluginModule !== "function") {
164
+ throw new Error(`Plugin '${pluginName}' does not export a valid plugin function.`);
165
+ }
166
+ mikelInstance.use(pluginModule(pluginOptions));
167
+ } catch (error) {
168
+ throw new Error(`Failed to load plugin '${pluginName}': ${error.message}`);
169
+ }
170
+ }
171
+
172
+ // load additional partials, helpers, and functions
173
+ await loadModules(options.partial, async (file) => {
174
+ try {
175
+ mikelInstance.addPartial(path.basename(file), await fs.readFile(file, "utf8"));
176
+ } catch (error) {
177
+ throw new Error(`Failed to read partial file '${file}': ${error.message}`);
178
+ }
179
+ });
180
+ await loadModules(options.helper, async (file) => {
181
+ const extension = path.extname(file);
182
+ if (extension !== ".js" && extension !== ".mjs") {
183
+ throw new Error(`Module '${file}' is not supported. Only ESM JavaScript (.js or .mjs) files are supported.`);
184
+ }
185
+ // import the module
186
+ const content = (await import(file)) || {};
187
+ if (typeof content === "object" && !!content) {
188
+ Object.keys(content).forEach(helperName => {
189
+ mikelInstance.addHelper(helperName, content[helperName]);
190
+ });
191
+ }
192
+ });
193
+ await loadModules(options.function, async (file) => {
194
+ const extension = path.extname(file);
195
+ if (extension !== ".js" && extension !== ".mjs") {
196
+ throw new Error(`Module '${file}' is not supported. Only ESM JavaScript (.js or .mjs) files are supported.`);
197
+ }
198
+ // import the module
199
+ const content = (await import(file)) || {};
200
+ if (typeof content === "object" && !!content) {
201
+ Object.keys(content).forEach(functionName => {
202
+ mikelInstance.addFunction(functionName, content[functionName]);
203
+ });
204
+ }
205
+ });
206
+ // process input files
207
+ for (let i = 0; i < inputs.length; i++) {
208
+ // const inputPath = path.resolve(process.cwd(), inputs[i]);
209
+ const inputPath = inputs[i]; // inputs contains absolute paths already
210
+ const relativeInputPath = path.relative(process.cwd(), inputPath);
211
+ if (!existsSync(inputPath)) {
212
+ throw new Error(`Template file '${inputPath}' was not found.`);
213
+ }
214
+ let template;
215
+ try {
216
+ template = await fs.readFile(inputPath, "utf8");
217
+ } catch (error) {
218
+ throw new Error(`Failed to read template file '${inputPath}': ${error.message}`);
219
+ }
220
+ // compile the template
221
+ let result;
222
+ try {
223
+ result = mikelInstance(template, data);
224
+ } catch (error) {
225
+ throw new Error(`Template compilation failed: ${error.message}`);
226
+ }
227
+ // check if output argument has been provided to write the result to a file
228
+ // this will also create any intermediary directory that does not exist
229
+ if (options.output || config.output) {
230
+ const outputPath = resolveOutput(relativeInputPath, options.output, config.output);
231
+ const outputDirectory = path.dirname(outputPath);
232
+ // make sure that any directory containing the output file exists
233
+ if (!existsSync(outputDirectory)) {
234
+ try {
235
+ await fs.mkdir(outputDirectory, { recursive: true });
236
+ } catch (error) {
237
+ throw new Error(`Failed to create output directory '${outputDirectory}': ${error.message}`);
238
+ }
239
+ }
240
+ try {
241
+ await fs.writeFile(outputPath, result, "utf8");
242
+ console.error(`✓ Saving '${inputPath}' -> '${outputPath}'`);
243
+ } catch (error) {
244
+ throw new Error(`Failed to write output file '${outputPath}': ${error.message}`);
245
+ }
246
+ }
247
+ // if no output file has been provided, print the result to console
248
+ else if (inputs.length === 1) {
249
+ process.stdout.write(result);
250
+ }
251
+ else {
252
+ throw new Error(`Unconsistent usage of input and output arguments.`);
253
+ }
254
+ }
255
+ // exit
256
+ process.exit(0);
257
+ };
258
+
259
+ // process arguments
260
+ const { positionals, values } = parseArgs({
261
+ options: {
262
+ config: {
263
+ type: "string",
264
+ short: "c",
265
+ },
266
+ data: {
267
+ type: "string",
268
+ short: "D",
269
+ },
270
+ output: {
271
+ type: "string",
272
+ short: "o",
273
+ },
274
+ helper: {
275
+ type: "string",
276
+ short: "H",
277
+ multiple: true,
278
+ },
279
+ function: {
280
+ type: "string",
281
+ short: "F",
282
+ multiple: true,
283
+ },
284
+ partial: {
285
+ type: "string",
286
+ short: "P",
287
+ multiple: true,
288
+ },
289
+ plugin: {
290
+ type: "string",
291
+ short: "L",
292
+ multiple: true,
293
+ },
294
+ help: {
295
+ type: "boolean",
296
+ short: "h",
297
+ },
298
+ },
299
+ allowPositionals: true,
300
+ });
301
+
302
+ // run main script
303
+ main(positionals[0], values).catch(error => {
304
+ console.error(`\n❌ Error: ${error.message}\n`);
305
+ process.exit(1);
306
+ });
package/index.js CHANGED
@@ -1,15 +1,10 @@
1
- #!/usr/bin/env node
2
-
3
1
  import fs from "node:fs/promises";
4
- import { existsSync } from "node:fs";
5
2
  import path from "node:path";
6
- import { parseArgs } from "node:util";
7
- import mikel from "mikel";
8
3
 
9
4
  // @description get the files that matches the provided patterns
10
5
  // this is a utility function to expand glob patterns to actual file paths.
11
6
  // it uses Node.js 24+ built-in fs.glob to handle glob patterns.
12
- const expandGlobPatterns = async (patterns = []) => {
7
+ export const expandGlobPatterns = async (patterns = []) => {
13
8
  const files = [];
14
9
  for (let i = 0; i < patterns.length; i++) {
15
10
  const pattern = patterns[i];
@@ -33,233 +28,16 @@ const expandGlobPatterns = async (patterns = []) => {
33
28
  });
34
29
  };
35
30
 
36
- // @description load JSON data from the provided path
37
- const loadData = async (file = null) => {
38
- if (!file) {
39
- return {};
40
- }
41
- // build the full data file path and check if exists
42
- const dataPath = path.resolve(process.cwd(), file);
43
- if (!existsSync(dataPath)) {
44
- throw new Error(`Data file '${dataPath}' was not found.`);
45
- }
46
- // read the file and parse it as JSON
47
- try {
48
- const content = await fs.readFile(dataPath, "utf8");
49
- return JSON.parse(content);
50
- } catch (error) {
51
- if (error instanceof SyntaxError) {
52
- throw new Error(`Invalid JSON in data file '${dataPath}': ${error.message}`);
53
- }
54
- throw new Error(`Failed to read data file '${dataPath}': ${error.message}`);
55
- }
56
- };
57
-
58
- // @description load javascript modules
59
- const loadModules = async (patterns = [], callback) => {
60
- const files = await expandGlobPatterns(patterns);
61
-
62
- for (let i = 0; i < files.length; i++) {
63
- const file = files[i]; // path.resolve(process.cwd(), uniqueFiles[i]);
64
- if (!existsSync(file)) {
65
- throw new Error(`File '${file}' was not found.`);
66
- }
67
-
68
- // Check if it's a file (not a directory)
69
- const stats = await fs.stat(file);
70
- if (!stats.isFile()) {
71
- continue; // Skip directories
72
- }
73
-
74
- // import the module
75
- try {
76
- await callback(file);
77
- } catch (error) {
78
- throw new Error(`Failed to load '${file}': ${error.message}`);
79
- }
80
- }
81
- };
82
-
83
- // print the help of the tool
84
- const printHelp = () => {
85
- console.log("Usage: ");
86
- console.log(" mikel --help");
87
- console.log(" mikel <template> [...options]");
88
- console.log("");
89
- console.log("Options:");
90
- console.log(" -h, --help Prints the usage information");
91
- console.log(" -P, --partial <file> Register a partial (supports glob patterns, can be used multiple times)");
92
- console.log(" -H, --helper <file> Register a helper (supports glob patterns, can be used multiple times)");
93
- console.log(" -F, --function <file> Register a function (supports glob patterns, can be used multiple times)");
94
- console.log(" -L, --plugin <file> Load a plugin from node_modules (can be used multiple times)");
95
- console.log(" -D, --data <file> Path to the data file to use (JSON)");
96
- console.log(" -o, --output <file> Output file");
97
- console.log("");
98
- console.log("Examples:");
99
- console.log(" mikel template.html --data data.json --output www/index.html");
100
- console.log(" mikel template.html --data data.json --partial header.html --partial footer.html --output www/index.html");
101
- console.log(" mikel template.html --helper helpers.js --function utils.js --output dist/index.html");
102
- console.log(" mikel template.html --partial 'partials/*.html' --helper 'helpers/*.js' --output dist/index.html");
103
- console.log(" mikel template.html --partial 'components/**/*.html' --output dist/index.html");
104
- console.log("");
105
- process.exit(0);
106
- };
107
-
108
- // @description main function
109
- const main = async (input = "", options = {}) => {
110
- // check to print help
111
- if (options.help) {
112
- return printHelp();
113
- }
114
-
115
- // make sure that input file exists
116
- if (!input) {
117
- throw new Error(`No input template file provided.`);
118
- }
119
- const inputPath = path.resolve(process.cwd(), input);
120
- if (!existsSync(inputPath)) {
121
- throw new Error(`Template file '${inputPath}' was not found.`);
122
- }
123
-
124
- let template;
125
- try {
126
- template = await fs.readFile(inputPath, "utf8");
127
- } catch (error) {
128
- throw new Error(`Failed to read template file '${inputPath}': ${error.message}`);
129
- }
130
-
131
- // initialize the template engine
132
- const mikelInstance = mikel.create({});
133
- const data = await loadData(options.data);
134
-
135
- // load plugins
136
- for (let i = 0; i < (options.plugin || []).length; i++) {
137
- const pluginName = options.plugin[i];
138
- let pluginModule;
139
- try {
140
- // try to import the plugin from node_modules
141
- pluginModule = (await import(pluginName))?.default;
142
- if (typeof pluginModule !== "function") {
143
- throw new Error(`Plugin '${pluginName}' does not export a valid plugin function.`);
144
- }
145
- mikelInstance.use(pluginModule());
146
- } catch (error) {
147
- throw new Error(`Failed to load plugin '${pluginName}': ${error.message}`);
148
- }
149
- }
150
-
151
- // load additional partials, helpers, and functions
152
- await loadModules(options.partial, async (file) => {
153
- try {
154
- mikelInstance.addPartial(path.basename(file), await fs.readFile(file, "utf8"));
155
- } catch (error) {
156
- throw new Error(`Failed to read partial file '${file}': ${error.message}`);
157
- }
158
- });
159
- await loadModules(options.helper, async (file) => {
160
- const extension = path.extname(file);
161
- if (extension !== ".js" && extension !== ".mjs") {
162
- throw new Error(`Module '${file}' is not supported. Only ESM JavaScript (.js or .mjs) files are supported.`);
163
- }
164
- // import the module
165
- const content = (await import(file)) || {};
166
- if (typeof content === "object" && !!content) {
167
- Object.keys(content).forEach(helperName => {
168
- mikelInstance.addHelper(helperName, content[helperName]);
169
- });
170
- }
171
- });
172
- await loadModules(options.function, async (file) => {
173
- const extension = path.extname(file);
174
- if (extension !== ".js" && extension !== ".mjs") {
175
- throw new Error(`Module '${file}' is not supported. Only ESM JavaScript (.js or .mjs) files are supported.`);
176
- }
177
- // import the module
178
- const content = (await import(file)) || {};
179
- if (typeof content === "object" && !!content) {
180
- Object.keys(content).forEach(functionName => {
181
- mikelInstance.addFunction(functionName, content[functionName]);
182
- });
183
- }
184
- });
185
-
186
- // compile the template
187
- let result;
188
- try {
189
- result = mikelInstance(template, data || {});
190
- } catch (error) {
191
- throw new Error(`Template compilation failed: ${error.message}`);
192
- }
193
-
194
- // check if output argument has been provided to write the result to a file
195
- // this will also create any intermediary directory that does not exist
196
- if (options.output) {
197
- const outputPath = path.resolve(process.cwd(), options.output);
198
- const outputDirectory = path.dirname(outputPath);
199
- // make sure that any directory containing the output file exists
200
- if (!existsSync(outputDirectory)) {
201
- try {
202
- await fs.mkdir(outputDirectory, { recursive: true });
203
- } catch (error) {
204
- throw new Error(`Failed to create output directory '${outputDirectory}': ${error.message}`);
205
- }
206
- }
207
- try {
208
- await fs.writeFile(outputPath, result, "utf8");
209
- console.error(`✓ Template rendered successfully to '${outputPath}'`);
210
- } catch (error) {
211
- throw new Error(`Failed to write output file '${outputPath}': ${error.message}`);
31
+ // @description apply a rename to the provided file path based on a rename configuration
32
+ // object
33
+ export const applyRename = (filePath, rename = {}) => {
34
+ const patterns = Object.keys(rename);
35
+ for (let i = 0; i < patterns.length; i++) {
36
+ const regex = new RegExp(patterns[i]);
37
+ if (regex.test(filePath)) {
38
+ return filePath.replace(regex, rename[patterns[i]]);
212
39
  }
213
- process.exit(0);
214
- }
215
- // if no output file has been provided, print the result to console
216
- else {
217
- process.stdout.write(result);
218
- process.exit(0);
219
40
  }
41
+ // fallback: only returns the basename of the file
42
+ return path.basename(filePath);
220
43
  };
221
-
222
- // process arguments
223
- const { positionals, values } = parseArgs({
224
- options: {
225
- data: {
226
- type: "string",
227
- short: "D",
228
- },
229
- output: {
230
- type: "string",
231
- short: "o",
232
- },
233
- helper: {
234
- type: "string",
235
- short: "H",
236
- multiple: true,
237
- },
238
- function: {
239
- type: "string",
240
- short: "F",
241
- multiple: true,
242
- },
243
- partial: {
244
- type: "string",
245
- short: "P",
246
- multiple: true,
247
- },
248
- plugin: {
249
- type: "string",
250
- short: "L",
251
- multiple: true,
252
- },
253
- help: {
254
- type: "boolean",
255
- short: "h",
256
- },
257
- },
258
- allowPositionals: true,
259
- });
260
-
261
- // run main script
262
- main(positionals[0], values).catch(error => {
263
- console.error(`\n❌ Error: ${error.message}\n`);
264
- process.exit(1);
265
- });
package/package.json CHANGED
@@ -1,31 +1,39 @@
1
1
  {
2
2
  "name": "mikel-cli",
3
3
  "description": "The cli tool for mikel templating.",
4
- "version": "0.33.0",
4
+ "version": "0.34.0",
5
5
  "type": "module",
6
6
  "license": "MIT",
7
7
  "author": {
8
8
  "name": "Josemi Juanes",
9
9
  "email": "hello@josemi.xyz"
10
10
  },
11
- "repository": "https://github.com/jmjuanes/mikel",
11
+ "repository": {
12
+ "url": "https://github.com/jmjuanes/mikel",
13
+ "directory": "packages/mikel-cli"
14
+ },
12
15
  "bugs": "https://github.com/jmjuanes/mikel/issues",
13
16
  "engines": {
14
17
  "node": ">=24.0.0"
15
18
  },
19
+ "scripts": {
20
+ "test": "node test.js"
21
+ },
16
22
  "exports": {
17
23
  ".": "./index.js",
18
24
  "./index.js": "./index.js",
25
+ "./cli.js": "./cli.js",
19
26
  "./package.json": "./package.json"
20
27
  },
21
28
  "bin": {
22
- "mikel": "index.js"
29
+ "mikel": "cli.js"
23
30
  },
24
31
  "peerDependencies": {
25
- "mikel": "^0.33.0"
32
+ "mikel": "^0.34.0"
26
33
  },
27
34
  "files": [
28
35
  "README.md",
36
+ "cli.js",
29
37
  "index.js"
30
38
  ],
31
39
  "keywords": [