news 2026/10/13 15:58:21

hygen 参数预填充与 index.js 钩子:让 CLI 参数绕过提示直达模板

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
hygen 参数预填充与 index.js 钩子:让 CLI 参数绕过提示直达模板
  • 开发工具
  • CLI

【免费下载链接】hygen

The simple, fast, and scalable code generator that lives in your project.

项目地址:https://gitcode.com/gh_mirrors/hy/hygen
点击查看免费下载

本篇文章围绕 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 三条核心机制的组合:

  1. CLI 参数预填充(prefill)——--email已经在命令行给出,交互式提示不再弹出;
  2. index.js钩子——动作目录中用index.js取代prompt.js,以函数式方式接管参数;
  3. 参数直通模板——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 的过滤逻辑,可以得到完整的优先级结论:

优先级参数来源说明
1CLI 命名选项(--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 变成脚本友好的生成器

结合上述机制,可以在自己的项目里复刻这套非交互流程:

  1. 在动作目录放置index.js,优先于prompt.js被加载;
  2. 用prompt: ({ prompter, args }) => Promise.resolve(args)无条件跳过提问,或用params对args做默认值、别名等整形;
  3. 调用时把所有变量以--key value传齐,例如hygen mailer new --email a@b.com --name fancy;
  4. 需要交互时保留prompt.js数组或函数式prompt,此时已有 CLI 参数的同名问题会自动被过滤,形成"能传参就传参、缺什么问什么"的弹性体验;
  5. 配合--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.

项目地址:https://gitcode.com/gh_mirrors/hy/hygen
点击查看免费下载

相关推荐

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/13 15:56:09

TypeScript类型编程入门:从Pick实现看keyof、泛型约束与映射类型

type-challenges 这个项目&#xff0c;我陆陆续续刷过好几轮。说实话&#xff0c;前面十几道基础题里&#xff0c;第 4 题“实现 Pick”是最值得停下来认真琢磨的一道。它表面上只让你做一个MyPick<T, K>&#xff0c;实际上把 TypeScript 类型系统里最常用的四块积木全串…

作者头像 李华
网站建设 2026/10/13 15:55:04

C语言数组与指针核心解析:从内存模型到工程实践

数组这堂课&#xff0c;几乎每个学C语言的人都逃不掉。可我发现一个有意思的现象&#xff1a;很多人学到指针之前&#xff0c;觉得数组简单得不行&#xff0c;不就是“一堆同类型数据排排坐”嘛&#xff1b;但一学到指针&#xff0c;再回头碰数组&#xff0c;突然又觉得哪哪都对…

作者头像 李华
网站建设 2026/10/13 15:51:11

OpenClaw高危漏洞预警:云主机Agent网关的安全加固实战指南

最近安全圈子里陆续在传一个消息&#xff1a;OpenClaw出现高危漏洞预警。如果你正好把OpenClaw这类Agent网关部署在优刻得云主机上&#xff0c;我建议先别急着翻补丁文档&#xff0c;当务之急是把资产暴露面收住&#xff0c;再做系统加固。这篇文章就从OpenClaw为什么容易被盯上…

作者头像 李华
网站建设 2026/10/13 15:51:08

litellm:统一多模型API的LLM网关实践与部署指南

做后端一年多了&#xff0c;从最早接各种大模型 API 一个个写 SDK 封装&#xff0c;到后来维护一套又一套接口代理&#xff0c;我算是被这类“重复造轮子”的活折磨过不少次。如果你最近也在折腾 LLM 应用&#xff0c;大概率听过一个叫litellm的名字。这东西最早是作为“把所有…

作者头像 李华
网站建设 2026/10/13 15:50:28

中国1km土地利用数据1980-2015:长时序分析实战与避坑指南

简介&#xff1a;这份资源是中国1km土地利用遥感监测数据集&#xff0c;覆盖1980至2015年间共45个年份&#xff0c;面向地理学、环境科学、城乡规划及经济学等领域的研究者与政策分析人员&#xff0c;用于探究近四十年土地利用变迁、城市扩张、生态保护与农业动态等议题。压缩包…

作者头像 李华
网站建设 2026/10/13 15:49:47

你用AI的方式,决定了你能拿走多少:从提示词工程到人机协作实战

你用AI的方式&#xff0c;决定了你能拿走多少最近跟几个做业务的朋友聊天&#xff0c;发现一个挺有意思的现象&#xff1a;同样的AI工具&#xff0c;有人用它把方案撰写时间从两天压缩到两小时&#xff0c;有人却还在抱怨AI写出来的东西“不能直接用”。这两拨人用的模型一样、…

作者头像 李华