管理属性)
JHipster RFC-5 解读在生成器中用包含式语法Inclusive Syntax管理属性【免费下载链接】generator-jhipsterJHipster is a development platform to quickly generate, develop, deploy modern web applications microservice architectures.项目地址: https://gitcode.com/gh_mirrors/ge/generator-jhipster导读JHipster 的生成器内部同时存在排他式exclusive与包含式inclusive两种选项风格前者如skipClient、skipServer后者如dto A、service A with serviceImpl。混用两种风格会导致生成器内大量重复的业务逻辑与认知负担。RFC-5JHipster-RFC-5定义了生成器内部选项数据模型的标准结构所有选项一律采用包含式语法——未定义时取合理默认值已定义时遵循用户决策。本文完整翻译并深度解读该 RFC同时结合本仓库源码lib/jhipster/application-options.ts、lib/jdl/core/built-in-options/unary-options.ts、generators/base-application/internal/utils.ts等印证其设计动机与落地路径帮助读者理解 JHipster 生成器配置模型的演进方向。文档定位与基本信息RFC-5 是 JHipster 技术决策文档RFC 系列中的一份设计提案核心元信息如下Feature NameInclusive syntax to manage properties in generatorStart DateV8即面向 JHipster V8 版本规划关联 Issuejhipster/generator-jhipster#14416该 RFC 的目标是定义生成器内部选项数据模型的标准结构。需要特别说明的是该标准并不强制要求 JDL 或 CLI 层立即全面改造——文档原文明确指出not necessary the jdl or cli, which should only be encouraged for new ones即 JDL 与 CLI 只需对新增选项鼓励采用新语法存量选项可以渐进迁移。动机Motivation排他语法与包含语法混用的代价当前 JHipster 的选项风格不统一部分选项使用排他语法exclusive syntax例如skipClient跳过客户端部分选项使用包含语法inclusive syntax例如dto A为实体 A 生成 DTO。两种风格混用会带来三个层面的问题额外的业务逻辑生成器必须同时处理跳过某物和启用某物两种语义分支判断大量重复认知负担代码中容易出现双重否定例如if (!skipClient) {}阅读时难以一眼理解逻辑框架能力受限排他语法天然不利于表达按需启用能力这类正向诉求限制了生成器可扩展的方向。从本仓库源码可以印证上述成本。在 lib/jhipster/application-options.ts 中排他选项被集中登记SKIP_CLIENT: skipClient, SKIP_SERVER: skipServer, SKIP_USER_MANAGEMENT: skipUserManagement,而 lib/jdl/core/built-in-options/unary-options.ts 将skipClient、skipServer与noFluentMethod、readOnly、filter、embedded一起定义为一元选项unary options。一元选项的语义是选项名出现即生效这种出现即否定/即启用的设计正是排他语法模糊性的来源之一。此外在 lib/jdl/core/built-in-options/tokens/application-tokens.ts 中可以看到skipClient与skipServer为了同时兼容应用配置项与实体选项两种场景被迫同时被归类为KEYWORD和UNARY_OPTION// This is actually needed as the skipClient skipServer options are both entity app options... if ([SKIP_CLIENT, SKIP_SERVER].includes(tokenConfig.name)) { tokenConfig.categories.push(KEYWORD, UNARY_OPTION); }这种一个选项承担多重身份的权宜之计正是 RFC-5 试图根治的复杂度。指南级说明Guide-level explanation两条核心规则RFC-5 提出的方案非常简洁选项应当始终是包含式的并遵循两条规则如果最终用户没有定义该选项生成器采用一个合理的默认值reasonable default如果最终用户指定了该选项生成器尊重最终用户的决策。规则示例以skipClient迁移为generateClient为例RFC 原文用一个具体例子说明如何将排他的skipClient迁移为符合规范的包含式选项一个没有声明任何generateClient属性的 JDLapplication将正常生成该应用的前端采用合理默认值一个声明了generateClient属性的 JDLapplication则遵循用户选择——若设置了generateClient: false则不生成前端。这一语义与当前仓库中默认值来源于派生计算的实现思路一脉相承。在 generators/base-application/internal/utils.ts 中可以看到类似逻辑skipClient: application.clientFrameworkReact || application.clientFrameworkVue,即当客户端框架是 React 或 Vue 时skipClient会被派生为true——这正是用户未显式定义时取合理默认思想的现网形态此处默认值与所选框架绑定。同文件 L226 的另一处派生为skipClient: !application.clientFrameworkAngular,可以看到为了计算同一个skipClient生成器内部需要在多处根据框架类型做反向推导这正是排他语法造成的认知负担与重复逻辑的实证。参考级说明Reference-level explanation配置落盘模型在技术层面RFC 要求选项的存在性应当体现为 JSON 对象层级的属性。也就是说无论选项是否被用户显式书写最终进入生成器的配置 JSON 中都应有对应的布尔/值属性而默认值同样被物化在 JSON 中。输入一段 JDL 示例RFC 原文给出的 JDL 如下application { config: { baseName: a skipClient: true } } ReadOnly entity E { } paginate * with pager except E输出一.yo-rc.json应用级配置经转换后生成器使用的简化JSON 对象如下{ generator-jhipster: { baseName: a, generateClient: true, _comment_for_above: see the change, and as it is a reasonable default, we may not have to specify it } }关键点用户书写的skipClient: true被正向表达为generateClient: true注意RFC 原文此处为true即生成客户端这一包含式语义同时注明这是合理默认值甚至可能无需显式落盘_comment_for_above是 RFC 用于说明语义的注释字段示意这是合理默认值将来实现时或许不必显式写入。需要强调的是RFC 中的generateClient: true是设计提案中的示意值用于表达包含式 合理默认值的理念当前仓库实际仍在应用级使用skipClient默认值定义见 lib/jhipster/application-options.ts类型为BOOLEAN见 L271迁移是 RFC 面向 V8 的演进目标。输出二.jhipster/E.json实体级配置同一个 JDL 中ReadOnly注解与paginate * with pager except E全局选项转换到实体E的配置如下{ name: E, config: { queryMethods: true, _comment_for_above: reasonable default we may not have to specify it, deleteMethods: false, _comment_for_above: result of the readonly option, saveMethods: false, _comment_for_above: result of the readonly option, generateEntityLayer: true, _comment_for_above: another reasonable default, ... as it is a reasonable default we may not have to specify it, paginate: false, _comment_for_above: end user choice } }这个例子展示了实体级配置如何被完全展开为显式属性属性值来源语义queryMethodstrue合理默认值用户未指定可不落盘deleteMethodsfalseReadOnly注解的结果只读实体不生成删除方法saveMethodsfalseReadOnly注解的结果只读实体不生成保存方法generateEntityLayertrue另一个合理默认值可不落盘paginatefalse最终用户决策except E排除了实体 E可见实体级配置中的每一项最终都被规范化成一个明确的布尔属性其值要么来自合理默认要么来自用户显式决策注解、全局选项、实体级选项从而让生成器消费配置时无需再做是否被定义的猜测。实测readOnly 在当前仓库中的落点RFC 中的ReadOnly场景在当前仓库中已存在对应实现。在 lib/jdl/core/built-in-options/unary-options.ts 中readOnly是一元选项相关 JDL 测试夹具如 lib/jdl/core/test-support/files/annotations.jdl与解析转换测试lib/jdl/converters/jdl-to-json/jdl-to-json-option-converter.spec.ts均覆盖了注解到 JSON 配置的转换路径感兴趣的读者可继续深入。缺点DrawbacksBlueprint 扩展的代价RFC 承认该方案存在缺点带有额外选项的 Blueprint蓝图将不得不覆盖对象定义。为此方案要求在 generator-core 层提供可覆盖的 API允许 Blueprint 开发者在每一层应用级、实体级等添加自己的属性。从仓库结构看这一诉求与当前 Blueprint 机制的设计方向一致generators/base/internal/blueprint.ts提供了蓝图解析与优先级处理逻辑generators/base-application则承载了应用与实体的可覆盖定义如 generators/base-application/entity.ts为每层可扩展属性提供了基础的继承与覆盖骨架。方案论证与备选Rationale and alternatives为什么排他语法不可行RFC 指出skip 语法存在多种解读方式且与二元选项binary options不兼容。排他语法的歧义RFC 原文给出的例子application { name: a, clientFramework: angular } entity A { } skipController A // No entity HTML, No entity Component, No entity Front consumer Service, No entity Back ITest, No entity Front ITest, no entity front test: is this behavior really clear for end users? /* Illegal syntax */ skipService A with serviceImpl // We generate with serviceClass or we do not generate at all?问题一目了然skipController A实际隐含了不生成实体 HTML、不生成实体组件、不生成前端消费服务、不生成后端集成测试、不生成前端集成测试、不生成前端测试等一长串连锁行为但对最终用户而言这些连锁效应完全不可见、不明确skipService A with serviceImpl这种组合直接就是非法语法——语义上无法确定是生成serviceClass还是什么都不生成。排他语法一旦与带参数的二元选项如with serviceImpl组合就陷入表达力不足的困境。包含式语法的对等表达同一需求用包含式语法表达为application { name: a, clientFramework: angular } entity A { } forms * except A clientServices * except A iTests * except A service * with serviceClass每一条都是明确的正向指令forms * except A所有实体都生成表单除 A 以外clientServices * except A所有实体都生成前端消费服务除 A 以外iTests * except A所有实体都生成集成测试除 A 以外service * with serviceClass所有实体都生成服务且采用serviceClass类型。对比可见包含式语法把原来隐藏在skipController背后的多条隐式连锁行为显式化为可单独控制、可精确排除的选项语义透明、可组合性强且天然支持except这种面向集合的修饰。先例Prior art该 RFC 的先例即其关联 Issuejhipster/generator-jhipster#14416该 Issue 承载了在生成器中使用包含式语法管理属性这一需求的社区讨论与背景。未来可能性Future possibilitiesRFC-5 提出的包含式属性模型为生成器打开了三个明确的演进方向按需激活各层layercontroller、forms、tests 等层次可以独立、按需地启用或关闭——这正是把隐式连锁行为拆成显式开关的自然延伸按需激活 C、R、U、D 能力Create、Read、Update、Delete 四类 CRUD 能力可独立控制与 RFC 中deleteMethods、saveMethods、queryMethods的实体级展开模型直接呼应不止生成 REST还能生成消息代理message broker的生产者与消费者这将显著增强微服务架构的附加值——当生成什么由正向、显式的选项表达时新增一类产出物如消息消费者只需新增对应的包含式选项而无需发明新的排他语法。总结从 RFC 到源码的验证闭环RFC-5 的核心主张可归纳为三点选项一律正向表达、未定义时取合理默认、定义时尊重用户决策。本仓库源码从多个侧面印证了这一设计动机排他选项的集中登记与默认值/类型定义见 lib/jhipster/application-options.tsskipClient/skipServer/skipUserManagement默认false、类型BOOLEAN一元选项含skipClient、readOnly等的语义定义见 lib/jdl/core/built-in-options/unary-options.tsskipClient同时作为应用选项与实体选项带来的归类复杂度见 lib/jdl/core/built-in-options/tokens/application-tokens.ts未显式定义时按框架派生默认值的现网实现见 generators/base-application/internal/utils.ts生成器消费skipClient做条件判断的实例见 generators/client/command.ts如主题选择仅在!config.skipClient时触发相关转换与渲染行为的测试快照见 generators/client/generators/bootstrap/snapshots/generator.spec.ts.snap 与 generators/entity/snapshots/generator.spec.ts.snap。对于 JHipster 生成器开发者和 Blueprint 维护者而言RFC-5 提供了清晰的演进路线新增选项时优先采用包含式命名如generateXxx、xxxMethods并将默认值物化到.yo-rc.json与.jhipster/*.json中从而逐步消除双重否定、减少分支逻辑为按需分层生成、按需 CRUD 与消息驱动生成等能力铺平道路。【免费下载链接】generator-jhipsterJHipster is a development platform to quickly generate, develop, deploy modern web applications microservice architectures.项目地址: https://gitcode.com/gh_mirrors/ge/generator-jhipster创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考