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
package/examples/slots.js
DELETED
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
// Example of using the TemplateEngine class to render a simple HTML template with slots.
|
|
2
|
-
// You can run this example with `npm run example:slots` or `yarn example:slots`.
|
|
3
|
-
|
|
4
|
-
const { TemplateEngine } = require("../src");
|
|
5
|
-
const verbose = true;
|
|
6
|
-
|
|
7
|
-
const templateEngine = new TemplateEngine();
|
|
8
|
-
|
|
9
|
-
// Data
|
|
10
|
-
const stylesheetEntries = [
|
|
11
|
-
{ href: "style.css", rel: "stylesheet" },
|
|
12
|
-
{ href: "theme.css", rel: "stylesheet" },
|
|
13
|
-
];
|
|
14
|
-
|
|
15
|
-
const scriptEntries = [
|
|
16
|
-
{ src: "app.js", defer: true },
|
|
17
|
-
{ src: "analytics.js", async: true },
|
|
18
|
-
];
|
|
19
|
-
|
|
20
|
-
// Templates
|
|
21
|
-
const htmlTemplate = [
|
|
22
|
-
{
|
|
23
|
-
tag: "html",
|
|
24
|
-
attributes: {
|
|
25
|
-
lang: "en",
|
|
26
|
-
},
|
|
27
|
-
children: [
|
|
28
|
-
{
|
|
29
|
-
tag: "head",
|
|
30
|
-
children: [
|
|
31
|
-
{
|
|
32
|
-
tag: "meta",
|
|
33
|
-
attributes: {
|
|
34
|
-
charset: "UTF-8",
|
|
35
|
-
},
|
|
36
|
-
},
|
|
37
|
-
{
|
|
38
|
-
tag: "title",
|
|
39
|
-
children: [
|
|
40
|
-
{
|
|
41
|
-
type: "text",
|
|
42
|
-
content: "Hello, World!",
|
|
43
|
-
},
|
|
44
|
-
],
|
|
45
|
-
},
|
|
46
|
-
{
|
|
47
|
-
type: "slot",
|
|
48
|
-
name: "head-end",
|
|
49
|
-
},
|
|
50
|
-
],
|
|
51
|
-
},
|
|
52
|
-
{
|
|
53
|
-
tag: "body",
|
|
54
|
-
children: [
|
|
55
|
-
{
|
|
56
|
-
type: "slot",
|
|
57
|
-
name: "body-beginning",
|
|
58
|
-
},
|
|
59
|
-
{
|
|
60
|
-
tag: "h1",
|
|
61
|
-
attributes: {
|
|
62
|
-
class: "title",
|
|
63
|
-
},
|
|
64
|
-
children: [
|
|
65
|
-
{
|
|
66
|
-
type: "text",
|
|
67
|
-
content: "Hello, World!",
|
|
68
|
-
},
|
|
69
|
-
],
|
|
70
|
-
},
|
|
71
|
-
{
|
|
72
|
-
type: "slot",
|
|
73
|
-
name: "body-end",
|
|
74
|
-
},
|
|
75
|
-
],
|
|
76
|
-
},
|
|
77
|
-
],
|
|
78
|
-
},
|
|
79
|
-
];
|
|
80
|
-
|
|
81
|
-
// Template formatting functions
|
|
82
|
-
const stylesheets = stylesheetEntries.map((stylesheetEntry) => ({
|
|
83
|
-
tag: "link",
|
|
84
|
-
attributes: { ...stylesheetEntry },
|
|
85
|
-
}));
|
|
86
|
-
|
|
87
|
-
const scripts = scriptEntries.map((script) => ({
|
|
88
|
-
tag: "script",
|
|
89
|
-
attributes: { ...script },
|
|
90
|
-
}));
|
|
91
|
-
|
|
92
|
-
// Render
|
|
93
|
-
(async () => {
|
|
94
|
-
await templateEngine.render(htmlTemplate, {
|
|
95
|
-
name: "slots",
|
|
96
|
-
slots: {
|
|
97
|
-
"head-end": stylesheets,
|
|
98
|
-
"body-end": scripts,
|
|
99
|
-
},
|
|
100
|
-
writeOutputFile: true,
|
|
101
|
-
verbose,
|
|
102
|
-
});
|
|
103
|
-
})();
|
|
@@ -1,249 +0,0 @@
|
|
|
1
|
-
const path = require("path");
|
|
2
|
-
const prettier = require("prettier");
|
|
3
|
-
const { writeOutputFile } = require("../handlers/FileHandler");
|
|
4
|
-
const createLogger = require("../helpers/createLogger");
|
|
5
|
-
|
|
6
|
-
const selfClosingTags = [
|
|
7
|
-
"area",
|
|
8
|
-
"base",
|
|
9
|
-
"br",
|
|
10
|
-
"col",
|
|
11
|
-
"command",
|
|
12
|
-
"embed",
|
|
13
|
-
"hr",
|
|
14
|
-
"img",
|
|
15
|
-
"input",
|
|
16
|
-
"keygen",
|
|
17
|
-
"link",
|
|
18
|
-
"meta",
|
|
19
|
-
"param",
|
|
20
|
-
"source",
|
|
21
|
-
"track",
|
|
22
|
-
"wbr",
|
|
23
|
-
];
|
|
24
|
-
|
|
25
|
-
class TemplateEngine {
|
|
26
|
-
constructor() {}
|
|
27
|
-
|
|
28
|
-
mergeOptions = (options) => {
|
|
29
|
-
let defaultOptions = {
|
|
30
|
-
attributeFormatter: (attribute, value) => ` ${attribute}="${value}"`,
|
|
31
|
-
fileExtension: ".html",
|
|
32
|
-
filename: options.name ?? "untitled",
|
|
33
|
-
outputDir: "dist",
|
|
34
|
-
preferSelfClosingTags: false,
|
|
35
|
-
prettierParser: "html",
|
|
36
|
-
writeOutputFile: false,
|
|
37
|
-
verbose: false,
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
if (options.extensions) {
|
|
41
|
-
options.extensions.forEach((extension) => {
|
|
42
|
-
if (extension.optionsHandler) {
|
|
43
|
-
defaultOptions = extension.optionsHandler(defaultOptions, options);
|
|
44
|
-
}
|
|
45
|
-
});
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
return { ...defaultOptions, ...options };
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
applyExtensionOverrides = (node, currentExtensionKey) => {
|
|
52
|
-
if (node.extensions && node.extensions[currentExtensionKey]) {
|
|
53
|
-
const extensionOverrides = node.extensions[currentExtensionKey];
|
|
54
|
-
|
|
55
|
-
Object.keys(extensionOverrides).forEach((key) => {
|
|
56
|
-
if (key === "ignore") {
|
|
57
|
-
// special handling for 'ignore' or other meta properties if necessary
|
|
58
|
-
} else {
|
|
59
|
-
node[key] = extensionOverrides[key];
|
|
60
|
-
}
|
|
61
|
-
});
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
return node;
|
|
65
|
-
};
|
|
66
|
-
|
|
67
|
-
render = async (
|
|
68
|
-
nodes,
|
|
69
|
-
options = {},
|
|
70
|
-
isRoot = true,
|
|
71
|
-
ancestorNodesContext = []
|
|
72
|
-
) => {
|
|
73
|
-
options = isRoot ? this.mergeOptions(options) : options;
|
|
74
|
-
let template = "";
|
|
75
|
-
|
|
76
|
-
const { verbose } = options;
|
|
77
|
-
const logger = createLogger(verbose, "render");
|
|
78
|
-
|
|
79
|
-
if (isRoot) {
|
|
80
|
-
logger.info("Starting template rendering process...");
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
for (let node of nodes) {
|
|
84
|
-
const currentNodeContext = [...ancestorNodesContext, node];
|
|
85
|
-
|
|
86
|
-
let shouldIgnoreNode = false;
|
|
87
|
-
|
|
88
|
-
if (options.extensions && node.extensions) {
|
|
89
|
-
logger.info(`Processing extensions for node: ${node.tag || "text"}`);
|
|
90
|
-
for (const extension of options.extensions) {
|
|
91
|
-
const currentExtensionKey = extension.key;
|
|
92
|
-
|
|
93
|
-
if (node.extensions[currentExtensionKey]) {
|
|
94
|
-
logger.info(
|
|
95
|
-
`Applying overrides from extension: ${currentExtensionKey}`
|
|
96
|
-
);
|
|
97
|
-
node = this.applyExtensionOverrides(node, currentExtensionKey);
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
if (node.extensions[currentExtensionKey]?.ignore) {
|
|
101
|
-
logger.info(`Node ignored by extension: ${currentExtensionKey}`);
|
|
102
|
-
shouldIgnoreNode = true;
|
|
103
|
-
break;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
if (!shouldIgnoreNode) {
|
|
107
|
-
logger.info(
|
|
108
|
-
`Calling nodeHandler from extension: ${currentExtensionKey}`
|
|
109
|
-
);
|
|
110
|
-
node = extension.nodeHandler(node, ancestorNodesContext);
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
if (shouldIgnoreNode) {
|
|
116
|
-
logger.info(`Node ignored: ${node.tag || "text"}. Skipping rendering.`);
|
|
117
|
-
continue; // Skip rendering this node
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
// Constructing the template string for this node
|
|
121
|
-
logger.info(`Rendering node: ${node.tag || "text"}`);
|
|
122
|
-
|
|
123
|
-
if (node.tag) {
|
|
124
|
-
const isSelfClosing =
|
|
125
|
-
(node.selfClosing ||
|
|
126
|
-
options.preferSelfClosingTags ||
|
|
127
|
-
selfClosingTags.includes(node.tag)) &&
|
|
128
|
-
!node.children;
|
|
129
|
-
|
|
130
|
-
if (isSelfClosing) {
|
|
131
|
-
template += `<${node.tag}`;
|
|
132
|
-
|
|
133
|
-
if (node.attributes || node.expressionAttributes) {
|
|
134
|
-
logger.info(`Processing attributes for node: ${node.tag}`);
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
if (node.attributes) {
|
|
138
|
-
for (const attribute in node.attributes) {
|
|
139
|
-
template += options.attributeFormatter(
|
|
140
|
-
attribute,
|
|
141
|
-
node.attributes[attribute],
|
|
142
|
-
false // indicating this is a standard attribute, not an expression
|
|
143
|
-
);
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
if (node.expressionAttributes) {
|
|
147
|
-
for (const attribute in node.expressionAttributes) {
|
|
148
|
-
template += options.attributeFormatter(
|
|
149
|
-
attribute,
|
|
150
|
-
node.expressionAttributes[attribute],
|
|
151
|
-
true // indicating this is an expression attribute
|
|
152
|
-
);
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
template += " />";
|
|
156
|
-
} else {
|
|
157
|
-
template += `<${node.tag}`;
|
|
158
|
-
if (node.attributes) {
|
|
159
|
-
for (const attribute in node.attributes) {
|
|
160
|
-
template += options.attributeFormatter(
|
|
161
|
-
attribute,
|
|
162
|
-
node.attributes[attribute],
|
|
163
|
-
false // standard attribute
|
|
164
|
-
);
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
if (node.expressionAttributes) {
|
|
168
|
-
for (const attribute in node.expressionAttributes) {
|
|
169
|
-
template += options.attributeFormatter(
|
|
170
|
-
attribute,
|
|
171
|
-
node.expressionAttributes[attribute],
|
|
172
|
-
true // expression attribute
|
|
173
|
-
);
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
template += ">";
|
|
177
|
-
|
|
178
|
-
if (node.children) {
|
|
179
|
-
logger.info(`Rendering children for node: ${node.tag}`);
|
|
180
|
-
template += await this.render(
|
|
181
|
-
node.children,
|
|
182
|
-
options,
|
|
183
|
-
false,
|
|
184
|
-
currentNodeContext
|
|
185
|
-
);
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
template += `</${node.tag}>`;
|
|
189
|
-
}
|
|
190
|
-
} else if (node.type === "text") {
|
|
191
|
-
// Direct text node handling
|
|
192
|
-
logger.info(`Adding text content: "${node.content}"`);
|
|
193
|
-
template += node.content;
|
|
194
|
-
} else if (
|
|
195
|
-
node.type === "slot" &&
|
|
196
|
-
node.name &&
|
|
197
|
-
options.slots &&
|
|
198
|
-
options.slots[node.name]
|
|
199
|
-
) {
|
|
200
|
-
logger.info(`Processing slot: ${node.name}`);
|
|
201
|
-
template += await this.render(
|
|
202
|
-
options.slots[node.name],
|
|
203
|
-
options,
|
|
204
|
-
false,
|
|
205
|
-
currentNodeContext
|
|
206
|
-
);
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
if (isRoot) {
|
|
211
|
-
logger.info("Finalizing template rendering...");
|
|
212
|
-
if (options.extensions) {
|
|
213
|
-
for (const extension of options.extensions) {
|
|
214
|
-
if (extension.rootHandler) {
|
|
215
|
-
template = extension.rootHandler(template, options);
|
|
216
|
-
break;
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
template = await prettier.format(template, {
|
|
222
|
-
parser: options.prettierParser,
|
|
223
|
-
});
|
|
224
|
-
|
|
225
|
-
const outputDir = path.join(
|
|
226
|
-
process.cwd(),
|
|
227
|
-
options.outputDir ? options.outputDir : "dist"
|
|
228
|
-
);
|
|
229
|
-
|
|
230
|
-
const outputPath = path.join(
|
|
231
|
-
outputDir,
|
|
232
|
-
`${options.filename}${options.fileExtension}`
|
|
233
|
-
);
|
|
234
|
-
|
|
235
|
-
if (options.writeOutputFile) {
|
|
236
|
-
writeOutputFile(template, outputPath, verbose);
|
|
237
|
-
logger.success(
|
|
238
|
-
`Template rendering complete. Output saved to: ${outputPath}`
|
|
239
|
-
);
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
return template;
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
return template;
|
|
246
|
-
};
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
module.exports = TemplateEngine;
|
package/src/extensions/bem.js
DELETED
|
@@ -1,108 +0,0 @@
|
|
|
1
|
-
const createLogger = require("../helpers/createLogger");
|
|
2
|
-
|
|
3
|
-
class BemExtension {
|
|
4
|
-
constructor(verbose = false) {
|
|
5
|
-
this.key = "bem";
|
|
6
|
-
this.logger = createLogger(verbose, "BemExtension");
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
setNodeExtensionOptionsShortcut = ({
|
|
10
|
-
block,
|
|
11
|
-
element,
|
|
12
|
-
modifiers,
|
|
13
|
-
modifier,
|
|
14
|
-
}) =>
|
|
15
|
-
block || element || modifiers || modifier
|
|
16
|
-
? {
|
|
17
|
-
extensions: {
|
|
18
|
-
bem: {
|
|
19
|
-
...(block ? { block } : {}),
|
|
20
|
-
...(element ? { element } : {}),
|
|
21
|
-
...(modifiers ? { modifiers } : {}),
|
|
22
|
-
...(modifier ? { modifier } : {}),
|
|
23
|
-
},
|
|
24
|
-
},
|
|
25
|
-
}
|
|
26
|
-
: {};
|
|
27
|
-
|
|
28
|
-
nodeHandler = (node, ancestorNodesContext) => {
|
|
29
|
-
if (node.ignoreBem) {
|
|
30
|
-
this.logger.info(
|
|
31
|
-
`Node ignored due to ignoreBem flag: ${JSON.stringify(node)}`
|
|
32
|
-
);
|
|
33
|
-
return node;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
this.logger.info(`Processing node: ${node.tag}`);
|
|
37
|
-
|
|
38
|
-
// use ancestorNodesContext to find the closest node where block is defined
|
|
39
|
-
if (node.tag) {
|
|
40
|
-
const closestAncestorBlockNode = ancestorNodesContext
|
|
41
|
-
.slice()
|
|
42
|
-
.reverse()
|
|
43
|
-
.find((ancestorNode) => ancestorNode.block);
|
|
44
|
-
|
|
45
|
-
const block = node?.block;
|
|
46
|
-
const element = node?.element;
|
|
47
|
-
const modifiers = [
|
|
48
|
-
...new Set([
|
|
49
|
-
...(node.modifiers ? node.modifiers : []),
|
|
50
|
-
...(node.modifier ? [node.modifier] : []),
|
|
51
|
-
]),
|
|
52
|
-
];
|
|
53
|
-
const inheritedBlock = closestAncestorBlockNode?.block;
|
|
54
|
-
|
|
55
|
-
if (inheritedBlock && !block) {
|
|
56
|
-
this.logger.info(
|
|
57
|
-
`Inheriting BEM block from ancestor: ${inheritedBlock}`
|
|
58
|
-
);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
const bemClasses = this.getBemClasses(
|
|
62
|
-
block,
|
|
63
|
-
element,
|
|
64
|
-
modifiers,
|
|
65
|
-
inheritedBlock
|
|
66
|
-
);
|
|
67
|
-
|
|
68
|
-
this.logger.info(
|
|
69
|
-
`Generated BEM classes: ${bemClasses} for node: ${node.tag}`
|
|
70
|
-
);
|
|
71
|
-
|
|
72
|
-
if (node.attributes) {
|
|
73
|
-
node.attributes.class = node.attributes.class
|
|
74
|
-
? `${bemClasses} ${node.attributes.class}` // TODO: maybe add options to append/prepend?
|
|
75
|
-
: bemClasses;
|
|
76
|
-
} else {
|
|
77
|
-
node.attributes = { class: bemClasses };
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
return node;
|
|
82
|
-
};
|
|
83
|
-
|
|
84
|
-
getBemClasses = (block, element, modifiers, inheritedBlock) => {
|
|
85
|
-
let bemClasses = [];
|
|
86
|
-
const blockToUse = block ?? inheritedBlock ?? "untitled-block";
|
|
87
|
-
let root;
|
|
88
|
-
|
|
89
|
-
// determine the root class based on the presence of block and element
|
|
90
|
-
if (element) {
|
|
91
|
-
root = `${blockToUse}__${element}`; // element present, use block__element
|
|
92
|
-
} else if (block) {
|
|
93
|
-
root = blockToUse; // only block present, use block
|
|
94
|
-
} else {
|
|
95
|
-
root = `${blockToUse}__untitled-element`; // neither block nor element defined, fallback
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
bemClasses.push(root);
|
|
99
|
-
|
|
100
|
-
modifiers.forEach((modifier) => {
|
|
101
|
-
bemClasses.push(`${root}--${modifier}`);
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
return bemClasses.join(" ");
|
|
105
|
-
};
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
module.exports = BemExtension;
|
package/src/extensions/react.js
DELETED
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
const createLogger = require("../helpers/createLogger");
|
|
2
|
-
|
|
3
|
-
class ReactExtension {
|
|
4
|
-
constructor(verbose = false) {
|
|
5
|
-
this.key = "react";
|
|
6
|
-
this.logger = createLogger(verbose, "ReactExtension");
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
sanitizeComponentName = (componentName) => {
|
|
10
|
-
return componentName
|
|
11
|
-
.split("-")
|
|
12
|
-
.map((part) => part.charAt(0).toUpperCase() + part.slice(1).toLowerCase())
|
|
13
|
-
.join("");
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
optionsHandler = (defaultOptions, options) => {
|
|
17
|
-
return {
|
|
18
|
-
...defaultOptions,
|
|
19
|
-
attributeFormatter: (attribute, value, isExpression = false) => {
|
|
20
|
-
if (!isExpression) {
|
|
21
|
-
return ` ${attribute}="${value}"`;
|
|
22
|
-
} else {
|
|
23
|
-
return ` ${attribute}={${value}}`;
|
|
24
|
-
}
|
|
25
|
-
},
|
|
26
|
-
filename: options.componentName ?? options.name ?? "UntitledComponent",
|
|
27
|
-
fileExtension: ".jsx",
|
|
28
|
-
outputDir: "dist/react",
|
|
29
|
-
preferSelfClosingTags: true,
|
|
30
|
-
prettierParser: "babel",
|
|
31
|
-
};
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
nodeHandler = (node) => {
|
|
35
|
-
const reactConfig = node.extensions && node.extensions.react;
|
|
36
|
-
|
|
37
|
-
if (reactConfig) {
|
|
38
|
-
this.logger.info(
|
|
39
|
-
`Processing React extension for node: ${node.tag || "text"}`
|
|
40
|
-
);
|
|
41
|
-
|
|
42
|
-
if (node.attributes) {
|
|
43
|
-
if (node.attributes.class) {
|
|
44
|
-
this.logger.info(
|
|
45
|
-
`Transforming 'class' to 'className' for React compatibility.`
|
|
46
|
-
);
|
|
47
|
-
node.attributes.className = node.attributes.class;
|
|
48
|
-
delete node.attributes.class;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
// TODO: handle other attributes that need to be renamed, maybe use a map?
|
|
52
|
-
if (node.attributes.onclick) {
|
|
53
|
-
node.attributes.onclick = node.attributes.onClick;
|
|
54
|
-
delete node.attributes.onclick;
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
if (reactConfig.attributes) {
|
|
59
|
-
node.attributes = { ...node.attributes, ...reactConfig.attributes };
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
if (reactConfig.expressionAttributes) {
|
|
63
|
-
node.expressionAttributes = reactConfig.expressionAttributes;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
// Log if there are specific overrides from reactConfig
|
|
67
|
-
if (reactConfig.tag) {
|
|
68
|
-
this.logger.info(
|
|
69
|
-
`Overriding node tag with React component: ${reactConfig.tag}`
|
|
70
|
-
);
|
|
71
|
-
}
|
|
72
|
-
if (reactConfig.tag) {
|
|
73
|
-
node.tag = reactConfig.tag;
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
return node;
|
|
78
|
-
};
|
|
79
|
-
|
|
80
|
-
rootHandler = (htmlContent, rendererOptions) => {
|
|
81
|
-
const rawName =
|
|
82
|
-
rendererOptions.componentName ??
|
|
83
|
-
rendererOptions.name ??
|
|
84
|
-
"UntitledComponent";
|
|
85
|
-
const componentName = this.sanitizeComponentName(rawName);
|
|
86
|
-
this.logger.info(`Generating React component: ${componentName}`);
|
|
87
|
-
|
|
88
|
-
return `
|
|
89
|
-
import React from 'react';
|
|
90
|
-
|
|
91
|
-
function ${componentName}() {
|
|
92
|
-
return (
|
|
93
|
-
${htmlContent}
|
|
94
|
-
);
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
export default ${componentName};
|
|
98
|
-
`.trim();
|
|
99
|
-
};
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
module.exports = ReactExtension;
|
|
@@ -1,116 +0,0 @@
|
|
|
1
|
-
const fs = require("fs");
|
|
2
|
-
const path = require("path");
|
|
3
|
-
const createLogger = require("../helpers/createLogger");
|
|
4
|
-
|
|
5
|
-
function readJsonFile(sourcePath) {
|
|
6
|
-
try {
|
|
7
|
-
const fileContent = fs.readFileSync(sourcePath, "utf8");
|
|
8
|
-
|
|
9
|
-
return JSON.parse(fileContent);
|
|
10
|
-
} catch (error) {
|
|
11
|
-
console.error(`Error reading or parsing JSON file ${sourcePath}:`, error);
|
|
12
|
-
throw error;
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
function writeOutputFile(template, outputPath, verbose = false) {
|
|
17
|
-
const logger = createLogger(verbose, "writeOutputFile");
|
|
18
|
-
const outputDir = path.dirname(outputPath);
|
|
19
|
-
|
|
20
|
-
if (!fs.existsSync(outputDir)) {
|
|
21
|
-
logger.info(`Creating output directory: ${outputDir}`);
|
|
22
|
-
fs.mkdirSync(outputDir, { recursive: true });
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
fs.writeFileSync(outputPath, template, "utf8");
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
async function getSourcePathType(sourcePath) {
|
|
29
|
-
const stats = fs.statSync(sourcePath);
|
|
30
|
-
|
|
31
|
-
if (stats.isDirectory()) {
|
|
32
|
-
return "directory";
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
if (stats.isFile()) {
|
|
36
|
-
return "file";
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
async function processFile(
|
|
41
|
-
sourcePath,
|
|
42
|
-
outputDir,
|
|
43
|
-
extensions,
|
|
44
|
-
templateEngine,
|
|
45
|
-
name,
|
|
46
|
-
componentName,
|
|
47
|
-
verbose
|
|
48
|
-
) {
|
|
49
|
-
const logger = createLogger(verbose, "processFile");
|
|
50
|
-
logger.info(`Processing file: ${sourcePath}`);
|
|
51
|
-
|
|
52
|
-
const templateData = readJsonFile(sourcePath);
|
|
53
|
-
const filenameWithoutExtension = path.basename(sourcePath, ".json");
|
|
54
|
-
|
|
55
|
-
try {
|
|
56
|
-
await templateEngine.render(templateData, {
|
|
57
|
-
name: name ?? filenameWithoutExtension,
|
|
58
|
-
componentName,
|
|
59
|
-
outputDir,
|
|
60
|
-
extensions,
|
|
61
|
-
writeOutputFile: true,
|
|
62
|
-
verbose,
|
|
63
|
-
});
|
|
64
|
-
logger.success(`Successfully processed file: ${sourcePath}`);
|
|
65
|
-
} catch (error) {
|
|
66
|
-
logger.error(`Error processing file ${sourcePath}: ${error.message}`);
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
async function processDirectory(
|
|
71
|
-
sourceDir,
|
|
72
|
-
outputDir,
|
|
73
|
-
extensions,
|
|
74
|
-
templateEngine,
|
|
75
|
-
verbose
|
|
76
|
-
) {
|
|
77
|
-
const logger = createLogger(verbose, "processDirectory");
|
|
78
|
-
logger.info(`Processing directory: ${sourceDir}`);
|
|
79
|
-
const entries = fs.readdirSync(sourceDir, { withFileTypes: true });
|
|
80
|
-
|
|
81
|
-
for (const entry of entries) {
|
|
82
|
-
const sourceEntryPath = path.join(sourceDir, entry.name);
|
|
83
|
-
const outputEntryPath = path.join(outputDir ?? "dist", entry.name);
|
|
84
|
-
|
|
85
|
-
if (entry.isDirectory()) {
|
|
86
|
-
logger.info(`Entering directory: ${entry.name}`);
|
|
87
|
-
await processDirectory(
|
|
88
|
-
sourceEntryPath,
|
|
89
|
-
outputEntryPath,
|
|
90
|
-
extensions,
|
|
91
|
-
templateEngine,
|
|
92
|
-
verbose
|
|
93
|
-
);
|
|
94
|
-
} else if (entry.isFile() && path.extname(entry.name) === ".json") {
|
|
95
|
-
logger.info(`Found JSON file: ${entry.name}`);
|
|
96
|
-
const templateData = readJsonFile(sourceEntryPath);
|
|
97
|
-
const filenameWithoutExtension = path.basename(entry.name, ".json");
|
|
98
|
-
|
|
99
|
-
await templateEngine.render(templateData, {
|
|
100
|
-
name: filenameWithoutExtension,
|
|
101
|
-
outputDir,
|
|
102
|
-
extensions,
|
|
103
|
-
writeOutputFile: true,
|
|
104
|
-
verbose,
|
|
105
|
-
});
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
module.exports = {
|
|
111
|
-
readJsonFile,
|
|
112
|
-
writeOutputFile,
|
|
113
|
-
getSourcePathType,
|
|
114
|
-
processFile,
|
|
115
|
-
processDirectory,
|
|
116
|
-
};
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
const signale = require("signale");
|
|
2
|
-
|
|
3
|
-
const createLogger = (verbose = false, prefix = "") => {
|
|
4
|
-
const logger = {};
|
|
5
|
-
const methods = ["info", "warn", "error", "debug", "success"];
|
|
6
|
-
|
|
7
|
-
methods.forEach((method) => {
|
|
8
|
-
logger[method] = (...args) => {
|
|
9
|
-
if (verbose) {
|
|
10
|
-
signale[method](prefix ? `[${prefix}]` : "", ...args);
|
|
11
|
-
}
|
|
12
|
-
// If not verbose, do nothing
|
|
13
|
-
};
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
return logger;
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
module.exports = createLogger;
|