An extensible library to highlight (and comment) JSX syntax in the Monaco Editor using Babel. It exposes its AST after it does its magic, so you can add your own syntax-based or custom highlights.
# with npm (assuming you are already using monaco-editor)
npm i @babel/parser @babel/traverse monaco-jsx-highlighter
# with yarn (assuming you are already using monaco-editor)
yarn add @babel/parser @babel/traverse monaco-jsx-highlighter
import monaco from 'monaco-editor';
import {parse} from "@babel/parser";
import traverse from "@babel/traverse";
import MonacoJSXHighlighter, {makeBabelParse} from 'monaco-jsx-highlighter';
// Minimal Babel setup for React JSX parsing:
const babelParse = code => parse(code, {
sourceType: "module",
plugins: ["jsx"]
});
// Instantiate the highlighter
const monacoJSXHighlighter = new MonacoJSXHighlighter(
monaco, babelParse, traverse, getMonacoEditor()
);
// Activate highlighting (debounceTime default: 100ms)
monacoJSXHighlighter.highlightOnDidChangeModelContent(100);
// Activate JSX commenting
monacoJSXHighlighter.addJSXCommentCommand();
// Done =)
function getMonacoEditor(){
return monaco.editor.create(
document.getElementById("editor"), {
value: 'const AB=<A x={d}><B>{"hello"}</B></A>;',
language: 'javascript'
});
}
//use makeBabelParse if unsure of the config you need for TSX
makeBabelParse
: babel's parse configuration for JSX/TSX (thanks @HaimCandiTech)If you have used 0.x versions, you'll notice JsCodeShift has been replaced with Babel:
- import j from 'jscodeshift';
+ import {parse} from "@babel/parser";
+ import traverse from "@babel/traverse";
This only affects the constructor signature:
+ const babelParse = code => parse(code, {sourceType: "module", plugins: ["jsx"]});
const monacoJSXHighlighter = new MonacoJSXHighlighter(
monaco,
- j,
+ babelParse, traverse,
monacoEditor
);
Also, monacoJSXHighlighter.highlightOnDidChangeModelContent
method now has an
optional debounce time as first parameter on its signature:
monacoJSXHighlighter.highlightOnDidChangeModelContent(
- afterHighlight: func,
+ debounceTime: number, afterHighlight: func,
...)
It requires monaco-editor
, @babel/parser
and @babel/traverse
, for
convenience, they are listed as peer dependencies and passed by reference (so
you can do lazy loading). Please install them before monaco-jsx-highlighter
;
Install the package in your project directory with:
# with npm
npm install @babel/parser
npm install @babel/traverse
npm install monaco-jsx-highlighter
# with yarn
yarn add @babel/parser
yarn add @babel/traverse
yarn add monaco-jsx-highlighter
import {JSXTypes} from 'monaco-jsx-highlighter';
// JSXTypes:JSX Syntax types and their CSS classnames.
// Customize the color font in JSX texts: .myCustomCSS {color: red;}
JSXTypes.JSXText.options.inlineClassName = "myCustomCSS";
Take a look of
the src/JSXColoringProvider.css
file
and override the CSS classes you need. Make sure to import your customization
CSS files after you import monaco-jsx-highlighter
.
After your have a Monaco JSX Highlighter instance, monacoJSXHighlighter
:
const defaultOptions = {
parser: 'babel', // for reference only, only babel is supported right now
isHighlightGlyph: false, // if JSX elements should decorate the line number gutter
iShowHover: false, // if JSX types should tooltip with their type info
isUseSeparateElementStyles: false, // if opening elements and closing elements have different styling
isThrowJSXParseErrors: false, // Only JSX Syntax Errors are not thrown by default when parsing, true will throw like any other parsign error
};
const monacoJSXHighlighter = new MonacoJSXHighlighter(
monaco, babelParse, traverse, monacoEditor, defaultOptions
);
The highlight activation method, monacoJSXHighlighter.highlightOnDidChangeModelContent(debounceTime: number, afterHighlight: func, ...)
, accepts a callback among other parameters. The callback afterHighlight
passes the AST used to highlight the code. Passing parameters and using the disposer function returned by the call are optional.
Note: The disposer is always called when the editor is disposed.
// Optional: Disable highlighting when needed (e.g. toggling, unmounting, pausing)
const highlighterDisposeFunc = monacoJSXHighlighter.
highlightOnDidChangeModelContent(
100,
ast=>{}
);
highlighterDisposeFunc(); // if you need to
// Internally the highlighter is triggering after each code change debounced
let tid = null;
let debounceTime = 100; // default
monacoEditor.onDidChangeModelContent(() => {
clearTimeout(tid);
tid = setTimeout(() => {
monacoJSXHighlighter.highlightCode();
},
debounceTime,
);
});
// You can do the higlighting directly at anytime
monacoJSXHighlighter.highlightCode();
// or customize its behavior by adding custom highlighting after the JSX highlighting
const afterHighlight = (
ast // the ast generate by Babel
) => {
//... your customization code, check Babel for more info about AST types
//optional: array with the decorators created by the highlighter, push your decorator ids to this array
monacoJSXHighlighter.JSXDecoratorIds.push(...yourdecoratorsIds);
};
monacoJSXHighlighter.highlightCode(
afterHighlight, //default: ast=>ast
onError, // default: error=>console.error(error)
getAstPromise, // default: parse(monacoEditor.getValue())
onParseErrors, // default: error=>error
);
Additionally, you can add JSX commenting to your monaco editor with
monacoJSXHighlighter.addJSXCommentCommand()
:
comments in JSX children will result in {/*...*/}
instead of //...
. It mimics the commenting behavior of
the WebStorm IDE.
Follow this code to find out other perks:
// Optional: Disable JSX commenting when needed (e.g. toggling, unmounting, pausing)
const commentDisposeFunc = monacoJSXHighlighter.addJSXCommentCommand();
commentDisposeFunc(); // if you need to
import {configureLocToMonacoRange} from 'monaco-jsx-highlighter';
// locToMonacoRange: converts Babel locations to Monaco Ranges
const locToMonacoRange = configureLocToMonacoRange(monaco);
const monacoRange = locToMonacoRange(babelAstNode.loc);