Skip to content

Commit

Permalink
Update to latest tsutils, fix import-blacklist (palantir#3258)
Browse files Browse the repository at this point in the history
  • Loading branch information
ajafff authored and HyphnKnight committed Apr 9, 2018
1 parent 791e5a2 commit c935f68
Show file tree
Hide file tree
Showing 10 changed files with 62 additions and 173 deletions.
2 changes: 1 addition & 1 deletion package.json
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
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
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
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
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
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
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
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
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
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

0 comments on commit c935f68

Please sign in to comment.