'use strict' module.exports = jsx jsx.displayName = 'jsx' jsx.aliases = [] function jsx(Prism) { ;(function (Prism) { var javascript = Prism.util.clone(Prism.languages.javascript) var space = /(?:\s|\/\/.*(?!.)|\/\*(?:[^*]|\*(?!\/))\*\/)/.source var braces = /(?:\{(?:\{(?:\{[^{}]*\}|[^{}])*\}|[^{}])*\})/.source var spread = /(?:\{*\.{3}(?:[^{}]|)*\})/.source /** * @param {string} source * @param {string} [flags] */ function re(source, flags) { source = source .replace(//g, function () { return space }) .replace(//g, function () { return braces }) .replace(//g, function () { return spread }) return RegExp(source, flags) } spread = re(spread).source Prism.languages.jsx = Prism.languages.extend('markup', javascript) Prism.languages.jsx.tag.pattern = re( /<\/?(?:[\w.:-]+(?:+(?:[\w.:$-]+(?:=(?:"(?:\\[\s\S]|[^\\"])*"|'(?:\\[\s\S]|[^\\'])*'|[^\s{'"/>=]+|))?|))**\/?)?>/ .source ) Prism.languages.jsx.tag.inside['tag'].pattern = /^<\/?[^\s>\/]*/ Prism.languages.jsx.tag.inside['attr-value'].pattern = /=(?!\{)(?:"(?:\\[\s\S]|[^\\"])*"|'(?:\\[\s\S]|[^\\'])*'|[^\s'">]+)/ Prism.languages.jsx.tag.inside['tag'].inside['class-name'] = /^[A-Z]\w*(?:\.[A-Z]\w*)*$/ Prism.languages.jsx.tag.inside['comment'] = javascript['comment'] Prism.languages.insertBefore( 'inside', 'attr-name', { spread: { pattern: re(//.source), inside: Prism.languages.jsx } }, Prism.languages.jsx.tag ) Prism.languages.insertBefore( 'inside', 'special-attr', { script: { // Allow for two levels of nesting pattern: re(/=/.source), alias: 'language-javascript', inside: { 'script-punctuation': { pattern: /^=(?=\{)/, alias: 'punctuation' }, rest: Prism.languages.jsx } } }, Prism.languages.jsx.tag ) // The following will handle plain text inside tags var stringifyToken = function (token) { if (!token) { return '' } if (typeof token === 'string') { return token } if (typeof token.content === 'string') { return token.content } return token.content.map(stringifyToken).join('') } var walkTokens = function (tokens) { var openedTags = [] for (var i = 0; i < tokens.length; i++) { var token = tokens[i] var notTagNorBrace = false if (typeof token !== 'string') { if ( token.type === 'tag' && token.content[0] && token.content[0].type === 'tag' ) { // We found a tag, now find its kind if (token.content[0].content[0].content === ' 0 && openedTags[openedTags.length - 1].tagName === stringifyToken(token.content[0].content[1]) ) { // Pop matching opening tag openedTags.pop() } } else { if (token.content[token.content.length - 1].content === '/>') { // Autoclosed tag, ignore } else { // Opening tag openedTags.push({ tagName: stringifyToken(token.content[0].content[1]), openedBraces: 0 }) } } } else if ( openedTags.length > 0 && token.type === 'punctuation' && token.content === '{' ) { // Here we might have entered a JSX context inside a tag openedTags[openedTags.length - 1].openedBraces++ } else if ( openedTags.length > 0 && openedTags[openedTags.length - 1].openedBraces > 0 && token.type === 'punctuation' && token.content === '}' ) { // Here we might have left a JSX context inside a tag openedTags[openedTags.length - 1].openedBraces-- } else { notTagNorBrace = true } } if (notTagNorBrace || typeof token === 'string') { if ( openedTags.length > 0 && openedTags[openedTags.length - 1].openedBraces === 0 ) { // Here we are inside a tag, and not inside a JSX context. // That's plain text: drop any tokens matched. var plainText = stringifyToken(token) // And merge text with adjacent text if ( i < tokens.length - 1 && (typeof tokens[i + 1] === 'string' || tokens[i + 1].type === 'plain-text') ) { plainText += stringifyToken(tokens[i + 1]) tokens.splice(i + 1, 1) } if ( i > 0 && (typeof tokens[i - 1] === 'string' || tokens[i - 1].type === 'plain-text') ) { plainText = stringifyToken(tokens[i - 1]) + plainText tokens.splice(i - 1, 1) i-- } tokens[i] = new Prism.Token( 'plain-text', plainText, null, plainText ) } } if (token.content && typeof token.content !== 'string') { walkTokens(token.content) } } } Prism.hooks.add('after-tokenize', function (env) { if (env.language !== 'jsx' && env.language !== 'tsx') { return } walkTokens(env.tokens) }) })(Prism) }