UNPKG

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