Skip to content

Commit 0d93406

Browse files
committed
feat(@angular/cli): add circular dependency detection
Circular dependencies, like `app.module.ts` importing `app.component.ts` which in turn imports `app.module.ts`, now display a warning during builds: ``` kamik@T460p MINGW64 /d/sandbox/master-project (master) $ ng build Hash: 3516b252f4e32d6c5bb8 Time: 8693ms chunk {0} polyfills.bundle.js, polyfills.bundle.js.map (polyfills) 160 kB {4} [initial] [rendered] chunk {1} main.bundle.js, main.bundle.js.map (main) 5.95 kB {3} [initial] [rendered] chunk {2} styles.bundle.js, styles.bundle.js.map (styles) 10.5 kB {4} [initial] [rendered] chunk {3} vendor.bundle.js, vendor.bundle.js.map (vendor) 1.88 MB [initial] [rendered] chunk {4} inline.bundle.js, inline.bundle.js.map (inline) 0 bytes [entry] [rendered] WARNING in Circular dependency detected: src\app\app.module.ts -> src\app\app.component.ts -> src\app\app.module.ts WARNING in Circular dependency detected: src\app\app.component.ts -> src\app\app.module.ts -> src\app\app.component.ts ``` It is important to detect and eliminate circular dependencies because leaving them in might lead to `Maximum call stack size exceeded` errors, or imports being `undefined` at runtime. To remove these warnings from your project you can factor out the circular dependency into a separate module. For instance, if module A imports `foo` from module B, and module B imports `bar` from module A, it is enough to extract `foo` into module C. You can turn off these warnings by running ng set apps.0.hideCircularDependencyWarnings=true. This will add the "hideCircularDependencyWarnings": true value to your .angular-cli.json and disable the warnings. Fix angular#6309 Fix angular#6739
1 parent 8867daf commit 0d93406

File tree

7 files changed

+37
-0
lines changed

7 files changed

+37
-0
lines changed

docs/documentation/angular-cli.md

+1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
- *testTsconfig* (`string`): The name of the TypeScript configuration file for unit tests.
2424
- *prefix* (`string`): The prefix to apply to generated selectors.
2525
- *serviceWorker* (`boolean`): Experimental support for a service worker from @angular/service-worker. Default is `false`.
26+
- *hideCircularDependencyWarnings* (`boolean`): Hide circular dependency warnings on builds. Default is `false`.
2627
- *styles* (`string|array`): Global styles to be included in the build.
2728
- *stylePreprocessorOptions* : Options to pass to style preprocessors.
2829
- *includePaths* (`array`): Paths to include. Paths will be resolved to project root.

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
"dependencies": {
4343
"autoprefixer": "^6.5.3",
4444
"chalk": "^1.1.3",
45+
"circular-dependency-plugin": "^3.0.0",
4546
"common-tags": "^1.3.1",
4647
"css-loader": "^0.28.1",
4748
"cssnano": "^3.10.0",

packages/@angular/cli/lib/config/schema.json

+5
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,11 @@
118118
"type": "boolean",
119119
"default": false
120120
},
121+
"hideCircularDependencyWarnings": {
122+
"description": "Hide circular dependency warnings on builds.",
123+
"type": "boolean",
124+
"default": false
125+
},
121126
"styles": {
122127
"description": "Global styles to be included in the build.",
123128
"type": "array",

packages/@angular/cli/models/webpack-configs/common.ts

+7
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { extraEntryParser, getOutputHashFormat } from './utils';
55
import { WebpackConfigOptions } from '../webpack-config';
66

77
const ProgressPlugin = require('webpack/lib/ProgressPlugin');
8+
const CircularDependencyPlugin = require('circular-dependency-plugin');
89

910

1011
/**
@@ -72,6 +73,12 @@ export function getCommonConfig(wco: WebpackConfigOptions) {
7273
}));
7374
}
7475

76+
if (!appConfig.hideCircularDependencyWarnings) {
77+
extraPlugins.push(new CircularDependencyPlugin({
78+
exclude: /(\\|\/)node_modules(\\|\/)/
79+
}));
80+
}
81+
7582
return {
7683
resolve: {
7784
extensions: ['.ts', '.js'],

packages/@angular/cli/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
"@ngtools/webpack": "1.5.0-rc.1",
3232
"autoprefixer": "^6.5.3",
3333
"chalk": "^1.1.3",
34+
"circular-dependency-plugin": "^3.0.0",
3435
"common-tags": "^1.3.1",
3536
"css-loader": "^0.28.1",
3637
"cssnano": "^3.10.0",

packages/@angular/cli/tasks/eject.ts

+4
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ const ExtractTextPlugin = require('extract-text-webpack-plugin');
2222
const HtmlWebpackPlugin = require('html-webpack-plugin');
2323
const SilentError = require('silent-error');
2424
const licensePlugin = require('license-webpack-plugin');
25+
const CircularDependencyPlugin = require('circular-dependency-plugin');
2526
const Task = require('../ember-cli/lib/models/task');
2627

2728
const ProgressPlugin = require('webpack/lib/ProgressPlugin');
@@ -178,6 +179,9 @@ class JsonWebpackSerializer {
178179
args = this._extractTextPluginSerialize(plugin);
179180
this.variableImports['extract-text-webpack-plugin'] = 'ExtractTextPlugin';
180181
break;
182+
case CircularDependencyPlugin:
183+
this.variableImports['circular-dependency-plugin'] = 'CircularDependencyPlugin';
184+
break;
181185
case AotPlugin:
182186
args = this._aotPluginSerialize(plugin);
183187
this._addImport('@ngtools/webpack', 'AotPlugin');
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { prependToFile } from '../../utils/fs';
2+
import { ng } from '../../utils/process';
3+
4+
5+
export default async function () {
6+
await prependToFile('src/app/app.component.ts',
7+
`import { AppModule } from './app.module'; console.log(AppModule);`);
8+
let output = await ng('build');
9+
if (!output.stdout.match(/WARNING in Circular dependency detected/)) {
10+
throw new Error('Expected to have circular dependency warning in output.');
11+
}
12+
13+
await ng('set', 'apps.0.hideCircularDependencyWarnings=true');
14+
output = await ng('build');
15+
if (output.stdout.match(/WARNING in Circular dependency detected/)) {
16+
throw new Error('Expected to not have circular dependency warning in output.');
17+
}
18+
}

0 commit comments

Comments
 (0)