formkl
Version:
Form marKup Language
3 lines (2 loc) • 13.1 kB
JavaScript
"use strict";var E=Object.defineProperty;var y=(s,e,t)=>e in s?E(s,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):s[e]=t;var c=(s,e,t)=>(y(s,typeof e!="symbol"?e+"":e,t),t);Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const n=s=>new RegExp(`^\\b(${[s,s.toLowerCase(),s.toUpperCase(),s.toLowerCase().charAt(0).toUpperCase()+s.toLowerCase().slice(1)].join("|")})\\b`),g=[[/^\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 d{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 g){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 _{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=s=>String(s).toLowerCase().charAt(0).toUpperCase()+String(s).toLowerCase().slice(1);class L{constructor(){c(this,"syntax");c(this,"tokenizer");c(this,"_lookahead");this.syntax="",this.tokenizer=new d(""),this._lookahead=null}parse(e){return this.syntax="",this._lookahead=null,this.syntax=e,this.tokenizer=new d(this.syntax),this._lookahead=this.tokenizer.getNextToken(),this.FormBlock()}stringify(e){return new _().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=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=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=S(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 k(s){return s}function T(s){return s}function f(s){return s}function m(s){return s}const x=new L;exports.Parser=L;exports.Tokenizer=d;exports.default=x;exports.defineField=f;exports.defineForm=k;exports.defineModel=m;exports.defineSection=T;