Skip to content
This repository has been archived by the owner on Mar 25, 2021. It is now read-only.

Update to latest tsutils, fix import-blacklist #3258

Merged
merged 2 commits into from
Oct 5, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
"resolve": "^1.3.2",
"semver": "^5.3.0",
"tslib": "^1.7.1",
"tsutils": "^2.8.1"
"tsutils": "^2.12.0"
},
"peerDependencies": {
"typescript": ">=2.1.0 || >=2.1.0-dev || >=2.2.0-dev || >=2.3.0-dev || >=2.4.0-dev || >=2.5.0-dev || >=2.6.0-dev || >=2.7.0-dev"
Expand Down
11 changes: 4 additions & 7 deletions src/rules/deprecationRule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@

import {
getDeclarationOfBindingElement,
getJsDoc,
isBindingElement,
isCallExpression,
isIdentifier,
isJsDoc,
isNewExpression,
isPropertyAccessExpression,
isTaggedTemplateExpression,
Expand Down Expand Up @@ -200,14 +200,11 @@ function getDeprecationFromDeclarations(declarations?: ts.Declaration[]): string
}

function getDeprecationFromDeclaration(declaration: ts.Node): string | undefined {
for (const child of declaration.getChildren()) {
if (!isJsDoc(child)) {
break;
}
if (child.tags === undefined) {
for (const comment of getJsDoc(declaration)) {
if (comment.tags === undefined) {
continue;
}
for (const tag of child.tags) {
for (const tag of comment.tags) {
if (tag.tagName.text === "deprecated") {
return tag.comment === undefined ? "" : tag.comment;
}
Expand Down
36 changes: 6 additions & 30 deletions src/rules/importBlacklistRule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,7 @@
* limitations under the License.
*/

import {
isCallExpression, isExternalModuleReference, isIdentifier, isImportDeclaration, isImportEqualsDeclaration, isTextualLiteral,
} from "tsutils";
import { findImports, ImportKind } from "tsutils";
import * as ts from "typescript";
import * as Lint from "../index";

Expand Down Expand Up @@ -51,36 +49,14 @@ export class Rule extends Lint.Rules.AbstractRule {
}

public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] {
return this.applyWithWalker(new ImportBlacklistWalker(sourceFile, this.ruleName, this.ruleArguments));
return this.applyWithFunction(sourceFile, walk, this.ruleArguments);
}
}

class ImportBlacklistWalker extends Lint.AbstractWalker<string[]> {
public walk(sourceFile: ts.SourceFile) {
const findRequire = (node: ts.Node): void => {
if (isCallExpression(node) && node.arguments.length === 1 &&
isIdentifier(node.expression) && node.expression.text === "require") {
this.checkForBannedImport(node.arguments[0]);
}
return ts.forEachChild(node, findRequire);
};

for (const statement of sourceFile.statements) {
if (isImportDeclaration(statement)) {
this.checkForBannedImport(statement.moduleSpecifier);
} else if (isImportEqualsDeclaration(statement)) {
if (isExternalModuleReference(statement.moduleReference) && statement.moduleReference.expression !== undefined) {
this.checkForBannedImport(statement.moduleReference.expression);
}
} else {
ts.forEachChild(statement, findRequire);
}
}
}

private checkForBannedImport(expression: ts.Expression) {
if (isTextualLiteral(expression) && this.options.indexOf(expression.text) !== -1) {
this.addFailure(expression.getStart(this.sourceFile) + 1, expression.end - 1, Rule.FAILURE_STRING);
function walk(ctx: Lint.WalkContext<string[]>) {
for (const name of findImports(ctx.sourceFile, ImportKind.All)) {
if (ctx.options.indexOf(name.text) !== -1) {
ctx.addFailure(name.getStart(ctx.sourceFile) + 1, name.end - 1, Rule.FAILURE_STRING);
}
}
}
36 changes: 9 additions & 27 deletions src/rules/noDuplicateImportsRule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
* limitations under the License.
*/

import { isImportDeclaration, isModuleDeclaration, isTextualLiteral } from "tsutils";
import { findImports, ImportKind } from "tsutils";
import * as ts from "typescript";
import * as Lint from "../index";

Expand All @@ -40,35 +40,17 @@ export class Rule extends Lint.Rules.AbstractRule {
}

public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] {
return this.applyWithWalker(new NoDuplicateImportsWalker(sourceFile, this.ruleName, undefined));
return this.applyWithFunction(sourceFile, walk);
}
}

class NoDuplicateImportsWalker extends Lint.AbstractWalker<void> {
private seenImports = new Set<string>();

public walk(sourceFile: ts.SourceFile) {
this.checkStatements(sourceFile.statements);
}

private checkStatements(statements: ts.NodeArray<ts.Statement>) {
for (const statement of statements) {
if (isImportDeclaration(statement)) {
this.checkImport(statement);
} else if (this.sourceFile.isDeclarationFile && isModuleDeclaration(statement) &&
statement.body !== undefined && statement.name.kind === ts.SyntaxKind.StringLiteral) {
// module augmentations in declaration files can contain imports
this.checkStatements((statement.body as ts.ModuleBlock).statements);
}
}
}

private checkImport(statement: ts.ImportDeclaration) {
if (isTextualLiteral(statement.moduleSpecifier)) {
if (this.seenImports.has(statement.moduleSpecifier.text)) {
return this.addFailureAtNode(statement, Rule.FAILURE_STRING(statement.moduleSpecifier.text));
}
this.seenImports.add(statement.moduleSpecifier.text);
function walk(ctx: Lint.WalkContext<void>) {
const seen = new Set<string>();
for (const {text, parent} of findImports(ctx.sourceFile, ImportKind.ImportDeclaration)) {
if (seen.has(text)) {
ctx.addFailureAtNode(parent!, Rule.FAILURE_STRING(text));
} else {
seen.add(text);
}
}
}
51 changes: 9 additions & 42 deletions src/rules/noReferenceImportRule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
* limitations under the License.
*/

import { isImportDeclaration, isImportEqualsDeclaration, isModuleDeclaration, isStringLiteral } from "tsutils";
import { findImports, ImportKind } from "tsutils";
import * as ts from "typescript";

import * as Lint from "../index";
Expand All @@ -37,51 +37,18 @@ export class Rule extends Lint.Rules.AbstractRule {
}

public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] {
return this.applyWithWalker(new NoReferenceImportWalker(sourceFile, this.ruleName, undefined));
return this.applyWithFunction(sourceFile, walk);
}
}

class NoReferenceImportWalker extends Lint.AbstractWalker<void> {
private imports = new Set<string>();
public walk(sourceFile: ts.SourceFile) {
if (sourceFile.typeReferenceDirectives.length === 0) {
return;
}
this.findImports(sourceFile.statements);
for (const ref of sourceFile.typeReferenceDirectives) {
if (this.imports.has(ref.fileName)) {
this.addFailure(ref.pos, ref.end, Rule.FAILURE_STRING(ref.fileName));
}
}
}

private findImports(statements: ReadonlyArray<ts.Statement>) {
for (const statement of statements) {
if (isImportDeclaration(statement)) {
this.addImport(statement.moduleSpecifier);
} else if (isImportEqualsDeclaration(statement)) {
if (statement.moduleReference.kind === ts.SyntaxKind.ExternalModuleReference &&
statement.moduleReference.expression !== undefined) {
this.addImport(statement.moduleReference.expression);
}
} else if (isModuleDeclaration(statement) && statement.body !== undefined && this.sourceFile.isDeclarationFile) {
// There can't be any imports in a module augmentation or namespace
this.findImportsInModule(statement.body);
}
}
function walk(ctx: Lint.WalkContext<void>) {
if (ctx.sourceFile.typeReferenceDirectives.length === 0) {
return;
}

private findImportsInModule(body: ts.ModuleBody): void {
if (body.kind === ts.SyntaxKind.ModuleBlock) {
return this.findImports(body.statements);
} else if (body.kind === ts.SyntaxKind.ModuleDeclaration && body.body !== undefined) {
return this.findImportsInModule(body.body);
}
}

private addImport(specifier: ts.Expression) {
if (isStringLiteral(specifier)) {
this.imports.add(specifier.text);
const imports = new Set(findImports(ctx.sourceFile, ImportKind.AllStaticImports).map((name) => name.text));
for (const ref of ctx.sourceFile.typeReferenceDirectives) {
if (imports.has(ref.fileName)) {
ctx.addFailure(ref.pos, ref.end, Rule.FAILURE_STRING(ref.fileName));
}
}
}
17 changes: 4 additions & 13 deletions src/rules/noRequireImportsRule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
* limitations under the License.
*/

import { isCallExpression, isIdentifier, isImportEqualsDeclaration } from "tsutils";
import { findImports, ImportKind } from "tsutils";
import * as ts from "typescript";

import * as Lint from "../index";
Expand All @@ -42,16 +42,7 @@ export class Rule extends Lint.Rules.AbstractRule {
}

function walk(ctx: Lint.WalkContext<void>) {
return ts.forEachChild(ctx.sourceFile, function cb(node): void {
if (isCallExpression(node)) {
if (node.arguments.length !== 0 &&
isIdentifier(node.expression) && node.expression.text === "require") {
ctx.addFailureAtNode(node, Rule.FAILURE_STRING);
}
} else if (isImportEqualsDeclaration(node) &&
node.moduleReference.kind === ts.SyntaxKind.ExternalModuleReference) {
ctx.addFailureAtNode(node.moduleReference, Rule.FAILURE_STRING);
}
return ts.forEachChild(node, cb);
});
for (const name of findImports(ctx.sourceFile, ImportKind.AllRequireLike)) {
ctx.addFailureAtNode(name.parent!, Rule.FAILURE_STRING);
}
}
68 changes: 16 additions & 52 deletions src/rules/noSubmoduleImportsRule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,7 @@
* limitations under the License.
*/

import {
isCallExpression,
isExternalModuleReference,
isIdentifier,
isImportDeclaration,
isImportEqualsDeclaration,
isTextualLiteral,
} from "tsutils";
import { findImports, ImportKind } from "tsutils";
import * as ts from "typescript";
import * as Lint from "../index";

Expand Down Expand Up @@ -51,59 +44,30 @@ export class Rule extends Lint.Rules.AbstractRule {
public static FAILURE_STRING = "Submodule import paths from this package are disallowed; import from the root instead";

public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] {
return this.applyWithWalker(new NoSubmoduleImportsWalker(sourceFile, this.ruleName, this.ruleArguments));
return this.applyWithFunction(sourceFile, walk, this.ruleArguments);
}
}

class NoSubmoduleImportsWalker extends Lint.AbstractWalker<string[]> {
public walk(sourceFile: ts.SourceFile) {
const findDynamicImport = (node: ts.Node): void => {
if (isCallExpression(node) && node.arguments.length === 1 &&
(isIdentifier(node.expression) && node.expression.text === "require" ||
node.expression.kind === ts.SyntaxKind.ImportKeyword)) {
this.checkForBannedImport(node.arguments[0]);
}
return ts.forEachChild(node, findDynamicImport);
};

for (const statement of sourceFile.statements) {
if (isImportDeclaration(statement)) {
this.checkForBannedImport(statement.moduleSpecifier);
} else if (isImportEqualsDeclaration(statement)) {
if (isExternalModuleReference(statement.moduleReference) && statement.moduleReference.expression !== undefined) {
this.checkForBannedImport(statement.moduleReference.expression);
}
} else {
ts.forEachChild(statement, findDynamicImport);
}
function walk(ctx: Lint.WalkContext<string[]>) {
for (const name of findImports(ctx.sourceFile, ImportKind.All)) {
// TODO remove assertion on upgrade to typescript@2.5.2
if (!(ts as any as {isExternalModuleNameRelative(m: string): boolean}).isExternalModuleNameRelative(name.text) &&
isSubmodulePath(name.text) &&
!isWhitelisted(name.text, ctx.options)) {
ctx.addFailureAtNode(name, Rule.FAILURE_STRING);
}
}
}

private checkForBannedImport(expression: ts.Expression) {
if (isTextualLiteral(expression) &&
// TODO remove assertion on upgrade to typescript@2.5.2
!(ts as any as {isExternalModuleNameRelative(m: string): boolean}).isExternalModuleNameRelative(expression.text) &&
isSubmodulePath(expression.text)) {
/*
* A submodule is being imported.
* Check if its path contains any
* of the whitelist packages.
*/
for (const option of this.options) {
if (expression.text === option || expression.text.startsWith(`${option}/`)) {
return;
}
}

this.addFailureAtNode(expression, Rule.FAILURE_STRING);
function isWhitelisted(path: string, whitelist: string[]): boolean {
for (const option of whitelist) {
if (path === option || path.startsWith(`${option}/`)) {
return true;
}
}
}

function isScopedPath(path: string): boolean {
return path[0] === "@";
return false;
}

function isSubmodulePath(path: string): boolean {
return path.split("/").length > (isScopedPath(path) ? 2 : 1);
return path.split("/").length > (path[0] === "@" ? 2 : 1);
}
3 changes: 3 additions & 0 deletions test/rules/import-blacklist/test.ts.lint
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,7 @@ import forOwn = require(lodash);

import * as notBlacklisted from "not-blacklisted";

export * from 'lodash';
~~~~~~ [0]

[0]: This import is blacklisted, import a submodule instead
3 changes: 3 additions & 0 deletions test/rules/no-submodule-imports/static-imports/test.ts.lint
Original file line number Diff line number Diff line change
Expand Up @@ -67,4 +67,7 @@ import { submodule } from "../myModule/a/package";
import submodule = require("./../node_modules/package");
import submodule = require("../myModule/a/package");

export * from "@angular/http/testing";
~~~~~~~~~~~~~~~~~~~~~~~ [0]

[0]: Submodule import paths from this package are disallowed; import from the root instead
8 changes: 7 additions & 1 deletion yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,7 @@ chalk@^1.1.0, chalk@^1.1.3:
strip-ansi "^3.0.0"
supports-color "^2.0.0"

chalk@^2.1.0:
chalk@^2.0.0, chalk@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.1.0.tgz#ac5becf14fa21b99c6c92ca7a7d7cfd5b17e743e"
dependencies:
Expand Down Expand Up @@ -1584,6 +1584,12 @@ tslint@^5.7.0:
tslib "^1.7.1"
tsutils "^2.8.1"

tsutils@^2.12.0:
version "2.12.0"
resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-2.12.0.tgz#c892a84c8f2f8de13f8ef32c2c5c38b457cceac6"
dependencies:
tslib "^1.7.1"

tsutils@^2.8.1:
version "2.8.1"
resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-2.8.1.tgz#3771404e7ca9f0bedf5d919a47a4b1890a68efff"
Expand Down