- 开发工具
- CLI
【免费下载链接】hygen
The simple, fast, and scalable code generator that lives in your project.
本篇文章围绕 hygen 测试夹具index-js-existing-params揭示的核心能力展开:当模板动作目录中存在index.js钩子时,如何通过prompt与params两种函数形态接管参数处理,并让命令行传入的--email等已有参数直接注入模板、跳过交互提示。读完你将掌握 hygen 的参数解析优先级、三种钩子写法以及如何在 CI 或脚本中实现完全非交互的代码生成。
一、一个测试夹具揭示了什么
在 hygen 的测试套件中,expected/index-js-existing-params/new.md 这个"文档"内容只有一行:
premade-email@foobar.com它并不是一篇教程,而是一个期望输出文件:hygen 在端到端测试中执行hygen index-js-existing-params new --email premade-email@foobar.com之后,应当生成出恰好包含这行文本的目标文件。夹具虽短,却完整演示了 hygen 三条核心机制的组合:
- CLI 参数预填充(prefill)——
--email已经在命令行给出,交互式提示不再弹出; index.js钩子——动作目录中用index.js取代prompt.js,以函数式方式接管参数;- 参数直通模板——
email最终作为 EJS 局部变量渲染进文件正文。
对应的测试用例记录在 metaverse.spec.ts,两条命令分别验证了prompt钩子与params钩子两种写法,生成结果都指向同一个期望文件。
二、CLI 参数是如何变成模板变量的
要理解"预填充",先看参数从命令行到模板的完整链路。入口是 src/engine.ts,它声明了通用调用形式:
hygen [option] GENERATOR ACTION [--name NAME] [data-options]其中所有--xxx value形式的数据选项(data-options)最终都会成为模板可用的变量。参数装配发生在 src/params.ts:
const params = async ( { templates, createPrompter }: RunnerConfig, externalArgv: string[], ): Promise<ParamsResult> => { const argv = yargs(externalArgv) // 解析 CLI 参数 const [generator, action, name] = await resolvePositionals(templates, argv._) ... const { _, ...cleanArgv } = argv // 剔除位置参数 const promptArgs = await prompt(createPrompter, actionfolder, { ...(name ? { name } : {}), ...cleanArgv, // CLI 参数一并传给钩子 }) const args = Object.assign( { templates, actionfolder, generator, action, subaction, }, { _, ts: process.env.HYGEN_TS || new Date().getTime() }, cleanArgv, // 第 1 优先级:CLI 选项 name && { name }, // 第 2 优先级:位置参数 name promptArgs, // 第 3 优先级:钩子返回值 ) return args }这段代码体现了三个关键事实:
yargs负责解析,argv._存放位置参数(generator / action / name),cleanArgv是去掉位置参数后的命名选项;- 钩子函数拿到的
args中已经包含了所有 CLI 选项(...cleanArgv),这正是"预填充"的数据来源; - 最终模板上下文里,
email、name等键都可以通过<%= email %>直接访问——文档 templates.md 中的"Local Variables"一节也说明:任何 CLI 参数或提示参数都会自动成为模板局部变量。
三、三种钩子形态:数组、prompt 函数、params 函数
参数处理钩子存放在动作目录(action folder)下,由 src/prompt.ts 按固定顺序探测:
const hooksfiles = [ 'index.js', 'index.ts', 'prompt.cjs', 'prompt.js', 'prompt.ts', ]也就是说,index.js的优先级高于prompt.js。找到钩子文件后,prompt.ts依据导出内容走三条分支:
1. 导出数组(传统prompt.js)
module.exports = [ { type: 'input', name: 'message', message: "What's your message?" } ]这是官方文档 generators.md 中"Interactive Prompt"介绍的写法,格式基于 enquirer 的问题描述。prompt.ts会先过滤掉那些已经在 CLI 中给出的问题(见下方代码),再调用 prompter 交互:
return prompter.prompt( hooksModule.filter( (p) => args[p.name] === undefined || args[p.name] === null || args[p.name].length === 0, ), )这正是"CLI 预填充"的落点:问题里的name字段若与已有参数同名且值非空,就不会再问用户。测试夹具 cli-prefill-prompt-vars 验证了这一点——命令行给出--message-from-cli hello-from-cli后,名为message-from-cli的 input 问题被直接跳过;falsy-values-are-ok 则证明只要提供了--include_something false,即使值是false(字符串形式)也会被视为"已有",不再追问。
2. 导出prompt函数(高级钩子)
动作目录改用index.js并导出含prompt字段的对象,即可完全接管交互逻辑。本夹具使用的正是这种写法:
// index-js-existing-params/new/index.js module.exports = { prompt: ({ prompter, args }) => Promise.resolve(args) }prompt函数接收{ prompter, inquirer, args, h }四元结构,可以自由决定是否调用prompter。这里的实现直接返回 args,等价于"什么都不问,把命令行已有的参数原样交出去"——prompt.ts的分支代码为:
if (hooksModule.params) { return hooksModule.params({ args, h: helpers }) } ... if (hooksModule.prompt) { return hooksModule.prompt({ prompter, inquirer, args, h: helpers }) }注意params分支排在prompt之前,二者只需实现其一即可。文档 generators.md 的"Advanced Interactive Prompt"一节给出了更复杂的用法,例如两步确认式提问,或依据args.age条件性跳过提示:
module.exports = { prompt: ({ prompter, args }) => { if (args.age > 18) { return Promise.resolve({ allow: true }) // 跳过提问 } return prompter.prompt({ type: 'input', name: 'age', message: 'whats your age?' }) } }3. 导出params函数(参数整形)
本夹具的另一个动作目录 new-params-alias 演示了params写法:
module.exports = { params: ({ prompter, args }) => Promise.resolve(args) }params与prompt的目标一致——产出新参数,但前者专用于"整形/映射"而非交互。文档特别指出:向用户提问与重塑 CLI 参数殊途同归,hygen 为了面向未来将两种意图拆成了prompt和params两个函数。常见的整形示例:
module.exports = { params: ({ args }) => { return { moreConvenientName: args.foobamboozle } } }本夹具中两个动作目录分别只放了index.js与模板文件,to:指向不同的目标路径,但最终都输出同一行邮箱——证明了prompt与params两条路径在"参数直通模板"上的行为完全一致。
四、参数优先级:为什么 CLI 说了算
综合 params.ts 的Object.assign顺序与 prompt.ts 的过滤逻辑,可以得到完整的优先级结论:
| 优先级 | 参数来源 | 说明 |
|---|---|---|
| 1 | CLI 命名选项(--email xxx) | 最先写入,且让同名提示问题被跳过 |
| 2 | 位置参数NAME | 自动映射为name变量(hygen 4.0.0+ 支持) |
| 3 | 钩子返回对象 | prompt/params函数或数组提问的返回值 |
| 4 | 预定义变量 | templates、actionfolder、generator、action、subaction、cwd等自动注入 |
也就是说:即使钩子想要提问,只要命令行已经给出了同名参数,提问就会被过滤或短路;而钩子返回值虽然最后合并,却依然可以覆盖 CLI 值、补充新变量或做二次加工。模板正文通过 EJS 直接消费这些变量:
--- to: given/index-js-existing-params/new.md --- <%= email %>五、端到端测试如何证明"没有弹提示"
本夹具隶属于 metaverse 测试套件。看 metaverse-utils.ts 中那段容易被忽略的代码:
const failPrompt = () => { throw new Error('set up prompt in testing') }测试框架在每次命令执行前把enquirer.prompt替换成这个必然抛错的函数(metaverse.spec.ts)。这意味着:如果某条测试命令真的触发了交互式提问,测试会立刻因set up prompt in testing而失败。
对于index-js-existing-params new/new-params-alias两条用例,命令形如:
hygen index-js-existing-params new --email premade-email@foobar.com hygen index-js-existing-params new-params-alias --email premade-email@foobar.com它们不仅通过,而且dirCompare逐一对比了given/与expected/目录下的所有生成文件,最终new.md与new-params-alias.md均逐字节等于premade-email@foobar.com。由此从侧面确证:index.js钩子 + CLI 参数预填充的组合,在真实运行中确实一次提示都不弹,直接完成文件生成。
六、实战启示:把 hygen 变成脚本友好的生成器
结合上述机制,可以在自己的项目里复刻这套非交互流程:
- 在动作目录放置
index.js,优先于prompt.js被加载; - 用
prompt: ({ prompter, args }) => Promise.resolve(args)无条件跳过提问,或用params对args做默认值、别名等整形; - 调用时把所有变量以
--key value传齐,例如hygen mailer new --email a@b.com --name fancy; - 需要交互时保留
prompt.js数组或函数式prompt,此时已有 CLI 参数的同名问题会自动被过滤,形成"能传参就传参、缺什么问什么"的弹性体验; - 配合
--dry先试运行(见 engine.ts 的帮助文案),再批量落地。
这套模式特别适合脚手架、模板仓库与 CI 流水线:把每次生成所需的变量提前固化在命令里,即可让代码生成全程无人值守,同时保持模板团队内可复用、可参数化。
七、相关源码与文档索引
- 参数装配与优先级:src/params.ts
- 钩子探测与三分支逻辑:src/prompt.ts
- 命令入口与帮助文案:src/engine.ts
- 端到端测试用例:src/tests/metaverse.spec.ts、src/tests/metaverse-utils.ts
- 本夹具模板与钩子:index.ejs.t、new/index.js
- 交互式与高级提示官方指南:hygen.io/docs/generators.md
- 局部变量与模板渲染规则:hygen.io/docs/templates.md
- 开发工具
- CLI
【免费下载链接】hygen
The simple, fast, and scalable code generator that lives in your project.
相关推荐
hygen 交互式提问预填机制:如何用 CLI 参数直接注入 Prompt 变量
hygen 交互式提问预填机制:如何用 CLI 参数直接注入 Prompt 变量 导读 hygen 允许开发者为生成器定义 prompt.js 交互式提问,而当
开发工具CLIhygen 命令行参数预填交互式提示(Prompt Prefill)实战:从测试到源码深入解析
hygen 命令行参数预填交互式提示(Prompt Prefill)实战:从测试到源码深入解析 导读 本文聚焦于 hygen 代码生成器的一个高频且实用的能力—
开发工具CLIOrcaSlicer超省料攻略:填充参数优化让3D打印成本直降20%
OrcaSlicer超省料攻略:填充参数优化让3D打印成本直降20% 你还在为3D打印材料成本居高不下而困扰?明明简单的模型却消耗大量 filament( fi
桌面应用3D渲染图形学
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考