UNPKG

formkl

Version:
517 lines (516 loc) 17.3 kB
var d = Object.defineProperty; var E = (s, e, t) => e in s ? d(s, e, { enumerable: !0, configurable: !0, writable: !0, value: t }) : s[e] = t; var c = (s, e, t) => (E(s, typeof e != "symbol" ? e + "" : e, t), t); const n = (s) => new RegExp( `^\\b(${[ s, s.toLowerCase(), s.toUpperCase(), s.toLowerCase().charAt(0).toUpperCase() + s.toLowerCase().slice(1) ].join("|")})\\b` ), y = [ [/^\n/, null], [/^\s+/, null], [/^\/\/.*/, null], [/^\/\*[\s\S]*?\*\//, null], [/^;/, ";"], [/^{/, "{"], [/^}/, "}"], [/^\(/, "("], [/^\)/, ")"], [/^\[/, "["], [/^\]/, "]"], [/^,/, ","], [/^\./, "."], [/^[<>]=?/, "OPERATOR_RELATIONAL"], [/^[=!]=/, "OPERATOR_EQUALITY"], [/^&&/, "LOGICAL_AND"], [/^\|\|/, "LOGICAL_OR"], [/^!/, "LOGICAL_NOT"], [n("VALID"), "VALID"], [n("REGEX"), "REGEX"], [n("URL"), "URL"], [n("REQUIRE"), "REQUIRE"], [n("AS"), "AS"], [n("OR"), "OR"], [n("AND"), "AND"], [n("HAS"), "HAS"], [n("INCLUDES"), "HAS"], [n("FORMKL"), "FORMKL"], [n("MULTIPLE"), "MULTIPLE"], [n("BASE"), "BASE"], [n("FLAT"), "FLAT"], [n("GET"), "HTTPMETHOD"], [n("POST"), "HTTPMETHOD"], [n("PUT"), "HTTPMETHOD"], [n("PATCH"), "HTTPMETHOD"], [n("DELETE"), "HTTPMETHOD"], ...["text", "paragraph", "number", "switch"].map( (s) => [n(s), "FIELD"] ), ...["checkbox", "radio", "select"].map( (s) => [n(s), "FIELDSELECTION"] ), ...["email", "zip", "age"].map((s) => [n(s), "FIELDVALIDATED"]), ...["datetimerange", "datetime", "daterange", "timerange", "time", "date"].map( (s) => [n(s), "FIELDDATETIME"] ), [/^\$\w+/, "FIELDCUSTOM"], [/^\d+/, "NUMBER"], [/^"[^"]*"/, "STRING"], [/^'[^']*'/, "STRING"], [n("NaN"), "NAN"], [n("FALSE"), "FALSE"], [n("TRUE"), "TRUE"], [n("NULL"), "NULL"], [n("UNDEFINED"), "UNDEFINED"] ]; class L { constructor(e) { c(this, "syntax"); c(this, "cursor"); c(this, "currentLine"); c(this, "currentColumn"); this.syntax = e, this.cursor = 0, this.currentLine = 1, this.currentColumn = 0; } isEOF() { return this.cursor === this.syntax.length; } hasMoreTokens() { return this.cursor < this.syntax.length; } getNextToken() { if (!this.hasMoreTokens()) return null; const e = this.syntax.slice(this.cursor); for (const [t, i] of y) { const a = this._match(t, e); if (a !== null) return i === null ? this.getNextToken() : { type: i, value: a }; } throw new SyntaxError( `Unexpected token: "${e[0]}" at ${this.currentLine}:${this.currentColumn}` ); } _match(e, t) { const i = e.exec(t); return i === null ? null : (this.cursor += i[0].length, e.source === "^\\n" && i !== null && (this.currentLine++, this.currentColumn = 0), this.currentColumn += i[0].length, i[0]); } } const u = (s) => s.replace(/([a-z])([A-Z])/g, "$1-$2").replace(/\s+/g, "-").toLowerCase(); class g { constructor() { } validateLogicAnd(e) { return e.map((i) => this.validationLogic(i)).join("%(s)and%(s)"); } validateLogicOr(e) { return e.map((i) => this.validationLogic(i)).join("%(s)or%(s)"); } validationLogic(e) { const i = Object.keys(e)[0], a = e[i]; return { $gt: () => `>%(s)${a}`, $lt: () => `>=%(s)${a}`, $gteq: () => `<%(s)${a}`, $lteq: () => `<=%(s)${a}`, $eq: () => `==%(s)${typeof a == "string" ? JSON.stringify(a) : a}`, $has: () => `has%(s)${typeof a == "string" ? JSON.stringify(a) : a}`, $and: () => e.$and && this.validateLogicAnd(e.$and), $or: () => e.$or && this.validateLogicOr(e.$or) }[i](); } validation(e) { return [ e.regex && `regex("${e.regex.source}")`, e.logic && `valid(${this.validationLogic(e.logic)})` ].filter((t) => t).join("%(s)"); } selectionField(e) { return `${e.type}${e.fetchUrl ? `${e.fetchDataPath ? `%(s)${e.fetchDataPath}` : ""}%(s)url("${[ e.fetchUrl, e.valueKey, e.labelKey ].map((t) => JSON.stringify(t)).join(", ")}")` : `%(s)(${e.options.map((t) => JSON.stringify(t)).join(", ")})`}`; } fields(e) { return "%(t)%(t)" + e.map( (t) => [ t.required && "require", t.maxResponseAllowed ? t.maxResponseAllowed : t.multiple && "multiple", u(t.label).toLowerCase() !== t.type && `"${t.label}"`, ["select", "radio", "checkbox"].includes(t.type) ? this.selectionField(t) : t.type, t.validation && this.validation(t.validation), u(t.label).toLowerCase() !== t.key && `as%(s)"${t.key}"` ].filter((i) => i).join("%(s)") + ";" ).join("%(n)%(t)%(t)"); } sections(e) { return e.map( (t) => [ "%(t)", t.multiple && "multiple%(s)", t.title && `"${t.title}"%(s)`, "has", "%(s)", "{", "%(n)", this.fields(t.fields), "%(n)", "%(t)", "}", !t.title && t.key || t.title && u(t.title).toLowerCase() !== t.key ? `%(s)as%(s)"${t.key}"` : "" ].join("") ).join("%(n)"); } stringify(e) { return `${[ "formkl", e.model === "flat" && "flat", e.title && JSON.stringify(e.title), e.description && JSON.stringify(e.description) ].filter((t) => t).join("%(s)")}%(s){%(n)${this.sections(e.sections)}%(n)}`.replace(/\%\(s\)/g, " ").replace(/\%\(t\)/g, " ").replace(/\%\(n\)/g, ` `); } } const _ = (s) => String(s).toLowerCase().charAt(0).toUpperCase() + String(s).toLowerCase().slice(1); class S { constructor() { c(this, "syntax"); c(this, "tokenizer"); c(this, "_lookahead"); this.syntax = "", this.tokenizer = new L(""), this._lookahead = null; } parse(e) { return this.syntax = "", this._lookahead = null, this.syntax = e, this.tokenizer = new L(this.syntax), this._lookahead = this.tokenizer.getNextToken(), this.FormBlock(); } stringify(e) { return new g().stringify(e); } FormBlock() { var i, a, o, l, r; const e = { model: "base", sections: [] }; this._eat("FORMKL"), ((i = this._lookahead) == null ? void 0 : i.type) === "FLAT" ? (this._eat("FLAT"), e.model = "flat") : (((a = this._lookahead) == null ? void 0 : a.type) === "BASE" && this._eat("BASE"), e.model = "base"), ((o = this._lookahead) == null ? void 0 : o.type) === "HTTPMETHOD" && (e.method = this._eat("HTTPMETHOD").value, this._eat("("), e.endpoint = this.StringLiteral(), this._eat(")")), ((l = this._lookahead) == null ? void 0 : l.type) === "STRING" && (e.title = this.StringLiteral()), ((r = this._lookahead) == null ? void 0 : r.type) === "STRING" && (e.description = this.StringLiteral()), this._eat("{"); const t = this.SectionBlockList(); if (t.length > 1) { const h = /* @__PURE__ */ new Set(); t.forEach((p) => { if (h.has(p.key)) throw new SyntaxError( `Duplicate section key "${p.key}", this will make the your schema looks confusing! Please use different aliases if your sections have the same title.` ); h.add(p.key); }); } return Object.assign(e, { sections: t }), this._eat("}"), e; } SectionBlockList(e = "}") { var i; const t = [this.SectionBlock()]; for (; this._lookahead != null && ((i = this._lookahead) == null ? void 0 : i.type) !== e; ) t.push(this.SectionBlock()); return t; } SectionBlock() { var i, a, o, l; const e = { fields: [] }; ((i = this._lookahead) == null ? void 0 : i.type) === "NUMBER" ? (e.maxResponseAllowed = this.NumericLiteral(), e.multiple = !0) : ((a = this._lookahead) == null ? void 0 : a.type) === "MULTIPLE" && (this._eat("MULTIPLE"), e.multiple = !0), ((o = this._lookahead) == null ? void 0 : o.type) === "STRING" && (e.title = this.StringLiteral(), e.key = u(e.title).toLowerCase()), this._eat("HAS"), this._eat("{"); const t = this.FieldStatementList(); if (this._eat("}"), ((l = this._lookahead) == null ? void 0 : l.type) === "AS" && (this._eat("AS"), e.key = this.StringLiteral()), t.length > 1) { const r = /* @__PURE__ */ new Set(); t.forEach((h) => { if (r.has(h.key)) throw new SyntaxError( `Duplicate field key "${h.key}", this will make the your schema looks confusing! Please use different aliases if your fields have the same name.` ); r.add(h.key); }); } if (e.multiple && t.some((r) => r.multiple)) throw new SyntaxError( "A section with multiple responses cannot have fields that also have multiple responses!" ); return Object.assign(e, { fields: t }), e; } FieldStatementList(e = "}") { var i; const t = [this.FieldStatement()]; for (; this._lookahead !== null && ((i = this._lookahead) == null ? void 0 : i.type) !== e; ) t.push(this.FieldStatement()); return t; } FieldStatement() { var t, i, a, o, l, r; const e = { type: "text", label: "", key: "" }; return ((t = this._lookahead) == null ? void 0 : t.type) === "NUMBER" && (e.maxResponseAllowed = this.NumericLiteral(), e.multiple = !0), ((i = this._lookahead) == null ? void 0 : i.type) === "REQUIRE" && (this._eat("REQUIRE"), e.required = !0), ((a = this._lookahead) == null ? void 0 : a.type) === "MULTIPLE" && (this._eat("MULTIPLE"), e.multiple = !0), ((o = this._lookahead) == null ? void 0 : o.type) === "STRING" ? e.label = this.StringLiteral() : e.label = _(String((l = this._lookahead) == null ? void 0 : l.value).replace(/^\$/g, "")), e.key = u(e.label).toLowerCase(), Object.assign(e, this.FieldExpression()), ((r = this._lookahead) == null ? void 0 : r.type) === "AS" && (this._eat("AS"), e.key = this.StringLiteral()), this._eat(";"), e; } FieldExpression() { var i, a; const e = {}, t = { FIELD: this.FieldDefaultExpression.bind(this), FIELDCUSTOM: this.FieldCustomExpression.bind(this), FIELDSELECTION: this.FieldSelectionExpression.bind(this), FIELDVALIDATED: this.FieldValidatedExpression.bind(this), FIELDDATETIME: this.FieldDatetimeExpression.bind(this) }[String((i = this._lookahead) == null ? void 0 : i.type)]; if (t) { Object.assign(e, t()); const o = this.ValidationExpression(); Object.assign(e, o); } else throw new SyntaxError(`Unsupported field type "${(a = this._lookahead) == null ? void 0 : a.value}"`); return e; } FieldDefaultExpression() { var t; const e = { type: "text" }; if (((t = this._lookahead) == null ? void 0 : t.type) === "FIELD") { const i = this._eat("FIELD").value; Object.assign(e, { type: i.toLowerCase() }); } return e; } FieldCustomExpression() { var t; const e = { type: "text" }; if (((t = this._lookahead) == null ? void 0 : t.type) === "FIELDCUSTOM") { const i = this._eat("FIELDCUSTOM").value; Object.assign(e, { type: i.toLowerCase() }); } return e; } FieldSelectionExpression() { var i, a, o, l; let e = ""; const t = { type: "select", options: [] }; if (((i = this._lookahead) == null ? void 0 : i.type) === "FIELDSELECTION") { const r = this._eat("FIELDSELECTION").value; Object.assign(t, { type: r.toLowerCase() }); } if (((a = this._lookahead) == null ? void 0 : a.type) === "STRING" && (e = this.StringLiteral()), ((o = this._lookahead) == null ? void 0 : o.type) === "URL") { this._eat("URL"), this._eat("("); const r = this.StringList(); if (r.length > 3) throw new SyntaxError( 'Selection field fetching data from URL can only have less or equal to 3 arguments ("fetchUrl", "valueKey", "labelKey")' ); this._eat(")"), Object.assign(t, { options: [], fetchUrl: r[0] || "", valueKey: r[1] || "id", labelKey: r[2] || "name" }); } if (((l = this._lookahead) == null ? void 0 : l.type) === "(") { this._eat("("); const r = this.StringList(); this._eat(")"), t.options = r; } return Object.assign(t, { fetchDataPath: e }), t; } FieldValidatedExpression() { var t; const e = { type: "text" }; if (((t = this._lookahead) == null ? void 0 : t.type) === "FIELDVALIDATED") { const i = this._eat("FIELDVALIDATED").value; Object.assign(e, { type: i.toLowerCase() }); } return e; } FieldDatetimeExpression() { var t; const e = { type: "datetime" }; if (((t = this._lookahead) == null ? void 0 : t.type) === "FIELDDATETIME") { const i = this._eat("FIELDDATETIME").value; Object.assign(e, { type: i.toLowerCase() }); } return e; } ValidationExpression() { var t, i, a; const e = {}; do { if (((t = this._lookahead) == null ? void 0 : t.type) === "VALID") { this._eat("VALID"), this._eat("("); const o = this.LogicalORExpression(); this._eat(")"), Object.assign(e, { logic: o }); } if (((i = this._lookahead) == null ? void 0 : i.type) === "REGEX") { this._eat("REGEX"), this._eat("("); const o = this.StringLiteral(); this._eat(")"), Object.assign(e, { regex: new RegExp(o) }); } } while (["REGEX", "VALID"].includes(String((a = this._lookahead) == null ? void 0 : a.type))); if (e.logic || e.regex) return { validation: e }; } LogicalORExpression() { var t; const e = []; do e.push(this.LogicalANDExpression()); while (((t = this._lookahead) == null ? void 0 : t.type) === "OR" && this._eat("OR")); return e.length > 1 ? { $or: e } : e[0]; } LogicalANDExpression() { var t; const e = []; do e.push(this.RelationalExpression()); while (((t = this._lookahead) == null ? void 0 : t.type) === "AND" && this._eat("AND")); return e.length > 1 ? { $and: e } : e[0]; } RelationalExpression() { var e, t, i, a, o; switch ((e = this._lookahead) == null ? void 0 : e.type) { case "OPERATOR_RELATIONAL": const l = this._eat("OPERATOR_RELATIONAL").value; switch (l) { case ">": return { $gt: this.NumericLiteral() }; case ">=": return { $gte: this.NumericLiteral() }; case "<": return { $lt: this.NumericLiteral() }; case "<=": return { $lte: this.NumericLiteral() }; default: throw new SyntaxError(`Unknown relational operator: ${l}`); } case "OPERATOR_EQUALITY": const r = this._eat("OPERATOR_EQUALITY").value, h = r === "==" ? "$eq" : "$neq"; switch (r) { case "==": case "!=": switch ((t = this._lookahead) == null ? void 0 : t.type) { case "NUMBER": return { [h]: this.NumericLiteral() }; case "NAN": return { [h]: this.NaNLiteral() }; case "NULL": return { [h]: this.NullLiteral() }; case "UNDEFINED": return { [h]: this.UndefinedLiteral() }; case "TRUE": case "FALSE": return { [h]: this.BooleanLiteral( (i = this._lookahead) == null ? void 0 : i.type ) }; case "STRING": return { [h]: this.StringLiteral() }; default: throw new SyntaxError(`Unknown equality value type: ${(a = this._lookahead) == null ? void 0 : a.type}`); } default: throw new SyntaxError(`Unknown equality operator: ${r}`); } case "HAS": return this._eat("HAS"), { $has: isNaN((o = this._lookahead) == null ? void 0 : o.value) ? this.StringLiteral() : this.NumericLiteral() }; } } StringList() { var t; const e = []; do e.push(this.StringLiteral()); while (((t = this._lookahead) == null ? void 0 : t.type) === "," && this._eat(",")); return e; } NaNLiteral() { return this._eat("NAN"), NaN; } NumericLiteral() { const e = this._eat("NUMBER"); return Number(e.value); } StringLiteral() { const e = this._eat("STRING"); return String(e.value).slice(1, -1); } BooleanLiteral(e) { return this._eat(e ? "TRUE" : "FALSE"), e; } NullLiteral() { return this._eat("NULL"), null; } UndefinedLiteral() { this._eat("UNDEFINED"); } _eat(e) { const t = this._lookahead; if (t === null) throw new SyntaxError(`Unexpected end of input, expected: "${e}"`); if (t.type !== e) throw new SyntaxError( `Unexpected token: "${t.value}" at ${this.tokenizer.currentLine}:${this.tokenizer.currentColumn}, expected: "${e}"` ); return this._lookahead = this.tokenizer.getNextToken(), t; } } function T(s) { return s; } function f(s) { return s; } function x(s) { return s; } function A(s) { return s; } const N = new S(); export { S as Parser, L as Tokenizer, N as default, x as defineField, T as defineForm, A as defineModel, f as defineSection };