Skip to content
This repository has been archived by the owner on Jul 15, 2023. It is now read-only.

Commit

Permalink
[Issue #125] new rule: import name must match export name
Browse files Browse the repository at this point in the history
closes #125
  • Loading branch information
HamletDRC committed May 10, 2016
1 parent a5b3980 commit d4e29e5
Show file tree
Hide file tree
Showing 4 changed files with 160 additions and 0 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ Rule Name | Description | Since
:---------- | :------------ | -------------
`chai-vague-errors` | Avoid Chai assertions that result in vague errors. For example, asserting `expect(something).to.be.true` will result in the failure message "Expected true received false". This is a vague error message that does not reveal the underlying problem. It is especially vague in TypeScript because stack trace line numbers often do not match the source code. A better pattern to follow is the xUnit Patterns [Assertion Message](http://xunitpatterns.com/Assertion%20Message.html) pattern. The previous code sample could be better written as `expect(something).to.equal(true, 'expected something to have occurred');`| 1.0
`export-name` | The name of the exported module must match the filename of the source file. This is case-sensitive but ignores file extension. Since version 1.0, this rule takes a list of regular expressions as a parameter. Any export name matching that regular expression will be ignored. For example, to allow an exported name like myChartOptions, then configure the rule like this: "export-name": \[true, "myChartOptionsg"\]| 0.0.3
`import-name` | The name of the imported module must match the name of the thing being imported. For example, it is valid to name imported modules the same as the module name: `import Service = require('x/y/z/Service')` and `import Service from 'x/y/z/Service'`. But it is invalid to change the name being imported, such as: `import MyCoolService = require('x/y/z/Service')` and `import MyCoolService from 'x/y/z/Service'`. | 2.0.5
`jquery-deferred-must-complete` | When a JQuery Deferred instance is created, then either reject() or resolve() must be called on it within all code branches in the scope. For more examples see the [feature request](https://github.com/Microsoft/tslint-microsoft-contrib/issues/26). | 1.0
`max-func-body-length` | Avoid long functions. The line count of a function body must not exceed the value configured within this rule's options. <br>You can setup a general max function body length applied for every function/method/arrow function e.g. \[true, 30\] or set different maximum length for every type e.g. \[true, \{ "func-body-length": 10 , "arrow-body-length": 5, "method-body-length": 15, "ctor-body-length": 5 \}\]. To specify a function name whose parameters you can ignore for this rule, pass a regular expression as a string(this can be useful for Mocha users to ignore the describe() function) | 2.0.3
`missing-jsdoc` | All files must have a top level [JSDoc](http://usejsdoc.org/) comment. A JSDoc comment starts with /** (not one more or one less asterisk) and a JSDoc at the 'top-level' appears without leading spaces. Trailing spaces are acceptable but not recommended. | 1.0
Expand Down
55 changes: 55 additions & 0 deletions src/importNameRule.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import * as ts from 'typescript';
import * as Lint from 'tslint/lib/lint';

import ErrorTolerantWalker = require('./utils/ErrorTolerantWalker');
import SyntaxKind = require('./utils/SyntaxKind');

/**
* Implementation of the import-name rule.
*/
export class Rule extends Lint.Rules.AbstractRule {
public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] {
return this.applyWithWalker(new ImportNameRuleWalker(sourceFile, this.getOptions()));
}
}

class ImportNameRuleWalker extends ErrorTolerantWalker {

protected visitImportEqualsDeclaration(node: ts.ImportEqualsDeclaration): void {
let name: string = node.name.text;

if (node.moduleReference.kind === SyntaxKind.current().ExternalModuleReference) {
let moduleRef: ts.ExternalModuleReference = <ts.ExternalModuleReference>node.moduleReference;
if (moduleRef.expression.kind === SyntaxKind.current().StringLiteral) {
let moduleName: string = (<ts.StringLiteral>moduleRef.expression).text;
this.validateImport(node, name, moduleName);
}
} else if (node.moduleReference.kind === SyntaxKind.current().QualifiedName) {
let moduleName = node.moduleReference.getText();
moduleName = moduleName.replace(/.*\./, ''); // chop off the qualified parts
this.validateImport(node, name, moduleName);
}
super.visitImportEqualsDeclaration(node);
}


protected visitImportDeclaration(node: ts.ImportDeclaration): void {
if (node.importClause.name != null) {
let name: string = node.importClause.name.text;
if (node.moduleSpecifier.kind === SyntaxKind.current().StringLiteral) {
let moduleName: string = (<ts.StringLiteral>node.moduleSpecifier).text;
this.validateImport(node, name, moduleName);
}
}
super.visitImportDeclaration(node);
}

private validateImport(node: ts.Node, importedName: string, moduleName: string): void {
moduleName = moduleName.replace(/.*\//, ''); // chop off the path
if (moduleName !== importedName) {
let message: string = `Misnamed import. Import should be named '${moduleName}' but found '${importedName}'`;
this.addFailure(this.createFailure(node.getStart(), node.getWidth(), message));
}
}

}
103 changes: 103 additions & 0 deletions tests/ImportNameRuleTests.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
/// <reference path="../typings/mocha.d.ts" />
/// <reference path="../typings/chai.d.ts" />

/* tslint:disable:quotemark */
/* tslint:disable:no-multiline-string */

import TestHelper = require('./TestHelper');

/**
* Unit tests.
*/
describe('importNameRule', () : void => {

var ruleName : string = 'import-name';

it('should pass on matching names of external module', () : void => {
var script : string = `
import App = require('App');
import App = require('x/y/z/App');
`;

TestHelper.assertViolations(ruleName, script, [ ]);
});

it('should pass on matching names of ES6 import', () : void => {
var script : string = `
import App from 'App';
import App from 'x/y/z/App';
`;

TestHelper.assertViolations(ruleName, script, [ ]);
});

it('should pass on matching names of simple import', () : void => {
var script : string = `
import DependencyManager = DM.DependencyManager;
`;

TestHelper.assertViolations(ruleName, script, [ ]);
});

it('should fail on misnamed external module', () : void => {
var script : string = `
import MyCoolApp = require('App');
import MyCoolApp2 = require('x/y/z/App');
`;

TestHelper.assertViolations(ruleName, script, [
{
"failure": "Misnamed import. Import should be named 'App' but found 'MyCoolApp'",
"name": "file.ts",
"ruleName": "import-name",
"startPosition": { "character": 13, "line": 2 }
},
{
"failure": "Misnamed import. Import should be named 'App' but found 'MyCoolApp2'",
"name": "file.ts",
"ruleName": "import-name",
"startPosition": { "character": 13, "line": 3 }
}
]);
});

it('should fail on misnamed import', () : void => {
var script : string = `
import MyCoolApp from 'App';
import MyCoolApp2 from 'x/y/z/App';
`;

TestHelper.assertViolations(ruleName, script, [
{
"failure": "Misnamed import. Import should be named 'App' but found 'MyCoolApp'",
"name": "file.ts",
"ruleName": "import-name",
"startPosition": { "character": 13, "line": 2 }
},
{
"failure": "Misnamed import. Import should be named 'App' but found 'MyCoolApp2'",
"name": "file.ts",
"ruleName": "import-name",
"startPosition": { "character": 13, "line": 3 }
}
]);
});

it('should fail on misnamed rename', () : void => {
var script : string = `
import Service = DM.DependencyManager;
`;

TestHelper.assertViolations(ruleName, script, [
{
"failure": "Misnamed import. Import should be named 'DependencyManager' but found 'Service'",
"name": "file.ts",
"ruleName": "import-name",
"startPosition": { "character": 13, "line": 2 }
}
]);
});

});
/* tslint:enable:quotemark */
/* tslint:enable:no-multiline-string */
1 change: 1 addition & 0 deletions tslint.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"rules": {
"chai-vague-errors": true,
"export-name": true,
"import-name": true,
"use-isnan": true,
"jquery-deferred-must-complete": true,
"missing-jsdoc": true,
Expand Down

0 comments on commit d4e29e5

Please sign in to comment.