chocola 2.0.0-next.1 → 2.0.0-next.11

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.
@@ -0,0 +1,183 @@
1
+ import path from "path";
2
+ import { promises as fs } from "fs";
3
+ import {
4
+ createDOM,
5
+ validateAppContainer,
6
+ getAppElements,
7
+ getAssetLinks,
8
+ getScriptElements,
9
+ appendRuntimeScript,
10
+ appendStylesheetLink,
11
+ serializeDOM,
12
+ } from "./dom-processor.js";
13
+ import { processAllComponents } from "./component-processor.js";
14
+ import { generateRuntimeScript } from "./runtime-generator.js";
15
+ import { deterministicHash, throwError, warnConstantCondition, findElementLine } from "./utils.js";
16
+ import { compileExpr, evaluateConstant, hasMountIf, getMountIf, removeMountIf } from "../parser/index.js";
17
+ import {
18
+ processStylesheet,
19
+ processIcons,
20
+ processScript,
21
+ copyStaticDir,
22
+ } from "./pipeline.js";
23
+
24
+ function processPageConditionals(parent, sourceFile, sourceContent, ctx = {}) {
25
+ const ctxProxy = new Proxy(ctx, {
26
+ has() { return true; },
27
+ get(target, key) { return target[key]; },
28
+ });
29
+ const children = [...parent.children];
30
+ let chainActive = false;
31
+ let chainRendered = false;
32
+
33
+ const getLocation = (child) => {
34
+ if (!sourceContent) return sourceFile;
35
+ const lineNum = findElementLine(sourceContent, child.outerHTML);
36
+ return lineNum !== null ? `${sourceFile}:${lineNum}` : sourceFile;
37
+ };
38
+
39
+ const warnIfConstant = (expr, child, attr) => {
40
+ const { constant, value } = evaluateConstant(expr);
41
+ if (!constant) return;
42
+ warnConstantCondition(getLocation(child), child.tagName.toLowerCase(), attr, Boolean(value));
43
+ };
44
+
45
+ for (const child of children) {
46
+ const hasIf = child.hasAttribute("if");
47
+ const hasDelIf = hasMountIf(child);
48
+ const hasElif = child.hasAttribute("elif");
49
+ const hasElse = child.hasAttribute("else");
50
+
51
+ if (hasIf || hasDelIf || hasElif) {
52
+ const stripBraces = (raw) => raw.startsWith("{") ? raw.slice(1, -1) : raw;
53
+ if (hasIf) warnIfConstant(stripBraces(child.getAttribute("if")), child, "if");
54
+ if (hasDelIf) warnIfConstant(stripBraces(getMountIf(child)), child, "mount:if");
55
+ if (hasElif) warnIfConstant(stripBraces(child.getAttribute("elif")), child, "elif");
56
+ }
57
+
58
+ if (hasElif || hasElse) {
59
+ if (!chainActive) {
60
+ const tag = child.tagName.toLowerCase();
61
+ const attr = hasElif ? "elif" : "else";
62
+ throwError(`${getLocation(child)}\n <${tag}> has ${attr} without a preceding if/mount:if sibling`);
63
+ }
64
+ if (chainRendered) { child.remove(); continue; }
65
+ }
66
+
67
+ if (hasIf) {
68
+ const raw = child.getAttribute("if");
69
+ const expr = raw.startsWith("{") ? raw.slice(1, -1) : raw;
70
+ const fn = compileExpr(expr, true);
71
+ const result = fn(ctxProxy);
72
+ chainActive = true;
73
+ if (result) {
74
+ chainRendered = true;
75
+ } else {
76
+ child.style.display = "none";
77
+ chainRendered = false;
78
+ }
79
+ child.removeAttribute("if");
80
+ } else if (hasDelIf) {
81
+ const raw = getMountIf(child);
82
+ const expr = raw.startsWith("{") ? raw.slice(1, -1) : raw;
83
+ const fn = compileExpr(expr, true);
84
+ const result = fn(ctxProxy);
85
+ chainActive = true;
86
+ if (result) {
87
+ chainRendered = true;
88
+ } else {
89
+ child.remove();
90
+ chainRendered = false;
91
+ }
92
+ removeMountIf(child);
93
+ } else if (hasElif) {
94
+ const raw = child.getAttribute("elif");
95
+ const expr = raw.startsWith("{") ? raw.slice(1, -1) : raw;
96
+ const fn = compileExpr(expr, true);
97
+ const result = fn(ctxProxy);
98
+ if (result) {
99
+ chainRendered = true;
100
+ } else {
101
+ child.remove();
102
+ }
103
+ child.removeAttribute("elif");
104
+ } else if (hasElse) {
105
+ chainRendered = true;
106
+ chainActive = false;
107
+ child.removeAttribute("else");
108
+ } else {
109
+ chainActive = false;
110
+ chainRendered = false;
111
+ }
112
+
113
+ if (child.parentNode) {
114
+ processPageConditionals(child, sourceFile, sourceContent, ctx);
115
+ }
116
+ }
117
+ }
118
+
119
+ async function processAssets(doc, graph, out) {
120
+ const { stylesheets, icons } = getAssetLinks(doc);
121
+ const scripts = getScriptElements(doc);
122
+
123
+ for (const link of stylesheets) {
124
+ await processStylesheet(link, graph.rootDir, graph.config.srcDir, out);
125
+ }
126
+
127
+ for (const link of icons) {
128
+ await processIcons(link, graph.rootDir, graph.config.srcDir, out);
129
+ }
130
+
131
+ for (const script of scripts) {
132
+ await processScript(doc, script, graph.rootDir, graph.config.srcDir, out);
133
+ }
134
+ }
135
+
136
+ export async function renderPage(graph, ctx = {}) {
137
+ const out = {
138
+ files: [],
139
+ copies: [],
140
+ ids: [],
141
+ outDir: graph.paths.outDir,
142
+ };
143
+
144
+ const page = graph.page;
145
+ const dom = createDOM(page.source);
146
+ const doc = dom.document;
147
+ const appContainer = validateAppContainer(doc);
148
+
149
+ processPageConditionals(appContainer, page.sourcePath, dom.protectedContent, ctx);
150
+
151
+ const appElements = getAppElements(appContainer);
152
+ const { runtimeScript, scopesCss, hashMap, csrClasses } = processAllComponents(
153
+ appElements,
154
+ graph.loadedComponents,
155
+ page.sourcePath,
156
+ page.source,
157
+ ctx,
158
+ graph.config.treeShakeRuntime,
159
+ graph.originalComponentNames
160
+ );
161
+
162
+ const csrSource = await fs.readFile(new URL("../runtime/index.js", import.meta.url), "utf-8");
163
+ const runtimeFiles = generateRuntimeScript(runtimeScript, csrSource, csrClasses);
164
+
165
+ await processAssets(doc, graph, out);
166
+
167
+ if (scopesCss) {
168
+ const fileName = "sc-" + deterministicHash(scopesCss, 6) + ".css";
169
+ out.files.push({ path: fileName, content: scopesCss });
170
+ appendStylesheetLink(doc, fileName);
171
+ }
172
+
173
+ for (const file of runtimeFiles) {
174
+ out.files.push(file);
175
+ appendRuntimeScript(doc, file.path);
176
+ }
177
+
178
+ const html = await serializeDOM(dom);
179
+
180
+ await copyStaticDir(graph.paths.src, out);
181
+
182
+ return { html, hashMap, files: out.files, copies: out.copies };
183
+ }
@@ -1,19 +1,24 @@
1
- import { promises as fs } from "fs";
2
- import path from "path";
3
- import { genRandomId } from "./utils.js";
1
+ import { deterministicHash } from "./utils.js";
4
2
 
5
- /**
6
- * Generates a runtime script file and returns its filename
7
- * @param {string} runtimeScript - The JavaScript code for runtime
8
- * @param {import("fs").PathLike} outDirPath
9
- * @returns {Promise<string>} - filename of the generated runtime script
10
- */
11
- export async function generateRuntimeScript(runtimeScript, outDirPath) {
12
- const fileIds = [];
13
- const runtimeFilename = "run-" + genRandomId(fileIds, 6) + ".js";
14
- const runtimeFileContents = `document.addEventListener("DOMContentLoaded", () => {${runtimeScript}})`;
3
+ export function generateRuntimeScript(runtimeScript, csrSource, csrClasses) {
4
+ const files = [];
15
5
 
16
- await fs.writeFile(path.join(outDirPath, runtimeFilename), runtimeFileContents);
6
+ if (csrSource) {
7
+ const source = csrSource.replace(/^export\s+\{?\s*[^}]+\s*}?\s*;?\s*$/m, "");
8
+ const name = "run-" + deterministicHash(source, 6) + ".js";
9
+ files.push({ path: name, content: source });
10
+ }
17
11
 
18
- return runtimeFilename;
12
+ if (csrClasses) {
13
+ const name = "run-" + deterministicHash(csrClasses, 6) + ".js";
14
+ files.push({ path: name, content: csrClasses });
15
+ }
16
+
17
+ if (runtimeScript) {
18
+ const content = `document.addEventListener("DOMContentLoaded", () => {${runtimeScript}})`;
19
+ const name = "run-" + deterministicHash(content, 6) + ".js";
20
+ files.push({ path: name, content });
21
+ }
22
+
23
+ return files;
19
24
  }
package/compiler/utils.js CHANGED
@@ -1,87 +1,93 @@
1
- import fs from "fs/promises";
2
- import path from "path";
3
- import { pathToFileURL } from "url";
4
- import chalk from "chalk";
1
+ import { createHash } from "crypto";
2
+ import chalk from "./chalk.js";
5
3
 
6
- /**
7
- * Throws a formatted error message and exits
8
- * @param {string|Error} err - Error message or Error object
9
- * @throws {Error}
10
- */
11
4
  export function throwError(err) {
12
5
  console.log(chalk.red.bold("Error!"), "A fatal error has occurred:\n");
13
6
  throw new Error(err);
14
7
  }
15
8
 
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
- }
9
+ export function warnConstantCondition(location, tag, attr, expr, truthy) {
10
+ const article = /^[aeiou]/.test(attr) ? "an" : "a";
11
+ console.warn(
12
+ chalk.bold.yellow("WARN "),
13
+ `${chalk.bold.green(location)} ${chalk.dim('—')} ${chalk.blue(`<${tag}>`)} \`${chalk.bold(attr + "={" + expr + "}")}\` is always ${chalk.green(truthy ? "truthy" : "falsy")}`
14
+ );
15
+ }
33
16
 
34
- const importRegex = /import\s+(\w+)\s+from\s+["'](.+?\.(html|css))["'];?/g;
17
+ export function warnUnusedDeclaration(location, kind, name) {
18
+ console.warn(
19
+ chalk.bold.yellow("WARN "),
20
+ `${chalk.bold.green(location)} ${chalk.dim('—')} ${kind} \`${chalk.blue(name)}\` is never used`
21
+ );
22
+ }
35
23
 
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);
24
+ function normalizeAttributeQuotes(html) {
25
+ return html
26
+ .replace(/(\s[\w:.-]+)=(['"])([\s\S]*?)\2/g, '$1="$3"')
27
+ .replace(/(\s[\w:.-]+)=([^\s"'<>=]+)/g, '$1="$2"');
28
+ }
42
29
 
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
- );
30
+ function getStartTag(outerHTML) {
31
+ let inQuote = null;
32
+ for (let i = 0; i < outerHTML.length; i++) {
33
+ const c = outerHTML[i];
34
+ if (inQuote) {
35
+ if (c === inQuote) inQuote = null;
36
+ } else if (c === '"' || c === "'") {
37
+ inQuote = c;
38
+ } else if (c === ">") {
39
+ return outerHTML.slice(0, i + 1);
51
40
  }
52
-
53
- replacements.push({ from: match[0], to: `const ${varName} = ${JSON.stringify(content)};` });
54
41
  }
42
+ return outerHTML;
43
+ }
55
44
 
56
- for (const { from, to } of replacements) {
57
- code = code.replace(from, to);
58
- }
45
+ function escapeRegex(str) {
46
+ return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
47
+ }
59
48
 
60
- const dataUrl =
61
- "data:text/javascript;base64," +
62
- Buffer.from(code).toString("base64");
49
+ function flexibleStartTagRegex(startTag) {
50
+ return new RegExp(
51
+ startTag
52
+ .split(/\s+/)
53
+ .filter(Boolean)
54
+ .map(escapeRegex)
55
+ .join("\\s+")
56
+ );
57
+ }
63
58
 
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
- );
59
+ export function findElementLine(sourceContent, outerHTML) {
60
+ const source = normalizeAttributeQuotes(sourceContent).toLowerCase().replace(/\/\s*>/g, ">");
61
+ const startTags = [
62
+ normalizeAttributeQuotes(getStartTag(outerHTML)),
63
+ normalizeAttributeQuotes(
64
+ getStartTag(
65
+ outerHTML
66
+ .replace(/&quot;/g, '"')
67
+ .replace(/&amp;/g, "&")
68
+ .replace(/&lt;/g, "<")
69
+ .replace(/&gt;/g, ">")
70
+ )
71
+ ),
72
+ ].map(tag => tag.toLowerCase());
73
+ const candidates = [
74
+ flexibleStartTagRegex(startTags[0]),
75
+ flexibleStartTagRegex(startTags[0].replace(/=""/g, "")),
76
+ flexibleStartTagRegex(startTags[1]),
77
+ ];
78
+ for (const re of candidates) {
79
+ const match = re.exec(source);
80
+ if (match) {
81
+ return source.substring(0, match.index).split("\n").length;
82
+ }
71
83
  }
84
+ return null;
72
85
  }
73
86
 
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
87
  export function genRandomId(collection = null, length = 10, lettersOnly = false) {
81
88
  let id;
82
89
  if (lettersOnly) {
83
- const letters = "abcdefghijklmnopqrstuvwxyz";
84
- id = Array.from({ length }, () => letters[Math.floor(Math.random() * letters.length)]).join("");
90
+ id = Array.from({ length }, () => ID_LETTERS[Math.floor(Math.random() * ID_LETTERS.length)]).join("");
85
91
  } else {
86
92
  id = Math.random().toString(36).substring(2, length + 2);
87
93
  }
@@ -94,70 +100,17 @@ export function genRandomId(collection = null, length = 10, lettersOnly = false)
94
100
  }
95
101
  }
96
102
 
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
- export function incrementAlfabet(letters) {
106
- const alfabet = "abcdefghijklmnopqrstuvwxyz";
107
- let arr = letters.split("");
108
- let i = arr.length - 1;
109
-
110
- while (i >= 0) {
111
- let pos = alfabet.indexOf(arr[i]);
112
- if (pos < 25) {
113
- arr[i] = alfabet[pos + 1];
114
- return arr.join("");
115
- } else {
116
- arr[i] = "a";
117
- i--;
118
- }
119
- }
120
-
121
- return "a" + arr.join("");
122
- }
123
-
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
- export function isWebLink(str) {
130
- try {
131
- const url = new URL(str);
132
- return url.protocol === "http:" || url.protocol === "https:";
133
- } catch (e) {
134
- return false;
103
+ export function deterministicHash(content, length = 8) {
104
+ const hash = createHash("sha256").update(content).digest();
105
+ let result = "";
106
+ for (let i = 0; i < length; i++) {
107
+ result += String.fromCharCode(97 + (hash[i % hash.length] % 26));
135
108
  }
109
+ return result;
136
110
  }
137
111
 
138
- const LBRACE_PH = "_%%CHOCOLA-LBRACE%%_";
139
- const RBRACE_PH = "_%%CHOCOLA-RBRACE%%_";
140
-
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
- export function protectCurlyBraces(html) {
148
- return html
149
- .replace(/&(?:lbrace|#123|#x7B);/gi, LBRACE_PH)
150
- .replace(/&(?:rcub|#125|#x7D);/gi, RBRACE_PH);
112
+ export function runtimeFunctionId(compName) {
113
+ return "r_" + deterministicHash(compName, 8);
151
114
  }
152
115
 
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
- export function restoreCurlyBraces(html) {
160
- return html
161
- .replace(/_%%CHOCOLA-LBRACE%%_/g, "{")
162
- .replace(/_%%CHOCOLA-RBRACE%%_/g, "}");
163
- }
116
+ const ID_LETTERS = "abcdefghijklmnopqrstuvwxyz";
package/dev/index.js CHANGED
@@ -1,14 +1,15 @@
1
1
  import http from "http";
2
2
  import fs from "fs";
3
3
  import path from "path";
4
- import chalk from "chalk";
4
+ import chalk from "../compiler/chalk.js";
5
5
  import compile from "../compiler/index.js";
6
6
  import { loadConfig, resolvePaths } from "../compiler/config.js";
7
- import { getChocolaConfig } from "../utils.js";
7
+ import { getConfig, isMissingConfigFile, queueConfigWarning } from "../utils.js";
8
8
 
9
- export async function serve(__rootdir) {
10
- await compile(__rootdir);
9
+ const warnedDevHostname = new Set();
10
+ const warnedDevPort = new Set();
11
11
 
12
+ export async function serve(__rootdir, { silent = false, port: cliPort, hostname: cliHostname, open } = {}) {
12
13
  let __outdir = "dist";
13
14
  let __config = {
14
15
  hostname: "localhost",
@@ -17,19 +18,43 @@ export async function serve(__rootdir) {
17
18
 
18
19
  let lastBuildTime = Date.now();
19
20
 
20
- const fullConfig = await getChocolaConfig(__rootdir);
21
- const config = await loadConfig(__rootdir);
21
+ const fullConfig = await getConfig(__rootdir, { silent });
22
+ const config = await loadConfig(__rootdir, { silent });
22
23
  const paths = resolvePaths(__rootdir, config);
23
- const devConfig = fullConfig.dev;
24
24
 
25
- if (devConfig.hostname) { __config.hostname = devConfig.hostname }
26
- else { console.warn(chalk.bold.yellow("WARNING!"), `hostname not defined in chocola.config.json file: using default ${__config.hostname} hostname.`) }
25
+ if (cliPort) {
26
+ __config.port = cliPort;
27
+ } else if (!silent) {
28
+ if (isMissingConfigFile(fullConfig)) {
29
+ // top-level already warned; use defaults silently
30
+ } else if (fullConfig.dev != null) {
31
+ const devConfig = fullConfig.dev;
32
+ if (devConfig.port) { __config.port = devConfig.port }
33
+ else if (!warnedDevPort.has(__rootdir)) {
34
+ warnedDevPort.add(__rootdir);
35
+ queueConfigWarning(__rootdir, chalk.bold.yellow("WARNING!"), `dev.port not defined in chocola.config.json file: using default ${__config.port} dev.port.`);
36
+ }
37
+ }
38
+ }
27
39
 
28
- if (devConfig.port) { __config.port = devConfig.port }
29
- else { console.warn(chalk.bold.yellow("WARNING!"), `port not defined in chocola.config.json file: using default ${__config.port} port.`) }
40
+ if (cliHostname) {
41
+ __config.hostname = cliHostname;
42
+ } else if (!silent) {
43
+ if (!cliPort && isMissingConfigFile(fullConfig)) {
44
+ // top-level already warned
45
+ } else if (fullConfig.dev != null) {
46
+ const devConfig = fullConfig.dev;
47
+ if (devConfig.hostname) { __config.hostname = devConfig.hostname }
48
+ else if (!warnedDevHostname.has(__rootdir)) {
49
+ warnedDevHostname.add(__rootdir);
50
+ queueConfigWarning(__rootdir, chalk.bold.yellow("WARNING!"), `dev.hostname not defined in chocola.config.json file: using default ${__config.hostname} dev.hostname.`);
51
+ }
52
+ }
53
+ }
54
+
55
+ await compile(__rootdir);
30
56
 
31
57
  const srcDir = paths.src;
32
- const componentsDir = paths.components;
33
58
 
34
59
  let compileTimeout;
35
60
  const scheduleRecompile = () => {
@@ -51,11 +76,6 @@ export async function serve(__rootdir) {
51
76
  scheduleRecompile();
52
77
  }
53
78
  });
54
- fs.watch(componentsDir, { recursive: true }, (eventType, filename) => {
55
- if (filename && !filename.includes("node_modules")) {
56
- scheduleRecompile();
57
- }
58
- });
59
79
 
60
80
  const server = http.createServer((req, res) => {
61
81
  if (req.url === "/api/hot-reload") {
@@ -149,10 +169,22 @@ export async function serve(__rootdir) {
149
169
  });
150
170
  });
151
171
 
152
- server.listen(__config.port, __config.hostname, () => {
153
- console.log(`Chocola App running at http://${__config.hostname}:${__config.port}/`);
154
- });
155
- }
172
+ server.listen(__config.port, __config.hostname, async () => {
173
+ console.log('Live server running at', chalk.hex("#68C4EE").underline(`http://${__config.hostname}:${__config.port}/`));
174
+ if (open) {
175
+ const url = __config.hostname === "0.0.0.0" ? `http://localhost:${__config.port}` : `http://${__config.hostname}:${__config.port}`;
176
+ try {
177
+ const openPkg = await import("open");
178
+ openPkg.default(url);
179
+ } catch {
180
+ const { exec } = await import("child_process");
181
+ const cmd = process.platform === "win32" ? `start ${url}` : process.platform === "darwin" ? `open ${url}` : `xdg-open ${url}`;
182
+ exec(cmd);
183
+ }
184
+ }
185
+ });
186
+ return server;
187
+ }
156
188
 
157
189
  /**
158
190
  * An intrinsic object that contains the Chocola Dev methods.
@@ -174,5 +206,5 @@ app.build(__dirname);
174
206
  * @example
175
207
  * @param {PathLike} __rootdir the directory where your Chocola App is
176
208
  */
177
- server(__rootdir) { serve(__rootdir) }
209
+ async server(__rootdir) { return serve(__rootdir) }
178
210
  };
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.11",
4
4
  "description": "The sweetest way to build the web",
5
5
  "keywords": [
6
6
  "web",
@@ -13,11 +13,11 @@
13
13
  ],
14
14
  "homepage": "https://chocolaframework.web.app/",
15
15
  "bugs": {
16
- "url": "https://github.com/sad-gabi/chocola/issues"
16
+ "url": "https://github.com/chocolajs/chocola/issues"
17
17
  },
18
18
  "repository": {
19
19
  "type": "git",
20
- "url": "git+https://github.com/sad-gabi/chocola.git"
20
+ "url": "git+https://github.com/chocolajs/chocola.git"
21
21
  },
22
22
  "license": "MIT",
23
23
  "author": "SadGabi",
@@ -25,22 +25,33 @@
25
25
  "main": "index.js",
26
26
  "funding": {
27
27
  "type": "github",
28
- "url": "https://github.com/sponsors/sad-gabi"
28
+ "url": "https://github.com/sponsors/chocolajs"
29
29
  },
30
30
  "scripts": {
31
- "test": "echo \"Error: no test specified\" && exit 1"
31
+ "test": "node --test \"tests/**/*.test.js\"",
32
+ "bench": "node bench/index.js",
33
+ "bench:csv": "node bench/index.js --csv"
32
34
  },
33
35
  "exports": {
34
36
  "./compiler": "./compiler/index.js",
35
37
  "./dev": "./dev/index.js",
36
- "./types": "./types/index.js"
38
+ "./server": "./server/index.js"
37
39
  },
38
- "dependencies": {
39
- "chalk": "^5.6.2",
40
- "js-beautify": "^1.15.4",
41
- "jsdom": "^27.4.0"
40
+ "bin": {
41
+ "chocola": "./bin/chocola.js",
42
+ "chjs": "./bin/chocola.js"
42
43
  },
43
- "devDependencies": {
44
- "js-beautify": "^1.15.4"
44
+ "files": [
45
+ "bin/",
46
+ "compiler/",
47
+ "dev/",
48
+ "server/",
49
+ "runtime/",
50
+ "parser/",
51
+ "utils.js"
52
+ ],
53
+ "dependencies": {
54
+ "acorn": "^8.18.0",
55
+ "linkedom": "^0.18.13"
45
56
  }
46
57
  }