comparison third_party/highlight/languages/javascript.js @ 173:827c6ac504cd hg-web

Merged in default here.
author MrJuneJune <me@mrjunejune.com>
date Mon, 19 Jan 2026 18:59:10 -0800
parents 2db6253f355d
children
comparison
equal deleted inserted replaced
151:c033667da5f9 173:827c6ac504cd
1 /*! `javascript` grammar compiled for Highlight.js 11.11.1 */
2 (function(){
3 var hljsGrammar = (function () {
4 'use strict';
5
6 const IDENT_RE = '[A-Za-z$_][0-9A-Za-z$_]*';
7 const KEYWORDS = [
8 "as", // for exports
9 "in",
10 "of",
11 "if",
12 "for",
13 "while",
14 "finally",
15 "var",
16 "new",
17 "function",
18 "do",
19 "return",
20 "void",
21 "else",
22 "break",
23 "catch",
24 "instanceof",
25 "with",
26 "throw",
27 "case",
28 "default",
29 "try",
30 "switch",
31 "continue",
32 "typeof",
33 "delete",
34 "let",
35 "yield",
36 "const",
37 "class",
38 // JS handles these with a special rule
39 // "get",
40 // "set",
41 "debugger",
42 "async",
43 "await",
44 "static",
45 "import",
46 "from",
47 "export",
48 "extends",
49 // It's reached stage 3, which is "recommended for implementation":
50 "using"
51 ];
52 const LITERALS = [
53 "true",
54 "false",
55 "null",
56 "undefined",
57 "NaN",
58 "Infinity"
59 ];
60
61 // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects
62 const TYPES = [
63 // Fundamental objects
64 "Object",
65 "Function",
66 "Boolean",
67 "Symbol",
68 // numbers and dates
69 "Math",
70 "Date",
71 "Number",
72 "BigInt",
73 // text
74 "String",
75 "RegExp",
76 // Indexed collections
77 "Array",
78 "Float32Array",
79 "Float64Array",
80 "Int8Array",
81 "Uint8Array",
82 "Uint8ClampedArray",
83 "Int16Array",
84 "Int32Array",
85 "Uint16Array",
86 "Uint32Array",
87 "BigInt64Array",
88 "BigUint64Array",
89 // Keyed collections
90 "Set",
91 "Map",
92 "WeakSet",
93 "WeakMap",
94 // Structured data
95 "ArrayBuffer",
96 "SharedArrayBuffer",
97 "Atomics",
98 "DataView",
99 "JSON",
100 // Control abstraction objects
101 "Promise",
102 "Generator",
103 "GeneratorFunction",
104 "AsyncFunction",
105 // Reflection
106 "Reflect",
107 "Proxy",
108 // Internationalization
109 "Intl",
110 // WebAssembly
111 "WebAssembly"
112 ];
113
114 const ERROR_TYPES = [
115 "Error",
116 "EvalError",
117 "InternalError",
118 "RangeError",
119 "ReferenceError",
120 "SyntaxError",
121 "TypeError",
122 "URIError"
123 ];
124
125 const BUILT_IN_GLOBALS = [
126 "setInterval",
127 "setTimeout",
128 "clearInterval",
129 "clearTimeout",
130
131 "require",
132 "exports",
133
134 "eval",
135 "isFinite",
136 "isNaN",
137 "parseFloat",
138 "parseInt",
139 "decodeURI",
140 "decodeURIComponent",
141 "encodeURI",
142 "encodeURIComponent",
143 "escape",
144 "unescape"
145 ];
146
147 const BUILT_IN_VARIABLES = [
148 "arguments",
149 "this",
150 "super",
151 "console",
152 "window",
153 "document",
154 "localStorage",
155 "sessionStorage",
156 "module",
157 "global" // Node.js
158 ];
159
160 const BUILT_INS = [].concat(
161 BUILT_IN_GLOBALS,
162 TYPES,
163 ERROR_TYPES
164 );
165
166 /*
167 Language: JavaScript
168 Description: JavaScript (JS) is a lightweight, interpreted, or just-in-time compiled programming language with first-class functions.
169 Category: common, scripting, web
170 Website: https://developer.mozilla.org/en-US/docs/Web/JavaScript
171 */
172
173
174 /** @type LanguageFn */
175 function javascript(hljs) {
176 const regex = hljs.regex;
177 /**
178 * Takes a string like "<Booger" and checks to see
179 * if we can find a matching "</Booger" later in the
180 * content.
181 * @param {RegExpMatchArray} match
182 * @param {{after:number}} param1
183 */
184 const hasClosingTag = (match, { after }) => {
185 const tag = "</" + match[0].slice(1);
186 const pos = match.input.indexOf(tag, after);
187 return pos !== -1;
188 };
189
190 const IDENT_RE$1 = IDENT_RE;
191 const FRAGMENT = {
192 begin: '<>',
193 end: '</>'
194 };
195 // to avoid some special cases inside isTrulyOpeningTag
196 const XML_SELF_CLOSING = /<[A-Za-z0-9\\._:-]+\s*\/>/;
197 const XML_TAG = {
198 begin: /<[A-Za-z0-9\\._:-]+/,
199 end: /\/[A-Za-z0-9\\._:-]+>|\/>/,
200 /**
201 * @param {RegExpMatchArray} match
202 * @param {CallbackResponse} response
203 */
204 isTrulyOpeningTag: (match, response) => {
205 const afterMatchIndex = match[0].length + match.index;
206 const nextChar = match.input[afterMatchIndex];
207 if (
208 // HTML should not include another raw `<` inside a tag
209 // nested type?
210 // `<Array<Array<number>>`, etc.
211 nextChar === "<" ||
212 // the , gives away that this is not HTML
213 // `<T, A extends keyof T, V>`
214 nextChar === ","
215 ) {
216 response.ignoreMatch();
217 return;
218 }
219
220 // `<something>`
221 // Quite possibly a tag, lets look for a matching closing tag...
222 if (nextChar === ">") {
223 // if we cannot find a matching closing tag, then we
224 // will ignore it
225 if (!hasClosingTag(match, { after: afterMatchIndex })) {
226 response.ignoreMatch();
227 }
228 }
229
230 // `<blah />` (self-closing)
231 // handled by simpleSelfClosing rule
232
233 let m;
234 const afterMatch = match.input.substring(afterMatchIndex);
235
236 // some more template typing stuff
237 // <T = any>(key?: string) => Modify<
238 if ((m = afterMatch.match(/^\s*=/))) {
239 response.ignoreMatch();
240 return;
241 }
242
243 // `<From extends string>`
244 // technically this could be HTML, but it smells like a type
245 // NOTE: This is ugh, but added specifically for https://github.com/highlightjs/highlight.js/issues/3276
246 if ((m = afterMatch.match(/^\s+extends\s+/))) {
247 if (m.index === 0) {
248 response.ignoreMatch();
249 // eslint-disable-next-line no-useless-return
250 return;
251 }
252 }
253 }
254 };
255 const KEYWORDS$1 = {
256 $pattern: IDENT_RE,
257 keyword: KEYWORDS,
258 literal: LITERALS,
259 built_in: BUILT_INS,
260 "variable.language": BUILT_IN_VARIABLES
261 };
262
263 // https://tc39.es/ecma262/#sec-literals-numeric-literals
264 const decimalDigits = '[0-9](_?[0-9])*';
265 const frac = `\\.(${decimalDigits})`;
266 // DecimalIntegerLiteral, including Annex B NonOctalDecimalIntegerLiteral
267 // https://tc39.es/ecma262/#sec-additional-syntax-numeric-literals
268 const decimalInteger = `0|[1-9](_?[0-9])*|0[0-7]*[89][0-9]*`;
269 const NUMBER = {
270 className: 'number',
271 variants: [
272 // DecimalLiteral
273 { begin: `(\\b(${decimalInteger})((${frac})|\\.)?|(${frac}))` +
274 `[eE][+-]?(${decimalDigits})\\b` },
275 { begin: `\\b(${decimalInteger})\\b((${frac})\\b|\\.)?|(${frac})\\b` },
276
277 // DecimalBigIntegerLiteral
278 { begin: `\\b(0|[1-9](_?[0-9])*)n\\b` },
279
280 // NonDecimalIntegerLiteral
281 { begin: "\\b0[xX][0-9a-fA-F](_?[0-9a-fA-F])*n?\\b" },
282 { begin: "\\b0[bB][0-1](_?[0-1])*n?\\b" },
283 { begin: "\\b0[oO][0-7](_?[0-7])*n?\\b" },
284
285 // LegacyOctalIntegerLiteral (does not include underscore separators)
286 // https://tc39.es/ecma262/#sec-additional-syntax-numeric-literals
287 { begin: "\\b0[0-7]+n?\\b" },
288 ],
289 relevance: 0
290 };
291
292 const SUBST = {
293 className: 'subst',
294 begin: '\\$\\{',
295 end: '\\}',
296 keywords: KEYWORDS$1,
297 contains: [] // defined later
298 };
299 const HTML_TEMPLATE = {
300 begin: '\.?html`',
301 end: '',
302 starts: {
303 end: '`',
304 returnEnd: false,
305 contains: [
306 hljs.BACKSLASH_ESCAPE,
307 SUBST
308 ],
309 subLanguage: 'xml'
310 }
311 };
312 const CSS_TEMPLATE = {
313 begin: '\.?css`',
314 end: '',
315 starts: {
316 end: '`',
317 returnEnd: false,
318 contains: [
319 hljs.BACKSLASH_ESCAPE,
320 SUBST
321 ],
322 subLanguage: 'css'
323 }
324 };
325 const GRAPHQL_TEMPLATE = {
326 begin: '\.?gql`',
327 end: '',
328 starts: {
329 end: '`',
330 returnEnd: false,
331 contains: [
332 hljs.BACKSLASH_ESCAPE,
333 SUBST
334 ],
335 subLanguage: 'graphql'
336 }
337 };
338 const TEMPLATE_STRING = {
339 className: 'string',
340 begin: '`',
341 end: '`',
342 contains: [
343 hljs.BACKSLASH_ESCAPE,
344 SUBST
345 ]
346 };
347 const JSDOC_COMMENT = hljs.COMMENT(
348 /\/\*\*(?!\/)/,
349 '\\*/',
350 {
351 relevance: 0,
352 contains: [
353 {
354 begin: '(?=@[A-Za-z]+)',
355 relevance: 0,
356 contains: [
357 {
358 className: 'doctag',
359 begin: '@[A-Za-z]+'
360 },
361 {
362 className: 'type',
363 begin: '\\{',
364 end: '\\}',
365 excludeEnd: true,
366 excludeBegin: true,
367 relevance: 0
368 },
369 {
370 className: 'variable',
371 begin: IDENT_RE$1 + '(?=\\s*(-)|$)',
372 endsParent: true,
373 relevance: 0
374 },
375 // eat spaces (not newlines) so we can find
376 // types or variables
377 {
378 begin: /(?=[^\n])\s/,
379 relevance: 0
380 }
381 ]
382 }
383 ]
384 }
385 );
386 const COMMENT = {
387 className: "comment",
388 variants: [
389 JSDOC_COMMENT,
390 hljs.C_BLOCK_COMMENT_MODE,
391 hljs.C_LINE_COMMENT_MODE
392 ]
393 };
394 const SUBST_INTERNALS = [
395 hljs.APOS_STRING_MODE,
396 hljs.QUOTE_STRING_MODE,
397 HTML_TEMPLATE,
398 CSS_TEMPLATE,
399 GRAPHQL_TEMPLATE,
400 TEMPLATE_STRING,
401 // Skip numbers when they are part of a variable name
402 { match: /\$\d+/ },
403 NUMBER,
404 // This is intentional:
405 // See https://github.com/highlightjs/highlight.js/issues/3288
406 // hljs.REGEXP_MODE
407 ];
408 SUBST.contains = SUBST_INTERNALS
409 .concat({
410 // we need to pair up {} inside our subst to prevent
411 // it from ending too early by matching another }
412 begin: /\{/,
413 end: /\}/,
414 keywords: KEYWORDS$1,
415 contains: [
416 "self"
417 ].concat(SUBST_INTERNALS)
418 });
419 const SUBST_AND_COMMENTS = [].concat(COMMENT, SUBST.contains);
420 const PARAMS_CONTAINS = SUBST_AND_COMMENTS.concat([
421 // eat recursive parens in sub expressions
422 {
423 begin: /(\s*)\(/,
424 end: /\)/,
425 keywords: KEYWORDS$1,
426 contains: ["self"].concat(SUBST_AND_COMMENTS)
427 }
428 ]);
429 const PARAMS = {
430 className: 'params',
431 // convert this to negative lookbehind in v12
432 begin: /(\s*)\(/, // to match the parms with
433 end: /\)/,
434 excludeBegin: true,
435 excludeEnd: true,
436 keywords: KEYWORDS$1,
437 contains: PARAMS_CONTAINS
438 };
439
440 // ES6 classes
441 const CLASS_OR_EXTENDS = {
442 variants: [
443 // class Car extends vehicle
444 {
445 match: [
446 /class/,
447 /\s+/,
448 IDENT_RE$1,
449 /\s+/,
450 /extends/,
451 /\s+/,
452 regex.concat(IDENT_RE$1, "(", regex.concat(/\./, IDENT_RE$1), ")*")
453 ],
454 scope: {
455 1: "keyword",
456 3: "title.class",
457 5: "keyword",
458 7: "title.class.inherited"
459 }
460 },
461 // class Car
462 {
463 match: [
464 /class/,
465 /\s+/,
466 IDENT_RE$1
467 ],
468 scope: {
469 1: "keyword",
470 3: "title.class"
471 }
472 },
473
474 ]
475 };
476
477 const CLASS_REFERENCE = {
478 relevance: 0,
479 match:
480 regex.either(
481 // Hard coded exceptions
482 /\bJSON/,
483 // Float32Array, OutT
484 /\b[A-Z][a-z]+([A-Z][a-z]*|\d)*/,
485 // CSSFactory, CSSFactoryT
486 /\b[A-Z]{2,}([A-Z][a-z]+|\d)+([A-Z][a-z]*)*/,
487 // FPs, FPsT
488 /\b[A-Z]{2,}[a-z]+([A-Z][a-z]+|\d)*([A-Z][a-z]*)*/,
489 // P
490 // single letters are not highlighted
491 // BLAH
492 // this will be flagged as a UPPER_CASE_CONSTANT instead
493 ),
494 className: "title.class",
495 keywords: {
496 _: [
497 // se we still get relevance credit for JS library classes
498 ...TYPES,
499 ...ERROR_TYPES
500 ]
501 }
502 };
503
504 const USE_STRICT = {
505 label: "use_strict",
506 className: 'meta',
507 relevance: 10,
508 begin: /^\s*['"]use (strict|asm)['"]/
509 };
510
511 const FUNCTION_DEFINITION = {
512 variants: [
513 {
514 match: [
515 /function/,
516 /\s+/,
517 IDENT_RE$1,
518 /(?=\s*\()/
519 ]
520 },
521 // anonymous function
522 {
523 match: [
524 /function/,
525 /\s*(?=\()/
526 ]
527 }
528 ],
529 className: {
530 1: "keyword",
531 3: "title.function"
532 },
533 label: "func.def",
534 contains: [ PARAMS ],
535 illegal: /%/
536 };
537
538 const UPPER_CASE_CONSTANT = {
539 relevance: 0,
540 match: /\b[A-Z][A-Z_0-9]+\b/,
541 className: "variable.constant"
542 };
543
544 function noneOf(list) {
545 return regex.concat("(?!", list.join("|"), ")");
546 }
547
548 const FUNCTION_CALL = {
549 match: regex.concat(
550 /\b/,
551 noneOf([
552 ...BUILT_IN_GLOBALS,
553 "super",
554 "import"
555 ].map(x => `${x}\\s*\\(`)),
556 IDENT_RE$1, regex.lookahead(/\s*\(/)),
557 className: "title.function",
558 relevance: 0
559 };
560
561 const PROPERTY_ACCESS = {
562 begin: regex.concat(/\./, regex.lookahead(
563 regex.concat(IDENT_RE$1, /(?![0-9A-Za-z$_(])/)
564 )),
565 end: IDENT_RE$1,
566 excludeBegin: true,
567 keywords: "prototype",
568 className: "property",
569 relevance: 0
570 };
571
572 const GETTER_OR_SETTER = {
573 match: [
574 /get|set/,
575 /\s+/,
576 IDENT_RE$1,
577 /(?=\()/
578 ],
579 className: {
580 1: "keyword",
581 3: "title.function"
582 },
583 contains: [
584 { // eat to avoid empty params
585 begin: /\(\)/
586 },
587 PARAMS
588 ]
589 };
590
591 const FUNC_LEAD_IN_RE = '(\\(' +
592 '[^()]*(\\(' +
593 '[^()]*(\\(' +
594 '[^()]*' +
595 '\\)[^()]*)*' +
596 '\\)[^()]*)*' +
597 '\\)|' + hljs.UNDERSCORE_IDENT_RE + ')\\s*=>';
598
599 const FUNCTION_VARIABLE = {
600 match: [
601 /const|var|let/, /\s+/,
602 IDENT_RE$1, /\s*/,
603 /=\s*/,
604 /(async\s*)?/, // async is optional
605 regex.lookahead(FUNC_LEAD_IN_RE)
606 ],
607 keywords: "async",
608 className: {
609 1: "keyword",
610 3: "title.function"
611 },
612 contains: [
613 PARAMS
614 ]
615 };
616
617 return {
618 name: 'JavaScript',
619 aliases: ['js', 'jsx', 'mjs', 'cjs'],
620 keywords: KEYWORDS$1,
621 // this will be extended by TypeScript
622 exports: { PARAMS_CONTAINS, CLASS_REFERENCE },
623 illegal: /#(?![$_A-z])/,
624 contains: [
625 hljs.SHEBANG({
626 label: "shebang",
627 binary: "node",
628 relevance: 5
629 }),
630 USE_STRICT,
631 hljs.APOS_STRING_MODE,
632 hljs.QUOTE_STRING_MODE,
633 HTML_TEMPLATE,
634 CSS_TEMPLATE,
635 GRAPHQL_TEMPLATE,
636 TEMPLATE_STRING,
637 COMMENT,
638 // Skip numbers when they are part of a variable name
639 { match: /\$\d+/ },
640 NUMBER,
641 CLASS_REFERENCE,
642 {
643 scope: 'attr',
644 match: IDENT_RE$1 + regex.lookahead(':'),
645 relevance: 0
646 },
647 FUNCTION_VARIABLE,
648 { // "value" container
649 begin: '(' + hljs.RE_STARTERS_RE + '|\\b(case|return|throw)\\b)\\s*',
650 keywords: 'return throw case',
651 relevance: 0,
652 contains: [
653 COMMENT,
654 hljs.REGEXP_MODE,
655 {
656 className: 'function',
657 // we have to count the parens to make sure we actually have the
658 // correct bounding ( ) before the =>. There could be any number of
659 // sub-expressions inside also surrounded by parens.
660 begin: FUNC_LEAD_IN_RE,
661 returnBegin: true,
662 end: '\\s*=>',
663 contains: [
664 {
665 className: 'params',
666 variants: [
667 {
668 begin: hljs.UNDERSCORE_IDENT_RE,
669 relevance: 0
670 },
671 {
672 className: null,
673 begin: /\(\s*\)/,
674 skip: true
675 },
676 {
677 begin: /(\s*)\(/,
678 end: /\)/,
679 excludeBegin: true,
680 excludeEnd: true,
681 keywords: KEYWORDS$1,
682 contains: PARAMS_CONTAINS
683 }
684 ]
685 }
686 ]
687 },
688 { // could be a comma delimited list of params to a function call
689 begin: /,/,
690 relevance: 0
691 },
692 {
693 match: /\s+/,
694 relevance: 0
695 },
696 { // JSX
697 variants: [
698 { begin: FRAGMENT.begin, end: FRAGMENT.end },
699 { match: XML_SELF_CLOSING },
700 {
701 begin: XML_TAG.begin,
702 // we carefully check the opening tag to see if it truly
703 // is a tag and not a false positive
704 'on:begin': XML_TAG.isTrulyOpeningTag,
705 end: XML_TAG.end
706 }
707 ],
708 subLanguage: 'xml',
709 contains: [
710 {
711 begin: XML_TAG.begin,
712 end: XML_TAG.end,
713 skip: true,
714 contains: ['self']
715 }
716 ]
717 }
718 ],
719 },
720 FUNCTION_DEFINITION,
721 {
722 // prevent this from getting swallowed up by function
723 // since they appear "function like"
724 beginKeywords: "while if switch catch for"
725 },
726 {
727 // we have to count the parens to make sure we actually have the correct
728 // bounding ( ). There could be any number of sub-expressions inside
729 // also surrounded by parens.
730 begin: '\\b(?!function)' + hljs.UNDERSCORE_IDENT_RE +
731 '\\(' + // first parens
732 '[^()]*(\\(' +
733 '[^()]*(\\(' +
734 '[^()]*' +
735 '\\)[^()]*)*' +
736 '\\)[^()]*)*' +
737 '\\)\\s*\\{', // end parens
738 returnBegin:true,
739 label: "func.def",
740 contains: [
741 PARAMS,
742 hljs.inherit(hljs.TITLE_MODE, { begin: IDENT_RE$1, className: "title.function" })
743 ]
744 },
745 // catch ... so it won't trigger the property rule below
746 {
747 match: /\.\.\./,
748 relevance: 0
749 },
750 PROPERTY_ACCESS,
751 // hack: prevents detection of keywords in some circumstances
752 // .keyword()
753 // $keyword = x
754 {
755 match: '\\$' + IDENT_RE$1,
756 relevance: 0
757 },
758 {
759 match: [ /\bconstructor(?=\s*\()/ ],
760 className: { 1: "title.function" },
761 contains: [ PARAMS ]
762 },
763 FUNCTION_CALL,
764 UPPER_CASE_CONSTANT,
765 CLASS_OR_EXTENDS,
766 GETTER_OR_SETTER,
767 {
768 match: /\$[(.]/ // relevance booster for a pattern common to JS libs: `$(something)` and `$.something`
769 }
770 ]
771 };
772 }
773
774 return javascript;
775
776 })();
777
778 hljs.registerLanguage('javascript', hljsGrammar);
779 })();