Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to configure terser? #2131

Closed
will opened this issue Jun 14, 2019 · 12 comments · Fixed by #2624
Closed

How to configure terser? #2131

will opened this issue Jun 14, 2019 · 12 comments · Fixed by #2624
Labels
babel/coreJs support Questions or unspecific problems

Comments

@will
Copy link

will commented Jun 14, 2019

Sorry in advance this is long-winded. I don’t know enough about frontend stuff to know what is important and what isn't, so I'm including everything that I think might be relevant.

I'm (hopefully) at the very end of a long process of migrating from the umaintained-for-over-two-years webpack-rails to webpacker, and everything is so close, but I have one dependency that is breaking in production but not in development. It appears to be due to magnification or mangling or something like that. It seems like a project called terser is what is being used now, but I'm not 100% sure.

maybe just exclude this from minification?

I'd be more than happy to exclude this file form minification, rather than figure out which options of mangle or compress or minify etc is the actual cause.

I see from terser's documentation https://webpack.js.org/plugins/terser-webpack-plugin#exclude to exclude a file you add

module.exports = {
  optimization: {
    minimizer: [
      new TerserPlugin({
        exclude: /\/excludes/,
      }),
    ],
  },
};

to webpack.config.js. However, unlike webpack-rails, I don’t think webpacker uses webpack.config.js at all, and instead it's all in config/webpack/* and config/webpack.yml. That's okay, however I cant figure out how to get this configuration in there.

After doing the generate task to drop all the new files for webpacker, my producion.js is only

process.env.NODE_ENV = process.env.NODE_ENV || 'production'

const environment = require('./environment')

module.exports = environment.toWebpackConfig()

My environment.js is

const { environment } = require('@rails/webpacker')

const webpack = require('webpack');
environment.plugins.append("Provide", new webpack.ProvidePlugin({
  $: 'jquery',
  jQuery: 'jquery',
  'window.jQuery': 'jquery',
}));

module.exports = environment

I added some things in there to get jquery to work, but otherwise it's as it came from the generator.

I see that in this project's webpacker/package/environments/production.js there is a lot of terser configuration, however I haven't tracked down yet how or if that gets called.

I'm using webpacker-4.0.7 gem and "@rails/webpacker": "^4.0.7", npm.

the actual problem

The project that is breaking for me is metrics-graphics and terser (or whatever is doing the minification) is doing this:

development

mg development Screen Shot 2019-06-13 at 6 52 36 PM

production

mg production Screen Shot 2019-06-13 at 6 50 25 PM

@jakeNiemiec
Copy link
Member

I think it might be babel and not terser. Babel/core-js is the one that polyfills things like typeof. Original source here.

Can you tell me if putting: environment.loaders.delete('nodeModules'); in your config/webpack/environment.js file fixes it? (put it right after const environment = require('./environment').

If that doesn't work, can you show me the context where you are importing metrics-graphics? Can you also give some more info on the files pictured?

@will
Copy link
Author

will commented Jun 14, 2019

@jakeNiemiec wow that fixed it. Thank you!! I put it in config/webpack/production.js instead of c/w/environment.js since I think that's what you meant. Is this good to just leave that way forever with removing the nodeModules loader?

I'm importing metrics-graphcis on a particular page, and the _typeof being undefined (well t after all the minification), was causing the component it was a part of to not be defined, so it couldn't be found with the ujs loader thing. The rest of the site would work, just not the page that imported metrics-graphics.

The pictures were the chrome debugger on that line that failed, I assume after all the source mapping, since the error messages didn't match up with the lines in the chrome debugger (t vs _typeof)

@jakeNiemiec jakeNiemiec added babel/coreJs support Questions or unspecific problems labels Jun 14, 2019
@jakeNiemiec
Copy link
Member

jakeNiemiec commented Jun 14, 2019

instead of c/w/environment.js since I think that's what you meant

No, the problem is that you are re-transpiling code that has already been transpiled. I would future-proof things by putting it in environment, but you could probobly get away with not doing it.

Think about if you use Google Translate to pass a phrase through many different languages. In the same way, nuance is lost and the result you get can break depending on what you started with. From what I can tell, the typeof polyfill broke after being passed through a newer version of Babel.

Is everything working now, can this be closed?

@will
Copy link
Author

will commented Jun 14, 2019

Ah okay, I see what you're saying. I wasn't aware that node_modules would already be transpiled at all.

I thought you meant production because that file has const environment = require('./environment'). whereas environment has a similar, but slightly different line const { environment } = require('@rails/webpacker'). I'll move it to the second line of environment.

Thank you for your help!

@will will closed this as completed Jun 14, 2019
@swrobel
Copy link
Contributor

swrobel commented Aug 29, 2019

I'm still curious how to configure Terser. I previously configured UglifyJS to strip console statements like so:

const uglifyOptions = environment.plugins.get('UglifyJs').options.uglifyOptions;

uglifyOptions.compress.drop_console = true; // Remove console statements

but it seems the configuration completely changed in Webpacker 4 and so Terser (and before it, Uglify) is no longer available via environment.plugins. cc: @gauravtiwari

related: #1235 (comment)

@jakeNiemiec
Copy link
Member

You could try playing around with environment.config.optimization.minimizer....

Here is where this is set (drop_console would be set under compress):

terserOptions: {
parse: {
// Let terser parse ecma 8 code but always output
// ES5 compliant code for older browsers
ecma: 8
},
compress: {
ecma: 5,
warnings: false,
comparisons: false
},
mangle: {
safari10: true
},
output: {
ecma: 5,
comments: false,
ascii_only: true
}

@swrobel
Copy link
Contributor

swrobel commented Sep 4, 2019

@jakeNiemiec thanks for getting me on the right path! This worked:

environment.config.optimization.minimizer.find(m => m.constructor.name === 'TerserPlugin').options.terserOptions.compress.drop_console = true

@ngottlieb
Copy link

ngottlieb commented Jan 22, 2020

@jakeNiemiec I've run into what is more or less the same problem trying to use mapbox gl js with a relatively vanilla rails 6 webpacker setup (here's a related issue on the Mapbox repo). Your recommendation --

environment.loaders.delete('nodeModules');

solved the problem, but it seems like a blunt instrument. I'm new to webpacker, but shouldn't default behavior transpile modules that need it and ignore ones that don't? Or, perhaps ignore all of them, like you're suggesting?

EDIT I changed it to just exclude mapbox-gl here, but it still seems like an open question as to whether or not this is really the best solution?

@jakeNiemiec
Copy link
Member

jakeNiemiec commented Feb 3, 2020

@ngottlieb @swrobel Glad to see you got it working.

it seems like a blunt instrument. I'm new to webpacker, but shouldn't default behavior transpile modules that need it and ignore ones that don't?

The inclusion of the nodeModules loader (which transpiles node_modules) is not a clear-cut topic; it can break things depending on what dependencies the user is using. For mapbox-gl, it has already been packed twice by the time it gets to node_modules. This problem usually arises when code is transpiled, then minified, then transpiled & minified again by webpacker.

This results in a kind of fidelity loss where included global polyfills, like _typeof, are overwritten by the global _typeof that webpacker provides. You can see this happening in Will's pictures above.


it still seems like an open question as to whether or not this is really the best solution?

I can't think of a scenario where not transpiling nodeModules would cause an error. I guess there is the concern that changing it will suddenly break things for folks who now rely on the current nodeModules loader behavior.

One helpful change that we could make would be to move the "Excluding node_modules From Being Transpiled By Babel-Loader" section from the upgrade doc to the readme doc since it is often required outside of upgrading from v3 to v4.


@gauravtiwari @javan @dhh What do you think? I feel like I have seen this issue enough times over the past year to warrant some discussion on the pain points it causes. Webpack 5 is already on the horizon, webpacker@5 could take advantage of the semver major release to address this problem as a breaking change.

Here are some earmarked examples from the past year since the webpacker@4 release where the nodeModules loader causes problems (newest to oldest):

@yannicklescure
Copy link

yannicklescure commented Feb 9, 2021

If someone needs, this is how I did:

`
// config/webpack/production.js
process.env.NODE_ENV = process.env.NODE_ENV || 'production'

const environment = require('./environment')

const TerserPlugin = require("terser-webpack-plugin");
environment.config.set('optimization.minimize', true);
environment.config.set('optimization.minimizer', [new TerserPlugin()]);

module.exports = environment.toWebpackConfig()
`

@hibachrach
Copy link

hibachrach commented Feb 25, 2021

The inclusion of the nodeModules loader (which transpiles node_modules) is not a clear-cut topic; it can break things depending on what dependencies the user is using. For mapbox-gl, it has already been packed twice by the time it gets to node_modules. This problem usually arises when code is transpiled, then minified, then transpiled & minified again by webpacker.

This results in a kind of fidelity loss where included global polyfills, like _typeof, are overwritten by the global _typeof that webpacker provides. You can see this happening in Will's pictures above.

@jakeNiemiec

First, apologies for necro-ing an old (closed) issue.

I think I'm either asking a silly question or revealing my ignorance here, but this doesn't make sense to me. The fidelity metaphor seems sensible, but if the transformation process is lossy, isn't that a bug in the transpiler/bundler/etc?

To not transpile node_modules requires trusting that all JS in the vendor dependency tree is upholding its burden to not accidentally use non-transpiled modern features.

@jakeNiemiec
Copy link
Member

To not transpile node_modules requires trusting that all JS in the vendor dependency tree is upholding its burden to not accidentally use non-transpiled modern features.

The expectation to only include non-transpiled modern features in the your source. That way, babel can include contingent code that gives old browsers the means to function despite the missing language feature.

The fidelity metaphor seems sensible, but if the transformation process is lossy, isn't that a bug in the transpiler/bundler/etc?

@HarrisonB No, this is by design. The users who eventually get the script don't need a source file, they are sent a chopped-down version that turns mySuperDetailedVarName to aac and removes the bits of code that are never used. See tree shaking. Think of the uncompiled source as a photo taken on a high-res camera. The image is millions of pixels (50 MB raw). If you upload the image to social media, they downsample the image to, say, 1 MB with thousands of pixels. If you make a low-res image bigger, the fidelity lost remains lost.

If you have any other questions, feel free to email me.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
babel/coreJs support Questions or unspecific problems
Projects
None yet
Development

Successfully merging a pull request may close this issue.

6 participants