免费获取学习方案
ARTICLE DETAIL

资讯详情

深耕编程基础知识与建站技术分享的一线实战洞察。

Amplication Code-Gen-Types 库深度解析:插件化代码生成契约层

Amplication Code-Gen-Types 库深度解析:插件化代码生成契约层 Amplication Code-Gen-Types 库深度解析插件化代码生成契约层【免费下载链接】amplicationAmplication brings order to the chaos of large-scale software development by creating Golden Paths for developers - streamlined workflows that drive consistency, enable high-quality code practices, simplify onboarding, and accelerate standardized delivery across teams.项目地址: https://gitcode.com/GitHub_Trending/am/amplication导读amplication/code-gen-types是 Amplication 数据服务生成器DSG的契约定义库它为整个代码生成流水线提供全部 TypeScript 类型、事件与 Schema 契约并专为插件体系设计——任何第三方插件都可以通过引用这些契约接入生成流程。本文基于仓库中 libs/util/code-gen-types/README.md 展开结合该库的源码实现系统讲解该库在 Amplication 中的定位与角色、核心类型契约的组成DSG 上下文、插件事件、DTO、文件映射等、字段数据类型与 JSON Schema 的映射机制、构建产物与 npm 发布流程以及单元测试与质量门槛。读完本文你将掌握如何将该库作为依赖引入插件项目、如何理解并使用其提供的DsgContext、EventNames、FileMap等核心契约以及如何在 monorepo 中构建、测试和发布该 npm 包。一、库定位Amplication 代码生成流水线的契约层Amplication 的核心能力之一是数据服务生成器DSG——根据资源定义自动生成可运行的 Node.jsNestJS服务甚至支持 .NETC#与 Blueprint 等生成目标。为了让这一流水线可扩展Amplication 采用插件化架构默认生成行为由 DSG 内置逻辑完成而插件可以在特定事件Event前后介入、修改甚至完全替换默认行为。amplication/code-gen-types正是为支撑这一架构而存在This library supplies all the contracts for Amplication Code Generation. The purpose is to make the contracts available for inclusion in plugins.README也就是说这个库不实现任何生成逻辑它只提供类型与接口契约并使其可以被插件独立引用。从 package.json 可以看到包名amplication/code-gen-types、版本3.2.1依赖ast-types、json-schema、prisma-schema-dsl-types、type-fest等类型支撑库并将amplication/csharp-ast、amplication/ast-types声明为 peerDependencies供 .NET/Blueprint 生成目标使用。在 project.json 中该库被声明为projectType: library并在构建build目标中将libs/util/code-gen-types/*.md作为 assets 一并打入产物——因此 README 会随 npm 包一起分发。二、库的入口与导出面一窥契约全貌整个库的公共 API 由 src/index.ts 统一导出按命名空间组织export { getSchemaForDataType } from ./get-schema-for-data-type; export type { Schema } from ./get-schema-for-data-type; export * as types from ./types; export * from ./code-gen-types; export * from ./plugins.types; export * from ./plugin-events-params.types; export * from ./plugin-events.types; export * from ./dsg-resource-data; export * from ./build-logger; export * from ./files; export * as dotnetTypes from ./dotnet-plugins.types; export * as dotnetPluginEventsParams from ./dotnet-plugin-events-params.types; export * as dotnetPluginEventsTypes from ./dotnet-plugin-events.types; export * as blueprintTypes from ./blueprint-types/blueprint-plugins.types; export * as blueprintPluginEventsParams from ./blueprint-types/blueprint-plugin-events-params.types; export * as blueprintPluginEventsTypes from ./blueprint-types/blueprint-plugin-events.types;可以看出契约分三大目标体系组织命名空间面向生成目标说明顶层导出Node.js/NestJSTypeScript 服务端生成DSG 默认目标dotnet*C# / .NET基于amplication/csharp-ast的 AST 文件映射blueprint*Blueprint基于amplication/ast-types的通用 AST 文件映射此外types命名空间由src/types目录提供——该目录不是手写的而是构建前由脚本从 JSON Schema 自动生成详见第四节。三、核心契约详解3.1 DSGResourceData一次构建的输入快照dsg-resource-data.ts 定义了DSGResourceData类它描述了一次代码生成构建所需的全部资源数据资源类型、应用信息、构建 ID、实体、角色、插件安装列表、模块容器/动作/DTO、消息主题、以及其他资源数据export class DSGResourceData { resourceType!: keyof typeof EnumResourceType; resourceInfo?: AppInfo; buildId!: string; entities?: Entity[]; roles?: Role[]; pluginInstallations!: PluginInstallation[]; packages?: Package[]; moduleContainers?: ModuleContainer[]; moduleActions?: ModuleAction[]; moduleDtos?: ModuleDto[]; resourceSettings?: ResourceSettings; relations?: Relation[]; serviceTopics?: ServiceTopics[]; topics?: Topic[]; otherResources?: DSGResourceData[]; }其中AppInfo携带应用名称、描述、版本、URL 与ServiceSettingsEntity则展开为字段EntityField、权限EntityPermission与复数名称等生成所需的完整形态见 code-gen-types.ts。这些类型大量使用Omit从 Prisma 生成的模型models.ts共 4310 行中剔除__typename、createdAt、updatedAt等无关字段只保留生成逻辑真正关心的内容。3.2 DsgContext插件在事件间共享的运行上下文如果说DSGResourceData是构建输入那么 plugins.types.ts 中的DsgContext就是插件运行时共享的上下文对象——它继承DSGResourceData并追加生成过程中的动态数据export interface DsgContext extends DSGResourceData { modules: ModuleMap; // 已生成的文件集合 DTOs: DTOs; // 实体 DTO 类声明 plugins: PluginMap; // 已注册插件的事件回调表 logger: BuildLogger; // 面向用户的日志器会出现在构建日志中 utils: ContextUtil; // 跳过默认行为 / 中止生成 / 导入静态模块等工具 clientDirectories: clientDirectories; // 客户端目录结构 serverDirectories: serverDirectories; // 服务端目录结构 userEntityName: string; userNameFieldName: string; userPasswordFieldName: string; userRolesFieldName: string; entityActionsMap: EntityActionsMap; moduleActionsAndDtoMap: ModuleActionsAndDtosMap; }ContextUtil提供了三个关键能力skipDefaultBehavior: boolean——设为true时跳过 DSG 的默认生成行为abortGeneration(msg)/abort/abortMessage——主动中止整个生成流程importStaticModules(source, basePath)——从磁盘导入静态模板文件生成ModuleMap。这些语义在 DSG 侧的 plugin-wrapper.ts 中有完整实现插件事件以before→ 默认行为 →after的管道顺序执行若skipDefaultBehavior为真则默认行为返回空ModuleMap事件抛错时若utils.abort为真则会以友好消息中止构建。3.3 插件事件体系EventNames、PluginEventType 与 Events插件机制的核心是事件。EventNames枚举plugins.types.ts定义了所有可挂钩的生成步骤例如CreateServer/CreateServerAuth/CreateServerAppModuleCreateEntityService/CreateEntityController/CreateEntityResolver及其*Base变体CreatePrismaSchema/CreateDTOsCreateAdminUI/CreateAdminAppModuleCreateMessageBroker系列TopicsEnum、NestJSModule、ClientOptionsFactory、ServiceCreateServerDockerCompose/CreateServerDockerComposeDB/CreateServerDockerComposeDevCreateServerPackageJson/CreateServerSecretsManager用于向 SecretsKeyNames 枚举追加密钥引用LoadStaticFiles每个事件对应一组参数类型定义在 plugin-events-params.types.ts374 行。例如创建 Service Base 时插件可拿到export interface CreateEntityServiceBaseParams extends EventParams { entityName: string; entity: Entity; templateMapping: { [key: string]: any }; passwordFields: EntityField[]; serviceId: namedTypes.Identifier; serviceBaseId: namedTypes.Identifier; delegateId: namedTypes.Identifier; template: namedTypes.File; moduleContainers: ModuleContainer[]; entityActions: entityActions; dtoNameToPath: Recordstring, string; }事件本身被抽象为before/after两个钩子plugins.types.tsexport type PluginBeforeEventT extends EventParams ( dsgContext: DsgContext, eventParams: T ) PromisableT; export type PluginAfterEventT extends EventParams ( dsgContext: DsgContext, eventParams: T, modules: ModuleMap ) PromisableModuleMap;before接收事件参数返回可能被修改过的参数供默认行为消费after接收事件参数与默认行为产出的ModuleMap返回最终可能被替换/增删过的ModuleMap。Events类型plugin-events.types.ts把所有事件名映射到对应的PluginEventTypeParams构成插件register()方法的返回类型约束export interface AmplicationPlugin { init?: (name: string, version: string) void; register: () Events; }结合 DSG 的 plugin-wrapper.ts 可以看到实际执行逻辑beforeEventsPipe将所有 before 回调以 Promise 链串行 reduceafterEventsPipe同理作用于ModuleMap最后把每个模块通过context.modules.replace()回写上下文保证后续事件能读到前面事件的全部产物。3.4 FileMap / ModuleMap生成文件的容器生成产物以路径 → 内容的映射形式在事件间流动。底层文件接口定义在 files/file.types.tsexport interface IFileT extends any | string | Buffer { path: string; code: T; }files/file-map.ts 中的FileMapT是对Mapstring, IFileT的封装提供merge/mergeMany合并其他文件映射set写入文件若路径已存在会通过logger.warn打印覆盖警告get/replace/removeMany查询、替换、批量删除replaceFilesPath/replaceFilesCode用回调函数批量改写路径或内容。ModuleMap是FileMapstring的遗留别名在 code-gen-types.ts 中标记为deprecated保留replaceModulesPath、replaceModulesCode、modules()等旧方法并内部委托给 FileMap 实现。FileMap的行为在 files/file-map.spec.ts 与 code-gen-types.spec.ts 中均有覆盖例如set覆盖已有文件时会调用logger.warn(File path already exists. Overwriting...)。3.5 BuildLogger构建日志契约插件需要向用户输出信息build-logger.ts 定义了BuildLogger接口统一info/warn/error三个方法签名一致info: ( message: string, params?: Recordstring, unknown, userFriendlyMessage?: string ) Promisevoid;值得注意params是应用内部日志参数不会显示在构建日志中而userFriendlyMessage是面向用户的日志消息会显示在构建日志中默认等于message。error额外接受一个error对象参数。DSG 在插件事件执行失败时正是通过context.logger.error(friendlyErrorMessage, { event }, friendlyErrorMessage, error)记录错误见 plugin-wrapper.ts。3.6 ModuleAction / ModuleDto声明式模块契约为支持自定义模块Custom Module这类高级能力库中还定义了声明式模块契约ModuleAction描述一个动作REST 动词、GraphQL 操作、输入/输出类型、restInputSource等ModuleDto与ModuleDtoProperty描述 DTO 及其属性类型ModuleContainer描述模块容器entityActions/entityDefaultDtos/ModuleActionsAndDtosMap等类型则把默认动作、默认 DTO 与自定义动作组织在一起code-gen-types.ts。配合EnumModuleActionType、EnumModuleDtoType、EnumModuleDtoDecoratorTypeObjectType/InputType/ArgsType等枚举DSG 可以按声明生成 REST/GraphQL 接口层。四、数据类型的 JSON Schema从 Schema 到 TypeScript 的自动生成4.1 20 个字段数据类型的 SchemaEntityField的properties会随dataType变化库用 JSON Schema 精确描述每种类型的合法属性。src/schemas/目录下存放了 20 个 JSON Schema 文件见 schemas/index.ts与 models.ts 中EnumDataType的 20 个枚举值一一对应Boolean, CreatedAt, DateTime, DecimalNumber, Email, File, GeographicLocation, Id, Json, Lookup, MultiLineText, MultiSelectOptionSet, OptionSet, Password, Roles, SingleLineText, UpdatedAt, Username, WholeNumberdata-type-to-schema.ts中的DATA_TYPE_TO_SCHEMA映射表把每个枚举值关联到对应 Schema其中WholeNumber、DecimalNumber、Lookup等类型带有todo注释提示与minimumValue、精度校验、实体选择校验相关的已知待完善点。以singleLineText.json为例{ $id: https://amplication.com/schema/entityfield/properties/singleLineText.json, $schema: http://json-schema.org/draft-07/schema#, title: singleLineText, type: object, required: [maxLength], additionalProperties: false, properties: { maxLength: { type: integer, description: The maximum length of the field, minimum: 1, default: 256 } } }几个典型 Schema 的核心约束wholeNumber.json必填minimumValue/maximumValue默认0/99999999999可选databaseFieldType枚举INT/BIG_INT默认INToptionSet.json必填options数组minItems: 1、uniqueItems: true每项必含label与value且value必须匹配正则^(?![0-9])[a-zA-Z0-9$_]$不能以数字开头lookup.json必填relatedEntityId、relatedFieldId、allowMultipleSelection并支持fkHolder、fkFieldName外键持有方与可选的外键字段名等关系字段。4.2 运行时查询getSchemaForDataTypeget-schema-for-data-type.ts 提供对映射表的统一访问入口export function getSchemaForDataType(dataType: models.EnumDataType): Schema { return DATA_TYPE_TO_SCHEMA[dataType]; }Schema即JSONSchema7类型。对应测试 get-schema-for-data-type.spec.ts 用test.each遍历EnumDataType全部枚举值断言每个类型都能取到定义——这保证了每个数据类型的属性 Schema 完整注册这一不变量。4.3 prebuild 脚本Schema 一键生成 TypeScript 类型src/types目录即index.ts中export * as types from ./types指向的目录不是手写维护的。在 project.json 的prebuild目标中prebuild: { executor: nx:run-commands, outputs: [{projectRoot}/src/types], options: { command: ts-node -P tsconfig.lib.json ./scripts/generate-types, cwd: libs/util/code-gen-types } }即每次构建前先执行 scripts/generate-types.ts用fast-glob扫描src/schemas/**/*.json借助json-schema-to-typescript的compileFromFile把每个 JSON Schema 编译为同名.ts文件写入src/types并自动生成聚合导出的index.ts。build目标dependsOn: [prebuild]test目标同样依赖prebuild因此本地开发时类型总是与 Schema 保持同步。五、构建、测试、发布在 monorepo 中的工作流5.1 单元测试与代码检查按 README 的说明在该 Nx monorepo 根目录下执行# 运行单元测试基于 Jest nx test amplication-code-gen-types # 运行代码检查基于 ESLint nx lint amplication-code-gen-types测试配置见 jest.config.ts使用ts-jest、testEnvironment: node并设置了严格的质量门槛——全局分支覆盖率不低于 92%、行覆盖率不低于 95%collectCoverageFrom排除了类型文件、scripts 与 spec 文件本身。project.json中test目标还配置了passWithNoTests: true并dependsOn: [prebuild]。5.2 发布到 npm发布amplication/code-gen-types的完整流程摘自 README版本号以仓库当前 package.json 的3.2.1为准更新package.json中的版本号在 monorepo 根目录执行# 从 monorepo 根目录 npm i npx nx build code-gen-types cd ./dist/libs/util/code-gen-types构建产物输出到dist/libs/util/code-gen-types对应 project.json 的outputPath并通过nx/js:tsc生成声明文件README 等.md资产也会一并复制到产物目录。发布为beta预发布版本npm publish --access public --tag beta发布为latest正式版本npm publish --access public--access public用于公开包的作用域发布。发布前请确认package.json中的 peerDependenciesamplication/csharp-ast、amplication/ast-types能被消费方正确安装因为 .NET 与 Blueprint 契约都依赖它们。六、如何在自己的插件中使用这些契约结合 packages/data-service-generator 的消费方式例如 plugin-wrapper.ts 中import { EventNames, EventParams, PluginAfterEvent, PluginBeforeEvent, ModuleMap } from amplication/code-gen-types一个典型插件可以这样接入import { AmplicationPlugin, Events, CreateServerParams, CreatePrismaSchemaParams, EventNames, DsgContext, ModuleMap, } from amplication/code-gen-types; class MyPlugin implements AmplicationPlugin { init?(name: string, version: string) { // 插件初始化可选 } register(): Events { return { [EventNames.CreateServer]: { before: async (context: DsgContext, eventParams: CreateServerParams) { // 在默认生成前调整事件参数 return eventParams; }, }, [EventNames.CreatePrismaSchema]: { after: async ( context: DsgContext, eventParams: CreatePrismaSchemaParams, modules: ModuleMap ) { // 在默认生成后增删/改写文件 return modules; }, }, }; } }关键要点事件参数类型CreateServerParams等全部来自 plugin-events-params.types.tsbefore钩子通过返回修改后的参数影响默认行为after钩子通过返回ModuleMap决定最终产物若要在after中完全接管可把context.utils.skipDefaultBehavior置为true此时默认行为返回空ModuleMap由插件全权产出文件语义定义见 plugins.types.ts 的ContextUtil实现见 plugin-wrapper.ts使用context.logger.info/warn/error输出会显示在构建日志中的用户可读信息build-logger.ts。七、扩展契约.NET 与 Blueprint 生成目标除默认的 Node.js/NestJS 生成目标外该库还通过命名空间导出为两种生成目标提供独立契约dotnet 系列dotnet-plugins.types.ts、dotnet-plugin-events.types.ts、dotnet-plugin-events-params.types.ts面向 C# 生成PluginAfterEvent操作的是FileMapAstNode节点来自amplication/csharp-astDsgContext中相应为files: FileMapAstNodeblueprint 系列blueprint-types/面向 Blueprint 生成目标同样基于FileMapIAstNode且ContextUtil额外提供importStaticFilesWithReplacements、replacePlaceholders、replaceText等模板替换工具方便从静态文件生成代码时做占位符与文本替换。这些契约的存在说明amplication/code-gen-types的插件事件模型是多生成目标复用的同一套 before/after 语义文件载体从字符串代码FileMapstring泛化为任意 AST 节点FileMapAstNode/FileMapIAstNodeBuildLogger、DSGResourceData等基础契约则完全共享。八、总结与延伸阅读amplication/code-gen-types是 Amplication 插件生态的地基它以纯类型库的形式承载了 DSG 的输入模型DSGResourceData、运行时上下文DsgContext、事件体系EventNames/Events/PluginEventType、产物容器FileMap/ModuleMap与字段类型 Schema并通过 prebuild 脚本保证 JSON Schema 与 TypeScript 类型永不脱节。理解这份契约就等于理解了 Amplication 插件在哪里挂钩、能拿到什么、能改什么的全部可能性。进一步探索建议插件事件在 DSG 中的实际调度实现packages/data-service-generator/src/plugin-wrapper.ts生成器如何消费DSGResourceData与上下文packages/data-service-generator/src/dsg-context.ts字段类型 Schema 与实体模型的定义libs/util/code-gen-types/src/schemas、libs/util/code-gen-types/src/models.tsFileMap 行为验证libs/util/code-gen-types/src/files/file-map.spec.tsSchema 生成脚本libs/util/code-gen-types/scripts/generate-types.ts。【免费下载链接】amplicationAmplication brings order to the chaos of large-scale software development by creating Golden Paths for developers - streamlined workflows that drive consistency, enable high-quality code practices, simplify onboarding, and accelerate standardized delivery across teams.项目地址: https://gitcode.com/GitHub_Trending/am/amplication创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表