diff --git a/package.json b/package.json index efe147d84f9..dee6299dfc8 100644 --- a/package.json +++ b/package.json @@ -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" diff --git a/src/rules/deprecationRule.ts b/src/rules/deprecationRule.ts index ed9997a0cbd..a6847921ded 100644 --- a/src/rules/deprecationRule.ts +++ b/src/rules/deprecationRule.ts @@ -17,10 +17,10 @@ import { getDeclarationOfBindingElement, + getJsDoc, isBindingElement, isCallExpression, isIdentifier, - isJsDoc, isNewExpression, isPropertyAccessExpression, isTaggedTemplateExpression, @@ -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; } diff --git a/src/rules/importBlacklistRule.ts b/src/rules/importBlacklistRule.ts index c1f8738737a..c8a3dcb95a0 100644 --- a/src/rules/importBlacklistRule.ts +++ b/src/rules/importBlacklistRule.ts @@ -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"; @@ -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 { - 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) { + 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); } } } diff --git a/src/rules/noDuplicateImportsRule.ts b/src/rules/noDuplicateImportsRule.ts index d1f46f048f4..25a0e3fba17 100644 --- a/src/rules/noDuplicateImportsRule.ts +++ b/src/rules/noDuplicateImportsRule.ts @@ -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"; @@ -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 { - private seenImports = new Set(); - - public walk(sourceFile: ts.SourceFile) { - this.checkStatements(sourceFile.statements); - } - - private checkStatements(statements: ts.NodeArray) { - 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) { + const seen = new Set(); + 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); } } } diff --git a/src/rules/noReferenceImportRule.ts b/src/rules/noReferenceImportRule.ts index bb2cb28d6d2..57a2205df9c 100644 --- a/src/rules/noReferenceImportRule.ts +++ b/src/rules/noReferenceImportRule.ts @@ -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"; @@ -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 { - private imports = new Set(); - 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) { - 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) { + 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)); } } } diff --git a/src/rules/noRequireImportsRule.ts b/src/rules/noRequireImportsRule.ts index 64378df484b..53584a15ab6 100644 --- a/src/rules/noRequireImportsRule.ts +++ b/src/rules/noRequireImportsRule.ts @@ -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"; @@ -42,16 +42,7 @@ export class Rule extends Lint.Rules.AbstractRule { } function walk(ctx: Lint.WalkContext) { - 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); + } } diff --git a/src/rules/noSubmoduleImportsRule.ts b/src/rules/noSubmoduleImportsRule.ts index 7630fb9416b..5ed3536a03d 100644 --- a/src/rules/noSubmoduleImportsRule.ts +++ b/src/rules/noSubmoduleImportsRule.ts @@ -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"; @@ -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 { - 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) { + 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); } diff --git a/test/rules/import-blacklist/test.ts.lint b/test/rules/import-blacklist/test.ts.lint index 2a2be028fd4..14887486fd6 100644 --- a/test/rules/import-blacklist/test.ts.lint +++ b/test/rules/import-blacklist/test.ts.lint @@ -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 diff --git a/test/rules/no-submodule-imports/static-imports/test.ts.lint b/test/rules/no-submodule-imports/static-imports/test.ts.lint index 59715145e31..75ddc078a89 100644 --- a/test/rules/no-submodule-imports/static-imports/test.ts.lint +++ b/test/rules/no-submodule-imports/static-imports/test.ts.lint @@ -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 diff --git a/yarn.lock b/yarn.lock index 252c03653fa..749306ad054 100644 --- a/yarn.lock +++ b/yarn.lock @@ -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: @@ -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"