Vue 2.7 + webpack: all variables inside debugger are undefined / null

42 Views Asked by At

We have a Vue 2.7 SPA at my workplace.

Nobody ever configured a dev build for it, nobody ever used a debugger (it's a docker-compose environment).

I'm currently implementing the dev build, and so far it's been a success, except for the fact that I cannot access variables inside a debugger.

Source maps seem to work, since I can successfully place breakpoints inside .vue component files.

I start the dev server by:

npm run start -- --port 8000 --host 0.0.0.0

(it's inside a docker-compose network, so don't mind the 0.0.0.0 bit)

The package.json file looks like this:

{
    "name": "...",
    "version": "1.0.0",
    "description": "",
    "scripts": {
        "start": "webpack-dev-server --open --history-api-fallback",
        "build": "webpack --mode production",
        "webpack": "webpack --mode=development --watch"
    },
    "browserslist": [
        "> 0.25%, not dead"
    ],
    "dependencies": {
        "autoprefixer": "^10.4.11",
        "axios": "^0.27.2",
        "bootstrap": "^4.6.1",
        "bootstrap-vue": "^2.21.2",
        "inputmask-core": "^2.2.0",
        "mini-toastr": "^0.8.1",
        "moment": "^2.29.4",
        "postcss-preset-env": "^7.8.2",
        "qs": "^6.11.0",
        "v-mask": "^2.3.0",
        "vue": "^2.6.12",
        "vue-i18n": "^8.27.2",
        "vue-notification": "^1.3.20",
        "vue-notifications": "^0.9.0",
        "vue-resource": "^1.5.1",
        "vue-router": "^3.4.9",
        "vue2-datepicker": "^3.11.0",
        "vuetable-2": "^1.7.5",
        "vuex": "^3.6.2"
    },
    "devDependencies": {
        "@babel/core": "^7.12.10",
        "@babel/preset-env": "^7.19.1",
        "@prerenderer/renderer-jsdom": "^0.2.0",
        "babel-loader": "^8.2.2",
        "clean-webpack-plugin": "^4.0.0",
        "copy-webpack-plugin": "^6.0.3",
        "core-js": "^3.25.3",
        "css-loader": "^3.6.0",
        "del-cli": "^3.0.1",
        "eslint": "^8.25.0",
        "eslint-plugin-vue": "^9.6.0",
        "file-loader": "^6.2.0",
        "html-webpack-plugin": "^4.5.1",
        "mini-css-extract-plugin": "^0.9.0",
        "optimize-css-assets-webpack-plugin": "^5.0.3",
        "postcss": "^8.4.16",
        "postcss-loader": "^4.3.0",
        "prerender-spa-plugin": "^3.4.0",
        "sass": "^1.56.1",
        "sass-loader": "^10.2.0",
        "style-loader": "^2.0.0",
        "svg-sprite-loader": "^6.0.11",
        "svgo-loader": "^3.0.1",
        "terser-webpack-plugin": "^3.0.6",
        "vue-loader": "^15.9.6",
        "vue-style-loader": "^4.1.2",
        "vue-template-compiler": "^2.6.12",
        "webpack": "^4.46.0",
        "webpack-cli": "^3.3.12",
        "webpack-dev-server": "^3.11.0",
        "webpack-fix-style-only-entries": "^0.5.1"
    }
}

Finally, the webpack.config.js looks like this:

const webpack = require('webpack');
const path = require('path');

const { CleanWebpackPlugin } = require('clean-webpack-plugin')
const { VueLoaderPlugin } = require('vue-loader')

const FixStyleOnlyEntriesPlugin = require("webpack-fix-style-only-entries")
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin')

const TerserPlugin = require('terser-webpack-plugin')
const SpriteLoaderPlugin = require('svg-sprite-loader/plugin');
const CopyPlugin = require('copy-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')

module.exports = {
    devtool: 'source-map',
    devServer: {
        allowedHosts: ["all"],
        disableHostCheck: true,
    },
    entry: {
        app: path.resolve(__dirname, 'src/index.js'),
    },
    output: {
        filename: '[name].[hash].js',
        chunkFilename: '[name].[hash].bundle.js',
        path: path.resolve(__dirname, 'dist'),
        publicPath: '/',
    },
    module: {
        rules: [
            {
                test: /\.vue$/,
                loader: 'vue-loader',
                options: {
                    loaders: {
                        css: 'vue-style-loader!css-loader!sass-loader', // <style lang="css">
                        scss: 'vue-style-loader!css-loader!sass-loader', // <style lang="scss">
                        sass: 'vue-style-loader!css-loader!sass-loader?indentedSyntax' // <style lang="sass">
                    }
                }
            },
            {
                test: /\.svg$/,
                include: path.resolve(__dirname, 'src/assets/icons'),
                use: [
                    {
                        loader: 'svg-sprite-loader',
                        options: {
                            extract: true,
                            publicPath: '/assets/icons/',
                            spriteFilename: svgPath => `sprite${svgPath.substr(-4)}`
                        }
                    },
                    // 'svg-sprite-loader',
                    {
                        loader: 'svgo-loader',
                        options: {
                            plugins: [
                                {
                                    name: 'removeAttributesBySelector',
                                    params: {
                                        selector: "[stroke='#787F9B']",
                                        attributes: "stroke"
                                    }
                                },
                                {
                                    name: 'removeAttributesBySelector',
                                    params: {
                                        selector: "[fill='#787F9B']",
                                        attributes: "fill"
                                    }
                                },
                                {
                                    name: 'removeAttributesBySelector',
                                    params: {
                                        selector: "[stroke='#B2BAD8']",
                                        attributes: 'stroke'
                                    }
                                },
                                {
                                    name: 'removeAttributesBySelector',
                                    params: {
                                        selector: "[fill='#B2BAD8']",
                                        attributes: 'fill'
                                    }
                                },
                                {
                                    name: 'removeAttributesBySelector',
                                    params: {
                                        selector: "[fill='none']",
                                        attributes: "fill"
                                    }
                                },
                            ]
                        }
                    }
                ]
            },
            {
                test: /\.m?js$/,
                exclude: /node_modules/,
                use: {
                    loader: 'babel-loader',
                    options: {
                        presets: [
                            [
                                "@babel/preset-env",
                                {
                                    "debug": true,
                                    "useBuiltIns": "entry",
                                    "corejs": "3.22"
                                }
                            ]
                        ],
                    }
                },
            },
            {
                test: /\.(png|jpe?g|gif|ico)$/i,
                loader: 'file-loader',
                options: {
                    name: 'assets/img/[name].[ext]',
                },
            },
            {
                test: /\.(ttf|woff|woff2|eot)$/,
                loader: 'file-loader',
                options: {
                    name: '[name].[ext]?[hash]'
                }
            },
            {
                test: /\.(sass|scss)$/,
                // include: path.resolve(__dirname, 'src/assets/scss'),
                exclude: /node_modules/,
                use: [
                    {
                        loader: MiniCssExtractPlugin.loader,
                    },
                    {
                        loader: 'css-loader',
                        options: {
                            url: false,
                        },
                    },
                    {
                        loader: 'sass-loader',
                        options: {
                            additionalData: "@import '@/assets/scss/colors.scss';"
                        },
                    },
                ],
            },
            {
                test: /\.css$/,
                use: [
                    'vue-style-loader',
                    MiniCssExtractPlugin.loader,
                    'css-loader',
                    'postcss-loader'
                ]
            }
        ],
    },
    optimization: {
        splitChunks: {
            chunks: 'all',
            cacheGroups: {
                vendor: {
                    test: /[\\/](node_modules)[\\/].+\.js$/,
                    name: 'vendor',
                }
            }
        },
        minimizer: [
            new OptimizeCssAssetsPlugin({
                cssProcessorOptions: { map: { inline: false, }, },
                cssProcessorPluginOptions: {
                    preset: [
                        'default',
                        { discardComments: { removeAll: true } },
                    ],
                },
            }),
            new TerserPlugin({
                extractComments: false,
                terserOptions: {
                    output: {
                        comments: false,
                    },
                },
            }),
        ],
    },
    resolve: {
        symlinks: false,
        modules: [path.resolve(__dirname, 'node_modules')],
        alias: {
            '@': path.resolve(__dirname, 'src'),
            layouts: path.resolve(__dirname, 'src/layouts'),
            utils: path.resolve(__dirname, 'src/utils'),
            routes: path.resolve(__dirname, 'src/routes'),
            assets: path.resolve(__dirname, 'src/assets'),
            vue$: 'vue/dist/vue.runtime.min.js'
            // 'vue-resource': 'vue-resource/dist/vue-resource.esm.js'
        },
        extensions: ['.js', '.vue', '.json', '.png']
    },
    plugins: [
        new webpack.DefinePlugin({
            'API_URL': `'${process.env.API_URL || ""}'`,
            'WS_URL': `'${process.env.WS_URL || ""}'`,
        }),
        new CleanWebpackPlugin(),
        new CopyPlugin({
            patterns: [
                { from: './src/robots.txt', to: './assets/robots.txt' },
                path.resolve(__dirname, 'src/assets/img/favicon.ico'),
            ],
        }),
        require('autoprefixer'),
        new SpriteLoaderPlugin({
            plainSprite: true,
        }),
        new VueLoaderPlugin(),
        new FixStyleOnlyEntriesPlugin(),
        new MiniCssExtractPlugin({
            filename: './assets/css/[name].[hash].bundle.css',
        }),
        new HtmlWebpackPlugin({
            // MANIFEST_FILENAME: '/assets/manifest.json',
            template: 'src/index.html',
            title: '...',
            favicon: 'src/assets/img/favicon.ico',
            // inject: false,
            // scripts: [`/app.js?v=${new Date().getTime()}`]
            meta: [{
                name: 'viewport',
                content: 'width=device-width, initial-scale=1.0'
            }]
            // links: [
            //     "https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.css",
            //     {rel: "shortcut icon", href: "/assets/icons/favicon.ico", type: "image/x-icon"},
            // ]
        }),
    ],
};

It's the last frontier of the dev environment, which I haven't yet been able to conquer.

I understand the problem: arguments / variables get mangled during the minification process. Here is component's method, which I was trying to debug:

async onAddUsdtTrc20Wallet() {
      try {
        const response = await this.createNewCryptoWallet('USDT-TRC20X');
        const address = response.wallet_address;
        this.showSuccess(this.$t('profilePage.wallets.successAddUsdtTrc20Wallet') + address);
      } catch (err) {
        this.showError(this.$t('profilePage.wallets.errorAddUsdtTrc20Wallet') + err.toString());
      }
    }

After minification it looks like this:

    onAddUsdtTrc20Wallet: function onAddUsdtTrc20Wallet() {
      var _this7 = this;
      return _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee2() {
        var response, address;
        return _regeneratorRuntime().wrap(function _callee2$(_context2) {
          while (1) {
            switch (_context2.prev = _context2.next) {
              case 0:
                _context2.prev = 0;
                _context2.next = 3;
                return _this7.createNewCryptoWallet('USDT-TRC20X');
              case 3:
                response = _context2.sent;
                address = response.wallet_address;
                _this7.showSuccess(_this7.$t('profilePage.wallets.successAddUsdtTrc20Wallet') + address);
                _context2.next = 11;
                break;
              case 8:
                _context2.prev = 8;
                _context2.t0 = _context2["catch"](0);
                _this7.showError(_this7.$t('profilePage.wallets.errorAddUsdtTrc20Wallet') + _context2.t0.toString());
              case 11:
              case "end":
                return _context2.stop();
            }
          }
        }, _callee2, null, [[0, 8]]);
      }))();
    }

What I tried:

  • I tried using "devtool": "eval-source-map".
  • Tried removing "minimizer" and setting "minify" field inside "optimization" to false.
  • Tried setting TerserPlugin options to {minify: TerserPlugin.esbuildMinify, terserOptions: { minify: false, minifyWhitespace: true, minifyIdentifiers: false, minifySyntax: true,}
  • Tried removing optimization altogether.

None of these steps changed the generated app.js output script. Double checked by exec -iting inside the container to make sure new webpack.config.js was in place.

Anybody had this kind of problem?

0

There are 0 best solutions below