chocola 1.3.5 → 1.3.6
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/component-processor.js +42 -10
- package/compiler/dom-processor.js +22 -0
- package/compiler/index.js +10 -34
- package/compiler/pipeline.js +0 -7
- package/compiler/utils.js +10 -3
- package/package.json +1 -1
|
@@ -2,6 +2,21 @@ import { JSDOM } from "jsdom";
|
|
|
2
2
|
import { extractContextFromElement } from "./dom-processor.js";
|
|
3
3
|
import { genRandomId, incrementAlfabet } from "./utils.js";
|
|
4
4
|
import chalk from "chalk";
|
|
5
|
+
import beautify from "js-beautify";
|
|
6
|
+
|
|
7
|
+
function scopeCss(cssString, cssId) {
|
|
8
|
+
return cssString.replace(/(^|\})([^{}]+)\{/g, (match, before, selector) => {
|
|
9
|
+
if (selector.trim().startsWith("@")) {
|
|
10
|
+
return match;
|
|
11
|
+
}
|
|
12
|
+
const scoped = selector
|
|
13
|
+
.split(",")
|
|
14
|
+
.map(sel => `.${cssId} ${sel.trim()}`)
|
|
15
|
+
.join(", ");
|
|
16
|
+
return `${before}${scoped}{`;
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
|
|
5
20
|
|
|
6
21
|
/**
|
|
7
22
|
* Processes a single component element and inserts it into the DOM
|
|
@@ -18,7 +33,10 @@ export function processComponentElement(
|
|
|
18
33
|
runtimeChunks,
|
|
19
34
|
compIdColl,
|
|
20
35
|
letterState,
|
|
21
|
-
runtimeMap
|
|
36
|
+
runtimeMap,
|
|
37
|
+
cssScopes,
|
|
38
|
+
cssScopesMap,
|
|
39
|
+
scopedStyles
|
|
22
40
|
) {
|
|
23
41
|
const tagName = element.tagName.toLowerCase();
|
|
24
42
|
const compName = tagName + ".js";
|
|
@@ -28,7 +46,7 @@ export function processComponentElement(
|
|
|
28
46
|
const instance = loadedComponents.get(compName);
|
|
29
47
|
if (!instance || instance === undefined) return false;
|
|
30
48
|
|
|
31
|
-
if (instance
|
|
49
|
+
if (instance.body) {
|
|
32
50
|
let body = instance.body;
|
|
33
51
|
body = body.replace(
|
|
34
52
|
/(?<!\b(?:if|del-if)=)\{(\w+)\}/g,
|
|
@@ -96,8 +114,18 @@ export function processComponentElement(
|
|
|
96
114
|
slot.replaceWith(slotFragment);
|
|
97
115
|
});
|
|
98
116
|
|
|
117
|
+
let style = instance.styles && instance.styles.toString();
|
|
118
|
+
if (style) {
|
|
119
|
+
let cssId = cssScopesMap && cssScopesMap.get(compName);
|
|
120
|
+
if (!cssId) {
|
|
121
|
+
cssId = genRandomId(cssScopes, 8, true);
|
|
122
|
+
cssScopesMap.set(compName, cssId);
|
|
123
|
+
fragment.firstChild.classList.add(cssId);
|
|
124
|
+
}
|
|
125
|
+
style = scopeCss(style, cssId);
|
|
126
|
+
scopedStyles.push(style);
|
|
127
|
+
}
|
|
99
128
|
element.replaceWith(fragment);
|
|
100
|
-
|
|
101
129
|
return true;
|
|
102
130
|
}
|
|
103
131
|
|
|
@@ -112,22 +140,26 @@ export function processComponentElement(
|
|
|
112
140
|
* @returns {{
|
|
113
141
|
* runtimeScript: string,
|
|
114
142
|
* hasComponents: boolean
|
|
143
|
+
* scopesCss: CSSString
|
|
115
144
|
* }}
|
|
116
145
|
*/
|
|
117
146
|
export function processAllComponents(appElements, loadedComponents) {
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
147
|
+
let runtimeChunks = [];
|
|
148
|
+
let compIdColl = [];
|
|
149
|
+
let letterState = { value: null };
|
|
150
|
+
let runtimeMap = new Map();
|
|
151
|
+
let cssScopes = [];
|
|
152
|
+
let cssScopesMap = new Map();
|
|
153
|
+
let scopedStyles = [];
|
|
122
154
|
|
|
123
155
|
appElements.forEach(el => {
|
|
124
|
-
processComponentElement(el, loadedComponents, runtimeChunks, compIdColl, letterState, runtimeMap);
|
|
156
|
+
processComponentElement(el, loadedComponents, runtimeChunks, compIdColl, letterState, runtimeMap, cssScopes, cssScopesMap, scopedStyles);
|
|
125
157
|
});
|
|
126
|
-
|
|
127
158
|
const runtimeScript = runtimeChunks.join("\n");
|
|
128
159
|
const hasComponents = runtimeChunks.length > 0;
|
|
160
|
+
const scopesCss = beautify.css(scopedStyles.join("\n"));
|
|
129
161
|
|
|
130
|
-
return { runtimeScript, hasComponents };
|
|
162
|
+
return { runtimeScript, hasComponents, scopesCss };
|
|
131
163
|
}
|
|
132
164
|
|
|
133
165
|
/**
|
|
@@ -81,6 +81,28 @@ export function getAssetLinks(doc) {
|
|
|
81
81
|
return { stylesheets, icons };
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
+
/**
|
|
85
|
+
* Writes CSS to output directory
|
|
86
|
+
* @param {string} css
|
|
87
|
+
* @param {import("fs").PathLike} outDirPath
|
|
88
|
+
* @param {string} filename
|
|
89
|
+
*/
|
|
90
|
+
export async function writeCSSOutput(css, outDirPath, filename = "scopes.css") {
|
|
91
|
+
await fs.writeFile(path.join(outDirPath, filename), css);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Appends a stylesheet link element to document head
|
|
96
|
+
* @param {Document} doc
|
|
97
|
+
* @param {string} filename
|
|
98
|
+
*/
|
|
99
|
+
export function appendStylesheetLink(doc, filename) {
|
|
100
|
+
const linkEl = doc.createElement("link");
|
|
101
|
+
linkEl.rel = "stylesheet";
|
|
102
|
+
linkEl.href = "./" + filename;
|
|
103
|
+
doc.head.appendChild(linkEl);
|
|
104
|
+
}
|
|
105
|
+
|
|
84
106
|
/**
|
|
85
107
|
* Appends a script element to document body
|
|
86
108
|
* @param {Document} doc
|
package/compiler/index.js
CHANGED
|
@@ -1,27 +1,21 @@
|
|
|
1
1
|
import path from "path";
|
|
2
2
|
import { promises as fs } from "fs";
|
|
3
3
|
import chalk from "chalk";
|
|
4
|
-
|
|
5
|
-
// Configuration
|
|
6
4
|
import { loadConfig, resolvePaths } from "./config.js";
|
|
7
|
-
|
|
8
|
-
// DOM Processing
|
|
9
5
|
import {
|
|
10
6
|
createDOM,
|
|
11
7
|
validateAppContainer,
|
|
12
8
|
getAppElements,
|
|
13
9
|
getAssetLinks,
|
|
14
10
|
appendRuntimeScript,
|
|
11
|
+
appendStylesheetLink,
|
|
15
12
|
serializeDOM,
|
|
16
13
|
writeHTMLOutput,
|
|
14
|
+
writeCSSOutput,
|
|
17
15
|
} from "./dom-processor.js";
|
|
18
|
-
|
|
19
|
-
// Component & Runtime Processing
|
|
20
16
|
import { processAllComponents } from "./component-processor.js";
|
|
21
17
|
import { generateRuntimeScript } from "./runtime-generator.js";
|
|
22
|
-
|
|
23
|
-
// Utilities & Pipeline
|
|
24
|
-
import { throwError } from "./utils.js";
|
|
18
|
+
import { genRandomId, throwError } from "./utils.js";
|
|
25
19
|
import {
|
|
26
20
|
copyResources,
|
|
27
21
|
getComponents,
|
|
@@ -31,8 +25,6 @@ import {
|
|
|
31
25
|
} from "./pipeline.js";
|
|
32
26
|
|
|
33
27
|
|
|
34
|
-
// ===== Logging & Banners =====
|
|
35
|
-
|
|
36
28
|
const logSeparation = chalk.yellow(`
|
|
37
29
|
________________________________________________________________________
|
|
38
30
|
========================================================================
|
|
@@ -75,8 +67,6 @@ function logSuccess(outDirPath) {
|
|
|
75
67
|
);
|
|
76
68
|
}
|
|
77
69
|
|
|
78
|
-
// ===== Directory Setup =====
|
|
79
|
-
|
|
80
70
|
async function setupOutputDirectory(outDirPath, emptyOutDir) {
|
|
81
71
|
if (emptyOutDir) {
|
|
82
72
|
await fs.rm(outDirPath, { recursive: true, force: true });
|
|
@@ -84,8 +74,6 @@ async function setupOutputDirectory(outDirPath, emptyOutDir) {
|
|
|
84
74
|
}
|
|
85
75
|
}
|
|
86
76
|
|
|
87
|
-
// ===== Component Loading =====
|
|
88
|
-
|
|
89
77
|
async function loadAndDisplayComponents(srcComponentsPath) {
|
|
90
78
|
const foundComponents = await getComponents(srcComponentsPath);
|
|
91
79
|
const { loadedComponents, notDefComps, componentsLib } = foundComponents;
|
|
@@ -102,8 +90,6 @@ async function loadAndDisplayComponents(srcComponentsPath) {
|
|
|
102
90
|
return loadedComponents;
|
|
103
91
|
}
|
|
104
92
|
|
|
105
|
-
// ===== Asset Processing =====
|
|
106
|
-
|
|
107
93
|
async function processAssets(doc, rootDir, srcDir, outDirPath) {
|
|
108
94
|
const { stylesheets, icons } = getAssetLinks(doc);
|
|
109
95
|
const fileIds = [];
|
|
@@ -117,8 +103,6 @@ async function processAssets(doc, rootDir, srcDir, outDirPath) {
|
|
|
117
103
|
}
|
|
118
104
|
}
|
|
119
105
|
|
|
120
|
-
// ===== Main Compilation Function =====
|
|
121
|
-
|
|
122
106
|
/**
|
|
123
107
|
* Compiles a static build of your Chocola project from the directory provided.
|
|
124
108
|
* @param {import("fs").PathLike} rootDir
|
|
@@ -131,23 +115,17 @@ export default async function runtime(rootDir, buildConfig) {
|
|
|
131
115
|
const isHotReload = buildConfig?.isHotReload || null;
|
|
132
116
|
!isHotReload && logBanner();
|
|
133
117
|
|
|
134
|
-
// Load Configuration
|
|
135
118
|
const config = await loadConfig(rootDir);
|
|
136
119
|
const paths = resolvePaths(rootDir, config);
|
|
137
120
|
!isHotReload && console.log(logSeparation);
|
|
138
121
|
|
|
139
|
-
// Setup Output Directory
|
|
140
122
|
await setupOutputDirectory(paths.outDir, config.emptyOutDir);
|
|
141
123
|
|
|
142
|
-
// Load Index File
|
|
143
124
|
const indexFiles = await getSrcIndex(paths.src);
|
|
144
125
|
const srcIndexContent = indexFiles.srcHtmlFile || indexFiles.srcChocoFile;
|
|
145
126
|
|
|
146
|
-
// Load Components
|
|
147
127
|
const loadedComponents = await loadAndDisplayComponents(paths.components);
|
|
148
128
|
!isHotReload && console.log(logSeparation);
|
|
149
|
-
|
|
150
|
-
// Create and Validate DOM
|
|
151
129
|
!isHotReload && console.log(` BUNDLING STATIC BUILD`);
|
|
152
130
|
!isHotReload && console.log(chalk.bold.green(">"), "Creating Chocola static build in directory", chalk.green.underline(paths.outDir) + "\n");
|
|
153
131
|
!isHotReload && console.log(logSeparation);
|
|
@@ -157,24 +135,22 @@ export default async function runtime(rootDir, buildConfig) {
|
|
|
157
135
|
const appContainer = validateAppContainer(doc);
|
|
158
136
|
const appElements = getAppElements(appContainer);
|
|
159
137
|
|
|
160
|
-
|
|
161
|
-
const { runtimeScript } = processAllComponents(appElements, loadedComponents);
|
|
162
|
-
|
|
163
|
-
// Generate Runtime File
|
|
138
|
+
const { runtimeScript, scopesCss } = processAllComponents(appElements, loadedComponents);
|
|
164
139
|
const runtimeFilename = await generateRuntimeScript(runtimeScript, paths.outDir);
|
|
165
|
-
|
|
166
|
-
// Process Assets (stylesheets, icons)
|
|
167
140
|
await processAssets(doc, rootDir, config.srcDir, paths.outDir);
|
|
168
141
|
|
|
169
|
-
|
|
142
|
+
if (scopesCss) {
|
|
143
|
+
const fileName = "sc-" + genRandomId(null, 6) + ".css";
|
|
144
|
+
await writeCSSOutput(scopesCss, paths.outDir, fileName);
|
|
145
|
+
appendStylesheetLink(doc, fileName);
|
|
146
|
+
};
|
|
147
|
+
|
|
170
148
|
appendRuntimeScript(doc, runtimeFilename);
|
|
171
149
|
const html = await serializeDOM(dom);
|
|
172
150
|
await writeHTMLOutput(html, paths.outDir);
|
|
173
151
|
|
|
174
|
-
// Copy Resources
|
|
175
152
|
await copyResources(rootDir, config.srcDir, paths.outDir);
|
|
176
153
|
|
|
177
|
-
// Success Message
|
|
178
154
|
!isHotReload && logSuccess(paths.outDir);
|
|
179
155
|
isHotReload && console.log("Dev server updated");
|
|
180
156
|
}
|
package/compiler/pipeline.js
CHANGED
|
@@ -42,13 +42,6 @@ export async function getComponents(libDir) {
|
|
|
42
42
|
|
|
43
43
|
const instance = module.default();
|
|
44
44
|
|
|
45
|
-
if (instance.bodyPath) {
|
|
46
|
-
instance.body = await fs.readFile(
|
|
47
|
-
path.resolve(libDir, instance.bodyPath),
|
|
48
|
-
"utf8"
|
|
49
|
-
);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
45
|
loadedComponents.set(comp.toLowerCase(), instance);
|
|
53
46
|
}
|
|
54
47
|
|
package/compiler/utils.js
CHANGED
|
@@ -54,10 +54,17 @@ export async function loadWithAssets(filePath) {
|
|
|
54
54
|
* @param {number} length - Desired length of the ID (default: 10)
|
|
55
55
|
* @returns {string} - Unique random ID
|
|
56
56
|
*/
|
|
57
|
-
export function genRandomId(collection, length = 10) {
|
|
58
|
-
|
|
57
|
+
export function genRandomId(collection = null, length = 10, lettersOnly = false) {
|
|
58
|
+
let id;
|
|
59
|
+
if (lettersOnly) {
|
|
60
|
+
const letters = "abcdefghijklmnopqrstuvwxyz";
|
|
61
|
+
id = Array.from({ length }, () => letters[Math.floor(Math.random() * letters.length)]).join("");
|
|
62
|
+
} else {
|
|
63
|
+
id = Math.random().toString(36).substring(2, length + 2);
|
|
64
|
+
}
|
|
65
|
+
if (!collection) return id;
|
|
59
66
|
if (collection.includes(id)) {
|
|
60
|
-
return genRandomId(collection, length);
|
|
67
|
+
return genRandomId(collection, length, lettersOnly);
|
|
61
68
|
} else {
|
|
62
69
|
collection.push(id);
|
|
63
70
|
return id;
|