chocola 2.0.0-next.1 → 2.0.0-next.4

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/utils.js CHANGED
@@ -3,85 +3,15 @@ import path from "path";
3
3
  import { pathToFileURL } from "url";
4
4
  import chalk from "chalk";
5
5
 
6
- /**
7
- * Throws a formatted error message and exits
8
- * @param {string|Error} err - Error message or Error object
9
- * @throws {Error}
10
- */
11
6
  export function throwError(err) {
12
7
  console.log(chalk.red.bold("Error!"), "A fatal error has occurred:\n");
13
8
  throw new Error(err);
14
9
  }
15
10
 
16
- /**
17
- * Loads a JavaScript module and inlines asset imports (HTML/CSS files)
18
- * This allows components to import external template files as strings
19
- * @param {import("fs").PathLike} filePath - Path to the JavaScript file
20
- * @returns {Promise<object>} - The imported module
21
- * @example
22
- * // Component with asset import
23
- * import template from "./button.html";
24
- * export default function Button() { return { body: template }; }
25
- */
26
- export async function loadWithAssets(filePath) {
27
- let code;
28
- try {
29
- code = await fs.readFile(filePath, "utf8");
30
- } catch (err) {
31
- throwError(`Failed to read component file "${filePath}": ${err.message || err}`);
32
- }
33
-
34
- const importRegex = /import\s+(\w+)\s+from\s+["'](.+?\.(html|css))["'];?/g;
35
-
36
- const replacements = [];
37
- let match;
38
- while ((match = importRegex.exec(code))) {
39
- const varName = match[1];
40
- const relPath = match[2];
41
- const absPath = path.resolve(path.dirname(filePath), relPath);
42
-
43
- let content;
44
- try {
45
- content = await fs.readFile(absPath, "utf8");
46
- } catch (err) {
47
- throwError(
48
- `Failed to read imported file "${relPath}" (resolved to "${absPath}") ` +
49
- `for variable "${varName}" in component "${filePath}": ${err.message || err}`
50
- );
51
- }
52
-
53
- replacements.push({ from: match[0], to: `const ${varName} = ${JSON.stringify(content)};` });
54
- }
55
-
56
- for (const { from, to } of replacements) {
57
- code = code.replace(from, to);
58
- }
59
-
60
- const dataUrl =
61
- "data:text/javascript;base64," +
62
- Buffer.from(code).toString("base64");
63
-
64
- try {
65
- const mod = await import(dataUrl);
66
- return mod;
67
- } catch (err) {
68
- throwError(
69
- `Failed to evaluate component "${filePath}": ${err.message || err}`
70
- );
71
- }
72
- }
73
-
74
- /**
75
- * Generates a random ID string
76
- * @param {Array} collection - Array to track used IDs (prevents duplicates)
77
- * @param {number} length - Desired length of the ID (default: 10)
78
- * @returns {string} - Unique random ID
79
- */
80
11
  export function genRandomId(collection = null, length = 10, lettersOnly = false) {
81
12
  let id;
82
13
  if (lettersOnly) {
83
- const letters = "abcdefghijklmnopqrstuvwxyz";
84
- id = Array.from({ length }, () => letters[Math.floor(Math.random() * letters.length)]).join("");
14
+ id = Array.from({ length }, () => ID_LETTERS[Math.floor(Math.random() * ID_LETTERS.length)]).join("");
85
15
  } else {
86
16
  id = Math.random().toString(36).substring(2, length + 2);
87
17
  }
@@ -94,23 +24,14 @@ export function genRandomId(collection = null, length = 10, lettersOnly = false)
94
24
  }
95
25
  }
96
26
 
97
- /**
98
- * Increments a letter or sequence of letters like Excel columns (a → b, z → aa)
99
- * @param {string} letters - Letter(s) to increment
100
- * @returns {string} - Incremented letter(s)
101
- * @example
102
- * incrementAlfabet("a") // "b"
103
- * incrementAlfabet("z") // "aa"
104
- */
105
27
  export function incrementAlfabet(letters) {
106
- const alfabet = "abcdefghijklmnopqrstuvwxyz";
107
28
  let arr = letters.split("");
108
29
  let i = arr.length - 1;
109
30
 
110
31
  while (i >= 0) {
111
- let pos = alfabet.indexOf(arr[i]);
32
+ let pos = arr[i].charCodeAt(0) - 97;
112
33
  if (pos < 25) {
113
- arr[i] = alfabet[pos + 1];
34
+ arr[i] = String.fromCharCode(pos + 97 + 1);
114
35
  return arr.join("");
115
36
  } else {
116
37
  arr[i] = "a";
@@ -121,11 +42,6 @@ export function incrementAlfabet(letters) {
121
42
  return "a" + arr.join("");
122
43
  }
123
44
 
124
- /**
125
- * Checks if a string is a valid HTTP/HTTPS URL
126
- * @param {string} str - String to check
127
- * @returns {boolean} - True if valid web link, false otherwise
128
- */
129
45
  export function isWebLink(str) {
130
46
  try {
131
47
  const url = new URL(str);
@@ -138,26 +54,28 @@ export function isWebLink(str) {
138
54
  const LBRACE_PH = "_%%CHOCOLA-LBRACE%%_";
139
55
  const RBRACE_PH = "_%%CHOCOLA-RBRACE%%_";
140
56
 
141
- /**
142
- * Replaces HTML entities for curly braces with placeholders before JSDOM parsing.
143
- * This prevents JSDOM from decoding them into literal braces before binding processing.
144
- * @param {string} html - Raw HTML content
145
- * @returns {string} - HTML with curly brace entities replaced
146
- */
147
57
  export function protectCurlyBraces(html) {
148
58
  return html
149
59
  .replace(/&(?:lbrace|#123|#x7B);/gi, LBRACE_PH)
150
60
  .replace(/&(?:rcub|#125|#x7D);/gi, RBRACE_PH);
151
61
  }
152
62
 
153
- /**
154
- * Restores curly brace placeholders back to literal characters.
155
- * Called after binding processing is complete and before final output.
156
- * @param {string} html - Serialized HTML with placeholders
157
- * @returns {string} - HTML with literal curly braces restored
158
- */
159
63
  export function restoreCurlyBraces(html) {
160
64
  return html
161
65
  .replace(/_%%CHOCOLA-LBRACE%%_/g, "{")
162
66
  .replace(/_%%CHOCOLA-RBRACE%%_/g, "}");
163
67
  }
68
+
69
+ const ID_LETTERS = "abcdefghijklmnopqrstuvwxyz";
70
+ const compiledCache = new Map();
71
+ export function compileExpression(expr, useCtx) {
72
+ const key = useCtx ? `ctx:${expr}` : `raw:${expr}`;
73
+ let fn = compiledCache.get(key);
74
+ if (!fn) {
75
+ fn = useCtx
76
+ ? new Function("ctx", `with(ctx) { return (${expr}); }`)
77
+ : new Function(`"use strict"; return (${expr})`);
78
+ compiledCache.set(key, fn);
79
+ }
80
+ return fn;
81
+ }
package/dev/index.js CHANGED
@@ -29,7 +29,6 @@ export async function serve(__rootdir) {
29
29
  else { console.warn(chalk.bold.yellow("WARNING!"), `port not defined in chocola.config.json file: using default ${__config.port} port.`) }
30
30
 
31
31
  const srcDir = paths.src;
32
- const componentsDir = paths.components;
33
32
 
34
33
  let compileTimeout;
35
34
  const scheduleRecompile = () => {
@@ -51,11 +50,6 @@ export async function serve(__rootdir) {
51
50
  scheduleRecompile();
52
51
  }
53
52
  });
54
- fs.watch(componentsDir, { recursive: true }, (eventType, filename) => {
55
- if (filename && !filename.includes("node_modules")) {
56
- scheduleRecompile();
57
- }
58
- });
59
53
 
60
54
  const server = http.createServer((req, res) => {
61
55
  if (req.url === "/api/hot-reload") {
@@ -174,5 +168,5 @@ app.build(__dirname);
174
168
  * @example
175
169
  * @param {PathLike} __rootdir the directory where your Chocola App is
176
170
  */
177
- server(__rootdir) { serve(__rootdir) }
171
+ async server(__rootdir) { return serve(__rootdir) }
178
172
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "chocola",
3
- "version": "2.0.0-next.1",
3
+ "version": "2.0.0-next.4",
4
4
  "description": "The sweetest way to build the web",
5
5
  "keywords": [
6
6
  "web",
@@ -32,8 +32,7 @@
32
32
  },
33
33
  "exports": {
34
34
  "./compiler": "./compiler/index.js",
35
- "./dev": "./dev/index.js",
36
- "./types": "./types/index.js"
35
+ "./dev": "./dev/index.js"
37
36
  },
38
37
  "dependencies": {
39
38
  "chalk": "^5.6.2",
package/utils.js CHANGED
@@ -2,11 +2,6 @@ import { promises as fs } from "fs";
2
2
  import path from "path";
3
3
  import { throwError } from "./compiler/utils.js";
4
4
 
5
- /**
6
- * Returns `chocola.config.json` as an object.
7
- * @param {import("fs").PathLike} __rootdir
8
- * @returns {Object}
9
- */
10
5
  export async function getChocolaConfig(__rootdir) {
11
6
  try {
12
7
  const config = await fs.readFile(path.join(__rootdir, "chocola.config.json"), "utf-8");