Last active
February 24, 2025 16:37
-
-
Save estrattonbailey/096256c9e20623b67648db2412c0e60e to your computer and use it in GitHub Desktop.
React Shortcodes
This file contains hidden or 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
import React from 'react' | |
import { renderToString } from 'react-dom/server' | |
import parser from './shortcode-parser' | |
export const add = parser.add | |
const Aside = ({ children, config }) => { | |
/** | |
* config = { | |
* foo: true, | |
* bar: false, | |
* } | |
*/ | |
return ( | |
<div className="post__aside"> | |
{children} | |
</div> | |
) | |
} | |
parser.add('test', (children, config) => { | |
return renderToString(<Aside children={children} config={config}/>) | |
}) | |
/** | |
* For example | |
* [test foo="true" bar="false"]This is text content[/test] | |
*/ | |
export const parse = content => { | |
return parser.parse(content) | |
} |
This file contains hidden or 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
/* eslint-disable no-useless-escape */ | |
/** | |
* Shortcode Parser | |
* by mendezcode | |
* @see https://github.com/mendezcode/shortcode-parser | |
* | |
* Slightly modified for the browser, and refactored into ES6 | |
*/ | |
const shortcodes = {} | |
const pattern = { | |
attrs: /(\s+([a-z0-9\-_]+|([a-z0-9\-_]+)\s*=\s*([a-z0-9\-_]+|\d+\.\d+|'[^']*'|"[^"]*")))*/.toString().slice(1, -1), | |
slash: /\s*\/?\s*/.toString().slice(1, -1), | |
open: name => /\[\s*%s/.toString().slice(1, -1).replace('%s', name), | |
rightBracket: '\\]', | |
close: name => /\[\s*\/\s*%s\s*\]/.toString().slice(1, -1).replace('%s', name), | |
content: /(.|\n|)*?/.toString().slice(1, -1), | |
space: /\s*/.toString().slice(1, -1), | |
} | |
function typecast(val) { | |
val = val.trim().replace(/(^['"]|['"]$)/g, '') | |
if (/^\d+$/.test(val)) { | |
return parseInt(val, 10) | |
} else if (/^\d+\.\d+$/.test(val)) { | |
return parseFloat(val) | |
} else if (/^(true|false)$/.test(val)) { | |
return (val === 'true') | |
} else if (/^undefined$/.test(val)) { | |
return undefined | |
} else if (/^null$/i.test(val)) { | |
return null | |
} else { | |
return val | |
} | |
} | |
function closeTagString(name) { | |
return /^[^a-z0-9]/.test(name) ? `[${name[0].replace('$', '\\$')}]?${name.slice(1)}` : name | |
} | |
function parseShortcode(name, buf, inline) { | |
let regex | |
let match | |
let data = {} | |
let attr = {} | |
if (inline) { | |
regex = new RegExp('^' + | |
pattern.open(name) + | |
pattern.attrs + | |
pattern.space + | |
pattern.slash + | |
pattern.rightBracket, 'i' | |
) | |
} else { | |
regex = new RegExp('^' + | |
pattern.open(name) + | |
pattern.attrs + | |
pattern.space + | |
pattern.rightBracket, 'i' | |
) | |
} | |
while ((match = buf.match(regex)) !== null) { | |
const key = match[3] || match[2] | |
const val = match[4] || match[3] | |
const pattern = match[1] | |
if (pattern) { | |
const idx = buf.lastIndexOf(pattern) | |
attr[key] = (val !== undefined) ? typecast(val) : true | |
buf = buf.slice(0, idx) + buf.slice(idx + pattern.length) | |
} else { | |
break | |
} | |
} | |
attr = Object.keys(attr).reverse().reduce((prev, current) => { | |
prev[current] = attr[current] | |
return prev | |
}, {}) | |
buf = buf.replace(regex, '').replace(new RegExp(pattern.close(closeTagString(name))), '') | |
return { | |
attr: attr, | |
content: inline ? buf : buf.replace(/(^\n|\n$)/g, ''), | |
} | |
} | |
export default { | |
_shortcodes: shortcodes, | |
add(name, callback) { | |
shortcodes[name] = callback | |
}, | |
remove(name) { | |
delete shortcodes[name] | |
}, | |
parse(buf, extra = {}, context = shortcodes) { | |
for (let name in context) { | |
// Allow absence of first char if not alpha numeric. E.g. [#shortcode]...[/shortcode] | |
const regex = { | |
wrapper: new RegExp( | |
pattern.open(name) + | |
pattern.attrs + | |
pattern.rightBracket + | |
pattern.content + | |
pattern.close(closeTagString(name)), 'gi' | |
), | |
inline: new RegExp( | |
pattern.open(name) + | |
pattern.attrs + | |
pattern.slash + | |
pattern.rightBracket, 'gi' | |
), | |
} | |
let matches = buf.match(regex.wrapper) | |
if (matches) { | |
for (let m, data, i = 0, len = matches.length; i < len; i++) { | |
m = matches[i] | |
data = parseShortcode(name, m) | |
buf = buf.replace(m, context[name].call(null, data.content, data.attr, extra)) | |
} | |
} | |
matches = buf.match(regex.inline) | |
if (matches) { | |
let m | |
while ((m = matches.shift()) !== undefined) { | |
let data = parseShortcode(name, m, true) | |
buf = buf.replace(m, context[name].call(null, data.content, data.attr, extra)) | |
} | |
} | |
} | |
return buf | |
}, | |
parseInContext(buf, context, data) { | |
return this.parse(buf, data, context) | |
}, | |
} | |
/* eslint-enable no-useless-escape */ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment