@vobs/cli 1.7.7 → 1.8.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 (81) hide show
  1. package/dist/banner.cjs +22 -3
  2. package/dist/banner.cjs.map +1 -1
  3. package/dist/banner.d.cts +6 -2
  4. package/dist/banner.d.ts +6 -2
  5. package/dist/banner.js +20 -3
  6. package/dist/banner.js.map +1 -1
  7. package/dist/cli.cjs +364 -24
  8. package/dist/cli.cjs.map +1 -1
  9. package/dist/cli.d.cts +10 -1
  10. package/dist/cli.d.ts +10 -1
  11. package/dist/cli.js +354 -15
  12. package/dist/cli.js.map +1 -1
  13. package/dist/commands/add.cjs +2 -2
  14. package/dist/commands/add.cjs.map +1 -1
  15. package/dist/commands/add.js +2 -2
  16. package/dist/commands/add.js.map +1 -1
  17. package/dist/commands/build.cjs +2 -2
  18. package/dist/commands/build.cjs.map +1 -1
  19. package/dist/commands/build.js +2 -2
  20. package/dist/commands/build.js.map +1 -1
  21. package/dist/commands/check.cjs +382 -0
  22. package/dist/commands/check.cjs.map +1 -0
  23. package/dist/commands/check.d.cts +34 -0
  24. package/dist/commands/check.d.ts +34 -0
  25. package/dist/commands/check.js +343 -0
  26. package/dist/commands/check.js.map +1 -0
  27. package/dist/commands/dev.cjs +2 -2
  28. package/dist/commands/dev.cjs.map +1 -1
  29. package/dist/commands/dev.js +2 -2
  30. package/dist/commands/dev.js.map +1 -1
  31. package/dist/commands/dsh.cjs +4 -4
  32. package/dist/commands/dsh.cjs.map +1 -1
  33. package/dist/commands/dsh.js +4 -4
  34. package/dist/commands/dsh.js.map +1 -1
  35. package/dist/commands/generate.cjs +2 -2
  36. package/dist/commands/generate.cjs.map +1 -1
  37. package/dist/commands/generate.js +2 -2
  38. package/dist/commands/generate.js.map +1 -1
  39. package/dist/commands/index.cjs +36 -19
  40. package/dist/commands/index.cjs.map +1 -1
  41. package/dist/commands/index.js +27 -10
  42. package/dist/commands/index.js.map +1 -1
  43. package/dist/commands/init.cjs +20 -3
  44. package/dist/commands/init.cjs.map +1 -1
  45. package/dist/commands/init.js +20 -3
  46. package/dist/commands/init.js.map +1 -1
  47. package/dist/index.cjs +402 -28
  48. package/dist/index.cjs.map +1 -1
  49. package/dist/index.js +391 -17
  50. package/dist/index.js.map +1 -1
  51. package/dist/repl.cjs +34 -17
  52. package/dist/repl.cjs.map +1 -1
  53. package/dist/repl.js +25 -8
  54. package/dist/repl.js.map +1 -1
  55. package/dist/start.cjs +402 -28
  56. package/dist/start.cjs.map +1 -1
  57. package/dist/start.d.cts +8 -1
  58. package/dist/start.d.ts +8 -1
  59. package/dist/start.js +391 -17
  60. package/dist/start.js.map +1 -1
  61. package/dist/utils/logger.cjs +2 -2
  62. package/dist/utils/logger.cjs.map +1 -1
  63. package/dist/utils/logger.js +2 -2
  64. package/dist/utils/logger.js.map +1 -1
  65. package/dist/version.cjs +47 -0
  66. package/dist/version.cjs.map +1 -0
  67. package/dist/version.d.cts +3 -0
  68. package/dist/version.d.ts +3 -0
  69. package/dist/version.js +23 -0
  70. package/dist/version.js.map +1 -0
  71. package/package.json +3 -2
  72. package/src/banner.ts +11 -5
  73. package/src/cli-process.test.ts +139 -0
  74. package/src/cli.ts +31 -3
  75. package/src/commands/check.test.ts +238 -0
  76. package/src/commands/check.ts +461 -0
  77. package/src/commands/dsh.ts +4 -2
  78. package/src/commands/init.ts +2 -1
  79. package/src/start.ts +66 -5
  80. package/src/utils/logger.ts +14 -2
  81. package/src/version.ts +27 -0
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/commands/init.ts","../../src/utils/logger.ts","../../src/utils/file.ts","../../src/utils/pm.ts"],"sourcesContent":["import { resolve } from 'node:path'\nimport { execSync } from 'node:child_process'\nimport * as p from '@clack/prompts'\nimport { logger } from '../utils/logger.js'\nimport { scaffoldProject } from '../utils/file.js'\nimport { installDependencies } from '../utils/pm.js'\nimport type { InitOptions } from '../types.js'\n\nexport async function initCommand(options: Partial<InitOptions>): Promise<void> {\n const projectName = options.name || (await p.text({\n message: 'Project name:',\n placeholder: 'my-vobs-app',\n validate: (v) => !v ? 'Project name is required' : undefined\n })) as string\n\n if (p.isCancel(projectName)) {\n p.cancel('Cancelled')\n return\n }\n\n const targetDir = options.targetDir || resolve(process.cwd(), projectName)\n\n const packageManager = options.packageManager || (await p.select({\n message: 'Package manager:',\n options: [\n { value: 'pnpm', label: 'pnpm' },\n { value: 'npm', label: 'npm' },\n { value: 'yarn', label: 'yarn' }\n ]\n })) as 'pnpm' | 'npm' | 'yarn'\n\n if (p.isCancel(packageManager)) {\n p.cancel('Cancelled')\n return\n }\n\n const useTypescript = options.typescript ?? (await p.confirm({\n message: 'Use TypeScript?',\n initialValue: true\n })) as boolean\n\n if (p.isCancel(useTypescript)) {\n p.cancel('Cancelled')\n return\n }\n\n const initGit = options.git ?? (await p.confirm({\n message: 'Initialize a git repository?',\n initialValue: true\n })) as boolean\n\n if (p.isCancel(initGit)) {\n p.cancel('Cancelled')\n return\n }\n\n logger.step(`Creating project ${projectName}...`)\n\n await scaffoldProject(targetDir, {\n name: projectName,\n packageName: projectName.toLowerCase().replace(/\\s+/g, '-'),\n typescript: useTypescript,\n vobsVersion: '1.0.0'\n })\n\n logger.success('Project scaffolded')\n\n logger.step('Installing dependencies...')\n installDependencies(targetDir, packageManager)\n logger.success('Dependencies installed')\n\n if (initGit) {\n logger.step('Initializing git repository...')\n try {\n execSync('git init', { cwd: targetDir, stdio: 'ignore' })\n logger.success('Git repository initialized')\n } catch {\n logger.warn('Git not found, skipping git init')\n }\n }\n\n logger.step('Done!')\n console.log(`\\n ${packageManager === 'pnpm' ? 'pnpm' : packageManager === 'yarn' ? 'yarn' : 'npm run'} dev\\n`)\n}","import pc from 'picocolors'\n\nfunction timestamp(): string {\n return pc.dim(`[${new Date().toLocaleTimeString()}]`)\n}\n\nexport function info(msg: string): void {\n console.log(`${timestamp()} ${pc.cyan('ℹ')} ${msg}`)\n}\n\nexport function success(msg: string): void {\n console.log(`${timestamp()} ${pc.green('✔')} ${msg}`)\n}\n\nexport function warn(msg: string): void {\n console.log(`${timestamp()} ${pc.yellow('⚠')} ${msg}`)\n}\n\nexport function error(msg: string): void {\n console.error(`${timestamp()} ${pc.red('✖')} ${msg}`)\n}\n\nexport function step(msg: string): void {\n console.log(`\\n ${pc.bold(pc.cyan(msg))}`)\n}\n\nexport const logger = { info, success, warn, error, step }","import { existsSync, mkdirSync, writeFileSync, readFileSync } from 'node:fs'\nimport { resolve, dirname } from 'node:path'\nimport { fileURLToPath } from 'node:url'\nimport Handlebars from 'handlebars'\n\nconst __dirname = dirname(fileURLToPath(import.meta.url))\nconst TEMPLATES_ROOT = resolve(__dirname, '..', 'templates')\n\n/** Get the absolute path to the templates directory */\nexport function getTemplatesDir(): string {\n return TEMPLATES_ROOT\n}\n\n/** Read a template file and compile it with Handlebars */\nexport function renderTemplate(\n templatePath: string,\n context: Record<string, unknown>\n): string {\n const source = readFileSync(templatePath, 'utf-8')\n const template = Handlebars.compile(source)\n return template(context)\n}\n\n/** Write a file, creating parent directories if needed */\nexport function writeFile(dir: string, filename: string, content: string): void {\n const filePath = resolve(dir, filename)\n mkdirSync(dirname(filePath), { recursive: true })\n writeFileSync(filePath, content, 'utf-8')\n}\n\n/** Ensure a directory exists */\nexport function ensureDir(dir: string): void {\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true })\n }\n}\n\n/** Check if a path exists */\nexport function pathExists(path: string): boolean {\n return existsSync(path)\n}\n\n/** Copy a template directory recursively, rendering each .hbs file */\nasync function copyDirRecursive(srcDir: string, destDir: string, context: Record<string, unknown>): Promise<void> {\n const { readdirSync } = await import('node:fs')\n const { join } = await import('node:path')\n\n const entries = readdirSync(srcDir, { withFileTypes: true })\n\n for (const entry of entries) {\n const srcPath = join(srcDir, entry.name)\n const isHbs = entry.name.endsWith('.hbs')\n const destName = isHbs ? entry.name.slice(0, -4) : entry.name\n const destPath = join(destDir, destName)\n\n if (entry.isDirectory()) {\n ensureDir(destPath)\n await copyDirRecursive(srcPath, destPath, context)\n } else if (isHbs) {\n const content = renderTemplate(srcPath, context)\n writeFile(destDir, destName, content)\n } else {\n const content = readFileSync(srcPath, 'utf-8')\n writeFile(destDir, destName, content)\n }\n }\n}\n\n/** Scaffold a project from a named template under `src/templates`. */\nexport async function scaffoldProject(\n targetDir: string,\n context: Record<string, unknown>,\n template = 'basic'\n): Promise<void> {\n const templateDir = resolve(TEMPLATES_ROOT, template)\n if (!existsSync(templateDir)) {\n throw new Error(`模板不存在: ${template}(可用模板见 ${TEMPLATES_ROOT})`)\n }\n ensureDir(targetDir)\n await copyDirRecursive(templateDir, targetDir, context)\n}","import { existsSync } from 'node:fs'\nimport { resolve } from 'node:path'\nimport { execSync } from 'node:child_process'\n\nexport type PackageManager = 'pnpm' | 'npm' | 'yarn'\n\n/** Detect the package manager to use */\nexport function detectPackageManager(targetDir?: string): PackageManager {\n // Check if a specific package manager is locked\n if (targetDir) {\n if (existsSync(resolve(targetDir, 'pnpm-lock.yaml'))) return 'pnpm'\n if (existsSync(resolve(targetDir, 'yarn.lock'))) return 'yarn'\n if (existsSync(resolve(targetDir, 'package-lock.json'))) return 'npm'\n }\n\n // Check for global preference\n const userAgent = process.env.npm_config_user_agent || ''\n if (userAgent.startsWith('pnpm')) return 'pnpm'\n if (userAgent.startsWith('yarn')) return 'yarn'\n if (userAgent.startsWith('npm')) return 'npm'\n\n // Default to pnpm\n return 'pnpm'\n}\n\n/** Install dependencies in a directory */\nexport function installDependencies(targetDir: string, pm: PackageManager): void {\n const cmd = pm === 'yarn' ? 'yarn' : `${pm} install`\n execSync(cmd, { cwd: targetDir, stdio: 'inherit' })\n}\n\n/** Add a package as a dependency */\nexport function addPackage(targetDir: string, pm: PackageManager, pkg: string, dev: boolean): void {\n const installFlag = dev ? '-D' : ''\n const cmd = pm === 'npm'\n ? `npm install ${installFlag} ${pkg}`\n : pm === 'yarn'\n ? `yarn add ${installFlag} ${pkg}`\n : `pnpm add ${installFlag} ${pkg}`\n\n execSync(cmd, { cwd: targetDir, stdio: 'inherit' })\n}"],"mappings":"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","names":["resolve","execSync","existsSync","resolve","resolve","execSync"]}
1
+ {"version":3,"sources":["../../src/commands/init.ts","../../src/utils/logger.ts","../../src/utils/file.ts","../../src/utils/pm.ts","../../src/version.ts"],"sourcesContent":["import { resolve } from 'node:path'\nimport { execSync } from 'node:child_process'\nimport * as p from '@clack/prompts'\nimport { logger } from '../utils/logger.js'\nimport { scaffoldProject } from '../utils/file.js'\nimport { installDependencies } from '../utils/pm.js'\nimport type { InitOptions } from '../types.js'\nimport { cliVersion } from '../version.js'\n\nexport async function initCommand(options: Partial<InitOptions>): Promise<void> {\n const projectName = options.name || (await p.text({\n message: 'Project name:',\n placeholder: 'my-vobs-app',\n validate: (v) => !v ? 'Project name is required' : undefined\n })) as string\n\n if (p.isCancel(projectName)) {\n p.cancel('Cancelled')\n return\n }\n\n const targetDir = options.targetDir || resolve(process.cwd(), projectName)\n\n const packageManager = options.packageManager || (await p.select({\n message: 'Package manager:',\n options: [\n { value: 'pnpm', label: 'pnpm' },\n { value: 'npm', label: 'npm' },\n { value: 'yarn', label: 'yarn' }\n ]\n })) as 'pnpm' | 'npm' | 'yarn'\n\n if (p.isCancel(packageManager)) {\n p.cancel('Cancelled')\n return\n }\n\n const useTypescript = options.typescript ?? (await p.confirm({\n message: 'Use TypeScript?',\n initialValue: true\n })) as boolean\n\n if (p.isCancel(useTypescript)) {\n p.cancel('Cancelled')\n return\n }\n\n const initGit = options.git ?? (await p.confirm({\n message: 'Initialize a git repository?',\n initialValue: true\n })) as boolean\n\n if (p.isCancel(initGit)) {\n p.cancel('Cancelled')\n return\n }\n\n logger.step(`Creating project ${projectName}...`)\n\n await scaffoldProject(targetDir, {\n name: projectName,\n packageName: projectName.toLowerCase().replace(/\\s+/g, '-'),\n typescript: useTypescript,\n vobsVersion: cliVersion()\n })\n\n logger.success('Project scaffolded')\n\n logger.step('Installing dependencies...')\n installDependencies(targetDir, packageManager)\n logger.success('Dependencies installed')\n\n if (initGit) {\n logger.step('Initializing git repository...')\n try {\n execSync('git init', { cwd: targetDir, stdio: 'ignore' })\n logger.success('Git repository initialized')\n } catch {\n logger.warn('Git not found, skipping git init')\n }\n }\n\n logger.step('Done!')\n console.log(`\\n ${packageManager === 'pnpm' ? 'pnpm' : packageManager === 'yarn' ? 'yarn' : 'npm run'} dev\\n`)\n}","import pc from 'picocolors'\n\n/*\n * 诊断/日志一律走 **stderr**,stdout 只留给**数据**。\n *\n * 为什么这不是洁癖:`vobs check --json` 的输出是给 AI 与开发台面板消费的机器接口,\n * 而 `--json --write` 时 `logger.info('报告写入 …')` 会先往 stdout 打一行带时间戳的\n * `[14:24:23] ℹ …`,于是 `JSON.parse(stdout)` 直接失败(实测\n * `.artifacts/probe-audit-cli-check.mjs`:同一脚本里 `--json` 可 parse、`--json --write` 不可)。\n * `warn` 同理。\n *\n * 注意:`error` 本来就走 stderr;`success`/`step` 保持 stdout(它们是给人看的**主输出**,\n * 不是旁路日志),`check` 的成功行也仍是 stdout。\n */\nfunction timestamp(): string {\n return pc.dim(`[${new Date().toLocaleTimeString()}]`)\n}\n\nexport function info(msg: string): void {\n console.error(`${timestamp()} ${pc.cyan('ℹ')} ${msg}`)\n}\n\nexport function success(msg: string): void {\n console.log(`${timestamp()} ${pc.green('✔')} ${msg}`)\n}\n\nexport function warn(msg: string): void {\n console.error(`${timestamp()} ${pc.yellow('⚠')} ${msg}`)\n}\n\nexport function error(msg: string): void {\n console.error(`${timestamp()} ${pc.red('✖')} ${msg}`)\n}\n\nexport function step(msg: string): void {\n console.log(`\\n ${pc.bold(pc.cyan(msg))}`)\n}\n\nexport const logger = { info, success, warn, error, step }","import { existsSync, mkdirSync, writeFileSync, readFileSync } from 'node:fs'\nimport { resolve, dirname } from 'node:path'\nimport { fileURLToPath } from 'node:url'\nimport Handlebars from 'handlebars'\n\nconst __dirname = dirname(fileURLToPath(import.meta.url))\nconst TEMPLATES_ROOT = resolve(__dirname, '..', 'templates')\n\n/** Get the absolute path to the templates directory */\nexport function getTemplatesDir(): string {\n return TEMPLATES_ROOT\n}\n\n/** Read a template file and compile it with Handlebars */\nexport function renderTemplate(\n templatePath: string,\n context: Record<string, unknown>\n): string {\n const source = readFileSync(templatePath, 'utf-8')\n const template = Handlebars.compile(source)\n return template(context)\n}\n\n/** Write a file, creating parent directories if needed */\nexport function writeFile(dir: string, filename: string, content: string): void {\n const filePath = resolve(dir, filename)\n mkdirSync(dirname(filePath), { recursive: true })\n writeFileSync(filePath, content, 'utf-8')\n}\n\n/** Ensure a directory exists */\nexport function ensureDir(dir: string): void {\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true })\n }\n}\n\n/** Check if a path exists */\nexport function pathExists(path: string): boolean {\n return existsSync(path)\n}\n\n/** Copy a template directory recursively, rendering each .hbs file */\nasync function copyDirRecursive(srcDir: string, destDir: string, context: Record<string, unknown>): Promise<void> {\n const { readdirSync } = await import('node:fs')\n const { join } = await import('node:path')\n\n const entries = readdirSync(srcDir, { withFileTypes: true })\n\n for (const entry of entries) {\n const srcPath = join(srcDir, entry.name)\n const isHbs = entry.name.endsWith('.hbs')\n const destName = isHbs ? entry.name.slice(0, -4) : entry.name\n const destPath = join(destDir, destName)\n\n if (entry.isDirectory()) {\n ensureDir(destPath)\n await copyDirRecursive(srcPath, destPath, context)\n } else if (isHbs) {\n const content = renderTemplate(srcPath, context)\n writeFile(destDir, destName, content)\n } else {\n const content = readFileSync(srcPath, 'utf-8')\n writeFile(destDir, destName, content)\n }\n }\n}\n\n/** Scaffold a project from a named template under `src/templates`. */\nexport async function scaffoldProject(\n targetDir: string,\n context: Record<string, unknown>,\n template = 'basic'\n): Promise<void> {\n const templateDir = resolve(TEMPLATES_ROOT, template)\n if (!existsSync(templateDir)) {\n throw new Error(`模板不存在: ${template}(可用模板见 ${TEMPLATES_ROOT})`)\n }\n ensureDir(targetDir)\n await copyDirRecursive(templateDir, targetDir, context)\n}","import { existsSync } from 'node:fs'\nimport { resolve } from 'node:path'\nimport { execSync } from 'node:child_process'\n\nexport type PackageManager = 'pnpm' | 'npm' | 'yarn'\n\n/** Detect the package manager to use */\nexport function detectPackageManager(targetDir?: string): PackageManager {\n // Check if a specific package manager is locked\n if (targetDir) {\n if (existsSync(resolve(targetDir, 'pnpm-lock.yaml'))) return 'pnpm'\n if (existsSync(resolve(targetDir, 'yarn.lock'))) return 'yarn'\n if (existsSync(resolve(targetDir, 'package-lock.json'))) return 'npm'\n }\n\n // Check for global preference\n const userAgent = process.env.npm_config_user_agent || ''\n if (userAgent.startsWith('pnpm')) return 'pnpm'\n if (userAgent.startsWith('yarn')) return 'yarn'\n if (userAgent.startsWith('npm')) return 'npm'\n\n // Default to pnpm\n return 'pnpm'\n}\n\n/** Install dependencies in a directory */\nexport function installDependencies(targetDir: string, pm: PackageManager): void {\n const cmd = pm === 'yarn' ? 'yarn' : `${pm} install`\n execSync(cmd, { cwd: targetDir, stdio: 'inherit' })\n}\n\n/** Add a package as a dependency */\nexport function addPackage(targetDir: string, pm: PackageManager, pkg: string, dev: boolean): void {\n const installFlag = dev ? '-D' : ''\n const cmd = pm === 'npm'\n ? `npm install ${installFlag} ${pkg}`\n : pm === 'yarn'\n ? `yarn add ${installFlag} ${pkg}`\n : `pnpm add ${installFlag} ${pkg}`\n\n execSync(cmd, { cwd: targetDir, stdio: 'inherit' })\n}","import { readFileSync } from 'node:fs'\n\n/**\n * CLI 版本号的**单一来源**:直接读本包的 `package.json`。\n *\n * 此前版本号在三处各自硬编码,且都与 `package.json` 不符:\n * - `banner.ts` 的 `𝗩𝗢𝗕𝗦 𝗖𝗟𝗜 𝘃𝟭.𝟬` 常量 —— 这就是 `vobs --version` 的**全部**输出;\n * - `cli.ts` 的 `cli.version('1.0.0')` —— `--help` 头部显示 `vobs/1.0.0`;\n * - `init.ts` 的 `vobsVersion: '1.0.0'` —— 写进脚手架产物。\n * 同一个仓库的 `commands/dsh.ts` 早就有正确的\"读 package.json\"实现,所以这不是能力问题,\n * 而是没人从**外部**看过 CLI 的版本输出(三处不一致也正因如此从未被发现)。\n */\nlet cached: string | undefined\n\nexport function cliVersion(): string {\n if (cached !== undefined) return cached\n try {\n const manifest = JSON.parse(\n readFileSync(new URL('../package.json', import.meta.url), 'utf8')\n ) as { version?: string }\n cached = manifest.version ?? '0.0.0'\n } catch {\n // 读不到 package.json(被打包成单文件等)时不要谎报一个像真版本号的常量\n cached = '0.0.0'\n }\n return cached\n}\n"],"mappings":"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","names":["resolve","execSync","existsSync","resolve","readFileSync","readFileSync","resolve","execSync"]}