Grabbed dndbeyond's source code

```
~/go/bin/sourcemapper -output ddb -jsurl https://media.dndbeyond.com/character-app/static/js/main.90aa78c5.js
```
This commit is contained in:
2025-05-28 11:50:03 -07:00
commit 8df9031d27
3592 changed files with 319051 additions and 0 deletions
@@ -0,0 +1 @@
module.exports = __WEBPACK_EXTERNAL_MODULE__297__;
+496
View File
@@ -0,0 +1,496 @@
'use strict';
const XHTMLEntities = require('./xhtml');
const hexNumber = /^[\da-fA-F]+$/;
const decimalNumber = /^\d+$/;
// The map to `acorn-jsx` tokens from `acorn` namespace objects.
const acornJsxMap = new WeakMap();
// Get the original tokens for the given `acorn` namespace object.
function getJsxTokens(acorn) {
acorn = acorn.Parser.acorn || acorn;
let acornJsx = acornJsxMap.get(acorn);
if (!acornJsx) {
const tt = acorn.tokTypes;
const TokContext = acorn.TokContext;
const TokenType = acorn.TokenType;
const tc_oTag = new TokContext('<tag', false);
const tc_cTag = new TokContext('</tag', false);
const tc_expr = new TokContext('<tag>...</tag>', true, true);
const tokContexts = {
tc_oTag: tc_oTag,
tc_cTag: tc_cTag,
tc_expr: tc_expr
};
const tokTypes = {
jsxName: new TokenType('jsxName'),
jsxText: new TokenType('jsxText', {beforeExpr: true}),
jsxTagStart: new TokenType('jsxTagStart', {startsExpr: true}),
jsxTagEnd: new TokenType('jsxTagEnd')
};
tokTypes.jsxTagStart.updateContext = function() {
this.context.push(tc_expr); // treat as beginning of JSX expression
this.context.push(tc_oTag); // start opening tag context
this.exprAllowed = false;
};
tokTypes.jsxTagEnd.updateContext = function(prevType) {
let out = this.context.pop();
if (out === tc_oTag && prevType === tt.slash || out === tc_cTag) {
this.context.pop();
this.exprAllowed = this.curContext() === tc_expr;
} else {
this.exprAllowed = true;
}
};
acornJsx = { tokContexts: tokContexts, tokTypes: tokTypes };
acornJsxMap.set(acorn, acornJsx);
}
return acornJsx;
}
// Transforms JSX element name to string.
function getQualifiedJSXName(object) {
if (!object)
return object;
if (object.type === 'JSXIdentifier')
return object.name;
if (object.type === 'JSXNamespacedName')
return object.namespace.name + ':' + object.name.name;
if (object.type === 'JSXMemberExpression')
return getQualifiedJSXName(object.object) + '.' +
getQualifiedJSXName(object.property);
}
module.exports = function(options) {
options = options || {};
return function(Parser) {
return plugin({
allowNamespaces: options.allowNamespaces !== false,
allowNamespacedObjects: !!options.allowNamespacedObjects,
autoCloseVoidElements: !!options.autoCloseVoidElements,
}, Parser);
};
};
// This is `tokTypes` of the peer dep.
// This can be different instances from the actual `tokTypes` this plugin uses.
Object.defineProperty(module.exports, "tokTypes", {
get: function get_tokTypes() {
return getJsxTokens(require("acorn")).tokTypes;
},
configurable: true,
enumerable: true
});
function plugin(options, Parser) {
const acorn = Parser.acorn || require("acorn");
const acornJsx = getJsxTokens(acorn);
const tt = acorn.tokTypes;
const tok = acornJsx.tokTypes;
const tokContexts = acorn.tokContexts;
const tc_oTag = acornJsx.tokContexts.tc_oTag;
const tc_cTag = acornJsx.tokContexts.tc_cTag;
const tc_expr = acornJsx.tokContexts.tc_expr;
const isNewLine = acorn.isNewLine;
const isIdentifierStart = acorn.isIdentifierStart;
const isIdentifierChar = acorn.isIdentifierChar;
return class extends Parser {
// Expose actual `tokTypes` and `tokContexts` to other plugins.
static get acornJsx() {
return acornJsx;
}
// Reads inline JSX contents token.
jsx_readToken() {
let out = '', chunkStart = this.pos;
for (;;) {
if (this.pos >= this.input.length)
this.raise(this.start, 'Unterminated JSX contents');
let ch = this.input.charCodeAt(this.pos);
switch (ch) {
case 60: // '<'
case 123: // '{'
if (this.pos === this.start) {
if (ch === 60 && this.exprAllowed) {
++this.pos;
return this.finishToken(tok.jsxTagStart);
}
return this.getTokenFromCode(ch);
}
out += this.input.slice(chunkStart, this.pos);
return this.finishToken(tok.jsxText, out);
case 38: // '&'
out += this.input.slice(chunkStart, this.pos);
out += this.jsx_readEntity();
chunkStart = this.pos;
break;
case 62: // '>'
case 125: // '}'
this.raise(
this.pos,
"Unexpected token `" + this.input[this.pos] + "`. Did you mean `" +
(ch === 62 ? "&gt;" : "&rbrace;") + "` or " + "`{\"" + this.input[this.pos] + "\"}" + "`?"
);
default:
if (isNewLine(ch)) {
out += this.input.slice(chunkStart, this.pos);
out += this.jsx_readNewLine(true);
chunkStart = this.pos;
} else {
++this.pos;
}
}
}
}
jsx_readNewLine(normalizeCRLF) {
let ch = this.input.charCodeAt(this.pos);
let out;
++this.pos;
if (ch === 13 && this.input.charCodeAt(this.pos) === 10) {
++this.pos;
out = normalizeCRLF ? '\n' : '\r\n';
} else {
out = String.fromCharCode(ch);
}
if (this.options.locations) {
++this.curLine;
this.lineStart = this.pos;
}
return out;
}
jsx_readString(quote) {
let out = '', chunkStart = ++this.pos;
for (;;) {
if (this.pos >= this.input.length)
this.raise(this.start, 'Unterminated string constant');
let ch = this.input.charCodeAt(this.pos);
if (ch === quote) break;
if (ch === 38) { // '&'
out += this.input.slice(chunkStart, this.pos);
out += this.jsx_readEntity();
chunkStart = this.pos;
} else if (isNewLine(ch)) {
out += this.input.slice(chunkStart, this.pos);
out += this.jsx_readNewLine(false);
chunkStart = this.pos;
} else {
++this.pos;
}
}
out += this.input.slice(chunkStart, this.pos++);
return this.finishToken(tt.string, out);
}
jsx_readEntity() {
let str = '', count = 0, entity;
let ch = this.input[this.pos];
if (ch !== '&')
this.raise(this.pos, 'Entity must start with an ampersand');
let startPos = ++this.pos;
while (this.pos < this.input.length && count++ < 10) {
ch = this.input[this.pos++];
if (ch === ';') {
if (str[0] === '#') {
if (str[1] === 'x') {
str = str.substr(2);
if (hexNumber.test(str))
entity = String.fromCharCode(parseInt(str, 16));
} else {
str = str.substr(1);
if (decimalNumber.test(str))
entity = String.fromCharCode(parseInt(str, 10));
}
} else {
entity = XHTMLEntities[str];
}
break;
}
str += ch;
}
if (!entity) {
this.pos = startPos;
return '&';
}
return entity;
}
// Read a JSX identifier (valid tag or attribute name).
//
// Optimized version since JSX identifiers can't contain
// escape characters and so can be read as single slice.
// Also assumes that first character was already checked
// by isIdentifierStart in readToken.
jsx_readWord() {
let ch, start = this.pos;
do {
ch = this.input.charCodeAt(++this.pos);
} while (isIdentifierChar(ch) || ch === 45); // '-'
return this.finishToken(tok.jsxName, this.input.slice(start, this.pos));
}
// Parse next token as JSX identifier
jsx_parseIdentifier() {
let node = this.startNode();
if (this.type === tok.jsxName)
node.name = this.value;
else if (this.type.keyword)
node.name = this.type.keyword;
else
this.unexpected();
this.next();
return this.finishNode(node, 'JSXIdentifier');
}
// Parse namespaced identifier.
jsx_parseNamespacedName() {
let startPos = this.start, startLoc = this.startLoc;
let name = this.jsx_parseIdentifier();
if (!options.allowNamespaces || !this.eat(tt.colon)) return name;
var node = this.startNodeAt(startPos, startLoc);
node.namespace = name;
node.name = this.jsx_parseIdentifier();
return this.finishNode(node, 'JSXNamespacedName');
}
// Parses element name in any form - namespaced, member
// or single identifier.
jsx_parseElementName() {
if (this.type === tok.jsxTagEnd) return '';
let startPos = this.start, startLoc = this.startLoc;
let node = this.jsx_parseNamespacedName();
if (this.type === tt.dot && node.type === 'JSXNamespacedName' && !options.allowNamespacedObjects) {
this.unexpected();
}
while (this.eat(tt.dot)) {
let newNode = this.startNodeAt(startPos, startLoc);
newNode.object = node;
newNode.property = this.jsx_parseIdentifier();
node = this.finishNode(newNode, 'JSXMemberExpression');
}
return node;
}
// Parses any type of JSX attribute value.
jsx_parseAttributeValue() {
switch (this.type) {
case tt.braceL:
let node = this.jsx_parseExpressionContainer();
if (node.expression.type === 'JSXEmptyExpression')
this.raise(node.start, 'JSX attributes must only be assigned a non-empty expression');
return node;
case tok.jsxTagStart:
case tt.string:
return this.parseExprAtom();
default:
this.raise(this.start, 'JSX value should be either an expression or a quoted JSX text');
}
}
// JSXEmptyExpression is unique type since it doesn't actually parse anything,
// and so it should start at the end of last read token (left brace) and finish
// at the beginning of the next one (right brace).
jsx_parseEmptyExpression() {
let node = this.startNodeAt(this.lastTokEnd, this.lastTokEndLoc);
return this.finishNodeAt(node, 'JSXEmptyExpression', this.start, this.startLoc);
}
// Parses JSX expression enclosed into curly brackets.
jsx_parseExpressionContainer() {
let node = this.startNode();
this.next();
node.expression = this.type === tt.braceR
? this.jsx_parseEmptyExpression()
: this.parseExpression();
this.expect(tt.braceR);
return this.finishNode(node, 'JSXExpressionContainer');
}
// Parses following JSX attribute name-value pair.
jsx_parseAttribute() {
let node = this.startNode();
if (this.eat(tt.braceL)) {
this.expect(tt.ellipsis);
node.argument = this.parseMaybeAssign();
this.expect(tt.braceR);
return this.finishNode(node, 'JSXSpreadAttribute');
}
node.name = this.jsx_parseNamespacedName();
node.value = this.eat(tt.eq) ? this.jsx_parseAttributeValue() : null;
return this.finishNode(node, 'JSXAttribute');
}
// Parses JSX opening tag starting after '<'.
jsx_parseOpeningElementAt(startPos, startLoc) {
let node = this.startNodeAt(startPos, startLoc);
node.attributes = [];
let nodeName = this.jsx_parseElementName();
if (nodeName) node.name = nodeName;
while (this.type !== tt.slash && this.type !== tok.jsxTagEnd)
node.attributes.push(this.jsx_parseAttribute());
node.selfClosing = this.eat(tt.slash);
this.expect(tok.jsxTagEnd);
const VOID_ELEMENTS = [
'area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'keygen',
'link', 'menuitem', 'meta', 'param', 'source', 'track', 'wbr'
]
if (options.autoCloseVoidElements && nodeName && VOID_ELEMENTS.includes(nodeName.name)) {
node.selfClosing = true;
}
return this.finishNode(node, nodeName ? 'JSXOpeningElement' : 'JSXOpeningFragment');
}
// Parses JSX closing tag starting after '</'.
jsx_parseClosingElementAt(startPos, startLoc) {
let node = this.startNodeAt(startPos, startLoc);
let nodeName = this.jsx_parseElementName();
if (nodeName) node.name = nodeName;
this.expect(tok.jsxTagEnd);
return this.finishNode(node, nodeName ? 'JSXClosingElement' : 'JSXClosingFragment');
}
// Parses entire JSX element, including it's opening tag
// (starting after '<'), attributes, contents and closing tag.
jsx_parseElementAt(startPos, startLoc) {
let node = this.startNodeAt(startPos, startLoc);
let children = [];
let openingElement = this.jsx_parseOpeningElementAt(startPos, startLoc);
let closingElement = null;
if (!openingElement.selfClosing) {
contents: for (;;) {
switch (this.type) {
case tok.jsxTagStart:
startPos = this.start; startLoc = this.startLoc;
this.next();
if (this.eat(tt.slash)) {
closingElement = this.jsx_parseClosingElementAt(startPos, startLoc);
break contents;
}
children.push(this.jsx_parseElementAt(startPos, startLoc));
break;
case tok.jsxText:
children.push(this.parseExprAtom());
break;
case tt.braceL:
children.push(this.jsx_parseExpressionContainer());
break;
default:
this.unexpected();
}
}
if (getQualifiedJSXName(closingElement.name) !== getQualifiedJSXName(openingElement.name)) {
this.raise(
closingElement.start,
'Expected corresponding JSX closing tag for <' + getQualifiedJSXName(openingElement.name) + '>');
}
}
let fragmentOrElement = openingElement.name ? 'Element' : 'Fragment';
node['opening' + fragmentOrElement] = openingElement;
node['closing' + fragmentOrElement] = closingElement;
node.children = children;
if (this.type === tt.relational && this.value === "<") {
this.raise(this.start, "Adjacent JSX elements must be wrapped in an enclosing tag");
}
return this.finishNode(node, 'JSX' + fragmentOrElement);
}
// Parse JSX text
jsx_parseText() {
let node = this.parseLiteral(this.value);
node.type = "JSXText";
return node;
}
// Parses entire JSX element from current position.
jsx_parseElement() {
let startPos = this.start, startLoc = this.startLoc;
this.next();
return this.jsx_parseElementAt(startPos, startLoc);
}
parseExprAtom(refShortHandDefaultPos) {
if (this.type === tok.jsxText)
return this.jsx_parseText();
else if (this.type === tok.jsxTagStart)
return this.jsx_parseElement();
else
return super.parseExprAtom(refShortHandDefaultPos);
}
readToken(code) {
let context = this.curContext();
if (context === tc_expr) return this.jsx_readToken();
if (context === tc_oTag || context === tc_cTag) {
if (isIdentifierStart(code)) return this.jsx_readWord();
if (code == 62) {
++this.pos;
return this.finishToken(tok.jsxTagEnd);
}
if ((code === 34 || code === 39) && context == tc_oTag)
return this.jsx_readString(code);
}
if (code === 60 && this.exprAllowed && this.input.charCodeAt(this.pos + 1) !== 33) {
++this.pos;
return this.finishToken(tok.jsxTagStart);
}
return super.readToken(code);
}
updateContext(prevType) {
if (this.type == tt.braceL) {
var curContext = this.curContext();
if (curContext == tc_oTag) this.context.push(tokContexts.b_expr);
else if (curContext == tc_expr) this.context.push(tokContexts.b_tmpl);
else super.updateContext(prevType);
this.exprAllowed = true;
} else if (this.type === tt.slash && prevType === tok.jsxTagStart) {
this.context.length -= 2; // do not consider JSX expr -> JSX open tag -> ... anymore
this.context.push(tc_cTag); // reconsider as closing tag context
this.exprAllowed = false;
} else {
return super.updateContext(prevType);
}
}
};
}
+255
View File
@@ -0,0 +1,255 @@
module.exports = {
quot: '\u0022',
amp: '&',
apos: '\u0027',
lt: '<',
gt: '>',
nbsp: '\u00A0',
iexcl: '\u00A1',
cent: '\u00A2',
pound: '\u00A3',
curren: '\u00A4',
yen: '\u00A5',
brvbar: '\u00A6',
sect: '\u00A7',
uml: '\u00A8',
copy: '\u00A9',
ordf: '\u00AA',
laquo: '\u00AB',
not: '\u00AC',
shy: '\u00AD',
reg: '\u00AE',
macr: '\u00AF',
deg: '\u00B0',
plusmn: '\u00B1',
sup2: '\u00B2',
sup3: '\u00B3',
acute: '\u00B4',
micro: '\u00B5',
para: '\u00B6',
middot: '\u00B7',
cedil: '\u00B8',
sup1: '\u00B9',
ordm: '\u00BA',
raquo: '\u00BB',
frac14: '\u00BC',
frac12: '\u00BD',
frac34: '\u00BE',
iquest: '\u00BF',
Agrave: '\u00C0',
Aacute: '\u00C1',
Acirc: '\u00C2',
Atilde: '\u00C3',
Auml: '\u00C4',
Aring: '\u00C5',
AElig: '\u00C6',
Ccedil: '\u00C7',
Egrave: '\u00C8',
Eacute: '\u00C9',
Ecirc: '\u00CA',
Euml: '\u00CB',
Igrave: '\u00CC',
Iacute: '\u00CD',
Icirc: '\u00CE',
Iuml: '\u00CF',
ETH: '\u00D0',
Ntilde: '\u00D1',
Ograve: '\u00D2',
Oacute: '\u00D3',
Ocirc: '\u00D4',
Otilde: '\u00D5',
Ouml: '\u00D6',
times: '\u00D7',
Oslash: '\u00D8',
Ugrave: '\u00D9',
Uacute: '\u00DA',
Ucirc: '\u00DB',
Uuml: '\u00DC',
Yacute: '\u00DD',
THORN: '\u00DE',
szlig: '\u00DF',
agrave: '\u00E0',
aacute: '\u00E1',
acirc: '\u00E2',
atilde: '\u00E3',
auml: '\u00E4',
aring: '\u00E5',
aelig: '\u00E6',
ccedil: '\u00E7',
egrave: '\u00E8',
eacute: '\u00E9',
ecirc: '\u00EA',
euml: '\u00EB',
igrave: '\u00EC',
iacute: '\u00ED',
icirc: '\u00EE',
iuml: '\u00EF',
eth: '\u00F0',
ntilde: '\u00F1',
ograve: '\u00F2',
oacute: '\u00F3',
ocirc: '\u00F4',
otilde: '\u00F5',
ouml: '\u00F6',
divide: '\u00F7',
oslash: '\u00F8',
ugrave: '\u00F9',
uacute: '\u00FA',
ucirc: '\u00FB',
uuml: '\u00FC',
yacute: '\u00FD',
thorn: '\u00FE',
yuml: '\u00FF',
OElig: '\u0152',
oelig: '\u0153',
Scaron: '\u0160',
scaron: '\u0161',
Yuml: '\u0178',
fnof: '\u0192',
circ: '\u02C6',
tilde: '\u02DC',
Alpha: '\u0391',
Beta: '\u0392',
Gamma: '\u0393',
Delta: '\u0394',
Epsilon: '\u0395',
Zeta: '\u0396',
Eta: '\u0397',
Theta: '\u0398',
Iota: '\u0399',
Kappa: '\u039A',
Lambda: '\u039B',
Mu: '\u039C',
Nu: '\u039D',
Xi: '\u039E',
Omicron: '\u039F',
Pi: '\u03A0',
Rho: '\u03A1',
Sigma: '\u03A3',
Tau: '\u03A4',
Upsilon: '\u03A5',
Phi: '\u03A6',
Chi: '\u03A7',
Psi: '\u03A8',
Omega: '\u03A9',
alpha: '\u03B1',
beta: '\u03B2',
gamma: '\u03B3',
delta: '\u03B4',
epsilon: '\u03B5',
zeta: '\u03B6',
eta: '\u03B7',
theta: '\u03B8',
iota: '\u03B9',
kappa: '\u03BA',
lambda: '\u03BB',
mu: '\u03BC',
nu: '\u03BD',
xi: '\u03BE',
omicron: '\u03BF',
pi: '\u03C0',
rho: '\u03C1',
sigmaf: '\u03C2',
sigma: '\u03C3',
tau: '\u03C4',
upsilon: '\u03C5',
phi: '\u03C6',
chi: '\u03C7',
psi: '\u03C8',
omega: '\u03C9',
thetasym: '\u03D1',
upsih: '\u03D2',
piv: '\u03D6',
ensp: '\u2002',
emsp: '\u2003',
thinsp: '\u2009',
zwnj: '\u200C',
zwj: '\u200D',
lrm: '\u200E',
rlm: '\u200F',
ndash: '\u2013',
mdash: '\u2014',
lsquo: '\u2018',
rsquo: '\u2019',
sbquo: '\u201A',
ldquo: '\u201C',
rdquo: '\u201D',
bdquo: '\u201E',
dagger: '\u2020',
Dagger: '\u2021',
bull: '\u2022',
hellip: '\u2026',
permil: '\u2030',
prime: '\u2032',
Prime: '\u2033',
lsaquo: '\u2039',
rsaquo: '\u203A',
oline: '\u203E',
frasl: '\u2044',
euro: '\u20AC',
image: '\u2111',
weierp: '\u2118',
real: '\u211C',
trade: '\u2122',
alefsym: '\u2135',
larr: '\u2190',
uarr: '\u2191',
rarr: '\u2192',
darr: '\u2193',
harr: '\u2194',
crarr: '\u21B5',
lArr: '\u21D0',
uArr: '\u21D1',
rArr: '\u21D2',
dArr: '\u21D3',
hArr: '\u21D4',
forall: '\u2200',
part: '\u2202',
exist: '\u2203',
empty: '\u2205',
nabla: '\u2207',
isin: '\u2208',
notin: '\u2209',
ni: '\u220B',
prod: '\u220F',
sum: '\u2211',
minus: '\u2212',
lowast: '\u2217',
radic: '\u221A',
prop: '\u221D',
infin: '\u221E',
ang: '\u2220',
and: '\u2227',
or: '\u2228',
cap: '\u2229',
cup: '\u222A',
'int': '\u222B',
there4: '\u2234',
sim: '\u223C',
cong: '\u2245',
asymp: '\u2248',
ne: '\u2260',
equiv: '\u2261',
le: '\u2264',
ge: '\u2265',
sub: '\u2282',
sup: '\u2283',
nsub: '\u2284',
sube: '\u2286',
supe: '\u2287',
oplus: '\u2295',
otimes: '\u2297',
perp: '\u22A5',
sdot: '\u22C5',
lceil: '\u2308',
rceil: '\u2309',
lfloor: '\u230A',
rfloor: '\u230B',
lang: '\u2329',
rang: '\u232A',
loz: '\u25CA',
spades: '\u2660',
clubs: '\u2663',
hearts: '\u2665',
diams: '\u2666'
};
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,349 @@
/* global JSX */
import * as Acorn from 'acorn'
import * as AcornJSX from 'acorn-jsx'
import React, { Fragment, ComponentType, ExoticComponent } from 'react'
import ATTRIBUTES from '../constants/attributeNames'
import { canHaveChildren, canHaveWhitespace } from '../constants/specialTags'
import { randomHash } from '../helpers/hash'
import { parseStyle } from '../helpers/parseStyle'
import { resolvePath } from '../helpers/resolvePath'
type ParsedJSX = JSX.Element | boolean | string
type ParsedTree = ParsedJSX | ParsedJSX[] | null
export type TProps = {
allowUnknownElements?: boolean,
autoCloseVoidElements?: boolean,
bindings?: { [key: string]: unknown; },
blacklistedAttrs?: Array<string | RegExp>,
blacklistedTags?: string[],
className?: string,
components?: Record<string, ComponentType | ExoticComponent>,
componentsOnly?: boolean,
disableFragments?: boolean,
disableKeyGeneration?: boolean,
jsx?: string,
onError?: (error: Error) => void,
showWarnings?: boolean,
renderError?: (props: { error: string }) => JSX.Element | null,
renderInWrapper?: boolean,
renderUnrecognized?: (tagName: string) => JSX.Element | null,
}
type Scope = Record<string, any>
/* eslint-disable consistent-return */
export default class JsxParser extends React.Component<TProps> {
static displayName = 'JsxParser'
static defaultProps: TProps = {
allowUnknownElements: true,
autoCloseVoidElements: false,
bindings: {},
blacklistedAttrs: [/^on.+/i],
blacklistedTags: ['script'],
className: '',
components: {},
componentsOnly: false,
disableFragments: false,
disableKeyGeneration: false,
jsx: '',
onError: () => { },
showWarnings: false,
renderError: undefined,
renderInWrapper: true,
renderUnrecognized: () => null,
}
private ParsedChildren: ParsedTree = null
#parseJSX = (jsx: string): JSX.Element | JSX.Element[] | null => {
const parser = Acorn.Parser.extend(AcornJSX.default({
autoCloseVoidElements: this.props.autoCloseVoidElements,
}))
const wrappedJsx = `<root>${jsx}</root>`
let parsed: AcornJSX.Expression[] = []
try {
// @ts-ignore - AcornJsx doesn't have typescript typings
parsed = parser.parse(wrappedJsx, { ecmaVersion: 'latest' })
// @ts-ignore - AcornJsx doesn't have typescript typings
parsed = parsed.body[0].expression.children || []
} catch (error) {
if (this.props.showWarnings) console.warn(error) // eslint-disable-line no-console
if (this.props.onError) this.props.onError(error)
if (this.props.renderError) {
return this.props.renderError({ error: String(error) })
}
return null
}
return parsed.map(p => this.#parseExpression(p)).filter(Boolean)
}
#parseExpression = (expression: AcornJSX.Expression, scope?: Scope): any => {
switch (expression.type) {
case 'JSXAttribute':
if (expression.value === null) return true
return this.#parseExpression(expression.value, scope)
case 'JSXElement':
case 'JSXFragment':
return this.#parseElement(expression, scope)
case 'JSXExpressionContainer':
return this.#parseExpression(expression.expression, scope)
case 'JSXText':
const key = this.props.disableKeyGeneration ? undefined : randomHash()
return this.props.disableFragments
? expression.value
: <Fragment key={key}>{expression.value}</Fragment>
case 'ArrayExpression':
return expression.elements.map(ele => this.#parseExpression(ele, scope)) as ParsedTree
case 'BinaryExpression':
/* eslint-disable eqeqeq,max-len */
switch (expression.operator) {
case '-': return this.#parseExpression(expression.left) - this.#parseExpression(expression.right)
case '!=': return this.#parseExpression(expression.left) != this.#parseExpression(expression.right)
case '!==': return this.#parseExpression(expression.left) !== this.#parseExpression(expression.right)
case '*': return this.#parseExpression(expression.left) * this.#parseExpression(expression.right)
case '**': return this.#parseExpression(expression.left) ** this.#parseExpression(expression.right)
case '/': return this.#parseExpression(expression.left) / this.#parseExpression(expression.right)
case '%': return this.#parseExpression(expression.left) % this.#parseExpression(expression.right)
case '+': return this.#parseExpression(expression.left) + this.#parseExpression(expression.right)
case '<': return this.#parseExpression(expression.left) < this.#parseExpression(expression.right)
case '<=': return this.#parseExpression(expression.left) <= this.#parseExpression(expression.right)
case '==': return this.#parseExpression(expression.left) == this.#parseExpression(expression.right)
case '===': return this.#parseExpression(expression.left) === this.#parseExpression(expression.right)
case '>': return this.#parseExpression(expression.left) > this.#parseExpression(expression.right)
case '>=': return this.#parseExpression(expression.left) >= this.#parseExpression(expression.right)
/* eslint-enable eqeqeq,max-len */
}
return undefined
case 'CallExpression':
const parsedCallee = this.#parseExpression(expression.callee)
if (parsedCallee === undefined) {
this.props.onError!(new Error(`The expression '${expression.callee}' could not be resolved, resulting in an undefined return value.`))
return undefined
}
return parsedCallee(...expression.arguments.map(
arg => this.#parseExpression(arg, expression.callee),
))
case 'ConditionalExpression':
return this.#parseExpression(expression.test)
? this.#parseExpression(expression.consequent)
: this.#parseExpression(expression.alternate)
case 'ExpressionStatement':
return this.#parseExpression(expression.expression)
case 'Identifier':
if (scope && expression.name in scope) {
return scope[expression.name]
}
return (this.props.bindings || {})[expression.name]
case 'Literal':
return expression.value
case 'LogicalExpression':
const left = this.#parseExpression(expression.left)
if (expression.operator === '||' && left) return left
if ((expression.operator === '&&' && left) || (expression.operator === '||' && !left)) {
return this.#parseExpression(expression.right)
}
return false
case 'MemberExpression':
return this.#parseMemberExpression(expression, scope)
case 'ObjectExpression':
const object: Record<string, any> = {}
expression.properties.forEach(prop => {
object[prop.key.name! || prop.key.value!] = this.#parseExpression(prop.value)
})
return object
case 'TemplateElement':
return expression.value.cooked
case 'TemplateLiteral':
return [...expression.expressions, ...expression.quasis]
.sort((a, b) => {
if (a.start < b.start) return -1
return 1
})
.map(item => this.#parseExpression(item))
.join('')
case 'UnaryExpression':
switch (expression.operator) {
case '+': return expression.argument.value
case '-': return -expression.argument.value
case '!': return !expression.argument.value
}
return undefined
case 'ArrowFunctionExpression':
if (expression.async || expression.generator) {
this.props.onError?.(new Error('Async and generator arrow functions are not supported.'))
}
return (...args: any[]) : any => {
const functionScope: Record<string, any> = {}
expression.params.forEach((param, idx) => {
functionScope[param.name] = args[idx]
})
return this.#parseExpression(expression.body, functionScope)
}
}
}
#parseMemberExpression = (expression: AcornJSX.MemberExpression, scope?: Scope): any => {
// eslint-disable-next-line prefer-destructuring
let { object } = expression
const path = [expression.property?.name ?? JSON.parse(expression.property?.raw ?? '""')]
if (expression.object.type !== 'Literal') {
while (object && ['MemberExpression', 'Literal'].includes(object?.type)) {
const { property } = (object as AcornJSX.MemberExpression)
if ((object as AcornJSX.MemberExpression).computed) {
path.unshift(this.#parseExpression(property!, scope))
} else {
path.unshift(property?.name ?? JSON.parse(property?.raw ?? '""'))
}
object = (object as AcornJSX.MemberExpression).object
}
}
const target = this.#parseExpression(object, scope)
try {
let parent = target
const member = path.reduce((value, next) => {
parent = value
return value[next]
}, target)
if (typeof member === 'function') return member.bind(parent)
return member
} catch {
const name = (object as AcornJSX.MemberExpression)?.name || 'unknown'
this.props.onError!(new Error(`Unable to parse ${name}["${path.join('"]["')}"]}`))
}
}
#parseName = (element: AcornJSX.JSXIdentifier | AcornJSX.JSXMemberExpression): string => {
if (element.type === 'JSXIdentifier') { return element.name }
return `${this.#parseName(element.object)}.${this.#parseName(element.property)}`
}
#parseElement = (
element: AcornJSX.JSXElement | AcornJSX.JSXFragment,
scope?: Scope,
): JSX.Element | JSX.Element[] | null => {
const { allowUnknownElements, components, componentsOnly, onError } = this.props
const { children: childNodes = [] } = element
const openingTag = element.type === 'JSXElement'
? element.openingElement
: element.openingFragment
const { attributes = [] } = openingTag
const name = element.type === 'JSXElement'
? this.#parseName(openingTag.name)
: ''
const blacklistedAttrs = (this.props.blacklistedAttrs || [])
.map(attr => (attr instanceof RegExp ? attr : new RegExp(attr, 'i')))
const blacklistedTags = (this.props.blacklistedTags || [])
.map(tag => tag.trim().toLowerCase()).filter(Boolean)
if (/^(html|head|body)$/i.test(name)) {
return childNodes.map(c => this.#parseElement(c, scope)) as JSX.Element[]
}
const tagName = name.trim().toLowerCase()
if (blacklistedTags.indexOf(tagName) !== -1) {
onError!(new Error(`The tag <${name}> is blacklisted, and will not be rendered.`))
return null
}
if (name !== '' && !resolvePath(components, name)) {
if (componentsOnly) {
onError!(new Error(`The component <${name}> is unrecognized, and will not be rendered.`))
return this.props.renderUnrecognized!(name)
}
if (!allowUnknownElements && document.createElement(name) instanceof HTMLUnknownElement) {
onError!(new Error(`The tag <${name}> is unrecognized in this browser, and will not be rendered.`))
return this.props.renderUnrecognized!(name)
}
}
let children
const component = element.type === 'JSXElement'
? resolvePath(components, name)
: Fragment
if (component || canHaveChildren(name)) {
children = childNodes.map(node => this.#parseExpression(node, scope))
if (!component && !canHaveWhitespace(name)) {
children = children.filter(child => (
typeof child !== 'string' || !/^\s*$/.test(child)
))
}
if (children.length === 0) {
children = undefined
} else if (children.length === 1) {
[children] = children
} else if (children.length > 1 && !this.props.disableKeyGeneration) {
// Add `key` to any child that is a react element (by checking if it has `.type`) if one
// does not already exist.
children = children.map((child, key) => (
(child?.type && !child?.key) ? { ...child, key: child.key || key } : child
))
}
}
const props: { [key: string]: any } = {
key: this.props.disableKeyGeneration ? undefined : randomHash(),
}
attributes.forEach( // eslint-disable-next-line max-len
(expr: AcornJSX.JSXAttribute | AcornJSX.JSXAttributeExpression | AcornJSX.JSXSpreadAttribute) => {
if (expr.type === 'JSXAttribute') {
const rawName = expr.name.name
const attributeName = ATTRIBUTES[rawName] || rawName
// if the value is null, this is an implicitly "true" prop, such as readOnly
const value = this.#parseExpression(expr, scope)
const matches = blacklistedAttrs.filter(re => re.test(attributeName))
if (matches.length === 0) {
props[attributeName] = value
}
} else if (
(expr.type === 'JSXSpreadAttribute' && expr.argument.type === 'Identifier')
|| expr.argument!.type === 'MemberExpression'
) {
const value = this.#parseExpression(expr.argument!, scope)
if (typeof value === 'object') {
Object.keys(value).forEach(rawName => {
const attributeName: string = ATTRIBUTES[rawName] || rawName
const matches = blacklistedAttrs.filter(re => re.test(attributeName))
if (matches.length === 0) {
props[attributeName] = value[rawName]
}
})
}
}
},
)
if (typeof props.style === 'string') {
props.style = parseStyle(props.style)
}
const lowerName = name.toLowerCase()
if (lowerName === 'option') {
children = children.props.children
}
return React.createElement(component || lowerName, props, children)
}
render = (): JSX.Element => {
const jsx = (this.props.jsx || '').trim().replace(/<!DOCTYPE([^>]*)>/g, '')
this.ParsedChildren = this.#parseJSX(jsx)
const className = [...new Set(['jsx-parser', ...String(this.props.className).split(' ')])]
.filter(Boolean)
.join(' ')
return (
this.props.renderInWrapper
? <div className={className}>{this.ParsedChildren}</div>
: <>{this.ParsedChildren}</>
)
}
}
/* eslint-enable consistent-return */
@@ -0,0 +1,7 @@
export default {
class: 'className',
for: 'htmlFor',
maxlength: 'maxLength',
colspan: 'colSpan',
rowspan: 'rowSpan',
} as Record<string, string>
@@ -0,0 +1,35 @@
const VOID_ELEMENTS = [
'area',
'base',
'br',
'col',
'embed',
'hr',
'img',
'input',
'keygen',
'link',
'menuitem',
'meta',
'param',
'source',
'track',
'wbr',
]
const NO_WHITESPACE = [
'table',
'tbody',
'tfoot',
'thead',
'tr',
]
export default VOID_ELEMENTS
export function canHaveChildren(tagName: string): boolean {
return VOID_ELEMENTS.indexOf(tagName.toLowerCase()) === -1
}
export function canHaveWhitespace(tagName: string): boolean {
return NO_WHITESPACE.indexOf(tagName.toLowerCase()) !== -1
}
@@ -0,0 +1,15 @@
/**
* Converts a string from other cases to camelCase
* @param string the value to camelCase
* @example
* camelCase('foo-bar') // 'fooBar'
*/
export const camelCase = (string: string) => string
.replace(/([A-Z])([A-Z])/g, '$1 $2')
.replace(/([a-z])([A-Z])/g, '$1 $2')
.replace(/[^a-zA-Z\u00C0-\u00ff]/g, ' ')
.toLowerCase()
.split(' ')
.filter(value => value)
.map((s, i) => (i > 0 ? s[0].toUpperCase() + s.slice(1) : s))
.join('')
@@ -0,0 +1,21 @@
/**
* Hashes a value
* @param value the value to hash
* @param radix the base-n to hash into (default 16)
*/
export const hash = (value: string = '', radix: number = 16): string => {
const string = String(value)
let h = 0
string.split('').forEach((char: string) => {
/* eslint-disable no-bitwise */
h = ((h << 5) - h) + char.charCodeAt(0)
h &= h // Convert to 32-bit integer
/* eslint-enable no-bitwise */
})
return Math.abs(h).toString(radix)
}
/**
* Hashes a Math.random() value, returning it in base16
*/
export const randomHash = () => hash(Math.random().toString())
@@ -0,0 +1,29 @@
import { camelCase } from './camelCase'
type Style = string | Partial<CSSStyleDeclaration>
/**
* Converts a CSS Style string
* @param {string | Partial<CSSStyleDeclaration>} style A string to convert, or object to return
* @returns {Partial<CSSStyleDeclaration>} a partial CSSStyleDeclaration
*/
export const parseStyle = (style: Style): Partial<CSSStyleDeclaration> | undefined => {
switch (typeof style) {
case 'string':
return style.split(';').filter(r => r)
.reduce((map, rule) => {
const name = rule.slice(0, rule.indexOf(':')).trim()
const value = rule.slice(rule.indexOf(':') + 1).trim()
return {
...map,
[camelCase(name)]: value,
}
}, {})
case 'object':
return style
default:
return undefined
}
}
@@ -0,0 +1,26 @@
const pathToArrayPath = (path: string) => {
if (path == null || path === '') return []
return path.split('.')
}
const resolveArrayPath = (object: any, path: string[]): string | undefined => {
const [property, ...subPath] = path
if (object == null || property == null) {
return undefined
}
return subPath.length === 0
? object[property]
: resolveArrayPath(object[property], subPath)
}
/**
* Returns the result of a path query from an object
* @param {any} object the object to search
* @param {string} path the path, whose value will be retrieved
* @returns {any} the value (undefined if the path doesn't exist)
* @example
* resolvePath({ foo: { bar: { baz: 3 } } }, 'foo.bar.baz') // 3
*/
export const resolvePath = (object: any, path: string): any => (
resolveArrayPath(object, pathToArrayPath(path))
)
@@ -0,0 +1,23 @@
// The module cache
var __webpack_module_cache__ = {};
// The require function
function __webpack_require__(moduleId) {
// Check if module is in cache
if(__webpack_module_cache__[moduleId]) {
return __webpack_module_cache__[moduleId].exports;
}
// Create a new module (and put it into the cache)
var module = __webpack_module_cache__[moduleId] = {
// no module.id needed
// no module.loaded needed
exports: {}
};
// Execute the module function
__webpack_modules__[moduleId].call(module.exports, module, module.exports, __webpack_require__);
// Return the exports of the module
return module.exports;
}
@@ -0,0 +1,8 @@
// getDefaultExport function for compatibility with non-harmony modules
__webpack_require__.n = (module) => {
var getter = module && module.__esModule ?
() => (module['default']) :
() => (module);
__webpack_require__.d(getter, { a: getter });
return getter;
};
@@ -0,0 +1,8 @@
// define getter functions for harmony exports
__webpack_require__.d = (exports, definition) => {
for(var key in definition) {
if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
}
}
};
@@ -0,0 +1 @@
__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))
@@ -0,0 +1,7 @@
// define __esModule on exports
__webpack_require__.r = (exports) => {
if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
}
Object.defineProperty(exports, '__esModule', { value: true });
};
@@ -0,0 +1,4 @@
// module exports must be returned from runtime so entry inlining is disabled
// startup
// Load entry module and return exports
return __webpack_require__(272);
@@ -0,0 +1,11 @@
(function webpackUniversalModuleDefinition(root, factory) {
if(typeof exports === 'object' && typeof module === 'object')
module.exports = factory(require("react"));
else if(typeof define === 'function' && define.amd)
define("react-jsx-parser", ["react"], factory);
else if(typeof exports === 'object')
exports["react-jsx-parser"] = factory(require("react"));
else
root["react-jsx-parser"] = factory(root["react"]);
})(this, function(__WEBPACK_EXTERNAL_MODULE__297__) {
return