chocola 1.1.20 → 1.2.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/compiler/fs.js CHANGED
@@ -1,20 +1,31 @@
1
1
  import { promises as fs } from "fs";
2
2
  import { throwError } from "./utils.js";
3
3
 
4
+ /**
5
+ * Reads a file and returns its contents as a UTF-8 string
6
+ * @param {import("fs").PathLike} filePath - Path to the file
7
+ * @returns {Promise<string>} - File contents
8
+ * @throws {Error} - If file cannot be read
9
+ */
4
10
  export async function readMyFile(filePath) {
5
- try {
6
- const data = await fs.readFile(filePath, "utf-8");
7
- return data;
8
- } catch (error) {
9
- throwError(`Got an error trying to read the file: ${error.message}`);
10
- }
11
+ try {
12
+ const data = await fs.readFile(filePath, "utf-8");
13
+ return data;
14
+ } catch (error) {
15
+ throwError(`Got an error trying to read the file: ${error.message}`);
16
+ }
11
17
  }
12
18
 
19
+ /**
20
+ * Checks if a file exists
21
+ * @param {import("fs").PathLike} filePath - Path to check
22
+ * @returns {Promise<boolean>} - True if file exists, false otherwise
23
+ */
13
24
  export async function checkFile(filePath) {
14
- try {
15
- await fs.access(filePath);
16
- return true;
17
- } catch {
18
- return false;
19
- }
25
+ try {
26
+ await fs.access(filePath);
27
+ return true;
28
+ } catch {
29
+ return false;
30
+ }
20
31
  }
package/compiler/index.js CHANGED
@@ -1,27 +1,48 @@
1
- import { throwError, genRandomId, incrementAlfabet, isWebLink } from "./utils.js";
2
- import { JSDOM } from "jsdom";
3
1
  import path from "path";
4
2
  import { promises as fs } from "fs";
5
- import { copyResources, getComponents, getSrcIndex, processIcons, processStylesheet } from "./pipeline.js";
6
- import chalk from 'chalk';
7
- import beautify from "js-beautify";
8
- import { getChocolaConfig } from "../utils.js";
9
-
3
+ import chalk from "chalk";
4
+
5
+ // Configuration
6
+ import { loadConfig, resolvePaths } from "./config.js";
7
+
8
+ // DOM Processing
9
+ import {
10
+ createDOM,
11
+ validateAppContainer,
12
+ getAppElements,
13
+ getAssetLinks,
14
+ appendRuntimeScript,
15
+ serializeDOM,
16
+ writeHTMLOutput,
17
+ } from "./dom-processor.js";
18
+
19
+ // Component & Runtime Processing
20
+ import { processAllComponents } from "./component-processor.js";
21
+ import { generateRuntimeScript } from "./runtime-generator.js";
22
+
23
+ // Utilities & Pipeline
24
+ import { throwError } from "./utils.js";
25
+ import {
26
+ copyResources,
27
+ getComponents,
28
+ getSrcIndex,
29
+ processIcons,
30
+ processStylesheet,
31
+ } from "./pipeline.js";
32
+
33
+
34
+ // ===== Logging & Banners =====
10
35
 
11
36
  const logSeparation = chalk.yellow(`
12
37
  ________________________________________________________________________
13
38
  ========================================================================
14
39
  `);
15
40
 
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")(`
41
+ function logBanner() {
42
+ console.log(chalk.bold.hex("#945e33")(`\n RUNNING CHOCOLA BUNDLER`));
43
+ console.log(logSeparation);
44
+ console.log(
45
+ chalk.hex("#945e33")(`
25
46
 
26
47
 
27
48
  ▄████▄ ██░ ██ ▒█████ ▄████▄ ▒█████ ██▓ ▄▄▄
@@ -30,171 +51,129 @@ export default async function runtime(__rootdir) {
30
51
  ▒▓▓▄ ▄██▒░▓█ ░██ ▒██ ██░▒▓▓▄ ▄██▒▒██ ██░▒██░ ░██▄▄▄▄██
31
52
  ▒ ▓███▀ ░░▓█▒░██▓░ ████▓▒░▒ ▓███▀ ░░ ████▓▒░░██████▒▓█ ▓██▒
32
53
  ░ ░▒ ▒ ░ ▒ ░░▒░▒░ ▒░▒░▒░ ░ ░▒ ▒ ░░ ▒░▒░▒░ ░ ▒░▓ ░▒▒ ▓▒█░
33
- ░ ▒ ▒ ░▒░ ░ ░ ▒ ▒░ ░ ▒ ░ ▒░ ░ ░ ▒ ░ ▒▒ ░
34
- ░ ░ ░░ ░░ ░ ░ ▒ ░ ░ ░ ░ ▒ ░ ░ ░ ▒
54
+ ░ ▒ ▒ ░▒░ ░ ░ ▒ ▒░ ░ ▒ ░ ▒░ ░ ░ ▒ ░ ▒▒ ░
55
+ ░ ░ ░░ ░░ ░ ░ ▒ ░ ░ ░ ░ ▒ ░ ░ ░ ▒
35
56
  ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░
36
57
  ░ ░
37
58
 
38
59
 
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.querySelectorAll('*'));
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 === undefined) return;
133
- if (instance && instance.body) {
134
- let body = instance.body;
135
- body = body.replace(/\$\{ctx\.(\w+)\}/g, (_, key) => ctx[key] || "");
136
- const fragment = JSDOM.fragment(body);
137
- const firstChild = fragment.firstChild;
138
- if (firstChild && firstChild.nodeType === 1) {
139
- if (instance.script || instance.effects) {
140
- let script;
141
- let effects;
142
- const compId = "chid-" + genRandomId(compIdColl);
143
- firstChild.setAttribute("chid", compId);
144
- instance.script && (script = instance.script.toString())
145
-
146
- if (!letter) {
147
- letter = "a";
148
- } else {
149
- letter = incrementAlfabet(letter);
150
- }
151
-
152
- script = script.replace(/RUNTIME/g, `${letter}RUNTIME`);
153
-
154
- runtimeChunks.push(`
155
- const ${letter} = document.querySelector('[chid="${compId}"]');
156
- ${script}
157
- ${letter}RUNTIME(${letter}, ${JSON.stringify(ctx)});`);
158
- }
159
- }
160
- el.replaceWith(fragment);
161
- } else {
162
- console.warn(chalk.yellow(`${compName} component could not be loaded`));
163
- }
164
- });
165
-
166
- runtimeScript = runtimeChunks.join("\n");
167
-
168
- let fileIds = [];
169
-
170
- const docLinks = Array.from(doc.querySelectorAll("link"));
171
-
172
- for (const link of docLinks) {
173
- const rel = link.rel;
174
- if (rel === "stylesheet") await processStylesheet(link, __rootdir, __srcdir, outDirPath, fileIds);
175
- if (rel === "icon") await processIcons(link, __rootdir, __srcdir, outDirPath, fileIds)
176
- }
177
-
178
-
179
- const runtimeFilename = "run-" + genRandomId(fileIds, 6) + ".js";
180
- const runtimeFileContents = `document.addEventListener("DOMContentLoaded", () => {${runtimeScript}})`;
181
- await fs.writeFile(path.join(outDirPath, runtimeFilename), runtimeFileContents);
182
- const runtimeScriptEl = doc.createElement("script");
183
- runtimeScriptEl.type = "module";
184
- runtimeScriptEl.src = "./" + runtimeFilename;
185
- doc.body.appendChild(runtimeScriptEl);
186
- const finalHtml = dom.serialize();
187
- const prettyHtml = beautify.html(finalHtml, { indent_size: 2 });
188
-
189
- await fs.writeFile(path.join(outDirPath, "index.html"), prettyHtml);
190
-
191
- await copyResources(__rootdir, __srcdir, outDirPath);
192
-
193
- console.log(`
60
+ `)
61
+ );
62
+ }
63
+
64
+ function logSuccess(outDirPath) {
65
+ console.log(`
194
66
  ▄▄ ▄▄▄ ▄▄▄▄ ▄▄▄▄ ▄▄▄ ▄▄ ▄▄ ▄▄▄▄▄ ██
195
67
  ██ ██▀██ ██▄██ ██▀██ ██▀██ ███▄██ ██▄▄ ██
196
68
  ▄▄█▀ ▀███▀ ██▄█▀ ████▀ ▀███▀ ██ ▀██ ██▄▄▄ ▄▄
197
69
 
198
70
  `);
199
- console.log(chalk.bold.green(">"), "Project bundled succesfully at", chalk.green.underline(outDirPath) + "\n\n");
71
+ console.log(
72
+ chalk.bold.green(">"),
73
+ "Project bundled succesfully at",
74
+ chalk.green.underline(outDirPath) + "\n\n"
75
+ );
76
+ }
77
+
78
+ // ===== Directory Setup =====
79
+
80
+ async function setupOutputDirectory(outDirPath, emptyOutDir) {
81
+ if (emptyOutDir) {
82
+ await fs.rm(outDirPath, { recursive: true, force: true });
83
+ await fs.mkdir(outDirPath);
84
+ }
85
+ }
86
+
87
+ // ===== Component Loading =====
88
+
89
+ async function loadAndDisplayComponents(srcComponentsPath) {
90
+ const foundComponents = await getComponents(srcComponentsPath);
91
+ const { loadedComponents, notDefComps, componentsLib } = foundComponents;
92
+
93
+ console.log(` LOADING COMPONENTS`);
94
+ console.log(chalk.bold.green(">"), "Components found in", chalk.green.underline(srcComponentsPath) + ":");
95
+ console.log(" ", componentsLib, "\n");
96
+
97
+ if (notDefComps.length > 0) {
98
+ console.warn(chalk.bold.yellow("WARNING!"), "The following components don't include a default export:");
99
+ console.log(" ", notDefComps);
100
+ }
101
+
102
+ return loadedComponents;
103
+ }
104
+
105
+ // ===== Asset Processing =====
106
+
107
+ async function processAssets(doc, rootDir, srcDir, outDirPath) {
108
+ const { stylesheets, icons } = getAssetLinks(doc);
109
+ const fileIds = [];
110
+
111
+ for (const link of stylesheets) {
112
+ await processStylesheet(link, rootDir, srcDir, outDirPath, fileIds);
113
+ }
114
+
115
+ for (const link of icons) {
116
+ await processIcons(link, rootDir, srcDir, outDirPath, fileIds);
117
+ }
118
+ }
119
+
120
+ // ===== Main Compilation Function =====
121
+
122
+ /**
123
+ * Compiles a static build of your Chocola project from the directory provided.
124
+ * @param {import("fs").PathLike} rootDir
125
+ * @param {Object<{
126
+ * isHotReload?: boolean
127
+ * }
128
+ * >} config
129
+ */
130
+ export default async function runtime(rootDir, config) {
131
+ !config.isHotReload && logBanner();
132
+
133
+ // Load Configuration
134
+ const config = await loadConfig(rootDir);
135
+ const paths = resolvePaths(rootDir, config);
136
+ !config.isHotReload && console.log(logSeparation);
137
+
138
+ // Setup Output Directory
139
+ await setupOutputDirectory(paths.outDir, config.emptyOutDir);
140
+
141
+ // Load Index File
142
+ const indexFiles = await getSrcIndex(paths.src);
143
+ const srcIndexContent = indexFiles.srcHtmlFile || indexFiles.srcChocoFile;
144
+
145
+ // Load Components
146
+ const loadedComponents = await loadAndDisplayComponents(paths.components);
147
+ !config.isHotReload && console.log(logSeparation);
148
+
149
+ // Create and Validate DOM
150
+ !config.isHotReload && console.log(` BUNDLING STATIC BUILD`);
151
+ !config.isHotReload && console.log(chalk.bold.green(">"), "Creating Chocola static build in directory", chalk.green.underline(paths.outDir) + "\n");
152
+ !config.isHotReload && console.log(logSeparation);
153
+
154
+ const dom = createDOM(srcIndexContent);
155
+ const doc = dom.window.document;
156
+ const appContainer = validateAppContainer(doc);
157
+ const appElements = getAppElements(appContainer);
158
+
159
+ // Process Components
160
+ const { runtimeScript } = processAllComponents(appElements, loadedComponents);
161
+
162
+ // Generate Runtime File
163
+ const runtimeFilename = await generateRuntimeScript(runtimeScript, paths.outDir);
164
+
165
+ // Process Assets (stylesheets, icons)
166
+ await processAssets(doc, rootDir, config.srcDir, paths.outDir);
167
+
168
+ // Finalize HTML
169
+ appendRuntimeScript(doc, runtimeFilename);
170
+ const html = await serializeDOM(dom);
171
+ await writeHTMLOutput(html, paths.outDir);
172
+
173
+ // Copy Resources
174
+ await copyResources(rootDir, config.srcDir, paths.outDir);
175
+
176
+ // Success Message
177
+ !config.isHotReload && logSuccess(paths.outDir);
178
+ config.isHotReload && console.log("Dev server updated");
200
179
  }