chocola 1.6.0 → 2.0.0-next.10

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.
@@ -1,71 +1,18 @@
1
1
  import path from "path";
2
- import chalk from "chalk";
3
- import { getChocolaConfig } from "../utils.js";
2
+ import { getConfig } from "../utils.js";
4
3
 
5
- /**
6
- * Loads and merges Chocola configuration with defaults
7
- * @param {import("fs").PathLike} rootDir
8
- * @returns {Promise<{
9
- * srcDir: string,
10
- * outDir: string,
11
- * libDir: string,
12
- * emptyOutDir: boolean
13
- * }>}
14
- */
15
4
  export async function loadConfig(rootDir) {
16
- const config = await getChocolaConfig(rootDir);
5
+ const config = await getConfig(rootDir);
17
6
  const bundleConfig = config.bundle || {};
18
7
 
19
8
  const srcDir = bundleConfig.srcDir || "src";
20
9
  const outDir = bundleConfig.outDir || "dist";
21
10
  const libDir = bundleConfig.libDir || "lib";
22
11
  const emptyOutDir = bundleConfig.emptyOutDir !== false;
23
- const assetImport = bundleConfig.assetImport || "legacy";
24
12
 
25
- logConfigWarnings(bundleConfig, emptyOutDir, assetImport);
26
-
27
- return { srcDir, outDir, libDir, emptyOutDir, assetImport };
28
- }
29
-
30
- function logConfigWarnings(bundleConfig, emptyOutDir, assetImport) {
31
- if (!bundleConfig.srcDir) {
32
- console.warn(
33
- chalk.bold.yellow("WARNING!"),
34
- 'srcDir not defined in chocola.config.json file: using default "src" directory.'
35
- );
36
- }
37
-
38
- if (!bundleConfig.outDir) {
39
- console.warn(
40
- chalk.bold.yellow("WARNING!"),
41
- 'outDir not defined in chocola.config.json file: using default "dist" directory.'
42
- );
43
- }
44
-
45
- if (!bundleConfig.libDir) {
46
- console.warn(
47
- chalk.bold.yellow("WARNING!"),
48
- 'libDir not defined in chocola.config.json file: using default "lib" directory.'
49
- );
50
- }
51
-
52
- console.log(`> using emptyOutDir = ${emptyOutDir}`);
53
-
54
- if (assetImport !== "static") {
55
- console.warn(
56
- chalk.bold.yellow("DEPRECATION WARNING!"),
57
- 'The legacy asset import system ("bundle.assetImport") will be removed in Chocola 2. ' +
58
- 'Set "bundle.assetImport": "static" in chocola.config.json and migrate your assets to src/static/.'
59
- );
60
- }
13
+ return { srcDir, outDir, libDir, emptyOutDir };
61
14
  }
62
15
 
63
- /**
64
- * Resolves all path directories based on configuration
65
- * @param {import("fs").PathLike} rootDir
66
- * @param {object} config
67
- * @returns {object}
68
- */
69
16
  export function resolvePaths(rootDir, config) {
70
17
  return {
71
18
  outDir: path.join(rootDir, config.outDir),
@@ -1,23 +1,17 @@
1
- import { JSDOM } from "jsdom";
1
+ import { parseHTML } from "linkedom";
2
2
  import { promises as fs } from "fs";
3
3
  import path from "path";
4
- import { throwError, protectCurlyBraces, restoreCurlyBraces } from "./utils.js";
4
+ import { protectCurlyBraces, restoreCurlyBraces } from "../utils.js";
5
+ import { throwError } from "./utils.js";
5
6
  import { readMyFile } from "./fs.js";
6
7
 
7
- /**
8
- * Creates a JSDOM instance from source index file
9
- * @param {string} srcIndexContent
10
- * @returns {JSDOM}
11
- */
12
8
  export function createDOM(srcIndexContent) {
13
- return new JSDOM(protectCurlyBraces(srcIndexContent));
9
+ const protectedContent = protectCurlyBraces(srcIndexContent);
10
+ const dom = parseHTML(protectedContent);
11
+ dom.protectedContent = protectedContent;
12
+ return dom;
14
13
  }
15
14
 
16
- /**
17
- * Validates that the index file has an <app> root element
18
- * @param {Document} doc
19
- * @throws {Error} if <app> element not found
20
- */
21
15
  export function validateAppContainer(doc) {
22
16
  const appContainer = doc.querySelector("app");
23
17
  if (!appContainer) {
@@ -26,62 +20,18 @@ export function validateAppContainer(doc) {
26
20
  return appContainer;
27
21
  }
28
22
 
29
- /**
30
- * Extracts all child elements from app container
31
- * @param {Element} appContainer
32
- * @returns {Element[]}
33
- */
34
23
  export function getAppElements(appContainer) {
35
24
  return Array.from(appContainer.querySelectorAll("*"));
36
25
  }
37
26
 
38
- /**
39
- * Extracts context attributes from element (ctx.* attributes)
40
- * @param {Element} element
41
- * @returns {object}
42
- */
43
- export function extractContextFromElement(element) {
44
- const ctx = {};
45
- for (const attr of element.attributes) {
46
- const key = attr.name;
47
- const val = attr.value;
48
- const matches = [...val.matchAll(/\{([^}]+)\}/g)];
49
- if (matches.length === 1 && matches[0][0] === val) {
50
- try {
51
- ctx[key] = Function(`"use strict"; return (${matches[0][1]})`)();
52
- continue;
53
- } catch {}
54
- }
55
- ctx[key] = val;
56
- }
57
- return ctx;
58
- }
59
-
60
- /**
61
- * Serializes and formats DOM to pretty HTML
62
- * @param {JSDOM} dom
63
- * @returns {string}
64
- */
65
27
  export async function serializeDOM(dom) {
66
- const beautify = (await import("js-beautify")).default;
67
- const finalHtml = restoreCurlyBraces(dom.serialize());
68
- return beautify.html(finalHtml, { indent_size: 2 });
28
+ return restoreCurlyBraces(dom.document.toString());
69
29
  }
70
30
 
71
- /**
72
- * Writes the final HTML to output directory
73
- * @param {string} html
74
- * @param {import("fs").PathLike} outDirPath
75
- */
76
31
  export async function writeHTMLOutput(html, outDirPath) {
77
32
  await fs.writeFile(path.join(outDirPath, "index.html"), html);
78
33
  }
79
34
 
80
- /**
81
- * Gets all stylesheet and icon links from document
82
- * @param {Document} doc
83
- * @returns {{stylesheets: HTMLLinkElement[], icons: HTMLLinkElement[]}}
84
- */
85
35
  export function getAssetLinks(doc) {
86
36
  const docLinks = Array.from(doc.querySelectorAll("link"));
87
37
  const stylesheets = docLinks.filter(link => link.rel === "stylesheet");
@@ -89,21 +39,10 @@ export function getAssetLinks(doc) {
89
39
  return { stylesheets, icons };
90
40
  }
91
41
 
92
- /**
93
- * Writes CSS to output directory
94
- * @param {string} css
95
- * @param {import("fs").PathLike} outDirPath
96
- * @param {string} filename
97
- */
98
42
  export async function writeCSSOutput(css, outDirPath, filename = "scopes.css") {
99
43
  await fs.writeFile(path.join(outDirPath, filename), css);
100
44
  }
101
45
 
102
- /**
103
- * Appends a stylesheet link element to document head
104
- * @param {Document} doc
105
- * @param {string} filename
106
- */
107
46
  export function appendStylesheetLink(doc, filename) {
108
47
  const linkEl = doc.createElement("link");
109
48
  linkEl.rel = "stylesheet";
@@ -111,14 +50,12 @@ export function appendStylesheetLink(doc, filename) {
111
50
  doc.head.appendChild(linkEl);
112
51
  }
113
52
 
114
- /**
115
- * Appends a script element to document body
116
- * @param {Document} doc
117
- * @param {string} filename
118
- */
53
+ export function getScriptElements(doc) {
54
+ return Array.from(doc.querySelectorAll("script[src]"));
55
+ }
56
+
119
57
  export function appendRuntimeScript(doc, filename) {
120
58
  const runtimeScriptEl = doc.createElement("script");
121
- runtimeScriptEl.type = "module";
122
59
  runtimeScriptEl.src = "./" + filename;
123
60
  doc.body.appendChild(runtimeScriptEl);
124
61
  }
package/compiler/fs.js CHANGED
@@ -1,12 +1,6 @@
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
- */
10
4
  export async function readMyFile(filePath) {
11
5
  try {
12
6
  const data = await fs.readFile(filePath, "utf-8");
@@ -16,11 +10,6 @@ export async function readMyFile(filePath) {
16
10
  }
17
11
  }
18
12
 
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
- */
24
13
  export async function checkFile(filePath) {
25
14
  try {
26
15
  await fs.access(filePath);
package/compiler/index.js CHANGED
@@ -1,254 +1,126 @@
1
- import { promises as fs } from "fs";
2
- import chalk from "chalk";
3
- import { loadConfig, resolvePaths } from "./config.js";
4
- import {
5
- createDOM,
6
- validateAppContainer,
7
- getAppElements,
8
- getAssetLinks,
9
- appendRuntimeScript,
10
- appendStylesheetLink,
11
- serializeDOM,
12
- writeHTMLOutput,
13
- writeCSSOutput,
14
- } from "./dom-processor.js";
15
- import { processAllComponents } from "./component-processor.js";
16
- import { generateRuntimeScript } from "./runtime-generator.js";
17
- import { genRandomId, throwError } from "./utils.js";
18
- import {
19
- copyResources,
20
- copyStaticDir,
21
- getComponents,
22
- getSrcIndex,
23
- processIcons,
24
- processStylesheet,
25
- } from "./pipeline.js";
26
-
27
-
28
- const logSeparation = chalk.yellow(`
29
- ________________________________________________________________________
30
- ========================================================================
31
- `);
32
-
33
- function logBanner() {
34
- console.log(chalk.bold.hex("#945e33")(`\n RUNNING CHOCOLA BUNDLER`));
35
- console.log(logSeparation);
36
- console.log(
37
- chalk.hex("#945e33")(`
38
-
39
-
40
- ▄████▄ ██░ ██ ▒█████ ▄████▄ ▒█████ ██▓ ▄▄▄
41
- ▒██▀ ▀█ ▓██░ ██▒▒██▒ ██▒▒██▀ ▀█ ▒██▒ ██▒▓██▒ ▒████▄
42
- ▒▓█ ▄ ▒██▀▀██░▒██░ ██▒▒▓█ ▄ ▒██░ ██▒▒██░ ▒██ ▀█▄
43
- ▒▓▓▄ ▄██▒░▓█ ░██ ▒██ ██░▒▓▓▄ ▄██▒▒██ ██░▒██░ ░██▄▄▄▄██
44
- ▒ ▓███▀ ░░▓█▒░██▓░ ████▓▒░▒ ▓███▀ ░░ ████▓▒░░██████▒▓█ ▓██▒
45
- ░ ░▒ ▒ ░ ▒ ░░▒░▒░ ▒░▒░▒░ ░ ░▒ ▒ ░░ ▒░▒░▒░ ░ ▒░▓ ░▒▒ ▓▒█░
46
- ░ ▒ ▒ ░▒░ ░ ░ ░ ▒ ▒░ ░ ▒ ░ ░ ▒░ ░ ░ ▒ ░ ░ ▒▒ ░
47
- ░ ░ ░░ ░░ ░ ░ ░ ▒ ░ ░ ░ ░ ░ ▒ ░ ░ ░ ▒
48
- ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░
49
- ░ ░
50
-
51
-
52
- `)
53
- );
54
- }
55
-
56
- function logSuccess(outDirPath) {
57
- console.log(`
58
- ▄▄ ▄▄▄ ▄▄▄▄ ▄▄▄▄ ▄▄▄ ▄▄ ▄▄ ▄▄▄▄▄ ██
59
- ██ ██▀██ ██▄██ ██▀██ ██▀██ ███▄██ ██▄▄ ██
60
- ▄▄█▀ ▀███▀ ██▄█▀ ████▀ ▀███▀ ██ ▀██ ██▄▄▄ ▄▄
61
-
62
- `);
63
- console.log(
64
- chalk.bold.green(">"),
65
- "Project bundled succesfully at",
66
- chalk.green.underline(outDirPath) + "\n\n"
67
- );
68
- }
69
-
70
- async function setupOutputDirectory(outDirPath, emptyOutDir) {
71
- if (emptyOutDir) {
72
- await fs.rm(outDirPath, { recursive: true, force: true });
73
- await fs.mkdir(outDirPath);
74
- }
75
- }
76
-
77
- async function loadAndDisplayComponents(srcComponentsPath) {
78
- const foundComponents = await getComponents(srcComponentsPath);
79
- const { loadedComponents, notDefComps, componentsLib } = foundComponents;
80
-
81
- console.log(` LOADING COMPONENTS`);
82
- console.log(chalk.bold.green(">"), "Components found in", chalk.green.underline(srcComponentsPath) + ":");
83
- console.log(" ", componentsLib, "\n");
84
-
85
- if (notDefComps.length > 0) {
86
- console.warn(chalk.bold.yellow("WARNING!"), "The following components don't include a default export:");
87
- console.log(" ", notDefComps);
88
- }
89
-
90
- return loadedComponents;
91
- }
92
-
93
- async function processAssets(doc, rootDir, srcDir, outDirPath) {
94
- const { stylesheets, icons } = getAssetLinks(doc);
95
- const fileIds = [];
96
- let cssContents = [];
97
-
98
- for (const link of stylesheets) {
99
- const css = await processStylesheet(link, rootDir, srcDir, outDirPath, fileIds);
100
- cssContents.push(css);
101
- }
102
-
103
- for (const link of icons) {
104
- await processIcons(link, rootDir, srcDir, outDirPath);
105
- }
106
- return cssContents
107
- }
108
-
109
- /**
110
- * Compiles a static build of your Chocola project from the directory provided.
111
- * @param {import("fs").PathLike} rootDir
112
- * @param {Object<{
113
- * isHotReload?: boolean
114
- * }
115
- * >} config
116
- */
117
- export default async function runtime(rootDir, buildConfig) {
118
- const isHotReload = buildConfig?.isHotReload || null;
119
- !isHotReload && logBanner();
120
-
121
- const config = await loadConfig(rootDir);
122
- const paths = resolvePaths(rootDir, config);
123
- !isHotReload && console.log(logSeparation);
124
-
125
- await setupOutputDirectory(paths.outDir, config.emptyOutDir);
126
-
127
- const indexFiles = await getSrcIndex(paths.src);
128
- const srcIndexContent = indexFiles.srcHtmlFile;
129
- const pageSourcePath = indexFiles.srcPath;
130
-
131
- const loadedComponents = await loadAndDisplayComponents(paths.components);
132
- !isHotReload && console.log(logSeparation);
133
- !isHotReload && console.log(` BUNDLING STATIC BUILD`);
134
- !isHotReload && console.log(chalk.bold.green(">"), "Creating Chocola static build in directory", chalk.green.underline(paths.outDir) + "\n");
135
- !isHotReload && console.log(logSeparation);
136
-
137
- const dom = createDOM(srcIndexContent);
138
- const doc = dom.window.document;
139
- const appContainer = validateAppContainer(doc);
140
-
141
- const warnedDeprecated = new Set();
142
- function hasDelIfAttr(el, sourceLoc) {
143
- if (el.hasAttribute("del-if") && !el.hasAttribute("del:if") && !warnedDeprecated.has(sourceLoc)) {
144
- warnedDeprecated.add(sourceLoc);
145
- const name = sourceLoc ? sourceLoc.split(/[\\/]/).pop() : "unknown";
146
- console.warn(chalk.yellow(`[deprecation] "del-if" is deprecated in ${chalk.cyan(name)} and will be removed in Chocola 2. Use "del:if" instead.`));
147
- }
148
- return el.hasAttribute("del-if") || el.hasAttribute("del:if");
149
- }
150
- function getDelIfAttr(el) {
151
- return el.getAttribute("del:if") ?? el.getAttribute("del-if");
152
- }
153
- function removeDelIfAttr(el) {
154
- el.removeAttribute("del-if");
155
- el.removeAttribute("del:if");
156
- }
157
-
158
- function processPageConditionals(parent) {
159
- const children = [...parent.children];
160
- let chainActive = false;
161
- let chainRendered = false;
162
-
163
- for (const child of children) {
164
- const hasIf = child.hasAttribute("if");
165
- const hasDelIf = hasDelIfAttr(child, pageSourcePath);
166
- const hasElif = child.hasAttribute("elif");
167
- const hasElse = child.hasAttribute("else");
168
-
169
- if (hasElif || hasElse) {
170
- if (!chainActive) continue;
171
- if (chainRendered) { child.remove(); continue; }
172
- }
173
-
174
- if (hasIf) {
175
- const raw = child.getAttribute("if");
176
- const expr = raw.startsWith("{") ? raw.slice(1, -1) : raw;
177
- const fn = new Function(`"use strict"; return (${expr})`);
178
- const result = fn();
179
- chainActive = true;
180
- if (result) {
181
- chainRendered = true;
182
- } else {
183
- child.remove();
184
- chainRendered = false;
185
- }
186
- child.removeAttribute("if");
187
- } else if (hasDelIf) {
188
- const raw = getDelIfAttr(child);
189
- const expr = raw.startsWith("{") ? raw.slice(1, -1) : raw;
190
- const fn = new Function(`"use strict"; return (${expr})`);
191
- const result = fn();
192
- chainActive = true;
193
- if (result) {
194
- chainRendered = true;
195
- } else {
196
- child.remove();
197
- chainRendered = false;
198
- }
199
- removeDelIfAttr(child);
200
- } else if (hasElif) {
201
- const raw = child.getAttribute("elif");
202
- const expr = raw.startsWith("{") ? raw.slice(1, -1) : raw;
203
- const fn = new Function(`"use strict"; return (${expr})`);
204
- const result = fn();
205
- if (result) {
206
- chainRendered = true;
207
- } else {
208
- child.remove();
209
- }
210
- child.removeAttribute("elif");
211
- } else if (hasElse) {
212
- chainRendered = true;
213
- chainActive = false;
214
- child.removeAttribute("else");
215
- } else {
216
- chainActive = false;
217
- chainRendered = false;
218
- }
219
-
220
- if (child.parentNode) {
221
- processPageConditionals(child);
222
- }
223
- }
224
- }
225
- processPageConditionals(appContainer);
226
-
227
- const appElements = getAppElements(appContainer);
228
- const { runtimeScript, scopesCss } = processAllComponents(appElements, loadedComponents, pageSourcePath, srcIndexContent);
229
- const runtimeFilename = await generateRuntimeScript(runtimeScript, paths.outDir);
230
- const globalCss = (await processAssets(doc, rootDir, config.srcDir, paths.outDir)).join("\n");
231
-
232
- if (scopesCss) {
233
- const fileName = "sc-" + genRandomId(null, 6) + ".css";
234
- await writeCSSOutput(scopesCss, paths.outDir, fileName);
235
- appendStylesheetLink(doc, fileName);
236
- };
237
-
238
- appendRuntimeScript(doc, runtimeFilename);
239
- const html = await serializeDOM(dom);
240
- await writeHTMLOutput(html, paths.outDir);
241
-
242
- try {
243
- if (config.assetImport === "static") {
244
- await copyStaticDir(paths.src, paths.outDir);
245
- } else {
246
- await copyResources(rootDir, scopesCss, globalCss, config.srcDir, paths.outDir);
247
- }
248
- } catch (err) {
249
- throwError(err.message || err);
250
- }
251
-
252
- !isHotReload && logSuccess(paths.outDir);
253
- isHotReload && console.log("Dev server updated");
254
- }
1
+ import { promises as fs } from "fs";
2
+ import path from "path";
3
+ import { performance } from "perf_hooks";
4
+ import chalk from "./chalk.js";
5
+ import { buildModuleGraph } from "./module-graph.js";
6
+ import { renderPage } from "./render.js";
7
+ import { writeHTMLOutput } from "./dom-processor.js";
8
+
9
+ export { buildModuleGraph } from "./module-graph.js";
10
+ export { renderPage } from "./render.js";
11
+ export { ChocolaModule, ModuleGraph } from "./module-graph.js";
12
+
13
+ const GOLD_COLOR = "#D87416";
14
+ const WHITE_COLOR = "#FAFAF8";
15
+ const TEXT_FAINT = "#E7EBE1";
16
+
17
+ function logBanner() {
18
+ console.log(
19
+ chalk.hex(GOLD_COLOR)(`
20
+ ┌─────────────────────────────────────────────┐
21
+ │┌-------------------------------------------┐│
22
+ ││ ││`)
23
+ );
24
+ console.log(
25
+ chalk.hex(GOLD_COLOR)(` ││ `) +
26
+ chalk.bold.hex(WHITE_COLOR)(`{`) +
27
+ chalk.bold.hex(GOLD_COLOR)(` C H O C O L A `) +
28
+ chalk.bold.hex(WHITE_COLOR)(`}`) +
29
+ chalk.hex(GOLD_COLOR)(` ││\n`) +
30
+ chalk.hex(GOLD_COLOR)(` ││ ││
31
+ ││ `) +
32
+ chalk.hex(TEXT_FAINT)(`THE SWEETEST WAY TO BUILD THE WEB`) +
33
+ chalk.hex(GOLD_COLOR)(` ││
34
+ ││ ││
35
+ │└-------------------------------------------┘│
36
+ └─────────────────────────────────────────────┘
37
+ `)
38
+ );
39
+ }
40
+
41
+ function logSuccess(outDirPath, durationMs) {
42
+ console.log(
43
+ chalk.bold.green(">"),
44
+ "Project bundled succesfully at",
45
+ chalk.green.underline(outDirPath));
46
+ console.log(chalk.bold.green(`\nJOB DONE!`) + chalk.hex(TEXT_FAINT)(` (${formatDuration(durationMs)})\n`));
47
+ }
48
+
49
+ function formatDuration(ms) {
50
+ if (ms >= 1000) return (ms / 1000).toFixed(2) + "s";
51
+ return Math.round(ms) + "ms";
52
+ }
53
+
54
+ async function setupOutputDirectory(outDirPath, emptyOutDir) {
55
+ if (emptyOutDir) {
56
+ await fs.rm(outDirPath, { recursive: true, force: true });
57
+ await fs.mkdir(outDirPath);
58
+ }
59
+ }
60
+
61
+ export async function emit(graph, options = {}) {
62
+ const outDir = graph.paths.outDir;
63
+ await setupOutputDirectory(outDir, graph.config.emptyOutDir);
64
+
65
+ const result = await renderPage(graph, options.ctx);
66
+
67
+ for (const file of result.files) {
68
+ await fs.writeFile(path.join(outDir, file.path), file.content);
69
+ }
70
+
71
+ for (const copy of result.copies) {
72
+ if (copy.recursive) {
73
+ await fs.cp(copy.from, copy.to, { recursive: true, force: true });
74
+ } else {
75
+ await fs.copyFile(copy.from, copy.to);
76
+ }
77
+ }
78
+
79
+ await writeHTMLOutput(result.html, outDir);
80
+
81
+ const chocolaDir = path.join(graph.rootDir, ".chocola");
82
+ await fs.mkdir(chocolaDir, { recursive: true });
83
+ await fs.writeFile(path.join(chocolaDir, "hashes.json"), JSON.stringify(result.hashMap, null, 2) + "\n");
84
+
85
+ return result;
86
+ }
87
+
88
+ export default async function compile(rootDir, buildConfig) {
89
+ const isHotReload = buildConfig?.isHotReload || null;
90
+ const startTime = performance.now();
91
+ !isHotReload && logBanner();
92
+
93
+ const graph = await buildModuleGraph(rootDir);
94
+
95
+ !isHotReload && console.log(chalk.bold.green(">"), "Creating Chocola static build in directory", chalk.green.underline(graph.paths.outDir));
96
+
97
+ await emit(graph);
98
+
99
+ const durationMs = performance.now() - startTime;
100
+
101
+ !isHotReload && logSuccess(graph.paths.outDir, durationMs);
102
+ isHotReload && console.log("Dev server updated " + chalk.hex(TEXT_FAINT)(`(${formatDuration(durationMs)})`));
103
+ }
104
+
105
+ /**
106
+ * An intrinsic object that contains the Chocola App methods.
107
+ */
108
+ export const app = {
109
+ /**
110
+ * Initializes your Chocola App using a root directory.
111
+ *
112
+ * ```js
113
+ * import { app } from "chocola/compiler"
114
+ import path from "path";
115
+ import { fileURLToPath } from "url";
116
+
117
+ const __filename = fileURLToPath(import.meta.url);
118
+ const __dirname = path.dirname(__filename);
119
+
120
+ app.build(__dirname);
121
+ ```
122
+ * @example
123
+ * @param {PathLike} __rootdir the directory where your Chocola App is
124
+ */
125
+ async build(__rootdir) { return compile(__rootdir) }
126
+ };