prism-react-renderer
Version:
Renders highlighted Prism output using React
336 lines (271 loc) • 9.19 kB
JavaScript
import Prism from '../prism/index.js';
export { default as Prism } from '../prism/index.js';
import theme from '../themes/duotoneDark';
import { Component } from 'react';
var defaultProps = {
// $FlowFixMe
Prism: Prism,
theme: theme
};
function _defineProperty(obj, key, value) {
if (key in obj) {
Object.defineProperty(obj, key, {
value: value,
enumerable: true,
configurable: true,
writable: true
});
} else {
obj[key] = value;
}
return obj;
}
function _extends() {
_extends = Object.assign || function (target) {
for (var i = 1; i < arguments.length; i++) {
var source = arguments[i];
for (var key in source) {
if (Object.prototype.hasOwnProperty.call(source, key)) {
target[key] = source[key];
}
}
}
return target;
};
return _extends.apply(this, arguments);
}
var newlineRe = /\r\n|\r|\n/; // Empty lines need to contain a single empty token, denoted with { empty: true }
var normalizeEmptyLines = function (line) {
if (line.length === 0) {
line.push({
types: ["plain"],
content: "\n",
empty: true
});
} else if (line.length === 1 && line[0].content === "") {
line[0].content = "\n";
line[0].empty = true;
}
};
var appendTypes = function (types, add) {
var typesSize = types.length;
if (typesSize > 0 && types[typesSize - 1] === add) {
return types;
}
return types.concat(add);
}; // Takes an array of Prism's tokens and groups them by line, turning plain
// strings into tokens as well. Tokens can become recursive in some cases,
// which means that their types are concatenated. Plain-string tokens however
// are always of type "plain".
// This is not recursive to avoid exceeding the call-stack limit, since it's unclear
// how nested Prism's tokens can become
var normalizeTokens = function (tokens) {
var typeArrStack = [[]];
var tokenArrStack = [tokens];
var tokenArrIndexStack = [0];
var tokenArrSizeStack = [tokens.length];
var i = 0;
var stackIndex = 0;
var currentLine = [];
var acc = [currentLine];
while (stackIndex > -1) {
while ((i = tokenArrIndexStack[stackIndex]++) < tokenArrSizeStack[stackIndex]) {
var content = void 0;
var types = typeArrStack[stackIndex];
var tokenArr = tokenArrStack[stackIndex];
var token = tokenArr[i]; // Determine content and append type to types if necessary
if (typeof token === "string") {
types = stackIndex > 0 ? types : ["plain"];
content = token;
} else {
types = appendTypes(types, token.type);
if (token.alias) {
types = appendTypes(types, token.alias);
}
content = token.content;
} // If token.content is an array, increase the stack depth and repeat this while-loop
if (typeof content !== "string") {
stackIndex++;
typeArrStack.push(types);
tokenArrStack.push(content);
tokenArrIndexStack.push(0);
tokenArrSizeStack.push(content.length);
continue;
} // Split by newlines
var splitByNewlines = content.split(newlineRe);
var newlineCount = splitByNewlines.length;
currentLine.push({
types: types,
content: splitByNewlines[0]
}); // Create a new line for each string on a new line
for (var i$1 = 1; i$1 < newlineCount; i$1++) {
normalizeEmptyLines(currentLine);
acc.push(currentLine = []);
currentLine.push({
types: types,
content: splitByNewlines[i$1]
});
}
} // Decreate the stack depth
stackIndex--;
typeArrStack.pop();
tokenArrStack.pop();
tokenArrIndexStack.pop();
tokenArrSizeStack.pop();
}
normalizeEmptyLines(currentLine);
return acc;
};
var themeToDict = function (theme, language) {
var plain = theme.plain; // $FlowFixMe
var base = Object.create(null);
var themeDict = theme.styles.reduce(function (acc, themeEntry) {
var languages = themeEntry.languages;
var style = themeEntry.style;
if (languages && !languages.includes(language)) {
return acc;
}
themeEntry.types.forEach(function (type) {
// $FlowFixMe
var accStyle = _extends({}, acc[type], style);
acc[type] = accStyle;
});
return acc;
}, base); // $FlowFixMe
themeDict.root = plain; // $FlowFixMe
themeDict.plain = _extends({}, plain, {
backgroundColor: null
});
return themeDict;
};
function objectWithoutProperties(obj, exclude) {
var target = {};
for (var k in obj) if (Object.prototype.hasOwnProperty.call(obj, k) && exclude.indexOf(k) === -1) target[k] = obj[k];
return target;
}
var Highlight = /*@__PURE__*/function (Component) {
function Highlight() {
var this$1 = this;
var args = [],
len = arguments.length;
while (len--) args[len] = arguments[len];
Component.apply(this, args);
_defineProperty(this, "getThemeDict", function (props) {
if (this$1.themeDict !== undefined && props.theme === this$1.prevTheme && props.language === this$1.prevLanguage) {
return this$1.themeDict;
}
this$1.prevTheme = props.theme;
this$1.prevLanguage = props.language;
var themeDict = props.theme ? themeToDict(props.theme, props.language) : undefined;
return this$1.themeDict = themeDict;
});
_defineProperty(this, "getLineProps", function (ref) {
var key = ref.key;
var className = ref.className;
var style = ref.style;
var rest$1 = objectWithoutProperties(ref, ["key", "className", "style", "line"]);
var rest = rest$1;
var output = _extends({}, rest, {
className: "token-line",
style: undefined,
key: undefined
});
var themeDict = this$1.getThemeDict(this$1.props);
if (themeDict !== undefined) {
output.style = themeDict.plain;
}
if (style !== undefined) {
output.style = output.style !== undefined ? _extends({}, output.style, style) : style;
}
if (key !== undefined) {
output.key = key;
}
if (className) {
output.className += " " + className;
}
return output;
});
_defineProperty(this, "getStyleForToken", function (ref) {
var types = ref.types;
var empty = ref.empty;
var typesSize = types.length;
var themeDict = this$1.getThemeDict(this$1.props);
if (themeDict === undefined) {
return undefined;
} else if (typesSize === 1 && types[0] === "plain") {
return empty ? {
display: "inline-block"
} : undefined;
} else if (typesSize === 1 && !empty) {
return themeDict[types[0]];
}
var baseStyle = empty ? {
display: "inline-block"
} : {}; // $FlowFixMe
var typeStyles = types.map(function (type) {
return themeDict[type];
});
return Object.assign.apply(Object, [baseStyle].concat(typeStyles));
});
_defineProperty(this, "getTokenProps", function (ref) {
var key = ref.key;
var className = ref.className;
var style = ref.style;
var token = ref.token;
var rest$1 = objectWithoutProperties(ref, ["key", "className", "style", "token"]);
var rest = rest$1;
var output = _extends({}, rest, {
className: "token " + token.types.join(" "),
children: token.content,
style: this$1.getStyleForToken(token),
key: undefined
});
if (style !== undefined) {
output.style = output.style !== undefined ? _extends({}, output.style, style) : style;
}
if (key !== undefined) {
output.key = key;
}
if (className) {
output.className += " " + className;
}
return output;
});
_defineProperty(this, "tokenize", function (Prism, code, grammar, language) {
var env = {
code: code,
grammar: grammar,
language: language,
tokens: []
};
Prism.hooks.run("before-tokenize", env);
var tokens = env.tokens = Prism.tokenize(env.code, env.grammar, env.language);
Prism.hooks.run("after-tokenize", env);
return tokens;
});
}
if (Component) Highlight.__proto__ = Component;
Highlight.prototype = Object.create(Component && Component.prototype);
Highlight.prototype.constructor = Highlight;
Highlight.prototype.render = function render() {
var ref = this.props;
var Prism = ref.Prism;
var language = ref.language;
var code = ref.code;
var children = ref.children;
var themeDict = this.getThemeDict(this.props);
var grammar = Prism.languages[language];
var mixedTokens = grammar !== undefined ? this.tokenize(Prism, code, grammar, language) : [code];
var tokens = normalizeTokens(mixedTokens);
return children({
tokens: tokens,
className: "prism-code language-" + language,
style: themeDict !== undefined ? themeDict.root : {},
getLineProps: this.getLineProps,
getTokenProps: this.getTokenProps
});
};
return Highlight;
}(Component);
export default Highlight;
export { defaultProps };