fast-ejs-builder 0.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 +401 -0
- package/bin/index.js +21 -0
- package/core/index.js +112 -0
- package/core/lib.js +231 -0
- package/core/prompter.js +99 -0
- package/fast.ejs.json +22 -0
- package/fast.ejs.schema.json +93 -0
- package/logo.png +0 -0
- package/package.json +39 -0
- package/src/build-dev.js +34 -0
- package/src/build.js +259 -0
- package/src/prettier.js +12 -0
- package/src/tailwind.js +105 -0
- package/templates/tailwind.css +3 -0
package/core/lib.js
ADDED
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
const { args, _r, _has, _ce, _d, _w, _md, _e, _p, _ds } = require(".");
|
|
2
|
+
const { $confirm, $input, $select, $number } = require("./prompter");
|
|
3
|
+
const default_fej = require("../fast.ejs.json");
|
|
4
|
+
const config = {
|
|
5
|
+
...default_fej,
|
|
6
|
+
$schema: "http://unpkg.com/fast-ejs-builder/fast.ejs.schema.json",
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
function getConfigFile() {
|
|
10
|
+
for (let i = 0; i < args.length; i++) {
|
|
11
|
+
const arg = args[i];
|
|
12
|
+
if (arg.toLowerCase() == "-c" || arg == "--config") {
|
|
13
|
+
const v = args[i + 1];
|
|
14
|
+
if (v) return v;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function smartMerge(target = {}, source = {}) {
|
|
20
|
+
if (!source) return target;
|
|
21
|
+
|
|
22
|
+
const result = { ...target };
|
|
23
|
+
|
|
24
|
+
for (const key in target) {
|
|
25
|
+
const targetValue = target[key];
|
|
26
|
+
const sourceValue = source[key];
|
|
27
|
+
|
|
28
|
+
const exists = (val) =>
|
|
29
|
+
val != null && val != undefined && val.toString().trim() != "";
|
|
30
|
+
|
|
31
|
+
const isObject = (val) =>
|
|
32
|
+
val && typeof val === "object" && !Array.isArray(val);
|
|
33
|
+
|
|
34
|
+
if (isObject(targetValue) && isObject(sourceValue)) {
|
|
35
|
+
result[key] = smartMerge(targetValue, sourceValue);
|
|
36
|
+
} else if (
|
|
37
|
+
exists(sourceValue) &&
|
|
38
|
+
typeof targetValue === typeof sourceValue
|
|
39
|
+
) {
|
|
40
|
+
result[key] = sourceValue;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return result;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
async function getConfig() {
|
|
48
|
+
const file = getConfigFile() || "fast.ejs.json";
|
|
49
|
+
|
|
50
|
+
if (!_ce(file, "Config file")) {
|
|
51
|
+
const auto = await $confirm("Do you want to use default settings ?", true);
|
|
52
|
+
if (!auto) {
|
|
53
|
+
config.pages.dir = await $input(
|
|
54
|
+
"Where are your pages ?",
|
|
55
|
+
config.pages.dir,
|
|
56
|
+
);
|
|
57
|
+
config.data.dir = await $input(
|
|
58
|
+
"Where are your data files ?",
|
|
59
|
+
config.data.dir,
|
|
60
|
+
);
|
|
61
|
+
config.components.dir = await $input(
|
|
62
|
+
"In which folder are your components ?",
|
|
63
|
+
config.components.dir,
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
config.build.output = await $input(
|
|
67
|
+
"Where do you want to output ?",
|
|
68
|
+
config.build.output,
|
|
69
|
+
);
|
|
70
|
+
config.build.interval = await $number(
|
|
71
|
+
"How many milliseconds between each build ?",
|
|
72
|
+
config.build.interval,
|
|
73
|
+
);
|
|
74
|
+
config.build.useIndexRouting = await $confirm(
|
|
75
|
+
"Do you want to use index routing ?",
|
|
76
|
+
true,
|
|
77
|
+
);
|
|
78
|
+
config.data.allow = await $select(
|
|
79
|
+
"In which format do you want to pass data ?",
|
|
80
|
+
["all", "js", "json"],
|
|
81
|
+
config.data.allow,
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
config.tailwind.output = await $input(
|
|
85
|
+
"Where should tailwind output ?",
|
|
86
|
+
config.tailwind.output,
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
const done = _w(file, config);
|
|
90
|
+
if (done) _ds(`Successfully generated '${file}'`);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const user_config = _r(file);
|
|
94
|
+
const merged_config = smartMerge(config, user_config);
|
|
95
|
+
|
|
96
|
+
for (let k in merged_config) {
|
|
97
|
+
config[k] = merged_config[k];
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function generateBaseDirs() {
|
|
102
|
+
if (!_ce(config.pages.dir, "Pages directory")) {
|
|
103
|
+
_md(config.pages.dir);
|
|
104
|
+
_d("Generated pages directory.");
|
|
105
|
+
}
|
|
106
|
+
_md(config.pages.dir + "/public");
|
|
107
|
+
|
|
108
|
+
if (!_ce(config.components.dir, "Components directory")) {
|
|
109
|
+
_md(config.components.dir);
|
|
110
|
+
_d("Generated components directory.");
|
|
111
|
+
}
|
|
112
|
+
if (!_ce(config.data.dir, "Data directory")) {
|
|
113
|
+
_md(config.data.dir);
|
|
114
|
+
_d("Generated data directory.");
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
async function getDatas() {
|
|
119
|
+
const globalDataPath = (type) => `${config.data.dir}/global.data.${type}`;
|
|
120
|
+
const localDataPath = (type) => `${config.data.dir}/local.data.${type}`;
|
|
121
|
+
|
|
122
|
+
const globalJs = globalDataPath("js");
|
|
123
|
+
const globalJson = globalDataPath("json");
|
|
124
|
+
|
|
125
|
+
const localJs = localDataPath("js");
|
|
126
|
+
const localJson = localDataPath("json");
|
|
127
|
+
|
|
128
|
+
const templates = {
|
|
129
|
+
globalJS: `
|
|
130
|
+
module.exports = async () => ({
|
|
131
|
+
year: new Date().getFullYear(),
|
|
132
|
+
environment: process.env.NODE_ENV,
|
|
133
|
+
});
|
|
134
|
+
`,
|
|
135
|
+
localJS: `
|
|
136
|
+
module.exports = () => ({
|
|
137
|
+
// index.ejs
|
|
138
|
+
index: {
|
|
139
|
+
title: process.env.APP_NAME,
|
|
140
|
+
},
|
|
141
|
+
about: {}, // about.ejs,
|
|
142
|
+
"contact/menu": {}, // contact/menu.ejs
|
|
143
|
+
});
|
|
144
|
+
`,
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
const default_data_args = {};
|
|
148
|
+
|
|
149
|
+
const parseJSDate = async (path) => {
|
|
150
|
+
const resolved = require.resolve(_p(path));
|
|
151
|
+
delete require.cache[resolved];
|
|
152
|
+
const v = require(resolved);
|
|
153
|
+
return typeof v == "function" ? await v(default_data_args) : v;
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
const getGlobalJSData = async () => {
|
|
157
|
+
if (_e(globalJs)) {
|
|
158
|
+
return await parseJSDate(globalJs);
|
|
159
|
+
} else {
|
|
160
|
+
_d(`Global file '${globalJs}' not found. It will be generated`);
|
|
161
|
+
_w(globalJs, templates.globalJS.trim());
|
|
162
|
+
return {};
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
const getLocalJSData = async () => {
|
|
167
|
+
if (_e(localJs)) {
|
|
168
|
+
return await parseJSDate(localJs);
|
|
169
|
+
} else {
|
|
170
|
+
_d(`Local file '${localJs}' not found. It will be generated`);
|
|
171
|
+
_w(localJs, templates.localJS.trim());
|
|
172
|
+
return {};
|
|
173
|
+
}
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
const getGlobalJSONData = () => {
|
|
177
|
+
if (_e(globalJson)) {
|
|
178
|
+
return _r(globalJson);
|
|
179
|
+
} else {
|
|
180
|
+
_d(`Global file '${globalJson}' not found. It will be generated`);
|
|
181
|
+
_w(globalJson, {});
|
|
182
|
+
return {};
|
|
183
|
+
}
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
const getLocalJSONData = () => {
|
|
187
|
+
if (_e(localJson)) {
|
|
188
|
+
return _r(localJson);
|
|
189
|
+
} else {
|
|
190
|
+
_d(`Local file '${localJson}' not found. It will be generated`);
|
|
191
|
+
_w(localJson, { index: { title: "HomePage" }, about: {} });
|
|
192
|
+
return {};
|
|
193
|
+
}
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
let globalData = {},
|
|
197
|
+
localData = {};
|
|
198
|
+
|
|
199
|
+
switch (config.data.allow) {
|
|
200
|
+
case "js":
|
|
201
|
+
globalData = await getGlobalJSData();
|
|
202
|
+
localData = await getLocalJSData();
|
|
203
|
+
break;
|
|
204
|
+
case "json":
|
|
205
|
+
globalData = getGlobalJSONData();
|
|
206
|
+
localData = getLocalJSONData();
|
|
207
|
+
break;
|
|
208
|
+
default:
|
|
209
|
+
globalData = { ...getGlobalJSONData(), ...(await getGlobalJSData()) };
|
|
210
|
+
localData = { ...getLocalJSONData(), ...(await getLocalJSData()) };
|
|
211
|
+
break;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
return { globalData, localData };
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
const configTailwindOutput = () => {
|
|
218
|
+
let o = config.tailwind.output;
|
|
219
|
+
if (!o.startsWith("/")) o = `/${o}`;
|
|
220
|
+
|
|
221
|
+
return _p(config.build.output + o);
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
module.exports = {
|
|
225
|
+
getConfigFile,
|
|
226
|
+
getConfig,
|
|
227
|
+
generateBaseDirs,
|
|
228
|
+
config,
|
|
229
|
+
configTailwindOutput,
|
|
230
|
+
getDatas,
|
|
231
|
+
};
|
package/core/prompter.js
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
const inquirer = require("@inquirer/prompts");
|
|
2
|
+
|
|
3
|
+
class Prompter {
|
|
4
|
+
static async $confirm(message, defaultValue) {
|
|
5
|
+
try {
|
|
6
|
+
const p = await inquirer.confirm(
|
|
7
|
+
{
|
|
8
|
+
message: message,
|
|
9
|
+
default: defaultValue,
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
clearPromptOnDone: true,
|
|
13
|
+
},
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
return p;
|
|
17
|
+
} catch (_) {}
|
|
18
|
+
}
|
|
19
|
+
static async $input(message, defaultValue = "") {
|
|
20
|
+
try {
|
|
21
|
+
const p = await inquirer.input(
|
|
22
|
+
{
|
|
23
|
+
message,
|
|
24
|
+
default: defaultValue,
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
clearPromptOnDone: true,
|
|
28
|
+
},
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
return p;
|
|
32
|
+
} catch (_) {}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
static async $password(message) {
|
|
36
|
+
try {
|
|
37
|
+
const p = await inquirer.password(
|
|
38
|
+
{
|
|
39
|
+
message,
|
|
40
|
+
mask: "*",
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
clearPromptOnDone: true,
|
|
44
|
+
},
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
return p;
|
|
48
|
+
} catch (_) {}
|
|
49
|
+
}
|
|
50
|
+
static async $select(message, choices, defaultValue) {
|
|
51
|
+
try {
|
|
52
|
+
const p = await inquirer.select(
|
|
53
|
+
{
|
|
54
|
+
message,
|
|
55
|
+
choices: choices,
|
|
56
|
+
default: defaultValue,
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
// clearPromptOnDone: true,
|
|
60
|
+
},
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
return p;
|
|
64
|
+
} catch (_) {}
|
|
65
|
+
}
|
|
66
|
+
static async $checkbox(message, choices) {
|
|
67
|
+
try {
|
|
68
|
+
const p = await inquirer.checkbox(
|
|
69
|
+
{
|
|
70
|
+
message,
|
|
71
|
+
choices,
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
clearPromptOnDone: true,
|
|
75
|
+
},
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
return p;
|
|
79
|
+
} catch (_) {}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
static async $number(message, defaultValue = 0) {
|
|
83
|
+
try {
|
|
84
|
+
const p = await inquirer.number(
|
|
85
|
+
{
|
|
86
|
+
message,
|
|
87
|
+
default: defaultValue,
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
clearPromptOnDone: true,
|
|
91
|
+
},
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
return p;
|
|
95
|
+
} catch (_) {}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
module.exports = Prompter;
|
package/fast.ejs.json
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "./fast.ejs.schema.json",
|
|
3
|
+
"build": {
|
|
4
|
+
"interval": 100,
|
|
5
|
+
"output": "build",
|
|
6
|
+
"useIndexRouting": true
|
|
7
|
+
},
|
|
8
|
+
"components": {
|
|
9
|
+
"dir": "app/components"
|
|
10
|
+
},
|
|
11
|
+
"data": {
|
|
12
|
+
"allow": "all",
|
|
13
|
+
"dir": "app/data"
|
|
14
|
+
},
|
|
15
|
+
"pages": {
|
|
16
|
+
"dir": "app/pages"
|
|
17
|
+
},
|
|
18
|
+
"tailwind": {
|
|
19
|
+
"imports": [],
|
|
20
|
+
"output": "public/app.css"
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"title": "fast-ejs configuration schema",
|
|
4
|
+
"type": "object",
|
|
5
|
+
"properties": {
|
|
6
|
+
"$schema": {
|
|
7
|
+
"type": "string"
|
|
8
|
+
},
|
|
9
|
+
"build": {
|
|
10
|
+
"properties": {
|
|
11
|
+
"output": {
|
|
12
|
+
"default": "build",
|
|
13
|
+
"description": "The output folder where HTML files will be generated",
|
|
14
|
+
"type": "string"
|
|
15
|
+
},
|
|
16
|
+
"interval": {
|
|
17
|
+
"default": 100,
|
|
18
|
+
"description": "Interval in milliseconds between each build",
|
|
19
|
+
"type": "number"
|
|
20
|
+
},
|
|
21
|
+
"useIndexRouting": {
|
|
22
|
+
"default": true,
|
|
23
|
+
"description": "Whether if should build route/index.html instead of route.html",
|
|
24
|
+
"type": "boolean"
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
"required": ["output"],
|
|
28
|
+
"additionalProperties": false
|
|
29
|
+
},
|
|
30
|
+
|
|
31
|
+
"components": {
|
|
32
|
+
"properties": {
|
|
33
|
+
"dir": {
|
|
34
|
+
"default": "app/components",
|
|
35
|
+
"description": "The folder containing your EJS components",
|
|
36
|
+
"type": "string"
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
"required": ["dir"],
|
|
40
|
+
"additionalProperties": false
|
|
41
|
+
},
|
|
42
|
+
"data": {
|
|
43
|
+
"properties": {
|
|
44
|
+
"dir": {
|
|
45
|
+
"default": "app/data",
|
|
46
|
+
"description": "The folder containing global and local data files (global.data.js/json, local.data.js/json)",
|
|
47
|
+
"type": "string"
|
|
48
|
+
},
|
|
49
|
+
"allow": {
|
|
50
|
+
"default": "all",
|
|
51
|
+
"description": "Specify the data file format to use: 'js' for JavaScript, 'json' for JSON, or 'all' to accept both",
|
|
52
|
+
"enum": ["js", "json", "all"],
|
|
53
|
+
"type": "string"
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
"required": ["dir"],
|
|
57
|
+
"additionalProperties": false
|
|
58
|
+
},
|
|
59
|
+
|
|
60
|
+
"pages": {
|
|
61
|
+
"properties": {
|
|
62
|
+
"dir": {
|
|
63
|
+
"default": "app/pages",
|
|
64
|
+
"description": "The folder containing your EJS templates",
|
|
65
|
+
"type": "string"
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
"required": ["dir"],
|
|
69
|
+
"additionalProperties": false
|
|
70
|
+
},
|
|
71
|
+
"tailwind": {
|
|
72
|
+
"properties": {
|
|
73
|
+
"imports": {
|
|
74
|
+
"default": [],
|
|
75
|
+
"description": "List of external CSS URLs to include in the Tailwind output",
|
|
76
|
+
"items": {
|
|
77
|
+
"type": "string"
|
|
78
|
+
},
|
|
79
|
+
"type": "array"
|
|
80
|
+
},
|
|
81
|
+
"output": {
|
|
82
|
+
"default": "public/app.css",
|
|
83
|
+
"description": "Output file of tailwind. Relative to pages directory.",
|
|
84
|
+
"type": "string"
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
"required": ["output"],
|
|
88
|
+
"additionalProperties": false
|
|
89
|
+
}
|
|
90
|
+
},
|
|
91
|
+
"additionalProperties": false,
|
|
92
|
+
"required": ["pages", "build"]
|
|
93
|
+
}
|
package/logo.png
ADDED
|
Binary file
|
package/package.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "fast-ejs-builder",
|
|
3
|
+
"version": "0.0.0",
|
|
4
|
+
"description": "Fast-EJS is a simple and fast tool to pre-render EJS templates into static HTML files with clean conventions and flexible data handling.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"tailwind",
|
|
7
|
+
"html",
|
|
8
|
+
"build"
|
|
9
|
+
],
|
|
10
|
+
"homepage": "https://github.com/D3R50N/tailwind-html#readme",
|
|
11
|
+
"bugs": {
|
|
12
|
+
"url": "https://github.com/D3R50N/tailwind-html/issues"
|
|
13
|
+
},
|
|
14
|
+
"repository": {
|
|
15
|
+
"type": "git",
|
|
16
|
+
"url": "git+https://github.com/D3R50N/tailwind-html.git"
|
|
17
|
+
},
|
|
18
|
+
"license": "MIT",
|
|
19
|
+
"author": "D3R50N",
|
|
20
|
+
"type": "commonjs",
|
|
21
|
+
"main": "index.js",
|
|
22
|
+
"bin": {
|
|
23
|
+
"fast-ejs": "bin/index.js"
|
|
24
|
+
},
|
|
25
|
+
"scripts": {
|
|
26
|
+
"test": "echo \"Error: no test specified\" && exit 1",
|
|
27
|
+
"dev": "nodemon index.js"
|
|
28
|
+
},
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"@inquirer/prompts": "^8.2.0",
|
|
31
|
+
"autoprefixer": "^10.4.23",
|
|
32
|
+
"dotenv": "^17.2.3",
|
|
33
|
+
"ejs": "^4.0.1",
|
|
34
|
+
"nodemon": "^3.1.11",
|
|
35
|
+
"postcss": "^8.5.6",
|
|
36
|
+
"prettier": "^3.8.0",
|
|
37
|
+
"tailwindcss": "^3.4.17"
|
|
38
|
+
}
|
|
39
|
+
}
|
package/src/build-dev.js
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
const { args, _d, _p } = require("../core");
|
|
2
|
+
const ejsbuild = require("../src/build");
|
|
3
|
+
const nodemon = require("nodemon");
|
|
4
|
+
const { config } = require("../core/lib");
|
|
5
|
+
|
|
6
|
+
async function ejsbuild_dev() {
|
|
7
|
+
const toWatch = [
|
|
8
|
+
config.pages.dir,
|
|
9
|
+
config.components.dir,
|
|
10
|
+
config.data.dir,
|
|
11
|
+
".env",
|
|
12
|
+
];
|
|
13
|
+
|
|
14
|
+
_d("Watching :", toWatch.map((d) => `'${d}'`).join(", "));
|
|
15
|
+
await ejsbuild(); // needs await to get config
|
|
16
|
+
|
|
17
|
+
nodemon({
|
|
18
|
+
watch: toWatch.map(_p),
|
|
19
|
+
ignore: [],
|
|
20
|
+
delay: config.build.interval,
|
|
21
|
+
ext: "*",
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
nodemon.on("restart", (files) => {
|
|
25
|
+
ejsbuild(1);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
nodemon.on("quit", () => {
|
|
29
|
+
_d("Session ended 🛑");
|
|
30
|
+
process.exit();
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
module.exports = ejsbuild_dev;
|