danillouz danillouz - 2 months ago 11
Node.js Question

Symlinking react modules with npm link for local development gives error

During development of my

React
npm
module, I symlinked it with
npm link
.
After doing this, the package gets linked correctly and also appears in the consumers app
node_modules
.

The module exposes an interface to create a
React
component. Because I'm using
React
,
jsx
and
es2015
, I'm using
babel
to transpile my module code in the pre publishing stage, using the
npm prepublish hook
.

However, when I try to build my consumer app with
webpack
(i.e. after linking it) an error occurs in my package stating:


Module build failed: Error: Couldn't find preset "es2015"


Now the funny part is that if I publish the package on npm, then
npm install
from the registry in my consumer app, build it, everything works fine.

I tried installing the
babel-preset-es2015
in my consumer app, but then it starts complaining about not finding
babel
:


Module not found: Error: Cannot resolve module 'babel'


Again, if I install it from the npm registry everything works fine, no errors are thrown during build.

I also tried installing
babel-core
,
babel-cli
and
babel-polyfill
, all to no avail.

I'm using
babel v6.1.x
everywhere and aware of all the recent changes, however I guess I'm missing something obvious and would really appreciate it if someone can help me out, because continuously publishing the module in order to test if stuff works is just bad practice.

For completeness sake here is the code:



These are the steps I follow to link the module:


  1. cd ~/Sites/me/react-leafbox

  2. npm link

  3. cd ~/Sites/me/mapp

  4. npm link react-leafbox

  5. npm start



Stack trace after building:

ERROR in ../react-leafbox/lib/index.js
Module build failed: Error: Couldn't find preset "es2015"
at OptionManager.mergePresets (/Users/daniel/Sites/me/mapp/node_modules/babel-core/lib/transformation/file/options/option-manager.js:329:17)
at OptionManager.mergeOptions (/Users/daniel/Sites/me/mapp/node_modules/babel-core/lib/transformation/file/options/option-manager.js:289:12)
at OptionManager.addConfig (/Users/daniel/Sites/me/mapp/node_modules/babel-core/lib/transformation/file/options/option-manager.js:223:10)
at OptionManager.findConfigs (/Users/daniel/Sites/me/mapp/node_modules/babel-core/lib/transformation/file/options/option-manager.js:366:16)
at OptionManager.init (/Users/daniel/Sites/me/mapp/node_modules/babel-core/lib/transformation/file/options/option-manager.js:410:12)
at File.initOptions (/Users/daniel/Sites/me/mapp/node_modules/babel-core/lib/transformation/file/index.js:191:75)
at new File (/Users/daniel/Sites/me/mapp/node_modules/babel-core/lib/transformation/file/index.js:122:22)
at Pipeline.transform (/Users/daniel/Sites/me/mapp/node_modules/babel-core/lib/transformation/pipeline.js:42:16)
at transpile (/Users/daniel/Sites/me/mapp/node_modules/babel-loader/index.js:14:22)
at Object.module.exports (/Users/daniel/Sites/me/mapp/node_modules/babel-loader/index.js:83:14)
@ ./src/js/components/App.jsx 2:10-34


Stack trace after adding extra babel related dependencies (which I believe should't be necessary, because they're available in the react-leafbox module):

ERROR in ../react-leafbox/lib/index.js
Module not found: Error: Cannot resolve module 'babel' in /Users/daniel/Sites/me/react-leafbox/lib
@ ../react-leafbox/lib/index.js 8:11-39


I'm using
node v5.0.0
with
npm v3.3.6
on
MAC OSX El Capitan v10.11.1
. I also tried using
node v4.2.1
with
npm 2.14.7
, which gives me the same errors.

Answer

I found a solution to my problem. I discovered this in the webpack docs:

IMPORTANT: The loaders here are resolved relative to the resource which they are applied to. This means they are not resolved relative the the configuration file. If you have loaders installed from npm and your node_modules folder is not in a parent folder of all source files, webpack cannot find the loader. You need to add the node_modules folder as absolute path to the resolveLoader.root option. (resolveLoader: { root: path.join(__dirname, "node_modules") })

After adding the root option to the webpack.config.js the error about not being able to resolve babel disappeared. Instead I got a new one saying it can’t resolve react. I have it defined as a peer dependency, which made me think it needs to fallback when it can’t find it locally, then I found this in the docs:

resolve.fallback A directory (or array of directories absolute paths), in which webpack should look for modules that weren’t found in resolve.root or resolve.modulesDirectories.

I combined these two options in the webpack.config.js of my consumer app and it worked!

// webpack.config.js
var path = require('path');

module.exports = {

    entry: path.resolve(__dirname, 'src/js/index.js'),

    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: "build.js"
    },

    module: {
        loaders: [
            {
                test: /\.jsx?$/,
                exclude: /(node_modules)/,
                loader: 'babel',
                query:
                    {
                        presets:[ 'react', 'es2015' ]
                    }
            }
        ]
    },

    resolve: {
        extensions: [ '', '.js', '.jsx' ],
        fallback: path.join(__dirname, "node_modules")
    },


    resolveLoader: {
        root: path.join(__dirname, "node_modules")
    }
};

I hope this helps anyone coming across a similar issue.

Update for Webpack ^2.5.0

Remove the resolveLoader Object and replace resolve.fallback with resolve.symlinks set to false:

resolve: {
    extensions: [ '', '.js', '.jsx' ],
    symlinks: false
}