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.
- package/LICENSE +1 -1
- package/README.md +45 -35
- package/bin/chocola.js +227 -0
- package/compiler/chalk.js +34 -0
- package/compiler/component-processor.js +771 -402
- package/compiler/config.js +43 -66
- package/compiler/dom-processor.js +12 -75
- package/compiler/fs.js +0 -11
- package/compiler/index.js +87 -230
- package/compiler/module-graph.js +257 -0
- package/compiler/pipeline.js +56 -196
- package/compiler/render.js +183 -0
- package/compiler/runtime-generator.js +20 -15
- package/compiler/utils.js +79 -126
- package/dev/index.js +54 -22
- package/package.json +23 -12
- package/parser/component.js +340 -0
- package/parser/context.js +59 -0
- package/parser/css.js +96 -0
- package/parser/index.js +6 -0
- package/parser/script.js +485 -0
- package/parser/script.test.js +103 -0
- package/parser/template.js +120 -0
- package/parser/utils.js +32 -0
- package/runtime/index.js +312 -0
- package/server/index.js +432 -0
- package/utils.js +113 -8
- package/index.js +0 -0
package/compiler/config.js
CHANGED
|
@@ -1,75 +1,52 @@
|
|
|
1
1
|
import path from "path";
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
2
|
+
import { promises as fsp } from "fs";
|
|
3
|
+
import chalk from "./chalk.js";
|
|
4
|
+
import { getConfig, isMissingConfigFile, queueConfigWarning } from "../utils.js";
|
|
5
|
+
|
|
6
|
+
const warnedBundleFields = new Set();
|
|
7
|
+
|
|
8
|
+
export async function loadConfig(rootDir, { silent, customPath, overrides } = {}) {
|
|
9
|
+
const config = customPath
|
|
10
|
+
? await fsp.readFile(customPath, "utf-8").then(r => JSON.parse(r))
|
|
11
|
+
: await getConfig(rootDir, { silent });
|
|
12
|
+
|
|
13
|
+
let srcDir = "src", outDir = "dist", libDir = "lib", emptyOutDir = true, treeShakeRuntime = true;
|
|
14
|
+
|
|
15
|
+
if (!customPath || !isMissingConfigFile(config)) {
|
|
16
|
+
const hasBundle = (config.bundle !== undefined && config.bundle !== null) || (config.build !== undefined && config.build !== null);
|
|
17
|
+
const bundleConfig = config.bundle || config.build || {};
|
|
18
|
+
const compilerConfig = config.compiler || {};
|
|
19
|
+
srcDir = bundleConfig.srcDir || "src";
|
|
20
|
+
outDir = bundleConfig.outDir || "dist";
|
|
21
|
+
libDir = bundleConfig.libDir || "lib";
|
|
22
|
+
emptyOutDir = bundleConfig.emptyOutDir !== false;
|
|
23
|
+
treeShakeRuntime = compilerConfig.treeShakeRuntime !== false;
|
|
24
|
+
|
|
25
|
+
if (!silent && hasBundle) {
|
|
26
|
+
if (bundleConfig.srcDir == null && !warnedBundleFields.has(rootDir + ":srcDir")) {
|
|
27
|
+
warnedBundleFields.add(rootDir + ":srcDir");
|
|
28
|
+
queueConfigWarning(rootDir, chalk.bold.yellow("WARNING!"), `bundle.srcDir not defined in chocola.config.json file: using default "src" bundle.srcDir.`);
|
|
29
|
+
}
|
|
30
|
+
if (bundleConfig.outDir == null && !warnedBundleFields.has(rootDir + ":outDir")) {
|
|
31
|
+
warnedBundleFields.add(rootDir + ":outDir");
|
|
32
|
+
queueConfigWarning(rootDir, chalk.bold.yellow("WARNING!"), `bundle.outDir not defined in chocola.config.json file: using default "dist" bundle.outDir.`);
|
|
33
|
+
}
|
|
34
|
+
if (bundleConfig.libDir == null && !warnedBundleFields.has(rootDir + ":libDir")) {
|
|
35
|
+
warnedBundleFields.add(rootDir + ":libDir");
|
|
36
|
+
queueConfigWarning(rootDir, chalk.bold.yellow("WARNING!"), `bundle.libDir not defined in chocola.config.json file: using default "lib" bundle.libDir.`);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
36
39
|
}
|
|
37
40
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
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
|
-
}
|
|
41
|
+
const result = { srcDir, outDir, libDir, emptyOutDir, treeShakeRuntime };
|
|
42
|
+
if (overrides) { Object.assign(result, overrides); }
|
|
43
|
+
return result;
|
|
61
44
|
}
|
|
62
45
|
|
|
63
|
-
/**
|
|
64
|
-
* Resolves all path directories based on configuration
|
|
65
|
-
* @param {import("fs").PathLike} rootDir
|
|
66
|
-
* @param {object} config
|
|
67
|
-
* @returns {object}
|
|
68
|
-
*/
|
|
69
46
|
export function resolvePaths(rootDir, config) {
|
|
70
47
|
return {
|
|
71
|
-
outDir: path.join(rootDir, config.outDir),
|
|
72
|
-
src: path.join(rootDir, config.srcDir),
|
|
73
|
-
components: path.join(rootDir, config.srcDir, config.libDir),
|
|
48
|
+
outDir: path.isAbsolute(config.outDir) ? config.outDir : path.join(rootDir, config.outDir),
|
|
49
|
+
src: path.isAbsolute(config.srcDir) ? config.srcDir : path.join(rootDir, config.srcDir),
|
|
50
|
+
components: path.isAbsolute(config.srcDir) ? path.join(config.srcDir, config.libDir) : path.join(rootDir, config.srcDir, config.libDir),
|
|
74
51
|
};
|
|
75
52
|
}
|
|
@@ -1,23 +1,17 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { parseHTML } from "linkedom";
|
|
2
2
|
import { promises as fs } from "fs";
|
|
3
3
|
import path from "path";
|
|
4
|
-
import {
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
116
|
-
|
|
117
|
-
|
|
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,72 +1,57 @@
|
|
|
1
1
|
import { promises as fs } from "fs";
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
4
|
-
import
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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);
|
|
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
|
+
import { flushConfigWarnings } from "../utils.js";
|
|
9
|
+
|
|
10
|
+
export { buildModuleGraph } from "./module-graph.js";
|
|
11
|
+
export { renderPage } from "./render.js";
|
|
12
|
+
export { ChocolaModule, ModuleGraph } from "./module-graph.js";
|
|
13
|
+
|
|
14
|
+
const GOLD_COLOR = "#D87416";
|
|
15
|
+
const WHITE_COLOR = "#FAFAF8";
|
|
16
|
+
const TEXT_FAINT = "#E7EBE1";
|
|
17
|
+
|
|
18
|
+
export function logBanner() {
|
|
19
|
+
const plain = process.env.NO_COLOR === "1";
|
|
20
|
+
const ch = plain ? (() => (s) => s) : chalk;
|
|
36
21
|
console.log(
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
▒██▀ ▀█ ▓██░ ██▒▒██▒ ██▒▒██▀ ▀█ ▒██▒ ██▒▓██▒ ▒████▄
|
|
42
|
-
▒▓█ ▄ ▒██▀▀██░▒██░ ██▒▒▓█ ▄ ▒██░ ██▒▒██░ ▒██ ▀█▄
|
|
43
|
-
▒▓▓▄ ▄██▒░▓█ ░██ ▒██ ██░▒▓▓▄ ▄██▒▒██ ██░▒██░ ░██▄▄▄▄██
|
|
44
|
-
▒ ▓███▀ ░░▓█▒░██▓░ ████▓▒░▒ ▓███▀ ░░ ████▓▒░░██████▒▓█ ▓██▒
|
|
45
|
-
░ ░▒ ▒ ░ ▒ ░░▒░▒░ ▒░▒░▒░ ░ ░▒ ▒ ░░ ▒░▒░▒░ ░ ▒░▓ ░▒▒ ▓▒█░
|
|
46
|
-
░ ▒ ▒ ░▒░ ░ ░ ░ ▒ ▒░ ░ ▒ ░ ░ ▒░ ░ ░ ▒ ░ ░ ▒▒ ░
|
|
47
|
-
░ ░ ░░ ░░ ░ ░ ░ ▒ ░ ░ ░ ░ ░ ▒ ░ ░ ░ ▒
|
|
48
|
-
░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░
|
|
49
|
-
░ ░
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
`)
|
|
22
|
+
ch.hex(GOLD_COLOR)(`
|
|
23
|
+
┌─────────────────────────────────────────────┐
|
|
24
|
+
│┌-------------------------------------------┐│
|
|
25
|
+
││ ││`)
|
|
53
26
|
);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
function logSuccess(outDirPath) {
|
|
57
|
-
console.log(`
|
|
58
|
-
▄▄ ▄▄▄ ▄▄▄▄ ▄▄▄▄ ▄▄▄ ▄▄ ▄▄ ▄▄▄▄▄ ██
|
|
59
|
-
██ ██▀██ ██▄██ ██▀██ ██▀██ ███▄██ ██▄▄ ██
|
|
60
|
-
▄▄█▀ ▀███▀ ██▄█▀ ████▀ ▀███▀ ██ ▀██ ██▄▄▄ ▄▄
|
|
61
|
-
|
|
62
|
-
`);
|
|
63
27
|
console.log(
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
28
|
+
ch.hex(GOLD_COLOR)(` ││ `) +
|
|
29
|
+
ch.bold.hex(WHITE_COLOR)(`{`) +
|
|
30
|
+
ch.bold.hex(GOLD_COLOR)(` C H O C O L A `) +
|
|
31
|
+
ch.bold.hex(WHITE_COLOR)(`}`) +
|
|
32
|
+
ch.hex(GOLD_COLOR)(` ││\n`) +
|
|
33
|
+
ch.hex(GOLD_COLOR)(` ││ ││
|
|
34
|
+
││ `) +
|
|
35
|
+
ch.hex(TEXT_FAINT)(`THE SWEETEST WAY TO BUILD THE WEB`) +
|
|
36
|
+
ch.hex(GOLD_COLOR)(` ││
|
|
37
|
+
││ ││
|
|
38
|
+
│└-------------------------------------------┘│
|
|
39
|
+
└─────────────────────────────────────────────┘
|
|
40
|
+
`)
|
|
67
41
|
);
|
|
68
42
|
}
|
|
69
43
|
|
|
44
|
+
function logSuccess(outDirPath, durationMs) {
|
|
45
|
+
const plain = process.env.NO_COLOR === "1";
|
|
46
|
+
const ch = plain ? (() => (s) => s) : chalk;
|
|
47
|
+
console.log(ch.bold.green(`\nJOB DONE!`) + ch.hex(TEXT_FAINT)(` (${formatDuration(durationMs)})\n`));
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function formatDuration(ms) {
|
|
51
|
+
if (ms >= 1000) return (ms / 1000).toFixed(2) + "s";
|
|
52
|
+
return Math.round(ms) + "ms";
|
|
53
|
+
}
|
|
54
|
+
|
|
70
55
|
async function setupOutputDirectory(outDirPath, emptyOutDir) {
|
|
71
56
|
if (emptyOutDir) {
|
|
72
57
|
await fs.rm(outDirPath, { recursive: true, force: true });
|
|
@@ -74,183 +59,55 @@ async function setupOutputDirectory(outDirPath, emptyOutDir) {
|
|
|
74
59
|
}
|
|
75
60
|
}
|
|
76
61
|
|
|
77
|
-
async function
|
|
78
|
-
const
|
|
79
|
-
|
|
62
|
+
export async function emit(graph, options = {}) {
|
|
63
|
+
const outDir = graph.paths.outDir;
|
|
64
|
+
await setupOutputDirectory(outDir, graph.config.emptyOutDir);
|
|
80
65
|
|
|
81
|
-
|
|
82
|
-
console.log(chalk.bold.green(">"), "Components found in", chalk.green.underline(srcComponentsPath) + ":");
|
|
83
|
-
console.log(" ", componentsLib, "\n");
|
|
66
|
+
const result = await renderPage(graph, options.ctx);
|
|
84
67
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
console.log(" ", notDefComps);
|
|
68
|
+
for (const file of result.files) {
|
|
69
|
+
await fs.writeFile(path.join(outDir, file.path), file.content);
|
|
88
70
|
}
|
|
89
71
|
|
|
90
|
-
|
|
91
|
-
|
|
72
|
+
for (const copy of result.copies) {
|
|
73
|
+
if (copy.recursive) {
|
|
74
|
+
await fs.cp(copy.from, copy.to, { recursive: true, force: true });
|
|
75
|
+
} else {
|
|
76
|
+
await fs.copyFile(copy.from, copy.to);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
92
79
|
|
|
93
|
-
|
|
94
|
-
const { stylesheets, icons } = getAssetLinks(doc);
|
|
95
|
-
const fileIds = [];
|
|
96
|
-
let cssContents = [];
|
|
80
|
+
await writeHTMLOutput(result.html, outDir);
|
|
97
81
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
}
|
|
82
|
+
const chocolaDir = path.join(graph.rootDir, ".chocola");
|
|
83
|
+
await fs.mkdir(chocolaDir, { recursive: true });
|
|
84
|
+
await fs.writeFile(path.join(chocolaDir, "hashes.json"), JSON.stringify(result.hashMap, null, 2) + "\n");
|
|
102
85
|
|
|
103
|
-
|
|
104
|
-
await processIcons(link, rootDir, srcDir, outDirPath);
|
|
105
|
-
}
|
|
106
|
-
return cssContents
|
|
86
|
+
return result;
|
|
107
87
|
}
|
|
108
88
|
|
|
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) {
|
|
89
|
+
export default async function compile(rootDir, buildConfig) {
|
|
118
90
|
const isHotReload = buildConfig?.isHotReload || null;
|
|
91
|
+
const overrides = buildConfig?.overrides || null;
|
|
92
|
+
const startTime = performance.now();
|
|
119
93
|
!isHotReload && logBanner();
|
|
120
94
|
|
|
121
|
-
const
|
|
122
|
-
const paths = resolvePaths(rootDir, config);
|
|
123
|
-
!isHotReload && console.log(logSeparation);
|
|
124
|
-
|
|
125
|
-
await setupOutputDirectory(paths.outDir, config.emptyOutDir);
|
|
95
|
+
const graph = await buildModuleGraph(rootDir, { overrides });
|
|
126
96
|
|
|
127
|
-
|
|
128
|
-
const srcIndexContent = indexFiles.srcHtmlFile;
|
|
129
|
-
const pageSourcePath = indexFiles.srcPath;
|
|
97
|
+
!isHotReload && console.log(chalk.bold.green(">"), "Creating Chocola static build in directory", chalk.green.underline(graph.paths.outDir));
|
|
130
98
|
|
|
131
|
-
|
|
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);
|
|
99
|
+
await emit(graph);
|
|
136
100
|
|
|
137
|
-
const
|
|
138
|
-
const doc = dom.window.document;
|
|
139
|
-
const appContainer = validateAppContainer(doc);
|
|
101
|
+
const durationMs = performance.now() - startTime;
|
|
140
102
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
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");
|
|
103
|
+
if (!isHotReload) {
|
|
104
|
+
flushConfigWarnings(rootDir);
|
|
105
|
+
logSuccess(graph.paths.outDir, durationMs);
|
|
149
106
|
}
|
|
150
|
-
|
|
151
|
-
|
|
107
|
+
if (isHotReload) {
|
|
108
|
+
flushConfigWarnings(rootDir);
|
|
109
|
+
console.log("Dev server updated " + chalk.hex(TEXT_FAINT)(`(${formatDuration(durationMs)})`));
|
|
152
110
|
}
|
|
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
111
|
}
|
|
255
112
|
|
|
256
113
|
/**
|
|
@@ -262,16 +119,16 @@ export const app = {
|
|
|
262
119
|
*
|
|
263
120
|
* ```js
|
|
264
121
|
* import { app } from "chocola/compiler"
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
122
|
+
import path from "path";
|
|
123
|
+
import { fileURLToPath } from "url";
|
|
124
|
+
|
|
125
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
126
|
+
const __dirname = path.dirname(__filename);
|
|
127
|
+
|
|
128
|
+
app.build(__dirname);
|
|
129
|
+
```
|
|
273
130
|
* @example
|
|
274
131
|
* @param {PathLike} __rootdir the directory where your Chocola App is
|
|
275
132
|
*/
|
|
276
|
-
build(__rootdir) {
|
|
133
|
+
async build(__rootdir) { return compile(__rootdir) }
|
|
277
134
|
};
|