No syntax highlighting for javascript written inside a script tag for an html file
See original GitHub issueI am making an online code editor and using monaco-editor. I wanted to support vscode themes and have written some code to get this mostly working. One of the issues I am seeing is with javascript written inside a script tag of an html file has no syntax highlighting. The html has syntax highlight, as does css inside of a style tag, but not javascript.
Screenshot: https://i.stack.imgur.com/SLcir.png.
I am not ruling out that my code has issues, however if it were not a monaco-editor issue then I would have expected the html and css to also not have syntax highlighting.
If this is a known issue I could not find any answers on stack overflow or by searching the open issues (however I may not have been searching for the correct keywords). If this is not an issue with monaco-editor any guidance to get this working would be greatly appreciated. I will add my webpack setup and editor source for reference:
webpack.config.common:
const path = require('path');
const fs = require('fs');
const webpack = require('webpack');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MonacoWebpackPlugin = require('monaco-editor-webpack-plugin');
module.exports = {
resolve: {
extensions: ['*', '.js', '.jsx', '.json', '.ttf'],
},
entry: path.resolve(__dirname, 'src/index.js'),
target: 'web',
output: {
path: path.resolve(__dirname, 'build'),
publicPath: '/',
filename: '[name].bundle.js',
},
node: {
fs: 'empty',
},
plugins: [
new CleanWebpackPlugin(),
new webpack.DefinePlugin({
'process.env': {
APP_VERSION: JSON.stringify(version),
},
}),
new HtmlWebpackPlugin({
template: './public/index.ejs',
favicon: './src/assets/svg/experiment.svg',
inject: true,
filename: 'index.html',
}),
new MonacoWebpackPlugin({
languages: [
'html',
'markdown',
'css',
// 'javascript',
// 'typescript',
// 'json',
],
features: ['!gotoSymbol'],
}),
],
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: [
{
loader: "babel-loader",
options: {
cacheDirectory: true,
presets: ['@babel/preset-env'],
plugins: [
[
'import', { libraryName: 'antd', libraryDirectory: 'es', style: true },
'react-hot-loader/babel',
],
],
},
},
],
},
{
test: /\.(jpe?g|png|gif|ico|ttf|svg)$/i,
use: [
{
loader: 'file-loader',
options: {
name: '[name].[ext]',
},
},
],
},
],
},
};
Monaco implementation:
import React, { Component, createRef } from 'react';
import ReactDOM from 'react-dom';
import PropTypes from 'prop-types';
import MonacoEditor from 'react-monaco-editor';
import { Registry } from 'monaco-textmate';
import { wireTmGrammars } from 'monaco-editor-textmate';
import shortid from 'shortid';
import ReactResizeDetector from 'react-resize-detector';
import { getCodeMode } from '../../../../helpers';
import {
EDITORS,
MONACO_DEFAULT_OPTIONS,
TEMPLATE_IDS,
LANGUAGES,
DEFAULT_IDLE_TIMEOUT,
SCRIPTS,
CODE_SNIPPETS,
} from '../../../../constants';
import { highlightJsxSyntax } from './utils/jsxSyntaxHighlight';
import { ConditionalWrapper, EditorContainer, JsxContainer } from './utils/monacoEditorStyles';
import htmlGrammar from './grammars/html.json';
import mdGrammar from './grammars/markdown.tmLanguage.json';
import cssGrammar from './grammars/css.tmGrammar.json';
import jsGrammar from './grammars/JavaScript.tmLanguage.json';
import jsxGrammar from './grammars/JavaScriptReact.tmLanguage.json';
import tsGrammar from './grammars/TypeScript.tmLanguage.json';
import tsxGrammar from './grammars/TypeScriptReact.tmLanguage.json';
import jsonGrammar from './grammars/json.tmLanguage.json';
import svelteGrammar from './grammars/svelte.tmLanguage.json';
const registry = new Registry({
getGrammarDefinition: async (scopeName) => {
if (scopeName === 'source.js') {
return {
format: 'json',
content: jsGrammar, // await (await fetch('./grammars/JavaScript.tmLanguage.json')).text()
};
}
if (scopeName === 'source.ts') {
return {
format: 'json',
content: tsGrammar, // await (await fetch('./grammars/TypeScript.tmLanguage.json')).text()
};
}
return null;
},
});
class monacoEditor extends Component {
editor = null;
constructor(props) {
super(props);
this.ref = createRef();
this.state = {
localCode: this.props.selectedFile.content,
language: getCodeMode(EDITORS.MONACO.value, this.props.selectedFile.mimeType),
idleTimer: DEFAULT_IDLE_TIMEOUT,
// map of monaco "language id's" to TextMate scopeNames
grammars: new Map(),
};
this.editorWillMount = this.editorWillMount.bind(this);
this.editorDidMount = this.editorDidMount.bind(this);
this.tick = this.tick.bind(this);
this.handleUpdate = this.handleUpdate.bind(this);
this.onChange = this.onChange.bind(this);
this.setCurrentTheme = this.setCurrentTheme.bind(this);
this.syntaxHighlight = this.syntaxHighlight.bind(this);
this.updateDecorations = this.updateDecorations.bind(this);
this.resetIdleTimer = this.resetIdleTimer.bind(this);
this.componentWillUnmount = this.componentWillUnmount.bind(this);
}
editorWillMount(monaco) {
const { grammars } = this.state;
const { selectedFile, templateId } = this.props;
// grammars.set('html', 'text.html.basic');
// grammars.set('svelte', 'source.svelte');
// grammars.set('markdown', 'text.html.markdown');
// grammars.set('css', 'source.css');
grammars.set('javascript', 'source.js');
// grammars.set('javascriptreact', 'source.js');
grammars.set('typescript', 'source.ts');
// grammars.set('typescriptreact', 'source.tsx');
// grammars.set('json', 'source.json');
// monaco.languages.register({id: 'html'});
// monaco.languages.register({id: 'svelte'});
// monaco.languages.register({id: 'markdown'});
// monaco.languages.register({id: 'css'});
monaco.languages.register({ id: 'javascript' });
// monaco.languages.register({id: 'javascriptreact'});
monaco.languages.register({ id: 'typescript' });
// monaco.languages.register({id: 'typescriptreact'});
// monaco.languages.register({id: 'json'});
// monaco.languages.register({id: 'svelte'});
this.setCurrentTheme(monaco);
if (templateId === TEMPLATE_IDS.REACT && selectedFile.mimeType === LANGUAGES.ES6.value) {
const container = document.getElementById("contrived-monaco");
const editor = monaco.editor.create(container, {
model: monaco.editor.createModel(selectedFile.content, "javascript", new monaco.Uri.file(`./${shortid.generate()}.jsx`)),
});
}
}
editorDidMount(editor, monaco) {
const { selectedFile } = this.props;
this.editor = editor;
this.monaco = monaco;
editor.focus();
}
componentDidUpdate(prevProps, prevState) {
const { selectedFile, selectedTheme } = this.props;
if (selectedFile.id !== prevProps.selectedFile.id) {
this.setState((_prevState) => ({ localCode: selectedFile.content }));
}
if (selectedTheme.id !== prevProps.selectedTheme.id) {
this.setCurrentTheme(this.monaco);
}
}
tick() {
if (this.state.idleTimer === 0) {
this.handleUpdate();
} else {
this.setState((prevState) => ({ idleTimer: prevState.idleTimer - 1 }));
}
}
handleUpdate() {
const { selectedFile, updateCode, setUnsavedChanges } = this.props;
const { localCode } = this.state;
updateCode({ code: localCode, selectedFile });
if (selectedFile.content !== localCode) setUnsavedChanges();
clearTimeout(this.timer);
}
onChange(newValue, e) {
const { selectedFile } = this.props;
this.setState((prevState) => ({ localCode: newValue }));
this.resetIdleTimer();
}
setCurrentTheme(monaco) {
if (monaco && this.props.selectedTheme) {
monaco.editor.defineTheme('custom-theme', this.props.selectedTheme.theme);
this.liftOff(monaco).then(() => monaco.editor.setTheme('custom-theme'));
}
}
async liftOff(monaco) {
await wireTmGrammars(monaco, registry, this.state.grammars, this.editor);
}
resetIdleTimer() {
clearInterval(this.timer);
this.setState((prevState) => ({ idleTimer: DEFAULT_IDLE_TIMEOUT }));
this.timer = setInterval(() => this.tick(), 300);
}
componentWillUnmount() {
if (typeof this.editor !== 'undefined') {
this.editor.dispose();
}
clearTimeout(this.timer);
ReactDOM.findDOMNode(this).removeEventListener('mousemove', this.resetIdleTimer);
ReactDOM.findDOMNode(this).removeEventListener('keydown', this.resetIdleTimer);
}
render() {
const { selectedFile, templateId, editorWidth } = this.props;
const { language, localCode } = this.state;
return (
<ReactResizeDetector
key={`${selectedFile.id}`}
handleWidth
handleHeight
onResize={() => {
if (this.editor) {
this.editor.layout();
}
}}
>
<ConditionalWrapper
condition={(templateId === TEMPLATE_IDS.REACT && selectedFile.mimeType === LANGUAGES.ES6.value)}
ifTrueWrapper={children => <JsxContainer id="contrived-monaco">{children}</JsxContainer>}
ifFalseWrapper={children => <EditorContainer id="contrived-monaco">{children}</EditorContainer>}
>
<MonacoEditor
ref="monaco"
language={language}
theme="vs-dark"
defaultValue=""
value={localCode}
options={MONACO_DEFAULT_OPTIONS}
onChange={this.onChange}
editorWillMount={this.editorWillMount}
editorDidMount={this.editorDidMount}
/>
</ConditionalWrapper>
</ReactResizeDetector>
);
}
}
monacoEditor.propTypes = {
editorWidth: PropTypes.number,
templateId: PropTypes.string.isRequired,
selectedFile: PropTypes.object.isRequired,
selectedTheme: PropTypes.object.isRequired,
loadedScripts: PropTypes.array.isRequired,
updateCode: PropTypes.func.isRequired,
setUnsavedChanges: PropTypes.func.isRequired,
};
monacoEditor.defaultProps = {
editorWidth: 0,
};
export default monacoEditor;
Issue Analytics
- State:
- Created 3 years ago
- Comments:8
Try with: npm install then npm run dev
Sorry for the trouble, I figured it out.