jamesandersen / string-replace-webpack-plugin

Replace string tokens in a bundle.
93 stars 27 forks source link

String replace plugin fails with Webpack 2.1 (2.1.0-beta.27) #20

Closed mlcohen closed 7 years ago

mlcohen commented 7 years ago

I'm trying to upgrade my project from webpack 1.3 to webpack 2.1 (currently 2.1.0-beta.27). In my webpack configuration I'm making use of the string replace webpack plugin which works fine with webpack 1.3 but ends up causing a build error using webpack 2.1. Here is an example of what my config currently looks like with the string replace webpack plugin:

let StringReplacePlugin  = require('string-replace-webpack-plugin');

module.exports = {
    module: {
        rules: [{
            enforce: 'pre',
            test: /\.js$/,
            use: [{
                loader: StringReplacePlugin.replace({
                    pattern: /id="@(\w+):([\w\.]+)"/g,
                    replacement: function (match, p1, p2) {
                        return `id="${p2}" defaultMessage={${p1}.${p2}.message}`
                    }
                })
            }]
        },
        // .... other rules
       ]
    },

    plugins: [
        new StringReplacePlugin()
    ]
} 

And here is the build error that I'm getting:

ERROR in ./main.js
Module build failed: Error: Cannot find module '/Users/mlc/tmp/app/node_modules/string-replace-webpack-plugin/loader.js?id=n7if95dcsxmh55jdgfhpeewmi'
    at Function.Module._resolveFilename (module.js:469:15)
    at Function.Module._load (module.js:417:25)
    at Module.require (module.js:497:17)
    at require (internal/module.js:20:19)
    at loadLoader (/Users/mlc/tmp/app/node_modules/loader-runner/lib/loadLoader.js:13:17)
    at iteratePitchingLoaders (/Users/mlc/tmp/app/node_modules/loader-runner/lib/LoaderRunner.js:164:2)
    at iteratePitchingLoaders (/Users/mlc/tmp/app/node_modules/loader-runner/lib/LoaderRunner.js:160:10)
    at /Users/mlc/tmp/app/node_modules/loader-runner/lib/LoaderRunner.js:168:18
    at loadLoader (/Users/mlc/tmp/app/node_modules/loader-runner/lib/loadLoader.js:36:3)
    at iteratePitchingLoaders (/Users/mlc/tmp/app/node_modules/loader-runner/lib/LoaderRunner.js:164:2)
    at iteratePitchingLoaders (/Users/mlc/tmp/app/node_modules/loader-runner/lib/LoaderRunner.js:160:10)
    at /Users/mlc/tmp/app/node_modules/loader-runner/lib/LoaderRunner.js:168:18
    at loadLoader (/Users/mlc/tmp/app/node_modules/loader-runner/lib/loadLoader.js:36:3)
    at iteratePitchingLoaders (/Users/mlc/tmp/app/node_modules/loader-runner/lib/LoaderRunner.js:164:2)
    at runLoaders (/Users/mlc/tmp/app/node_modules/loader-runner/lib/LoaderRunner.js:357:2)
    at NormalModule.doBuild (/Users/mlc/tmp/app/node_modules/webpack/lib/NormalModule.js:131:2)
    at NormalModule.build (/Users/mlc/tmp/app/node_modules/webpack/lib/NormalModule.js:179:15)
    at Compilation.buildModule (/Users/mlc/tmp/app/node_modules/webpack/lib/Compilation.js:127:9)
    at Compilation.<anonymous> (/Users/mlc/tmp/app/node_modules/webpack/lib/Compilation.js:404:8)
    at /Users/mlc/tmp/app/node_modules/webpack/lib/NormalModuleFactory.js:74:13
    at NormalModuleFactory.applyPluginsAsyncWaterfall (/Users/mlc/tmp/app/node_modules/tapable/lib/Tapable.js:123:70)
    at onDoneResolving (/Users/mlc/tmp/app/node_modules/webpack/lib/NormalModuleFactory.js:49:11)
    at onDoneResolving (/Users/mlc/tmp/app/node_modules/webpack/lib/NormalModuleFactory.js:165:6)
    at /Users/mlc/tmp/app/node_modules/webpack/lib/NormalModuleFactory.js:161:6
    at /Users/mlc/tmp/app/node_modules/webpack/node_modules/async/dist/async.js:3694:9
    at /Users/mlc/tmp/app/node_modules/webpack/node_modules/async/dist/async.js:356:16
    at iteratorCallback (/Users/mlc/tmp/app/node_modules/webpack/node_modules/async/dist/async.js:936:13)
    at /Users/mlc/tmp/app/node_modules/webpack/node_modules/async/dist/async.js:840:16
    at /Users/mlc/tmp/app/node_modules/webpack/node_modules/async/dist/async.js:3691:13
    at apply (/Users/mlc/tmp/app/node_modules/webpack/node_modules/async/dist/async.js:21:25)

Any thoughts on how I can address this? Is there a simple fix that can be applied to my webpack config or is this a compatibility issue between the string replace plugin and webpack 2?

Code to reproduce the problem can be found here:

https://gist.github.com/mlcohen/6d338ce0494f5a2e966ef83474b3ab1b

other details

jamesandersen commented 7 years ago

@mlcohen Hi there, I don't know of simple fix off the bat and am unfortunately a bit short on time to investigate just now. If you get to it before I do, a PR would be welcome.

rayalan commented 7 years ago

I came across this tonight and investigated it a little bit. It looks like something about the way loaders are returned has changed (e.g. no more passing back a random id in the query string). So the immediate problem is that if one tacks on ?id=..., webpack can't find the loader to call it.

The subsequent problem is that as soon as one stops returning the id, one can't find the original options any more when the loader is called. I suspect that webpack2 redid the loader interface and it would be trivial to fix if the new interface was documented/familiar to someone...unfortunately, that isn't me.

mlcohen commented 7 years ago

I finally figured it out. After some searching I came across this raised issue: https://github.com/webpack/extract-text-webpack-plugin/issues/275. Turns out that you can't currently use the new use webpack2 config syntax for plugins like the extract text webpack plugin, which is also the case for the string replace plugin. Instead of use, apply the loader syntax, like so:

module: {
    rules: [{
        enforce: "pre",
        test: /\.js$/,
        exclude: /node_modules/,
        loader: StringReplacePlugin.replace({
            replacements: [{
                pattern: /id="@(\w+):([\w\.]+)"/g,
                replacement: function (match, p1, p2) {
                    return `id="${p2}" defaultMessage={${p1}.${p2}.message}`
                }
            }]
        })
    }
}

I went back over the webpack2 docs and it does indeed show ExtractTextPlugin being applied to loader within the list of rules. I'm not sure if this subtle distinction is intended, but if it is, it would be really nice if the docs would clarify when to apply use and when to only apply loader. In any case, I can now build with the string replace plugin even with the latest version of webpack2 (v2.2.0-rc.1).

RomanHotsiy commented 7 years ago

On the latest webpack 2.2.0-rc.4 both syntaxes work fine for me:

loader: StringReplacePlugin.replace({...

and

rules: [{
  enforce: 'pre',
  test: /\.js$/,
  use: [{
    loader: StringReplacePlugin.replace({...

So maybe this issue should be closed

mlcohen commented 7 years ago

@RomanGotsiy Excellent. Thanks for the info 👍🏼