Spaces:
Sleeping
Sleeping
import type {CodeKeywordDefinition, AnySchemaObject, KeywordCxt, Code, Name} from "ajv" | |
import {_, stringify, getProperty} from "ajv/dist/compile/codegen" | |
type TransformName = | |
| "trimStart" | |
| "trimEnd" | |
| "trimLeft" | |
| "trimRight" | |
| "trim" | |
| "toLowerCase" | |
| "toUpperCase" | |
| "toEnumCase" | |
interface TransformConfig { | |
hash: Record<string, string | undefined> | |
} | |
type Transform = (s: string, cfg?: TransformConfig) => string | |
const transform: {[key in TransformName]: Transform} = { | |
trimStart: (s) => s.trimStart(), | |
trimEnd: (s) => s.trimEnd(), | |
trimLeft: (s) => s.trimStart(), | |
trimRight: (s) => s.trimEnd(), | |
trim: (s) => s.trim(), | |
toLowerCase: (s) => s.toLowerCase(), | |
toUpperCase: (s) => s.toUpperCase(), | |
toEnumCase: (s, cfg) => cfg?.hash[configKey(s)] || s, | |
} | |
const getDef: (() => CodeKeywordDefinition) & { | |
transform: typeof transform | |
} = Object.assign(_getDef, {transform}) | |
function _getDef(): CodeKeywordDefinition { | |
return { | |
keyword: "transform", | |
schemaType: "array", | |
before: "enum", | |
code(cxt: KeywordCxt) { | |
const {gen, data, schema, parentSchema, it} = cxt | |
const {parentData, parentDataProperty} = it | |
const tNames: string[] = schema | |
if (!tNames.length) return | |
let cfg: Name | undefined | |
if (tNames.includes("toEnumCase")) { | |
const config = getEnumCaseCfg(parentSchema) | |
cfg = gen.scopeValue("obj", {ref: config, code: stringify(config)}) | |
} | |
gen.if(_`typeof ${data} == "string" && ${parentData} !== undefined`, () => { | |
gen.assign(data, transformExpr(tNames.slice())) | |
gen.assign(_`${parentData}[${parentDataProperty}]`, data) | |
}) | |
function transformExpr(ts: string[]): Code { | |
if (!ts.length) return data | |
const t = ts.pop() as string | |
if (!(t in transform)) throw new Error(`transform: unknown transformation ${t}`) | |
const func = gen.scopeValue("func", { | |
ref: transform[t as TransformName], | |
code: _`require("ajv-keywords/dist/definitions/transform").transform${getProperty(t)}`, | |
}) | |
const arg = transformExpr(ts) | |
return cfg && t === "toEnumCase" ? _`${func}(${arg}, ${cfg})` : _`${func}(${arg})` | |
} | |
}, | |
metaSchema: { | |
type: "array", | |
items: {type: "string", enum: Object.keys(transform)}, | |
}, | |
} | |
} | |
function getEnumCaseCfg(parentSchema: AnySchemaObject): TransformConfig { | |
// build hash table to enum values | |
const cfg: TransformConfig = {hash: {}} | |
// requires `enum` in the same schema as transform | |
if (!parentSchema.enum) throw new Error('transform: "toEnumCase" requires "enum"') | |
for (const v of parentSchema.enum) { | |
if (typeof v !== "string") continue | |
const k = configKey(v) | |
// requires all `enum` values have unique keys | |
if (cfg.hash[k]) { | |
throw new Error('transform: "toEnumCase" requires all lowercased "enum" values to be unique') | |
} | |
cfg.hash[k] = v | |
} | |
return cfg | |
} | |
function configKey(s: string): string { | |
return s.toLowerCase() | |
} | |
export default getDef | |
module.exports = getDef | |