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

speed-measure-webpack-plugin does not work with create-react-app #164

Open
danvln opened this issue Mar 20, 2021 · 8 comments
Open

speed-measure-webpack-plugin does not work with create-react-app #164

danvln opened this issue Mar 20, 2021 · 8 comments

Comments

@danvln
Copy link

danvln commented Mar 20, 2021

Thanks for building this plugin. It would be super useful to make it working with create-react-app, since this has been growing by a large factor, and becomes the de-facto.

Error: Below is the error that I receive in case this is not a known issue:

Users/danv/repos/protos/cra-build/node_modules/neo-async/async.js:16
    throw new Error('Callback was already called.');
    ^

Error: Callback was already called.
    at throwError (/Users/danv/repos/protos/cra-build/node_modules/neo-async/async.js:16:11)
    at /Users/danv/repos/protos/cra-build/node_modules/neo-async/async.js:2819:7
    at processTicksAndRejections (internal/process/task_queues.js:75:11)
npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! cra-build@0.1.0 start: `node scripts/start.js`
npm ERR! Exit status 1

Project is standard, CRA + Typescript:
const smp = new SpeedMeasurePlugin();
const webpackConfigWithMeasure = (webpackEnv) => smp.wrap(webpackConfig(webpackEnv));
module.exports = webpackConfigWithMeasure;

Repro:
Note, that the project build, but it crashes at recompile.

@floviolleau
Copy link

floviolleau commented Mar 26, 2021

Hi,

I faced to that same problem few weeks ago. I thought first time it was because I used craco.
I finally ejected yesterday, removed craco and still faced to the problem. After some trial and error steps, I discovered it was because I used this plugin.

dilanx/craco#259

For me the problem occured only when doing a yarn run start.

My dependencies' versions:

"@craco/craco": "^6.1.0",
"@types/history": "^4.7.0",
"@types/node": "^14.14.0",
"@types/react": "^17.0.0",
"@types/react-dom": "^17.0.0",
"@types/react-redux": "^5.0.0",
"@types/react-router-dom": "^5.1.0",
"copy-webpack-plugin": "^6.4.0",
"express": "^4.17.0",
"history": "^4.10.0",
"html-webpack-plugin": "^4.5.0",
"react": "^17.0.0",
"react-dev-utils": "^11.0.0",
"react-dom": "^17.0.0",
"react-intl": "^5.12.0",
"react-redux": "^5.1.0",
"react-router-dom": "^5.2.0",
"react-scripts": "^4.0.0",
"typescript": "^4.1.0",
"web-vitals": "^1.1.0"

Thanks!

@wctiger
Copy link

wctiger commented Jun 4, 2021

Seeing same error when recompiling with react-scripts ^3.4.4 and react-app-rewired.

@IhsanMujdeci
Copy link

I got it working with such a configuration

const smp = new SpeedMeasurePlugin();
module.exports ={
  webpack: {
    configure: webpackConfig => (smp.wrap(webpackConfig))
  }
};

@silverboyir
Copy link

I got it working with such a configuration

const smp = new SpeedMeasurePlugin();
module.exports ={
  webpack: {
    configure: webpackConfig => (smp.wrap(webpackConfig))
  }
};

how should I use this configuration?

@FeiFanLiang
Copy link

I got it working with such a configuration

const smp = new SpeedMeasurePlugin();
module.exports ={
  webpack: {
    configure: webpackConfig => (smp.wrap(webpackConfig))
  }
};

not apply to me,its not work

@HarveyZgit
Copy link

I got it working with such a configuration

const smp = new SpeedMeasurePlugin();
module.exports ={
  webpack: {
    configure: webpackConfig => (smp.wrap(webpackConfig))
  }
};

how should I use this configuration?

Use it in config-overrides.js.
doc: react-app-rewired/READEME.md

By default, the config-overrides.js file exports a single function to use when customising the webpack configuration for compiling your react app in development or production mode. It is possible to instead export an object from this file that contains up to three fields, each of which is a function.

@Ricola3D
Copy link

Ricola3D commented Nov 25, 2021

Issue still present, it does not work with CRA. I narrowed the issue and did a little work-around for now. It doesn't measure all, but it measures mosts of the build.

I added in my config-override.js, after the instanciation of smp:

/**
 * Change SpeedMeasurePlugin's wrap function so it is functional with CRA (and react-scripts
 * @param {*} config
 * @returns
 */
const wrapForCRA = function (config) {
  if (this.options.disable) return config
  if (Array.isArray(config)) return config.map(this.wrap)
  if (typeof config === 'function')
    return (...args) => this.wrap(config(...args))

  config.plugins = (config.plugins || []).map((plugin) => {
    const pluginName =
      Object.keys(this.options.pluginNames || {}).find(
        (pluginName) => plugin === this.options.pluginNames[pluginName]
      ) ||
      (plugin.constructor && plugin.constructor.name) ||
      '(unable to deduce plugin name)'

    // DOES NOT WORK WITH CRA: "URIError: Failed to decode param '/%PUBLIC_URL%/*** */.js'
    // if (plugin instanceof HtmlWebpackPlugin) {
    //   return new WrappedPlugin(plugin, pluginName, this)
    // }
    if (plugin instanceof InlineChunkHtmlPlugin) {
      return new WrappedPlugin(plugin, pluginName, this)
    }
    // DOES NOT WORK WITH CRA: "URIError: Failed to decode param '/%PUBLIC_URL%/*** */.js'
    // if (plugin instanceof InterpolateHtmlPlugin) {
    //   return new WrappedPlugin(plugin, pluginName, this)
    // }
    if (plugin instanceof ModuleNotFoundPlugin) {
      return new WrappedPlugin(plugin, pluginName, this)
    }
    if (plugin instanceof webpack.DefinePlugin) {
      return new WrappedPlugin(plugin, pluginName, this)
    }
    if (plugin instanceof webpack.HotModuleReplacementPlugin) {
      return new WrappedPlugin(plugin, pluginName, this)
    }
    if (plugin instanceof CaseSensitivePathsPlugin) {
      return new WrappedPlugin(plugin, pluginName, this)
    }
    if (plugin instanceof WatchMissingNodeModulesPlugin) {
      return new WrappedPlugin(plugin, pluginName, this)
    }
    if (plugin instanceof MiniCssExtractPlugin) {
      return new WrappedPlugin(plugin, pluginName, this)
    }
    if (plugin instanceof ManifestPlugin) {
      return new WrappedPlugin(plugin, pluginName, this)
    }
    if (plugin instanceof webpack.IgnorePlugin) {
      return new WrappedPlugin(plugin, pluginName, this)
    }
    if (plugin instanceof WorkboxWebpackPlugin.InjectManifest) {
      return new WrappedPlugin(plugin, pluginName, this)
    }
    if (plugin instanceof ForkTsCheckerWebpackPlugin) {
      return new WrappedPlugin(plugin, pluginName, this)
    }
    if (plugin instanceof ESLintPlugin) {
      return new WrappedPlugin(plugin, pluginName, this)
    }
    if (plugin instanceof UglifyJsPlugin) {
      return new WrappedPlugin(plugin, pluginName, this)
    }

    return plugin
  })

  if (config.optimization && config.optimization.minimizer) {
    config.optimization.minimizer = config.optimization.minimizer.map(
      (plugin) => {
        return new WrappedPlugin(plugin, plugin.constructor.name, this)
      }
    )
  }

  // DOES NOT WORK WITH CRA: "./src/index.css Module build failed: Error: Final loader (./node_modules/speed-measure-webpack-plugin/loader.js) didn't return a Buffer or String"
  // if (config.module && this.options.granularLoaderData) {
  //   config.module = prependLoader(config.module)
  // }

  if (!this.smpPluginAdded) {
    config.plugins = config.plugins.concat(this)
    this.smpPluginAdded = true
  }

  return config
}

smp.wrap = wrapForCRA.bind(smp)

Also you can also integrate "customize-cra" to have a progress bar and a few more details. Here is the full-file. DO NOT FORGET TO INSTALL THE PLUGINS to have it work:

/* eslint-disable no-param-reassign */
/* eslint-disable func-names */
/* eslint-disable import/no-extraneous-dependencies */
/**
 * Recommanded way to overide create-react-app build config without ejecting.
 * We use react-app-rewired to plug the config-overrides.js and export a new webpack config
 * We use customize-cra methods to provide shortcut methods.
 * See doc here: https://v4.mui.com/guides/minimizing-bundle-size/
 */
const webpack = require('webpack')
const {
  addBundleVisualizer,
  addWebpackPlugin,
  disableEsLint,
  override,
  overrideDevServer,
  useBabelRc,
} = require('customize-cra')

const ProgressBarPlugin = require('progress-bar-webpack-plugin')
const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
const SpeedMeasurePlugin = require('speed-measure-webpack-plugin')
const { WrappedPlugin } = require('speed-measure-webpack-plugin/WrappedPlugin')
const { prependLoader } = require('speed-measure-webpack-plugin/utils')

const PnpWebpackPlugin = require('pnp-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin')
const InlineChunkHtmlPlugin = require('react-dev-utils/InlineChunkHtmlPlugin')
const TerserPlugin = require('terser-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin')
const ManifestPlugin = require('webpack-manifest-plugin')
const InterpolateHtmlPlugin = require('react-dev-utils/InterpolateHtmlPlugin')
const WorkboxWebpackPlugin = require('workbox-webpack-plugin')
const WatchMissingNodeModulesPlugin = require('react-dev-utils/WatchMissingNodeModulesPlugin')
const ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin')
const ESLintPlugin = require('eslint-webpack-plugin')
const ModuleNotFoundPlugin = require('react-dev-utils/ModuleNotFoundPlugin')
const ForkTsCheckerWebpackPlugin = require('react-dev-utils/ForkTsCheckerWebpackPlugin')

/**
 * Measure build speed
 * Documentation: https://github.com/stephencookdev/speed-measure-webpack-plugin
 */
const smp = new SpeedMeasurePlugin({
  compareLoadersBuild: {
    filePath: './buildInfo.json',
  },
  disable: !process.env.MEASURE,
  granularLoaderData: true,
  loaderTopFiles: 10,
  outputFormat: 'humanVerbose',
})

/**
 * Change SpeedMeasurePlugin's wrap function so it is functional with CRA (and react-scripts
 * @param {*} config
 * @returns
 */
const wrapForCRA = function (config) {
  if (this.options.disable) return config
  if (Array.isArray(config)) return config.map(this.wrap)
  if (typeof config === 'function')
    return (...args) => this.wrap(config(...args))

  config.plugins = (config.plugins || []).map((plugin) => {
    const pluginName =
      Object.keys(this.options.pluginNames || {}).find(
        (pluginName) => plugin === this.options.pluginNames[pluginName]
      ) ||
      (plugin.constructor && plugin.constructor.name) ||
      '(unable to deduce plugin name)'

    // DOES NOT WORK WITH CRA: "URIError: Failed to decode param '/%PUBLIC_URL%/*** */.js'
    // if (plugin instanceof HtmlWebpackPlugin) {
    //   return new WrappedPlugin(plugin, pluginName, this)
    // }
    if (plugin instanceof InlineChunkHtmlPlugin) {
      return new WrappedPlugin(plugin, pluginName, this)
    }
    // DOES NOT WORK WITH CRA: "URIError: Failed to decode param '/%PUBLIC_URL%/*** */.js'
    // if (plugin instanceof InterpolateHtmlPlugin) {
    //   return new WrappedPlugin(plugin, pluginName, this)
    // }
    if (plugin instanceof ModuleNotFoundPlugin) {
      return new WrappedPlugin(plugin, pluginName, this)
    }
    if (plugin instanceof webpack.DefinePlugin) {
      return new WrappedPlugin(plugin, pluginName, this)
    }
    if (plugin instanceof webpack.HotModuleReplacementPlugin) {
      return new WrappedPlugin(plugin, pluginName, this)
    }
    if (plugin instanceof CaseSensitivePathsPlugin) {
      return new WrappedPlugin(plugin, pluginName, this)
    }
    if (plugin instanceof WatchMissingNodeModulesPlugin) {
      return new WrappedPlugin(plugin, pluginName, this)
    }
    if (plugin instanceof MiniCssExtractPlugin) {
      return new WrappedPlugin(plugin, pluginName, this)
    }
    if (plugin instanceof ManifestPlugin) {
      return new WrappedPlugin(plugin, pluginName, this)
    }
    if (plugin instanceof webpack.IgnorePlugin) {
      return new WrappedPlugin(plugin, pluginName, this)
    }
    if (plugin instanceof WorkboxWebpackPlugin.InjectManifest) {
      return new WrappedPlugin(plugin, pluginName, this)
    }
    if (plugin instanceof ForkTsCheckerWebpackPlugin) {
      return new WrappedPlugin(plugin, pluginName, this)
    }
    if (plugin instanceof ESLintPlugin) {
      return new WrappedPlugin(plugin, pluginName, this)
    }
    if (plugin instanceof UglifyJsPlugin) {
      return new WrappedPlugin(plugin, pluginName, this)
    }

    return plugin
  })

  if (config.optimization && config.optimization.minimizer) {
    config.optimization.minimizer = config.optimization.minimizer.map(
      (plugin) => {
        return new WrappedPlugin(plugin, plugin.constructor.name, this)
      }
    )
  }

  // DOES NOT WORK WITH CRA: "./src/index.css Module build failed: Error: Final loader (./node_modules/speed-measure-webpack-plugin/loader.js) didn't return a Buffer or String"
  // if (config.module && this.options.granularLoaderData) {
  //   config.module = prependLoader(config.module)
  // }

  if (!this.smpPluginAdded) {
    config.plugins = config.plugins.concat(this)
    this.smpPluginAdded = true
  }

  return config
}

smp.wrap = wrapForCRA.bind(smp)

/**
 * Exporting the new config for react-app-rewired
 */
module.exports = {
  // The function to use to create a webpack dev server configuration when running the development
  // server with 'npm run start' or 'yarn start'.
  // Example: set the dev server to use a specific certificate in https.
  devServer: overrideDevServer(),
  // The Jest config to use when running your jest tests - note that the normal rewires do not
  // work here.
  jest(config) {
    // ...add your jest config customisation...
    return config
  },
  // The paths config to use when compiling your react app for development or production.
  paths(paths /* , env */) {
    // ...add your paths config
    return paths
  },
  webpack: smp.wrap(
    override(
      // Use .babelrc file. See doc here: https://v4.mui.com/guides/minimizing-bundle-size/
      // eslint-disable-next-line react-hooks/rules-of-hooks
      useBabelRc(),
      // disable eslint in webpack
      disableEsLint(),
      // add webpack bundle visualizer if BUNDLE_VISUALIZE flag is enabled
      process.env.BUNDLE_VISUALIZE === 1 && addBundleVisualizer(),
      // Add a build progress bar
      addWebpackPlugin(new ProgressBarPlugin()),
      // Add uglify plugin
      addWebpackPlugin(
        new UglifyJsPlugin({
          uglifyOptions: {
            compress: {
              drop_console: true,
              drop_debugger: true,
            },
            warning: false,
          },
        })
      )
    )
  ),
}

@nektro
Copy link

nektro commented Feb 3, 2022

const smp = new SpeedMeasurePlugin();
module.exports ={
  webpack: {
    configure: webpackConfig => (smp.wrap(webpackConfig))
  }
};

this route is actually what caused the issue for me. what got it working for me was:

const smp = new SpeedMeasurePlugin();
module.exports = {
  webpack: smp.wrap({
    plugins: [
      //...
    ],
  }),
};

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

9 participants