chocola 1.3.4 → 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.
@@ -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 && instance.body) {
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
- const runtimeChunks = [];
119
- const compIdColl = [];
120
- const letterState = { value: null };
121
- const runtimeMap = new Map();
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
- // Process Components
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
- // Finalize HTML
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
  }
@@ -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
- const id = Math.random().toString(36).substring(2, length + 2);
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;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "chocola",
3
- "version": "1.3.4",
3
+ "version": "1.3.6",
4
4
  "description": "The sweetest way to build reactive web apps",
5
5
  "keywords": [
6
6
  "web",
package/chocola-1.3.2.tgz DELETED
Binary file
package/chocola-1.3.3.tgz DELETED
Binary file