UNPKG

prism-react-renderer

Version:

Renders highlighted Prism output using React

336 lines (271 loc) 9.19 kB
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 };