fix(lint): enforce that module-private members have @internal.
This is needed to prevent leaking internal APIs to users via our published .d.ts typings. Fixes #4645 Closes #4989
This commit is contained in:
parent
44188b9072
commit
098201d0b8
@ -294,6 +294,7 @@ gulp.task('lint', ['build.tools'], function() {
|
|||||||
// https://github.com/palantir/tslint#supported-rules
|
// https://github.com/palantir/tslint#supported-rules
|
||||||
var tslintConfig = {
|
var tslintConfig = {
|
||||||
"rules": {
|
"rules": {
|
||||||
|
"requireInternalWithUnderscore": true,
|
||||||
"requireParameterType": true,
|
"requireParameterType": true,
|
||||||
"requireReturnType": true,
|
"requireReturnType": true,
|
||||||
"semicolon": true,
|
"semicolon": true,
|
||||||
|
@ -265,6 +265,7 @@ class _ConvertAstIntoProtoRecords implements AstVisitor {
|
|||||||
return this._addRecord(RecordType.Chain, "chain", null, args, null, 0);
|
return this._addRecord(RecordType.Chain, "chain", null, args, null, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @internal */
|
||||||
_visitAll(asts: any[]) {
|
_visitAll(asts: any[]) {
|
||||||
var res = ListWrapper.createFixedSize(asts.length);
|
var res = ListWrapper.createFixedSize(asts.length);
|
||||||
for (var i = 0; i < asts.length; ++i) {
|
for (var i = 0; i < asts.length; ++i) {
|
||||||
@ -273,6 +274,7 @@ class _ConvertAstIntoProtoRecords implements AstVisitor {
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @internal */
|
||||||
_addRecord(type, name, funcOrValue, args, fixedArgs, context) {
|
_addRecord(type, name, funcOrValue, args, fixedArgs, context) {
|
||||||
var selfIndex = this._records.length + 1;
|
var selfIndex = this._records.length + 1;
|
||||||
if (context instanceof DirectiveIndex) {
|
if (context instanceof DirectiveIndex) {
|
||||||
|
@ -52,8 +52,8 @@ export abstract class LifeCycle {
|
|||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class LifeCycle_ extends LifeCycle {
|
export class LifeCycle_ extends LifeCycle {
|
||||||
|
/** @internal */
|
||||||
static _tickScope: WtfScopeFn = wtfCreateScope('LifeCycle#tick()');
|
static _tickScope: WtfScopeFn = wtfCreateScope('LifeCycle#tick()');
|
||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
_changeDetectors: ChangeDetector[];
|
_changeDetectors: ChangeDetector[];
|
||||||
/** @internal */
|
/** @internal */
|
||||||
|
@ -119,11 +119,13 @@ export class DirectiveDependency extends Dependency {
|
|||||||
DirectiveDependency._attributeName(d.properties), DirectiveDependency._query(d.properties));
|
DirectiveDependency._attributeName(d.properties), DirectiveDependency._query(d.properties));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @internal */
|
||||||
static _attributeName(properties): string {
|
static _attributeName(properties): string {
|
||||||
var p = <AttributeMetadata>ListWrapper.find(properties, (p) => p instanceof AttributeMetadata);
|
var p = <AttributeMetadata>ListWrapper.find(properties, (p) => p instanceof AttributeMetadata);
|
||||||
return isPresent(p) ? p.attributeName : null;
|
return isPresent(p) ? p.attributeName : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @internal */
|
||||||
static _query(properties): QueryMetadata {
|
static _query(properties): QueryMetadata {
|
||||||
return <QueryMetadata>ListWrapper.find(properties, (p) => p instanceof QueryMetadata);
|
return <QueryMetadata>ListWrapper.find(properties, (p) => p instanceof QueryMetadata);
|
||||||
}
|
}
|
||||||
|
@ -81,6 +81,7 @@ var defaultLocale: string = 'en-US';
|
|||||||
@Pipe({name: 'date'})
|
@Pipe({name: 'date'})
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class DatePipe implements PipeTransform {
|
export class DatePipe implements PipeTransform {
|
||||||
|
/** @internal */
|
||||||
static _ALIASES: {[key: string]: String} = {
|
static _ALIASES: {[key: string]: String} = {
|
||||||
'medium': 'yMMMdjms',
|
'medium': 'yMMMdjms',
|
||||||
'short': 'yMdjm',
|
'short': 'yMdjm',
|
||||||
|
@ -23,6 +23,7 @@ var _re = RegExpWrapper.create('^(\\d+)?\\.((\\d+)(\\-(\\d+))?)?$');
|
|||||||
@CONST()
|
@CONST()
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class NumberPipe {
|
export class NumberPipe {
|
||||||
|
/** @internal */
|
||||||
static _format(value: number, style: NumberFormatStyle, digits: string, currency: string = null,
|
static _format(value: number, style: NumberFormatStyle, digits: string, currency: string = null,
|
||||||
currencyAsSymbol: boolean = false): string {
|
currencyAsSymbol: boolean = false): string {
|
||||||
if (isBlank(value)) return null;
|
if (isBlank(value)) return null;
|
||||||
|
@ -99,6 +99,7 @@ export class KeyEventsPlugin extends EventManagerPlugin {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @internal */
|
||||||
static _normalizeKey(keyName: string): string {
|
static _normalizeKey(keyName: string): string {
|
||||||
// TODO: switch to a StringMap if the mapping grows too much
|
// TODO: switch to a StringMap if the mapping grows too much
|
||||||
switch (keyName) {
|
switch (keyName) {
|
||||||
|
@ -7,6 +7,7 @@ import {
|
|||||||
import {global} from 'angular2/src/core/facade/lang';
|
import {global} from 'angular2/src/core/facade/lang';
|
||||||
|
|
||||||
class PublicTestability {
|
class PublicTestability {
|
||||||
|
/** @internal */
|
||||||
_testability: Testability;
|
_testability: Testability;
|
||||||
|
|
||||||
constructor(testability: Testability) { this._testability = testability; }
|
constructor(testability: Testability) { this._testability = testability; }
|
||||||
|
@ -142,6 +142,7 @@ class MessageData {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the value from the StringMap if present. Otherwise returns null
|
* Returns the value from the StringMap if present. Otherwise returns null
|
||||||
|
* @internal
|
||||||
*/
|
*/
|
||||||
_getValueIfPresent(data: {[key: string]: any}, key: string) {
|
_getValueIfPresent(data: {[key: string]: any}, key: string) {
|
||||||
if (StringMapWrapper.contains(data, key)) {
|
if (StringMapWrapper.contains(data, key)) {
|
||||||
|
44
tools/tslint/requireInternalWithUnderscoreRule.ts
Normal file
44
tools/tslint/requireInternalWithUnderscoreRule.ts
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
import {RuleFailure} from 'tslint/lib/lint';
|
||||||
|
import {AbstractRule} from 'tslint/lib/rules';
|
||||||
|
import {RuleWalker} from 'tslint/lib/language/walker';
|
||||||
|
import * as ts from 'tslint/node_modules/typescript';
|
||||||
|
|
||||||
|
export class Rule extends AbstractRule {
|
||||||
|
public apply(sourceFile: ts.SourceFile): RuleFailure[] {
|
||||||
|
const typedefWalker = new TypedefWalker(sourceFile, this.getOptions());
|
||||||
|
return this.applyWithWalker(typedefWalker);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class TypedefWalker extends RuleWalker {
|
||||||
|
protected visitPropertyDeclaration(node: ts.PropertyDeclaration): void {
|
||||||
|
this.assertInternalAnnotationPresent(node);
|
||||||
|
super.visitPropertyDeclaration(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
public visitMethodDeclaration(node: ts.MethodDeclaration): void {
|
||||||
|
this.assertInternalAnnotationPresent(node);
|
||||||
|
super.visitMethodDeclaration(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
private hasInternalAnnotation(range: ts.CommentRange): boolean {
|
||||||
|
let text = this.getSourceFile().text;
|
||||||
|
let comment = text.substring(range.pos, range.end);
|
||||||
|
return comment.indexOf("@internal") >= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private assertInternalAnnotationPresent(node: ts.Declaration) {
|
||||||
|
if (node.name.getText().charAt(0) !== '_') return;
|
||||||
|
if (node.modifiers && node.modifiers.flags & ts.NodeFlags.Private) return;
|
||||||
|
|
||||||
|
const ranges = ts.getLeadingCommentRanges(this.getSourceFile().text, node.pos);
|
||||||
|
if (ranges) {
|
||||||
|
for (let i = 0; i < ranges.length; i++) {
|
||||||
|
if (this.hasInternalAnnotation(ranges[i])) return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.addFailure(this.createFailure(
|
||||||
|
node.getStart(), node.getWidth(),
|
||||||
|
`module-private member ${node.name.getText()} must be annotated @internal`));
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user