GithubHelp home page GithubHelp logo

Comments (92)

alexdima avatar alexdima commented on May 22, 2024 75

Thank you for your patience! ❤️


I am happy to let you know that we have just published v0.11.0, which is the first release to include the editor packaged in an ESM format under the esm folder. This pretty much means that the editor can be consumed as simple as:

import * as monaco from 'monaco-editor';

There are 2 complete samples of using the editor with webpack that I have put together:


1. "Basic"

Full working example here.

The integration burden consists of:

  • You must package web workers. See webpack.config.js
  • You must let the runtime know where you have packaged those web workers. See index.js
  • You must ignore require() calls in typescriptServices.js when packaging. See webpack.config.js

2. "Picky"

Full working example here.

Same integration burden as above, but this example loads only the core of the editor, the find widget, the python colorization and nothing else! See index.js. This would allow you to pick which editor features you want in order to get a smaller bundle size.

from monaco-editor.

NikGovorov avatar NikGovorov commented on May 22, 2024 40

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.

from monaco-editor.

TheLarkInn avatar TheLarkInn commented on May 22, 2024 39

Hi there!!! Sean (from webpack). I'd love to discuss the possibility of shipping Monaco using the Harmony Module format (esm). What's the best medium to propose this?

from monaco-editor.

timkendrick avatar timkendrick commented on May 22, 2024 26

@TheLarkInn I had a chance to take another look at this over the Christmas break, and I've managed to get Monaco compiling entirely via webpack from the original TypeScript source files, with full source map support. It's a lot cleaner than my previous attempt, but there are still a few hacks and workarounds to get everything playing nicely together.

TL;DR: head to the @timkendrick/monaco-editor npm package if you want to grab a batteries-included equivalent of the official monaco-editor package (includes a standalone build containing everything inlined into a single JS file, as well as a build that consists of separate JS, CSS and worker JS files).

How it works

  • Webpack seems to have trouble loading pre-built VS Code UMD bundles, so I've had to pull in all the additional libraries as Git submodules and rebuild all the extensions from source as well

  • The webpack config defines aliases for all the bundled VS Code modules (vs, vs/basic-languages etc) so that imports are wired up correctly

  • Some of the modules use a runtime require() call to load additional modules; the webpack config defines individual contexts for each of these modules to effectively inline a predetermined set of additional modules

  • The main entry point (editor.main.ts) is run through my custom @timkendrick/monaco-editor-loader webpack loader, which bundles up the main entry point script along with the extension entry point scripts, plus the entry points for the additional worker scripts

  • All the TypeScript source files are run through ts-loader with the VS Code tsconfig.json settings plus a couple of additional overrides - notably module: "commonjs", rather than module: "amd" ("es6" seems to present problems…)

  • Once they have been transpiled to JS, the source files are run through a couple of custom Babel transforms (defined in @timkendrick/babel-preset-vscode), which a) rewrite the AMD-style require() calls to CommonJS style, and b) convert any vs/css!foo imports to foo.css

  • Any CSS imports are siphoned off by the style-loader / ExtractTextWebpackPlugin and handled via webpack

  • Worker scripts are either bundled into the main output file as blob: URLs or emitted as separate files and loaded by the browser (custom webpack loaders are used to implement both of these mechanisms)

  • I haven't looked into supporting the vs/nls stuff correctly; the default language seems to work fine out-of-the-box but I suspect that adding proper i18n support would entail writing another fairly simple custom loader

  • The webpack setup currently requires webpack 4.x due to a bugfix that I submitted for an issue that was breaking the webpack 3 build

All in all, this is still a pretty complex webpack setup but it feels a lot more robust than my previous attempt, and as predicted, startup performance is much better. To me, this approach feels stable enough to use in an actual project.

I'd say based on this experiment that if the VS Code team felt that webpack compatibility was a desirable goal for the Monaco editor then it probably wouldn't take long to convert the codebase - there are a few dynamic require() call expressions that could be rewritten statically to avoid the need for webpack context plugins, and you'd need to use webpack loaders as a replacement for the AMD-dependent require.toUrl() runtime worker loading and for the vs/nls stuff, but apart from that it should all be pretty trivial. I haven't had much experience with webpack's code splitting, but I imagine this could be very handy if there was a need for a minimal core and async extension loading.

This was all done as a proof-of-concept so I can't guarantee that I'll be able to maintain these npm packages. If anyone feels like investigating what would need to be done to adapt the main VS Code codebase for webpack, I'd recommend looking at timkendrick/monaco-editor/webpack/createConfig.js to see how I worked around the various edge cases, and timkendrick/monaco-editor-loader/lib/loaders/monaco.js to see how it compiles the main entry point and bundles the workers.

Let me know if there's any more info I can provide on this!

from monaco-editor.

Matthias247 avatar Matthias247 commented on May 22, 2024 18

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.

from monaco-editor.

NikGovorov avatar NikGovorov commented on May 22, 2024 16

@alexandrudima is any update on this?

from monaco-editor.

Kaijun avatar Kaijun commented on May 22, 2024 14

any updates?

from monaco-editor.

timkendrick avatar timkendrick commented on May 22, 2024 14

@alexandrudima @rebornix @TheLarkInn I decided to have a crack at this over the weekend seeing as there doesn't seem to have been much progress on this issue lately.

TL;DR: I've made a proof of concept that uses Webpack to successfully compile the editor/plugins/CSS/etc into a single massive UMD script with no dependencies (published as @timkendrick/monaco-editor). It inlines CSS and remote worker scripts into the client-side bundle, and if you're feeling adventurous you can create a custom build using the Webpack loader I wrote. In short: it's totally possible to bundle Monaco via Webpack without any upstream changes.

In-depth technical details:

While this method seems to work fine, it uses a fairly elaborate webpack setup internally and I'm by no means suggesting that any of it is remotely sane. I'll try and explain the approaches I explored and suggest potential alternative avenues as I go along.

It was a bit of an ordeal trying to get this working: most of the time went into writing a custom Webpack loader (@timkendrick/monaco-editor-loader), which wraps the AMD modules with an injected RequireJS shim that gets executed at runtime and stitches all the modules together, taking into account the various extensions you've loaded etc. Ideally you'd just be able to run it through webpack and have done with it, or failing that I would have preferred to at least use something off-the-shelf like Almond, but the CSS and web worker parts of the Monaco code rely on require.toUrl() which isn't supported by Webpack or Almond, so I ended up writing my own custom shim. This started off as a few lines, but kept growing as more features were needed - looking back now it might have been better to investigate adding toUrl() behavior to Almond or something. It's several years since I've used RequireJS and the API docs are pretty vague, so I'm not super-confident that my loader is 100% compatible with the Monaco loader.js, but I basically just kept adding features till everything worked… it seems to get it all done without any problems though.

A nice benefit of going down the webpack route is that it gives you a lot more control than RequireJS over how you want to bundle your assets. As well as supporting async asset loading via baseUrl, my loader allows the user to choose to inline specific assets (e.g. editor.main.css, workerMain.js etc) into the main bundle, so that you don't need to serve these separately. Any CSS assets you specify will be inlined as data: URIs and any worker scripts will be inlined as blob: URIs (and loaded via URL.createObjectURL(blob)). This went surprisingly smoothly. Somewhat trickier was the fact that workerMain.js contains its own inbuilt version of the AMD loader, which asynchronously loads in any remote plugin worker scripts from within the worker process. Obviously we don't want this behaviour if we've already downloaded the remote scripts in the main bundle, so instead you need to inject them into the worker script through some other means. I ended up writing another webpack loader that mocks the importScripts() function exposed to the bundled worker script (in this case, workerMain.js), inlining the additional bundled remote scripts as part of the main worker script blob (which itself is inlined into the main bundle), thereby allowing you to combine all the additional remote worker scripts into the same local bundle, along with the main JS and CSS. If you choose not to bundle some or all of the assets, it'll fall back to the default behavior of loading them from the baseUrl (both in the main process and in the worker process).

Bearing all this in mind, If I were to do this again I imagine a much cleaner overall approach would be to run Webpack on the source TypeScript files directly. This isn't possible as it stands, seeing as Monaco relies on the additional AMD require() properties that aren't present on the node/webpack require() function, so Webpack will complain about being used dynamically (as far as I can gather, in Webpack require() operates more like a keyword than an object), so anything that relies on the extra AMD stuff will cause the build to break. Perhaps the least dirty approach would be to apply some kind of an AST transformation to all the Monaco source files at compile time, replacing require.toUrl() calls with an inlined data: or blob: URL containing the target asset contents instead? I feel like it's either that, or monkey-patch Webpack's require() somehow while still maintaining the usual Webpack require behaviour (not sure if that's even possible without an AST transformation). There's also the issue of the vs/css!... and vs/nls!... plugin paths, but I suspect these would be pretty simple to solve via style-loader, and maybe some kind of custom i18n loader, I haven't really looked into it. As far as I'm aware though, assuming you set up a vs path resolver alias and equivalent loaders for the vs/css and vs/nls plugins, the only real impediment to using Monaco directly within Webpack is the use of require.toUrl/require.config etc., so if you can get around this it should be plain sailing.

Anyway, this was just an experiment, so I wouldn't recommend using my package in production: I haven't looked into browser compatibility / proper sourcemapping etc., and I'm not planning to maintain it, hence the @timkendrick/ prefixes) – but at least it proves it's possible. Hopefully this will be a useful starting point if others want to pick it up from here.

Give me a shout if you want more details on anything, I'm happy to help!

from monaco-editor.

tugberkugurlu avatar tugberkugurlu commented on May 22, 2024 12

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

from monaco-editor.

alexdima avatar alexdima commented on May 22, 2024 12

@TheLarkInn

I'm not intimately familiar with webpack, and I've only tangentially followed the ES Harmony module proposal, but I am quite knowledgeable in AMD (having written our AMD loader, bundler, etc.). I can describe what we're doing and why we're doing it and hopefully we can use our collective brain power to come to a good solution.

Some of the requirements are probably quite general of any large software project, some of them are unique...

Basic considerations

  • the monaco-editor node module comes together by "stitching" together the monaco-editor-core node module and several monaco-* plugins (also distributed via npm) that add various language supports. Their location is described here. This makes it possible, by forking this repository (and tweaking the metadata.js root file), for someone to create a monaco-editor distribution which contains the html plugin, but not the json plugin, etc. Or someone can package a monaco-editor distribution with an additional plugin that they author.
  • for the purpose of better supporting webpack, we can change the monaco-* plugins shape more or less freely (changing them only impacts the monaco editor), so we don't need to think too deeply about them, except for the lazy loading point below.
  • the monaco-editor-core node module, (a language agnostic editor), gets shipped straight from VS Code's sources. We must maintain single source with VS Code, otherwise all the good work we're doing for the VS Code editor would probably never make it to the monaco editor.
    • VS Code is the editor's "home". The monaco-editor-core simply shims many services the editor code needs such that it can be hosted in a standalone manner. e.g. IProgressService, IMessageService, IKeybindingService, etc
    • we want to keep our CSS next to the code that needs it. e.g.
      • hover.ts requires `vs/css!./hover'
      • this indicates that the factory method of hover.ts should be invoked only after the CSS in hover.css has been loaded.
    • we want i18n to be painless for developers. e.g.
      • we import nls from vs/nls
      • to define a translatable string, we make a simple call in the source code to nls.localize where we define an identifier (that disappears at runtime) and the English value for the string. Sometimes, we can even provide more metadata with explanations for translators if we make the call with a different shape. e.g. : simple, with comments intended for translators
    • we have an entire translation pipeline set up for VS Code from which the Monaco Editor benefits. nls.localize calls can all be analyzed statically and strings to be translated are extracted. They then get pushed to a format Transifex likes and then translations can be pulled back from it and get converted to .json files in our repository.
    • when we compile with a "release" target in mind (i.e. not just for running out of sources), all the code that required vs/nls gets patched to signal the caller module it e.g. a require of vs/nls from vs/editor/contrib/hover/browser/hover.ts gets rewritten to vs/nls!vs/editor/contrib/hover/browser/hover, all the English strings are extracted to a separate file hover.nls.js and all the nls.localize calls are rewritten to pass in the numeric index of the string in a file, to give a nice array cost (O(1)) of using a localized string.
    • at bundle time, we start with vs/editor/editor.main and ship only the subset of modules that are needed (referenced) for the monaco editor.
    • nls: the vs/nls plugin participates in the bundling phase and concatenates all the nls strings in a single file, and consolidates dependencies. e.g. vs/nls!vs/editor/contrib/hover/browser/hover is defined as define('vs/nls!vs/editor/contrib/hover/browser/hover', ['vs/nls', 'vs/nls!vs/editor/editor.main']), function(nls, data) { return nls.create("vs/editor/contrib/hover/browser/hover", data); }); . so all of the nls dependencies end up redirecting to vs/nls!vs/editor/editor.main, and the plugin generates various nls.*.js bundles for the various languages we ship (e.g. German, Chinese, etc.)
    • css: the vs/css plugin also participates in the bundling phase and concatenates all the CSS, taking of course into consideration that the relative paths to images have changed and inlines all of the images below a certain size (we have lots of small svg icons).
    • the bundler does some simple transformations to reduce code size, such as eliminating duplicated TS helper definitions and hoisting up all the AMD module names such that they don't get repeated again and again and again.
    • finally, "because life", we shim our octicons usage since we want those in VS Code, but their size cost is not paying off for the monaco editor.

Requirements

Even after all of this stuff has happened and we have extracted the precise subset of source code, nls strings, CSS, and images that the monaco editor needs out of VS Code, there are still some special requirements we have.

We want to use web workers to have a never blocking UI. e.g. if you use the monaco diff editor, the diff is always computed in a web worker. if you use JS/TS/etc, the ASTs and analysis and heavy lifting of computing completions, etc. happens on a web worker.

We want to lazy load languages. e.g. if you instantiate an editor model with the language css, we want to dynamically load only the CSS specific code. i.e. on the main thread, we load the CSS colorizer and the CSS part of the plugin that registers the CSS language smarts provider; the CSS plugin makes use of some straight forward API to create a web worker and load its specific code in the new web worker. The basics of a web worker that can do buffer sync is at vs/base/worker/workerMain.js.

If, later, you instantiate an editor model with the language html, etc, the same happens for html.

Integrator burden

  • we try to hide most of this complexity to the point where an integrator "simply" needs to ship a bit-to-bit copy of the files we distribute to production, and the lazy loading of programming languages and the configurable loading of natural languages aspect brings in the runtime dependency towards an AMD loader.

If the integrator has their own AMD compliant loader (i.e. that supports plugins), we will run with that. Otherwise, integrators can use the AMD loader we supply at vs/loader.js.

e.g. a simple-case integrator can get away with:

require.config({ paths: { 'vs': '../path/to/vs/folder' } });
require(['vs/editor/editor.main'], function() { /* editor loaded here */ })

A fancier integrator that is concerned about the loading waterfall can load the editor synchronously by doing something like. e.g.:

<link rel="stylesheet" data-name="vs/editor/editor.main" href="path/to/vs/editor/editor.main.css">
<script>var require = { paths: { 'vs': 'path/to/vs' } };</script>
<script src="path/to/vs/loader.js"></script>
<script src="path/to/vs/editor/editor.main.nls.js"></script>
<script src="path/to/vs/editor/editor.main.js"></script>
<script>/* editor loaded here */</script>

An integrator that wants to load a different natural language can either load editor.main.nls.de.js or configure the nls plugin. e.g.

Conclusions

My problem is I'm not really sure what the requirement is here and what the desire is.

Do integrators want to bundle the monaco editor through their own webpack config

  • Do we then need to ship 1000s of files, some of them not needed (i.e. files only used in VS Code)
  • Do the integrators want to deal with patching our nls.localize calls and correlating those with translated strings and creating nls bundle files?
  • Do the integrators want to deal with concatenating our 100s of CSS files and inlining small svg icons?
  • Do the integrators want to deal with our N entry points: vs/editor/editor.main.js, vs/base/worker/workerMain.js, vs/basic-languages/src/XXXX.js, vs/languages/json/{jsonMode,jsonWorker}.js, do the integrators want to know that the TS/JS plugin consists of 3 files: a mode.js, a worker.js and typescriptServices.js that is loaded both on the main thread and in the worker -- it is loaded on the main thread for colorization purposes and on the worker thread for language services purposes.

Do integrators want to load the monaco editor with webpack

  • but... this should just work, right? since webpack supports loading AMD code.

I apologise if I'm missing the point, supporting the Harmony module format might be as easy as us replacing our calls to define('moduleId', ['req1', 'req2', ... ]) with module "moduleId" { import X from 'req1' ... } and our lazy loading from require(['lazyCode'], function() { ... }) with Loader.load calls, but there still remains the challenge of what vs/css and vs/nls should map to. Shipping our code in such a shape would also mean the editor source code is not loadable in IE11 without a loader that ... loads the code via xhr and uses eval / regexes to discover those dependencies ?

I'm also happy to do more reading on the subject if you know of a good article explaining what's expected from us to be ES Harmony module compliant in what we ship. Or ... if you have any ideas, I'd love to hear them.

from monaco-editor.

Matthias247 avatar Matthias247 commented on May 22, 2024 11

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?)

from monaco-editor.

Matthias247 avatar Matthias247 commented on May 22, 2024 10

Btw, I improved my approach since the last posting a little bit. I moved all monaco loading code into a seperate angular2 service component/class:

monacoeditorloader.ts:

declare const require: any;

export class MonacoEditorLoader {
  private _loaded = false;
  private _loadPromise: Promise<void>;

  constructor(
  ) {
    this._loadPromise = new Promise<void>(resolve => {
      // Fast path - monaco is already loaded
      if (typeof((<any>window).monaco) === 'object') {
        resolve();
        return;
      }

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

      // 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();
      }
    });
  }

  get monacoLoaded() {
    return this._loaded;
  }

  // Returns promise that will be fulfilled when monaco is available
  waitForMonaco(): Promise<void> {
    return this._loadPromise;
  }
}

I expose that through an angular2 module, which declares this as a provider amongst some other common services:

@NgModule({
  declarations: [],
  imports: [
    BrowserModule,
    HttpModule,
  ],
  providers: [
    ...
    MonacoEditorLoader,
  ],
})
export class ServicesModule {
}

In all components that want to utilize monaco later on I get the loader through DI, wait for the load to complete and create monaco editors. The advantage of this approach is that loading is separated from utilizing Monaco and that it's easy to use Monaco in multiple components without big code duplication. The only drawback is that loading is still asynchronous - the editor won't be immediatly there after ngAfterViewInit. It's sometimes visible that the editor pops up later. But for me that's not a real problem.

@Component({
  selector: 'editor',
  template: `
    <div #editor class="editor-container" style="width: 1000px; height: 1000px">
  `,
})
export class Editor implements OnInit, AfterViewInit, OnDestroy {
  @ViewChild('editor') editorRef: ElementRef;
  private _disposed = false;
  private _editor: monaco.editor.IStandaloneCodeEditor;

  constructor(
    private _monacoLoader: MonacoEditorLoader
  ) {
  }

  ngAfterViewInit() {
    // Wait until monaco editor is available
    this._monacoLoader.waitForMonaco().then(() => {
      // Need to check if the view has already been destroyed before Monaco was loaded
      if (this._disposed) return;
      this.initMonaco();
    });
  }

  ngOnDestroy() {
    if (this._disposed) return;
    this._disposed = true;

    // Close possibly loaded editor component
    if (this._editor) this._editor.dispose();
    this._editor= null;
  }

  initMonaco() {
    this._editor = monaco.editor.create(this.editorRef.nativeElement, {
      value: '',
      language: 'javascript',
    });
  }
}

In my real project I have actually even a step in between, as I want to add some custom functionality (syntax highlighting, autocompletion, ...) to Monaco. I put this into another service which uses the MonacoEditorLoader to load Monaco and on Completion it adds the new functionality. My view components then wait on the service in between to finish loading instead of waiting directly on the MonacoEditorLoader. But that really depends on what you exactly want to achieve.

from monaco-editor.

ebertmi avatar ebertmi commented on May 22, 2024 9

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.

from monaco-editor.

rebornix avatar rebornix commented on May 22, 2024 7

@alexandrudima already had a thorough check list above. I played with webpack these few days and synced with @TheLarkInn . Here are my findings, correct me if I made anything wrong.

Integrate with Webpack: Load

There are already a few solutions above about how to make Monaco Editor work with Webpack, the key point is

  • Ask webpack not to resolve Monaco related modules, e.g. vs/editor/editor.main.
  • Copy Monaco bits to output folder, you can use CopyWebpackPlugin.
  • Use a amd loader to load Monaco Editor and corresponding plugins.

We can even put amd loader into a closure to avoid conflict with other module system.

Integrate with Webpack: Bundle

Ppl may want to bundle Monaco editor with Webpack. Webpack will try to resolve modules when bundling Monaco modules, to make it compatible:

  • For vs/css!, we can use CSS loader to bundle CSS the same way as our AMD CSS Plugin. One catch here is we don't put file extension to CSS file when referencing, we can use NormalModuleReplacementPlugin to mitigate this problem.
  • For vs/nls!, we need to write a custom loader for NLS.

However, if we directly use webpack to bundle our npm package, we'll see following errors/warnings like below

  • Cannot resolve module vs/editor/edcore.main
  • define cannot be used indirect
  • Critical dependency: require function is used in a way in which dependencies cannot be statically extracted
  • Critical dependency: the request of a dependency is an expression

This is due to Webpack's module resolution mechanism. It tries to resolve modules statically so code like

var module = languageDefinitions[languageId].module;
return new _monaco.Promise(function (c, e, p) {
      require([module], function (mod) {         
      }, e);
});

won't work as webpack doesn't know to evaluate module. This explains

Cannot resolve module vs/editor/edcore.main

we didn't use string literal directly when defining vs/editor/edcore.main in the bundled main entry editor.main.js, it confuses webpack. Workarounds are using string literal when bunding Monaco or shipping all files and then webpack can resolve modules by file paths/names.

Critical dependency: require function is used in a way in which dependencies cannot be statically extracted
Critical dependency: the request of a dependency is an expression

If those modules already exist in the component, we can leverage https://webpack.github.io/docs/context.html#dynamic-requires to solve the problem. We still use above code snippet as example

https://github.com/Microsoft/monaco-languages/blob/master/src/monaco.contribution.ts#L26

var module = languageDefinitions[languageId].module;
return new _monaco.Promise(function (c, e, p) {
      require([module], function (mod) {         
      }, e);
});

As we know all language modules are in the same folder, so changing it to

var module = languageDefinitions[languageId].module;
return new _monaco.Promise(function (c, e, p) {
      require(['./' + module], function (mod) {         
      }, e);
});

and then webpack will create a context module, this module contains references to all modules in that directory that matches a certain pattern.

There are a few other places (in the core and all builtin extensions) that we need to apply dynamic require.

Open Items

I can already bundle Monaco-Editor-Core + Basic Languages with Webpack but didn't succeed bundling all extensions. There are still several questions I don't have answer yet

  • Can we define multiple AMD modules in a single file and tell Webpack to resolve modules from that file?
  • Is it possible to tell webpack skip some require function all when webpack_require fails? There are some places that the modules we try to require are real dynamic (they don't exist yet when bundling), falling back to a simple require and then the module can be loaded correctly on runtime. webpack/webpack#4545

If above two problems are solved someway, then the left work is tweaking core and extensions, creating entry points for both main thread and web worker, even though it still requires a lot code change but still doable.

from monaco-editor.

danvk avatar danvk commented on May 22, 2024 6

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.');
    }
  }
}

from monaco-editor.

TheLarkInn avatar TheLarkInn commented on May 22, 2024 6

OMG! WOW! This is awesome! I'll be sure to try this out and provide feedback.

from monaco-editor.

superRaytin avatar superRaytin commented on May 22, 2024 4

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).

from monaco-editor.

kube avatar kube commented on May 22, 2024 4

@rebornix @TheLarkInn Sorry for the ping, but is this something that will come in a near future?
It seems the idea was abandoned.

from monaco-editor.

alexdima avatar alexdima commented on May 22, 2024 4

@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.

from monaco-editor.

fxck avatar fxck commented on May 22, 2024 3

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

from monaco-editor.

chrisber avatar chrisber commented on May 22, 2024 3

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

from monaco-editor.

kube avatar kube commented on May 22, 2024 3

@cloverich

As I said:

"I cannot load Monaco by appending the loader.js script"

And it's exactly this solution I used, and it does not work well when using Webpack (Webpack proxifies all require calls, and it's not a good idea to start hacking the require function).

"Also note that when running the project in Electron, you don't actually need webpack"

I know what is Electron, and using it with Webpack permits to have Hot Reload, a transpilation pipeline easier to setup than with a tool like Gulp, and all the stuff Webpack and its DevServer permit.

from monaco-editor.

TheLarkInn avatar TheLarkInn commented on May 22, 2024 3

@timkendrick Thanks for this awesome write-up.

I would love to see the possibility of having all of Monaco/code bundled with webpack. For one, webpack's ability to bundle electron apps typically result in over 40% start time increase (so theres a legit win there). This would also let one convert most of the existing modules to be ESM.

One of my concerns is that there is a set of dynamic nature that Monaco relies on for loading plugins and services. Because webpack is a static build tool, there would have to be some separation between the core vscode functionality, and the extension system and providers, etc.

externals[vscode-extension-runtime: ?] => dynamicExtentionProvider
[vscode core code: esm]

This is kind of off the cuff and not well thought out at the moment, but I think there's a lot of ROI (build perf, general Monaco adoption, code launch and runtime perf), that could be gained from the transition.

from monaco-editor.

0plus1 avatar 0plus1 commented on May 22, 2024 2

@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!

from monaco-editor.

siovene avatar siovene commented on May 22, 2024 2

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();
        }
    }

from monaco-editor.

kube avatar kube commented on May 22, 2024 2

@alexandrudima

From what I see in the gulpfile edcore.main is simply an alias to editor.main.
But I don't see any define of it.

https://github.com/Microsoft/monaco-editor/blob/master/gulpfile.js#L100

I don't really understand what this does, and I never used AMD modules before so it's a little bit obscure for me.

I really need to integrate Monaco in my setup with Webpack and Electron, and I'm pretty sure it's not much to modify to make it work.

Could you explain to us what this addPluginContribs function really does, and what really is edcore.main?

Monaco and VSCode are awesome projects, and it's demoralizing to see that we can not use it in our Webpack projects.

from monaco-editor.

FrankFang avatar FrankFang commented on May 22, 2024 2

What a pity. No webpack support.

from monaco-editor.

nescalante avatar nescalante commented on May 22, 2024 2

@TheLarkInn, looking forward for your implementation for webpack.
Let me know if I can do something to help on accelerating the development of that since now I am blocked due this issue.

from monaco-editor.

MazheM avatar MazheM commented on May 22, 2024 1

@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.

from monaco-editor.

mbana avatar mbana commented on May 22, 2024 1

i think the html5 imports* is probably the most simple approach at the moment:

  • the server of monaco will need to enable http2 to get decent performance (based on experience).
  • html5 imports is a decent-enough module system. i was suprised when i discovered what one could do with it.
  • in Polymer, one would use a similar tool to bundle web components (think AMD modules), see https://github.com/Polymer/vulcanize. however, this is going to become obselete due to http2.
  • build process becomes much simpler.

all in all, everything becomes much simpler.

@alexandrudima, have you had a chance to look at this?


as an example, when editor.main has finished loading, the script below executes.

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

from monaco-editor.

otakustay avatar otakustay commented on May 22, 2024 1

In CodeMirror we can bundle it with webpack by hacking a little of it's public overridable APIs, heres what we tried and succeed:

import {noop} from 'lodash';
import CodeMirror from 'codemirror/lib/codemirror';

import 'codemirror/mode/meta';
import 'codemirror/mode/brainfuck/brainfuck';
import 'codemirror/mode/clike/clike';
import 'codemirror/mode/cmake/cmake';
import 'codemirror/mode/coffeescript/coffeescript';
import 'codemirror/mode/commonlisp/commonlisp';
import 'codemirror/mode/css/css';
import 'codemirror/mode/d/d';
import 'codemirror/mode/diff/diff';
import 'codemirror/mode/eiffel/eiffel';
import 'codemirror/mode/fortran/fortran';
import 'codemirror/mode/gherkin/gherkin';
import 'codemirror/mode/go/go';
import 'codemirror/mode/groovy/groovy';
import 'codemirror/mode/htmlembedded/htmlembedded';
import 'codemirror/mode/htmlmixed/htmlmixed';
import 'codemirror/mode/idl/idl';
import 'codemirror/mode/jade/jade';
import 'codemirror/mode/javascript/javascript';
import 'codemirror/mode/jsx/jsx';
import 'codemirror/mode/julia/julia';
import 'codemirror/mode/lua/lua';
import 'codemirror/mode/markdown/markdown';
import 'codemirror/mode/mathematica/mathematica';
import 'codemirror/mode/mllike/mllike';
import 'codemirror/mode/nsis/nsis';
import 'codemirror/mode/perl/perl';
import 'codemirror/mode/php/php';
import 'codemirror/mode/powershell/powershell';
import 'codemirror/mode/properties/properties';
import 'codemirror/mode/protobuf/protobuf';
import 'codemirror/mode/puppet/puppet';
import 'codemirror/mode/python/python';
import 'codemirror/mode/q/q';
import 'codemirror/mode/rpm/rpm';
import 'codemirror/mode/rst/rst';
import 'codemirror/mode/ruby/ruby';
import 'codemirror/mode/rust/rust';
import 'codemirror/mode/sass/sass';
import 'codemirror/mode/scheme/scheme';
import 'codemirror/mode/shell/shell';
import 'codemirror/mode/smarty/smarty';
import 'codemirror/mode/soy/soy';
import 'codemirror/mode/sql/sql';
import 'codemirror/mode/stex/stex';
import 'codemirror/mode/swift/swift';
import 'codemirror/mode/tcl/tcl';
import 'codemirror/mode/toml/toml';
import 'codemirror/mode/troff/troff';
import 'codemirror/mode/ttcn-cfg/ttcn-cfg';
import 'codemirror/mode/vb/vb';
import 'codemirror/mode/vbscript/vbscript';
import 'codemirror/mode/verilog/verilog';
import 'codemirror/mode/xml/xml';
import 'codemirror/mode/yaml/yaml';

import 'codemirror/addon/search/search';
import 'codemirror/addon/search/searchcursor';
import 'codemirror/addon/search/jump-to-line';
import 'codemirror/addon/search/matchesonscrollbar';
import 'codemirror/addon/search/match-highlighter';
import 'codemirror/addon/scroll/annotatescrollbar';
import 'codemirror/addon/scroll/simplescrollbars';
import 'codemirror/addon/dialog/dialog';
import 'codemirror/addon/selection/active-line';
import 'codemirror/addon/display/fullscreen';
import 'codemirror/addon/display/autorefresh';
import 'codemirror/addon/edit/trailingspace';
import 'codemirror/addon/mode/overlay';

import 'codemirror/lib/codemirror.css';
import 'codemirror/addon/dialog/dialog.css';

import './CodeMirror.global.less';

CodeMirror.requireMode = noop;
CodeMirror.autoLoadMode = noop;

export default CodeMirror;

These code is simple:

  1. importing CodeMirror from npm packages
  2. importing modes (language syntax highlighting) and addons (plugins) on demand
  3. Override the requireMode and autoLoadMode static methods to noop, this disables the "loader" shipped by CodeMirror (which originally supports both CJS and AMD)

After override requireMode and autoLoadMode it's not possible to load language modes or addons on demand, so when dynamic load is required, we can just leave these two methods and do a copy/paste work, in this way CodeMirror works similar to how current monaco-editor behaves

Bundle with webpack is very useful for applications in a predictable and fast network environment

from monaco-editor.

otakustay avatar otakustay commented on May 22, 2024 1

is there any way to reference module b in entry files (index.js)?

As webpack ks a module bundler, there is no separated module in a bundle by default, you can make modules accessible by exporting them in your entry, or to configure webpack to export some modules, in both solutions the exports must be explicitly defined

how can we require module a and b at runtime

Webpack can bundle a CommonJS/AMD library so you can reference the module using CommonJS require or an AMD loader, but your code is probably:

define('myApp', ['monaco'], function (monaco) {
    const a = monaco.a;
    const b = monaco.b;

    // ...
});

from monaco-editor.

ev45ive avatar ev45ive commented on May 22, 2024 1

Benefit of using standard module loading convention in your code is that it allow for moving all loading/bundling logic to be placed "outside of your code". That is - in bundler / loader config.

All popular loaders handle (via plugins) things like importing css, translation, lazy loading of code and bundling / code splitting.

Monaco is great piece of software and there is lot potential for it, especially when used as part of specialised software.

IMHO the goal here is not only to make it easy to just drop in but also to make these things configurable for integrator who needs custom stuff. Doesnt have to be weback, but Webpack is perfect for that, making your code agnostic of way it its built and consumed. Everything is just a module (TM).

Some modules are sync, some are asyc (webpack handles that also), some are css or strings or internationalization strings(!).

Best part is that your code doesnt care. Loader does. You just import strings. Thats why its important for people i guess.

Any progress on this.btw?' :-)

from monaco-editor.

felixfbecker avatar felixfbecker commented on May 22, 2024 1

Not sure if this helps, but it's also possible to use dynamic import() with webpack, which works well with TypeScript too.

In general, I think the best way is to have Monaco just use ES6 imports everywhere and the consumers can decide whether they want to bundle everything, let webpack figure out good chunks to load async or to transpile everything to AMD.

from monaco-editor.

mnpenner avatar mnpenner commented on May 22, 2024 1

Thanks @timkendrick! Works great.

I wrapped it in a React component. Here's the gist if anyone's looking for a quick copy-paste solution:

import React from 'react';
import * as monaco from '@timkendrick/monaco-editor';

export default class MonacoEditor extends React.Component {
    
    componentDidMount() {
        monaco.editor.create(this.el, {
            language: 'javascript',
        });
    }
    
    render() {
        return <div style={{width:'800px',height:'600px',border:'1px solid grey'}} ref={n=>this.el = n}/>
    }
}

from monaco-editor.

corbinu avatar corbinu commented on May 22, 2024 1

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

from monaco-editor.

alexdima avatar alexdima commented on May 22, 2024 1

@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.

from monaco-editor.

rockymontana avatar rockymontana commented on May 22, 2024 1

@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!

from monaco-editor.

corbinu avatar corbinu commented on May 22, 2024 1

@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

from monaco-editor.

moranje avatar moranje commented on May 22, 2024 1

@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.

from monaco-editor.

MazheM avatar MazheM commented on May 22, 2024

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

from monaco-editor.

alexdima avatar alexdima commented on May 22, 2024

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?

from monaco-editor.

NikGovorov avatar NikGovorov commented on May 22, 2024

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.

from monaco-editor.

alexdima avatar alexdima commented on May 22, 2024

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?

from monaco-editor.

NikGovorov avatar NikGovorov commented on May 22, 2024

@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

from monaco-editor.

MazheM avatar MazheM commented on May 22, 2024

@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'],

from monaco-editor.

egoist avatar egoist commented on May 22, 2024

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

from monaco-editor.

rrfenton avatar rrfenton commented on May 22, 2024

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/[email protected]/dev/vs/loader.js:1552:36)
        at eval (http://localhost:14992/jspm_packages/npm/[email protected]/dev/vs/loader.js:1650:5)
        at Object.eval (http://localhost:14992/jspm_packages/npm/[email protected]/dev/vs/loader.js:2228:3)
        at eval (http://localhost:14992/jspm_packages/npm/[email protected]/dev/vs/loader.js:2230:4)
        at eval (http://localhost:14992/jspm_packages/npm/[email protected]/dev/vs/loader.js:2231:3)
    Evaluating http://localhost:14992/jspm_packages/npm/[email protected]/dev/vs/loader.js
    Evaluating http://localhost:14992/jspm_packages/npm/[email protected]
    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/[email protected]/dev/vs/editor/editor.main.js:14:5)
        at eval (http://localhost:14992/jspm_packages/npm/[email protected]/dev/vs/editor/editor.main.js:74765:6)
        at Object.eval (http://localhost:14992/jspm_packages/npm/[email protected]/dev/vs/editor/editor.main.js:75181:3)
        at eval (http://localhost:14992/jspm_packages/npm/[email protected]/dev/vs/editor/editor.main.js:75183:4)
        at eval (http://localhost:14992/jspm_packages/npm/[email protected]/dev/vs/editor/editor.main.js:75184:3)
    Evaluating http://localhost:14992/jspm_packages/npm/[email protected]/dev/vs/editor/editor.main.js
    Evaluating http://localhost:14992/jspm_packages/npm/[email protected]
    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:[email protected]"
},
"npm:[email protected]": {
     "fs": "github:jspm/[email protected]",
      "os": "github:jspm/[email protected]",
      "path": "github:jspm/[email protected]",
      "process": "github:jspm/[email protected]"
},

And the requires generated:

module.exports = require("npm:[email protected]/dev/vs/loader");
module.exports = require("npm:[email protected]/dev/vs/editor/editor.main");

from monaco-editor.

bvaughn avatar bvaughn commented on May 22, 2024

@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.

from monaco-editor.

Matthias247 avatar Matthias247 commented on May 22, 2024

@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.

from monaco-editor.

bvaughn avatar bvaughn commented on May 22, 2024

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.

from monaco-editor.

0plus1 avatar 0plus1 commented on May 22, 2024

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

from monaco-editor.

Matthias247 avatar Matthias247 commented on May 22, 2024

Maybe the build script for this special project already copies the whole node_modules folder to the output. This would means monaco is in the correct place. Or it drops the main output file into the main folder of the project. However it's hard to tell what exactly it does since it's so big. In the general case you have to assure that monaco is available in the built project.

from monaco-editor.

mattgodbolt avatar mattgodbolt commented on May 22, 2024

I'm encountering similar problems when trying to use require.js and r.js. A normal build works locally, but trying to generate a single bundle with r.js yields errors of the form:

Tracing dependencies for: main
Error: ENOENT: no such file or directory, open '/home/mgodbolt/personal/dev/compiler-explorer/out/dist/ext/monaco-editor/dev/vs/editor/edcore.main.js'
In module tree:
    main
      hub
        editor
          colour
            monaco
              vs/editor/editor.main

and edcore.main.js is nowhere to be found in the npm package. Ideas welcome on how to fix this (would love to switch to monaco, but can't until I can deploy!)

from monaco-editor.

kube avatar kube commented on May 22, 2024

I'm using Electron with Webpack, and integrating Monaco is really a mess.

I cannot load Monaco by appending the loader.js script, as the loader won't recognize in which context it is, and I get too much errors.

And when loading using Webpack require directly

require.ensure(['vs/editor/editor.main'], () => {
  require('vs/editor/editor.main')
  // ...
})

It cannot findedcore.main:

Module not found: Error: Can't resolve 'vs/editor/edcore.main' in 'node_modules/monaco-editor/dev/vs/editor'

from monaco-editor.

cloverich avatar cloverich commented on May 22, 2024

@kube I used the below based on what alexandru posted in another issue (which I seem to have misplaced).

index.html in an Electron / webpack project:

	<script>
		// require node modules before loader.js comes in
		const path = require('path');
		const nodeRequire = global.require;
	</script>
	<script src="../node_modules/monaco-editor/min/vs/loader.js"></script>
	<script>
		const amdRequire = global.require;
		global.require = nodeRequire;

		function uriFromPath(_path) {
			var pathName = path.resolve(_path).replace(/\\/g, '/');
			if (pathName.length > 0 && pathName.charAt(0) !== '/') {
				pathName = '/' + pathName;
			}
			return encodeURI('file://' + pathName);
		}

		amdRequire.config({
			baseUrl: uriFromPath(path.join(__dirname, '../node_modules/monaco-editor/min'))
		});

		// workaround monaco-css not understanding the environment
		self.module = undefined;

		// workaround monaco-typescript not understanding the environment
		self.process.browser = true;

		amdRequire(['vs/editor/editor.main'], function() {

		});
	</script>

Also note that when running the project in Electron, you don't actually need webpack -- you can require all of your source directly into the browser. Hope this helps!

from monaco-editor.

cloverich avatar cloverich commented on May 22, 2024

@kube that is the solution I used when I was using webpack. Can you share the your code, or a minimum setup with the issue?

from monaco-editor.

icfantv avatar icfantv commented on May 22, 2024

@Matthias247 Hey, I'm interested in incorporating your findings into our project. Is what you have here the latest version? Also, I'm interested in seeing the custom functionality bit. Thanks.

from monaco-editor.

Matthias247 avatar Matthias247 commented on May 22, 2024

@icfantv Hey, yes, that's basically still my latest version. In the real code of the component which uses the editors I had some more code around the lifecycle hooks which allow for safe destruction and recreation of editor components. I added a little bit more around that in the example above.

Unfortunately can't show more about the remaining code since it contains mostly proprietary features. But the idea is simply that the intermediate component which adds custom functionality waits for the basic monaco loader to be ready, calls Monaco APIs to add custom syntax highlighting, autocompletions, etc. and then dispatches a ready event itself (or completes a promise). The component which wants to use a Monaco editor in the end then waits for this ready event instead of the _monacoLoader.waitForMonaco() event. E.g. it could be _monacoWithExtensionsLoader.waitForMonaco().

from monaco-editor.

icfantv avatar icfantv commented on May 22, 2024

@Matthias247 got it and looks easy enough. Thanks again for posting this.

from monaco-editor.

P-de-Jong avatar P-de-Jong commented on May 22, 2024

So i've managed to get it working with the Angular CLI.
In you angular-cli.json put this in the assets:

"assets": [
       "assets",
       "favicon.ico",
       {
         "glob": "**/*",
         "input": "../node_modules/monaco-editor/min/vs",
         "output": "./assets/monaco/vs/"
       }
],

This will serve the monaco folder in the assets folder. When building the application it will also copy the files to the dist/assets folder. Works great! 👍

Then use the code provided by @Matthias247 over here, but replace the 'loadPromise' in the monacoeditorloader.ts with the following:

this._loadPromise = new Promise<void>(resolve => {
            // Fast path - monaco is already loaded
            if (typeof ((<any>window).monaco) === 'object') {
                resolve();
                return;
            }

            const onGotAmdLoader = () => {
                // Load monaco
                (<any>window).require.config({ paths: { 'vs': '/assets/monaco/vs' } });
                (<any>window).require(['vs/editor/editor.main'], () => {
                    this._loaded = true;
                    resolve();
                });
            };

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

from monaco-editor.

leolorenzoluis avatar leolorenzoluis commented on May 22, 2024

Hello everyone,

Thanks to @Matthias247. I created a simple library for Angular to make sure that Monaco editor is ready without using timeouts or then with Promises. I did not want to clutter my components with those functions. Please check it out.

Introducing 🎉🎉🎉 Angular Monaco Editor Loader 🎉🎉🎉

https://github.com/leolorenzoluis/xyz.MonacoEditorLoader

All I have to do now is

<div *loadMonacoEditor id="container"></div> 

from monaco-editor.

OneCyrus avatar OneCyrus commented on May 22, 2024

has anyone tried to auto convert it with tools like amd-to-es6?

from monaco-editor.

TheLarkInn avatar TheLarkInn commented on May 22, 2024

Can we define multiple AMD modules in a single file and tell Webpack to resolve modules from that file?

I'm not sure what you mean, could you provide an example.

Is it possible to tell webpack skip some require function all when webpack_require fails? There are some places that the modules we try to require are real dynamic (they don't exist yet when bundling), falling back to a simple require and then the module can be loaded correctly on runtime. webpack/webpack#4545

Yes, you can! Just as the NormalModuleReplacement allows one to "hijack and modify" the request from a NormalModule, the ContextReplacementPlugin will allow one to define a custom context, or filter an existing ContextModule request. You can find more information in this thread.

from monaco-editor.

rebornix avatar rebornix commented on May 22, 2024

@TheLarkInn thanks for the answer of require fallback. About my first question, it's about resolving modules in bundled files. Let's say I have a bundled file (which might be from node modules), it contains several modules

a.js

define('b', [], function () {
}

define('a', ['./b'], function () {
}

and we try to reference these two modules in entry file index.js.

define('index', ['./a', 'b'], function () {
}

a is accessible as the file a.js is in the correct folder. While webpack resolving module a, as module b is in the same file and already defined, webpack knows how to resolve a's dependencies.

My question is, is there any way to reference module b in entry files (index.js)? Moreover how can we require module a and b at runtime since webpack bundles them and probably replace the module name with absolute path.

from monaco-editor.

ProTip avatar ProTip commented on May 22, 2024

I hope I'm not noising this thread up, but it has by far been the most useful resource in getting monaco working with my setup. I'm utilizing Monaco in an electron app and was having a little trouble with the loader @Matthias247 posted. Here is my working version based on a mashup of the above example and the official electron guidance:

declare const global: any
declare const require: any

const anySelf: any = self

const nodeRequire = global.require
let amdLoader

export default class MonacoEditorLoader {
    private _loaded = false
    private _loadPromise: Promise<void>

    // Returns promise that will be fulfilled when monaco is available
    waitForMonaco(): Promise<void> {
        if (this._loadPromise) {
            return this._loadPromise
        }
        this._loadPromise = new Promise<void>(resolve => {
            if (typeof((<any>window).monaco) === 'object') {
                resolve()
                return
            }

            var onGotAmdLoader = () => {
                amdLoader = global.require
                global.require = nodeRequire

                anySelf.module = undefined
                anySelf.process.browser = true

                amdLoader(['vs/editor/editor.main'], () => {
                    this._loaded = true
                    resolve()
                })
            }

            let loaderScript = document.createElement('script')
            loaderScript.type = 'text/javascript'
            loaderScript.src = 'vs/loader.js'
            loaderScript.addEventListener('load', onGotAmdLoader)
            document.body.appendChild(loaderScript)
        })
        return this._loadPromise
    }
}

I have copied vs into my build output directory to make it work.

It would be great if we could import and have everything JustWork™. My mind wandered to (BR)ACE and CodeMirror at first when hitting the initial blocks, but stuck with it because how much I love vscode and TypeScript. To live its life to the fullest!!! outside vscode it may need to meet developer's modern bundling/importing/building expectations for low friction adoption. Not sure if that means vscode needs to dogfood it as an external yet internal package or what, but the project appears to be sufficiently complex enough I'll leave that to the experts ;)

from monaco-editor.

ThorstenBux avatar ThorstenBux commented on May 22, 2024

I just came across this one here: https://www.npmjs.com/package/monaco-loader
Haven't tested it myself but maybe someone finds it helpful.

from monaco-editor.

mjkkirschner avatar mjkkirschner commented on May 22, 2024

we use require.js to bundle our application, I have tried various ways of including the minified editor-main.js file using require - and it does correctly get bundled, even executed, but monaco is left undefined - is it true that you can use require.js somehow to load monaco?
apologies if I should start a different issue.

from monaco-editor.

ev45ive avatar ev45ive commented on May 22, 2024

Where can one find details on loader and i18n stuff. Maybe we could work on getting plugins for webpack so that integrator can decide which modules are bundled and which are lazy loaded, but all with their (integrators) own bundling system.

I Guess the main issue here is that i have 2 loaders now:
1- webpack - witch i have full control over (configuration)
2- monaco custom require which i cannot configure

also lazy loading, assets and internationalization loading logic is duplicated then.

from monaco-editor.

ev45ive avatar ev45ive commented on May 22, 2024

@TheLarkInn - is there maybe an option to "capture" calls to require in loaded modules and put in there custom logic? Especially for async requires or dynamic ones like monaco uses. Sorry for bother, but Can't find it in webpack docs.

from monaco-editor.

timkendrick avatar timkendrick commented on May 22, 2024

@TheLarkInn yep totally agree – my runtime AMD-style shim was just a hack to get it working, I'd much prefer if it were 100% webpack.

As for the plugin-loading system, I've been facing the same problem in a pluggable Electron webpack project I'm working on – I've ended up with the core bundle exposing a global registerPlugin() style method on the window object, which each of the extension bundles calls at runtime when they start up (this feels a bit like it's not making the best use of the bundler, but it works fine I guess).

I might be misunderstanding what you're saying, but, isn't Monaco already essentially performing this same process via the global monaco api (see snippet)? If that's the case then I can't imagine how webpack would pose any problems here…

https://github.com/Microsoft/monaco-typescript/blob/fd172013f77fa599e03a1c17b79aab62637569fa/src/monaco.contribution.ts#L194-L222

from monaco-editor.

rockymontana avatar rockymontana commented on May 22, 2024

Hello and nice work!
I've been trying to get this to work with webpack today. I've come as far so that I can see the editor, but I get a lot of console errors:

bild

(I especially like the "window not found" error ;)

Anyways.
I mainly wonder three thing:

  1. What does self point to in self.MonacoEnvironment? I don't see any references to self and when I've been poking around in the sources it seems like it should be pointing to window- is that correct?

  2. The workers in MonacoEnvironment object points to ./some.worker.js. Would it be just as fine to link to something like window.location.href + 'editor.worker.js'?

  3. Is it important that the webpack config keeps the workers in seperate files rather than bundling all files to the same script?

Sorry if my questions are ridicolous, I'm still kinda new to this. :-)

from monaco-editor.

timkendrick avatar timkendrick commented on May 22, 2024

@rockymontana did you add the webpack ignore plugin as specified in the final step of the "Basic" instructions above?

from monaco-editor.

rockymontana avatar rockymontana commented on May 22, 2024

Yes I did.

entry: {
    app: './src/main.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',
  },
  output: {
    path: config.build.assetsRoot,
    filename: '[name].js',
    publicPath: process.env.NODE_ENV === 'production'
      ? config.build.assetsPublicPath
      : config.dev.assetsPublicPath
  },
  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/
    )
  ],

Like that

from monaco-editor.

TheLarkInn avatar TheLarkInn commented on May 22, 2024

@rockymontana I'd add html-webpack-plugin and remove the default index.html with one that can be generated automatically to inject bundles for you.

from monaco-editor.

rockymontana avatar rockymontana commented on May 22, 2024

The bundles are automatically injected. I see them in the markup. The rest of what you said was above my level of understanding 😭

from monaco-editor.

OneCyrus avatar OneCyrus commented on May 22, 2024

wow thanks! that's great.

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

from monaco-editor.

corbinu avatar corbinu commented on May 22, 2024

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

from monaco-editor.

mnpenner avatar mnpenner commented on May 22, 2024

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.

from monaco-editor.

timkendrick avatar timkendrick commented on May 22, 2024

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

from monaco-editor.

rockymontana avatar rockymontana commented on May 22, 2024

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

from monaco-editor.

rockymontana avatar rockymontana commented on May 22, 2024

What’s your email?

from monaco-editor.

rebornix avatar rebornix commented on May 22, 2024

@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.

from monaco-editor.

corbinu avatar corbinu commented on May 22, 2024

edit: removed email

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

from monaco-editor.

rockymontana avatar rockymontana commented on May 22, 2024

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

from monaco-editor.

meyer avatar meyer commented on May 22, 2024

@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.

from monaco-editor.

rockymontana avatar rockymontana commented on May 22, 2024

@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.
¯_(ツ)_/¯

from monaco-editor.

moranje avatar moranje commented on May 22, 2024

@rebornix @CompuIves thanks, that'll be great

from monaco-editor.

corbinu avatar corbinu commented on May 22, 2024

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/

from monaco-editor.

TheLarkInn avatar TheLarkInn commented on May 22, 2024

@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.

from monaco-editor.

TheLarkInn avatar TheLarkInn commented on May 22, 2024

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!)

from monaco-editor.

ngohungphuc avatar ngohungphuc commented on May 22, 2024

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

from monaco-editor.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.