UNPKG

4.62 kBJavaScriptView Raw
1(function (Prism) {
2
3 var javascript = Prism.util.clone(Prism.languages.javascript);
4
5 var space = /(?:\s|\/\/.*(?!.)|\/\*(?:[^*]|\*(?!\/))\*\/)/.source;
6 var braces = /(?:\{(?:\{(?:\{[^{}]*\}|[^{}])*\}|[^{}])*\})/.source;
7 var spread = /(?:\{<S>*\.{3}(?:[^{}]|<BRACES>)*\})/.source;
8
9 /**
10 * @param {string} source
11 * @param {string} [flags]
12 */
13 function re(source, flags) {
14 source = source
15 .replace(/<S>/g, function () { return space; })
16 .replace(/<BRACES>/g, function () { return braces; })
17 .replace(/<SPREAD>/g, function () { return spread; });
18 return RegExp(source, flags);
19 }
20
21 spread = re(spread).source;
22
23
24 Prism.languages.jsx = Prism.languages.extend('markup', javascript);
25 Prism.languages.jsx.tag.pattern = re(
26 /<\/?(?:[\w.:-]+(?:<S>+(?:[\w.:$-]+(?:=(?:"(?:\\[\s\S]|[^\\"])*"|'(?:\\[\s\S]|[^\\'])*'|[^\s{'"/>=]+|<BRACES>))?|<SPREAD>))*<S>*\/?)?>/.source
27 );
28
29 Prism.languages.jsx.tag.inside['tag'].pattern = /^<\/?[^\s>\/]*/;
30 Prism.languages.jsx.tag.inside['attr-value'].pattern = /=(?!\{)(?:"(?:\\[\s\S]|[^\\"])*"|'(?:\\[\s\S]|[^\\'])*'|[^\s'">]+)/;
31 Prism.languages.jsx.tag.inside['tag'].inside['class-name'] = /^[A-Z]\w*(?:\.[A-Z]\w*)*$/;
32 Prism.languages.jsx.tag.inside['comment'] = javascript['comment'];
33
34 Prism.languages.insertBefore('inside', 'attr-name', {
35 'spread': {
36 pattern: re(/<SPREAD>/.source),
37 inside: Prism.languages.jsx
38 }
39 }, Prism.languages.jsx.tag);
40
41 Prism.languages.insertBefore('inside', 'special-attr', {
42 'script': {
43 // Allow for two levels of nesting
44 pattern: re(/=<BRACES>/.source),
45 alias: 'language-javascript',
46 inside: {
47 'script-punctuation': {
48 pattern: /^=(?=\{)/,
49 alias: 'punctuation'
50 },
51 rest: Prism.languages.jsx
52 },
53 }
54 }, Prism.languages.jsx.tag);
55
56 // The following will handle plain text inside tags
57 var stringifyToken = function (token) {
58 if (!token) {
59 return '';
60 }
61 if (typeof token === 'string') {
62 return token;
63 }
64 if (typeof token.content === 'string') {
65 return token.content;
66 }
67 return token.content.map(stringifyToken).join('');
68 };
69
70 var walkTokens = function (tokens) {
71 var openedTags = [];
72 for (var i = 0; i < tokens.length; i++) {
73 var token = tokens[i];
74 var notTagNorBrace = false;
75
76 if (typeof token !== 'string') {
77 if (token.type === 'tag' && token.content[0] && token.content[0].type === 'tag') {
78 // We found a tag, now find its kind
79
80 if (token.content[0].content[0].content === '</') {
81 // Closing tag
82 if (openedTags.length > 0 && openedTags[openedTags.length - 1].tagName === stringifyToken(token.content[0].content[1])) {
83 // Pop matching opening tag
84 openedTags.pop();
85 }
86 } else {
87 if (token.content[token.content.length - 1].content === '/>') {
88 // Autoclosed tag, ignore
89 } else {
90 // Opening tag
91 openedTags.push({
92 tagName: stringifyToken(token.content[0].content[1]),
93 openedBraces: 0
94 });
95 }
96 }
97 } else if (openedTags.length > 0 && token.type === 'punctuation' && token.content === '{') {
98
99 // Here we might have entered a JSX context inside a tag
100 openedTags[openedTags.length - 1].openedBraces++;
101
102 } else if (openedTags.length > 0 && openedTags[openedTags.length - 1].openedBraces > 0 && token.type === 'punctuation' && token.content === '}') {
103
104 // Here we might have left a JSX context inside a tag
105 openedTags[openedTags.length - 1].openedBraces--;
106
107 } else {
108 notTagNorBrace = true;
109 }
110 }
111 if (notTagNorBrace || typeof token === 'string') {
112 if (openedTags.length > 0 && openedTags[openedTags.length - 1].openedBraces === 0) {
113 // Here we are inside a tag, and not inside a JSX context.
114 // That's plain text: drop any tokens matched.
115 var plainText = stringifyToken(token);
116
117 // And merge text with adjacent text
118 if (i < tokens.length - 1 && (typeof tokens[i + 1] === 'string' || tokens[i + 1].type === 'plain-text')) {
119 plainText += stringifyToken(tokens[i + 1]);
120 tokens.splice(i + 1, 1);
121 }
122 if (i > 0 && (typeof tokens[i - 1] === 'string' || tokens[i - 1].type === 'plain-text')) {
123 plainText = stringifyToken(tokens[i - 1]) + plainText;
124 tokens.splice(i - 1, 1);
125 i--;
126 }
127
128 tokens[i] = new Prism.Token('plain-text', plainText, null, plainText);
129 }
130 }
131
132 if (token.content && typeof token.content !== 'string') {
133 walkTokens(token.content);
134 }
135 }
136 };
137
138 Prism.hooks.add('after-tokenize', function (env) {
139 if (env.language !== 'jsx' && env.language !== 'tsx') {
140 return;
141 }
142 walkTokens(env.tokens);
143 });
144
145}(Prism));