project-runner 0.1.0 → 0.1.2
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/index.js +648 -0
- package/package.json +8 -8
- package/src/analyzer/dependencies.ts +0 -118
- package/src/analyzer/index.ts +0 -56
- package/src/analyzer/package-manager.ts +0 -119
- package/src/analyzer/scripts.ts +0 -98
- package/src/cli/info.ts +0 -87
- package/src/cli/run.ts +0 -117
- package/src/cli/script.ts +0 -59
- package/src/index.ts +0 -141
- package/src/runner/executor.ts +0 -93
- package/src/utils/log.ts +0 -86
|
@@ -1,118 +0,0 @@
|
|
|
1
|
-
import { stat } from 'fs/promises'
|
|
2
|
-
import { join } from 'path'
|
|
3
|
-
|
|
4
|
-
export interface DependencyStatus {
|
|
5
|
-
hasNodeModules: boolean
|
|
6
|
-
needsInstall: boolean
|
|
7
|
-
reason?: string
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
// Lockfile 列表
|
|
11
|
-
const LOCKFILES = [
|
|
12
|
-
'bun.lockb',
|
|
13
|
-
'bun.lock',
|
|
14
|
-
'pnpm-lock.yaml',
|
|
15
|
-
'yarn.lock',
|
|
16
|
-
'package-lock.json',
|
|
17
|
-
]
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* 检测项目的依赖状态
|
|
21
|
-
* 判断是否需要执行 install
|
|
22
|
-
*/
|
|
23
|
-
export async function checkDependencyStatus(projectDir: string): Promise<DependencyStatus> {
|
|
24
|
-
const nodeModulesPath = join(projectDir, 'node_modules')
|
|
25
|
-
|
|
26
|
-
// 1. 检查 node_modules 是否存在
|
|
27
|
-
const nodeModulesExists = await directoryExists(nodeModulesPath)
|
|
28
|
-
if (!nodeModulesExists) {
|
|
29
|
-
return {
|
|
30
|
-
hasNodeModules: false,
|
|
31
|
-
needsInstall: true,
|
|
32
|
-
reason: 'node_modules 不存在',
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
// 2. 检查 lockfile 是否比 node_modules 更新
|
|
37
|
-
const lockfilePath = await findLockfile(projectDir)
|
|
38
|
-
if (lockfilePath) {
|
|
39
|
-
const lockfileMtime = await getModifiedTime(lockfilePath)
|
|
40
|
-
const nodeModulesMtime = await getModifiedTime(nodeModulesPath)
|
|
41
|
-
|
|
42
|
-
if (lockfileMtime && nodeModulesMtime && lockfileMtime > nodeModulesMtime) {
|
|
43
|
-
return {
|
|
44
|
-
hasNodeModules: true,
|
|
45
|
-
needsInstall: true,
|
|
46
|
-
reason: 'lockfile 已更新',
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
// 3. 检查 package.json 是否比 node_modules 更新
|
|
52
|
-
const packageJsonPath = join(projectDir, 'package.json')
|
|
53
|
-
const packageJsonMtime = await getModifiedTime(packageJsonPath)
|
|
54
|
-
const nodeModulesMtime = await getModifiedTime(nodeModulesPath)
|
|
55
|
-
|
|
56
|
-
if (packageJsonMtime && nodeModulesMtime && packageJsonMtime > nodeModulesMtime) {
|
|
57
|
-
return {
|
|
58
|
-
hasNodeModules: true,
|
|
59
|
-
needsInstall: true,
|
|
60
|
-
reason: 'package.json 已更新',
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
// 依赖状态正常
|
|
65
|
-
return {
|
|
66
|
-
hasNodeModules: true,
|
|
67
|
-
needsInstall: false,
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
* 查找项目中的 lockfile
|
|
73
|
-
*/
|
|
74
|
-
async function findLockfile(projectDir: string): Promise<string | null> {
|
|
75
|
-
for (const lockfile of LOCKFILES) {
|
|
76
|
-
const lockfilePath = join(projectDir, lockfile)
|
|
77
|
-
if (await fileExists(lockfilePath)) {
|
|
78
|
-
return lockfilePath
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
return null
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
/**
|
|
85
|
-
* 获取文件/目录的修改时间
|
|
86
|
-
*/
|
|
87
|
-
async function getModifiedTime(path: string): Promise<number | null> {
|
|
88
|
-
try {
|
|
89
|
-
const stats = await stat(path)
|
|
90
|
-
return stats.mtimeMs
|
|
91
|
-
} catch {
|
|
92
|
-
return null
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* 检查目录是否存在
|
|
98
|
-
*/
|
|
99
|
-
async function directoryExists(path: string): Promise<boolean> {
|
|
100
|
-
try {
|
|
101
|
-
const stats = await stat(path)
|
|
102
|
-
return stats.isDirectory()
|
|
103
|
-
} catch {
|
|
104
|
-
return false
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
/**
|
|
109
|
-
* 检查文件是否存在
|
|
110
|
-
*/
|
|
111
|
-
async function fileExists(path: string): Promise<boolean> {
|
|
112
|
-
try {
|
|
113
|
-
const stats = await stat(path)
|
|
114
|
-
return stats.isFile()
|
|
115
|
-
} catch {
|
|
116
|
-
return false
|
|
117
|
-
}
|
|
118
|
-
}
|
package/src/analyzer/index.ts
DELETED
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
import { detectPackageManager, type PackageManagerInfo } from './package-manager'
|
|
2
|
-
import { analyzeScripts, type ScriptsInfo } from './scripts'
|
|
3
|
-
import { checkDependencyStatus, type DependencyStatus } from './dependencies'
|
|
4
|
-
|
|
5
|
-
export interface ProjectInfo {
|
|
6
|
-
type: 'nodejs' | 'python' | 'unknown'
|
|
7
|
-
packageManager: PackageManagerInfo
|
|
8
|
-
scripts: ScriptsInfo | null
|
|
9
|
-
dependencies: DependencyStatus
|
|
10
|
-
name?: string
|
|
11
|
-
version?: string
|
|
12
|
-
description?: string
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* 分析项目,获取完整的项目信息
|
|
17
|
-
*/
|
|
18
|
-
export async function analyzeProject(projectDir: string): Promise<ProjectInfo> {
|
|
19
|
-
// 检测项目类型(目前仅支持 Node.js)
|
|
20
|
-
const packageJsonPath = `${projectDir}/package.json`
|
|
21
|
-
const hasPackageJson = await Bun.file(packageJsonPath).exists()
|
|
22
|
-
|
|
23
|
-
if (!hasPackageJson) {
|
|
24
|
-
return {
|
|
25
|
-
type: 'unknown',
|
|
26
|
-
packageManager: { name: 'npm', source: 'default' },
|
|
27
|
-
scripts: null,
|
|
28
|
-
dependencies: { hasNodeModules: false, needsInstall: false },
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
// 读取 package.json 基本信息
|
|
33
|
-
const packageJson = await Bun.file(packageJsonPath).json().catch(() => ({}))
|
|
34
|
-
|
|
35
|
-
// 并行执行检测
|
|
36
|
-
const [packageManager, scripts, dependencies] = await Promise.all([
|
|
37
|
-
detectPackageManager(projectDir),
|
|
38
|
-
analyzeScripts(projectDir),
|
|
39
|
-
checkDependencyStatus(projectDir),
|
|
40
|
-
])
|
|
41
|
-
|
|
42
|
-
return {
|
|
43
|
-
type: 'nodejs',
|
|
44
|
-
packageManager,
|
|
45
|
-
scripts,
|
|
46
|
-
dependencies,
|
|
47
|
-
name: packageJson.name,
|
|
48
|
-
version: packageJson.version,
|
|
49
|
-
description: packageJson.description,
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
// 导出子模块
|
|
54
|
-
export { detectPackageManager, type PackageManagerInfo } from './package-manager'
|
|
55
|
-
export { analyzeScripts, type ScriptsInfo, getAvailableScripts, hasScript } from './scripts'
|
|
56
|
-
export { checkDependencyStatus, type DependencyStatus } from './dependencies'
|
|
@@ -1,119 +0,0 @@
|
|
|
1
|
-
// 包管理器类型
|
|
2
|
-
export type PackageManager = 'npm' | 'yarn' | 'pnpm' | 'bun'
|
|
3
|
-
|
|
4
|
-
// 包管理器检测来源
|
|
5
|
-
export type DetectionSource = 'packageManager' | 'volta' | 'lockfile' | 'default'
|
|
6
|
-
|
|
7
|
-
export interface PackageManagerInfo {
|
|
8
|
-
name: PackageManager
|
|
9
|
-
version?: string
|
|
10
|
-
source: DetectionSource
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
// Lockfile 检测映射
|
|
14
|
-
const LOCKFILE_MAP: Record<string, PackageManager> = {
|
|
15
|
-
'bun.lockb': 'bun',
|
|
16
|
-
'bun.lock': 'bun',
|
|
17
|
-
'pnpm-lock.yaml': 'pnpm',
|
|
18
|
-
'yarn.lock': 'yarn',
|
|
19
|
-
'package-lock.json': 'npm',
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* 检测项目使用的包管理器
|
|
24
|
-
* 优先级: packageManager 字段 > volta 字段 > lockfile
|
|
25
|
-
*/
|
|
26
|
-
export async function detectPackageManager(projectDir: string): Promise<PackageManagerInfo> {
|
|
27
|
-
const packageJsonPath = `${projectDir}/package.json`
|
|
28
|
-
|
|
29
|
-
// 读取 package.json
|
|
30
|
-
const packageJson = await readPackageJson(packageJsonPath)
|
|
31
|
-
if (!packageJson) {
|
|
32
|
-
return { name: 'npm', source: 'default' }
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
// 1. 检查 packageManager 字段 (corepack)
|
|
36
|
-
if (packageJson.packageManager) {
|
|
37
|
-
const match = packageJson.packageManager.match(/^(npm|yarn|pnpm|bun)@(.+)$/)
|
|
38
|
-
if (match) {
|
|
39
|
-
return {
|
|
40
|
-
name: match[1] as PackageManager,
|
|
41
|
-
version: match[2],
|
|
42
|
-
source: 'packageManager',
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
// 2. 检查 volta 字段
|
|
48
|
-
if (packageJson.volta) {
|
|
49
|
-
for (const pm of ['pnpm', 'yarn', 'npm'] as PackageManager[]) {
|
|
50
|
-
if (packageJson.volta[pm]) {
|
|
51
|
-
return {
|
|
52
|
-
name: pm,
|
|
53
|
-
version: packageJson.volta[pm],
|
|
54
|
-
source: 'volta',
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
// 3. 检测 lockfile
|
|
61
|
-
for (const [lockfile, pm] of Object.entries(LOCKFILE_MAP)) {
|
|
62
|
-
const exists = await Bun.file(`${projectDir}/${lockfile}`).exists()
|
|
63
|
-
if (exists) {
|
|
64
|
-
return { name: pm, source: 'lockfile' }
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
// 默认使用 npm
|
|
69
|
-
return { name: 'npm', source: 'default' }
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
/**
|
|
73
|
-
* 获取包管理器的运行命令
|
|
74
|
-
*/
|
|
75
|
-
export function getRunCommand(pm: PackageManager, script: string): string[] {
|
|
76
|
-
switch (pm) {
|
|
77
|
-
case 'bun':
|
|
78
|
-
return ['bun', 'run', script]
|
|
79
|
-
case 'pnpm':
|
|
80
|
-
return ['pnpm', script]
|
|
81
|
-
case 'yarn':
|
|
82
|
-
return ['yarn', script]
|
|
83
|
-
case 'npm':
|
|
84
|
-
default:
|
|
85
|
-
return ['npm', 'run', script]
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* 获取包管理器的安装命令
|
|
91
|
-
*/
|
|
92
|
-
export function getInstallCommand(pm: PackageManager): string[] {
|
|
93
|
-
switch (pm) {
|
|
94
|
-
case 'bun':
|
|
95
|
-
return ['bun', 'install']
|
|
96
|
-
case 'pnpm':
|
|
97
|
-
return ['pnpm', 'install']
|
|
98
|
-
case 'yarn':
|
|
99
|
-
return ['yarn', 'install']
|
|
100
|
-
case 'npm':
|
|
101
|
-
default:
|
|
102
|
-
return ['npm', 'install']
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
/**
|
|
107
|
-
* 读取 package.json
|
|
108
|
-
*/
|
|
109
|
-
async function readPackageJson(path: string): Promise<any | null> {
|
|
110
|
-
try {
|
|
111
|
-
const file = Bun.file(path)
|
|
112
|
-
if (await file.exists()) {
|
|
113
|
-
return await file.json()
|
|
114
|
-
}
|
|
115
|
-
} catch {
|
|
116
|
-
// 忽略错误
|
|
117
|
-
}
|
|
118
|
-
return null
|
|
119
|
-
}
|
package/src/analyzer/scripts.ts
DELETED
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
export interface ScriptsInfo {
|
|
2
|
-
scripts: Record<string, string>
|
|
3
|
-
// 识别出的主要命令(dev, test, build, start)
|
|
4
|
-
detected: {
|
|
5
|
-
dev?: string // 开发启动脚本名
|
|
6
|
-
test?: string // 测试脚本名
|
|
7
|
-
build?: string // 构建脚本名
|
|
8
|
-
start?: string // 生产启动脚本名
|
|
9
|
-
}
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
// 开发命令的常见名称(按优先级排序)
|
|
13
|
-
const DEV_PATTERNS = ['dev', 'serve', 'start:dev', 'develop', 'watch']
|
|
14
|
-
|
|
15
|
-
// 测试命令的常见名称
|
|
16
|
-
const TEST_PATTERNS = ['test', 'test:unit', 'test:all', 'spec']
|
|
17
|
-
|
|
18
|
-
// 构建命令的常见名称
|
|
19
|
-
const BUILD_PATTERNS = ['build', 'compile', 'bundle', 'dist']
|
|
20
|
-
|
|
21
|
-
// 生产启动命令的常见名称
|
|
22
|
-
const START_PATTERNS = ['start', 'serve', 'preview', 'production']
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* 读取并分析 package.json 的 scripts 字段
|
|
26
|
-
*/
|
|
27
|
-
export async function analyzeScripts(projectDir: string): Promise<ScriptsInfo | null> {
|
|
28
|
-
const packageJsonPath = `${projectDir}/package.json`
|
|
29
|
-
|
|
30
|
-
try {
|
|
31
|
-
const file = Bun.file(packageJsonPath)
|
|
32
|
-
if (!await file.exists()) {
|
|
33
|
-
return null
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
const packageJson = await file.json()
|
|
37
|
-
const scripts = packageJson.scripts || {}
|
|
38
|
-
|
|
39
|
-
// 智能识别主要命令
|
|
40
|
-
const detected = {
|
|
41
|
-
dev: findMatchingScript(scripts, DEV_PATTERNS),
|
|
42
|
-
test: findMatchingScript(scripts, TEST_PATTERNS),
|
|
43
|
-
build: findMatchingScript(scripts, BUILD_PATTERNS),
|
|
44
|
-
start: findMatchingScript(scripts, START_PATTERNS),
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
return { scripts, detected }
|
|
48
|
-
} catch {
|
|
49
|
-
return null
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* 从 scripts 中找到匹配的脚本名
|
|
55
|
-
* @param scripts package.json 的 scripts 对象
|
|
56
|
-
* @param patterns 要匹配的模式列表(按优先级排序)
|
|
57
|
-
*/
|
|
58
|
-
function findMatchingScript(scripts: Record<string, string>, patterns: string[]): string | undefined {
|
|
59
|
-
const scriptNames = Object.keys(scripts)
|
|
60
|
-
|
|
61
|
-
// 按优先级检查每个模式
|
|
62
|
-
for (const pattern of patterns) {
|
|
63
|
-
// 精确匹配
|
|
64
|
-
if (scripts[pattern]) {
|
|
65
|
-
return pattern
|
|
66
|
-
}
|
|
67
|
-
// 模糊匹配(包含模式的脚本名)
|
|
68
|
-
const fuzzyMatch = scriptNames.find(name =>
|
|
69
|
-
name.toLowerCase().includes(pattern.toLowerCase())
|
|
70
|
-
)
|
|
71
|
-
if (fuzzyMatch) {
|
|
72
|
-
return fuzzyMatch
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
return undefined
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
/**
|
|
80
|
-
* 获取所有可用的脚本列表
|
|
81
|
-
*/
|
|
82
|
-
export function getAvailableScripts(scriptsInfo: ScriptsInfo): string[] {
|
|
83
|
-
return Object.keys(scriptsInfo.scripts)
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* 检查脚本是否存在
|
|
88
|
-
*/
|
|
89
|
-
export function hasScript(scriptsInfo: ScriptsInfo, scriptName: string): boolean {
|
|
90
|
-
return scriptName in scriptsInfo.scripts
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
/**
|
|
94
|
-
* 获取脚本的实际命令内容
|
|
95
|
-
*/
|
|
96
|
-
export function getScriptCommand(scriptsInfo: ScriptsInfo, scriptName: string): string | undefined {
|
|
97
|
-
return scriptsInfo.scripts[scriptName]
|
|
98
|
-
}
|
package/src/cli/info.ts
DELETED
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
import { analyzeProject } from '../analyzer'
|
|
2
|
-
import { colors } from '../utils/log'
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* pr info 命令
|
|
6
|
-
* 显示项目分析结果
|
|
7
|
-
*/
|
|
8
|
-
export async function infoCommand(projectDir: string) {
|
|
9
|
-
const project = await analyzeProject(projectDir)
|
|
10
|
-
|
|
11
|
-
if (project.type === 'unknown') {
|
|
12
|
-
console.log(`${colors.red}✗${colors.reset} 未检测到项目类型`)
|
|
13
|
-
console.log(' 请确保当前目录包含 package.json 或其他项目配置文件')
|
|
14
|
-
return
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
// 项目基本信息
|
|
18
|
-
console.log()
|
|
19
|
-
console.log(`${colors.cyan}${colors.bold}pr - 项目分析结果${colors.reset}`)
|
|
20
|
-
console.log('─'.repeat(40))
|
|
21
|
-
|
|
22
|
-
if (project.name) {
|
|
23
|
-
console.log(`${colors.bold}项目名称:${colors.reset} ${project.name}`)
|
|
24
|
-
}
|
|
25
|
-
if (project.version) {
|
|
26
|
-
console.log(`${colors.bold}版本:${colors.reset} ${project.version}`)
|
|
27
|
-
}
|
|
28
|
-
if (project.description) {
|
|
29
|
-
console.log(`${colors.bold}描述:${colors.reset} ${project.description}`)
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
console.log(`${colors.bold}项目类型:${colors.reset} ${project.type}`)
|
|
33
|
-
|
|
34
|
-
// 包管理器信息
|
|
35
|
-
const pm = project.packageManager
|
|
36
|
-
let pmInfo = pm.name
|
|
37
|
-
if (pm.version) {
|
|
38
|
-
pmInfo += `@${pm.version}`
|
|
39
|
-
}
|
|
40
|
-
pmInfo += ` ${colors.dim}(${pm.source})${colors.reset}`
|
|
41
|
-
console.log(`${colors.bold}包管理器:${colors.reset} ${pmInfo}`)
|
|
42
|
-
|
|
43
|
-
// 依赖状态
|
|
44
|
-
const deps = project.dependencies
|
|
45
|
-
const depsStatus = deps.needsInstall
|
|
46
|
-
? `${colors.yellow}需要安装${colors.reset} (${deps.reason})`
|
|
47
|
-
: `${colors.green}已就绪${colors.reset}`
|
|
48
|
-
console.log(`${colors.bold}依赖状态:${colors.reset} ${depsStatus}`)
|
|
49
|
-
|
|
50
|
-
console.log()
|
|
51
|
-
|
|
52
|
-
// Scripts 信息
|
|
53
|
-
if (project.scripts) {
|
|
54
|
-
const { scripts, detected } = project.scripts
|
|
55
|
-
|
|
56
|
-
// 显示识别的主要命令
|
|
57
|
-
console.log(`${colors.bold}识别的命令:${colors.reset}`)
|
|
58
|
-
if (detected.dev) {
|
|
59
|
-
console.log(` ${colors.green}pr run${colors.reset} → ${pm.name} ${detected.dev}`)
|
|
60
|
-
}
|
|
61
|
-
if (detected.test) {
|
|
62
|
-
console.log(` ${colors.green}pr test${colors.reset} → ${pm.name} ${detected.test}`)
|
|
63
|
-
}
|
|
64
|
-
if (detected.build) {
|
|
65
|
-
console.log(` ${colors.green}pr build${colors.reset} → ${pm.name} ${detected.build}`)
|
|
66
|
-
}
|
|
67
|
-
if (detected.start) {
|
|
68
|
-
console.log(` ${colors.green}pr start${colors.reset} → ${pm.name} ${detected.start}`)
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
console.log()
|
|
72
|
-
|
|
73
|
-
// 显示所有可用脚本
|
|
74
|
-
const allScripts = Object.keys(scripts)
|
|
75
|
-
if (allScripts.length > 0) {
|
|
76
|
-
console.log(`${colors.bold}所有脚本:${colors.reset}`)
|
|
77
|
-
for (const name of allScripts) {
|
|
78
|
-
const cmd = scripts[name]
|
|
79
|
-
// 截断过长的命令
|
|
80
|
-
const displayCmd = cmd.length > 40 ? cmd.slice(0, 40) + '...' : cmd
|
|
81
|
-
console.log(` ${colors.cyan}${name}${colors.reset} ${colors.dim}→ ${displayCmd}${colors.reset}`)
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
console.log()
|
|
87
|
-
}
|
package/src/cli/run.ts
DELETED
|
@@ -1,117 +0,0 @@
|
|
|
1
|
-
import { analyzeProject, type ProjectInfo } from '../analyzer'
|
|
2
|
-
import { getRunCommand, getInstallCommand } from '../analyzer/package-manager'
|
|
3
|
-
import { execute } from '../runner/executor'
|
|
4
|
-
import { log, error, warn, success, info, newline } from '../utils/log'
|
|
5
|
-
|
|
6
|
-
type ScriptType = 'dev' | 'test' | 'build' | 'start'
|
|
7
|
-
|
|
8
|
-
interface RunOptions {
|
|
9
|
-
noInstall?: boolean
|
|
10
|
-
scriptType?: ScriptType
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* qy run 命令
|
|
15
|
-
* 完整流程:检测 → install → 启动
|
|
16
|
-
*/
|
|
17
|
-
export async function runCommand(projectDir: string, options: RunOptions = {}) {
|
|
18
|
-
const { noInstall = false, scriptType = 'dev' } = options
|
|
19
|
-
|
|
20
|
-
// 1. 分析项目
|
|
21
|
-
log('正在分析项目...')
|
|
22
|
-
const project = await analyzeProject(projectDir)
|
|
23
|
-
|
|
24
|
-
if (project.type === 'unknown') {
|
|
25
|
-
error('未检测到项目类型。请确保当前目录包含 package.json')
|
|
26
|
-
process.exit(1)
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
// 2. 输出检测结果
|
|
30
|
-
log(`项目类型: ${project.type}`)
|
|
31
|
-
log(`包管理器: ${project.packageManager.name} (from ${project.packageManager.source})`)
|
|
32
|
-
|
|
33
|
-
if (!project.scripts) {
|
|
34
|
-
error('无法读取 package.json 的 scripts')
|
|
35
|
-
process.exit(1)
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
// 3. 确定要执行的脚本
|
|
39
|
-
const scriptName = findScript(project, scriptType)
|
|
40
|
-
|
|
41
|
-
if (!scriptName) {
|
|
42
|
-
error(`未找到 ${scriptType} 相关的脚本`)
|
|
43
|
-
showAvailableScripts(project)
|
|
44
|
-
process.exit(1)
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
log(`将执行脚本: ${scriptName}`)
|
|
48
|
-
|
|
49
|
-
// 4. 检查依赖状态,决定是否需要 install
|
|
50
|
-
if (!noInstall && project.dependencies.needsInstall) {
|
|
51
|
-
log(`依赖状态: ${project.dependencies.reason || '需要安装'}`)
|
|
52
|
-
newline()
|
|
53
|
-
|
|
54
|
-
// 执行 install
|
|
55
|
-
const installCmd = getInstallCommand(project.packageManager.name)
|
|
56
|
-
const installExitCode = await execute(installCmd, { cwd: projectDir })
|
|
57
|
-
|
|
58
|
-
if (installExitCode !== 0) {
|
|
59
|
-
error('依赖安装失败')
|
|
60
|
-
process.exit(installExitCode)
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
success('依赖安装完成')
|
|
64
|
-
newline()
|
|
65
|
-
} else if (!noInstall) {
|
|
66
|
-
log('依赖状态: 已是最新')
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
// 5. 执行脚本
|
|
70
|
-
const runCmd = getRunCommand(project.packageManager.name, scriptName)
|
|
71
|
-
const exitCode = await execute(runCmd, { cwd: projectDir })
|
|
72
|
-
|
|
73
|
-
if (exitCode !== 0) {
|
|
74
|
-
process.exit(exitCode)
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
/**
|
|
79
|
-
* 根据脚本类型找到对应的脚本名
|
|
80
|
-
*/
|
|
81
|
-
function findScript(project: ProjectInfo, scriptType: ScriptType): string | undefined {
|
|
82
|
-
const scripts = project.scripts
|
|
83
|
-
if (!scripts) return undefined
|
|
84
|
-
|
|
85
|
-
// 使用智能检测的结果
|
|
86
|
-
const detected = scripts.detected[scriptType]
|
|
87
|
-
if (detected) {
|
|
88
|
-
return detected
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
// 如果没有检测到,尝试精确匹配
|
|
92
|
-
if (scripts.scripts[scriptType]) {
|
|
93
|
-
return scriptType
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
return undefined
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
/**
|
|
100
|
-
* 显示可用的脚本列表
|
|
101
|
-
*/
|
|
102
|
-
function showAvailableScripts(project: ProjectInfo) {
|
|
103
|
-
if (!project.scripts) return
|
|
104
|
-
|
|
105
|
-
const scriptNames = Object.keys(project.scripts.scripts)
|
|
106
|
-
if (scriptNames.length === 0) {
|
|
107
|
-
warn('package.json 中没有定义任何 scripts')
|
|
108
|
-
return
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
info('可用的脚本:')
|
|
112
|
-
for (const name of scriptNames) {
|
|
113
|
-
console.log(` - ${name}`)
|
|
114
|
-
}
|
|
115
|
-
console.log()
|
|
116
|
-
info('使用 qy <script> 运行任意脚本')
|
|
117
|
-
}
|
package/src/cli/script.ts
DELETED
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
import { analyzeProject } from '../analyzer'
|
|
2
|
-
import { getRunCommand } from '../analyzer/package-manager'
|
|
3
|
-
import { execute } from '../runner/executor'
|
|
4
|
-
import { error, log, info } from '../utils/log'
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* 运行任意 package.json 脚本
|
|
8
|
-
*/
|
|
9
|
-
export async function scriptCommand(projectDir: string, scriptName: string) {
|
|
10
|
-
// 分析项目
|
|
11
|
-
const project = await analyzeProject(projectDir)
|
|
12
|
-
|
|
13
|
-
if (project.type === 'unknown') {
|
|
14
|
-
error('未检测到项目类型。请确保当前目录包含 package.json')
|
|
15
|
-
process.exit(1)
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
if (!project.scripts) {
|
|
19
|
-
error('无法读取 package.json 的 scripts')
|
|
20
|
-
process.exit(1)
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
// 检查脚本是否存在
|
|
24
|
-
const scripts = project.scripts.scripts
|
|
25
|
-
if (!(scriptName in scripts)) {
|
|
26
|
-
error(`脚本 "${scriptName}" 不存在`)
|
|
27
|
-
console.log()
|
|
28
|
-
showAvailableScripts(scripts)
|
|
29
|
-
process.exit(1)
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
log(`包管理器: ${project.packageManager.name}`)
|
|
33
|
-
log(`执行脚本: ${scriptName}`)
|
|
34
|
-
|
|
35
|
-
// 执行脚本
|
|
36
|
-
const runCmd = getRunCommand(project.packageManager.name, scriptName)
|
|
37
|
-
const exitCode = await execute(runCmd, { cwd: projectDir })
|
|
38
|
-
|
|
39
|
-
if (exitCode !== 0) {
|
|
40
|
-
process.exit(exitCode)
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* 显示可用脚本列表
|
|
46
|
-
*/
|
|
47
|
-
function showAvailableScripts(scripts: Record<string, string>) {
|
|
48
|
-
const scriptNames = Object.keys(scripts)
|
|
49
|
-
|
|
50
|
-
if (scriptNames.length === 0) {
|
|
51
|
-
info('package.json 中没有定义任何脚本')
|
|
52
|
-
return
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
info('可用的脚本:')
|
|
56
|
-
for (const name of scriptNames) {
|
|
57
|
-
console.log(` - ${name}`)
|
|
58
|
-
}
|
|
59
|
-
}
|