Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

webpack/browserify compatible AMD headers #18

Closed
NikGovorov opened this issue Jun 22, 2016 · 92 comments
Closed

webpack/browserify compatible AMD headers #18

NikGovorov opened this issue Jun 22, 2016 · 92 comments
Assignees
Milestone

Comments

@NikGovorov
Copy link

NikGovorov commented Jun 22, 2016

Hello,

Is there any way to change AMD headers to be compatible with webpack/browserify?

Are there any plans to support CommonJS, UMD or ECMAscript 6 for the package and external modules in monaco.d.ts?

@MazheM
Copy link

MazheM commented Jun 22, 2016

+1
I want to use it in my app, but i cant because use webpack for modules.

@alexdima
Copy link
Member

alexdima commented Jun 23, 2016

https://webpack.github.io/docs/amd.html

I thought webpack understands AMD code? What more should be done on our side to get the editor loadable with webpack?

@NikGovorov
Copy link
Author

I think webpack parses headers(imports/exports) and obviously using the constructs like https://github.com/Microsoft/vscode/blob/master/build/lib/bundle.ts#L278 breaks parsing.

@alexdima
Copy link
Member

Well, the editor.main.js that ships is already packed (contains 500+ AMD modules). Is the intent here to repack it (run the editor.main.js + all the javascript files we ship) through webpack? I have not used webpack so I'm not sure what we can do on our side... Should we also ship an unbundled/unpacked version where each AMD module sits in its own file?

@MazheM
Copy link

MazheM commented Jun 23, 2016

@alexandrudima i tried to use monaco in my script like that:

require(['vs/editor/editor.main'], function() {
    var editor = monaco.editor.create(document.getElementById('container'), {
        value: [
            'function x() {',
            '\tconsole.log("Hello world!");',
            '}'
        ].join('\n'),
        language: 'javascript'
    });
});

But it dosent work because Webpack cant find "edcore.main" for example, because he try to find it like file in "vs/editor" folder.
Log:

Module not found: Error: Cannot resolve module 'vs/editor/edcore.main' in src/js/vendor/vs/editor
resolve module vs/editor/edcore.main in src/js/vendor/vs/editor
  looking for modules in bower_components
    bower_components/vs doesn't exist (module as directory)
  looking for modules in src/js/vendor
    resolve 'file' or 'directory' editor/edcore.main in src/js/vendor/vs
      resolve file
        src/js/vendor/vs/editor/edcore.main doesn't exist
        src/js/vendor/vs/editor/edcore.main.web.js doesn't exist
        src/js/vendor/vs/editor/edcore.main.webpack.js doesn't exist
        src/js/vendor/vs/editor/edcore.main.js doesn't exist
        src/js/vendor/vs/editor/edcore.main.json doesn't exist
      resolve directory
        src/js/vendor/vs/editor/edcore.main doesn't exist (directory default file)
        src/js/vendor/vs/editor/edcore.main/package.json doesn't exist (directory description file)
[bower_components/vs]
[src/js/vendor/vs/editor/edcore.main]
[src/js/vendor/vs/editor/edcore.main.web.js]
[src/js/vendor/vs/editor/edcore.main.webpack.js]
[src/js/vendor/vs/editor/edcore.main.js]
[src/js/vendor/vs/editor/edcore.main.json]

Maybe unpacked version will be work.

@NikGovorov
Copy link
Author

NikGovorov commented Jun 23, 2016

@alexandrudima, yes I think shipping not bundled amd modules would definitely work for webpack/browserify.

@MazheM before you can see the error I faced you need to remap paths: 'vs': path.join(__dirname, 'node_modules/monaco-editor-core/dev/vs/'
https://webpack.github.io/docs/resolving.html

@NikGovorov
Copy link
Author

To be totally honest I believe that main npm package should only contain CommonJS version + d.ts with external modules. Bundles for different formats amd, umd etc(and ambient d.ts) should be distributed as bower or jspm packages, or at least as secondary npm packages.

@MazheM
Copy link

MazheM commented Jun 23, 2016

@NikGovorov Yes, i know it. I placed monaco to src/js/vendor/vs and in webpack config i have modulesDirectories: ['node_modules', 'src/js/vendor'],

@Matthias247
Copy link

I would also appreciate some examples or help on how to get it to run with webpack.
I tested the following approaches:

Added to webpack resolve configuration:

resolve: {
    alias: {
        vs: path.resolve(__dirname, 'node_modules/monaco-editor/dev/vs'),
    },

and did

require('vs/editor/editor.main')

in my main module.
Alternatively to the second step I also tried putting the following in the plugins section of webpack:

new webpack.ProvidePlugin({
       "monaco": "monaco-editor/dev/vs/editor/editor.main"
    }),

Both end up with missing references to edcore.main and fs (which is referenced by typescriptServices.js - but fs is a node module and shouldn't be available in the browser?)

@NikGovorov
Copy link
Author

@alexandrudima is any update on this?

@Matthias247
Copy link

Matthias247 commented Jul 11, 2016

I got monaco working now with webpack and angular2.

What I did:
Configure webpack to simply copy the bundled monaco version to my output directory with CopyWebpackPlugin:

plugins: [
    new CopyWebpackPlugin([
        {
            from: 'node_modules/monaco-editor/min/vs',
            to: 'vs',
        }
    ]),
],

and build a monaco angular2 component that loads the AMD loader if required and then loads monaco as a global variable:

import { Component, ViewChild, ElementRef, ViewQuery }
         from '@angular/core';
import { COMMON_DIRECTIVES  } 
         from '@angular/common';

import * as _ from 'lodash';

declare const monaco: any;
declare const require: any;

@Component({
  selector: 'monaco-editor',
  directives: [COMMON_DIRECTIVES],
  template: `
    <div id='editor' #editor class="monaco-editor" style="width: 1000px; height: 1000px">
  `,
})
export class MonacoEditor {
  @ViewChild('editor') editorContent: ElementRef;

  constructor(
  ) {
  }

  ngAfterViewInit() {
    var onGotAmdLoader = () => {
      // Load monaco
      (<any>window).require(['vs/editor/editor.main'], () => {
        this.initMonaco();
      });
    };

    // Load AMD loader if necessary
    if (!(<any>window).require) {
      var loaderScript = document.createElement('script');
      loaderScript.type = 'text/javascript';
      loaderScript.src = 'vs/loader.js';
      loaderScript.addEventListener('load', onGotAmdLoader);
      document.body.appendChild(loaderScript);
    } else {
      onGotAmdLoader();
    }
  }

  // Will be called once monaco library is available
  initMonaco() {
    var myDiv: HTMLDivElement = this.editorContent.nativeElement;
    var editor = monaco.editor.create(myDiv, {
      value: [
        'function x() {',
        '\tconsole.log("Hello world!");',
        '}'
      ].join('\n'),
      language: 'javascript'
    });
  }
}

This should work in a similar fashion for a react component.

It might be problematic if another application part loads an incompatible AMD loader and a commonjs or unbundled version might be easier to integrate, but it works for me now.
The plus side with that approach is that all monaco sources and licenses are at a clear path in the output folder.

@ebertmi
Copy link

ebertmi commented Jul 12, 2016

Still, it would be more usable in many projects if we could use it with webpack without the vs/loader.js. I tried to require the monaco-editor-core/vs/editor/editor.main which works until it tries to execute the define function with following error define cannot be used indirect.

@egoist
Copy link

egoist commented Jul 14, 2016

just did as what @Matthias247 suggested, but got Module not found: edcore.main, full error log:

ERROR in ./app/~/monaco-editor/dev/vs/editor/editor.main.js
Module not found: Error: Cannot resolve 'file' or 'directory' /Users/egoist/dev/jslab/app/node_modules/monaco-editor/dev/vs/editor/edcore.main in /Users/egoist/dev/jslab/app/node_modules/monaco-editor/dev/vs/editor
@ ./app/~/monaco-editor/dev/vs/editor/editor.main.js 83129:173-266 83136:173-267 83143:0-3357 83144:0-244

Version 0.5.3

@fxck
Copy link

fxck commented Jul 15, 2016

Any update? It's basically unusable in angular2 projects as it is.

@tugberkugurlu
Copy link

This is a major missing part 😞 would be really nice to get it working with webpack without hacking with CopyWebpackPlugin.

@rrfenton
Copy link

rrfenton commented Aug 4, 2016

Tried to implement monaco using systemJS with jspm, which supports AMD modules.

Getting the following error on import:

(index):14 Error: (SystemJS) Cannot read property 'performance' of undefined
    TypeError: Cannot read property 'performance' of undefined
        at AMDLoader (http://localhost:14992/jspm_packages/npm/monaco-editor@0.5.3/dev/vs/loader.js:1552:36)
        at eval (http://localhost:14992/jspm_packages/npm/monaco-editor@0.5.3/dev/vs/loader.js:1650:5)
        at Object.eval (http://localhost:14992/jspm_packages/npm/monaco-editor@0.5.3/dev/vs/loader.js:2228:3)
        at eval (http://localhost:14992/jspm_packages/npm/monaco-editor@0.5.3/dev/vs/loader.js:2230:4)
        at eval (http://localhost:14992/jspm_packages/npm/monaco-editor@0.5.3/dev/vs/loader.js:2231:3)
    Evaluating http://localhost:14992/jspm_packages/npm/monaco-editor@0.5.3/dev/vs/loader.js
    Evaluating http://localhost:14992/jspm_packages/npm/monaco-editor@0.5.3.js
    Evaluating http://localhost:14992/app/app.component.js
    Evaluating http://localhost:14992/app/main.js
    Error loading http://localhost:14992/app/main.js

On closer inspection it looks like it doesn't like global for _amdLoaderGlobal, so I set it to AMDLoader. Same thing with global being set for the NLSLoaderPlugin.

It then blows up in editor.main.js with the following error:

    TypeError: define is not a function
        at eval (http://localhost:14992/jspm_packages/npm/monaco-editor@0.5.3/dev/vs/editor/editor.main.js:14:5)
        at eval (http://localhost:14992/jspm_packages/npm/monaco-editor@0.5.3/dev/vs/editor/editor.main.js:74765:6)
        at Object.eval (http://localhost:14992/jspm_packages/npm/monaco-editor@0.5.3/dev/vs/editor/editor.main.js:75181:3)
        at eval (http://localhost:14992/jspm_packages/npm/monaco-editor@0.5.3/dev/vs/editor/editor.main.js:75183:4)
        at eval (http://localhost:14992/jspm_packages/npm/monaco-editor@0.5.3/dev/vs/editor/editor.main.js:75184:3)
    Evaluating http://localhost:14992/jspm_packages/npm/monaco-editor@0.5.3/dev/vs/editor/editor.main.js
    Evaluating http://localhost:14992/jspm_packages/npm/monaco-editor@0.5.3.js
    Evaluating http://localhost:14992/app/app.component.js
    Evaluating http://localhost:14992/app/main.js
    Error loading http://localhost:14992/app/main.js

Setting the window require and define to the systemJS implementations of amdRequire and amdDefine doesn't help, but setting the define / require in the editor.main.js to use the window level objects does.

At that point, it hits a monaco is undefined error.

Edit: here's the systemJS mapping:

map:{
"monaco-editor": "npm:monaco-editor@0.5.3"
},
"npm:monaco-editor@0.5.3": {
     "fs": "github:jspm/nodelibs-fs@0.1.2",
      "os": "github:jspm/nodelibs-os@0.1.0",
      "path": "github:jspm/nodelibs-path@0.1.0",
      "process": "github:jspm/nodelibs-process@0.1.2"
},

And the requires generated:

module.exports = require("npm:monaco-editor@0.5.3/dev/vs/loader");
module.exports = require("npm:monaco-editor@0.5.3/dev/vs/editor/editor.main");

@chrisber
Copy link

chrisber commented Sep 4, 2016

thanks to @Matthias247 I got it working with angular rc5 with angular-cli: 1.0.0-beta.11-webpack.8
@fxck @waywaaard my additions to @Matthias247 can be found here:
https://gist.github.com/chrisber/ef567098216319784c0596c5dac8e3aa

@superRaytin
Copy link

I have created a package named react-monaco-editor, it based on the approaches mentioned above (thanks to @Matthias247), hope it helps if someone uses React stack. Also hope it to be abandoned in the near future (after a better way appeared).

@danvk
Copy link

danvk commented Sep 6, 2016

For anyone using TypeScript and React, here's the component I came up with:

/// <reference path="../node_modules/monaco-editor/monaco.d.ts" />

import * as React from 'react';

declare const require: any;

interface Props {
  value: string;
  language: string;
  onChange: (newValue: string) => any;
}

export default class MonacoEditor extends React.Component<Props, {}> {
  editor: monaco.editor.IStandaloneCodeEditor;

  render(): JSX.Element {
    return <div className='monaco-editor' ref='editor'></div>;
  }

  componentDidMount() {
    // Monaco requires the AMD module loader to be present on the page. It is not yet
    // compatible with ES6 imports. Once that happens, we can get rid of this.
    // See https://github.com/Microsoft/monaco-editor/issues/18
    (window['require'])(['vs/editor/editor.main'], () => {
      this.editor = monaco.editor.create(this.refs['editor'] as HTMLDivElement, {
        value: this.props.value,
        language: this.props.language,
        lineNumbers: false,
      });

      this.editor.onDidChangeModelContent(event => {
        this.props.onChange(this.editor.getValue());
      });
    });
  }

  componentDidUpdate(prevProps: Props) {
    if (prevProps.value !== this.props.value && this.editor) {
      this.editor.setValue(this.props.value);
    }

    if (prevProps.language !== this.props.language) {
      throw new Error('<MonacoEditor> language cannot be changed.');
    }
  }
}

@bvaughn
Copy link

bvaughn commented Sep 19, 2016

@danvk, @Matthias247: How are you working around the edcore.main and fs errors in vs/language/typescript/lib/typescriptServices.js? The approaches you've both mentioned above still fail mid-way for me.

Sorry if this is a silly question. Kind of confused though as I've tried essentially the same thing the 2 of you mentioned but with different results.

@Matthias247
Copy link

@bvaughn I had that error only when trying to use Webpack ProvidePlugin.
The other approach that I describe here which uses CopyWebpackPlugin and monacos AMD loader does not produce an error.

@bvaughn
Copy link

bvaughn commented Sep 19, 2016

Hm. I was getting the error using their AMD loader and require.config (rather than CopyWebpackPlugin). I tried CopyWebpackPlugin at one point earlier but wasn't able to get it working.

@0plus1
Copy link

0plus1 commented Oct 21, 2016

@Matthias247 I took your example and nicely packaged it into an ugly npm package https://github.com/0plus1/ng2-monaco-editor, it's there for myself mainly and for the community, if anybody wishes to contribute please feel free!

@siovene
Copy link

siovene commented Nov 16, 2016

Regarding @Matthias247 's example, I would like to add that using this angular2 seed, it's not necessary to do any symlinking or using webpack to copy files.

You can simply import from node_modules as shown below:

    ngAfterViewInit() {
        var onGotAmdLoader = () => {
            // Load monaco
            (<any>window).require.config({ paths: { 'vs': 'node_modules/monaco-editor/min/vs' } });
            (<any>window).require(['vs/editor/editor.main'], () => {
                this.initMonaco();
            });
        };

        // Load AMD loader if necessary
        if (!(<any>window).require) {
            var loaderScript = document.createElement('script');
            loaderScript.type = 'text/javascript';
            loaderScript.src = 'node_modules/monaco-editor/min/vs/loader.js';
            loaderScript.addEventListener('load', onGotAmdLoader);
            document.body.appendChild(loaderScript);
        } else {
            onGotAmdLoader();
        }
    }

@0plus1
Copy link

0plus1 commented Nov 17, 2016

@siovene thanks for that, tested it on angular-cli and it doesn't work :-(

@corbinu
Copy link

corbinu commented Mar 14, 2018

@alexandrudima I believe #40 #60 can now be closed also

@alexdima
Copy link
Member

@rockymontana

The challenge with the editor is that is creates web workers. Web workers have a completely different JS context, i.e. they are a fresh runtime. Also, web workers must behave differently than the source code in the UI thread. In the UI thread, our sources set up a monaco API which you can then use in order to create an editor. In the worker thread, our sources begin listening to messages and reacting to them correctly, according to a protocol which we define between the UI thread and the workers.

@timkendrick has managed to bundle the worker code as a blob in the bundle (probably similar to how CSS can be included as a blob), but my limited webpack experience prevented me from setting up something so cool :).

The error you are running into is IMHO a configuration error. I believe you are not defining the getWorkerUrl correctly in your application. i.e.:

self.MonacoEnvironment = {
	getWorkerUrl: function (moduleId, label) {
		if (label === 'json') {
			return './json.worker.bundle.js';
		}
		if (label === 'css') {
			return './css.worker.bundle.js';
		}
		if (label === 'html') {
			return './html.worker.bundle.js';
		}
		if (label === 'typescript' || label === 'javascript') {
			return './ts.worker.bundle.js';
		}
		return './editor.worker.bundle.js';
	}
}

The UI thread talks 100% the same way to the web workers, both in the AMD and the ESM case. In the AMD case, a web worker is loaded and then it gets specialized into a language worker via loadForeignModule. In the ESM case, we load a web worker which is directly specialized and then it will ignore the loadForeignModule message, as it has already that module loaded. So I believe there is a configuration problem in your code where the non-specialized worker is loaded for a language worker.

TL;DR put a breakpoint in your getWorkerUrl implementation and make sure you return the correct URL. Also, if you are creating your own web workers, be sure to also cover those in that method.


@TheLarkInn @timkendrick I am a webpack noob and this might be obvious :). When you get a chance, can you please take a look at the two examples and let me know if there are easier ways to set things up for webpack or if there are more things we can do in the way we distribute the ESM source code to make things easier.

@OneCyrus
Copy link

wow thanks! that's great.

@TheLarkInn wouldn't it be possible to use dynamic imports to generate seperate bundles (files) for the workers?

@rockymontana
Copy link

@alexandrudima
Thanks a lot for the answer! I myself is quite the webpack noob so it's probably me doing something silly. The first thing I see is that you set self.MonacoEnvironment as where I set it to window.monacoEnvironment. The reason why I did that is so that I'm able to use the bundle where I need it instead of loading it globally even though I might not need it.

The second thing is that I try to use your config with my project which is a VueJS project.
I think I understand what has to be done, so I'll give it another shot and if I get lucky I'll see if I can explain what and why. Again - Thanks for the help!

@corbinu
Copy link

corbinu commented Mar 15, 2018

@rockymontana I am trying to get it up in a vue app also if you want to collaborate.

@mnpenner
Copy link

mnpenner commented Mar 15, 2018

Would require.context be of use here? It would force webpack to bundle all the workers without having to create separate entry points for each one, and should let you pull the paths out so you can load them into workers so we don't have to implement getWorkerUrl either, if I'm not mistaken.

@timkendrick
Copy link
Contributor

@mnpenner it turns out it's not quite as simple as that - see discussion on #759

@rockymontana
Copy link

@corbinu Absolutely! How would you like to do that?

@corbinu
Copy link

corbinu commented Mar 16, 2018

@rockymontana Well at the moment my version is in a private Gitlab repo. I can give you access or try to open source it later today. If it is easier your welcome to drop me an email

@rockymontana
Copy link

What’s your email?

@moranje
Copy link

moranje commented Mar 16, 2018

@rockymontana If you do find a solution for vue, do share. I'm trying to set this up in a CodeSandBox vue environment but I'm having trouble figuring this out as well. See the editor component if you are curious.

@rebornix
Copy link
Member

@moranje it might be because that Codesandbox does the bundling for you, which you can't choose the entry file (then no way to set up the worker). I'll sync with Ives and see if we can fix this problem or set up a Monaco editor template.

@corbinu
Copy link

corbinu commented Mar 16, 2018

edit: removed email

I will note mine is using the new vue-cli 3 beta

@rockymontana
Copy link

rockymontana commented Mar 16, 2018

I really don't understand how I should configure the getWorkerUrl.

If you look at the following screenshots you'll see that I can retrieve the workers, and I can use the MonacoEnvironment from the console:
bild
As you see in the screenshot above I can do this:
MonacoEnvironment.getWorkerUrl('', 'html')
which returns:
"http://127.0.0.1:8080/html.worker.js"

And the "proof" that it's reachable:
bild

If I can configure this wrong, could you explain it to me like I'm a five year old, because I really don't see what's being misconfigured here.

Here's the webpack config again:

  entry: {
    vue: './src/main.js',
    app: './src/monaco.js',
    "editor.worker": './node_modules/monaco-editor/esm/vs/editor/editor.worker.js',
    "json.worker": './node_modules/monaco-editor/esm/vs/language/json/json.worker',
    "css.worker": './node_modules/monaco-editor/esm/vs/language/css/css.worker',
    "html.worker": './node_modules/monaco-editor/esm/vs/language/html/html.worker',
    "ts.worker": './node_modules/monaco-editor/esm/vs/language/typescript/ts.worker',
  },
  plugins: [
    // Ignore require() calls in vs/language/typescript/lib/typescriptServices.js
    new webpack.IgnorePlugin(
      /^((fs)|(path)|(os)|(crypto)|(source-map-support))$/,
      /vs\/language\/typescript\/lib/
    )
  ],

And the generated markup from my index-file (at least the body parts):

    <script type="text/javascript" src="/vue.js"></script>
    <script type="text/javascript" src="/css.worker.js"></script>
    <script type="text/javascript" src="/html.worker.js"></script>
    <script type="text/javascript" src="/json.worker.js"></script>
    <script type="text/javascript" src="/ts.worker.js"></script>
    <script type="text/javascript" src="/editor.worker.js"></script>
    <script type="text/javascript" src="/app.js"></script>

Does that give you any ideas on why it's failing?

Update - Added Plugin section from the webpack config

@meyer
Copy link

meyer commented Mar 16, 2018

@rockymontana i don’t think the web worker files should be included in a script tag. The editor will create a new worker using the URLs you provide. You probably need to customise the template to exclude web worker files.

this probably belongs in a new issue btw. the self.require issue is being discussed in #759 and the remaining issues appear to be related to web workers being loaded in a non-web worker environment.

@rockymontana
Copy link

@meyer
I tried running the webpack example to see whether or not that would generate the workers as script tags, and just as you say, they actually don't. However - I can't really see anything that differs in the config, but then again - I suck pretty hard at webpack, so who am I to say hehe.

However - I checked the debugger dev tool tab and I can see that ts.worker.js is defined as a worker there, so I guess the browser can figure out that it's a worker even though it's a script tag.
¯_(ツ)_/¯

@moranje
Copy link

moranje commented Mar 16, 2018

@rebornix @CompuIves thanks, that'll be great

@corbinu
Copy link

corbinu commented Mar 16, 2018

I opensourced what I have so far here. It is a bit of a mess but everybody feel free to make experimental PRs or I am happy to add people as developers. I will move it to github as soon as I have time to clean it up and write a readme.

https://gitlab.com/corbinu/monaco-editor/

@TheLarkInn
Copy link
Member

@corbinu Can I recommend that for specific changes you break them up and submit PR's to the source? This can help ensure that changes are tracked and understood and the Diff's can be reviewed etc.

@TheLarkInn
Copy link
Member

And have issues that tie to said PR's it would be nice to have people discussion implementation details and whatnot in a shared context like a code review (even if it doesn't get merged, it still helps identify all the things that have to be cleaned up for this to work really well!)

@alexdima
Copy link
Member

@timkendrick has created a webpack plugin that hides all of this config away in PR #767 , and adds cool options to be able to pick whichever languages you want to use, or whichever features you want to use.

I will create a new editor release with this plugin shortly, and I hope that will simplify things tremendously.

@ngohungphuc
Copy link

Hi guys. Sorry to bother but I have write a blog post here to show how to integrate Monaco editor into Angular 7.

@vscodebot vscodebot bot locked and limited conversation to collaborators Oct 29, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests