chocola 1.1.2 → 1.1.3
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/compiler/index.js +197 -0
- package/compiler/pipeline.js +109 -0
- package/compiler/utils.js +66 -0
- package/dev/index.js +64 -0
- package/index.js +30 -2
- package/package.json +1 -1
- package/utils.js +17 -0
- package/runtime/index.js +0 -116
- package/runtime/package.json +0 -13
- package/runtime/pipeline.js +0 -67
- package/runtime/utils.js +0 -6
- /package/{runtime → compiler}/fs.js +0 -0
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import { throwError, genRandomId, incrementAlfabet } from "./utils.js";
|
|
2
|
+
import { JSDOM } from "jsdom";
|
|
3
|
+
import path from "path";
|
|
4
|
+
import { promises as fs } from "fs";
|
|
5
|
+
import { getComponents, getSrcIndex, processStylesheet } from "./pipeline.js";
|
|
6
|
+
import chalk from 'chalk';
|
|
7
|
+
import beautify from "js-beautify";
|
|
8
|
+
import { getChocolaConfig } from "../utils.js";
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
const logSeparation = chalk.yellow(`
|
|
12
|
+
________________________________________________________________________
|
|
13
|
+
========================================================================
|
|
14
|
+
`);
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Compiles a static build of your Chocola project from the directory provided.
|
|
18
|
+
* @param {import("fs").PathLike} __rootdir
|
|
19
|
+
* @param {string} __srcdir
|
|
20
|
+
*/
|
|
21
|
+
export default async function runtime(__rootdir) {
|
|
22
|
+
console.log(chalk.bold.hex("#945e33")(`\n RUNNING CHOCOLA BUNDLER`))
|
|
23
|
+
console.log(logSeparation);
|
|
24
|
+
console.log(chalk.hex("#945e33")(`
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
▄████▄ ██░ ██ ▒█████ ▄████▄ ▒█████ ██▓ ▄▄▄
|
|
28
|
+
▒██▀ ▀█ ▓██░ ██▒▒██▒ ██▒▒██▀ ▀█ ▒██▒ ██▒▓██▒ ▒████▄
|
|
29
|
+
▒▓█ ▄ ▒██▀▀██░▒██░ ██▒▒▓█ ▄ ▒██░ ██▒▒██░ ▒██ ▀█▄
|
|
30
|
+
▒▓▓▄ ▄██▒░▓█ ░██ ▒██ ██░▒▓▓▄ ▄██▒▒██ ██░▒██░ ░██▄▄▄▄██
|
|
31
|
+
▒ ▓███▀ ░░▓█▒░██▓░ ████▓▒░▒ ▓███▀ ░░ ████▓▒░░██████▒▓█ ▓██▒
|
|
32
|
+
░ ░▒ ▒ ░ ▒ ░░▒░▒░ ▒░▒░▒░ ░ ░▒ ▒ ░░ ▒░▒░▒░ ░ ▒░▓ ░▒▒ ▓▒█░
|
|
33
|
+
░ ▒ ▒ ░▒░ ░ ░ ▒ ▒░ ░ ▒ ░ ▒ ▒░ ░ ░ ▒ ░ ▒ ▒▒ ░
|
|
34
|
+
░ ░ ░░ ░░ ░ ░ ▒ ░ ░ ░ ░ ▒ ░ ░ ░ ▒
|
|
35
|
+
░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░
|
|
36
|
+
░ ░
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
`));
|
|
40
|
+
let __srcdir = "src";
|
|
41
|
+
let __outDir = "dist";
|
|
42
|
+
let __libDir = "lib";
|
|
43
|
+
let __emptyOutDir = true;
|
|
44
|
+
|
|
45
|
+
const config = await getChocolaConfig(__rootdir);
|
|
46
|
+
const bundleConfig = config.bundle;
|
|
47
|
+
|
|
48
|
+
if (bundleConfig.srcDir) { __srcdir = bundleConfig.srcDir }
|
|
49
|
+
else { console.warn(chalk.bold.yellow("WARNING!"), 'srcDir not defined in chocola.config.json file: using default "src" directory.') }
|
|
50
|
+
|
|
51
|
+
if (bundleConfig.outDir) { __outDir = bundleConfig.outDir }
|
|
52
|
+
else { console.warn(chalk.bold.yellow("WARNING!"), 'outDir not defined in chocola.config.json file: using default "dist" directory.') }
|
|
53
|
+
|
|
54
|
+
if (bundleConfig.libDir) { __libDir = bundleConfig.libDir }
|
|
55
|
+
else { console.warn(chalk.bold.yellow("WARNING!"), 'libDir not defined in chocola.config.json file: using default "lib" directory.') }
|
|
56
|
+
|
|
57
|
+
if (bundleConfig.emptyOutDir) {
|
|
58
|
+
__emptyOutDir = bundleConfig.emptyOutDir;
|
|
59
|
+
console.log(`> using emptyOutDir = ${__emptyOutDir}`);
|
|
60
|
+
} else {
|
|
61
|
+
console.log(`> using default emptyOutDir = ${__emptyOutDir}`);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
console.log(logSeparation);
|
|
65
|
+
|
|
66
|
+
const outDirPath = path.join(__rootdir, __outDir);
|
|
67
|
+
const srcPath = path.join(__rootdir, __srcdir);
|
|
68
|
+
const srcComponents = path.join(srcPath, __libDir);
|
|
69
|
+
|
|
70
|
+
if (__emptyOutDir) {
|
|
71
|
+
await fs.rm(path.join(outDirPath), { recursive: true, force: true });
|
|
72
|
+
await fs.mkdir(path.join(outDirPath));
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
let indexFiles = await getSrcIndex(srcPath);
|
|
76
|
+
let srcHtmlFile = indexFiles.srcHtmlFile;
|
|
77
|
+
let srcChocoFile = indexFiles.srcChocoFile;
|
|
78
|
+
|
|
79
|
+
console.log(` LOADING COMPONENTS`)
|
|
80
|
+
|
|
81
|
+
const foundComponents = await getComponents(srcComponents);
|
|
82
|
+
|
|
83
|
+
let loadedComponents = foundComponents.loadedComponents;
|
|
84
|
+
let notDefComps = foundComponents.notDefComps;
|
|
85
|
+
|
|
86
|
+
const srcDirData = {
|
|
87
|
+
index: srcHtmlFile || srcChocoFile,
|
|
88
|
+
components: foundComponents.componentsLib,
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
console.log(chalk.bold.green(">"), "Components found in", chalk.green.underline(srcComponents) + ":");
|
|
92
|
+
console.log(" ", srcDirData.components, "\n");
|
|
93
|
+
|
|
94
|
+
if (notDefComps.length > 0) {
|
|
95
|
+
console.warn(chalk.bold.yellow("WARNING!"), "The following components don't include a default export:");
|
|
96
|
+
console.log(" ", notDefComps);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
console.log(logSeparation);
|
|
100
|
+
console.log(` BUNDLING STATIC BUILD`);
|
|
101
|
+
console.log(chalk.bold.green(">"), "Creating Chocola static build in directory", chalk.green.underline(outDirPath) + "\n");
|
|
102
|
+
console.log(logSeparation);
|
|
103
|
+
|
|
104
|
+
const dom = new JSDOM(srcDirData.index);
|
|
105
|
+
const doc = dom.window.document;
|
|
106
|
+
const appContainer = doc.querySelector("app");
|
|
107
|
+
|
|
108
|
+
if (!appContainer) {
|
|
109
|
+
throwError("Index page must have an " + chalk.blue("<app>") + " element");
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const appElements = Array.from(appContainer.children);
|
|
113
|
+
|
|
114
|
+
let runtimeChunks = [];
|
|
115
|
+
let runtimeScript = "";
|
|
116
|
+
let compIdColl = [];
|
|
117
|
+
let letter;
|
|
118
|
+
|
|
119
|
+
appElements.forEach(el => {
|
|
120
|
+
const tagName = el.tagName.toLowerCase();
|
|
121
|
+
const compName = tagName + ".js";
|
|
122
|
+
|
|
123
|
+
const ctx = {};
|
|
124
|
+
for (const attr of el.attributes) {
|
|
125
|
+
if (attr.name.startsWith("ctx.")) {
|
|
126
|
+
const key = attr.name.slice(4);
|
|
127
|
+
ctx[key] = attr.value;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const instance = loadedComponents.get(compName);
|
|
132
|
+
if (instance && instance.body) {
|
|
133
|
+
let body = instance.body;
|
|
134
|
+
body = body.replace(/\$\{ctx\.(\w+)\}/g, (_, key) => ctx[key] || "");
|
|
135
|
+
const fragment = JSDOM.fragment(body);
|
|
136
|
+
const firstChild = fragment.firstChild;
|
|
137
|
+
if (firstChild && firstChild.nodeType === 1) {
|
|
138
|
+
if (instance.script || instance.effects) {
|
|
139
|
+
let script;
|
|
140
|
+
let effects;
|
|
141
|
+
const compId = "chid-" + genRandomId(compIdColl);
|
|
142
|
+
firstChild.setAttribute("chid", compId);
|
|
143
|
+
instance.script && (script = instance.script.toString())
|
|
144
|
+
|
|
145
|
+
if (!letter) {
|
|
146
|
+
letter = "a";
|
|
147
|
+
} else {
|
|
148
|
+
letter = incrementAlfabet(letter);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
script = script.replace(/RUNTIME/g, `${letter}RUNTIME`);
|
|
152
|
+
|
|
153
|
+
runtimeChunks.push(`
|
|
154
|
+
const ${letter} = document.querySelector('[chid="${compId}"]');
|
|
155
|
+
${script}
|
|
156
|
+
${letter}RUNTIME(${letter}, ${JSON.stringify(ctx)});`);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
el.replaceWith(fragment);
|
|
160
|
+
} else {
|
|
161
|
+
console.warn(chalk.yellow(`${compName} component could not be loaded`));
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
runtimeScript = runtimeChunks.join("\n");
|
|
166
|
+
|
|
167
|
+
let fileIds = [];
|
|
168
|
+
|
|
169
|
+
const docLinks = Array.from(doc.querySelectorAll("link"));
|
|
170
|
+
|
|
171
|
+
for (const link of docLinks) {
|
|
172
|
+
const rel = link.rel;
|
|
173
|
+
if (rel === "stylesheet") await processStylesheet(link, __rootdir, __srcdir, outDirPath, fileIds);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
const runtimeFilename = "run-" + genRandomId(fileIds, 6) + ".js";
|
|
178
|
+
const runtimeFileContents = `document.addEventListener("DOMContentLoaded", () => {${runtimeScript}})`;
|
|
179
|
+
await fs.writeFile(path.join(outDirPath, runtimeFilename), runtimeFileContents);
|
|
180
|
+
const runtimeScriptEl = doc.createElement("script");
|
|
181
|
+
runtimeScriptEl.type = "module";
|
|
182
|
+
runtimeScriptEl.src = "./" + runtimeFilename;
|
|
183
|
+
doc.body.appendChild(runtimeScriptEl);
|
|
184
|
+
const finalHtml = dom.serialize();
|
|
185
|
+
const prettyHtml = beautify.html(finalHtml, { indent_size: 2 });
|
|
186
|
+
|
|
187
|
+
await fs.writeFile(path.join(outDirPath, "index.html"), prettyHtml);
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
console.log(`
|
|
191
|
+
▄▄ ▄▄▄ ▄▄▄▄ ▄▄▄▄ ▄▄▄ ▄▄ ▄▄ ▄▄▄▄▄ ██
|
|
192
|
+
██ ██▀██ ██▄██ ██▀██ ██▀██ ███▄██ ██▄▄ ██
|
|
193
|
+
▄▄█▀ ▀███▀ ██▄█▀ ████▀ ▀███▀ ██ ▀██ ██▄▄▄ ▄▄
|
|
194
|
+
|
|
195
|
+
`);
|
|
196
|
+
console.log(chalk.bold.green(">"), "Project bundled succesfully at", chalk.green.underline(outDirPath) + "\n\n");
|
|
197
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { promises as fs } from "fs";
|
|
2
|
+
import { loadWithAssets, throwError, genRandomId } from "./utils.js";
|
|
3
|
+
import { readMyFile, checkFile } from "./fs.js";
|
|
4
|
+
import path from "path";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Returns a collection with the found components, the loaded ones (unpacked) and the undefined ones.
|
|
8
|
+
* @param {import("node:fs").PathLike} libDir
|
|
9
|
+
* @returns {{
|
|
10
|
+
* componentsLib: PathLike[],
|
|
11
|
+
* loadedComponents: Object[],
|
|
12
|
+
* notDefComps: PathLike[]
|
|
13
|
+
* }}
|
|
14
|
+
*/
|
|
15
|
+
export async function getComponents(libDir) {
|
|
16
|
+
let componentsLib = [];
|
|
17
|
+
let loadedComponents = new Map();
|
|
18
|
+
let notDefComps = [];
|
|
19
|
+
|
|
20
|
+
const components = await fs.readdir(libDir);
|
|
21
|
+
|
|
22
|
+
for (const comp of components) {
|
|
23
|
+
if (!comp.endsWith(".js") || comp[0] !== comp[0].toUpperCase()) continue;
|
|
24
|
+
|
|
25
|
+
componentsLib.push(comp);
|
|
26
|
+
|
|
27
|
+
const module = await loadWithAssets(path.join(libDir, comp));
|
|
28
|
+
|
|
29
|
+
if (typeof module.default !== "function") {
|
|
30
|
+
notDefComps.push(comp);
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const instance = module.default();
|
|
35
|
+
|
|
36
|
+
if (instance.bodyPath) {
|
|
37
|
+
instance.body = await fs.readFile(
|
|
38
|
+
path.resolve(libDir, instance.bodyPath),
|
|
39
|
+
"utf8"
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
loadedComponents.set(comp.toLowerCase(), instance);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return { componentsLib, loadedComponents, notDefComps };
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Returns the Chocola project index HTML or .chocola file contents.
|
|
51
|
+
* If both HTML and .choco index file exist, it will throw an error.
|
|
52
|
+
* Note: It throws an error when using .choco files since it is not supported yet.
|
|
53
|
+
* @param {PathLike} srcPath
|
|
54
|
+
* @returns {{
|
|
55
|
+
* srcHtmlFile: string | null,
|
|
56
|
+
* srcChocoFile: string | null
|
|
57
|
+
* }}
|
|
58
|
+
*/
|
|
59
|
+
export async function getSrcIndex(srcPath) {
|
|
60
|
+
const srcHtmlPath = path.join(srcPath, "index.html");
|
|
61
|
+
const srcChocoPath = path.join(srcPath, "index.choco");
|
|
62
|
+
|
|
63
|
+
const htmlExists = await checkFile(srcHtmlPath);
|
|
64
|
+
const chocoExists = await checkFile(srcChocoPath);
|
|
65
|
+
|
|
66
|
+
let srcHtmlFile = null;
|
|
67
|
+
let srcChocoFile = null;
|
|
68
|
+
|
|
69
|
+
if (htmlExists && chocoExists) {
|
|
70
|
+
throwError("Can't have both .choco and .html source index files at a time: please remove one of the two");
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (htmlExists) {
|
|
74
|
+
try {
|
|
75
|
+
srcHtmlFile = await readMyFile(srcHtmlPath);
|
|
76
|
+
return { srcHtmlFile: srcHtmlFile, srcChocoFile: srcChocoFile }
|
|
77
|
+
} catch (err) {
|
|
78
|
+
throwError(err);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (chocoExists) {
|
|
83
|
+
throwError(".choco files are not supported yet")
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Generates the new CSS files attached to the build index.html.
|
|
89
|
+
* @param {HTMLLinkElement} link
|
|
90
|
+
* @param {PathLike} __rootdir
|
|
91
|
+
* @param {PathLike} __srcdir
|
|
92
|
+
* @param {PathLike} outDirPath
|
|
93
|
+
* @param {Array} fileIds
|
|
94
|
+
*/
|
|
95
|
+
export async function processStylesheet(link, __rootdir, __srcdir, outDirPath, fileIds) {
|
|
96
|
+
try {
|
|
97
|
+
const href = link.href;
|
|
98
|
+
const stylesheetPath = path.join(__rootdir, __srcdir, href);
|
|
99
|
+
const css = await fs.readFile(stylesheetPath, { encoding: "utf8" });
|
|
100
|
+
const cssFileName = "css-" + genRandomId(fileIds, 6) + ".css";
|
|
101
|
+
|
|
102
|
+
await fs.writeFile(path.join(outDirPath, cssFileName), css);
|
|
103
|
+
|
|
104
|
+
link.setAttribute("href", "./" + cssFileName);
|
|
105
|
+
} catch (err) {
|
|
106
|
+
throwError(err)
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import fs from "fs/promises";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import { pathToFileURL } from "url";
|
|
4
|
+
import chalk from "chalk";
|
|
5
|
+
|
|
6
|
+
export function throwError(err) {
|
|
7
|
+
console.log(chalk.red.bold("Error!"), "A fatal error has ocurred:\n");
|
|
8
|
+
throw new Error(err)
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Returns the given module function adjusted to work with NPM without any dependency.
|
|
13
|
+
* @param {import("fs").PathLike} filePath
|
|
14
|
+
* @returns {Function}
|
|
15
|
+
*/
|
|
16
|
+
export async function loadWithAssets(filePath) {
|
|
17
|
+
let code = await fs.readFile(filePath, "utf8");
|
|
18
|
+
|
|
19
|
+
const importRegex = /import\s+(\w+)\s+from\s+["'](.+?\.(html|css))["'];?/g;
|
|
20
|
+
|
|
21
|
+
let match;
|
|
22
|
+
while ((match = importRegex.exec(code))) {
|
|
23
|
+
const varName = match[1];
|
|
24
|
+
const relPath = match[2];
|
|
25
|
+
|
|
26
|
+
const absPath = path.resolve(path.dirname(filePath), relPath);
|
|
27
|
+
let content = await fs.readFile(absPath, "utf8");
|
|
28
|
+
|
|
29
|
+
const replacement = `const ${varName} = ${JSON.stringify(content)};`;
|
|
30
|
+
|
|
31
|
+
code = code.replace(match[0], replacement);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const dataUrl =
|
|
35
|
+
"data:text/javascript;base64," +
|
|
36
|
+
Buffer.from(code).toString("base64");
|
|
37
|
+
|
|
38
|
+
const mod = await import(dataUrl);
|
|
39
|
+
return mod;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function genRandomId(compIdColl, length = 10) {
|
|
43
|
+
const id = Math.random().toString(36).substring(2, length + 2);
|
|
44
|
+
if (compIdColl.includes(id)) { return genRandomCompId() }
|
|
45
|
+
else {
|
|
46
|
+
compIdColl.push(id);
|
|
47
|
+
return id
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
export function incrementAlfabet(letters) {
|
|
52
|
+
const alfabet = "abcdefghijklmnopqrstuvwxyz";
|
|
53
|
+
let arr = letters.split("");
|
|
54
|
+
let i = arr.length - 1;
|
|
55
|
+
while (i >= 0) {
|
|
56
|
+
let pos = alfabet.indexOf(arr[i]);
|
|
57
|
+
if (pos < 25) {
|
|
58
|
+
arr[i] = alfabet[pos + 1];
|
|
59
|
+
return arr.join("");
|
|
60
|
+
} else {
|
|
61
|
+
arr[i] = "a";
|
|
62
|
+
i--;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return "a" + arr.join("");
|
|
66
|
+
}
|
package/dev/index.js
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import http from "http";
|
|
2
|
+
import fs from "fs";
|
|
3
|
+
import path from "path";
|
|
4
|
+
import chalk from "chalk";
|
|
5
|
+
import compile from "../compiler/index.js";
|
|
6
|
+
import { getChocolaConfig } from "../utils.js";
|
|
7
|
+
|
|
8
|
+
export async function serve(__rootdir) {
|
|
9
|
+
await compile(__rootdir);
|
|
10
|
+
|
|
11
|
+
let __outdir = "dist";
|
|
12
|
+
let __config = {
|
|
13
|
+
hostname: "127.0.0.1",
|
|
14
|
+
port: 3000,
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const config = await getChocolaConfig(__rootdir);
|
|
18
|
+
const devConfig = config.dev;
|
|
19
|
+
|
|
20
|
+
if (devConfig.hostname) { __config.hostname = devConfig.hostname }
|
|
21
|
+
else { console.warn(chalk.bold.yellow("WARNING!"), `hostname not defined in chocola.config.json file: using default ${__config.hostname} hostname.`) }
|
|
22
|
+
|
|
23
|
+
if (devConfig.port) { __config.port = devConfig.port }
|
|
24
|
+
else { console.warn(chalk.bold.yellow("WARNING!"), `port not defined in chocola.config.json file: using default ${__config.port} port.`) }
|
|
25
|
+
|
|
26
|
+
const server = http.createServer((req, res) => {
|
|
27
|
+
let filePath = path.join(
|
|
28
|
+
__outdir,
|
|
29
|
+
req.url === "/" ? "index.html" : req.url
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
const extname = String(path.extname(filePath)).toLowerCase();
|
|
33
|
+
const mimeTypes = {
|
|
34
|
+
".html": "text/html",
|
|
35
|
+
".js": "text/javascript",
|
|
36
|
+
".css": "text/css",
|
|
37
|
+
".json": "application/json",
|
|
38
|
+
".png": "image/png",
|
|
39
|
+
".jpg": "image/jpg",
|
|
40
|
+
".gif": "image/gif",
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const contentType = mimeTypes[extname] || "application/octet-stream";
|
|
44
|
+
|
|
45
|
+
fs.readFile(filePath, (error, content) => {
|
|
46
|
+
if (error) {
|
|
47
|
+
if (error.code === "ENOENT") {
|
|
48
|
+
res.writeHead(404, { "Content-Type": "text/html" });
|
|
49
|
+
res.end("<h1>404 Not Found</h1>", "utf-8");
|
|
50
|
+
} else {
|
|
51
|
+
res.writeHead(500);
|
|
52
|
+
res.end("Error interno: " + error.code);
|
|
53
|
+
}
|
|
54
|
+
} else {
|
|
55
|
+
res.writeHead(200, { "Content-Type": contentType });
|
|
56
|
+
res.end(content, "utf-8");
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
server.listen(__config.port, __config.hostname, () => {
|
|
62
|
+
console.log(`Chocola App running at http://${__config.hostname}:${__config.port}/`);
|
|
63
|
+
});
|
|
64
|
+
}
|
package/index.js
CHANGED
|
@@ -1,3 +1,31 @@
|
|
|
1
|
-
import
|
|
1
|
+
import compile from "./compiler/index.js";
|
|
2
|
+
import * as development from "./dev/index.js";
|
|
2
3
|
|
|
3
|
-
|
|
4
|
+
/**
|
|
5
|
+
* An intrinsic object that contains the Chocola App methods.
|
|
6
|
+
*/
|
|
7
|
+
export const app = {
|
|
8
|
+
/**
|
|
9
|
+
* Initializes your Chocola App using a root directory and a source directory.
|
|
10
|
+
*
|
|
11
|
+
* ```js
|
|
12
|
+
* import { app } from "chocola"
|
|
13
|
+
import path from "path";
|
|
14
|
+
import { fileURLToPath } from "url";
|
|
15
|
+
|
|
16
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
17
|
+
const __dirname = path.dirname(__filename);
|
|
18
|
+
|
|
19
|
+
app.build(__dirname, "src");
|
|
20
|
+
```
|
|
21
|
+
* @example
|
|
22
|
+
* @param {PathLike} __rootdir the directory where your Chocola App is
|
|
23
|
+
* @param {PathLike} __srcdir the directory where your app sources files are
|
|
24
|
+
* @returns {Object}
|
|
25
|
+
*/
|
|
26
|
+
build(__rootdir, __srcdir) { compile(__rootdir, __srcdir) }
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export const dev = {
|
|
30
|
+
server(__rootdir, __srcdir, port) { development.serve(__rootdir, __srcdir, port) }
|
|
31
|
+
};
|
package/package.json
CHANGED
package/utils.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { promises as fs } from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import { throwError } from "./compiler/utils.js";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Returns `chocola.config.json` as an object.
|
|
7
|
+
* @param {import("fs").PathLike} __rootdir
|
|
8
|
+
* @returns {Object}
|
|
9
|
+
*/
|
|
10
|
+
export async function getChocolaConfig(__rootdir) {
|
|
11
|
+
try {
|
|
12
|
+
const config = await fs.readFile(path.join(__rootdir, "chocola.config.json"), "utf-8");
|
|
13
|
+
return JSON.parse(config);
|
|
14
|
+
} catch(err) {
|
|
15
|
+
throwError("An error ocurred while fetching the Chocola config file:\n" + err);
|
|
16
|
+
}
|
|
17
|
+
}
|
package/runtime/index.js
DELETED
|
@@ -1,116 +0,0 @@
|
|
|
1
|
-
import { throwError } from "./utils.js";
|
|
2
|
-
import { JSDOM } from "jsdom";
|
|
3
|
-
import path from "path";
|
|
4
|
-
import { promises as fs } from "fs";
|
|
5
|
-
import { getComponents, getSrcIndex } from "./pipeline.js";
|
|
6
|
-
import chalk from 'chalk';
|
|
7
|
-
import beautify from "js-beautify";
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
const logSeparation = chalk.yellow(`
|
|
11
|
-
________________________________________________________________________
|
|
12
|
-
========================================================================
|
|
13
|
-
`)
|
|
14
|
-
|
|
15
|
-
export default async function runtime(__rootdir, __srcdir) {
|
|
16
|
-
console.log(chalk.bold.hex("#945e33")(`\n RUNNING CHOCOLA BUNDLER`))
|
|
17
|
-
console.log(logSeparation);
|
|
18
|
-
console.log(chalk.hex("#945e33")(`
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
▄████▄ ██░ ██ ▒█████ ▄████▄ ▒█████ ██▓ ▄▄▄
|
|
22
|
-
▒██▀ ▀█ ▓██░ ██▒▒██▒ ██▒▒██▀ ▀█ ▒██▒ ██▒▓██▒ ▒████▄
|
|
23
|
-
▒▓█ ▄ ▒██▀▀██░▒██░ ██▒▒▓█ ▄ ▒██░ ██▒▒██░ ▒██ ▀█▄
|
|
24
|
-
▒▓▓▄ ▄██▒░▓█ ░██ ▒██ ██░▒▓▓▄ ▄██▒▒██ ██░▒██░ ░██▄▄▄▄██
|
|
25
|
-
▒ ▓███▀ ░░▓█▒░██▓░ ████▓▒░▒ ▓███▀ ░░ ████▓▒░░██████▒▓█ ▓██▒
|
|
26
|
-
░ ░▒ ▒ ░ ▒ ░░▒░▒░ ▒░▒░▒░ ░ ░▒ ▒ ░░ ▒░▒░▒░ ░ ▒░▓ ░▒▒ ▓▒█░
|
|
27
|
-
░ ▒ ▒ ░▒░ ░ ░ ▒ ▒░ ░ ▒ ░ ▒ ▒░ ░ ░ ▒ ░ ▒ ▒▒ ░
|
|
28
|
-
░ ░ ░░ ░░ ░ ░ ▒ ░ ░ ░ ░ ▒ ░ ░ ░ ▒
|
|
29
|
-
░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░
|
|
30
|
-
░ ░
|
|
31
|
-
|
|
32
|
-
`));
|
|
33
|
-
console.log(logSeparation);
|
|
34
|
-
|
|
35
|
-
const srcPath = path.join(__rootdir, __srcdir);
|
|
36
|
-
const srcComponents = path.join(srcPath, "lib")
|
|
37
|
-
|
|
38
|
-
let indexFiles = await getSrcIndex(srcPath);
|
|
39
|
-
let srcHtmlFile = indexFiles.srcHtmlFile;
|
|
40
|
-
let srcChocoFile = indexFiles.srcChocoFile;
|
|
41
|
-
|
|
42
|
-
const outDirPath = path.join(__rootdir, "dist");
|
|
43
|
-
|
|
44
|
-
console.log(`
|
|
45
|
-
▄▄▄▄ ▄▄▄ ▄▄ ▄▄ ▄▄▄▄ ▄▄▄ ▄▄ ▄▄ ▄▄▄▄▄ ▄▄ ▄▄ ▄▄▄▄▄▄ ▄▄▄▄
|
|
46
|
-
██▀▀▀ ██▀██ ██▀▄▀██ ██▄█▀ ██▀██ ███▄██ ██▄▄ ███▄██ ██ ███▄▄
|
|
47
|
-
▀████ ▀███▀ ██ ██ ██ ▀███▀ ██ ▀██ ██▄▄▄ ██ ▀██ ██ ▄▄██▀
|
|
48
|
-
|
|
49
|
-
`)
|
|
50
|
-
|
|
51
|
-
const foundComponents = await getComponents(srcComponents);
|
|
52
|
-
|
|
53
|
-
let loadedComponents = foundComponents.loadedComponents;
|
|
54
|
-
let notDefComps = foundComponents.notDefComps;
|
|
55
|
-
|
|
56
|
-
const srcDirData = {
|
|
57
|
-
index: srcHtmlFile || srcChocoFile,
|
|
58
|
-
components: foundComponents.componentsLib,
|
|
59
|
-
};
|
|
60
|
-
|
|
61
|
-
console.log(chalk.bold.green(">"), "Components found in", chalk.green.underline(srcComponents) + ":");
|
|
62
|
-
console.log(" ", srcDirData.components, "\n");
|
|
63
|
-
|
|
64
|
-
if (notDefComps.length > 0) {
|
|
65
|
-
console.warn(chalk.bold.yellow("WARNING!"), "The following components don't include a default export:");
|
|
66
|
-
console.log(" ", notDefComps);
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
console.log(logSeparation);
|
|
70
|
-
|
|
71
|
-
console.log(`
|
|
72
|
-
▄▄▄▄ ▄▄ ▄▄ ▄▄ ▄▄ ▄▄▄▄ ▄▄ ▄▄ ▄▄ ▄▄▄▄
|
|
73
|
-
██▄██ ██ ██ ██ ██ ██▀██ ██ ███▄██ ██ ▄▄
|
|
74
|
-
██▄█▀ ▀███▀ ██ ██▄▄▄ ████▀ ██ ██ ▀██ ▀███▀
|
|
75
|
-
|
|
76
|
-
`);
|
|
77
|
-
console.log(chalk.bold.green(">"), "Creating Chocola static build in directory", chalk.green.underline(outDirPath) + "\n");
|
|
78
|
-
|
|
79
|
-
console.log(logSeparation)
|
|
80
|
-
|
|
81
|
-
const dom = new JSDOM(srcDirData.index);
|
|
82
|
-
const doc = dom.window.document;
|
|
83
|
-
const appContainer = doc.querySelector("app");
|
|
84
|
-
|
|
85
|
-
if (!appContainer) {
|
|
86
|
-
throwError("Index page must have an " + chalk.blue("<app>") + " element");
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
const appElements = Array.from(appContainer.children);
|
|
90
|
-
|
|
91
|
-
appElements.forEach(el => {
|
|
92
|
-
const tagName = el.tagName.toLowerCase();
|
|
93
|
-
const compName = tagName + ".js";
|
|
94
|
-
|
|
95
|
-
const instance = loadedComponents.get(compName);
|
|
96
|
-
if (instance && instance.body) {
|
|
97
|
-
const fragment = JSDOM.fragment(instance.body);
|
|
98
|
-
el.replaceWith(fragment);
|
|
99
|
-
} else {
|
|
100
|
-
console.warn(chalk.yellow(`No se pudo renderizar ${compName}`));
|
|
101
|
-
}
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
const finalHtml = dom.serialize();
|
|
105
|
-
const prettyHtml = beautify.html(finalHtml, { indent_size: 2 });
|
|
106
|
-
await fs.writeFile(path.join(outDirPath, "index.html"), prettyHtml);
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
console.log(`
|
|
110
|
-
▄▄ ▄▄▄ ▄▄▄▄ ▄▄▄▄ ▄▄▄ ▄▄ ▄▄ ▄▄▄▄▄ ██
|
|
111
|
-
██ ██▀██ ██▄██ ██▀██ ██▀██ ███▄██ ██▄▄ ██
|
|
112
|
-
▄▄█▀ ▀███▀ ██▄█▀ ████▀ ▀███▀ ██ ▀██ ██▄▄▄ ▄▄
|
|
113
|
-
|
|
114
|
-
`);
|
|
115
|
-
console.log(chalk.bold.green(">"), "Project bundled succesfully at", chalk.green.underline(outDirPath) + "\n\n");
|
|
116
|
-
}
|
package/runtime/package.json
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@chocola/runtime",
|
|
3
|
-
"version": "1.0.0",
|
|
4
|
-
"description": "",
|
|
5
|
-
"main": "index.js",
|
|
6
|
-
"scripts": {
|
|
7
|
-
"test": "echo \"Error: no test specified\" && exit 1"
|
|
8
|
-
},
|
|
9
|
-
"keywords": [],
|
|
10
|
-
"author": "SadGabi",
|
|
11
|
-
"license": "ISC",
|
|
12
|
-
"type": "module"
|
|
13
|
-
}
|
package/runtime/pipeline.js
DELETED
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
import { pathToFileURL } from "node:url";
|
|
2
|
-
import { promises as fs } from "fs";
|
|
3
|
-
import { throwError } from "./utils.js";
|
|
4
|
-
import { readMyFile, checkFile } from "./fs.js";
|
|
5
|
-
import path from "path";
|
|
6
|
-
|
|
7
|
-
export async function getComponents(libDir) {
|
|
8
|
-
let componentsLib = [];
|
|
9
|
-
let loadedComponents = new Map();
|
|
10
|
-
let notDefComps = [];
|
|
11
|
-
|
|
12
|
-
try {
|
|
13
|
-
const components = await fs.readdir(libDir);
|
|
14
|
-
|
|
15
|
-
if (!components) {
|
|
16
|
-
throwError("Your src directory must have a `lib` folder for your components");
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
for (const comp of components) {
|
|
20
|
-
if (!comp.endsWith(".js") && comp[0] !== comp[0].toUpperCase()) continue;
|
|
21
|
-
|
|
22
|
-
componentsLib.push(comp);
|
|
23
|
-
|
|
24
|
-
const fileUrl = pathToFileURL(path.join(libDir, comp)).href;
|
|
25
|
-
const module = await import(fileUrl);
|
|
26
|
-
|
|
27
|
-
if (typeof module.default === "function") {
|
|
28
|
-
const instance = module.default();
|
|
29
|
-
loadedComponents.set(comp.toLowerCase(), instance);
|
|
30
|
-
} else {
|
|
31
|
-
notDefComps.push(comp)
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
return {componentsLib, loadedComponents, notDefComps};
|
|
36
|
-
} catch (err) {
|
|
37
|
-
throwError(err);
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
export async function getSrcIndex(srcPath) {
|
|
42
|
-
const srcHtmlPath = path.join(srcPath, "index.html");
|
|
43
|
-
const srcChocoPath = path.join(srcPath, "index.choco");
|
|
44
|
-
|
|
45
|
-
const htmlExists = await checkFile(srcHtmlPath);
|
|
46
|
-
const chocoExists = await checkFile(srcChocoPath);
|
|
47
|
-
|
|
48
|
-
let srcHtmlFile = null;
|
|
49
|
-
let srcChocoFile = null;
|
|
50
|
-
|
|
51
|
-
if (htmlExists && chocoExists) {
|
|
52
|
-
throwError("Can't have both .choco and .html source index files at a time: please remove one of the two");
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
if (htmlExists) {
|
|
56
|
-
try {
|
|
57
|
-
srcHtmlFile = await readMyFile(srcHtmlPath);
|
|
58
|
-
return {srcHtmlFile: srcHtmlFile, srcChocoFile: srcChocoFile}
|
|
59
|
-
} catch (err) {
|
|
60
|
-
throwError(err);
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
if (chocoExists) {
|
|
65
|
-
throwError(".choco files are not supported yet")
|
|
66
|
-
}
|
|
67
|
-
}
|
package/runtime/utils.js
DELETED
|
File without changes
|