aackerman / circular-dependency-plugin Goto Github PK
View Code? Open in Web Editor NEWDetect circular dependencies in modules compiled with Webpack
Home Page: https://www.npmjs.com/package/circular-dependency-plugin
License: ISC License
Detect circular dependencies in modules compiled with Webpack
Home Page: https://www.npmjs.com/package/circular-dependency-plugin
License: ISC License
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.
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()
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.
Would there be any interest in adding a type definition file to the repo? I have a type definition ready for this package.
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(),
}),
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.
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.
It appears that this plugin is expecting a RegExp
, but it'd be nice if it accepted multiple types consistent with Webpack: https://webpack.js.org/configuration/module/#condition
Steps to Reproduce:
TableRenderer.ts
:import { Table } from './Table';
const renderTable = (table: Table) => console.log(JSON.stringify(table));
Table.ts
:import * as TableRenderer from './TableRenderer';
export const renderMethods = TableRenderer;
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.
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?
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.
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.
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?
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/
}
Upgrading to latest circular-dependency-plugin causes performance degradation on recompile in dev mode.
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",
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"
}
}
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
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!
it might be helpful to update the messaging that this plugin has when it detects a circular-dependency as some users are seeing the errors in dependent modules and reporting them, I can create a pull that notes that a circular dependency isn't an error by itself or some such
see react-bootstrap/react-bootstrap#2147, nodejs/readable-stream#280, and unshiftio/url-parse#49
Currently the plugin lists all file paths having circular dependencies in absolute paths. This is too wide in my console.
Would be good to list them just relative from the project folder.
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.jserror
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.
The note on allowAsyncCycles gives an example of using it for weak imports:
// allow import cycles that include an asyncronous import,
// e.g. via import(/* webpackMode: "weak" */ './file.js')
allowAsyncCycles: false,
But it would be nice if this also worked for regular async imports that aren't marked with webpackMode: "weak"
. It sounds like the default mode for async imports is lazy
. https://webpack.js.org/api/module-methods/#magic-comments
could you add var CircularDependencyPlugin = require('circular-dependency-plugin');
to the readme?
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",
}
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
const CircularDependencyPlugin = require('circular-dependency-plugin');
plugins: [
new CircularDependencyPlugin({
exclude: /a.js|node_modules/,
include: /src/,
failOnError: true,
})
]
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?
Is the order of cyclic modules that are output in the paths
array consistent between builds?
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.
Would be neat, thanks
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)
I have following suggestions to improve the readability of the error output
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.
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
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.
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
I'd love to run this for my nodejs server. Do you think you could port this to use without webpack?
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.
It will be really helpful for find commits if the repository had release branches and/or version tags
E.g.
exclude: /node_modules|src\/components\/myComponent/,
works on Linux but on Windows I get the cyclic dependency errors for that component.
A workaround is to use [\/\\]
instead of just \/
. Is there a better way?
would be good
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 }),
],
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
ERROR in Circular dependency detected:
src/skin/_styles.scss -> src/skin/_styles.scss
Sass file itself is 100% okay.
The error occurred with Webpack 5.
I am using style-loader
-> css-loader
-> fast-sass-loader
.
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
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}`,
);
});
},
};
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!
Here in webpack 2: https://webpack.js.org/guides/code-splitting-import/
Spec: https://github.com/tc39/proposal-dynamic-import
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)
...
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!
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)
Webpack 4 prints following deprecation warning when CircularDependencyPlugin is enabled.
(node:23969) DeprecationWarning: Tapable.plugin is deprecated. Use new API on `.hooks` instead
Having some trouble configuring this plugin to work on the code!
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.