@composy/launcher
@composy/launcher
@composy/launcher 是一个基于 Vite
8 的前端项目启动、构建、预览、诊断、脚手架和部署工具。它把常见项目工作流收敛到一个 CLI,同时保留可编程的
Launcher、配置解析、引擎、插件、部署和项目分析 API。
本文档以当前包源码、package.json、CLI 的 help --all --json
输出和公共类型为准。命令数量、选项和导出路径发生变化时,优先以当前版本的机器可读帮助为最终事实来源:
launcher help --all --json
launcher version --json
V8:可选插件 Hook 性能分析(源码级能力)
运行 launcher build --profile 时,除 Launcher 的配置解析、引擎初始化与 Vite 构建阶段耗时外,还会输出 Launcher 注入的 Vite 插件 Hook 的累计耗时、调用次数与失败次数(按累计耗时倒序,最多展示前 10 组):
launcher build --profile
程序化调用可以获取结构化数据:
const launcher = new Launcher({ cwd: process.cwd(), profile: true })
try {
await launcher.build()
const report = launcher.getPerformanceReport()
console.log(report?.pluginHooks?.hooks.slice(0, 10))
console.log(report?.pluginHooks?.omittedSamples)
} finally {
await launcher.destroy()
}
诊断仅在显式启用 profile 的单次构建中装配,关闭时不包装插件。支持同步、异步、Promise 插件选项及对象形式的 Hook,并保留原插件实例不变。指标为单次调用的墙钟耗时累计,不代表独占 CPU 时间;异步 Hook 的等待时间也会计入。为保护 Watch/长期运行进程,阶段明细最多保留约 1024 条(活跃阶段不会被强制丢弃)、插件 Hook 分组最多 256 组,终端会截断较长的结果并提示省略数量。
限制:不会覆盖由用户 vite.config.* 文件或 Vite 内部单独注入的插件;Watch 模式尚未开放按轮次的 Hook 报告。完整工作区的 Vite 集成性能基准尚未验证。
包信息
| 项目 | 当前值 |
|---|---|
| npm 包 | @composy/launcher |
| 当前版本 | 0.0.1 |
| 运行时 | Node.js ^20.19.0 或 >=22.12.0 |
| 构建基础 | Vite 8.1.4、TypeScript、ESM |
| CLI bin | launcher、ldesign-launcher |
| 包入口 | ESM dist/index.js,CommonJS dist/index.cjs,类型 dist/index.d.ts |
| 许可证 | MIT |
包是 ESM 包,但根入口、CLI、core、utils、types、config 等 Node 侧导出同时提供
import 和 require 条件;@composy/launcher/client/*
是浏览器侧 ESM 导出,不提供 CommonJS 分支。
能解决什么问题
- 统一启动链:零配置执行
dev、build、preview,并保留 Vite 的常用选项。 - 自动识别工程:根据依赖、配置和文件结构解析 React、Vue 2/3、Svelte、Solid、Preact、Lit、Qwik、Angular、Vanilla 等项目。
- 运行模式解析:对 engine 接管、hybrid 和 delegated 项目使用不同的执行策略,避免对框架原生 CLI 的假设。
- 配置治理:统一处理配置文件搜索、环境配置、代理、别名、缓存、SSR、库模式、插件和生命周期。
- 内置插件:配置注入、应用配置、加载进度、Mock、SVG、字体、PWA、API 文档和 pnpm workspace 别名等能力可按阶段启用。
- 项目生产力:
init/create脚手架、generate代码生成、env环境文件管理、features扩展管理、snippet片段管理。 - 诊断与优化:
analyze、doctor、info、lint、cache和upgrade提供可脚本化的检查入口。 - 部署收口:支持配置化目标、环境和版本覆盖,并兼容 Vercel、Netlify、Cloudflare、GitHub Pages、Surge、FTP、SFTP、SSH、Docker、Kubernetes 和 custom provider 类型。
框架与运行模式
框架“可识别”不等于每个框架都由同一套 Vite engine 完全接管。运行时会先解析框架适配器,再决定每个操作的执行模式。
| 模式 | 当前框架范围 | 行为 |
|---|---|---|
| Engine-managed | React、Vue 2/3、Svelte、Solid、Preact、Lit、Qwik、Angular、Vanilla | Launcher 直接调用 engine 的 dev、build、preview,并可注入 builtin plugins。 |
| Hybrid | Remix、SvelteKit、Marko | 某些操作可由 Vite engine 接管;不适合接管的操作会委托框架命令。 |
| Delegated | Astro | 保留统一命令入口,但实际执行主要委托 Astro 原生 CLI。 |
建议在 hybrid/delegated 项目中先运行:
launcher info --json
launcher help dev
launcher help build
这些命令可以确认当前工程画像、解析出的 framework/engine 和可用选项。README 不把未在当前 checkout 中实际运行的外部框架 fixture、浏览器流程或云平台凭据验证误写成发布承诺。
安装
pnpm add -D @composy/launcher
# 或
npm install -D @composy/launcher
推荐在 package.json 中收敛脚本:
{
"scripts": {
"dev": "launcher dev",
"build": "launcher build",
"preview": "launcher preview",
"analyze": "launcher analyze --offline",
"doctor": "launcher doctor",
"info": "launcher info"
}
}
第七轮:按需构建阶段诊断
通过 --profile 同时显示 Launcher 各阶段耗时和当前进程的采样内存统计:
launcher build --profile
launcher build --fast --profile --analyze
阶段包含配置加载、框架解析、引擎初始化、框架/内置插件装配、Vite 构建以及操作后钩子。 这不是 Vite 插件 Hook 逐项耗时,也不是子进程的内存分析;采样仅在显式启用时进行。
程序化 API 也可启用同一诊断:
import { Launcher } from '@composy/launcher'
const launcher = new Launcher({ cwd: process.cwd(), profile: true })
try {
await launcher.build()
console.log(launcher.getPerformanceReport())
}
finally {
await launcher.destroy()
}
普通 build() 的引擎会在该轮构建完成(或失败)后立即释放;dev()、preview()
与 buildWatch() 所持有的长驻引擎继续由 destroy() 管理。对同一个 Launcher
并发触发多项操作时,getPerformanceReport() 代表最近完成的那次操作。
快速开始
零配置运行
在已有的标准前端项目根目录执行:
launcher dev
launcher build
launcher preview
默认值为:开发端口 3000、预览端口 4173、输出目录
dist、开发/预览不自动打开浏览器、构建启用压缩并清空输出目录。
生成配置
launcher config init
会在项目中生成
.ldesign/launcher.config.ts(如果已有配置,按命令提示处理)。也可以生成配置模板:
launcher generate config
查看当前解析结果
launcher config resolved --json
launcher info --json
launcher config builtins --json
CLI 完整目录
每次 launcher / ldesign-launcher 命令都会在项目根目录创建完整执行日志:
.ldesign/.logs/launcher/<ISO 时间>-<pid>.log
日志包含启动参数、运行环境、终端输出、异常堆栈、退出码与耗时。token、密码、secret 与 API key 等常见敏感参数会自动脱敏;日志系统采用 best-effort 写入,不会影响启动、构建或预览命令的原有结果。
当前 CLI 有 20 个命令。别名、参数和示例来自
launcher help --all --json;需要某个版本的完整机器可读描述时,不要依赖 README 中的静态表格。
CLI 选项元数据契约
launcher help --all --json
暴露的命令选项元数据也是运行时校验契约。扩展命令可以为选项声明
required、choices 和
validate:缺少必填值、传入不在候选集合中的值,或未通过自定义校验时,处理器不会被调用,CLI 会返回非零退出码。数组选项会逐项校验
choices,别名、默认值和类型转换则在校验前统一归一化,因此帮助输出与实际执行规则保持一致。
命令注册还会拒绝把一个已有命令的别名重新占用为新命令名,避免卸载命令后留下不可预测的别名映射。
launcher help --all --json > launcher-commands.json
launcher version --json
| 命令 | 别名 | 用途 | 常用参数 |
|---|---|---|---|
analyze |
— | 分析项目画像、配置、构建产物和依赖 | --type project|bundle|deps|all、--dir、--budget、--report、--json、--fix-plan、--offline |
build |
bundle |
生产构建 | --outDir、--sourcemap、--minify、--watch、--environment、--target、--report、--emptyOutDir、--ssr、--analyze |
cache |
c |
查看或清理缓存 | <list|clear>、--type all|launcher|vite|deps|build|temp、--force、--dry-run |
config |
cfg |
读取、修改、验证和初始化配置 | <list|get|set|delete|validate|resolved|init|builtins>、--json、--pretty、--environment |
create |
new |
创建前端项目 | [project-name]、--template、--framework、--variant、--no-install、--no-git、--list、--packageManager、--force |
deploy |
— | 执行配置化或 legacy 部署 | --target、--platform、--profile、--environment、--release、--build/--no-build、--dryRun、--open、凭据和远程路径参数 |
dev |
serve、start |
启动开发服务器 | --port、--host、--open、--https、--force、--cors、--strictPort、--clearScreen、--environment |
doctor |
diag |
工程健康检查和诊断报告 | 无必选参数 |
env |
e |
管理 .env 文件和变量 |
--list、--file、--set、--get、--remove、--search、--validate、--compare、--copy、--example、--export、--import |
features |
feature、feat |
管理 feature、依赖和 manifest | --list、--status、--enable、--disable、--force、--use、--config、--from、--manifest-path、--sync-manifest、--tree、--json |
generate |
gen、g |
生成 component、page 或 config | <component|page|config> [name]、--framework、--list、--dir、--route、--force、--style |
help |
h |
查看帮助、分组和搜索结果 | [command]、--all、--group、--search、--json |
info |
i |
查看当前项目运行时信息 | --json |
init |
bootstrap |
初始化前端项目 | 同 create:--template、--framework、--variant、--install、--git、--list、--packageManager、--force |
lint |
— | 代码质量检查 | --fix、--eslint、--typescript/--ts、--prettier、--path |
mock |
m |
管理 API Mock 场景 | --list、--create、--switch、--delete、--record、--generate、--template、--analyze、--scenario |
preview |
serve-build |
预览构建产物 | --port、--host、--open、--https、--outDir、--cors、--strictPort、--environment |
snippet |
snip、s |
管理代码片段 | --list、--search、--use、--add、--remove、--export、--import、--language、--category |
upgrade |
up、update |
分析和升级依赖 | --analyze、--safe、--check、--changelog |
version |
v |
查看版本 | --json、--verbose |
工作流命令示例
# 开发、构建、预览
launcher dev --host 0.0.0.0 --port 8080
launcher build --outDir build --sourcemap --analyze
launcher preview --outDir build --port 4174
# 配置和环境
launcher config get server.port
launcher config set server.port 8080
launcher config validate --environment production
launcher env --set VITE_API_URL=https://api.example.com --file .env.production
launcher env --compare .env:.env.production
# 诊断和分析
launcher analyze --type project --json --offline --fix-plan
launcher analyze --type bundle --dir dist --report reports/bundle.html
launcher doctor
launcher lint --typescript --eslint
# 缓存和扩展
launcher cache list
launcher cache clear --type vite --dry-run
launcher features --status --json
launcher mock --create local --switch local
帮助、JSON 输出和长驻会话
launcher help dev
launcher help --group workflow
launcher help --search cache
launcher help --all --json > launcher-commands.json
dev、preview 和 build watch 是长驻进程。交互终端中通常可使用 h
查看快捷键、c 清屏、o 打开地址、r 重启 dev、u 重新打印 URL、b
打印构建摘要、q 退出并清理资源;非交互 CI 应使用显式参数和
--strictPort,不要依赖按键输入。
配置文件解析
搜索顺序
不指定 --config 时,launcher 按以下优先级查找配置:
.ldesign/launcher.config.<environment>.*launcher.config.<environment>.*- 兼容旧命名
.ldesign/launcher.<environment>.config.*和launcher.<environment>.config.* - 基础
.ldesign/launcher.config.* - 根目录
launcher.config.* - 兼容的
vite.config.*
支持扩展名:.ts、.mjs、.js、.cjs、.json。环境名内置支持
development、production、test、staging、preview;可以用
--environment 或 --mode 选择解析视图,--config <path> 可明确指定文件。
launcher dev --environment staging
launcher build --environment production
launcher config resolved --environment production --json
launcher dev --config .ldesign/launcher.config.ts
launcher config --global
保留为兼容参数但当前明确不支持全局写入,会返回错误;请把配置放在项目配置文件中,或通过
--config 指定路径。
完整配置示例
import { defineConfig } from '@composy/launcher'
import type { ViteLauncherConfig } from '@composy/launcher'
export default defineConfig({
root: process.cwd(),
server: {
host: '0.0.0.0',
port: 3000,
open: true,
cors: true
},
build: {
outDir: 'dist',
target: 'es2015',
sourcemap: true,
minify: true,
emptyOutDir: true
},
preview: {
host: 'localhost',
port: 4173
},
proxy: {
api: {
target: 'http://localhost:8080',
pathPrefix: '/api',
rewrite: true
}
},
framework: {
type: 'auto'
},
launcher: {
logLevel: 'info',
debug: false,
autoRestart: true,
hooks: {
// 旧版配置生命周期;新项目推荐用上方 Launcher API 的 hooks
beforeBuild: async () => {}
},
plugins: {
builtin: {
configInjection: { enabled: true },
appConfig: { enabled: true },
loadingProgress: {
enabled: true,
stages: ['dev', 'build'],
variant: 'bar',
text: 'Loading application'
},
workspaceAliases: {
enabled: true,
mode: 'dependencies',
stage: 'dev',
styles: true
},
mock: {
enabled: false,
mockDir: 'mock'
}
}
},
alias: {
enabled: true,
stages: ['dev', 'build', 'preview']
},
cache: {
enabled: true,
strategy: 'hybrid'
},
deployment: {
defaultTarget: 'production',
defaultEnvironment: 'production',
targets: {
production: {
name: 'production',
provider: 'vercel',
sourceDir: '.',
outputDir: 'dist',
buildBeforeDeploy: true,
config: {
projectName: 'my-app'
},
// credentials 建议从环境变量读取,不要提交到仓库
credentials: {}
}
},
environments: {
staging: {
outputDir: 'dist-staging',
mode: 'staging'
}
}
}
},
resolve: {
alias: [
{ find: '@app', replacement: './src', stage: 'all' },
{ find: '@test', replacement: './test', stage: 'dev' },
{ find: 'lodash', replacement: 'lodash-es', stage: 'build' },
{
find: /^@shared\/(.+)$/,
replacement: './packages/shared/$1',
stage: 'dev'
}
]
}
} satisfies ViteLauncherConfig)
常用配置字段
| 位置 | 字段 | 说明 |
|---|---|---|
| Vite 根级 | root、mode、logLevel、clearScreen |
原生 Vite 基础配置,launcher 会在运行时合并。 |
server |
host、port、open、https、cors |
开发服务器。命令行选项会覆盖对应值。 |
build |
outDir、target、sourcemap、minify、watch、ssr |
生产构建与 watch。 |
preview |
host、port、open、https、cors |
构建产物预览。 |
launcher |
autoRestart、debug、logLevel、cwd、mode、cache |
launcher 自身运行时。 |
launcher.plugins |
plugins、config、hotReload、builtin |
外部插件、插件配置和 builtin plugin。 |
launcher.alias |
enabled、stages |
控制内置 @ 和 ~ 别名。默认启用。 |
launcher.workspaceAliases |
enabled、mode、stage、sourceDir、styles、workspaceRoot |
从 pnpm workspace 包生成别名。 |
launcher.deployment |
defaultTarget、targets、environments、versions、hooks |
配置化部署模型。 |
resolve.alias |
find、replacement、stage/stages |
Vite 别名;带阶段字段的别名只在对应操作生效。 |
proxy |
target、pathPrefix、rewrite 等 |
launcher 增强代理配置。 |
阶段化别名
launcher 默认提供 @ -> <cwd>/src、~ -> <cwd>,默认在
dev、build、preview 都生效。可以关闭或限定阶段:
export default defineConfig({
launcher: {
alias: {
enabled: true,
stages: ['dev', 'build']
}
},
resolve: {
alias: [
{ find: '@shared', replacement: './packages/shared/src', stage: 'dev' },
{ find: '@deploy-only', replacement: './src/deploy', stage: 'build' },
{ find: '@everywhere', replacement: './src', stage: 'all' },
// 兼容旧数组字段:stages: ['dev', 'build']
{ find: '@legacy', replacement: './src/legacy', stages: ['dev', 'build'] }
]
}
})
没有 stage/stages 的普通 Vite
alias 会作为所有操作的兼容别名保留;customResolver
等 Vite 原生字段也会被保留。
Builtin plugins
统一配置形态:
launcher: {
plugins: {
builtin: {
svg: {
enabled: true,
stages: ['dev', 'build'],
mode: 'component',
sourceDir: 'src/assets/icons',
outputDir: 'src/components/icons',
framework: 'vue3',
typescript: true,
componentPrefix: 'Icon',
},
},
},
}
当前类型注册的 builtin 名称:appConfig、apiDocs、configInjection、devLogger、font、image、loadingProgress、mock、pwa、svg、workspaceAliases。每个插件可以是
true/false 或带 enabled、stages 和插件专属字段的对象。运行:
launcher config builtins
launcher config builtins --json
Workspace aliases
launcher: {
workspaceAliases: {
enabled: true,
mode: 'dependencies', // dependencies | all
stage: 'dev', // dev | build | all
sourceDir: 'src',
styles: true,
workspaceRoot: '../..',
},
}
它会查找 pnpm-workspace.yaml,为工作区包的源码入口以及可选的 styles
入口生成 alias。发布构建和外部包消费时,应确认 alias 不会把应用依赖错误指向工作区源码。
程序化 API
Launcher(推荐)
import { Launcher } from '@composy/launcher'
const launcher = new Launcher({
cwd: process.cwd(),
mode: 'development',
hookErrorPolicy: 'strict',
hooks: {
beforeOperation: async ({ operation, config }) => {
if (operation === 'build') {
return {
...config,
build: { ...config.build, sourcemap: true }
}
}
},
afterOperation: ({ operation, duration }) => {
console.log(`${operation} finished in ${duration}ms`)
}
}
})
try {
const config = await launcher.getConfig()
const result = await launcher.build()
console.log(config.build?.outDir, result)
} finally {
await launcher.destroy()
}
构造选项:configFile、cwd、logLevel、debug、mode、watch、inlineConfig、onConfigChange、hooks、hookErrorPolicy。公开操作:
dev()、build()、buildWatch()、preview()getConfig()、getCurrentRuntimeResolution()getCurrentEngine()、getCurrentDevServer()、getCurrentPreviewServer()、getCurrentBuildWatcher()restartDev()、destroy()
生命周期 hook 分为
beforeOperation、afterOperation、onOperationError、beforeDestroy、afterDestroy。beforeOperation
可以返回新配置;hookErrorPolicy 默认为 warn,设置为 strict
时 hook 异常会中断主流程。
懒加载工厂
import { createLauncher } from '@composy/launcher'
const factory = createLauncher()
const Launcher = await factory.Launcher()
const launcher = new Launcher({ cwd: process.cwd() })
const config = await launcher.getConfig()
await launcher.destroy()
工厂还提供 version、ViteLauncher()、ConfigManager() 和
createCli(),适合 CLI、脚本或只希望按需加载模块的宿主。
ViteLauncher 兼容 API
ViteLauncher 仍作为 Vite 专用兼容层导出,包含
initialize、startDev、stopDev、restartDev、restartDevWithConfig、build、buildWatch、preview、loadConfig、getConfig、abort、destroy、dispose。新代码优先使用
Launcher;已有 ViteLauncher 集成可以逐步迁移,不要把旧 API 和新 facade 的 hook 语义混用。
部署
推荐把部署目标写在 launcher.deployment:
launcher: {
deployment: {
defaultTarget: 'production',
defaultEnvironment: 'production',
defaultVersion: 'stable',
targets: {
production: {
provider: 'vercel',
sourceDir: '.',
outputDir: 'dist',
buildCommand: 'launcher build',
buildBeforeDeploy: true,
environment: 'production',
config: { projectName: 'my-app' },
credentials: {},
hooks: {
beforeDeploy: [],
afterDeploy: [],
onSuccess: [],
onFailure: [],
},
},
},
environments: {
staging: { outputDir: 'dist-staging', mode: 'staging' },
},
versions: {
canary: { outputDir: 'dist-canary', mode: 'canary' },
},
},
}
目标字段包括
provider、sourceDir、outputDir、buildCommand、buildBeforeDeploy、preview、openAfterDeploy、mode、env、environment、version、credentials、config、platformConfig、remotePath、共享文件/目录、保留 release 数量和部署 hooks。目标既可以是对象,也可以是带
name 的数组。
launcher deploy
launcher deploy --target production
launcher deploy --target production --environment staging --release canary
launcher deploy --target production --dryRun
launcher deploy --no-build
--dryRun
会解析并校验目标、输出目录和部署计划,不执行构建或上传,适合 CI 的配置检查。凭据请使用环境变量或 CI
secret;不要提交 token、密码或私钥。CLI 还保留 legacy
--platform、--token、--site、--host、--port、--username、--password、--key、--path、--clean、--profile、--history
等参数,用于旧的 deploy profile 或平台适配器。
当前 provider 类型注册为:vercel、netlify、github-pages、cloudflare、cloudflare-pages、surge、ftp、sftp、ssh、docker、kubernetes、custom。真实云平台上传仍取决于凭据、网络和目标平台配置;--dryRun
不等于远端部署成功。
客户端配置读取
根入口提供 @composy/launcher/client/* 浏览器侧 ESM
hooks,用于读取由配置注入插件暴露的应用配置。可用子路径包括:
@composy/launcher/client
@composy/launcher/client/react
@composy/launcher/client/vue
@composy/launcher/client/vue2
@composy/launcher/client/svelte
@composy/launcher/client/solid
@composy/launcher/client/qwik
@composy/launcher/client/lit
@composy/launcher/client/angular
这些入口不会替代各框架自己的运行时;它们只负责在浏览器端读取 launcher 注入的配置数据。
导出路径
| 导入路径 | 内容 |
|---|---|
@composy/launcher |
Launcher、ViteLauncher、createLauncher、defineConfig、引擎、框架、部署、插件、错误和公共类型。 |
/cli |
CLI 创建器、命令注册和命令上下文。 |
/core |
Launcher、ConfigManager、EngineManager、AliasManager、bootstrap 等核心模块。 |
/utils |
alias、配置、环境、文件系统、日志、构建分析等工具。 |
/types |
ViteLauncherConfig、部署、引擎、框架、插件和 API 类型。 |
/constants |
默认值、版本、消息和常量。 |
/config |
配置报告和配置相关能力。 |
/features |
feature 注册、兼容性和诊断能力。 |
/project |
项目画像和项目检测。 |
/client/* |
浏览器侧 ESM 配置读取 hooks。 |
core 还提供显式子路径
/core/Launcher、/core/ViteLauncher、/core/ConfigManager、/core/EngineManager、/core/AliasManager、/core/ConfigPresets、/core/bootstrap。直接导入子路径时请保留完整路径和大小写。
脚手架、环境和扩展工作流
# 查看模板并创建工程
launcher init --list
launcher create my-app --framework react --variant ts
launcher create my-app --template vue3-ts --no-install
# 生成代码
launcher generate --list
launcher generate component HeroBanner --framework vue3 --style
launcher generate page Dashboard --route dashboard
# 环境文件
launcher env --list --file .env.development
launcher env --set VITE_API_URL=http://localhost:8080 --file .env
launcher env --example
launcher env --export .env.json
# feature 与片段
launcher features --list
launcher features --enable health
launcher features --tree
launcher snippet --list
launcher snippet --search vue
脚手架的模板清单和框架变体以当前安装包为准,使用 --list
查看,不要在自动化脚本中硬编码可能随版本变化的模板名称。
包开发、验证和发布检查
在本包目录 tools/launcher 中:
pnpm run quality:check # lint、format、typecheck
pnpm run test:run # Vitest 全量测试
pnpm run build # raw bundle + declarations + pack verify
pnpm run size # Brotli 预算
pnpm run pack:check # npm pack 内容和声明输出检查
pnpm run docs:build # VitePress 文档构建
pnpm run release:verify
主包构建由当前 workspace 的 @composy/pack CLI 驱动:build:raw 生成 ESM/CJS,
build:types 使用严格的 .ldesign/tsconfig.build.json 生成声明,最后由
build:verify 校验 package.json 声明的 71 个产物。pnpm run build
只是这三个步骤的显式串联,不再依赖历史的 pack run --tool 参数。
当前构建由工作区 @composy/pack(tools/tsup-config)驱动。生产入口在
.ldesign/pack.config.ts 中显式维护,生成 ESM/CJS JavaScript 和 tsc
声明文件。ESLint 权威配置为
.ldesign/eslint.config.js;开发态和声明构建 TypeScript 配置分别为
.ldesign/tsconfig.json 与
.ldesign/tsconfig.build.json。根目录同名配置保留为兼容入口,包脚本直接调用该目录中的权威配置。不要把“源码 glob”理解为 npm 包会发布测试文件。package.json.files
只发布 dist、bin、两个 README 和 CHANGELOG.md,pack verify
会检查声明的 71 个输出。
pnpm run build 会将 raw bundle、声明生成和 pack verify 的完整输出写入
.ldesign/.logs/launcher/;日志只保存在当前项目,不上传,且常见凭据参数会脱敏。
本次源码审计的验证基线:70 个测试文件、862 个测试通过;构建输出声明检查 71/71;Brotli 体积约 230.29 kB(预算 2 MB 内);npm pack 约 288 个文件、packed size 约 3.45 MB、unpacked size 约 16.42 MB。该数字是当前 checkout 的验证记录,不代表所有未来版本恒定不变。
嵌入式运行与安全语义
createCli().run(argv)返回命令退出码,库层不会调用process.exit();只有bin/启动边界负责把非零结果交给宿主进程。ConfigManager对显式配置文件采用 fail-closed 语义:解析失败会抛出错误,不会静默改用默认配置。- 环境变量展示接口会掩码 token、password、key 等敏感值;读-改-写通过 raw 内部值保留真实 secret,
getMergedEnv()仅供可信运行时使用,展示场景使用getMergedEnvView()。 DeployService的DeployCallbacks携带AbortSignal,内置构建可被取消;部署历史使用原子写入,结果通过historyPersisted标记持久化是否成功。
源码结构
src/
cli/ CLI 命令、解析和命令上下文
client/ 浏览器侧配置读取 hooks
constants/ 常量、版本和默认值
core/ 配置管理、运行时解析、Launcher 主链
deploy/ 部署选择、服务和平台适配器
engines/ engine 抽象与 Vite engine
features/ feature 管理、兼容性和诊断
frameworks/ 框架适配器与检测器
mock/ Mock 场景和服务
plugins/ builtin plugins 与 Vite 集成
project/ 项目画像与项目检测
registry/ engine/framework 注册中心
scaffolding/ 脚手架和代码生成
types/ 公共类型
utils/ alias、配置、环境、日志等工具
进一步阅读
与 @composy/cli 集成
@composy/cli 可以把本包接入统一的 ldesign 命令:
pnpm add -D @composy/cli
ldesign launcher --help
ldesign tools run launcher --help
本包自身仍提供 launcher 和 ldesign-launcher
两个原生 bin;统一 CLI 只是转发入口。
License
MIT
通过 @composy/cli 统一接入
- 接入类型:
bin - 统一命令:
ldesign launcher - 命令别名:
ldesign-launcher - 包内原生 bin:
launcher、ldesign-launcher
当前包通过独立 bin 接入,统一命令会转发到包自身 CLI。
pnpm add -D @composy/cli
ldesign launcher --help
ldesign tools run launcher --help
V5 Watch 模式事件与资源清理
launcher build --watch 现基于 Rollup 实际事件展示每次重建结果,监听器启动不再误报编译完成。对于编程 API 的事件时机及销毁规则,请参阅 Watch 模式生命周期说明。
Watch 增量构建报告(V9)
Watch 模式使用同一套有界事件状态机统计构建轮次,避免两套启动入口出现不同的完成/失败判断。END 事件不会单独表示成功:只有同一轮出现 BUNDLE_END,随后收到 END 才会统计成功。中断和失败各自计数,重复 END 不会重复执行构建后置钩子。
launcher build --watch --profile
退出 Watch 时输出成功、失败、中断轮次,以及已完成轮次的平均/最长耗时。这里的时间基于 Launcher 进程观察到的构建事件,不等于 Vite 内部每个插件 Hook 的执行时间。
程序化接口也可随时读取:
import { Launcher } from '@composy/launcher'
const launcher = new Launcher({ cwd: process.cwd() })
try {
const watcher = await launcher.buildWatch()
// ...监听文件修改,或通过 watcher.on('event', ...) 订阅原生事件
const report = launcher.getWatchBuildReport()
console.log(report?.successes, report?.failures, report?.averageDuration)
} finally {
await launcher.destroy()
}
ViteLauncher 兼容入口也提供 getWatchBuildReport()。每次 Watch 会话最多保存最近 64 条摘要,仅聚合数量、时长与结果,不保留 RollupOutput、源文件内容或错误对象;停止后可以查询已完成的会话摘要。对于长驻进程的完整时间线,请在应用侧按需持久化 build:watch-completed 等事件。
V10:高频 Watch 的后置 Hook 背压控制
launcher build --watch --profile 新增展示后置 Hook 的 executed / coalesced / failed。为避免长耗时 Hook 在高频重建时生成无限 Promise 队列,两套启动入口现共用最多一个运行中任务 + 一个最新待处理任务的有界调度器;停止监听时完成这些任务。
注意:构建成功/失败事件及计数不会合并,但繁忙时中间成功轮次的 afterOperation / afterBuild 可能被合并。要求逐轮执行的审计、发布等动作,应自行订阅 build:watch-completed 并使用可持久化队列。详见 Watch 生命周期文档。
V11:开发服务并发与运行时插件去重
Launcher.restartDev() 现在将同时到达的手动重启合并为同一次关闭/重新启动操作;如果开发服务尚在启动,则等待其就绪再执行重启。destroy() 会等待该重启任务结束,销毁开始后不再创建新服务。
兼容入口 ViteLauncher.startDev() 也对并发启动做了单任务复用,其 dispose() 会等待在途启动完成后统一清理。旧版入口在开发与预览过程中注入的 app-config/config-injection 插件按实例身份识别和替换,避免反复重启积累旧实例,同时不会误删用户同名插件。插件合并统一保留用户配置优先、Promise 懒加载及注册顺序。
离线回归(不依赖完整 monorepo):node scripts/smoke-v11.mjs 或 npm run test:smoke-source。这些检查不等同于真实 Vite、Vitest 和跨框架集成测试。
V12 runtime consistency notes
The modern Launcher now snapshots lifecycle callbacks per operation. Concurrent
build(), dev() and preview() calls cannot swap config-defined afterOperation
or onOperationError callbacks. Watch rebuilds likewise retain the callbacks
from their own started configuration. The global getConfig()/destroy hooks
remain instance-level for backwards compatibility.
The compatibility ViteLauncher now treats its prepared dev/preview plugin list
as session-local; plugin instances injected for one server run do not become
the base config for later builds. Its mergeConfig() merges only plain records,
ignores dangerous object keys, preserves plugin/Date/RegExp references and rejects
cyclic plain records. Concurrent calls to preview() share a startup operation;
dispose() waits for an in-flight preview to settle before closing the server.
These are behavioral safety fixes, not measured build-time speedups. Run
npm run test:smoke-source for dependency-free regression checks, including
scripts/smoke-v12.mjs. Full workspace type checking, Vite integration and
memory benchmarks still require the monorepo dependencies and .ldesign config.
V13:普通构建的 Watcher 安全检查及兼容入口幂等销毁
旧版 ViteLauncher.build() 现在与新引擎一样拒绝 build.watch:
即使该选项来自 vite.config.* 合并阶段,也不会把 Vite 返回的后台 Watcher
误当成普通产物而报告构建成功;意外 Watcher 会被关闭,若关闭失败则保留引用,
供后续 dispose() 重试。需要持续构建时请使用 buildWatch()。
兼容入口的多次并发 dispose() 现在共享清理任务,已完成清理的调用保持幂等;
若资源关闭或配置监听器销毁失败,后续调用仍可重试。异步插件工厂遇到循环数组会明确报错;
运行时 Alias 只对纯字符串条目进行安全去重,不再因闭包字符串化而误删动态替换。
离线验证:node scripts/smoke-v13.mjs 或 npm run test:smoke-source。
完整 TypeScript 类型检查、真实 Vite 构建与性能数据仍需在完整 monorepo 中验证;
详见 THIRTEENTH_ROUND_SOURCE_AUDIT.zh-CN.md。
V14:统一内置插件装配与 Alias 去重
V14 将内置插件合并接入 core/pluginMerge.ts 的共享策略,保留显式配置插件优先级,
稳定维持批量前置插件的原始顺序,并支持嵌套与 Promise 插件选项;循环嵌套数组
会被明确拒绝。此变更修复了旧版 unshift() 前置多插件时可能倒序的问题。
utils/aliasDedupe.ts 统一配置解析、内置插件与分阶段 Alias 的保守去重:
只折叠完全相同的纯字符串 Alias;RegExp、函数、customResolver 与其他扩展属性
不会通过字符串化被误判为重复,也不修改输入配置。
新入口 Launcher 在 没有任何 beforeOperation Hook 时,会复用配置加载阶段已经
完成的框架解析结果,不再重复运行一次框架检测和适配器创建。存在前置 Hook 时
仍重新解析,以兼容 Hook 对配置的原地修改。
离线行为验证:node scripts/smoke-v14.mjs 或 npm run test:smoke-source。
这些检查不等同于完整 monorepo 类型检查、真实 Vite 打包或性能基准。
详见 FOURTEENTH_ROUND_SOURCE_AUDIT.zh-CN.md。
内置插件配置优先级(V15)
兼容入口 ViteLauncher 与新版 Launcher 均遵循 launcher.plugins.builtin 的显式配置。
若同时存在旧版 tools.pwa、launcher.mock 等配置,builtin.<name>.enabled: false
始终优先,不会因旧配置开启而重新加载插件。stages 限制适用于开发、构建和预览阶段。
export default {
tools: { pwa: { enabled: true } }, // 旧版配置,仅作为兼容输入
launcher: {
plugins: {
builtin: {
pwa: { enabled: false }, // 显式禁用优先
loadingProgress: { enabled: true, stages: ['preview'] },
appConfig: { enabled: true, configFile: 'app.config.ts' },
configInjection: { enabled: false },
},
},
},
}
V15 避免为禁用的运行时插件创建实例,同时保留用户同名插件和注册顺序。
离线专项回归:npm run test:smoke-source(无需完整工作区安装,仅需要 Node.js 和 TypeScript)。
真实 Vite 版本兼容性及性能收益仍需要在完整工作区执行集成测试和基准测量。
V16:缓存一致性与诊断插件防护
CacheManager 继续维持原有 get、set、getOrSet、delete 和 clear API,不需要迁移调用代码。本轮加强三个高频并发场景:
- 旧磁盘读取与新写入重叠:如果文件读取期间同一键被更新,旧结果不会重新写回内存。短暂竞争时调用方可能收到
null,可通过getOrSet重新计算。 - 慢工厂与显式更新重叠:
getOrSet已开始执行的异步工厂不会覆盖随后执行的同键set();已在等待的原始调用方仍收到工厂计算结果。不同键之间互不取消。追踪令牌只在操作进行期间存在,不保存无限增长的键历史。 - 清空磁盘缓存与新写入重叠:
clear()正在删除旧文件时,新get()/set()会等待清理屏障;清理开始之后的新记录不会被旧文件清理误删。
插件性能分析选项 --profile 现在也会对循环嵌套的 Vite 插件选项明确抛出 TypeError,而不是递归直到栈溢出;合法复用同一个嵌套数组依然支持。其余插件对象不被修改。
独立验证:node scripts/smoke-v16.mjs,或运行包含 V1–V16 离线检查的 npm run test:smoke-source。完整 Vitest、TypeScript 类型检查、实际 Vite 构建性能和长时间 Watch 内存基准仍需完整 monorepo 环境。
V17:缓存删除顺序与磁盘记录一致性
CacheManager 的 get()、set()、getOrSet()、delete() 和 clear() 方法签名保持不变。
- 同键
delete/set顺序:磁盘写入、删除、损坏记录淘汰统一进入按键串行队列;先调用的删除不能删除随后提交的新值。删除一个键不再全局取消其他键的异步计算。 - 内存与磁盘一致:当最新缓存数据包含函数、循环引用等不可安全序列化的数据,或超过
maxDiskEntryBytes时,清除该键原有磁盘记录,避免下次启动恢复旧配置。当前进程仍可从内存读取最新值。 - 失效缓存淘汰:对过期、超限及损坏的磁盘记录,清理前重新核对文件信息并观察同键更新标记,不误删当前进程后续写入的新记录。
- LRU 更新:覆盖已有缓存键时不再误淘汰其他键。短期读取、写入保护和磁盘任务完成后释放对应跟踪记录。
执行 node scripts/smoke-v17.mjs 或 npm run test:smoke-source 运行包含受控 I/O 竞态的测试。
边界:按键队列只协调同一个 CacheManager 实例内的异步操作;多个进程/实例共享目录时仍没有跨进程文件锁或强一致性保证。全量工作区类型检查、Vitest、真实 Vite 构建和长期内存基准未在独立源码 ZIP 环境验证。详见 SEVENTEENTH_ROUND_SOURCE_AUDIT.zh-CN.md。
构建性能诊断与 CI JSON 报告(V18)
--profile 在终端展示 Launcher 阶段耗时与采样 RSS/Heap;--profileJson 独立开启相同采样,并将机器可读数据原子写入指定文件:
launcher build --profile --profileJson .launcher/reports/build.json
launcher build --watch --profileJson .launcher/reports/watch.json
- 文件包含
schemaVersion: 1、kind(build或watch)、timestamp和status;构建报告包含measurements.buildDurationMs、assetCount、outputDirectory、memory和phases。 - 构建失败时仍会尝试生成
status: "failed"及错误名称、消息;报告在构建资源清理结束后写入,不将清理失败误记为构建成功。 - Watch 报告在会话停止并清理资源后生成,包含成功、失败、中断轮次以及后置钩子队列统计。
- JSON 使用同目录随机临时文件加
rename()提交,写入失败会将 CLI 退出状态标记为失败。报告可能包含绝对路径与错误消息,上传 CI Artifacts 前应设置合适的访问权限。 - RSS/Heap 峰值为 Launcher 进程采样值,不含子进程,不应理解为系统峰值或 Vite 所有工作线程的总内存占用。
- 插件 Hook 统计仅覆盖 Launcher 注入的插件,不包括全部 Vite 内部插件。
缓存读取在 V18 改为从打开的文件句柄按配置上限读取,并检查文件类型、读取前后元数据、尾部增长与路径替换;在操作系统支持 O_NOFOLLOW 时拒绝跟随缓存文件符号链接。此措施不等同于完整的多进程缓存事务机制。
Disk cache safety (V19)
Launcher only removes its own 32-character hexadecimal .json cache records
when CacheManager.clear() is invoked. Other JSON files in a custom cache
directory are preserved. Cache records with unsafe JSON keys or accessors are
kept in memory if appropriate but are not written to disk; malformed disk
records are rejected and removed when they can be safely identified.
New cache files use restrictive permissions (0600), and new cache
directories use 0700 on POSIX platforms, subject to platform and filesystem
semantics. Existing files and directories are not automatically chmod-ed.
Validate these behaviors with node scripts/smoke-v19.mjs or the project's
test:smoke-source script. The cache still offers process-local ordering;
concurrent write/clear operations involving different processes require a
separate coordination strategy and are not claimed to be strongly consistent.
V20:有界磁盘缓存与安全缓存 CLI
CacheManager 增加 maxDiskEntries(默认 2048)、maxDiskBytes(默认 256 MiB)、diskPruneEveryWrites(默认 64)。成功磁盘写入达到阈值时,在构建主流程外触发磁盘维护。也可以显式执行 await cache.pruneDisk() 获取扫描条数、回收空间和是否扫描截断的报告。
const cache = new CacheManager(process.cwd(), {
maxDiskEntryBytes: 8 * 1024 * 1024,
maxDiskEntries: 2048,
maxDiskBytes: 256 * 1024 * 1024,
diskPruneEveryWrites: 64,
})
const maintenance = await cache.pruneDisk()
console.log(maintenance.remainingBytes, maintenance.truncated)
上述限制为软配额:自动清理进行中后续写入可能暂时超过目标;同目录多进程写入不是强一致事务。一次只扫描最多 50,000 个符合文件命名的记录,超限时可执行后续维护。
缓存 CLI 新增 --type launcher。--type all 包含 Launcher 自身的 .launcher-cache,但不会删除实际安装的 node_modules/.pnpm 或 dist/build/.output;后者需要显式指定 --type build。
launcher cache list --type all
launcher cache clear --type launcher --dry-run
launcher cache clear --type all --force
launcher cache clear --type build --dry-run
详见 TWENTIETH_ROUND_SOURCE_AUDIT.zh-CN.md。
V21:程序化 Vite 项目根目录和实例内模块复用
import { ViteEngine } from './src/engines/vite/ViteEngine'
const engine = new ViteEngine('/absolute/path/to/app')
await engine.initialize()
try {
// 未显式传入 root 时,Vite 使用 ViteEngine 的项目目录,
// 而不是宿主进程的 process.cwd()。
await engine.build({ configFile: false })
} finally {
await engine.dispose()
}
ViteConfigTransformer 不再生成空的 server / preview 配置;只有明确传入 Launcher 的顶层 proxy 时才覆写 server.proxy。Vite 模块定位结果只在当前引擎实例内缓存(最多 8 个项目根目录),失败可重试、销毁即释放。详细验证边界见 TWENTY_FIRST_ROUND_SOURCE_AUDIT.zh-CN.md。
V22 长驻程序化调用的生命周期保障
新版 Launcher 和兼容入口 ViteLauncher 均支持同时运行开发服务、预览和构建任务。
V22 针对兼容入口补全以下约束:
dispose()会等待当前仍在运行的普通build()结束后,再关闭共享资源;开始销毁后拒绝新的普通构建。- 并发调用
stopDev()会共享同一次关闭任务。即使beforeClose钩子失败,也会尝试关闭 Vite 服务器,并向调用者传播钩子异常。 startDev()在前次关闭仍在进行时等待其结束,避免再次绑定套接字。- 在同一
ViteLauncher实例中,Vite 模块解析成功后复用结果,失败不缓存;销毁时清空实例级引用。多项目实例之间不共享该缓存。
这些是兼容性和资源生命周期改进,不能代替真实打包时间、峰值 RSS 或跨框架集成测试。
无需安装完整 workspace 即可运行源码级回归:node scripts/smoke-v22.mjs;全套离线回归:npm run test:smoke-source。
详细实现、测试范围和遗留风险见 TWENTY_SECOND_ROUND_SOURCE_AUDIT.zh-CN.md。
V23 兼容入口的统一插件准备流水线
ViteLauncher 的开发、普通构建、增量 Watch 和预览现在复用相同的阶段化配置准备方法,以减少四条入口的逻辑漂移:
- dev:智能插件 → Mock → PWA → SVG/Font → HTTPS → 页面加载进度;
- build / Watch:智能插件 → PWA → SVG/Font → 图片优化 → API 文档 → 页面加载进度;
- preview:HTTPS → SVG/Font → 页面加载进度。
运行时 AppConfig / ConfigInjection 内置插件仍遵循各命令原有的生命周期边界,在执行 Vite 对应操作前按配置注入。配置覆盖不会写回 ViteLauncher 的基础配置。
可在没有完整 monorepo 依赖的环境下执行回归:
npm run test:smoke-source
npm run test:source-syntax
验收范围:以上脚本只验证源代码语法与离线行为。完整类型检查需 .ldesign/tsconfig.json 及工作区依赖,真实打包速度和内存改善仍需使用实际 Vue / React 等项目测试。有关逐文件变更与验证结果见 TWENTY_THIRD_ROUND_SOURCE_AUDIT.zh-CN.md。
V24:异步启动与资源销毁协调
针对兼容入口 ViteLauncher,V24 补齐了异步初始化、服务启动与关闭之间的资源归属约束:
dispose()等待正在进行的initialize(),避免配置管理器销毁后重新进入已初始化状态。stopDev()可以在 Vite 服务器仍处于createServer()或listen()阶段时调用:先等待启动完成,再关闭实际资源。afterStart钩子可以主动调用stopDev(),不会因为相互等待形成死锁;已被关闭的服务器不再报告就绪。- 销毁期间迟到的
createServer()/preview()返回值会被释放,不再继续对外提供监听服务。 - 如果某项资源首次关闭失败,会保留内部错误事件监听器,支持后续再次调用
dispose()重试。
node scripts/smoke-v24.mjs # 专项并发测试(不依赖 Vite 安装)
npm run test:smoke-source # V1–V24 离线回归
npm run test:source-syntax # TypeScript 语法解析,不等于类型检查
完整工程的 typecheck、Vitest 及真实 Vite 构建仍须恢复工作区 .ldesign、src/types 与依赖后执行。详见 TWENTY_FOURTH_ROUND_SOURCE_AUDIT.zh-CN.md。
V25:开发服务器重启并发控制
兼容入口 ViteLauncher 的手动重启和配置热更新现在共用同一个有界重启调度器。同一时间至多执行一个关闭/启动流程,并只保存最新一份待应用配置;多次同时发起的手动 restartDev() 会等待当前重启,而不是重复关闭或绑定端口。
await launcher.restartDev() // 对当前配置执行一次手动重启
await launcher.restartDevWithConfig(nextConfig) // 应用新配置并串行重启
- 配置发生高频变化时,中间未执行的配置版本会合并,最终应用最新版本;不要依赖每个中间配置都触发完整服务器重启。
dispose()会等待重启中的操作,并阻止销毁后再次监听端口。- 开发启动不再为打印日志而无条件加载
appConfig模块;仍由已存在的内置插件装配器按需加载。 - 离线回归:
npm run test:smoke-source(包含smoke-v25.mjs)。源码语法:npm run test:source-syntax。
本轮验证不包括真实 Vite Socket/HMR 集成或生产构建性能基准。参见 TWENTY_FIFTH_ROUND_SOURCE_AUDIT.zh-CN.md。
可重复的实际构建性能基准(维护工具)
仓库内提供无第三方运行依赖的 scripts/benchmark-build.mjs,用于测试指定项目自己的构建命令,而不是用模拟产物代替真实构建。
# 在待测项目目录运行;将脚本路径调整为 Launcher 源码所在位置。
node /path/to/launcher/scripts/benchmark-build.mjs \
--runs 5 --warmups 1 --sample-interval-ms 200 \
--output .launcher/reports/baseline.json \
-- pnpm exec launcher build
# 修改 Launcher 后,在同一项目中使用完全相同的命令对比:
node /path/to/launcher/scripts/benchmark-build.mjs \
--runs 5 --warmups 1 --baseline .launcher/reports/baseline.json \
--max-regression-percent 10 \
--output .launcher/reports/candidate.json \
-- pnpm exec launcher build
也可通过 pnpm run benchmark:build -- --runs 5 -- node build.mjs 启动。每次运行顺序执行,失败即停止,支持 --timeout-ms 和 --max-median-ms 性能预算;预算失败时以非零状态退出,并保留 JSON 报告。输出包含中位数、P95、均值、成功样本数以及成功运行的采样 RSS。
统计限制:预热后连续构建是“热缓存”场景,不代表独立冷启动;系统负载、依赖和项目内容应保持一致。Linux 下内存项是定期采集的子进程树 RSS 求和,共享页可能重复计算,不能与独占的物理内存占用划等号;非 Linux 平台当前不提供内存值。CI 性能预算应在固定执行环境下使用。
Watch 模式的增量构建和后置 Hook 一旦出现失败,本次会话退出码及 --profileJson 状态标记为失败;用户主动退出造成的未完成增量构建不自动视为构建错误。
发布前先运行 pnpm run test:smoke-source、pnpm run verify:readiness,再在依赖和工作区齐全时完成 pnpm run typecheck、pnpm run test:run 及真实构建和框架集成测试。离线语法与行为测试不能替代生产构建验收。
Related Packages
Construction et rechargement à chaud des frontends de chaque module Nodefony — V...
Version History
| Version | Released | Vue | License |
|---|---|---|---|
| 0.1.0 | 3.5.32 | MIT | |
| 0.0.1 | 3.5.39 | MIT |