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