js-template-engine 1.0.0 → 2.0.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/README.md +104 -157
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +10 -0
- package/dist/commands/render.d.ts +29 -0
- package/dist/commands/render.js +73 -0
- package/dist/commands/validate.d.ts +10 -0
- package/dist/commands/validate.js +42 -0
- package/dist/component-name.d.ts +15 -0
- package/dist/component-name.js +30 -0
- package/dist/extensions.d.ts +23 -0
- package/dist/extensions.js +38 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.js +29 -0
- package/dist/output.d.ts +11 -0
- package/dist/output.js +22 -0
- package/dist/program.d.ts +16 -0
- package/dist/program.js +74 -0
- package/dist/reporting.d.ts +15 -0
- package/dist/reporting.js +27 -0
- package/dist/template-sources.d.ts +22 -0
- package/dist/template-sources.js +71 -0
- package/package.json +47 -23
- package/.eslintrc.js +0 -26
- package/bin/cli.js +0 -129
- package/dist/bem.html +0 -12
- package/examples/bem.js +0 -81
- package/examples/react.js +0 -109
- package/examples/slots.js +0 -103
- package/src/engine/TemplateEngine.js +0 -249
- package/src/extensions/bem.js +0 -108
- package/src/extensions/react.js +0 -102
- package/src/handlers/FileHandler.js +0 -116
- package/src/helpers/createLogger.js +0 -19
- package/src/helpers/mergeNodeExtensionOptions.js +0 -8
- package/src/index.js +0 -12
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { type StylingName } from './extensions';
|
|
3
|
+
/**
|
|
4
|
+
* Parses the `--styling` value: a comma-separated list of styling
|
|
5
|
+
* extension names, kept in the order given.
|
|
6
|
+
*
|
|
7
|
+
* @param value - The raw flag value, e.g. `'bem,tailwind'`.
|
|
8
|
+
* @returns The styling extension names.
|
|
9
|
+
*/
|
|
10
|
+
export declare function parseStylingList(value: string): StylingName[];
|
|
11
|
+
/**
|
|
12
|
+
* Creates the `js-template-engine` command-line program.
|
|
13
|
+
*
|
|
14
|
+
* @returns The configured program; call `parseAsync` to run it.
|
|
15
|
+
*/
|
|
16
|
+
export declare function createProgram(): Command;
|
package/dist/program.js
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.parseStylingList = parseStylingList;
|
|
4
|
+
exports.createProgram = createProgram;
|
|
5
|
+
const node_fs_1 = require("node:fs");
|
|
6
|
+
const node_path_1 = require("node:path");
|
|
7
|
+
const commander_1 = require("commander");
|
|
8
|
+
const render_1 = require("./commands/render");
|
|
9
|
+
const validate_1 = require("./commands/validate");
|
|
10
|
+
const extensions_1 = require("./extensions");
|
|
11
|
+
const outputStrategies = ['inline', 'in-file', 'separate-file'];
|
|
12
|
+
/**
|
|
13
|
+
* Parses the `--styling` value: a comma-separated list of styling
|
|
14
|
+
* extension names, kept in the order given.
|
|
15
|
+
*
|
|
16
|
+
* @param value - The raw flag value, e.g. `'bem,tailwind'`.
|
|
17
|
+
* @returns The styling extension names.
|
|
18
|
+
*/
|
|
19
|
+
function parseStylingList(value) {
|
|
20
|
+
const names = value
|
|
21
|
+
.split(',')
|
|
22
|
+
.map((name) => name.trim())
|
|
23
|
+
.filter((name) => name.length > 0);
|
|
24
|
+
for (const name of names) {
|
|
25
|
+
if (!extensions_1.stylingNames.includes(name)) {
|
|
26
|
+
throw new commander_1.InvalidArgumentError(`Unknown styling extension '${name}'. Allowed choices are ${extensions_1.stylingNames.join(', ')}.`);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return names;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Creates the `js-template-engine` command-line program.
|
|
33
|
+
*
|
|
34
|
+
* @returns The configured program; call `parseAsync` to run it.
|
|
35
|
+
*/
|
|
36
|
+
function createProgram() {
|
|
37
|
+
const { version } = JSON.parse((0, node_fs_1.readFileSync)((0, node_path_1.join)(__dirname, '..', 'package.json'), 'utf8'));
|
|
38
|
+
const program = new commander_1.Command();
|
|
39
|
+
program
|
|
40
|
+
.name('js-template-engine')
|
|
41
|
+
.description('Render data-defined component templates to HTML, React, Vue, or Svelte')
|
|
42
|
+
.version(version);
|
|
43
|
+
program
|
|
44
|
+
.command('render')
|
|
45
|
+
.description('Render a template file, or every template in a directory, to output files')
|
|
46
|
+
.argument('<source>', 'template file or directory of template files')
|
|
47
|
+
.addOption(new commander_1.Option('--framework <name>', 'framework to render with; omitted renders HTML').choices(extensions_1.frameworkNames))
|
|
48
|
+
.option('--styling <names>', `comma-separated styling extensions (${extensions_1.stylingNames.join(', ')}), applied in order`, parseStylingList, [])
|
|
49
|
+
.option('-o, --output-directory <path>', 'directory generated files are written to', './output')
|
|
50
|
+
.option('-n, --component-name <name>', 'component name for templates that declare none; defaults to the source filename in PascalCase')
|
|
51
|
+
.addOption(new commander_1.Option('--styling-strategy <strategy>', 'style output strategy')
|
|
52
|
+
.choices(outputStrategies)
|
|
53
|
+
.default('in-file'))
|
|
54
|
+
.addOption(new commander_1.Option('--styling-language <language>', 'stylesheet output language')
|
|
55
|
+
.choices(['css', 'scss'])
|
|
56
|
+
.default('css'))
|
|
57
|
+
.addOption(new commander_1.Option('--scripting-strategy <strategy>', 'script output strategy')
|
|
58
|
+
.choices(outputStrategies)
|
|
59
|
+
.default('in-file'))
|
|
60
|
+
.addOption(new commander_1.Option('--scripting-language <language>', 'script output language')
|
|
61
|
+
.choices(['javascript', 'typescript'])
|
|
62
|
+
.default('javascript'))
|
|
63
|
+
.option('--bem-element-separator <separator>', "separator between BEM block and element (default '__')")
|
|
64
|
+
.option('--bem-modifier-separator <separator>', "separator before a BEM modifier (default '--')")
|
|
65
|
+
.addOption(new commander_1.Option('--tailwind-output <output>', "what the Tailwind extension contributes: utility classes, or styles converted from them (default 'classes')").choices(['classes', 'styles']))
|
|
66
|
+
.option('--tailwind-convert-styles', "convert each element's authored style into Tailwind utility classes")
|
|
67
|
+
.action(render_1.renderCommand);
|
|
68
|
+
program
|
|
69
|
+
.command('validate')
|
|
70
|
+
.description('Validate a template file, or every template in a directory')
|
|
71
|
+
.argument('<source>', 'template file or directory of template files')
|
|
72
|
+
.action(validate_1.validateCommand);
|
|
73
|
+
return program;
|
|
74
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The label a source file is reported under: its path relative to the
|
|
3
|
+
* working directory, falling back to the absolute path outside of it.
|
|
4
|
+
*
|
|
5
|
+
* @param filePath - Absolute path of the source file.
|
|
6
|
+
* @returns The display label.
|
|
7
|
+
*/
|
|
8
|
+
export declare function sourceLabel(filePath: string): string;
|
|
9
|
+
/**
|
|
10
|
+
* Formats a thrown value for the error stream.
|
|
11
|
+
*
|
|
12
|
+
* @param error - The thrown value.
|
|
13
|
+
* @returns Its message.
|
|
14
|
+
*/
|
|
15
|
+
export declare function formatError(error: unknown): string;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.sourceLabel = sourceLabel;
|
|
4
|
+
exports.formatError = formatError;
|
|
5
|
+
const node_path_1 = require("node:path");
|
|
6
|
+
/**
|
|
7
|
+
* The label a source file is reported under: its path relative to the
|
|
8
|
+
* working directory, falling back to the absolute path outside of it.
|
|
9
|
+
*
|
|
10
|
+
* @param filePath - Absolute path of the source file.
|
|
11
|
+
* @returns The display label.
|
|
12
|
+
*/
|
|
13
|
+
function sourceLabel(filePath) {
|
|
14
|
+
const relativePath = (0, node_path_1.relative)(process.cwd(), filePath);
|
|
15
|
+
return relativePath === '' || relativePath.startsWith('..')
|
|
16
|
+
? filePath
|
|
17
|
+
: relativePath;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Formats a thrown value for the error stream.
|
|
21
|
+
*
|
|
22
|
+
* @param error - The thrown value.
|
|
23
|
+
* @returns Its message.
|
|
24
|
+
*/
|
|
25
|
+
function formatError(error) {
|
|
26
|
+
return error instanceof Error ? error.message : String(error);
|
|
27
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { Template } from '@js-template-engine/types';
|
|
2
|
+
/**
|
|
3
|
+
* Resolves a source argument to the template files it names.
|
|
4
|
+
*
|
|
5
|
+
* A file path resolves to itself; a directory resolves to every template
|
|
6
|
+
* file directly inside it (`.json`, `.ts`, `.js`, `.mjs`, `.cjs`), sorted
|
|
7
|
+
* by name. Subdirectories are not searched.
|
|
8
|
+
*
|
|
9
|
+
* @param source - A template file or a directory of template files.
|
|
10
|
+
* @returns Absolute paths of the template files to process.
|
|
11
|
+
*/
|
|
12
|
+
export declare function resolveTemplateSources(source: string): string[];
|
|
13
|
+
/**
|
|
14
|
+
* Loads a template from a file.
|
|
15
|
+
*
|
|
16
|
+
* `.json` files are parsed as JSON. `.ts`, `.js`, `.mjs`, and `.cjs` files
|
|
17
|
+
* are loaded as modules and must default-export the template.
|
|
18
|
+
*
|
|
19
|
+
* @param filePath - Absolute path of the template file.
|
|
20
|
+
* @returns The template the file defines.
|
|
21
|
+
*/
|
|
22
|
+
export declare function loadTemplate(filePath: string): Promise<Template>;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.resolveTemplateSources = resolveTemplateSources;
|
|
4
|
+
exports.loadTemplate = loadTemplate;
|
|
5
|
+
const node_fs_1 = require("node:fs");
|
|
6
|
+
const node_path_1 = require("node:path");
|
|
7
|
+
const jiti_1 = require("jiti");
|
|
8
|
+
/** The file extensions recognized as template sources. */
|
|
9
|
+
const templateFileExtensions = ['.json', '.ts', '.js', '.mjs', '.cjs'];
|
|
10
|
+
const jiti = (0, jiti_1.createJiti)(__filename, { interopDefault: true });
|
|
11
|
+
/**
|
|
12
|
+
* Resolves a source argument to the template files it names.
|
|
13
|
+
*
|
|
14
|
+
* A file path resolves to itself; a directory resolves to every template
|
|
15
|
+
* file directly inside it (`.json`, `.ts`, `.js`, `.mjs`, `.cjs`), sorted
|
|
16
|
+
* by name. Subdirectories are not searched.
|
|
17
|
+
*
|
|
18
|
+
* @param source - A template file or a directory of template files.
|
|
19
|
+
* @returns Absolute paths of the template files to process.
|
|
20
|
+
*/
|
|
21
|
+
function resolveTemplateSources(source) {
|
|
22
|
+
const absolute = (0, node_path_1.resolve)(source);
|
|
23
|
+
if (!(0, node_fs_1.existsSync)(absolute)) {
|
|
24
|
+
throw new Error(`Template source '${source}' does not exist`);
|
|
25
|
+
}
|
|
26
|
+
if ((0, node_fs_1.statSync)(absolute).isFile()) {
|
|
27
|
+
return [absolute];
|
|
28
|
+
}
|
|
29
|
+
const files = (0, node_fs_1.readdirSync)(absolute, { withFileTypes: true })
|
|
30
|
+
.filter((entry) => entry.isFile() &&
|
|
31
|
+
templateFileExtensions.includes((0, node_path_1.extname)(entry.name)) &&
|
|
32
|
+
!entry.name.endsWith('.d.ts'))
|
|
33
|
+
.map((entry) => (0, node_path_1.join)(absolute, entry.name))
|
|
34
|
+
.sort();
|
|
35
|
+
if (files.length === 0) {
|
|
36
|
+
throw new Error(`No template files found in '${source}'`);
|
|
37
|
+
}
|
|
38
|
+
return files;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Loads a template from a file.
|
|
42
|
+
*
|
|
43
|
+
* `.json` files are parsed as JSON. `.ts`, `.js`, `.mjs`, and `.cjs` files
|
|
44
|
+
* are loaded as modules and must default-export the template.
|
|
45
|
+
*
|
|
46
|
+
* @param filePath - Absolute path of the template file.
|
|
47
|
+
* @returns The template the file defines.
|
|
48
|
+
*/
|
|
49
|
+
async function loadTemplate(filePath) {
|
|
50
|
+
if ((0, node_path_1.extname)(filePath) === '.json') {
|
|
51
|
+
return JSON.parse((0, node_fs_1.readFileSync)(filePath, 'utf8'));
|
|
52
|
+
}
|
|
53
|
+
const template = await jiti.import(filePath, { default: true });
|
|
54
|
+
if (!isTemplateShaped(template)) {
|
|
55
|
+
throw new Error(`'${filePath}' does not default-export a template`);
|
|
56
|
+
}
|
|
57
|
+
return template;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Whether a module's default export has a template's root shape — a node
|
|
61
|
+
* array or a node object. Modules without a default export resolve to
|
|
62
|
+
* their namespace object, which this rules out.
|
|
63
|
+
*/
|
|
64
|
+
function isTemplateShaped(value) {
|
|
65
|
+
if (Array.isArray(value)) {
|
|
66
|
+
return true;
|
|
67
|
+
}
|
|
68
|
+
return (typeof value === 'object' &&
|
|
69
|
+
value !== null &&
|
|
70
|
+
typeof value.type === 'string');
|
|
71
|
+
}
|
package/package.json
CHANGED
|
@@ -1,42 +1,66 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "js-template-engine",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "
|
|
5
|
-
"main": "index.js",
|
|
3
|
+
"version": "2.0.0",
|
|
4
|
+
"description": "Command-line interface for the JS Template Engine: render data-defined component templates to HTML, React, Vue, or Svelte",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"import": "./dist/index.js",
|
|
11
|
+
"require": "./dist/index.js"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"dist",
|
|
16
|
+
"README.md"
|
|
17
|
+
],
|
|
6
18
|
"bin": {
|
|
7
|
-
"js-template-engine": "./
|
|
19
|
+
"js-template-engine": "./dist/cli.js"
|
|
8
20
|
},
|
|
9
|
-
"
|
|
10
|
-
"
|
|
11
|
-
"
|
|
12
|
-
"
|
|
21
|
+
"dependencies": {
|
|
22
|
+
"commander": "^12.1.0",
|
|
23
|
+
"jiti": "^2.4.2",
|
|
24
|
+
"@js-template-engine/core": "^2.0.0",
|
|
25
|
+
"@js-template-engine/extension-bem": "^2.0.0",
|
|
26
|
+
"@js-template-engine/extension-react": "^2.0.0",
|
|
27
|
+
"@js-template-engine/extension-tailwind": "^2.0.0",
|
|
28
|
+
"@js-template-engine/extension-vue": "^2.0.0",
|
|
29
|
+
"@js-template-engine/extension-svelte": "^2.0.0",
|
|
30
|
+
"@js-template-engine/types": "^2.0.0"
|
|
13
31
|
},
|
|
14
32
|
"repository": {
|
|
15
33
|
"type": "git",
|
|
16
|
-
"url": "git+https://github.com/djurnamn/js-template-engine.git"
|
|
34
|
+
"url": "git+https://github.com/djurnamn/js-template-engine.git",
|
|
35
|
+
"directory": "packages/cli"
|
|
17
36
|
},
|
|
18
37
|
"keywords": [
|
|
19
|
-
"
|
|
20
|
-
"
|
|
38
|
+
"js-template-engine",
|
|
39
|
+
"cli",
|
|
21
40
|
"template",
|
|
22
|
-
"
|
|
23
|
-
"engine",
|
|
24
|
-
"json",
|
|
41
|
+
"code-generation",
|
|
25
42
|
"react",
|
|
26
|
-
"
|
|
43
|
+
"vue",
|
|
44
|
+
"svelte"
|
|
27
45
|
],
|
|
28
46
|
"author": "Björn Djurnamn <bjorn@djurnamn.co>",
|
|
29
47
|
"license": "MIT",
|
|
30
48
|
"bugs": {
|
|
31
49
|
"url": "https://github.com/djurnamn/js-template-engine/issues"
|
|
32
50
|
},
|
|
33
|
-
"homepage": "https://github.com/djurnamn/js-template-engine",
|
|
34
|
-
"dependencies": {
|
|
35
|
-
"signale": "^1.4.0",
|
|
36
|
-
"yargs": "^17.7.2"
|
|
37
|
-
},
|
|
51
|
+
"homepage": "https://github.com/djurnamn/js-template-engine/tree/main/packages/cli",
|
|
38
52
|
"devDependencies": {
|
|
39
|
-
"
|
|
40
|
-
"
|
|
53
|
+
"@types/node": "^20.0.0",
|
|
54
|
+
"rimraf": "^5.0.5",
|
|
55
|
+
"typescript": "^5.3.3",
|
|
56
|
+
"vitest": "^1.6.1"
|
|
57
|
+
},
|
|
58
|
+
"scripts": {
|
|
59
|
+
"build": "tsc",
|
|
60
|
+
"build:watch": "tsc --watch",
|
|
61
|
+
"clean": "rimraf dist",
|
|
62
|
+
"test": "vitest run",
|
|
63
|
+
"test:watch": "vitest",
|
|
64
|
+
"type-check": "tsc --noEmit -p tsconfig.type-check.json"
|
|
41
65
|
}
|
|
42
|
-
}
|
|
66
|
+
}
|
package/.eslintrc.js
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
module.exports = {
|
|
2
|
-
env: {
|
|
3
|
-
node: true,
|
|
4
|
-
browser: true,
|
|
5
|
-
commonjs: true,
|
|
6
|
-
es2021: true,
|
|
7
|
-
},
|
|
8
|
-
extends: "eslint:recommended",
|
|
9
|
-
overrides: [
|
|
10
|
-
{
|
|
11
|
-
env: {
|
|
12
|
-
node: true,
|
|
13
|
-
},
|
|
14
|
-
files: [".eslintrc.{js,cjs}"],
|
|
15
|
-
parserOptions: {
|
|
16
|
-
sourceType: "script",
|
|
17
|
-
},
|
|
18
|
-
},
|
|
19
|
-
],
|
|
20
|
-
parserOptions: {
|
|
21
|
-
ecmaVersion: "latest",
|
|
22
|
-
},
|
|
23
|
-
rules: {
|
|
24
|
-
"no-unused-vars": ["error", { argsIgnorePattern: "^_" }],
|
|
25
|
-
},
|
|
26
|
-
};
|
package/bin/cli.js
DELETED
|
@@ -1,129 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
const fs = require("fs");
|
|
4
|
-
const path = require("path");
|
|
5
|
-
const process = require("process");
|
|
6
|
-
const yargs = require("yargs/yargs");
|
|
7
|
-
const { hideBin } = require("yargs/helpers");
|
|
8
|
-
const TemplateEngine = require("../src/engine/TemplateEngine");
|
|
9
|
-
const {
|
|
10
|
-
getSourcePathType,
|
|
11
|
-
processFile,
|
|
12
|
-
processDirectory,
|
|
13
|
-
} = require("../src/handlers/FileHandler");
|
|
14
|
-
const createLogger = require("../src/helpers/createLogger");
|
|
15
|
-
|
|
16
|
-
function loadExtensions() {
|
|
17
|
-
const extensions = {};
|
|
18
|
-
const fullPath = path.join(__dirname, "..", "src", "extensions");
|
|
19
|
-
|
|
20
|
-
fs.readdirSync(fullPath).forEach((file) => {
|
|
21
|
-
if (path.extname(file) !== ".js") return;
|
|
22
|
-
|
|
23
|
-
const extName = path.basename(file, ".js");
|
|
24
|
-
const ExtensionClass = require(path.join(fullPath, file));
|
|
25
|
-
extensions[extName] = ExtensionClass;
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
return extensions;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
const availableExtensions = loadExtensions();
|
|
32
|
-
|
|
33
|
-
// Utility function for error handling and extension validation
|
|
34
|
-
function validateExtensions(requestedExtensions, availableExtensions) {
|
|
35
|
-
const missingExtensions = requestedExtensions.filter(
|
|
36
|
-
(ext) => !availableExtensions[ext]
|
|
37
|
-
);
|
|
38
|
-
if (missingExtensions.length > 0) {
|
|
39
|
-
console.error(
|
|
40
|
-
"One or more specified extensions could not be loaded:",
|
|
41
|
-
missingExtensions.join(", ")
|
|
42
|
-
);
|
|
43
|
-
return false;
|
|
44
|
-
}
|
|
45
|
-
return true;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
yargs(hideBin(process.argv))
|
|
49
|
-
.command(
|
|
50
|
-
"render <sourcePath> [options]",
|
|
51
|
-
"Render templates from JSON to HTML/JSX",
|
|
52
|
-
(yargs) => {
|
|
53
|
-
yargs
|
|
54
|
-
.positional("sourcePath", {
|
|
55
|
-
describe: "Source file or directory containing JSON templates",
|
|
56
|
-
type: "string",
|
|
57
|
-
})
|
|
58
|
-
.option("outputDir", {
|
|
59
|
-
alias: "o",
|
|
60
|
-
describe: "Output directory for rendered templates",
|
|
61
|
-
type: "string",
|
|
62
|
-
})
|
|
63
|
-
.option("extensions", {
|
|
64
|
-
alias: "e",
|
|
65
|
-
describe: "Extensions to use for template processing",
|
|
66
|
-
type: "array",
|
|
67
|
-
default: [],
|
|
68
|
-
})
|
|
69
|
-
.option("name", {
|
|
70
|
-
alias: "n",
|
|
71
|
-
describe: "Base name for output files",
|
|
72
|
-
type: "string",
|
|
73
|
-
})
|
|
74
|
-
.option("componentName", {
|
|
75
|
-
alias: "c",
|
|
76
|
-
describe: "Component name for framework-specific templates",
|
|
77
|
-
type: "string",
|
|
78
|
-
})
|
|
79
|
-
.option("verbose", {
|
|
80
|
-
alias: "v",
|
|
81
|
-
type: "boolean",
|
|
82
|
-
description: "Run with verbose logging",
|
|
83
|
-
});
|
|
84
|
-
},
|
|
85
|
-
async (argv) => {
|
|
86
|
-
const { verbose } = argv;
|
|
87
|
-
const logger = createLogger(verbose, "cli");
|
|
88
|
-
logger.info("Starting template rendering process...");
|
|
89
|
-
|
|
90
|
-
// Validate requested extensions before attempting to use them
|
|
91
|
-
if (!validateExtensions(argv.extensions, availableExtensions)) {
|
|
92
|
-
// Exit if validation fails
|
|
93
|
-
return;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
// Instantiate extensions with verbosity
|
|
97
|
-
const extensions = argv.extensions.map(
|
|
98
|
-
(extension) => new availableExtensions[extension](verbose)
|
|
99
|
-
);
|
|
100
|
-
|
|
101
|
-
const { name, componentName } = argv;
|
|
102
|
-
const sourcePath = path.join(process.cwd(), argv.sourcePath);
|
|
103
|
-
const outputDir = argv.outputDir ?? "";
|
|
104
|
-
const sourcePathType = await getSourcePathType(sourcePath);
|
|
105
|
-
const templateEngine = new TemplateEngine();
|
|
106
|
-
|
|
107
|
-
if (sourcePathType === "directory") {
|
|
108
|
-
await processDirectory(
|
|
109
|
-
sourcePath,
|
|
110
|
-
outputDir,
|
|
111
|
-
extensions,
|
|
112
|
-
templateEngine,
|
|
113
|
-
verbose
|
|
114
|
-
);
|
|
115
|
-
} else if (sourcePathType === "file") {
|
|
116
|
-
await processFile(
|
|
117
|
-
sourcePath,
|
|
118
|
-
outputDir,
|
|
119
|
-
extensions,
|
|
120
|
-
templateEngine,
|
|
121
|
-
name,
|
|
122
|
-
componentName,
|
|
123
|
-
verbose
|
|
124
|
-
);
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
)
|
|
128
|
-
.demandCommand(1, "You need at least one command before moving on")
|
|
129
|
-
.help().argv;
|
package/dist/bem.html
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
<nav class="nav">
|
|
2
|
-
<ul class="nav__list">
|
|
3
|
-
<li class="nav__item nav__item--active">
|
|
4
|
-
<a href="/" class="nav__link"><span class="nav__link-text">Home</span></a>
|
|
5
|
-
</li>
|
|
6
|
-
<li class="nav__item">
|
|
7
|
-
<a href="/about" class="nav__link"
|
|
8
|
-
><span class="nav__link-text">About</span></a
|
|
9
|
-
>
|
|
10
|
-
</li>
|
|
11
|
-
</ul>
|
|
12
|
-
</nav>
|
package/examples/bem.js
DELETED
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
// Example of using the TemplateEngine class to render a simple HTML template with a class naming convention using the BEM extension.
|
|
2
|
-
// You can run this example with `npm run example:slots` or `yarn example:slots`.
|
|
3
|
-
|
|
4
|
-
const { TemplateEngine, BemExtension } = require("../src");
|
|
5
|
-
const verbose = true;
|
|
6
|
-
|
|
7
|
-
const templateEngine = new TemplateEngine();
|
|
8
|
-
const bemExtension = new BemExtension(verbose);
|
|
9
|
-
|
|
10
|
-
const bem = bemExtension.setNodeExtensionOptionsShortcut;
|
|
11
|
-
|
|
12
|
-
/*
|
|
13
|
-
Using the BEM extension options shortcut allows us to write:
|
|
14
|
-
|
|
15
|
-
{
|
|
16
|
-
tag: "nav",
|
|
17
|
-
...bem({ block: "nav" }),
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
instead of:
|
|
21
|
-
|
|
22
|
-
{
|
|
23
|
-
tag: "nav",
|
|
24
|
-
extensions: {
|
|
25
|
-
bem: {
|
|
26
|
-
block: "nav",
|
|
27
|
-
},
|
|
28
|
-
},
|
|
29
|
-
}
|
|
30
|
-
*/
|
|
31
|
-
|
|
32
|
-
// Data
|
|
33
|
-
const navigationItems = [
|
|
34
|
-
{ name: "Home", url: "/", iconName: "home" },
|
|
35
|
-
{ name: "About", url: "/about", iconName: "about" },
|
|
36
|
-
];
|
|
37
|
-
|
|
38
|
-
// Templates
|
|
39
|
-
const navigationTemplate = [
|
|
40
|
-
{
|
|
41
|
-
tag: "nav",
|
|
42
|
-
...bem({ block: "nav" }),
|
|
43
|
-
children: [
|
|
44
|
-
{
|
|
45
|
-
tag: "ul",
|
|
46
|
-
...bem({ element: "list" }),
|
|
47
|
-
children: navigationItems.map((item, itemIndex) => ({
|
|
48
|
-
tag: "li",
|
|
49
|
-
...bem({
|
|
50
|
-
element: "item",
|
|
51
|
-
modifiers: itemIndex === 0 ? ["active"] : [],
|
|
52
|
-
}),
|
|
53
|
-
children: [
|
|
54
|
-
{
|
|
55
|
-
tag: "a",
|
|
56
|
-
...bem({ element: "link" }),
|
|
57
|
-
attributes: { href: item.url },
|
|
58
|
-
children: [
|
|
59
|
-
{
|
|
60
|
-
tag: "span",
|
|
61
|
-
...bem({ element: "link-text" }),
|
|
62
|
-
children: [{ type: "text", content: item.name }],
|
|
63
|
-
},
|
|
64
|
-
],
|
|
65
|
-
},
|
|
66
|
-
],
|
|
67
|
-
})),
|
|
68
|
-
},
|
|
69
|
-
],
|
|
70
|
-
},
|
|
71
|
-
];
|
|
72
|
-
|
|
73
|
-
// Render
|
|
74
|
-
(async () => {
|
|
75
|
-
await templateEngine.render(navigationTemplate, {
|
|
76
|
-
name: "bem",
|
|
77
|
-
extensions: [bemExtension], // Only BEM extension is needed for this example
|
|
78
|
-
writeOutputFile: true,
|
|
79
|
-
verbose,
|
|
80
|
-
});
|
|
81
|
-
})();
|
package/examples/react.js
DELETED
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
// Example of using the TemplateEngine class to render a simple JSX component with using the React extension.
|
|
2
|
-
// You can run this example with `npm run example:react` or `yarn example:react`.
|
|
3
|
-
|
|
4
|
-
const { TemplateEngine, ReactExtension } = require("../src");
|
|
5
|
-
const verbose = true;
|
|
6
|
-
|
|
7
|
-
const templateEngine = new TemplateEngine();
|
|
8
|
-
const reactExtension = new ReactExtension(verbose);
|
|
9
|
-
|
|
10
|
-
const initialTodos = [
|
|
11
|
-
{ id: 1, text: "Learn JavaScript", completed: false },
|
|
12
|
-
{ id: 2, text: "Build a todo app", completed: false },
|
|
13
|
-
];
|
|
14
|
-
|
|
15
|
-
const todoAppTemplate = [
|
|
16
|
-
{
|
|
17
|
-
tag: "div",
|
|
18
|
-
attributes: { class: "todo-app" },
|
|
19
|
-
children: [
|
|
20
|
-
{
|
|
21
|
-
tag: "input",
|
|
22
|
-
attributes: {
|
|
23
|
-
type: "text",
|
|
24
|
-
id: "todoInput",
|
|
25
|
-
placeholder: "Add a new todo",
|
|
26
|
-
},
|
|
27
|
-
},
|
|
28
|
-
{
|
|
29
|
-
tag: "button",
|
|
30
|
-
attributes: {
|
|
31
|
-
onclick: "handleAddTodo",
|
|
32
|
-
},
|
|
33
|
-
children: [{ type: "text", content: "Add" }],
|
|
34
|
-
extensions: {
|
|
35
|
-
react: {
|
|
36
|
-
tag: "DefaultButton",
|
|
37
|
-
attributes: {
|
|
38
|
-
color: "primary",
|
|
39
|
-
label: "Add",
|
|
40
|
-
},
|
|
41
|
-
expressionAttributes: {
|
|
42
|
-
onClick: "handleAddTodo",
|
|
43
|
-
},
|
|
44
|
-
},
|
|
45
|
-
},
|
|
46
|
-
},
|
|
47
|
-
{
|
|
48
|
-
tag: "ul",
|
|
49
|
-
attributes: { id: "todoList" },
|
|
50
|
-
children: initialTodos.map((todo) => ({
|
|
51
|
-
tag: "li",
|
|
52
|
-
children: [{ type: "text", content: todo.text }],
|
|
53
|
-
attributes: {
|
|
54
|
-
onclick: "this.parentNode.removeChild(this);",
|
|
55
|
-
},
|
|
56
|
-
extensions: {
|
|
57
|
-
react: {
|
|
58
|
-
tag: "TodoCard",
|
|
59
|
-
expressionAttributes: {
|
|
60
|
-
onClick: "() => handleRemoveTodo()",
|
|
61
|
-
},
|
|
62
|
-
},
|
|
63
|
-
},
|
|
64
|
-
})),
|
|
65
|
-
},
|
|
66
|
-
{
|
|
67
|
-
tag: "script",
|
|
68
|
-
children: [
|
|
69
|
-
{
|
|
70
|
-
type: "text",
|
|
71
|
-
content: `
|
|
72
|
-
function handleAddTodo() {
|
|
73
|
-
const todoList = document.getElementById('todoList');
|
|
74
|
-
const newTodoText = document.getElementById('todoInput').value;
|
|
75
|
-
const newTodoItem = document.createElement('li');
|
|
76
|
-
|
|
77
|
-
newTodoItem.textContent = newTodoText;
|
|
78
|
-
todoList.appendChild(newTodoItem);
|
|
79
|
-
|
|
80
|
-
document.getElementById('todoInput').value = ''; // Clear the input field
|
|
81
|
-
}
|
|
82
|
-
`,
|
|
83
|
-
},
|
|
84
|
-
],
|
|
85
|
-
extensions: {
|
|
86
|
-
react: {
|
|
87
|
-
ignore: true,
|
|
88
|
-
},
|
|
89
|
-
},
|
|
90
|
-
},
|
|
91
|
-
],
|
|
92
|
-
},
|
|
93
|
-
];
|
|
94
|
-
|
|
95
|
-
// Render
|
|
96
|
-
(async () => {
|
|
97
|
-
await templateEngine.render(todoAppTemplate, {
|
|
98
|
-
name: "html-todo-app",
|
|
99
|
-
writeOutputFile: true,
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
await templateEngine.render(todoAppTemplate, {
|
|
103
|
-
name: "react-todo-app",
|
|
104
|
-
extensions: [reactExtension], // Only React extension is needed for this example
|
|
105
|
-
outputDir: "dist", // React extension defaults to "dist/react"
|
|
106
|
-
writeOutputFile: true,
|
|
107
|
-
verbose,
|
|
108
|
-
});
|
|
109
|
-
})();
|