An extensible library to highlight (and comment) JSX syntax in the Monaco Editor using Babel. It exposes its AST, so you can add your own syntax-based or custom highlights.
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);