Last active
June 1, 2021 12:22
-
-
Save ClassicOldSong/6bba3be1e73af51d50e077ee37b8de6d to your computer and use it in GitHub Desktop.
ef-ast-xml-compiler
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* Usage: | |
import parseEft from 'eft-parser' | |
import compileToXML from 'ef-ast-xml-compiler-with-data.js' | |
const ast = parseEft(` | |
>h1 | |
.Hello {{name}}! | |
-mountPoint | |
+listMountPoint | |
`) | |
const compiled = compileToXML(ast, {name: 'Yukino'}, {mountPoint: compileToXML(...), listMountPoint: [compileToXML(...)]}) | |
console.log(compiled) | |
*/ | |
const selfClosingTags = { | |
area: true, | |
base: true, | |
br: true, | |
col: true, | |
command: true, | |
embed: true, | |
hr: true, | |
img: true, | |
input: true, | |
keygen: true, | |
link: true, | |
menuitem: true, | |
meta: true, | |
param: true, | |
source: true, | |
track: true, | |
wbr: true | |
} | |
const mixStr = (strs, ...exprs) => { | |
let string = '' | |
for (let i = 0; i < exprs.length; i++) { | |
if (typeof exprs[i] === 'undefined') string += strs[i] | |
else string += (strs[i] + exprs[i]) | |
} | |
return string + strs[strs.length - 1] | |
} | |
const escapeXML = str => `${str}` | |
.replace(/&/g, "&") | |
.replace(/</g, "<") | |
.replace(/>/g, ">") | |
.replace(/"/g, """) | |
.replace(/'/g, "'") | |
const getVal = (pathArr, data) => pathArr.reduce((p, c) => (p ? p[c] : p), data) | |
const getEscapedVal = (expr, data) => { | |
const [pathArr, defaultVal] = expr | |
const val = getVal(pathArr, data) | |
if (typeof val === 'undefined') { | |
if (typeof defaultVal === 'undefined') return '' | |
return escapeXML(defaultVal) | |
} | |
return escapeXML(val) | |
} | |
const getAttr = (attr, val, data) => { | |
const attrValStrs = [` ${attr}`] | |
if (val) { | |
if (typeof val === 'string') { | |
attrValStrs.push(`="${escapeXML(val)}"`) | |
} else { | |
const [strs, ...exprs] = val | |
if (strs === 0) { | |
const [pathArr, defaultVal] = exprs[0] | |
const val = getVal(pathArr, data) | |
if (typeof val === 'undefined') { | |
/* eslint {max-depth: 'off'} */ | |
if (typeof defaultVal === 'undefined') attrValStrs.shift() | |
else { | |
attrValStrs.push('="') | |
attrValStrs.push(escapeXML(defaultVal)) | |
attrValStrs.push('"') | |
} | |
} else { | |
attrValStrs.push('="') | |
attrValStrs.push(escapeXML(val)) | |
attrValStrs.push('"') | |
} | |
} else { | |
attrValStrs.push('="') | |
const _exprs = exprs.map(expr => getEscapedVal(expr, data)) | |
attrValStrs.push(mixStr(strs, _exprs)) | |
attrValStrs.push('"') | |
} | |
} | |
} | |
return attrValStrs.join('') | |
} | |
const compileToXML = (ast, data = {}, mountPoints = {}) => { | |
// Handle static string | |
if (typeof ast === 'string') return escapeXML(ast) | |
// Handle mount points | |
const {n: mountPointName, t: mountPointType} = ast | |
if (typeof mountPointName === 'string' && typeof mountPointType === 'number') { | |
const mountPoint = mountPoints[mountPointName] | |
if (!mountPoint) return '' | |
if (mountPointType) { | |
return mountPoint.join('') | |
} | |
return mountPoint | |
} | |
// Handle if is an mustache | |
if (Array.isArray(ast[0])) { | |
return getEscapedVal(ast, data) | |
} | |
const [{t: tag, a: attrs}, ...children] = ast | |
const xmlStringFrags = [] | |
if (tag) xmlStringFrags.push(`<${tag}`) | |
if (attrs) xmlStringFrags.push(...Object.entries(attrs).map(([attr, val]) => getAttr(attr, val, data))) | |
if (children.length > 0) { | |
if (tag) xmlStringFrags.push('>') | |
xmlStringFrags.push(...children.map(item => compileToXML(item, data, mountPoints))) | |
if (tag) xmlStringFrags.push(`</${tag}>`) | |
} else if (tag) { | |
if (selfClosingTags[tag]) xmlStringFrags.push('/>') | |
else xmlStringFrags.push(`></${tag}>`) | |
} | |
return xmlStringFrags.join('') | |
} | |
export default compileToXML |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* Usage: | |
import parseEft from 'eft-parser' | |
import compileToXML from 'ef-ast-xml-renderer.js' | |
const ast = parseEft(` | |
>h1 | |
.Your efml | |
`) | |
const compiled = compileToXML(ast) | |
console.log(compiled) | |
*/ | |
const selfClosingTags = { | |
area: true, | |
base: true, | |
br: true, | |
col: true, | |
command: true, | |
embed: true, | |
hr: true, | |
img: true, | |
input: true, | |
keygen: true, | |
link: true, | |
menuitem: true, | |
meta: true, | |
param: true, | |
source: true, | |
track: true, | |
wbr: true | |
} | |
const escapeXML = str => str | |
.replace(/&/g, "&") | |
.replace(/</g, "<") | |
.replace(/>/g, ">") | |
.replace(/"/g, """) | |
.replace(/'/g, "'") | |
const getAttr = ([attr, val]) => { | |
if (val) return ` ${attr}="${escapeXML(val)}"` | |
return ` ${attr}` | |
} | |
const compileToXML = (ast) => { | |
if (typeof ast === 'string') return escapeXML(ast) | |
const [{t: tag, a: attrs}, ...children] = ast | |
const xmlStringFrags = [] | |
if (tag) xmlStringFrags.push(`<${tag}`) | |
if (attrs) xmlStringFrags.push(...Object.entries(attrs).map(getAttr)) | |
if (children.length > 0) { | |
if (tag) xmlStringFrags.push('>') | |
xmlStringFrags.push(...children.map(compileToXML)) | |
if (tag) xmlStringFrags.push(`</${tag}>`) | |
} else if (tag) { | |
if (selfClosingTags[tag]) xmlStringFrags.push('/>') | |
else xmlStringFrags.push(`></${tag}>`) | |
} | |
return xmlStringFrags.join('') | |
} | |
export default compileToXML |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment