news 2026/9/13 14:29:49

NocoBase nb scaffold migration 命令深度解析:从脚手架生成到插件迁移脚本原理

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
NocoBase nb scaffold migration 命令深度解析:从脚手架生成到插件迁移脚本原理

NocoBase nb scaffold migration 命令深度解析:从脚手架生成到插件迁移脚本原理

【免费下载链接】nocobaseNocoBase is an open-source AI + no-code platform for building business systems fast. Instead of generating everything from scratch, AI works on top of production-proven infrastructure and a WYSIWYG no-code interface, so you get both speed and reliability.项目地址: https://gitcode.com/GitHub_Trending/no/nocobase

nb scaffold migration是 NocoBase CLI(nb命令)中用于快速生成插件数据库迁移(migration)脚本的脚手架命令。本文基于命令参考文档 migration.md 展开,并结合当前仓库中 CLI 的实际源码实现,完整覆盖命令的用法、参数约束、底层执行链路,以及生成的迁移脚本应遵循的up/down结构与插件生命周期挂载时机(beforeLoad/afterSync/afterLoad)。读完后你可以:直接复制命令生成迁移文件、理解 CLI 参数校验与委托执行的实现细节、并掌握在插件中正确挂载迁移的时机选择。

1. 命令用途与快速上手

nb scaffold migration的核心职责只有一件事:生成插件迁移脚本文件Generate a plugin migration file)。当你为一个 NocoBase 插件新增数据表、调整字段结构或初始化种子数据时,迁移脚本是标准做法——它让数据库变更以可编程、可回滚(down方法)的方式随插件加载执行。

标准用法:

nb scaffold migration <name> --pkg <pkg> [flags]

文档中的两个典型示例(与命令源码中内置的 examples 完全一致):

nb scaffold migration migration-name --pkg @nocobase/plugin-acl nb scaffold migration migration-name --pkg @nocobase/plugin-acl --on afterLoad

参数说明

参数类型说明
<name>string迁移脚本名称,必填(位置参数)
--pkgstring所属插件包名,必填
--onstring执行时机:beforeLoadafterSyncafterLoad,可选,且仅限这三个取值

需要强调的是,--name--pkg都是硬性约束:<name>为必填的位置参数,--pkg为必填 flag,缺省会直接触发 oclif 的参数校验失败;而--on是枚举型 flag,传入beforeLoad/afterSync/afterLoad之外的值同样会被拒绝。

2. 命令的源码实现:一个基于 oclif 的薄封装

该命令的实际实现位于 scaffold/migration.ts,它是一个标准的 oclifCommand子类,源码清晰地展示了参数定义与文档的一致性:

export default class ScaffoldMigration extends Command { static override args = { name: Args.string({description: 'migration name', required: true}), } static override description = 'Generate a plugin migration file.'; static override flags = { pkg: Flags.string({description: 'plugin package name', required: true}), on: Flags.string({description: 'on', required: false, options: ['beforeLoad', 'afterSync', 'afterLoad']}), } public async run(): Promise<void> { const { args, flags } = await this.parse(ScaffoldMigration) const npmArgs = ['create-migration', args.name, '--pkg', flags.pkg]; if (flags.on) { npmArgs.push('--on', flags.on); } try { await runNocoBaseCommand(npmArgs, { env: { LOGGER_SILENT: 'true' } }); } catch (error: unknown) { const message = error instanceof Error ? error.message : String(error); this.error(message); } } }

从源码结构可以看出三个关键实现点:

  1. 参数定义即文档Args.string({required: true})对应必填的<name>Flags.string({required: true})对应必填的--pkg--on通过options数组实现白名单校验,即文档中beforeLoad | afterSync | afterLoad三选一的来源。
  2. 错误处理:任何执行异常都会被捕获并通过this.error(message)抛出,因此命令失败时你看到的错误信息会直接来自底层执行过程。
  3. 无本地模板:该命令自身不渲染任何模板文件,真正的文件生成逻辑发生在它委托调用的底层命令中(见下一节)。

3. 执行链路:委托给 nocobase-v1 的 create-migration

run()方法将参数组装为['create-migration', args.name, '--pkg', flags.pkg, ...],然后调用 run-npm.ts 中的runNocoBaseCommand。从 runNocoBaseCommand 的实现 看,这条委托链路有三个值得注意的行为:

export function runNocoBaseCommand(args: string[], options?: Omit<RunProcessOptions, 'errorName'>): Promise<void> { const cwd = resolveProjectCwd(options?.cwd); const localBin = path.join(cwd, 'node_modules', '.bin'); return run('nocobase-v1', [...args], { ...options, cwd, errorName: 'nocobase command', env: { PATH: `${localBin}${path.delimiter}${process.env.PATH ?? ''}`, ...options?.env, }, }); }
  • 定位项目目录resolveProjectCwd会从当前目录(或--cwd指定目录)逐级向上查找node_modules/.bin/nocobase-v1二进制(见 resolveProjectCwd),找到才将其作为子进程工作目录。这意味着:该命令必须在安装了 NocoBase 的项目目录下执行,否则找不到目标工程会报错Couldn't find a NocoBase source project
  • PATH 注入:子进程的PATH前缀注入了node_modules/.bin,保证nocobase-v1能被正确解析。
  • 静默日志nb scaffold migration调用时额外注入环境变量LOGGER_SILENT: 'true',用于压制底层命令的冗余日志输出。

因此完整的执行链条为:

nb scaffold migration <name> --pkg <pkg> [--on <timing>] → ScaffoldMigration.run() (oclif 参数解析与校验) → runNocoBaseCommand(...) (定位 NocoBase 项目、注入 PATH) → spawn nocobase-v1 create-migration <name> --pkg <pkg> [--on <timing>]

nb scaffold migration本质上是 v1 CLI(cli-v1 包)中create-migration命令的面向开发者封装。

4. 迁移执行时机:beforeLoad / afterSync / afterLoad

--on参数决定迁移在插件生命周期中的挂载时机,三个取值分别对应插件加载流程中的不同阶段:

取值含义(结合插件加载流程)
beforeLoad插件load之前执行。适合在插件主体逻辑(如注册模型/资源)之前就需要数据库结构就绪的场景
afterSync数据库同步(schema sync)完成后执行。适合依赖其他插件/核心模型已同步完成、再补充额外结构的场景
afterLoad插件load完成之后执行。适合不阻塞插件加载、可延后执行的变更

由于文档未逐项展开语义细节,以上阶段划分是结合插件加载流程的通行约定推断出的;实际项目中建议以所用插件server/index.ts中迁移的注册位置为准。如果你不指定--on,则使用底层create-migration的默认行为。

5. 生成的迁移脚本长什么样:参考示例与 API

生成的脚本遵循 NocoBase 的标准Migration结构。仓库中提供了一个可直接运行的完整示例 add-migration.ts,其核心结构如下:

import { DataTypes } from '@nocobase/database'; import { Application, Migration } from '@nocobase/server'; class MyMigration extends Migration { async up() { /* 可用的属性 this.app; this.db; this.queryInterface; this.sequelize; */ await this.queryInterface.createTable('test', { name: DataTypes.STRING, }); } async down() { await this.queryInterface.dropTable('test'); } } app.db.addMigration({ name: 'my-migration', migration: MyMigration, });

从该示例可确认迁移脚本的关键要素:

  • 继承自@nocobase/server导出的Migration类,实现up()down()两个异步方法;
  • this上下文中可用this.appthis.dbthis.queryInterfacethis.sequelize四个属性,其中queryInterface是操作表结构的常用入口(建表、删表、加字段等);
  • 通过app.db.addMigration({ name, migration })将迁移注册到应用数据库管理器;
  • 数据表结构定义复用@nocobase/database(Sequelize)的DataTypes

示例文件头部注释还给出了该示例的运行方式(yarn run:example app/migrations/add-migration migrator up/migrator down),可作为理解迁移如何被驱动的参考。对于插件场景,nb scaffold migration生成的文件会放入--pkg指定插件的迁移目录,并在插件加载流程中按--on指定的时机被调度执行。

6. 实操建议与相关命令

  • 先建插件再生成迁移nb scaffold migration依赖--pkg指向的插件包,若插件尚不存在,可先用 nb scaffold plugin 命令生成插件脚手架。对照其源码 scaffold/plugin.ts,该命令同样是对底层pm create <pkg>的封装,并支持--force-recreate强制重建。
  • 在正确的目录下执行:务必在安装了 NocoBase、且node_modules/.bin下存在nocobase-v1的项目根目录(或其子目录)中执行命令,否则resolveProjectCwd的向上查找会失败。
  • 明确迁移时机:为--on选择时机时,考虑迁移对其他插件已同步模型的依赖关系——依赖外部结构就用afterSync,希望尽早建表就用beforeLoad
  • 生成后务必复核:脚手架生成的只是骨架,up/down的具体 DDL 需要按插件需求补全,并确保downup可互逆,以便安全回滚。

相关文档可进一步阅读:插件开发 与命令参考 nb scaffold plugin。

【免费下载链接】nocobaseNocoBase is an open-source AI + no-code platform for building business systems fast. Instead of generating everything from scratch, AI works on top of production-proven infrastructure and a WYSIWYG no-code interface, so you get both speed and reliability.项目地址: https://gitcode.com/GitHub_Trending/no/nocobase

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

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

ESP32开发环境搭建:WSL2+ESP-IDF+Clangd协同配置指南

1. 为什么ESP32环境搭建总卡在“第一步”&#xff1f;——不是工具链问题&#xff0c;是认知断层你是不是也经历过&#xff1a;下载完ESP-IDF&#xff0c;执行install.bat后满屏红色报错&#xff1b;VS Code里点编译&#xff0c;提示idf.py: command not found&#xff1b;或者…

作者头像 李华
网站建设 2026/9/13 14:27:34

小爱音箱接入大模型完整教程:用 MiGPT 把它调教成专属语音助手

小爱音箱接入大模型完整教程&#xff1a;用 MiGPT 把它调教成专属语音助手 【免费下载链接】mi-gpt &#x1f3e0; 将小爱音箱接入 ChatGPT 和豆包&#xff0c;改造成你的专属语音助手。 项目地址: https://gitcode.com/GitHub_Trending/mi/mi-gpt 问小爱音箱"明天…

作者头像 李华
网站建设 2026/9/13 14:25:40

Cursor Plus 会员深度实测:20 美元月费到底值不值?

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/13 14:24:03

Go语言实现Raft共识算法:原理与实战优化

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华