0

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?

7
  • It's unclear what is the problem exactly. Which variables do you debug and in which way? You can try to set terserOptions.mangle to false, but they don't look minified, and this obviously won't make all variable names the same because some have to be different due to the way code is transpiled Commented Mar 7, 2024 at 2:01
  • @EstusFlask all I'm trying to do is to debug the code inside a chrome / firefox debugger. If I place the breakpoint on the this.showError line, when it's hit I cannot access err, as it is undefined. Same goes for this (which becomes _this7), etc. Commented Mar 7, 2024 at 6:46
  • @EstusFlask The source maps seem to be in place, since I can hit the breakpoint correctly and reliably. When I developed for vue3, I could always access the variables from any browser's debugger. Commented Mar 7, 2024 at 6:55
  • It's not directly related to Vue version. This totally depends on what the variables are. In this case they are certainly affected by transpilation transforms that are needed for your ES target and not by a minifier. Source map makes you able to hover on a variable in a debugger at a breakpoint and show it's value like coderpad.io/blog/development/javascript-debugging-in-chrome , it physically cannot affect how things appear in a console and create references for vars that don't exist in this scope. Commented Mar 7, 2024 at 9:22
  • The relevant part isn't shown, which is babel config. It affects ES target, currently it appears to be ES5, needs to be higher to get rid of most side effects, at least ES2017. You can have a different ES target for dev mode but then there can be transpilation-related surprises when things stop working the way they should in prod. Possible duplicate of stackoverflow.com/questions/54712662/… Commented Mar 7, 2024 at 9:25

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.