@korajs/cli 0.1.6 → 0.1.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin.cjs +84 -73
- package/dist/bin.cjs.map +1 -1
- package/dist/bin.js +50 -53
- package/dist/bin.js.map +1 -1
- package/dist/{chunk-TZPACPP6.js → chunk-AUDUNRYA.js} +27 -11
- package/dist/chunk-AUDUNRYA.js.map +1 -0
- package/dist/create.cjs.map +1 -1
- package/dist/create.js +1 -1
- package/package.json +2 -2
- package/templates/react-basic/package.json.hbs +2 -0
- package/templates/react-basic/src/kora-worker.ts +3 -0
- package/templates/react-basic/src/main.tsx +7 -2
- package/templates/react-basic/vite.config.ts +34 -1
- package/templates/react-sync/package.json.hbs +2 -0
- package/templates/react-sync/src/kora-worker.ts +3 -0
- package/templates/react-sync/src/main.tsx +4 -1
- package/templates/react-sync/vite.config.ts +34 -1
- package/dist/chunk-TZPACPP6.js.map +0 -1
|
@@ -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 { existsSync, 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 and derives a\n * compatible version range for all @korajs packages.\n *\n * The CLI may be a patch ahead of other packages (e.g. CLI-only fixes),\n * so we use the major.minor range (^major.minor.0) which matches all\n * packages in the same release series.\n */\nfunction resolveKoraVersion(): string {\n\ttry {\n\t\tlet dir = dirname(fileURLToPath(import.meta.url))\n\t\tfor (let i = 0; i < 5; i++) {\n\t\t\tconst pkgPath = resolve(dir, 'package.json')\n\t\t\tif (existsSync(pkgPath)) {\n\t\t\t\tconst pkg = JSON.parse(readFileSync(pkgPath, 'utf-8')) as { name?: string; version: string }\n\t\t\t\tif (pkg.name === '@korajs/cli') {\n\t\t\t\t\tif (pkg.version === '0.0.0') return 'latest'\n\t\t\t\t\t// Use ^major.minor.0 so all packages in the series match\n\t\t\t\t\tconst parts = pkg.version.split('.')\n\t\t\t\t\treturn `^${parts[0]}.${parts[1]}.0`\n\t\t\t\t}\n\t\t\t}\n\t\t\tdir = dirname(dir)\n\t\t}\n\t\treturn 'latest'\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 * On Windows, npm creates .cmd shims instead of extensionless files.\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 binDir = join(projectRoot, 'node_modules', '.bin')\n\t// On Windows, try .cmd first (npm/pnpm create .cmd shims)\n\tconst candidates =\n\t\tprocess.platform === 'win32'\n\t\t\t? [join(binDir, `${binaryName}.cmd`), join(binDir, binaryName)]\n\t\t\t: [join(binDir, binaryName)]\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// continue\n\t\t}\n\t}\n\treturn null\n}\n\n/**\n * Checks whether the `tsx` package is installed in the project's node_modules.\n * Used to determine if we can use `--import tsx` with process.execPath.\n */\nexport async function hasTsxInstalled(projectRoot: string): Promise<boolean> {\n\ttry {\n\t\tawait access(join(projectRoot, 'node_modules', 'tsx', 'package.json'))\n\t\treturn true\n\t} catch {\n\t\treturn false\n\t}\n}\n\n/**\n * Resolves the JS entry point for a package binary.\n * Reads the package.json bin field to find the actual JS file,\n * avoiding .cmd shims and shell:true on Windows.\n *\n * @returns Absolute path to the JS entry point, or null if not found\n */\nexport async function resolveProjectBinaryEntryPoint(\n\tprojectRoot: string,\n\tpackageName: string,\n\tbinaryName: string,\n): Promise<string | null> {\n\tconst pkgJsonPath = join(projectRoot, 'node_modules', packageName, 'package.json')\n\ttry {\n\t\tconst content = await readFile(pkgJsonPath, 'utf-8')\n\t\tconst pkg = JSON.parse(content) as { bin?: string | Record<string, string> }\n\t\tlet binPath: string | undefined\n\t\tif (typeof pkg.bin === 'string') {\n\t\t\tbinPath = pkg.bin\n\t\t} else if (typeof pkg.bin === 'object' && pkg.bin !== null) {\n\t\t\tbinPath = pkg.bin[binaryName]\n\t\t}\n\t\tif (!binPath) return null\n\t\tconst fullPath = join(projectRoot, 'node_modules', packageName, binPath)\n\t\tawait access(fullPath)\n\t\treturn fullPath\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 { existsSync } from 'node:fs'\nimport { 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 * After tsup bundling, import.meta.url points to dist/<file>.js (1 level from root).\n * In source, it's src/commands/create/template-engine.ts (3 levels from root).\n * We walk up from the current file to find the package root containing templates/.\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\tlet dir = dirname(fileURLToPath(import.meta.url))\n\tfor (let i = 0; i < 5; i++) {\n\t\tif (existsSync(resolve(dir, 'templates'))) {\n\t\t\treturn resolve(dir, 'templates', templateName)\n\t\t}\n\t\tdir = dirname(dir)\n\t}\n\t// Fallback: assume bundled (1 level from package root)\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","existsSync","dirname","resolve","fileURLToPath","resolve","readFile","dirname","join","resolve","resolve","execSync","dirname","fileURLToPath","existsSync"]}
|
package/dist/create.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/create.ts","../src/commands/create/create-command.ts","../src/errors.ts","../src/types.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 { runMain } from 'citty'\nimport { createCommand } from './commands/create/create-command'\n\nrunMain(createCommand)\n","import { execSync } from 'node:child_process'\nimport { existsSync, 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 and derives a\n * compatible version range for all @korajs packages.\n *\n * The CLI may be a patch ahead of other packages (e.g. CLI-only fixes),\n * so we use the major.minor range (^major.minor.0) which matches all\n * packages in the same release series.\n */\nfunction resolveKoraVersion(): string {\n\ttry {\n\t\tlet dir = dirname(fileURLToPath(import.meta.url))\n\t\tfor (let i = 0; i < 5; i++) {\n\t\t\tconst pkgPath = resolve(dir, 'package.json')\n\t\t\tif (existsSync(pkgPath)) {\n\t\t\t\tconst pkg = JSON.parse(readFileSync(pkgPath, 'utf-8')) as { name?: string; version: string }\n\t\t\t\tif (pkg.name === '@korajs/cli') {\n\t\t\t\t\tif (pkg.version === '0.0.0') return 'latest'\n\t\t\t\t\t// Use ^major.minor.0 so all packages in the series match\n\t\t\t\t\tconst parts = pkg.version.split('.')\n\t\t\t\t\treturn `^${parts[0]}.${parts[1]}.0`\n\t\t\t\t}\n\t\t\t}\n\t\t\tdir = dirname(dir)\n\t\t}\n\t\treturn 'latest'\n\t} catch {\n\t\treturn 'latest'\n\t}\n}\n","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","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 * On Windows, npm creates .cmd shims instead of extensionless files.\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 binDir = join(projectRoot, 'node_modules', '.bin')\n\t// On Windows, try .cmd first (npm/pnpm create .cmd shims)\n\tconst candidates =\n\t\tprocess.platform === 'win32'\n\t\t\t? [join(binDir, `${binaryName}.cmd`), join(binDir, binaryName)]\n\t\t\t: [join(binDir, binaryName)]\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// continue\n\t\t}\n\t}\n\treturn null\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 { existsSync } from 'node:fs'\nimport { 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 * After tsup bundling, import.meta.url points to dist/<file>.js (1 level from root).\n * In source, it's src/commands/create/template-engine.ts (3 levels from root).\n * We walk up from the current file to find the package root containing templates/.\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\tlet dir = dirname(fileURLToPath(import.meta.url))\n\tfor (let i = 0; i < 5; i++) {\n\t\tif (existsSync(resolve(dir, 'templates'))) {\n\t\t\treturn resolve(dir, 'templates', templateName)\n\t\t}\n\t\tdir = dirname(dir)\n\t}\n\t// Fallback: assume bundled (1 level from package root)\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":["import_citty","import_node_child_process","import_node_fs","import_node_path","import_node_url","resolve","import_promises","import_node_path","import_meta"]}
|
|
1
|
+
{"version":3,"sources":["../src/create.ts","../src/commands/create/create-command.ts","../src/errors.ts","../src/types.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 { runMain } from 'citty'\nimport { createCommand } from './commands/create/create-command'\n\nrunMain(createCommand)\n","import { execSync } from 'node:child_process'\nimport { existsSync, 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 and derives a\n * compatible version range for all @korajs packages.\n *\n * The CLI may be a patch ahead of other packages (e.g. CLI-only fixes),\n * so we use the major.minor range (^major.minor.0) which matches all\n * packages in the same release series.\n */\nfunction resolveKoraVersion(): string {\n\ttry {\n\t\tlet dir = dirname(fileURLToPath(import.meta.url))\n\t\tfor (let i = 0; i < 5; i++) {\n\t\t\tconst pkgPath = resolve(dir, 'package.json')\n\t\t\tif (existsSync(pkgPath)) {\n\t\t\t\tconst pkg = JSON.parse(readFileSync(pkgPath, 'utf-8')) as { name?: string; version: string }\n\t\t\t\tif (pkg.name === '@korajs/cli') {\n\t\t\t\t\tif (pkg.version === '0.0.0') return 'latest'\n\t\t\t\t\t// Use ^major.minor.0 so all packages in the series match\n\t\t\t\t\tconst parts = pkg.version.split('.')\n\t\t\t\t\treturn `^${parts[0]}.${parts[1]}.0`\n\t\t\t\t}\n\t\t\t}\n\t\t\tdir = dirname(dir)\n\t\t}\n\t\treturn 'latest'\n\t} catch {\n\t\treturn 'latest'\n\t}\n}\n","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","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 * On Windows, npm creates .cmd shims instead of extensionless files.\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 binDir = join(projectRoot, 'node_modules', '.bin')\n\t// On Windows, try .cmd first (npm/pnpm create .cmd shims)\n\tconst candidates =\n\t\tprocess.platform === 'win32'\n\t\t\t? [join(binDir, `${binaryName}.cmd`), join(binDir, binaryName)]\n\t\t\t: [join(binDir, binaryName)]\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// continue\n\t\t}\n\t}\n\treturn null\n}\n\n/**\n * Checks whether the `tsx` package is installed in the project's node_modules.\n * Used to determine if we can use `--import tsx` with process.execPath.\n */\nexport async function hasTsxInstalled(projectRoot: string): Promise<boolean> {\n\ttry {\n\t\tawait access(join(projectRoot, 'node_modules', 'tsx', 'package.json'))\n\t\treturn true\n\t} catch {\n\t\treturn false\n\t}\n}\n\n/**\n * Resolves the JS entry point for a package binary.\n * Reads the package.json bin field to find the actual JS file,\n * avoiding .cmd shims and shell:true on Windows.\n *\n * @returns Absolute path to the JS entry point, or null if not found\n */\nexport async function resolveProjectBinaryEntryPoint(\n\tprojectRoot: string,\n\tpackageName: string,\n\tbinaryName: string,\n): Promise<string | null> {\n\tconst pkgJsonPath = join(projectRoot, 'node_modules', packageName, 'package.json')\n\ttry {\n\t\tconst content = await readFile(pkgJsonPath, 'utf-8')\n\t\tconst pkg = JSON.parse(content) as { bin?: string | Record<string, string> }\n\t\tlet binPath: string | undefined\n\t\tif (typeof pkg.bin === 'string') {\n\t\t\tbinPath = pkg.bin\n\t\t} else if (typeof pkg.bin === 'object' && pkg.bin !== null) {\n\t\t\tbinPath = pkg.bin[binaryName]\n\t\t}\n\t\tif (!binPath) return null\n\t\tconst fullPath = join(projectRoot, 'node_modules', packageName, binPath)\n\t\tawait access(fullPath)\n\t\treturn fullPath\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 { existsSync } from 'node:fs'\nimport { 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 * After tsup bundling, import.meta.url points to dist/<file>.js (1 level from root).\n * In source, it's src/commands/create/template-engine.ts (3 levels from root).\n * We walk up from the current file to find the package root containing templates/.\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\tlet dir = dirname(fileURLToPath(import.meta.url))\n\tfor (let i = 0; i < 5; i++) {\n\t\tif (existsSync(resolve(dir, 'templates'))) {\n\t\t\treturn resolve(dir, 'templates', templateName)\n\t\t}\n\t\tdir = dirname(dir)\n\t}\n\t// Fallback: assume bundled (1 level from package root)\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":["import_citty","import_node_child_process","import_node_fs","import_node_path","import_node_url","resolve","import_promises","import_node_path","import_meta"]}
|
package/dist/create.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@korajs/cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.10",
|
|
4
4
|
"description": "Kora.js CLI tooling and project scaffolding",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
],
|
|
33
33
|
"dependencies": {
|
|
34
34
|
"citty": "^0.1.6",
|
|
35
|
-
"@korajs/core": "0.1.
|
|
35
|
+
"@korajs/core": "0.1.3"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
38
|
"typescript": "^5.7.3",
|
|
@@ -5,11 +5,16 @@ import { createRoot } from 'react-dom/client'
|
|
|
5
5
|
import schema from './schema'
|
|
6
6
|
import { App } from './App'
|
|
7
7
|
|
|
8
|
-
const app = createApp({
|
|
8
|
+
const app = createApp({
|
|
9
|
+
schema,
|
|
10
|
+
store: {
|
|
11
|
+
workerUrl: new URL('./kora-worker.ts', import.meta.url),
|
|
12
|
+
},
|
|
13
|
+
})
|
|
9
14
|
|
|
10
15
|
createRoot(document.getElementById('root')!).render(
|
|
11
16
|
<StrictMode>
|
|
12
|
-
<KoraProvider app={app}>
|
|
17
|
+
<KoraProvider app={app} fallback={<div>Loading...</div>}>
|
|
13
18
|
<App />
|
|
14
19
|
</KoraProvider>
|
|
15
20
|
</StrictMode>,
|
|
@@ -1,6 +1,39 @@
|
|
|
1
1
|
import react from '@vitejs/plugin-react'
|
|
2
|
+
import type { Plugin } from 'vite'
|
|
2
3
|
import { defineConfig } from 'vite'
|
|
3
4
|
|
|
5
|
+
/**
|
|
6
|
+
* Adds Cross-Origin-Opener-Policy and Cross-Origin-Embedder-Policy headers
|
|
7
|
+
* required for SharedArrayBuffer. Without these headers, OPFS SAH Pool VFS
|
|
8
|
+
* cannot be used and SQLite WASM falls back to in-memory storage (no persistence).
|
|
9
|
+
*/
|
|
10
|
+
function crossOriginIsolation(): Plugin {
|
|
11
|
+
return {
|
|
12
|
+
name: 'cross-origin-isolation',
|
|
13
|
+
configureServer(server) {
|
|
14
|
+
server.middlewares.use((_req, res, next) => {
|
|
15
|
+
res.setHeader('Cross-Origin-Opener-Policy', 'same-origin')
|
|
16
|
+
res.setHeader('Cross-Origin-Embedder-Policy', 'require-corp')
|
|
17
|
+
next()
|
|
18
|
+
})
|
|
19
|
+
},
|
|
20
|
+
configurePreviewServer(server) {
|
|
21
|
+
server.middlewares.use((_req, res, next) => {
|
|
22
|
+
res.setHeader('Cross-Origin-Opener-Policy', 'same-origin')
|
|
23
|
+
res.setHeader('Cross-Origin-Embedder-Policy', 'require-corp')
|
|
24
|
+
next()
|
|
25
|
+
})
|
|
26
|
+
},
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
4
30
|
export default defineConfig({
|
|
5
|
-
plugins: [react()],
|
|
31
|
+
plugins: [react(), crossOriginIsolation()],
|
|
32
|
+
optimizeDeps: {
|
|
33
|
+
exclude: ['@sqlite.org/sqlite-wasm', '@korajs/store'],
|
|
34
|
+
include: ['yjs'],
|
|
35
|
+
},
|
|
36
|
+
resolve: {
|
|
37
|
+
dedupe: ['yjs'],
|
|
38
|
+
},
|
|
6
39
|
})
|
|
@@ -10,6 +10,9 @@ const app = createApp({
|
|
|
10
10
|
sync: {
|
|
11
11
|
url: 'ws://localhost:3001',
|
|
12
12
|
},
|
|
13
|
+
store: {
|
|
14
|
+
workerUrl: new URL('./kora-worker.ts', import.meta.url),
|
|
15
|
+
},
|
|
13
16
|
})
|
|
14
17
|
|
|
15
18
|
// Connect to sync server once the app is ready
|
|
@@ -17,7 +20,7 @@ app.ready.then(() => app.sync?.connect())
|
|
|
17
20
|
|
|
18
21
|
createRoot(document.getElementById('root')!).render(
|
|
19
22
|
<StrictMode>
|
|
20
|
-
<KoraProvider app={app}>
|
|
23
|
+
<KoraProvider app={app} fallback={<div>Loading...</div>}>
|
|
21
24
|
<App />
|
|
22
25
|
</KoraProvider>
|
|
23
26
|
</StrictMode>,
|
|
@@ -1,6 +1,39 @@
|
|
|
1
1
|
import react from '@vitejs/plugin-react'
|
|
2
|
+
import type { Plugin } from 'vite'
|
|
2
3
|
import { defineConfig } from 'vite'
|
|
3
4
|
|
|
5
|
+
/**
|
|
6
|
+
* Adds Cross-Origin-Opener-Policy and Cross-Origin-Embedder-Policy headers
|
|
7
|
+
* required for SharedArrayBuffer. Without these headers, OPFS SAH Pool VFS
|
|
8
|
+
* cannot be used and SQLite WASM falls back to in-memory storage (no persistence).
|
|
9
|
+
*/
|
|
10
|
+
function crossOriginIsolation(): Plugin {
|
|
11
|
+
return {
|
|
12
|
+
name: 'cross-origin-isolation',
|
|
13
|
+
configureServer(server) {
|
|
14
|
+
server.middlewares.use((_req, res, next) => {
|
|
15
|
+
res.setHeader('Cross-Origin-Opener-Policy', 'same-origin')
|
|
16
|
+
res.setHeader('Cross-Origin-Embedder-Policy', 'require-corp')
|
|
17
|
+
next()
|
|
18
|
+
})
|
|
19
|
+
},
|
|
20
|
+
configurePreviewServer(server) {
|
|
21
|
+
server.middlewares.use((_req, res, next) => {
|
|
22
|
+
res.setHeader('Cross-Origin-Opener-Policy', 'same-origin')
|
|
23
|
+
res.setHeader('Cross-Origin-Embedder-Policy', 'require-corp')
|
|
24
|
+
next()
|
|
25
|
+
})
|
|
26
|
+
},
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
4
30
|
export default defineConfig({
|
|
5
|
-
plugins: [react()],
|
|
31
|
+
plugins: [react(), crossOriginIsolation()],
|
|
32
|
+
optimizeDeps: {
|
|
33
|
+
exclude: ['@sqlite.org/sqlite-wasm', '@korajs/store'],
|
|
34
|
+
include: ['yjs'],
|
|
35
|
+
},
|
|
36
|
+
resolve: {
|
|
37
|
+
dedupe: ['yjs'],
|
|
38
|
+
},
|
|
6
39
|
})
|
|
@@ -1 +0,0 @@
|
|
|
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 { existsSync, 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 and derives a\n * compatible version range for all @korajs packages.\n *\n * The CLI may be a patch ahead of other packages (e.g. CLI-only fixes),\n * so we use the major.minor range (^major.minor.0) which matches all\n * packages in the same release series.\n */\nfunction resolveKoraVersion(): string {\n\ttry {\n\t\tlet dir = dirname(fileURLToPath(import.meta.url))\n\t\tfor (let i = 0; i < 5; i++) {\n\t\t\tconst pkgPath = resolve(dir, 'package.json')\n\t\t\tif (existsSync(pkgPath)) {\n\t\t\t\tconst pkg = JSON.parse(readFileSync(pkgPath, 'utf-8')) as { name?: string; version: string }\n\t\t\t\tif (pkg.name === '@korajs/cli') {\n\t\t\t\t\tif (pkg.version === '0.0.0') return 'latest'\n\t\t\t\t\t// Use ^major.minor.0 so all packages in the series match\n\t\t\t\t\tconst parts = pkg.version.split('.')\n\t\t\t\t\treturn `^${parts[0]}.${parts[1]}.0`\n\t\t\t\t}\n\t\t\t}\n\t\t\tdir = dirname(dir)\n\t\t}\n\t\treturn 'latest'\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 * On Windows, npm creates .cmd shims instead of extensionless files.\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 binDir = join(projectRoot, 'node_modules', '.bin')\n\t// On Windows, try .cmd first (npm/pnpm create .cmd shims)\n\tconst candidates =\n\t\tprocess.platform === 'win32'\n\t\t\t? [join(binDir, `${binaryName}.cmd`), join(binDir, binaryName)]\n\t\t\t: [join(binDir, binaryName)]\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// continue\n\t\t}\n\t}\n\treturn null\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 { existsSync } from 'node:fs'\nimport { 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 * After tsup bundling, import.meta.url points to dist/<file>.js (1 level from root).\n * In source, it's src/commands/create/template-engine.ts (3 levels from root).\n * We walk up from the current file to find the package root containing templates/.\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\tlet dir = dirname(fileURLToPath(import.meta.url))\n\tfor (let i = 0; i < 5; i++) {\n\t\tif (existsSync(resolve(dir, 'templates'))) {\n\t\t\treturn resolve(dir, 'templates', templateName)\n\t\t}\n\t\tdir = dirname(dir)\n\t}\n\t// Fallback: assume bundled (1 level from package root)\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","existsSync","dirname","resolve","fileURLToPath","resolve","readFile","dirname","join","resolve","resolve","execSync","dirname","fileURLToPath","existsSync"]}
|