@korajs/cli 0.1.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 (38) hide show
  1. package/dist/bin.cjs +2170 -0
  2. package/dist/bin.cjs.map +1 -0
  3. package/dist/bin.js +1640 -0
  4. package/dist/bin.js.map +1 -0
  5. package/dist/chunk-N36PFOSA.js +103 -0
  6. package/dist/chunk-N36PFOSA.js.map +1 -0
  7. package/dist/chunk-REOTYAM6.js +370 -0
  8. package/dist/chunk-REOTYAM6.js.map +1 -0
  9. package/dist/chunk-ZVB4HAB3.js +88 -0
  10. package/dist/chunk-ZVB4HAB3.js.map +1 -0
  11. package/dist/create.cjs +313 -0
  12. package/dist/create.cjs.map +1 -0
  13. package/dist/create.js +10 -0
  14. package/dist/create.js.map +1 -0
  15. package/dist/index.cjs +218 -0
  16. package/dist/index.cjs.map +1 -0
  17. package/dist/index.d.cts +72 -0
  18. package/dist/index.d.ts +72 -0
  19. package/dist/index.js +26 -0
  20. package/dist/index.js.map +1 -0
  21. package/package.json +48 -0
  22. package/templates/react-basic/index.html.hbs +12 -0
  23. package/templates/react-basic/kora.config.ts +13 -0
  24. package/templates/react-basic/package.json.hbs +26 -0
  25. package/templates/react-basic/src/App.tsx +48 -0
  26. package/templates/react-basic/src/main.tsx +16 -0
  27. package/templates/react-basic/src/schema.ts +15 -0
  28. package/templates/react-basic/tsconfig.json +14 -0
  29. package/templates/react-basic/vite.config.ts +6 -0
  30. package/templates/react-sync/index.html.hbs +12 -0
  31. package/templates/react-sync/kora.config.ts +17 -0
  32. package/templates/react-sync/package.json.hbs +28 -0
  33. package/templates/react-sync/server.ts +11 -0
  34. package/templates/react-sync/src/App.tsx +50 -0
  35. package/templates/react-sync/src/main.tsx +24 -0
  36. package/templates/react-sync/src/schema.ts +15 -0
  37. package/templates/react-sync/tsconfig.json +14 -0
  38. package/templates/react-sync/vite.config.ts +6 -0
@@ -0,0 +1,370 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ PACKAGE_MANAGERS,
4
+ ProjectExistsError,
5
+ TEMPLATES,
6
+ TEMPLATE_INFO
7
+ } from "./chunk-ZVB4HAB3.js";
8
+
9
+ // src/commands/create/create-command.ts
10
+ import { execSync as execSync2 } from "child_process";
11
+ import { readFileSync } from "fs";
12
+ import { dirname as dirname3, resolve as resolve3 } from "path";
13
+ import { fileURLToPath as fileURLToPath2 } from "url";
14
+ import { defineCommand } from "citty";
15
+
16
+ // src/utils/fs-helpers.ts
17
+ import { access, readFile } from "fs/promises";
18
+ import { dirname, join, resolve } from "path";
19
+ async function directoryExists(path) {
20
+ try {
21
+ await access(path);
22
+ return true;
23
+ } catch {
24
+ return false;
25
+ }
26
+ }
27
+ async function findProjectRoot(startDir) {
28
+ let current = resolve(startDir ?? process.cwd());
29
+ for (; ; ) {
30
+ const pkgPath = join(current, "package.json");
31
+ try {
32
+ const content = await readFile(pkgPath, "utf-8");
33
+ const pkg = JSON.parse(content);
34
+ if (isKoraProject(pkg)) {
35
+ return current;
36
+ }
37
+ } catch {
38
+ }
39
+ const parent = dirname(current);
40
+ if (parent === current) break;
41
+ current = parent;
42
+ }
43
+ return null;
44
+ }
45
+ async function findSchemaFile(projectRoot) {
46
+ const candidates = [
47
+ join(projectRoot, "src", "schema.ts"),
48
+ join(projectRoot, "schema.ts"),
49
+ join(projectRoot, "src", "schema.js"),
50
+ join(projectRoot, "schema.js")
51
+ ];
52
+ for (const candidate of candidates) {
53
+ try {
54
+ await access(candidate);
55
+ return candidate;
56
+ } catch {
57
+ }
58
+ }
59
+ return null;
60
+ }
61
+ async function resolveProjectBinary(projectRoot, binaryName) {
62
+ const binaryPath = join(projectRoot, "node_modules", ".bin", binaryName);
63
+ try {
64
+ await access(binaryPath);
65
+ return binaryPath;
66
+ } catch {
67
+ return null;
68
+ }
69
+ }
70
+ function isKoraProject(pkg) {
71
+ if (typeof pkg !== "object" || pkg === null) return false;
72
+ const record = pkg;
73
+ return hasKoraDep(record.dependencies) || hasKoraDep(record.devDependencies);
74
+ }
75
+ function hasKoraDep(deps) {
76
+ if (typeof deps !== "object" || deps === null) return false;
77
+ return Object.keys(deps).some((key) => key === "kora" || key.startsWith("@korajs/"));
78
+ }
79
+
80
+ // src/utils/logger.ts
81
+ var RESET = "\x1B[0m";
82
+ var BOLD = "\x1B[1m";
83
+ var DIM = "\x1B[2m";
84
+ var GREEN = "\x1B[32m";
85
+ var YELLOW = "\x1B[33m";
86
+ var RED = "\x1B[31m";
87
+ var CYAN = "\x1B[36m";
88
+ function createLogger(options) {
89
+ const colorDisabled = options?.noColor === true || process.env.NO_COLOR !== void 0 || !process.stdout.isTTY;
90
+ function color(code, text) {
91
+ return colorDisabled ? text : `${code}${text}${RESET}`;
92
+ }
93
+ return {
94
+ info(message) {
95
+ console.log(color(CYAN, message));
96
+ },
97
+ success(message) {
98
+ console.log(color(GREEN, ` \u2713 ${message}`));
99
+ },
100
+ warn(message) {
101
+ console.warn(color(YELLOW, ` \u26A0 ${message}`));
102
+ },
103
+ error(message) {
104
+ console.error(color(RED, ` \u2717 ${message}`));
105
+ },
106
+ step(message) {
107
+ console.log(color(DIM, ` ${message}`));
108
+ },
109
+ blank() {
110
+ console.log();
111
+ },
112
+ banner() {
113
+ console.log();
114
+ console.log(
115
+ color(BOLD + CYAN, " Kora.js") + color(DIM, " \u2014 Offline-first application framework")
116
+ );
117
+ console.log();
118
+ }
119
+ };
120
+ }
121
+
122
+ // src/utils/package-manager.ts
123
+ import { execSync } from "child_process";
124
+ function detectPackageManager() {
125
+ const userAgent = process.env.npm_config_user_agent;
126
+ if (!userAgent) return "npm";
127
+ if (userAgent.startsWith("pnpm/")) return "pnpm";
128
+ if (userAgent.startsWith("yarn/")) return "yarn";
129
+ if (userAgent.startsWith("bun/")) return "bun";
130
+ return "npm";
131
+ }
132
+ function getInstallCommand(pm) {
133
+ return pm === "yarn" ? "yarn" : `${pm} install`;
134
+ }
135
+ function getRunDevCommand(pm) {
136
+ if (pm === "npm") return "npm run dev";
137
+ return `${pm} dev`;
138
+ }
139
+
140
+ // src/utils/prompt.ts
141
+ import { createInterface } from "readline";
142
+ function promptText(message, defaultValue, options) {
143
+ return new Promise((resolve4) => {
144
+ const rl = createReadline(options);
145
+ const suffix = defaultValue !== void 0 ? ` (${defaultValue})` : "";
146
+ rl.question(` ? ${message}${suffix}: `, (answer) => {
147
+ rl.close();
148
+ const trimmed = answer.trim();
149
+ resolve4(trimmed || defaultValue || "");
150
+ });
151
+ });
152
+ }
153
+ function promptSelect(message, choices, options) {
154
+ return new Promise((resolve4) => {
155
+ const rl = createReadline(options);
156
+ const out = options?.output ?? process.stdout;
157
+ out.write(` ? ${message}
158
+ `);
159
+ for (let i = 0; i < choices.length; i++) {
160
+ const choice = choices[i];
161
+ if (choice) {
162
+ out.write(` ${i + 1}) ${choice.label}
163
+ `);
164
+ }
165
+ }
166
+ const ask = () => {
167
+ rl.question(" > ", (answer) => {
168
+ const index = Number.parseInt(answer.trim(), 10) - 1;
169
+ const selected = choices[index];
170
+ if (selected) {
171
+ rl.close();
172
+ resolve4(selected.value);
173
+ } else {
174
+ out.write(` Please enter a number between 1 and ${choices.length}
175
+ `);
176
+ ask();
177
+ }
178
+ });
179
+ };
180
+ ask();
181
+ });
182
+ }
183
+ function promptConfirm(message, defaultValue = false, options) {
184
+ return new Promise((resolve4) => {
185
+ const rl = createReadline(options);
186
+ const suffix = defaultValue ? "Y/n" : "y/N";
187
+ const ask = () => {
188
+ rl.question(` ? ${message} (${suffix}): `, (answer) => {
189
+ const normalized = answer.trim().toLowerCase();
190
+ if (normalized.length === 0) {
191
+ rl.close();
192
+ resolve4(defaultValue);
193
+ return;
194
+ }
195
+ if (normalized === "y" || normalized === "yes") {
196
+ rl.close();
197
+ resolve4(true);
198
+ return;
199
+ }
200
+ if (normalized === "n" || normalized === "no") {
201
+ rl.close();
202
+ resolve4(false);
203
+ return;
204
+ }
205
+ (options?.output ?? process.stdout).write(" Please answer with y or n\n");
206
+ ask();
207
+ });
208
+ };
209
+ ask();
210
+ });
211
+ }
212
+ function createReadline(options) {
213
+ return createInterface({
214
+ input: options?.input ?? process.stdin,
215
+ output: options?.output ?? process.stdout
216
+ });
217
+ }
218
+
219
+ // src/commands/create/template-engine.ts
220
+ import { copyFile, mkdir, readFile as readFile2, readdir, stat, writeFile } from "fs/promises";
221
+ import { dirname as dirname2, join as join2, resolve as resolve2 } from "path";
222
+ import { fileURLToPath } from "url";
223
+ function substituteVariables(template, context) {
224
+ return template.replace(/\{\{(\w+)\}\}/g, (_match, key) => {
225
+ const value = context[key];
226
+ return value !== void 0 ? value : `{{${key}}}`;
227
+ });
228
+ }
229
+ function getTemplatePath(templateName) {
230
+ const currentDir = dirname2(fileURLToPath(import.meta.url));
231
+ return resolve2(currentDir, "..", "..", "..", "templates", templateName);
232
+ }
233
+ async function scaffoldTemplate(templateName, targetDir, context) {
234
+ const templateDir = getTemplatePath(templateName);
235
+ const vars = {
236
+ projectName: context.projectName,
237
+ packageManager: context.packageManager,
238
+ koraVersion: context.koraVersion
239
+ };
240
+ await copyDirectory(templateDir, targetDir, vars);
241
+ }
242
+ async function copyDirectory(src, dest, vars) {
243
+ await mkdir(dest, { recursive: true });
244
+ const entries = await readdir(src);
245
+ for (const entry of entries) {
246
+ const srcPath = join2(src, entry);
247
+ const srcStat = await stat(srcPath);
248
+ if (srcStat.isDirectory()) {
249
+ await copyDirectory(srcPath, join2(dest, entry), vars);
250
+ } else if (entry.endsWith(".hbs")) {
251
+ const content = await readFile2(srcPath, "utf-8");
252
+ const outputName = entry.slice(0, -4);
253
+ await writeFile(join2(dest, outputName), substituteVariables(content, vars), "utf-8");
254
+ } else {
255
+ await copyFile(srcPath, join2(dest, entry));
256
+ }
257
+ }
258
+ }
259
+
260
+ // src/commands/create/create-command.ts
261
+ var createCommand = defineCommand({
262
+ meta: {
263
+ name: "create",
264
+ description: "Create a new Kora application"
265
+ },
266
+ args: {
267
+ name: {
268
+ type: "positional",
269
+ description: "Project directory name",
270
+ required: false
271
+ },
272
+ template: {
273
+ type: "string",
274
+ description: "Project template (react-basic, react-sync)"
275
+ },
276
+ pm: {
277
+ type: "string",
278
+ description: "Package manager (pnpm, npm, yarn, bun)"
279
+ },
280
+ "skip-install": {
281
+ type: "boolean",
282
+ description: "Skip installing dependencies",
283
+ default: false
284
+ }
285
+ },
286
+ async run({ args }) {
287
+ const logger = createLogger();
288
+ logger.banner();
289
+ const projectName = args.name || await promptText("Project name", "my-kora-app");
290
+ if (!projectName) {
291
+ logger.error("Project name is required");
292
+ process.exitCode = 1;
293
+ return;
294
+ }
295
+ let template;
296
+ if (args.template && isValidTemplate(args.template)) {
297
+ template = args.template;
298
+ } else {
299
+ template = await promptSelect(
300
+ "Select a template:",
301
+ TEMPLATE_INFO.map((t) => ({ label: `${t.label} \u2014 ${t.description}`, value: t.name }))
302
+ );
303
+ }
304
+ let pm;
305
+ if (args.pm && isValidPackageManager(args.pm)) {
306
+ pm = args.pm;
307
+ } else {
308
+ const detected = detectPackageManager();
309
+ pm = await promptSelect(
310
+ "Package manager:",
311
+ PACKAGE_MANAGERS.map((p) => ({
312
+ label: p === detected ? `${p} (detected)` : p,
313
+ value: p
314
+ }))
315
+ );
316
+ }
317
+ const targetDir = resolve3(process.cwd(), projectName);
318
+ if (await directoryExists(targetDir)) {
319
+ throw new ProjectExistsError(projectName);
320
+ }
321
+ const koraVersion = resolveKoraVersion();
322
+ logger.step(`Creating ${projectName} with ${template} template...`);
323
+ await scaffoldTemplate(template, targetDir, {
324
+ projectName,
325
+ packageManager: pm,
326
+ koraVersion
327
+ });
328
+ logger.success("Project scaffolded");
329
+ if (!args["skip-install"]) {
330
+ logger.step("Installing dependencies...");
331
+ try {
332
+ execSync2(getInstallCommand(pm), { cwd: targetDir, stdio: "inherit" });
333
+ logger.success("Dependencies installed");
334
+ } catch {
335
+ logger.warn("Failed to install dependencies. Run install manually.");
336
+ }
337
+ }
338
+ logger.blank();
339
+ logger.info("Done! Next steps:");
340
+ logger.blank();
341
+ logger.step(` cd ${projectName}`);
342
+ logger.step(` ${getRunDevCommand(pm)}`);
343
+ logger.blank();
344
+ }
345
+ });
346
+ function isValidTemplate(value) {
347
+ return TEMPLATES.includes(value);
348
+ }
349
+ function isValidPackageManager(value) {
350
+ return PACKAGE_MANAGERS.includes(value);
351
+ }
352
+ function resolveKoraVersion() {
353
+ try {
354
+ const cliDir = resolve3(dirname3(fileURLToPath2(import.meta.url)), "..", "..", "..");
355
+ const pkg = JSON.parse(readFileSync(resolve3(cliDir, "package.json"), "utf-8"));
356
+ return pkg.version === "0.0.0" ? "latest" : `^${pkg.version}`;
357
+ } catch {
358
+ return "latest";
359
+ }
360
+ }
361
+
362
+ export {
363
+ findProjectRoot,
364
+ findSchemaFile,
365
+ resolveProjectBinary,
366
+ createLogger,
367
+ promptConfirm,
368
+ createCommand
369
+ };
370
+ //# sourceMappingURL=chunk-REOTYAM6.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/create/create-command.ts","../src/utils/fs-helpers.ts","../src/utils/logger.ts","../src/utils/package-manager.ts","../src/utils/prompt.ts","../src/commands/create/template-engine.ts"],"sourcesContent":["import { execSync } from 'node:child_process'\nimport { readFileSync } from 'node:fs'\nimport { dirname, resolve } from 'node:path'\nimport { fileURLToPath } from 'node:url'\nimport { defineCommand } from 'citty'\nimport { ProjectExistsError } from '../../errors'\nimport { PACKAGE_MANAGERS, TEMPLATES, TEMPLATE_INFO } from '../../types'\nimport type { PackageManager, TemplateName } from '../../types'\nimport { directoryExists } from '../../utils/fs-helpers'\nimport { createLogger } from '../../utils/logger'\nimport {\n\tdetectPackageManager,\n\tgetInstallCommand,\n\tgetRunDevCommand,\n} from '../../utils/package-manager'\nimport { promptSelect, promptText } from '../../utils/prompt'\nimport { scaffoldTemplate } from './template-engine'\n\n/**\n * The `create` command — scaffolds a new Kora project.\n * Used as `kora create <name>` or `create-kora-app <name>`.\n */\nexport const createCommand = defineCommand({\n\tmeta: {\n\t\tname: 'create',\n\t\tdescription: 'Create a new Kora application',\n\t},\n\targs: {\n\t\tname: {\n\t\t\ttype: 'positional',\n\t\t\tdescription: 'Project directory name',\n\t\t\trequired: false,\n\t\t},\n\t\ttemplate: {\n\t\t\ttype: 'string',\n\t\t\tdescription: 'Project template (react-basic, react-sync)',\n\t\t},\n\t\tpm: {\n\t\t\ttype: 'string',\n\t\t\tdescription: 'Package manager (pnpm, npm, yarn, bun)',\n\t\t},\n\t\t'skip-install': {\n\t\t\ttype: 'boolean',\n\t\t\tdescription: 'Skip installing dependencies',\n\t\t\tdefault: false,\n\t\t},\n\t},\n\tasync run({ args }) {\n\t\tconst logger = createLogger()\n\t\tlogger.banner()\n\n\t\t// Resolve project name\n\t\tconst projectName = args.name || (await promptText('Project name', 'my-kora-app'))\n\t\tif (!projectName) {\n\t\t\tlogger.error('Project name is required')\n\t\t\tprocess.exitCode = 1\n\t\t\treturn\n\t\t}\n\n\t\t// Resolve template\n\t\tlet template: TemplateName\n\t\tif (args.template && isValidTemplate(args.template)) {\n\t\t\ttemplate = args.template\n\t\t} else {\n\t\t\ttemplate = await promptSelect(\n\t\t\t\t'Select a template:',\n\t\t\t\tTEMPLATE_INFO.map((t) => ({ label: `${t.label} — ${t.description}`, value: t.name })),\n\t\t\t)\n\t\t}\n\n\t\t// Resolve package manager\n\t\tlet pm: PackageManager\n\t\tif (args.pm && isValidPackageManager(args.pm)) {\n\t\t\tpm = args.pm\n\t\t} else {\n\t\t\tconst detected = detectPackageManager()\n\t\t\tpm = await promptSelect(\n\t\t\t\t'Package manager:',\n\t\t\t\tPACKAGE_MANAGERS.map((p) => ({\n\t\t\t\t\tlabel: p === detected ? `${p} (detected)` : p,\n\t\t\t\t\tvalue: p,\n\t\t\t\t})),\n\t\t\t)\n\t\t}\n\n\t\t// Validate target directory\n\t\tconst targetDir = resolve(process.cwd(), projectName)\n\t\tif (await directoryExists(targetDir)) {\n\t\t\tthrow new ProjectExistsError(projectName)\n\t\t}\n\n\t\t// Resolve kora version from this package's own package.json\n\t\tconst koraVersion = resolveKoraVersion()\n\n\t\t// Scaffold\n\t\tlogger.step(`Creating ${projectName} with ${template} template...`)\n\t\tawait scaffoldTemplate(template, targetDir, {\n\t\t\tprojectName,\n\t\t\tpackageManager: pm,\n\t\t\tkoraVersion,\n\t\t})\n\t\tlogger.success('Project scaffolded')\n\n\t\t// Install dependencies\n\t\tif (!args['skip-install']) {\n\t\t\tlogger.step('Installing dependencies...')\n\t\t\ttry {\n\t\t\t\texecSync(getInstallCommand(pm), { cwd: targetDir, stdio: 'inherit' })\n\t\t\t\tlogger.success('Dependencies installed')\n\t\t\t} catch {\n\t\t\t\tlogger.warn('Failed to install dependencies. Run install manually.')\n\t\t\t}\n\t\t}\n\n\t\t// Print next steps\n\t\tlogger.blank()\n\t\tlogger.info('Done! Next steps:')\n\t\tlogger.blank()\n\t\tlogger.step(` cd ${projectName}`)\n\t\tlogger.step(` ${getRunDevCommand(pm)}`)\n\t\tlogger.blank()\n\t},\n})\n\nfunction isValidTemplate(value: string): value is TemplateName {\n\treturn (TEMPLATES as readonly string[]).includes(value)\n}\n\nfunction isValidPackageManager(value: string): value is PackageManager {\n\treturn (PACKAGE_MANAGERS as readonly string[]).includes(value)\n}\n\n/**\n * Reads the version from @korajs/cli's own package.json.\n * Scaffolded projects pin their kora dependencies to this version.\n */\nfunction resolveKoraVersion(): string {\n\ttry {\n\t\tconst cliDir = resolve(dirname(fileURLToPath(import.meta.url)), '..', '..', '..')\n\t\tconst pkg = JSON.parse(readFileSync(resolve(cliDir, 'package.json'), 'utf-8')) as { version: string }\n\t\treturn pkg.version === '0.0.0' ? 'latest' : `^${pkg.version}`\n\t} catch {\n\t\treturn 'latest'\n\t}\n}\n","import { access, readFile } from 'node:fs/promises'\nimport { dirname, join, resolve } from 'node:path'\n\n/** Checks if a directory exists at the given path */\nexport async function directoryExists(path: string): Promise<boolean> {\n\ttry {\n\t\tawait access(path)\n\t\treturn true\n\t} catch {\n\t\treturn false\n\t}\n}\n\n/**\n * Walks up the directory tree from startDir looking for a package.json\n * that contains a kora or @korajs/* dependency.\n *\n * @param startDir - Directory to start searching from (defaults to cwd)\n * @returns Absolute path to the project root, or null if not found\n */\nexport async function findProjectRoot(startDir?: string): Promise<string | null> {\n\tlet current = resolve(startDir ?? process.cwd())\n\n\t// Walk up until the filesystem root (where dirname(x) === x)\n\tfor (;;) {\n\t\tconst pkgPath = join(current, 'package.json')\n\t\ttry {\n\t\t\tconst content = await readFile(pkgPath, 'utf-8')\n\t\t\tconst pkg: unknown = JSON.parse(content)\n\t\t\tif (isKoraProject(pkg)) {\n\t\t\t\treturn current\n\t\t\t}\n\t\t} catch {\n\t\t\t// No package.json at this level, keep walking up\n\t\t}\n\t\tconst parent = dirname(current)\n\t\tif (parent === current) break\n\t\tcurrent = parent\n\t}\n\n\treturn null\n}\n\n/**\n * Searches for a schema file in common locations within a project.\n *\n * @param projectRoot - The project root directory\n * @returns Absolute path to the schema file, or null if not found\n */\nexport async function findSchemaFile(projectRoot: string): Promise<string | null> {\n\tconst candidates = [\n\t\tjoin(projectRoot, 'src', 'schema.ts'),\n\t\tjoin(projectRoot, 'schema.ts'),\n\t\tjoin(projectRoot, 'src', 'schema.js'),\n\t\tjoin(projectRoot, 'schema.js'),\n\t]\n\n\tfor (const candidate of candidates) {\n\t\ttry {\n\t\t\tawait access(candidate)\n\t\t\treturn candidate\n\t\t} catch {\n\t\t\t// Not found, try next\n\t\t}\n\t}\n\n\treturn null\n}\n\n/**\n * Resolves a binary from a project's local node_modules/.bin directory.\n *\n * @param projectRoot - The project root directory\n * @param binaryName - Binary filename (for example: vite, tsx, kora)\n * @returns Absolute path to the binary, or null if not found\n */\nexport async function resolveProjectBinary(\n\tprojectRoot: string,\n\tbinaryName: string,\n): Promise<string | null> {\n\tconst binaryPath = join(projectRoot, 'node_modules', '.bin', binaryName)\n\n\ttry {\n\t\tawait access(binaryPath)\n\t\treturn binaryPath\n\t} catch {\n\t\treturn null\n\t}\n}\n\nfunction isKoraProject(pkg: unknown): boolean {\n\tif (typeof pkg !== 'object' || pkg === null) return false\n\tconst record = pkg as Record<string, unknown>\n\treturn hasKoraDep(record.dependencies) || hasKoraDep(record.devDependencies)\n}\n\nfunction hasKoraDep(deps: unknown): boolean {\n\tif (typeof deps !== 'object' || deps === null) return false\n\treturn Object.keys(deps).some((key) => key === 'kora' || key.startsWith('@korajs/'))\n}\n","/** ANSI escape codes for terminal colors */\nconst RESET = '\\x1b[0m'\nconst BOLD = '\\x1b[1m'\nconst DIM = '\\x1b[2m'\nconst GREEN = '\\x1b[32m'\nconst YELLOW = '\\x1b[33m'\nconst RED = '\\x1b[31m'\nconst CYAN = '\\x1b[36m'\n\nexport interface LoggerOptions {\n\t/** Disable ANSI colors */\n\tnoColor?: boolean\n}\n\nexport interface Logger {\n\tinfo(message: string): void\n\tsuccess(message: string): void\n\twarn(message: string): void\n\terror(message: string): void\n\tstep(message: string): void\n\tblank(): void\n\tbanner(): void\n}\n\n/**\n * Creates a logger with optional ANSI color support.\n * Respects the NO_COLOR environment variable and TTY detection.\n */\nexport function createLogger(options?: LoggerOptions): Logger {\n\tconst colorDisabled =\n\t\toptions?.noColor === true || process.env.NO_COLOR !== undefined || !process.stdout.isTTY\n\n\tfunction color(code: string, text: string): string {\n\t\treturn colorDisabled ? text : `${code}${text}${RESET}`\n\t}\n\n\treturn {\n\t\tinfo(message: string): void {\n\t\t\tconsole.log(color(CYAN, message))\n\t\t},\n\t\tsuccess(message: string): void {\n\t\t\tconsole.log(color(GREEN, ` ✓ ${message}`))\n\t\t},\n\t\twarn(message: string): void {\n\t\t\tconsole.warn(color(YELLOW, ` ⚠ ${message}`))\n\t\t},\n\t\terror(message: string): void {\n\t\t\tconsole.error(color(RED, ` ✗ ${message}`))\n\t\t},\n\t\tstep(message: string): void {\n\t\t\tconsole.log(color(DIM, ` ${message}`))\n\t\t},\n\t\tblank(): void {\n\t\t\tconsole.log()\n\t\t},\n\t\tbanner(): void {\n\t\t\tconsole.log()\n\t\t\tconsole.log(\n\t\t\t\tcolor(BOLD + CYAN, ' Kora.js') + color(DIM, ' — Offline-first application framework'),\n\t\t\t)\n\t\t\tconsole.log()\n\t\t},\n\t}\n}\n","import { execSync } from 'node:child_process'\nimport type { PackageManager } from '../types'\n\n/**\n * Detects the package manager used to invoke the current process\n * by reading the npm_config_user_agent environment variable.\n * Falls back to 'npm' if detection fails.\n */\nexport function detectPackageManager(): PackageManager {\n\tconst userAgent = process.env.npm_config_user_agent\n\tif (!userAgent) return 'npm'\n\n\tif (userAgent.startsWith('pnpm/')) return 'pnpm'\n\tif (userAgent.startsWith('yarn/')) return 'yarn'\n\tif (userAgent.startsWith('bun/')) return 'bun'\n\treturn 'npm'\n}\n\n/** Returns the install command for the given package manager */\nexport function getInstallCommand(pm: PackageManager): string {\n\treturn pm === 'yarn' ? 'yarn' : `${pm} install`\n}\n\n/** Returns the dev server run command for the given package manager */\nexport function getRunDevCommand(pm: PackageManager): string {\n\tif (pm === 'npm') return 'npm run dev'\n\treturn `${pm} dev`\n}\n\n/** Checks if a package manager is available on PATH */\nexport function isPackageManagerAvailable(pm: PackageManager): boolean {\n\ttry {\n\t\texecSync(`${pm} --version`, { stdio: 'ignore' })\n\t\treturn true\n\t} catch {\n\t\treturn false\n\t}\n}\n","import { type Interface as ReadlineInterface, createInterface } from 'node:readline'\n\nexport interface PromptOptions {\n\t/** Input stream (defaults to process.stdin) */\n\tinput?: NodeJS.ReadableStream\n\t/** Output stream (defaults to process.stdout) */\n\toutput?: NodeJS.WritableStream\n}\n\n/**\n * Prompts the user for text input.\n *\n * @param message - The prompt message to display\n * @param defaultValue - Optional default value shown in brackets\n * @param options - Optional input/output streams for testing\n */\nexport function promptText(\n\tmessage: string,\n\tdefaultValue?: string,\n\toptions?: PromptOptions,\n): Promise<string> {\n\treturn new Promise((resolve) => {\n\t\tconst rl = createReadline(options)\n\t\tconst suffix = defaultValue !== undefined ? ` (${defaultValue})` : ''\n\t\trl.question(` ? ${message}${suffix}: `, (answer) => {\n\t\t\trl.close()\n\t\t\tconst trimmed = answer.trim()\n\t\t\tresolve(trimmed || defaultValue || '')\n\t\t})\n\t})\n}\n\n/**\n * Prompts the user to select from a numbered list of options.\n *\n * @param message - The prompt message to display\n * @param choices - Array of { label, value } options\n * @param options - Optional input/output streams for testing\n */\nexport function promptSelect<T extends string>(\n\tmessage: string,\n\tchoices: readonly { label: string; value: T }[],\n\toptions?: PromptOptions,\n): Promise<T> {\n\treturn new Promise((resolve) => {\n\t\tconst rl = createReadline(options)\n\t\tconst out = options?.output ?? process.stdout\n\n\t\tout.write(` ? ${message}\\n`)\n\t\tfor (let i = 0; i < choices.length; i++) {\n\t\t\tconst choice = choices[i]\n\t\t\tif (choice) {\n\t\t\t\tout.write(` ${i + 1}) ${choice.label}\\n`)\n\t\t\t}\n\t\t}\n\n\t\tconst ask = (): void => {\n\t\t\trl.question(' > ', (answer) => {\n\t\t\t\tconst index = Number.parseInt(answer.trim(), 10) - 1\n\t\t\t\tconst selected = choices[index]\n\t\t\t\tif (selected) {\n\t\t\t\t\trl.close()\n\t\t\t\t\tresolve(selected.value)\n\t\t\t\t} else {\n\t\t\t\t\tout.write(` Please enter a number between 1 and ${choices.length}\\n`)\n\t\t\t\t\task()\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\n\t\task()\n\t})\n}\n\n/**\n * Prompts the user with a yes/no confirmation.\n *\n * @param message - The prompt message to display\n * @param defaultValue - Default when input is empty (true => yes)\n * @param options - Optional input/output streams for testing\n */\nexport function promptConfirm(\n\tmessage: string,\n\tdefaultValue = false,\n\toptions?: PromptOptions,\n): Promise<boolean> {\n\treturn new Promise((resolve) => {\n\t\tconst rl = createReadline(options)\n\t\tconst suffix = defaultValue ? 'Y/n' : 'y/N'\n\n\t\tconst ask = (): void => {\n\t\t\trl.question(` ? ${message} (${suffix}): `, (answer) => {\n\t\t\t\tconst normalized = answer.trim().toLowerCase()\n\t\t\t\tif (normalized.length === 0) {\n\t\t\t\t\trl.close()\n\t\t\t\t\tresolve(defaultValue)\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tif (normalized === 'y' || normalized === 'yes') {\n\t\t\t\t\trl.close()\n\t\t\t\t\tresolve(true)\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tif (normalized === 'n' || normalized === 'no') {\n\t\t\t\t\trl.close()\n\t\t\t\t\tresolve(false)\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\t(options?.output ?? process.stdout).write(' Please answer with y or n\\n')\n\t\t\t\task()\n\t\t\t})\n\t\t}\n\n\t\task()\n\t})\n}\n\nfunction createReadline(options?: PromptOptions): ReadlineInterface {\n\treturn createInterface({\n\t\tinput: options?.input ?? process.stdin,\n\t\toutput: options?.output ?? process.stdout,\n\t})\n}\n","import { copyFile, mkdir, readFile, readdir, stat, writeFile } from 'node:fs/promises'\nimport { dirname, join, resolve } from 'node:path'\nimport { fileURLToPath } from 'node:url'\nimport type { TemplateContext, TemplateName } from '../../types'\n\n/**\n * Replaces {{variable}} placeholders in a template string with context values.\n *\n * @param template - The template string containing {{variable}} placeholders\n * @param context - Key-value pairs to substitute\n * @returns The template with all placeholders replaced\n */\nexport function substituteVariables(template: string, context: Record<string, string>): string {\n\treturn template.replace(/\\{\\{(\\w+)\\}\\}/g, (_match, key: string) => {\n\t\tconst value = context[key]\n\t\treturn value !== undefined ? value : `{{${key}}}`\n\t})\n}\n\n/**\n * Resolves the absolute path to a bundled template directory.\n *\n * @param templateName - Name of the template (e.g. 'react-basic')\n * @returns Absolute path to the template directory\n */\nexport function getTemplatePath(templateName: TemplateName): string {\n\t// Navigate from src/commands/create/ up to package root, then into templates/\n\tconst currentDir = dirname(fileURLToPath(import.meta.url))\n\treturn resolve(currentDir, '..', '..', '..', 'templates', templateName)\n}\n\n/**\n * Scaffolds a project from a bundled template.\n * Copies all files from the template directory to the target, applying\n * variable substitution to .hbs files and stripping the .hbs extension.\n *\n * @param templateName - Which template to use\n * @param targetDir - Destination directory (must not exist yet)\n * @param context - Variables for template substitution\n */\nexport async function scaffoldTemplate(\n\ttemplateName: TemplateName,\n\ttargetDir: string,\n\tcontext: TemplateContext,\n): Promise<void> {\n\tconst templateDir = getTemplatePath(templateName)\n\tconst vars: Record<string, string> = {\n\t\tprojectName: context.projectName,\n\t\tpackageManager: context.packageManager,\n\t\tkoraVersion: context.koraVersion,\n\t}\n\tawait copyDirectory(templateDir, targetDir, vars)\n}\n\nasync function copyDirectory(\n\tsrc: string,\n\tdest: string,\n\tvars: Record<string, string>,\n): Promise<void> {\n\tawait mkdir(dest, { recursive: true })\n\tconst entries = await readdir(src)\n\n\tfor (const entry of entries) {\n\t\tconst srcPath = join(src, entry)\n\t\tconst srcStat = await stat(srcPath)\n\n\t\tif (srcStat.isDirectory()) {\n\t\t\tawait copyDirectory(srcPath, join(dest, entry), vars)\n\t\t} else if (entry.endsWith('.hbs')) {\n\t\t\t// Template file: substitute variables and strip .hbs extension\n\t\t\tconst content = await readFile(srcPath, 'utf-8')\n\t\t\tconst outputName = entry.slice(0, -4) // Remove .hbs\n\t\t\tawait writeFile(join(dest, outputName), substituteVariables(content, vars), 'utf-8')\n\t\t} else {\n\t\t\t// Regular file: copy as-is\n\t\t\tawait copyFile(srcPath, join(dest, entry))\n\t\t}\n\t}\n}\n"],"mappings":"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","names":["execSync","dirname","resolve","fileURLToPath","resolve","readFile","dirname","join","resolve","resolve","execSync","dirname","fileURLToPath"]}
@@ -0,0 +1,88 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/errors.ts
4
+ import { KoraError } from "@korajs/core";
5
+ var CliError = class extends KoraError {
6
+ constructor(message, context) {
7
+ super(message, "CLI_ERROR", context);
8
+ this.name = "CliError";
9
+ }
10
+ };
11
+ var ProjectExistsError = class extends KoraError {
12
+ constructor(directory) {
13
+ super(
14
+ `Directory "${directory}" already exists. Choose a different name or remove the existing directory.`,
15
+ "PROJECT_EXISTS",
16
+ { directory }
17
+ );
18
+ this.directory = directory;
19
+ this.name = "ProjectExistsError";
20
+ }
21
+ directory;
22
+ };
23
+ var SchemaNotFoundError = class extends KoraError {
24
+ constructor(searchedPaths) {
25
+ super(
26
+ `Could not find a schema file. Searched: ${searchedPaths.join(", ")}. Create a schema file using defineSchema() from @korajs/core.`,
27
+ "SCHEMA_NOT_FOUND",
28
+ { searchedPaths }
29
+ );
30
+ this.searchedPaths = searchedPaths;
31
+ this.name = "SchemaNotFoundError";
32
+ }
33
+ searchedPaths;
34
+ };
35
+ var InvalidProjectError = class extends KoraError {
36
+ constructor(directory) {
37
+ super(
38
+ `"${directory}" is not a valid Kora project. No package.json with a kora dependency found. Run this command from inside a Kora project.`,
39
+ "INVALID_PROJECT",
40
+ { directory }
41
+ );
42
+ this.directory = directory;
43
+ this.name = "InvalidProjectError";
44
+ }
45
+ directory;
46
+ };
47
+ var DevServerError = class extends KoraError {
48
+ constructor(binary, searchPath) {
49
+ super(
50
+ `Could not find required binary "${binary}" at ${searchPath}. Install project dependencies and try again.`,
51
+ "DEV_SERVER_ERROR",
52
+ { binary, searchPath }
53
+ );
54
+ this.binary = binary;
55
+ this.searchPath = searchPath;
56
+ this.name = "DevServerError";
57
+ }
58
+ binary;
59
+ searchPath;
60
+ };
61
+
62
+ // src/types.ts
63
+ var PACKAGE_MANAGERS = ["pnpm", "npm", "yarn", "bun"];
64
+ var TEMPLATES = ["react-basic", "react-sync"];
65
+ var TEMPLATE_INFO = [
66
+ {
67
+ name: "react-basic",
68
+ label: "React (basic)",
69
+ description: "Local-only React app with Kora \u2014 no sync server"
70
+ },
71
+ {
72
+ name: "react-sync",
73
+ label: "React (with sync)",
74
+ description: "React app with Kora sync server included"
75
+ }
76
+ ];
77
+
78
+ export {
79
+ CliError,
80
+ ProjectExistsError,
81
+ SchemaNotFoundError,
82
+ InvalidProjectError,
83
+ DevServerError,
84
+ PACKAGE_MANAGERS,
85
+ TEMPLATES,
86
+ TEMPLATE_INFO
87
+ };
88
+ //# sourceMappingURL=chunk-ZVB4HAB3.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/errors.ts","../src/types.ts"],"sourcesContent":["import { KoraError } from '@korajs/core'\n\n/**\n * Base error class for all CLI errors.\n */\nexport class CliError extends KoraError {\n\tconstructor(message: string, context?: Record<string, unknown>) {\n\t\tsuper(message, 'CLI_ERROR', context)\n\t\tthis.name = 'CliError'\n\t}\n}\n\n/**\n * Thrown when the target project directory already exists.\n */\nexport class ProjectExistsError extends KoraError {\n\tconstructor(public readonly directory: string) {\n\t\tsuper(\n\t\t\t`Directory \"${directory}\" already exists. Choose a different name or remove the existing directory.`,\n\t\t\t'PROJECT_EXISTS',\n\t\t\t{ directory },\n\t\t)\n\t\tthis.name = 'ProjectExistsError'\n\t}\n}\n\n/**\n * Thrown when a schema file cannot be found in the project.\n */\nexport class SchemaNotFoundError extends KoraError {\n\tconstructor(public readonly searchedPaths: string[]) {\n\t\tsuper(\n\t\t\t`Could not find a schema file. Searched: ${searchedPaths.join(', ')}. Create a schema file using defineSchema() from @korajs/core.`,\n\t\t\t'SCHEMA_NOT_FOUND',\n\t\t\t{ searchedPaths },\n\t\t)\n\t\tthis.name = 'SchemaNotFoundError'\n\t}\n}\n\n/**\n * Thrown when a command is run outside a valid Kora project.\n */\nexport class InvalidProjectError extends KoraError {\n\tconstructor(public readonly directory: string) {\n\t\tsuper(\n\t\t\t`\"${directory}\" is not a valid Kora project. No package.json with a kora dependency found. Run this command from inside a Kora project.`,\n\t\t\t'INVALID_PROJECT',\n\t\t\t{ directory },\n\t\t)\n\t\tthis.name = 'InvalidProjectError'\n\t}\n}\n\n/**\n * Thrown when a required local dev server binary cannot be found.\n */\nexport class DevServerError extends KoraError {\n\tconstructor(\n\t\tpublic readonly binary: string,\n\t\tpublic readonly searchPath: string,\n\t) {\n\t\tsuper(\n\t\t\t`Could not find required binary \"${binary}\" at ${searchPath}. Install project dependencies and try again.`,\n\t\t\t'DEV_SERVER_ERROR',\n\t\t\t{ binary, searchPath },\n\t\t)\n\t\tthis.name = 'DevServerError'\n\t}\n}\n","/** Supported package managers */\nexport const PACKAGE_MANAGERS = ['pnpm', 'npm', 'yarn', 'bun'] as const\nexport type PackageManager = (typeof PACKAGE_MANAGERS)[number]\n\n/** Available project templates */\nexport const TEMPLATES = ['react-basic', 'react-sync'] as const\nexport type TemplateName = (typeof TEMPLATES)[number]\n\n/** Metadata for a project template */\nexport interface TemplateInfo {\n\tname: TemplateName\n\tlabel: string\n\tdescription: string\n}\n\n/** Available templates with their descriptions */\nexport const TEMPLATE_INFO: readonly TemplateInfo[] = [\n\t{\n\t\tname: 'react-basic',\n\t\tlabel: 'React (basic)',\n\t\tdescription: 'Local-only React app with Kora — no sync server',\n\t},\n\t{\n\t\tname: 'react-sync',\n\t\tlabel: 'React (with sync)',\n\t\tdescription: 'React app with Kora sync server included',\n\t},\n] as const\n\n/** Variables available for template substitution */\nexport interface TemplateContext {\n\tprojectName: string\n\tpackageManager: PackageManager\n\tkoraVersion: string\n}\n"],"mappings":";;;AAAA,SAAS,iBAAiB;AAKnB,IAAM,WAAN,cAAuB,UAAU;AAAA,EACvC,YAAY,SAAiB,SAAmC;AAC/D,UAAM,SAAS,aAAa,OAAO;AACnC,SAAK,OAAO;AAAA,EACb;AACD;AAKO,IAAM,qBAAN,cAAiC,UAAU;AAAA,EACjD,YAA4B,WAAmB;AAC9C;AAAA,MACC,cAAc,SAAS;AAAA,MACvB;AAAA,MACA,EAAE,UAAU;AAAA,IACb;AAL2B;AAM3B,SAAK,OAAO;AAAA,EACb;AAAA,EAP4B;AAQ7B;AAKO,IAAM,sBAAN,cAAkC,UAAU;AAAA,EAClD,YAA4B,eAAyB;AACpD;AAAA,MACC,2CAA2C,cAAc,KAAK,IAAI,CAAC;AAAA,MACnE;AAAA,MACA,EAAE,cAAc;AAAA,IACjB;AAL2B;AAM3B,SAAK,OAAO;AAAA,EACb;AAAA,EAP4B;AAQ7B;AAKO,IAAM,sBAAN,cAAkC,UAAU;AAAA,EAClD,YAA4B,WAAmB;AAC9C;AAAA,MACC,IAAI,SAAS;AAAA,MACb;AAAA,MACA,EAAE,UAAU;AAAA,IACb;AAL2B;AAM3B,SAAK,OAAO;AAAA,EACb;AAAA,EAP4B;AAQ7B;AAKO,IAAM,iBAAN,cAA6B,UAAU;AAAA,EAC7C,YACiB,QACA,YACf;AACD;AAAA,MACC,mCAAmC,MAAM,QAAQ,UAAU;AAAA,MAC3D;AAAA,MACA,EAAE,QAAQ,WAAW;AAAA,IACtB;AAPgB;AACA;AAOhB,SAAK,OAAO;AAAA,EACb;AAAA,EATiB;AAAA,EACA;AASlB;;;ACpEO,IAAM,mBAAmB,CAAC,QAAQ,OAAO,QAAQ,KAAK;AAItD,IAAM,YAAY,CAAC,eAAe,YAAY;AAW9C,IAAM,gBAAyC;AAAA,EACrD;AAAA,IACC,MAAM;AAAA,IACN,OAAO;AAAA,IACP,aAAa;AAAA,EACd;AAAA,EACA;AAAA,IACC,MAAM;AAAA,IACN,OAAO;AAAA,IACP,aAAa;AAAA,EACd;AACD;","names":[]}