assembler
assembler

Reputation: 3300

webpack is failing while trying to compile in production mode

I've been using React 16.x.x and Jqwidgets 6.2.0 and React Boilerplate 3.7.0. My whole project was mounted over the React Boilerplate scaffold with its own configuration of webpack. I set jqwigets-scripts as a npm dependency and I tried excluding node_modules but including jqwidgets-scripts/jqwidgets-react through a regular expression as I read in some github. This is my current webpack.base.babel.js file:

...
 module: {
    rules: [
      {
        test: /\.js$/, // Transform all .js files required somewhere with Babel
        exclude: /node_modules\/(?!(jqwidgets-scripts\/jqwidgets-react)\/).*/,
        use: {
          loader: 'babel-loader',
          options: options.babelQuery,
         },
       },
...

With this configuration webpack compiles with no warnings and no errors like a charm, that is to say npm start. The big deal comes out to stage if I say npm run-script build, if so it complains with this:

Hash: 7b0feef0f8bbac0c9421                                                           
Version: webpack 4.27.1
Time: 41489ms
Built at: 2019-01-24 08:13:05
 251 assets
Entrypoint main = runtime~main.43b100be02c6a9dbfc5a.js vendor.4d1e1f30cd3a7c98cfd8.chunk.js main.5ce13ffd96704eeb6b0d.chunk.js

ERROR in ./node_modules/jqwidgets-scripts/jqwidgets-react/react_jqxgrid.js 1216:12
Module parse failed: Unexpected token (1216:12)
You may need an appropriate loader to handle this file type.
|     render() {
|         return (
>             <div id={this.state.id}>{this.props.value}{this.props.children}</div>
|         )
|     };
 @ ./app/modules/MyModule_1/index.js 43:0-70 954:37-44
 @ ./app/modules/MyModule_2/Loadable.js
 @ ./app/modules/MyModule_3/_nav.js
 @ ./app/modules/MyModule_4/index.js
 @ ./app/modules/MyModule_5/Loadable.js
 @ ./app/containers/MainLayout/_nav.js
 @ ./app/containers/MainLayout/index.js
 @ ./app/containers/MainLayout/Loadable.js
 @ ./app/containers/App/index.js
 @ ./app/app.js
 @ multi ./node_modules/react-app-polyfill/ie11.js ./app/app.js

I tried changing my webpack configuration as recommended in the official jqwidgets page (https://www.jqwidgets.com/community/topic/webpack-module-parse-failed/) like this:

 module: {
    rules: [
      {
        test: /\.js$/,
        exclude: '/node_modules',
        include: '/node_modules/jqwidgets-scripts/jqwidgets-react',
        use: {
          loader: 'babel-loader',
          options: options.babelQuery
        }
      },

Then the complain is this while running npm start:

webpack built be60767d46a99fe48e6b in 1353ms
✖ 「wdm」: 
ERROR in ./app/app.js 80:4
Module parse failed: Unexpected token (80:4)
You may need an appropriate loader to handle this file type.
| const render = messages => {
|   ReactDOM.render(
>     <AppContainer>
|       <Provider store={store}>
|         <LanguageProvider messages={messages}>
 @ multi ./node_modules/react-app-polyfill/ie11.js webpack-hot-middleware/client?reload=true ./app/app.js main[2]

Also, this is my babel.config.js

module.exports = {
  presets: [
    [
      '@babel/preset-env',
      {
        modules: false,
      },
    ],
    '@babel/preset-react',
  ],
  plugins: [
    'styled-components',
    '@babel/plugin-proposal-export-namespace-from',
    '@babel/plugin-proposal-class-properties',
    '@babel/plugin-syntax-dynamic-import',
  ],
  env: {
    production: {
      only: ['app'],
      plugins: [
        'lodash',
        'transform-react-remove-prop-types',
        '@babel/plugin-transform-react-inline-elements',
        '@babel/plugin-transform-react-constant-elements',
      ],
    },
    test: {
      plugins: [
        '@babel/plugin-transform-modules-commonjs',
        'dynamic-import-node',
      ],
    },
  },
};

My package.json:

{
  "name": "react-boilerplate",
  "version": "3.7.0",
  "description": "A highly scalable, offline-first foundation with the best DX and a focus on performance and best practices",
  "repository": {
    "type": "git",
    "url": "git://github.com/react-boilerplate/react-boilerplate.git"
  },
  "engines": {
    "npm": ">=5",
    "node": ">=8.10.0"
  },
  "author": "Max Stoiber",
  "license": "MIT",
  "scripts": {
    "analyze:clean": "rimraf stats.json",
    "preanalyze": "npm run analyze:clean",
    "analyze": "node ./internals/scripts/analyze.js",
    "extract-intl": "node ./internals/scripts/extract-intl.js",
    "npmcheckversion": "node ./internals/scripts/npmcheckversion.js",
    "preinstall": "npm run npmcheckversion",
    "prebuild": "npm run build:clean",
    "build": "cross-env NODE_ENV=production webpack --config internals/webpack/webpack.prod.babel.js --color -p --progress --hide-modules --display-optimization-bailout",
    "build:clean": "rimraf ./build",
    "start": "cross-env NODE_ENV=development env-cmd .env node server",
    "start:tunnel": "cross-env NODE_ENV=development ENABLE_TUNNEL=true node server",
    "start:production": "npm run test && npm run build && npm run start:prod",
    "start:prod": "cross-env NODE_ENV=production node server",
    "presetup": "npm i chalk shelljs",
    "setup": "node ./internals/scripts/setup.js",
    "clean": "shjs ./internals/scripts/clean.js",
    "clean:all": "npm run analyze:clean && npm run test:clean && npm run build:clean",
    "generate": "plop --plopfile internals/generators/index.js",
    "lint": "npm run lint:js",
    "lint:css": "stylelint './app/**/*.js'",
    "lint:eslint": "eslint --ignore-path .gitignore --ignore-pattern internals/scripts",
    "lint:eslint:fix": "eslint --ignore-path .gitignore --ignore-pattern internals/scripts --fix",
    "lint:js": "npm run lint:eslint -- . ",
    "lint:staged": "lint-staged",
    "pretest": "npm run test:clean && npm run lint",
    "test:clean": "rimraf ./coverage",
    "test": "cross-env NODE_ENV=test jest --coverage",
    "test:watch": "cross-env NODE_ENV=test jest --watchAll",
    "coveralls": "cat ./coverage/lcov.info | coveralls",
    "prettify": "prettier --write"
  },
  "lint-staged": {
    "*.js": [
      "npm run lint:eslint:fix",
      "git add --force"
    ],
    "*.json": [
      "prettier --write",
      "git add --force"
    ]
  },
  "pre-commit": "lint:staged",
  "resolutions": {
    "babel-core": "7.0.0-bridge.0"
  },
  "dependencies": {
    "@coreui/coreui": "^2.1.4",
    "@coreui/coreui-plugin-chartjs-custom-tooltips": "^1.2.0",
    "@coreui/icons": "^0.3.0",
    "@coreui/react": "^2.1.1",
    "ajv": "^6.6.1",
    "babel-polyfill": "^6.26.0",
    "block-ui": "^2.70.1",
    "bootstrap": "^4.1.3",
    "chalk": "^2.4.1",
    "chart.js": "^2.7.3",
    "classnames": "^2.2.6",
    "compression": "^1.7.3",
    "connected-react-router": "^4.5.0",
    "core-js": "^2.6.0",
    "env-cmd": "^8.0.2",
    "exports-loader": "^0.7.0",
    "flag-icon-css": "^3.2.1",
    "font-awesome": "^4.7.0",
    "fontfaceobserver": "2.0.13",
    "history": "^4.7.2",
    "hoist-non-react-statics": "3.0.1",
    "immutable": "^3.8.2",
    "intl": "^1.2.5",
    "invariant": "^2.2.4",
    "ip": "^1.1.5",
    "is-url-external": "^1.0.3",
    "isnumeric": "^0.3.3",
    "jquery": "^3.3.1",
    "jqwidgets-scripts": "^6.2.0",
    "loadable-components": "^2.2.3",
    "lodash": "^4.17.11",
    "minimist": "1.2.0",
    "moment": "^2.22.2",
    "numeral": "^2.0.6",
    "prop-types": "^15.6.2",
    "react": "^16.6.3",
    "react-chartjs-2": "^2.7.4",
    "react-dom": "^16.6.3",
    "react-helmet": "^5.2.0",
    "react-hot-loader": "^4.6.3",
    "react-intl": "^2.7.2",
    "react-loadable": "^5.5.0",
    "react-redux": "^5.1.1",
    "react-router-dom": "^4.3.1",
    "react-sizeme": "^2.5.2",
    "reactstrap": "^6.5.0",
    "redux": "^4.0.1",
    "redux-immutable": "^4.0.0",
    "redux-saga": "^0.16.2",
    "reselect": "4.0.0",
    "resize-sensor": "0.0.6",
    "sanitize.css": "4.1.0",
    "simple-line-icons": "^2.4.1",
    "styled-components": "^4.1.2",
    "warning": "^4.0.2"
  },
  "devDependencies": {
    "@babel/cli": "7.1.2",
    "@babel/core": "7.1.2",
    "@babel/plugin-proposal-class-properties": "7.1.0",
    "@babel/plugin-proposal-export-namespace-from": "^7.2.0",
    "@babel/plugin-syntax-dynamic-import": "7.0.0",
    "@babel/plugin-transform-modules-commonjs": "7.1.0",
    "@babel/plugin-transform-react-constant-elements": "7.0.0",
    "@babel/plugin-transform-react-inline-elements": "7.0.0",
    "@babel/polyfill": "^7.0.0",
    "@babel/preset-env": "7.1.0",
    "@babel/preset-react": "7.0.0",
    "@babel/register": "7.0.0",
    "add-asset-html-webpack-plugin": "3.1.1",
    "babel-core": "7.0.0-bridge.0",
    "babel-eslint": "10.0.1",
    "babel-loader": "8.0.4",
    "babel-plugin-dynamic-import-node": "2.2.0",
    "babel-plugin-lodash": "3.3.4",
    "babel-plugin-react-intl": "3.0.1",
    "babel-plugin-react-transform": "3.0.0",
    "babel-plugin-styled-components": "1.8.0",
    "babel-plugin-transform-react-remove-prop-types": "0.4.19",
    "circular-dependency-plugin": "5.0.2",
    "compare-versions": "3.4.0",
    "compression-webpack-plugin": "2.0.0",
    "copy-webpack-plugin": "^4.6.0",
    "coveralls": "3.0.2",
    "cross-env": "^5.2.0",
    "css-loader": "1.0.0",
    "enzyme": "3.7.0",
    "enzyme-adapter-react-16": "1.6.0",
    "enzyme-to-json": "3.3.4",
    "eslint": "5.7.0",
    "eslint-config-airbnb": "17.1.0",
    "eslint-config-airbnb-base": "13.1.0",
    "eslint-config-prettier": "3.1.0",
    "eslint-import-resolver-webpack": "0.10.1",
    "eslint-plugin-import": "^2.14.0",
    "eslint-plugin-jsx-a11y": "6.1.2",
    "eslint-plugin-prettier": "3.0.0",
    "eslint-plugin-react": "7.11.1",
    "eslint-plugin-redux-saga": "0.9.0",
    "express": "^4.16.4",
    "file-loader": "2.0.0",
    "html-loader": "0.5.5",
    "html-webpack-plugin": "3.2.0",
    "http-proxy-middleware": "^0.19.1",
    "image-webpack-loader": "^4.6.0",
    "imports-loader": "0.8.0",
    "jest-cli": "23.6.0",
    "jest-styled-components": "6.2.2",
    "lint-staged": "7.3.0",
    "ngrok": "3.1.0",
    "node-plop": "0.16.0",
    "node-sass": "^4.11.0",
    "null-loader": "0.1.1",
    "offline-plugin": "5.0.5",
    "optimize-css-assets-webpack-plugin": "^5.0.1",
    "plop": "2.1.0",
    "pre-commit": "1.2.2",
    "prettier": "1.14.3",
    "react-app-polyfill": "0.1.3",
    "react-test-renderer": "16.6.0",
    "rimraf": "2.6.2",
    "sass-loader": "^7.1.0",
    "shelljs": "^0.8.3",
    "style-loader": "0.23.1",
    "stylelint": "9.6.0",
    "stylelint-config-recommended": "2.1.0",
    "stylelint-config-styled-components": "0.1.1",
    "stylelint-processor-styled-components": "1.5.0",
    "svg-url-loader": "2.3.2",
    "terser-webpack-plugin": "1.1.0",
    "uglifyjs-webpack-plugin": "^2.0.1",
    "url-loader": "1.1.2",
    "webpack": "^4.27.1",
    "webpack-cli": "^3.1.2",
    "webpack-dev-middleware": "3.4.0",
    "webpack-hot-middleware": "2.24.3 ",
    "webpack-pwa-manifest": "3.7.1",
    "whatwg-fetch": "3.0.0"
  }
}

My webpack.prod.babel.js:

// Important modules this config uses
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const WebpackPwaManifest = require('webpack-pwa-manifest');
const OfflinePlugin = require('offline-plugin');
const { HashedModuleIdsPlugin } = require('webpack');
const TerserPlugin = require('terser-webpack-plugin');
const CompressionPlugin = require('compression-webpack-plugin');

module.exports = require('./webpack.base.babel')({
  mode: 'production',

  // In production, we skip all hot-reloading stuff
  entry: [
    require.resolve('react-app-polyfill/ie11'),
    path.join(process.cwd(), 'app/app.js'),
  ],

  // Utilize long-term caching by adding content hashes (not compilation hashes) to compiled assets
  output: {
    filename: '[name].[chunkhash].js',
    chunkFilename: '[name].[chunkhash].chunk.js',
  },

  optimization: {
    minimize: true,
    minimizer: [
      new TerserPlugin({
        terserOptions: {
          warnings: false,
          compress: {
            comparisons: false,
          },
          parse: {},
          mangle: true,
          output: {
            comments: false,
            ascii_only: true,
          },
        },
        parallel: true,
        cache: true,
        sourceMap: true,
      }),
    ],
    nodeEnv: 'production',
    sideEffects: true,
    concatenateModules: true,
    splitChunks: {
      chunks: 'all',
      minSize: 30000,
      minChunks: 1,
      maxAsyncRequests: 10,
      maxInitialRequests: 3,
      name: true,
      cacheGroups: {
        commons: {
          test: /[\\/]node_modules[\\/]/,
          name: 'vendor',
          chunks: 'all',
        },
        main: {
          chunks: 'all',
          minChunks: 2,
          reuseExistingChunk: true,
          enforce: true,
        },
      },
    },
    runtimeChunk: true,
  },

  plugins: [
    // Minify and optimize the index.html
    new HtmlWebpackPlugin({
      template: 'app/index.html',
      minify: {
        removeComments: true,
        collapseWhitespace: true,
        removeRedundantAttributes: true,
        useShortDoctype: true,
        removeEmptyAttributes: true,
        removeStyleLinkTypeAttributes: true,
        keepClosingSlash: true,
        minifyJS: true,
        minifyCSS: true,
        minifyURLs: true,
      },
      inject: true,
    }),

    // Put it in the end to capture all the HtmlWebpackPlugin's
    // assets manipulations and do leak its manipulations to HtmlWebpackPlugin
    new OfflinePlugin({
      relativePaths: false,
      publicPath: '/',
      appShell: '/',

      // No need to cache .htaccess. See http://mxs.is/googmp,
      // this is applied before any match in `caches` section
      excludes: ['.htaccess'],

      caches: {
        main: [':rest:'],

        // All chunks marked as `additional`, loaded after main section
        // and do not prevent SW to install. Change to `optional` if
        // do not want them to be preloaded at all (cached only when first loaded)
        additional: ['*.chunk.js'],
      },

      // Removes warning for about `additional` section usage
      safeToUseOptionalCaches: true,
    }),

    new CompressionPlugin({
      algorithm: 'gzip',
      test: /\.js$|\.css$|\.html$/,
      threshold: 10240,
      minRatio: 0.8,
    }),

    new WebpackPwaManifest({
      name: 'React Boilerplate',
      short_name: 'React BP',
      description: 'My React Boilerplate-based project!',
      background_color: '#fafafa',
      theme_color: '#b1624d',
      inject: true,
      ios: true,
      icons: [
        {
          src: path.resolve('app/images/icon-512x512.png'),
          sizes: [72, 96, 128, 144, 192, 384, 512],
        },
        {
          src: path.resolve('app/images/icon-512x512.png'),
          sizes: [120, 152, 167, 180],
          ios: true,
        },
      ],
    }),

    new HashedModuleIdsPlugin({
      hashFunction: 'sha256',
      hashDigest: 'hex',
      hashDigestLength: 20,
    }),
  ],

  performance: {
    assetFilter: assetFilename =>
      !/(\.map$)|(^(main\.|favicon\.))/.test(assetFilename),
  },
});

My webpack.dev.babel.js:

/**
 * DEVELOPMENT WEBPACK CONFIGURATION
 */

const path = require('path');
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CircularDependencyPlugin = require('circular-dependency-plugin');

module.exports = require('./webpack.base.babel')({
  mode: 'development',

  // Add hot reloading in development
  entry: [
    require.resolve('react-app-polyfill/ie11'),
    'webpack-hot-middleware/client?reload=true',
    path.join(process.cwd(), 'app/app.js'), // Start with js/app.js
  ],

  // Don't use hashes in dev mode for better performance
  output: {
    filename: '[name].js',
    chunkFilename: '[name].chunk.js',
  },

  optimization: {
    splitChunks: {
      chunks: 'all',
    },
  },

  // Add development plugins
  plugins: [
    new webpack.HotModuleReplacementPlugin(), // Tell webpack we want hot reloading
    new HtmlWebpackPlugin({
      inject: true, // Inject all files that are generated by webpack, e.g. bundle.js
      template: 'app/index.html',
    }),
    new CircularDependencyPlugin({
      exclude: /a\.js|node_modules/, // exclude node_modules
      failOnError: false, // show a warning when there is a circular dependency
    }),
  ],

  // Emit a source map for easier debugging
  // See https://webpack.js.org/configuration/devtool/#devtool
  devtool: 'eval-source-map',

  performance: {
    hints: false,
  },
});

And finally my webpack.base.babel.js:

/**
 * COMMON WEBPACK CONFIGURATION
 */

const path = require('path');
const webpack = require('webpack');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const config = require('../../server/config');

// Remove this line once the following warning goes away (it was meant for webpack loader authors not users):
// 'DeprecationWarning: loaderUtils.parseQuery() received a non-string value which can be problematic,
// see https://github.com/webpack/loader-utils/issues/56 parseQuery() will be replaced with getOptions()
// in the next major version of loader-utils.'
process.noDeprecation = true;

module.exports = options => ({
  mode: options.mode,
  entry: options.entry,
  output: Object.assign(
    {
      // Compile into js/build.js
      path: path.resolve(process.cwd(), 'build'),
      publicPath: '/',
    },
    options.output,
  ), // Merge with env dependent settings
  optimization: options.optimization,
  module: {
    rules: [
      {
        test: /\.js$|\.jsx$/, // Transform all .js files required somewhere with Babel
        exclude: /node_modules\/(?!(jqwidgets-scripts\/jqwidgets-react)\/).*/,
        use: {
          loader: 'babel-loader',
          options: options.babelQuery,
        },
      },
      // {
      //   test: /\.js$|\.jsx$/,
      //   exclude: '/node_modules',
      //   include: '/jqwidgets-scripts/jqwidgets-react',
      //   use: {
      //     loader: 'babel-loader',
      //     options: options.babelQuery
      //   }
      // },
      {
        // Preprocess our own .css files
        // This is the place to add your own loaders (e.g. sass/less etc.)
        // for a list of loaders, see https://webpack.js.org/loaders/#styling
        test: /\.scss$/,
        exclude: /node_modules/,
        use: ['style-loader', 'css-loader', 'sass-loader'],
      },
      {
        // Preprocess 3rd party .css files located in node_modules
        test: /\.css$/,
        include: /node_modules/,
        use: ['style-loader', 'css-loader'],
      },
      {
        test: /\.(eot|otf|ttf|woff|woff2)$/,
        use: 'file-loader',
      },
      {
        test: /\.svg$/,
        use: [
          {
            loader: 'svg-url-loader',
            options: {
              // Inline files smaller than 10 kB
              limit: 10 * 1024,
              noquotes: true,
            },
          },
        ],
      },
      {
        test: /\.(jpg|png|gif)$/,
        use: [
          {
            loader: 'url-loader',
            options: {
              // Inline files smaller than 10 kB
              limit: 10 * 1024,
            },
          },
          {
            loader: 'image-webpack-loader',
            options: {
              mozjpeg: {
                enabled: false,
                // NOTE: mozjpeg is disabled as it causes errors in some Linux environments
                // Try enabling it in your environment by switching the config to:
                // enabled: true,
                // progressive: true,
              },
              gifsicle: {
                interlaced: false,
              },
              optipng: {
                optimizationLevel: 7,
              },
              pngquant: {
                quality: '65-90',
                speed: 4,
              },
            },
          },
        ],
      },
      {
        test: /\.html$/,
        use: 'html-loader',
      },
      {
        test: /\.(mp4|webm)$/,
        use: {
          loader: 'url-loader',
          options: {
            limit: 10000,
          },
        },
      },
    ],
  },
  plugins: options.plugins.concat([
    // Always expose NODE_ENV to webpack, in order to use `process.env.NODE_ENV`
    // inside your code for any environment checks; Terser will automatically
    // drop any unreachable code.

    /* new webpack.ProvidePlugin({
      $: 'jquery',
      jQuery: 'jquery',
    }), */

    new webpack.DefinePlugin({
      'process.env': {
        NODE_ENV: JSON.stringify(config.env),
        HOST: JSON.stringify(config.server_host),
        PORT: JSON.stringify(config.server_port)
      },
    }),

    new webpack.NamedModulesPlugin(),
    new CopyWebpackPlugin([{ from: 'static' }]),
  ]),
  resolve: {
    modules: ['node_modules', 'app'],
    extensions: ['.js', '.jsx', '.react.js'],
    mainFields: ['browser', 'jsnext:main', 'main'],
    alias: {
      moment$: 'moment/moment.js',
    },
  },
  devtool: options.devtool,
  target: 'web', // Make web variables accessible to webpack, e.g. window
  performance: options.performance || {},
});

My question is, how to configure webpack in order to work properly in production mode?

Upvotes: 0

Views: 5877

Answers (0)

Related Questions