diff --git a/src/languages/javascript.js b/src/languages/javascript.js index 9636f508da..3ab0675ba6 100644 --- a/src/languages/javascript.js +++ b/src/languages/javascript.js @@ -5,15 +5,9 @@ Category: common, scripting Website: https://developer.mozilla.org/en-US/docs/Web/JavaScript */ +import { JSX_MODE, JSX_LANGUAGE } from "./lib/jsx"; + export default function(hljs) { - var FRAGMENT = { - begin: '<>', - end: '' - }; - var XML_TAG = { - begin: /<[A-Za-z0-9\\._:-]+/, - end: /\/[A-Za-z0-9\\._:-]+>|\/>/ - }; var IDENT_RE = '[A-Za-z$_][0-9A-Za-z$_]*'; var KEYWORDS = { keyword: @@ -101,6 +95,15 @@ export default function(hljs) { contains: PARAMS_CONTAINS }; + const containers = [ + hljs.APOS_STRING_MODE, + hljs.QUOTE_STRING_MODE, + TEMPLATE_STRING, + hljs.C_BLOCK_COMMENT_MODE, + hljs.C_LINE_COMMENT_MODE + ]; + hljs.registerLanguage("_jsx", JSX_LANGUAGE({containers})); + return { name: 'JavaScript', aliases: ['js', 'jsx', 'mjs', 'cjs'], @@ -206,19 +209,7 @@ export default function(hljs) { end: /\s*/, skip: true, }, - { // JSX - variants: [ - { begin: FRAGMENT.begin, end: FRAGMENT.end }, - { begin: XML_TAG.begin, end: XML_TAG.end } - ], - subLanguage: 'xml', - contains: [ - { - begin: XML_TAG.begin, end: XML_TAG.end, skip: true, - contains: ['self'] - } - ] - }, + JSX_MODE ], relevance: 0 }, diff --git a/src/languages/lib/jsx.js b/src/languages/lib/jsx.js new file mode 100644 index 0000000000..b5c477e41b --- /dev/null +++ b/src/languages/lib/jsx.js @@ -0,0 +1,110 @@ +import { inherit } from "../../lib/utils"; + +const FRAGMENT = { + begin: '<>', + end: '' +}; +const XML_TAG = { + begin: /<[A-Za-z0-9\\._:-]+/, + end: /\/[A-Za-z0-9\\._:-]+>|\/>/ +}; + +export const JSX_MODE = { // JSX + variants: [ + { begin: FRAGMENT.begin, end: FRAGMENT.end }, + { begin: XML_TAG.begin, end: XML_TAG.end } + ], + subLanguage: '_jsx', + contains: [ + { + begin: XML_TAG.begin, end: XML_TAG.end, skip: true, + contains: ['self'] + } + ] +}; + +// note: this is not a language grammar itself but when called +// it returns a function that is the language grammar +// This could be improved if our registerLanguage API was enhanced +// to support passing options to grammars. +export const JSX_LANGUAGE = function({containers}) { + + // containers are any blocks which could prevent an inline JS mode + // from ending... inline javascript is denoted with `{ code }` + // so an example would be a string: ` { "}" }` Become the } is + // inside the container it should be ignored and not counted as + // ending the inline JS snippet + // + // these are passed in because it makes sense that these would + // already be defined by the parent language + containers = containers.map((x) => + // we need these to be quiet, and not output + inherit(x, {skip: true, className: null}) + ) + + return function (hljs) { + + const XML_IDENT_RE = '[A-Za-z0-9\\._:-]+'; + const XML_ENTITIES = { + className: 'symbol', + begin: '&[a-z]+;|&#[0-9]+;|&#x[a-f0-9]+;' + }; + const INLINE_JS = { + begin: /{/, + end: /}/, + // needs to eat strings, regex, comments, etc. + contains: [].concat(containers), + subLanguage: "javascript" + }; + const TAG_INTERNALS = { + endsWithParent: true, + illegal: /`]+/} + ] + } + ] + } + ] + }; + const SIMPLE_TAG = { + className: 'tag', + begin: '', + contains: [ + { + className: 'name', begin: /[^\/><\s]+/, relevance: 0 + }, + TAG_INTERNALS + ] + } + + return { + className:"xml", + case_insensitive: true, + disableAutodetect: true, + contains: [ + SIMPLE_TAG, + INLINE_JS + ] + }; + + } +} + diff --git a/test/markup/javascript/jsx.txt b/test/markup/javascript/jsx.txt index a76ef8eecd..777ac0daae 100644 --- a/test/markup/javascript/jsx.txt +++ b/test/markup/javascript/jsx.txt @@ -16,8 +16,13 @@ class App extends Component { return (
+ {/* comment */} + { test "}" } +
);