question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

No syntax highlighting for javascript written inside a script tag for an html file

See original GitHub issue

I 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:closed
  • Created 3 years ago
  • Comments:8

github_iconTop GitHub Comments

1reaction
ChristopherHButlercommented, Apr 23, 2021

Try with: npm install then npm run dev

1reaction
ChristopherHButlercommented, Feb 23, 2021

Sorry for the trouble, I figured it out.

Read more comments on GitHub >

github_iconTop Results From Across the Web

monaco-editor does not syntax highlight inside script tag of ...
I am using react-monaco-editor in an application and I have noticed that it does not syntax-highlight javascript code inside a script tag of ......
Read more >
Syntax highlighting does not work for script tag with non ...
Hello everyone, I have search several similar posts but did not find a solution for my problem. My script-tag inside an html file...
Read more >
HTML script tag - W3Schools
The <script> tag is used to embed a client-side script (JavaScript). The <script> element either contains scripting statements, or it points to an...
Read more >
All about <script> - Level Up Coding
In this article, you'll learn about JavaScript code injection in an HTML document, the possible ways to do it, the difference between these...
Read more >
How to add Syntax Highlighting to your Webflow Site - YouTube
Sometimes on your website, you want to show code and not render it. You could write out your code in a paragraph along...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found