Giter Site home page Giter Site logo

aackerman / circular-dependency-plugin Goto Github PK

View Code? Open in Web Editor NEW
904.0 6.0 44.0 845 KB

Detect circular dependencies in modules compiled with Webpack

Home Page: https://www.npmjs.com/package/circular-dependency-plugin

License: ISC License

JavaScript 99.52% TypeScript 0.48%
javascript webpack

circular-dependency-plugin's Introduction

Circular Dependency Plugin

Detect modules with circular dependencies when bundling with webpack.

Circular dependencies are often a necessity in complex software, the presence of a circular dependency doesn't always imply a bug, but in the case where you believe a bug exists, this module may help find it.

Webpack Versions

The latest major version of this plugin 5, supports webpack 4.0.1 and greater as a peer dependency. Major version 4 of this plugin and below are intended to support webpack 3.x.x and below as a peer dependency.

Basic Usage

// webpack.config.js
const CircularDependencyPlugin = require('circular-dependency-plugin')

module.exports = {
  entry: "./src/index",
  plugins: [
    new CircularDependencyPlugin({
      // exclude detection of files based on a RegExp
      exclude: /a\.js|node_modules/,
      // include specific files based on a RegExp
      include: /dir/,
      // add errors to webpack instead of warnings
      failOnError: true,
      // allow import cycles that include an asyncronous import,
      // e.g. via import(/* webpackMode: "weak" */ './file.js')
      allowAsyncCycles: false,
      // set the current working directory for displaying module paths
      cwd: process.cwd(),
    })
  ]
}

Advanced Usage

// webpack.config.js
const CircularDependencyPlugin = require('circular-dependency-plugin')

module.exports = {
  entry: "./src/index",
  plugins: [
    new CircularDependencyPlugin({
      // `onStart` is called before the cycle detection starts
      onStart({ compilation }) {
        console.log('start detecting webpack modules cycles');
      },
      // `onDetected` is called for each module that is cyclical
      onDetected({ module: webpackModuleRecord, paths, compilation }) {
        // `paths` will be an Array of the relative module paths that make up the cycle
        // `module` will be the module record generated by webpack that caused the cycle
        compilation.errors.push(new Error(paths.join(' -> ')))
      },
      // `onEnd` is called before the cycle detection ends
      onEnd({ compilation }) {
        console.log('end detecting webpack modules cycles');
      },
    })
  ]
}

If you have some number of cycles and want to fail if any new ones are introduced, you can use the life cycle methods to count and fail when the count is exceeded. (Note if you care about detecting a cycle being replaced by another, this won't catch that.)

// webpack.config.js
const CircularDependencyPlugin = require('circular-dependency-plugin')

const MAX_CYCLES = 5;
let numCyclesDetected = 0;

module.exports = {
  entry: "./src/index",
  plugins: [
    new CircularDependencyPlugin({
      onStart({ compilation }) {
        numCyclesDetected = 0;
      },
      onDetected({ module: webpackModuleRecord, paths, compilation }) {
        numCyclesDetected++;
        compilation.warnings.push(new Error(paths.join(' -> ')))
      },
      onEnd({ compilation }) {
        if (numCyclesDetected > MAX_CYCLES) {
          compilation.errors.push(new Error(
            `Detected ${numCyclesDetected} cycles which exceeds configured limit of ${MAX_CYCLES}`
          ));
        }
      },
    })
  ]
}

Maintenance

This module is maintained despite few changes being necessary, please open issues if you find any bugs relating to integration with webpack core.

circular-dependency-plugin's People

Contributors

aackerman avatar berickson1 avatar dependabot[bot] avatar gpoitch avatar hedgepigdaniel avatar jspizziri avatar pavelvlasov avatar rqrqrqrq avatar sfrieson avatar spaced avatar turadg avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

circular-dependency-plugin's Issues

Webpack 4 deprecation warning

Webpack 4 prints following deprecation warning when CircularDependencyPlugin is enabled.

(node:23969) DeprecationWarning: Tapable.plugin is deprecated. Use new API on `.hooks` instead

Improve readme.md

Is it possible to improve the readme.md to be more explicit as to how to implement this plugin ?
As in provinding a few example on how to download it and use it with common project (npm/node, etc)

ReferenceError: CircularDependencyPlugin is not defined

Hi!
I cannot get your plugin working :(
I installed plugin via

npm install --save-dev circular-dependency-plugin

My config:

var webpack = require('webpack');

module.exports = {
    entry: "./src/main.js",
    output : {
        path: "./dist.env.prod",
        filename: "bundle.js"
    },
    module: {
        loaders: [{
            test: /\.js$/,
            exclude: /node_modules/,
            loaders: ['babel-loader']
        }]
    },
    plugins: [
        new webpack.DefinePlugin({
            'process.env':{
                'NODE_ENV': JSON.stringify('production')
            }
        }),
        new CircularDependencyPlugin({
            // exclude detection of files based on a RegExp
            // exclude: /a\.js/,
            // add errors to webpack instead of warnings
            failOnError: true
        })
    ]
};

Trying to pack it, i got following message:

E:\1__projects\webstorm\react.js\beam.server.ui\webpack.config.env.prod.js:22
        new CircularDependencyPlugin({
            ^

ReferenceError: CircularDependencyPlugin is not defined
    at Object.<anonymous> (E:\1__projects\webstorm\react.js\beam.server.ui\webpack.config.env.prod.js:22:13)
    at Module._compile (module.js:413:34)
    at Object.Module._extensions..js (module.js:422:10)
    at Module.load (module.js:357:32)
    at Function.Module._load (module.js:314:12)
    at Module.require (module.js:367:17)
    at require (internal/module.js:16:19)
    at module.exports (E:\2__LIB\NPM_global\npm\node_modules\webpack\bin\convert-argv.js:30:13)
    at Object.<anonymous> (E:\2__LIB\NPM_global\npm\node_modules\webpack\bin\webpack.js:56:40)
    at Module._compile (module.js:413:34)

I'm not proficient in JS, so I could make a mistake somewhere in configuration, but I don't see it. Please, correct me, or fix this issue.
Thanks.

webpack@5 support

Hi. Thanks for your work on this. What I come with is a question that sooner or later surely would come anyway and that is whether you are planing a webpack@5 checkup.

I'm now helping with polishing out kinks in it and of issues that came up in my project are many new circular dependency errors — some of which don't make any sense like one file depend on itself.

TypeError: Cannot read property 'compilation' of undefined

I'm trying to integrate circular-dependency-plugin to see the circular imports in storybook's webpack.config.js. Here is my webpack config

webpakc.config.js

// webpack.config.js
const CircularDependencyPlugin = require('circular-dependency-plugin')
const genDefaultConfig = require('@storybook/react-native/dist/server/config/defaults/webpack.config.js')

module.exports = (baseConfig, env) => {
  const defaultConfig = genDefaultConfig(baseConfig, env)
  defaultConfig.plugins.push(
    new CircularDependencyPlugin({
      // exclude detection of files based on a RegExp
      exclude: /node_modules/,
      // add errors to webpack instead of warnings
      failOnError: true
    })
  )
  return defaultConfig
}

After running npm run storybook, we are getting this error. TypeError: Cannot read property 'compilation' of undefined.

storybook version: 3.3.15.

Thanks!

Detection with the ModuleConcatenationPlugin

When using the webpack.optimize.ModuleConcatenationPlugin introduced in webpack 3, it appears that this plugin in unable to detect a lot of circular dependencies.

When removing ModuleConcatenationPlugin, it outputs more warnings.

Doesn't seem to be working in create-react-app based build

Hi,

I've added this to my create-react-app based build that I ejected and have only adding this plugin too but I just manually fixed a circular dependency and the tool never warned me about it.

Here's my config

'use strict';

const autoprefixer = require('autoprefixer');
const path = require('path');
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin');
const InterpolateHtmlPlugin = require('react-dev-utils/InterpolateHtmlPlugin');
const WatchMissingNodeModulesPlugin = require('react-dev-utils/WatchMissingNodeModulesPlugin');
const eslintFormatter = require('react-dev-utils/eslintFormatter');
const ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin');
const getClientEnvironment = require('./env');
const paths = require('./paths');
const CircularDependencyPlugin = require('circular-dependency-plugin');

// Webpack uses `publicPath` to determine where the app is being served from.
// In development, we always serve from the root. This makes config easier.
const publicPath = '/';
// `publicUrl` is just like `publicPath`, but we will provide it to our app
// as %PUBLIC_URL% in `index.html` and `process.env.PUBLIC_URL` in JavaScript.
// Omit trailing slash as %PUBLIC_PATH%/xyz looks better than %PUBLIC_PATH%xyz.
const publicUrl = '';
// Get environment variables to inject into our app.
const env = getClientEnvironment(publicUrl);

// This is the development configuration.
// It is focused on developer experience and fast rebuilds.
// The production configuration is different and lives in a separate file.
module.exports = {
  // You may want 'eval' instead if you prefer to see the compiled output in DevTools.
  // See the discussion in https://github.com/facebookincubator/create-react-app/issues/343.
  devtool: 'cheap-module-source-map',
  // These are the "entry points" to our application.
  // This means they will be the "root" imports that are included in JS bundle.
  // The first two entry points enable "hot" CSS and auto-refreshes for JS.
  entry: [
    // We ship a few polyfills by default:
    require.resolve('./polyfills'),
    // Include an alternative client for WebpackDevServer. A client's job is to
    // connect to WebpackDevServer by a socket and get notified about changes.
    // When you save a file, the client will either apply hot updates (in case
    // of CSS changes), or refresh the page (in case of JS changes). When you
    // make a syntax error, this client will display a syntax error overlay.
    // Note: instead of the default WebpackDevServer client, we use a custom one
    // to bring better experience for Create React App users. You can replace
    // the line below with these two lines if you prefer the stock client:
    // require.resolve('webpack-dev-server/client') + '?/',
    // require.resolve('webpack/hot/dev-server'),
    require.resolve('react-dev-utils/webpackHotDevClient'),
    // Finally, this is your app's code:
    paths.appIndexJs,
    // We include the app code last so that if there is a runtime error during
    // initialization, it doesn't blow up the WebpackDevServer client, and
    // changing JS code would still trigger a refresh.
  ],
  output: {
    // Add /* filename */ comments to generated require()s in the output.
    pathinfo: true,
    // This does not produce a real file. It's just the virtual path that is
    // served by WebpackDevServer in development. This is the JS bundle
    // containing code from all our entry points, and the Webpack runtime.
    filename: 'static/js/bundle.js',
    // There are also additional JS chunk files if you use code splitting.
    chunkFilename: 'static/js/[name].chunk.js',
    // This is the URL that app is served from. We use "/" in development.
    publicPath: publicPath,
    // Point sourcemap entries to original disk location (format as URL on Windows)
    devtoolModuleFilenameTemplate: info =>
      path.resolve(info.absoluteResourcePath).replace(/\\/g, '/'),
  },
  resolve: {
    // This allows you to set a fallback for where Webpack should look for modules.
    // We placed these paths second because we want `node_modules` to "win"
    // if there are any conflicts. This matches Node resolution mechanism.
    // https://github.com/facebookincubator/create-react-app/issues/253
    modules: ['node_modules', paths.appNodeModules].concat(
      // It is guaranteed to exist because we tweak it in `env.js`
      process.env.NODE_PATH.split(path.delimiter).filter(Boolean)
    ),
    // These are the reasonable defaults supported by the Node ecosystem.
    // We also include JSX as a common component filename extension to support
    // some tools, although we do not recommend using it, see:
    // https://github.com/facebookincubator/create-react-app/issues/290
    // `web` extension prefixes have been added for better support
    // for React Native Web.
    extensions: ['.web.js', '.mjs', '.js', '.json', '.web.jsx', '.jsx'],
    alias: {
      
      // Support React Native Web
      // https://www.smashingmagazine.com/2016/08/a-glimpse-into-the-future-with-react-native-for-web/
      'react-native': 'react-native-web',
    },
    plugins: [
      // Prevents users from importing files from outside of src/ (or node_modules/).
      // This often causes confusion because we only process files within src/ with babel.
      // To fix this, we prevent you from importing files out of src/ -- if you'd like to,
      // please link the files into your node_modules/ and let module-resolution kick in.
      // Make sure your source files are compiled, as they will not be processed in any way.
      new ModuleScopePlugin(paths.appSrc, [paths.appPackageJson]),
      new CircularDependencyPlugin({
        // exclude detection of files based on a RegExp
        exclude: /node_modules/,
        // add errors to webpack instead of warnings
        failOnError: true
      }),
    ],
  },
  module: {
    strictExportPresence: true,
    rules: [
      // TODO: Disable require.ensure as it's not a standard language feature.
      // We are waiting for https://github.com/facebookincubator/create-react-app/issues/2176.
      // { parser: { requireEnsure: false } },

      // First, run the linter.
      // It's important to do this before Babel processes the JS.
      {
        test: /\.(js|jsx|mjs)$/,
        enforce: 'pre',
        use: [
          {
            options: {
              formatter: eslintFormatter,
              eslintPath: require.resolve('eslint'),
              
            },
            loader: require.resolve('eslint-loader'),
          },
        ],
        include: paths.appSrc,
      },
      {
        // "oneOf" will traverse all following loaders until one will
        // match the requirements. When no loader matches it will fall
        // back to the "file" loader at the end of the loader list.
        oneOf: [
          // "url" loader works like "file" loader except that it embeds assets
          // smaller than specified limit in bytes as data URLs to avoid requests.
          // A missing `test` is equivalent to a match.
          {
            test: [/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/],
            loader: require.resolve('url-loader'),
            options: {
              limit: 10000,
              name: 'static/media/[name].[hash:8].[ext]',
            },
          },
          // Process JS with Babel.
          {
            test: /\.(js|jsx|mjs)$/,
            include: paths.appSrc,
            loader: require.resolve('babel-loader'),
            options: {
              
              // This is a feature of `babel-loader` for webpack (not Babel itself).
              // It enables caching results in ./node_modules/.cache/babel-loader/
              // directory for faster rebuilds.
              cacheDirectory: true,
            },
          },
          // "postcss" loader applies autoprefixer to our CSS.
          // "css" loader resolves paths in CSS and adds assets as dependencies.
          // "style" loader turns CSS into JS modules that inject <style> tags.
          // In production, we use a plugin to extract that CSS to a file, but
          // in development "style" loader enables hot editing of CSS.
          {
            test: /\.css$/,
            use: [
              require.resolve('style-loader'),
              {
                loader: require.resolve('css-loader'),
                options: {
                  importLoaders: 1,
                },
              },
              {
                loader: require.resolve('postcss-loader'),
                options: {
                  // Necessary for external CSS imports to work
                  // https://github.com/facebookincubator/create-react-app/issues/2677
                  ident: 'postcss',
                  plugins: () => [
                    require('postcss-flexbugs-fixes'),
                    autoprefixer({
                      browsers: [
                        '>1%',
                        'last 4 versions',
                        'Firefox ESR',
                        'not ie < 9', // React doesn't support IE8 anyway
                      ],
                      flexbox: 'no-2009',
                    }),
                  ],
                },
              },
            ],
          },
          // "file" loader makes sure those assets get served by WebpackDevServer.
          // When you `import` an asset, you get its (virtual) filename.
          // In production, they would get copied to the `build` folder.
          // This loader doesn't use a "test" so it will catch all modules
          // that fall through the other loaders.
          {
            // Exclude `js` files to keep "css" loader working as it injects
            // it's runtime that would otherwise processed through "file" loader.
            // Also exclude `html` and `json` extensions so they get processed
            // by webpacks internal loaders.
            exclude: [/\.js$/, /\.html$/, /\.json$/],
            loader: require.resolve('file-loader'),
            options: {
              name: 'static/media/[name].[hash:8].[ext]',
            },
          },
        ],
      },
      // ** STOP ** Are you adding a new loader?
      // Make sure to add the new loader(s) before the "file" loader.
    ],
  },
  plugins: [
    // Makes some environment variables available in index.html.
    // The public URL is available as %PUBLIC_URL% in index.html, e.g.:
    // <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
    // In development, this will be an empty string.
    new InterpolateHtmlPlugin(env.raw),
    // Generates an `index.html` file with the <script> injected.
    new HtmlWebpackPlugin({
      inject: true,
      template: paths.appHtml,
    }),
    // Add module names to factory functions so they appear in browser profiler.
    new webpack.NamedModulesPlugin(),
    // Makes some environment variables available to the JS code, for example:
    // if (process.env.NODE_ENV === 'development') { ... }. See `./env.js`.
    new webpack.DefinePlugin(env.stringified),
    // This is necessary to emit hot updates (currently CSS only):
    new webpack.HotModuleReplacementPlugin(),
    // Watcher doesn't work well if you mistype casing in a path so we use
    // a plugin that prints an error when you attempt to do this.
    // See https://github.com/facebookincubator/create-react-app/issues/240
    new CaseSensitivePathsPlugin(),
    // If you require a missing module and then `npm install` it, you still have
    // to restart the development server for Webpack to discover it. This plugin
    // makes the discovery automatic so you don't have to restart.
    // See https://github.com/facebookincubator/create-react-app/issues/186
    new WatchMissingNodeModulesPlugin(paths.appNodeModules),
    // Moment.js is an extremely popular library that bundles large locale files
    // by default due to how Webpack interprets its code. This is a practical
    // solution that requires the user to opt into importing specific locales.
    // https://github.com/jmblog/how-to-optimize-momentjs-with-webpack
    // You can remove this if you don't use Moment.js:
    new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
  ],
  // Some libraries import Node modules but don't use them in the browser.
  // Tell Webpack to provide empty mocks for them so importing them works.
  node: {
    dgram: 'empty',
    fs: 'empty',
    net: 'empty',
    tls: 'empty',
    child_process: 'empty',
  },
  // Turn off performance hints during development because we don't do any
  // splitting or minification in interest of speed. These warnings become
  // cumbersome.
  performance: {
    hints: false,
  },
};

Is there anything I can do to debug this or to run it in isolation? I've used it on other projects and I know it works. Maybe the create-react-app build is swallowing the errors.

TypeError: Path must be a string. Received undefined

I see a flaw in the code.
On top, it uses "let cwd = process.cwd()".
This will not work if I run webpack in different directory. Many people nowadays have separate Server and Client folders. And the server runs webpack on client folder when building.
At that point, cwd=process.cwd() will be a server path, while cwd is supposed to be Client path.
Please at least give an option for a user to customize cwd.

Named exports and imports circulars

Should this be considered as circular dependency?

file a.ts

import { anotherUtil } from "b.ts"

export const util = () => true

export const otherUtil = () => false

export const extraUtil = () => anotherUtil()

file b.ts

import { util } from "a.ts"

const function = () => util()

export const anotherUtil = () => true

only the exported named exports are attached to the code with tree shaking enabled, not whole file right?

Clarification on `exclude` option

Wonderful plugin, just wanted some documentation/clarification on the exclude option. I had to read the source to figure out passing onDetected would still fire for node_modules even with exclude: /node_modules/ set. i.e. I just copy and pasted the example code and nothing was getting excluded. Alternatively, just move the exclude test above the onDetected handler

Version 4.2.0

Thanks!

Does not work with Webpack 5

74% module optimization CircularDependencyPlugin(node:58408) UnhandledPromiseRejectionWarning: Error: module property was removed from Dependency (use compilation.moduleGraph.getModule(dependency) instead)
    at HarmonyCompatibilityDependency.get (/Users/xxx/node_modules/webpack/lib/Dependency.js:158:9)
    at CircularDependencyPlugin.isCyclic (/Users/xxx/node_modules/circular-dependency-plugin/index.js:82:34)

Fast builds during development

CircularDependencyPlugin is currently too expensive to run during development, especially during hot reloads where it can take more than ~50% of the total build time.
Reference: #62 / angular/angular-cli#19794

optimizeModules hook is currently used to support setups with ModuleConcatenationPlugin enabled. In most setups though, ModuleConcatenationPlugin is disabled during development which means we can move the processing at the end of the compilation process, and achieve super-fast hot reloads.

Here's the required change: https://github.com/niksrc/circular-dependency-plugin/blob/4aa03d124943ccae00b7624696318c155acc5709/index.js#L17-L33 to make it work.

This is similar to the way the plugin used to work before ModuleConcatenationPlugin support was added: cce7601

I recommend not using this in dev mode where you want/need fast compilation, 6 seconds is brutal enough.

I have no strong intentions to make this plugin faster because I recommend it to be used to trigge errors in CI and if the duration is 12 seconds in CI, that's not important enough to micro-optimize.

Originally posted by @aackerman in #62 (comment)

The tradeoff of leaving this enabled in dev mode still stands as devs would be able to spot these issues immediately and avoid dumping hours of work later on (when they open a PR).

@aackerman I can open a PR for this if you feel this is an acceptable fix.

Webpack 5

Related #60

Setup

I'm migrating my project from Webpack 4 to 5. Circular-dependency-plugin was running without errors in Webpack 4, in 5 index.js imports are being marked as circular dependency errors.

| Package                    | Version |
| -------------------------- | ------- |
| webpack                    | 5.24.4  |
| circular-dependency-plugin | 5.2.2   |

package-lock.json

    "circular-dependency-plugin": {
      "version": "5.2.2",
      "resolved": "https://registry.npmjs.org/circular-dependency-plugin/-/circular-dependency-plugin-5.2.2.tgz",
      "integrity": "sha512-g38K9Cm5WRwlaH6g03B9OEz/0qRizI+2I7n+Gz+L5DxXJAPAiWQvwlYNm1V1jkdpUv95bOe/ASm2vfi/G560jQ==",
      "dev": true
    }

    "webpack-dev-server": {
      "version": "3.11.2",
      "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-3.11.2.tgz",
      "integrity": "sha512-A80BkuHRQfCiNtGBS1EMf2ChTUs0x+B3wGDFmOeT4rmJOHhHTCH2naNxIHhmkr0/UillP4U3yeIyv1pNp+QDLQ==",
      "dev": true
    }

    "webpack": {
      "version": "5.24.4",
      "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.24.4.tgz",
      "integrity": "sha512-RXOdxF9hFFFhg47BryCgyFrEyyu7Y/75/uiI2DoUiTMqysK+WczVSTppvkR47oZcmI/DPaXCiCiaXBP8QjkNpA==",
      "dev": true
    }

I'm pretty sure the following is not a circular dependency, but I'd like some feedback. I may have been handling import/export wrong all this time.

Here's a very simplistic view:

Folder structure:

folder1
  file1a.js
  file1b.js
  index.js

file1a

  export default () => something_to_return

file1b

  import { file1a } from './'
  export default () => file1a()

index.js

  import file1a from './file1a.js'
  import file2a from './file2a.js'
  export { file1a, file2a }

message

ERROR in Circular dependency detected:
file1b.js -> index.js -> file1b.js

add require to readme.md

could you add var CircularDependencyPlugin = require('circular-dependency-plugin'); to the readme?

Importing flow types causes circular dependency error

First off -- amazing plugin, this has saved me from a bunch of problems. Thank you so much!

The issue I'm seeing is this:

// foo.js

import { Bar } from './bar';
// bar.js

import { type Foo } from './foo';

This will cause circular-dependency-plugin to fail. But this makes it very difficult to do things like dependency injection, when the dependency isn't declared statically, but it's passed in.

I get that some people may wish to guard against this, still -- but would it be possible to add a flag to ignore circular dependencies when it's only types which are being circularly imported?

Thanks again for your time on this!

Version 5 doesn't work with webpack 4 (while 4+ works quite well)

TypeError: Cannot read property 'tap' of undefined
    at CircularDependencyPlugin.apply (/home/js/Projects/samwise/search-ql/node_modules/circular-dependency-plugin/index.js:20:32)
    at plugins.forEach.plugin (/home/js/Projects/samwise/search-ql/node_modules/enhanced-resolve/lib/ResolverFactory.js:276:10)
    at Array.forEach (<anonymous>)
    at Object.exports.createResolver (/home/js/Projects/samwise/search-ql/node_modules/enhanced-resolve/lib/ResolverFactory.js:275:10)
    at ResolverFactory._create (/home/js/Projects/samwise/search-ql/node_modules/webpack/lib/ResolverFactory.js:60:28)
    at ResolverFactory.get (/home/js/Projects/samwise/search-ql/node_modules/webpack/lib/ResolverFactory.js:53:28)
    at NormalModuleFactory.getResolver (/home/js/Projects/samwise/search-ql/node_modules/webpack/lib/NormalModuleFactory.js:474:31)
    at /home/js/Projects/samwise/search-ql/node_modules/webpack/lib/NormalModuleFactory.js:163:32
    at /home/js/Projects/samwise/search-ql/node_modules/webpack/lib/NormalModuleFactory.js:117:4
    at hooks.beforeResolve.callAsync (/home/js/Projects/samwise/search-ql/node_modules/webpack/lib/NormalModuleFactory.js:357:5)
    at AsyncSeriesWaterfallHook.eval [as callAsync] (eval at create (/home/js/Projects/samwise/search-ql/node_modules/tapable/lib/HookCodeFactory.js:24:12), <anonymous>:6:1)
    at AsyncSeriesWaterfallHook.lazyCompileHook [as _callAsync] (/home/js/Projects/samwise/search-ql/node_modules/tapable/lib/Hook.js:35:21)
    at NormalModuleFactory.create (/home/js/Projects/samwise/search-ql/node_modules/webpack/lib/NormalModuleFactory.js:338:28)
    at semaphore.acquire (/home/js/Projects/samwise/search-ql/node_modules/webpack/lib/Compilation.js:649:18)
    at Semaphore.acquire (/home/js/Projects/samwise/search-ql/node_modules/webpack/lib/util/Semaphore.js:16:4)
    at Compilation._addModuleChain (/home/js/Projects/samwise/search-ql/node_modules/webpack/lib/Compilation.js:648:18)

from config:


    plugins: [
      new CircularDependencyPlugin({ exclude: /node_modules/, failOnError: true }),
    ],

Circular dependency checker can be slow

Angular runs circular-dependency-plugin on each incremental build, so speed is somewhat important

On a largeish hybrid app with complicate imports (in particular, the angularjs part doesn't use modules / barrel files, so the import graph is pretty complicated) a no-change incremental build is about 8s with and 3s without circular dependency checking on a state of the art desktop.

Initially reported at angular/angular-cli#19794

There is a PR languishing at #49, but Tarjan's algorithm is also suboptimal for detecting whether there is a cycle (however it is a good algorithm for enumerating all cycles).

See also: https://stackoverflow.com/questions/261573/best-algorithm-for-detecting-cycles-in-a-directed-graph

Error: "Path must be a string.". Doesn't work with DllReferencePlugin

Getting error when using with DllReferencePlugin:

TypeError: Path must be a string. Received undefined
    at assertPath (path.js:7:11)
    at Object.relative (path.js:1227:5)
    at CircularDependencyPlugin.isCyclic (/node_modules/circular-dependency-plugin/index.js:92:18)

Tried to log the depModule which doesn't have resource:

DelegatedModule {
  dependencies:
   [ DelegatedSourceDependency {
       module: [Object],
       weak: false,
       optional: false,
       loc: undefined,
       request: 'dll-reference vendor_a41f543bb658b494f9b0',
       userRequest: 'dll-reference vendor_a41f543bb658b494f9b0',
       __NormalModuleFactoryCache: [Object] },
     DelegatedExportsDependency {
       module: null,
       weak: false,
       optional: false,
       loc: undefined,
       originModule: [Circular],
       exports: true } ],
  blocks: [],
  variables: [],
  type: 'require',
  context: null,
  debugId: 2261,
  hash: undefined,
  renderedHash: undefined,
  resolveOptions: {},
  factoryMeta: {},
  warnings: [],
  errors: [],
  buildMeta: { providedExports: true },
  buildInfo: {},
  reasons:
   [ ModuleReason {
       module: [Object],
       dependency: [Object],
       explanation: undefined,
       _chunks: null }],
  _chunks:
   SortableSet { ... },
  id: null,
  index: 1191,
  index2: 1181,
  depth: 5,
  issuer:
   NormalModule {
     dependencies:
      [ [Object]],
     blocks: [],
     variables: [],
     type: 'javascript/auto',
     context: '/Users/xxx/node_modules/xxx',
     debugId: 3876,
     hash: undefined,
     renderedHash: undefined,
     resolveOptions: {},
     factoryMeta: {},
     warnings: [],
     errors: [],
     buildMeta: { providedExports: true },
     buildInfo:
      { cacheable: true,
        fileDependencies: [Object],
        contextDependencies: Set {},
        strict: true },
     reasons: [ [Object], [Object] ],
     _chunks:
      SortableSet {
        [Object],
        _sortFn: [Function: sortById],
        _lastActiveSortFn: null,
        _cache: undefined,
        _cacheOrderIndependent: undefined },
     id: null,
     index: 1213,
     index2: 1246,
     depth: 6,
     issuer:
      NormalModule { ...l },
     profile: undefined,
     prefetched: false,
     built: true,
     used: null,
     usedExports: null,
     optimizationBailout: [],
     _rewriteChunkInReasons: undefined,
     useSourceMap: true,
     request: '/Users/xxx/node_modules/xxx/xxx.js',
     userRequest: '/Users/xxx/node_modules/xxx/xxx.js',
     rawRequest: './xxx',
     binary: false,
     parser:
      Parser {
        _pluginCompat: [Object],
        hooks: [Object],
        options: {},
        sourceType: 'auto',
        scope: undefined,
        state: undefined,
        comments: undefined },
     generator: JavascriptGenerator {},
     resource: '/Users/xxx/xxx/node_modules/xxx/xxx.js',
     loaders: [],
     error: null,
     _source:
      OriginalSource {
        _value: '.....code',
        _name: 'xxx.js' },
     buildTimestamp: 1523397148514,
     _cachedSource: undefined,
     _cachedSourceHash: undefined,
     lineToLine: false,
     _lastSuccessfulBuildMeta: { providedExports: true },
     _ast: null },
  profile: undefined,
  prefetched: false,
  built: true,
  used: null,
  usedExports: null,
  optimizationBailout: [],
  _rewriteChunkInReasons: undefined,
  useSourceMap: true,
  sourceRequest: 'dll-reference vendor_a41f543bb658b494f9b0',
  request: './node_modules/lodash/isNil.js',
  userRequest: './node_modules/lodash/isNil.js',
  originalRequest:
   NormalModule {...},
  delegateData:
   { id: './node_modules/lodash/isNil.js',
     buildMeta: { providedExports: true } } }

Dependencies:

  "dependencies": {
    "add-asset-html-webpack-plugin": "^2.1.3",
    "autodll-webpack-plugin": "^0.3.9",
    "autoprefixer": "^8.2.0",
    "babel-eslint": "^8.2.2",
    "babel-loader": "^8.0.0-beta.2",
    "babel-plugin-import": "^1.7.0",
    "circular-dependency-plugin": "^5.0.1",
    "compression-webpack-plugin": "^1.1.11",
    "css-loader": "^0.28.11",
    "eslint-plugin-babel": "^5.0.0",
    "eslint-plugin-promise": "^3.7.0",
    "file-loader": "^1.1.11",
    "font-loader": "^0.1.2",
    "happypack": "^5.0.0-beta.3",
    "html-webpack-plugin": "^3.2.0",
    "lodash": "^4.17.5",
    "null-loader": "^0.1.1",
    "postcss-loader": "^2.1.3",
    "preload-image-loader": "^1.0.0",
    "react-hot-loader": "^4.0.1",
    "resolve-url-loader": "^2.3.0",
    "sass-loader": "^6.0.7",
    "stats-webpack-plugin": "^0.6.2",
    "style-loader": "^0.20.3",
    "uglifyjs-webpack-plugin": "^1.2.4",
    "webpack": "^4.5.0",
    "webpack-cli": "^2.0.14"
  }

webpack plugins:

  new webpack.DllReferencePlugin({
    context: basePath,
    manifest:path.join(dllFolder, 'vendor.manifest.json'),
  }),
  new HtmlWebpackPlugin(),
  new AddAssetHtmlPlugin({
    filepath: path.resolve(dllFolder, './*.dll.js'),
  }),
  new CircularDependencyPlugin({
    cwd: basePath,
    exclude: /node_modules/,
    failOnError: false,
  }),

And I found a workaround which fixes this issue:
Using different webpack object for DllReferencePlugin, i.e., something like this

const webpack2 = require('another-webpack-node-module-path')
webpack2.DllReferencePlugin()

Cannot read property 'tap' of undefined

Hi!
I tried circular-dependency-plugin 4 and 5 version and get the same error.

package.json:

{
  "name": "mvs-dash",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "@babel/core": "7.2.0",
    "@nivo/bar": "^0.52.1",
    "@nivo/line": "^0.52.1",
    "@nivo/pie": "^0.52.1",
    "@svgr/webpack": "4.1.0",
    "ag-grid-community": "^19.1.4",
    "ag-grid-enterprise": "^19.1.4",
    "ag-grid-react": "^19.1.2",
    "antd": "^3.13.6",
    "apollo-boost": "^0.1.28",
    "apollo-link-context": "^1.0.17",
    "axios": "^0.18.0",
    "babel-core": "7.0.0-bridge.0",
    "babel-eslint": "10.0.1",
    "babel-jest": "^24.5.0",
    "babel-loader": "8.0.4",
    "babel-plugin-named-asset-import": "^0.2.2",
    "babel-polyfill": "^6.26.0",
    "babel-preset-react-app": "^6.1.0",
    "base-64": "^0.1.0",
    "base64-img": "^1.0.4",
    "bfj": "6.1.1",
    "build-url": "^1.3.2",
    "case-sensitive-paths-webpack-plugin": "2.1.2",
    "chalk": "2.4.1",
    "circular-dependency-plugin": "^4.0.0",
    "cors": "^2.8.5",
    "css-loader": "1.0.1",
    "dotenv": "6.2.0",
    "dotenv-expand": "4.2.0",
    "draft-js": "^0.10.5",
    "draftjs-to-html": "^0.8.4",
    "eslint": "5.9.0",
    "eslint-config-react-app": "^3.0.8",
    "eslint-loader": "2.1.1",
    "eslint-plugin-flowtype": "3.2.0",
    "eslint-plugin-import": "2.14.0",
    "eslint-plugin-jsx-a11y": "6.1.2",
    "eslint-plugin-react": "7.11.1",
    "file-loader": "2.0.0",
    "fork-ts-checker-webpack-plugin-alt": "^0.4.14",
    "fs-extra": "7.0.1",
    "geodist": "^0.2.1",
    "graphql": "^14.1.1",
    "graphql-tag": "^2.10.1",
    "html-to-draftjs": "^1.4.0",
    "html-webpack-plugin": "4.0.0-alpha.2",
    "identity-obj-proxy": "3.0.0",
    "jest": "^24.5.0",
    "jest-pnp-resolver": "1.0.2",
    "jest-resolve": "23.6.0",
    "jwt-decode": "^2.2.0",
    "konva": "^3.2.3",
    "leaflet": "^1.4.0",
    "lodash": "^4.17.11",
    "mini-css-extract-plugin": "0.4.5",
    "minio": "^7.0.5",
    "moment": "^2.24.0",
    "nivo": "^0.31.0",
    "optimize-css-assets-webpack-plugin": "5.0.1",
    "pnp-webpack-plugin": "1.2.1",
    "postcss-flexbugs-fixes": "4.1.0",
    "postcss-loader": "3.0.0",
    "postcss-preset-env": "6.4.0",
    "postcss-safe-parser": "4.0.1",
    "prop-types": "^15.7.2",
    "quill": "^1.3.6",
    "randomcolor": "^0.5.4",
    "react": "^16.8.3",
    "react-apollo": "2.5.2",
    "react-app-polyfill": "^0.1.3",
    "react-beautiful-dnd": "^10.1.0",
    "react-blocks": "^1.1.4",
    "react-dev-utils": "^6.0.4",
    "react-dom": "^16.8.3",
    "react-draft-wysiwyg": "^1.13.2",
    "react-dropzone": "^9.0.0",
    "react-icons": "^3.5.0",
    "react-image": "^1.5.1",
    "react-image-gallery": "^0.8.14",
    "react-intl": "^2.8.0",
    "react-konva": "^16.8.6",
    "react-leaflet": "^2.2.1",
    "react-localization": "^1.0.13",
    "react-native-base64": "0.0.2",
    "react-panelgroup": "^1.0.7",
    "react-quill": "^1.3.3",
    "react-router-dom": "^4.3.1",
    "react-split-pane": "^0.1.85",
    "react-table": "^6.9.2",
    "react-text-ellipsis": "^1.5.2",
    "react-vis": "^1.11.6",
    "recharts": "^1.5.0",
    "recharts-color-utils": "^0.1.1",
    "resolve": "1.8.1",
    "seafile-js": "^0.2.68",
    "style-loader": "0.23.1",
    "terser-webpack-plugin": "1.1.0",
    "text-diff": "^1.0.1",
    "ts-react-json-table": "^0.1.1",
    "url-loader": "1.1.2",
    "webdav": "^2.6.0",
    "webpack": "4.27.1",
    "webpack-dev-server": "^3.2.1",
    "webpack-manifest-plugin": "2.0.4",
    "workbox-webpack-plugin": "3.6.3",
    "xlsx": "^0.14.1"
  },
  "scripts": {
    "start": "node $NODE_DEBUG_OPTION scripts/start.js",
    "build:prod": "node --max_old_space_size=4096 scripts/build.js",
    "build:dev": "REACT_APP_GQL_SERVER_DEV=1 node --max_old_space_size=4096 scripts/build.js",
    "test": "node scripts/test.js"
  },
  "eslintConfig": {
    "extends": "react-app"
  },
  "browserslist": [
    ">0.2%",
    "not dead",
    "not ie <= 11",
    "not op_mini all"
  ],
  "devDependencies": {
    "@types/minio": "^7.0.1",
    "node-sass": "^4.9.4",
    "sass-loader": "^7.1.0",
    "webpack-cli": "^3.2.3"
  },
  "jest": {
    "collectCoverageFrom": [
      "src/**/*.{js,jsx}"
    ],
    "resolver": "jest-pnp-resolver",
    "setupFiles": [
      "react-app-polyfill/jsdom"
    ],
    "testMatch": [
      "<rootDir>/src/**/__tests__/**/*.{js,jsx}",
      "<rootDir>/src/**/?(*.)(spec|test).{js,jsx}"
    ],
    "testEnvironment": "jsdom",
    "testURL": "http://localhost",
    "transform": {
      "^.+\\.(js|jsx)$": "<rootDir>/node_modules/babel-jest",
      "^.+\\.css$": "<rootDir>/config/jest/cssTransform.js",
      "^(?!.*\\.(js|jsx|css|json)$)": "<rootDir>/config/jest/fileTransform.js"
    },
    "transformIgnorePatterns": [
      "[/\\\\]node_modules[/\\\\].+\\.(js|jsx)$",
      "^.+\\.module\\.(css|sass|scss)$"
    ],
    "moduleNameMapper": {
      "^react-native$": "react-native-web",
      "^.+\\.module\\.(css|sass|scss)$": "identity-obj-proxy"
    },
    "moduleFileExtensions": [
      "web.js",
      "js",
      "json",
      "web.jsx",
      "jsx",
      "node"
    ]
  },
  "babel": {
    "presets": [
      "react-app"
    ]
  }
}

here my webpack.config.dev.js:

new CircularDependencyPlugin({
        // exclude detection of files based on a RegExp
        exclude: /a\.js|node_modules/,
        // add errors to webpack instead of warnings
        failOnError: true,
        // set the current working directory for displaying module paths
        cwd: process.cwd(),
      }),

Cannot read property 'tap' of undefined

TypeError: Cannot read property 'tap' of undefined
    at CircularDependencyPlugin.apply (/xxx/node_modules/circular-dependency-plugin/index.js:22:32)
    at /xxx/node_modules/webpack/node_modules/enhanced-resolve/lib/ResolverFactory.js:327:10
    at Array.forEach (<anonymous>)
    at Object.exports.createResolver (/xxx/node_modules/webpack/node_modules/enhanced-resolve/lib/ResolverFactory.js:326:10)
    at ResolverFactory._create (/xxx/node_modules/webpack/lib/ResolverFactory.js:57:28)
    at ResolverFactory.get (/xxx/node_modules/webpack/lib/ResolverFactory.js:49:28)
    at NormalModuleFactory.getResolver (/xxx/node_modules/webpack/lib/NormalModuleFactory.js:521:31)
    at /xxx/node_modules/webpack/lib/NormalModuleFactory.js:165:32
    at /xxx/node_modules/webpack/lib/NormalModuleFactory.js:129:4
    at handleExternal (/xxx/node_modules/webpack/lib/ExternalModuleFactoryPlugin.js:28:34)

{
"circular-dependency-plugin": "^5.2.2",
"webpack": "^4.44.1",
}

Integration with Jenkins CI

I'am looking for the usage and implementation of this plugin in Vue project. Is there any issue to integrate this plugin for Jenkins CI build

Support for ignore React.lazy async imports

Apologies if this is too similar to #67 - I think this issue I'm having would require a slightly different solution so decided on a new issue.

The 'allowAsyncCycles' flag does not seem to prevent imports like this from being highlighted/warned about:
const TestView= React.lazy(() => import('views/TestView'));

To clarify what I'm attempting to do: I'm looking to determine how many circular dependencies we have with the intent of replacing key ones with React.lazy to support code splitting, but since I currently can't exclude the async imports, I am struggling to see an effective way to achieve this.

[email protected] support with circular dependency

I'm migrating my project from [email protected] -> [email protected]. I'm running into circular dependency issue.

Error: Circular dependency detected:
assets/images/card_auth_illustration.png -> assets/images/card_auth_illustration.png

ERROR in Error: Child compilation failed:
Circular dependency detected:
src/index.html -> src/index.html
Error: Circular dependency detected:
src/index.html -> src/index.html

  • index.js:55
    [spa]/[circular-dependency-plugin]/index.js:55:25

  • Hook.js:14 Hook.CALL_DELEGATE [as _call]
    [spa]/[webpack]/[tapable]/lib/Hook.js:14:14

  • Compilation.js:2048 Compilation.seal
    [spa]/[webpack]/lib/Compilation.js:2048:37

  • Compiler.js:1050
    [spa]/[webpack]/lib/Compiler.js:1050:20

  • Compilation.js:1877
    [spa]/[webpack]/lib/Compilation.js:1877:4

  • FlagDependencyExportsPlugin.js:332
    [spa]/[webpack]/lib/FlagDependencyExportsPlugin.js:332:11

  • async.js:2830
    [spa]/[neo-async]/async.js:2830:7

  • async.js:2850 Object.each
    [spa]/[neo-async]/async.js:2850:39

  • FlagDependencyExportsPlugin.js:311
    [spa]/[webpack]/lib/FlagDependencyExportsPlugin.js:311:18

  • async.js:2830
    [spa]/[neo-async]/async.js:2830:7

  • async.js:2850 Object.each
    [spa]/[neo-async]/async.js:2850:39

  • FlagDependencyExportsPlugin.js:46
    [spa]/[webpack]/lib/FlagDependencyExportsPlugin.js:46:16

  • Hook.js:18 Hook.CALL_ASYNC_DELEGATE [as _callAsync]
    [spa]/[webpack]/[tapable]/lib/Hook.js:18:14

  • Compilation.js:1848 Compilation.finish
    [spa]/[webpack]/lib/Compilation.js:1848:28

  • Compiler.js:1045
    [spa]/[webpack]/lib/Compiler.js:1045:19

  • task_queues.js:75 processTicksAndRejections
    internal/process/task_queues.js:75:11

  • task_queues.js:62 runNextTicks
    internal/process/task_queues.js:62:3

  • timers.js:434 processImmediate
    internal/timers.js:434:9

  • child-compiler.js:163
    [spa]/[html-webpack-plugin]/lib/child-compiler.js:163:18

  • Compiler.js:511
    [spa]/[webpack]/lib/Compiler.js:511:11

  • Compiler.js:1059
    [spa]/[webpack]/lib/Compiler.js:1059:17

  • Hook.js:18 Hook.CALL_ASYNC_DELEGATE [as _callAsync]
    [spa]/[webpack]/[tapable]/lib/Hook.js:18:14

  • Compiler.js:1055
    [spa]/[webpack]/lib/Compiler.js:1055:33

  • Compilation.js:2180
    [spa]/[webpack]/lib/Compilation.js:2180:10

  • Hook.js:18 Hook.CALL_ASYNC_DELEGATE [as _callAsync]
    [spa]/[webpack]/[tapable]/lib/Hook.js:18:14

  • Compilation.js:2173
    [spa]/[webpack]/lib/Compilation.js:2173:37

  • Compilation.js:409
    [spa]/[webpack]/lib/Compilation.js:409:10

  • SourceMapDevToolPlugin.js:540
    [spa]/[webpack]/lib/SourceMapDevToolPlugin.js:540:10

  • async.js:2830
    [spa]/[neo-async]/async.js:2830:7

  • async.js:2857 Object.each
    [spa]/[neo-async]/async.js:2857:9

  • SourceMapDevToolPlugin.js:376
    [spa]/[webpack]/lib/SourceMapDevToolPlugin.js:376:17

  • async.js:2830
    [spa]/[neo-async]/async.js:2830:7

  • async.js:2857 Object.each
    [spa]/[neo-async]/async.js:2857:9

  • SourceMapDevToolPlugin.js:204
    [spa]/[webpack]/lib/SourceMapDevToolPlugin.js:204:15

  • Compilation.js:398 fn
    [spa]/[webpack]/lib/Compilation.js:398:9

  • Compilation.js:381 fn
    [spa]/[webpack]/lib/Compilation.js:381:9

  • Hook.js:18 Hook.CALL_ASYNC_DELEGATE [as _callAsync]
    [spa]/[webpack]/[tapable]/lib/Hook.js:18:14

  • Compilation.js:2147 cont
    [spa]/[webpack]/lib/Compilation.js:2147:33

  • Compilation.js:2193
    [spa]/[webpack]/lib/Compilation.js:2193:9

  • async.js:2830
    [spa]/[neo-async]/async.js:2830:7

  • async.js:2850 Object.each
    [spa]/[neo-async]/async.js:2850:39

  • Compilation.js:3258 Compilation.createChunkAssets
    [spa]/[webpack]/lib/Compilation.js:3258:12

webpack.commom.js

const CircularDependencyPlugin = require('circular-dependency-plugin');
plugins: [
new CircularDependencyPlugin({
exclude: /a.js|node_modules/,
include: /src/,
failOnError: true,
})
]

Log to file?

We tried wiring things up to have a file generated at startup, but unfortunately we can't find a way to prevent the plugin from constantly re-creating the files when the project is refreshed. If we try something like setting isCompleted = true inside of onEnd() - but isCompleted ends up being set to true before we'd expect and so no report files are generated. I looked at the code but can't figure out what might cause this. Maybe there's something I don't understand about how Webpack runs plugins?

Performance degradation webpack 5.3.2 + this plugin 5.2.2 on recompile

Upgrading to latest circular-dependency-plugin causes performance degradation on recompile in dev mode.

with plugin ~12s
image

without plugin ~2.5s
image

current webpack configuration:

// / <binding ProjectOpened='Watch - Development' />
import { cpus } from 'os';
import * as path from 'path';
import * as webpack from 'webpack';
import * as webpackDevServer from 'webpack-dev-server';
import { BundleAnalyzerPlugin } from 'webpack-bundle-analyzer';
import * as MiniCssExtractPlugin from 'mini-css-extract-plugin';
import TsCheckerPlugin = require('fork-ts-checker-webpack-plugin');
import TerserJSPlugin = require('terser-webpack-plugin');
import OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
import TsCheckerNotifierWebpackPlugin = require('fork-ts-checker-notifier-webpack-plugin');
import { writeFileSync } from 'fs';
const WebpackBuildNotifierPlugin = require('webpack-build-notifier');
const CircularDependencyPlugin = require('circular-dependency-plugin');
const threadLoader = require('thread-loader');

const CPU_COUNT = cpus().length;

const processCwd = process.cwd();

const circularDependencies = {
  count: 0,
  countInMluviiUiPackage: 0,
  dependencyCircles: {} as { [key: string]: string[] | string[][] }
};

interface Configuration extends webpack.Configuration {
  devServer?: webpackDevServer.Configuration;
}

const isProduction = process.argv[process.argv.indexOf('--mode') + 1] === 'production';
const analyzeBuild = process.argv.some(a => a === '--analyze');
const profilingBuild = isProduction && process.argv.some(a => a === '--profiling');

const profilingAliases = profilingBuild
  ? {
      'react-dom$': 'react-dom/profiling',
      'scheduler/tracing': 'scheduler/tracing-profiling'
    }
  : {};

const enforceCRLF = (text: string = '') => text.replace(/\r\n/gm, '\n').replace(/\n/gm, '\r\n');

const tsPool = {
  workers: CPU_COUNT - 2,
  // number of jobs a worker processes in parallel
  // defaults to 20
  workerParallelJobs: 10,
  // additional node.js arguments
  workerNodeArgs: ['--max-old-space-size=1024'],
  // Allow to respawn a dead worker pool
  // respawning slows down the entire compilation
  // and should be set to false for development
  poolRespawn: isProduction,
  // timeout for killing the worker processes when idle
  // defaults to 500 (ms)
  // can be set to Infinity for watching builds to keep workers alive
  poolTimeout: isProduction ? 500 : Infinity,
  // number of jobs the pool distributes to the workers
  // defaults to 200
  // decrease of less efficient but more fair distribution
  poolParallelJobs: 300
  // can be used to create different pools with elsewise identical options
  // name: "ts-pool"
};

threadLoader.warmup(tsPool, ['ts-loader']);

const devServer: webpackDevServer.Configuration = {
  port: 3000,
  https: true,
  disableHostCheck: true,
  headers: { 'Access-Control-Allow-Origin': '*' },
  contentBase: path.resolve(__dirname, '../../../dist')
};

const config: Configuration = {
  devServer,
  entry: {
    'app.operator': './src/ui/application/entrypoint.tsx',
    'app.guest': './src/ui/guest/app.tsx',
    'app.qrupload': './src/qrupload/qrupload.ts',
    'lib.edge': './src/edge.js',
    'lib.firefox': './src/firefox.js',
    'lib.chrome': './src/chrome.js',
    'lib.ie10': './src/ie10.js',
    'lib.ie11': './src/ie11.js',
    'lib.otherbrowser': './src/otherbrowser.js',
    'lib.safari': './src/safari.js',
    'lib.pdfworker': 'pdfjs-dist/build/pdf.worker'
  },
  target: ['web', 'es5'],
  output: {
    libraryTarget: 'this',
    path: path.resolve(__dirname, '../../../dist'),
    filename: 'js/[name].js',
    publicPath: '/appcontent/',
    chunkFilename: 'js/[name].js?[contenthash]',
    pathinfo: !isProduction
  },
  resolve: {
    alias: {
      src: path.resolve(__dirname, '../src/'),
      core: path.resolve(__dirname, '../src/core'),
      ui: path.resolve(__dirname, '../src/ui'),
      tslib: path.resolve(__dirname, '../../../node_modules/tslib'),
      '@mluvii/ui': path.resolve(__dirname, '../../../node_modules/@mluvii/ui/dist'),
      ...profilingAliases
    },
    extensions: ['.js', '.jsx', '.ts', '.tsx']
  },
  module: {
    rules: [
      {
        test: require.resolve('janus-gateway'),
        loader: 'exports-loader',
        options: {
          exports: 'Janus'
        }
      },
      {
        test: /src[\\/]ui[\\/].*\.url\.svg$/,
        loader: 'file-loader',
        options: {
          name: 'svg/[name].[ext]?[md5:hash:base62]'
        }
      },
      {
        test: /src[\\/]ui[\\/].*(?<!\.url)\.svg$/,
        exclude: /node_modules/,
        use: [
          {
            loader: 'ts-loader',
            options: {
              happyPackMode: true,
              compilerOptions: {
                jsx: 'preserve',
                allowJs: true,
                checkJs: false
              }
            }
          },
          'react-svg-loader'
        ]
      },
      {
        test: /\.json$/,
        loader: 'json-loader',
        exclude: /node_modules/,
        type: 'javascript/auto'
      },
      {
        test: [/\.tsx?$/, /\.js$/],
        exclude: /node_modules/,
        use: [
          {
            loader: 'thread-loader',
            options: tsPool
          },
          {
            loader: 'ts-loader',
            options: {
              happyPackMode: true
            }
          }
        ]
      },
      {
        // TODO: review webpack bundling rules
        test: /\.jsx?$/,
        exclude: /(node_modules)(?![/|\\](swiper|dom7|ssr\-window))/,
        use: [
          {
            loader: 'thread-loader',
            options: tsPool
          },
          {
            loader: 'ts-loader',
            options: {
              happyPackMode: true,
              compilerOptions: {
                allowJs: true,
                checkJs: false
              }
            }
          }
        ]
      },
      {
        test: /\.css$/,
        use: [MiniCssExtractPlugin.loader, 'css-loader']
      },
      {
        test: /\.(png|gif|jpg|jpeg)$/,
        loader: 'file-loader',
        options: {
          name: 'img/[name].[ext]?[md5:hash:base62]'
        }
      },
      {
        test: /fonts[\\/].*\.(woff|woff2|eot|ttf|svg)$/,
        loader: 'file-loader',
        options: {
          name: 'fonts/[name].[ext]?[md5:hash:base62]'
        }
      },
      {
        test: /\.mp3$/,
        loader: 'file-loader',
        options: {
          name: 'sounds/[name].[ext]?[md5:hash:base62]'
        }
      },
      {
        test: /\.html$/i,
        loader: 'html-loader',
        options: {
          minimize: true
        }
      },
      // https://github.com/webpack/webpack/issues/11467
      {
        test: /\.m?js/,
        resolve: {
          fullySpecified: false
        }
      }
    ],
    noParse: [/pdfjs-dist/]
  },
  externals: {
    './chrome/chrome_shim': 'webrtc_adapter_chrome_shim',
    './edge/edge_shim': 'webrtc_adapter_edge_shim',
    './firefox/firefox_shim': 'webrtc_adapter_firefox_shim',
    './safari/safari_shim': 'webrtc_adapter_safari_shim'
  },
  optimization: {
    minimizer: isProduction
      ? [
          new TerserJSPlugin({
            parallel: true,
            terserOptions: {
              sourceMap: true
            }
          }),
          new OptimizeCSSAssetsPlugin({})
        ]
      : undefined
  },
  mode: isProduction ? 'production' : 'development',
  devtool: isProduction ? false : 'eval-source-map',
  plugins: [
    new webpack.DefinePlugin({
      PRODUCTION: isProduction
    }),
    new webpack.ProvidePlugin({ adapter: 'webrtc-adapter' }),
    new webpack.DllReferencePlugin({
      context: path.join(__dirname, '..'),
      manifest: require('../../../dist/lib.base-manifest.json')
    }),
    isProduction &&
      new webpack.SourceMapDevToolPlugin({
        filename: '../source-map/[name].js.map',
        moduleFilenameTemplate: '[resource-path]',
        append: '\n//# sourceMappingURL=[name].js.map',
        exclude: /lib\..*.*/
      }),
    analyzeBuild &&
      new BundleAnalyzerPlugin({
        analyzerMode: 'static',
        generateStatsFile: true,
        openAnalyzer: false
      }),
    new CircularDependencyPlugin({
      exclude: /node_modules/,
      failOnError: false,
      allowAsyncCycles: false,
      cwd: processCwd,
      onStart() {
        circularDependencies.count = 0;
        circularDependencies.countInMluviiUiPackage = 0;
        circularDependencies.dependencyCircles = {};
      },
      onDetected({ compilation, paths }) {
        circularDependencies.count++;
        const [source, ...deps] = paths.map(pa => pa.replace(/\\/g, '/'));

        if (paths.some(p => /[\\/]ui[\\/]dist/.test(p))) {
          circularDependencies.countInMluviiUiPackage++;
          compilation.errors.push(
            new Error(
              `[CircularDependency] in ${path.join(processCwd, paths[0])}:\n ${deps.join(' -> ')}`
            )
          );
        }

        if (circularDependencies.dependencyCircles[source]) {
          circularDependencies.dependencyCircles[source] = [
            circularDependencies.dependencyCircles[source],
            deps
          ];
        } else {
          circularDependencies.dependencyCircles[source] = deps;
        }
      },
      onEnd({ compilation }: { compilation: any }) {
        if (circularDependencies.count > 0) {
          compilation.warnings.push(
            new Error(`Detected ${circularDependencies.count} cycles in dependency tree.`)
          );
        }
        if (circularDependencies.countInMluviiUiPackage > 0) {
          compilation.errors.push(
            new Error(
              `Detected ${circularDependencies.countInMluviiUiPackage} cycles in dependency tree of @mluvii/ui - please refactor code to eliminate them.`
            )
          );
        }
        if (!isProduction) {
          const content = JSON.stringify(circularDependencies, null, 2);
          writeFileSync(path.join(__dirname, '../../../circularDeps.json'), enforceCRLF(content), {
            encoding: 'utf-8'
          });
        }
      }
    }),
    new MiniCssExtractPlugin({
      filename: 'css/[name].css?[contenthash]'
    }),
    new webpack.IgnorePlugin({
      resourceRegExp: /^\.\/locale$/,
      contextRegExp: /moment$/
    }),
    !isProduction &&
      new WebpackBuildNotifierPlugin({
        title: 'Application built',
        suppressSuccess: true
      }),
    !isProduction &&
      new TsCheckerNotifierWebpackPlugin({
        title: 'Application',
        skipSuccessful: true
      }),
    new TsCheckerPlugin({
      async: !isProduction,
      typescript: {
        memoryLimit: 4096,
        configFile: path.resolve(__dirname, '../tsconfig.json'),
        diagnosticOptions: {
          declaration: false,
          global: false,
          syntactic: true,
          semantic: true
        },
        configOverwrite: {
          compilerOptions: {
            skipLibCheck: false,
            sourceMap: false,
            inlineSourceMap: false,
            declarationMap: false
          },
          include: [
            path.resolve(__dirname, '../src'),
            path.resolve(__dirname, '../node_modules/@mluvii/ui/dist')
          ]
        }
      }
    })
  ].filter(Boolean)
};

export default config;

in comparison with old versions of webpack and this plugin, recompile tooks ~6s
"webpack": "^4.44.2" and "circular-dependency-plugin": "^5.2.0",
image

package.json

{
  "scripts": {
    "clean": "rimraf dist cache",
    "build:dll": "webpack --color --bail --config=./webpack/webpack.dll.config.js",
    "build:app": "webpack --color --bail --config=./webpack/webpack.config.ts",
    "build": "yarn build:dll --mode production && yarn build:app --mode production",
    "build:analyze": "yarn build:dll --mode production --analyze && yarn build:app --mode production --analyze",
    "build:profiling": "yarn build --profiling",
    "watch": "yarn clean && yarn && yarn build:dll --mode development && yarn build:app --mode development --watch",
    "start": "yarn && yarn build:dll --mode development && webpack-dev-server --mode development --config=./webpack/webpack.config.ts --no-inline --output-public-path https://localhost:3000/appcontent/",
    "measure": "webpack-dev-server --mode development --config=./webpack/speed-measure.ts --no-inline --output-public-path https://localhost:3000/appcontent/"
  },
  "devDependencies": {
    "@types/glob": "^5.0.35",
    "@types/moment-duration-format": "^2.2.2",
    "@types/node": "^8.9.1",
    "@types/react": "16.8.6",
    "@types/react-custom-scrollbars": "^4.0.5",
    "@types/react-dom": "^16.8.6",
    "@types/react-router": "^5.1.2",
    "@types/react-router-dom": "^5.1.0",
    "@types/redux-thunk": "^2.1.0",
    "@types/webpack-dev-server": "^3.11.1",
    "css-loader": "^5.0.0",
    "exports-loader": "^1.1.1",
    "file-loader": "^6.2.0",
    "fork-ts-checker-notifier-webpack-plugin": "^3.0.0",
    "fs": "^0.0.1-security",
    "glob": "^7.1.2",
    "html-loader": "^1.3.2",
    "mini-css-extract-plugin": "^1.2.1",
    "optimize-css-assets-webpack-plugin": "^5.0.4",
    "path": "^0.12.7",
    "prettier": "^2.1.1",
    "rimraf": "^2.6.2",
    "source-map-loader": "^1.1.2",
    "speed-measure-webpack-plugin": "^1.3.1",
    "style-loader": "^2.0.0",
    "terser-webpack-plugin": "^5.0.3",
    "ts-loader": "^8.0.7",
    "webpack-build-notifier": "^2.1.0",
    "webpack-bundle-analyzer": "^3.9.0",
    "webpack-cli": "^4.1.0",
    "webpack-dev-server": "^3.11.0",
    "worker-loader": "^3.0.5",
    "xlsx": "^0.14.0"
  },
  "dependencies": {
    "@aspnet/signalr": "1.1.4",
    "@sentry/browser": "^5.27.2",
    "@sentry/webpack-plugin": "^1.13.0",
    "@types/chrome": "^0.0.46",
    "@types/cytoscape": "^3.14.0",
    "@types/faker": "^4.1.9",
    "@types/hammerjs": "^2.0.35",
    "@types/i18next": "^13.0.0",
    "@types/markdown-it": "^10.0.2",
    "@types/pdfjs-dist": "^2.1.5",
    "@types/quill": "^1.3.10",
    "@types/ramda": "types/npm-ramda#dist",
    "@types/react-color": "^2.13.4",
    "@types/react-grid-layout": "^0.17.2",
    "@types/react-plotly.js": "^2.2.4",
    "@types/react-redux": "^7.1.9",
    "@types/react-select": "^2.0.2",
    "@types/react-sortable-hoc": "^0.6.1",
    "@types/react-textarea-autosize": "^4.3.3",
    "@types/react-transition-group": "^2.0.11",
    "@types/react-virtualized": "^9.8.0",
    "@types/react-youtube": "^7.4.1",
    "@types/redux-form": "^8.1.9",
    "@types/redux-thunk": "^2.1.0",
    "@types/rx-dom": "^7.0.0",
    "@types/service_worker_api": "^0.0.9",
    "@types/swiper": "^4.4.2",
    "adaptivecards": "^1.2.0",
    "autoprefixer": "^7.2.5",
    "axios": "^0.19.2",
    "botframework-directlinejs": "^0.13.1",
    "botframework-webchat": "^4.10.1",
    "botframework-webchat-component": "^4.10.1",
    "circular-dependency-plugin": "^5.2.2",
    "comlink": "^4.3.0",
    "connected-react-router": "^6.5.2",
    "core-js": "^3.4.7",
    "css-element-queries": "^1.0.2",
    "cytoscape": "^3.14.1",
    "cytoscape-canvas": "^3.0.1",
    "cytoscape-node-html-label": "^1.2.0",
    "cytoscape-panzoom": "^2.5.3",
    "eventbusjs": "^0.2.0",
    "faker": "^4.1.0",
    "fela": "^11.2.0",
    "fela-plugin-embedded": "^11.2.0",
    "fela-preset-web": "^11.2.0",
    "final-form": "^4.17.0",
    "final-form-arrays": "^3.0.1",
    "flubber": "^0.4.2",
    "font-awesome": "^4.7.0",
    "fork-ts-checker-webpack-plugin": "^5.2.1",
    "fp-ts": "^2.6.5",
    "gsap": "^3.1.1",
    "hammerjs": "^2.0.8",
    "history": "^4.10.1",
    "i18next": "^19.8.3",
    "immer": "^6.0.9",
    "intl": "^1.2.5",
    "io-ts": "^2.2.4",
    "janus-gateway": "git://github.com/meetecho/janus-gateway.git#v0.9.5",
    "json-loader": "^0.5.7",
    "konva": "^4.0.4",
    "markdown-it": "^12.0.2",
    "moment": "^2.18.1",
    "moment-duration-format": "^2.3.2",
    "moment-timezone": "^0.5.11",
    "papaparse": "^5.2.0",
    "paper": "^0.11.4",
    "pdfjs-dist": "^2.5.207",
    "plotly.js": "^1.54.0",
    "postcss-loader": "^2.1.0",
    "precss": "^3.1.0",
    "ramda": "^0.26.1",
    "react": "^16.12.0",
    "react-color": "^2.14.1",
    "react-copy-to-clipboard": "^5.0.0",
    "react-custom-scrollbars": "4.1.1",
    "react-dom": "^16.12.0",
    "react-draggable": "^3.3.0",
    "react-dropzone": "^7.0.1",
    "react-fela": "^11.2.0",
    "react-final-form": "^6.3.0",
    "react-final-form-arrays": "^3.1.1",
    "react-flip-move": "^3.0.4",
    "react-grid-layout": "^1.0.0",
    "react-hint": "^3.0.0",
    "react-i18next": "^11.7.3",
    "react-konva": "^16.9.0-0",
    "react-plotly.js": "^2.4.0",
    "react-qr-svg": "^2.3.0",
    "react-quill": "^1.3.3",
    "react-redux": "^7.1.1",
    "react-router": "^5.1.2",
    "react-router-dom": "^5.1.2",
    "react-select": "^2.0.0",
    "react-sortable-hoc": "^0.6.8",
    "react-spring": "^8.0.27",
    "react-svg-loader": "^3.0.3",
    "react-textarea-autosize": "^5.1.0",
    "react-transition-group": "^2.5.2",
    "react-virtualized": "^9.8.0",
    "react-youtube": "^7.4.0",
    "redux": "^4.0.4",
    "redux-devtools-extension": "^2.13.8",
    "redux-form": "^8.2.6",
    "redux-observable": "^1.2.0",
    "redux-thunk": "^2.3.0",
    "regenerator-runtime": "^0.13.3",
    "reselect": "^4.0.0",
    "rx-dom": "^7.0.3",
    "rxjs": "^6.5.3",
    "sip.js": "^0.17.1",
    "swiper": "^4.5.0",
    "thread-loader": "^3.0.1",
    "ts-node": "^4.1.0",
    "tslib": "^2.0.1",
    "twemoji": "^2.5.0",
    "typescript": "^4.0.3",
    "webpack": "^5.3.2",
    "webrtc-adapter": "6.4.8"
  }
}

Suppress warning if any dependency in the circular chain is loaded completely

Thanks for the plugin!

I'm in a situation where circular dependencies cannot be avoided altogether, but where some of the require calls can be postponed until the dependent modules are loaded completely. Here's an example:

const b = require('b');
export class a;
let a;
export class b {
  constructor() {
    a = require('a');
  }
};

As far as I can see, your plugin would flag this as a circular dependency, but it shouldn't be a problematic one, as module b could be required before it required a. Do you see any way to prevent warnings/errors for these cases?

Option to exclude circular dependencies chains including a module

Hello,

Thank you for the great plugin!

I have a module 'moduleA' in my project which I would like to exclude from the circular dependency check. I can use the "exclude" option":

{
  exclude: /moduleA/
}

But I would also like to be able to exclude any circular dependency traversing 'moduleA'... For now, the "exclude" option only tests the resource of the module being compiled, but doesn't test on the chain of the circular dependency.

Would it be possible to add this option? something similar to "excludeIfTraverses"?

{
  excludeIfTraverses: /moduleA/
}

improve readability of the error output

I have following suggestions to improve the readability of the error output

  1. The base statement. Right now if webpack spits out the warnings it reads like this
    WARNING in Circular dependency detected:
    i'd suggest to use the name of the plugin, like this
    WARNING in [circular-dependency-plugin]:
    This however does not improve readability. Its just a personal preference.

  2. The line length. Right now, all paths are written into a single line joined with " -> ".

frontend/modules/foo/foo.js -> frontend/modules/bar/bar.js -> frontend/modules/baz/baz.js

This is hard to read when there are many paths inside the circle.
My suggestion is to write each path in its own line. Maybe indent with a TAB and add an utf8 arrow symbol, like this

new Error(["[circular dependency]", ...circularPath].join("\r\n\t↳ "))

which renders like this

WARNING in [circular-dependency-plugin]
	↳ frontend/modules/foo/foo.js
	↳ frontend/modules/bar/bar.js
	↳ frontend/modules/baz/baz.js
  1. Report detected circles once. Right now, each circle is reported at least twice (for each file involved in the circle). The plugin could track already detected circles and then check each detected new circle against that list.
function isAlreadyTracked(trackedList, detected) {
  for (const circle of trackedList) {
    if (circle.length !== detected.length) {
      continue
    }
    const index = circle.indexOf(detected[0])
    if (index === -1) {
      continue
    }
    const len = detected.length - 1 // first and last entries are the same, skip the last
    return detected.every((it, i) => circle[(i + index) % len] === it)
  }
  return false
}

I might open a pull request, but that might take me some time. I thought i'd share my ideas up front.

Great plugin, helped me a lot.

TypeError: Path must be a string. Received undefined

path.js:8
    throw new TypeError('Path must be a string. Received ' +
    ^

TypeError: Path must be a string. Received undefined
  at assertPath (path.js:8:11)
  at Object.posix.relative (path.js:496:3)
  at /home/michael-heuberger/Development/smx3-frontend/node_modules/circular-dependency-plugin/index.js:42:41
  at Array.forEach (native)
...

Changelog !! No info about changes

I had version 3.0.0. Now version 4.2.1 is out. I want to upgrade but there is no info what have been done.
No info about breaking changes...
In your releases is nothing. And you dont have even changelog.
Only info is in your commits. But commit is not a changelog.

Please start making CHANGELOG - http://keepachangelog.com

Circular Dependencies Listed Twice

Hello!

I'm seeing detections which I would consider to be duplicates.

Imagine that I have a.ts and b.ts and they import from one another, creating a circular dependency. In this case I would expect a single detection like a.ts -> b.ts -> a.ts, but instead I see a.ts -> b.ts -> a.ts and b.ts -> a.ts -> b.ts.

This is easy enough to work around with custom configuration, and I will include mine at the bottom of this message, but I'm wondering if the tool itself should dedupe these? Happy to create an MR if I'm pointed in the right direction. Thanks!

My Dedupe Config:

let circularDependencies = [];

module.exports = {
    exclude: /node_modules/,
    onStart() {
        circularDependencies = [];
    },
    onDetected({ paths }) {
        circularDependencies.push(paths);
    },
    onEnd({ compilation }) {
        // De-dupe paths if they they are identical when wrapped
        // e.g. 'a -> b -> a' and 'b -> a -> b' are identical
        circularDependencies
            .reduce(
                (memo, paths) => {
                    const combinations = [];
                    let currentCombination = [...paths];
                    for (let i = 0; i < paths.length - 1; i++) {
                        const msg = currentCombination.join(' > ');
                        if (memo.asSet.has(msg)) return memo;
                        combinations.push(msg);
                        currentCombination = [...paths.slice(1), paths[1]];
                    }
                    combinations.forEach(msg => memo.asSet.add(msg));
                    memo.asListOfMessages.push(combinations[0]);
                    return memo;
                },
                { asSet: new Set(), asListOfMessages: [] },
            )
            .asListOfMessages.forEach(msg => {
                compilation.warnings.push(
                    `Circular dependency detected:\n${msg}`,
                );
            });
    },
};

Incompatibility with ts-loader

It looks like something breaks this awesome plugin.


index.ts:

import Editor from '@editor/editor'

export default Editor

editor.tsx

// @ts-ignore
import DrawFlow from './draw-flow'

export default class Editor {}

console.log(DrawFlow)

draw-flow.js

export default () => {}


errors: (only with ts-loader)

Circular dependency detected:
packages/editor/src/draw-flow.js -> packages/editor/src/editor.tsx -> packages/editor/src/draw-flow.js

error

Circular dependency detected:
packages/editor/src/editor.tsx -> packages/editor/src/draw-flow.js -> packages/editor/src/editor.tsx


I minimized one of the packages which those errors appeared:

https://github.com/stavalfi/jstream

branch: reproduce-bug

run: yarn install --ignore-scripts && yarn editor

all the webpack stuff is here: cd packages/build/.config/webpack

all this is installed here: packages/build/package.json

"circular-dependency-plugin": "^5.1.0",
"webpack": "^4.35.0",
"ts-loader": "^6.0.0",
"typescript": "^3.5.2",

Please tell me if i can be any assistent to you.

@types/circular-dependency-plugin is not compatible with Webpack 5

Hi.

@types/circular-dependency-plugin has a different webpack version dependency, so Compiler type is incompatible:

      Type '(compiler: import("/Users/d/Documents/git/slapdash/node_modules/@types/circular-dependency-plugin/node_modules/@types/webpack/index").Compiler) => void' is not assignable to type '(compiler: import("/Users/d/Documents/git/slapdash/node_modules/webpack/types").Compiler) => void'.
        Types of parameters 'compiler' and 'compiler' are incompatible.
          Type 'Compiler' is missing the following properties from type 'Compiler': _pluginCompat, _plugins, plugin, apply, and 17 more.

Typescript Type Dependencies Not Found

Steps to Reproduce:

  1. Have a webpack 4 project with typescript.
  2. Create file TableRenderer.ts:
import { Table } from './Table';

const renderTable = (table: Table) => console.log(JSON.stringify(table));
  1. Create file Table.ts:
import * as TableRenderer from './TableRenderer';

export const renderMethods = TableRenderer;
  1. Run webpack with circular-dependency-plugin enabled.

Expected Result: Circular dependency of TableRenderer -> Table -> TableRenderer.
Actual Result: No circular dependency found.

Due to type erasure, and the circular-dependency-plugin's usage of compilation.hooks.optimizeModules, it looks like this isn't easily solvable. Perhaps there is a hook earlier in the webpack life cycle that provides dependencies prior to type erasure? I'm not sure.

Type definitions for TypeScript

Would there be any interest in adding a type definition file to the repo? I have a type definition ready for this package.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.