[79a0317] | 1 | import type {CodeKeywordDefinition, AnySchemaObject, KeywordCxt, Code, Name} from "ajv"
|
---|
| 2 | import {_, stringify, getProperty} from "ajv/dist/compile/codegen"
|
---|
| 3 |
|
---|
| 4 | type TransformName =
|
---|
| 5 | | "trimStart"
|
---|
| 6 | | "trimEnd"
|
---|
| 7 | | "trimLeft"
|
---|
| 8 | | "trimRight"
|
---|
| 9 | | "trim"
|
---|
| 10 | | "toLowerCase"
|
---|
| 11 | | "toUpperCase"
|
---|
| 12 | | "toEnumCase"
|
---|
| 13 |
|
---|
| 14 | interface TransformConfig {
|
---|
| 15 | hash: Record<string, string | undefined>
|
---|
| 16 | }
|
---|
| 17 |
|
---|
| 18 | type Transform = (s: string, cfg?: TransformConfig) => string
|
---|
| 19 |
|
---|
| 20 | const transform: {[key in TransformName]: Transform} = {
|
---|
| 21 | trimStart: (s) => s.trimStart(),
|
---|
| 22 | trimEnd: (s) => s.trimEnd(),
|
---|
| 23 | trimLeft: (s) => s.trimStart(),
|
---|
| 24 | trimRight: (s) => s.trimEnd(),
|
---|
| 25 | trim: (s) => s.trim(),
|
---|
| 26 | toLowerCase: (s) => s.toLowerCase(),
|
---|
| 27 | toUpperCase: (s) => s.toUpperCase(),
|
---|
| 28 | toEnumCase: (s, cfg) => cfg?.hash[configKey(s)] || s,
|
---|
| 29 | }
|
---|
| 30 |
|
---|
| 31 | const getDef: (() => CodeKeywordDefinition) & {
|
---|
| 32 | transform: typeof transform
|
---|
| 33 | } = Object.assign(_getDef, {transform})
|
---|
| 34 |
|
---|
| 35 | function _getDef(): CodeKeywordDefinition {
|
---|
| 36 | return {
|
---|
| 37 | keyword: "transform",
|
---|
| 38 | schemaType: "array",
|
---|
| 39 | before: "enum",
|
---|
| 40 | code(cxt: KeywordCxt) {
|
---|
| 41 | const {gen, data, schema, parentSchema, it} = cxt
|
---|
| 42 | const {parentData, parentDataProperty} = it
|
---|
| 43 | const tNames: string[] = schema
|
---|
| 44 | if (!tNames.length) return
|
---|
| 45 | let cfg: Name | undefined
|
---|
| 46 | if (tNames.includes("toEnumCase")) {
|
---|
| 47 | const config = getEnumCaseCfg(parentSchema)
|
---|
| 48 | cfg = gen.scopeValue("obj", {ref: config, code: stringify(config)})
|
---|
| 49 | }
|
---|
| 50 | gen.if(_`typeof ${data} == "string" && ${parentData} !== undefined`, () => {
|
---|
| 51 | gen.assign(data, transformExpr(tNames.slice()))
|
---|
| 52 | gen.assign(_`${parentData}[${parentDataProperty}]`, data)
|
---|
| 53 | })
|
---|
| 54 |
|
---|
| 55 | function transformExpr(ts: string[]): Code {
|
---|
| 56 | if (!ts.length) return data
|
---|
| 57 | const t = ts.pop() as string
|
---|
| 58 | if (!(t in transform)) throw new Error(`transform: unknown transformation ${t}`)
|
---|
| 59 | const func = gen.scopeValue("func", {
|
---|
| 60 | ref: transform[t as TransformName],
|
---|
| 61 | code: _`require("ajv-keywords/dist/definitions/transform").transform${getProperty(t)}`,
|
---|
| 62 | })
|
---|
| 63 | const arg = transformExpr(ts)
|
---|
| 64 | return cfg && t === "toEnumCase" ? _`${func}(${arg}, ${cfg})` : _`${func}(${arg})`
|
---|
| 65 | }
|
---|
| 66 | },
|
---|
| 67 | metaSchema: {
|
---|
| 68 | type: "array",
|
---|
| 69 | items: {type: "string", enum: Object.keys(transform)},
|
---|
| 70 | },
|
---|
| 71 | }
|
---|
| 72 | }
|
---|
| 73 |
|
---|
| 74 | function getEnumCaseCfg(parentSchema: AnySchemaObject): TransformConfig {
|
---|
| 75 | // build hash table to enum values
|
---|
| 76 | const cfg: TransformConfig = {hash: {}}
|
---|
| 77 |
|
---|
| 78 | // requires `enum` in the same schema as transform
|
---|
| 79 | if (!parentSchema.enum) throw new Error('transform: "toEnumCase" requires "enum"')
|
---|
| 80 | for (const v of parentSchema.enum) {
|
---|
| 81 | if (typeof v !== "string") continue
|
---|
| 82 | const k = configKey(v)
|
---|
| 83 | // requires all `enum` values have unique keys
|
---|
| 84 | if (cfg.hash[k]) {
|
---|
| 85 | throw new Error('transform: "toEnumCase" requires all lowercased "enum" values to be unique')
|
---|
| 86 | }
|
---|
| 87 | cfg.hash[k] = v
|
---|
| 88 | }
|
---|
| 89 |
|
---|
| 90 | return cfg
|
---|
| 91 | }
|
---|
| 92 |
|
---|
| 93 | function configKey(s: string): string {
|
---|
| 94 | return s.toLowerCase()
|
---|
| 95 | }
|
---|
| 96 |
|
---|
| 97 | export default getDef
|
---|
| 98 | module.exports = getDef
|
---|