scniro / react-codemirror2

Codemirror integrated components for React
MIT License
1.66k stars 193 forks source link

CodeMirror.defineMode is not a function with Webpack #93

Closed shawnatxapp closed 6 years ago

shawnatxapp commented 6 years ago

Hi.

I am attempting to add a mode to the Editor.

It is configured as such:

import CodeMirror from "codemirror";
import React, { PureComponent } from "react";
import { Controlled as CodeMirrorEditor, IInstance } from "react-codemirror2";
...
import "codemirror/lib/codemirror.css";
...
require("codemirror/mode/javascript/javascript");

and implemented like:

render() {
        const codeMirrorOpts = {
            lineNumbers: true,
            mode: "javascript"
        };

        return (
            <CodeMirrorEditor
                        value={mutableLogic.compiled}
                        onBeforeChange={this.handleEditorChange}
                        options={codeMirrorOpts} />
        );
    }

(Full gist here)

When the page loads, the following error is printed to the console: TypeError: CodeMirror.defineMode is not a function

Has anyone else had issues with setting this up with Webpack?

scniro commented 6 years ago

@shawnatxapp The example for defineMode uses webpack just fine. Closing for now as this seems to be specific to your usage

Manuelbaun commented 6 years ago

Hi, I have the same issue. My imports:

import React, { Component } from 'react';
import { UnControlled as CodeMirror } from 'react-codemirror2'
// import CodeMirror from "react-codemirror";
import 'codemirror/lib/codemirror.css';
import 'codemirror/theme/material.css';
import 'codemirror/addon/mode/simple.js';

....
console.log(CodeMirror.defineSimpleMode) // already undefined

the 'codemirror/addon/mode/simple.js'; exists and defineSimpleMode should work, but it's not.

I am using react, my package.json:

{
  "name": "client",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "@material-ui/core": "^1.2.3",
    "axios": "^0.18.0",
    "react": "^16.4.1",
    "react-codemirror2": "^5.1.0",
    "react-dom": "^16.4.1",
    "react-scripts": "1.1.4"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test --env=jsdom",
    "eject": "react-scripts eject"
  },
  "devDependencies": {
    "codemirror": "^5.39.2",
    "react-codemirror": "^1.0.0"
  }
}
shawnatxapp commented 6 years ago

Haven't had much opportunity to look into it, but I think it likely has something to do with async loading or some form of lazy module resolution?

JoshCheek commented 5 years ago

Hi, I'm hitting this also. I'm pretty confident it's a webpack issue, but I don't know what to search for on that end >.<

When I copy the examples into a brand new npx create-react-app everything works fine. But when I copy them into it to my app, which isn't using create-react-app, it blows up b/c the mode expects CodeMirror.defineMode to exist, but it's doesn't. Maybe it's a scoping issue where something was required but not available, maybe it's an async issue as someone previously suggested, where the order of the requiring can go funky, IDK, I can think of a few other possibilities even though I can't articulate them well. Unfortunately, the dev who set Webpack up is on vacation, so I'm sort of left trying to make sense of it.

I ejected create-react-app so I could see their settings. I've been comparing their settings to ours, but so far haven't found anything promising. If we get it figured out, I'll report back here. If anyone has any suggestions for ways of debugging this, I'd appreciate your insights! ❤️

Here is the code I'm trying it against

import React, { Component } from 'react'
import {UnControlled as CodeMirror} from 'react-codemirror2'

import 'codemirror/lib/codemirror.css'
import 'codemirror/theme/material.css'
import 'codemirror/mode/ruby/ruby'

class App extends Component {
  render() {
    return <CodeMirror
      value={`def greet(target)\n  "Hello, #{target}"\nend`}
      options={{
        mode: 'ruby',
        theme: 'material',
        lineNumbers: true
      }}
      onChange={(editor, data, value) => {
      }}
    />
  }
}

export default App

Here is the error

image

Here is that file in Safari's "Resources" tab

image

JoshCheek commented 5 years ago

I've discovered that if I change the mode's import line it works (here)

// currently it is this, which returns an empty object
if (typeof exports == "object" && typeof module == "object") // CommonJS
  mod(require("../../lib/codemirror"));

// it works when I change it to this
if (typeof exports == "object" && typeof module == "object") // CommonJS
  mod(require("codemirror"));

I assume this is due to my webpack config or my babel config, but don't know how to figure out why this behaviour is happening. I tried commenting out plugins that sounded like they might be related, tried messing with Babel's presets["@babel/preset-env"].modules (eg setting it to commonjs or amd), tried adding create-react-app's named asset importer (I don't think I succeeded at this). Tried removing exclude: [/node_modules/] from the webpack rule for babel-loader.

scniro commented 5 years ago

@JoshCheek thanks for elaborating with some details. Given what you've provided I'm still not entirely sure what the issue would be as I've yet to experience it. Fortunately this project has a full blown demo build with webpack and all, perhaps you can compare the config to what you're running on your end? Something might jump out, just a thought!

JoshCheek commented 5 years ago

Will do. I also figured out that why it's coming back with an empty object: it's finding codemirror.css instead of codemirror.js Was able to figure this out by adding console.log(require.resolve("../../lib/codemirror")) to node_modules/codemirror/mode/ruby/ruby.js So somehow my webpack is not preferring the JS file. I'll compare with your examples, ty :)

JoshCheek commented 5 years ago

Figured it out: our resolver is somehow wrong. When I remove it (and update the import statements to use relative paths) then it works correctly:

image

JoshCheek commented 5 years ago

Found a presumably better solution, that resolver is supposed to resolve JS, it shouldn't have included CSS in it, there is apparently a separate resolver for CSS. 🤷‍♂️

Cheers, mate, sorry for bugging you 😝