UNPKG

@xuda.io/runtime-bundle

Version:

The Xuda Runtime Bundle refers to a collection of scripts and libraries packaged together to provide the necessary runtime environment for executing plugins or components in the Xuda platform.

1 lines • 13.1 kB
export const childlessTags=["style","script","template"];export const closingTags=["html","head","body","p","dt","dd","li","option","thead","th","tbody","tr","td","tfoot","colgroup"];export const closingTagAncestorBreakers={li:["ul","ol","menu"],dt:["dl"],dd:["dl"],tbody:["table"],thead:["table"],tfoot:["table"],tr:["table"],td:["table"]};export const voidTags=["!doctype","area","base","br","col","command","embed","hr","img","input","keygen","link","meta","param","source","track","wbr"];export function uuidv4(){return([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g,c=>(c^crypto.getRandomValues(new Uint8Array(1))[0]&15>>c/4).toString(16))}export function isObject(val){return val instanceof Object}export function formatAttributes(attributes){return Object.keys(attributes).reduce((attrs,attrKey)=>{const key=attrKey;var value=attributes[attrKey];if(value===null||!value){return`${attrs} ${key}`}if(isObject(value))value=JSON.stringify(value);value=value.toString().replaceAll('"',"'");return`${attrs} ${key}="${value}"`},"")}export function toHTML(tree,options){return tree.map(node=>{if(!node.type)return;if(node.type==="text"){return node.content}if(node.type==="comment"){return`<!--${node.content}-->`}var text="";if(node.content){text=node.content}const{tagName,attributes,children}=node;if(!attributes.internal_tree_id&&!options.remove_tree_id)attributes.internal_tree_id=node.id;if(options.remove_tree_id){delete attributes.internal_tree_id}if(options.add_path){attributes.internal_path=node?.path?.toString?.()}const isSelfClosing=arrayIncludes(options.voidTags,tagName.toLowerCase());return isSelfClosing?`<${tagName}${formatAttributes(attributes)}>`:`<${tagName}${formatAttributes(attributes)}>${text}${toHTML(children,options)}</${tagName}>`}).join("")}export function parser(tokens,options){const root={tagName:null,children:[]};const state={tokens:tokens,options:options,cursor:0,stack:[root]};parserParse(state);return root.children}export function hasTerminalParent(tagName,stack,terminals){const tagParents=terminals[tagName];if(tagParents){let currentIndex=stack.length-1;while(currentIndex>=0){const parentTagName=stack[currentIndex].tagName;if(parentTagName===tagName){break}if(arrayIncludes(tagParents,parentTagName)){return true}currentIndex--}}return false}export function rewindStack(stack,newLength,childrenEndPosition,endPosition){stack[newLength].position.end=endPosition;for(let i=newLength+1,len=stack.length;i<len;i++){stack[i].position.end=childrenEndPosition}stack.splice(newLength)}export function parserParse(state){const{tokens,options}=state;let{stack}=state;let nodes=stack[stack.length-1].children;const len=tokens.length;let{cursor}=state;while(cursor<len){const token=tokens[cursor];if(token.type!=="tag-start"){nodes.push(token);cursor++;continue}const tagToken=tokens[++cursor];cursor++;const tagName=tagToken.content.toLowerCase();if(token.close){let index=stack.length;let shouldRewind=false;while(--index>-1){if(stack[index].tagName===tagName){shouldRewind=true;break}}while(cursor<len){const endToken=tokens[cursor];if(endToken.type!=="tag-end")break;cursor++}if(shouldRewind){rewindStack(stack,index,token.position.start,tokens[cursor-1].position.end);break}else{continue}}const isClosingTag=arrayIncludes(options.closingTags,tagName);let shouldRewindToAutoClose=isClosingTag;if(shouldRewindToAutoClose){const{closingTagAncestorBreakers:terminals}=options;shouldRewindToAutoClose=!hasTerminalParent(tagName,stack,terminals)}if(shouldRewindToAutoClose){let currentIndex=stack.length-1;while(currentIndex>0){if(tagName===stack[currentIndex].tagName){rewindStack(stack,currentIndex,token.position.start,token.position.start);const previousIndex=currentIndex-1;nodes=stack[previousIndex].children;break}currentIndex=currentIndex-1}}let attributes={};let attrToken;while(cursor<len){attrToken=tokens[cursor];if(attrToken.type==="tag-end")break;attributes[attrToken.content]="";cursor++}cursor++;const children=[];const position={start:token.position.start,end:attrToken.position.end};const elementNode={type:"element",tagName:tagToken.content,attributes:attributes,children:children,position:position};nodes.push(elementNode);const hasChildren=!(attrToken.close||arrayIncludes(options.voidTags,tagName));if(hasChildren){const size=stack.push({tagName:tagName,children:children,position:position});const innerState={tokens:tokens,options:options,cursor:cursor,stack:stack};parserParse(innerState);cursor=innerState.cursor;const rewoundInElement=stack.length===size;if(rewoundInElement){elementNode.position.end=tokens[cursor-1].position.end}}}state.cursor=cursor}export function feedPosition(position,str,len){const start=position.index;const end=position.index=start+len;for(let i=start;i<end;i++){const char=str.charAt(i);if(char==="\n"){position.line++;position.column=0}else{position.column++}}}export function jumpPosition(position,str,end){const len=end-position.index;return feedPosition(position,str,len)}export function makeInitialPosition(){return{index:0,column:0,line:0}}export function copyPosition(position){return{index:position.index,line:position.line,column:position.column}}export function lexer(str,options){const state={str:str,options:options,position:makeInitialPosition(),tokens:[]};lex(state);return state.tokens}export function lex(state){const{str,options:{childlessTags}}=state;const len=str.length;while(state.position.index<len){const start=state.position.index;lexText(state);if(state.position.index===start){const isComment=startsWith(str,"!--",start+1);if(isComment){lexComment(state)}else{const tagName=lexTag(state);const safeTag=tagName.toLowerCase();if(arrayIncludes(childlessTags,safeTag)){lexSkipTag(tagName,state)}}}}}const alphanumeric=/[A-Za-z0-9]/;export function findTextEnd(str,index){while(true){const textEnd=str.indexOf("<",index);if(textEnd===-1){return textEnd}const char=str.charAt(textEnd+1);if(char==="/"||char==="!"||alphanumeric.test(char)){return textEnd}index=textEnd+1}}export function lexText(state){const type="text";const{str,position}=state;let textEnd=findTextEnd(str,position.index);if(textEnd===position.index)return;if(textEnd===-1){textEnd=str.length}const start=copyPosition(position);const content=str.slice(position.index,textEnd)?.trim();jumpPosition(position,str,textEnd);const end=copyPosition(position);if(content)state.tokens.push({type:type,content:content,position:{start:start,end:end}})}export function lexComment(state){const{str,position}=state;const start=copyPosition(position);feedPosition(position,str,4);let contentEnd=str.indexOf("--\x3e",position.index);let commentEnd=contentEnd+3;if(contentEnd===-1){contentEnd=commentEnd=str.length}const content=str.slice(position.index,contentEnd);jumpPosition(position,str,commentEnd);state.tokens.push({type:"comment",content:content,position:{start:start,end:copyPosition(position)}})}export function lexTag(state){const{str,position}=state;{const secondChar=str.charAt(position.index+1);const close=secondChar==="/";const start=copyPosition(position);feedPosition(position,str,close?2:1);state.tokens.push({type:"tag-start",close:close,position:{start:start}})}const tagName=lexTagName(state);lexTagAttributes(state);{const firstChar=str.charAt(position.index);const close=firstChar==="/";feedPosition(position,str,close?2:1);const end=copyPosition(position);state.tokens.push({type:"tag-end",close:close,position:{end:end}})}return tagName}const whitespace=/\s/;export function isWhitespaceChar(char){return whitespace.test(char)}export function lexTagName(state){const{str,position}=state;const len=str.length;let start=position.index;while(start<len){const char=str.charAt(start);const isTagChar=!(isWhitespaceChar(char)||char==="/"||char===">");if(isTagChar)break;start++}let end=start+1;while(end<len){const char=str.charAt(end);const isTagChar=!(isWhitespaceChar(char)||char==="/"||char===">");if(!isTagChar)break;end++}jumpPosition(position,str,end);const tagName=str.slice(start,end);state.tokens.push({type:"tag",content:tagName});return tagName}export function lexTagAttributes(state){const{str,position,tokens}=state;let cursor=position.index;let quote=null;let wordBegin=cursor;const words=[];const len=str.length;while(cursor<len){const char=str.charAt(cursor);if(quote){const isQuoteEnd=char===quote;if(isQuoteEnd){quote=null}cursor++;continue}const isTagEnd=char==="/"||char===">";if(isTagEnd){if(cursor!==wordBegin){words.push(str.slice(wordBegin,cursor))}break}const isWordEnd=isWhitespaceChar(char);if(isWordEnd){if(cursor!==wordBegin){words.push(str.slice(wordBegin,cursor))}wordBegin=cursor+1;cursor++;continue}const isQuoteStart=char==="'"||char==='"';if(isQuoteStart){quote=char;cursor++;continue}cursor++}jumpPosition(position,str,cursor);const wLen=words.length;const type="attribute";for(let i=0;i<wLen;i++){const word=words[i];const isNotPair=word.indexOf("=")===-1;if(isNotPair){const secondWord=words[i+1];if(secondWord&&startsWith(secondWord,"=")){if(secondWord.length>1){const newWord=word+secondWord;tokens.push({type:type,content:newWord});i+=1;continue}const thirdWord=words[i+2];i+=1;if(thirdWord){const newWord=word+"="+thirdWord;tokens.push({type:type,content:newWord});i+=1;continue}}}if(endsWith(word,"=")){const secondWord=words[i+1];if(secondWord&&!stringIncludes(secondWord,"=")){const newWord=word+secondWord;tokens.push({type:type,content:newWord});i+=1;continue}const newWord=word.slice(0,-1);tokens.push({type:type,content:newWord});continue}tokens.push({type:type,content:word})}}const push=[].push;export function lexSkipTag(tagName,state){const{str,position,tokens}=state;const safeTagName=tagName.toLowerCase();const len=str.length;let index=position.index;while(index<len){const nextTag=str.indexOf("</",index);if(nextTag===-1){lexText(state);break}const tagStartPosition=copyPosition(position);jumpPosition(tagStartPosition,str,nextTag);const tagState={str:str,position:tagStartPosition,tokens:[]};const name=lexTag(tagState);if(safeTagName!==name.toLowerCase()){index=tagState.position.index;continue}if(nextTag!==position.index){const textStart=copyPosition(position);jumpPosition(position,str,nextTag);tokens.push({type:"text",content:str.slice(textStart.index,nextTag),position:{start:textStart,end:copyPosition(position)}})}push.apply(tokens,tagState.tokens);jumpPosition(position,str,tagState.position.index);break}}export function startsWith(str,searchString,position){return str.substr(position||0,searchString.length)===searchString}export function endsWith(str,searchString,position){const index=(position||str.length)-searchString.length;const lastIndex=str.lastIndexOf(searchString,index);return lastIndex!==-1&&lastIndex===index}export function stringIncludes(str,searchString,position){return str.indexOf(searchString,position||0)!==-1}export function isRealNaN(x){return typeof x==="number"&&isNaN(x)}export function arrayIncludes(array,searchElement,position){const len=array.length;if(len===0)return false;const lookupIndex=position|0;const isNaNElement=isRealNaN(searchElement);let searchIndex=lookupIndex>=0?lookupIndex:len+lookupIndex;while(searchIndex<len){const element=array[searchIndex++];if(element===searchElement)return true;if(isNaNElement&&isRealNaN(element))return true}return false}export function splitHead(str,sep){const idx=str.indexOf(sep);if(idx===-1)return[str];return[str.slice(0,idx),str.slice(idx+sep.length)]}export function unquote(str){const car=str.charAt(0);const end=str.length-1;const isQuoteStart=car==='"'||car==="'";if(isQuoteStart&&car===str.charAt(end)){return str.slice(1,end)}return str}export function format(nodes,options){return nodes.map(node=>{var outputNode={};const type=node.type;if(node.children){var textIndex=node.children?.findIndex(e=>{return e.type==="text"});if(textIndex!==-1){outputNode.content=node.children[textIndex]?.content?.trim();node.children.splice(textIndex,1)}}switch(type){case"element":var ATTRS=renderFormatAttributes(node.attributes);delete ATTRS.internal_tree_id;outputNode={...outputNode,type:type,tagName:node.tagName.toLowerCase(),attributes:ATTRS,children:format(node.children,options),id:node.id||generateTreeId()};break;default:outputNode={type:type,content:node.content?.trim()};break}if(options.includePositions){outputNode.position=node.position}return outputNode})}export function renderFormatAttributes(attributes){var ret={};Object.entries(attributes).forEach(([attribute,val])=>{const parts=splitHead(attribute.trim(),"=");const key=parts[0];const value=typeof parts[1]==="string"?unquote(parts[1]):null;var getValue=value=>{try{return eval(value)}catch(error){return value}};ret[key]=getValue(value)});return ret}export const parseDefaults={voidTags:voidTags,closingTags:closingTags,childlessTags:childlessTags,closingTagAncestorBreakers:closingTagAncestorBreakers,includePositions:false};export function generateTreeId(){return"node-"+uuidv4()}export function xudaPrase(str,options=parseDefaults){const tokens=lexer(str,options);const nodes=parser(tokens,options);return format(nodes,{...parseDefaults,...options})}export function xudaStringify(ast,options){return toHTML(ast,{...parseDefaults,...options})}