importPatternsRule.ts 2.6 KB
Newer Older
1 2 3 4 5 6
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/

import * as ts from 'typescript';
E
Erich Gamma 已提交
7
import * as Lint from 'tslint';
8
import * as minimatch from 'minimatch';
9
import { join } from 'path';
10 11 12

interface ImportPatternsConfig {
	target: string;
13
	restrictions: string | string[];
14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
}

export class Rule extends Lint.Rules.AbstractRule {
	public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] {

		const configs = <ImportPatternsConfig[]>this.getOptions().ruleArguments;


		for (const config of configs) {
			if (minimatch(sourceFile.fileName, config.target)) {
				return this.applyWithWalker(new ImportPatterns(sourceFile, this.getOptions(), config));
			}
		}

		return [];
	}
}

class ImportPatterns extends Lint.RuleWalker {

	constructor(file: ts.SourceFile, opts: Lint.IOptions, private _config: ImportPatternsConfig) {
		super(file, opts);
	}

38 39 40 41 42 43
	protected visitImportEqualsDeclaration(node: ts.ImportEqualsDeclaration): void {
		if (node.moduleReference.kind === ts.SyntaxKind.ExternalModuleReference) {
			this._validateImport(node.moduleReference.expression.getText(), node);
		}
	}

44
	protected visitImportDeclaration(node: ts.ImportDeclaration): void {
45 46
		this._validateImport(node.moduleSpecifier.getText(), node);
	}
47

48 49 50 51 52 53 54 55 56 57
	protected visitCallExpression(node: ts.CallExpression): void {
		super.visitCallExpression(node);

		// import('foo') statements inside the code
		if (node.expression.kind === ts.SyntaxKind.ImportKeyword) {
			const [path] = node.arguments;
			this._validateImport(path.getText(), node);
		}
	}

58
	private _validateImport(path: string, node: ts.Node): void {
59 60 61
		// remove quotes
		path = path.slice(1, -1);

62
		// resolve relative paths
63
		if (path[0] === '.') {
64
			path = join(this.getSourceFile().fileName, path);
65 66
		}

67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83
		let restrictions: string[];
		if (typeof this._config.restrictions === 'string') {
			restrictions = [this._config.restrictions];
		} else {
			restrictions = this._config.restrictions;
		}

		let matched = false;
		for (const pattern of restrictions) {
			if (minimatch(path, pattern)) {
				matched = true;
				break;
			}
		}

		if (!matched) {
			// None of the restrictions matched
84
			this.addFailure(this.createFailure(node.getStart(), node.getWidth(), `Imports violates '${restrictions.join(' or ')}' restrictions. See https://github.com/Microsoft/vscode/wiki/Code-Organization`));
85 86 87
		}
	}
}