babel / minify

:scissors: An ES6+ aware minifier based on the Babel toolchain (beta)
https://babeljs.io/repl
MIT License
4.39k stars 225 forks source link

Doesnt remove unused variables #481

Closed budarin closed 7 years ago

budarin commented 7 years ago

I use React and used to define propTypes like this

import React from 'react';
....

    const { func, object, number } = React.PropTypes;
...

class Counter extends React.Component {

    static propTypes = {
        asyncIncrement: PropTypes.func.isRequired,
        increment: PropTypes.func.isRequired,
        decrement: PropTypes.func.isRequired,
        counter: PropTypes.number.isRequired,
        classes: PropTypes.object,
    };

    static defaultProps = {
        classes: {},
    };
...

after building a project with the plugin I see in the code declaration of unused variables

O = w.default.PropTypes, I = O.number, Y = O.func, N = O.object

and theses variables are never use in the module

hzoo commented 7 years ago

Hey @budarin! We really appreciate you taking the time to report an issue. The collaborators on this project attempt to help as many people as possible, but we're a limited number of volunteers, so it's possible this won't be addressed swiftly.

If you need any help, or just have general Babel or JavaScript questions, we have a vibrant Slack community that typically always has someone willing to help. You can sign-up here for an invite.

boopathi commented 7 years ago

Can you create a [REPL link](https://babeljs.io/repl/#?babili=true&evaluate=false&lineWrap=true&presets=stage-2&targets=&browsers=chrome%2052&builtIns=false&code=function%20foo()%20%7B%0A%20%20const%20React%20%3D%20require(%22react%22)%3B%0A%20%20const%20%7B%20func%2C%20object%2C%20number%20%7D%20%3D%20React.PropTypes%3B%0A%20%20class%20X%20extends%20React.Component%20%7B%0A%20%20%20%20static%20propTypes%20%3D%20%7B%0A%20%20%20%20%20%20asyncIncrement%3A%20PropTypes.func.isRequired%2C%0A%20%20%20%20%20%20increment%3A%20PropTypes.func.isRequired%2C%0A%20%20%20%20%20%20decrement%3A%20PropTypes.func.isRequired%2C%0A%20%20%20%20%20%20counter%3A%20PropTypes.number.isRequired%2C%0A%20%20%20%20%20%20classes%3A%20PropTypes.object%2C%0A%20%20%20%20%7D%0A%20%20%7D%0A%7D&experimental=true&loose=false&spec=false&playground=false) with your example.

budarin commented 7 years ago

I also use babel-plugin-transform-react-remove-prop-types - it removes propTypes but leaves unused variables... What to do in this case? They are not able to remove them

boopathi commented 7 years ago

Are you using babili preset in your babelrc ? along with other presets - react, es2015, and plugins - transform-react-remove-prop-types ?

budarin commented 7 years ago

I use it as webpack plugin and env-preset

budarin commented 7 years ago

and its seems not working like preset at all ... I've include it as preset but code after build is not minified at all

vigneshshanmugam commented 7 years ago

@budarin Can you share your webpack config?

budarin commented 7 years ago
import fs from 'fs';
import path from 'path';
import webpack from 'webpack';
import HappyPack from 'happypack';
import BabiliPlugin from 'babili-webpack-plugin';
import WebpackChunkHash from 'webpack-chunk-hash';
import HtmlWebpackPlugin from 'html-webpack-plugin';
import CopyWebpackPlugin from 'copy-webpack-plugin';
import BundleAnalyzer from 'webpack-bundle-analyzer';
import ProgressBarPlugin from 'progress-bar-webpack-plugin';
import ChunkManifestPlugin from 'chunk-manifest-webpack-plugin';
import ServiceWorkerWebpackPlugin from 'serviceworker-webpack-plugin';
import ScriptExtHtmlWebpackPlugin from 'script-ext-html-webpack-plugin';

import clientBabelLoaderConfig from './client.babel.loader.config';
const clientConfig = {
    target: 'web',
    cache: true,
    entry: {
        vendor1: [
            'react',
            'react-dom',
            'debug',
        ],
        vendor2: [
            'zousan',
            'fetch-ponyfill',
            'react-redux',
            'redux-saga',
            'normalizr',
            'react-helmet',
            'classnames',
        ],
        client: './src/client/index.js',
    },
    output: {
        path: path.resolve('./.build/client'),
        filename: '[name].[chunkhash].js',
        chunkFilename: '[name].[chunkhash].js',
        publicPath: '/',
    },
    devtool: 'source-map',
    module: {
        noParse: [/\.min\.js/],
        rules: [
            {
                test: /\.(js|jsx)$/,
                include: path.resolve('./src'),
                exclude: path.resolve('node_modules'),
                use: 'happypack/loader',
            },
            {
                test: /\.(svg|png|jpg|gif)$/,
                include: path.resolve('./src'),
                exclude: path.resolve('node_modules'),
                use: {
                    loader: 'url-loader',
                    options: {
                        name: '[name].[hash:8].[ext]',
                        hash: 'sha512',
                        digest: 'hex',
                    },
                },
            },
            {
                test: /\.css$/,
                use: [
                    'style-loader/useable',
                    {
                        loader: 'css-loader',
                        options: {
                            modules: true,
                            localIdentName: '[local]_[hash:base64:7]',
                            sourceMap: false,
                            import: false,
                            url: false,
                            minimize: {
                                colormin: false,
                                calc: false,
                                zindex: false,
                                discardComments: { removeAll: true },
                            },
                        },
                    },
                    'postcss-loader',
                ],
            },
        ],
    },
    plugins: [
        new webpack.NoEmitOnErrorsPlugin(),
        new ProgressBarPlugin(),
        new HappyPack({
            loaders: [{
                path: 'babel-loader',
                query: clientBabelLoaderConfig,
            }],
            tempDir: '.temp/client/happypack',
            threads: 4,
            verbose: false,
            enabled: true,
            cache: true,
            cacheContext: { env: process.env.NODE_ENV },
        }),
        new HtmlWebpackPlugin({
            template: './src/common/assets/index.ejs',
            cache: true,
            minify: {
                collapseWhitespace: true,
                removeRedundantAttributes: true,
                useShortDoctype: true,
                removeEmptyAttributes: true,
                removeStyleLinkTypeAttributes: true,
                keepClosingSlash: true,
                minifyJS: true,
                minifyCSS: true,
                minifyURLs: true,
            },
        }),
        new webpack.optimize.CommonsChunkPlugin({
            name: ['vendor1', 'vendor2', 'manifest'], // vendor libs + extracted manifest
            minChunks: Infinity,
        }),
        new webpack.HashedModuleIdsPlugin(),
        new WebpackChunkHash(),
        new ChunkManifestPlugin({
            filename: 'chunk-manifest.json',
            manifestVariable: 'webpackManifest',
        }),
        new ScriptExtHtmlWebpackPlugin({
            inline: ['manifest'],
            defer: ['vendor1', 'vendor2', 'client'],
            preload: ['vendor1', 'vendor2', 'client'],
        }),
        new webpack.ProvidePlugin({
            Promise: 'zousan',
        }),
        new webpack.DefinePlugin({
            'process.env': {
                NODE_ENV: JSON.stringify('production'),
            },
            '__TARGET__': JSON.stringify('CLIENT'),
            '__PRODUCTION__': JSON.stringify(true),
        }),
        new ServiceWorkerWebpackPlugin({ entry: path.resolve('./src/sw/sw.js') }),
        new BundleAnalyzer.BundleAnalyzerPlugin({
            analyzerMode: 'static',
            reportFilename: 'stats_report.html',
        }),
        new BabiliPlugin(),
    ],
    resolve: {
        extensions: ['.js', '.jsx'],
        modules: [
            'node_modules',
            path.resolve('./src'),
        ],
    },
};

log('Running client\'s webpack in PRODUCTION mode ...');

export default clientConfig;

and ./client.babel.loader.config

{
    babelrc: false,
    presets: [
        'react',
        ['env', {
            targets: {
                browsers: [
                    '> 5%',
                    'last 2 versions',
                    'not ie <= 11',
                ],
            },
            debug: false,
            cacheDirectory: true,
        }],
    ],
    plugins: [
        'transform-class-properties',
        'syntax-trailing-function-commas',
        'transform-flow-strip-types',
        'transform-decorators-legacy',
        ['transform-runtime', {
            helpers: false,
            polyfill: false,
            regenerator: true,
            moduleName: 'babel-runtime',
        }],

    ],
    env: {
        development: {
            plugins: [
                'babel-plugin-transform-react-jsx-self',
                'babel-plugin-transform-react-jsx-source',
            ],
        },
        production: {
            plugins: [
                'transform-react-constant-elements',
                'transform-react-remove-prop-types',
                'transform-react-inline-elements',
            ],
        },
    },
}
boopathi commented 7 years ago

The unused variables in your example - they are of the form a = foo.bar. Babili treats member expressions foo.bar to be a getter with side-effects and will not remove the expression. We can of course replace the assignment with the member expression, but it's part of a bigger problem - https://github.com/babel/babili/pull/221#issuecomment-260615569 and will be solved with that approach.

budarin commented 7 years ago

If I use such type of notation

Time.propTypes = {
    seconds: React.PropTypes.number.isRequired,
};

Time.defaultProps = {
    seconds: 0,
};

There are no broken variables in the code Only this variant is polluting the code

const { number } = React.PropTypes;
Time.propTypes = {
    seconds: number.isRequired,
};

Time.defaultProps = {
    seconds: 0,
};

Why? transform-es2015-destructuring is wrong with transpiling?