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

Commit

Permalink
Support disallowing namespaces (noNamespace). (#1133)
Browse files Browse the repository at this point in the history
This allows code bases to outlaw pre-ES6 modules and namespaces. It's often
still useful to declare namespaces when interacting with non-TypeScript code, so
this has an option to allow "declare namespace" style usage.
  • Loading branch information
mprobst authored and jkillian committed Apr 26, 2016
1 parent 5a5d485 commit cdb56c3
Show file tree
Hide file tree
Showing 10 changed files with 88 additions and 8 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,8 @@ A sample configuration file with all options is available [here](https://github.
* `no-inferrable-types` disallows explicit type declarations for variables or parameters initialized to a number, string, or boolean.
* `no-internal-module` disallows internal `module` (use `namespace` instead).
* `no-invalid-this` disallows using the `this` keyword outside of classes.
* `no-namespace` disallows both internal `module`s and `namespace`, but allows ES6-style external modules.
* `allow-declarations` Allow `declare module ... {}` to describe external APIs.
* `no-null-keyword` disallows use of the `null` keyword literal.
* `no-reference` disallows `/// <reference path=>` imports (use ES6-style imports instead).
* `no-require-imports` disallows invocation of `require()` (use ES6-style imports instead).
Expand Down
11 changes: 11 additions & 0 deletions src/language/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,3 +123,14 @@ export function isNodeFlagSet(node: ts.Node, flagToCheck: ts.NodeFlags): boolean
return (node.flags & flagToCheck) !== 0;
/* tslint:enable:no-bitwise */
}


/**
* Returns true if decl is a nested module declaration, i.e. represents a segment of a dotted module path.
*/
export function isNestedModuleDeclaration(decl: ts.ModuleDeclaration) {
// in a declaration expression like 'module a.b.c' - 'a' is the top level module declaration node and 'b' and 'c'
// are nested therefore we can depend that a node's position will only match with its name's position for nested
// nodes
return decl.name.pos === decl.pos;
}
9 changes: 1 addition & 8 deletions src/rules/noInternalModuleRule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,19 +38,12 @@ class NoInternalModuleWalker extends Lint.RuleWalker {
// an internal module declaration is not a namespace or a nested declaration
// for external modules, node.name.kind will be a LiteralExpression instead of Identifier
return !Lint.isNodeFlagSet(node, ts.NodeFlags.Namespace)
&& !isNestedDeclaration(node)
&& !Lint.isNestedModuleDeclaration(node)
&& node.name.kind === ts.SyntaxKind.Identifier
&& !isGlobalAugmentation(node);
}
}

function isNestedDeclaration(node: ts.ModuleDeclaration) {
// in a declaration expression like 'module a.b.c' - 'a' is the top level module declaration node and 'b' and 'c'
// are nested therefore we can depend that a node's position will only match with its name's position for nested
// nodes
return node.name.pos === node.pos;
}

function isGlobalAugmentation(node: ts.ModuleDeclaration) {
// augmenting global uses a sepcial syntax that is allowed
// see https://github.com/Microsoft/TypeScript/pull/6213
Expand Down
38 changes: 38 additions & 0 deletions src/rules/noNamespaceRule.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/**
* @license
* Copyright 2016 Palantir Technologies, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import * as ts from "typescript";
import * as Lint from "../lint";

export class Rule extends Lint.Rules.AbstractRule {
public static FAILURE_STRING = "'namespace' and 'module' are disallowed";

public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] {
return this.applyWithWalker(new NoNamespaceWalker(sourceFile, this.getOptions()));
}
}

class NoNamespaceWalker extends Lint.RuleWalker {
public visitModuleDeclaration(decl: ts.ModuleDeclaration) {
super.visitModuleDeclaration(decl);
// declare module 'foo' {} is an external module, not a namespace.
if (decl.name.kind === ts.SyntaxKind.StringLiteral) { return; }
if (Lint.isNodeFlagSet(decl, ts.NodeFlags.Ambient) && this.hasOption("allow-declarations")) { return; }
if (Lint.isNestedModuleDeclaration(decl)) { return; }
this.addFailure(this.createFailure(decl.getStart(), decl.getWidth(), Rule.FAILURE_STRING));
}
}
1 change: 1 addition & 0 deletions src/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@
"rules/noInferrableTypesRule.ts",
"rules/noInternalModuleRule.ts",
"rules/noInvalidThisRule.ts",
"rules/noNamespaceRule.ts",
"rules/noNullKeywordRule.ts",
"rules/noReferenceRule.ts",
"rules/noRequireImportsRule.ts",
Expand Down
3 changes: 3 additions & 0 deletions test/rules/no-namespace/allow-declarations/test.ts.lint
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
declare namespace Foo {

}
5 changes: 5 additions & 0 deletions test/rules/no-namespace/allow-declarations/tslint.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"rules": {
"no-namespace": [true, "allow-declarations"]
}
}
21 changes: 21 additions & 0 deletions test/rules/no-namespace/default/test.ts.lint
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
namespace Foo {
~~~~~~~~~~~~~~~
}
~ ['namespace' and 'module' are disallowed]
namespace Foo.Bar {
~~~~~~~~~~~~~~~~~~~
}
~ ['namespace' and 'module' are disallowed]

module Foo {
~~~~~~~~~~~~
}
~ ['namespace' and 'module' are disallowed]

declare namespace Foo {
~~~~~~~~~~~~~~~~~~~~~~~
}
~ ['namespace' and 'module' are disallowed]

declare module 'foo' {
}
5 changes: 5 additions & 0 deletions test/rules/no-namespace/default/tslint.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"rules": {
"no-namespace": [true]
}
}
1 change: 1 addition & 0 deletions test/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@
"../src/rules/noInferrableTypesRule.ts",
"../src/rules/noInternalModuleRule.ts",
"../src/rules/noInvalidThisRule.ts",
"../src/rules/noNamespaceRule.ts",
"../src/rules/noNullKeywordRule.ts",
"../src/rules/noReferenceRule.ts",
"../src/rules/noRequireImportsRule.ts",
Expand Down

0 comments on commit cdb56c3

Please sign in to comment.