babel-loader: `resolveLoader.root` not being taken into account?

(I’m not sure if this issue/question belongs here so apologies if that’s not the case)

I’m using Babel 6 and I’m trying to use babel-loader for code which lives in a 3rd party directory, i.e. it’s not a parent folder of webpack.config.js. This is accessible to webpack via resolve.root. Besides that, I’m setting resolveLoader: { root: path.join(__dirname, 'node_modules') } as described in the webpack documentation.

This seems to work as the Babel code is run. However, it complains about the es2015 preset being missing:

Module build failed: Error: Couldn't find preset "es2015" relative to directory "myDirectory"
    at OptionManager.mergePresets (~/node_modules/babel-core/lib/transformation/file/options/option-manager.js:327:17)
    at OptionManager.mergeOptions (~/node_modules/babel-core/lib/transformation/file/options/option-manager.js:287:12)
    at OptionManager.init (~/node_modules/babel-core/lib/transformation/file/options/option-manager.js:416:10)
    at File.initOptions (~/node_modules/babel-core/lib/transformation/file/index.js:190:75)
    at new File (~/node_modules/babel-core/lib/transformation/file/index.js:121:22)
    at Pipeline.transform (~/node_modules/babel-core/lib/transformation/pipeline.js:42:16)
    at transpile (~/node_modules/babel-loader/index.js:14:22)
    at ~/node_modules/babel-loader/lib/fs-cache.js:140:16
    at ReadFileContext.callback (~/node_modules/babel-loader/lib/fs-cache.js:27:23)
    at FSReqWrap.readFileAfterOpen [as oncomplete] (fs.js:324:13)
 @ multi editor

(here myDirectory is the 3rd party directory).

Of course if I npm install babel-loader babel-core babel-preset-es2015 in the 3rd party directory it works, however, if I understand it correctly, that defeats the purpose of webpack’s resolveLoader.root.

Am I missing something? Is there something wrong in my understanding? And more importantly, does anyone have any suggestions on how to proceed? Thanks in advance!

About this issue

  • Original URL
  • State: closed
  • Created 9 years ago
  • Reactions: 12
  • Comments: 23 (2 by maintainers)

Commits related to this issue

Most upvoted comments

Would also like to see a proper fix for this. Using require.resolve seems to be workaround:

    query: {
      presets: [
        require.resolve('babel-preset-es2015'),
        require.resolve('babel-preset-react'),
        require.resolve('babel-preset-stage-0'),
      ],
    },

@globexdesigns Instead of using require.resolve for each entry, I did this: (not JSON; I’m using the Babel require hook)

plugins: [
    "babel-plugin-add-module-exports",
    "babel-plugin-transform-decorators-legacy"
].map(require.resolve),
presets: [
    "babel-preset-es2015",
    "babel-preset-stage-0",
    "babel-preset-react"
].map(require.resolve)

Especially useful if you have a bunch of entries.

If you want, you can add babel-{preset, plugin} with the map function, allowing you to use an unmodified plugin/preset stanza (but I prefer using the npm name).

FYI: The resolve hack for this is also needed for plugins as well:

plugins: [
    require.resolve("babel-plugin-add-module-exports"),
    require.resolve("babel-plugin-transform-decorators-legacy")
],
presets: [
    require.resolve("babel-preset-es2015"),
    require.resolve("babel-preset-stage-0"),
    require.resolve("babel-preset-react")
]

To support passing preset / plugin configuration along with declarations, I’ve created a tiny function that I call localResolve:

function localResolve(preset) {
    return Array.isArray(preset) ?
           [require.resolve(preset[0]), preset[1]] :
           require.resolve(preset);
}

which I then use like this:

{
    test: /\.js?$/,
    loader: "babel-loader",
    exclude: /node_modules/,
        query: {
            presets: [
                ["babel-preset-env", {
                    modules: false,
                    "targets": {
                        "browsers": ["last 2 versions"]
                    },
                }],
                "babel-preset-stage-3",
            ].map(localResolve),

            "plugins": [
                "babel-plugin-transform-runtime"
            ].map(localResolve),
        },
    },
}

Btw you can also pass plugins/presets arrays filled with references, not the strings. You require needed stuff first:

    const babelPresets = [
        require('babel-preset-es2015')
    ];
    const babelPlugins = [
        require('babel-plugin-transform-class-properties')
    ];

And then you connect that to the webpack’s config with babel property name:

        babel: {
            presets: babelPresets,
            plugins: babelPlugins,
            cacheDirectory: true
        }

In this case obviously babel doesn’t resolve plugins/presets locations by string names, but uses passed by the reference instances. But it’s not a very flexible way since it’s a global config, not the per loader config. Passing instantiated plugins/presets references to the loader’s query property doesn’t work since webpack converts references to strings.

I did a brief issue debugging and so far for me it looks like that it’s not in babel-loader power to make resolveLoader.root webpack’s property work for babel since babel resolves plugins/presets modules locations based on the processed file and babel has no explicit option like modules root directory.

resolveLoader only affects modules referenced within the code being bundled, not things loaded by the loaders themselves, like babel-loader’s plugins.

babel-loader resolves all of it’s arguments relative to the current working directory, so you should either explicitly require plugins/presets that you want to use, or consider setting cwd to the conceptual root of your project. Which of these you do kind of depends on why your dependencies aren’t already resolvable.

Note: This working-directory-relative behavior is new in Babel 7. Babel 6 resolved things relative to each individual file, which was definitely terrible, so hopefully that reduces the need to handle this stuff manually anyway.

Almost 2018 - Has this been addressed ? Or require.resolve is still in play ?

Update: Seems that creating a symlink pointing to the node_modules is also a viable workaround.

👍 I am running into this as well.

Having the same issue when using symlinks…