123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258 |
- /*
- 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 regex = hljs.regex;
- const BUILT_INS = {
- $pattern: /[\w.\/]+/,
- built_in: [
- '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 = {
- $pattern: /[\w.\/]+/,
- 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 = regex.either(
- DOUBLE_QUOTED_ID_REGEX,
- SINGLE_QUOTED_ID_REGEX,
- BRACKET_QUOTED_ID_REGEX,
- PLAIN_ID_REGEX
- );
- const IDENTIFIER_REGEX = regex.concat(
- regex.optional(/\.|\.\/|\//), // relative or absolute path
- ANY_ID,
- regex.anyNumberOfTimes(regex.concat(
- PATH_DELIMITER_REGEX,
- ANY_ID
- ))
- );
- // identifier followed by a equal-sign (without the equal sign)
- const HASH_PARAM_REGEX = regex.concat(
- '(',
- BRACKET_QUOTED_ID_REGEX, '|',
- PLAIN_ID_REGEX,
- ')(?==)'
- );
- const HELPER_NAME_OR_PATH_EXPRESSION = { begin: IDENTIFIER_REGEX };
- 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 ]
- }
- ]
- };
- }
- export { handlebars as default };
|