Skip to content
This repository has been archived by the owner on Sep 16, 2023. It is now read-only.

Webpack builds are not deterministic (omit sometimes behaves differently) #162

Open
jfly opened this issue Jul 11, 2019 · 2 comments
Open
Labels

Comments

@jfly
Copy link

jfly commented Jul 11, 2019

We recently noticed that repeated runs of our webpack build do not always produce the same compiled javascript. After much digging, we eventually traced it back to this plugin.

The problem is related to the order in which webpack invokes the nmf.hooks.afterResolve callback this plugin registers (the callback fires in different orders based on when filesystem reads finish). I put together a simple reproducible example over in https://github.com/jfly/2019-07-lodash-webpack-plugin-nondeterminism. With the following 3 files:

index.js

import './a';
import './b';

a.js

import _ from 'lodash';
function duplicate(n) {
  return [n, n];
}
_.flatMap([1, 2], duplicate);

b.js

import _ from 'lodash';
let b = _.omit({'magickey': 1}, 'magickey');
console.log(b);

It will invoke webpack two times, once with no delays on filesytems reads, and once with a 1 second delay before returning the contents of a.js:

$ git clone https://github.com/jfly/2019-07-lodash-webpack-plugin-nondeterminism.git
...
$ cd 2019-07-lodash-webpack-plugin-nondeterminism 
$ make run
...
Compiling (with no delay for accessing 'a.js')
Running dist/main.nodelay.js (this will print an empty object)
{}

Compiling (with 1 second delay for accessing 'a.js')
Running dist/main.withdelay.js (this will print a non-empty object)
{ magickey: 1 }

Note that output differs between these two compiled artifacts!

The root cause seems to be related to the fact that lodash-webpack-plugin updates some internal state (this.patterns) inside of the callback that gets fired in a non-deterministic order. In the example above, if a.js is read first, then the overrides for flatMap get loaded, which enables flattening. However, if b.js is read first, then flattening will not have been enabled yet, and omit will behave differently.

One workaround is to explicitly enable flattening in our lodash-webpack-plugin configuration, but it does feel to me like this plugin should be rewritten to behave deterministically, regardless of the order in which webpack reads files.

@jdalton jdalton added the bug label Jul 11, 2019
@ElMassimo
Copy link
Contributor

ElMassimo commented Jul 24, 2019

I've experienced this problem as well, for example with camelCase when turning deburring off, as well as with flatten and flatMap.

The non-deterministic aspect causes some builds to actually require deburr, and some builds to use identity.

When deburr is included, the assumption that the returned object will be a String is valid, but when it's not, the assumption doesn't hold.

As a result, sometimes camelCase({}) returns objectObject, and sometimes it causes a replace is not a function error.

Made #163 to address this difference in behavior, but the non-deterministic aspect would remain.

@h2oearth
Copy link

This issue sounds quite critical! Seems to me that this project is death :-(.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Development

No branches or pull requests

4 participants