chocola 1.3.5 → 1.3.7

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/README.md CHANGED
@@ -1,427 +1,41 @@
1
- # 🍫 Chocola JS
1
+ # chocola
2
2
 
3
- > A Sweet Taste of Reactive Web Development
3
+ Chocola is a JavaScript library for creating web user interfaces.
4
4
 
5
- Chocola is a lightweight, reactive component-based web framework that brings simplicity and modularity to modern web development. Build dynamic components with reactivity, global state management, and a developer experience as sweet as chocolate.
5
+ `chocola` only contains the functionality to build and serve web static builds. For production you may want to implement `chocola` to your workflow with other frameworks as Vite until official workflows are provided.
6
6
 
7
- ## ✨ Features
8
-
9
- - **🧩 Component-Based Architecture** - Build reusable, modular components with ease
10
- - **⚡ Reactive Runtime** - Components automatically update when reactive variables change
11
- - **🔄 Reactive Variables** - Mutable state with `&{sfx.var}` syntax and automatic re-rendering
12
- - **🎯 Context System** - Pass data to components using intuitive `ctx.*` attributes
13
- - **🌐 Global State Management** - Share state across components with `sfx` variables
14
- - **📡 Variable Subscription** - Subscribe to state changes across your application
15
- - **🎭 Conditional Rendering** - Show/hide components dynamically with `chif` attribute
16
- - **🔌 Component Lifecycle API** - Public APIs for mounting, manipulating, and removing components
17
- - **📦 Built-in Bundler** - Automatic compilation and optimization
18
- - **🔥 Hot Reload Development** - See changes instantly with the dev server
19
- - **🎨 Template Syntax** - Clean HTML templates with `{}` and `&{}` interpolation
20
- - **⚙️ Zero Config** - Works out of the box with sensible defaults
21
-
22
- ## 🚀 Quick Start
23
-
24
- ### Installation
25
-
26
- ```bash
27
- npm install chocola
28
- ```
29
-
30
- ### Project Structure
31
-
32
- ```
33
- my-chocola-app/
34
- ├── src/
35
- │ ├── lib/
36
- │ │ ├── Counter.js
37
- │ │ ├── TodoItem.js
38
- │ │ └── html/
39
- │ │ ├── counter.body.html
40
- │ │ └── todoItem.body.html
41
- │ ├── styles/
42
- │ │ └── mainStyle.css
43
- │ └── index.html
44
- ├── chocola.config.json
45
- ├── chocola.server.js
46
- └── index.js
47
- ```
48
-
49
- ### Configuration
50
-
51
- Create a `chocola.config.json` file:
52
-
53
- ```json
54
- {
55
- "bundle": {
56
- "srcDir": "/src",
57
- "outDir": "/dist",
58
- "libDir": "/lib",
59
- "emptyOutDir": true
60
- },
61
- "dev": {
62
- "hostname": "localhost",
63
- "port": 3000
64
- }
65
- }
66
- ```
67
-
68
- ## 📝 Creating Components
69
-
70
- ### 1. Define Your HTML Template
71
-
72
- > NOTE: Reactivity, component APIs and global variables are not implemented yet.
73
- > Any of these features displayed here are for future references and may be modified.
74
-
75
- Create `src/lib/html/counter.html`:
7
+ ## Usage
76
8
 
77
9
  ```html
78
- <div class="counter">
79
- <h2>{ctx.title}</h2>
80
- <!-- Use & to set reactivity -->
81
- <p>Count: &{sfx.count}</p>
82
- <button class="increment">+</button>
83
- <button class="decrement">-</button>
10
+ <!-- counter.html -->
11
+ <div>
12
+ <button title={title}>{text}</button>
13
+
14
+ <div class="main">
15
+ <div class="number">${count}</div>
16
+ </div>
84
17
  </div>
85
18
  ```
86
19
 
87
- ### 2. Create the Component Logic
88
-
89
- Create `src/lib/Counter.js`:
90
-
91
- ```javascript
92
- import { lib } from "chocola";
20
+ ```js
93
21
  import HTML from "./html/counter.html";
22
+ import CSS from "./css/counter.css";
94
23
 
95
24
  function RUNTIME(self, ctx) {
96
- // Acces to component effects
97
- let sfx = self.sfx;
98
-
99
- // Event handlers
100
- self.querySelector(".increment").addEventListener("click", () => {
101
- sfx.incrementCount(); // Automatically updates the UI
102
- });
103
-
104
- self.querySelector(".decrement").addEventListener("click", () => {
105
- sfx.decrementCount(); // Automatically updates the UI
106
- });
107
- }
108
-
109
- function EFFECTS(self, sfx) {
110
- // Component lifecycle effects
111
- sfx.onMount: () => {
112
- console.log("Counter mounted with count:", sfx.count);
113
- },
114
- sfx.onUpdate: () => {
115
- // Log updated variables
116
- console.log("Counter updated:", sfx.diff);
117
- },
118
- sfx.onRemove: () => {
119
- console.log("Counter removed");
120
- };
121
-
122
- // Create the component public API
123
- const api = lib.api(self, sfx);
124
-
125
- // Create methods to expose
126
- api.incrementCount = () => {
127
- sfx.count++
128
- }
129
- api.decrementCount = () => {
130
- sfx.count--
131
- }
132
-
133
- // Expose the component API
134
- return api
25
+ self.querySelector("button").addEventListener("click", () => {
26
+ ctx.count++;
27
+ })
135
28
  }
136
29
 
137
30
  export default function Counter() {
138
31
  return {
139
32
  body: HTML,
140
- script: RUNTIME,
141
- effects: EFFECTS
142
- };
143
- }
144
- ```
145
-
146
- ### 3. Use the Component
147
-
148
- In your `src/index.html`:
149
-
150
- ```html
151
- <html>
152
- <head>
153
- <title>Chocola Counter App</title>
154
- </head>
155
- <body>
156
- <app>
157
- <Counter ctx.title="My Counter" sfx.initialCount="0"></Counter>
158
- </app>
159
- </body>
160
- </html>
161
- ```
162
-
163
- ## 🎭 Component Anatomy
164
-
165
- ### Template (`body`)
166
- - Standard HTML with template variables
167
- - **Static context**: `{ctx.propertyName}`, `{sfx.propertyName}` - rendered once at initialization
168
- - **Reactive state**: `&{sfx.propertyName}` - automatically updates on change
169
- - Clean separation of markup and logic
170
-
171
- ### Runtime (`script`) - Optional
172
- - Function that receives `self` (component API, properties and DOM element), `ctx` (static context), and `sfx` (dynamic context)
173
- - Initialize runtime script and event handlers
174
- - Full access to DOM APIs and browser features
175
- - Executes when the component mounts
176
-
177
- ### Effects (`effects`) - Optional
178
- - Function that receives `self` and `sfx`
179
- - Returns lifecycle hooks: `onMount`, `onUpdate`, `onRemove`
180
- - Manage side effects and component lifecycle
181
- - Clean up resources when component is removed
182
-
183
- ## ⚡ Reactivity System
184
-
185
- ### Mutable Reactive Variables
186
-
187
- Use `&{sfx.varName}` in templates for automatic reactivity:
188
-
189
- ```html
190
- <div>
191
- <p>Username: &{sfx.username}</p>
192
- <p>Online: &{sfx.isOnline}</p>
193
- </div>
194
- ```
195
-
196
- ```javascript
197
- function RUNTIME(self, ctx) {
198
- let sfx = self.sfx;
199
-
200
- // Changes automatically update the UI
201
- setTimeout(() => {
202
- sfx.username = "Alice";
203
- sfx.isOnline = true;
204
- }, 2000);
205
- }
206
- ```
207
-
208
- ### Variable Subscription
209
-
210
- Subscribe to changes in reactive variables across components:
211
-
212
- ```javascript
213
- import { app } from "chocola";
214
-
215
- function RUNTIME(self, ctx) {
216
- let sfx = self.sfx;
217
-
218
- // Subscribe to global state
219
- app.watch("globalCounter", (newValue) => {
220
- console.log("Global counter changed to:", newValue);
221
- sfx.localCount = newValue * 2;
222
- });
223
- }
224
- ```
225
-
226
- ### Conditional Rendering
227
-
228
- Use the `chif` attribute to conditionally render components:
229
-
230
- ```html
231
- <app>
232
- <!-- Based on ctx variable (static) -->
233
- <LoginForm chif="ctx.isLoggedOut"></LoginForm>
234
-
235
- <!-- Based on sfx variable (reactive with &) -->
236
- <Dashboard chif="sfx.&isAuthenticated"></Dashboard>
237
- <LoadingSpinner chif="sfx.&isLoading"></LoadingSpinner>
238
- </app>
239
- ```
240
-
241
- ```javascript
242
- function RUNTIME(self, ctx) {
243
- let sfx = self.sfx;
244
-
245
- // Simulate authentication
246
- setTimeout(() => {
247
- sfx.isLoading = false;
248
- sfx.isAuthenticated = true;
249
- // Dashboard appears, LoadingSpinner disappears
250
- }, 2000);
251
- }
252
- ```
253
-
254
- ## 🌐 Global State Management
255
-
256
- Share state across your entire application:
257
-
258
- ```javascript
259
- // In any component
260
- import * as globals from "path/to/globals.js";
261
-
262
- function RUNTIME(self, ctx) {
263
- // Set global variables
264
- globals.userTheme = "dark";
265
- globals.notifications = [];
266
-
267
- // Access global variables from other components
268
- console.log(globals.userTheme);
269
- }
270
- ```
271
-
272
- ## 🔌 Component Public API
273
-
274
- ### Mounting Components Dynamically
275
-
276
- ```javascript
277
- // In any component
278
- import { lib } from "chocola";
279
- import Counter from "./lib/Counter.js";
280
-
281
- // Mount a component programmatically in RUNTIME and/or EFFECTS
282
- function RUNTIME(self, ctx) {
283
- const counterInstance = lib.mount(Counter)
284
- .defCtx({
285
- title: "Dynamic Counter",
286
- initialCount: "10"
287
- })
288
- .render() // Render accepts a DOM element or component
289
- // instance as a target parameter; default
290
- // target is set as your <app> element
291
- }
292
- ```
293
-
294
- ### Manipulating Components
295
-
296
- ```javascript
297
- // Acces and modify variables
298
- console.log(counterInstance.ctx.title);
299
- counterInstance.sfx.count = 1;
300
-
301
- // Call component API methods
302
- counterInstance.reset();
303
- console.log(counterInstance.getCount())
304
- ```
305
-
306
- ### Removing Components
307
-
308
- ```javascript
309
- // Remove and clean up
310
- counterInstance.remove();
311
- ```
312
-
313
- ## 🔧 Development
314
-
315
- ### Start Development Server
316
-
317
- Create `chocola.server.js`:
318
-
319
- ```javascript
320
- import { dev } from "chocola";
321
- import path from "path";
322
- import { fileURLToPath } from "url";
323
-
324
- const __filename = fileURLToPath(import.meta.url);
325
- const __dirname = path.dirname(__filename);
326
-
327
- dev.server(__dirname);
328
- ```
329
-
330
- Run:
331
- ```bash
332
- node chocola.server.js
333
- ```
334
-
335
- Visit `http://localhost:3000` to see your app with hot reload enabled.
336
-
337
- ### Build for Production
338
-
339
- Create `index.js`:
340
-
341
- ```javascript
342
- import { app } from "chocola";
343
- import path from "path";
344
- import { fileURLToPath } from "url";
345
-
346
- const __filename = fileURLToPath(import.meta.url);
347
- const __dirname = path.dirname(__filename);
348
-
349
- app.build(__dirname);
350
- ```
351
-
352
- Build:
353
- ```bash
354
- node index.js
355
- ```
356
-
357
- Your optimized app will be in the `dist/` directory.
358
-
359
- ---
360
-
361
- ## 🏗️ Build Output
362
-
363
- Chocola automatically:
364
- - ✅ Compiles components into optimized JavaScript
365
- - ✅ Generates unique component IDs (`chid`)
366
- - ✅ Bundles scripts with proper dependency resolution
367
- - ✅ Processes templates and injects runtime code
368
- - ✅ Sets up reactivity system for `&{sfx.*}` variables
369
- - ✅ Optimizes conditional rendering with `chif` attributes
370
- - ✅ Generates component API wrappers for mounting/unmounting
371
- - ✅ Optimizes assets for production
372
-
373
- ## 🎯 Best Practices
374
-
375
- ### When to Use `ctx` vs `sfx`
376
-
377
- - **Use `ctx`** for static configuration that won't change (titles, labels, initial values)
378
- - **Use `sfx`** for dynamic data that updates over time and its configurations (counts, user input, API responses)
379
-
380
- ### Component Lifecycle
381
-
382
- ```javascript
383
- function EFFECTS(self, sfx) {
384
- return {
385
- onMount: () => {
386
- // Initialize, fetch data, set up subscriptions
387
- },
388
- onUpdate: () => {
389
- // React to specific state changes
390
- // Only runs when sfx variables change
391
- },
392
- onRemove: () => {
393
- // Clean up timers, subscriptions, event listeners
394
- }
395
- };
33
+ styles: CSS,
34
+ script: RUNTIME
35
+ }
396
36
  }
397
37
  ```
398
38
 
399
- ### Conditional Rendering Performance
400
-
401
- - Use `chif` with reactive variables for dynamic show/hide
402
- - Use `chif` with static variables for static conditional rendering
403
- - Components with `chif="false"` are not mounted at all (performance optimization)
404
-
405
- ## 🤝 Contributing
406
-
407
- Contributions are welcome! Chocola is in active development and we'd love your input.
408
-
409
- ## 📄 License
410
-
411
- MIT License - feel free to use Chocola in your projects!
412
-
413
- ## 🍫 Why Chocola?
414
-
415
- - **Simple**: No complex build configurations or CLI tools to learn
416
- - **Reactive**: Built-in reactivity without the complexity of larger frameworks
417
- - **Fast**: Minimal runtime overhead with efficient reactive updates
418
- - **Flexible**: Use as much or as little as you need
419
- - **Modern**: Built with ES modules and modern JavaScript features
420
- - **Powerful**: Global state, subscriptions, and lifecycle hooks out of the box
421
- - **Sweet**: Developer experience that's actually enjoyable
422
-
423
- ---
424
-
425
- Made with 🍫 and ❤️
39
+ ## Documentation
426
40
 
427
- **Start building sweet reactive web apps today!**
41
+ See https://github.com/sad-gabi/chocola/wiki
@@ -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.5",
3
+ "version": "1.3.7",
4
4
  "description": "The sweetest way to build reactive web apps",
5
5
  "keywords": [
6
6
  "web",