123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352 |
- /**
- * @param {string} value
- * @returns {RegExp}
- * */
- /**
- * @param {RegExp | string } re
- * @returns {string}
- */
- function source(re) {
- if (!re) return null;
- if (typeof re === "string") return re;
- return re.source;
- }
- /**
- * @param {RegExp | string } re
- * @returns {string}
- */
- function anyNumberOfTimes(re) {
- return concat('(', re, ')*');
- }
- /**
- * @param {RegExp | string } re
- * @returns {string}
- */
- function optional(re) {
- return concat('(', re, ')?');
- }
- /**
- * @param {...(RegExp | string) } args
- * @returns {string}
- */
- function concat(...args) {
- const joined = args.map((x) => source(x)).join("");
- return joined;
- }
- /**
- * Any of the passed expresssions may match
- *
- * Creates a huge this | this | that | that match
- * @param {(RegExp | string)[] } args
- * @returns {string}
- */
- function either(...args) {
- const joined = '(' + args.map((x) => source(x)).join("|") + ")";
- return joined;
- }
- /*
- Language: Handlebars
- Requires: xml.js
- Author: Robin Ward <robin.ward@gmail.com>
- Description: Matcher for Handlebars as well as EmberJS additions.
- Website: https://handlebarsjs.com
- Category: template
- */
- function handlebars(hljs) {
- const BUILT_INS = {
- 'builtin-name': [
- 'action',
- 'bindattr',
- 'collection',
- 'component',
- 'concat',
- 'debugger',
- 'each',
- 'each-in',
- 'get',
- 'hash',
- 'if',
- 'in',
- 'input',
- 'link-to',
- 'loc',
- 'log',
- 'lookup',
- 'mut',
- 'outlet',
- 'partial',
- 'query-params',
- 'render',
- 'template',
- 'textarea',
- 'unbound',
- 'unless',
- 'view',
- 'with',
- 'yield'
- ]
- };
- const LITERALS = {
- literal: [
- 'true',
- 'false',
- 'undefined',
- 'null'
- ]
- };
- // as defined in https://handlebarsjs.com/guide/expressions.html#literal-segments
- // this regex matches literal segments like ' abc ' or [ abc ] as well as helpers and paths
- // like a/b, ./abc/cde, and abc.bcd
- const DOUBLE_QUOTED_ID_REGEX = /""|"[^"]+"/;
- const SINGLE_QUOTED_ID_REGEX = /''|'[^']+'/;
- const BRACKET_QUOTED_ID_REGEX = /\[\]|\[[^\]]+\]/;
- const PLAIN_ID_REGEX = /[^\s!"#%&'()*+,.\/;<=>@\[\\\]^`{|}~]+/;
- const PATH_DELIMITER_REGEX = /(\.|\/)/;
- const ANY_ID = either(
- DOUBLE_QUOTED_ID_REGEX,
- SINGLE_QUOTED_ID_REGEX,
- BRACKET_QUOTED_ID_REGEX,
- PLAIN_ID_REGEX
- );
- const IDENTIFIER_REGEX = concat(
- optional(/\.|\.\/|\//), // relative or absolute path
- ANY_ID,
- anyNumberOfTimes(concat(
- PATH_DELIMITER_REGEX,
- ANY_ID
- ))
- );
- // identifier followed by a equal-sign (without the equal sign)
- const HASH_PARAM_REGEX = concat(
- '(',
- BRACKET_QUOTED_ID_REGEX, '|',
- PLAIN_ID_REGEX,
- ')(?==)'
- );
- const HELPER_NAME_OR_PATH_EXPRESSION = {
- begin: IDENTIFIER_REGEX,
- lexemes: /[\w.\/]+/
- };
- const HELPER_PARAMETER = hljs.inherit(HELPER_NAME_OR_PATH_EXPRESSION, {
- keywords: LITERALS
- });
- const SUB_EXPRESSION = {
- begin: /\(/,
- end: /\)/
- // the "contains" is added below when all necessary sub-modes are defined
- };
- const HASH = {
- // fka "attribute-assignment", parameters of the form 'key=value'
- className: 'attr',
- begin: HASH_PARAM_REGEX,
- relevance: 0,
- starts: {
- begin: /=/,
- end: /=/,
- starts: {
- contains: [
- hljs.NUMBER_MODE,
- hljs.QUOTE_STRING_MODE,
- hljs.APOS_STRING_MODE,
- HELPER_PARAMETER,
- SUB_EXPRESSION
- ]
- }
- }
- };
- const BLOCK_PARAMS = {
- // parameters of the form '{{#with x as | y |}}...{{/with}}'
- begin: /as\s+\|/,
- keywords: {
- keyword: 'as'
- },
- end: /\|/,
- contains: [
- {
- // define sub-mode in order to prevent highlighting of block-parameter named "as"
- begin: /\w+/
- }
- ]
- };
- const HELPER_PARAMETERS = {
- contains: [
- hljs.NUMBER_MODE,
- hljs.QUOTE_STRING_MODE,
- hljs.APOS_STRING_MODE,
- BLOCK_PARAMS,
- HASH,
- HELPER_PARAMETER,
- SUB_EXPRESSION
- ],
- returnEnd: true
- // the property "end" is defined through inheritance when the mode is used. If depends
- // on the surrounding mode, but "endsWithParent" does not work here (i.e. it includes the
- // end-token of the surrounding mode)
- };
- const SUB_EXPRESSION_CONTENTS = hljs.inherit(HELPER_NAME_OR_PATH_EXPRESSION, {
- className: 'name',
- keywords: BUILT_INS,
- starts: hljs.inherit(HELPER_PARAMETERS, {
- end: /\)/
- })
- });
- SUB_EXPRESSION.contains = [SUB_EXPRESSION_CONTENTS];
- const OPENING_BLOCK_MUSTACHE_CONTENTS = hljs.inherit(HELPER_NAME_OR_PATH_EXPRESSION, {
- keywords: BUILT_INS,
- className: 'name',
- starts: hljs.inherit(HELPER_PARAMETERS, {
- end: /\}\}/
- })
- });
- const CLOSING_BLOCK_MUSTACHE_CONTENTS = hljs.inherit(HELPER_NAME_OR_PATH_EXPRESSION, {
- keywords: BUILT_INS,
- className: 'name'
- });
- const BASIC_MUSTACHE_CONTENTS = hljs.inherit(HELPER_NAME_OR_PATH_EXPRESSION, {
- className: 'name',
- keywords: BUILT_INS,
- starts: hljs.inherit(HELPER_PARAMETERS, {
- end: /\}\}/
- })
- });
- const ESCAPE_MUSTACHE_WITH_PRECEEDING_BACKSLASH = {
- begin: /\\\{\{/,
- skip: true
- };
- const PREVENT_ESCAPE_WITH_ANOTHER_PRECEEDING_BACKSLASH = {
- begin: /\\\\(?=\{\{)/,
- skip: true
- };
- return {
- name: 'Handlebars',
- aliases: [
- 'hbs',
- 'html.hbs',
- 'html.handlebars',
- 'htmlbars'
- ],
- case_insensitive: true,
- subLanguage: 'xml',
- contains: [
- ESCAPE_MUSTACHE_WITH_PRECEEDING_BACKSLASH,
- PREVENT_ESCAPE_WITH_ANOTHER_PRECEEDING_BACKSLASH,
- hljs.COMMENT(/\{\{!--/, /--\}\}/),
- hljs.COMMENT(/\{\{!/, /\}\}/),
- {
- // open raw block "{{{{raw}}}} content not evaluated {{{{/raw}}}}"
- className: 'template-tag',
- begin: /\{\{\{\{(?!\/)/,
- end: /\}\}\}\}/,
- contains: [OPENING_BLOCK_MUSTACHE_CONTENTS],
- starts: {
- end: /\{\{\{\{\//,
- returnEnd: true,
- subLanguage: 'xml'
- }
- },
- {
- // close raw block
- className: 'template-tag',
- begin: /\{\{\{\{\//,
- end: /\}\}\}\}/,
- contains: [CLOSING_BLOCK_MUSTACHE_CONTENTS]
- },
- {
- // open block statement
- className: 'template-tag',
- begin: /\{\{#/,
- end: /\}\}/,
- contains: [OPENING_BLOCK_MUSTACHE_CONTENTS]
- },
- {
- className: 'template-tag',
- begin: /\{\{(?=else\}\})/,
- end: /\}\}/,
- keywords: 'else'
- },
- {
- className: 'template-tag',
- begin: /\{\{(?=else if)/,
- end: /\}\}/,
- keywords: 'else if'
- },
- {
- // closing block statement
- className: 'template-tag',
- begin: /\{\{\//,
- end: /\}\}/,
- contains: [CLOSING_BLOCK_MUSTACHE_CONTENTS]
- },
- {
- // template variable or helper-call that is NOT html-escaped
- className: 'template-variable',
- begin: /\{\{\{/,
- end: /\}\}\}/,
- contains: [BASIC_MUSTACHE_CONTENTS]
- },
- {
- // template variable or helper-call that is html-escaped
- className: 'template-variable',
- begin: /\{\{/,
- end: /\}\}/,
- contains: [BASIC_MUSTACHE_CONTENTS]
- }
- ]
- };
- }
- /*
- Language: HTMLBars (legacy)
- Requires: xml.js
- Description: Matcher for Handlebars as well as EmberJS additions.
- Website: https://github.com/tildeio/htmlbars
- Category: template
- */
- function htmlbars(hljs) {
- const definition = handlebars(hljs);
- definition.name = "HTMLbars";
- // HACK: This lets handlebars do the auto-detection if it's been loaded (by
- // default the build script will load in alphabetical order) and if not (perhaps
- // an install is only using `htmlbars`, not `handlebars`) then this will still
- // allow HTMLBars to participate in the auto-detection
- // worse case someone will have HTMLbars and handlebars competing for the same
- // content and will need to change their setup to only require handlebars, but
- // I don't consider this a breaking change
- if (hljs.getLanguage("handlebars")) {
- definition.disableAutodetect = true;
- }
- return definition;
- }
- module.exports = htmlbars;
|