123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767 |
- const IDENT_RE = '[A-Za-z$_][0-9A-Za-z$_]*';
- const KEYWORDS = [
- "as", // for exports
- "in",
- "of",
- "if",
- "for",
- "while",
- "finally",
- "var",
- "new",
- "function",
- "do",
- "return",
- "void",
- "else",
- "break",
- "catch",
- "instanceof",
- "with",
- "throw",
- "case",
- "default",
- "try",
- "switch",
- "continue",
- "typeof",
- "delete",
- "let",
- "yield",
- "const",
- "class",
- // JS handles these with a special rule
- // "get",
- // "set",
- "debugger",
- "async",
- "await",
- "static",
- "import",
- "from",
- "export",
- "extends"
- ];
- const LITERALS = [
- "true",
- "false",
- "null",
- "undefined",
- "NaN",
- "Infinity"
- ];
- // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects
- const TYPES = [
- // Fundamental objects
- "Object",
- "Function",
- "Boolean",
- "Symbol",
- // numbers and dates
- "Math",
- "Date",
- "Number",
- "BigInt",
- // text
- "String",
- "RegExp",
- // Indexed collections
- "Array",
- "Float32Array",
- "Float64Array",
- "Int8Array",
- "Uint8Array",
- "Uint8ClampedArray",
- "Int16Array",
- "Int32Array",
- "Uint16Array",
- "Uint32Array",
- "BigInt64Array",
- "BigUint64Array",
- // Keyed collections
- "Set",
- "Map",
- "WeakSet",
- "WeakMap",
- // Structured data
- "ArrayBuffer",
- "SharedArrayBuffer",
- "Atomics",
- "DataView",
- "JSON",
- // Control abstraction objects
- "Promise",
- "Generator",
- "GeneratorFunction",
- "AsyncFunction",
- // Reflection
- "Reflect",
- "Proxy",
- // Internationalization
- "Intl",
- // WebAssembly
- "WebAssembly"
- ];
- const ERROR_TYPES = [
- "Error",
- "EvalError",
- "InternalError",
- "RangeError",
- "ReferenceError",
- "SyntaxError",
- "TypeError",
- "URIError"
- ];
- const BUILT_IN_GLOBALS = [
- "setInterval",
- "setTimeout",
- "clearInterval",
- "clearTimeout",
- "require",
- "exports",
- "eval",
- "isFinite",
- "isNaN",
- "parseFloat",
- "parseInt",
- "decodeURI",
- "decodeURIComponent",
- "encodeURI",
- "encodeURIComponent",
- "escape",
- "unescape"
- ];
- const BUILT_IN_VARIABLES = [
- "arguments",
- "this",
- "super",
- "console",
- "window",
- "document",
- "localStorage",
- "sessionStorage",
- "module",
- "global" // Node.js
- ];
- const BUILT_INS = [].concat(
- BUILT_IN_GLOBALS,
- TYPES,
- ERROR_TYPES
- );
- /*
- Language: JavaScript
- Description: JavaScript (JS) is a lightweight, interpreted, or just-in-time compiled programming language with first-class functions.
- Category: common, scripting, web
- Website: https://developer.mozilla.org/en-US/docs/Web/JavaScript
- */
- /** @type LanguageFn */
- function javascript(hljs) {
- const regex = hljs.regex;
- /**
- * Takes a string like "<Booger" and checks to see
- * if we can find a matching "</Booger" later in the
- * content.
- * @param {RegExpMatchArray} match
- * @param {{after:number}} param1
- */
- const hasClosingTag = (match, { after }) => {
- const tag = "</" + match[0].slice(1);
- const pos = match.input.indexOf(tag, after);
- return pos !== -1;
- };
- const IDENT_RE$1 = IDENT_RE;
- const FRAGMENT = {
- begin: '<>',
- end: '</>'
- };
- // to avoid some special cases inside isTrulyOpeningTag
- const XML_SELF_CLOSING = /<[A-Za-z0-9\\._:-]+\s*\/>/;
- const XML_TAG = {
- begin: /<[A-Za-z0-9\\._:-]+/,
- end: /\/[A-Za-z0-9\\._:-]+>|\/>/,
- /**
- * @param {RegExpMatchArray} match
- * @param {CallbackResponse} response
- */
- isTrulyOpeningTag: (match, response) => {
- const afterMatchIndex = match[0].length + match.index;
- const nextChar = match.input[afterMatchIndex];
- if (
- // HTML should not include another raw `<` inside a tag
- // nested type?
- // `<Array<Array<number>>`, etc.
- nextChar === "<" ||
- // the , gives away that this is not HTML
- // `<T, A extends keyof T, V>`
- nextChar === ","
- ) {
- response.ignoreMatch();
- return;
- }
- // `<something>`
- // Quite possibly a tag, lets look for a matching closing tag...
- if (nextChar === ">") {
- // if we cannot find a matching closing tag, then we
- // will ignore it
- if (!hasClosingTag(match, { after: afterMatchIndex })) {
- response.ignoreMatch();
- }
- }
- // `<blah />` (self-closing)
- // handled by simpleSelfClosing rule
- let m;
- const afterMatch = match.input.substring(afterMatchIndex);
- // some more template typing stuff
- // <T = any>(key?: string) => Modify<
- if ((m = afterMatch.match(/^\s*=/))) {
- response.ignoreMatch();
- return;
- }
- // `<From extends string>`
- // technically this could be HTML, but it smells like a type
- // NOTE: This is ugh, but added specifically for https://github.com/highlightjs/highlight.js/issues/3276
- if ((m = afterMatch.match(/^\s+extends\s+/))) {
- if (m.index === 0) {
- response.ignoreMatch();
- // eslint-disable-next-line no-useless-return
- return;
- }
- }
- }
- };
- const KEYWORDS$1 = {
- $pattern: IDENT_RE,
- keyword: KEYWORDS,
- literal: LITERALS,
- built_in: BUILT_INS,
- "variable.language": BUILT_IN_VARIABLES
- };
- // https://tc39.es/ecma262/#sec-literals-numeric-literals
- const decimalDigits = '[0-9](_?[0-9])*';
- const frac = `\\.(${decimalDigits})`;
- // DecimalIntegerLiteral, including Annex B NonOctalDecimalIntegerLiteral
- // https://tc39.es/ecma262/#sec-additional-syntax-numeric-literals
- const decimalInteger = `0|[1-9](_?[0-9])*|0[0-7]*[89][0-9]*`;
- const NUMBER = {
- className: 'number',
- variants: [
- // DecimalLiteral
- { begin: `(\\b(${decimalInteger})((${frac})|\\.)?|(${frac}))` +
- `[eE][+-]?(${decimalDigits})\\b` },
- { begin: `\\b(${decimalInteger})\\b((${frac})\\b|\\.)?|(${frac})\\b` },
- // DecimalBigIntegerLiteral
- { begin: `\\b(0|[1-9](_?[0-9])*)n\\b` },
- // NonDecimalIntegerLiteral
- { begin: "\\b0[xX][0-9a-fA-F](_?[0-9a-fA-F])*n?\\b" },
- { begin: "\\b0[bB][0-1](_?[0-1])*n?\\b" },
- { begin: "\\b0[oO][0-7](_?[0-7])*n?\\b" },
- // LegacyOctalIntegerLiteral (does not include underscore separators)
- // https://tc39.es/ecma262/#sec-additional-syntax-numeric-literals
- { begin: "\\b0[0-7]+n?\\b" },
- ],
- relevance: 0
- };
- const SUBST = {
- className: 'subst',
- begin: '\\$\\{',
- end: '\\}',
- keywords: KEYWORDS$1,
- contains: [] // defined later
- };
- const HTML_TEMPLATE = {
- begin: '\.?html`',
- end: '',
- starts: {
- end: '`',
- returnEnd: false,
- contains: [
- hljs.BACKSLASH_ESCAPE,
- SUBST
- ],
- subLanguage: 'xml'
- }
- };
- const CSS_TEMPLATE = {
- begin: '\.?css`',
- end: '',
- starts: {
- end: '`',
- returnEnd: false,
- contains: [
- hljs.BACKSLASH_ESCAPE,
- SUBST
- ],
- subLanguage: 'css'
- }
- };
- const GRAPHQL_TEMPLATE = {
- begin: '\.?gql`',
- end: '',
- starts: {
- end: '`',
- returnEnd: false,
- contains: [
- hljs.BACKSLASH_ESCAPE,
- SUBST
- ],
- subLanguage: 'graphql'
- }
- };
- const TEMPLATE_STRING = {
- className: 'string',
- begin: '`',
- end: '`',
- contains: [
- hljs.BACKSLASH_ESCAPE,
- SUBST
- ]
- };
- const JSDOC_COMMENT = hljs.COMMENT(
- /\/\*\*(?!\/)/,
- '\\*/',
- {
- relevance: 0,
- contains: [
- {
- begin: '(?=@[A-Za-z]+)',
- relevance: 0,
- contains: [
- {
- className: 'doctag',
- begin: '@[A-Za-z]+'
- },
- {
- className: 'type',
- begin: '\\{',
- end: '\\}',
- excludeEnd: true,
- excludeBegin: true,
- relevance: 0
- },
- {
- className: 'variable',
- begin: IDENT_RE$1 + '(?=\\s*(-)|$)',
- endsParent: true,
- relevance: 0
- },
- // eat spaces (not newlines) so we can find
- // types or variables
- {
- begin: /(?=[^\n])\s/,
- relevance: 0
- }
- ]
- }
- ]
- }
- );
- const COMMENT = {
- className: "comment",
- variants: [
- JSDOC_COMMENT,
- hljs.C_BLOCK_COMMENT_MODE,
- hljs.C_LINE_COMMENT_MODE
- ]
- };
- const SUBST_INTERNALS = [
- hljs.APOS_STRING_MODE,
- hljs.QUOTE_STRING_MODE,
- HTML_TEMPLATE,
- CSS_TEMPLATE,
- GRAPHQL_TEMPLATE,
- TEMPLATE_STRING,
- // Skip numbers when they are part of a variable name
- { match: /\$\d+/ },
- NUMBER,
- // This is intentional:
- // See https://github.com/highlightjs/highlight.js/issues/3288
- // hljs.REGEXP_MODE
- ];
- SUBST.contains = SUBST_INTERNALS
- .concat({
- // we need to pair up {} inside our subst to prevent
- // it from ending too early by matching another }
- begin: /\{/,
- end: /\}/,
- keywords: KEYWORDS$1,
- contains: [
- "self"
- ].concat(SUBST_INTERNALS)
- });
- const SUBST_AND_COMMENTS = [].concat(COMMENT, SUBST.contains);
- const PARAMS_CONTAINS = SUBST_AND_COMMENTS.concat([
- // eat recursive parens in sub expressions
- {
- begin: /(\s*)\(/,
- end: /\)/,
- keywords: KEYWORDS$1,
- contains: ["self"].concat(SUBST_AND_COMMENTS)
- }
- ]);
- const PARAMS = {
- className: 'params',
- // convert this to negative lookbehind in v12
- begin: /(\s*)\(/, // to match the parms with
- end: /\)/,
- excludeBegin: true,
- excludeEnd: true,
- keywords: KEYWORDS$1,
- contains: PARAMS_CONTAINS
- };
- // ES6 classes
- const CLASS_OR_EXTENDS = {
- variants: [
- // class Car extends vehicle
- {
- match: [
- /class/,
- /\s+/,
- IDENT_RE$1,
- /\s+/,
- /extends/,
- /\s+/,
- regex.concat(IDENT_RE$1, "(", regex.concat(/\./, IDENT_RE$1), ")*")
- ],
- scope: {
- 1: "keyword",
- 3: "title.class",
- 5: "keyword",
- 7: "title.class.inherited"
- }
- },
- // class Car
- {
- match: [
- /class/,
- /\s+/,
- IDENT_RE$1
- ],
- scope: {
- 1: "keyword",
- 3: "title.class"
- }
- },
- ]
- };
- const CLASS_REFERENCE = {
- relevance: 0,
- match:
- regex.either(
- // Hard coded exceptions
- /\bJSON/,
- // Float32Array, OutT
- /\b[A-Z][a-z]+([A-Z][a-z]*|\d)*/,
- // CSSFactory, CSSFactoryT
- /\b[A-Z]{2,}([A-Z][a-z]+|\d)+([A-Z][a-z]*)*/,
- // FPs, FPsT
- /\b[A-Z]{2,}[a-z]+([A-Z][a-z]+|\d)*([A-Z][a-z]*)*/,
- // P
- // single letters are not highlighted
- // BLAH
- // this will be flagged as a UPPER_CASE_CONSTANT instead
- ),
- className: "title.class",
- keywords: {
- _: [
- // se we still get relevance credit for JS library classes
- ...TYPES,
- ...ERROR_TYPES
- ]
- }
- };
- const USE_STRICT = {
- label: "use_strict",
- className: 'meta',
- relevance: 10,
- begin: /^\s*['"]use (strict|asm)['"]/
- };
- const FUNCTION_DEFINITION = {
- variants: [
- {
- match: [
- /function/,
- /\s+/,
- IDENT_RE$1,
- /(?=\s*\()/
- ]
- },
- // anonymous function
- {
- match: [
- /function/,
- /\s*(?=\()/
- ]
- }
- ],
- className: {
- 1: "keyword",
- 3: "title.function"
- },
- label: "func.def",
- contains: [ PARAMS ],
- illegal: /%/
- };
- const UPPER_CASE_CONSTANT = {
- relevance: 0,
- match: /\b[A-Z][A-Z_0-9]+\b/,
- className: "variable.constant"
- };
- function noneOf(list) {
- return regex.concat("(?!", list.join("|"), ")");
- }
- const FUNCTION_CALL = {
- match: regex.concat(
- /\b/,
- noneOf([
- ...BUILT_IN_GLOBALS,
- "super",
- "import"
- ].map(x => `${x}\\s*\\(`)),
- IDENT_RE$1, regex.lookahead(/\s*\(/)),
- className: "title.function",
- relevance: 0
- };
- const PROPERTY_ACCESS = {
- begin: regex.concat(/\./, regex.lookahead(
- regex.concat(IDENT_RE$1, /(?![0-9A-Za-z$_(])/)
- )),
- end: IDENT_RE$1,
- excludeBegin: true,
- keywords: "prototype",
- className: "property",
- relevance: 0
- };
- const GETTER_OR_SETTER = {
- match: [
- /get|set/,
- /\s+/,
- IDENT_RE$1,
- /(?=\()/
- ],
- className: {
- 1: "keyword",
- 3: "title.function"
- },
- contains: [
- { // eat to avoid empty params
- begin: /\(\)/
- },
- PARAMS
- ]
- };
- const FUNC_LEAD_IN_RE = '(\\(' +
- '[^()]*(\\(' +
- '[^()]*(\\(' +
- '[^()]*' +
- '\\)[^()]*)*' +
- '\\)[^()]*)*' +
- '\\)|' + hljs.UNDERSCORE_IDENT_RE + ')\\s*=>';
- const FUNCTION_VARIABLE = {
- match: [
- /const|var|let/, /\s+/,
- IDENT_RE$1, /\s*/,
- /=\s*/,
- /(async\s*)?/, // async is optional
- regex.lookahead(FUNC_LEAD_IN_RE)
- ],
- keywords: "async",
- className: {
- 1: "keyword",
- 3: "title.function"
- },
- contains: [
- PARAMS
- ]
- };
- return {
- name: 'JavaScript',
- aliases: ['js', 'jsx', 'mjs', 'cjs'],
- keywords: KEYWORDS$1,
- // this will be extended by TypeScript
- exports: { PARAMS_CONTAINS, CLASS_REFERENCE },
- illegal: /#(?![$_A-z])/,
- contains: [
- hljs.SHEBANG({
- label: "shebang",
- binary: "node",
- relevance: 5
- }),
- USE_STRICT,
- hljs.APOS_STRING_MODE,
- hljs.QUOTE_STRING_MODE,
- HTML_TEMPLATE,
- CSS_TEMPLATE,
- GRAPHQL_TEMPLATE,
- TEMPLATE_STRING,
- COMMENT,
- // Skip numbers when they are part of a variable name
- { match: /\$\d+/ },
- NUMBER,
- CLASS_REFERENCE,
- {
- className: 'attr',
- begin: IDENT_RE$1 + regex.lookahead(':'),
- relevance: 0
- },
- FUNCTION_VARIABLE,
- { // "value" container
- begin: '(' + hljs.RE_STARTERS_RE + '|\\b(case|return|throw)\\b)\\s*',
- keywords: 'return throw case',
- relevance: 0,
- contains: [
- COMMENT,
- hljs.REGEXP_MODE,
- {
- className: 'function',
- // we have to count the parens to make sure we actually have the
- // correct bounding ( ) before the =>. There could be any number of
- // sub-expressions inside also surrounded by parens.
- begin: FUNC_LEAD_IN_RE,
- returnBegin: true,
- end: '\\s*=>',
- contains: [
- {
- className: 'params',
- variants: [
- {
- begin: hljs.UNDERSCORE_IDENT_RE,
- relevance: 0
- },
- {
- className: null,
- begin: /\(\s*\)/,
- skip: true
- },
- {
- begin: /(\s*)\(/,
- end: /\)/,
- excludeBegin: true,
- excludeEnd: true,
- keywords: KEYWORDS$1,
- contains: PARAMS_CONTAINS
- }
- ]
- }
- ]
- },
- { // could be a comma delimited list of params to a function call
- begin: /,/,
- relevance: 0
- },
- {
- match: /\s+/,
- relevance: 0
- },
- { // JSX
- variants: [
- { begin: FRAGMENT.begin, end: FRAGMENT.end },
- { match: XML_SELF_CLOSING },
- {
- begin: XML_TAG.begin,
- // we carefully check the opening tag to see if it truly
- // is a tag and not a false positive
- 'on:begin': XML_TAG.isTrulyOpeningTag,
- end: XML_TAG.end
- }
- ],
- subLanguage: 'xml',
- contains: [
- {
- begin: XML_TAG.begin,
- end: XML_TAG.end,
- skip: true,
- contains: ['self']
- }
- ]
- }
- ],
- },
- FUNCTION_DEFINITION,
- {
- // prevent this from getting swallowed up by function
- // since they appear "function like"
- beginKeywords: "while if switch catch for"
- },
- {
- // we have to count the parens to make sure we actually have the correct
- // bounding ( ). There could be any number of sub-expressions inside
- // also surrounded by parens.
- begin: '\\b(?!function)' + hljs.UNDERSCORE_IDENT_RE +
- '\\(' + // first parens
- '[^()]*(\\(' +
- '[^()]*(\\(' +
- '[^()]*' +
- '\\)[^()]*)*' +
- '\\)[^()]*)*' +
- '\\)\\s*\\{', // end parens
- returnBegin:true,
- label: "func.def",
- contains: [
- PARAMS,
- hljs.inherit(hljs.TITLE_MODE, { begin: IDENT_RE$1, className: "title.function" })
- ]
- },
- // catch ... so it won't trigger the property rule below
- {
- match: /\.\.\./,
- relevance: 0
- },
- PROPERTY_ACCESS,
- // hack: prevents detection of keywords in some circumstances
- // .keyword()
- // $keyword = x
- {
- match: '\\$' + IDENT_RE$1,
- relevance: 0
- },
- {
- match: [ /\bconstructor(?=\s*\()/ ],
- className: { 1: "title.function" },
- contains: [ PARAMS ]
- },
- FUNCTION_CALL,
- UPPER_CASE_CONSTANT,
- CLASS_OR_EXTENDS,
- GETTER_OR_SETTER,
- {
- match: /\$[(.]/ // relevance booster for a pattern common to JS libs: `$(something)` and `$.something`
- }
- ]
- };
- }
- export { javascript as default };
|