Skip to main content
  1. All Posts/

inspectpack

Tools TypeScript




An inspection tool for Webpack frontend JavaScript bundles.
inspectpack provides insight into your webpack-built JS bundles and detailed analysis of opportunites to reduce module sizes, unneeded duplicates, etc. It can be used as a webpack plugin during your compilations or as an offline CLI tool to report on your previous builds.
It is also the engine for the handy webpack-dashboard plugin.

Plugin

The DuplicatesPlugin identifies unnecessarily duplicated code in your webpack bundles with an actionable report to help you trim down wasted bytes.
To get started, install the plugin:

$ npm install --save-dev inspectpack # OR
$ yarn add --dev inspectpack

Then, add the plugin to your webpack.config.js file:

const { DuplicatesPlugin } = require("inspectpack/plugin");

module.exports = {
  // ...
  plugins: [
    // ...
    new DuplicatesPlugin({
      // Emit compilation warning or error? (Default: `false`)
      emitErrors: false,
      // Handle all messages with handler function (`(report: string)`)
      // Overrides `emitErrors` output.
      emitHandler: undefined,
      // List of packages that can be ignored. (Default: `[]`)
      // - If a string, then a prefix match of `{$name}/` for each module.
      // - If a regex, then `.test(pattern)` which means you should add slashes
      //   where appropriate.
      //
      // **Note**: Uses posix paths for all matching (e.g., on windows `/` not ``).
      ignoredPackages: undefined,
      // Display full duplicates information? (Default: `false`)
      verbose: false
    })
  ]
};

And from there you’ll get actionable reports!

A quick tour

Let’s see the plugin in action with a quick scenario that has various duplicates from a simple examples repository. (Side note: we’ve got lots of other interesting inspection scenarios in our test fixtures directory.)

The problem

In this scenario, we have an application that imports a simplified, fake version of lodash in (1) the root application, (2) transitively via a one dependency, and (3) again via a two dependency. Abstractly, the dependency tree (with semver ranges from pacakge.json) looks like:

- my-app:             # Resolved
  - lodash@^4.1.0     # 4.2.3
  - one@1.2.3:        # 1.2.3
    - lodash@^3.0.0   # 3.1.0
  - two@2.3.4:        # 2.3.4
    - lodash@^3.0.0   # 3.1.0

Using modern npm or yarn to install this project gives us the following on-disk node_modules folder (with version resolutions noted):

node_modules          # Resolved
  lodash              # 4.2.3
  one                 # 1.2.3
    node_modules
      lodash          # 3.1.0 (Cannot be collapsed)
  two                 # 2.3.4
    node_modules
      lodash          # 3.1.0 (Cannot be collapsed)

Looking to our resulting bundle we have the following duplicated code sources:

  • node_modules/lodash/index.js (4.2.3): This code is similar to the code for 3.1.0.
  • node_modules/one/node_modules/lodash/index.js, node_modules/two/node_modules/lodash/index.js (3.1.0): These two files are identical code sources. They are only included twice in the bundle because npm/yarn could not flatten the dependencies during installation.

So, we’ve got inefficient code that we discovered via a manual inspection. Wouldn’t it be nice to have a report that specifically highlighted problems like these with useful information?
… enter the DuplicatesPlugin.

Diagnosing duplicates

Simple report

With our plugin enabled in the standard configuration:

new DuplicatesPlugin()

we get a summary report of the duplicates upon running the webpack command:

WARNING in Duplicate Sources / Packages - Duplicates found! ⚠️

* Duplicates: Found 2 similar files across 3 code sources (both identical + similar)
  accounting for 703 bundled bytes.
* Packages: Found 1 packages with 2 resolved, 3 installed, and 3 depended versions.

## bundle.js
lodash (Found 2 resolved, 3 installed, 3 depended. Latest 4.2.3.)
  3.1.0 ~/one/~/lodash
    scenario-new-webpack-new-npm-unflattened@* -> one@1.2.3 -> lodash@^3.0.0
  3.1.0 ~/two/~/lodash
    scenario-new-webpack-new-npm-unflattened@* -> two@2.3.4 -> lodash@^3.0.0
  4.2.3 ~/lodash
    scenario-new-webpack-new-npm-unflattened@* -> lodash@^4.1.0

Breaking down this report, we get a webpack “warning” emitted by default with an initial summary
of the report.

  • The Duplicates summary looks at what is in the webpack bundle. It tells us there are 2 files that are not identical, but the same package file path (e.g 3.1.0 vs 4.2.3 for lodash/index.js) and that there are 3 code sources that end up in our final bundle (which includes two for 3.1.0). We also get a byte count for all the files at issue (703 bytes), which presumably could roughly be cut by 2/3 if we could collapse to just one file to do the same thing.
  • The Packages summary looks at what npm installed to node_modules. This is the other “view” into our problems.
    • Terminology: Let’s dig in to what things mean here.

      • Resolved: We have one package (lodash) that has 2 resolved versions (3.1.0 and 4.2.3). A “resolution” means that upon inspecting the dependency tree and what’s in a registry source, these specific versions “match”. The results may differ at a different point in time
      • Installed: These are actual packages installed to the local disk. In our case, we have three installs for 2 resolutions because we place an identical version twice.
      • Depended: These are the number of upstream packages that create a dependency from a unique path in the graph to a package. Put more concretely, in our case, three unique package.json files have an entry for lodash.

        • Note: This is a bit of a complicated assessment, since aside from the root package.json the rest of the dependency graph depends on what is resolved at the next level to give a dependent package.json and so on recusively.
    •   <li>
          <code>~</code> Note: The <code>~</code> shorthand represents the <code>node_modules</code> folder, which is a common abbreviation for webpack tools. E.g., <code>~/two/~/lodash</code> really means <code>node_modules/two/node_modules/lodash</code>.
        </li>
        <li>
          Note &#8211; Duplicates Only: Unlike the CLI <code>--action=versions</code> report, the <code>DuplicatesPlugin</code> only reports package version skews when there are <strong>actual duplicated files</strong> (either similar or identical). This means there may be multiple versions of a package with different files as part of your bundle. If you&#8217;d like to see these, use the CLI reporting tool!
        </li>
      </ul>
      

    After the plugin runs, we get a duplicates/package report for asset (e.g. outputted “bundle” files) with duplicate packages that produce duplicate sources in our bundles in the form of:

    ## {ASSET_NAME}
    {PACKAGE_NAME} (Found {NUM} resolved, {NUM} installed, {NUM} depended. Latest version {VERSION}.)
      {INSTALLED_PACKAGE_VERSION NO 1} {INSTALLED_PACKAGE_PATH NO 1}
        {DEPENDENCY PATH NO 1}
        {DEPENDENCY PATH NO 2}
        ...
      {INSTALLED_PACKAGE_VERSION NO 1} {INSTALLED_PACKAGE_PATH NO 2}
      ...
      {INSTALLED_PACKAGE_VERSION NO 2} {INSTALLED_PACKAGE_PATH NO 3}
      ...
    

    Looking to our specific report for lodash, we see that we have:

    • Two installed paths (~/one/~/lodash, ~/two/~/lodash) for one resolved version (3.1.0). These are part of the dependency tree because of two depended paths:
      • ROOT -> one@1.2.3 -> lodash@^3.0.0
      • ROOT -> two@2.3.4 -> lodash@^3.0.0
    • One installed path (~/lodash) for another resolved version (4.2.3). This is part of the dependency tree because of the one root depended path (ROOT -> lodash@^4.1.0).
    • Take these numbers together and you get our summary of 2 resolved, 3 installed, and 3 depended packages from our summary besides the package name.

    Thus for actionable information, there is a naive “quick out” that if we could switch the root dependency also to ^3.0.0 or something that resolves to lodash@3.1.0 all three packages would collapse to one using modern npm or yarn!

    Verbose report

    But, let’s say we want a little more information on the dependency tree besides the packages that end up on disk. For this, we can enable verbose output, which will include information on the bundled files that webpack is bringing in.

    new DuplicatesPlugin({
      verbose: true
    })

    Our resulting report is:

    WARNING in Duplicate Sources / Packages - Duplicates found! ⚠️
    
    * Duplicates: Found 2 similar files across 3 code sources (both identical + similar)
      accounting for 703 bundled bytes.
    * Packages: Found 1 packages with 2 resolved, 3 installed, and 3 depended versions.
    
    ## bundle.js
    lodash (Found 2 resolved, 3 installed, 3 depended. Latest 4.2.3.)
      3.1.0
        ~/one/~/lodash
          * Dependency graph
            scenario-new-webpack-new-npm-unflattened@* -> one@1.2.3 -> lodash@^3.0.0
          * Duplicated files in bundle.js
            lodash/index.js (I, 249)
    
        ~/two/~/lodash
          * Dependency graph
            scenario-new-webpack-new-npm-unflattened@* -> two@2.3.4 -> lodash@^3.0.0
          * Duplicated files in bundle.js
            lodash/index.js (I, 249)
    
      4.2.3
        ~/lodash
          * Dependency graph
            scenario-new-webpack-new-npm-unflattened@* -> lodash@^4.1.0
          * Duplicated files in bundle.js
            lodash/index.js (S, 205)
    

    We’ve got the same summary and organization as our previous report, but now we additionally have the bundled code sources with some additional information. Let’s look at our first one for 3.1.0 ~/one/~/lodash:

    lodash/index.js (I, 249)
    

    this takes the form of:

    {FILE_PATH} ({[I]DENTICAL or [S]IMILAR}, {NUMBER_OF_BYTES})
    

    which means the file index.js from the lodash package is identical to at least one other file in the bundle (the I designation) is 249 bytes in size.
    Looking at the last one for 4.2.3 ~/lodash:

    lodash/index.js (S, 205)
    

    we have the same file name as the others, but it is not identical to any other file in the bundle — instead it is only similar (the S designation) and is 205 bytes in size.
    So now, with this verbose report we can see:

    • The specific files in play that are duplicated sources in the bundle.
    • Whether they have any identical matches elsewhere in the bundle.
    • The…