kadence-lang 0.2.0

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.
Files changed (67) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +208 -0
  3. package/bin/kadence.js +806 -0
  4. package/package.json +64 -0
  5. package/src/compiler.js +2291 -0
  6. package/src/vite-plugin-kadence.js +39 -0
  7. package/stdlib/check-helpers.js +13 -0
  8. package/stdlib/check.js +57 -0
  9. package/stdlib/check.kade +21 -0
  10. package/stdlib/color-helpers.js +34 -0
  11. package/stdlib/color.js +60 -0
  12. package/stdlib/color.kade +24 -0
  13. package/stdlib/console.js +57 -0
  14. package/stdlib/console.kade +21 -0
  15. package/stdlib/crypto-helpers.js +17 -0
  16. package/stdlib/crypto.js +46 -0
  17. package/stdlib/crypto.kade +11 -0
  18. package/stdlib/datetime.js +68 -0
  19. package/stdlib/datetime.kade +32 -0
  20. package/stdlib/encoding.js +55 -0
  21. package/stdlib/encoding.kade +19 -0
  22. package/stdlib/env.js +46 -0
  23. package/stdlib/env.kade +11 -0
  24. package/stdlib/file.js +94 -0
  25. package/stdlib/file.kade +57 -0
  26. package/stdlib/html.js +65 -0
  27. package/stdlib/html.kade +29 -0
  28. package/stdlib/json.js +63 -0
  29. package/stdlib/json.kade +28 -0
  30. package/stdlib/list.js +109 -0
  31. package/stdlib/list.kade +75 -0
  32. package/stdlib/math.js +76 -0
  33. package/stdlib/math.kade +39 -0
  34. package/stdlib/network.js +66 -0
  35. package/stdlib/network.kade +38 -0
  36. package/stdlib/number.js +53 -0
  37. package/stdlib/number.kade +17 -0
  38. package/stdlib/object-helpers.js +44 -0
  39. package/stdlib/object.js +59 -0
  40. package/stdlib/object.kade +23 -0
  41. package/stdlib/path.js +58 -0
  42. package/stdlib/path.kade +23 -0
  43. package/stdlib/process-helpers.js +18 -0
  44. package/stdlib/process.js +62 -0
  45. package/stdlib/process.kade +29 -0
  46. package/stdlib/promise-helpers.js +21 -0
  47. package/stdlib/promise.js +54 -0
  48. package/stdlib/promise.kade +19 -0
  49. package/stdlib/random.js +82 -0
  50. package/stdlib/random.kade +46 -0
  51. package/stdlib/regex-helpers.js +18 -0
  52. package/stdlib/regex.js +46 -0
  53. package/stdlib/regex.kade +11 -0
  54. package/stdlib/stream.js +58 -0
  55. package/stdlib/stream.kade +22 -0
  56. package/stdlib/string-helpers.js +16 -0
  57. package/stdlib/string.js +101 -0
  58. package/stdlib/string.kade +66 -0
  59. package/stdlib/system.js +66 -0
  60. package/stdlib/system.kade +31 -0
  61. package/stdlib/test-helpers.js +18 -0
  62. package/stdlib/test.js +72 -0
  63. package/stdlib/test.kade +37 -0
  64. package/stdlib/url.js +50 -0
  65. package/stdlib/url.kade +14 -0
  66. package/stdlib/uuid.js +70 -0
  67. package/stdlib/uuid.kade +35 -0
package/bin/kadence.js ADDED
@@ -0,0 +1,806 @@
1
+ #!/usr/bin/env node
2
+ const fs = require("fs");
3
+ const path = require("path");
4
+ const readline = require("readline");
5
+ const { execSync } = require("child_process");
6
+ const { compile } = require("../src/compiler");
7
+
8
+ const VERSION = "0.2.0";
9
+
10
+ const colors = {
11
+ reset: "\x1b[0m",
12
+ bright: "\x1b[1m",
13
+ green: "\x1b[32m",
14
+ blue: "\x1b[34m",
15
+ red: "\x1b[31m",
16
+ cyan: "\x1b[36m",
17
+ yellow: "\x1b[33m",
18
+ dim: "\x1b[2m",
19
+ };
20
+
21
+ function log(msg, color = colors.reset) {
22
+ console.log(`${color}${msg}${colors.reset}`);
23
+ }
24
+
25
+ /** Normalize package spec to name (e.g. @scope/pkg@1.0.0 -> @scope/pkg, lodash@1.0.0 -> lodash). */
26
+ function getPkgName(pkg) {
27
+ if (!pkg) return "";
28
+ if (pkg.startsWith("@")) {
29
+ const parts = pkg.split("@");
30
+ return parts.length >= 3 ? "@" + parts[1] + "/" + parts[2] : pkg;
31
+ }
32
+ return pkg.split("@")[0];
33
+ }
34
+
35
+ function printHelp() {
36
+ log(`Kadence CLI v${VERSION}`, colors.bright + colors.cyan);
37
+ console.log(`
38
+ Usage:
39
+ kadence Start interactive REPL
40
+ kadence <file> Compile and run a .kade file
41
+ kadence -c <file> Compile to JavaScript only
42
+ kadence -c <file> -o <out> --target browser Compile for browser (no Node APIs)
43
+ kadence -c <file> -o <out> --sourcemap Emit source map for debugging
44
+ kadence create <name> [--web] [--yes] Create project folder and run npm install
45
+ kadence init [--web] Initialize a new project in current directory
46
+ kadence install <pkg> Install a package (npm/github)
47
+ kadence uninstall <pkg> Remove a package
48
+ kadence update Update all dependencies
49
+ kadence list List installed packages
50
+ kadence run <script> Run a script from kadence.json
51
+ kadence build Compile all .kade files in project
52
+ kadence test Run all .test.kade files
53
+ kadence docs Generate documentation from comments
54
+ kadence dev Start Vite development server
55
+ kadence -v Show version
56
+ kadence help Show this help
57
+ `);
58
+ }
59
+
60
+ function compileFile(filename, opts = {}) {
61
+ const fullPath = path.resolve(filename);
62
+ if (!fs.existsSync(fullPath)) return false;
63
+ const source = fs.readFileSync(fullPath, "utf8").trim();
64
+ try {
65
+ const result = compile(source, Object.assign({ target: "node", sourceFile: path.basename(fullPath) }, opts));
66
+ const js = typeof result === "string" ? result : result.code;
67
+ const map = typeof result === "object" ? result.map : null;
68
+
69
+ let outPath = opts.outputFile;
70
+ if (!outPath) {
71
+ if (opts.outputDir) {
72
+ const rel = path.relative(opts.baseDir || process.cwd(), fullPath);
73
+ outPath = path.join(opts.outputDir, rel.replace(/\.kade$/, ".js"));
74
+ const outDir = path.dirname(outPath);
75
+ if (!fs.existsSync(outDir)) fs.mkdirSync(outDir, { recursive: true });
76
+ } else {
77
+ outPath = fullPath.replace(/\.kade$/, ".js");
78
+ }
79
+ }
80
+
81
+ let toWrite = js;
82
+ if (opts.sourcemap && map) {
83
+ const mapPath = outPath + ".map";
84
+ fs.writeFileSync(mapPath, map, "utf8");
85
+ toWrite = js + "\n//# sourceMappingURL=" + path.basename(mapPath);
86
+ }
87
+
88
+ fs.writeFileSync(outPath, toWrite, "utf8");
89
+ return true;
90
+ } catch (e) {
91
+ log(`Error in ${filename}: ${e.message}`, colors.red);
92
+ return false;
93
+ }
94
+ }
95
+
96
+ function buildProject(dir = process.cwd(), isRoot = true, opts = {}) {
97
+ if (isRoot) {
98
+ log(`Building project in ${dir}...`, colors.cyan);
99
+ if (fs.existsSync(path.join(dir, "kadence.json"))) {
100
+ const _config = JSON.parse(fs.readFileSync(path.join(dir, "kadence.json"), "utf8"));
101
+ // Default convention: src -> dist
102
+ opts.outputDir = opts.outputDir || path.join(dir, "dist");
103
+ opts.baseDir = opts.baseDir || path.join(dir, "src");
104
+ if (fs.existsSync(opts.baseDir)) {
105
+ dir = opts.baseDir;
106
+ }
107
+ }
108
+ }
109
+
110
+ const files = fs.readdirSync(dir);
111
+ let count = 0;
112
+ files.forEach((file) => {
113
+ const full = path.join(dir, file);
114
+ if (fs.statSync(full).isDirectory()) {
115
+ if (file !== "node_modules" && file !== "dist" && !file.startsWith(".")) {
116
+ count += buildProject(full, false, opts);
117
+ }
118
+ } else if (file.endsWith(".kade")) {
119
+ if (compileFile(full, opts)) {
120
+ log(` Compiled ${path.relative(process.cwd(), full)}`, colors.dim);
121
+ count++;
122
+ }
123
+ }
124
+ });
125
+ if (isRoot) log(`Done. Compiled ${count} files.`, colors.green);
126
+ return count;
127
+ }
128
+
129
+ /** Write project scaffold into targetDir. opts: { projectName, version, description, author, isWeb }. */
130
+ function writeScaffold(targetDir, opts) {
131
+ const { projectName, version, description, author, isWeb } = opts;
132
+ const dirs = ["src", "dist", "src/components", "src/lib", "src/assets"];
133
+ dirs.forEach((d) => {
134
+ const full = path.join(targetDir, d);
135
+ if (!fs.existsSync(full)) fs.mkdirSync(full, { recursive: true });
136
+ });
137
+
138
+ const config = {
139
+ name: projectName,
140
+ version: version || "1.0.0",
141
+ description: description || "",
142
+ author: author || "",
143
+ scripts: isWeb
144
+ ? { dev: "kadence dev", build: "kadence build --web", preview: "vite preview" }
145
+ : { start: "kadence src/main.kade", build: "kadence build" },
146
+ dependencies: {},
147
+ };
148
+
149
+ const mainKade = path.join(targetDir, "src", "main.kade");
150
+ if (!fs.existsSync(mainKade)) {
151
+ fs.writeFileSync(mainKade, "// Welcome to your new Kadence project!\nsay \"Hello, World!\"\n", "utf8");
152
+ }
153
+
154
+ const gitignore = path.join(targetDir, ".gitignore");
155
+ if (!fs.existsSync(gitignore)) {
156
+ fs.writeFileSync(gitignore, "node_modules/\ndist/\n*.js\n*.js.map\n", "utf8");
157
+ }
158
+
159
+ const kadenceConfig = path.join(targetDir, "kadence.config.js");
160
+ if (!fs.existsSync(kadenceConfig)) {
161
+ fs.writeFileSync(
162
+ kadenceConfig,
163
+ "/** @type {import('kadence-lang').Config} */\nmodule.exports = {\n outDir: 'dist',\n srcDir: 'src',\n target: 'node'\n};\n",
164
+ "utf8"
165
+ );
166
+ }
167
+
168
+ fs.writeFileSync(path.join(targetDir, "kadence.json"), JSON.stringify(config, null, 2));
169
+
170
+ const pkgPath = path.join(targetDir, "package.json");
171
+ if (!fs.existsSync(pkgPath)) {
172
+ const pkg = {
173
+ name: projectName,
174
+ version: version || "1.0.0",
175
+ type: "commonjs",
176
+ dependencies: { "kadence-lang": "latest" },
177
+ };
178
+ if (isWeb) {
179
+ pkg.devDependencies = { vite: "latest", "vite-plugin-kadence": "latest" };
180
+ }
181
+ fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2));
182
+ }
183
+
184
+ if (isWeb) {
185
+ const indexHtml = path.join(targetDir, "index.html");
186
+ if (!fs.existsSync(indexHtml)) {
187
+ fs.writeFileSync(
188
+ indexHtml,
189
+ `<!DOCTYPE html>
190
+ <html>
191
+ <head>
192
+ <title>${projectName}</title>
193
+ </head>
194
+ <body>
195
+ <div id="app"></div>
196
+ <script type="module" src="/src/main.kade"></script>
197
+ </body>
198
+ </html>`,
199
+ "utf8"
200
+ );
201
+ }
202
+ const viteConfig = path.join(targetDir, "vite.config.js");
203
+ if (!fs.existsSync(viteConfig)) {
204
+ fs.writeFileSync(
205
+ viteConfig,
206
+ `import { defineConfig } from 'vite';
207
+ import { kadencePlugin } from './vite-plugin-kadence';
208
+
209
+ export default defineConfig({
210
+ plugins: [kadencePlugin()]
211
+ });`,
212
+ "utf8"
213
+ );
214
+ }
215
+ const pluginSrc = path.join(__dirname, "../src/vite-plugin-kadence.js");
216
+ const pluginDest = path.join(targetDir, "vite-plugin-kadence.js");
217
+ if (fs.existsSync(pluginSrc) && !fs.existsSync(pluginDest)) {
218
+ fs.copyFileSync(pluginSrc, pluginDest);
219
+ }
220
+ }
221
+ }
222
+
223
+ function initProject() {
224
+ const rl = readline.createInterface({
225
+ input: process.stdin,
226
+ output: process.stdout,
227
+ });
228
+
229
+ log("Initialize Kadence Project", colors.bright + colors.cyan);
230
+
231
+ rl.question(`Project name (${path.basename(process.cwd())}): `, (name) => {
232
+ const projectName = name || path.basename(process.cwd());
233
+ rl.question("Version (1.0.0): ", (ver) => {
234
+ const version = ver || "1.0.0";
235
+ rl.question("Description: ", (desc) => {
236
+ const description = desc || "";
237
+ rl.question("Author: ", (author) => {
238
+ const isWeb = process.argv.includes("--web");
239
+ writeScaffold(process.cwd(), {
240
+ projectName,
241
+ version,
242
+ description,
243
+ author: author || "",
244
+ isWeb,
245
+ });
246
+ log(`\n✨ Project '${projectName}' initialized.`, colors.green);
247
+ log(`Next steps:\n npm install\n ${isWeb ? "kadence dev" : "kadence run start"}`, colors.dim);
248
+ rl.close();
249
+ process.exit(0);
250
+ });
251
+ });
252
+ });
253
+ });
254
+ }
255
+
256
+ function createProject(appName) {
257
+ const isWeb = process.argv.includes("--web");
258
+ if (!appName || appName.startsWith("-")) {
259
+ log("Usage: kadence create <project-name> [--web] [--yes]", colors.red);
260
+ log("Example: kadence create my-app --web", colors.dim);
261
+ process.exit(1);
262
+ }
263
+ const targetDir = path.resolve(process.cwd(), appName);
264
+ if (fs.existsSync(targetDir)) {
265
+ const stat = fs.statSync(targetDir);
266
+ if (stat.isDirectory() && fs.readdirSync(targetDir).length > 0) {
267
+ log(`Error: Directory '${appName}' already exists and is not empty.`, colors.red);
268
+ process.exit(1);
269
+ }
270
+ }
271
+ log(`Creating Kadence project '${appName}'...`, colors.cyan);
272
+ fs.mkdirSync(targetDir, { recursive: true });
273
+ writeScaffold(targetDir, {
274
+ projectName: appName,
275
+ version: "1.0.0",
276
+ description: "",
277
+ author: "",
278
+ isWeb,
279
+ });
280
+ log(`✨ Project created. Installing dependencies...`, colors.green);
281
+ try {
282
+ execSync("npm install", { cwd: targetDir, stdio: "inherit" });
283
+ } catch (_e) {
284
+ log("npm install failed. Run 'npm install' inside the project manually.", colors.yellow);
285
+ }
286
+ log(`\nNext steps:`, colors.bright);
287
+ log(` cd ${appName}`, colors.cyan);
288
+ log(` ${isWeb ? "npm run dev" : "npm run start"}`, colors.cyan);
289
+ process.exit(0);
290
+ }
291
+
292
+ function installPackage(pkg) {
293
+ if (!pkg) {
294
+ // If no package specified, install all from kadence.json
295
+ if (fs.existsSync("kadence.json")) {
296
+ const config = JSON.parse(fs.readFileSync("kadence.json", "utf8"));
297
+ const deps = Object.keys(config.dependencies || {});
298
+ if (deps.length === 0) {
299
+ log("No dependencies to install.", colors.yellow);
300
+ process.exit(0);
301
+ }
302
+ log(`Installing ${deps.length} dependencies...`, colors.cyan);
303
+ deps.forEach((d) => {
304
+ try {
305
+ execSync(`npm install ${d}`, { stdio: "inherit" });
306
+ } catch (_e) {
307
+ log(`Failed to install ${d}`, colors.red);
308
+ }
309
+ });
310
+ log("All dependencies installed.", colors.green);
311
+ process.exit(0);
312
+ } else {
313
+ log("Usage: kadence install <package_name>", colors.red);
314
+ log("Or run 'kadence init' first to create kadence.json", colors.dim);
315
+ process.exit(1);
316
+ }
317
+ }
318
+
319
+ log(`Installing ${pkg}...`, colors.cyan);
320
+
321
+ // Initialize package.json if not exists
322
+ if (!fs.existsSync("package.json")) {
323
+ execSync("npm init -y", { stdio: "ignore" });
324
+ }
325
+
326
+ try {
327
+ execSync(`npm install ${pkg}`, { stdio: "inherit" });
328
+
329
+ // Get actual installed version from package.json
330
+ const pkgName = getPkgName(pkg);
331
+ let installedVersion = "latest";
332
+ if (fs.existsSync("package.json")) {
333
+ const npmPkg = JSON.parse(fs.readFileSync("package.json", "utf8"));
334
+ if (npmPkg.dependencies && npmPkg.dependencies[pkgName]) {
335
+ installedVersion = npmPkg.dependencies[pkgName];
336
+ }
337
+ }
338
+
339
+ // Update kadence.json (create if missing)
340
+ let config;
341
+ if (fs.existsSync("kadence.json")) {
342
+ config = JSON.parse(fs.readFileSync("kadence.json", "utf8"));
343
+ } else {
344
+ config = {
345
+ name:
346
+ (fs.existsSync("package.json")
347
+ ? JSON.parse(fs.readFileSync("package.json", "utf8")).name
348
+ : null) || path.basename(process.cwd()),
349
+ version:
350
+ (fs.existsSync("package.json")
351
+ ? JSON.parse(fs.readFileSync("package.json", "utf8")).version
352
+ : null) || "1.0.0",
353
+ dependencies: {},
354
+ };
355
+ }
356
+ config.dependencies = config.dependencies || {};
357
+ config.dependencies[pkgName] = installedVersion;
358
+ fs.writeFileSync("kadence.json", JSON.stringify(config, null, 2));
359
+ log(`Added ${pkgName}@${installedVersion} to kadence.json`, colors.green);
360
+
361
+ // Auto-compile if it's a Kadence package
362
+ const pkgDir = path.join("node_modules", pkgName);
363
+ if (
364
+ fs.existsSync(path.join(pkgDir, "kadence.json")) ||
365
+ fs.readdirSync(pkgDir).some((f) => f.endsWith(".kade"))
366
+ ) {
367
+ log(`Kadence package detected. Compiling...`, colors.blue);
368
+ buildProject(pkgDir, false);
369
+ }
370
+ } catch (_e) {
371
+ log(`Failed to install ${pkg}`, colors.red);
372
+ process.exit(1);
373
+ }
374
+ process.exit(0);
375
+ }
376
+
377
+ function uninstallPackage(pkg) {
378
+ if (!pkg) {
379
+ log("Usage: kadence uninstall <package_name>", colors.red);
380
+ process.exit(1);
381
+ }
382
+
383
+ log(`Uninstalling ${pkg}...`, colors.cyan);
384
+
385
+ try {
386
+ execSync(`npm uninstall ${pkg}`, { stdio: "inherit" });
387
+
388
+ const pkgName = getPkgName(pkg);
389
+ if (fs.existsSync("kadence.json")) {
390
+ const config = JSON.parse(fs.readFileSync("kadence.json", "utf8"));
391
+ if (config.dependencies && config.dependencies[pkgName]) {
392
+ delete config.dependencies[pkgName];
393
+ fs.writeFileSync("kadence.json", JSON.stringify(config, null, 2));
394
+ log(`Removed ${pkgName} from kadence.json`, colors.green);
395
+ }
396
+ }
397
+ process.exit(0);
398
+ } catch (_e) {
399
+ log(`Failed to uninstall ${pkg}`, colors.red);
400
+ process.exit(1);
401
+ }
402
+ }
403
+
404
+ function updatePackages() {
405
+ log("Updating all packages...", colors.cyan);
406
+
407
+ try {
408
+ execSync("npm update", { stdio: "inherit" });
409
+
410
+ // Sync versions back to kadence.json
411
+ if (fs.existsSync("kadence.json") && fs.existsSync("package.json")) {
412
+ const config = JSON.parse(fs.readFileSync("kadence.json", "utf8"));
413
+ const npmPkg = JSON.parse(fs.readFileSync("package.json", "utf8"));
414
+
415
+ for (const dep of Object.keys(config.dependencies || {})) {
416
+ if (npmPkg.dependencies && npmPkg.dependencies[dep]) {
417
+ config.dependencies[dep] = npmPkg.dependencies[dep];
418
+ }
419
+ }
420
+ fs.writeFileSync("kadence.json", JSON.stringify(config, null, 2));
421
+ log("Updated kadence.json with new versions.", colors.green);
422
+ }
423
+ } catch (_e) {
424
+ log("Failed to update packages.", colors.red);
425
+ process.exit(1);
426
+ }
427
+ process.exit(0);
428
+ }
429
+
430
+ function listPackages() {
431
+ log("Installed Packages:", colors.bright + colors.cyan);
432
+
433
+ if (!fs.existsSync("kadence.json")) {
434
+ log("No kadence.json found. Run 'kadence init' first.", colors.yellow);
435
+ process.exit(0);
436
+ }
437
+
438
+ const config = JSON.parse(fs.readFileSync("kadence.json", "utf8"));
439
+ const deps = config.dependencies || {};
440
+ const depList = Object.entries(deps);
441
+
442
+ if (depList.length === 0) {
443
+ log(" (no dependencies)", colors.dim);
444
+ } else {
445
+ depList.forEach(([name, version]) => {
446
+ log(` ${name}: ${version}`, colors.reset);
447
+ });
448
+ }
449
+ process.exit(0);
450
+ }
451
+
452
+ function runScript(scriptName) {
453
+ if (!fs.existsSync("kadence.json")) {
454
+ log("No kadence.json found. Run 'kadence init' first.", colors.red);
455
+ process.exit(1);
456
+ }
457
+
458
+ const config = JSON.parse(fs.readFileSync("kadence.json", "utf8"));
459
+ const scripts = config.scripts || {};
460
+
461
+ if (!scriptName) {
462
+ log("Available scripts:", colors.cyan);
463
+ const scriptList = Object.keys(scripts);
464
+ if (scriptList.length === 0) {
465
+ log(" (no scripts defined)", colors.dim);
466
+ } else {
467
+ scriptList.forEach((s) => log(` ${s}: ${scripts[s]}`, colors.reset));
468
+ }
469
+ process.exit(0);
470
+ }
471
+
472
+ if (!scripts[scriptName]) {
473
+ log(`Script '${scriptName}' not found in kadence.json`, colors.red);
474
+ process.exit(1);
475
+ }
476
+
477
+ const scriptFile = scripts[scriptName];
478
+ log(`Running ${scriptName}: ${scriptFile}`, colors.blue);
479
+
480
+ try {
481
+ // If it's a .kade file, run it with kadence
482
+ if (scriptFile.endsWith(".kade")) {
483
+ execSync(`node "${__filename}" "${scriptFile}"`, { stdio: "inherit" });
484
+ } else {
485
+ execSync(scriptFile, { stdio: "inherit", shell: true });
486
+ }
487
+ } catch (_e) {
488
+ process.exit(1);
489
+ }
490
+ process.exit(0);
491
+ }
492
+
493
+ /**
494
+ * REPL with history, context persistence, and basic completion.
495
+ */
496
+ function startRepl() {
497
+ const vm = require("vm");
498
+ const repl = require("repl");
499
+
500
+ log(`Kadence ${VERSION} ♪`, colors.bright + colors.green);
501
+ log(`Type '.exit' to quit. Context is preserved.`, colors.dim);
502
+ console.log("");
503
+
504
+ // Create a persistent context for the session
505
+ const context = vm.createContext({
506
+ require: require,
507
+ console: console,
508
+ process: process,
509
+ exports: {},
510
+ module: { exports: {} },
511
+ __kadence_echo: (val) => { console.log("\x1b[32m" + String(val) + "\x1b[0m"); },
512
+ __kadence_add: (p, c) => Array.isArray(p) ? p.push(c) : p.appendChild(c),
513
+ __kadence_min: (v) => Array.isArray(v) ? Math.min(...v) : v,
514
+ __kadence_max: (v) => Array.isArray(v) ? Math.max(...v) : v,
515
+ fs: require('fs')
516
+ });
517
+
518
+ // Custom eval function that compiles Kadence -> JS -> runs in VM
519
+ function kadenceEval(cmd, _context, _filename, callback) {
520
+ const input = cmd.trim();
521
+ if (!input) return callback(null);
522
+
523
+ // Filter out REPL commands
524
+ if (input.startsWith(".")) {
525
+ // Allow default REPL commands to handle it if we return undefined?
526
+ // Actually standard Node REPL handles .exit etc before this.
527
+ }
528
+
529
+ try {
530
+ // Compile snippet to JS body
531
+ // We wrap it in an async IIFE to support top-level await if needed
532
+ // But for variables to persist, we need to strip 'let'/'const' maybe?
533
+ // Or just compile it as a program.
534
+
535
+ // Attempt to compile
536
+ const compiled = compile(input, { target: "node", sourceFile: "repl" });
537
+
538
+ // The compiler outputs a full program with "const fs = require..."
539
+ // We need to strip the preamble for REPL use to avoid redeclaration errors
540
+ // and allow "let x = 1" to stick in the global scope if possible.
541
+
542
+ let jsParams = compiled.replace(/const fs = require\("fs"\);/g, "")
543
+ .replace(/const \S+ = require\(.*\);/g, "") // Strip imports for now
544
+ .replace(/^\s*function __kadence_.+?}/gms, "") // Strip helpers
545
+ .replace(/^\(async \(\) => {\s*/, "") // Strip wrapper start
546
+ .replace(/\s*}\)\(\)\.catch\(.+?\);?\s*$/, ""); // Strip wrapper end
547
+
548
+ // Run in the persistent context
549
+ const result = vm.runInContext(jsParams, context);
550
+ callback(null, result);
551
+ } catch (e) {
552
+ if (e.message.includes("Unexpected end of input")) {
553
+ return callback(new repl.Recoverable(e));
554
+ }
555
+ callback(e);
556
+ }
557
+ }
558
+
559
+ const r = repl.start({
560
+ prompt: `${colors.cyan}kadence> ${colors.reset}`,
561
+ eval: kadenceEval,
562
+ writer: (output) => {
563
+ // format output nicely
564
+ if (output === undefined) return "";
565
+ return require("util").inspect(output, { colors: true });
566
+ },
567
+ useColors: true
568
+ });
569
+
570
+ // Expose context to REPL
571
+ Object.assign(r.context, context);
572
+ }
573
+
574
+ function runTests(dir = process.cwd()) {
575
+ if (dir === process.cwd()) log(`Running tests...`, colors.cyan);
576
+
577
+ const stats = { total: 0, passed: 0, failed: 0 };
578
+ const files = fs.readdirSync(dir);
579
+
580
+ files.forEach((file) => {
581
+ const full = path.join(dir, file);
582
+ if (fs.statSync(full).isDirectory()) {
583
+ if (file !== "node_modules" && file !== "dist" && !file.startsWith(".")) {
584
+ const subStats = runTests(full);
585
+ stats.total += subStats.total;
586
+ stats.passed += subStats.passed;
587
+ stats.failed += subStats.failed;
588
+ }
589
+ } else if (file.endsWith(".test.kade")) {
590
+ stats.total++;
591
+ log(`\n📄 ${path.relative(process.cwd(), full)}`, colors.bright);
592
+ const src = fs.readFileSync(full, "utf8");
593
+ try {
594
+ // Compile
595
+ const built = compile(src, { target: "node", sourceFile: file });
596
+
597
+ // Run
598
+ const tempFile = path.join(dir, `.${file}.js`);
599
+ fs.writeFileSync(tempFile, built);
600
+
601
+ try {
602
+ // We run synchronously to capture output in order
603
+ execSync(`node "${tempFile}"`, {
604
+ stdio: "inherit",
605
+ cwd: dir,
606
+ env: { ...process.env, KADENCE_TEST_MODE: "true" }
607
+ });
608
+ stats.passed++;
609
+ } catch (_err) {
610
+ stats.failed++;
611
+ // output already shown
612
+ } finally {
613
+ if (fs.existsSync(tempFile)) fs.unlinkSync(tempFile);
614
+ }
615
+ } catch (e) {
616
+ log(` Compilation Error: ${e.message}`, colors.red);
617
+ stats.failed++;
618
+ }
619
+ }
620
+ });
621
+
622
+ if (dir === process.cwd()) {
623
+ console.log("");
624
+ log("--- Test Summary ---", colors.bright);
625
+ if (stats.failed > 0) log(`Tests: ${stats.failed} failed, ${stats.passed} passed, ${stats.total} total`, colors.red);
626
+ else log(`Tests: ${stats.passed} passed, ${stats.total} total`, colors.green);
627
+ }
628
+ return stats;
629
+ }
630
+
631
+ function generateDocs(dir = process.cwd(), isRoot = true) {
632
+ if (isRoot) log(`Generating documentation...`, colors.cyan);
633
+
634
+ const files = fs.readdirSync(dir);
635
+ const docs = [];
636
+
637
+ files.forEach((file) => {
638
+ const full = path.join(dir, file);
639
+ if (fs.statSync(full).isDirectory()) {
640
+ if (file !== "node_modules" && file !== "dist" && !file.startsWith(".")) {
641
+ docs.push(...generateDocs(full, false));
642
+ }
643
+ } else if (file.endsWith(".kade")) {
644
+ const content = fs.readFileSync(full, "utf8");
645
+ const lines = content.split("\n");
646
+ let currentNote = "";
647
+
648
+ lines.forEach((line) => {
649
+ const trimmed = line.trim();
650
+ if (trimmed.startsWith("note:")) {
651
+ currentNote = trimmed.replace(/^note:\s*/, "").trim();
652
+ } else if (trimmed.startsWith("export function ") || trimmed.startsWith("export async function ")) {
653
+ const name = trimmed.replace(/^export (async )?function /, "").split(/[ ([]/)[0];
654
+ docs.push({ file: path.relative(process.cwd(), full), name, note: currentNote, type: "function" });
655
+ currentNote = "";
656
+ } else if (trimmed.startsWith("export const ") || trimmed.startsWith("export let ")) {
657
+ const name = trimmed.replace(/^export (const|let) /, "").split(/[ =]/)[0];
658
+ docs.push({ file: path.relative(process.cwd(), full), name, note: currentNote, type: "variable" });
659
+ currentNote = "";
660
+ } else if (trimmed.length > 0) {
661
+ // If a line is not a note and not an export, reset note if it was just a floating note
662
+ // But we keep it if it was immediately above
663
+ }
664
+ });
665
+ }
666
+ });
667
+
668
+ if (isRoot) {
669
+ if (docs.length === 0) {
670
+ log("No exported functions or variables found.", colors.yellow);
671
+ } else {
672
+ log("\n📖 API Documentation\n", colors.bright + colors.green);
673
+ let currentFile = "";
674
+ docs.forEach((d) => {
675
+ if (d.file !== currentFile) {
676
+ log(`\n📄 ${d.file}`, colors.blue + colors.bright);
677
+ currentFile = d.file;
678
+ }
679
+ const typeChar = d.type === "function" ? "ƒ" : "ν";
680
+ log(` ${colors.yellow}${typeChar} ${colors.reset}${colors.bright}${d.name}${colors.reset}${d.note ? " - " + colors.dim + d.note : ""}`);
681
+ });
682
+ console.log("");
683
+ }
684
+ }
685
+
686
+ return docs;
687
+ }
688
+
689
+ const args = process.argv.slice(2);
690
+
691
+ if (args.length === 0) {
692
+ startRepl();
693
+ } else if (args[0] === "help") {
694
+ printHelp();
695
+ } else if (args[0] === "-v") {
696
+ log(`Kadence ${VERSION}`, colors.green);
697
+ } else if (args[0] === "create") {
698
+ createProject(args[1]);
699
+ } else if (args[0] === "init") {
700
+ initProject();
701
+ } else if (args[0] === "install") {
702
+ installPackage(args[1]);
703
+ } else if (args[0] === "uninstall" || args[0] === "remove") {
704
+ uninstallPackage(args[1]);
705
+ } else if (args[0] === "update") {
706
+ updatePackages();
707
+ } else if (args[0] === "list" || args[0] === "ls") {
708
+ listPackages();
709
+ } else if (args[0] === "dev") {
710
+ try {
711
+ execSync("npx vite", { stdio: "inherit" });
712
+ } catch (_e) {
713
+ process.exit(1);
714
+ }
715
+ } else if (args[0] === "run") {
716
+ runScript(args[1]);
717
+ } else if (args[0] === "build") {
718
+ buildProject();
719
+ process.exit(0);
720
+ } else if (args[0] === "test") {
721
+ runTests();
722
+ process.exit(0);
723
+ } else if (args[0] === "docs") {
724
+ generateDocs();
725
+ process.exit(0);
726
+ } else {
727
+ let compileOnly = false;
728
+ let outputFile = null;
729
+ let filename = null;
730
+ let target = "node";
731
+ let sourcemap = false;
732
+
733
+ for (let i = 0; i < args.length; i++) {
734
+ if (args[i] === "-c") compileOnly = true;
735
+ else if (args[i] === "-o" && args[i + 1]) {
736
+ outputFile = args[i + 1];
737
+ i++;
738
+ } else if ((args[i] === "--target" || args[i] === "-t") && args[i + 1]) {
739
+ target = args[i + 1];
740
+ i++;
741
+ } else if (args[i] === "--sourcemap") sourcemap = true;
742
+ else if (!filename) filename = args[i];
743
+ }
744
+
745
+ if (!filename) {
746
+ log("Error: No file specified", colors.red);
747
+ process.exit(1);
748
+ }
749
+
750
+ const fullPath = path.resolve(filename);
751
+ if (!fs.existsSync(fullPath)) {
752
+ log(`Error: File not found: ${filename}`, colors.red);
753
+ process.exit(1);
754
+ }
755
+
756
+ const source = fs.readFileSync(fullPath, "utf8").trim();
757
+
758
+ try {
759
+ const result = compile(source, { target, sourcemap, sourceFile: path.basename(fullPath) });
760
+ const js = typeof result === "string" ? result : result.code;
761
+ const map = typeof result === "object" ? result.map : null;
762
+
763
+ if (outputFile) {
764
+ let toWrite = js;
765
+ const outPath = path.resolve(outputFile);
766
+ if (sourcemap && map) {
767
+ const mapPath = outPath + ".map";
768
+ fs.writeFileSync(mapPath, map, "utf8");
769
+ toWrite = js + "\n//# sourceMappingURL=" + path.basename(mapPath);
770
+ }
771
+ fs.writeFileSync(outPath, toWrite, "utf8");
772
+ log(`Successfully compiled to ${outputFile}`, colors.green);
773
+ } else if (compileOnly) {
774
+ console.log(js);
775
+ } else {
776
+ log(`--- Running ${path.basename(filename)} ---`, colors.blue);
777
+
778
+ const tempDir = path.dirname(fullPath);
779
+ const tempFile = path.join(tempDir, `.${path.basename(filename)}.js`);
780
+ let jsToRun = js;
781
+ if (sourcemap && map) {
782
+ const mapPath = tempFile + ".map";
783
+ fs.writeFileSync(mapPath, map, "utf8");
784
+ jsToRun = js + "\n//# sourceMappingURL=" + path.basename(mapPath);
785
+ }
786
+ fs.writeFileSync(tempFile, jsToRun, "utf8");
787
+
788
+ try {
789
+ const nodeCmd = sourcemap
790
+ ? `node --enable-source-maps "${tempFile}"`
791
+ : `node "${tempFile}"`;
792
+ execSync(nodeCmd, { stdio: "inherit", cwd: tempDir });
793
+ } catch (_err) {
794
+ // Error is already printed by inherit stdio
795
+ } finally {
796
+ if (fs.existsSync(tempFile)) fs.unlinkSync(tempFile);
797
+ const tempMap = tempFile + ".map";
798
+ if (fs.existsSync(tempMap)) fs.unlinkSync(tempMap);
799
+ }
800
+ }
801
+ } catch (e) {
802
+ log("Kadence Error:", colors.red + colors.bright);
803
+ console.error(e.message);
804
+ process.exit(1);
805
+ }
806
+ }