chocola 2.0.0-next.1 → 2.0.0-next.4

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,17 +1,6 @@
1
1
  import path from "path";
2
- import chalk from "chalk";
3
2
  import { getChocolaConfig } 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
5
  const config = await getChocolaConfig(rootDir);
17
6
  const bundleConfig = config.bundle || {};
@@ -20,52 +9,10 @@ export async function loadConfig(rootDir) {
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,13 @@
1
1
  import { JSDOM } from "jsdom";
2
2
  import { promises as fs } from "fs";
3
3
  import path from "path";
4
- import { throwError, protectCurlyBraces, restoreCurlyBraces } from "./utils.js";
4
+ import { throwError, protectCurlyBraces, restoreCurlyBraces, compileExpression } from "./utils.js";
5
5
  import { readMyFile } from "./fs.js";
6
6
 
7
- /**
8
- * Creates a JSDOM instance from source index file
9
- * @param {string} srcIndexContent
10
- * @returns {JSDOM}
11
- */
12
7
  export function createDOM(srcIndexContent) {
13
8
  return new JSDOM(protectCurlyBraces(srcIndexContent));
14
9
  }
15
10
 
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
11
  export function validateAppContainer(doc) {
22
12
  const appContainer = doc.querySelector("app");
23
13
  if (!appContainer) {
@@ -26,29 +16,20 @@ export function validateAppContainer(doc) {
26
16
  return appContainer;
27
17
  }
28
18
 
29
- /**
30
- * Extracts all child elements from app container
31
- * @param {Element} appContainer
32
- * @returns {Element[]}
33
- */
34
19
  export function getAppElements(appContainer) {
35
20
  return Array.from(appContainer.querySelectorAll("*"));
36
21
  }
37
22
 
38
- /**
39
- * Extracts context attributes from element (ctx.* attributes)
40
- * @param {Element} element
41
- * @returns {object}
42
- */
43
23
  export function extractContextFromElement(element) {
44
24
  const ctx = {};
45
25
  for (const attr of element.attributes) {
46
26
  const key = attr.name;
47
27
  const val = attr.value;
28
+ if (!val.includes("{")) { ctx[key] = val; continue; }
48
29
  const matches = [...val.matchAll(/\{([^}]+)\}/g)];
49
30
  if (matches.length === 1 && matches[0][0] === val) {
50
31
  try {
51
- ctx[key] = Function(`"use strict"; return (${matches[0][1]})`)();
32
+ ctx[key] = compileExpression(matches[0][1], false)();
52
33
  continue;
53
34
  } catch {}
54
35
  }
@@ -57,31 +38,16 @@ export function extractContextFromElement(element) {
57
38
  return ctx;
58
39
  }
59
40
 
60
- /**
61
- * Serializes and formats DOM to pretty HTML
62
- * @param {JSDOM} dom
63
- * @returns {string}
64
- */
65
41
  export async function serializeDOM(dom) {
66
42
  const beautify = (await import("js-beautify")).default;
67
43
  const finalHtml = restoreCurlyBraces(dom.serialize());
68
44
  return beautify.html(finalHtml, { indent_size: 2 });
69
45
  }
70
46
 
71
- /**
72
- * Writes the final HTML to output directory
73
- * @param {string} html
74
- * @param {import("fs").PathLike} outDirPath
75
- */
76
47
  export async function writeHTMLOutput(html, outDirPath) {
77
48
  await fs.writeFile(path.join(outDirPath, "index.html"), html);
78
49
  }
79
50
 
80
- /**
81
- * Gets all stylesheet and icon links from document
82
- * @param {Document} doc
83
- * @returns {{stylesheets: HTMLLinkElement[], icons: HTMLLinkElement[]}}
84
- */
85
51
  export function getAssetLinks(doc) {
86
52
  const docLinks = Array.from(doc.querySelectorAll("link"));
87
53
  const stylesheets = docLinks.filter(link => link.rel === "stylesheet");
@@ -89,21 +55,10 @@ export function getAssetLinks(doc) {
89
55
  return { stylesheets, icons };
90
56
  }
91
57
 
92
- /**
93
- * Writes CSS to output directory
94
- * @param {string} css
95
- * @param {import("fs").PathLike} outDirPath
96
- * @param {string} filename
97
- */
98
58
  export async function writeCSSOutput(css, outDirPath, filename = "scopes.css") {
99
59
  await fs.writeFile(path.join(outDirPath, filename), css);
100
60
  }
101
61
 
102
- /**
103
- * Appends a stylesheet link element to document head
104
- * @param {Document} doc
105
- * @param {string} filename
106
- */
107
62
  export function appendStylesheetLink(doc, filename) {
108
63
  const linkEl = doc.createElement("link");
109
64
  linkEl.rel = "stylesheet";
@@ -111,11 +66,6 @@ export function appendStylesheetLink(doc, filename) {
111
66
  doc.head.appendChild(linkEl);
112
67
  }
113
68
 
114
- /**
115
- * Appends a script element to document body
116
- * @param {Document} doc
117
- * @param {string} filename
118
- */
119
69
  export function appendRuntimeScript(doc, filename) {
120
70
  const runtimeScriptEl = doc.createElement("script");
121
71
  runtimeScriptEl.type = "module";
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
@@ -14,9 +14,8 @@ import {
14
14
  } from "./dom-processor.js";
15
15
  import { processAllComponents } from "./component-processor.js";
16
16
  import { generateRuntimeScript } from "./runtime-generator.js";
17
- import { genRandomId, throwError } from "./utils.js";
17
+ import { genRandomId, throwError, compileExpression } from "./utils.js";
18
18
  import {
19
- copyResources,
20
19
  copyStaticDir,
21
20
  getComponents,
22
21
  getSrcIndex,
@@ -76,15 +75,15 @@ async function setupOutputDirectory(outDirPath, emptyOutDir) {
76
75
 
77
76
  async function loadAndDisplayComponents(srcComponentsPath) {
78
77
  const foundComponents = await getComponents(srcComponentsPath);
79
- const { loadedComponents, notDefComps, componentsLib } = foundComponents;
78
+ const { loadedComponents, notDefComps: emptyComps, componentsLib } = foundComponents;
80
79
 
81
80
  console.log(` LOADING COMPONENTS`);
82
81
  console.log(chalk.bold.green(">"), "Components found in", chalk.green.underline(srcComponentsPath) + ":");
83
82
  console.log(" ", componentsLib, "\n");
84
83
 
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);
84
+ if (emptyComps?.length > 0) {
85
+ console.warn(chalk.bold.yellow("WARNING!"), "The following component files are empty:");
86
+ console.log(" ", emptyComps);
88
87
  }
89
88
 
90
89
  return loadedComponents;
@@ -106,15 +105,7 @@ async function processAssets(doc, rootDir, srcDir, outDirPath) {
106
105
  return cssContents
107
106
  }
108
107
 
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) {
108
+ export default async function compile(rootDir, buildConfig) {
118
109
  const isHotReload = buildConfig?.isHotReload || null;
119
110
  !isHotReload && logBanner();
120
111
 
@@ -138,20 +129,13 @@ export default async function runtime(rootDir, buildConfig) {
138
129
  const doc = dom.window.document;
139
130
  const appContainer = validateAppContainer(doc);
140
131
 
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");
132
+ function hasDelIfAttr(el) {
133
+ return el.hasAttribute("del:if");
149
134
  }
150
135
  function getDelIfAttr(el) {
151
- return el.getAttribute("del:if") ?? el.getAttribute("del-if");
136
+ return el.getAttribute("del:if");
152
137
  }
153
138
  function removeDelIfAttr(el) {
154
- el.removeAttribute("del-if");
155
139
  el.removeAttribute("del:if");
156
140
  }
157
141
 
@@ -162,7 +146,7 @@ export default async function runtime(rootDir, buildConfig) {
162
146
 
163
147
  for (const child of children) {
164
148
  const hasIf = child.hasAttribute("if");
165
- const hasDelIf = hasDelIfAttr(child, pageSourcePath);
149
+ const hasDelIf = hasDelIfAttr(child);
166
150
  const hasElif = child.hasAttribute("elif");
167
151
  const hasElse = child.hasAttribute("else");
168
152
 
@@ -174,7 +158,7 @@ export default async function runtime(rootDir, buildConfig) {
174
158
  if (hasIf) {
175
159
  const raw = child.getAttribute("if");
176
160
  const expr = raw.startsWith("{") ? raw.slice(1, -1) : raw;
177
- const fn = new Function(`"use strict"; return (${expr})`);
161
+ const fn = compileExpression(expr, false);
178
162
  const result = fn();
179
163
  chainActive = true;
180
164
  if (result) {
@@ -187,7 +171,7 @@ export default async function runtime(rootDir, buildConfig) {
187
171
  } else if (hasDelIf) {
188
172
  const raw = getDelIfAttr(child);
189
173
  const expr = raw.startsWith("{") ? raw.slice(1, -1) : raw;
190
- const fn = new Function(`"use strict"; return (${expr})`);
174
+ const fn = compileExpression(expr, false);
191
175
  const result = fn();
192
176
  chainActive = true;
193
177
  if (result) {
@@ -200,7 +184,7 @@ export default async function runtime(rootDir, buildConfig) {
200
184
  } else if (hasElif) {
201
185
  const raw = child.getAttribute("elif");
202
186
  const expr = raw.startsWith("{") ? raw.slice(1, -1) : raw;
203
- const fn = new Function(`"use strict"; return (${expr})`);
187
+ const fn = compileExpression(expr, false);
204
188
  const result = fn();
205
189
  if (result) {
206
190
  chainRendered = true;
@@ -227,7 +211,7 @@ export default async function runtime(rootDir, buildConfig) {
227
211
  const appElements = getAppElements(appContainer);
228
212
  const { runtimeScript, scopesCss } = processAllComponents(appElements, loadedComponents, pageSourcePath, srcIndexContent);
229
213
  const runtimeFilename = await generateRuntimeScript(runtimeScript, paths.outDir);
230
- const globalCss = (await processAssets(doc, rootDir, config.srcDir, paths.outDir)).join("\n");
214
+ await processAssets(doc, rootDir, config.srcDir, paths.outDir);
231
215
 
232
216
  if (scopesCss) {
233
217
  const fileName = "sc-" + genRandomId(null, 6) + ".css";
@@ -240,11 +224,7 @@ export default async function runtime(rootDir, buildConfig) {
240
224
  await writeHTMLOutput(html, paths.outDir);
241
225
 
242
226
  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
- }
227
+ await copyStaticDir(paths.src, paths.outDir);
248
228
  } catch (err) {
249
229
  throwError(err.message || err);
250
230
  }
@@ -273,5 +253,5 @@ export const app = {
273
253
  * @example
274
254
  * @param {PathLike} __rootdir the directory where your Chocola App is
275
255
  */
276
- build(__rootdir) { runtime(__rootdir) }
256
+ async build(__rootdir) { return compile(__rootdir) }
277
257
  };
@@ -1,26 +1,13 @@
1
1
  import { promises as fs } from "fs";
2
- import { loadWithAssets, throwError, genRandomId, isWebLink } from "./utils.js";
2
+ import { throwError, genRandomId } from "./utils.js";
3
3
  import { readMyFile, checkFile } from "./fs.js";
4
- import { JSDOM } from "jsdom";
5
4
  import path from "path";
6
5
 
7
- /**
8
- * Discovers and loads all components from a library directory.
9
- * Components are JavaScript files that start with an uppercase letter.
10
- * They must have a default export that is a function
11
- * @param {import("node:fs").PathLike} libDir - Directory containing component files
12
- * @returns {Promise<{
13
- * componentsLib: string[],
14
- * loadedComponents: Map<string, object>,
15
- * notDefComps: string[]
16
- * }>}
17
- * @throws {Error} if libDir cannot be found
18
- */
19
6
  export async function getComponents(libDir) {
20
7
  try {
21
8
  let componentsLib = [];
22
9
  let loadedComponents = new Map();
23
- let notDefComps = [];
10
+ let emptyComps = [];
24
11
 
25
12
  const components = await fs.readdir(libDir);
26
13
 
@@ -29,22 +16,15 @@ export async function getComponents(libDir) {
29
16
  }
30
17
 
31
18
  for (const comp of components) {
32
- if (!comp.endsWith(".js") || comp[0] !== comp[0].toUpperCase()) continue;
19
+ if (!comp.endsWith(".html")) continue;
33
20
 
34
21
  componentsLib.push(comp);
35
22
 
36
- let module;
37
23
  try {
38
- module = await loadWithAssets(path.join(libDir, comp));
39
-
40
- if (typeof module.default !== "function") {
41
- notDefComps.push(comp);
42
- continue;
43
- }
44
-
45
- const instance = module.default();
46
24
  const compPath = path.join(libDir, comp);
47
- instance.__sourceFile = compPath;
25
+ const instance = await fs.readFile(compPath, "utf-8");
26
+
27
+ if (instance === "" || instance.trim().length === 0) emptyComps.push(comp);
48
28
 
49
29
  loadedComponents.set(comp.toLowerCase(), instance);
50
30
  } catch (err) {
@@ -52,22 +32,12 @@ export async function getComponents(libDir) {
52
32
  }
53
33
  }
54
34
 
55
- return { componentsLib, loadedComponents, notDefComps };
35
+ return { componentsLib, loadedComponents, emptyComps };
56
36
  } catch (err) {
57
37
  throwError(`Failed to load components from ${libDir}: ${err.message}`);
58
38
  }
59
39
  }
60
40
 
61
- /**
62
- * Loads the project index file (HTML or .choco)
63
- * If both HTML and .choco files exist, throws an error
64
- * @param {import("fs").PathLike} srcPath - Source directory
65
- * @returns {Promise<{
66
- * srcHtmlFile: string | null,
67
- * srcChocoFile: string | null
68
- * }>}
69
- * @throws {Error} if both index files exist or .choco is used (not yet supported)
70
- */
71
41
  export async function getSrcIndex(srcPath) {
72
42
  const srcHtmlPath = path.join(srcPath, "index.html");
73
43
 
@@ -85,15 +55,6 @@ export async function getSrcIndex(srcPath) {
85
55
  }
86
56
  }
87
57
 
88
- /**
89
- * Processes stylesheet links: copies CSS files to output and updates link href
90
- * @param {HTMLLinkElement} link - The link element to process
91
- * @param {import("fs").PathLike} rootDir - Root project directory
92
- * @param {string} srcDir - Source directory name
93
- * @param {import("fs").PathLike} outDirPath - Output directory path
94
- * @param {Array} fileIds - Array to track used file IDs
95
- * @throws {Error} if stylesheet cannot be read or written
96
- */
97
58
  export async function processStylesheet(link, rootDir, srcDir, outDirPath, fileIds) {
98
59
  try {
99
60
  const href = link.href;
@@ -111,14 +72,6 @@ export async function processStylesheet(link, rootDir, srcDir, outDirPath, fileI
111
72
  }
112
73
  }
113
74
 
114
- /**
115
- * Processes icon links: copies icon files to output directory
116
- * @param {HTMLLinkElement} link - The link element to process
117
- * @param {import("fs").PathLike} rootDir - Root project directory
118
- * @param {string} srcDir - Source directory name
119
- * @param {import("fs").PathLike} outDirPath - Output directory path
120
- * @throws {Error} if icon cannot be copied
121
- */
122
75
  export async function processIcons(link, rootDir, srcDir, outDirPath) {
123
76
  try {
124
77
  const href = link.href;
@@ -130,139 +83,6 @@ export async function processIcons(link, rootDir, srcDir, outDirPath) {
130
83
  }
131
84
  }
132
85
 
133
- export function getCssAssets(css) {
134
- const results = new Set();
135
- const urlRegex = /url\(([^)]+)\)/gi;
136
- const importStringRegex = /@import\s+["']([^"']+)["']/gi;
137
- const importUrlRegex = /@import\s+url\(([^)]+)\)/gi;
138
-
139
- const clean = (raw) => {
140
- let p = raw.trim();
141
- if (
142
- (p.startsWith('"') && p.endsWith('"')) ||
143
- (p.startsWith("'") && p.endsWith("'"))
144
- ) {
145
- p = p.slice(1, -1);
146
- }
147
- if (p.startsWith("data:")) return null;
148
- return p;
149
- };
150
-
151
- let match;
152
- while ((match = urlRegex.exec(css))) {
153
- const p = clean(match[1]);
154
- if (p) results.add(p);
155
- }
156
- while ((match = importStringRegex.exec(css))) {
157
- const p = clean(match[1]);
158
- if (p) results.add(p);
159
- }
160
- while ((match = importUrlRegex.exec(css))) {
161
- const p = clean(match[1]);
162
- if (p) results.add(p);
163
- }
164
- return [...results];
165
- }
166
-
167
- function getComponentSource(el) {
168
- let cur = el;
169
- while (cur) {
170
- const src = cur.getAttribute?.("data-ch-source");
171
- if (src) return src;
172
- cur = cur.parentElement;
173
- }
174
- return null;
175
- }
176
-
177
- /**
178
- * Copies all local resources (images, fonts, etc.) to output directory.
179
- * Excludes web links and script/link tags.
180
- * @param {import("fs").PathLike} rootDir - Root project directory
181
- * @param {string} scopesCss - Scoped CSS content
182
- * @param {string} globalCss - Global CSS content
183
- * @param {string} srcDir - Source directory name
184
- * @param {import("fs").PathLike} outDirPath - Output directory path
185
- */
186
- export async function copyResources(rootDir, scopesCss, globalCss, srcDir, outDirPath) {
187
- try {
188
- const newIndex = await fs.readFile(path.join(outDirPath, "index.html"), "utf8");
189
- const newDoc = new JSDOM(newIndex);
190
- const newElements = Array.from(newDoc.window.document.querySelectorAll("*"));
191
- const inlinePathsRegex = /url\((.*?)\)/gi;
192
-
193
- for (const el of newElements) {
194
- if (el.tagName === "LINK" || el.tagName === "SCRIPT") continue;
195
-
196
- const attrName = el.getAttribute("src") ? "src" : "href";
197
- const src = el.getAttribute(attrName);
198
- if (src && !isWebLink(src) && !src.startsWith("#") && !src.startsWith("/")) {
199
- const srcPath = path.join(rootDir, srcDir, src);
200
- const destPath = path.join(outDirPath, src);
201
- try {
202
- await fs.mkdir(path.dirname(destPath), { recursive: true });
203
- await fs.copyFile(srcPath, destPath);
204
- } catch (err) {
205
- throw new Error(
206
- `Failed to copy resource referenced by <${el.tagName.toLowerCase()} ${attrName}="${src}">` +
207
- (getComponentSource(el) ? ` in component "${getComponentSource(el)}"` : "") +
208
- `: ${err.message || err}`
209
- );
210
- }
211
- }
212
-
213
- const styles = el.getAttribute("style");
214
- if (styles) {
215
- let stylesMatch;
216
- while ((stylesMatch = inlinePathsRegex.exec(styles)) !== null) {
217
- let filePath = stylesMatch[1].trim();
218
- if (
219
- (filePath.startsWith('"') && filePath.endsWith('"')) ||
220
- (filePath.startsWith("'") && filePath.endsWith("'"))
221
- ) {
222
- filePath = filePath.slice(1, -1);
223
- }
224
- const srcPath = path.join(rootDir, srcDir, filePath);
225
- const destPath = path.join(outDirPath, filePath);
226
- try {
227
- await fs.mkdir(path.dirname(destPath), { recursive: true });
228
- await fs.copyFile(srcPath, destPath);
229
- } catch (err) {
230
- throw new Error(
231
- `Failed to copy resource from inline style on <${el.tagName.toLowerCase()}> url("${filePath}")` +
232
- (getComponentSource(el) ? ` in component "${getComponentSource(el)}"` : "") +
233
- `: ${err.message || err}`
234
- );
235
- }
236
- }
237
- }
238
- }
239
-
240
- const cssAssets = [...getCssAssets(scopesCss || ""), ...getCssAssets(globalCss || "")];
241
- for (const assetPath of cssAssets) {
242
- try {
243
- if (assetPath.startsWith('http:') || assetPath.startsWith('https:')) continue;
244
- const srcPath = path.join(rootDir, srcDir, assetPath);
245
- const destPath = path.join(outDirPath, assetPath);
246
- await fs.mkdir(path.dirname(destPath), { recursive: true });
247
- await fs.copyFile(srcPath, destPath);
248
- } catch (err) {
249
- throw new Error(
250
- `Failed to copy CSS asset "${assetPath}" referenced from stylesheets: ${err.message || err}`
251
- );
252
- }
253
- }
254
- } catch (err) {
255
- throw err;
256
- }
257
- }
258
-
259
- /**
260
- * Copies the static/ directory from source to output.
261
- * The entire static/ directory tree is copied recursively.
262
- * Silently skips if static/ does not exist.
263
- * @param {import("fs").PathLike} srcPath - Source directory path
264
- * @param {import("fs").PathLike} outDirPath - Output directory path
265
- */
266
86
  export async function copyStaticDir(srcPath, outDirPath) {
267
87
  const staticSrc = path.join(srcPath, "static");
268
88
  const staticDest = path.join(outDirPath, "static");
@@ -270,6 +90,5 @@ export async function copyStaticDir(srcPath, outDirPath) {
270
90
  await fs.access(staticSrc);
271
91
  await fs.cp(staticSrc, staticDest, { recursive: true, force: true });
272
92
  } catch {
273
- // static/ directory doesn't exist — nothing to copy
274
93
  }
275
94
  }
@@ -2,12 +2,6 @@ import { promises as fs } from "fs";
2
2
  import path from "path";
3
3
  import { genRandomId } from "./utils.js";
4
4
 
5
- /**
6
- * Generates a runtime script file and returns its filename
7
- * @param {string} runtimeScript - The JavaScript code for runtime
8
- * @param {import("fs").PathLike} outDirPath
9
- * @returns {Promise<string>} - filename of the generated runtime script
10
- */
11
5
  export async function generateRuntimeScript(runtimeScript, outDirPath) {
12
6
  const fileIds = [];
13
7
  const runtimeFilename = "run-" + genRandomId(fileIds, 6) + ".js";