New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

tslint-no-circular-imports

Package Overview
Dependencies
Maintainers
1
Versions
19
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

tslint-no-circular-imports - npm Package Compare versions

Comparing version 0.5.0 to 0.5.1

tslint.json

208

noCircularImportsRule.js
"use strict";
var __extends = (this && this.__extends) || (function () {
var extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
return extendStatics(d, b);
}
return function (d, b) {

@@ -12,2 +15,6 @@ extendStatics(d, b);

})();
var __makeTemplateObject = (this && this.__makeTemplateObject) || function (cooked, raw) {
if (Object.defineProperty) { Object.defineProperty(cooked, "raw", { value: raw }); } else { cooked.raw = raw; }
return cooked;
};
var __values = (this && this.__values) || function (o) {

@@ -55,4 +62,7 @@ var m = typeof Symbol === "function" && o[Symbol.iterator], i = 0;

imports.delete(resolvedFile);
var walker = new NoCircularImportsWalker(sourceFile, this.getOptions(), program);
return this.applyWithWalker(walker);
var compilerOptions = program.getCompilerOptions();
return this.applyWithFunction(sourceFile, walk, {
compilerOptions: compilerOptions,
rootDir: compilerOptions.rootDir || process.cwd()
}, program.getTypeChecker());
};

@@ -63,3 +73,3 @@ Rule.FAILURE_STRING = 'circular import detected';

description: 'Disallows circular imports.',
rationale: (_a = ["\n Circular dependencies cause hard-to-catch runtime exceptions."], _a.raw = ["\n Circular dependencies cause hard-to-catch runtime exceptions."], Lint.Utils.dedent(_a)),
rationale: Lint.Utils.dedent(templateObject_1 || (templateObject_1 = __makeTemplateObject(["\n Circular dependencies cause hard-to-catch runtime exceptions."], ["\n Circular dependencies cause hard-to-catch runtime exceptions."]))),
optionsDescription: 'Not configurable.',

@@ -79,60 +89,46 @@ options: null,

var nodeModulesRe = new RegExp("\\" + path_1.sep + "node_modules\\" + path_1.sep);
var NoCircularImportsWalker = /** @class */ (function (_super) {
__extends(NoCircularImportsWalker, _super);
function NoCircularImportsWalker(sourceFile, options, program) {
var _this = _super.call(this, sourceFile, options) || this;
_this.program = program;
return _this;
}
NoCircularImportsWalker.prototype.visitSourceFile = function (sourceFile) {
var _this = this;
// Instead of visiting all children, this is faster. We know imports are statements anyway.
sourceFile.statements.forEach(function (statement) {
// export declarations seem to be missing from the current SyntaxWalker
if (ts.isExportDeclaration(statement)) {
_this.visitImportOrExportDeclaration(statement);
}
else if (ts.isImportDeclaration(statement)) {
_this.visitImportOrExportDeclaration(statement);
}
});
var fileName = sourceFile.fileName;
// Check for cycles, remove any cycles that have been found already (otherwise we'll report
// false positive on every files that import from the real cycles, and users will be driven
// mad).
// The checkCycle is many order of magnitude faster than getCycle, but does not keep a history
// of the cycle itself. Only get the full cycle if we found one.
if (this.checkCycle(fileName)) {
var allCycles = this.getAllCycles(fileName);
var _loop_1 = function (maybeCycle) {
function walk(context) {
var e_1, _a;
// Instead of visiting all children, this is faster. We know imports are statements anyway.
context.sourceFile.statements.forEach(function (statement) {
// export declarations seem to be missing from the current SyntaxWalker
if (ts.isExportDeclaration(statement)) {
visitImportOrExportDeclaration(statement);
}
else if (ts.isImportDeclaration(statement)) {
visitImportOrExportDeclaration(statement);
}
});
var fileName = context.sourceFile.fileName;
// Check for cycles, remove any cycles that have been found already (otherwise we'll report
// false positive on every files that import from the real cycles, and users will be driven
// mad).
// The checkCycle is many order of magnitude faster than getCycle, but does not keep a history
// of the cycle itself. Only get the full cycle if we found one.
if (checkCycle(fileName)) {
var allCycles = getAllCycles(fileName);
try {
for (var allCycles_1 = __values(allCycles), allCycles_1_1 = allCycles_1.next(); !allCycles_1_1.done; allCycles_1_1 = allCycles_1.next()) {
var maybeCycle = allCycles_1_1.value;
// Slice the array so we don't match this file twice.
if (maybeCycle.slice(1, -1).some(function (fileName) { return found.has(fileName); })) {
return "continue";
continue;
}
maybeCycle.forEach(function (x) { return found.add(x); });
var node = imports.get(fileName).get(maybeCycle[1]);
var compilerOptions = this_1.program.getCompilerOptions();
this_1.addFailureAt(node.getStart(), node.getWidth(), Rule.FAILURE_STRING + ": " + maybeCycle
context.addFailureAt(node.getStart(), node.getWidth(), Rule.FAILURE_STRING + ': ' + maybeCycle
.concat(fileName)
.map(function (x) { return path_1.relative(compilerOptions.rootDir || process.cwd(), x); })
.map(function (x) { return path_1.relative(context.options.rootDir, x); })
.join(' -> '));
};
var this_1 = this;
}
}
catch (e_1_1) { e_1 = { error: e_1_1 }; }
finally {
try {
for (var allCycles_1 = __values(allCycles), allCycles_1_1 = allCycles_1.next(); !allCycles_1_1.done; allCycles_1_1 = allCycles_1.next()) {
var maybeCycle = allCycles_1_1.value;
_loop_1(maybeCycle);
}
if (allCycles_1_1 && !allCycles_1_1.done && (_a = allCycles_1.return)) _a.call(allCycles_1);
}
catch (e_1_1) { e_1 = { error: e_1_1 }; }
finally {
try {
if (allCycles_1_1 && !allCycles_1_1.done && (_a = allCycles_1.return)) _a.call(allCycles_1);
}
finally { if (e_1) throw e_1.error; }
}
finally { if (e_1) throw e_1.error; }
}
var e_1, _a;
};
NoCircularImportsWalker.prototype.visitImportOrExportDeclaration = function (node) {
}
function visitImportOrExportDeclaration(node) {
if (!node.parent || !ts.isSourceFile(node.parent)) {

@@ -149,4 +145,3 @@ return;

var importFileName = node.moduleSpecifier.text;
var compilerOptions = this.program.getCompilerOptions();
var resolved = ts.resolveModuleName(importFileName, fileName, compilerOptions, ts.sys);
var resolved = ts.resolveModuleName(importFileName, fileName, context.options.compilerOptions, ts.sys);
if (!resolved || !resolved.resolvedModule) {

@@ -161,57 +156,56 @@ return;

}
this.addToGraph(fileName, resolvedImportFileName, node);
};
NoCircularImportsWalker.prototype.addToGraph = function (thisFileName, importCanonicalName, node) {
var i = imports.get(thisFileName);
if (!i) {
imports.set(thisFileName, i = new Map);
addToGraph(fileName, resolvedImportFileName, node);
}
}
function addToGraph(thisFileName, importCanonicalName, node) {
var i = imports.get(thisFileName);
if (!i) {
imports.set(thisFileName, i = new Map);
}
i.set(importCanonicalName, node);
}
function checkCycle(moduleName) {
var accumulator = new Set();
var moduleImport = imports.get(moduleName);
if (!moduleImport)
return false;
var toCheck = Array.from(moduleImport.keys());
for (var i = 0; i < toCheck.length; i++) {
var current = toCheck[i];
if (current === moduleName) {
return true;
}
i.set(importCanonicalName, node);
};
NoCircularImportsWalker.prototype.checkCycle = function (moduleName) {
var accumulator = new Set();
var moduleImport = imports.get(moduleName);
if (!moduleImport)
return false;
var toCheck = Array.from(moduleImport.keys());
for (var i = 0; i < toCheck.length; i++) {
var current = toCheck[i];
if (current == moduleName) {
return true;
}
accumulator.add(current);
toCheck.push.apply(toCheck, __spread(Array.from((imports.get(current) || new Map).keys())
.filter(function (i) { return !accumulator.has(i); })));
accumulator.add(current);
toCheck.push.apply(toCheck, __spread(Array.from((imports.get(current) || new Map).keys())
.filter(function (i) { return !accumulator.has(i); })));
}
return false;
}
function getAllCycles(moduleName, accumulator) {
if (accumulator === void 0) { accumulator = []; }
var e_2, _a;
var moduleImport = imports.get(moduleName);
if (!moduleImport)
return [];
if (accumulator.indexOf(moduleName) !== -1)
return [accumulator];
var all = [];
try {
for (var _b = __values(Array.from(moduleImport.keys())), _c = _b.next(); !_c.done; _c = _b.next()) {
var imp = _c.value;
var c = getAllCycles(imp, accumulator.concat(moduleName));
if (c.length)
all.push.apply(all, __spread(c));
}
return false;
};
NoCircularImportsWalker.prototype.getAllCycles = function (moduleName, accumulator) {
if (accumulator === void 0) { accumulator = []; }
var moduleImport = imports.get(moduleName);
if (!moduleImport)
return [];
if (accumulator.indexOf(moduleName) !== -1)
return [accumulator];
var all = [];
}
catch (e_2_1) { e_2 = { error: e_2_1 }; }
finally {
try {
for (var _a = __values(Array.from(moduleImport.keys())), _b = _a.next(); !_b.done; _b = _a.next()) {
var imp = _b.value;
var c = this.getAllCycles(imp, accumulator.concat(moduleName));
if (c.length)
all.push.apply(all, __spread(c));
}
if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
}
catch (e_2_1) { e_2 = { error: e_2_1 }; }
finally {
try {
if (_b && !_b.done && (_c = _a.return)) _c.call(_a);
}
finally { if (e_2) throw e_2.error; }
}
return all;
var e_2, _c;
};
return NoCircularImportsWalker;
}(Lint.RuleWalker));
var _a;
finally { if (e_2) throw e_2.error; }
}
return all;
}
var templateObject_1;
//# sourceMappingURL=noCircularImportsRule.js.map

@@ -1,6 +0,13 @@

import { relative, sep } from 'path';
import * as Lint from 'tslint';
import { IOptions } from 'tslint/lib/language/rule/rule';
import * as ts from 'typescript';
import { relative, sep } from 'path'
import * as Lint from 'tslint'
import * as ts from 'typescript'
interface Options {
/** @internal */
compilerOptions: ts.CompilerOptions
/** @internal */
rootDir: string
}
export class Rule extends Lint.Rules.TypedRule {

@@ -25,5 +32,12 @@ static FAILURE_STRING = 'circular import detected'

const walker = new NoCircularImportsWalker(sourceFile, this.getOptions(), program)
const compilerOptions = program.getCompilerOptions()
return this.applyWithWalker(walker)
return this.applyWithFunction(
sourceFile,
walk,
{
compilerOptions,
rootDir: compilerOptions.rootDir || process.cwd()
},
program.getTypeChecker())
}

@@ -38,51 +52,43 @@ }

class NoCircularImportsWalker extends Lint.RuleWalker {
constructor(sourceFile: ts.SourceFile, options: IOptions, private program: ts.Program) {
super(sourceFile, options)
}
function walk(context: Lint.WalkContext<Options>) {
// Instead of visiting all children, this is faster. We know imports are statements anyway.
context.sourceFile.statements.forEach(statement => {
// export declarations seem to be missing from the current SyntaxWalker
if (ts.isExportDeclaration(statement)) {
visitImportOrExportDeclaration(statement)
} else if (ts.isImportDeclaration(statement)) {
visitImportOrExportDeclaration(statement)
}
})
visitSourceFile(sourceFile: ts.SourceFile) {
// Instead of visiting all children, this is faster. We know imports are statements anyway.
sourceFile.statements.forEach(statement => {
// export declarations seem to be missing from the current SyntaxWalker
if (ts.isExportDeclaration(statement)) {
this.visitImportOrExportDeclaration(statement)
}
else if (ts.isImportDeclaration(statement)) {
this.visitImportOrExportDeclaration(statement)
}
})
const fileName = context.sourceFile.fileName
const fileName = sourceFile.fileName
// Check for cycles, remove any cycles that have been found already (otherwise we'll report
// false positive on every files that import from the real cycles, and users will be driven
// mad).
// The checkCycle is many order of magnitude faster than getCycle, but does not keep a history
// of the cycle itself. Only get the full cycle if we found one.
if (checkCycle(fileName)) {
const allCycles = getAllCycles(fileName)
// Check for cycles, remove any cycles that have been found already (otherwise we'll report
// false positive on every files that import from the real cycles, and users will be driven
// mad).
// The checkCycle is many order of magnitude faster than getCycle, but does not keep a history
// of the cycle itself. Only get the full cycle if we found one.
if (this.checkCycle(fileName)) {
const allCycles = this.getAllCycles(fileName)
for (const maybeCycle of allCycles) {
// Slice the array so we don't match this file twice.
if (maybeCycle.slice(1, -1).some(fileName => found.has(fileName))) {
continue
}
maybeCycle.forEach(x => found.add(x))
const node = imports.get(fileName) !.get(maybeCycle[1]) !
for (const maybeCycle of allCycles) {
// Slice the array so we don't match this file twice.
if (maybeCycle.slice(1, -1).some(fileName => found.has(fileName))) {
continue
}
maybeCycle.forEach(x => found.add(x))
const node = imports.get(fileName) !.get(maybeCycle[1]) !
const compilerOptions = this.program.getCompilerOptions()
this.addFailureAt(node.getStart(), node.getWidth(), Rule.FAILURE_STRING + ": " + maybeCycle
.concat(fileName)
.map(x => relative(compilerOptions.rootDir || process.cwd(), x))
.join(' -> '))
}
context.addFailureAt(node.getStart(), node.getWidth(), Rule.FAILURE_STRING + ': ' + maybeCycle
.concat(fileName)
.map(x => relative(context.options.rootDir, x))
.join(' -> '))
}
}
visitImportOrExportDeclaration(node: ts.ImportDeclaration | ts.ExportDeclaration) {
function visitImportOrExportDeclaration(node: ts.ImportDeclaration | ts.ExportDeclaration) {
if (!node.parent || !ts.isSourceFile(node.parent)) {
return
}
if(!node.moduleSpecifier) {
if (!node.moduleSpecifier) {
return

@@ -96,5 +102,4 @@ }

const importFileName = node.moduleSpecifier.text
const compilerOptions = this.program.getCompilerOptions()
const resolved = ts.resolveModuleName(importFileName, fileName, compilerOptions, ts.sys)
const resolved = ts.resolveModuleName(importFileName, fileName, context.options.compilerOptions, ts.sys)
if (!resolved || !resolved.resolvedModule) {

@@ -111,53 +116,53 @@ return

this.addToGraph(fileName, resolvedImportFileName, node)
addToGraph(fileName, resolvedImportFileName, node)
}
}
private addToGraph(thisFileName: string, importCanonicalName: string, node: ts.Node) {
let i = imports.get(thisFileName)
if (!i) {
imports.set(thisFileName, i = new Map)
}
i.set(importCanonicalName, node)
function addToGraph(thisFileName: string, importCanonicalName: string, node: ts.Node) {
let i = imports.get(thisFileName)
if (!i) {
imports.set(thisFileName, i = new Map)
}
i.set(importCanonicalName, node)
}
private checkCycle(moduleName: string): boolean {
const accumulator = new Set<string>()
function checkCycle(moduleName: string): boolean {
const accumulator = new Set<string>()
const moduleImport = imports.get(moduleName)
if (!moduleImport)
return false
const moduleImport = imports.get(moduleName)
if (!moduleImport)
return false
const toCheck = Array.from(moduleImport.keys())
for (let i = 0; i < toCheck.length; i++) {
const current = toCheck[i]
if (current == moduleName) {
return true
}
accumulator.add(current)
toCheck.push(
...Array.from((imports.get(current) || new Map).keys())
.filter(i => !accumulator.has(i))
)
const toCheck = Array.from(moduleImport.keys())
for (let i = 0; i < toCheck.length; i++) {
const current = toCheck[i]
if (current === moduleName) {
return true
}
accumulator.add(current)
return false
toCheck.push(
...Array.from((imports.get(current) || new Map).keys())
.filter(i => !accumulator.has(i))
)
}
private getAllCycles(moduleName: string, accumulator: string[] = []): string[][] {
const moduleImport = imports.get(moduleName)
if (!moduleImport) return []
if (accumulator.indexOf(moduleName) !== -1)
return [accumulator]
return false
}
const all: string[][] = []
for (const imp of Array.from(moduleImport.keys())) {
const c = this.getAllCycles(imp, accumulator.concat(moduleName))
function getAllCycles(moduleName: string, accumulator: string[] = []): string[][] {
const moduleImport = imports.get(moduleName)
if (!moduleImport) return []
if (accumulator.indexOf(moduleName) !== -1)
return [accumulator]
if (c.length)
all.push(...c)
}
const all: string[][] = []
for (const imp of Array.from(moduleImport.keys())) {
const c = getAllCycles(imp, accumulator.concat(moduleName))
return all
if (c.length)
all.push(...c)
}
return all
}
{
"name": "tslint-no-circular-imports",
"version": "0.5.0",
"version": "0.5.1",
"description": "TSLint plugin to detect and warn about circular imports",

@@ -28,6 +28,6 @@ "main": "tslint-no-circular-imports.json",

"devDependencies": {
"@types/node": "^4.2.23",
"tslint": "^5.7.0",
"typescript": "^2.5.2"
"@types/node": "^10.7.1",
"tslint": "^5.11.0",
"typescript": "^3.0.1"
}
}
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const assert = require("assert");
const child_process_1 = require("child_process");
const assert = require("assert");
console.log(__dirname);
child_process_1.exec('../node_modules/.bin/tslint -c ./tslint.json -r ../ ./*.ts', { cwd: __dirname }, (error, stdout, stderr) => {
assert.equal(stdout, `
ERROR: case1.ts[1, 1]: circular import detected: case1.ts -> case1.1.ts -> case1.ts
ERROR: case1.ts[2, 1]: circular import detected: case1.ts -> case1.2.ts -> case1.ts
`);
const path_1 = require("path");
const tslintBin = path_1.join('..', 'node_modules', '.bin', 'tslint');
const tslintConfig = path_1.join('.', 'tslint.json');
const tslintFormat = 'json';
const tsFiles = `${path_1.join('.', '*.ts')} ${path_1.join('.', '*/*.ts')}`;
const tsconfig = path_1.join('.', 'tsconfig.json');
child_process_1.exec(`${tslintBin} -p ${tsconfig} -c ${tslintConfig} -r .. -t ${tslintFormat} ${tsFiles}`, { cwd: __dirname }, (error, stdout, stderr) => {
// Only validate failures and names.
const actual = JSON.parse(stdout)
.map(x => ({ failure: x.failure, name: x.name }));
assert.deepEqual(actual, [
// case1
{
failure: 'circular import detected: case1.ts -> case1.2.ts -> case1.ts',
name: path_1.join(__dirname, 'case1.ts')
},
{
failure: 'circular import detected: case1.1.ts -> case1.ts -> case1.1.ts',
name: path_1.join(__dirname, 'case1.1.ts')
},
// case2
{
failure: 'circular import detected: case2/a.ts -> case2/b.ts -> case2/a.ts',
name: path_1.join(__dirname, 'case2/a.ts')
},
// case3
{
failure: 'circular import detected: case3/a.ts -> case3/b.ts -> case3/a.ts',
name: path_1.join(__dirname, 'case3/a.ts')
},
// case4
{
failure: 'circular import detected: case4/a.ts -> case4/index.ts -> case4/a.ts',
name: path_1.join(__dirname, 'case4/a.ts')
}
]);
});
//# sourceMappingURL=test.js.map

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc