@ formspec/ 构建
用于 FormSpec 的建时计划生成.
本套件涵盖:
- 将 DSL 链到 JSON Schema / UI Schema 汇编
- 静态分析 TypeScript 类,接口,以及 TSDoc 标签的别名类型
- 混合作者计划生成
- 带有自定义供应商关键字的扩展意识计划生成
安装
pnpm add @formspec/build
大多数应用代码可以使用 formspec,但使用时 @formspec/build 直接当您需要静态分析或低等生成 API 时.
公共条目
| 入境点 | 目的 |
|---|---|
@formspec/build | 公共建筑 API |
@formspec/build/browser | 浏览器安全链- DSL 和 IR 表面 |
@formspec/build/internals | 不稳定的低水平IR/分析器 API |
链式 DSL 生成
import { buildFormSchemas } from "@formspec/build";
import { field, formspec } from "@formspec/dsl";
const form = formspec(
field.text("name", { required: true }),
field.enum("status", ["draft", "published"] as const)
);
const { jsonSchema, uiSchema } = buildFormSchemas(form);
静态分析
generateSchemas() 是TSDoc支持生成的主要切入点.
import { generateSchemas } from "@formspec/build";
const { jsonSchema, uiSchema } = generateSchemas({
filePath: "./src/forms.ts",
typeName: "ProductConfig",
});
generateSchemasFromClass() 当输入绝对是类声明时仍可使用.
import { generateSchemasFromClass } from "@formspec/build";
const result = generateSchemasFromClass({
filePath: "./src/forms.ts",
className: "ProductConfig",
});
静态构建背景
在需要检查出口、申报时使用静态构建上下文 API, 或方法上的签名,然后决定产生什么计划.
此工作流程中的公共助手 :
createStaticBuildContext(filePath)- 从文件中创建可重复使用的编译器后置上下文.createStaticBuildContextFromProgram(program, filePath)- 重新使用主机拥有ts.Program。 。 。 .resolveModuleExport(context, exportName?)- 解决任何出口的符号,包括功能和其他非化学申报.resolveModuleExportDeclaration(context, exportName?)- 只解决计划来源的申报(class, (中文(简体) ),interface, (中文(简体) ),type(别名).resolveDeclarationMetadata(...)- 利用FormSpec的主动元数据政策,解决命名类型、方法和属性的元数据.generateSchemasFromDeclaration(...)- 产生一个解决的 计划源声明.generateSchemasFromParameter(...)- 从方法或函数参数声明生成.generateSchemasFromReturnType(...)- 从方法或函数返回类型生成,待取Promise<T>- 代代相传.generateSchemasFromType(...)- 直接从解决后产生ts.Type。 。 。 .
使用 resolveModuleExportDeclaration(...) 当你的工具想要 提交一个解决
直接声明为 generateSchemasFromDeclaration(...)启用 resolveModuleExport(...)
当您首先需要更低级的 TypeScript 访问时, 例如检查一个函数
输出,然后从其中一种签名类型生成计划.
import * as ts from "typescript";
import {
createStaticBuildContext,
generateSchemasFromDeclaration,
generateSchemasFromParameter,
generateSchemasFromReturnType,
resolveDeclarationMetadata,
resolveModuleExport,
resolveModuleExportDeclaration,
} from "@formspec/build";
const context = createStaticBuildContext("./src/service.ts");
const serviceDeclaration = resolveModuleExportDeclaration(context, "PaymentService");
if (serviceDeclaration && ts.isClassDeclaration(serviceDeclaration)) {
const submitMethod = serviceDeclaration.members.find(
(member): member is ts.MethodDeclaration =>
ts.isMethodDeclaration(member) &&
ts.isIdentifier(member.name) &&
member.name.text === "submit"
);
if (submitMethod?.parameters[0]) {
const methodMetadata = resolveDeclarationMetadata({
context,
declaration: submitMethod,
});
const inputSchemas = generateSchemasFromParameter({
context,
parameter: submitMethod.parameters[0],
});
}
}
const inputDeclaration = resolveModuleExportDeclaration(context, "SubmitInput");
if (inputDeclaration) {
const inputSchemas = generateSchemasFromDeclaration({
context,
declaration: inputDeclaration,
});
}
const paymentSymbol = resolveModuleExport(context, "submitPayment");
const paymentDeclaration = paymentSymbol?.declarations?.find(ts.isFunctionDeclaration);
if (paymentDeclaration) {
const outputSchemas = generateSchemasFromReturnType({
context,
declaration: paymentDeclaration,
});
}
如果你已经拥有一个 ts.Program,使用时 createStaticBuildContextFromProgram(program, filePath)
而不是让FormSpec创建一个。 如果您的工具已经解决了原始
ts.Type 或签字声明、使用 generateSchemasFromType(...) 或者说
generateSchemasFromReturnType(...) 直接说.
这是用于构建时段分析工作流程的辅助公共路径
需求 @formspec/build/internals。 。 。 .
支持的TSDoc实例
export interface ProductConfig {
/** @displayName Product Name @minLength 1 */
name: string;
/** @format email */
supportEmail?: string;
/** @placeholder Search products */
query?: string;
/** @minimum 0 @maximum 9999.99 */
price: number;
/** @uniqueItems */
tags: string[];
}
扩展- 智能生成
静态分析和混合作者生成 API 接受 extensionRegistry 财务报告和审定财务报表 vendorPrefix链式 DSL 生成接受 vendorPrefix,但不会 extensionRegistry。 。 。 .
import { createExtensionRegistry, generateSchemas } from "@formspec/build";
const registry = createExtensionRegistry([myExtension]);
const result = generateSchemas({
filePath: "./src/forms.ts",
typeName: "Invoice",
extensionRegistry: registry,
vendorPrefix: "x-acme",
});
世代在排放出计划之前验证犬类IR. 无效的输入失败生成, 结构化的诊断代码在抛出错误中浮出水面 .
处理生成失败
静态生成目前使用关于主要切入点的明显错误报告:
generateSchemas({ ..., errorReporting: "throw" })财务报告和审定财务报表generateSchemasFromProgram({ ..., errorReporting: "throw" })保持简单的扔出错误的合同.generateSchemas({ ..., errorReporting: "diagnostics" })财务报告和审定财务报表generateSchemasFromProgram({ ..., errorReporting: "diagnostics" })返回结构化诊断,而不是投放分析和验证失败.generateSchemasBatch()财务报告和审定财务报表generateSchemasBatchFromProgram()继续通过多个目标返回每个目标的诊断.
使用 "throw" 模式。 使用 "diagnostics" 模式中,当您想要在一条通道中尽可能多地显示反馈,特别是在编辑器、CI或迁移工具中。 年长者 generateSchemasDetailed() 财务报告和审定财务报表 generateSchemasFromProgramDetailed() 包装纸仅作为已贬值的相容 shims 保留.
import { generateSchemas, generateSchemasBatch } from "@formspec/build";
try {
const { jsonSchema, uiSchema } = generateSchemas({
filePath: "./src/forms.ts",
typeName: "Invoice",
errorReporting: "throw",
});
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
if (message.includes("UNSUPPORTED_CUSTOM_TYPE_OVERRIDE")) {
// An extension tried to override a TS global built-in such as Date or Array
} else if (message.includes("SYNTHETIC_SETUP_FAILURE")) {
// Extension setup failed before tag type-checking could run
} else if (message.includes("TYPE_MISMATCH")) {
// A tag was applied to an incompatible field or target type
}
throw error;
}
const detailed = generateSchemas({
filePath: "./src/forms.ts",
typeName: "Invoice",
errorReporting: "diagnostics",
});
if (!detailed.ok) {
for (const diagnostic of detailed.diagnostics) {
console.error(`${diagnostic.code}: ${diagnostic.message}`);
}
}
const batch = generateSchemasBatch({
targets: [
{ filePath: "./src/forms.ts", typeName: "Invoice" },
{ filePath: "./src/forms.ts", typeName: "PaymentTerms" },
],
});
for (const result of batch) {
if (!result.ok) {
console.error(
`${result.typeName} failed: ${result.diagnostics.map((diagnostic) => diagnostic.code).join(", ")}`
);
}
}
用于扩展后静态分析失败的最相关代码有:
UNSUPPORTED_CUSTOM_TYPE_OVERRIDE对于与不支持的 TypeScript 全球内置相冲突的扩展自定义类型.SYNTHETIC_SETUP_FAILURE用于无效或相冲突的扩展自定义类型注册和其他合成编译器设置失败.TYPE_MISMATCH用于作者源中的普通标签类型相容性.TYPE_NOT_FOUND当请求的导出目标无法解决时.UNSUPPORTED_ROOT_TYPE财务报告和审定财务报表DUPLICATE_ROOT_PROPERTIES当所请求的类型别名不能被当作一个chema root.PROGRAM_CONTEXT_FAILURE当软件包无法为请求的文件创建或再使用 TypeScript 程序时.
作为拇指规则, UNSUPPORTED_CUSTOM_TYPE_OVERRIDE 财务报告和审定财务报表 SYNTHETIC_SETUP_FAILURE 显示扩展配置问题,同时 TYPE_MISMATCH 通常表示分析出源中的作者错误.
内部入口
低级犬类IR生成器,分析器原始器,验证辅助器被故意不再从包根输出出. 如果你需要那些不稳定的内脏 在单体内,从 @formspec/build/internals。 。 。 .
主要出口
buildFormSchemas(form, options?)generateJsonSchema(form, options?)generateUiSchema(form)writeSchemas(form, options)generateSchemas(options)generateSchemasFromClass(options)generateSchemasFromProgram(options)generateSchemasBatch(options)generateSchemasBatchFromProgram(options)buildMixedAuthoringSchemas(options)createExtensionRegistry(extensions)
writeSchemas() 是用于写出文件的链- DSL 便利包装器 。 扩展登记册适用于上面的静态分析和混合授权生成流程,不适用于 writeSchemas()。 。 。 .
许可证
这个包是FormSpec monorepo的一部分,根据MIT许可证发行. 见 自由主义 详细情况.