Merge branch 'master' into fix10193

This commit is contained in:
Kanchalai Tanglertsampan
2016-09-30 10:43:45 -07:00
5880 changed files with 324963 additions and 146813 deletions
+812 -16
View File
@@ -27,7 +27,7 @@ namespace ts {
return ModuleInstanceState.ConstEnumOnly;
}
// 3. non-exported import declarations
else if ((node.kind === SyntaxKind.ImportDeclaration || node.kind === SyntaxKind.ImportEqualsDeclaration) && !(node.flags & NodeFlags.Export)) {
else if ((node.kind === SyntaxKind.ImportDeclaration || node.kind === SyntaxKind.ImportEqualsDeclaration) && !(hasModifier(node, ModifierFlags.Export))) {
return ModuleInstanceState.NonInstantiated;
}
// 4. other uninstantiated module declarations.
@@ -131,6 +131,10 @@ namespace ts {
const unreachableFlow: FlowNode = { flags: FlowFlags.Unreachable };
const reportedUnreachableFlow: FlowNode = { flags: FlowFlags.Unreachable };
// state used to aggregate transform flags during bind.
let subtreeTransformFlags: TransformFlags = TransformFlags.None;
let skipTransformFlagAggregation: boolean;
function bindSourceFile(f: SourceFile, opts: CompilerOptions) {
file = f;
options = opts;
@@ -138,6 +142,7 @@ namespace ts {
inStrictMode = !!file.externalModuleIndicator;
classifiableNames = createMap<string>();
symbolCount = 0;
skipTransformFlagAggregation = isDeclarationFile(file);
Symbol = objectAllocator.getSymbolConstructor();
@@ -164,6 +169,7 @@ namespace ts {
activeLabels = undefined;
hasExplicitReturn = false;
emitFlags = NodeFlags.None;
subtreeTransformFlags = TransformFlags.None;
}
return bindSourceFile;
@@ -253,7 +259,7 @@ namespace ts {
case SyntaxKind.FunctionDeclaration:
case SyntaxKind.ClassDeclaration:
return node.flags & NodeFlags.Default ? "default" : undefined;
return hasModifier(node, ModifierFlags.Default) ? "default" : undefined;
case SyntaxKind.JSDocFunctionType:
return isJSDocConstructSignature(node) ? "__new" : "__call";
case SyntaxKind.Parameter:
@@ -262,7 +268,7 @@ namespace ts {
Debug.assert(node.parent.kind === SyntaxKind.JSDocFunctionType);
let functionType = <JSDocFunctionType>node.parent;
let index = indexOf(functionType.parameters, node);
return "p" + index;
return "arg" + index;
case SyntaxKind.JSDocTypedefTag:
const parentNode = node.parent && node.parent.parent;
let nameFromParentNode: string;
@@ -293,7 +299,7 @@ namespace ts {
function declareSymbol(symbolTable: SymbolTable, parent: Symbol, node: Declaration, includes: SymbolFlags, excludes: SymbolFlags): Symbol {
Debug.assert(!hasDynamicName(node));
const isDefaultExport = node.flags & NodeFlags.Default;
const isDefaultExport = hasModifier(node, ModifierFlags.Default);
// The exported symbol for an export default function/class node is always named "default"
const name = isDefaultExport && parent ? "default" : getDeclarationName(node);
@@ -385,7 +391,7 @@ namespace ts {
}
function declareModuleMember(node: Declaration, symbolFlags: SymbolFlags, symbolExcludes: SymbolFlags): Symbol {
const hasExportModifier = getCombinedNodeFlags(node) & NodeFlags.Export;
const hasExportModifier = getCombinedModifierFlags(node) & ModifierFlags.Export;
if (symbolFlags & SymbolFlags.Alias) {
if (node.kind === SyntaxKind.ExportSpecifier || (node.kind === SyntaxKind.ImportEqualsDeclaration && hasExportModifier)) {
return declareSymbol(container.symbol.exports, container.symbol, node, symbolFlags, symbolExcludes);
@@ -526,13 +532,28 @@ namespace ts {
}
function bindChildren(node: Node): void {
if (skipTransformFlagAggregation) {
bindChildrenWorker(node);
}
else if (node.transformFlags & TransformFlags.HasComputedFlags) {
skipTransformFlagAggregation = true;
bindChildrenWorker(node);
skipTransformFlagAggregation = false;
}
else {
const savedSubtreeTransformFlags = subtreeTransformFlags;
subtreeTransformFlags = 0;
bindChildrenWorker(node);
subtreeTransformFlags = savedSubtreeTransformFlags | computeTransformFlagsForNode(node, subtreeTransformFlags);
}
}
function bindChildrenWorker(node: Node): void {
// Binding of JsDocComment should be done before the current block scope container changes.
// because the scope of JsDocComment should not be affected by whether the current node is a
// container or not.
if (isInJavaScriptFile(node) && node.jsDocComments) {
for (const jsDocComment of node.jsDocComments) {
bind(jsDocComment);
}
forEach(node.jsDocComments, bind);
}
if (checkUnreachable(node)) {
forEachChild(node, bind);
@@ -581,6 +602,9 @@ namespace ts {
case SyntaxKind.PrefixUnaryExpression:
bindPrefixUnaryExpressionFlow(<PrefixUnaryExpression>node);
break;
case SyntaxKind.PostfixUnaryExpression:
bindPostfixUnaryExpressionFlow(<PostfixUnaryExpression>node);
break;
case SyntaxKind.BinaryExpression:
bindBinaryExpressionFlow(<BinaryExpression>node);
break;
@@ -1096,6 +1120,16 @@ namespace ts {
}
else {
forEachChild(node, bind);
if (node.operator === SyntaxKind.PlusEqualsToken || node.operator === SyntaxKind.MinusMinusToken) {
bindAssignmentTargetFlow(node.operand);
}
}
}
function bindPostfixUnaryExpressionFlow(node: PostfixUnaryExpression) {
forEachChild(node, bind);
if (node.operator === SyntaxKind.PlusPlusToken || node.operator === SyntaxKind.MinusMinusToken) {
bindAssignmentTargetFlow(node.operand);
}
}
@@ -1140,8 +1174,8 @@ namespace ts {
currentFlow = finishFlowLabel(postExpressionLabel);
}
function bindInitializedVariableFlow(node: VariableDeclaration | BindingElement) {
const name = node.name;
function bindInitializedVariableFlow(node: VariableDeclaration | ArrayBindingElement) {
const name = !isOmittedExpression(node) ? node.name : undefined;
if (isBindingPattern(name)) {
for (const child of name.elements) {
bindInitializedVariableFlow(child);
@@ -1321,7 +1355,7 @@ namespace ts {
}
function declareClassMember(node: Declaration, symbolFlags: SymbolFlags, symbolExcludes: SymbolFlags) {
return node.flags & NodeFlags.Static
return hasModifier(node, ModifierFlags.Static)
? declareSymbol(container.symbol.exports, container.symbol, node, symbolFlags, symbolExcludes)
: declareSymbol(container.symbol.members, container.symbol, node, symbolFlags, symbolExcludes);
}
@@ -1358,7 +1392,7 @@ namespace ts {
function bindModuleDeclaration(node: ModuleDeclaration) {
setExportContextFlag(node);
if (isAmbientModule(node)) {
if (node.flags & NodeFlags.Export) {
if (hasModifier(node, ModifierFlags.Export)) {
errorOnFirstToken(node, Diagnostics.export_modifier_cannot_be_applied_to_ambient_modules_and_module_augmentations_since_they_are_always_visible);
}
if (isExternalModuleAugmentation(node)) {
@@ -1625,7 +1659,7 @@ namespace ts {
}
}
function checkStrictModeNumericLiteral(node: LiteralExpression) {
function checkStrictModeNumericLiteral(node: NumericLiteral) {
if (inStrictMode && node.isOctalLiteral) {
file.bindDiagnostics.push(createDiagnosticForNode(node, Diagnostics.Octal_literals_are_not_allowed_in_strict_mode));
}
@@ -1700,6 +1734,9 @@ namespace ts {
}
parent = saveParent;
}
else if (!skipTransformFlagAggregation && (node.transformFlags & TransformFlags.HasComputedFlags) === 0) {
subtreeTransformFlags |= computeTransformFlagsForNode(node, 0);
}
inStrictMode = saveInStrictMode;
}
@@ -1770,7 +1807,7 @@ namespace ts {
case SyntaxKind.DeleteExpression:
return checkStrictModeDeleteExpression(<DeleteExpression>node);
case SyntaxKind.NumericLiteral:
return checkStrictModeNumericLiteral(<LiteralExpression>node);
return checkStrictModeNumericLiteral(<NumericLiteral>node);
case SyntaxKind.PostfixUnaryExpression:
return checkStrictModePostfixUnaryExpression(<PostfixUnaryExpression>node);
case SyntaxKind.PrefixUnaryExpression:
@@ -1802,7 +1839,7 @@ namespace ts {
return bindPropertyOrMethodOrAccessor(<Declaration>node, SymbolFlags.EnumMember, SymbolFlags.EnumMemberExcludes);
case SyntaxKind.JsxSpreadAttribute:
emitFlags |= NodeFlags.HasJsxSpreadAttribute;
emitFlags |= NodeFlags.HasJsxSpreadAttributes;
return;
case SyntaxKind.CallSignature:
@@ -2220,7 +2257,7 @@ namespace ts {
if (currentFlow === unreachableFlow) {
const reportError =
// report error on all statements except empty ones
(isStatement(node) && node.kind !== SyntaxKind.EmptyStatement) ||
(isStatementButNotDeclaration(node) && node.kind !== SyntaxKind.EmptyStatement) ||
// report error on class declarations
node.kind === SyntaxKind.ClassDeclaration ||
// report error on instantiated modules or const-enums only modules if preserveConstEnums is set
@@ -2257,4 +2294,763 @@ namespace ts {
return true;
}
}
/**
* Computes the transform flags for a node, given the transform flags of its subtree
*
* @param node The node to analyze
* @param subtreeFlags Transform flags computed for this node's subtree
*/
export function computeTransformFlagsForNode(node: Node, subtreeFlags: TransformFlags): TransformFlags {
const kind = node.kind;
switch (kind) {
case SyntaxKind.CallExpression:
return computeCallExpression(<CallExpression>node, subtreeFlags);
case SyntaxKind.ModuleDeclaration:
return computeModuleDeclaration(<ModuleDeclaration>node, subtreeFlags);
case SyntaxKind.ParenthesizedExpression:
return computeParenthesizedExpression(<ParenthesizedExpression>node, subtreeFlags);
case SyntaxKind.BinaryExpression:
return computeBinaryExpression(<BinaryExpression>node, subtreeFlags);
case SyntaxKind.ExpressionStatement:
return computeExpressionStatement(<ExpressionStatement>node, subtreeFlags);
case SyntaxKind.Parameter:
return computeParameter(<ParameterDeclaration>node, subtreeFlags);
case SyntaxKind.ArrowFunction:
return computeArrowFunction(<ArrowFunction>node, subtreeFlags);
case SyntaxKind.FunctionExpression:
return computeFunctionExpression(<FunctionExpression>node, subtreeFlags);
case SyntaxKind.FunctionDeclaration:
return computeFunctionDeclaration(<FunctionDeclaration>node, subtreeFlags);
case SyntaxKind.VariableDeclaration:
return computeVariableDeclaration(<VariableDeclaration>node, subtreeFlags);
case SyntaxKind.VariableDeclarationList:
return computeVariableDeclarationList(<VariableDeclarationList>node, subtreeFlags);
case SyntaxKind.VariableStatement:
return computeVariableStatement(<VariableStatement>node, subtreeFlags);
case SyntaxKind.LabeledStatement:
return computeLabeledStatement(<LabeledStatement>node, subtreeFlags);
case SyntaxKind.ClassDeclaration:
return computeClassDeclaration(<ClassDeclaration>node, subtreeFlags);
case SyntaxKind.ClassExpression:
return computeClassExpression(<ClassExpression>node, subtreeFlags);
case SyntaxKind.HeritageClause:
return computeHeritageClause(<HeritageClause>node, subtreeFlags);
case SyntaxKind.ExpressionWithTypeArguments:
return computeExpressionWithTypeArguments(<ExpressionWithTypeArguments>node, subtreeFlags);
case SyntaxKind.Constructor:
return computeConstructor(<ConstructorDeclaration>node, subtreeFlags);
case SyntaxKind.PropertyDeclaration:
return computePropertyDeclaration(<PropertyDeclaration>node, subtreeFlags);
case SyntaxKind.MethodDeclaration:
return computeMethod(<MethodDeclaration>node, subtreeFlags);
case SyntaxKind.GetAccessor:
case SyntaxKind.SetAccessor:
return computeAccessor(<AccessorDeclaration>node, subtreeFlags);
case SyntaxKind.ImportEqualsDeclaration:
return computeImportEquals(<ImportEqualsDeclaration>node, subtreeFlags);
case SyntaxKind.PropertyAccessExpression:
return computePropertyAccess(<PropertyAccessExpression>node, subtreeFlags);
default:
return computeOther(node, kind, subtreeFlags);
}
}
function computeCallExpression(node: CallExpression, subtreeFlags: TransformFlags) {
let transformFlags = subtreeFlags;
const expression = node.expression;
const expressionKind = expression.kind;
if (subtreeFlags & TransformFlags.ContainsSpreadElementExpression
|| isSuperOrSuperProperty(expression, expressionKind)) {
// If the this node contains a SpreadElementExpression, or is a super call, then it is an ES6
// node.
transformFlags |= TransformFlags.AssertES6;
}
node.transformFlags = transformFlags | TransformFlags.HasComputedFlags;
return transformFlags & ~TransformFlags.ArrayLiteralOrCallOrNewExcludes;
}
function isSuperOrSuperProperty(node: Node, kind: SyntaxKind) {
switch (kind) {
case SyntaxKind.SuperKeyword:
return true;
case SyntaxKind.PropertyAccessExpression:
case SyntaxKind.ElementAccessExpression:
const expression = (<PropertyAccessExpression | ElementAccessExpression>node).expression;
const expressionKind = expression.kind;
return expressionKind === SyntaxKind.SuperKeyword;
}
return false;
}
function computeBinaryExpression(node: BinaryExpression, subtreeFlags: TransformFlags) {
let transformFlags = subtreeFlags;
const operatorTokenKind = node.operatorToken.kind;
const leftKind = node.left.kind;
if (operatorTokenKind === SyntaxKind.EqualsToken
&& (leftKind === SyntaxKind.ObjectLiteralExpression
|| leftKind === SyntaxKind.ArrayLiteralExpression)) {
// Destructuring assignments are ES6 syntax.
transformFlags |= TransformFlags.AssertES6 | TransformFlags.DestructuringAssignment;
}
else if (operatorTokenKind === SyntaxKind.AsteriskAsteriskToken
|| operatorTokenKind === SyntaxKind.AsteriskAsteriskEqualsToken) {
// Exponentiation is ES7 syntax.
transformFlags |= TransformFlags.AssertES7;
}
node.transformFlags = transformFlags | TransformFlags.HasComputedFlags;
return transformFlags & ~TransformFlags.NodeExcludes;
}
function computeParameter(node: ParameterDeclaration, subtreeFlags: TransformFlags) {
let transformFlags = subtreeFlags;
const modifierFlags = getModifierFlags(node);
const name = node.name;
const initializer = node.initializer;
const dotDotDotToken = node.dotDotDotToken;
// If the parameter has a question token, then it is TypeScript syntax.
if (node.questionToken) {
transformFlags |= TransformFlags.AssertTypeScript;
}
// If the parameter's name is 'this', then it is TypeScript syntax.
if (subtreeFlags & TransformFlags.ContainsDecorators
|| (name && isIdentifier(name) && name.originalKeywordKind === SyntaxKind.ThisKeyword)) {
transformFlags |= TransformFlags.AssertTypeScript;
}
// If a parameter has an accessibility modifier, then it is TypeScript syntax.
if (modifierFlags & ModifierFlags.ParameterPropertyModifier) {
transformFlags |= TransformFlags.AssertTypeScript | TransformFlags.ContainsParameterPropertyAssignments;
}
// If a parameter has an initializer, a binding pattern or a dotDotDot token, then
// it is ES6 syntax and its container must emit default value assignments or parameter destructuring downlevel.
if (subtreeFlags & TransformFlags.ContainsBindingPattern || initializer || dotDotDotToken) {
transformFlags |= TransformFlags.AssertES6 | TransformFlags.ContainsDefaultValueAssignments;
}
node.transformFlags = transformFlags | TransformFlags.HasComputedFlags;
return transformFlags & ~TransformFlags.ParameterExcludes;
}
function computeParenthesizedExpression(node: ParenthesizedExpression, subtreeFlags: TransformFlags) {
let transformFlags = subtreeFlags;
const expression = node.expression;
const expressionKind = expression.kind;
const expressionTransformFlags = expression.transformFlags;
// If the node is synthesized, it means the emitter put the parentheses there,
// not the user. If we didn't want them, the emitter would not have put them
// there.
if (expressionKind === SyntaxKind.AsExpression
|| expressionKind === SyntaxKind.TypeAssertionExpression) {
transformFlags |= TransformFlags.AssertTypeScript;
}
// If the expression of a ParenthesizedExpression is a destructuring assignment,
// then the ParenthesizedExpression is a destructuring assignment.
if (expressionTransformFlags & TransformFlags.DestructuringAssignment) {
transformFlags |= TransformFlags.DestructuringAssignment;
}
node.transformFlags = transformFlags | TransformFlags.HasComputedFlags;
return transformFlags & ~TransformFlags.NodeExcludes;
}
function computeClassDeclaration(node: ClassDeclaration, subtreeFlags: TransformFlags) {
let transformFlags: TransformFlags;
const modifierFlags = getModifierFlags(node);
if (modifierFlags & ModifierFlags.Ambient) {
// An ambient declaration is TypeScript syntax.
transformFlags = TransformFlags.AssertTypeScript;
}
else {
// A ClassDeclaration is ES6 syntax.
transformFlags = subtreeFlags | TransformFlags.AssertES6;
// A class with a parameter property assignment, property initializer, or decorator is
// TypeScript syntax.
// An exported declaration may be TypeScript syntax.
if ((subtreeFlags & TransformFlags.TypeScriptClassSyntaxMask)
|| (modifierFlags & ModifierFlags.Export)) {
transformFlags |= TransformFlags.AssertTypeScript;
}
if (subtreeFlags & TransformFlags.ContainsLexicalThisInComputedPropertyName) {
// A computed property name containing `this` might need to be rewritten,
// so propagate the ContainsLexicalThis flag upward.
transformFlags |= TransformFlags.ContainsLexicalThis;
}
}
node.transformFlags = transformFlags | TransformFlags.HasComputedFlags;
return transformFlags & ~TransformFlags.ClassExcludes;
}
function computeClassExpression(node: ClassExpression, subtreeFlags: TransformFlags) {
// A ClassExpression is ES6 syntax.
let transformFlags = subtreeFlags | TransformFlags.AssertES6;
// A class with a parameter property assignment, property initializer, or decorator is
// TypeScript syntax.
if (subtreeFlags & TransformFlags.TypeScriptClassSyntaxMask) {
transformFlags |= TransformFlags.AssertTypeScript;
}
if (subtreeFlags & TransformFlags.ContainsLexicalThisInComputedPropertyName) {
// A computed property name containing `this` might need to be rewritten,
// so propagate the ContainsLexicalThis flag upward.
transformFlags |= TransformFlags.ContainsLexicalThis;
}
node.transformFlags = transformFlags | TransformFlags.HasComputedFlags;
return transformFlags & ~TransformFlags.ClassExcludes;
}
function computeHeritageClause(node: HeritageClause, subtreeFlags: TransformFlags) {
let transformFlags = subtreeFlags;
switch (node.token) {
case SyntaxKind.ExtendsKeyword:
// An `extends` HeritageClause is ES6 syntax.
transformFlags |= TransformFlags.AssertES6;
break;
case SyntaxKind.ImplementsKeyword:
// An `implements` HeritageClause is TypeScript syntax.
transformFlags |= TransformFlags.AssertTypeScript;
break;
default:
Debug.fail("Unexpected token for heritage clause");
break;
}
node.transformFlags = transformFlags | TransformFlags.HasComputedFlags;
return transformFlags & ~TransformFlags.NodeExcludes;
}
function computeExpressionWithTypeArguments(node: ExpressionWithTypeArguments, subtreeFlags: TransformFlags) {
// An ExpressionWithTypeArguments is ES6 syntax, as it is used in the
// extends clause of a class.
let transformFlags = subtreeFlags | TransformFlags.AssertES6;
// If an ExpressionWithTypeArguments contains type arguments, then it
// is TypeScript syntax.
if (node.typeArguments) {
transformFlags |= TransformFlags.AssertTypeScript;
}
node.transformFlags = transformFlags | TransformFlags.HasComputedFlags;
return transformFlags & ~TransformFlags.NodeExcludes;
}
function computeConstructor(node: ConstructorDeclaration, subtreeFlags: TransformFlags) {
let transformFlags = subtreeFlags;
const body = node.body;
if (body === undefined) {
// An overload constructor is TypeScript syntax.
transformFlags |= TransformFlags.AssertTypeScript;
}
node.transformFlags = transformFlags | TransformFlags.HasComputedFlags;
return transformFlags & ~TransformFlags.ConstructorExcludes;
}
function computeMethod(node: MethodDeclaration, subtreeFlags: TransformFlags) {
// A MethodDeclaration is ES6 syntax.
let transformFlags = subtreeFlags | TransformFlags.AssertES6;
const modifierFlags = getModifierFlags(node);
const body = node.body;
const typeParameters = node.typeParameters;
const asteriskToken = node.asteriskToken;
// A MethodDeclaration is TypeScript syntax if it is either async, abstract, overloaded,
// generic, or has a decorator.
if (!body
|| typeParameters
|| (modifierFlags & (ModifierFlags.Async | ModifierFlags.Abstract))
|| (subtreeFlags & TransformFlags.ContainsDecorators)) {
transformFlags |= TransformFlags.AssertTypeScript;
}
// Currently, we only support generators that were originally async function bodies.
if (asteriskToken && getEmitFlags(node) & EmitFlags.AsyncFunctionBody) {
transformFlags |= TransformFlags.AssertGenerator;
}
node.transformFlags = transformFlags | TransformFlags.HasComputedFlags;
return transformFlags & ~TransformFlags.MethodOrAccessorExcludes;
}
function computeAccessor(node: AccessorDeclaration, subtreeFlags: TransformFlags) {
let transformFlags = subtreeFlags;
const modifierFlags = getModifierFlags(node);
const body = node.body;
// A MethodDeclaration is TypeScript syntax if it is either async, abstract, overloaded,
// generic, or has a decorator.
if (!body
|| (modifierFlags & (ModifierFlags.Async | ModifierFlags.Abstract))
|| (subtreeFlags & TransformFlags.ContainsDecorators)) {
transformFlags |= TransformFlags.AssertTypeScript;
}
node.transformFlags = transformFlags | TransformFlags.HasComputedFlags;
return transformFlags & ~TransformFlags.MethodOrAccessorExcludes;
}
function computePropertyDeclaration(node: PropertyDeclaration, subtreeFlags: TransformFlags) {
// A PropertyDeclaration is TypeScript syntax.
let transformFlags = subtreeFlags | TransformFlags.AssertTypeScript;
// If the PropertyDeclaration has an initializer, we need to inform its ancestor
// so that it handle the transformation.
if (node.initializer) {
transformFlags |= TransformFlags.ContainsPropertyInitializer;
}
node.transformFlags = transformFlags | TransformFlags.HasComputedFlags;
return transformFlags & ~TransformFlags.NodeExcludes;
}
function computeFunctionDeclaration(node: FunctionDeclaration, subtreeFlags: TransformFlags) {
let transformFlags: TransformFlags;
const modifierFlags = getModifierFlags(node);
const body = node.body;
const asteriskToken = node.asteriskToken;
if (!body || (modifierFlags & ModifierFlags.Ambient)) {
// An ambient declaration is TypeScript syntax.
// A FunctionDeclaration without a body is an overload and is TypeScript syntax.
transformFlags = TransformFlags.AssertTypeScript;
}
else {
transformFlags = subtreeFlags | TransformFlags.ContainsHoistedDeclarationOrCompletion;
// If a FunctionDeclaration is exported, then it is either ES6 or TypeScript syntax.
if (modifierFlags & ModifierFlags.Export) {
transformFlags |= TransformFlags.AssertTypeScript | TransformFlags.AssertES6;
}
// If a FunctionDeclaration is async, then it is TypeScript syntax.
if (modifierFlags & ModifierFlags.Async) {
transformFlags |= TransformFlags.AssertTypeScript;
}
// If a FunctionDeclaration's subtree has marked the container as needing to capture the
// lexical this, or the function contains parameters with initializers, then this node is
// ES6 syntax.
if (subtreeFlags & TransformFlags.ES6FunctionSyntaxMask) {
transformFlags |= TransformFlags.AssertES6;
}
// If a FunctionDeclaration is generator function and is the body of a
// transformed async function, then this node can be transformed to a
// down-level generator.
// Currently we do not support transforming any other generator fucntions
// down level.
if (asteriskToken && getEmitFlags(node) & EmitFlags.AsyncFunctionBody) {
transformFlags |= TransformFlags.AssertGenerator;
}
}
node.transformFlags = transformFlags | TransformFlags.HasComputedFlags;
return transformFlags & ~TransformFlags.FunctionExcludes;
}
function computeFunctionExpression(node: FunctionExpression, subtreeFlags: TransformFlags) {
let transformFlags = subtreeFlags;
const modifierFlags = getModifierFlags(node);
const asteriskToken = node.asteriskToken;
// An async function expression is TypeScript syntax.
if (modifierFlags & ModifierFlags.Async) {
transformFlags |= TransformFlags.AssertTypeScript;
}
// If a FunctionExpression's subtree has marked the container as needing to capture the
// lexical this, or the function contains parameters with initializers, then this node is
// ES6 syntax.
if (subtreeFlags & TransformFlags.ES6FunctionSyntaxMask) {
transformFlags |= TransformFlags.AssertES6;
}
// If a FunctionExpression is generator function and is the body of a
// transformed async function, then this node can be transformed to a
// down-level generator.
// Currently we do not support transforming any other generator fucntions
// down level.
if (asteriskToken && getEmitFlags(node) & EmitFlags.AsyncFunctionBody) {
transformFlags |= TransformFlags.AssertGenerator;
}
node.transformFlags = transformFlags | TransformFlags.HasComputedFlags;
return transformFlags & ~TransformFlags.FunctionExcludes;
}
function computeArrowFunction(node: ArrowFunction, subtreeFlags: TransformFlags) {
// An ArrowFunction is ES6 syntax, and excludes markers that should not escape the scope of an ArrowFunction.
let transformFlags = subtreeFlags | TransformFlags.AssertES6;
const modifierFlags = getModifierFlags(node);
// An async arrow function is TypeScript syntax.
if (modifierFlags & ModifierFlags.Async) {
transformFlags |= TransformFlags.AssertTypeScript;
}
// If an ArrowFunction contains a lexical this, its container must capture the lexical this.
if (subtreeFlags & TransformFlags.ContainsLexicalThis) {
transformFlags |= TransformFlags.ContainsCapturedLexicalThis;
}
node.transformFlags = transformFlags | TransformFlags.HasComputedFlags;
return transformFlags & ~TransformFlags.ArrowFunctionExcludes;
}
function computePropertyAccess(node: PropertyAccessExpression, subtreeFlags: TransformFlags) {
let transformFlags = subtreeFlags;
const expression = node.expression;
const expressionKind = expression.kind;
// If a PropertyAccessExpression starts with a super keyword, then it is
// ES6 syntax, and requires a lexical `this` binding.
if (expressionKind === SyntaxKind.SuperKeyword) {
transformFlags |= TransformFlags.ContainsLexicalThis;
}
node.transformFlags = transformFlags | TransformFlags.HasComputedFlags;
return transformFlags & ~TransformFlags.NodeExcludes;
}
function computeVariableDeclaration(node: VariableDeclaration, subtreeFlags: TransformFlags) {
let transformFlags = subtreeFlags;
const nameKind = node.name.kind;
// A VariableDeclaration with a binding pattern is ES6 syntax.
if (nameKind === SyntaxKind.ObjectBindingPattern || nameKind === SyntaxKind.ArrayBindingPattern) {
transformFlags |= TransformFlags.AssertES6 | TransformFlags.ContainsBindingPattern;
}
node.transformFlags = transformFlags | TransformFlags.HasComputedFlags;
return transformFlags & ~TransformFlags.NodeExcludes;
}
function computeVariableStatement(node: VariableStatement, subtreeFlags: TransformFlags) {
let transformFlags: TransformFlags;
const modifierFlags = getModifierFlags(node);
const declarationListTransformFlags = node.declarationList.transformFlags;
// An ambient declaration is TypeScript syntax.
if (modifierFlags & ModifierFlags.Ambient) {
transformFlags = TransformFlags.AssertTypeScript;
}
else {
transformFlags = subtreeFlags;
// If a VariableStatement is exported, then it is either ES6 or TypeScript syntax.
if (modifierFlags & ModifierFlags.Export) {
transformFlags |= TransformFlags.AssertES6 | TransformFlags.AssertTypeScript;
}
if (declarationListTransformFlags & TransformFlags.ContainsBindingPattern) {
transformFlags |= TransformFlags.AssertES6;
}
}
node.transformFlags = transformFlags | TransformFlags.HasComputedFlags;
return transformFlags & ~TransformFlags.NodeExcludes;
}
function computeLabeledStatement(node: LabeledStatement, subtreeFlags: TransformFlags) {
let transformFlags = subtreeFlags;
// A labeled statement containing a block scoped binding *may* need to be transformed from ES6.
if (subtreeFlags & TransformFlags.ContainsBlockScopedBinding
&& isIterationStatement(node, /*lookInLabeledStatements*/ true)) {
transformFlags |= TransformFlags.AssertES6;
}
node.transformFlags = transformFlags | TransformFlags.HasComputedFlags;
return transformFlags & ~TransformFlags.NodeExcludes;
}
function computeImportEquals(node: ImportEqualsDeclaration, subtreeFlags: TransformFlags) {
let transformFlags = subtreeFlags;
// An ImportEqualsDeclaration with a namespace reference is TypeScript.
if (!isExternalModuleImportEqualsDeclaration(node)) {
transformFlags |= TransformFlags.AssertTypeScript;
}
node.transformFlags = transformFlags | TransformFlags.HasComputedFlags;
return transformFlags & ~TransformFlags.NodeExcludes;
}
function computeExpressionStatement(node: ExpressionStatement, subtreeFlags: TransformFlags) {
let transformFlags = subtreeFlags;
// If the expression of an expression statement is a destructuring assignment,
// then we treat the statement as ES6 so that we can indicate that we do not
// need to hold on to the right-hand side.
if (node.expression.transformFlags & TransformFlags.DestructuringAssignment) {
transformFlags |= TransformFlags.AssertES6;
}
node.transformFlags = transformFlags | TransformFlags.HasComputedFlags;
return transformFlags & ~TransformFlags.NodeExcludes;
}
function computeModuleDeclaration(node: ModuleDeclaration, subtreeFlags: TransformFlags) {
let transformFlags = TransformFlags.AssertTypeScript;
const modifierFlags = getModifierFlags(node);
if ((modifierFlags & ModifierFlags.Ambient) === 0) {
transformFlags |= subtreeFlags;
}
node.transformFlags = transformFlags | TransformFlags.HasComputedFlags;
return transformFlags & ~TransformFlags.ModuleExcludes;
}
function computeVariableDeclarationList(node: VariableDeclarationList, subtreeFlags: TransformFlags) {
let transformFlags = subtreeFlags | TransformFlags.ContainsHoistedDeclarationOrCompletion;
if (subtreeFlags & TransformFlags.ContainsBindingPattern) {
transformFlags |= TransformFlags.AssertES6;
}
// If a VariableDeclarationList is `let` or `const`, then it is ES6 syntax.
if (node.flags & NodeFlags.BlockScoped) {
transformFlags |= TransformFlags.AssertES6 | TransformFlags.ContainsBlockScopedBinding;
}
node.transformFlags = transformFlags | TransformFlags.HasComputedFlags;
return transformFlags & ~TransformFlags.VariableDeclarationListExcludes;
}
function computeOther(node: Node, kind: SyntaxKind, subtreeFlags: TransformFlags) {
// Mark transformations needed for each node
let transformFlags = subtreeFlags;
let excludeFlags = TransformFlags.NodeExcludes;
switch (kind) {
case SyntaxKind.PublicKeyword:
case SyntaxKind.PrivateKeyword:
case SyntaxKind.ProtectedKeyword:
case SyntaxKind.AbstractKeyword:
case SyntaxKind.DeclareKeyword:
case SyntaxKind.AsyncKeyword:
case SyntaxKind.ConstKeyword:
case SyntaxKind.AwaitExpression:
case SyntaxKind.EnumDeclaration:
case SyntaxKind.EnumMember:
case SyntaxKind.TypeAssertionExpression:
case SyntaxKind.AsExpression:
case SyntaxKind.NonNullExpression:
case SyntaxKind.ReadonlyKeyword:
// These nodes are TypeScript syntax.
transformFlags |= TransformFlags.AssertTypeScript;
break;
case SyntaxKind.JsxElement:
case SyntaxKind.JsxSelfClosingElement:
case SyntaxKind.JsxOpeningElement:
case SyntaxKind.JsxText:
case SyntaxKind.JsxClosingElement:
case SyntaxKind.JsxAttribute:
case SyntaxKind.JsxSpreadAttribute:
case SyntaxKind.JsxExpression:
// These nodes are Jsx syntax.
transformFlags |= TransformFlags.AssertJsx;
break;
case SyntaxKind.ExportKeyword:
// This node is both ES6 and TypeScript syntax.
transformFlags |= TransformFlags.AssertES6 | TransformFlags.AssertTypeScript;
break;
case SyntaxKind.DefaultKeyword:
case SyntaxKind.NoSubstitutionTemplateLiteral:
case SyntaxKind.TemplateHead:
case SyntaxKind.TemplateMiddle:
case SyntaxKind.TemplateTail:
case SyntaxKind.TemplateExpression:
case SyntaxKind.TaggedTemplateExpression:
case SyntaxKind.ShorthandPropertyAssignment:
case SyntaxKind.ForOfStatement:
// These nodes are ES6 syntax.
transformFlags |= TransformFlags.AssertES6;
break;
case SyntaxKind.YieldExpression:
// This node is ES6 syntax.
transformFlags |= TransformFlags.AssertES6 | TransformFlags.ContainsYield;
break;
case SyntaxKind.AnyKeyword:
case SyntaxKind.NumberKeyword:
case SyntaxKind.NeverKeyword:
case SyntaxKind.StringKeyword:
case SyntaxKind.BooleanKeyword:
case SyntaxKind.SymbolKeyword:
case SyntaxKind.VoidKeyword:
case SyntaxKind.TypeParameter:
case SyntaxKind.PropertySignature:
case SyntaxKind.MethodSignature:
case SyntaxKind.CallSignature:
case SyntaxKind.ConstructSignature:
case SyntaxKind.IndexSignature:
case SyntaxKind.TypePredicate:
case SyntaxKind.TypeReference:
case SyntaxKind.FunctionType:
case SyntaxKind.ConstructorType:
case SyntaxKind.TypeQuery:
case SyntaxKind.TypeLiteral:
case SyntaxKind.ArrayType:
case SyntaxKind.TupleType:
case SyntaxKind.UnionType:
case SyntaxKind.IntersectionType:
case SyntaxKind.ParenthesizedType:
case SyntaxKind.InterfaceDeclaration:
case SyntaxKind.TypeAliasDeclaration:
case SyntaxKind.ThisType:
case SyntaxKind.LiteralType:
// Types and signatures are TypeScript syntax, and exclude all other facts.
transformFlags = TransformFlags.AssertTypeScript;
excludeFlags = TransformFlags.TypeExcludes;
break;
case SyntaxKind.ComputedPropertyName:
// Even though computed property names are ES6, we don't treat them as such.
// This is so that they can flow through PropertyName transforms unaffected.
// Instead, we mark the container as ES6, so that it can properly handle the transform.
transformFlags |= TransformFlags.ContainsComputedPropertyName;
if (subtreeFlags & TransformFlags.ContainsLexicalThis) {
// A computed method name like `[this.getName()](x: string) { ... }` needs to
// distinguish itself from the normal case of a method body containing `this`:
// `this` inside a method doesn't need to be rewritten (the method provides `this`),
// whereas `this` inside a computed name *might* need to be rewritten if the class/object
// is inside an arrow function:
// `_this = this; () => class K { [_this.getName()]() { ... } }`
// To make this distinction, use ContainsLexicalThisInComputedPropertyName
// instead of ContainsLexicalThis for computed property names
transformFlags |= TransformFlags.ContainsLexicalThisInComputedPropertyName;
}
break;
case SyntaxKind.SpreadElementExpression:
// This node is ES6 syntax, but is handled by a containing node.
transformFlags |= TransformFlags.ContainsSpreadElementExpression;
break;
case SyntaxKind.SuperKeyword:
// This node is ES6 syntax.
transformFlags |= TransformFlags.AssertES6;
break;
case SyntaxKind.ThisKeyword:
// Mark this node and its ancestors as containing a lexical `this` keyword.
transformFlags |= TransformFlags.ContainsLexicalThis;
break;
case SyntaxKind.ObjectBindingPattern:
case SyntaxKind.ArrayBindingPattern:
// These nodes are ES6 syntax.
transformFlags |= TransformFlags.AssertES6 | TransformFlags.ContainsBindingPattern;
break;
case SyntaxKind.Decorator:
// This node is TypeScript syntax, and marks its container as also being TypeScript syntax.
transformFlags |= TransformFlags.AssertTypeScript | TransformFlags.ContainsDecorators;
break;
case SyntaxKind.ObjectLiteralExpression:
excludeFlags = TransformFlags.ObjectLiteralExcludes;
if (subtreeFlags & TransformFlags.ContainsComputedPropertyName) {
// If an ObjectLiteralExpression contains a ComputedPropertyName, then it
// is an ES6 node.
transformFlags |= TransformFlags.AssertES6;
}
if (subtreeFlags & TransformFlags.ContainsLexicalThisInComputedPropertyName) {
// A computed property name containing `this` might need to be rewritten,
// so propagate the ContainsLexicalThis flag upward.
transformFlags |= TransformFlags.ContainsLexicalThis;
}
break;
case SyntaxKind.ArrayLiteralExpression:
case SyntaxKind.NewExpression:
excludeFlags = TransformFlags.ArrayLiteralOrCallOrNewExcludes;
if (subtreeFlags & TransformFlags.ContainsSpreadElementExpression) {
// If the this node contains a SpreadElementExpression, then it is an ES6
// node.
transformFlags |= TransformFlags.AssertES6;
}
break;
case SyntaxKind.DoStatement:
case SyntaxKind.WhileStatement:
case SyntaxKind.ForStatement:
case SyntaxKind.ForInStatement:
// A loop containing a block scoped binding *may* need to be transformed from ES6.
if (subtreeFlags & TransformFlags.ContainsBlockScopedBinding) {
transformFlags |= TransformFlags.AssertES6;
}
break;
case SyntaxKind.SourceFile:
if (subtreeFlags & TransformFlags.ContainsCapturedLexicalThis) {
transformFlags |= TransformFlags.AssertES6;
}
break;
case SyntaxKind.ReturnStatement:
case SyntaxKind.ContinueStatement:
case SyntaxKind.BreakStatement:
transformFlags |= TransformFlags.ContainsHoistedDeclarationOrCompletion;
break;
}
node.transformFlags = transformFlags | TransformFlags.HasComputedFlags;
return transformFlags & ~excludeFlags;
}
}
+1296 -715
View File
File diff suppressed because it is too large Load Diff
+206 -15
View File
@@ -5,12 +5,15 @@
/// <reference path="scanner.ts"/>
namespace ts {
/* @internal */
export const compileOnSaveCommandLineOption: CommandLineOption = { name: "compileOnSave", type: "boolean" };
/* @internal */
export const optionDeclarations: CommandLineOption[] = [
{
name: "charset",
type: "string",
},
compileOnSaveCommandLineOption,
{
name: "declaration",
shortName: "d",
@@ -30,6 +33,7 @@ namespace ts {
{
name: "extendedDiagnostics",
type: "boolean",
experimental: true
},
{
name: "emitBOM",
@@ -126,6 +130,10 @@ namespace ts {
type: "boolean",
description: Diagnostics.Do_not_emit_outputs_if_any_errors_were_reported,
},
{
name: "noErrorTruncation",
type: "boolean"
},
{
name: "noImplicitAny",
type: "boolean",
@@ -289,6 +297,7 @@ namespace ts {
"classic": ModuleResolutionKind.Classic,
}),
description: Diagnostics.Specify_module_resolution_strategy_Colon_node_Node_js_or_classic_TypeScript_pre_1_6,
paramType: Diagnostics.STRATEGY,
},
{
name: "allowUnusedLabels",
@@ -402,6 +411,7 @@ namespace ts {
"es2017": "lib.es2017.d.ts",
// Host only
"dom": "lib.dom.d.ts",
"dom.iterable": "lib.dom.iterable.d.ts",
"webworker": "lib.webworker.d.ts",
"scripthost": "lib.scripthost.d.ts",
// ES2015 Or ESNext By-feature options
@@ -429,6 +439,11 @@ namespace ts {
name: "strictNullChecks",
type: "boolean",
description: Diagnostics.Enable_strict_null_checks
},
{
name: "importHelpers",
type: "boolean",
description: Diagnostics.Import_emit_helpers_from_tslib
}
];
@@ -462,6 +477,14 @@ namespace ts {
shortOptionNames: Map<string>;
}
/* @internal */
export const defaultInitCompilerOptions: CompilerOptions = {
module: ModuleKind.CommonJS,
target: ScriptTarget.ES5,
noImplicitAny: false,
sourceMap: false,
};
let optionNameMapCache: OptionNameMap;
/* @internal */
@@ -657,16 +680,104 @@ namespace ts {
* @param fileName The path to the config file
* @param jsonText The text of the config file
*/
export function parseConfigFileTextToJson(fileName: string, jsonText: string): { config?: any; error?: Diagnostic } {
export function parseConfigFileTextToJson(fileName: string, jsonText: string, stripComments = true): { config?: any; error?: Diagnostic } {
try {
const jsonTextWithoutComments = removeComments(jsonText);
return { config: /\S/.test(jsonTextWithoutComments) ? JSON.parse(jsonTextWithoutComments) : {} };
const jsonTextToParse = stripComments ? removeComments(jsonText) : jsonText;
return { config: /\S/.test(jsonTextToParse) ? JSON.parse(jsonTextToParse) : {} };
}
catch (e) {
return { error: createCompilerDiagnostic(Diagnostics.Failed_to_parse_file_0_Colon_1, fileName, e.message) };
}
}
/**
* Generate tsconfig configuration when running command line "--init"
* @param options commandlineOptions to be generated into tsconfig.json
* @param fileNames array of filenames to be generated into tsconfig.json
*/
/* @internal */
export function generateTSConfig(options: CompilerOptions, fileNames: string[]): { compilerOptions: Map<CompilerOptionsValue> } {
const compilerOptions = extend(options, defaultInitCompilerOptions);
const configurations: any = {
compilerOptions: serializeCompilerOptions(compilerOptions)
};
if (fileNames && fileNames.length) {
// only set the files property if we have at least one file
configurations.files = fileNames;
}
return configurations;
function getCustomTypeMapOfCommandLineOption(optionDefinition: CommandLineOption): Map<string | number> | undefined {
if (optionDefinition.type === "string" || optionDefinition.type === "number" || optionDefinition.type === "boolean") {
// this is of a type CommandLineOptionOfPrimitiveType
return undefined;
}
else if (optionDefinition.type === "list") {
return getCustomTypeMapOfCommandLineOption((<CommandLineOptionOfListType>optionDefinition).element);
}
else {
return (<CommandLineOptionOfCustomType>optionDefinition).type;
}
}
function getNameOfCompilerOptionValue(value: CompilerOptionsValue, customTypeMap: MapLike<string | number>): string | undefined {
// There is a typeMap associated with this command-line option so use it to map value back to its name
for (const key in customTypeMap) {
if (customTypeMap[key] === value) {
return key;
}
}
return undefined;
}
function serializeCompilerOptions(options: CompilerOptions): Map<CompilerOptionsValue> {
const result = createMap<CompilerOptionsValue>();
const optionsNameMap = getOptionNameMap().optionNameMap;
for (const name in options) {
if (hasProperty(options, name)) {
// tsconfig only options cannot be specified via command line,
// so we can assume that only types that can appear here string | number | boolean
switch (name) {
case "init":
case "watch":
case "version":
case "help":
case "project":
break;
default:
const value = options[name];
let optionDefinition = optionsNameMap[name.toLowerCase()];
if (optionDefinition) {
const customTypeMap = getCustomTypeMapOfCommandLineOption(optionDefinition);
if (!customTypeMap) {
// There is no map associated with this compiler option then use the value as-is
// This is the case if the value is expect to be string, number, boolean or list of string
result[name] = value;
}
else {
if (optionDefinition.type === "list") {
const convertedValue: string[] = [];
for (const element of value as (string | number)[]) {
convertedValue.push(getNameOfCompilerOptionValue(element, customTypeMap));
}
result[name] = convertedValue;
}
else {
// There is a typeMap associated with this command-line option so use it to map value back to its name
result[name] = getNameOfCompilerOptionValue(value, customTypeMap);
}
}
}
break;
}
}
}
return result;
}
}
/**
* Remove the comments from a json like text.
* Comments can be single line comments (starting with # or //) or multiline comments using / * * /
@@ -699,15 +810,49 @@ namespace ts {
* @param basePath A root directory to resolve relative path entries in the config
* file to. e.g. outDir
*/
export function parseJsonConfigFileContent(json: any, host: ParseConfigHost, basePath: string, existingOptions: CompilerOptions = {}, configFileName?: string): ParsedCommandLine {
export function parseJsonConfigFileContent(json: any, host: ParseConfigHost, basePath: string, existingOptions: CompilerOptions = {}, configFileName?: string, resolutionStack: Path[] = []): ParsedCommandLine {
const errors: Diagnostic[] = [];
const compilerOptions: CompilerOptions = convertCompilerOptionsFromJsonWorker(json["compilerOptions"], basePath, errors, configFileName);
const options = extend(existingOptions, compilerOptions);
const getCanonicalFileName = createGetCanonicalFileName(host.useCaseSensitiveFileNames);
const resolvedPath = toPath(configFileName || "", basePath, getCanonicalFileName);
if (resolutionStack.indexOf(resolvedPath) >= 0) {
return {
options: {},
fileNames: [],
typingOptions: {},
raw: json,
errors: [createCompilerDiagnostic(Diagnostics.Circularity_detected_while_resolving_configuration_Colon_0, [...resolutionStack, resolvedPath].join(" -> "))],
wildcardDirectories: {}
};
}
let options: CompilerOptions = convertCompilerOptionsFromJsonWorker(json["compilerOptions"], basePath, errors, configFileName);
const typingOptions: TypingOptions = convertTypingOptionsFromJsonWorker(json["typingOptions"], basePath, errors, configFileName);
if (json["extends"]) {
let [include, exclude, files, baseOptions]: [string[], string[], string[], CompilerOptions] = [undefined, undefined, undefined, {}];
if (typeof json["extends"] === "string") {
[include, exclude, files, baseOptions] = (tryExtendsName(json["extends"]) || [include, exclude, files, baseOptions]);
}
else {
errors.push(createCompilerDiagnostic(Diagnostics.Compiler_option_0_requires_a_value_of_type_1, "extends", "string"));
}
if (include && !json["include"]) {
json["include"] = include;
}
if (exclude && !json["exclude"]) {
json["exclude"] = exclude;
}
if (files && !json["files"]) {
json["files"] = files;
}
options = assign({}, baseOptions, options);
}
options = extend(existingOptions, options);
options.configFilePath = configFileName;
const { fileNames, wildcardDirectories } = getFileNames(errors);
const compileOnSave = convertCompileOnSaveOptionFromJson(json, basePath, errors);
return {
options,
@@ -715,9 +860,43 @@ namespace ts {
typingOptions,
raw: json,
errors,
wildcardDirectories
wildcardDirectories,
compileOnSave
};
function tryExtendsName(extendedConfig: string): [string[], string[], string[], CompilerOptions] {
// If the path isn't a rooted or relative path, don't try to resolve it (we reserve the right to special case module-id like paths in the future)
if (!(isRootedDiskPath(extendedConfig) || startsWith(normalizeSlashes(extendedConfig), "./") || startsWith(normalizeSlashes(extendedConfig), "../"))) {
errors.push(createCompilerDiagnostic(Diagnostics.The_path_in_an_extends_options_must_be_relative_or_rooted));
return;
}
let extendedConfigPath = toPath(extendedConfig, basePath, getCanonicalFileName);
if (!host.fileExists(extendedConfigPath) && !endsWith(extendedConfigPath, ".json")) {
extendedConfigPath = `${extendedConfigPath}.json` as Path;
if (!host.fileExists(extendedConfigPath)) {
errors.push(createCompilerDiagnostic(Diagnostics.File_0_does_not_exist, extendedConfig));
return;
}
}
const extendedResult = readConfigFile(extendedConfigPath, path => host.readFile(path));
if (extendedResult.error) {
errors.push(extendedResult.error);
return;
}
const extendedDirname = getDirectoryPath(extendedConfigPath);
const relativeDifference = convertToRelativePath(extendedDirname, basePath, getCanonicalFileName);
const updatePath: (path: string) => string = path => isRootedDiskPath(path) ? path : combinePaths(relativeDifference, path);
// Merge configs (copy the resolution stack so it is never reused between branches in potential diamond-problem scenarios)
const result = parseJsonConfigFileContent(extendedResult.config, host, extendedDirname, /*existingOptions*/undefined, getBaseFileName(extendedConfigPath), resolutionStack.concat([resolvedPath]));
errors.push(...result.errors);
const [include, exclude, files] = map(["include", "exclude", "files"], key => {
if (!json[key] && extendedResult.config[key]) {
return map(extendedResult.config[key], updatePath);
}
});
return [include, exclude, files, result.options];
}
function getFileNames(errors: Diagnostic[]): ExpandResult {
let fileNames: string[];
if (hasProperty(json, "files")) {
@@ -752,14 +931,13 @@ namespace ts {
errors.push(createCompilerDiagnostic(Diagnostics.Unknown_option_excludes_Did_you_mean_exclude));
}
else {
// By default, exclude common package folders
// By default, exclude common package folders and the outDir
excludeSpecs = ["node_modules", "bower_components", "jspm_packages"];
}
// Always exclude the output directory unless explicitly included
const outDir = json["compilerOptions"] && json["compilerOptions"]["outDir"];
if (outDir) {
excludeSpecs.push(outDir);
const outDir = json["compilerOptions"] && json["compilerOptions"]["outDir"];
if (outDir) {
excludeSpecs.push(outDir);
}
}
if (fileNames === undefined && includeSpecs === undefined) {
@@ -770,13 +948,24 @@ namespace ts {
}
}
export function convertCompileOnSaveOptionFromJson(jsonOption: any, basePath: string, errors: Diagnostic[]): boolean {
if (!hasProperty(jsonOption, compileOnSaveCommandLineOption.name)) {
return false;
}
const result = convertJsonOption(compileOnSaveCommandLineOption, jsonOption["compileOnSave"], basePath, errors);
if (typeof result === "boolean" && result) {
return result;
}
return false;
}
export function convertCompilerOptionsFromJson(jsonOptions: any, basePath: string, configFileName?: string): { options: CompilerOptions, errors: Diagnostic[] } {
const errors: Diagnostic[] = [];
const options = convertCompilerOptionsFromJsonWorker(jsonOptions, basePath, errors, configFileName);
return { options, errors };
}
export function convertTypingOptionsFromJson(jsonOptions: any, basePath: string, configFileName?: string): { options: CompilerOptions, errors: Diagnostic[] } {
export function convertTypingOptionsFromJson(jsonOptions: any, basePath: string, configFileName?: string): { options: TypingOptions, errors: Diagnostic[] } {
const errors: Diagnostic[] = [];
const options = convertTypingOptionsFromJsonWorker(jsonOptions, basePath, errors, configFileName);
return { options, errors };
@@ -785,7 +974,9 @@ namespace ts {
function convertCompilerOptionsFromJsonWorker(jsonOptions: any,
basePath: string, errors: Diagnostic[], configFileName?: string): CompilerOptions {
const options: CompilerOptions = getBaseFileName(configFileName) === "jsconfig.json" ? { allowJs: true } : {};
const options: CompilerOptions = getBaseFileName(configFileName) === "jsconfig.json"
? { allowJs: true, maxNodeModuleJsDepth: 2, allowSyntheticDefaultImports: true }
: {};
convertOptionsFromJson(optionDeclarations, jsonOptions, basePath, options, Diagnostics.Unknown_compiler_option_0, errors);
return options;
}
+347
View File
@@ -0,0 +1,347 @@
/// <reference path="sourcemap.ts" />
/* @internal */
namespace ts {
export interface CommentWriter {
reset(): void;
setSourceFile(sourceFile: SourceFile): void;
emitNodeWithComments(emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void): void;
emitBodyWithDetachedComments(node: Node, detachedRange: TextRange, emitCallback: (node: Node) => void): void;
emitTrailingCommentsOfPosition(pos: number): void;
}
export function createCommentWriter(host: EmitHost, writer: EmitTextWriter, sourceMap: SourceMapWriter): CommentWriter {
const compilerOptions = host.getCompilerOptions();
const extendedDiagnostics = compilerOptions.extendedDiagnostics;
const newLine = host.getNewLine();
const { emitPos } = sourceMap;
let containerPos = -1;
let containerEnd = -1;
let declarationListContainerEnd = -1;
let currentSourceFile: SourceFile;
let currentText: string;
let currentLineMap: number[];
let detachedCommentsInfo: { nodePos: number, detachedCommentEndPos: number}[];
let hasWrittenComment = false;
let disabled: boolean = compilerOptions.removeComments;
return {
reset,
setSourceFile,
emitNodeWithComments,
emitBodyWithDetachedComments,
emitTrailingCommentsOfPosition,
};
function emitNodeWithComments(emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void) {
if (disabled) {
emitCallback(emitContext, node);
return;
}
if (node) {
const { pos, end } = getCommentRange(node);
const emitFlags = getEmitFlags(node);
if ((pos < 0 && end < 0) || (pos === end)) {
// Both pos and end are synthesized, so just emit the node without comments.
if (emitFlags & EmitFlags.NoNestedComments) {
disabled = true;
emitCallback(emitContext, node);
disabled = false;
}
else {
emitCallback(emitContext, node);
}
}
else {
if (extendedDiagnostics) {
performance.mark("preEmitNodeWithComment");
}
const isEmittedNode = node.kind !== SyntaxKind.NotEmittedStatement;
const skipLeadingComments = pos < 0 || (emitFlags & EmitFlags.NoLeadingComments) !== 0;
const skipTrailingComments = end < 0 || (emitFlags & EmitFlags.NoTrailingComments) !== 0;
// Emit leading comments if the position is not synthesized and the node
// has not opted out from emitting leading comments.
if (!skipLeadingComments) {
emitLeadingComments(pos, isEmittedNode);
}
// Save current container state on the stack.
const savedContainerPos = containerPos;
const savedContainerEnd = containerEnd;
const savedDeclarationListContainerEnd = declarationListContainerEnd;
if (!skipLeadingComments) {
containerPos = pos;
}
if (!skipTrailingComments) {
containerEnd = end;
// To avoid invalid comment emit in a down-level binding pattern, we
// keep track of the last declaration list container's end
if (node.kind === SyntaxKind.VariableDeclarationList) {
declarationListContainerEnd = end;
}
}
if (extendedDiagnostics) {
performance.measure("commentTime", "preEmitNodeWithComment");
}
if (emitFlags & EmitFlags.NoNestedComments) {
disabled = true;
emitCallback(emitContext, node);
disabled = false;
}
else {
emitCallback(emitContext, node);
}
if (extendedDiagnostics) {
performance.mark("beginEmitNodeWithComment");
}
// Restore previous container state.
containerPos = savedContainerPos;
containerEnd = savedContainerEnd;
declarationListContainerEnd = savedDeclarationListContainerEnd;
// Emit trailing comments if the position is not synthesized and the node
// has not opted out from emitting leading comments and is an emitted node.
if (!skipTrailingComments && isEmittedNode) {
emitTrailingComments(end);
}
if (extendedDiagnostics) {
performance.measure("commentTime", "beginEmitNodeWithComment");
}
}
}
}
function emitBodyWithDetachedComments(node: Node, detachedRange: TextRange, emitCallback: (node: Node) => void) {
if (extendedDiagnostics) {
performance.mark("preEmitBodyWithDetachedComments");
}
const { pos, end } = detachedRange;
const emitFlags = getEmitFlags(node);
const skipLeadingComments = pos < 0 || (emitFlags & EmitFlags.NoLeadingComments) !== 0;
const skipTrailingComments = disabled || end < 0 || (emitFlags & EmitFlags.NoTrailingComments) !== 0;
if (!skipLeadingComments) {
emitDetachedCommentsAndUpdateCommentsInfo(detachedRange);
}
if (extendedDiagnostics) {
performance.measure("commentTime", "preEmitBodyWithDetachedComments");
}
if (emitFlags & EmitFlags.NoNestedComments && !disabled) {
disabled = true;
emitCallback(node);
disabled = false;
}
else {
emitCallback(node);
}
if (extendedDiagnostics) {
performance.mark("beginEmitBodyWithDetachedCommetns");
}
if (!skipTrailingComments) {
emitLeadingComments(detachedRange.end, /*isEmittedNode*/ true);
}
if (extendedDiagnostics) {
performance.measure("commentTime", "beginEmitBodyWithDetachedCommetns");
}
}
function emitLeadingComments(pos: number, isEmittedNode: boolean) {
hasWrittenComment = false;
if (isEmittedNode) {
forEachLeadingCommentToEmit(pos, emitLeadingComment);
}
else if (pos === 0) {
// If the node will not be emitted in JS, remove all the comments(normal, pinned and ///) associated with the node,
// unless it is a triple slash comment at the top of the file.
// For Example:
// /// <reference-path ...>
// declare var x;
// /// <reference-path ...>
// interface F {}
// The first /// will NOT be removed while the second one will be removed even though both node will not be emitted
forEachLeadingCommentToEmit(pos, emitTripleSlashLeadingComment);
}
}
function emitTripleSlashLeadingComment(commentPos: number, commentEnd: number, kind: SyntaxKind, hasTrailingNewLine: boolean, rangePos: number) {
if (isTripleSlashComment(commentPos, commentEnd)) {
emitLeadingComment(commentPos, commentEnd, kind, hasTrailingNewLine, rangePos);
}
}
function emitLeadingComment(commentPos: number, commentEnd: number, kind: SyntaxKind, hasTrailingNewLine: boolean, rangePos: number) {
if (!hasWrittenComment) {
emitNewLineBeforeLeadingCommentOfPosition(currentLineMap, writer, rangePos, commentPos);
hasWrittenComment = true;
}
// Leading comments are emitted at /*leading comment1 */space/*leading comment*/space
emitPos(commentPos);
writeCommentRange(currentText, currentLineMap, writer, commentPos, commentEnd, newLine);
emitPos(commentEnd);
if (hasTrailingNewLine) {
writer.writeLine();
}
else {
writer.write(" ");
}
}
function emitTrailingComments(pos: number) {
forEachTrailingCommentToEmit(pos, emitTrailingComment);
}
function emitTrailingComment(commentPos: number, commentEnd: number, kind: SyntaxKind, hasTrailingNewLine: boolean) {
// trailing comments are emitted at space/*trailing comment1 */space/*trailing comment2*/
if (!writer.isAtStartOfLine()) {
writer.write(" ");
}
emitPos(commentPos);
writeCommentRange(currentText, currentLineMap, writer, commentPos, commentEnd, newLine);
emitPos(commentEnd);
if (hasTrailingNewLine) {
writer.writeLine();
}
}
function emitTrailingCommentsOfPosition(pos: number) {
if (disabled) {
return;
}
if (extendedDiagnostics) {
performance.mark("beforeEmitTrailingCommentsOfPosition");
}
forEachTrailingCommentToEmit(pos, emitTrailingCommentOfPosition);
if (extendedDiagnostics) {
performance.measure("commentTime", "beforeEmitTrailingCommentsOfPosition");
}
}
function emitTrailingCommentOfPosition(commentPos: number, commentEnd: number, kind: SyntaxKind, hasTrailingNewLine: boolean) {
// trailing comments of a position are emitted at /*trailing comment1 */space/*trailing comment*/space
emitPos(commentPos);
writeCommentRange(currentText, currentLineMap, writer, commentPos, commentEnd, newLine);
emitPos(commentEnd);
if (hasTrailingNewLine) {
writer.writeLine();
}
else {
writer.write(" ");
}
}
function forEachLeadingCommentToEmit(pos: number, cb: (commentPos: number, commentEnd: number, kind: SyntaxKind, hasTrailingNewLine: boolean, rangePos: number) => void) {
// Emit the leading comments only if the container's pos doesn't match because the container should take care of emitting these comments
if (containerPos === -1 || pos !== containerPos) {
if (hasDetachedComments(pos)) {
forEachLeadingCommentWithoutDetachedComments(cb);
}
else {
forEachLeadingCommentRange(currentText, pos, cb, /*state*/ pos);
}
}
}
function forEachTrailingCommentToEmit(end: number, cb: (commentPos: number, commentEnd: number, kind: SyntaxKind, hasTrailingNewLine: boolean) => void) {
// Emit the trailing comments only if the container's end doesn't match because the container should take care of emitting these comments
if (containerEnd === -1 || (end !== containerEnd && end !== declarationListContainerEnd)) {
forEachTrailingCommentRange(currentText, end, cb);
}
}
function reset() {
currentSourceFile = undefined;
currentText = undefined;
currentLineMap = undefined;
detachedCommentsInfo = undefined;
}
function setSourceFile(sourceFile: SourceFile) {
currentSourceFile = sourceFile;
currentText = currentSourceFile.text;
currentLineMap = getLineStarts(currentSourceFile);
detachedCommentsInfo = undefined;
}
function hasDetachedComments(pos: number) {
return detachedCommentsInfo !== undefined && lastOrUndefined(detachedCommentsInfo).nodePos === pos;
}
function forEachLeadingCommentWithoutDetachedComments(cb: (commentPos: number, commentEnd: number, kind: SyntaxKind, hasTrailingNewLine: boolean, rangePos: number) => void) {
// get the leading comments from detachedPos
const pos = lastOrUndefined(detachedCommentsInfo).detachedCommentEndPos;
if (detachedCommentsInfo.length - 1) {
detachedCommentsInfo.pop();
}
else {
detachedCommentsInfo = undefined;
}
forEachLeadingCommentRange(currentText, pos, cb, /*state*/ pos);
}
function emitDetachedCommentsAndUpdateCommentsInfo(range: TextRange) {
const currentDetachedCommentInfo = emitDetachedComments(currentText, currentLineMap, writer, writeComment, range, newLine, disabled);
if (currentDetachedCommentInfo) {
if (detachedCommentsInfo) {
detachedCommentsInfo.push(currentDetachedCommentInfo);
}
else {
detachedCommentsInfo = [currentDetachedCommentInfo];
}
}
}
function writeComment(text: string, lineMap: number[], writer: EmitTextWriter, commentPos: number, commentEnd: number, newLine: string) {
emitPos(commentPos);
writeCommentRange(text, lineMap, writer, commentPos, commentEnd, newLine);
emitPos(commentEnd);
}
/**
* Determine if the given comment is a triple-slash
*
* @return true if the comment is a triple-slash comment else false
**/
function isTripleSlashComment(commentPos: number, commentEnd: number) {
// Verify this is /// comment, but do the regexp match only when we first can find /// in the comment text
// so that we don't end up computing comment string and doing match for all // comments
if (currentText.charCodeAt(commentPos + 1) === CharacterCodes.slash &&
commentPos + 2 < commentEnd &&
currentText.charCodeAt(commentPos + 2) === CharacterCodes.slash) {
const textSubStr = currentText.substring(commentPos, commentEnd);
return textSubStr.match(fullTripleSlashReferencePathRegEx) ||
textSubStr.match(fullTripleSlashAMDReferencePathRegEx) ?
true : false;
}
return false;
}
}
}
+589 -54
View File
@@ -1,7 +1,6 @@
/// <reference path="types.ts"/>
/// <reference path="types.ts"/>
/// <reference path="performance.ts" />
/* @internal */
namespace ts {
/**
@@ -47,6 +46,7 @@ namespace ts {
contains,
remove,
forEachValue: forEachValueInMap,
getKeys,
clear,
};
@@ -56,6 +56,14 @@ namespace ts {
}
}
function getKeys() {
const keys: Path[] = [];
for (const key in files) {
keys.push(<Path>key);
}
return keys;
}
// path should already be well-formed so it does not need to be normalized
function get(path: Path): T {
return files[toKey(path)];
@@ -113,8 +121,39 @@ namespace ts {
return undefined;
}
/** Like `forEach`, but assumes existence of array and fails if no truthy value is found. */
export function find<T, U>(array: T[], callback: (element: T, index: number) => U | undefined): U {
/**
* Iterates through `array` by index and performs the callback on each element of array until the callback
* returns a falsey value, then returns false.
* If no such value is found, the callback is applied to each element of array and `true` is returned.
*/
export function every<T>(array: T[], callback: (element: T, index: number) => boolean): boolean {
if (array) {
for (let i = 0, len = array.length; i < len; i++) {
if (!callback(array[i], i)) {
return false;
}
}
}
return true;
}
/** Works like Array.prototype.find, returning `undefined` if no element satisfying the predicate is found. */
export function find<T>(array: T[], predicate: (element: T, index: number) => boolean): T | undefined {
for (let i = 0, len = array.length; i < len; i++) {
const value = array[i];
if (predicate(value, i)) {
return value;
}
}
return undefined;
}
/**
* Returns the first truthy result of `callback`, or else fails.
* This is like `forEach`, but never returns undefined.
*/
export function findMap<T, U>(array: T[], callback: (element: T, index: number) => U | undefined): U {
for (let i = 0, len = array.length; i < len; i++) {
const result = callback(array[i], i);
if (result) {
@@ -155,11 +194,12 @@ namespace ts {
return -1;
}
export function countWhere<T>(array: T[], predicate: (x: T) => boolean): number {
export function countWhere<T>(array: T[], predicate: (x: T, i: number) => boolean): number {
let count = 0;
if (array) {
for (const v of array) {
if (predicate(v)) {
for (let i = 0; i < array.length; i++) {
const v = array[i];
if (predicate(v, i)) {
count++;
}
}
@@ -171,6 +211,8 @@ namespace ts {
* Filters an array by a predicate function. Returns the same array instance if the predicate is
* true for all elements, otherwise returns a new array instance containing the filtered subset.
*/
export function filter<T, U extends T>(array: T[], f: (x: T) => x is U): U[];
export function filter<T>(array: T[], f: (x: T) => boolean): T[]
export function filter<T>(array: T[], f: (x: T) => boolean): T[] {
if (array) {
const len = array.length;
@@ -218,12 +260,140 @@ namespace ts {
array.length = outIndex;
}
export function map<T, U>(array: T[], f: (x: T) => U): U[] {
export function map<T, U>(array: T[], f: (x: T, i: number) => U): U[] {
let result: U[];
if (array) {
result = [];
for (let i = 0; i < array.length; i++) {
const v = array[i];
result.push(f(v, i));
}
}
return result;
}
/**
* Flattens an array containing a mix of array or non-array elements.
*
* @param array The array to flatten.
*/
export function flatten<T>(array: (T | T[])[]): T[] {
let result: T[];
if (array) {
result = [];
for (const v of array) {
result.push(f(v));
if (v) {
if (isArray(v)) {
addRange(result, v);
}
else {
result.push(v);
}
}
}
}
return result;
}
/**
* Maps an array. If the mapped value is an array, it is spread into the result.
*
* @param array The array to map.
* @param mapfn The callback used to map the result into one or more values.
*/
export function flatMap<T, U>(array: T[], mapfn: (x: T, i: number) => U | U[]): U[] {
let result: U[];
if (array) {
result = [];
for (let i = 0; i < array.length; i++) {
const v = mapfn(array[i], i);
if (v) {
if (isArray(v)) {
addRange(result, v);
}
else {
result.push(v);
}
}
}
}
return result;
}
/**
* Computes the first matching span of elements and returns a tuple of the first span
* and the remaining elements.
*/
export function span<T>(array: T[], f: (x: T, i: number) => boolean): [T[], T[]] {
if (array) {
for (let i = 0; i < array.length; i++) {
if (!f(array[i], i)) {
return [array.slice(0, i), array.slice(i)];
}
}
return [array.slice(0), []];
}
return undefined;
}
/**
* Maps contiguous spans of values with the same key.
*
* @param array The array to map.
* @param keyfn A callback used to select the key for an element.
* @param mapfn A callback used to map a contiguous chunk of values to a single value.
*/
export function spanMap<T, K, U>(array: T[], keyfn: (x: T, i: number) => K, mapfn: (chunk: T[], key: K, start: number, end: number) => U): U[] {
let result: U[];
if (array) {
result = [];
const len = array.length;
let previousKey: K;
let key: K;
let start = 0;
let pos = 0;
while (start < len) {
while (pos < len) {
const value = array[pos];
key = keyfn(value, pos);
if (pos === 0) {
previousKey = key;
}
else if (key !== previousKey) {
break;
}
pos++;
}
if (start < pos) {
const v = mapfn(array.slice(start, pos), previousKey, start, pos);
if (v) {
result.push(v);
}
start = pos;
}
previousKey = key;
pos++;
}
}
return result;
}
export function mapObject<T, U>(object: MapLike<T>, f: (key: string, x: T) => [string, U]): MapLike<U> {
let result: MapLike<U>;
if (object) {
result = {};
for (const v of getOwnKeys(object)) {
const [key, value]: [string, U] = f(v, object[v]) || [undefined, undefined];
if (key !== undefined) {
result[key] = value;
}
}
}
return result;
@@ -232,10 +402,10 @@ namespace ts {
export function concatenate<T>(array1: T[], array2: T[]): T[] {
if (!array2 || !array2.length) return array1;
if (!array1 || !array1.length) return array2;
return array1.concat(array2);
return [...array1, ...array2];
}
// TODO: fixme (N^2) - add optional comparer so collection can be sorted before deduplication.
export function deduplicate<T>(array: T[], areEqual?: (a: T, b: T) => boolean): T[] {
let result: T[];
if (array) {
@@ -252,6 +422,27 @@ namespace ts {
return result;
}
/**
* Compacts an array, removing any falsey elements.
*/
export function compact<T>(array: T[]): T[] {
let result: T[];
if (array) {
for (let i = 0; i < array.length; i++) {
const v = array[i];
if (result || !v) {
if (!result) {
result = array.slice(0, i);
}
if (v) {
result.push(v);
}
}
}
}
return result || array;
}
export function sum(array: any[], prop: string): number {
let result = 0;
for (const v of array) {
@@ -263,7 +454,9 @@ namespace ts {
export function addRange<T>(to: T[], from: T[]): void {
if (to && from) {
for (const v of from) {
to.push(v);
if (v !== undefined) {
to.push(v);
}
}
}
}
@@ -278,15 +471,31 @@ namespace ts {
return true;
}
export function firstOrUndefined<T>(array: T[]): T {
return array && array.length > 0
? array[0]
: undefined;
}
export function singleOrUndefined<T>(array: T[]): T {
return array && array.length === 1
? array[0]
: undefined;
}
export function singleOrMany<T>(array: T[]): T | T[] {
return array && array.length === 1
? array[0]
: array;
}
/**
* Returns the last element of an array if non-empty, undefined otherwise.
*/
export function lastOrUndefined<T>(array: T[]): T {
if (array.length === 0) {
return undefined;
}
return array[array.length - 1];
return array && array.length > 0
? array[array.length - 1]
: undefined;
}
/**
@@ -296,18 +505,25 @@ namespace ts {
* @param array A sorted array whose first element must be no larger than number
* @param number The value to be searched for in the array.
*/
export function binarySearch(array: number[], value: number): number {
export function binarySearch<T>(array: T[], value: T, comparer?: (v1: T, v2: T) => number): number {
if (!array || array.length === 0) {
return -1;
}
let low = 0;
let high = array.length - 1;
comparer = comparer !== undefined
? comparer
: (v1, v2) => (v1 < v2 ? -1 : (v1 > v2 ? 1 : 0));
while (low <= high) {
const middle = low + ((high - low) >> 1);
const midValue = array[middle];
if (midValue === value) {
if (comparer(midValue, value) === 0) {
return middle;
}
else if (midValue > value) {
else if (comparer(midValue, value) > 0) {
high = middle - 1;
}
else {
@@ -318,14 +534,15 @@ namespace ts {
return ~low;
}
export function reduceLeft<T>(array: T[], f: (a: T, x: T) => T): T;
export function reduceLeft<T, U>(array: T[], f: (a: U, x: T) => U, initial: U): U;
export function reduceLeft<T, U>(array: T[], f: (a: U, x: T) => U, initial?: U): U {
if (array) {
const count = array.length;
if (count > 0) {
let pos = 0;
let result: T | U;
export function reduceLeft<T, U>(array: T[], f: (memo: U, value: T, i: number) => U, initial: U, start?: number, count?: number): U;
export function reduceLeft<T>(array: T[], f: (memo: T, value: T, i: number) => T): T;
export function reduceLeft<T>(array: T[], f: (memo: T, value: T, i: number) => T, initial?: T, start?: number, count?: number): T {
if (array && array.length > 0) {
const size = array.length;
if (size > 0) {
let pos = start === undefined || start < 0 ? 0 : start;
const end = count === undefined || pos + count > size - 1 ? size - 1 : pos + count;
let result: T;
if (arguments.length <= 2) {
result = array[pos];
pos++;
@@ -333,23 +550,25 @@ namespace ts {
else {
result = initial;
}
while (pos < count) {
result = f(<U>result, array[pos]);
while (pos <= end) {
result = f(result, array[pos], pos);
pos++;
}
return <U>result;
return result;
}
}
return initial;
}
export function reduceRight<T>(array: T[], f: (a: T, x: T) => T): T;
export function reduceRight<T, U>(array: T[], f: (a: U, x: T) => U, initial: U): U;
export function reduceRight<T, U>(array: T[], f: (a: U, x: T) => U, initial?: U): U {
export function reduceRight<T, U>(array: T[], f: (memo: U, value: T, i: number) => U, initial: U, start?: number, count?: number): U;
export function reduceRight<T>(array: T[], f: (memo: T, value: T, i: number) => T): T;
export function reduceRight<T>(array: T[], f: (memo: T, value: T, i: number) => T, initial?: T, start?: number, count?: number): T {
if (array) {
let pos = array.length - 1;
if (pos >= 0) {
let result: T | U;
const size = array.length;
if (size > 0) {
let pos = start === undefined || start > size - 1 ? size - 1 : start;
const end = count === undefined || pos - count < 0 ? 0 : pos - count;
let result: T;
if (arguments.length <= 2) {
result = array[pos];
pos--;
@@ -357,11 +576,11 @@ namespace ts {
else {
result = initial;
}
while (pos >= 0) {
result = f(<U>result, array[pos]);
while (pos >= end) {
result = f(result, array[pos], pos);
pos--;
}
return <U>result;
return result;
}
}
return initial;
@@ -450,6 +669,18 @@ namespace ts {
}
}
export function assign<T1 extends MapLike<{}>, T2, T3>(t: T1, arg1: T2, arg2: T3): T1 & T2 & T3;
export function assign<T1 extends MapLike<{}>, T2>(t: T1, arg1: T2): T1 & T2;
export function assign<T1 extends MapLike<{}>>(t: T1, ...args: any[]): any;
export function assign<T1 extends MapLike<{}>>(t: T1, ...args: any[]) {
for (const arg of args) {
for (const p of getOwnKeys(arg)) {
t[p] = arg[p];
}
}
return t;
}
/**
* Reduce the properties of a map.
*
@@ -525,6 +756,15 @@ namespace ts {
return result;
}
export function isEmpty<T>(map: Map<T>) {
for (const id in map) {
if (hasProperty(map, id)) {
return false;
}
}
return true;
}
export function cloneMap<T>(map: Map<T>) {
const clone = createMap<T>();
copyProperties(map, clone);
@@ -552,6 +792,36 @@ namespace ts {
return result;
}
/**
* Adds the value to an array of values associated with the key, and returns the array.
* Creates the array if it does not already exist.
*/
export function multiMapAdd<V>(map: Map<V[]>, key: string, value: V): V[] {
const values = map[key];
if (values) {
values.push(value);
return values;
}
else {
return map[key] = [value];
}
}
/**
* Removes a value from an array of values associated with the key.
* Does not preserve the order of those values.
* Does nothing if `key` is not in `map`, or `value` is not in `map[key]`.
*/
export function multiMapRemove<V>(map: Map<V[]>, key: string, value: V): void {
const values = map[key];
if (values) {
unorderedRemoveItem(values, value);
if (!values.length) {
delete map[key];
}
}
}
/**
* Tests whether a value is an array.
*/
@@ -570,6 +840,72 @@ namespace ts {
};
}
/**
* High-order function, creates a function that executes a function composition.
* For example, `chain(a, b)` is the equivalent of `x => ((a', b') => y => b'(a'(y)))(a(x), b(x))`
*
* @param args The functions to chain.
*/
export function chain<T, U>(...args: ((t: T) => (u: U) => U)[]): (t: T) => (u: U) => U;
export function chain<T, U>(a: (t: T) => (u: U) => U, b: (t: T) => (u: U) => U, c: (t: T) => (u: U) => U, d: (t: T) => (u: U) => U, e: (t: T) => (u: U) => U): (t: T) => (u: U) => U {
if (e) {
const args: ((t: T) => (u: U) => U)[] = [];
for (let i = 0; i < arguments.length; i++) {
args[i] = arguments[i];
}
return t => compose(...map(args, f => f(t)));
}
else if (d) {
return t => compose(a(t), b(t), c(t), d(t));
}
else if (c) {
return t => compose(a(t), b(t), c(t));
}
else if (b) {
return t => compose(a(t), b(t));
}
else if (a) {
return t => compose(a(t));
}
else {
return t => u => u;
}
}
/**
* High-order function, composes functions. Note that functions are composed inside-out;
* for example, `compose(a, b)` is the equivalent of `x => b(a(x))`.
*
* @param args The functions to compose.
*/
export function compose<T>(...args: ((t: T) => T)[]): (t: T) => T;
export function compose<T>(a: (t: T) => T, b: (t: T) => T, c: (t: T) => T, d: (t: T) => T, e: (t: T) => T): (t: T) => T {
if (e) {
const args: ((t: T) => T)[] = [];
for (let i = 0; i < arguments.length; i++) {
args[i] = arguments[i];
}
return t => reduceLeft<(t: T) => T, T>(args, (u, f) => f(u), t);
}
else if (d) {
return t => d(c(b(a(t))));
}
else if (c) {
return t => c(b(a(t)));
}
else if (b) {
return t => b(a(t));
}
else if (a) {
return t => a(t);
}
else {
return t => t;
}
}
function formatStringFromArgs(text: string, args: { [index: number]: any; }, baseIndex?: number): string {
baseIndex = baseIndex || 0;
@@ -790,7 +1126,8 @@ namespace ts {
return 0;
}
export let directorySeparator = "/";
export const directorySeparator = "/";
const directorySeparatorCharCode = CharacterCodes.slash;
function getNormalizedParts(normalizedSlashedPath: string, rootLength: number) {
const parts = normalizedSlashedPath.substr(rootLength).split(directorySeparator);
const normalized: string[] = [];
@@ -815,8 +1152,20 @@ namespace ts {
export function normalizePath(path: string): string {
path = normalizeSlashes(path);
const rootLength = getRootLength(path);
const root = path.substr(0, rootLength);
const normalized = getNormalizedParts(path, rootLength);
return path.substr(0, rootLength) + normalized.join(directorySeparator);
if (normalized.length) {
const joinedParts = root + normalized.join(directorySeparator);
return pathEndsWithDirectorySeparator(path) ? joinedParts + directorySeparator : joinedParts;
}
else {
return root;
}
}
/** A path ending with '/' refers to a directory only, never a file. */
export function pathEndsWithDirectorySeparator(path: string): boolean {
return path.charCodeAt(path.length - 1) === directorySeparatorCharCode;
}
export function getDirectoryPath(path: Path): Path;
@@ -829,10 +1178,49 @@ namespace ts {
return path && !isRootedDiskPath(path) && path.indexOf("://") !== -1;
}
export function isExternalModuleNameRelative(moduleName: string): boolean {
// TypeScript 1.0 spec (April 2014): 11.2.1
// An external module name is "relative" if the first term is "." or "..".
return /^\.\.?($|[\\/])/.test(moduleName);
}
export function getEmitScriptTarget(compilerOptions: CompilerOptions) {
return compilerOptions.target || ScriptTarget.ES3;
}
export function getEmitModuleKind(compilerOptions: CompilerOptions) {
return typeof compilerOptions.module === "number" ?
compilerOptions.module :
getEmitScriptTarget(compilerOptions) === ScriptTarget.ES6 ? ModuleKind.ES6 : ModuleKind.CommonJS;
}
/* @internal */
export function hasZeroOrOneAsteriskCharacter(str: string): boolean {
let seenAsterisk = false;
for (let i = 0; i < str.length; i++) {
if (str.charCodeAt(i) === CharacterCodes.asterisk) {
if (!seenAsterisk) {
seenAsterisk = true;
}
else {
// have already seen asterisk
return false;
}
}
}
return true;
}
export function isRootedDiskPath(path: string) {
return getRootLength(path) !== 0;
}
export function convertToRelativePath(absoluteOrRelativePath: string, basePath: string, getCanonicalFileName: (path: string) => string): string {
return !isRootedDiskPath(absoluteOrRelativePath)
? absoluteOrRelativePath
: getRelativePathToDirectoryOrUrl(basePath, absoluteOrRelativePath, basePath, getCanonicalFileName, /*isAbsolutePathAnUrl*/ false);
}
function normalizedPathComponents(path: string, rootLength: number) {
const normalizedParts = getNormalizedParts(path, rootLength);
return [path.substr(0, rootLength)].concat(normalizedParts);
@@ -1315,6 +1703,8 @@ namespace ts {
* List of supported extensions in order of file resolution precedence.
*/
export const supportedTypeScriptExtensions = [".ts", ".tsx", ".d.ts"];
/** Must have ".d.ts" first because if ".ts" goes first, that will be detected as the extension instead of ".d.ts". */
export const supportedTypescriptExtensionsForExtractExtension = [".d.ts", ".ts", ".tsx"];
export const supportedJavascriptExtensions = [".js", ".jsx"];
const allSupportedExtensions = supportedTypeScriptExtensions.concat(supportedJavascriptExtensions);
@@ -1322,6 +1712,14 @@ namespace ts {
return options && options.allowJs ? allSupportedExtensions : supportedTypeScriptExtensions;
}
export function hasJavaScriptFileExtension(fileName: string) {
return forEach(supportedJavascriptExtensions, extension => fileExtensionIs(fileName, extension));
}
export function hasTypeScriptFileExtension(fileName: string) {
return forEach(supportedTypeScriptExtensions, extension => fileExtensionIs(fileName, extension));
}
export function isSupportedSourceFileName(fileName: string, compilerOptions?: CompilerOptions) {
if (!fileName) { return false; }
@@ -1397,8 +1795,12 @@ namespace ts {
return path;
}
export function tryRemoveExtension(path: string, extension: string): string {
return fileExtensionIs(path, extension) ? path.substring(0, path.length - extension.length) : undefined;
export function tryRemoveExtension(path: string, extension: string): string | undefined {
return fileExtensionIs(path, extension) ? removeExtension(path, extension) : undefined;
}
export function removeExtension(path: string, extension: string): string {
return path.substring(0, path.length - extension.length);
}
export function isJsxOrTsxExtension(ext: string): boolean {
@@ -1433,11 +1835,15 @@ namespace ts {
}
function Node(this: Node, kind: SyntaxKind, pos: number, end: number) {
this.id = 0;
this.kind = kind;
this.pos = pos;
this.end = end;
this.flags = NodeFlags.None;
this.modifierFlagsCache = ModifierFlags.None;
this.transformFlags = TransformFlags.None;
this.parent = undefined;
this.original = undefined;
}
export let objectAllocator: ObjectAllocator = {
@@ -1458,10 +1864,13 @@ namespace ts {
}
export namespace Debug {
const currentAssertionLevel = AssertionLevel.None;
declare var process: any;
declare var require: any;
let currentAssertionLevel: AssertionLevel;
export function shouldAssert(level: AssertionLevel): boolean {
return currentAssertionLevel >= level;
return getCurrentAssertionLevel() >= level;
}
export function assert(expression: boolean, message?: string, verboseDebugInfo?: () => string): void {
@@ -1478,22 +1887,148 @@ namespace ts {
export function fail(message?: string): void {
Debug.assert(/*expression*/ false, message);
}
function getCurrentAssertionLevel() {
if (currentAssertionLevel !== undefined) {
return currentAssertionLevel;
}
if (sys === undefined) {
return AssertionLevel.None;
}
const developmentMode = /^development$/i.test(getEnvironmentVariable("NODE_ENV"));
currentAssertionLevel = developmentMode
? AssertionLevel.Normal
: AssertionLevel.None;
return currentAssertionLevel;
}
}
export function copyListRemovingItem<T>(item: T, list: T[]) {
const copiedList: T[] = [];
for (const e of list) {
if (e !== item) {
copiedList.push(e);
export function getEnvironmentVariable(name: string, host?: CompilerHost) {
if (host && host.getEnvironmentVariable) {
return host.getEnvironmentVariable(name);
}
if (sys && sys.getEnvironmentVariable) {
return sys.getEnvironmentVariable(name);
}
return "";
}
/** Remove an item from an array, moving everything to its right one space left. */
export function orderedRemoveItemAt<T>(array: T[], index: number): void {
// This seems to be faster than either `array.splice(i, 1)` or `array.copyWithin(i, i+ 1)`.
for (let i = index; i < array.length - 1; i++) {
array[i] = array[i + 1];
}
array.pop();
}
export function unorderedRemoveItemAt<T>(array: T[], index: number): void {
// Fill in the "hole" left at `index`.
array[index] = array[array.length - 1];
array.pop();
}
/** Remove the *first* occurrence of `item` from the array. */
export function unorderedRemoveItem<T>(array: T[], item: T): void {
unorderedRemoveFirstItemWhere(array, element => element === item);
}
/** Remove the *first* element satisfying `predicate`. */
function unorderedRemoveFirstItemWhere<T>(array: T[], predicate: (element: T) => boolean): void {
for (let i = 0; i < array.length; i++) {
if (predicate(array[i])) {
unorderedRemoveItemAt(array, i);
break;
}
}
return copiedList;
}
export function createGetCanonicalFileName(useCaseSensitivefileNames: boolean): (fileName: string) => string {
return useCaseSensitivefileNames
export function createGetCanonicalFileName(useCaseSensitiveFileNames: boolean): (fileName: string) => string {
return useCaseSensitiveFileNames
? ((fileName) => fileName)
: ((fileName) => fileName.toLowerCase());
}
/**
* patternStrings contains both pattern strings (containing "*") and regular strings.
* Return an exact match if possible, or a pattern match, or undefined.
* (These are verified by verifyCompilerOptions to have 0 or 1 "*" characters.)
*/
/* @internal */
export function matchPatternOrExact(patternStrings: string[], candidate: string): string | Pattern | undefined {
const patterns: Pattern[] = [];
for (const patternString of patternStrings) {
const pattern = tryParsePattern(patternString);
if (pattern) {
patterns.push(pattern);
}
else if (patternString === candidate) {
// pattern was matched as is - no need to search further
return patternString;
}
}
return findBestPatternMatch(patterns, _ => _, candidate);
}
/* @internal */
export function patternText({prefix, suffix}: Pattern): string {
return `${prefix}*${suffix}`;
}
/**
* Given that candidate matches pattern, returns the text matching the '*'.
* E.g.: matchedText(tryParsePattern("foo*baz"), "foobarbaz") === "bar"
*/
/* @internal */
export function matchedText(pattern: Pattern, candidate: string): string {
Debug.assert(isPatternMatch(pattern, candidate));
return candidate.substr(pattern.prefix.length, candidate.length - pattern.suffix.length);
}
/** Return the object corresponding to the best pattern to match `candidate`. */
/* @internal */
export function findBestPatternMatch<T>(values: T[], getPattern: (value: T) => Pattern, candidate: string): T | undefined {
let matchedValue: T | undefined = undefined;
// use length of prefix as betterness criteria
let longestMatchPrefixLength = -1;
for (const v of values) {
const pattern = getPattern(v);
if (isPatternMatch(pattern, candidate) && pattern.prefix.length > longestMatchPrefixLength) {
longestMatchPrefixLength = pattern.prefix.length;
matchedValue = v;
}
}
return matchedValue;
}
function isPatternMatch({prefix, suffix}: Pattern, candidate: string) {
return candidate.length >= prefix.length + suffix.length &&
startsWith(candidate, prefix) &&
endsWith(candidate, suffix);
}
/* @internal */
export function tryParsePattern(pattern: string): Pattern | undefined {
// This should be verified outside of here and a proper error thrown.
Debug.assert(hasZeroOrOneAsteriskCharacter(pattern));
const indexOfStar = pattern.indexOf("*");
return indexOfStar === -1 ? undefined : {
prefix: pattern.substr(0, indexOfStar),
suffix: pattern.substr(indexOfStar + 1)
};
}
export function positionIsSynthesized(pos: number): boolean {
// This is a fast way of testing the following conditions:
// pos === undefined || pos === null || isNaN(pos) || pos < 0;
return !(pos >= 0);
}
}
+49 -42
View File
@@ -36,12 +36,12 @@ namespace ts {
return declarationDiagnostics.getDiagnostics(targetSourceFile ? targetSourceFile.fileName : undefined);
function getDeclarationDiagnosticsFromFile({ declarationFilePath }: EmitFileNames, sources: SourceFile[], isBundledEmit: boolean) {
emitDeclarations(host, resolver, declarationDiagnostics, declarationFilePath, sources, isBundledEmit);
emitDeclarations(host, resolver, declarationDiagnostics, declarationFilePath, sources, isBundledEmit, /*emitOnlyDtsFiles*/ false);
}
}
function emitDeclarations(host: EmitHost, resolver: EmitResolver, emitterDiagnostics: DiagnosticCollection, declarationFilePath: string,
sourceFiles: SourceFile[], isBundledEmit: boolean): DeclarationEmit {
sourceFiles: SourceFile[], isBundledEmit: boolean, emitOnlyDtsFiles: boolean): DeclarationEmit {
const newLine = host.getNewLine();
const compilerOptions = host.getCompilerOptions();
@@ -98,7 +98,7 @@ namespace ts {
// global file reference is added only
// - if it is not bundled emit (because otherwise it would be self reference)
// - and it is not already added
if (writeReferencePath(referencedFile, !isBundledEmit && !addedGlobalFileReference)) {
if (writeReferencePath(referencedFile, !isBundledEmit && !addedGlobalFileReference, emitOnlyDtsFiles)) {
addedGlobalFileReference = true;
}
emittedReferencedFiles.push(referencedFile);
@@ -306,7 +306,7 @@ namespace ts {
}
function trackSymbol(symbol: Symbol, enclosingDeclaration?: Node, meaning?: SymbolFlags) {
handleSymbolAccessibilityError(resolver.isSymbolAccessible(symbol, enclosingDeclaration, meaning));
handleSymbolAccessibilityError(resolver.isSymbolAccessible(symbol, enclosingDeclaration, meaning, /*shouldComputeAliasesToMakeVisible*/ true));
recordTypeReferenceDirectivesIfNecessary(resolver.getTypeReferenceDirectivesForSymbol(symbol, meaning));
}
@@ -327,7 +327,7 @@ namespace ts {
}
else {
errorNameNode = declaration.name;
resolver.writeTypeOfDeclaration(declaration, enclosingDeclaration, TypeFormatFlags.UseTypeOfFunction, writer);
resolver.writeTypeOfDeclaration(declaration, enclosingDeclaration, TypeFormatFlags.UseTypeOfFunction | TypeFormatFlags.UseTypeAliasValue, writer);
errorNameNode = undefined;
}
}
@@ -341,7 +341,7 @@ namespace ts {
}
else {
errorNameNode = signature.name;
resolver.writeReturnTypeOfSignatureDeclaration(signature, enclosingDeclaration, TypeFormatFlags.UseTypeOfFunction, writer);
resolver.writeReturnTypeOfSignatureDeclaration(signature, enclosingDeclaration, TypeFormatFlags.UseTypeOfFunction | TypeFormatFlags.UseTypeAliasValue, writer);
errorNameNode = undefined;
}
}
@@ -374,7 +374,7 @@ namespace ts {
const jsDocComments = getJsDocCommentsFromText(declaration, currentText);
emitNewLineBeforeLeadingComments(currentLineMap, writer, declaration, jsDocComments);
// jsDoc comments are emitted at /*leading comment1 */space/*leading comment*/space
emitComments(currentText, currentLineMap, writer, jsDocComments, /*trailingSeparator*/ true, newLine, writeCommentRange);
emitComments(currentText, currentLineMap, writer, jsDocComments, /*leadingSeparator*/ false, /*trailingSeparator*/ true, newLine, writeCommentRange);
}
}
@@ -563,7 +563,7 @@ namespace ts {
write(tempVarName);
write(": ");
writer.getSymbolAccessibilityDiagnostic = getDefaultExportAccessibilityDiagnostic;
resolver.writeTypeOfExpression(node.expression, enclosingDeclaration, TypeFormatFlags.UseTypeOfFunction, writer);
resolver.writeTypeOfExpression(node.expression, enclosingDeclaration, TypeFormatFlags.UseTypeOfFunction | TypeFormatFlags.UseTypeAliasValue, writer);
write(";");
writeLine();
write(node.isExportEquals ? "export = " : "export default ");
@@ -655,12 +655,13 @@ namespace ts {
function emitModuleElementDeclarationFlags(node: Node) {
// If the node is parented in the current source file we need to emit export declare or just export
if (node.parent.kind === SyntaxKind.SourceFile) {
const modifiers = getModifierFlags(node);
// If the node is exported
if (node.flags & NodeFlags.Export) {
if (modifiers & ModifierFlags.Export) {
write("export ");
}
if (node.flags & NodeFlags.Default) {
if (modifiers & ModifierFlags.Default) {
write("default ");
}
else if (node.kind !== SyntaxKind.InterfaceDeclaration && !noDeclare) {
@@ -669,21 +670,21 @@ namespace ts {
}
}
function emitClassMemberDeclarationFlags(flags: NodeFlags) {
if (flags & NodeFlags.Private) {
function emitClassMemberDeclarationFlags(flags: ModifierFlags) {
if (flags & ModifierFlags.Private) {
write("private ");
}
else if (flags & NodeFlags.Protected) {
else if (flags & ModifierFlags.Protected) {
write("protected ");
}
if (flags & NodeFlags.Static) {
if (flags & ModifierFlags.Static) {
write("static ");
}
if (flags & NodeFlags.Readonly) {
if (flags & ModifierFlags.Readonly) {
write("readonly ");
}
if (flags & NodeFlags.Abstract) {
if (flags & ModifierFlags.Abstract) {
write("abstract ");
}
}
@@ -692,7 +693,7 @@ namespace ts {
// note usage of writer. methods instead of aliases created, just to make sure we are using
// correct writer especially to handle asynchronous alias writing
emitJsDocComments(node);
if (node.flags & NodeFlags.Export) {
if (hasModifier(node, ModifierFlags.Export)) {
write("export ");
}
write("import ");
@@ -731,7 +732,7 @@ namespace ts {
function writeImportDeclaration(node: ImportDeclaration) {
emitJsDocComments(node);
if (node.flags & NodeFlags.Export) {
if (hasModifier(node, ModifierFlags.Export)) {
write("export ");
}
write("import ");
@@ -926,7 +927,7 @@ namespace ts {
}
function isPrivateMethodTypeParameter(node: TypeParameterDeclaration) {
return node.parent.kind === SyntaxKind.MethodDeclaration && (node.parent.flags & NodeFlags.Private);
return node.parent.kind === SyntaxKind.MethodDeclaration && hasModifier(node.parent, ModifierFlags.Private);
}
function emitTypeParameters(typeParameters: TypeParameterDeclaration[]) {
@@ -976,7 +977,7 @@ namespace ts {
case SyntaxKind.MethodDeclaration:
case SyntaxKind.MethodSignature:
if (node.parent.flags & NodeFlags.Static) {
if (hasModifier(node.parent, ModifierFlags.Static)) {
diagnosticMessage = Diagnostics.Type_parameter_0_of_public_static_method_from_exported_class_has_or_is_using_private_name_1;
}
else if (node.parent.parent.kind === SyntaxKind.ClassDeclaration) {
@@ -1025,7 +1026,7 @@ namespace ts {
}
else {
writer.getSymbolAccessibilityDiagnostic = getHeritageClauseVisibilityError;
resolver.writeBaseConstructorTypeOfClass(<ClassLikeDeclaration>enclosingDeclaration, enclosingDeclaration, TypeFormatFlags.UseTypeOfFunction, writer);
resolver.writeBaseConstructorTypeOfClass(<ClassLikeDeclaration>enclosingDeclaration, enclosingDeclaration, TypeFormatFlags.UseTypeOfFunction | TypeFormatFlags.UseTypeAliasValue, writer);
}
function getHeritageClauseVisibilityError(symbolAccessibilityResult: SymbolAccessibilityResult): SymbolAccessibilityDiagnostic {
@@ -1055,7 +1056,7 @@ namespace ts {
function emitParameterProperties(constructorDeclaration: ConstructorDeclaration) {
if (constructorDeclaration) {
forEach(constructorDeclaration.parameters, param => {
if (param.flags & NodeFlags.ParameterPropertyModifier) {
if (hasModifier(param, ModifierFlags.ParameterPropertyModifier)) {
emitPropertyDeclaration(param);
}
});
@@ -1064,7 +1065,7 @@ namespace ts {
emitJsDocComments(node);
emitModuleElementDeclarationFlags(node);
if (node.flags & NodeFlags.Abstract) {
if (hasModifier(node, ModifierFlags.Abstract)) {
write("abstract ");
}
@@ -1114,7 +1115,7 @@ namespace ts {
}
emitJsDocComments(node);
emitClassMemberDeclarationFlags(node.flags);
emitClassMemberDeclarationFlags(getModifierFlags(node));
emitVariableDeclaration(<VariableDeclaration>node);
write(";");
writeLine();
@@ -1132,14 +1133,20 @@ namespace ts {
// it if it's not a well known symbol. In that case, the text of the name will be exactly
// what we want, namely the name expression enclosed in brackets.
writeTextOfNode(currentText, node.name);
// If optional property emit ?
if ((node.kind === SyntaxKind.PropertyDeclaration || node.kind === SyntaxKind.PropertySignature || node.kind === SyntaxKind.Parameter) && hasQuestionToken(node)) {
// If optional property emit ? but in the case of parameterProperty declaration with "?" indicating optional parameter for the constructor
// we don't want to emit property declaration with "?"
if ((node.kind === SyntaxKind.PropertyDeclaration || node.kind === SyntaxKind.PropertySignature ||
(node.kind === SyntaxKind.Parameter && !isParameterPropertyDeclaration(node))) && hasQuestionToken(node)) {
write("?");
}
if ((node.kind === SyntaxKind.PropertyDeclaration || node.kind === SyntaxKind.PropertySignature) && node.parent.kind === SyntaxKind.TypeLiteral) {
emitTypeOfVariableDeclarationFromTypeLiteral(node);
}
else if (!(node.flags & NodeFlags.Private)) {
else if (resolver.isLiteralConstDeclaration(node)) {
write(" = ");
resolver.writeLiteralConstValue(node, writer);
}
else if (!hasModifier(node, ModifierFlags.Private)) {
writeTypeOfDeclaration(node, node.type, getVariableDeclarationTypeVisibilityError);
}
}
@@ -1156,7 +1163,7 @@ namespace ts {
// This check is to ensure we don't report error on constructor parameter property as that error would be reported during parameter emit
else if (node.kind === SyntaxKind.PropertyDeclaration || node.kind === SyntaxKind.PropertySignature) {
// TODO(jfreeman): Deal with computed properties in error reporting.
if (node.flags & NodeFlags.Static) {
if (hasModifier(node, ModifierFlags.Static)) {
return symbolAccessibilityResult.errorModuleName ?
symbolAccessibilityResult.accessibility === SymbolAccessibility.CannotBeNamed ?
Diagnostics.Public_static_property_0_of_exported_class_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named :
@@ -1267,9 +1274,9 @@ namespace ts {
if (node === accessors.firstAccessor) {
emitJsDocComments(accessors.getAccessor);
emitJsDocComments(accessors.setAccessor);
emitClassMemberDeclarationFlags(node.flags | (accessors.setAccessor ? 0 : NodeFlags.Readonly));
emitClassMemberDeclarationFlags(getModifierFlags(node) | (accessors.setAccessor ? 0 : ModifierFlags.Readonly));
writeTextOfNode(currentText, node.name);
if (!(node.flags & NodeFlags.Private)) {
if (!hasModifier(node, ModifierFlags.Private)) {
accessorWithTypeAnnotation = node;
let type = getTypeAnnotationFromAccessor(node);
if (!type) {
@@ -1300,7 +1307,7 @@ namespace ts {
let diagnosticMessage: DiagnosticMessage;
if (accessorWithTypeAnnotation.kind === SyntaxKind.SetAccessor) {
// Setters have to have type named and cannot infer it so, the type should always be named
if (accessorWithTypeAnnotation.parent.flags & NodeFlags.Static) {
if (hasModifier(accessorWithTypeAnnotation.parent, ModifierFlags.Static)) {
diagnosticMessage = symbolAccessibilityResult.errorModuleName ?
Diagnostics.Parameter_0_of_public_static_property_setter_from_exported_class_has_or_is_using_name_1_from_private_module_2 :
Diagnostics.Parameter_0_of_public_static_property_setter_from_exported_class_has_or_is_using_private_name_1;
@@ -1318,7 +1325,7 @@ namespace ts {
};
}
else {
if (accessorWithTypeAnnotation.flags & NodeFlags.Static) {
if (hasModifier(accessorWithTypeAnnotation, ModifierFlags.Static)) {
diagnosticMessage = symbolAccessibilityResult.errorModuleName ?
symbolAccessibilityResult.accessibility === SymbolAccessibility.CannotBeNamed ?
Diagnostics.Return_type_of_public_static_property_getter_from_exported_class_has_or_is_using_name_0_from_external_module_1_but_cannot_be_named :
@@ -1354,7 +1361,7 @@ namespace ts {
emitModuleElementDeclarationFlags(node);
}
else if (node.kind === SyntaxKind.MethodDeclaration || node.kind === SyntaxKind.Constructor) {
emitClassMemberDeclarationFlags(node.flags);
emitClassMemberDeclarationFlags(getModifierFlags(node));
}
if (node.kind === SyntaxKind.FunctionDeclaration) {
write("function ");
@@ -1385,7 +1392,7 @@ namespace ts {
if (node.kind === SyntaxKind.IndexSignature) {
// Index signature can have readonly modifier
emitClassMemberDeclarationFlags(node.flags);
emitClassMemberDeclarationFlags(getModifierFlags(node));
write("[");
}
else {
@@ -1426,7 +1433,7 @@ namespace ts {
emitType(node.type);
}
}
else if (node.kind !== SyntaxKind.Constructor && !(node.flags & NodeFlags.Private)) {
else if (node.kind !== SyntaxKind.Constructor && !hasModifier(node, ModifierFlags.Private)) {
writeReturnTypeAtSignature(node, getReturnTypeVisibilityError);
}
@@ -1466,7 +1473,7 @@ namespace ts {
case SyntaxKind.MethodDeclaration:
case SyntaxKind.MethodSignature:
if (node.flags & NodeFlags.Static) {
if (hasModifier(node, ModifierFlags.Static)) {
diagnosticMessage = symbolAccessibilityResult.errorModuleName ?
symbolAccessibilityResult.accessibility === SymbolAccessibility.CannotBeNamed ?
Diagnostics.Return_type_of_public_static_method_from_exported_class_has_or_is_using_name_0_from_external_module_1_but_cannot_be_named :
@@ -1532,7 +1539,7 @@ namespace ts {
node.parent.parent.kind === SyntaxKind.TypeLiteral) {
emitTypeOfVariableDeclarationFromTypeLiteral(node);
}
else if (!(node.parent.flags & NodeFlags.Private)) {
else if (!hasModifier(node.parent, ModifierFlags.Private)) {
writeTypeOfDeclaration(node, node.type, getParameterDeclarationTypeVisibilityError);
}
@@ -1568,7 +1575,7 @@ namespace ts {
case SyntaxKind.MethodDeclaration:
case SyntaxKind.MethodSignature:
if (node.parent.flags & NodeFlags.Static) {
if (hasModifier(node.parent, ModifierFlags.Static)) {
return symbolAccessibilityResult.errorModuleName ?
symbolAccessibilityResult.accessibility === SymbolAccessibility.CannotBeNamed ?
Diagnostics.Parameter_0_of_public_static_method_from_exported_class_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named :
@@ -1713,7 +1720,7 @@ namespace ts {
* @param referencedFile
* @param addBundledFileReference Determines if global file reference corresponding to bundled file should be emitted or not
*/
function writeReferencePath(referencedFile: SourceFile, addBundledFileReference: boolean): boolean {
function writeReferencePath(referencedFile: SourceFile, addBundledFileReference: boolean, emitOnlyDtsFiles: boolean): boolean {
let declFileName: string;
let addedBundledEmitReference = false;
if (isDeclarationFile(referencedFile)) {
@@ -1722,7 +1729,7 @@ namespace ts {
}
else {
// Get the declaration file path
forEachExpectedEmitFile(host, getDeclFileName, referencedFile);
forEachExpectedEmitFile(host, getDeclFileName, referencedFile, emitOnlyDtsFiles);
}
if (declFileName) {
@@ -1751,8 +1758,8 @@ namespace ts {
}
/* @internal */
export function writeDeclarationFile(declarationFilePath: string, sourceFiles: SourceFile[], isBundledEmit: boolean, host: EmitHost, resolver: EmitResolver, emitterDiagnostics: DiagnosticCollection) {
const emitDeclarationResult = emitDeclarations(host, resolver, emitterDiagnostics, declarationFilePath, sourceFiles, isBundledEmit);
export function writeDeclarationFile(declarationFilePath: string, sourceFiles: SourceFile[], isBundledEmit: boolean, host: EmitHost, resolver: EmitResolver, emitterDiagnostics: DiagnosticCollection, emitOnlyDtsFiles: boolean) {
const emitDeclarationResult = emitDeclarations(host, resolver, emitterDiagnostics, declarationFilePath, sourceFiles, isBundledEmit, emitOnlyDtsFiles);
const emitSkipped = emitDeclarationResult.reportedDeclarationError || host.isEmitBlocked(declarationFilePath) || host.getCompilerOptions().noEmit;
if (!emitSkipped) {
const declarationOutput = emitDeclarationResult.referencesOutput
+61 -19
View File
@@ -819,6 +819,10 @@
"category": "Error",
"code": 1253
},
"A 'const' initializer in an ambient context must be a string or numeric literal.": {
"category": "Error",
"code": 1254
},
"'with' statements are not allowed in an async function block.": {
"category": "Error",
"code": 1300
@@ -827,10 +831,6 @@
"category": "Error",
"code": 1308
},
"Async functions are only available when targeting ECMAScript 2015 or higher.": {
"category": "Error",
"code": 1311
},
"'=' can only be used in an object literal property inside a destructuring assignment.": {
"category": "Error",
"code": 1312
@@ -1047,7 +1047,7 @@
"category": "Error",
"code": 2348
},
"Cannot invoke an expression whose type lacks a call signature.": {
"Cannot invoke an expression whose type lacks a call signature. Type '{0}' has no compatible call signatures.": {
"category": "Error",
"code": 2349
},
@@ -1067,10 +1067,6 @@
"category": "Error",
"code": 2353
},
"No best common type exists among return expressions.": {
"category": "Error",
"code": 2354
},
"A function whose declared type is neither 'void' nor 'any' must return a value.": {
"category": "Error",
"code": 2355
@@ -1283,7 +1279,7 @@
"category": "Error",
"code": 2409
},
"All symbols within a 'with' block will be resolved to 'any'.": {
"The 'with' statement is not supported. All symbols in a 'with' block will have type 'any'.": {
"category": "Error",
"code": 2410
},
@@ -1635,10 +1631,6 @@
"category": "Error",
"code": 2503
},
"No best common type exists among yield expressions.": {
"category": "Error",
"code": 2504
},
"A generator cannot have a 'void' type annotation.": {
"category": "Error",
"code": 2505
@@ -1703,7 +1695,7 @@
"category": "Error",
"code": 2521
},
"The 'arguments' object cannot be referenced in an async arrow function. Consider using a standard async function expression.": {
"The 'arguments' object cannot be referenced in an async function or method in ES3 and ES5. Consider using a standard function or method.": {
"category": "Error",
"code": 2522
},
@@ -1951,6 +1943,31 @@
"category": "Error",
"code": 2690
},
"An import path cannot end with a '{0}' extension. Consider importing '{1}' instead.": {
"category": "Error",
"code": 2691
},
"'{0}' is a primitive, but '{1}' is a wrapper object. Prefer using '{0}' when possible.": {
"category": "Error",
"code": 2692
},
"'{0}' only refers to a type, but is being used as a value here.": {
"category": "Error",
"code": 2693
},
"Namespace '{0}' has no exported member '{1}'.": {
"category": "Error",
"code": 2694
},
"Left side of comma operator is unused and has no side effects.": {
"category": "Error",
"code": 2695
},
"The 'Object' type is assignable to very few other types. Did you mean to use the 'any' type instead?": {
"category": "Error",
"code": 2696
},
"Import declaration '{0}' is using private name '{1}'.": {
"category": "Error",
"code": 4000
@@ -2468,6 +2485,10 @@
"category": "Message",
"code": 6038
},
"STRATEGY": {
"category": "Message",
"code": 6039
},
"Compilation complete. Watching for file changes.": {
"category": "Message",
"code": 6042
@@ -2832,6 +2853,14 @@
"category": "Error",
"code": 6138
},
"Import emit helpers from 'tslib'.": {
"category": "Message",
"code": 6139
},
"Auto discovery for typings is enabled in project '{0}'. Running extra resolution pass for module '{1}' using cache location '{2}'.": {
"category": "Error",
"code": 6140
},
"Variable '{0}' implicitly has an '{1}' type.": {
"category": "Error",
"code": 7005
@@ -2864,10 +2893,6 @@
"category": "Error",
"code": 7015
},
"Property '{0}' implicitly has type 'any', because its 'set' accessor lacks a type annotation.": {
"category": "Error",
"code": 7016
},
"Index signature of object type implicitly has an 'any' type.": {
"category": "Error",
"code": 7017
@@ -2924,6 +2949,14 @@
"category": "Error",
"code": 7031
},
"Property '{0}' implicitly has type 'any', because its set accessor lacks a parameter type annotation.": {
"category": "Error",
"code": 7032
},
"Property '{0}' implicitly has type 'any', because its get accessor lacks a return type annotation.": {
"category": "Error",
"code": 7033
},
"You cannot rename this element.": {
"category": "Error",
"code": 8000
@@ -3035,5 +3068,14 @@
"Unknown typing option '{0}'.": {
"category": "Error",
"code": 17010
},
"Circularity detected while resolving configuration: {0}": {
"category": "Error",
"code": 18000
},
"The path in an 'extends' options must be relative or rooted.": {
"category": "Error",
"code": 18001
}
}
+2803 -8273
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+744
View File
@@ -0,0 +1,744 @@
/// <reference path="core.ts" />
/// <reference path="diagnosticInformationMap.generated.ts" />
namespace ts {
/* @internal */
export function trace(host: ModuleResolutionHost, message: DiagnosticMessage, ...args: any[]): void;
export function trace(host: ModuleResolutionHost, message: DiagnosticMessage): void {
host.trace(formatMessage.apply(undefined, arguments));
}
/* @internal */
export function isTraceEnabled(compilerOptions: CompilerOptions, host: ModuleResolutionHost): boolean {
return compilerOptions.traceResolution && host.trace !== undefined;
}
/* @internal */
export function createResolvedModule(resolvedFileName: string, isExternalLibraryImport: boolean, failedLookupLocations: string[]): ResolvedModuleWithFailedLookupLocations {
return { resolvedModule: resolvedFileName ? { resolvedFileName, isExternalLibraryImport } : undefined, failedLookupLocations };
}
function moduleHasNonRelativeName(moduleName: string): boolean {
return !(isRootedDiskPath(moduleName) || isExternalModuleNameRelative(moduleName));
}
/* @internal */
export interface ModuleResolutionState {
host: ModuleResolutionHost;
compilerOptions: CompilerOptions;
traceEnabled: boolean;
// skip .tsx files if jsx is not enabled
skipTsx: boolean;
}
function tryReadTypesSection(packageJsonPath: string, baseDirectory: string, state: ModuleResolutionState): string {
const jsonContent = readJson(packageJsonPath, state.host);
function tryReadFromField(fieldName: string) {
if (hasProperty(jsonContent, fieldName)) {
const typesFile = (<any>jsonContent)[fieldName];
if (typeof typesFile === "string") {
const typesFilePath = normalizePath(combinePaths(baseDirectory, typesFile));
if (state.traceEnabled) {
trace(state.host, Diagnostics.package_json_has_0_field_1_that_references_2, fieldName, typesFile, typesFilePath);
}
return typesFilePath;
}
else {
if (state.traceEnabled) {
trace(state.host, Diagnostics.Expected_type_of_0_field_in_package_json_to_be_string_got_1, fieldName, typeof typesFile);
}
}
}
}
const typesFilePath = tryReadFromField("typings") || tryReadFromField("types");
if (typesFilePath) {
return typesFilePath;
}
// Use the main module for inferring types if no types package specified and the allowJs is set
if (state.compilerOptions.allowJs && jsonContent.main && typeof jsonContent.main === "string") {
if (state.traceEnabled) {
trace(state.host, Diagnostics.No_types_specified_in_package_json_but_allowJs_is_set_so_returning_main_value_of_0, jsonContent.main);
}
const mainFilePath = normalizePath(combinePaths(baseDirectory, jsonContent.main));
return mainFilePath;
}
return undefined;
}
function readJson(path: string, host: ModuleResolutionHost): { typings?: string, types?: string, main?: string } {
try {
const jsonText = host.readFile(path);
return jsonText ? JSON.parse(jsonText) : {};
}
catch (e) {
// gracefully handle if readFile fails or returns not JSON
return {};
}
}
const typeReferenceExtensions = [".d.ts"];
export function getEffectiveTypeRoots(options: CompilerOptions, host: { directoryExists?: (directoryName: string) => boolean, getCurrentDirectory?: () => string }): string[] | undefined {
if (options.typeRoots) {
return options.typeRoots;
}
let currentDirectory: string;
if (options.configFilePath) {
currentDirectory = getDirectoryPath(options.configFilePath);
}
else if (host.getCurrentDirectory) {
currentDirectory = host.getCurrentDirectory();
}
return currentDirectory && getDefaultTypeRoots(currentDirectory, host);
}
/**
* Returns the path to every node_modules/@types directory from some ancestor directory.
* Returns undefined if there are none.
*/
function getDefaultTypeRoots(currentDirectory: string, host: { directoryExists?: (directoryName: string) => boolean }): string[] | undefined {
if (!host.directoryExists) {
return [combinePaths(currentDirectory, nodeModulesAtTypes)];
// And if it doesn't exist, tough.
}
let typeRoots: string[];
while (true) {
const atTypes = combinePaths(currentDirectory, nodeModulesAtTypes);
if (host.directoryExists(atTypes)) {
(typeRoots || (typeRoots = [])).push(atTypes);
}
const parent = getDirectoryPath(currentDirectory);
if (parent === currentDirectory) {
break;
}
currentDirectory = parent;
}
return typeRoots;
}
const nodeModulesAtTypes = combinePaths("node_modules", "@types");
/**
* @param {string | undefined} containingFile - file that contains type reference directive, can be undefined if containing file is unknown.
* This is possible in case if resolution is performed for directives specified via 'types' parameter. In this case initial path for secondary lookups
* is assumed to be the same as root directory of the project.
*/
export function resolveTypeReferenceDirective(typeReferenceDirectiveName: string, containingFile: string, options: CompilerOptions, host: ModuleResolutionHost): ResolvedTypeReferenceDirectiveWithFailedLookupLocations {
const traceEnabled = isTraceEnabled(options, host);
const moduleResolutionState: ModuleResolutionState = {
compilerOptions: options,
host: host,
skipTsx: true,
traceEnabled
};
const typeRoots = getEffectiveTypeRoots(options, host);
if (traceEnabled) {
if (containingFile === undefined) {
if (typeRoots === undefined) {
trace(host, Diagnostics.Resolving_type_reference_directive_0_containing_file_not_set_root_directory_not_set, typeReferenceDirectiveName);
}
else {
trace(host, Diagnostics.Resolving_type_reference_directive_0_containing_file_not_set_root_directory_1, typeReferenceDirectiveName, typeRoots);
}
}
else {
if (typeRoots === undefined) {
trace(host, Diagnostics.Resolving_type_reference_directive_0_containing_file_1_root_directory_not_set, typeReferenceDirectiveName, containingFile);
}
else {
trace(host, Diagnostics.Resolving_type_reference_directive_0_containing_file_1_root_directory_2, typeReferenceDirectiveName, containingFile, typeRoots);
}
}
}
const failedLookupLocations: string[] = [];
// Check primary library paths
if (typeRoots && typeRoots.length) {
if (traceEnabled) {
trace(host, Diagnostics.Resolving_with_primary_search_path_0, typeRoots.join(", "));
}
const primarySearchPaths = typeRoots;
for (const typeRoot of primarySearchPaths) {
const candidate = combinePaths(typeRoot, typeReferenceDirectiveName);
const candidateDirectory = getDirectoryPath(candidate);
const resolvedFile = loadNodeModuleFromDirectory(typeReferenceExtensions, candidate, failedLookupLocations,
!directoryProbablyExists(candidateDirectory, host), moduleResolutionState);
if (resolvedFile) {
if (traceEnabled) {
trace(host, Diagnostics.Type_reference_directive_0_was_successfully_resolved_to_1_primary_Colon_2, typeReferenceDirectiveName, resolvedFile, true);
}
return {
resolvedTypeReferenceDirective: { primary: true, resolvedFileName: resolvedFile },
failedLookupLocations
};
}
}
}
else {
if (traceEnabled) {
trace(host, Diagnostics.Root_directory_cannot_be_determined_skipping_primary_search_paths);
}
}
let resolvedFile: string;
let initialLocationForSecondaryLookup: string;
if (containingFile) {
initialLocationForSecondaryLookup = getDirectoryPath(containingFile);
}
if (initialLocationForSecondaryLookup !== undefined) {
// check secondary locations
if (traceEnabled) {
trace(host, Diagnostics.Looking_up_in_node_modules_folder_initial_location_0, initialLocationForSecondaryLookup);
}
resolvedFile = loadModuleFromNodeModules(typeReferenceDirectiveName, initialLocationForSecondaryLookup, failedLookupLocations, moduleResolutionState, /*checkOneLevel*/ false);
if (traceEnabled) {
if (resolvedFile) {
trace(host, Diagnostics.Type_reference_directive_0_was_successfully_resolved_to_1_primary_Colon_2, typeReferenceDirectiveName, resolvedFile, false);
}
else {
trace(host, Diagnostics.Type_reference_directive_0_was_not_resolved, typeReferenceDirectiveName);
}
}
}
else {
if (traceEnabled) {
trace(host, Diagnostics.Containing_file_is_not_specified_and_root_directory_cannot_be_determined_skipping_lookup_in_node_modules_folder);
}
}
return {
resolvedTypeReferenceDirective: resolvedFile
? { primary: false, resolvedFileName: resolvedFile }
: undefined,
failedLookupLocations
};
}
/**
* Given a set of options, returns the set of type directive names
* that should be included for this program automatically.
* This list could either come from the config file,
* or from enumerating the types root + initial secondary types lookup location.
* More type directives might appear in the program later as a result of loading actual source files;
* this list is only the set of defaults that are implicitly included.
*/
export function getAutomaticTypeDirectiveNames(options: CompilerOptions, host: ModuleResolutionHost): string[] {
// Use explicit type list from tsconfig.json
if (options.types) {
return options.types;
}
// Walk the primary type lookup locations
const result: string[] = [];
if (host.directoryExists && host.getDirectories) {
const typeRoots = getEffectiveTypeRoots(options, host);
if (typeRoots) {
for (const root of typeRoots) {
if (host.directoryExists(root)) {
for (const typeDirectivePath of host.getDirectories(root)) {
const normalized = normalizePath(typeDirectivePath);
const packageJsonPath = pathToPackageJson(combinePaths(root, normalized));
// tslint:disable-next-line:no-null-keyword
const isNotNeededPackage = host.fileExists(packageJsonPath) && readJson(packageJsonPath, host).typings === null;
if (!isNotNeededPackage) {
// Return just the type directive names
result.push(getBaseFileName(normalized));
}
}
}
}
}
}
return result;
}
export function resolveModuleName(moduleName: string, containingFile: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost): ResolvedModuleWithFailedLookupLocations {
const traceEnabled = isTraceEnabled(compilerOptions, host);
if (traceEnabled) {
trace(host, Diagnostics.Resolving_module_0_from_1, moduleName, containingFile);
}
let moduleResolution = compilerOptions.moduleResolution;
if (moduleResolution === undefined) {
moduleResolution = getEmitModuleKind(compilerOptions) === ModuleKind.CommonJS ? ModuleResolutionKind.NodeJs : ModuleResolutionKind.Classic;
if (traceEnabled) {
trace(host, Diagnostics.Module_resolution_kind_is_not_specified_using_0, ModuleResolutionKind[moduleResolution]);
}
}
else {
if (traceEnabled) {
trace(host, Diagnostics.Explicitly_specified_module_resolution_kind_Colon_0, ModuleResolutionKind[moduleResolution]);
}
}
let result: ResolvedModuleWithFailedLookupLocations;
switch (moduleResolution) {
case ModuleResolutionKind.NodeJs:
result = nodeModuleNameResolver(moduleName, containingFile, compilerOptions, host);
break;
case ModuleResolutionKind.Classic:
result = classicNameResolver(moduleName, containingFile, compilerOptions, host);
break;
}
if (traceEnabled) {
if (result.resolvedModule) {
trace(host, Diagnostics.Module_name_0_was_successfully_resolved_to_1, moduleName, result.resolvedModule.resolvedFileName);
}
else {
trace(host, Diagnostics.Module_name_0_was_not_resolved, moduleName);
}
}
return result;
}
/*
* Every module resolution kind can has its specific understanding how to load module from a specific path on disk
* I.e. for path '/a/b/c':
* - Node loader will first to try to check if '/a/b/c' points to a file with some supported extension and if this fails
* it will try to load module from directory: directory '/a/b/c' should exist and it should have either 'package.json' with
* 'typings' entry or file 'index' with some supported extension
* - Classic loader will only try to interpret '/a/b/c' as file.
*/
type ResolutionKindSpecificLoader = (candidate: string, extensions: string[], failedLookupLocations: string[], onlyRecordFailures: boolean, state: ModuleResolutionState) => string;
/**
* Any module resolution kind can be augmented with optional settings: 'baseUrl', 'paths' and 'rootDirs' - they are used to
* mitigate differences between design time structure of the project and its runtime counterpart so the same import name
* can be resolved successfully by TypeScript compiler and runtime module loader.
* If these settings are set then loading procedure will try to use them to resolve module name and it can of failure it will
* fallback to standard resolution routine.
*
* - baseUrl - this setting controls how non-relative module names are resolved. If this setting is specified then non-relative
* names will be resolved relative to baseUrl: i.e. if baseUrl is '/a/b' then candidate location to resolve module name 'c/d' will
* be '/a/b/c/d'
* - paths - this setting can only be used when baseUrl is specified. allows to tune how non-relative module names
* will be resolved based on the content of the module name.
* Structure of 'paths' compiler options
* 'paths': {
* pattern-1: [...substitutions],
* pattern-2: [...substitutions],
* ...
* pattern-n: [...substitutions]
* }
* Pattern here is a string that can contain zero or one '*' character. During module resolution module name will be matched against
* all patterns in the list. Matching for patterns that don't contain '*' means that module name must be equal to pattern respecting the case.
* If pattern contains '*' then to match pattern "<prefix>*<suffix>" module name must start with the <prefix> and end with <suffix>.
* <MatchedStar> denotes part of the module name between <prefix> and <suffix>.
* If module name can be matches with multiple patterns then pattern with the longest prefix will be picked.
* After selecting pattern we'll use list of substitutions to get candidate locations of the module and the try to load module
* from the candidate location.
* Substitution is a string that can contain zero or one '*'. To get candidate location from substitution we'll pick every
* substitution in the list and replace '*' with <MatchedStar> string. If candidate location is not rooted it
* will be converted to absolute using baseUrl.
* For example:
* baseUrl: /a/b/c
* "paths": {
* // match all module names
* "*": [
* "*", // use matched name as is,
* // <matched name> will be looked as /a/b/c/<matched name>
*
* "folder1/*" // substitution will convert matched name to 'folder1/<matched name>',
* // since it is not rooted then final candidate location will be /a/b/c/folder1/<matched name>
* ],
* // match module names that start with 'components/'
* "components/*": [ "/root/components/*" ] // substitution will convert /components/folder1/<matched name> to '/root/components/folder1/<matched name>',
* // it is rooted so it will be final candidate location
* }
*
* 'rootDirs' allows the project to be spreaded across multiple locations and resolve modules with relative names as if
* they were in the same location. For example lets say there are two files
* '/local/src/content/file1.ts'
* '/shared/components/contracts/src/content/protocols/file2.ts'
* After bundling content of '/shared/components/contracts/src' will be merged with '/local/src' so
* if file1 has the following import 'import {x} from "./protocols/file2"' it will be resolved successfully in runtime.
* 'rootDirs' provides the way to tell compiler that in order to get the whole project it should behave as if content of all
* root dirs were merged together.
* I.e. for the example above 'rootDirs' will have two entries: [ '/local/src', '/shared/components/contracts/src' ].
* Compiler will first convert './protocols/file2' into absolute path relative to the location of containing file:
* '/local/src/content/protocols/file2' and try to load it - failure.
* Then it will search 'rootDirs' looking for a longest matching prefix of this absolute path and if such prefix is found - absolute path will
* be converted to a path relative to found rootDir entry './content/protocols/file2' (*). As a last step compiler will check all remaining
* entries in 'rootDirs', use them to build absolute path out of (*) and try to resolve module from this location.
*/
function tryLoadModuleUsingOptionalResolutionSettings(moduleName: string, containingDirectory: string, loader: ResolutionKindSpecificLoader,
failedLookupLocations: string[], supportedExtensions: string[], state: ModuleResolutionState): string {
if (moduleHasNonRelativeName(moduleName)) {
return tryLoadModuleUsingBaseUrl(moduleName, loader, failedLookupLocations, supportedExtensions, state);
}
else {
return tryLoadModuleUsingRootDirs(moduleName, containingDirectory, loader, failedLookupLocations, supportedExtensions, state);
}
}
function tryLoadModuleUsingRootDirs(moduleName: string, containingDirectory: string, loader: ResolutionKindSpecificLoader,
failedLookupLocations: string[], supportedExtensions: string[], state: ModuleResolutionState): string {
if (!state.compilerOptions.rootDirs) {
return undefined;
}
if (state.traceEnabled) {
trace(state.host, Diagnostics.rootDirs_option_is_set_using_it_to_resolve_relative_module_name_0, moduleName);
}
const candidate = normalizePath(combinePaths(containingDirectory, moduleName));
let matchedRootDir: string;
let matchedNormalizedPrefix: string;
for (const rootDir of state.compilerOptions.rootDirs) {
// rootDirs are expected to be absolute
// in case of tsconfig.json this will happen automatically - compiler will expand relative names
// using location of tsconfig.json as base location
let normalizedRoot = normalizePath(rootDir);
if (!endsWith(normalizedRoot, directorySeparator)) {
normalizedRoot += directorySeparator;
}
const isLongestMatchingPrefix =
startsWith(candidate, normalizedRoot) &&
(matchedNormalizedPrefix === undefined || matchedNormalizedPrefix.length < normalizedRoot.length);
if (state.traceEnabled) {
trace(state.host, Diagnostics.Checking_if_0_is_the_longest_matching_prefix_for_1_2, normalizedRoot, candidate, isLongestMatchingPrefix);
}
if (isLongestMatchingPrefix) {
matchedNormalizedPrefix = normalizedRoot;
matchedRootDir = rootDir;
}
}
if (matchedNormalizedPrefix) {
if (state.traceEnabled) {
trace(state.host, Diagnostics.Longest_matching_prefix_for_0_is_1, candidate, matchedNormalizedPrefix);
}
const suffix = candidate.substr(matchedNormalizedPrefix.length);
// first - try to load from a initial location
if (state.traceEnabled) {
trace(state.host, Diagnostics.Loading_0_from_the_root_dir_1_candidate_location_2, suffix, matchedNormalizedPrefix, candidate);
}
const resolvedFileName = loader(candidate, supportedExtensions, failedLookupLocations, !directoryProbablyExists(containingDirectory, state.host), state);
if (resolvedFileName) {
return resolvedFileName;
}
if (state.traceEnabled) {
trace(state.host, Diagnostics.Trying_other_entries_in_rootDirs);
}
// then try to resolve using remaining entries in rootDirs
for (const rootDir of state.compilerOptions.rootDirs) {
if (rootDir === matchedRootDir) {
// skip the initially matched entry
continue;
}
const candidate = combinePaths(normalizePath(rootDir), suffix);
if (state.traceEnabled) {
trace(state.host, Diagnostics.Loading_0_from_the_root_dir_1_candidate_location_2, suffix, rootDir, candidate);
}
const baseDirectory = getDirectoryPath(candidate);
const resolvedFileName = loader(candidate, supportedExtensions, failedLookupLocations, !directoryProbablyExists(baseDirectory, state.host), state);
if (resolvedFileName) {
return resolvedFileName;
}
}
if (state.traceEnabled) {
trace(state.host, Diagnostics.Module_resolution_using_rootDirs_has_failed);
}
}
return undefined;
}
function tryLoadModuleUsingBaseUrl(moduleName: string, loader: ResolutionKindSpecificLoader, failedLookupLocations: string[],
supportedExtensions: string[], state: ModuleResolutionState): string {
if (!state.compilerOptions.baseUrl) {
return undefined;
}
if (state.traceEnabled) {
trace(state.host, Diagnostics.baseUrl_option_is_set_to_0_using_this_value_to_resolve_non_relative_module_name_1, state.compilerOptions.baseUrl, moduleName);
}
// string is for exact match
let matchedPattern: Pattern | string | undefined = undefined;
if (state.compilerOptions.paths) {
if (state.traceEnabled) {
trace(state.host, Diagnostics.paths_option_is_specified_looking_for_a_pattern_to_match_module_name_0, moduleName);
}
matchedPattern = matchPatternOrExact(getOwnKeys(state.compilerOptions.paths), moduleName);
}
if (matchedPattern) {
const matchedStar = typeof matchedPattern === "string" ? undefined : matchedText(matchedPattern, moduleName);
const matchedPatternText = typeof matchedPattern === "string" ? matchedPattern : patternText(matchedPattern);
if (state.traceEnabled) {
trace(state.host, Diagnostics.Module_name_0_matched_pattern_1, moduleName, matchedPatternText);
}
for (const subst of state.compilerOptions.paths[matchedPatternText]) {
const path = matchedStar ? subst.replace("*", matchedStar) : subst;
const candidate = normalizePath(combinePaths(state.compilerOptions.baseUrl, path));
if (state.traceEnabled) {
trace(state.host, Diagnostics.Trying_substitution_0_candidate_module_location_Colon_1, subst, path);
}
const resolvedFileName = loader(candidate, supportedExtensions, failedLookupLocations, !directoryProbablyExists(getDirectoryPath(candidate), state.host), state);
if (resolvedFileName) {
return resolvedFileName;
}
}
return undefined;
}
else {
const candidate = normalizePath(combinePaths(state.compilerOptions.baseUrl, moduleName));
if (state.traceEnabled) {
trace(state.host, Diagnostics.Resolving_module_name_0_relative_to_base_url_1_2, moduleName, state.compilerOptions.baseUrl, candidate);
}
return loader(candidate, supportedExtensions, failedLookupLocations, !directoryProbablyExists(getDirectoryPath(candidate), state.host), state);
}
}
export function nodeModuleNameResolver(moduleName: string, containingFile: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost): ResolvedModuleWithFailedLookupLocations {
const containingDirectory = getDirectoryPath(containingFile);
const supportedExtensions = getSupportedExtensions(compilerOptions);
const traceEnabled = isTraceEnabled(compilerOptions, host);
const failedLookupLocations: string[] = [];
const state = { compilerOptions, host, traceEnabled, skipTsx: false };
let resolvedFileName = tryLoadModuleUsingOptionalResolutionSettings(moduleName, containingDirectory, nodeLoadModuleByRelativeName,
failedLookupLocations, supportedExtensions, state);
let isExternalLibraryImport = false;
if (!resolvedFileName) {
if (moduleHasNonRelativeName(moduleName)) {
if (traceEnabled) {
trace(host, Diagnostics.Loading_module_0_from_node_modules_folder, moduleName);
}
resolvedFileName = loadModuleFromNodeModules(moduleName, containingDirectory, failedLookupLocations, state, /*checkOneLevel*/ false);
isExternalLibraryImport = resolvedFileName !== undefined;
}
else {
const candidate = normalizePath(combinePaths(containingDirectory, moduleName));
resolvedFileName = nodeLoadModuleByRelativeName(candidate, supportedExtensions, failedLookupLocations, /*onlyRecordFailures*/ false, state);
}
}
if (resolvedFileName && host.realpath) {
const originalFileName = resolvedFileName;
resolvedFileName = normalizePath(host.realpath(resolvedFileName));
if (traceEnabled) {
trace(host, Diagnostics.Resolving_real_path_for_0_result_1, originalFileName, resolvedFileName);
}
}
return createResolvedModule(resolvedFileName, isExternalLibraryImport, failedLookupLocations);
}
function nodeLoadModuleByRelativeName(candidate: string, supportedExtensions: string[], failedLookupLocations: string[],
onlyRecordFailures: boolean, state: ModuleResolutionState): string {
if (state.traceEnabled) {
trace(state.host, Diagnostics.Loading_module_as_file_Slash_folder_candidate_module_location_0, candidate);
}
const resolvedFileName = !pathEndsWithDirectorySeparator(candidate) && loadModuleFromFile(candidate, supportedExtensions, failedLookupLocations, onlyRecordFailures, state);
return resolvedFileName || loadNodeModuleFromDirectory(supportedExtensions, candidate, failedLookupLocations, onlyRecordFailures, state);
}
/* @internal */
export function directoryProbablyExists(directoryName: string, host: { directoryExists?: (directoryName: string) => boolean }): boolean {
// if host does not support 'directoryExists' assume that directory will exist
return !host.directoryExists || host.directoryExists(directoryName);
}
/**
* @param {boolean} onlyRecordFailures - if true then function won't try to actually load files but instead record all attempts as failures. This flag is necessary
* in cases when we know upfront that all load attempts will fail (because containing folder does not exists) however we still need to record all failed lookup locations.
*/
function loadModuleFromFile(candidate: string, extensions: string[], failedLookupLocation: string[], onlyRecordFailures: boolean, state: ModuleResolutionState): string | undefined {
// First, try adding an extension. An import of "foo" could be matched by a file "foo.ts", or "foo.js" by "foo.js.ts"
const resolvedByAddingExtension = tryAddingExtensions(candidate, extensions, failedLookupLocation, onlyRecordFailures, state);
if (resolvedByAddingExtension) {
return resolvedByAddingExtension;
}
// If that didn't work, try stripping a ".js" or ".jsx" extension and replacing it with a TypeScript one;
// e.g. "./foo.js" can be matched by "./foo.ts" or "./foo.d.ts"
if (hasJavaScriptFileExtension(candidate)) {
const extensionless = removeFileExtension(candidate);
if (state.traceEnabled) {
const extension = candidate.substring(extensionless.length);
trace(state.host, Diagnostics.File_name_0_has_a_1_extension_stripping_it, candidate, extension);
}
return tryAddingExtensions(extensionless, extensions, failedLookupLocation, onlyRecordFailures, state);
}
}
/** Try to return an existing file that adds one of the `extensions` to `candidate`. */
function tryAddingExtensions(candidate: string, extensions: string[], failedLookupLocation: string[], onlyRecordFailures: boolean, state: ModuleResolutionState): string | undefined {
if (!onlyRecordFailures) {
// check if containing folder exists - if it doesn't then just record failures for all supported extensions without disk probing
const directory = getDirectoryPath(candidate);
if (directory) {
onlyRecordFailures = !directoryProbablyExists(directory, state.host);
}
}
return forEach(extensions, ext =>
!(state.skipTsx && isJsxOrTsxExtension(ext)) && tryFile(candidate + ext, failedLookupLocation, onlyRecordFailures, state));
}
/** Return the file if it exists. */
function tryFile(fileName: string, failedLookupLocation: string[], onlyRecordFailures: boolean, state: ModuleResolutionState): string | undefined {
if (!onlyRecordFailures && state.host.fileExists(fileName)) {
if (state.traceEnabled) {
trace(state.host, Diagnostics.File_0_exist_use_it_as_a_name_resolution_result, fileName);
}
return fileName;
}
else {
if (state.traceEnabled) {
trace(state.host, Diagnostics.File_0_does_not_exist, fileName);
}
failedLookupLocation.push(fileName);
return undefined;
}
}
function loadNodeModuleFromDirectory(extensions: string[], candidate: string, failedLookupLocation: string[], onlyRecordFailures: boolean, state: ModuleResolutionState): string {
const packageJsonPath = pathToPackageJson(candidate);
const directoryExists = !onlyRecordFailures && directoryProbablyExists(candidate, state.host);
if (directoryExists && state.host.fileExists(packageJsonPath)) {
if (state.traceEnabled) {
trace(state.host, Diagnostics.Found_package_json_at_0, packageJsonPath);
}
const typesFile = tryReadTypesSection(packageJsonPath, candidate, state);
if (typesFile) {
const onlyRecordFailures = !directoryProbablyExists(getDirectoryPath(typesFile), state.host);
// A package.json "typings" may specify an exact filename, or may choose to omit an extension.
const result = tryFile(typesFile, failedLookupLocation, onlyRecordFailures, state) ||
tryAddingExtensions(typesFile, extensions, failedLookupLocation, onlyRecordFailures, state);
if (result) {
return result;
}
}
else {
if (state.traceEnabled) {
trace(state.host, Diagnostics.package_json_does_not_have_types_field);
}
}
}
else {
if (state.traceEnabled) {
trace(state.host, Diagnostics.File_0_does_not_exist, packageJsonPath);
}
// record package json as one of failed lookup locations - in the future if this file will appear it will invalidate resolution results
failedLookupLocation.push(packageJsonPath);
}
return loadModuleFromFile(combinePaths(candidate, "index"), extensions, failedLookupLocation, !directoryExists, state);
}
function pathToPackageJson(directory: string): string {
return combinePaths(directory, "package.json");
}
function loadModuleFromNodeModulesFolder(moduleName: string, directory: string, failedLookupLocations: string[], state: ModuleResolutionState): string {
const nodeModulesFolder = combinePaths(directory, "node_modules");
const nodeModulesFolderExists = directoryProbablyExists(nodeModulesFolder, state.host);
const candidate = normalizePath(combinePaths(nodeModulesFolder, moduleName));
const supportedExtensions = getSupportedExtensions(state.compilerOptions);
let result = loadModuleFromFile(candidate, supportedExtensions, failedLookupLocations, !nodeModulesFolderExists, state);
if (result) {
return result;
}
result = loadNodeModuleFromDirectory(supportedExtensions, candidate, failedLookupLocations, !nodeModulesFolderExists, state);
if (result) {
return result;
}
}
/* @internal */
export function loadModuleFromNodeModules(moduleName: string, directory: string, failedLookupLocations: string[], state: ModuleResolutionState, checkOneLevel: boolean): string {
directory = normalizeSlashes(directory);
while (true) {
const baseName = getBaseFileName(directory);
if (baseName !== "node_modules") {
// Try to load source from the package
const packageResult = loadModuleFromNodeModulesFolder(moduleName, directory, failedLookupLocations, state);
if (packageResult && hasTypeScriptFileExtension(packageResult)) {
// Always prefer a TypeScript (.ts, .tsx, .d.ts) file shipped with the package
return packageResult;
}
else {
// Else prefer a types package over non-TypeScript results (e.g. JavaScript files)
const typesResult = loadModuleFromNodeModulesFolder(combinePaths("@types", moduleName), directory, failedLookupLocations, state);
if (typesResult || packageResult) {
return typesResult || packageResult;
}
}
}
const parentPath = getDirectoryPath(directory);
if (parentPath === directory || checkOneLevel) {
break;
}
directory = parentPath;
}
return undefined;
}
export function classicNameResolver(moduleName: string, containingFile: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost): ResolvedModuleWithFailedLookupLocations {
const traceEnabled = isTraceEnabled(compilerOptions, host);
const state = { compilerOptions, host, traceEnabled, skipTsx: !compilerOptions.jsx };
const failedLookupLocations: string[] = [];
const supportedExtensions = getSupportedExtensions(compilerOptions);
let containingDirectory = getDirectoryPath(containingFile);
const resolvedFileName = tryLoadModuleUsingOptionalResolutionSettings(moduleName, containingDirectory, loadModuleFromFile, failedLookupLocations, supportedExtensions, state);
if (resolvedFileName) {
return createResolvedModule(resolvedFileName, /*isExternalLibraryImport*/false, failedLookupLocations);
}
let referencedSourceFile: string;
if (moduleHasNonRelativeName(moduleName)) {
while (true) {
const searchName = normalizePath(combinePaths(containingDirectory, moduleName));
referencedSourceFile = loadModuleFromFile(searchName, supportedExtensions, failedLookupLocations, /*onlyRecordFailures*/ false, state);
if (referencedSourceFile) {
break;
}
const parentPath = getDirectoryPath(containingDirectory);
if (parentPath === containingDirectory) {
break;
}
containingDirectory = parentPath;
}
}
else {
const candidate = normalizePath(combinePaths(containingDirectory, moduleName));
referencedSourceFile = loadModuleFromFile(candidate, supportedExtensions, failedLookupLocations, /*onlyRecordFailures*/ false, state);
}
return referencedSourceFile
? { resolvedModule: { resolvedFileName: referencedSourceFile }, failedLookupLocations }
: { resolvedModule: undefined, failedLookupLocations };
}
}
+428 -276
View File
File diff suppressed because it is too large Load Diff
+30 -811
View File
@@ -4,15 +4,14 @@
namespace ts {
/** The version of the TypeScript compiler release */
export const version = "2.1.0";
const emptyArray: any[] = [];
const defaultTypeRoots = ["node_modules/@types"];
export function findConfigFile(searchPath: string, fileExists: (fileName: string) => boolean): string {
export function findConfigFile(searchPath: string, fileExists: (fileName: string) => boolean, configName = "tsconfig.json"): string {
while (true) {
const fileName = combinePaths(searchPath, "tsconfig.json");
const fileName = combinePaths(searchPath, configName);
if (fileExists(fileName)) {
return fileName;
}
@@ -76,766 +75,6 @@ namespace ts {
return getNormalizedPathFromPathComponents(commonPathComponents);
}
function trace(host: ModuleResolutionHost, message: DiagnosticMessage, ...args: any[]): void;
function trace(host: ModuleResolutionHost, message: DiagnosticMessage): void {
host.trace(formatMessage.apply(undefined, arguments));
}
function isTraceEnabled(compilerOptions: CompilerOptions, host: ModuleResolutionHost): boolean {
return compilerOptions.traceResolution && host.trace !== undefined;
}
/* @internal */
export function hasZeroOrOneAsteriskCharacter(str: string): boolean {
let seenAsterisk = false;
for (let i = 0; i < str.length; i++) {
if (str.charCodeAt(i) === CharacterCodes.asterisk) {
if (!seenAsterisk) {
seenAsterisk = true;
}
else {
// have already seen asterisk
return false;
}
}
}
return true;
}
function createResolvedModule(resolvedFileName: string, isExternalLibraryImport: boolean, failedLookupLocations: string[]): ResolvedModuleWithFailedLookupLocations {
return { resolvedModule: resolvedFileName ? { resolvedFileName, isExternalLibraryImport } : undefined, failedLookupLocations };
}
function moduleHasNonRelativeName(moduleName: string): boolean {
return !(isRootedDiskPath(moduleName) || isExternalModuleNameRelative(moduleName));
}
interface ModuleResolutionState {
host: ModuleResolutionHost;
compilerOptions: CompilerOptions;
traceEnabled: boolean;
// skip .tsx files if jsx is not enabled
skipTsx: boolean;
}
function tryReadTypesSection(packageJsonPath: string, baseDirectory: string, state: ModuleResolutionState): string {
const jsonContent = readJson(packageJsonPath, state.host);
function tryReadFromField(fieldName: string) {
if (hasProperty(jsonContent, fieldName)) {
const typesFile = (<any>jsonContent)[fieldName];
if (typeof typesFile === "string") {
const typesFilePath = normalizePath(combinePaths(baseDirectory, typesFile));
if (state.traceEnabled) {
trace(state.host, Diagnostics.package_json_has_0_field_1_that_references_2, fieldName, typesFile, typesFilePath);
}
return typesFilePath;
}
else {
if (state.traceEnabled) {
trace(state.host, Diagnostics.Expected_type_of_0_field_in_package_json_to_be_string_got_1, fieldName, typeof typesFile);
}
}
}
}
const typesFilePath = tryReadFromField("typings") || tryReadFromField("types");
if (typesFilePath) {
return typesFilePath;
}
// Use the main module for inferring types if no types package specified and the allowJs is set
if (state.compilerOptions.allowJs && jsonContent.main && typeof jsonContent.main === "string") {
if (state.traceEnabled) {
trace(state.host, Diagnostics.No_types_specified_in_package_json_but_allowJs_is_set_so_returning_main_value_of_0, jsonContent.main);
}
const mainFilePath = normalizePath(combinePaths(baseDirectory, jsonContent.main));
return mainFilePath;
}
return undefined;
}
function readJson(path: string, host: ModuleResolutionHost): { typings?: string, types?: string, main?: string } {
try {
const jsonText = host.readFile(path);
return jsonText ? JSON.parse(jsonText) : {};
}
catch (e) {
// gracefully handle if readFile fails or returns not JSON
return {};
}
}
const typeReferenceExtensions = [".d.ts"];
function getEffectiveTypeRoots(options: CompilerOptions, host: ModuleResolutionHost) {
if (options.typeRoots) {
return options.typeRoots;
}
let currentDirectory: string;
if (options.configFilePath) {
currentDirectory = getDirectoryPath(options.configFilePath);
}
else if (host.getCurrentDirectory) {
currentDirectory = host.getCurrentDirectory();
}
if (!currentDirectory) {
return undefined;
}
return map(defaultTypeRoots, d => combinePaths(currentDirectory, d));
}
/**
* @param {string | undefined} containingFile - file that contains type reference directive, can be undefined if containing file is unknown.
* This is possible in case if resolution is performed for directives specified via 'types' parameter. In this case initial path for secondary lookups
* is assumed to be the same as root directory of the project.
*/
export function resolveTypeReferenceDirective(typeReferenceDirectiveName: string, containingFile: string, options: CompilerOptions, host: ModuleResolutionHost): ResolvedTypeReferenceDirectiveWithFailedLookupLocations {
const traceEnabled = isTraceEnabled(options, host);
const moduleResolutionState: ModuleResolutionState = {
compilerOptions: options,
host: host,
skipTsx: true,
traceEnabled
};
const typeRoots = getEffectiveTypeRoots(options, host);
if (traceEnabled) {
if (containingFile === undefined) {
if (typeRoots === undefined) {
trace(host, Diagnostics.Resolving_type_reference_directive_0_containing_file_not_set_root_directory_not_set, typeReferenceDirectiveName);
}
else {
trace(host, Diagnostics.Resolving_type_reference_directive_0_containing_file_not_set_root_directory_1, typeReferenceDirectiveName, typeRoots);
}
}
else {
if (typeRoots === undefined) {
trace(host, Diagnostics.Resolving_type_reference_directive_0_containing_file_1_root_directory_not_set, typeReferenceDirectiveName, containingFile);
}
else {
trace(host, Diagnostics.Resolving_type_reference_directive_0_containing_file_1_root_directory_2, typeReferenceDirectiveName, containingFile, typeRoots);
}
}
}
const failedLookupLocations: string[] = [];
// Check primary library paths
if (typeRoots && typeRoots.length) {
if (traceEnabled) {
trace(host, Diagnostics.Resolving_with_primary_search_path_0, typeRoots.join(", "));
}
const primarySearchPaths = typeRoots;
for (const typeRoot of primarySearchPaths) {
const candidate = combinePaths(typeRoot, typeReferenceDirectiveName);
const candidateDirectory = getDirectoryPath(candidate);
const resolvedFile = loadNodeModuleFromDirectory(typeReferenceExtensions, candidate, failedLookupLocations,
!directoryProbablyExists(candidateDirectory, host), moduleResolutionState);
if (resolvedFile) {
if (traceEnabled) {
trace(host, Diagnostics.Type_reference_directive_0_was_successfully_resolved_to_1_primary_Colon_2, typeReferenceDirectiveName, resolvedFile, true);
}
return {
resolvedTypeReferenceDirective: { primary: true, resolvedFileName: resolvedFile },
failedLookupLocations
};
}
}
}
else {
if (traceEnabled) {
trace(host, Diagnostics.Root_directory_cannot_be_determined_skipping_primary_search_paths);
}
}
let resolvedFile: string;
let initialLocationForSecondaryLookup: string;
if (containingFile) {
initialLocationForSecondaryLookup = getDirectoryPath(containingFile);
}
if (initialLocationForSecondaryLookup !== undefined) {
// check secondary locations
if (traceEnabled) {
trace(host, Diagnostics.Looking_up_in_node_modules_folder_initial_location_0, initialLocationForSecondaryLookup);
}
resolvedFile = loadModuleFromNodeModules(typeReferenceDirectiveName, initialLocationForSecondaryLookup, failedLookupLocations, moduleResolutionState);
if (traceEnabled) {
if (resolvedFile) {
trace(host, Diagnostics.Type_reference_directive_0_was_successfully_resolved_to_1_primary_Colon_2, typeReferenceDirectiveName, resolvedFile, false);
}
else {
trace(host, Diagnostics.Type_reference_directive_0_was_not_resolved, typeReferenceDirectiveName);
}
}
}
else {
if (traceEnabled) {
trace(host, Diagnostics.Containing_file_is_not_specified_and_root_directory_cannot_be_determined_skipping_lookup_in_node_modules_folder);
}
}
return {
resolvedTypeReferenceDirective: resolvedFile
? { primary: false, resolvedFileName: resolvedFile }
: undefined,
failedLookupLocations
};
}
export function resolveModuleName(moduleName: string, containingFile: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost): ResolvedModuleWithFailedLookupLocations {
const traceEnabled = isTraceEnabled(compilerOptions, host);
if (traceEnabled) {
trace(host, Diagnostics.Resolving_module_0_from_1, moduleName, containingFile);
}
let moduleResolution = compilerOptions.moduleResolution;
if (moduleResolution === undefined) {
moduleResolution = getEmitModuleKind(compilerOptions) === ModuleKind.CommonJS ? ModuleResolutionKind.NodeJs : ModuleResolutionKind.Classic;
if (traceEnabled) {
trace(host, Diagnostics.Module_resolution_kind_is_not_specified_using_0, ModuleResolutionKind[moduleResolution]);
}
}
else {
if (traceEnabled) {
trace(host, Diagnostics.Explicitly_specified_module_resolution_kind_Colon_0, ModuleResolutionKind[moduleResolution]);
}
}
let result: ResolvedModuleWithFailedLookupLocations;
switch (moduleResolution) {
case ModuleResolutionKind.NodeJs:
result = nodeModuleNameResolver(moduleName, containingFile, compilerOptions, host);
break;
case ModuleResolutionKind.Classic:
result = classicNameResolver(moduleName, containingFile, compilerOptions, host);
break;
}
if (traceEnabled) {
if (result.resolvedModule) {
trace(host, Diagnostics.Module_name_0_was_successfully_resolved_to_1, moduleName, result.resolvedModule.resolvedFileName);
}
else {
trace(host, Diagnostics.Module_name_0_was_not_resolved, moduleName);
}
}
return result;
}
/*
* Every module resolution kind can has its specific understanding how to load module from a specific path on disk
* I.e. for path '/a/b/c':
* - Node loader will first to try to check if '/a/b/c' points to a file with some supported extension and if this fails
* it will try to load module from directory: directory '/a/b/c' should exist and it should have either 'package.json' with
* 'typings' entry or file 'index' with some supported extension
* - Classic loader will only try to interpret '/a/b/c' as file.
*/
type ResolutionKindSpecificLoader = (candidate: string, extensions: string[], failedLookupLocations: string[], onlyRecordFailures: boolean, state: ModuleResolutionState) => string;
/**
* Any module resolution kind can be augmented with optional settings: 'baseUrl', 'paths' and 'rootDirs' - they are used to
* mitigate differences between design time structure of the project and its runtime counterpart so the same import name
* can be resolved successfully by TypeScript compiler and runtime module loader.
* If these settings are set then loading procedure will try to use them to resolve module name and it can of failure it will
* fallback to standard resolution routine.
*
* - baseUrl - this setting controls how non-relative module names are resolved. If this setting is specified then non-relative
* names will be resolved relative to baseUrl: i.e. if baseUrl is '/a/b' then candidate location to resolve module name 'c/d' will
* be '/a/b/c/d'
* - paths - this setting can only be used when baseUrl is specified. allows to tune how non-relative module names
* will be resolved based on the content of the module name.
* Structure of 'paths' compiler options
* 'paths': {
* pattern-1: [...substitutions],
* pattern-2: [...substitutions],
* ...
* pattern-n: [...substitutions]
* }
* Pattern here is a string that can contain zero or one '*' character. During module resolution module name will be matched against
* all patterns in the list. Matching for patterns that don't contain '*' means that module name must be equal to pattern respecting the case.
* If pattern contains '*' then to match pattern "<prefix>*<suffix>" module name must start with the <prefix> and end with <suffix>.
* <MatchedStar> denotes part of the module name between <prefix> and <suffix>.
* If module name can be matches with multiple patterns then pattern with the longest prefix will be picked.
* After selecting pattern we'll use list of substitutions to get candidate locations of the module and the try to load module
* from the candidate location.
* Substitution is a string that can contain zero or one '*'. To get candidate location from substitution we'll pick every
* substitution in the list and replace '*' with <MatchedStar> string. If candidate location is not rooted it
* will be converted to absolute using baseUrl.
* For example:
* baseUrl: /a/b/c
* "paths": {
* // match all module names
* "*": [
* "*", // use matched name as is,
* // <matched name> will be looked as /a/b/c/<matched name>
*
* "folder1/*" // substitution will convert matched name to 'folder1/<matched name>',
* // since it is not rooted then final candidate location will be /a/b/c/folder1/<matched name>
* ],
* // match module names that start with 'components/'
* "components/*": [ "/root/components/*" ] // substitution will convert /components/folder1/<matched name> to '/root/components/folder1/<matched name>',
* // it is rooted so it will be final candidate location
* }
*
* 'rootDirs' allows the project to be spreaded across multiple locations and resolve modules with relative names as if
* they were in the same location. For example lets say there are two files
* '/local/src/content/file1.ts'
* '/shared/components/contracts/src/content/protocols/file2.ts'
* After bundling content of '/shared/components/contracts/src' will be merged with '/local/src' so
* if file1 has the following import 'import {x} from "./protocols/file2"' it will be resolved successfully in runtime.
* 'rootDirs' provides the way to tell compiler that in order to get the whole project it should behave as if content of all
* root dirs were merged together.
* I.e. for the example above 'rootDirs' will have two entries: [ '/local/src', '/shared/components/contracts/src' ].
* Compiler will first convert './protocols/file2' into absolute path relative to the location of containing file:
* '/local/src/content/protocols/file2' and try to load it - failure.
* Then it will search 'rootDirs' looking for a longest matching prefix of this absolute path and if such prefix is found - absolute path will
* be converted to a path relative to found rootDir entry './content/protocols/file2' (*). As a last step compiler will check all remaining
* entries in 'rootDirs', use them to build absolute path out of (*) and try to resolve module from this location.
*/
function tryLoadModuleUsingOptionalResolutionSettings(moduleName: string, containingDirectory: string, loader: ResolutionKindSpecificLoader,
failedLookupLocations: string[], supportedExtensions: string[], state: ModuleResolutionState): string {
if (moduleHasNonRelativeName(moduleName)) {
return tryLoadModuleUsingBaseUrl(moduleName, loader, failedLookupLocations, supportedExtensions, state);
}
else {
return tryLoadModuleUsingRootDirs(moduleName, containingDirectory, loader, failedLookupLocations, supportedExtensions, state);
}
}
function tryLoadModuleUsingRootDirs(moduleName: string, containingDirectory: string, loader: ResolutionKindSpecificLoader,
failedLookupLocations: string[], supportedExtensions: string[], state: ModuleResolutionState): string {
if (!state.compilerOptions.rootDirs) {
return undefined;
}
if (state.traceEnabled) {
trace(state.host, Diagnostics.rootDirs_option_is_set_using_it_to_resolve_relative_module_name_0, moduleName);
}
const candidate = normalizePath(combinePaths(containingDirectory, moduleName));
let matchedRootDir: string;
let matchedNormalizedPrefix: string;
for (const rootDir of state.compilerOptions.rootDirs) {
// rootDirs are expected to be absolute
// in case of tsconfig.json this will happen automatically - compiler will expand relative names
// using location of tsconfig.json as base location
let normalizedRoot = normalizePath(rootDir);
if (!endsWith(normalizedRoot, directorySeparator)) {
normalizedRoot += directorySeparator;
}
const isLongestMatchingPrefix =
startsWith(candidate, normalizedRoot) &&
(matchedNormalizedPrefix === undefined || matchedNormalizedPrefix.length < normalizedRoot.length);
if (state.traceEnabled) {
trace(state.host, Diagnostics.Checking_if_0_is_the_longest_matching_prefix_for_1_2, normalizedRoot, candidate, isLongestMatchingPrefix);
}
if (isLongestMatchingPrefix) {
matchedNormalizedPrefix = normalizedRoot;
matchedRootDir = rootDir;
}
}
if (matchedNormalizedPrefix) {
if (state.traceEnabled) {
trace(state.host, Diagnostics.Longest_matching_prefix_for_0_is_1, candidate, matchedNormalizedPrefix);
}
const suffix = candidate.substr(matchedNormalizedPrefix.length);
// first - try to load from a initial location
if (state.traceEnabled) {
trace(state.host, Diagnostics.Loading_0_from_the_root_dir_1_candidate_location_2, suffix, matchedNormalizedPrefix, candidate);
}
const resolvedFileName = loader(candidate, supportedExtensions, failedLookupLocations, !directoryProbablyExists(containingDirectory, state.host), state);
if (resolvedFileName) {
return resolvedFileName;
}
if (state.traceEnabled) {
trace(state.host, Diagnostics.Trying_other_entries_in_rootDirs);
}
// then try to resolve using remaining entries in rootDirs
for (const rootDir of state.compilerOptions.rootDirs) {
if (rootDir === matchedRootDir) {
// skip the initially matched entry
continue;
}
const candidate = combinePaths(normalizePath(rootDir), suffix);
if (state.traceEnabled) {
trace(state.host, Diagnostics.Loading_0_from_the_root_dir_1_candidate_location_2, suffix, rootDir, candidate);
}
const baseDirectory = getDirectoryPath(candidate);
const resolvedFileName = loader(candidate, supportedExtensions, failedLookupLocations, !directoryProbablyExists(baseDirectory, state.host), state);
if (resolvedFileName) {
return resolvedFileName;
}
}
if (state.traceEnabled) {
trace(state.host, Diagnostics.Module_resolution_using_rootDirs_has_failed);
}
}
return undefined;
}
function tryLoadModuleUsingBaseUrl(moduleName: string, loader: ResolutionKindSpecificLoader, failedLookupLocations: string[],
supportedExtensions: string[], state: ModuleResolutionState): string {
if (!state.compilerOptions.baseUrl) {
return undefined;
}
if (state.traceEnabled) {
trace(state.host, Diagnostics.baseUrl_option_is_set_to_0_using_this_value_to_resolve_non_relative_module_name_1, state.compilerOptions.baseUrl, moduleName);
}
// string is for exact match
let matchedPattern: Pattern | string | undefined = undefined;
if (state.compilerOptions.paths) {
if (state.traceEnabled) {
trace(state.host, Diagnostics.paths_option_is_specified_looking_for_a_pattern_to_match_module_name_0, moduleName);
}
matchedPattern = matchPatternOrExact(getOwnKeys(state.compilerOptions.paths), moduleName);
}
if (matchedPattern) {
const matchedStar = typeof matchedPattern === "string" ? undefined : matchedText(matchedPattern, moduleName);
const matchedPatternText = typeof matchedPattern === "string" ? matchedPattern : patternText(matchedPattern);
if (state.traceEnabled) {
trace(state.host, Diagnostics.Module_name_0_matched_pattern_1, moduleName, matchedPatternText);
}
for (const subst of state.compilerOptions.paths[matchedPatternText]) {
const path = matchedStar ? subst.replace("*", matchedStar) : subst;
const candidate = normalizePath(combinePaths(state.compilerOptions.baseUrl, path));
if (state.traceEnabled) {
trace(state.host, Diagnostics.Trying_substitution_0_candidate_module_location_Colon_1, subst, path);
}
const resolvedFileName = loader(candidate, supportedExtensions, failedLookupLocations, !directoryProbablyExists(getDirectoryPath(candidate), state.host), state);
if (resolvedFileName) {
return resolvedFileName;
}
}
return undefined;
}
else {
const candidate = normalizePath(combinePaths(state.compilerOptions.baseUrl, moduleName));
if (state.traceEnabled) {
trace(state.host, Diagnostics.Resolving_module_name_0_relative_to_base_url_1_2, moduleName, state.compilerOptions.baseUrl, candidate);
}
return loader(candidate, supportedExtensions, failedLookupLocations, !directoryProbablyExists(getDirectoryPath(candidate), state.host), state);
}
}
/**
* patternStrings contains both pattern strings (containing "*") and regular strings.
* Return an exact match if possible, or a pattern match, or undefined.
* (These are verified by verifyCompilerOptions to have 0 or 1 "*" characters.)
*/
function matchPatternOrExact(patternStrings: string[], candidate: string): string | Pattern | undefined {
const patterns: Pattern[] = [];
for (const patternString of patternStrings) {
const pattern = tryParsePattern(patternString);
if (pattern) {
patterns.push(pattern);
}
else if (patternString === candidate) {
// pattern was matched as is - no need to search further
return patternString;
}
}
return findBestPatternMatch(patterns, _ => _, candidate);
}
function patternText({prefix, suffix}: Pattern): string {
return `${prefix}*${suffix}`;
}
/**
* Given that candidate matches pattern, returns the text matching the '*'.
* E.g.: matchedText(tryParsePattern("foo*baz"), "foobarbaz") === "bar"
*/
function matchedText(pattern: Pattern, candidate: string): string {
Debug.assert(isPatternMatch(pattern, candidate));
return candidate.substr(pattern.prefix.length, candidate.length - pattern.suffix.length);
}
/** Return the object corresponding to the best pattern to match `candidate`. */
/* @internal */
export function findBestPatternMatch<T>(values: T[], getPattern: (value: T) => Pattern, candidate: string): T | undefined {
let matchedValue: T | undefined = undefined;
// use length of prefix as betterness criteria
let longestMatchPrefixLength = -1;
for (const v of values) {
const pattern = getPattern(v);
if (isPatternMatch(pattern, candidate) && pattern.prefix.length > longestMatchPrefixLength) {
longestMatchPrefixLength = pattern.prefix.length;
matchedValue = v;
}
}
return matchedValue;
}
function isPatternMatch({prefix, suffix}: Pattern, candidate: string) {
return candidate.length >= prefix.length + suffix.length &&
startsWith(candidate, prefix) &&
endsWith(candidate, suffix);
}
/* @internal */
export function tryParsePattern(pattern: string): Pattern | undefined {
// This should be verified outside of here and a proper error thrown.
Debug.assert(hasZeroOrOneAsteriskCharacter(pattern));
const indexOfStar = pattern.indexOf("*");
return indexOfStar === -1 ? undefined : {
prefix: pattern.substr(0, indexOfStar),
suffix: pattern.substr(indexOfStar + 1)
};
}
export function nodeModuleNameResolver(moduleName: string, containingFile: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost): ResolvedModuleWithFailedLookupLocations {
const containingDirectory = getDirectoryPath(containingFile);
const supportedExtensions = getSupportedExtensions(compilerOptions);
const traceEnabled = isTraceEnabled(compilerOptions, host);
const failedLookupLocations: string[] = [];
const state = { compilerOptions, host, traceEnabled, skipTsx: false };
let resolvedFileName = tryLoadModuleUsingOptionalResolutionSettings(moduleName, containingDirectory, nodeLoadModuleByRelativeName,
failedLookupLocations, supportedExtensions, state);
let isExternalLibraryImport = false;
if (!resolvedFileName) {
if (moduleHasNonRelativeName(moduleName)) {
if (traceEnabled) {
trace(host, Diagnostics.Loading_module_0_from_node_modules_folder, moduleName);
}
resolvedFileName = loadModuleFromNodeModules(moduleName, containingDirectory, failedLookupLocations, state);
isExternalLibraryImport = resolvedFileName !== undefined;
}
else {
const candidate = normalizePath(combinePaths(containingDirectory, moduleName));
resolvedFileName = nodeLoadModuleByRelativeName(candidate, supportedExtensions, failedLookupLocations, /*onlyRecordFailures*/ false, state);
}
}
if (resolvedFileName && host.realpath) {
const originalFileName = resolvedFileName;
resolvedFileName = normalizePath(host.realpath(resolvedFileName));
if (traceEnabled) {
trace(host, Diagnostics.Resolving_real_path_for_0_result_1, originalFileName, resolvedFileName);
}
}
return createResolvedModule(resolvedFileName, isExternalLibraryImport, failedLookupLocations);
}
function nodeLoadModuleByRelativeName(candidate: string, supportedExtensions: string[], failedLookupLocations: string[],
onlyRecordFailures: boolean, state: ModuleResolutionState): string {
if (state.traceEnabled) {
trace(state.host, Diagnostics.Loading_module_as_file_Slash_folder_candidate_module_location_0, candidate);
}
const resolvedFileName = loadModuleFromFile(candidate, supportedExtensions, failedLookupLocations, onlyRecordFailures, state);
return resolvedFileName || loadNodeModuleFromDirectory(supportedExtensions, candidate, failedLookupLocations, onlyRecordFailures, state);
}
/* @internal */
export function directoryProbablyExists(directoryName: string, host: { directoryExists?: (directoryName: string) => boolean }): boolean {
// if host does not support 'directoryExists' assume that directory will exist
return !host.directoryExists || host.directoryExists(directoryName);
}
/**
* @param {boolean} onlyRecordFailures - if true then function won't try to actually load files but instead record all attempts as failures. This flag is necessary
* in cases when we know upfront that all load attempts will fail (because containing folder does not exists) however we still need to record all failed lookup locations.
*/
function loadModuleFromFile(candidate: string, extensions: string[], failedLookupLocation: string[], onlyRecordFailures: boolean, state: ModuleResolutionState): string {
// First try to keep/add an extension: importing "./foo.ts" can be matched by a file "./foo.ts", and "./foo" by "./foo.d.ts"
const resolvedByAddingOrKeepingExtension = loadModuleFromFileWorker(candidate, extensions, failedLookupLocation, onlyRecordFailures, state);
if (resolvedByAddingOrKeepingExtension) {
return resolvedByAddingOrKeepingExtension;
}
// Then try stripping a ".js" or ".jsx" extension and replacing it with a TypeScript one, e.g. "./foo.js" can be matched by "./foo.ts" or "./foo.d.ts"
if (hasJavaScriptFileExtension(candidate)) {
const extensionless = removeFileExtension(candidate);
if (state.traceEnabled) {
const extension = candidate.substring(extensionless.length);
trace(state.host, Diagnostics.File_name_0_has_a_1_extension_stripping_it, candidate, extension);
}
return loadModuleFromFileWorker(extensionless, extensions, failedLookupLocation, onlyRecordFailures, state);
}
}
function loadModuleFromFileWorker(candidate: string, extensions: string[], failedLookupLocation: string[], onlyRecordFailures: boolean, state: ModuleResolutionState): string {
if (!onlyRecordFailures) {
// check if containing folder exists - if it doesn't then just record failures for all supported extensions without disk probing
const directory = getDirectoryPath(candidate);
if (directory) {
onlyRecordFailures = !directoryProbablyExists(directory, state.host);
}
}
return forEach(extensions, tryLoad);
function tryLoad(ext: string): string {
if (state.skipTsx && isJsxOrTsxExtension(ext)) {
return undefined;
}
const fileName = fileExtensionIs(candidate, ext) ? candidate : candidate + ext;
if (!onlyRecordFailures && state.host.fileExists(fileName)) {
if (state.traceEnabled) {
trace(state.host, Diagnostics.File_0_exist_use_it_as_a_name_resolution_result, fileName);
}
return fileName;
}
else {
if (state.traceEnabled) {
trace(state.host, Diagnostics.File_0_does_not_exist, fileName);
}
failedLookupLocation.push(fileName);
return undefined;
}
}
}
function loadNodeModuleFromDirectory(extensions: string[], candidate: string, failedLookupLocation: string[], onlyRecordFailures: boolean, state: ModuleResolutionState): string {
const packageJsonPath = pathToPackageJson(candidate);
const directoryExists = !onlyRecordFailures && directoryProbablyExists(candidate, state.host);
if (directoryExists && state.host.fileExists(packageJsonPath)) {
if (state.traceEnabled) {
trace(state.host, Diagnostics.Found_package_json_at_0, packageJsonPath);
}
const typesFile = tryReadTypesSection(packageJsonPath, candidate, state);
if (typesFile) {
const result = loadModuleFromFile(typesFile, extensions, failedLookupLocation, !directoryProbablyExists(getDirectoryPath(typesFile), state.host), state);
if (result) {
return result;
}
}
else {
if (state.traceEnabled) {
trace(state.host, Diagnostics.package_json_does_not_have_types_field);
}
}
}
else {
if (state.traceEnabled) {
trace(state.host, Diagnostics.File_0_does_not_exist, packageJsonPath);
}
// record package json as one of failed lookup locations - in the future if this file will appear it will invalidate resolution results
failedLookupLocation.push(packageJsonPath);
}
return loadModuleFromFile(combinePaths(candidate, "index"), extensions, failedLookupLocation, !directoryExists, state);
}
function pathToPackageJson(directory: string): string {
return combinePaths(directory, "package.json");
}
function loadModuleFromNodeModulesFolder(moduleName: string, directory: string, failedLookupLocations: string[], state: ModuleResolutionState): string {
const nodeModulesFolder = combinePaths(directory, "node_modules");
const nodeModulesFolderExists = directoryProbablyExists(nodeModulesFolder, state.host);
const candidate = normalizePath(combinePaths(nodeModulesFolder, moduleName));
const supportedExtensions = getSupportedExtensions(state.compilerOptions);
let result = loadModuleFromFile(candidate, supportedExtensions, failedLookupLocations, !nodeModulesFolderExists, state);
if (result) {
return result;
}
result = loadNodeModuleFromDirectory(supportedExtensions, candidate, failedLookupLocations, !nodeModulesFolderExists, state);
if (result) {
return result;
}
}
function loadModuleFromNodeModules(moduleName: string, directory: string, failedLookupLocations: string[], state: ModuleResolutionState): string {
directory = normalizeSlashes(directory);
while (true) {
const baseName = getBaseFileName(directory);
if (baseName !== "node_modules") {
// Try to load source from the package
const packageResult = loadModuleFromNodeModulesFolder(moduleName, directory, failedLookupLocations, state);
if (packageResult && hasTypeScriptFileExtension(packageResult)) {
// Always prefer a TypeScript (.ts, .tsx, .d.ts) file shipped with the package
return packageResult;
}
else {
// Else prefer a types package over non-TypeScript results (e.g. JavaScript files)
const typesResult = loadModuleFromNodeModulesFolder(combinePaths("@types", moduleName), directory, failedLookupLocations, state);
if (typesResult || packageResult) {
return typesResult || packageResult;
}
}
}
const parentPath = getDirectoryPath(directory);
if (parentPath === directory) {
break;
}
directory = parentPath;
}
return undefined;
}
export function classicNameResolver(moduleName: string, containingFile: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost): ResolvedModuleWithFailedLookupLocations {
const traceEnabled = isTraceEnabled(compilerOptions, host);
const state = { compilerOptions, host, traceEnabled, skipTsx: !compilerOptions.jsx };
const failedLookupLocations: string[] = [];
const supportedExtensions = getSupportedExtensions(compilerOptions);
let containingDirectory = getDirectoryPath(containingFile);
const resolvedFileName = tryLoadModuleUsingOptionalResolutionSettings(moduleName, containingDirectory, loadModuleFromFile, failedLookupLocations, supportedExtensions, state);
if (resolvedFileName) {
return createResolvedModule(resolvedFileName, /*isExternalLibraryImport*/false, failedLookupLocations);
}
let referencedSourceFile: string;
if (moduleHasNonRelativeName(moduleName)) {
while (true) {
const searchName = normalizePath(combinePaths(containingDirectory, moduleName));
referencedSourceFile = loadModuleFromFile(searchName, supportedExtensions, failedLookupLocations, /*onlyRecordFailures*/ false, state);
if (referencedSourceFile) {
break;
}
const parentPath = getDirectoryPath(containingDirectory);
if (parentPath === containingDirectory) {
break;
}
containingDirectory = parentPath;
}
}
else {
const candidate = normalizePath(combinePaths(containingDirectory, moduleName));
referencedSourceFile = loadModuleFromFile(candidate, supportedExtensions, failedLookupLocations, /*onlyRecordFailures*/ false, state);
}
return referencedSourceFile
? { resolvedModule: { resolvedFileName: referencedSourceFile }, failedLookupLocations }
: { resolvedModule: undefined, failedLookupLocations };
}
/* @internal */
export const defaultInitCompilerOptions: CompilerOptions = {
module: ModuleKind.CommonJS,
target: ScriptTarget.ES5,
noImplicitAny: false,
sourceMap: false,
};
interface OutputFingerprint {
hash: string;
byteOrderMark: boolean;
@@ -967,6 +206,7 @@ namespace ts {
readFile: fileName => sys.readFile(fileName),
trace: (s: string) => sys.write(s + newLine),
directoryExists: directoryName => sys.directoryExists(directoryName),
getEnvironmentVariable: name => getEnvironmentVariable(name, /*host*/ undefined),
getDirectories: (path: string) => sys.getDirectories(path),
realpath
};
@@ -1049,44 +289,6 @@ namespace ts {
return resolutions;
}
/**
* Given a set of options, returns the set of type directive names
* that should be included for this program automatically.
* This list could either come from the config file,
* or from enumerating the types root + initial secondary types lookup location.
* More type directives might appear in the program later as a result of loading actual source files;
* this list is only the set of defaults that are implicitly included.
*/
export function getAutomaticTypeDirectiveNames(options: CompilerOptions, host: ModuleResolutionHost): string[] {
// Use explicit type list from tsconfig.json
if (options.types) {
return options.types;
}
// Walk the primary type lookup locations
const result: string[] = [];
if (host.directoryExists && host.getDirectories) {
const typeRoots = getEffectiveTypeRoots(options, host);
if (typeRoots) {
for (const root of typeRoots) {
if (host.directoryExists(root)) {
for (const typeDirectivePath of host.getDirectories(root)) {
const normalized = normalizePath(typeDirectivePath);
const packageJsonPath = pathToPackageJson(combinePaths(root, normalized));
// tslint:disable-next-line:no-null-keyword
const isNotNeededPackage = host.fileExists(packageJsonPath) && readJson(packageJsonPath, host).typings === null;
if (!isNotNeededPackage) {
// Return just the type directive names
result.push(getBaseFileName(normalized));
}
}
}
}
}
}
return result;
}
export function createProgram(rootNames: string[], options: CompilerOptions, host?: CompilerHost, oldProgram?: Program): Program {
let program: Program;
let files: SourceFile[] = [];
@@ -1105,7 +307,7 @@ namespace ts {
// - This calls resolveModuleNames, and then calls findSourceFile for each resolved module.
// As all these operations happen - and are nested - within the createProgram call, they close over the below variables.
// The current resolution depth is tracked by incrementing/decrementing as the depth first search progresses.
const maxNodeModulesJsDepth = typeof options.maxNodeModuleJsDepth === "number" ? options.maxNodeModuleJsDepth : 2;
const maxNodeModulesJsDepth = typeof options.maxNodeModuleJsDepth === "number" ? options.maxNodeModuleJsDepth : 0;
let currentNodeModulesDepth = 0;
// If a module has some of its imports skipped due to being at the depth limit under node_modules, then track
@@ -1209,7 +411,8 @@ namespace ts {
getSymbolCount: () => getDiagnosticsProducingTypeChecker().getSymbolCount(),
getTypeCount: () => getDiagnosticsProducingTypeChecker().getTypeCount(),
getFileProcessingDiagnostics: () => fileProcessingDiagnostics,
getResolvedTypeReferenceDirectives: () => resolvedTypeReferenceDirectives
getResolvedTypeReferenceDirectives: () => resolvedTypeReferenceDirectives,
dropDiagnosticsProducingTypeChecker
};
verifyCompilerOptions();
@@ -1405,19 +608,23 @@ namespace ts {
return diagnosticsProducingTypeChecker || (diagnosticsProducingTypeChecker = createTypeChecker(program, /*produceDiagnostics:*/ true));
}
function dropDiagnosticsProducingTypeChecker() {
diagnosticsProducingTypeChecker = undefined;
}
function getTypeChecker() {
return noDiagnosticsTypeChecker || (noDiagnosticsTypeChecker = createTypeChecker(program, /*produceDiagnostics:*/ false));
}
function emit(sourceFile?: SourceFile, writeFileCallback?: WriteFileCallback, cancellationToken?: CancellationToken): EmitResult {
return runWithCancellationToken(() => emitWorker(program, sourceFile, writeFileCallback, cancellationToken));
function emit(sourceFile?: SourceFile, writeFileCallback?: WriteFileCallback, cancellationToken?: CancellationToken, emitOnlyDtsFiles?: boolean): EmitResult {
return runWithCancellationToken(() => emitWorker(program, sourceFile, writeFileCallback, cancellationToken, emitOnlyDtsFiles));
}
function isEmitBlocked(emitFileName: string): boolean {
return hasEmitBlockingDiagnostics.contains(toPath(emitFileName, currentDirectory, getCanonicalFileName));
}
function emitWorker(program: Program, sourceFile: SourceFile, writeFileCallback: WriteFileCallback, cancellationToken: CancellationToken): EmitResult {
function emitWorker(program: Program, sourceFile: SourceFile, writeFileCallback: WriteFileCallback, cancellationToken: CancellationToken, emitOnlyDtsFiles?: boolean): EmitResult {
let declarationDiagnostics: Diagnostic[] = [];
if (options.noEmit) {
@@ -1462,7 +669,8 @@ namespace ts {
const emitResult = emitFiles(
emitResolver,
getEmitHost(writeFileCallback),
sourceFile);
sourceFile,
emitOnlyDtsFiles);
performance.mark("afterEmit");
performance.measure("Emit", "beforeEmit", "afterEmit");
@@ -1710,7 +918,7 @@ namespace ts {
return false;
}
function checkModifiers(modifiers: ModifiersArray): boolean {
function checkModifiers(modifiers: NodeArray<Modifier>): boolean {
if (modifiers) {
for (const modifier of modifiers) {
switch (modifier.kind) {
@@ -1794,6 +1002,17 @@ namespace ts {
let imports: LiteralExpression[];
let moduleAugmentations: LiteralExpression[];
// If we are importing helpers, we need to add a synthetic reference to resolve the
// helpers library.
if (options.importHelpers
&& (options.isolatedModules || isExternalModuleFile)
&& !file.isDeclarationFile) {
const externalHelpersModuleReference = <StringLiteral>createNode(SyntaxKind.StringLiteral);
externalHelpersModuleReference.text = externalHelpersModuleNameText;
externalHelpersModuleReference.parent = file;
imports = [externalHelpersModuleReference];
}
for (const node of file.statements) {
collectModuleReferences(node, /*inAmbientModule*/ false);
if (isJavaScriptFile) {
@@ -1827,7 +1046,7 @@ namespace ts {
}
break;
case SyntaxKind.ModuleDeclaration:
if (isAmbientModule(<ModuleDeclaration>node) && (inAmbientModule || node.flags & NodeFlags.Ambient || isDeclarationFile(file))) {
if (isAmbientModule(<ModuleDeclaration>node) && (inAmbientModule || hasModifier(node, ModifierFlags.Ambient) || isDeclarationFile(file))) {
const moduleName = <LiteralExpression>(<ModuleDeclaration>node).name;
// Ambient module declarations can be interpreted as augmentations for some existing external modules.
// This will happen in two cases:
@@ -2310,7 +1529,7 @@ namespace ts {
programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Option_0_cannot_be_specified_without_specifying_option_1, "emitDecoratorMetadata", "experimentalDecorators"));
}
if (options.reactNamespace && !isIdentifier(options.reactNamespace, languageVersion)) {
if (options.reactNamespace && !isIdentifierText(options.reactNamespace, languageVersion)) {
programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Invalid_value_for_reactNamespace_0_is_not_a_valid_identifier, options.reactNamespace));
}
+129 -53
View File
@@ -27,6 +27,7 @@ namespace ts {
reScanSlashToken(): SyntaxKind;
reScanTemplateToken(): SyntaxKind;
scanJsxIdentifier(): SyntaxKind;
scanJsxAttributeValue(): SyntaxKind;
reScanJsxToken(): SyntaxKind;
scanJsxToken(): SyntaxKind;
scanJSDocToken(): SyntaxKind;
@@ -439,9 +440,7 @@ namespace ts {
/* @internal */
export function skipTrivia(text: string, pos: number, stopAfterLineBreak?: boolean, stopAtComments = false): number {
// Using ! with a greater than test is a fast way of testing the following conditions:
// pos === undefined || pos === null || isNaN(pos) || pos < 0;
if (!(pos >= 0)) {
if (positionIsSynthesized(pos)) {
return pos;
}
@@ -590,20 +589,34 @@ namespace ts {
}
/**
* Extract comments from text prefixing the token closest following `pos`.
* The return value is an array containing a TextRange for each comment.
* Single-line comment ranges include the beginning '//' characters but not the ending line break.
* Multi - line comment ranges include the beginning '/* and ending '<asterisk>/' characters.
* The return value is undefined if no comments were found.
* @param trailing
* If false, whitespace is skipped until the first line break and comments between that location
* and the next token are returned.
* If true, comments occurring between the given position and the next line break are returned.
* Invokes a callback for each comment range following the provided position.
*
* Single-line comment ranges include the leading double-slash characters but not the ending
* line break. Multi-line comment ranges include the leading slash-asterisk and trailing
* asterisk-slash characters.
*
* @param reduce If true, accumulates the result of calling the callback in a fashion similar
* to reduceLeft. If false, iteration stops when the callback returns a truthy value.
* @param text The source text to scan.
* @param pos The position at which to start scanning.
* @param trailing If false, whitespace is skipped until the first line break and comments
* between that location and the next token are returned. If true, comments occurring
* between the given position and the next line break are returned.
* @param cb The callback to execute as each comment range is encountered.
* @param state A state value to pass to each iteration of the callback.
* @param initial An initial value to pass when accumulating results (when "reduce" is true).
* @returns If "reduce" is true, the accumulated value. If "reduce" is false, the first truthy
* return value of the callback.
*/
function getCommentRanges(text: string, pos: number, trailing: boolean): CommentRange[] {
let result: CommentRange[];
function iterateCommentRanges<T, U>(reduce: boolean, text: string, pos: number, trailing: boolean, cb: (pos: number, end: number, kind: SyntaxKind, hasTrailingNewLine: boolean, state: T, memo: U) => U, state: T, initial?: U): U {
let pendingPos: number;
let pendingEnd: number;
let pendingKind: SyntaxKind;
let pendingHasTrailingNewLine: boolean;
let hasPendingCommentRange = false;
let collecting = trailing || pos === 0;
while (pos < text.length) {
let accumulator = initial;
scan: while (pos >= 0 && pos < text.length) {
const ch = text.charCodeAt(pos);
switch (ch) {
case CharacterCodes.carriageReturn:
@@ -613,12 +626,14 @@ namespace ts {
case CharacterCodes.lineFeed:
pos++;
if (trailing) {
return result;
break scan;
}
collecting = true;
if (result && result.length) {
lastOrUndefined(result).hasTrailingNewLine = true;
if (hasPendingCommentRange) {
pendingHasTrailingNewLine = true;
}
continue;
case CharacterCodes.tab:
case CharacterCodes.verticalTab:
@@ -651,38 +666,78 @@ namespace ts {
pos++;
}
}
if (collecting) {
if (!result) {
result = [];
if (hasPendingCommentRange) {
accumulator = cb(pendingPos, pendingEnd, pendingKind, pendingHasTrailingNewLine, state, accumulator);
if (!reduce && accumulator) {
// If we are not reducing and we have a truthy result, return it.
return accumulator;
}
hasPendingCommentRange = false;
}
result.push({ pos: startPos, end: pos, hasTrailingNewLine, kind });
pendingPos = startPos;
pendingEnd = pos;
pendingKind = kind;
pendingHasTrailingNewLine = hasTrailingNewLine;
hasPendingCommentRange = true;
}
continue;
}
break;
break scan;
default:
if (ch > CharacterCodes.maxAsciiCharacter && (isWhiteSpace(ch))) {
if (result && result.length && isLineBreak(ch)) {
lastOrUndefined(result).hasTrailingNewLine = true;
if (hasPendingCommentRange && isLineBreak(ch)) {
pendingHasTrailingNewLine = true;
}
pos++;
continue;
}
break;
break scan;
}
return result;
}
return result;
if (hasPendingCommentRange) {
accumulator = cb(pendingPos, pendingEnd, pendingKind, pendingHasTrailingNewLine, state, accumulator);
}
return accumulator;
}
export function forEachLeadingCommentRange<T, U>(text: string, pos: number, cb: (pos: number, end: number, kind: SyntaxKind, hasTrailingNewLine: boolean, state: T) => U, state?: T) {
return iterateCommentRanges(/*reduce*/ false, text, pos, /*trailing*/ false, cb, state);
}
export function forEachTrailingCommentRange<T, U>(text: string, pos: number, cb: (pos: number, end: number, kind: SyntaxKind, hasTrailingNewLine: boolean, state: T) => U, state?: T) {
return iterateCommentRanges(/*reduce*/ false, text, pos, /*trailing*/ true, cb, state);
}
export function reduceEachLeadingCommentRange<T, U>(text: string, pos: number, cb: (pos: number, end: number, kind: SyntaxKind, hasTrailingNewLine: boolean, state: T, memo: U) => U, state: T, initial: U) {
return iterateCommentRanges(/*reduce*/ true, text, pos, /*trailing*/ false, cb, state, initial);
}
export function reduceEachTrailingCommentRange<T, U>(text: string, pos: number, cb: (pos: number, end: number, kind: SyntaxKind, hasTrailingNewLine: boolean, state: T, memo: U) => U, state: T, initial: U) {
return iterateCommentRanges(/*reduce*/ true, text, pos, /*trailing*/ true, cb, state, initial);
}
function appendCommentRange(pos: number, end: number, kind: SyntaxKind, hasTrailingNewLine: boolean, state: any, comments: CommentRange[]) {
if (!comments) {
comments = [];
}
comments.push({ pos, end, hasTrailingNewLine, kind });
return comments;
}
export function getLeadingCommentRanges(text: string, pos: number): CommentRange[] {
return getCommentRanges(text, pos, /*trailing*/ false);
return reduceEachLeadingCommentRange(text, pos, appendCommentRange, undefined, undefined);
}
export function getTrailingCommentRanges(text: string, pos: number): CommentRange[] {
return getCommentRanges(text, pos, /*trailing*/ true);
return reduceEachTrailingCommentRange(text, pos, appendCommentRange, undefined, undefined);
}
/** Optionally, get the shebang */
@@ -705,7 +760,7 @@ namespace ts {
}
/* @internal */
export function isIdentifier(name: string, languageVersion: ScriptTarget): boolean {
export function isIdentifierText(name: string, languageVersion: ScriptTarget): boolean {
if (!isIdentifierStart(name.charCodeAt(0), languageVersion)) {
return false;
}
@@ -763,6 +818,7 @@ namespace ts {
reScanSlashToken,
reScanTemplateToken,
scanJsxIdentifier,
scanJsxAttributeValue,
reScanJsxToken,
scanJsxToken,
scanJSDocToken,
@@ -857,7 +913,7 @@ namespace ts {
return value;
}
function scanString(): string {
function scanString(allowEscapes = true): string {
const quote = text.charCodeAt(pos);
pos++;
let result = "";
@@ -875,7 +931,7 @@ namespace ts {
pos++;
break;
}
if (ch === CharacterCodes.backslash) {
if (ch === CharacterCodes.backslash && allowEscapes) {
result += text.substring(start, pos);
result += scanEscapeSequence();
start = pos;
@@ -1683,46 +1739,66 @@ namespace ts {
return token;
}
function scanJsxAttributeValue(): SyntaxKind {
startPos = pos;
switch (text.charCodeAt(pos)) {
case CharacterCodes.doubleQuote:
case CharacterCodes.singleQuote:
tokenValue = scanString(/*allowEscapes*/ false);
return token = SyntaxKind.StringLiteral;
default:
// If this scans anything other than `{`, it's a parse error.
return scan();
}
}
function scanJSDocToken(): SyntaxKind {
if (pos >= end) {
return token = SyntaxKind.EndOfFileToken;
}
startPos = pos;
// Eat leading whitespace
let ch = text.charCodeAt(pos);
while (pos < end) {
ch = text.charCodeAt(pos);
if (isWhiteSpaceSingleLine(ch)) {
pos++;
}
else {
break;
}
}
tokenPos = pos;
const ch = text.charCodeAt(pos);
switch (ch) {
case CharacterCodes.tab:
case CharacterCodes.verticalTab:
case CharacterCodes.formFeed:
case CharacterCodes.space:
while (pos < end && isWhiteSpaceSingleLine(text.charCodeAt(pos))) {
pos++;
}
return token = SyntaxKind.WhitespaceTrivia;
case CharacterCodes.at:
return pos += 1, token = SyntaxKind.AtToken;
pos++;
return token = SyntaxKind.AtToken;
case CharacterCodes.lineFeed:
case CharacterCodes.carriageReturn:
return pos += 1, token = SyntaxKind.NewLineTrivia;
pos++;
return token = SyntaxKind.NewLineTrivia;
case CharacterCodes.asterisk:
return pos += 1, token = SyntaxKind.AsteriskToken;
pos++;
return token = SyntaxKind.AsteriskToken;
case CharacterCodes.openBrace:
return pos += 1, token = SyntaxKind.OpenBraceToken;
pos++;
return token = SyntaxKind.OpenBraceToken;
case CharacterCodes.closeBrace:
return pos += 1, token = SyntaxKind.CloseBraceToken;
pos++;
return token = SyntaxKind.CloseBraceToken;
case CharacterCodes.openBracket:
return pos += 1, token = SyntaxKind.OpenBracketToken;
pos++;
return token = SyntaxKind.OpenBracketToken;
case CharacterCodes.closeBracket:
return pos += 1, token = SyntaxKind.CloseBracketToken;
pos++;
return token = SyntaxKind.CloseBracketToken;
case CharacterCodes.equals:
return pos += 1, token = SyntaxKind.EqualsToken;
pos++;
return token = SyntaxKind.EqualsToken;
case CharacterCodes.comma:
return pos += 1, token = SyntaxKind.CommaToken;
pos++;
return token = SyntaxKind.CommaToken;
}
if (isIdentifierStart(ch, ScriptTarget.Latest)) {
+192 -99
View File
@@ -3,19 +3,73 @@
/* @internal */
namespace ts {
export interface SourceMapWriter {
getSourceMapData(): SourceMapData;
setSourceFile(sourceFile: SourceFile): void;
emitPos(pos: number): void;
emitStart(range: TextRange): void;
emitEnd(range: TextRange, stopOverridingSpan?: boolean): void;
changeEmitSourcePos(): void;
getText(): string;
getSourceMappingURL(): string;
/**
* Initialize the SourceMapWriter for a new output file.
*
* @param filePath The path to the generated output file.
* @param sourceMapFilePath The path to the output source map file.
* @param sourceFiles The input source files for the program.
* @param isBundledEmit A value indicating whether the generated output file is a bundle.
*/
initialize(filePath: string, sourceMapFilePath: string, sourceFiles: SourceFile[], isBundledEmit: boolean): void;
/**
* Reset the SourceMapWriter to an empty state.
*/
reset(): void;
/**
* Set the current source file.
*
* @param sourceFile The source file.
*/
setSourceFile(sourceFile: SourceFile): void;
/**
* Emits a mapping.
*
* If the position is synthetic (undefined or a negative value), no mapping will be
* created.
*
* @param pos The position.
*/
emitPos(pos: number): void;
/**
* Emits a node with possible leading and trailing source maps.
*
* @param emitContext The current emit context
* @param node The node to emit.
* @param emitCallback The callback used to emit the node.
*/
emitNodeWithSourceMap(emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void): void;
/**
* Emits a token of a node node with possible leading and trailing source maps.
*
* @param node The node containing the token.
* @param token The token to emit.
* @param tokenStartPos The start pos of the token.
* @param emitCallback The callback used to emit the token.
*/
emitTokenWithSourceMap(node: Node, token: SyntaxKind, tokenStartPos: number, emitCallback: (token: SyntaxKind, tokenStartPos: number) => number): number;
/**
* Gets the text for the source map.
*/
getText(): string;
/**
* Gets the SourceMappingURL for the source map.
*/
getSourceMappingURL(): string;
/**
* Gets test data for source maps.
*/
getSourceMapData(): SourceMapData;
}
let nullSourceMapWriter: SourceMapWriter;
// Used for initialize lastEncodedSourceMapSpan and reset lastEncodedSourceMapSpan when updateLastEncodedAndRecordedSpans
const defaultLastEncodedSourceMapSpan: SourceMapSpan = {
emittedLine: 1,
@@ -25,32 +79,12 @@ namespace ts {
sourceIndex: 0
};
export function getNullSourceMapWriter(): SourceMapWriter {
if (nullSourceMapWriter === undefined) {
nullSourceMapWriter = {
getSourceMapData(): SourceMapData { return undefined; },
setSourceFile(sourceFile: SourceFile): void { },
emitStart(range: TextRange): void { },
emitEnd(range: TextRange, stopOverridingSpan?: boolean): void { },
emitPos(pos: number): void { },
changeEmitSourcePos(): void { },
getText(): string { return undefined; },
getSourceMappingURL(): string { return undefined; },
initialize(filePath: string, sourceMapFilePath: string, sourceFiles: SourceFile[], isBundledEmit: boolean): void { },
reset(): void { },
};
}
return nullSourceMapWriter;
}
export function createSourceMapWriter(host: EmitHost, writer: EmitTextWriter): SourceMapWriter {
const compilerOptions = host.getCompilerOptions();
const extendedDiagnostics = compilerOptions.extendedDiagnostics;
let currentSourceFile: SourceFile;
let currentSourceText: string;
let sourceMapDir: string; // The directory in which sourcemap will be
let stopOverridingSpan = false;
let modifyLastSourcePos = false;
// Current source map file and its index in the sources list
let sourceMapSourceIndex: number;
@@ -62,26 +96,39 @@ namespace ts {
// Source map data
let sourceMapData: SourceMapData;
let disabled: boolean = !(compilerOptions.sourceMap || compilerOptions.inlineSourceMap);
return {
initialize,
reset,
getSourceMapData: () => sourceMapData,
setSourceFile,
emitPos,
emitStart,
emitEnd,
changeEmitSourcePos,
emitNodeWithSourceMap,
emitTokenWithSourceMap,
getText,
getSourceMappingURL,
initialize,
reset,
};
/**
* Initialize the SourceMapWriter for a new output file.
*
* @param filePath The path to the generated output file.
* @param sourceMapFilePath The path to the output source map file.
* @param sourceFiles The input source files for the program.
* @param isBundledEmit A value indicating whether the generated output file is a bundle.
*/
function initialize(filePath: string, sourceMapFilePath: string, sourceFiles: SourceFile[], isBundledEmit: boolean) {
if (disabled) {
return;
}
if (sourceMapData) {
reset();
}
currentSourceFile = undefined;
currentSourceText = undefined;
// Current source map file and its index in the sources list
sourceMapSourceIndex = -1;
@@ -140,7 +187,14 @@ namespace ts {
}
}
/**
* Reset the SourceMapWriter to an empty state.
*/
function reset() {
if (disabled) {
return;
}
currentSourceFile = undefined;
sourceMapDir = undefined;
sourceMapSourceIndex = undefined;
@@ -150,47 +204,6 @@ namespace ts {
sourceMapData = undefined;
}
function updateLastEncodedAndRecordedSpans() {
if (modifyLastSourcePos) {
// Reset the source pos
modifyLastSourcePos = false;
// Change Last recorded Map with last encoded emit line and character
lastRecordedSourceMapSpan.emittedLine = lastEncodedSourceMapSpan.emittedLine;
lastRecordedSourceMapSpan.emittedColumn = lastEncodedSourceMapSpan.emittedColumn;
// Pop sourceMapDecodedMappings to remove last entry
sourceMapData.sourceMapDecodedMappings.pop();
// Point the lastEncodedSourceMapSpace to the previous encoded sourceMapSpan
// If the list is empty which indicates that we are at the beginning of the file,
// we have to reset it to default value (same value when we first initialize sourceMapWriter)
lastEncodedSourceMapSpan = sourceMapData.sourceMapDecodedMappings.length ?
sourceMapData.sourceMapDecodedMappings[sourceMapData.sourceMapDecodedMappings.length - 1] :
defaultLastEncodedSourceMapSpan;
// TODO: Update lastEncodedNameIndex
// Since we dont support this any more, lets not worry about it right now.
// When we start supporting nameIndex, we will get back to this
// Change the encoded source map
const sourceMapMappings = sourceMapData.sourceMapMappings;
let lenthToSet = sourceMapMappings.length - 1;
for (; lenthToSet >= 0; lenthToSet--) {
const currentChar = sourceMapMappings.charAt(lenthToSet);
if (currentChar === ",") {
// Separator for the entry found
break;
}
if (currentChar === ";" && lenthToSet !== 0 && sourceMapMappings.charAt(lenthToSet - 1) !== ";") {
// Last line separator found
break;
}
}
sourceMapData.sourceMapMappings = sourceMapMappings.substr(0, Math.max(0, lenthToSet));
}
}
// Encoding for sourcemap span
function encodeLastRecordedSourceMapSpan() {
if (!lastRecordedSourceMapSpan || lastRecordedSourceMapSpan === lastEncodedSourceMapSpan) {
@@ -236,8 +249,16 @@ namespace ts {
sourceMapData.sourceMapDecodedMappings.push(lastEncodedSourceMapSpan);
}
/**
* Emits a mapping.
*
* If the position is synthetic (undefined or a negative value), no mapping will be
* created.
*
* @param pos The position.
*/
function emitPos(pos: number) {
if (pos === -1) {
if (disabled || positionIsSynthesized(pos)) {
return;
}
@@ -273,45 +294,103 @@ namespace ts {
sourceColumn: sourceLinePos.character,
sourceIndex: sourceMapSourceIndex
};
stopOverridingSpan = false;
}
else if (!stopOverridingSpan) {
else {
// Take the new pos instead since there is no change in emittedLine and column since last location
lastRecordedSourceMapSpan.sourceLine = sourceLinePos.line;
lastRecordedSourceMapSpan.sourceColumn = sourceLinePos.character;
lastRecordedSourceMapSpan.sourceIndex = sourceMapSourceIndex;
}
updateLastEncodedAndRecordedSpans();
if (extendedDiagnostics) {
performance.mark("afterSourcemap");
performance.measure("Source Map", "beforeSourcemap", "afterSourcemap");
}
}
function getStartPos(range: TextRange) {
const rangeHasDecorators = !!(range as Node).decorators;
return range.pos !== -1 ? skipTrivia(currentSourceFile.text, rangeHasDecorators ? (range as Node).decorators.end : range.pos) : -1;
/**
* Emits a node with possible leading and trailing source maps.
*
* @param node The node to emit.
* @param emitCallback The callback used to emit the node.
*/
function emitNodeWithSourceMap(emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void) {
if (disabled) {
return emitCallback(emitContext, node);
}
if (node) {
const emitNode = node.emitNode;
const emitFlags = emitNode && emitNode.flags;
const { pos, end } = emitNode && emitNode.sourceMapRange || node;
if (node.kind !== SyntaxKind.NotEmittedStatement
&& (emitFlags & EmitFlags.NoLeadingSourceMap) === 0
&& pos >= 0) {
emitPos(skipTrivia(currentSourceText, pos));
}
if (emitFlags & EmitFlags.NoNestedSourceMaps) {
disabled = true;
emitCallback(emitContext, node);
disabled = false;
}
else {
emitCallback(emitContext, node);
}
if (node.kind !== SyntaxKind.NotEmittedStatement
&& (emitFlags & EmitFlags.NoTrailingSourceMap) === 0
&& end >= 0) {
emitPos(end);
}
}
}
function emitStart(range: TextRange) {
emitPos(getStartPos(range));
}
function emitEnd(range: TextRange, stopOverridingEnd?: boolean) {
emitPos(range.end);
stopOverridingSpan = stopOverridingEnd;
}
function changeEmitSourcePos() {
Debug.assert(!modifyLastSourcePos);
modifyLastSourcePos = true;
/**
* Emits a token of a node with possible leading and trailing source maps.
*
* @param node The node containing the token.
* @param token The token to emit.
* @param tokenStartPos The start pos of the token.
* @param emitCallback The callback used to emit the token.
*/
function emitTokenWithSourceMap(node: Node, token: SyntaxKind, tokenPos: number, emitCallback: (token: SyntaxKind, tokenStartPos: number) => number) {
if (disabled) {
return emitCallback(token, tokenPos);
}
const emitNode = node && node.emitNode;
const emitFlags = emitNode && emitNode.flags;
const range = emitNode && emitNode.tokenSourceMapRanges && emitNode.tokenSourceMapRanges[token];
tokenPos = skipTrivia(currentSourceText, range ? range.pos : tokenPos);
if ((emitFlags & EmitFlags.NoTokenLeadingSourceMaps) === 0 && tokenPos >= 0) {
emitPos(tokenPos);
}
tokenPos = emitCallback(token, tokenPos);
if (range) tokenPos = range.end;
if ((emitFlags & EmitFlags.NoTokenTrailingSourceMaps) === 0 && tokenPos >= 0) {
emitPos(tokenPos);
}
return tokenPos;
}
/**
* Set the current source file.
*
* @param sourceFile The source file.
*/
function setSourceFile(sourceFile: SourceFile) {
if (disabled) {
return;
}
currentSourceFile = sourceFile;
currentSourceText = currentSourceFile.text;
// Add the file to tsFilePaths
// If sourceroot option: Use the relative path corresponding to the common directory path
@@ -330,15 +409,22 @@ namespace ts {
sourceMapData.sourceMapSources.push(source);
// The one that can be used from program to get the actual source file
sourceMapData.inputSourceFileNames.push(sourceFile.fileName);
sourceMapData.inputSourceFileNames.push(currentSourceFile.fileName);
if (compilerOptions.inlineSources) {
sourceMapData.sourceMapSourcesContent.push(sourceFile.text);
sourceMapData.sourceMapSourcesContent.push(currentSourceFile.text);
}
}
}
/**
* Gets the text for the source map.
*/
function getText() {
if (disabled) {
return;
}
encodeLastRecordedSourceMapSpan();
return stringify({
@@ -352,7 +438,14 @@ namespace ts {
});
}
/**
* Gets the SourceMappingURL for the source map.
*/
function getSourceMappingURL() {
if (disabled) {
return;
}
if (compilerOptions.inlineSourceMap) {
// Encode the sourceMap into the sourceMap url
const base64SourceMapText = convertToBase64(getText());
+47 -17
View File
@@ -32,6 +32,8 @@ namespace ts {
getMemoryUsage?(): number;
exit(exitCode?: number): void;
realpath?(path: string): string;
/*@internal*/ getEnvironmentVariable(name: string): string;
/*@internal*/ tryEnableSourceMapsForHost?(): void;
}
export interface FileWatcher {
@@ -78,6 +80,7 @@ namespace ts {
watchFile?(path: string, callback: FileWatcherCallback): FileWatcher;
watchDirectory?(path: string, callback: DirectoryWatcherCallback, recursive?: boolean): FileWatcher;
realpath(path: string): string;
getEnvironmentVariable?(name: string): string;
};
export var sys: System = (function() {
@@ -212,6 +215,9 @@ namespace ts {
return shell.CurrentDirectory;
},
getDirectories,
getEnvironmentVariable(name: string) {
return new ActiveXObject("WScript.Shell").ExpandEnvironmentStrings(`%${name}%`);
},
readDirectory,
exit(exitCode?: number): void {
try {
@@ -267,7 +273,7 @@ namespace ts {
}
function addFileWatcherCallback(filePath: string, callback: FileWatcherCallback): void {
(fileWatcherCallbacks[filePath] || (fileWatcherCallbacks[filePath] = [])).push(callback);
multiMapAdd(fileWatcherCallbacks, filePath, callback);
}
function addFile(fileName: string, callback: FileWatcherCallback): WatchedFile {
@@ -283,16 +289,7 @@ namespace ts {
}
function removeFileWatcherCallback(filePath: string, callback: FileWatcherCallback) {
const callbacks = fileWatcherCallbacks[filePath];
if (callbacks) {
const newCallbacks = copyListRemovingItem(callback, callbacks);
if (newCallbacks.length === 0) {
delete fileWatcherCallbacks[filePath];
}
else {
fileWatcherCallbacks[filePath] = newCallbacks;
}
}
multiMapRemove(fileWatcherCallbacks, filePath, callback);
}
function fileEventHandler(eventName: string, relativeFileName: string, baseDirPath: string) {
@@ -432,7 +429,7 @@ namespace ts {
}
function getDirectories(path: string): string[] {
return filter<string>(_fs.readdirSync(path), p => fileSystemEntryExists(combinePaths(path, p), FileSystemEntryKind.Directory));
return filter<string>(_fs.readdirSync(path), dir => fileSystemEntryExists(combinePaths(path, dir), FileSystemEntryKind.Directory));
}
const nodeSystem: System = {
@@ -508,6 +505,9 @@ namespace ts {
return process.cwd();
},
getDirectories,
getEnvironmentVariable(name: string) {
return process.env[name] || "";
},
readDirectory,
getModifiedTime(path) {
try {
@@ -543,6 +543,14 @@ namespace ts {
},
realpath(path: string): string {
return _fs.realpathSync(path);
},
tryEnableSourceMapsForHost() {
try {
require("source-map-support").install();
}
catch (e) {
// Could not enable source maps.
}
}
};
return nodeSystem;
@@ -574,6 +582,7 @@ namespace ts {
getExecutingFilePath: () => ChakraHost.executingFile,
getCurrentDirectory: () => ChakraHost.currentDirectory,
getDirectories: ChakraHost.getDirectories,
getEnvironmentVariable: ChakraHost.getEnvironmentVariable || ((name: string) => ""),
readDirectory: (path: string, extensions?: string[], excludes?: string[], includes?: string[]) => {
const pattern = getFileMatcherPatterns(path, extensions, excludes, includes, !!ChakraHost.useCaseSensitiveFileNames, ChakraHost.currentDirectory);
return ChakraHost.readDirectory(path, extensions, pattern.basePaths, pattern.excludePattern, pattern.includeFilePattern, pattern.includeDirectoryPattern);
@@ -583,19 +592,40 @@ namespace ts {
};
}
function recursiveCreateDirectory(directoryPath: string, sys: System) {
const basePath = getDirectoryPath(directoryPath);
const shouldCreateParent = directoryPath !== basePath && !sys.directoryExists(basePath);
if (shouldCreateParent) {
recursiveCreateDirectory(basePath, sys);
}
if (shouldCreateParent || !sys.directoryExists(directoryPath)) {
sys.createDirectory(directoryPath);
}
}
let sys: System;
if (typeof ChakraHost !== "undefined") {
return getChakraSystem();
sys = getChakraSystem();
}
else if (typeof WScript !== "undefined" && typeof ActiveXObject === "function") {
return getWScriptSystem();
sys = getWScriptSystem();
}
else if (typeof process !== "undefined" && process.nextTick && !process.browser && typeof require !== "undefined") {
// process and process.nextTick checks if current environment is node-like
// process.browser check excludes webpack and browserify
return getNodeSystem();
sys = getNodeSystem();
}
else {
return undefined; // Unsupported host
if (sys) {
// patch writefile to create folder before writing the file
const originalWriteFile = sys.writeFile;
sys.writeFile = function(path, data, writeBom) {
const directoryPath = getDirectoryPath(normalizeSlashes(path));
if (directoryPath && !sys.directoryExists(directoryPath)) {
recursiveCreateDirectory(directoryPath, sys);
}
originalWriteFile.call(sys, path, data, writeBom);
};
}
return sys;
})();
}
+342
View File
@@ -0,0 +1,342 @@
/// <reference path="visitor.ts" />
/// <reference path="transformers/ts.ts" />
/// <reference path="transformers/jsx.ts" />
/// <reference path="transformers/es7.ts" />
/// <reference path="transformers/es6.ts" />
/// <reference path="transformers/generators.ts" />
/// <reference path="transformers/module/module.ts" />
/// <reference path="transformers/module/system.ts" />
/// <reference path="transformers/module/es6.ts" />
/* @internal */
namespace ts {
const moduleTransformerMap = createMap<Transformer>({
[ModuleKind.ES6]: transformES6Module,
[ModuleKind.System]: transformSystemModule,
[ModuleKind.AMD]: transformModule,
[ModuleKind.CommonJS]: transformModule,
[ModuleKind.UMD]: transformModule,
[ModuleKind.None]: transformModule,
});
const enum SyntaxKindFeatureFlags {
Substitution = 1 << 0,
EmitNotifications = 1 << 1,
}
export interface TransformationResult {
/**
* Gets the transformed source files.
*/
transformed: SourceFile[];
/**
* Emits the substitute for a node, if one is available; otherwise, emits the node.
*
* @param emitContext The current emit context.
* @param node The node to substitute.
* @param emitCallback A callback used to emit the node or its substitute.
*/
emitNodeWithSubstitution(emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void): void;
/**
* Emits a node with possible notification.
*
* @param emitContext The current emit context.
* @param node The node to emit.
* @param emitCallback A callback used to emit the node.
*/
emitNodeWithNotification(emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void): void;
}
export interface TransformationContext extends LexicalEnvironment {
getCompilerOptions(): CompilerOptions;
getEmitResolver(): EmitResolver;
getEmitHost(): EmitHost;
/**
* Hoists a function declaration to the containing scope.
*/
hoistFunctionDeclaration(node: FunctionDeclaration): void;
/**
* Hoists a variable declaration to the containing scope.
*/
hoistVariableDeclaration(node: Identifier): void;
/**
* Enables expression substitutions in the pretty printer for the provided SyntaxKind.
*/
enableSubstitution(kind: SyntaxKind): void;
/**
* Determines whether expression substitutions are enabled for the provided node.
*/
isSubstitutionEnabled(node: Node): boolean;
/**
* Hook used by transformers to substitute expressions just before they
* are emitted by the pretty printer.
*/
onSubstituteNode?: (emitContext: EmitContext, node: Node) => Node;
/**
* Enables before/after emit notifications in the pretty printer for the provided
* SyntaxKind.
*/
enableEmitNotification(kind: SyntaxKind): void;
/**
* Determines whether before/after emit notifications should be raised in the pretty
* printer when it emits a node.
*/
isEmitNotificationEnabled(node: Node): boolean;
/**
* Hook used to allow transformers to capture state before or after
* the printer emits a node.
*/
onEmitNode?: (emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void) => void;
}
/* @internal */
export type Transformer = (context: TransformationContext) => (node: SourceFile) => SourceFile;
export function getTransformers(compilerOptions: CompilerOptions) {
const jsx = compilerOptions.jsx;
const languageVersion = getEmitScriptTarget(compilerOptions);
const moduleKind = getEmitModuleKind(compilerOptions);
const transformers: Transformer[] = [];
transformers.push(transformTypeScript);
transformers.push(moduleTransformerMap[moduleKind] || moduleTransformerMap[ModuleKind.None]);
if (jsx === JsxEmit.React) {
transformers.push(transformJsx);
}
transformers.push(transformES7);
if (languageVersion < ScriptTarget.ES6) {
transformers.push(transformES6);
transformers.push(transformGenerators);
}
return transformers;
}
/**
* Transforms an array of SourceFiles by passing them through each transformer.
*
* @param resolver The emit resolver provided by the checker.
* @param host The emit host.
* @param sourceFiles An array of source files
* @param transforms An array of Transformers.
*/
export function transformFiles(resolver: EmitResolver, host: EmitHost, sourceFiles: SourceFile[], transformers: Transformer[]): TransformationResult {
const lexicalEnvironmentVariableDeclarationsStack: VariableDeclaration[][] = [];
const lexicalEnvironmentFunctionDeclarationsStack: FunctionDeclaration[][] = [];
const enabledSyntaxKindFeatures = new Array<SyntaxKindFeatureFlags>(SyntaxKind.Count);
let lexicalEnvironmentStackOffset = 0;
let hoistedVariableDeclarations: VariableDeclaration[];
let hoistedFunctionDeclarations: FunctionDeclaration[];
let lexicalEnvironmentDisabled: boolean;
// The transformation context is provided to each transformer as part of transformer
// initialization.
const context: TransformationContext = {
getCompilerOptions: () => host.getCompilerOptions(),
getEmitResolver: () => resolver,
getEmitHost: () => host,
hoistVariableDeclaration,
hoistFunctionDeclaration,
startLexicalEnvironment,
endLexicalEnvironment,
onSubstituteNode: (emitContext, node) => node,
enableSubstitution,
isSubstitutionEnabled,
onEmitNode: (node, emitContext, emitCallback) => emitCallback(node, emitContext),
enableEmitNotification,
isEmitNotificationEnabled
};
// Chain together and initialize each transformer.
const transformation = chain(...transformers)(context);
// Transform each source file.
const transformed = map(sourceFiles, transformSourceFile);
// Disable modification of the lexical environment.
lexicalEnvironmentDisabled = true;
return {
transformed,
emitNodeWithSubstitution,
emitNodeWithNotification
};
/**
* Transforms a source file.
*
* @param sourceFile The source file to transform.
*/
function transformSourceFile(sourceFile: SourceFile) {
if (isDeclarationFile(sourceFile)) {
return sourceFile;
}
return transformation(sourceFile);
}
/**
* Enables expression substitutions in the pretty printer for the provided SyntaxKind.
*/
function enableSubstitution(kind: SyntaxKind) {
enabledSyntaxKindFeatures[kind] |= SyntaxKindFeatureFlags.Substitution;
}
/**
* Determines whether expression substitutions are enabled for the provided node.
*/
function isSubstitutionEnabled(node: Node) {
return (enabledSyntaxKindFeatures[node.kind] & SyntaxKindFeatureFlags.Substitution) !== 0
&& (getEmitFlags(node) & EmitFlags.NoSubstitution) === 0;
}
/**
* Emits a node with possible substitution.
*
* @param emitContext The current emit context.
* @param node The node to emit.
* @param emitCallback The callback used to emit the node or its substitute.
*/
function emitNodeWithSubstitution(emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void) {
if (node) {
if (isSubstitutionEnabled(node)) {
const substitute = context.onSubstituteNode(emitContext, node);
if (substitute && substitute !== node) {
emitCallback(emitContext, substitute);
return;
}
}
emitCallback(emitContext, node);
}
}
/**
* Enables before/after emit notifications in the pretty printer for the provided SyntaxKind.
*/
function enableEmitNotification(kind: SyntaxKind) {
enabledSyntaxKindFeatures[kind] |= SyntaxKindFeatureFlags.EmitNotifications;
}
/**
* Determines whether before/after emit notifications should be raised in the pretty
* printer when it emits a node.
*/
function isEmitNotificationEnabled(node: Node) {
return (enabledSyntaxKindFeatures[node.kind] & SyntaxKindFeatureFlags.EmitNotifications) !== 0
|| (getEmitFlags(node) & EmitFlags.AdviseOnEmitNode) !== 0;
}
/**
* Emits a node with possible emit notification.
*
* @param emitContext The current emit context.
* @param node The node to emit.
* @param emitCallback The callback used to emit the node.
*/
function emitNodeWithNotification(emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void) {
if (node) {
if (isEmitNotificationEnabled(node)) {
context.onEmitNode(emitContext, node, emitCallback);
}
else {
emitCallback(emitContext, node);
}
}
}
/**
* Records a hoisted variable declaration for the provided name within a lexical environment.
*/
function hoistVariableDeclaration(name: Identifier): void {
Debug.assert(!lexicalEnvironmentDisabled, "Cannot modify the lexical environment during the print phase.");
const decl = createVariableDeclaration(name);
if (!hoistedVariableDeclarations) {
hoistedVariableDeclarations = [decl];
}
else {
hoistedVariableDeclarations.push(decl);
}
}
/**
* Records a hoisted function declaration within a lexical environment.
*/
function hoistFunctionDeclaration(func: FunctionDeclaration): void {
Debug.assert(!lexicalEnvironmentDisabled, "Cannot modify the lexical environment during the print phase.");
if (!hoistedFunctionDeclarations) {
hoistedFunctionDeclarations = [func];
}
else {
hoistedFunctionDeclarations.push(func);
}
}
/**
* Starts a new lexical environment. Any existing hoisted variable or function declarations
* are pushed onto a stack, and the related storage variables are reset.
*/
function startLexicalEnvironment(): void {
Debug.assert(!lexicalEnvironmentDisabled, "Cannot start a lexical environment during the print phase.");
// Save the current lexical environment. Rather than resizing the array we adjust the
// stack size variable. This allows us to reuse existing array slots we've
// already allocated between transformations to avoid allocation and GC overhead during
// transformation.
lexicalEnvironmentVariableDeclarationsStack[lexicalEnvironmentStackOffset] = hoistedVariableDeclarations;
lexicalEnvironmentFunctionDeclarationsStack[lexicalEnvironmentStackOffset] = hoistedFunctionDeclarations;
lexicalEnvironmentStackOffset++;
hoistedVariableDeclarations = undefined;
hoistedFunctionDeclarations = undefined;
}
/**
* Ends a lexical environment. The previous set of hoisted declarations are restored and
* any hoisted declarations added in this environment are returned.
*/
function endLexicalEnvironment(): Statement[] {
Debug.assert(!lexicalEnvironmentDisabled, "Cannot end a lexical environment during the print phase.");
let statements: Statement[];
if (hoistedVariableDeclarations || hoistedFunctionDeclarations) {
if (hoistedFunctionDeclarations) {
statements = [...hoistedFunctionDeclarations];
}
if (hoistedVariableDeclarations) {
const statement = createVariableStatement(
/*modifiers*/ undefined,
createVariableDeclarationList(hoistedVariableDeclarations)
);
if (!statements) {
statements = [statement];
}
else {
statements.push(statement);
}
}
}
// Restore the previous lexical environment.
lexicalEnvironmentStackOffset--;
hoistedVariableDeclarations = lexicalEnvironmentVariableDeclarationsStack[lexicalEnvironmentStackOffset];
hoistedFunctionDeclarations = lexicalEnvironmentFunctionDeclarationsStack[lexicalEnvironmentStackOffset];
return statements;
}
}
}
+445
View File
@@ -0,0 +1,445 @@
/// <reference path="../factory.ts" />
/// <reference path="../visitor.ts" />
/*@internal*/
namespace ts {
/**
* Flattens a destructuring assignment expression.
*
* @param root The destructuring assignment expression.
* @param needsValue Indicates whether the value from the right-hand-side of the
* destructuring assignment is needed as part of a larger expression.
* @param recordTempVariable A callback used to record new temporary variables.
* @param visitor An optional visitor to use to visit expressions.
*/
export function flattenDestructuringAssignment(
context: TransformationContext,
node: BinaryExpression,
needsValue: boolean,
recordTempVariable: (node: Identifier) => void,
visitor?: (node: Node) => VisitResult<Node>): Expression {
if (isEmptyObjectLiteralOrArrayLiteral(node.left)) {
const right = node.right;
if (isDestructuringAssignment(right)) {
return flattenDestructuringAssignment(context, right, needsValue, recordTempVariable, visitor);
}
else {
return node.right;
}
}
let location: TextRange = node;
let value = node.right;
const expressions: Expression[] = [];
if (needsValue) {
// If the right-hand value of the destructuring assignment needs to be preserved (as
// is the case when the destructuring assignmen) is part of a larger expression),
// then we need to cache the right-hand value.
//
// The source map location for the assignment should point to the entire binary
// expression.
value = ensureIdentifier(value, /*reuseIdentifierExpressions*/ true, location, emitTempVariableAssignment, visitor);
}
else if (nodeIsSynthesized(node)) {
// Generally, the source map location for a destructuring assignment is the root
// expression.
//
// However, if the root expression is synthesized (as in the case
// of the initializer when transforming a ForOfStatement), then the source map
// location should point to the right-hand value of the expression.
location = value;
}
flattenDestructuring(context, node, value, location, emitAssignment, emitTempVariableAssignment, visitor);
if (needsValue) {
expressions.push(value);
}
const expression = inlineExpressions(expressions);
aggregateTransformFlags(expression);
return expression;
function emitAssignment(name: Identifier, value: Expression, location: TextRange) {
const expression = createAssignment(name, value, location);
// NOTE: this completely disables source maps, but aligns with the behavior of
// `emitAssignment` in the old emitter.
setEmitFlags(expression, EmitFlags.NoNestedSourceMaps);
aggregateTransformFlags(expression);
expressions.push(expression);
}
function emitTempVariableAssignment(value: Expression, location: TextRange) {
const name = createTempVariable(recordTempVariable);
emitAssignment(name, value, location);
return name;
}
}
/**
* Flattens binding patterns in a parameter declaration.
*
* @param node The ParameterDeclaration to flatten.
* @param value The rhs value for the binding pattern.
* @param visitor An optional visitor to use to visit expressions.
*/
export function flattenParameterDestructuring(
context: TransformationContext,
node: ParameterDeclaration,
value: Expression,
visitor?: (node: Node) => VisitResult<Node>) {
const declarations: VariableDeclaration[] = [];
flattenDestructuring(context, node, value, node, emitAssignment, emitTempVariableAssignment, visitor);
return declarations;
function emitAssignment(name: Identifier, value: Expression, location: TextRange) {
const declaration = createVariableDeclaration(name, /*type*/ undefined, value, location);
// NOTE: this completely disables source maps, but aligns with the behavior of
// `emitAssignment` in the old emitter.
setEmitFlags(declaration, EmitFlags.NoNestedSourceMaps);
aggregateTransformFlags(declaration);
declarations.push(declaration);
}
function emitTempVariableAssignment(value: Expression, location: TextRange) {
const name = createTempVariable(/*recordTempVariable*/ undefined);
emitAssignment(name, value, location);
return name;
}
}
/**
* Flattens binding patterns in a variable declaration.
*
* @param node The VariableDeclaration to flatten.
* @param value An optional rhs value for the binding pattern.
* @param visitor An optional visitor to use to visit expressions.
*/
export function flattenVariableDestructuring(
context: TransformationContext,
node: VariableDeclaration,
value?: Expression,
visitor?: (node: Node) => VisitResult<Node>,
recordTempVariable?: (node: Identifier) => void) {
const declarations: VariableDeclaration[] = [];
let pendingAssignments: Expression[];
flattenDestructuring(context, node, value, node, emitAssignment, emitTempVariableAssignment, visitor);
return declarations;
function emitAssignment(name: Identifier, value: Expression, location: TextRange, original: Node) {
if (pendingAssignments) {
pendingAssignments.push(value);
value = inlineExpressions(pendingAssignments);
pendingAssignments = undefined;
}
const declaration = createVariableDeclaration(name, /*type*/ undefined, value, location);
declaration.original = original;
// NOTE: this completely disables source maps, but aligns with the behavior of
// `emitAssignment` in the old emitter.
setEmitFlags(declaration, EmitFlags.NoNestedSourceMaps);
declarations.push(declaration);
aggregateTransformFlags(declaration);
}
function emitTempVariableAssignment(value: Expression, location: TextRange) {
const name = createTempVariable(recordTempVariable);
if (recordTempVariable) {
const assignment = createAssignment(name, value, location);
if (pendingAssignments) {
pendingAssignments.push(assignment);
}
else {
pendingAssignments = [assignment];
}
}
else {
emitAssignment(name, value, location, /*original*/ undefined);
}
return name;
}
}
/**
* Flattens binding patterns in a variable declaration and transforms them into an expression.
*
* @param node The VariableDeclaration to flatten.
* @param recordTempVariable A callback used to record new temporary variables.
* @param nameSubstitution An optional callback used to substitute binding names.
* @param visitor An optional visitor to use to visit expressions.
*/
export function flattenVariableDestructuringToExpression(
context: TransformationContext,
node: VariableDeclaration,
recordTempVariable: (name: Identifier) => void,
nameSubstitution?: (name: Identifier) => Expression,
visitor?: (node: Node) => VisitResult<Node>) {
const pendingAssignments: Expression[] = [];
flattenDestructuring(context, node, /*value*/ undefined, node, emitAssignment, emitTempVariableAssignment, visitor);
const expression = inlineExpressions(pendingAssignments);
aggregateTransformFlags(expression);
return expression;
function emitAssignment(name: Identifier, value: Expression, location: TextRange, original: Node) {
const left = nameSubstitution && nameSubstitution(name) || name;
emitPendingAssignment(left, value, location, original);
}
function emitTempVariableAssignment(value: Expression, location: TextRange) {
const name = createTempVariable(recordTempVariable);
emitPendingAssignment(name, value, location, /*original*/ undefined);
return name;
}
function emitPendingAssignment(name: Expression, value: Expression, location: TextRange, original: Node) {
const expression = createAssignment(name, value, location);
expression.original = original;
// NOTE: this completely disables source maps, but aligns with the behavior of
// `emitAssignment` in the old emitter.
setEmitFlags(expression, EmitFlags.NoNestedSourceMaps);
pendingAssignments.push(expression);
return expression;
}
}
function flattenDestructuring(
context: TransformationContext,
root: BindingElement | BinaryExpression,
value: Expression,
location: TextRange,
emitAssignment: (name: Identifier, value: Expression, location: TextRange, original: Node) => void,
emitTempVariableAssignment: (value: Expression, location: TextRange) => Identifier,
visitor?: (node: Node) => VisitResult<Node>) {
if (value && visitor) {
value = visitNode(value, visitor, isExpression);
}
if (isBinaryExpression(root)) {
emitDestructuringAssignment(root.left, value, location);
}
else {
emitBindingElement(root, value);
}
function emitDestructuringAssignment(bindingTarget: Expression | ShorthandPropertyAssignment, value: Expression, location: TextRange) {
// When emitting target = value use source map node to highlight, including any temporary assignments needed for this
let target: Expression;
if (isShorthandPropertyAssignment(bindingTarget)) {
const initializer = visitor
? visitNode(bindingTarget.objectAssignmentInitializer, visitor, isExpression)
: bindingTarget.objectAssignmentInitializer;
if (initializer) {
value = createDefaultValueCheck(value, initializer, location);
}
target = bindingTarget.name;
}
else if (isBinaryExpression(bindingTarget) && bindingTarget.operatorToken.kind === SyntaxKind.EqualsToken) {
const initializer = visitor
? visitNode(bindingTarget.right, visitor, isExpression)
: bindingTarget.right;
value = createDefaultValueCheck(value, initializer, location);
target = bindingTarget.left;
}
else {
target = bindingTarget;
}
if (target.kind === SyntaxKind.ObjectLiteralExpression) {
emitObjectLiteralAssignment(<ObjectLiteralExpression>target, value, location);
}
else if (target.kind === SyntaxKind.ArrayLiteralExpression) {
emitArrayLiteralAssignment(<ArrayLiteralExpression>target, value, location);
}
else {
const name = getMutableClone(<Identifier>target);
setSourceMapRange(name, target);
setCommentRange(name, target);
emitAssignment(name, value, location, /*original*/ undefined);
}
}
function emitObjectLiteralAssignment(target: ObjectLiteralExpression, value: Expression, location: TextRange) {
const properties = target.properties;
if (properties.length !== 1) {
// For anything but a single element destructuring we need to generate a temporary
// to ensure value is evaluated exactly once.
// When doing so we want to hightlight the passed in source map node since thats the one needing this temp assignment
value = ensureIdentifier(value, /*reuseIdentifierExpressions*/ true, location, emitTempVariableAssignment);
}
for (const p of properties) {
if (p.kind === SyntaxKind.PropertyAssignment || p.kind === SyntaxKind.ShorthandPropertyAssignment) {
const propName = <Identifier | LiteralExpression>(<PropertyAssignment>p).name;
const target = p.kind === SyntaxKind.ShorthandPropertyAssignment ? <ShorthandPropertyAssignment>p : (<PropertyAssignment>p).initializer || propName;
// Assignment for target = value.propName should highligh whole property, hence use p as source map node
emitDestructuringAssignment(target, createDestructuringPropertyAccess(value, propName), p);
}
}
}
function emitArrayLiteralAssignment(target: ArrayLiteralExpression, value: Expression, location: TextRange) {
const elements = target.elements;
const numElements = elements.length;
if (numElements !== 1) {
// For anything but a single element destructuring we need to generate a temporary
// to ensure value is evaluated exactly once.
// When doing so we want to hightlight the passed in source map node since thats the one needing this temp assignment
value = ensureIdentifier(value, /*reuseIdentifierExpressions*/ true, location, emitTempVariableAssignment);
}
for (let i = 0; i < numElements; i++) {
const e = elements[i];
if (e.kind !== SyntaxKind.OmittedExpression) {
// Assignment for target = value.propName should highligh whole property, hence use e as source map node
if (e.kind !== SyntaxKind.SpreadElementExpression) {
emitDestructuringAssignment(e, createElementAccess(value, createLiteral(i)), e);
}
else if (i === numElements - 1) {
emitDestructuringAssignment((<SpreadElementExpression>e).expression, createArraySlice(value, i), e);
}
}
}
}
function emitBindingElement(target: BindingElement, value: Expression) {
// Any temporary assignments needed to emit target = value should point to target
const initializer = visitor ? visitNode(target.initializer, visitor, isExpression) : target.initializer;
if (initializer) {
// Combine value and initializer
value = value ? createDefaultValueCheck(value, initializer, target) : initializer;
}
else if (!value) {
// Use 'void 0' in absence of value and initializer
value = createVoidZero();
}
const name = target.name;
if (isBindingPattern(name)) {
const elements = name.elements;
const numElements = elements.length;
if (numElements !== 1) {
// For anything other than a single-element destructuring we need to generate a temporary
// to ensure value is evaluated exactly once. Additionally, if we have zero elements
// we need to emit *something* to ensure that in case a 'var' keyword was already emitted,
// so in that case, we'll intentionally create that temporary.
value = ensureIdentifier(value, /*reuseIdentifierExpressions*/ numElements !== 0, target, emitTempVariableAssignment);
}
for (let i = 0; i < numElements; i++) {
const element = elements[i];
if (isOmittedExpression(element)) {
continue;
}
else if (name.kind === SyntaxKind.ObjectBindingPattern) {
// Rewrite element to a declaration with an initializer that fetches property
const propName = element.propertyName || <Identifier>element.name;
emitBindingElement(element, createDestructuringPropertyAccess(value, propName));
}
else {
if (!element.dotDotDotToken) {
// Rewrite element to a declaration that accesses array element at index i
emitBindingElement(element, createElementAccess(value, i));
}
else if (i === numElements - 1) {
emitBindingElement(element, createArraySlice(value, i));
}
}
}
}
else {
emitAssignment(name, value, target, target);
}
}
function createDefaultValueCheck(value: Expression, defaultValue: Expression, location: TextRange): Expression {
value = ensureIdentifier(value, /*reuseIdentifierExpressions*/ true, location, emitTempVariableAssignment);
return createConditional(
createStrictEquality(value, createVoidZero()),
createToken(SyntaxKind.QuestionToken),
defaultValue,
createToken(SyntaxKind.ColonToken),
value
);
}
/**
* Creates either a PropertyAccessExpression or an ElementAccessExpression for the
* right-hand side of a transformed destructuring assignment.
*
* @param expression The right-hand expression that is the source of the property.
* @param propertyName The destructuring property name.
*/
function createDestructuringPropertyAccess(expression: Expression, propertyName: PropertyName): LeftHandSideExpression {
if (isComputedPropertyName(propertyName)) {
return createElementAccess(
expression,
ensureIdentifier(propertyName.expression, /*reuseIdentifierExpressions*/ false, /*location*/ propertyName, emitTempVariableAssignment)
);
}
else if (isLiteralExpression(propertyName)) {
const clone = getSynthesizedClone(propertyName);
clone.text = unescapeIdentifier(clone.text);
return createElementAccess(expression, clone);
}
else {
if (isGeneratedIdentifier(propertyName)) {
const clone = getSynthesizedClone(propertyName);
clone.text = unescapeIdentifier(clone.text);
return createPropertyAccess(expression, clone);
}
else {
return createPropertyAccess(expression, createIdentifier(unescapeIdentifier(propertyName.text)));
}
}
}
}
/**
* Ensures that there exists a declared identifier whose value holds the given expression.
* This function is useful to ensure that the expression's value can be read from in subsequent expressions.
* Unless 'reuseIdentifierExpressions' is false, 'value' will be returned if it is just an identifier.
*
* @param value the expression whose value needs to be bound.
* @param reuseIdentifierExpressions true if identifier expressions can simply be returned;
* false if it is necessary to always emit an identifier.
* @param location The location to use for source maps and comments.
* @param emitTempVariableAssignment A callback used to emit a temporary variable.
* @param visitor An optional callback used to visit the value.
*/
function ensureIdentifier(
value: Expression,
reuseIdentifierExpressions: boolean,
location: TextRange,
emitTempVariableAssignment: (value: Expression, location: TextRange) => Identifier,
visitor?: (node: Node) => VisitResult<Node>) {
if (isIdentifier(value) && reuseIdentifierExpressions) {
return value;
}
else {
if (visitor) {
value = visitNode(value, visitor, isExpression);
}
return emitTempVariableAssignment(value, location);
}
}
}
File diff suppressed because it is too large Load Diff
+101
View File
@@ -0,0 +1,101 @@
/// <reference path="../factory.ts" />
/// <reference path="../visitor.ts" />
/*@internal*/
namespace ts {
export function transformES7(context: TransformationContext) {
const { hoistVariableDeclaration } = context;
return transformSourceFile;
function transformSourceFile(node: SourceFile) {
if (isDeclarationFile(node)) {
return node;
}
return visitEachChild(node, visitor, context);
}
function visitor(node: Node): VisitResult<Node> {
if (node.transformFlags & TransformFlags.ES7) {
return visitorWorker(node);
}
else if (node.transformFlags & TransformFlags.ContainsES7) {
return visitEachChild(node, visitor, context);
}
else {
return node;
}
}
function visitorWorker(node: Node): VisitResult<Node> {
switch (node.kind) {
case SyntaxKind.BinaryExpression:
return visitBinaryExpression(<BinaryExpression>node);
default:
Debug.failBadSyntaxKind(node);
return visitEachChild(node, visitor, context);
}
}
function visitBinaryExpression(node: BinaryExpression): Expression {
// We are here because ES7 adds support for the exponentiation operator.
const left = visitNode(node.left, visitor, isExpression);
const right = visitNode(node.right, visitor, isExpression);
if (node.operatorToken.kind === SyntaxKind.AsteriskAsteriskEqualsToken) {
let target: Expression;
let value: Expression;
if (isElementAccessExpression(left)) {
// Transforms `a[x] **= b` into `(_a = a)[_x = x] = Math.pow(_a[_x], b)`
const expressionTemp = createTempVariable(hoistVariableDeclaration);
const argumentExpressionTemp = createTempVariable(hoistVariableDeclaration);
target = createElementAccess(
createAssignment(expressionTemp, left.expression, /*location*/ left.expression),
createAssignment(argumentExpressionTemp, left.argumentExpression, /*location*/ left.argumentExpression),
/*location*/ left
);
value = createElementAccess(
expressionTemp,
argumentExpressionTemp,
/*location*/ left
);
}
else if (isPropertyAccessExpression(left)) {
// Transforms `a.x **= b` into `(_a = a).x = Math.pow(_a.x, b)`
const expressionTemp = createTempVariable(hoistVariableDeclaration);
target = createPropertyAccess(
createAssignment(expressionTemp, left.expression, /*location*/ left.expression),
left.name,
/*location*/ left
);
value = createPropertyAccess(
expressionTemp,
left.name,
/*location*/ left
);
}
else {
// Transforms `a **= b` into `a = Math.pow(a, b)`
target = left;
value = left;
}
return createAssignment(target, createMathPow(value, right, /*location*/ node), /*location*/ node);
}
else if (node.operatorToken.kind === SyntaxKind.AsteriskAsteriskToken) {
// Transforms `a ** b` into `Math.pow(a, b)`
return createMathPow(left, right, /*location*/ node);
}
else {
Debug.failBadSyntaxKind(node);
return visitEachChild(node, visitor, context);
}
}
}
}
File diff suppressed because it is too large Load Diff
+535
View File
@@ -0,0 +1,535 @@
/// <reference path="../factory.ts" />
/// <reference path="../visitor.ts" />
/*@internal*/
namespace ts {
const entities: Map<number> = createEntitiesMap();
export function transformJsx(context: TransformationContext) {
const compilerOptions = context.getCompilerOptions();
let currentSourceFile: SourceFile;
return transformSourceFile;
/**
* Transform JSX-specific syntax in a SourceFile.
*
* @param node A SourceFile node.
*/
function transformSourceFile(node: SourceFile) {
if (isDeclarationFile(node)) {
return node;
}
currentSourceFile = node;
node = visitEachChild(node, visitor, context);
currentSourceFile = undefined;
return node;
}
function visitor(node: Node): VisitResult<Node> {
if (node.transformFlags & TransformFlags.Jsx) {
return visitorWorker(node);
}
else if (node.transformFlags & TransformFlags.ContainsJsx) {
return visitEachChild(node, visitor, context);
}
else {
return node;
}
}
function visitorWorker(node: Node): VisitResult<Node> {
switch (node.kind) {
case SyntaxKind.JsxElement:
return visitJsxElement(<JsxElement>node, /*isChild*/ false);
case SyntaxKind.JsxSelfClosingElement:
return visitJsxSelfClosingElement(<JsxSelfClosingElement>node, /*isChild*/ false);
case SyntaxKind.JsxExpression:
return visitJsxExpression(<JsxExpression>node);
default:
Debug.failBadSyntaxKind(node);
return undefined;
}
}
function transformJsxChildToExpression(node: JsxChild): Expression {
switch (node.kind) {
case SyntaxKind.JsxText:
return visitJsxText(<JsxText>node);
case SyntaxKind.JsxExpression:
return visitJsxExpression(<JsxExpression>node);
case SyntaxKind.JsxElement:
return visitJsxElement(<JsxElement>node, /*isChild*/ true);
case SyntaxKind.JsxSelfClosingElement:
return visitJsxSelfClosingElement(<JsxSelfClosingElement>node, /*isChild*/ true);
default:
Debug.failBadSyntaxKind(node);
return undefined;
}
}
function visitJsxElement(node: JsxElement, isChild: boolean) {
return visitJsxOpeningLikeElement(node.openingElement, node.children, isChild, /*location*/ node);
}
function visitJsxSelfClosingElement(node: JsxSelfClosingElement, isChild: boolean) {
return visitJsxOpeningLikeElement(node, /*children*/ undefined, isChild, /*location*/ node);
}
function visitJsxOpeningLikeElement(node: JsxOpeningLikeElement, children: JsxChild[], isChild: boolean, location: TextRange) {
const tagName = getTagName(node);
let objectProperties: Expression;
const attrs = node.attributes;
if (attrs.length === 0) {
// When there are no attributes, React wants "null"
objectProperties = createNull();
}
else {
// Map spans of JsxAttribute nodes into object literals and spans
// of JsxSpreadAttribute nodes into expressions.
const segments = flatten(
spanMap(attrs, isJsxSpreadAttribute, (attrs, isSpread) => isSpread
? map(attrs, transformJsxSpreadAttributeToExpression)
: createObjectLiteral(map(attrs, transformJsxAttributeToObjectLiteralElement))
)
);
if (isJsxSpreadAttribute(attrs[0])) {
// We must always emit at least one object literal before a spread
// argument.
segments.unshift(createObjectLiteral());
}
// Either emit one big object literal (no spread attribs), or
// a call to the __assign helper.
objectProperties = singleOrUndefined(segments)
|| createAssignHelper(currentSourceFile.externalHelpersModuleName, segments);
}
const element = createReactCreateElement(
compilerOptions.reactNamespace,
tagName,
objectProperties,
filter(map(children, transformJsxChildToExpression), isDefined),
node,
location
);
if (isChild) {
startOnNewLine(element);
}
return element;
}
function transformJsxSpreadAttributeToExpression(node: JsxSpreadAttribute) {
return visitNode(node.expression, visitor, isExpression);
}
function transformJsxAttributeToObjectLiteralElement(node: JsxAttribute) {
const name = getAttributeName(node);
const expression = transformJsxAttributeInitializer(node.initializer);
return createPropertyAssignment(name, expression);
}
function transformJsxAttributeInitializer(node: StringLiteral | JsxExpression) {
if (node === undefined) {
return createLiteral(true);
}
else if (node.kind === SyntaxKind.StringLiteral) {
const decoded = tryDecodeEntities((<StringLiteral>node).text);
return decoded ? createLiteral(decoded, /*location*/ node) : node;
}
else if (node.kind === SyntaxKind.JsxExpression) {
return visitJsxExpression(<JsxExpression>node);
}
else {
Debug.failBadSyntaxKind(node);
}
}
function visitJsxText(node: JsxText) {
const text = getTextOfNode(node, /*includeTrivia*/ true);
let parts: Expression[];
let firstNonWhitespace = 0;
let lastNonWhitespace = -1;
// JSX trims whitespace at the end and beginning of lines, except that the
// start/end of a tag is considered a start/end of a line only if that line is
// on the same line as the closing tag. See examples in
// tests/cases/conformance/jsx/tsxReactEmitWhitespace.tsx
for (let i = 0; i < text.length; i++) {
const c = text.charCodeAt(i);
if (isLineBreak(c)) {
if (firstNonWhitespace !== -1 && (lastNonWhitespace - firstNonWhitespace + 1 > 0)) {
const part = text.substr(firstNonWhitespace, lastNonWhitespace - firstNonWhitespace + 1);
if (!parts) {
parts = [];
}
// We do not escape the string here as that is handled by the printer
// when it emits the literal. We do, however, need to decode JSX entities.
parts.push(createLiteral(decodeEntities(part)));
}
firstNonWhitespace = -1;
}
else if (!isWhiteSpace(c)) {
lastNonWhitespace = i;
if (firstNonWhitespace === -1) {
firstNonWhitespace = i;
}
}
}
if (firstNonWhitespace !== -1) {
const part = text.substr(firstNonWhitespace);
if (!parts) {
parts = [];
}
// We do not escape the string here as that is handled by the printer
// when it emits the literal. We do, however, need to decode JSX entities.
parts.push(createLiteral(decodeEntities(part)));
}
if (parts) {
return reduceLeft(parts, aggregateJsxTextParts);
}
return undefined;
}
/**
* Aggregates two expressions by interpolating them with a whitespace literal.
*/
function aggregateJsxTextParts(left: Expression, right: Expression) {
return createAdd(createAdd(left, createLiteral(" ")), right);
}
/**
* Replace entities like "&nbsp;", "&#123;", and "&#xDEADBEEF;" with the characters they encode.
* See https://en.wikipedia.org/wiki/List_of_XML_and_HTML_character_entity_references
*/
function decodeEntities(text: string): string {
return text.replace(/&((#((\d+)|x([\da-fA-F]+)))|(\w+));/g, (match, _all, _number, _digits, decimal, hex, word) => {
if (decimal) {
return String.fromCharCode(parseInt(decimal, 10));
}
else if (hex) {
return String.fromCharCode(parseInt(hex, 16));
}
else {
const ch = entities[word];
// If this is not a valid entity, then just use `match` (replace it with itself, i.e. don't replace)
return ch ? String.fromCharCode(ch) : match;
}
});
}
/** Like `decodeEntities` but returns `undefined` if there were no entities to decode. */
function tryDecodeEntities(text: string): string | undefined {
const decoded = decodeEntities(text);
return decoded === text ? undefined : decoded;
}
function getTagName(node: JsxElement | JsxOpeningLikeElement): Expression {
if (node.kind === SyntaxKind.JsxElement) {
return getTagName((<JsxElement>node).openingElement);
}
else {
const name = (<JsxOpeningLikeElement>node).tagName;
if (isIdentifier(name) && isIntrinsicJsxName(name.text)) {
return createLiteral(name.text);
}
else {
return createExpressionFromEntityName(name);
}
}
}
/**
* Emit an attribute name, which is quoted if it needs to be quoted. Because
* these emit into an object literal property name, we don't need to be worried
* about keywords, just non-identifier characters
*/
function getAttributeName(node: JsxAttribute): StringLiteral | Identifier {
const name = node.name;
if (/^[A-Za-z_]\w*$/.test(name.text)) {
return name;
}
else {
return createLiteral(name.text);
}
}
function visitJsxExpression(node: JsxExpression) {
return visitNode(node.expression, visitor, isExpression);
}
}
function createEntitiesMap(): Map<number> {
return createMap<number>({
"quot": 0x0022,
"amp": 0x0026,
"apos": 0x0027,
"lt": 0x003C,
"gt": 0x003E,
"nbsp": 0x00A0,
"iexcl": 0x00A1,
"cent": 0x00A2,
"pound": 0x00A3,
"curren": 0x00A4,
"yen": 0x00A5,
"brvbar": 0x00A6,
"sect": 0x00A7,
"uml": 0x00A8,
"copy": 0x00A9,
"ordf": 0x00AA,
"laquo": 0x00AB,
"not": 0x00AC,
"shy": 0x00AD,
"reg": 0x00AE,
"macr": 0x00AF,
"deg": 0x00B0,
"plusmn": 0x00B1,
"sup2": 0x00B2,
"sup3": 0x00B3,
"acute": 0x00B4,
"micro": 0x00B5,
"para": 0x00B6,
"middot": 0x00B7,
"cedil": 0x00B8,
"sup1": 0x00B9,
"ordm": 0x00BA,
"raquo": 0x00BB,
"frac14": 0x00BC,
"frac12": 0x00BD,
"frac34": 0x00BE,
"iquest": 0x00BF,
"Agrave": 0x00C0,
"Aacute": 0x00C1,
"Acirc": 0x00C2,
"Atilde": 0x00C3,
"Auml": 0x00C4,
"Aring": 0x00C5,
"AElig": 0x00C6,
"Ccedil": 0x00C7,
"Egrave": 0x00C8,
"Eacute": 0x00C9,
"Ecirc": 0x00CA,
"Euml": 0x00CB,
"Igrave": 0x00CC,
"Iacute": 0x00CD,
"Icirc": 0x00CE,
"Iuml": 0x00CF,
"ETH": 0x00D0,
"Ntilde": 0x00D1,
"Ograve": 0x00D2,
"Oacute": 0x00D3,
"Ocirc": 0x00D4,
"Otilde": 0x00D5,
"Ouml": 0x00D6,
"times": 0x00D7,
"Oslash": 0x00D8,
"Ugrave": 0x00D9,
"Uacute": 0x00DA,
"Ucirc": 0x00DB,
"Uuml": 0x00DC,
"Yacute": 0x00DD,
"THORN": 0x00DE,
"szlig": 0x00DF,
"agrave": 0x00E0,
"aacute": 0x00E1,
"acirc": 0x00E2,
"atilde": 0x00E3,
"auml": 0x00E4,
"aring": 0x00E5,
"aelig": 0x00E6,
"ccedil": 0x00E7,
"egrave": 0x00E8,
"eacute": 0x00E9,
"ecirc": 0x00EA,
"euml": 0x00EB,
"igrave": 0x00EC,
"iacute": 0x00ED,
"icirc": 0x00EE,
"iuml": 0x00EF,
"eth": 0x00F0,
"ntilde": 0x00F1,
"ograve": 0x00F2,
"oacute": 0x00F3,
"ocirc": 0x00F4,
"otilde": 0x00F5,
"ouml": 0x00F6,
"divide": 0x00F7,
"oslash": 0x00F8,
"ugrave": 0x00F9,
"uacute": 0x00FA,
"ucirc": 0x00FB,
"uuml": 0x00FC,
"yacute": 0x00FD,
"thorn": 0x00FE,
"yuml": 0x00FF,
"OElig": 0x0152,
"oelig": 0x0153,
"Scaron": 0x0160,
"scaron": 0x0161,
"Yuml": 0x0178,
"fnof": 0x0192,
"circ": 0x02C6,
"tilde": 0x02DC,
"Alpha": 0x0391,
"Beta": 0x0392,
"Gamma": 0x0393,
"Delta": 0x0394,
"Epsilon": 0x0395,
"Zeta": 0x0396,
"Eta": 0x0397,
"Theta": 0x0398,
"Iota": 0x0399,
"Kappa": 0x039A,
"Lambda": 0x039B,
"Mu": 0x039C,
"Nu": 0x039D,
"Xi": 0x039E,
"Omicron": 0x039F,
"Pi": 0x03A0,
"Rho": 0x03A1,
"Sigma": 0x03A3,
"Tau": 0x03A4,
"Upsilon": 0x03A5,
"Phi": 0x03A6,
"Chi": 0x03A7,
"Psi": 0x03A8,
"Omega": 0x03A9,
"alpha": 0x03B1,
"beta": 0x03B2,
"gamma": 0x03B3,
"delta": 0x03B4,
"epsilon": 0x03B5,
"zeta": 0x03B6,
"eta": 0x03B7,
"theta": 0x03B8,
"iota": 0x03B9,
"kappa": 0x03BA,
"lambda": 0x03BB,
"mu": 0x03BC,
"nu": 0x03BD,
"xi": 0x03BE,
"omicron": 0x03BF,
"pi": 0x03C0,
"rho": 0x03C1,
"sigmaf": 0x03C2,
"sigma": 0x03C3,
"tau": 0x03C4,
"upsilon": 0x03C5,
"phi": 0x03C6,
"chi": 0x03C7,
"psi": 0x03C8,
"omega": 0x03C9,
"thetasym": 0x03D1,
"upsih": 0x03D2,
"piv": 0x03D6,
"ensp": 0x2002,
"emsp": 0x2003,
"thinsp": 0x2009,
"zwnj": 0x200C,
"zwj": 0x200D,
"lrm": 0x200E,
"rlm": 0x200F,
"ndash": 0x2013,
"mdash": 0x2014,
"lsquo": 0x2018,
"rsquo": 0x2019,
"sbquo": 0x201A,
"ldquo": 0x201C,
"rdquo": 0x201D,
"bdquo": 0x201E,
"dagger": 0x2020,
"Dagger": 0x2021,
"bull": 0x2022,
"hellip": 0x2026,
"permil": 0x2030,
"prime": 0x2032,
"Prime": 0x2033,
"lsaquo": 0x2039,
"rsaquo": 0x203A,
"oline": 0x203E,
"frasl": 0x2044,
"euro": 0x20AC,
"image": 0x2111,
"weierp": 0x2118,
"real": 0x211C,
"trade": 0x2122,
"alefsym": 0x2135,
"larr": 0x2190,
"uarr": 0x2191,
"rarr": 0x2192,
"darr": 0x2193,
"harr": 0x2194,
"crarr": 0x21B5,
"lArr": 0x21D0,
"uArr": 0x21D1,
"rArr": 0x21D2,
"dArr": 0x21D3,
"hArr": 0x21D4,
"forall": 0x2200,
"part": 0x2202,
"exist": 0x2203,
"empty": 0x2205,
"nabla": 0x2207,
"isin": 0x2208,
"notin": 0x2209,
"ni": 0x220B,
"prod": 0x220F,
"sum": 0x2211,
"minus": 0x2212,
"lowast": 0x2217,
"radic": 0x221A,
"prop": 0x221D,
"infin": 0x221E,
"ang": 0x2220,
"and": 0x2227,
"or": 0x2228,
"cap": 0x2229,
"cup": 0x222A,
"int": 0x222B,
"there4": 0x2234,
"sim": 0x223C,
"cong": 0x2245,
"asymp": 0x2248,
"ne": 0x2260,
"equiv": 0x2261,
"le": 0x2264,
"ge": 0x2265,
"sub": 0x2282,
"sup": 0x2283,
"nsub": 0x2284,
"sube": 0x2286,
"supe": 0x2287,
"oplus": 0x2295,
"otimes": 0x2297,
"perp": 0x22A5,
"sdot": 0x22C5,
"lceil": 0x2308,
"rceil": 0x2309,
"lfloor": 0x230A,
"rfloor": 0x230B,
"lang": 0x2329,
"rang": 0x232A,
"loz": 0x25CA,
"spades": 0x2660,
"clubs": 0x2663,
"hearts": 0x2665,
"diams": 0x2666
});
}
}
+144
View File
@@ -0,0 +1,144 @@
/// <reference path="../../factory.ts" />
/// <reference path="../../visitor.ts" />
/*@internal*/
namespace ts {
export function transformES6Module(context: TransformationContext) {
const compilerOptions = context.getCompilerOptions();
const resolver = context.getEmitResolver();
let currentSourceFile: SourceFile;
return transformSourceFile;
function transformSourceFile(node: SourceFile) {
if (isDeclarationFile(node)) {
return node;
}
if (isExternalModule(node) || compilerOptions.isolatedModules) {
currentSourceFile = node;
return visitEachChild(node, visitor, context);
}
return node;
}
function visitor(node: Node): VisitResult<Node> {
switch (node.kind) {
case SyntaxKind.ImportDeclaration:
return visitImportDeclaration(<ImportDeclaration>node);
case SyntaxKind.ImportEqualsDeclaration:
return visitImportEqualsDeclaration(<ImportEqualsDeclaration>node);
case SyntaxKind.ImportClause:
return visitImportClause(<ImportClause>node);
case SyntaxKind.NamedImports:
case SyntaxKind.NamespaceImport:
return visitNamedBindings(<NamedImportBindings>node);
case SyntaxKind.ImportSpecifier:
return visitImportSpecifier(<ImportSpecifier>node);
case SyntaxKind.ExportAssignment:
return visitExportAssignment(<ExportAssignment>node);
case SyntaxKind.ExportDeclaration:
return visitExportDeclaration(<ExportDeclaration>node);
case SyntaxKind.NamedExports:
return visitNamedExports(<NamedExports>node);
case SyntaxKind.ExportSpecifier:
return visitExportSpecifier(<ExportSpecifier>node);
}
return node;
}
function visitExportAssignment(node: ExportAssignment): ExportAssignment {
if (node.isExportEquals) {
return undefined; // do not emit export equals for ES6
}
const original = getOriginalNode(node);
return nodeIsSynthesized(original) || resolver.isValueAliasDeclaration(original) ? node : undefined;
}
function visitExportDeclaration(node: ExportDeclaration): ExportDeclaration {
if (!node.exportClause) {
return resolver.moduleExportsSomeValue(node.moduleSpecifier) ? node : undefined;
}
if (!resolver.isValueAliasDeclaration(node)) {
return undefined;
}
const newExportClause = visitNode(node.exportClause, visitor, isNamedExports, /*optional*/ true);
if (node.exportClause === newExportClause) {
return node;
}
return newExportClause
? createExportDeclaration(
/*decorators*/ undefined,
/*modifiers*/ undefined,
newExportClause,
node.moduleSpecifier)
: undefined;
}
function visitNamedExports(node: NamedExports): NamedExports {
const newExports = visitNodes(node.elements, visitor, isExportSpecifier);
if (node.elements === newExports) {
return node;
}
return newExports.length ? createNamedExports(newExports) : undefined;
}
function visitExportSpecifier(node: ExportSpecifier): ExportSpecifier {
return resolver.isValueAliasDeclaration(node) ? node : undefined;
}
function visitImportEqualsDeclaration(node: ImportEqualsDeclaration): ImportEqualsDeclaration {
return !isExternalModuleImportEqualsDeclaration(node) || resolver.isReferencedAliasDeclaration(node) ? node : undefined;
}
function visitImportDeclaration(node: ImportDeclaration) {
if (node.importClause) {
const newImportClause = visitNode(node.importClause, visitor, isImportClause);
if (!newImportClause.name && !newImportClause.namedBindings) {
return undefined;
}
else if (newImportClause !== node.importClause) {
return createImportDeclaration(
/*decorators*/ undefined,
/*modifiers*/ undefined,
newImportClause,
node.moduleSpecifier);
}
}
return node;
}
function visitImportClause(node: ImportClause): ImportClause {
let newDefaultImport = node.name;
if (!resolver.isReferencedAliasDeclaration(node)) {
newDefaultImport = undefined;
}
const newNamedBindings = visitNode(node.namedBindings, visitor, isNamedImportBindings, /*optional*/ true);
return newDefaultImport !== node.name || newNamedBindings !== node.namedBindings
? createImportClause(newDefaultImport, newNamedBindings)
: node;
}
function visitNamedBindings(node: NamedImportBindings): VisitResult<NamedImportBindings> {
if (node.kind === SyntaxKind.NamespaceImport) {
return resolver.isReferencedAliasDeclaration(node) ? node : undefined;
}
else {
const newNamedImportElements = visitNodes((<NamedImports>node).elements, visitor, isImportSpecifier);
if (!newNamedImportElements || newNamedImportElements.length == 0) {
return undefined;
}
if (newNamedImportElements === (<NamedImports>node).elements) {
return node;
}
return createNamedImports(newNamedImportElements);
}
}
function visitImportSpecifier(node: ImportSpecifier) {
return resolver.isReferencedAliasDeclaration(node) ? node : undefined;
}
}
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+62 -87
View File
@@ -6,6 +6,11 @@ namespace ts {
fileWatcher?: FileWatcher;
}
interface Statistic {
name: string;
value: string;
}
const defaultFormatDiagnosticsHost: FormatDiagnosticsHost = {
getCurrentDirectory: () => sys.getCurrentDirectory(),
getNewLine: () => sys.newLine,
@@ -230,18 +235,6 @@ namespace ts {
return s;
}
function reportStatisticalValue(name: string, value: string) {
sys.write(padRight(name + ":", 12) + padLeft(value.toString(), 10) + sys.newLine);
}
function reportCountStatistic(name: string, count: number) {
reportStatisticalValue(name, "" + count);
}
function reportTimeStatistic(name: string, time: number) {
reportStatisticalValue(name, (time / 1000).toFixed(2) + "s");
}
function isJSONSupported() {
return typeof JSON === "object" && typeof JSON.parse === "function";
}
@@ -489,10 +482,7 @@ namespace ts {
sourceFile.fileWatcher.close();
sourceFile.fileWatcher = undefined;
if (removed) {
const index = rootFileNames.indexOf(sourceFile.fileName);
if (index >= 0) {
rootFileNames.splice(index, 1);
}
unorderedRemoveItem(rootFileNames, sourceFile.fileName);
}
startTimerForRecompilation();
}
@@ -550,7 +540,11 @@ namespace ts {
function compile(fileNames: string[], compilerOptions: CompilerOptions, compilerHost: CompilerHost) {
const hasDiagnostics = compilerOptions.diagnostics || compilerOptions.extendedDiagnostics;
if (hasDiagnostics) performance.enable();
let statistics: Statistic[];
if (hasDiagnostics) {
performance.enable();
statistics = [];
}
const program = createProgram(fileNames, compilerOptions, compilerHost);
const exitStatus = compileProgram();
@@ -594,6 +588,7 @@ namespace ts {
reportTimeStatistic("Emit time", emitTime);
}
reportTimeStatistic("Total time", programTime + bindTime + checkTime + emitTime);
reportStatistics();
performance.disable();
}
@@ -635,6 +630,36 @@ namespace ts {
}
return ExitStatus.Success;
}
function reportStatistics() {
let nameSize = 0;
let valueSize = 0;
for (const { name, value } of statistics) {
if (name.length > nameSize) {
nameSize = name.length;
}
if (value.length > valueSize) {
valueSize = value.length;
}
}
for (const { name, value } of statistics) {
sys.write(padRight(name + ":", nameSize + 2) + padLeft(value.toString(), valueSize) + sys.newLine);
}
}
function reportStatisticalValue(name: string, value: string) {
statistics.push({ name, value });
}
function reportCountStatistic(name: string, count: number) {
reportStatisticalValue(name, "" + count);
}
function reportTimeStatistic(name: string, time: number) {
reportStatisticalValue(name, (time / 1000).toFixed(2) + "s");
}
}
function printVersion() {
@@ -642,7 +667,7 @@ namespace ts {
}
function printHelp() {
let output = "";
const output: string[] = [];
// We want to align our "syntax" and "examples" commands to a certain margin.
const syntaxLength = getDiagnosticText(Diagnostics.Syntax_Colon_0, "").length;
@@ -653,17 +678,17 @@ namespace ts {
let syntax = makePadding(marginLength - syntaxLength);
syntax += "tsc [" + getDiagnosticText(Diagnostics.options) + "] [" + getDiagnosticText(Diagnostics.file) + " ...]";
output += getDiagnosticText(Diagnostics.Syntax_Colon_0, syntax);
output += sys.newLine + sys.newLine;
output.push(getDiagnosticText(Diagnostics.Syntax_Colon_0, syntax));
output.push(sys.newLine + sys.newLine);
// Build up the list of examples.
const padding = makePadding(marginLength);
output += getDiagnosticText(Diagnostics.Examples_Colon_0, makePadding(marginLength - examplesLength) + "tsc hello.ts") + sys.newLine;
output += padding + "tsc --outFile file.js file.ts" + sys.newLine;
output += padding + "tsc @args.txt" + sys.newLine;
output += sys.newLine;
output.push(getDiagnosticText(Diagnostics.Examples_Colon_0, makePadding(marginLength - examplesLength) + "tsc hello.ts") + sys.newLine);
output.push(padding + "tsc --outFile file.js file.ts" + sys.newLine);
output.push(padding + "tsc @args.txt" + sys.newLine);
output.push(sys.newLine);
output += getDiagnosticText(Diagnostics.Options_Colon) + sys.newLine;
output.push(getDiagnosticText(Diagnostics.Options_Colon) + sys.newLine);
// Sort our options by their names, (e.g. "--noImplicitAny" comes before "--watch")
const optsList = filter(optionDeclarations.slice(), v => !v.experimental);
@@ -730,18 +755,20 @@ namespace ts {
const usage = usageColumn[i];
const description = descriptionColumn[i];
const kindsList = optionsDescriptionMap[description];
output += usage + makePadding(marginLength - usage.length + 2) + description + sys.newLine;
output.push(usage + makePadding(marginLength - usage.length + 2) + description + sys.newLine);
if (kindsList) {
output += makePadding(marginLength + 4);
output.push(makePadding(marginLength + 4));
for (const kind of kindsList) {
output += kind + " ";
output.push(kind + " ");
}
output += sys.newLine;
output.push(sys.newLine);
}
}
sys.write(output);
for (const line of output) {
sys.write(line);
}
return;
function getParamType(option: CommandLineOption) {
@@ -763,68 +790,16 @@ namespace ts {
reportDiagnostic(createCompilerDiagnostic(Diagnostics.A_tsconfig_json_file_is_already_defined_at_Colon_0, file), /* host */ undefined);
}
else {
const compilerOptions = extend(options, defaultInitCompilerOptions);
const configurations: any = {
compilerOptions: serializeCompilerOptions(compilerOptions)
};
if (fileNames && fileNames.length) {
// only set the files property if we have at least one file
configurations.files = fileNames;
}
else {
configurations.exclude = ["node_modules"];
if (compilerOptions.outDir) {
configurations.exclude.push(compilerOptions.outDir);
}
}
sys.writeFile(file, JSON.stringify(configurations, undefined, 4));
sys.writeFile(file, JSON.stringify(generateTSConfig(options, fileNames), undefined, 4));
reportDiagnostic(createCompilerDiagnostic(Diagnostics.Successfully_created_a_tsconfig_json_file), /* host */ undefined);
}
return;
function serializeCompilerOptions(options: CompilerOptions): Map<string | number | boolean> {
const result = createMap<string | number | boolean>();
const optionsNameMap = getOptionNameMap().optionNameMap;
for (const name in options) {
if (hasProperty(options, name)) {
// tsconfig only options cannot be specified via command line,
// so we can assume that only types that can appear here string | number | boolean
const value = <string | number | boolean>options[name];
switch (name) {
case "init":
case "watch":
case "version":
case "help":
case "project":
break;
default:
let optionDefinition = optionsNameMap[name.toLowerCase()];
if (optionDefinition) {
if (typeof optionDefinition.type === "string") {
// string, number or boolean
result[name] = value;
}
else {
// Enum
const typeMap = <Map<number>>optionDefinition.type;
for (const key in typeMap) {
if (typeMap[key] === value) {
result[name] = key;
}
}
}
}
break;
}
}
}
return result;
}
}
}
if (ts.sys.tryEnableSourceMapsForHost && /^development$/i.test(ts.sys.getEnvironmentVariable("NODE_ENV"))) {
ts.sys.tryEnableSourceMapsForHost();
}
ts.executeCommandLine(ts.sys.args);
+13
View File
@@ -20,6 +20,19 @@
"utilities.ts",
"binder.ts",
"checker.ts",
"factory.ts",
"visitor.ts",
"transformers/ts.ts",
"transformers/jsx.ts",
"transformers/es7.ts",
"transformers/es6.ts",
"transformers/generators.ts",
"transformers/destructuring.ts",
"transformers/module/module.ts",
"transformers/module/system.ts",
"transformers/module/es6.ts",
"transformer.ts",
"comments.ts",
"sourcemap.ts",
"declarationEmitter.ts",
"emitter.ts",
+324 -80
View File
@@ -19,6 +19,7 @@ namespace ts {
remove(fileName: Path): void;
forEachValue(f: (key: Path, v: T) => void): void;
getKeys(): Path[];
clear(): void;
}
@@ -351,14 +352,24 @@ namespace ts {
JSDocPropertyTag,
JSDocTypeLiteral,
JSDocLiteralType,
JSDocNullKeyword,
JSDocUndefinedKeyword,
JSDocNeverKeyword,
// Synthesized list
SyntaxList,
// Transformation nodes
NotEmittedStatement,
PartiallyEmittedExpression,
// Enum value count
Count,
// Markers
FirstAssignment = EqualsToken,
LastAssignment = CaretEqualsToken,
FirstCompoundAssignment = PlusEqualsToken,
LastCompoundAssignment = CaretEqualsToken,
FirstReservedWord = BreakKeyword,
LastReservedWord = WithKeyword,
FirstKeyword = BreakKeyword,
@@ -383,10 +394,51 @@ namespace ts {
FirstJSDocNode = JSDocTypeExpression,
LastJSDocNode = JSDocLiteralType,
FirstJSDocTagNode = JSDocComment,
LastJSDocTagNode = JSDocLiteralType
LastJSDocTagNode = JSDocNeverKeyword
}
export const enum NodeFlags {
None = 0,
Let = 1 << 0, // Variable declaration
Const = 1 << 1, // Variable declaration
NestedNamespace = 1 << 2, // Namespace declaration
Synthesized = 1 << 3, // Node was synthesized during transformation
Namespace = 1 << 4, // Namespace declaration
ExportContext = 1 << 5, // Export context (initialized by binding)
ContainsThis = 1 << 6, // Interface contains references to "this"
HasImplicitReturn = 1 << 7, // If function implicitly returns on one of codepaths (initialized by binding)
HasExplicitReturn = 1 << 8, // If function has explicit reachable return on one of codepaths (initialized by binding)
GlobalAugmentation = 1 << 9, // Set if module declaration is an augmentation for the global scope
HasClassExtends = 1 << 10, // If the file has a non-ambient class with an extends clause in ES5 or lower (initialized by binding)
HasDecorators = 1 << 11, // If the file has decorators (initialized by binding)
HasParamDecorators = 1 << 12, // If the file has parameter decorators (initialized by binding)
HasAsyncFunctions = 1 << 13, // If the file has async functions (initialized by binding)
HasJsxSpreadAttributes = 1 << 14, // If the file as JSX spread attributes (initialized by binding)
DisallowInContext = 1 << 15, // If node was parsed in a context where 'in-expressions' are not allowed
YieldContext = 1 << 16, // If node was parsed in the 'yield' context created when parsing a generator
DecoratorContext = 1 << 17, // If node was parsed as part of a decorator
AwaitContext = 1 << 18, // If node was parsed in the 'await' context created when parsing an async function
ThisNodeHasError = 1 << 19, // If the parser encountered an error when parsing the code that created this node
JavaScriptFile = 1 << 20, // If node was parsed in a JavaScript
ThisNodeOrAnySubNodesHasError = 1 << 21, // If this node or any of its children had an error
HasAggregatedChildData = 1 << 22, // If we've computed data from children and cached it in this node
BlockScoped = Let | Const,
ReachabilityCheckFlags = HasImplicitReturn | HasExplicitReturn,
EmitHelperFlags = HasClassExtends | HasDecorators | HasParamDecorators | HasAsyncFunctions | HasJsxSpreadAttributes,
ReachabilityAndEmitFlags = ReachabilityCheckFlags | EmitHelperFlags,
// Parsing context flags
ContextFlags = DisallowInContext | YieldContext | DecoratorContext | AwaitContext | JavaScriptFile,
// Exclude these flags when parsing a Type
TypeExcludesFlags = YieldContext | AwaitContext,
}
export type ModifiersArray = NodeArray<Modifier>;
export const enum ModifierFlags {
None = 0,
Export = 1 << 0, // Declarations
Ambient = 1 << 1, // Declarations
@@ -398,43 +450,14 @@ namespace ts {
Abstract = 1 << 7, // Class/Method/ConstructSignature
Async = 1 << 8, // Property/Method/Function
Default = 1 << 9, // Function/Class (export default declaration)
Let = 1 << 10, // Variable declaration
Const = 1 << 11, // Variable declaration
Namespace = 1 << 12, // Namespace declaration
ExportContext = 1 << 13, // Export context (initialized by binding)
ContainsThis = 1 << 14, // Interface contains references to "this"
HasImplicitReturn = 1 << 15, // If function implicitly returns on one of codepaths (initialized by binding)
HasExplicitReturn = 1 << 16, // If function has explicit reachable return on one of codepaths (initialized by binding)
GlobalAugmentation = 1 << 17, // Set if module declaration is an augmentation for the global scope
HasClassExtends = 1 << 18, // If the file has a non-ambient class with an extends clause in ES5 or lower (initialized by binding)
HasDecorators = 1 << 19, // If the file has decorators (initialized by binding)
HasParamDecorators = 1 << 20, // If the file has parameter decorators (initialized by binding)
HasAsyncFunctions = 1 << 21, // If the file has async functions (initialized by binding)
DisallowInContext = 1 << 22, // If node was parsed in a context where 'in-expressions' are not allowed
YieldContext = 1 << 23, // If node was parsed in the 'yield' context created when parsing a generator
DecoratorContext = 1 << 24, // If node was parsed as part of a decorator
AwaitContext = 1 << 25, // If node was parsed in the 'await' context created when parsing an async function
ThisNodeHasError = 1 << 26, // If the parser encountered an error when parsing the code that created this node
JavaScriptFile = 1 << 27, // If node was parsed in a JavaScript
ThisNodeOrAnySubNodesHasError = 1 << 28, // If this node or any of its children had an error
HasAggregatedChildData = 1 << 29, // If we've computed data from children and cached it in this node
HasJsxSpreadAttribute = 1 << 30,
Const = 1 << 11, // Variable declaration
HasComputedFlags = 1 << 29, // Modifier flags have been computed
Modifier = Export | Ambient | Public | Private | Protected | Static | Abstract | Default | Async | Readonly,
AccessibilityModifier = Public | Private | Protected,
// Accessibility modifiers and 'readonly' can be attached to a parameter in a constructor to make it a property.
ParameterPropertyModifier = AccessibilityModifier | Readonly,
BlockScoped = Let | Const,
ReachabilityCheckFlags = HasImplicitReturn | HasExplicitReturn,
EmitHelperFlags = HasClassExtends | HasDecorators | HasParamDecorators | HasAsyncFunctions,
ReachabilityAndEmitFlags = ReachabilityCheckFlags | EmitHelperFlags,
// Parsing context flags
ContextFlags = DisallowInContext | YieldContext | DecoratorContext | AwaitContext | JavaScriptFile,
// Exclude these flags when parsing a Type
TypeExcludesFlags = YieldContext | AwaitContext,
NonPublicAccessibilityModifier = Private | Protected,
}
export const enum JsxFlags {
@@ -457,26 +480,27 @@ namespace ts {
export interface Node extends TextRange {
kind: SyntaxKind;
flags: NodeFlags;
/* @internal */ modifierFlagsCache?: ModifierFlags;
/* @internal */ transformFlags?: TransformFlags;
decorators?: NodeArray<Decorator>; // Array of decorators (in document order)
modifiers?: ModifiersArray; // Array of modifiers
/* @internal */ id?: number; // Unique id (used to look up NodeLinks)
parent?: Node; // Parent node (initialized by binding
/* @internal */ jsDocComments?: JSDocComment[]; // JSDoc for the node, if it has any. Only for .js files.
parent?: Node; // Parent node (initialized by binding)
/* @internal */ original?: Node; // The original node if this is an updated node.
/* @internal */ startsOnNewLine?: boolean; // Whether a synthesized node should start on a new line (used by transforms).
/* @internal */ jsDocComments?: JSDoc[]; // JSDoc for the node, if it has any.
/* @internal */ symbol?: Symbol; // Symbol declared by node (initialized by binding)
/* @internal */ locals?: SymbolTable; // Locals associated with node (initialized by binding)
/* @internal */ nextContainer?: Node; // Next container in declaration order (initialized by binding)
/* @internal */ localSymbol?: Symbol; // Local symbol declared by node (initialized by binding only for exported nodes)
/* @internal */ flowNode?: FlowNode; // Associated FlowNode (initialized by binding)
/* @internal */ emitNode?: EmitNode; // Associated EmitNode (initialized by transforms)
}
export interface NodeArray<T> extends Array<T>, TextRange {
export interface NodeArray<T extends Node> extends Array<T>, TextRange {
hasTrailingComma?: boolean;
}
export interface ModifiersArray extends NodeArray<Modifier> {
flags: NodeFlags;
}
export interface Token extends Node {
__tokenTag: any;
}
@@ -493,10 +517,26 @@ namespace ts {
// @kind(SyntaxKind.StaticKeyword)
export interface Modifier extends Token { }
/*@internal*/
export const enum GeneratedIdentifierKind {
None, // Not automatically generated.
Auto, // Automatically generated identifier.
Loop, // Automatically generated identifier with a preference for '_i'.
Unique, // Unique name based on the 'text' property.
Node, // Unique name based on the node in the 'original' property.
}
// @kind(SyntaxKind.Identifier)
export interface Identifier extends PrimaryExpression {
text: string; // Text of identifier (with escapes converted to characters)
originalKeywordKind?: SyntaxKind; // Original syntaxKind which get set so that we can report an error later
/*@internal*/ autoGenerateKind?: GeneratedIdentifierKind; // Specifies whether to auto-generate the text for an identifier.
/*@internal*/ autoGenerateId?: number; // Ensures unique generated identifiers get unique names, but clones get the same name.
}
// Transient identifier node (marked by id === -1)
export interface TransientIdentifier extends Identifier {
resolvedSymbol: Symbol;
}
// @kind(SyntaxKind.QualifiedName)
@@ -553,10 +593,12 @@ namespace ts {
// @kind(SyntaxKind.ConstructSignature)
export interface ConstructSignatureDeclaration extends SignatureDeclaration, TypeElement { }
export type BindingName = Identifier | BindingPattern;
// @kind(SyntaxKind.VariableDeclaration)
export interface VariableDeclaration extends Declaration {
parent?: VariableDeclarationList;
name: Identifier | BindingPattern; // Declared variable name
name: BindingName; // Declared variable name
type?: TypeNode; // Optional type annotation
initializer?: Expression; // Optional initializer
}
@@ -569,7 +611,7 @@ namespace ts {
// @kind(SyntaxKind.Parameter)
export interface ParameterDeclaration extends Declaration {
dotDotDotToken?: Node; // Present on rest parameter
name: Identifier | BindingPattern; // Declared parameter name
name: BindingName; // Declared parameter name
questionToken?: Node; // Present on optional parameter
type?: TypeNode; // Optional type annotation
initializer?: Expression; // Optional initializer
@@ -579,7 +621,7 @@ namespace ts {
export interface BindingElement extends Declaration {
propertyName?: PropertyName; // Binding property name (in object binding pattern)
dotDotDotToken?: Node; // Present on rest binding element
name: Identifier | BindingPattern; // Declared binding element name
name: BindingName; // Declared binding element name
initializer?: Expression; // Optional initializer
}
@@ -604,6 +646,8 @@ namespace ts {
name?: PropertyName;
}
export type ObjectLiteralElementLike = PropertyAssignment | ShorthandPropertyAssignment | MethodDeclaration | AccessorDeclaration;
// @kind(SyntaxKind.PropertyAssignment)
export interface PropertyAssignment extends ObjectLiteralElement {
_propertyAssignmentBrand: any;
@@ -644,14 +688,20 @@ namespace ts {
}
export interface BindingPattern extends Node {
elements: NodeArray<BindingElement>;
elements: NodeArray<BindingElement | ArrayBindingElement>;
}
// @kind(SyntaxKind.ObjectBindingPattern)
export interface ObjectBindingPattern extends BindingPattern { }
export interface ObjectBindingPattern extends BindingPattern {
elements: NodeArray<BindingElement>;
}
export type ArrayBindingElement = BindingElement | OmittedExpression;
// @kind(SyntaxKind.ArrayBindingPattern)
export interface ArrayBindingPattern extends BindingPattern { }
export interface ArrayBindingPattern extends BindingPattern {
elements: NodeArray<ArrayBindingElement>;
}
/**
* Several node kinds share function-like features such as a signature,
@@ -807,6 +857,8 @@ namespace ts {
// @kind(SyntaxKind.StringLiteral)
export interface StringLiteral extends LiteralExpression {
_stringLiteralBrand: any;
/* @internal */
textSourceNode?: Identifier | StringLiteral; // Allows a StringLiteral to get its text from another node (used by transforms).
}
// Note: 'brands' in our syntax nodes serve to give us a small amount of nominal typing.
@@ -822,7 +874,17 @@ namespace ts {
}
// @kind(SyntaxKind.OmittedExpression)
export interface OmittedExpression extends Expression { }
export interface OmittedExpression extends Expression {
_omittedExpressionBrand: any;
}
// Represents an expression that is elided as part of a transformation to emit comments on a
// not-emitted node. The 'expression' property of a NotEmittedExpression should be emitted.
// @internal
// @kind(SyntaxKind.NotEmittedExpression)
export interface PartiallyEmittedExpression extends LeftHandSideExpression {
expression: Expression;
}
export interface UnaryExpression extends Expression {
_unaryExpressionBrand: any;
@@ -934,13 +996,18 @@ namespace ts {
// The text property of a LiteralExpression stores the interpreted value of the literal in text form. For a StringLiteral,
// or any literal of a template, this means quotes have been removed and escapes have been converted to actual characters.
// For a NumericLiteral, the stored value is the toString() representation of the number. For example 1, 1.00, and 1e0 are all stored as just "1".
// @kind(SyntaxKind.NumericLiteral)
// @kind(SyntaxKind.RegularExpressionLiteral)
// @kind(SyntaxKind.NoSubstitutionTemplateLiteral)
export interface LiteralExpression extends LiteralLikeNode, PrimaryExpression {
_literalExpressionBrand: any;
}
// @kind(SyntaxKind.NumericLiteral)
export interface NumericLiteral extends LiteralExpression {
_numericLiteralBrand: any;
trailingComment?: string;
}
// @kind(SyntaxKind.TemplateHead)
// @kind(SyntaxKind.TemplateMiddle)
// @kind(SyntaxKind.TemplateTail)
@@ -948,6 +1015,8 @@ namespace ts {
_templateLiteralFragmentBrand: any;
}
export type Template = TemplateExpression | LiteralExpression;
// @kind(SyntaxKind.TemplateExpression)
export interface TemplateExpression extends PrimaryExpression {
head: TemplateLiteralFragment;
@@ -979,10 +1048,19 @@ namespace ts {
expression: Expression;
}
/**
* This interface is a base interface for ObjectLiteralExpression and JSXAttributes to extend from. JSXAttributes is similar to
* ObjectLiteralExpression in that it contains array of properties; however, JSXAttributes' properties can only be
* JSXAttribute or JSXSpreadAttribute. ObjectLiteralExpression, on the other hand, can only have properties of type
* ObjectLiteralElement (e.g. PropertyAssignment, ShorthandPropertyAssignment etc.)
**/
export interface ObjectLiteralExpressionBase<T extends ObjectLiteralElement> extends PrimaryExpression, Declaration {
properties: NodeArray<T>;
}
// An ObjectLiteralExpression is the declaration node for an anonymous symbol.
// @kind(SyntaxKind.ObjectLiteralExpression)
export interface ObjectLiteralExpression extends PrimaryExpression, Declaration {
properties: NodeArray<ObjectLiteralElement>;
export interface ObjectLiteralExpression extends ObjectLiteralExpressionBase<ObjectLiteralElementLike> {
/* @internal */
multiLine?: boolean;
}
@@ -1026,7 +1104,7 @@ namespace ts {
// @kind(SyntaxKind.TaggedTemplateExpression)
export interface TaggedTemplateExpression extends MemberExpression {
tag: LeftHandSideExpression;
template: LiteralExpression | TemplateExpression;
template: Template;
}
export type CallLikeExpression = CallExpression | NewExpression | TaggedTemplateExpression | Decorator;
@@ -1077,11 +1155,13 @@ namespace ts {
/// Either the opening tag in a <Tag>...</Tag> pair, or the lone <Tag /> in a self-closing form
export type JsxOpeningLikeElement = JsxSelfClosingElement | JsxOpeningElement;
export type JsxAttributeLike = JsxAttribute | JsxSpreadAttribute;
// @kind(SyntaxKind.JsxAttribute)
export interface JsxAttribute extends Node {
name: Identifier;
/// JSX attribute initializers are optional; <X y /> is sugar for <X y={true} />
initializer?: Expression;
initializer?: StringLiteral | JsxExpression;
}
// @kind(SyntaxKind.JsxSpreadAttribute)
@@ -1110,6 +1190,13 @@ namespace ts {
_statementBrand: any;
}
// Represents a statement that is elided as part of a transformation to emit comments on a
// not-emitted node.
// @internal
// @kind(SyntaxKind.NotEmittedStatement)
export interface NotEmittedStatement extends Statement {
}
// @kind(SyntaxKind.EmptyStatement)
export interface EmptyStatement extends Statement { }
@@ -1126,6 +1213,7 @@ namespace ts {
// @kind(SyntaxKind.Block)
export interface Block extends Statement {
statements: NodeArray<Statement>;
/*@internal*/ multiLine?: boolean;
}
// @kind(SyntaxKind.VariableStatement)
@@ -1159,22 +1247,24 @@ namespace ts {
expression: Expression;
}
export type ForInitializer = VariableDeclarationList | Expression;
// @kind(SyntaxKind.ForStatement)
export interface ForStatement extends IterationStatement {
initializer?: VariableDeclarationList | Expression;
initializer?: ForInitializer;
condition?: Expression;
incrementor?: Expression;
}
// @kind(SyntaxKind.ForInStatement)
export interface ForInStatement extends IterationStatement {
initializer: VariableDeclarationList | Expression;
initializer: ForInitializer;
expression: Expression;
}
// @kind(SyntaxKind.ForOfStatement)
export interface ForOfStatement extends IterationStatement {
initializer: VariableDeclarationList | Expression;
initializer: ForInitializer;
expression: Expression;
}
@@ -1304,7 +1394,7 @@ namespace ts {
export interface EnumMember extends Declaration {
// This does include ComputedPropertyName, but the parser will give an error
// if it parses a ComputedPropertyName in an EnumMember
name: DeclarationName;
name: PropertyName;
initializer?: Expression;
}
@@ -1316,6 +1406,8 @@ namespace ts {
export type ModuleBody = ModuleBlock | ModuleDeclaration;
export type ModuleName = Identifier | StringLiteral;
// @kind(SyntaxKind.ModuleDeclaration)
export interface ModuleDeclaration extends DeclarationStatement {
name: Identifier | LiteralExpression;
@@ -1327,13 +1419,15 @@ namespace ts {
statements: NodeArray<Statement>;
}
export type ModuleReference = EntityName | ExternalModuleReference;
// @kind(SyntaxKind.ImportEqualsDeclaration)
export interface ImportEqualsDeclaration extends DeclarationStatement {
name: Identifier;
// 'EntityName' for an internal module reference, 'ExternalModuleReference' for an external
// module reference.
moduleReference: EntityName | ExternalModuleReference;
moduleReference: ModuleReference;
}
// @kind(SyntaxKind.ExternalModuleReference)
@@ -1351,6 +1445,8 @@ namespace ts {
moduleSpecifier: Expression;
}
export type NamedImportBindings = NamespaceImport | NamedImports;
// In case of:
// import d from "mod" => name = d, namedBinding = undefined
// import * as ns from "mod" => name = undefined, namedBinding: NamespaceImport = { name: ns }
@@ -1360,7 +1456,7 @@ namespace ts {
// @kind(SyntaxKind.ImportClause)
export interface ImportClause extends Declaration {
name?: Identifier; // Default binding
namedBindings?: NamespaceImport | NamedImports;
namedBindings?: NamedImportBindings;
}
// @kind(SyntaxKind.NamespaceImport)
@@ -1468,7 +1564,7 @@ namespace ts {
// @kind(SyntaxKind.JSDocRecordType)
export interface JSDocRecordType extends JSDocType, TypeLiteralNode {
members: NodeArray<JSDocRecordMember>;
literal: TypeLiteralNode;
}
// @kind(SyntaxKind.JSDocTypeReference)
@@ -1516,14 +1612,16 @@ namespace ts {
}
// @kind(SyntaxKind.JSDocComment)
export interface JSDocComment extends Node {
tags: NodeArray<JSDocTag>;
export interface JSDoc extends Node {
tags: NodeArray<JSDocTag> | undefined;
comment: string | undefined;
}
// @kind(SyntaxKind.JSDocTag)
export interface JSDocTag extends Node {
atToken: Node;
tagName: Identifier;
comment: string | undefined;
}
// @kind(SyntaxKind.JSDocTemplateTag)
@@ -1562,9 +1660,13 @@ namespace ts {
// @kind(SyntaxKind.JSDocParameterTag)
export interface JSDocParameterTag extends JSDocTag {
/** the parameter name, if provided *before* the type (TypeScript-style) */
preParameterName?: Identifier;
typeExpression?: JSDocTypeExpression;
/** the parameter name, if provided *after* the type (JSDoc-standard) */
postParameterName?: Identifier;
/** the parameter name, regardless of the location it was provided */
parameterName: Identifier;
isBracketed: boolean;
}
@@ -1699,6 +1801,8 @@ namespace ts {
/* @internal */ imports: LiteralExpression[];
/* @internal */ moduleAugmentations: LiteralExpression[];
/* @internal */ patternAmbientModules?: PatternAmbientModule[];
// The synthesized identifier for an imported external helpers module.
/* @internal */ externalHelpersModuleName?: Identifier;
}
export interface ScriptReferenceHost {
@@ -1718,6 +1822,8 @@ namespace ts {
* @param path The path to test.
*/
fileExists(path: string): boolean;
readFile(path: string): string;
}
export interface WriteFileCallback {
@@ -1755,7 +1861,7 @@ namespace ts {
* used for writing the JavaScript and declaration files. Otherwise, the writeFile parameter
* will be invoked when writing the JavaScript and declaration files.
*/
emit(targetSourceFile?: SourceFile, writeFile?: WriteFileCallback, cancellationToken?: CancellationToken): EmitResult;
emit(targetSourceFile?: SourceFile, writeFile?: WriteFileCallback, cancellationToken?: CancellationToken, emitOnlyDtsFiles?: boolean): EmitResult;
getOptionsDiagnostics(cancellationToken?: CancellationToken): Diagnostic[];
getGlobalDiagnostics(cancellationToken?: CancellationToken): Diagnostic[];
@@ -1773,6 +1879,7 @@ namespace ts {
// For testing purposes only. Should not be used by any other consumers (including the
// language service).
/* @internal */ getDiagnosticsProducingTypeChecker(): TypeChecker;
/* @internal */ dropDiagnosticsProducingTypeChecker(): void;
/* @internal */ getClassifiableNames(): Map<string>;
@@ -1886,6 +1993,7 @@ namespace ts {
getJsxElementAttributesType(elementNode: JsxOpeningLikeElement): Type;
getJsxIntrinsicTagNames(): Symbol[];
isOptionalParameter(node: ParameterDeclaration): boolean;
getAmbientModules(): Symbol[];
// Should not be called directly. Should only be accessed through the Program instance.
/* @internal */ getDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): Diagnostic[];
@@ -1943,6 +2051,7 @@ namespace ts {
UseFullyQualifiedType = 0x00000080, // Write out the fully qualified type name (eg. Module.Type, instead of Type)
InFirstTypeArgument = 0x00000100, // Writing first type argument of the instantiated type
InTypeAlias = 0x00000200, // Writing type in type alias declaration
UseTypeAliasValue = 0x00000400, // Serialize the type instead of using type-alias. This is needed when we emit declaration file.
}
export const enum SymbolFormatFlags {
@@ -2016,12 +2125,13 @@ namespace ts {
// function that can be reached at runtime (e.g. a `class`
// declaration or a `var` declaration for the static side
// of a type, such as the global `Promise` type in lib.d.ts).
VoidType, // The TypeReferenceNode resolves to a Void-like type.
VoidNullableOrNeverType, // The TypeReferenceNode resolves to a Void-like, Nullable, or Never type.
NumberLikeType, // The TypeReferenceNode resolves to a Number-like type.
StringLikeType, // The TypeReferenceNode resolves to a String-like type.
BooleanType, // The TypeReferenceNode resolves to a Boolean-like type.
ArrayLikeType, // The TypeReferenceNode resolves to an Array-like type.
ESSymbolType, // The TypeReferenceNode resolves to the ESSymbol type.
Promise, // The TypeReferenceNode resolved to the global Promise constructor symbol.
TypeWithCallSignature, // The TypeReferenceNode resolves to a Function type or a type
// with call signatures.
ObjectType, // The TypeReferenceNode resolves to any other type.
@@ -2030,7 +2140,7 @@ namespace ts {
/* @internal */
export interface EmitResolver {
hasGlobalName(name: string): boolean;
getReferencedExportContainer(node: Identifier): SourceFile | ModuleDeclaration | EnumDeclaration;
getReferencedExportContainer(node: Identifier, prefixLocals?: boolean): SourceFile | ModuleDeclaration | EnumDeclaration;
getReferencedImportDeclaration(node: Identifier): Declaration;
getReferencedDeclarationWithCollidingName(node: Identifier): Declaration;
isDeclarationWithCollidingName(node: Declaration): boolean;
@@ -2045,18 +2155,20 @@ namespace ts {
writeReturnTypeOfSignatureDeclaration(signatureDeclaration: SignatureDeclaration, enclosingDeclaration: Node, flags: TypeFormatFlags, writer: SymbolWriter): void;
writeTypeOfExpression(expr: Expression, enclosingDeclaration: Node, flags: TypeFormatFlags, writer: SymbolWriter): void;
writeBaseConstructorTypeOfClass(node: ClassLikeDeclaration, enclosingDeclaration: Node, flags: TypeFormatFlags, writer: SymbolWriter): void;
isSymbolAccessible(symbol: Symbol, enclosingDeclaration: Node, meaning: SymbolFlags): SymbolAccessibilityResult;
isSymbolAccessible(symbol: Symbol, enclosingDeclaration: Node, meaning: SymbolFlags, shouldComputeAliasToMarkVisible: boolean): SymbolAccessibilityResult;
isEntityNameVisible(entityName: EntityNameOrEntityNameExpression, enclosingDeclaration: Node): SymbolVisibilityResult;
// Returns the constant value this property access resolves to, or 'undefined' for a non-constant
getConstantValue(node: EnumMember | PropertyAccessExpression | ElementAccessExpression): number;
getReferencedValueDeclaration(reference: Identifier): Declaration;
getTypeReferenceSerializationKind(typeName: EntityName): TypeReferenceSerializationKind;
getTypeReferenceSerializationKind(typeName: EntityName, location?: Node): TypeReferenceSerializationKind;
isOptionalParameter(node: ParameterDeclaration): boolean;
moduleExportsSomeValue(moduleReferenceExpression: Expression): boolean;
isArgumentsLocalBinding(node: Identifier): boolean;
getExternalModuleFileFromDeclaration(declaration: ImportEqualsDeclaration | ImportDeclaration | ExportDeclaration | ModuleDeclaration): SourceFile;
getTypeReferenceDirectivesForEntityName(name: EntityNameOrEntityNameExpression): string[];
getTypeReferenceDirectivesForSymbol(symbol: Symbol, meaning?: SymbolFlags): string[];
isLiteralConstDeclaration(node: VariableDeclaration): boolean;
writeLiteralConstValue(node: VariableDeclaration, writer: SymbolWriter): void;
}
export const enum SymbolFlags {
@@ -2177,7 +2289,8 @@ namespace ts {
mapper?: TypeMapper; // Type mapper for instantiation alias
referenced?: boolean; // True if alias symbol has been referenced as a value
containingType?: UnionOrIntersectionType; // Containing union or intersection type for synthetic property
hasCommonType?: boolean; // True if constituents of synthetic property all have same type
hasNonUniformType?: boolean; // True if constituents have non-uniform types
isPartial?: boolean; // True if syntheric property of union type occurs in some but not all constituents
isDiscriminantProperty?: boolean; // True if discriminant synthetic property
resolvedExports?: SymbolTable; // Resolved exports of module
exportsChecked?: boolean; // True if exports of external module have been checked
@@ -2225,6 +2338,8 @@ namespace ts {
BodyScopedClassBinding = 0x00100000, // Binding to a decorated class inside of the class's body.
NeedsLoopOutParameter = 0x00200000, // Block scoped binding whose value should be explicitly copied outside of the converted loop
AssignmentsMarked = 0x00400000, // Parameter assignments have been marked
ClassWithConstructorReference = 0x00800000, // Class that contains a binding to its constructor inside of the class body.
ConstructorReferenceInClass = 0x01000000, // Binding to a class constructor inside of the class's body.
}
/* @internal */
@@ -2263,7 +2378,7 @@ namespace ts {
Class = 1 << 15, // Class
Interface = 1 << 16, // Interface
Reference = 1 << 17, // Generic type reference
Tuple = 1 << 18, // Tuple
Tuple = 1 << 18, // Synthesized generic tuple type
Union = 1 << 19, // Union (T | U)
Intersection = 1 << 20, // Intersection (T & U)
Anonymous = 1 << 21, // Anonymous
@@ -2271,7 +2386,7 @@ namespace ts {
/* @internal */
ObjectLiteral = 1 << 23, // Originates in an object literal
/* @internal */
FreshObjectLiteral = 1 << 24, // Fresh object literal type
FreshLiteral = 1 << 24, // Fresh literal type
/* @internal */
ContainsWideningType = 1 << 25, // Type is or contains undefined or null widening type
/* @internal */
@@ -2284,6 +2399,7 @@ namespace ts {
/* @internal */
Nullable = Undefined | Null,
Literal = StringLiteral | NumberLiteral | BooleanLiteral | EnumLiteral,
StringOrNumberLiteral = StringLiteral | NumberLiteral,
/* @internal */
DefinitelyFalsy = StringLiteral | NumberLiteral | BooleanLiteral | Void | Undefined | Null,
PossiblyFalsy = DefinitelyFalsy | String | Number | Boolean,
@@ -2325,12 +2441,15 @@ namespace ts {
/* @internal */
// Intrinsic types (TypeFlags.Intrinsic)
export interface IntrinsicType extends Type {
intrinsicName: string; // Name of intrinsic type
intrinsicName: string; // Name of intrinsic type
}
// String literal types (TypeFlags.StringLiteral)
// Numeric literal types (TypeFlags.NumberLiteral)
export interface LiteralType extends Type {
text: string; // Text of string literal
text: string; // Text of literal
freshType?: LiteralType; // Fresh version of type
regularType?: LiteralType; // Regular version of type
}
// Enum types (TypeFlags.Enum)
@@ -2340,7 +2459,7 @@ namespace ts {
// Enum types (TypeFlags.EnumLiteral)
export interface EnumLiteralType extends LiteralType {
baseType: EnumType & UnionType;
baseType: EnumType & UnionType; // Base enum type
}
// Object types (TypeFlags.ObjectType)
@@ -2385,11 +2504,6 @@ namespace ts {
instantiations: Map<TypeReference>; // Generic instantiation cache
}
export interface TupleType extends ObjectType {
elementTypes: Type[]; // Element types
thisType?: Type; // This-type of tuple (only needed for tuples that are constraints of type parameters)
}
export interface UnionOrIntersectionType extends Type {
types: Type[]; // Constituent types
/* @internal */
@@ -2505,13 +2619,14 @@ namespace ts {
export interface TypeInferences {
primary: Type[]; // Inferences made directly to a type parameter
secondary: Type[]; // Inferences made to a type parameter in a union type
topLevel: boolean; // True if all inferences were made from top-level (not nested in object type) locations
isFixed: boolean; // Whether the type parameter is fixed, as defined in section 4.12.2 of the TypeScript spec
// If a type parameter is fixed, no more inferences can be made for the type parameter
}
/* @internal */
export interface InferenceContext {
typeParameters: TypeParameter[]; // Type parameters for which inferences are made
signature: Signature; // Generic signature for which inferences are made
inferUnionTypes: boolean; // Infer union types for disjoint candidates (otherwise undefinedType)
inferences: TypeInferences[]; // Inferences made for each type parameter
inferredTypes: Type[]; // Inferred type for each type parameter
@@ -2598,6 +2713,7 @@ namespace ts {
experimentalDecorators?: boolean;
forceConsistentCasingInFileNames?: boolean;
/*@internal*/help?: boolean;
importHelpers?: boolean;
/*@internal*/init?: boolean;
inlineSourceMap?: boolean;
inlineSources?: boolean;
@@ -2735,6 +2851,7 @@ namespace ts {
raw?: any;
errors: Diagnostic[];
wildcardDirectories?: MapLike<WatchDirectoryFlags>;
compileOnSave?: boolean;
}
export const enum WatchDirectoryFlags {
@@ -2766,7 +2883,7 @@ namespace ts {
/* @internal */
export interface CommandLineOptionOfCustomType extends CommandLineOptionBase {
type: Map<number | string>; // an object literal mapping named values to actual values
type: Map<number | string>; // an object literal mapping named values to actual values
}
/* @internal */
@@ -2985,8 +3102,135 @@ namespace ts {
* This method is a companion for 'resolveModuleNames' and is used to resolve 'types' references to actual type declaration files
*/
resolveTypeReferenceDirectives?(typeReferenceDirectiveNames: string[], containingFile: string): ResolvedTypeReferenceDirective[];
getEnvironmentVariable?(name: string): string;
}
/* @internal */
export const enum TransformFlags {
None = 0,
// Facts
// - Flags used to indicate that a node or subtree contains syntax that requires transformation.
TypeScript = 1 << 0,
ContainsTypeScript = 1 << 1,
Jsx = 1 << 2,
ContainsJsx = 1 << 3,
ES7 = 1 << 4,
ContainsES7 = 1 << 5,
ES6 = 1 << 6,
ContainsES6 = 1 << 7,
DestructuringAssignment = 1 << 8,
Generator = 1 << 9,
ContainsGenerator = 1 << 10,
// Markers
// - Flags used to indicate that a subtree contains a specific transformation.
ContainsDecorators = 1 << 11,
ContainsPropertyInitializer = 1 << 12,
ContainsLexicalThis = 1 << 13,
ContainsCapturedLexicalThis = 1 << 14,
ContainsLexicalThisInComputedPropertyName = 1 << 15,
ContainsDefaultValueAssignments = 1 << 16,
ContainsParameterPropertyAssignments = 1 << 17,
ContainsSpreadElementExpression = 1 << 18,
ContainsComputedPropertyName = 1 << 19,
ContainsBlockScopedBinding = 1 << 20,
ContainsBindingPattern = 1 << 21,
ContainsYield = 1 << 22,
ContainsHoistedDeclarationOrCompletion = 1 << 23,
HasComputedFlags = 1 << 29, // Transform flags have been computed.
// Assertions
// - Bitmasks that are used to assert facts about the syntax of a node and its subtree.
AssertTypeScript = TypeScript | ContainsTypeScript,
AssertJsx = Jsx | ContainsJsx,
AssertES7 = ES7 | ContainsES7,
AssertES6 = ES6 | ContainsES6,
AssertGenerator = Generator | ContainsGenerator,
// Scope Exclusions
// - Bitmasks that exclude flags from propagating out of a specific context
// into the subtree flags of their container.
NodeExcludes = TypeScript | Jsx | ES7 | ES6 | DestructuringAssignment | Generator | HasComputedFlags,
ArrowFunctionExcludes = NodeExcludes | ContainsDecorators | ContainsDefaultValueAssignments | ContainsLexicalThis | ContainsParameterPropertyAssignments | ContainsBlockScopedBinding | ContainsYield | ContainsHoistedDeclarationOrCompletion,
FunctionExcludes = NodeExcludes | ContainsDecorators | ContainsDefaultValueAssignments | ContainsCapturedLexicalThis | ContainsLexicalThis | ContainsParameterPropertyAssignments | ContainsBlockScopedBinding | ContainsYield | ContainsHoistedDeclarationOrCompletion,
ConstructorExcludes = NodeExcludes | ContainsDefaultValueAssignments | ContainsLexicalThis | ContainsCapturedLexicalThis | ContainsBlockScopedBinding | ContainsYield | ContainsHoistedDeclarationOrCompletion,
MethodOrAccessorExcludes = NodeExcludes | ContainsDefaultValueAssignments | ContainsLexicalThis | ContainsCapturedLexicalThis | ContainsBlockScopedBinding | ContainsYield | ContainsHoistedDeclarationOrCompletion,
ClassExcludes = NodeExcludes | ContainsDecorators | ContainsPropertyInitializer | ContainsLexicalThis | ContainsCapturedLexicalThis | ContainsComputedPropertyName | ContainsParameterPropertyAssignments | ContainsLexicalThisInComputedPropertyName,
ModuleExcludes = NodeExcludes | ContainsDecorators | ContainsLexicalThis | ContainsCapturedLexicalThis | ContainsBlockScopedBinding | ContainsHoistedDeclarationOrCompletion,
TypeExcludes = ~ContainsTypeScript,
ObjectLiteralExcludes = NodeExcludes | ContainsDecorators | ContainsComputedPropertyName | ContainsLexicalThisInComputedPropertyName,
ArrayLiteralOrCallOrNewExcludes = NodeExcludes | ContainsSpreadElementExpression,
VariableDeclarationListExcludes = NodeExcludes | ContainsBindingPattern,
ParameterExcludes = NodeExcludes | ContainsBindingPattern,
// Masks
// - Additional bitmasks
TypeScriptClassSyntaxMask = ContainsParameterPropertyAssignments | ContainsPropertyInitializer | ContainsDecorators,
ES6FunctionSyntaxMask = ContainsCapturedLexicalThis | ContainsDefaultValueAssignments,
}
/* @internal */
export interface EmitNode {
flags?: EmitFlags;
commentRange?: TextRange;
sourceMapRange?: TextRange;
tokenSourceMapRanges?: Map<TextRange>;
annotatedNodes?: Node[]; // Tracks Parse-tree nodes with EmitNodes for eventual cleanup.
constantValue?: number;
}
/* @internal */
export const enum EmitFlags {
EmitEmitHelpers = 1 << 0, // Any emit helpers should be written to this node.
EmitExportStar = 1 << 1, // The export * helper should be written to this node.
EmitSuperHelper = 1 << 2, // Emit the basic _super helper for async methods.
EmitAdvancedSuperHelper = 1 << 3, // Emit the advanced _super helper for async methods.
UMDDefine = 1 << 4, // This node should be replaced with the UMD define helper.
SingleLine = 1 << 5, // The contents of this node should be emitted on a single line.
AdviseOnEmitNode = 1 << 6, // The printer should invoke the onEmitNode callback when printing this node.
NoSubstitution = 1 << 7, // Disables further substitution of an expression.
CapturesThis = 1 << 8, // The function captures a lexical `this`
NoLeadingSourceMap = 1 << 9, // Do not emit a leading source map location for this node.
NoTrailingSourceMap = 1 << 10, // Do not emit a trailing source map location for this node.
NoSourceMap = NoLeadingSourceMap | NoTrailingSourceMap, // Do not emit a source map location for this node.
NoNestedSourceMaps = 1 << 11, // Do not emit source map locations for children of this node.
NoTokenLeadingSourceMaps = 1 << 12, // Do not emit leading source map location for token nodes.
NoTokenTrailingSourceMaps = 1 << 13, // Do not emit trailing source map location for token nodes.
NoTokenSourceMaps = NoTokenLeadingSourceMaps | NoTokenTrailingSourceMaps, // Do not emit source map locations for tokens of this node.
NoLeadingComments = 1 << 14, // Do not emit leading comments for this node.
NoTrailingComments = 1 << 15, // Do not emit trailing comments for this node.
NoComments = NoLeadingComments | NoTrailingComments, // Do not emit comments for this node.
NoNestedComments = 1 << 16,
ExportName = 1 << 17, // Ensure an export prefix is added for an identifier that points to an exported declaration with a local name (see SymbolFlags.ExportHasLocal).
LocalName = 1 << 18, // Ensure an export prefix is not added for an identifier that points to an exported declaration.
Indented = 1 << 19, // Adds an explicit extra indentation level for class and function bodies when printing (used to match old emitter).
NoIndentation = 1 << 20, // Do not indent the node.
AsyncFunctionBody = 1 << 21,
ReuseTempVariableScope = 1 << 22, // Reuse the existing temp variable scope during emit.
CustomPrologue = 1 << 23, // Treat the statement as if it were a prologue directive (NOTE: Prologue directives are *not* transformed).
}
/* @internal */
export const enum EmitContext {
SourceFile, // Emitting a SourceFile
Expression, // Emitting an Expression
IdentifierName, // Emitting an IdentifierName
Unspecified, // Emitting an otherwise unspecified node
}
/** Additional context provided to `visitEachChild` */
/* @internal */
export interface LexicalEnvironment {
/** Starts a new lexical environment. */
startLexicalEnvironment(): void;
/** Ends a lexical environment, returning any declarations. */
endLexicalEnvironment(): Statement[];
}
export interface TextSpan {
start: number;
length: number;
+1765 -418
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+10 -220
View File
@@ -1,8 +1,6 @@
/// <reference path="harness.ts" />
/// <reference path="runnerbase.ts" />
/// <reference path="typeWriter.ts" />
// In harness baselines, null is different than undefined. See `generateActual` in `harness.ts`.
/* tslint:disable:no-null-keyword */
const enum CompilerTestType {
Conformance,
@@ -136,27 +134,14 @@ class CompilerBaselineRunner extends RunnerBase {
otherFiles = undefined;
});
function getByteOrderMarkText(file: Harness.Compiler.GeneratedFile): string {
return file.writeByteOrderMark ? "\u00EF\u00BB\u00BF" : "";
}
function getErrorBaseline(toBeCompiled: Harness.Compiler.TestFile[], otherFiles: Harness.Compiler.TestFile[], result: Harness.Compiler.CompilerResult) {
return Harness.Compiler.getErrorBaseline(toBeCompiled.concat(otherFiles), result.errors);
}
// check errors
it("Correct errors for " + fileName, () => {
if (this.errors) {
Harness.Baseline.runBaseline("Correct errors for " + fileName, justName.replace(/\.tsx?$/, ".errors.txt"), (): string => {
if (result.errors.length === 0) return null;
return getErrorBaseline(toBeCompiled, otherFiles, result);
});
}
Harness.Compiler.doErrorBaseline(justName, toBeCompiled.concat(otherFiles), result.errors);
});
it (`Correct module resolution tracing for ${fileName}`, () => {
if (options.traceResolution) {
Harness.Baseline.runBaseline("Correct module resolution tracing for " + fileName, justName.replace(/\.tsx?$/, ".trace.json"), () => {
Harness.Baseline.runBaseline(justName.replace(/\.tsx?$/, ".trace.json"), () => {
return JSON.stringify(result.traceResults || [], undefined, 4);
});
}
@@ -165,11 +150,13 @@ class CompilerBaselineRunner extends RunnerBase {
// Source maps?
it("Correct sourcemap content for " + fileName, () => {
if (options.sourceMap || options.inlineSourceMap) {
Harness.Baseline.runBaseline("Correct sourcemap content for " + fileName, justName.replace(/\.tsx?$/, ".sourcemap.txt"), () => {
Harness.Baseline.runBaseline(justName.replace(/\.tsx?$/, ".sourcemap.txt"), () => {
const record = result.getSourceMapRecord();
if (options.noEmitOnError && result.errors.length !== 0 && record === undefined) {
// Because of the noEmitOnError option no files are created. We need to return null because baselining isn"t required.
if ((options.noEmitOnError && result.errors.length !== 0) || record === undefined) {
// Because of the noEmitOnError option no files are created. We need to return null because baselining isn't required.
/* tslint:disable:no-null-keyword */
return null;
/* tslint:enable:no-null-keyword */
}
return record;
});
@@ -178,87 +165,12 @@ class CompilerBaselineRunner extends RunnerBase {
it("Correct JS output for " + fileName, () => {
if (hasNonDtsFiles && this.emit) {
if (!options.noEmit && result.files.length === 0 && result.errors.length === 0) {
throw new Error("Expected at least one js file to be emitted or at least one error to be created.");
}
// check js output
Harness.Baseline.runBaseline("Correct JS output for " + fileName, justName.replace(/\.tsx?/, ".js"), () => {
let tsCode = "";
const tsSources = otherFiles.concat(toBeCompiled);
if (tsSources.length > 1) {
tsCode += "//// [" + fileName + "] ////\r\n\r\n";
}
for (let i = 0; i < tsSources.length; i++) {
tsCode += "//// [" + Harness.Path.getFileName(tsSources[i].unitName) + "]\r\n";
tsCode += tsSources[i].content + (i < (tsSources.length - 1) ? "\r\n" : "");
}
let jsCode = "";
for (let i = 0; i < result.files.length; i++) {
jsCode += "//// [" + Harness.Path.getFileName(result.files[i].fileName) + "]\r\n";
jsCode += getByteOrderMarkText(result.files[i]);
jsCode += result.files[i].code;
}
if (result.declFilesCode.length > 0) {
jsCode += "\r\n\r\n";
for (let i = 0; i < result.declFilesCode.length; i++) {
jsCode += "//// [" + Harness.Path.getFileName(result.declFilesCode[i].fileName) + "]\r\n";
jsCode += getByteOrderMarkText(result.declFilesCode[i]);
jsCode += result.declFilesCode[i].code;
}
}
const declFileCompilationResult =
Harness.Compiler.compileDeclarationFiles(
toBeCompiled, otherFiles, result, harnessSettings, options, /*currentDirectory*/ undefined);
if (declFileCompilationResult && declFileCompilationResult.declResult.errors.length) {
jsCode += "\r\n\r\n//// [DtsFileErrors]\r\n";
jsCode += "\r\n\r\n";
jsCode += getErrorBaseline(declFileCompilationResult.declInputFiles, declFileCompilationResult.declOtherFiles, declFileCompilationResult.declResult);
}
if (jsCode.length > 0) {
return tsCode + "\r\n\r\n" + jsCode;
}
else {
return null;
}
});
Harness.Compiler.doJsEmitBaseline(justName, fileName, options, result, toBeCompiled, otherFiles, harnessSettings);
}
});
it("Correct Sourcemap output for " + fileName, () => {
if (options.inlineSourceMap) {
if (result.sourceMaps.length > 0) {
throw new Error("No sourcemap files should be generated if inlineSourceMaps was set.");
}
return null;
}
else if (options.sourceMap) {
if (result.sourceMaps.length !== result.files.length) {
throw new Error("Number of sourcemap files should be same as js files.");
}
Harness.Baseline.runBaseline("Correct Sourcemap output for " + fileName, justName.replace(/\.tsx?/, ".js.map"), () => {
if (options.noEmitOnError && result.errors.length !== 0 && result.sourceMaps.length === 0) {
// We need to return null here or the runBaseLine will actually create a empty file.
// Baselining isn't required here because there is no output.
return null;
}
let sourceMapCode = "";
for (let i = 0; i < result.sourceMaps.length; i++) {
sourceMapCode += "//// [" + Harness.Path.getFileName(result.sourceMaps[i].fileName) + "]\r\n";
sourceMapCode += getByteOrderMarkText(result.sourceMaps[i]);
sourceMapCode += result.sourceMaps[i].code;
}
return sourceMapCode;
});
}
Harness.Compiler.doSourcemapBaseline(justName, options, result);
});
it("Correct type/symbol baselines for " + fileName, () => {
@@ -266,129 +178,7 @@ class CompilerBaselineRunner extends RunnerBase {
return;
}
// NEWTODO: Type baselines
if (result.errors.length !== 0) {
return;
}
// The full walker simulates the types that you would get from doing a full
// compile. The pull walker simulates the types you get when you just do
// a type query for a random node (like how the LS would do it). Most of the
// time, these will be the same. However, occasionally, they can be different.
// Specifically, when the compiler internally depends on symbol IDs to order
// things, then we may see different results because symbols can be created in a
// different order with 'pull' operations, and thus can produce slightly differing
// output.
//
// For example, with a full type check, we may see a type displayed as: number | string
// But with a pull type check, we may see it as: string | number
//
// These types are equivalent, but depend on what order the compiler observed
// certain parts of the program.
const program = result.program;
const allFiles = toBeCompiled.concat(otherFiles).filter(file => !!program.getSourceFile(file.unitName));
const fullWalker = new TypeWriterWalker(program, /*fullTypeCheck*/ true);
const fullResults = ts.createMap<TypeWriterResult[]>();
const pullResults = ts.createMap<TypeWriterResult[]>();
for (const sourceFile of allFiles) {
fullResults[sourceFile.unitName] = fullWalker.getTypeAndSymbols(sourceFile.unitName);
pullResults[sourceFile.unitName] = fullWalker.getTypeAndSymbols(sourceFile.unitName);
}
// Produce baselines. The first gives the types for all expressions.
// The second gives symbols for all identifiers.
let e1: Error, e2: Error;
try {
checkBaseLines(/*isSymbolBaseLine*/ false);
}
catch (e) {
e1 = e;
}
try {
checkBaseLines(/*isSymbolBaseLine*/ true);
}
catch (e) {
e2 = e;
}
if (e1 || e2) {
throw e1 || e2;
}
return;
function checkBaseLines(isSymbolBaseLine: boolean) {
const fullBaseLine = generateBaseLine(fullResults, isSymbolBaseLine);
const pullBaseLine = generateBaseLine(pullResults, isSymbolBaseLine);
const fullExtension = isSymbolBaseLine ? ".symbols" : ".types";
const pullExtension = isSymbolBaseLine ? ".symbols.pull" : ".types.pull";
if (fullBaseLine !== pullBaseLine) {
Harness.Baseline.runBaseline("Correct full information for " + fileName, justName.replace(/\.tsx?/, fullExtension), () => fullBaseLine);
Harness.Baseline.runBaseline("Correct pull information for " + fileName, justName.replace(/\.tsx?/, pullExtension), () => pullBaseLine);
}
else {
Harness.Baseline.runBaseline("Correct information for " + fileName, justName.replace(/\.tsx?/, fullExtension), () => fullBaseLine);
}
}
function generateBaseLine(typeWriterResults: ts.Map<TypeWriterResult[]>, isSymbolBaseline: boolean): string {
const typeLines: string[] = [];
const typeMap: { [fileName: string]: { [lineNum: number]: string[]; } } = {};
allFiles.forEach(file => {
const codeLines = file.content.split("\n");
typeWriterResults[file.unitName].forEach(result => {
if (isSymbolBaseline && !result.symbol) {
return;
}
const typeOrSymbolString = isSymbolBaseline ? result.symbol : result.type;
const formattedLine = result.sourceText.replace(/\r?\n/g, "") + " : " + typeOrSymbolString;
if (!typeMap[file.unitName]) {
typeMap[file.unitName] = {};
}
let typeInfo = [formattedLine];
const existingTypeInfo = typeMap[file.unitName][result.line];
if (existingTypeInfo) {
typeInfo = existingTypeInfo.concat(typeInfo);
}
typeMap[file.unitName][result.line] = typeInfo;
});
typeLines.push("=== " + file.unitName + " ===\r\n");
for (let i = 0; i < codeLines.length; i++) {
const currentCodeLine = codeLines[i];
typeLines.push(currentCodeLine + "\r\n");
if (typeMap[file.unitName]) {
const typeInfo = typeMap[file.unitName][i];
if (typeInfo) {
typeInfo.forEach(ty => {
typeLines.push(">" + ty + "\r\n");
});
if (i + 1 < codeLines.length && (codeLines[i + 1].match(/^\s*[{|}]\s*$/) || codeLines[i + 1].trim() === "")) {
}
else {
typeLines.push("\r\n");
}
}
}
else {
typeLines.push("No type information for this code.");
}
}
});
return typeLines.join("");
}
Harness.Compiler.doTypeAndSymbolBaseline(justName, result, toBeCompiled.concat(otherFiles).filter(file => !!result.program.getSourceFile(file.unitName)));
});
});
}
+418 -183
View File
@@ -88,6 +88,10 @@ namespace FourSlash {
marker?: Marker;
}
interface ImplementationLocationInformation extends ts.ImplementationLocation {
matched?: boolean;
}
export interface TextSpan {
start: number;
end: number;
@@ -202,10 +206,28 @@ namespace FourSlash {
// Whether or not we should format on keystrokes
public enableFormatting = true;
public formatCodeOptions: ts.FormatCodeOptions;
public formatCodeSettings: ts.FormatCodeSettings;
private inputFiles = ts.createMap<string>(); // Map between inputFile's fileName and its content for easily looking up when resolving references
private static getDisplayPartsJson(displayParts: ts.SymbolDisplayPart[]) {
let result = "";
ts.forEach(displayParts, part => {
if (result) {
result += ",\n ";
}
else {
result = "[\n ";
}
result += JSON.stringify(part);
});
if (result) {
result += "\n]";
}
return result;
}
// Add input file which has matched file name with the given reference-file path.
// This is necessary when resolveReference flag is specified
private addMatchedInputFile(referenceFilePath: string, extensions: string[]) {
@@ -245,22 +267,31 @@ namespace FourSlash {
constructor(private basePath: string, private testType: FourSlashTestType, public testData: FourSlashData) {
// Create a new Services Adapter
this.cancellationToken = new TestCancellationToken();
const compilationOptions = convertGlobalOptionsToCompilerOptions(this.testData.globalOptions);
if (compilationOptions.typeRoots) {
compilationOptions.typeRoots = compilationOptions.typeRoots.map(p => ts.getNormalizedAbsolutePath(p, this.basePath));
}
let compilationOptions = convertGlobalOptionsToCompilerOptions(this.testData.globalOptions);
compilationOptions.skipDefaultLibCheck = true;
const languageServiceAdapter = this.getLanguageServiceAdapter(testType, this.cancellationToken, compilationOptions);
this.languageServiceAdapterHost = languageServiceAdapter.getHost();
this.languageService = languageServiceAdapter.getLanguageService();
// Initialize the language service with all the scripts
let startResolveFileRef: FourSlashFile;
ts.forEach(testData.files, file => {
// Create map between fileName and its content for easily looking up when resolveReference flag is specified
this.inputFiles[file.fileName] = file.content;
if (ts.getBaseFileName(file.fileName).toLowerCase() === "tsconfig.json") {
const configJson = ts.parseConfigFileTextToJson(file.fileName, file.content);
assert.isTrue(configJson.config !== undefined);
// Extend our existing compiler options so that we can also support tsconfig only options
if (configJson.config.compilerOptions) {
const baseDirectory = ts.normalizePath(ts.getDirectoryPath(file.fileName));
const tsConfig = ts.convertCompilerOptionsFromJson(configJson.config.compilerOptions, baseDirectory, file.fileName);
if (!tsConfig.errors || !tsConfig.errors.length) {
compilationOptions = ts.extend(compilationOptions, tsConfig.options);
}
}
}
if (!startResolveFileRef && file.fileOptions[metadataOptionNames.resolveReference] === "true") {
startResolveFileRef = file;
}
@@ -270,6 +301,15 @@ namespace FourSlash {
}
});
if (compilationOptions.typeRoots) {
compilationOptions.typeRoots = compilationOptions.typeRoots.map(p => ts.getNormalizedAbsolutePath(p, this.basePath));
}
const languageServiceAdapter = this.getLanguageServiceAdapter(testType, this.cancellationToken, compilationOptions);
this.languageServiceAdapterHost = languageServiceAdapter.getHost();
this.languageService = languageServiceAdapter.getLanguageService();
if (startResolveFileRef) {
// Add the entry-point file itself into the languageServiceShimHost
this.languageServiceAdapterHost.addScript(startResolveFileRef.fileName, startResolveFileRef.content, /*isRootFile*/ true);
@@ -310,24 +350,26 @@ namespace FourSlash {
Harness.Compiler.getDefaultLibrarySourceFile().text, /*isRootFile*/ false);
}
this.formatCodeOptions = {
BaseIndentSize: 0,
IndentSize: 4,
TabSize: 4,
NewLineCharacter: Harness.IO.newLine(),
ConvertTabsToSpaces: true,
IndentStyle: ts.IndentStyle.Smart,
InsertSpaceAfterCommaDelimiter: true,
InsertSpaceAfterSemicolonInForStatements: true,
InsertSpaceBeforeAndAfterBinaryOperators: true,
InsertSpaceAfterKeywordsInControlFlowStatements: true,
InsertSpaceAfterFunctionKeywordForAnonymousFunctions: false,
InsertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis: false,
InsertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets: false,
InsertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces: false,
InsertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces: false,
PlaceOpenBraceOnNewLineForFunctions: false,
PlaceOpenBraceOnNewLineForControlBlocks: false,
this.formatCodeSettings = {
baseIndentSize: 0,
indentSize: 4,
tabSize: 4,
newLineCharacter: Harness.IO.newLine(),
convertTabsToSpaces: true,
indentStyle: ts.IndentStyle.Smart,
insertSpaceAfterCommaDelimiter: true,
insertSpaceAfterSemicolonInForStatements: true,
insertSpaceBeforeAndAfterBinaryOperators: true,
insertSpaceAfterKeywordsInControlFlowStatements: true,
insertSpaceAfterFunctionKeywordForAnonymousFunctions: false,
insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis: false,
insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets: false,
insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces: true,
insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces: false,
insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces: false,
insertSpaceAfterTypeAssertion: false,
placeOpenBraceOnNewLineForFunctions: false,
placeOpenBraceOnNewLineForControlBlocks: false,
};
// Open the first file by default
@@ -507,6 +549,67 @@ namespace FourSlash {
}
}
public verifyGoToDefinitionIs(endMarker: string | string[]) {
this.verifyGoToDefinitionWorker(endMarker instanceof Array ? endMarker : [endMarker]);
}
public verifyGoToDefinition(arg0: any, endMarkerNames?: string | string[]) {
if (endMarkerNames) {
this.verifyGoToDefinitionPlain(arg0, endMarkerNames);
}
else if (arg0 instanceof Array) {
const pairs: [string | string[], string | string[]][] = arg0;
for (const [start, end] of pairs) {
this.verifyGoToDefinitionPlain(start, end);
}
}
else {
const obj: { [startMarkerName: string]: string | string[] } = arg0;
for (const startMarkerName in obj) {
if (ts.hasProperty(obj, startMarkerName)) {
this.verifyGoToDefinitionPlain(startMarkerName, obj[startMarkerName]);
}
}
}
}
private verifyGoToDefinitionPlain(startMarkerNames: string | string[], endMarkerNames: string | string[]) {
if (startMarkerNames instanceof Array) {
for (const start of startMarkerNames) {
this.verifyGoToDefinitionSingle(start, endMarkerNames);
}
}
else {
this.verifyGoToDefinitionSingle(startMarkerNames, endMarkerNames);
}
}
public verifyGoToDefinitionForMarkers(markerNames: string[]) {
for (const markerName of markerNames) {
this.verifyGoToDefinitionSingle(`${markerName}Reference`, `${markerName}Definition`);
}
}
private verifyGoToDefinitionSingle(startMarkerName: string, endMarkerNames: string | string[]) {
this.goToMarker(startMarkerName);
this.verifyGoToDefinitionWorker(endMarkerNames instanceof Array ? endMarkerNames : [endMarkerNames]);
}
private verifyGoToDefinitionWorker(endMarkers: string[]) {
const definitions = this.languageService.getDefinitionAtPosition(this.activeFile.fileName, this.currentCaretPosition) || [];
if (endMarkers.length !== definitions.length) {
this.raiseError(`goToDefinitions failed - expected to find ${endMarkers.length} definitions but got ${definitions.length}`);
}
for (let i = 0; i < endMarkers.length; i++) {
const marker = this.getMarkerByName(endMarkers[i]), definition = definitions[i];
if (marker.fileName !== definition.fileName || marker.position !== definition.textSpan.start) {
this.raiseError(`goToDefinition failed for definition ${i}: expected ${marker.fileName} at ${marker.position}, got ${definition.fileName} at ${definition.textSpan.start}`);
}
}
}
public verifyGetEmitOutputForCurrentFile(expected: string): void {
const emit = this.languageService.getEmitOutput(this.activeFile.fileName);
if (emit.outputFiles.length !== 1) {
@@ -651,10 +754,10 @@ namespace FourSlash {
}
}
public verifyCompletionListContains(symbol: string, text?: string, documentation?: string, kind?: string) {
public verifyCompletionListContains(symbol: string, text?: string, documentation?: string, kind?: string, spanIndex?: number) {
const completions = this.getCompletionListAtCaret();
if (completions) {
this.assertItemInCompletionList(completions.entries, symbol, text, documentation, kind);
this.assertItemInCompletionList(completions.entries, symbol, text, documentation, kind, spanIndex);
}
else {
this.raiseError(`No completions at position '${this.currentCaretPosition}' when looking for '${symbol}'.`);
@@ -670,25 +773,32 @@ namespace FourSlash {
* @param expectedText the text associated with the symbol
* @param expectedDocumentation the documentation text associated with the symbol
* @param expectedKind the kind of symbol (see ScriptElementKind)
* @param spanIndex the index of the range that the completion item's replacement text span should match
*/
public verifyCompletionListDoesNotContain(symbol: string, expectedText?: string, expectedDocumentation?: string, expectedKind?: string) {
public verifyCompletionListDoesNotContain(symbol: string, expectedText?: string, expectedDocumentation?: string, expectedKind?: string, spanIndex?: number) {
const that = this;
let replacementSpan: ts.TextSpan;
if (spanIndex !== undefined) {
replacementSpan = this.getTextSpanForRangeAtIndex(spanIndex);
}
function filterByTextOrDocumentation(entry: ts.CompletionEntry) {
const details = that.getCompletionEntryDetails(entry.name);
const documentation = ts.displayPartsToString(details.documentation);
const text = ts.displayPartsToString(details.displayParts);
if (expectedText && expectedDocumentation) {
return (documentation === expectedDocumentation && text === expectedText) ? true : false;
// If any of the expected values are undefined, assume that users don't
// care about them.
if (replacementSpan && !TestState.textSpansEqual(replacementSpan, entry.replacementSpan)) {
return false;
}
else if (expectedText && !expectedDocumentation) {
return text === expectedText ? true : false;
else if (expectedText && text !== expectedText) {
return false;
}
else if (expectedDocumentation && !expectedText) {
return documentation === expectedDocumentation ? true : false;
else if (expectedDocumentation && documentation !== expectedDocumentation) {
return false;
}
// Because expectedText and expectedDocumentation are undefined, we assume that
// users don"t care to compare them so we will treat that entry as if the entry has matching text and documentation
// and keep it in the list of filtered entry.
return true;
}
@@ -712,6 +822,10 @@ namespace FourSlash {
if (expectedKind) {
error += "Expected kind: " + expectedKind + " to equal: " + filterCompletions[0].kind + ".";
}
if (replacementSpan) {
const spanText = filterCompletions[0].replacementSpan ? stringify(filterCompletions[0].replacementSpan) : undefined;
error += "Expected replacement span: " + stringify(replacementSpan) + " to equal: " + spanText + ".";
}
this.raiseError(error);
}
}
@@ -777,6 +891,20 @@ namespace FourSlash {
ts.forEachProperty(this.rangesByText(), ranges => this.verifyRangesReferenceEachOther(ranges));
}
public verifyDisplayPartsOfReferencedSymbol(expected: ts.SymbolDisplayPart[]) {
const referencedSymbols = this.findReferencesAtCaret();
if (referencedSymbols.length === 0) {
this.raiseError("No referenced symbols found at current caret position");
}
else if (referencedSymbols.length > 1) {
this.raiseError("More than one referenced symbol found");
}
assert.equal(TestState.getDisplayPartsJson(referencedSymbols[0].definition.displayParts),
TestState.getDisplayPartsJson(expected), this.messageAtLastKnownMarker("referenced symbol definition display parts"));
}
private verifyReferencesWorker(references: ts.ReferenceEntry[], fileName: string, start: number, end: number, isWriteAccess?: boolean, isDefinition?: boolean) {
for (let i = 0; i < references.length; i++) {
const reference = references[i];
@@ -811,6 +939,10 @@ namespace FourSlash {
return this.languageService.getReferencesAtPosition(this.activeFile.fileName, this.currentCaretPosition);
}
private findReferencesAtCaret() {
return this.languageService.findReferences(this.activeFile.fileName, this.currentCaretPosition);
}
public getSyntacticDiagnostics(expected: string) {
const diagnostics = this.languageService.getSyntacticDiagnostics(this.activeFile.fileName);
this.testDiagnostics(expected, diagnostics);
@@ -827,59 +959,47 @@ namespace FourSlash {
assert.equal(actual, expected);
}
public verifyQuickInfoString(negative: boolean, expectedText?: string, expectedDocumentation?: string) {
public verifyQuickInfoAt(markerName: string, expectedText: string, expectedDocumentation?: string) {
this.goToMarker(markerName);
this.verifyQuickInfoString(expectedText, expectedDocumentation);
}
public verifyQuickInfos(namesAndTexts: { [name: string]: string | [string, string] }) {
ts.forEachProperty(ts.createMap(namesAndTexts), (text, name) => {
if (text instanceof Array) {
assert(text.length === 2);
const [expectedText, expectedDocumentation] = text;
this.verifyQuickInfoAt(name, expectedText, expectedDocumentation);
}
else {
this.verifyQuickInfoAt(name, text);
}
});
}
public verifyQuickInfoString(expectedText: string, expectedDocumentation?: string) {
if (expectedDocumentation === "") {
throw new Error("Use 'undefined' instead");
}
const actualQuickInfo = this.languageService.getQuickInfoAtPosition(this.activeFile.fileName, this.currentCaretPosition);
const actualQuickInfoText = actualQuickInfo ? ts.displayPartsToString(actualQuickInfo.displayParts) : "";
const actualQuickInfoDocumentation = actualQuickInfo ? ts.displayPartsToString(actualQuickInfo.documentation) : "";
if (negative) {
if (expectedText !== undefined) {
assert.notEqual(actualQuickInfoText, expectedText, this.messageAtLastKnownMarker("quick info text"));
}
// TODO: should be '==='?
if (expectedDocumentation != undefined) {
assert.notEqual(actualQuickInfoDocumentation, expectedDocumentation, this.messageAtLastKnownMarker("quick info doc comment"));
}
}
else {
if (expectedText !== undefined) {
assert.equal(actualQuickInfoText, expectedText, this.messageAtLastKnownMarker("quick info text"));
}
// TODO: should be '==='?
if (expectedDocumentation != undefined) {
assert.equal(actualQuickInfoDocumentation, expectedDocumentation, this.assertionMessageAtLastKnownMarker("quick info doc"));
}
}
assert.equal(actualQuickInfoText, expectedText, this.messageAtLastKnownMarker("quick info text"));
assert.equal(actualQuickInfoDocumentation, expectedDocumentation || "", this.assertionMessageAtLastKnownMarker("quick info doc"));
}
public verifyQuickInfoDisplayParts(kind: string, kindModifiers: string, textSpan: { start: number; length: number; },
displayParts: ts.SymbolDisplayPart[],
documentation: ts.SymbolDisplayPart[]) {
function getDisplayPartsJson(displayParts: ts.SymbolDisplayPart[]) {
let result = "";
ts.forEach(displayParts, part => {
if (result) {
result += ",\n ";
}
else {
result = "[\n ";
}
result += JSON.stringify(part);
});
if (result) {
result += "\n]";
}
return result;
}
const actualQuickInfo = this.languageService.getQuickInfoAtPosition(this.activeFile.fileName, this.currentCaretPosition);
assert.equal(actualQuickInfo.kind, kind, this.messageAtLastKnownMarker("QuickInfo kind"));
assert.equal(actualQuickInfo.kindModifiers, kindModifiers, this.messageAtLastKnownMarker("QuickInfo kindModifiers"));
assert.equal(JSON.stringify(actualQuickInfo.textSpan), JSON.stringify(textSpan), this.messageAtLastKnownMarker("QuickInfo textSpan"));
assert.equal(getDisplayPartsJson(actualQuickInfo.displayParts), getDisplayPartsJson(displayParts), this.messageAtLastKnownMarker("QuickInfo displayParts"));
assert.equal(getDisplayPartsJson(actualQuickInfo.documentation), getDisplayPartsJson(documentation), this.messageAtLastKnownMarker("QuickInfo documentation"));
assert.equal(TestState.getDisplayPartsJson(actualQuickInfo.displayParts), TestState.getDisplayPartsJson(displayParts), this.messageAtLastKnownMarker("QuickInfo displayParts"));
assert.equal(TestState.getDisplayPartsJson(actualQuickInfo.documentation), TestState.getDisplayPartsJson(documentation), this.messageAtLastKnownMarker("QuickInfo documentation"));
}
public verifyRenameLocations(findInStrings: boolean, findInComments: boolean, ranges?: Range[]) {
@@ -1132,12 +1252,10 @@ namespace FourSlash {
}
Harness.Baseline.runBaseline(
"Breakpoint Locations for " + this.activeFile.fileName,
baselineFile,
() => {
return this.baselineCurrentFileLocations(pos => this.getBreakpointStatementLocation(pos));
},
true /* run immediately */);
});
}
public baselineGetEmitOutput() {
@@ -1159,7 +1277,6 @@ namespace FourSlash {
}
Harness.Baseline.runBaseline(
"Generate getEmitOutput baseline : " + emitFiles.join(" "),
this.testData.globalOptions[metadataOptionNames.baselineFile],
() => {
let resultString = "";
@@ -1185,8 +1302,23 @@ namespace FourSlash {
});
return resultString;
},
true /* run immediately */);
});
}
public baselineQuickInfo() {
let baselineFile = this.testData.globalOptions[metadataOptionNames.baselineFile];
if (!baselineFile) {
baselineFile = ts.getBaseFileName(this.activeFile.fileName).replace(".ts", ".baseline");
}
Harness.Baseline.runBaseline(
baselineFile,
() => stringify(
this.testData.markers.map(marker => ({
marker,
quickInfo: this.languageService.getQuickInfoAtPosition(marker.fileName, marker.position)
}))
));
}
public printBreakpointLocation(pos: number) {
@@ -1296,7 +1428,7 @@ namespace FourSlash {
// Handle post-keystroke formatting
if (this.enableFormatting) {
const edits = this.languageService.getFormattingEditsAfterKeystroke(this.activeFile.fileName, offset, ch, this.formatCodeOptions);
const edits = this.languageService.getFormattingEditsAfterKeystroke(this.activeFile.fileName, offset, ch, this.formatCodeSettings);
if (edits.length) {
offset += this.applyEdits(this.activeFile.fileName, edits, /*isFormattingEdit*/ true);
// this.checkPostEditInvariants();
@@ -1334,7 +1466,7 @@ namespace FourSlash {
// Handle post-keystroke formatting
if (this.enableFormatting) {
const edits = this.languageService.getFormattingEditsAfterKeystroke(this.activeFile.fileName, offset, ch, this.formatCodeOptions);
const edits = this.languageService.getFormattingEditsAfterKeystroke(this.activeFile.fileName, offset, ch, this.formatCodeSettings);
if (edits.length) {
offset += this.applyEdits(this.activeFile.fileName, edits, /*isFormattingEdit*/ true);
}
@@ -1382,7 +1514,7 @@ namespace FourSlash {
// Handle post-keystroke formatting
if (this.enableFormatting) {
const edits = this.languageService.getFormattingEditsAfterKeystroke(this.activeFile.fileName, offset, ch, this.formatCodeOptions);
const edits = this.languageService.getFormattingEditsAfterKeystroke(this.activeFile.fileName, offset, ch, this.formatCodeSettings);
if (edits.length) {
offset += this.applyEdits(this.activeFile.fileName, edits, /*isFormattingEdit*/ true);
}
@@ -1406,7 +1538,7 @@ namespace FourSlash {
// Handle formatting
if (this.enableFormatting) {
const edits = this.languageService.getFormattingEditsForRange(this.activeFile.fileName, start, offset, this.formatCodeOptions);
const edits = this.languageService.getFormattingEditsForRange(this.activeFile.fileName, start, offset, this.formatCodeSettings);
if (edits.length) {
offset += this.applyEdits(this.activeFile.fileName, edits, /*isFormattingEdit*/ true);
}
@@ -1479,30 +1611,30 @@ namespace FourSlash {
return runningOffset;
}
public copyFormatOptions(): ts.FormatCodeOptions {
return ts.clone(this.formatCodeOptions);
public copyFormatOptions(): ts.FormatCodeSettings {
return ts.clone(this.formatCodeSettings);
}
public setFormatOptions(formatCodeOptions: ts.FormatCodeOptions): ts.FormatCodeOptions {
const oldFormatCodeOptions = this.formatCodeOptions;
this.formatCodeOptions = formatCodeOptions;
public setFormatOptions(formatCodeOptions: ts.FormatCodeOptions | ts.FormatCodeSettings): ts.FormatCodeSettings {
const oldFormatCodeOptions = this.formatCodeSettings;
this.formatCodeSettings = ts.toEditorSettings(formatCodeOptions);
return oldFormatCodeOptions;
}
public formatDocument() {
const edits = this.languageService.getFormattingEditsForDocument(this.activeFile.fileName, this.formatCodeOptions);
const edits = this.languageService.getFormattingEditsForDocument(this.activeFile.fileName, this.formatCodeSettings);
this.currentCaretPosition += this.applyEdits(this.activeFile.fileName, edits, /*isFormattingEdit*/ true);
this.fixCaretPosition();
}
public formatSelection(start: number, end: number) {
const edits = this.languageService.getFormattingEditsForRange(this.activeFile.fileName, start, end, this.formatCodeOptions);
const edits = this.languageService.getFormattingEditsForRange(this.activeFile.fileName, start, end, this.formatCodeSettings);
this.currentCaretPosition += this.applyEdits(this.activeFile.fileName, edits, /*isFormattingEdit*/ true);
this.fixCaretPosition();
}
public formatOnType(pos: number, key: string) {
const edits = this.languageService.getFormattingEditsAfterKeystroke(this.activeFile.fileName, pos, key, this.formatCodeOptions);
const edits = this.languageService.getFormattingEditsAfterKeystroke(this.activeFile.fileName, pos, key, this.formatCodeSettings);
this.currentCaretPosition += this.applyEdits(this.activeFile.fileName, edits, /*isFormattingEdit*/ true);
this.fixCaretPosition();
}
@@ -1547,25 +1679,10 @@ namespace FourSlash {
this.goToPosition(len);
}
public goToDefinition(definitionIndex: number) {
const definitions = this.languageService.getDefinitionAtPosition(this.activeFile.fileName, this.currentCaretPosition);
if (!definitions || !definitions.length) {
this.raiseError("goToDefinition failed - expected to at least one definition location but got 0");
}
if (definitionIndex >= definitions.length) {
this.raiseError(`goToDefinition failed - definitionIndex value (${definitionIndex}) exceeds definition list size (${definitions.length})`);
}
const definition = definitions[definitionIndex];
this.openFile(definition.fileName);
this.currentCaretPosition = definition.textSpan.start;
}
public goToTypeDefinition(definitionIndex: number) {
const definitions = this.languageService.getTypeDefinitionAtPosition(this.activeFile.fileName, this.currentCaretPosition);
if (!definitions || !definitions.length) {
this.raiseError("goToTypeDefinition failed - expected to at least one definition location but got 0");
this.raiseError("goToTypeDefinition failed - expected to find at least one definition location but got 0");
}
if (definitionIndex >= definitions.length) {
@@ -1577,28 +1694,6 @@ namespace FourSlash {
this.currentCaretPosition = definition.textSpan.start;
}
public verifyDefinitionLocationExists(negative: boolean) {
const definitions = this.languageService.getDefinitionAtPosition(this.activeFile.fileName, this.currentCaretPosition);
const foundDefinitions = definitions && definitions.length;
if (foundDefinitions && negative) {
this.raiseError(`goToDefinition - expected to 0 definition locations but got ${definitions.length}`);
}
else if (!foundDefinitions && !negative) {
this.raiseError("goToDefinition - expected to at least one definition location but got 0");
}
}
public verifyDefinitionsCount(negative: boolean, expectedCount: number) {
const assertFn = negative ? assert.notEqual : assert.equal;
const definitions = this.languageService.getDefinitionAtPosition(this.activeFile.fileName, this.currentCaretPosition);
const actualCount = definitions && definitions.length || 0;
assertFn(actualCount, expectedCount, this.messageAtLastKnownMarker("Definitions Count"));
}
public verifyTypeDefinitionsCount(negative: boolean, expectedCount: number) {
const assertFn = negative ? assert.notEqual : assert.equal;
@@ -1608,17 +1703,98 @@ namespace FourSlash {
assertFn(actualCount, expectedCount, this.messageAtLastKnownMarker("Type definitions Count"));
}
public verifyDefinitionsName(negative: boolean, expectedName: string, expectedContainerName: string) {
public verifyImplementationListIsEmpty(negative: boolean) {
const implementations = this.languageService.getImplementationAtPosition(this.activeFile.fileName, this.currentCaretPosition);
if (negative) {
assert.isTrue(implementations && implementations.length > 0, "Expected at least one implementation but got 0");
}
else {
assert.isUndefined(implementations, "Expected implementation list to be empty but implementations returned");
}
}
public verifyGoToDefinitionName(expectedName: string, expectedContainerName: string) {
const definitions = this.languageService.getDefinitionAtPosition(this.activeFile.fileName, this.currentCaretPosition);
const actualDefinitionName = definitions && definitions.length ? definitions[0].name : "";
const actualDefinitionContainerName = definitions && definitions.length ? definitions[0].containerName : "";
if (negative) {
assert.notEqual(actualDefinitionName, expectedName, this.messageAtLastKnownMarker("Definition Info Name"));
assert.notEqual(actualDefinitionContainerName, expectedContainerName, this.messageAtLastKnownMarker("Definition Info Container Name"));
assert.equal(actualDefinitionName, expectedName, this.messageAtLastKnownMarker("Definition Info Name"));
assert.equal(actualDefinitionContainerName, expectedContainerName, this.messageAtLastKnownMarker("Definition Info Container Name"));
}
public goToImplementation() {
const implementations = this.languageService.getImplementationAtPosition(this.activeFile.fileName, this.currentCaretPosition);
if (!implementations || !implementations.length) {
this.raiseError("goToImplementation failed - expected to find at least one implementation location but got 0");
}
else {
assert.equal(actualDefinitionName, expectedName, this.messageAtLastKnownMarker("Definition Info Name"));
assert.equal(actualDefinitionContainerName, expectedContainerName, this.messageAtLastKnownMarker("Definition Info Container Name"));
if (implementations.length > 1) {
this.raiseError(`goToImplementation failed - more than 1 implementation returned (${implementations.length})`);
}
const implementation = implementations[0];
this.openFile(implementation.fileName);
this.currentCaretPosition = implementation.textSpan.start;
}
public verifyRangesInImplementationList(markerName: string) {
this.goToMarker(markerName);
const implementations: ImplementationLocationInformation[] = this.languageService.getImplementationAtPosition(this.activeFile.fileName, this.currentCaretPosition);
if (!implementations || !implementations.length) {
this.raiseError("verifyRangesInImplementationList failed - expected to find at least one implementation location but got 0");
}
for (let i = 0; i < implementations.length; i++) {
for (let j = 0; j < implementations.length; j++) {
if (i !== j && implementationsAreEqual(implementations[i], implementations[j])) {
const { textSpan, fileName } = implementations[i];
const end = textSpan.start + textSpan.length;
this.raiseError(`Duplicate implementations returned for range (${textSpan.start}, ${end}) in ${fileName}`);
}
}
}
const ranges = this.getRanges();
if (!ranges || !ranges.length) {
this.raiseError("verifyRangesInImplementationList failed - expected to find at least one range in test source");
}
const unsatisfiedRanges: Range[] = [];
for (const range of ranges) {
const length = range.end - range.start;
const matchingImpl = ts.find(implementations, impl =>
range.fileName === impl.fileName && range.start === impl.textSpan.start && length === impl.textSpan.length);
if (matchingImpl) {
matchingImpl.matched = true;
}
else {
unsatisfiedRanges.push(range);
}
}
const unmatchedImplementations = implementations.filter(impl => !impl.matched);
if (unmatchedImplementations.length || unsatisfiedRanges.length) {
let error = "Not all ranges or implementations are satisfied";
if (unsatisfiedRanges.length) {
error += "\nUnsatisfied ranges:";
for (const range of unsatisfiedRanges) {
error += `\n (${range.start}, ${range.end}) in ${range.fileName}: ${this.rangeText(range)}`;
}
}
if (unmatchedImplementations.length) {
error += "\nUnmatched implementations:";
for (const impl of unmatchedImplementations) {
const end = impl.textSpan.start + impl.textSpan.length;
error += `\n (${impl.textSpan.start}, ${end}) in ${impl.fileName}: ${this.getFileContent(impl.fileName).slice(impl.textSpan.start, end)}`;
}
}
this.raiseError(error);
}
function implementationsAreEqual(a: ImplementationLocationInformation, b: ImplementationLocationInformation) {
return a.fileName === b.fileName && TestState.textSpansEqual(a.textSpan, b.textSpan);
}
}
@@ -1627,6 +1803,10 @@ namespace FourSlash {
return this.testData.markers.slice(0);
}
public getMarkerNames(): string[] {
return Object.keys(this.testData.markerPositions);
}
public getRanges(): Range[] {
return this.testData.ranges;
}
@@ -1635,8 +1815,7 @@ namespace FourSlash {
const result = ts.createMap<Range[]>();
for (const range of this.getRanges()) {
const text = this.rangeText(range);
const ranges = result[text] || (result[text] = []);
ranges.push(range);
ts.multiMapAdd(result, text, range);
}
return result;
}
@@ -1656,11 +1835,9 @@ namespace FourSlash {
}
private getIndentation(fileName: string, position: number, indentStyle: ts.IndentStyle, baseIndentSize: number): number {
const formatOptions = ts.clone(this.formatCodeOptions);
formatOptions.IndentStyle = indentStyle;
formatOptions.BaseIndentSize = baseIndentSize;
const formatOptions = ts.clone(this.formatCodeSettings);
formatOptions.indentStyle = indentStyle;
formatOptions.baseIndentSize = baseIndentSize;
return this.languageService.getIndentationAtPosition(fileName, position, formatOptions);
}
@@ -1730,13 +1907,11 @@ namespace FourSlash {
public baselineCurrentFileNameOrDottedNameSpans() {
Harness.Baseline.runBaseline(
"Name OrDottedNameSpans for " + this.activeFile.fileName,
this.testData.globalOptions[metadataOptionNames.baselineFile],
() => {
return this.baselineCurrentFileLocations(pos =>
this.getNameOrDottedNameSpan(pos));
},
true /* run immediately */);
});
}
public printNameOrDottedNameSpans(pos: number) {
@@ -1952,11 +2127,12 @@ namespace FourSlash {
}
/*
Check number of navigationItems which match both searchValue and matchKind.
Check number of navigationItems which match both searchValue and matchKind,
if a filename is passed in, limit the results to that file.
Report an error if expected value and actual value do not match.
*/
public verifyNavigationItemsCount(expected: number, searchValue: string, matchKind?: string) {
const items = this.languageService.getNavigateToItems(searchValue);
public verifyNavigationItemsCount(expected: number, searchValue: string, matchKind?: string, fileName?: string) {
const items = this.languageService.getNavigateToItems(searchValue, /*maxResultCount*/ undefined, fileName);
let actual = 0;
let item: ts.NavigateToItem;
@@ -2155,7 +2331,7 @@ namespace FourSlash {
return text.substring(startPos, endPos);
}
private assertItemInCompletionList(items: ts.CompletionEntry[], name: string, text?: string, documentation?: string, kind?: string) {
private assertItemInCompletionList(items: ts.CompletionEntry[], name: string, text?: string, documentation?: string, kind?: string, spanIndex?: number) {
for (let i = 0; i < items.length; i++) {
const item = items[i];
if (item.name === name) {
@@ -2174,6 +2350,11 @@ namespace FourSlash {
assert.equal(item.kind, kind, this.assertionMessageAtLastKnownMarker("completion item kind for " + name));
}
if (spanIndex !== undefined) {
const span = this.getTextSpanForRangeAtIndex(spanIndex);
assert.isTrue(TestState.textSpansEqual(span, item.replacementSpan), this.assertionMessageAtLastKnownMarker(stringify(span) + " does not equal " + stringify(item.replacementSpan) + " replacement span for " + name));
}
return;
}
}
@@ -2230,6 +2411,17 @@ namespace FourSlash {
return `line ${(pos.line + 1)}, col ${pos.character}`;
}
private getTextSpanForRangeAtIndex(index: number): ts.TextSpan {
const ranges = this.getRanges();
if (ranges && ranges.length > index) {
const range = ranges[index];
return { start: range.start, length: range.end - range.start };
}
else {
this.raiseError("Supplied span index: " + index + " does not exist in range list of size: " + (ranges ? 0 : ranges.length));
}
}
public getMarkerByName(markerName: string) {
const markerPos = this.testData.markerPositions[markerName];
if (markerPos === undefined) {
@@ -2253,6 +2445,10 @@ namespace FourSlash {
public resetCancelled(): void {
this.cancellationToken.resetCancelled();
}
private static textSpansEqual(a: ts.TextSpan, b: ts.TextSpan) {
return a && b && a.start === b.start && a.length === b.length;
}
}
export function runFourSlashTest(basePath: string, testType: FourSlashTestType, fileName: string) {
@@ -2261,12 +2457,16 @@ namespace FourSlash {
}
export function runFourSlashTestContent(basePath: string, testType: FourSlashTestType, content: string, fileName: string): void {
// Give file paths an absolute path for the virtual file system
const absoluteBasePath = ts.combinePaths(Harness.virtualFileSystemRoot, basePath);
const absoluteFileName = ts.combinePaths(Harness.virtualFileSystemRoot, fileName);
// Parse out the files and their metadata
const testData = parseTestData(basePath, content, fileName);
const state = new TestState(basePath, testType, testData);
const testData = parseTestData(absoluteBasePath, content, absoluteFileName);
const state = new TestState(absoluteBasePath, testType, testData);
const output = ts.transpileModule(content, { reportDiagnostics: true });
if (output.diagnostics.length > 0) {
throw new Error(`Syntax error in ${basePath}: ${output.diagnostics[0].messageText}`);
throw new Error(`Syntax error in ${absoluteBasePath}: ${output.diagnostics[0].messageText}`);
}
runCode(output.outputText, state);
}
@@ -2731,6 +2931,10 @@ namespace FourSlashInterface {
return this.state.getMarkers();
}
public markerNames(): string[] {
return this.state.getMarkerNames();
}
public marker(name?: string): FourSlash.Marker {
return this.state.getMarkerByName(name);
}
@@ -2766,14 +2970,14 @@ namespace FourSlashInterface {
this.state.goToEOF();
}
public definition(definitionIndex = 0) {
this.state.goToDefinition(definitionIndex);
}
public type(definitionIndex = 0) {
this.state.goToTypeDefinition(definitionIndex);
}
public implementation() {
this.state.goToImplementation();
}
public position(position: number, fileIndex?: number): void;
public position(position: number, fileName?: string): void;
public position(position: number, fileNameOrIndex?: any): void {
@@ -2819,12 +3023,12 @@ namespace FourSlashInterface {
// Verifies the completion list contains the specified symbol. The
// completion list is brought up if necessary
public completionListContains(symbol: string, text?: string, documentation?: string, kind?: string) {
public completionListContains(symbol: string, text?: string, documentation?: string, kind?: string, spanIndex?: number) {
if (this.negative) {
this.state.verifyCompletionListDoesNotContain(symbol, text, documentation, kind);
this.state.verifyCompletionListDoesNotContain(symbol, text, documentation, kind, spanIndex);
}
else {
this.state.verifyCompletionListContains(symbol, text, documentation, kind);
this.state.verifyCompletionListContains(symbol, text, documentation, kind, spanIndex);
}
}
@@ -2866,28 +3070,16 @@ namespace FourSlashInterface {
this.state.verifyErrorExistsAfterMarker(markerName, !this.negative, /*after*/ false);
}
public quickInfoIs(expectedText?: string, expectedDocumentation?: string) {
this.state.verifyQuickInfoString(this.negative, expectedText, expectedDocumentation);
}
public quickInfoExists() {
this.state.verifyQuickInfoExists(this.negative);
}
public definitionCountIs(expectedCount: number) {
this.state.verifyDefinitionsCount(this.negative, expectedCount);
}
public typeDefinitionCountIs(expectedCount: number) {
this.state.verifyTypeDefinitionsCount(this.negative, expectedCount);
}
public definitionLocationExists() {
this.state.verifyDefinitionLocationExists(this.negative);
}
public verifyDefinitionsName(name: string, containerName: string) {
this.state.verifyDefinitionsName(this.negative, name, containerName);
public implementationListIsEmpty() {
this.state.verifyImplementationListIsEmpty(this.negative);
}
public isValidBraceCompletionAtPosition(openingBrace: string) {
@@ -2900,6 +3092,18 @@ namespace FourSlashInterface {
super(state);
}
public quickInfoIs(expectedText: string, expectedDocumentation?: string) {
this.state.verifyQuickInfoString(expectedText, expectedDocumentation);
}
public quickInfoAt(markerName: string, expectedText?: string, expectedDocumentation?: string) {
this.state.verifyQuickInfoAt(markerName, expectedText, expectedDocumentation);
}
public quickInfos(namesAndTexts: { [name: string]: string }) {
this.state.verifyQuickInfos(namesAndTexts);
}
public caretAtMarker(markerName?: string) {
this.state.verifyCaretAtMarker(markerName);
}
@@ -2933,6 +3137,25 @@ namespace FourSlashInterface {
this.state.verifyCurrentFileContent(text);
}
public goToDefinitionIs(endMarkers: string | string[]) {
this.state.verifyGoToDefinitionIs(endMarkers);
}
public goToDefinition(startMarkerName: string | string[], endMarkerName: string | string[]): void;
public goToDefinition(startsAndEnds: [string | string[], string | string[]][]): void;
public goToDefinition(startsAndEnds: { [startMarkerName: string]: string | string[] }): void;
public goToDefinition(arg0: any, endMarkerName?: string | string[]) {
this.state.verifyGoToDefinition(arg0, endMarkerName);
}
public goToDefinitionForMarkers(...markerNames: string[]) {
this.state.verifyGoToDefinitionForMarkers(markerNames);
}
public goToDefinitionName(name: string, containerName: string) {
this.state.verifyGoToDefinitionName(name, containerName);
}
public verifyGetEmitOutputForCurrentFile(expected: string): void {
this.state.verifyGetEmitOutputForCurrentFile(expected);
}
@@ -2953,6 +3176,10 @@ namespace FourSlashInterface {
this.state.verifyRangesReferenceEachOther(ranges);
}
public findReferencesDefinitionDisplayPartsAtCaretAre(expected: ts.SymbolDisplayPart[]) {
this.state.verifyDisplayPartsOfReferencedSymbol(expected);
}
public rangesWithSameTextReferenceEachOther() {
this.state.verifyRangesWithSameTextReferenceEachOther();
}
@@ -3005,6 +3232,10 @@ namespace FourSlashInterface {
this.state.baselineGetEmitOutput();
}
public baselineQuickInfo() {
this.state.baselineQuickInfo();
}
public nameOrDottedNameSpanTextIs(text: string) {
this.state.verifyCurrentNameOrDottedNameSpanText(text);
}
@@ -3037,8 +3268,8 @@ namespace FourSlashInterface {
this.state.verifyNavigationBar(json);
}
public navigationItemsListCount(count: number, searchValue: string, matchKind?: string) {
this.state.verifyNavigationItemsCount(count, searchValue, matchKind);
public navigationItemsListCount(count: number, searchValue: string, matchKind?: string, fileName?: string) {
this.state.verifyNavigationItemsCount(count, searchValue, matchKind, fileName);
}
public navigationItemsListContains(
@@ -3119,6 +3350,10 @@ namespace FourSlashInterface {
public ProjectInfo(expected: string[]) {
this.state.verifyProjectInfo(expected);
}
public allRangesAppearInImplementationList(markerName: string) {
this.state.verifyRangesInImplementationList(markerName);
}
}
export class Edit {
@@ -3248,7 +3483,7 @@ namespace FourSlashInterface {
this.state.formatDocument();
}
public copyFormatOptions(): ts.FormatCodeOptions {
public copyFormatOptions(): ts.FormatCodeSettings {
return this.state.copyFormatOptions();
}
@@ -3268,7 +3503,7 @@ namespace FourSlashInterface {
public setOption(name: string, value: string): void;
public setOption(name: string, value: boolean): void;
public setOption(name: string, value: any): void {
this.state.formatCodeOptions[name] = value;
(<any>this.state.formatCodeSettings)[name] = value;
}
}
+345 -57
View File
@@ -374,7 +374,7 @@ namespace Utils {
// call this on both nodes to ensure all propagated flags have been set (and thus can be
// compared).
assert.equal(ts.containsParseError(node1), ts.containsParseError(node2));
assert.equal(node1.flags, node2.flags, "node1.flags !== node2.flags");
assert.equal(node1.flags & ~ts.NodeFlags.ReachabilityAndEmitFlags, node2.flags & ~ts.NodeFlags.ReachabilityAndEmitFlags, "node1.flags !== node2.flags");
ts.forEachChild(node1,
child1 => {
@@ -416,6 +416,60 @@ namespace Utils {
throw new Error("Could not find child in parent");
}
const maxHarnessFrames = 1;
export function filterStack(error: Error, stackTraceLimit: number = Infinity) {
const stack = <string>(<any>error).stack;
if (stack) {
const lines = stack.split(/\r\n?|\n/g);
const filtered: string[] = [];
let frameCount = 0;
let harnessFrameCount = 0;
for (let line of lines) {
if (isStackFrame(line)) {
if (frameCount >= stackTraceLimit
|| isMocha(line)
|| isNode(line)) {
continue;
}
if (isHarness(line)) {
if (harnessFrameCount >= maxHarnessFrames) {
continue;
}
harnessFrameCount++;
}
line = line.replace(/\bfile:\/\/\/(.*?)(?=(:\d+)*($|\)))/, (_, path) => ts.sys.resolvePath(path));
frameCount++;
}
filtered.push(line);
}
(<any>error).stack = filtered.join(Harness.IO.newLine());
}
return error;
}
function isStackFrame(line: string) {
return /^\s+at\s/.test(line);
}
function isMocha(line: string) {
return /[\\/](node_modules|components)[\\/]mocha(js)?[\\/]|[\\/]mocha\.js/.test(line);
}
function isNode(line: string) {
return /\((timers|events|node|module)\.js:/.test(line);
}
function isHarness(line: string) {
return /[\\/]src[\\/]harness[\\/]|[\\/]run\.js/.test(line);
}
}
namespace Harness.Path {
@@ -452,12 +506,17 @@ namespace Harness {
getExecutingFilePath(): string;
exit(exitCode?: number): void;
readDirectory(path: string, extension?: string[], exclude?: string[], include?: string[]): string[];
tryEnableSourceMapsForHost?(): void;
getEnvironmentVariable?(name: string): string;
}
export var IO: IO;
// harness always uses one kind of new line
const harnessNewLine = "\r\n";
// Root for file paths that are stored in a virtual file system
export const virtualFileSystemRoot = "/";
namespace IOImpl {
declare class Enumerator {
public atEnd(): boolean;
@@ -490,6 +549,7 @@ namespace Harness {
export const directoryExists: typeof IO.directoryExists = fso.FolderExists;
export const fileExists: typeof IO.fileExists = fso.FileExists;
export const log: typeof IO.log = global.WScript && global.WScript.StdOut.WriteLine;
export const getEnvironmentVariable: typeof IO.getEnvironmentVariable = name => ts.sys.getEnvironmentVariable(name);
export const readDirectory: typeof IO.readDirectory = (path, extension, exclude, include) => ts.sys.readDirectory(path, extension, exclude, include);
export function createDirectory(path: string) {
@@ -559,7 +619,13 @@ namespace Harness {
export const writeFile: typeof IO.writeFile = (path, content) => ts.sys.writeFile(path, content);
export const fileExists: typeof IO.fileExists = fs.existsSync;
export const log: typeof IO.log = s => console.log(s);
export const getEnvironmentVariable: typeof IO.getEnvironmentVariable = name => ts.sys.getEnvironmentVariable(name);
export function tryEnableSourceMapsForHost() {
if (ts.sys.tryEnableSourceMapsForHost) {
ts.sys.tryEnableSourceMapsForHost();
}
}
export const readDirectory: typeof IO.readDirectory = (path, extension, exclude, include) => ts.sys.readDirectory(path, extension, exclude, include);
export function createDirectory(path: string) {
@@ -710,7 +776,16 @@ namespace Harness {
return dirPath;
}
export let directoryName: typeof IO.directoryName = Utils.memoize(directoryNameImpl);
export const resolvePath = (path: string) => directoryName(path);
export function resolvePath(path: string) {
const response = Http.getFileFromServerSync(serverRoot + path + "?resolve=true");
if (response.status === 200) {
return response.responseText;
}
else {
return undefined;
}
}
export function fileExists(path: string): boolean {
const response = Http.getFileFromServerSync(serverRoot + path);
@@ -784,7 +859,9 @@ namespace Harness {
namespace Harness {
export const libFolder = "built/local/";
const tcServicesFileName = ts.combinePaths(libFolder, Utils.getExecutionEnvironment() === Utils.ExecutionEnvironment.Browser ? "typescriptServicesInBrowserTest.js" : "typescriptServices.js");
export const tcServicesFile = IO.readFile(tcServicesFileName);
export const tcServicesFile = IO.readFile(tcServicesFileName) + (Utils.getExecutionEnvironment() !== Utils.ExecutionEnvironment.Browser
? IO.newLine() + `//# sourceURL=${IO.resolvePath(tcServicesFileName)}`
: "");
export interface SourceMapEmitterCallback {
(emittedFile: string, emittedLine: number, emittedColumn: number, sourceFile: string, sourceLine: number, sourceColumn: number, sourceName: string): void;
@@ -1328,6 +1405,233 @@ namespace Harness {
Harness.IO.newLine() + Harness.IO.newLine() + outputLines.join("\r\n");
}
export function doErrorBaseline(baselinePath: string, inputFiles: TestFile[], errors: ts.Diagnostic[]) {
Harness.Baseline.runBaseline(baselinePath.replace(/\.tsx?$/, ".errors.txt"), (): string => {
if (!errors || (errors.length === 0)) {
/* tslint:disable:no-null-keyword */
return null;
/* tslint:enable:no-null-keyword */
}
return getErrorBaseline(inputFiles, errors);
});
}
export function doTypeAndSymbolBaseline(baselinePath: string, result: CompilerResult, allFiles: {unitName: string, content: string}[], opts?: Harness.Baseline.BaselineOptions) {
if (result.errors.length !== 0) {
return;
}
// The full walker simulates the types that you would get from doing a full
// compile. The pull walker simulates the types you get when you just do
// a type query for a random node (like how the LS would do it). Most of the
// time, these will be the same. However, occasionally, they can be different.
// Specifically, when the compiler internally depends on symbol IDs to order
// things, then we may see different results because symbols can be created in a
// different order with 'pull' operations, and thus can produce slightly differing
// output.
//
// For example, with a full type check, we may see a type displayed as: number | string
// But with a pull type check, we may see it as: string | number
//
// These types are equivalent, but depend on what order the compiler observed
// certain parts of the program.
const program = result.program;
const fullWalker = new TypeWriterWalker(program, /*fullTypeCheck*/ true);
const fullResults = ts.createMap<TypeWriterResult[]>();
for (const sourceFile of allFiles) {
fullResults[sourceFile.unitName] = fullWalker.getTypeAndSymbols(sourceFile.unitName);
}
// Produce baselines. The first gives the types for all expressions.
// The second gives symbols for all identifiers.
let typesError: Error, symbolsError: Error;
try {
checkBaseLines(/*isSymbolBaseLine*/ false);
}
catch (e) {
typesError = e;
}
try {
checkBaseLines(/*isSymbolBaseLine*/ true);
}
catch (e) {
symbolsError = e;
}
if (typesError && symbolsError) {
throw new Error(typesError.message + ts.sys.newLine + symbolsError.message);
}
if (typesError) {
throw typesError;
}
if (symbolsError) {
throw symbolsError;
}
return;
function checkBaseLines(isSymbolBaseLine: boolean) {
const fullBaseLine = generateBaseLine(fullResults, isSymbolBaseLine);
const fullExtension = isSymbolBaseLine ? ".symbols" : ".types";
// When calling this function from rwc-runner, the baselinePath will have no extension.
// As rwc test- file is stored in json which ".json" will get stripped off.
// When calling this function from compiler-runner, the baselinePath will then has either ".ts" or ".tsx" extension
const outputFileName = ts.endsWith(baselinePath, ".ts") || ts.endsWith(baselinePath, ".tsx") ?
baselinePath.replace(/\.tsx?/, fullExtension) : baselinePath.concat(fullExtension);
Harness.Baseline.runBaseline(outputFileName, () => fullBaseLine, opts);
}
function generateBaseLine(typeWriterResults: ts.Map<TypeWriterResult[]>, isSymbolBaseline: boolean): string {
const typeLines: string[] = [];
const typeMap: { [fileName: string]: { [lineNum: number]: string[]; } } = {};
allFiles.forEach(file => {
const codeLines = file.content.split("\n");
typeWriterResults[file.unitName].forEach(result => {
if (isSymbolBaseline && !result.symbol) {
return;
}
const typeOrSymbolString = isSymbolBaseline ? result.symbol : result.type;
const formattedLine = result.sourceText.replace(/\r?\n/g, "") + " : " + typeOrSymbolString;
if (!typeMap[file.unitName]) {
typeMap[file.unitName] = {};
}
let typeInfo = [formattedLine];
const existingTypeInfo = typeMap[file.unitName][result.line];
if (existingTypeInfo) {
typeInfo = existingTypeInfo.concat(typeInfo);
}
typeMap[file.unitName][result.line] = typeInfo;
});
typeLines.push("=== " + file.unitName + " ===\r\n");
for (let i = 0; i < codeLines.length; i++) {
const currentCodeLine = codeLines[i];
typeLines.push(currentCodeLine + "\r\n");
if (typeMap[file.unitName]) {
const typeInfo = typeMap[file.unitName][i];
if (typeInfo) {
typeInfo.forEach(ty => {
typeLines.push(">" + ty + "\r\n");
});
if (i + 1 < codeLines.length && (codeLines[i + 1].match(/^\s*[{|}]\s*$/) || codeLines[i + 1].trim() === "")) {
}
else {
typeLines.push("\r\n");
}
}
}
else {
typeLines.push("No type information for this code.");
}
}
});
return typeLines.join("");
}
}
function getByteOrderMarkText(file: Harness.Compiler.GeneratedFile): string {
return file.writeByteOrderMark ? "\u00EF\u00BB\u00BF" : "";
}
export function doSourcemapBaseline(baselinePath: string, options: ts.CompilerOptions, result: CompilerResult) {
if (options.inlineSourceMap) {
if (result.sourceMaps.length > 0) {
throw new Error("No sourcemap files should be generated if inlineSourceMaps was set.");
}
return;
}
else if (options.sourceMap) {
if (result.sourceMaps.length !== result.files.length) {
throw new Error("Number of sourcemap files should be same as js files.");
}
Harness.Baseline.runBaseline(baselinePath.replace(/\.tsx?/, ".js.map"), () => {
if ((options.noEmitOnError && result.errors.length !== 0) || result.sourceMaps.length === 0) {
// We need to return null here or the runBaseLine will actually create a empty file.
// Baselining isn't required here because there is no output.
/* tslint:disable:no-null-keyword */
return null;
/* tslint:enable:no-null-keyword */
}
let sourceMapCode = "";
for (let i = 0; i < result.sourceMaps.length; i++) {
sourceMapCode += "//// [" + Harness.Path.getFileName(result.sourceMaps[i].fileName) + "]\r\n";
sourceMapCode += getByteOrderMarkText(result.sourceMaps[i]);
sourceMapCode += result.sourceMaps[i].code;
}
return sourceMapCode;
});
}
}
export function doJsEmitBaseline(baselinePath: string, header: string, options: ts.CompilerOptions, result: CompilerResult, toBeCompiled: Harness.Compiler.TestFile[], otherFiles: Harness.Compiler.TestFile[], harnessSettings: Harness.TestCaseParser.CompilerSettings) {
if (!options.noEmit && result.files.length === 0 && result.errors.length === 0) {
throw new Error("Expected at least one js file to be emitted or at least one error to be created.");
}
// check js output
Harness.Baseline.runBaseline(baselinePath.replace(/\.tsx?/, ".js"), () => {
let tsCode = "";
const tsSources = otherFiles.concat(toBeCompiled);
if (tsSources.length > 1) {
tsCode += "//// [" + header + "] ////\r\n\r\n";
}
for (let i = 0; i < tsSources.length; i++) {
tsCode += "//// [" + Harness.Path.getFileName(tsSources[i].unitName) + "]\r\n";
tsCode += tsSources[i].content + (i < (tsSources.length - 1) ? "\r\n" : "");
}
let jsCode = "";
for (let i = 0; i < result.files.length; i++) {
jsCode += "//// [" + Harness.Path.getFileName(result.files[i].fileName) + "]\r\n";
jsCode += getByteOrderMarkText(result.files[i]);
jsCode += result.files[i].code;
}
if (result.declFilesCode.length > 0) {
jsCode += "\r\n\r\n";
for (let i = 0; i < result.declFilesCode.length; i++) {
jsCode += "//// [" + Harness.Path.getFileName(result.declFilesCode[i].fileName) + "]\r\n";
jsCode += getByteOrderMarkText(result.declFilesCode[i]);
jsCode += result.declFilesCode[i].code;
}
}
const declFileCompilationResult =
Harness.Compiler.compileDeclarationFiles(
toBeCompiled, otherFiles, result, harnessSettings, options, /*currentDirectory*/ undefined);
if (declFileCompilationResult && declFileCompilationResult.declResult.errors.length) {
jsCode += "\r\n\r\n//// [DtsFileErrors]\r\n";
jsCode += "\r\n\r\n";
jsCode += Harness.Compiler.getErrorBaseline(declFileCompilationResult.declInputFiles.concat(declFileCompilationResult.declOtherFiles), declFileCompilationResult.declResult.errors);
}
if (jsCode.length > 0) {
return tsCode + "\r\n\r\n" + jsCode;
}
else {
/* tslint:disable:no-null-keyword */
return null;
/* tslint:enable:no-null-keyword */
}
});
}
export function collateOutputs(outputFiles: Harness.Compiler.GeneratedFile[]): string {
// Collect, test, and sort the fileNames
outputFiles.sort((a, b) => cleanName(a.fileName).localeCompare(cleanName(b.fileName)));
@@ -1421,7 +1725,7 @@ namespace Harness {
}
public getSourceMapRecord() {
if (this.sourceMapData) {
if (this.sourceMapData && this.sourceMapData.length > 0) {
return Harness.SourceMapRecorder.getSourceMapRecord(this.sourceMapData, this.program, this.files);
}
}
@@ -1540,7 +1844,8 @@ namespace Harness {
const parseConfigHost: ts.ParseConfigHost = {
useCaseSensitiveFileNames: false,
readDirectory: (name) => [],
fileExists: (name) => true
fileExists: (name) => true,
readFile: (name) => ts.forEach(testUnitData, data => data.name.toLowerCase() === name.toLowerCase() ? data.content : undefined)
};
// check if project has tsconfig.json in the list of files
@@ -1558,7 +1863,7 @@ namespace Harness {
tsConfig.options.configFilePath = data.name;
// delete entry from the list
testUnitData.splice(i, 1);
ts.orderedRemoveItemAt(testUnitData, i);
break;
}
@@ -1604,31 +1909,7 @@ namespace Harness {
}
const fileCache: { [idx: string]: boolean } = {};
function generateActual(actualFileName: string, generateContent: () => string): string {
// For now this is written using TypeScript, because sys is not available when running old test cases.
// But we need to move to sys once we have
// Creates the directory including its parent if not already present
function createDirectoryStructure(dirName: string) {
if (fileCache[dirName] || IO.directoryExists(dirName)) {
fileCache[dirName] = true;
return;
}
const parentDirectory = IO.directoryName(dirName);
if (parentDirectory != "") {
createDirectoryStructure(parentDirectory);
}
IO.createDirectory(dirName);
fileCache[dirName] = true;
}
// Create folders if needed
createDirectoryStructure(Harness.IO.directoryName(actualFileName));
// Delete the actual file in case it fails
if (IO.fileExists(actualFileName)) {
IO.deleteFile(actualFileName);
}
function generateActual(generateContent: () => string): string {
const actual = generateContent();
@@ -1663,42 +1944,49 @@ namespace Harness {
return { expected, actual };
}
function writeComparison(expected: string, actual: string, relativeFileName: string, actualFileName: string, descriptionForDescribe: string) {
function writeComparison(expected: string, actual: string, relativeFileName: string, actualFileName: string) {
// For now this is written using TypeScript, because sys is not available when running old test cases.
// But we need to move to sys once we have
// Creates the directory including its parent if not already present
function createDirectoryStructure(dirName: string) {
if (fileCache[dirName] || IO.directoryExists(dirName)) {
fileCache[dirName] = true;
return;
}
const parentDirectory = IO.directoryName(dirName);
if (parentDirectory != "") {
createDirectoryStructure(parentDirectory);
}
IO.createDirectory(dirName);
fileCache[dirName] = true;
}
// Create folders if needed
createDirectoryStructure(Harness.IO.directoryName(actualFileName));
// Delete the actual file in case it fails
if (IO.fileExists(actualFileName)) {
IO.deleteFile(actualFileName);
}
const encoded_actual = Utils.encodeString(actual);
if (expected !== encoded_actual) {
if (actual === NoContent) {
IO.writeFile(relativeFileName + ".delete", "");
IO.writeFile(actualFileName + ".delete", "");
}
else {
IO.writeFile(relativeFileName, actual);
IO.writeFile(actualFileName, actual);
}
// Overwrite & issue error
const errMsg = "The baseline file " + relativeFileName + " has changed.";
throw new Error(errMsg);
throw new Error(`The baseline file ${relativeFileName} has changed.`);
}
}
export function runBaseline(
descriptionForDescribe: string,
relativeFileName: string,
generateContent: () => string,
runImmediately = false,
opts?: BaselineOptions): void {
let actual = <string>undefined;
export function runBaseline(relativeFileName: string, generateContent: () => string, opts?: BaselineOptions): void {
const actualFileName = localPath(relativeFileName, opts && opts.Baselinefolder, opts && opts.Subfolder);
if (runImmediately) {
actual = generateActual(actualFileName, generateContent);
const comparison = compareToBaseline(actual, relativeFileName, opts);
writeComparison(comparison.expected, comparison.actual, relativeFileName, actualFileName, descriptionForDescribe);
}
else {
actual = generateActual(actualFileName, generateContent);
const comparison = compareToBaseline(actual, relativeFileName, opts);
writeComparison(comparison.expected, comparison.actual, relativeFileName, actualFileName, descriptionForDescribe);
}
const actual = generateActual(generateContent);
const comparison = compareToBaseline(actual, relativeFileName, opts);
writeComparison(comparison.expected, comparison.actual, relativeFileName, actualFileName);
}
}
+67 -12
View File
@@ -123,7 +123,7 @@ namespace Harness.LanguageService {
}
export class LanguageServiceAdapterHost {
protected fileNameToScript = ts.createMap<ScriptInfo>();
protected virtualFileSystem: Utils.VirtualFileSystem = new Utils.VirtualFileSystem(virtualFileSystemRoot, /*useCaseSensitiveFilenames*/false);
constructor(protected cancellationToken = DefaultHostCancellationToken.Instance,
protected settings = ts.getDefaultCompilerOptions()) {
@@ -135,22 +135,24 @@ namespace Harness.LanguageService {
public getFilenames(): string[] {
const fileNames: string[] = [];
ts.forEachProperty(this.fileNameToScript, (scriptInfo) => {
for (const virtualEntry of this.virtualFileSystem.getAllFileEntries()){
const scriptInfo = virtualEntry.content;
if (scriptInfo.isRootFile) {
// only include root files here
// usually it means that we won't include lib.d.ts in the list of root files so it won't mess the computation of compilation root dir.
fileNames.push(scriptInfo.fileName);
}
});
}
return fileNames;
}
public getScriptInfo(fileName: string): ScriptInfo {
return this.fileNameToScript[fileName];
const fileEntry = this.virtualFileSystem.traversePath(fileName);
return fileEntry && fileEntry.isFile() ? (<Utils.VirtualFile>fileEntry).content : undefined;
}
public addScript(fileName: string, content: string, isRootFile: boolean): void {
this.fileNameToScript[fileName] = new ScriptInfo(fileName, content, isRootFile);
this.virtualFileSystem.addFile(fileName, new ScriptInfo(fileName, content, isRootFile));
}
public editScript(fileName: string, start: number, end: number, newText: string) {
@@ -171,7 +173,7 @@ namespace Harness.LanguageService {
* @param col 0 based index
*/
public positionToLineAndCharacter(fileName: string, position: number): ts.LineAndCharacter {
const script: ScriptInfo = this.fileNameToScript[fileName];
const script: ScriptInfo = this.getScriptInfo(fileName);
assert.isOk(script);
return ts.computeLineAndCharacterOfPosition(script.getLineMap(), position);
@@ -182,8 +184,14 @@ namespace Harness.LanguageService {
class NativeLanguageServiceHost extends LanguageServiceAdapterHost implements ts.LanguageServiceHost {
getCompilationSettings() { return this.settings; }
getCancellationToken() { return this.cancellationToken; }
getDirectories(path: string): string[] { return []; }
getCurrentDirectory(): string { return ""; }
getDirectories(path: string): string[] {
const dir = this.virtualFileSystem.traversePath(path);
if (dir && dir.isDirectory()) {
return ts.map((<Utils.VirtualDirectory>dir).getDirectories(), (d) => ts.combinePaths(path, d.name));
}
return [];
}
getCurrentDirectory(): string { return virtualFileSystemRoot; }
getDefaultLibFileName(): string { return Harness.Compiler.defaultLibFileName; }
getScriptFileNames(): string[] { return this.getFilenames(); }
getScriptSnapshot(fileName: string): ts.IScriptSnapshot {
@@ -196,6 +204,25 @@ namespace Harness.LanguageService {
return script ? script.version.toString() : undefined;
}
fileExists(fileName: string): boolean {
const script = this.getScriptSnapshot(fileName);
return script !== undefined;
}
readDirectory(path: string, extensions?: string[], exclude?: string[], include?: string[]): string[] {
return ts.matchFiles(path, extensions, exclude, include,
/*useCaseSensitiveFileNames*/false,
this.getCurrentDirectory(),
(p) => this.virtualFileSystem.getAccessibleFileSystemEntries(p));
}
readFile(path: string, encoding?: string): string {
const snapshot = this.getScriptSnapshot(path);
return snapshot.getText(0, snapshot.getLength());
}
getTypeRootsVersion() {
return 0;
}
log(s: string): void { }
trace(s: string): void { }
error(s: string): void { }
@@ -384,6 +411,9 @@ namespace Harness.LanguageService {
getCompletionEntryDetails(fileName: string, position: number, entryName: string): ts.CompletionEntryDetails {
return unwrapJSONCallResult(this.shim.getCompletionEntryDetails(fileName, position, entryName));
}
getCompletionEntrySymbol(fileName: string, position: number, entryName: string): ts.Symbol {
throw new Error("getCompletionEntrySymbol not implemented across the shim layer.");
}
getQuickInfoAtPosition(fileName: string, position: number): ts.QuickInfo {
return unwrapJSONCallResult(this.shim.getQuickInfoAtPosition(fileName, position));
}
@@ -408,6 +438,9 @@ namespace Harness.LanguageService {
getTypeDefinitionAtPosition(fileName: string, position: number): ts.DefinitionInfo[] {
return unwrapJSONCallResult(this.shim.getTypeDefinitionAtPosition(fileName, position));
}
getImplementationAtPosition(fileName: string, position: number): ts.ImplementationLocation[] {
return unwrapJSONCallResult(this.shim.getImplementationAtPosition(fileName, position));
}
getReferencesAtPosition(fileName: string, position: number): ts.ReferenceEntry[] {
return unwrapJSONCallResult(this.shim.getReferencesAtPosition(fileName, position));
}
@@ -462,6 +495,9 @@ namespace Harness.LanguageService {
getNonBoundSourceFile(fileName: string): ts.SourceFile {
throw new Error("SourceFile can not be marshaled across the shim layer.");
}
getSourceFile(fileName: string): ts.SourceFile {
throw new Error("SourceFile can not be marshaled across the shim layer.");
}
dispose(): void { this.shim.dispose({}); }
}
@@ -572,7 +608,6 @@ namespace Harness.LanguageService {
this.writeMessage(message);
}
readFile(fileName: string): string {
if (fileName.indexOf(Harness.Compiler.defaultLibFileName) >= 0) {
fileName = Harness.Compiler.defaultLibFileName;
@@ -617,6 +652,10 @@ namespace Harness.LanguageService {
return [];
}
getEnvironmentVariable(name: string): string {
return ts.sys.getEnvironmentVariable(name);
}
readDirectory(path: string, extension?: string[], exclude?: string[], include?: string[]): string[] {
throw new Error("Not implemented Yet.");
}
@@ -644,7 +683,11 @@ namespace Harness.LanguageService {
return true;
}
isVerbose() {
getLogFileName(): string {
return undefined;
}
hasLevel() {
return false;
}
@@ -666,6 +709,14 @@ namespace Harness.LanguageService {
clearTimeout(timeoutId: any): void {
clearTimeout(timeoutId);
}
setImmediate(callback: (...args: any[]) => void, ms: number, ...args: any[]): any {
return setImmediate(callback, args);
}
clearImmediate(timeoutId: any): void {
clearImmediate(timeoutId);
}
}
export class ServerLanguageServiceAdapter implements LanguageServiceAdapter {
@@ -680,8 +731,12 @@ namespace Harness.LanguageService {
// host to answer server queries about files on disk
const serverHost = new SessionServerHost(clientHost);
const server = new ts.server.Session(serverHost,
Buffer ? Buffer.byteLength : (string: string, encoding?: string) => string.length,
process.hrtime, serverHost);
{ isCancellationRequested: () => false },
/*useOneInferredProject*/ false,
/*typingsInstaller*/ undefined,
Utils.byteLength,
process.hrtime, serverHost,
/*canUseEvents*/ true);
// Fake the connection between the client and the server
serverHost.writeMessage = client.onMessage.bind(client);
+2
View File
@@ -1,6 +1,8 @@
/// <reference path="..\..\src\compiler\sys.ts" />
/// <reference path="..\..\src\harness\harness.ts" />
/// <reference path="..\..\src\harness\harnessLanguageService.ts" />
/// <reference path="..\..\src\harness\runnerbase.ts" />
/// <reference path="..\..\src\harness\typeWriter.ts" />
interface FileInformation {
contents: string;
+17 -15
View File
@@ -222,6 +222,7 @@ class ProjectRunner extends RunnerBase {
useCaseSensitiveFileNames: Harness.IO.useCaseSensitiveFileNames(),
fileExists,
readDirectory,
readFile
};
const configParseResult = ts.parseJsonConfigFileContent(configObject, configParseHost, ts.getDirectoryPath(configFileName), compilerOptions);
if (configParseResult.errors.length > 0) {
@@ -292,6 +293,10 @@ class ProjectRunner extends RunnerBase {
return Harness.IO.fileExists(getFileNameInTheProjectTest(fileName));
}
function readFile(fileName: string): string {
return Harness.IO.readFile(getFileNameInTheProjectTest(fileName));
}
function getSourceFileText(fileName: string): string {
let text: string = undefined;
try {
@@ -459,7 +464,7 @@ class ProjectRunner extends RunnerBase {
});
it("Resolution information of (" + moduleNameToString(moduleKind) + "): " + testCaseFileName, () => {
Harness.Baseline.runBaseline("Resolution information of (" + moduleNameToString(compilerResult.moduleKind) + "): " + testCaseFileName, getBaselineFolder(compilerResult.moduleKind) + testCaseJustName + ".json", () => {
Harness.Baseline.runBaseline(getBaselineFolder(compilerResult.moduleKind) + testCaseJustName + ".json", () => {
return JSON.stringify(getCompilerResolutionInfo(), undefined, " ");
});
});
@@ -467,13 +472,12 @@ class ProjectRunner extends RunnerBase {
it("Errors for (" + moduleNameToString(moduleKind) + "): " + testCaseFileName, () => {
if (compilerResult.errors.length) {
Harness.Baseline.runBaseline("Errors for (" + moduleNameToString(compilerResult.moduleKind) + "): " + testCaseFileName, getBaselineFolder(compilerResult.moduleKind) + testCaseJustName + ".errors.txt", () => {
Harness.Baseline.runBaseline(getBaselineFolder(compilerResult.moduleKind) + testCaseJustName + ".errors.txt", () => {
return getErrorsBaseline(compilerResult);
});
}
});
it("Baseline of emitted result (" + moduleNameToString(moduleKind) + "): " + testCaseFileName, () => {
if (testCase.baselineCheck) {
const errs: Error[] = [];
@@ -481,7 +485,7 @@ class ProjectRunner extends RunnerBase {
// There may be multiple files with different baselines. Run all and report at the end, else
// it stops copying the remaining emitted files from 'local/projectOutput' to 'local/project'.
try {
Harness.Baseline.runBaseline("Baseline of emitted result (" + moduleNameToString(compilerResult.moduleKind) + "): " + testCaseFileName, getBaselineFolder(compilerResult.moduleKind) + outputFile.fileName, () => {
Harness.Baseline.runBaseline(getBaselineFolder(compilerResult.moduleKind) + outputFile.fileName, () => {
try {
return Harness.IO.readFile(getProjectOutputFolder(outputFile.fileName, compilerResult.moduleKind));
}
@@ -500,23 +504,21 @@ class ProjectRunner extends RunnerBase {
}
});
it("SourceMapRecord for (" + moduleNameToString(moduleKind) + "): " + testCaseFileName, () => {
if (compilerResult.sourceMapData) {
Harness.Baseline.runBaseline("SourceMapRecord for (" + moduleNameToString(compilerResult.moduleKind) + "): " + testCaseFileName, getBaselineFolder(compilerResult.moduleKind) + testCaseJustName + ".sourcemap.txt", () => {
return Harness.SourceMapRecorder.getSourceMapRecord(compilerResult.sourceMapData, compilerResult.program,
ts.filter(compilerResult.outputFiles, outputFile => Harness.Compiler.isJS(outputFile.emittedFileName)));
});
}
});
// it("SourceMapRecord for (" + moduleNameToString(moduleKind) + "): " + testCaseFileName, () => {
// if (compilerResult.sourceMapData) {
// Harness.Baseline.runBaseline(getBaselineFolder(compilerResult.moduleKind) + testCaseJustName + ".sourcemap.txt", () => {
// return Harness.SourceMapRecorder.getSourceMapRecord(compilerResult.sourceMapData, compilerResult.program,
// ts.filter(compilerResult.outputFiles, outputFile => Harness.Compiler.isJS(outputFile.emittedFileName)));
// });
// }
// });
// Verify that all the generated .d.ts files compile
it("Errors in generated Dts files for (" + moduleNameToString(moduleKind) + "): " + testCaseFileName, () => {
if (!compilerResult.errors.length && testCase.declaration) {
const dTsCompileResult = compileCompileDTsFiles(compilerResult);
if (dTsCompileResult && dTsCompileResult.errors.length) {
Harness.Baseline.runBaseline("Errors in generated Dts files for (" + moduleNameToString(compilerResult.moduleKind) + "): " + testCaseFileName, getBaselineFolder(compilerResult.moduleKind) + testCaseJustName + ".dts.errors.txt", () => {
Harness.Baseline.runBaseline(getBaselineFolder(compilerResult.moduleKind) + testCaseJustName + ".dts.errors.txt", () => {
return getErrorsBaseline(dTsCompileResult);
});
}
+8
View File
@@ -82,6 +82,7 @@ interface TestConfig {
light?: boolean;
taskConfigsFolder?: string;
workerCount?: number;
stackTraceLimit?: number | "full";
tasks?: TaskSet[];
test?: string[];
runUnitTests?: boolean;
@@ -116,6 +117,13 @@ if (testConfigContent !== "") {
}
}
if (testConfig.stackTraceLimit === "full") {
(<any>Error).stackTraceLimit = Infinity;
}
else if ((+testConfig.stackTraceLimit | 0) > 0) {
(<any>Error).stackTraceLimit = testConfig.stackTraceLimit;
}
if (testConfig.test && testConfig.test.length > 0) {
for (const option of testConfig.test) {
if (!option) {
+22 -14
View File
@@ -79,6 +79,7 @@ namespace RWC {
useCaseSensitiveFileNames: Harness.IO.useCaseSensitiveFileNames(),
fileExists: Harness.IO.fileExists,
readDirectory: Harness.IO.readDirectory,
readFile: Harness.IO.readFile
};
const configParseResult = ts.parseJsonConfigFileContent(parsedTsconfigFileContents.config, configParseHost, ts.getDirectoryPath(tsconfigFile.path));
fileNames = configParseResult.fileNames;
@@ -158,55 +159,56 @@ namespace RWC {
it("has the expected emitted code", () => {
Harness.Baseline.runBaseline("has the expected emitted code", baseName + ".output.js", () => {
Harness.Baseline.runBaseline(`${baseName}.output.js`, () => {
return Harness.Compiler.collateOutputs(compilerResult.files);
}, false, baselineOpts);
}, baselineOpts);
});
it("has the expected declaration file content", () => {
Harness.Baseline.runBaseline("has the expected declaration file content", baseName + ".d.ts", () => {
Harness.Baseline.runBaseline(`${baseName}.d.ts`, () => {
if (!compilerResult.declFilesCode.length) {
return null;
}
return Harness.Compiler.collateOutputs(compilerResult.declFilesCode);
}, false, baselineOpts);
}, baselineOpts);
});
it("has the expected source maps", () => {
Harness.Baseline.runBaseline("has the expected source maps", baseName + ".map", () => {
Harness.Baseline.runBaseline(`${baseName}.map`, () => {
if (!compilerResult.sourceMaps.length) {
return null;
}
return Harness.Compiler.collateOutputs(compilerResult.sourceMaps);
}, false, baselineOpts);
}, baselineOpts);
});
/*it("has correct source map record", () => {
if (compilerOptions.sourceMap) {
Harness.Baseline.runBaseline("has correct source map record", baseName + ".sourcemap.txt", () => {
Harness.Baseline.runBaseline(baseName + ".sourcemap.txt", () => {
return compilerResult.getSourceMapRecord();
}, false, baselineOpts);
}, baselineOpts);
}
});*/
it("has the expected errors", () => {
Harness.Baseline.runBaseline("has the expected errors", baseName + ".errors.txt", () => {
Harness.Baseline.runBaseline(`${baseName}.errors.txt`, () => {
if (compilerResult.errors.length === 0) {
return null;
}
// Do not include the library in the baselines to avoid noise
const baselineFiles = inputFiles.concat(otherFiles).filter(f => !Harness.isDefaultLibraryFile(f.unitName));
return Harness.Compiler.getErrorBaseline(baselineFiles, compilerResult.errors);
}, false, baselineOpts);
const errors = compilerResult.errors.filter(e => !Harness.isDefaultLibraryFile(e.file.fileName));
return Harness.Compiler.getErrorBaseline(baselineFiles, errors);
}, baselineOpts);
});
// Ideally, a generated declaration file will have no errors. But we allow generated
// declaration file errors as part of the baseline.
it("has the expected errors in generated declaration files", () => {
if (compilerOptions.declaration && !compilerResult.errors.length) {
Harness.Baseline.runBaseline("has the expected errors in generated declaration files", baseName + ".dts.errors.txt", () => {
Harness.Baseline.runBaseline(`${baseName}.dts.errors.txt`, () => {
const declFileCompilationResult = Harness.Compiler.compileDeclarationFiles(
inputFiles, otherFiles, compilerResult, /*harnessSettings*/ undefined, compilerOptions, currentDirectory);
@@ -217,11 +219,17 @@ namespace RWC {
return Harness.Compiler.minimalDiagnosticsToString(declFileCompilationResult.declResult.errors) +
Harness.IO.newLine() + Harness.IO.newLine() +
Harness.Compiler.getErrorBaseline(declFileCompilationResult.declInputFiles.concat(declFileCompilationResult.declOtherFiles), declFileCompilationResult.declResult.errors);
}, false, baselineOpts);
}, baselineOpts);
}
});
// TODO: Type baselines (need to refactor out from compilerRunner)
it("has the expected types", () => {
// We don't need to pass the extension here because "doTypeAndSymbolBaseline" will append appropriate extension of ".types" or ".symbols"
Harness.Compiler.doTypeAndSymbolBaseline(baseName, compilerResult, inputFiles
.concat(otherFiles)
.filter(file => !!compilerResult.program.getSourceFile(file.unitName))
.filter(e => !Harness.isDefaultLibraryFile(e.unitName)), baselineOpts);
});
});
}
}
+6 -6
View File
@@ -67,21 +67,21 @@ class Test262BaselineRunner extends RunnerBase {
});
it("has the expected emitted code", () => {
Harness.Baseline.runBaseline("has the expected emitted code", testState.filename + ".output.js", () => {
Harness.Baseline.runBaseline(testState.filename + ".output.js", () => {
const files = testState.compilerResult.files.filter(f => f.fileName !== Test262BaselineRunner.helpersFilePath);
return Harness.Compiler.collateOutputs(files);
}, false, Test262BaselineRunner.baselineOptions);
}, Test262BaselineRunner.baselineOptions);
});
it("has the expected errors", () => {
Harness.Baseline.runBaseline("has the expected errors", testState.filename + ".errors.txt", () => {
Harness.Baseline.runBaseline(testState.filename + ".errors.txt", () => {
const errors = testState.compilerResult.errors;
if (errors.length === 0) {
return null;
}
return Harness.Compiler.getErrorBaseline(testState.inputFiles, errors);
}, false, Test262BaselineRunner.baselineOptions);
}, Test262BaselineRunner.baselineOptions);
});
it("satisfies invariants", () => {
@@ -90,10 +90,10 @@ class Test262BaselineRunner extends RunnerBase {
});
it("has the expected AST", () => {
Harness.Baseline.runBaseline("has the expected AST", testState.filename + ".AST.txt", () => {
Harness.Baseline.runBaseline(testState.filename + ".AST.txt", () => {
const sourceFile = testState.compilerResult.program.getSourceFile(Test262BaselineRunner.getTestFilePath(testState.filename));
return Utils.sourceFileToJSON(sourceFile);
}, false, Test262BaselineRunner.baselineOptions);
}, Test262BaselineRunner.baselineOptions);
});
});
}
+19 -1
View File
@@ -22,6 +22,19 @@
"../compiler/utilities.ts",
"../compiler/binder.ts",
"../compiler/checker.ts",
"../compiler/factory.ts",
"../compiler/visitor.ts",
"../compiler/transformers/ts.ts",
"../compiler/transformers/jsx.ts",
"../compiler/transformers/es7.ts",
"../compiler/transformers/es6.ts",
"../compiler/transformers/generators.ts",
"../compiler/transformers/destructuring.ts",
"../compiler/transformers/module/module.ts",
"../compiler/transformers/module/system.ts",
"../compiler/transformers/module/es6.ts",
"../compiler/transformer.ts",
"../compiler/comments.ts",
"../compiler/sourcemap.ts",
"../compiler/declarationEmitter.ts",
"../compiler/emitter.ts",
@@ -86,9 +99,14 @@
"./unittests/moduleResolution.ts",
"./unittests/tsconfigParsing.ts",
"./unittests/commandLineParsing.ts",
"./unittests/configurationExtension.ts",
"./unittests/convertCompilerOptionsFromJson.ts",
"./unittests/convertTypingOptionsFromJson.ts",
"./unittests/tsserverProjectSystem.ts",
"./unittests/matchFiles.ts"
"./unittests/matchFiles.ts",
"./unittests/initializeTSConfig.ts",
"./unittests/compileOnSave.ts",
"./unittests/typingsInstaller.ts",
"./unittests/projectErrors.ts"
]
}
+1 -1
View File
@@ -29,7 +29,7 @@ class TypeWriterWalker {
}
private visitNode(node: ts.Node): void {
if (ts.isExpression(node) || node.kind === ts.SyntaxKind.Identifier) {
if (ts.isPartOfExpression(node) || node.kind === ts.SyntaxKind.Identifier) {
this.logTypeAndSymbol(node);
}
+26 -27
View File
@@ -47,6 +47,7 @@ namespace ts {
return "";
},
getDirectories: (path: string) => [],
getEnvironmentVariable: (name: string) => "",
readDirectory: (path: string, extension?: string[], exclude?: string[], include?: string[]): string[] => {
throw new Error("NYI");
},
@@ -63,26 +64,29 @@ namespace ts {
};
},
setTimeout,
clearTimeout
clearTimeout,
setImmediate,
clearImmediate
};
}
function createProject(rootFile: string, serverHost: server.ServerHost): { project: server.Project, rootScriptInfo: server.ScriptInfo } {
const logger: server.Logger = {
close() { },
isVerbose: () => false,
hasLevel: () => false,
loggingEnabled: () => false,
perftrc: (s: string) => { },
info: (s: string) => { },
startGroup: () => { },
endGroup: () => { },
msg: (s: string, type?: string) => { }
msg: (s: string, type?: string) => { },
getLogFileName: (): string => undefined
};
const projectService = new server.ProjectService(serverHost, logger);
const rootScriptInfo = projectService.openFile(rootFile, /* openedByClient */true);
const project = projectService.createInferredProject(rootScriptInfo);
project.setProjectOptions({ files: [rootScriptInfo.fileName], compilerOptions: { module: ts.ModuleKind.AMD } });
const projectService = new server.ProjectService(serverHost, logger, { isCancellationRequested: () => false }, /*useOneInferredProject*/ false, /*typingsInstaller*/ undefined);
const rootScriptInfo = projectService.getOrCreateScriptInfo(rootFile, /* openedByClient */true, /*containingProject*/ undefined);
const project = projectService.createInferredProjectWithRootFileIfNecessary(rootScriptInfo);
project.setCompilerOptions({ module: ts.ModuleKind.AMD } );
return {
project,
rootScriptInfo
@@ -105,10 +109,9 @@ namespace ts {
const { project, rootScriptInfo } = createProject(root.name, serverHost);
// ensure that imported file was found
let diags = project.compilerService.languageService.getSemanticDiagnostics(imported.name);
let diags = project.getLanguageService().getSemanticDiagnostics(imported.name);
assert.equal(diags.length, 1);
let content = rootScriptInfo.getText();
const originalFileExists = serverHost.fileExists;
{
@@ -120,10 +123,9 @@ namespace ts {
const newContent = `import {x} from "f1"
var x: string = 1;`;
rootScriptInfo.editContent(0, content.length, newContent);
content = newContent;
rootScriptInfo.editContent(0, root.content.length, newContent);
// trigger synchronization to make sure that import will be fetched from the cache
diags = project.compilerService.languageService.getSemanticDiagnostics(imported.name);
diags = project.getLanguageService().getSemanticDiagnostics(imported.name);
// ensure file has correct number of errors after edit
assert.equal(diags.length, 1);
}
@@ -138,12 +140,11 @@ namespace ts {
return originalFileExists.call(serverHost, fileName);
};
const newContent = `import {x} from "f2"`;
rootScriptInfo.editContent(0, content.length, newContent);
content = newContent;
rootScriptInfo.editContent(0, root.content.length, newContent);
try {
// trigger synchronization to make sure that LSHost will try to find 'f2' module on disk
project.compilerService.languageService.getSemanticDiagnostics(imported.name);
project.getLanguageService().getSemanticDiagnostics(imported.name);
assert.isTrue(false, `should not find file '${imported.name}'`);
}
catch (e) {
@@ -164,20 +165,18 @@ namespace ts {
};
const newContent = `import {x} from "f1"`;
rootScriptInfo.editContent(0, content.length, newContent);
content = newContent;
project.compilerService.languageService.getSemanticDiagnostics(imported.name);
rootScriptInfo.editContent(0, root.content.length, newContent);
project.getLanguageService().getSemanticDiagnostics(imported.name);
assert.isTrue(fileExistsCalled);
// setting compiler options discards module resolution cache
fileExistsCalled = false;
const opts = ts.clone(project.projectOptions);
opts.compilerOptions = ts.clone(opts.compilerOptions);
opts.compilerOptions.target = ts.ScriptTarget.ES5;
project.setProjectOptions(opts);
const compilerOptions = ts.clone(project.getCompilerOptions());
compilerOptions.target = ts.ScriptTarget.ES5;
project.setCompilerOptions(compilerOptions);
project.compilerService.languageService.getSemanticDiagnostics(imported.name);
project.getLanguageService().getSemanticDiagnostics(imported.name);
assert.isTrue(fileExistsCalled);
}
});
@@ -210,8 +209,8 @@ namespace ts {
};
const { project, rootScriptInfo } = createProject(root.name, serverHost);
const content = rootScriptInfo.getText();
let diags = project.compilerService.languageService.getSemanticDiagnostics(root.name);
let diags = project.getLanguageService().getSemanticDiagnostics(root.name);
assert.isTrue(fileExistsCalledForBar, "'fileExists' should be called");
assert.isTrue(diags.length === 1, "one diagnostic expected");
assert.isTrue(typeof diags[0].messageText === "string" && ((<string>diags[0].messageText).indexOf("Cannot find module") === 0), "should be 'cannot find module' message");
@@ -219,9 +218,9 @@ namespace ts {
// assert that import will success once file appear on disk
fileMap[imported.name] = imported;
fileExistsCalledForBar = false;
rootScriptInfo.editContent(0, content.length, `import {y} from "bar"`);
rootScriptInfo.editContent(0, root.content.length, `import {y} from "bar"`);
diags = project.compilerService.languageService.getSemanticDiagnostics(root.name);
diags = project.getLanguageService().getSemanticDiagnostics(root.name);
assert.isTrue(fileExistsCalledForBar, "'fileExists' should be called");
assert.isTrue(diags.length === 0);
});
+3 -3
View File
@@ -60,7 +60,7 @@ namespace ts {
assertParseResult(["--lib", "es5,invalidOption", "0.ts"],
{
errors: [{
messageText: "Argument for '--lib' option must be: 'es5', 'es6', 'es2015', 'es7', 'es2016', 'es2017', 'dom', 'webworker', 'scripthost', 'es2015.core', 'es2015.collection', 'es2015.generator', 'es2015.iterable', 'es2015.promise', 'es2015.proxy', 'es2015.reflect', 'es2015.symbol', 'es2015.symbol.wellknown', 'es2016.array.include', 'es2017.object', 'es2017.sharedmemory'",
messageText: "Argument for '--lib' option must be: 'es5', 'es6', 'es2015', 'es7', 'es2016', 'es2017', 'dom', 'dom.iterable', 'webworker', 'scripthost', 'es2015.core', 'es2015.collection', 'es2015.generator', 'es2015.iterable', 'es2015.promise', 'es2015.proxy', 'es2015.reflect', 'es2015.symbol', 'es2015.symbol.wellknown', 'es2016.array.include', 'es2017.object', 'es2017.sharedmemory'",
category: ts.Diagnostics.Argument_for_0_option_must_be_Colon_1.category,
code: ts.Diagnostics.Argument_for_0_option_must_be_Colon_1.code,
@@ -263,7 +263,7 @@ namespace ts {
assertParseResult(["--lib", "es5,", "es7", "0.ts"],
{
errors: [{
messageText: "Argument for '--lib' option must be: 'es5', 'es6', 'es2015', 'es7', 'es2016', 'es2017', 'dom', 'webworker', 'scripthost', 'es2015.core', 'es2015.collection', 'es2015.generator', 'es2015.iterable', 'es2015.promise', 'es2015.proxy', 'es2015.reflect', 'es2015.symbol', 'es2015.symbol.wellknown', 'es2016.array.include', 'es2017.object', 'es2017.sharedmemory'",
messageText: "Argument for '--lib' option must be: 'es5', 'es6', 'es2015', 'es7', 'es2016', 'es2017', 'dom', 'dom.iterable', 'webworker', 'scripthost', 'es2015.core', 'es2015.collection', 'es2015.generator', 'es2015.iterable', 'es2015.promise', 'es2015.proxy', 'es2015.reflect', 'es2015.symbol', 'es2015.symbol.wellknown', 'es2016.array.include', 'es2017.object', 'es2017.sharedmemory'",
category: ts.Diagnostics.Argument_for_0_option_must_be_Colon_1.category,
code: ts.Diagnostics.Argument_for_0_option_must_be_Colon_1.code,
@@ -283,7 +283,7 @@ namespace ts {
assertParseResult(["--lib", "es5, ", "es7", "0.ts"],
{
errors: [{
messageText: "Argument for '--lib' option must be: 'es5', 'es6', 'es2015', 'es7', 'es2016', 'es2017', 'dom', 'webworker', 'scripthost', 'es2015.core', 'es2015.collection', 'es2015.generator', 'es2015.iterable', 'es2015.promise', 'es2015.proxy', 'es2015.reflect', 'es2015.symbol', 'es2015.symbol.wellknown', 'es2016.array.include', 'es2017.object', 'es2017.sharedmemory'",
messageText: "Argument for '--lib' option must be: 'es5', 'es6', 'es2015', 'es7', 'es2016', 'es2017', 'dom', 'dom.iterable', 'webworker', 'scripthost', 'es2015.core', 'es2015.collection', 'es2015.generator', 'es2015.iterable', 'es2015.promise', 'es2015.proxy', 'es2015.reflect', 'es2015.symbol', 'es2015.symbol.wellknown', 'es2016.array.include', 'es2017.object', 'es2017.sharedmemory'",
category: ts.Diagnostics.Argument_for_0_option_must_be_Colon_1.category,
code: ts.Diagnostics.Argument_for_0_option_must_be_Colon_1.code,
+494
View File
@@ -0,0 +1,494 @@
/// <reference path="../harness.ts" />
/// <reference path="./tsserverProjectSystem.ts" />
/// <reference path="../../server/typingsInstaller/typingsInstaller.ts" />
namespace ts.projectSystem {
function createTestTypingsInstaller(host: server.ServerHost) {
return new TestTypingsInstaller("/a/data/", /*throttleLimit*/5, host);
}
describe("CompileOnSave affected list", () => {
function sendAffectedFileRequestAndCheckResult(session: server.Session, request: server.protocol.Request, expectedFileList: { projectFileName: string, files: FileOrFolder[] }[]) {
const response: server.protocol.CompileOnSaveAffectedFileListSingleProject[] = session.executeCommand(request).response;
const actualResult = response.sort((list1, list2) => compareStrings(list1.projectFileName, list2.projectFileName));
expectedFileList = expectedFileList.sort((list1, list2) => compareStrings(list1.projectFileName, list2.projectFileName));
assert.equal(actualResult.length, expectedFileList.length, `Actual result project number is different from the expected project number`);
for (let i = 0; i < actualResult.length; i++) {
const actualResultSingleProject = actualResult[i];
const expectedResultSingleProject = expectedFileList[i];
assert.equal(actualResultSingleProject.projectFileName, expectedResultSingleProject.projectFileName, `Actual result contains different projects than the expected result`);
const actualResultSingleProjectFileNameList = actualResultSingleProject.fileNames.sort();
const expectedResultSingleProjectFileNameList = map(expectedResultSingleProject.files, f => f.path).sort();
assert.isTrue(
arrayIsEqualTo(actualResultSingleProjectFileNameList, expectedResultSingleProjectFileNameList),
`For project ${actualResultSingleProject.projectFileName}, the actual result is ${actualResultSingleProjectFileNameList}, while expected ${expectedResultSingleProjectFileNameList}`);
}
}
describe("for configured projects", () => {
let moduleFile1: FileOrFolder;
let file1Consumer1: FileOrFolder;
let file1Consumer2: FileOrFolder;
let moduleFile2: FileOrFolder;
let globalFile3: FileOrFolder;
let configFile: FileOrFolder;
let changeModuleFile1ShapeRequest1: server.protocol.Request;
let changeModuleFile1InternalRequest1: server.protocol.Request;
let changeModuleFile1ShapeRequest2: server.protocol.Request;
// A compile on save affected file request using file1
let moduleFile1FileListRequest: server.protocol.Request;
beforeEach(() => {
moduleFile1 = {
path: "/a/b/moduleFile1.ts",
content: "export function Foo() { };"
};
file1Consumer1 = {
path: "/a/b/file1Consumer1.ts",
content: `import {Foo} from "./moduleFile1"; export var y = 10;`
};
file1Consumer2 = {
path: "/a/b/file1Consumer2.ts",
content: `import {Foo} from "./moduleFile1"; let z = 10;`
};
moduleFile2 = {
path: "/a/b/moduleFile2.ts",
content: `export var Foo4 = 10;`
};
globalFile3 = {
path: "/a/b/globalFile3.ts",
content: `interface GlobalFoo { age: number }`
};
configFile = {
path: "/a/b/tsconfig.json",
content: `{
"compileOnSave": true
}`
};
// Change the content of file1 to `export var T: number;export function Foo() { };`
changeModuleFile1ShapeRequest1 = makeSessionRequest<server.protocol.ChangeRequestArgs>(server.CommandNames.Change, {
file: moduleFile1.path,
line: 1,
offset: 1,
endLine: 1,
endOffset: 1,
insertString: `export var T: number;`
});
// Change the content of file1 to `export var T: number;export function Foo() { };`
changeModuleFile1InternalRequest1 = makeSessionRequest<server.protocol.ChangeRequestArgs>(server.CommandNames.Change, {
file: moduleFile1.path,
line: 1,
offset: 1,
endLine: 1,
endOffset: 1,
insertString: `var T1: number;`
});
// Change the content of file1 to `export var T: number;export function Foo() { };`
changeModuleFile1ShapeRequest2 = makeSessionRequest<server.protocol.ChangeRequestArgs>(server.CommandNames.Change, {
file: moduleFile1.path,
line: 1,
offset: 1,
endLine: 1,
endOffset: 1,
insertString: `export var T2: number;`
});
moduleFile1FileListRequest = makeSessionRequest<server.protocol.FileRequestArgs>(server.CommandNames.CompileOnSaveAffectedFileList, { file: moduleFile1.path, projectFileName: configFile.path });
});
it("should contains only itself if a module file's shape didn't change, and all files referencing it if its shape changed", () => {
const host = createServerHost([moduleFile1, file1Consumer1, file1Consumer2, globalFile3, moduleFile2, configFile, libFile]);
const typingsInstaller = createTestTypingsInstaller(host);
const session = new server.Session(host, nullCancellationToken, /*useSingleInferredProject*/ false, typingsInstaller, Utils.byteLength, process.hrtime, nullLogger, /*canUseEvents*/ false);
openFilesForSession([moduleFile1, file1Consumer1], session);
// Send an initial compileOnSave request
sendAffectedFileRequestAndCheckResult(session, moduleFile1FileListRequest, [{ projectFileName: configFile.path, files: [moduleFile1, file1Consumer1, file1Consumer2] }]);
session.executeCommand(changeModuleFile1ShapeRequest1);
sendAffectedFileRequestAndCheckResult(session, moduleFile1FileListRequest, [{ projectFileName: configFile.path, files: [moduleFile1, file1Consumer1, file1Consumer2] }]);
// Change the content of file1 to `export var T: number;export function Foo() { console.log('hi'); };`
const changeFile1InternalRequest = makeSessionRequest<server.protocol.ChangeRequestArgs>(server.CommandNames.Change, {
file: moduleFile1.path,
line: 1,
offset: 46,
endLine: 1,
endOffset: 46,
insertString: `console.log('hi');`
});
session.executeCommand(changeFile1InternalRequest);
sendAffectedFileRequestAndCheckResult(session, moduleFile1FileListRequest, [{ projectFileName: configFile.path, files: [moduleFile1] }]);
});
it("should be up-to-date with the reference map changes", () => {
const host = createServerHost([moduleFile1, file1Consumer1, file1Consumer2, globalFile3, moduleFile2, configFile, libFile]);
const typingsInstaller = createTestTypingsInstaller(host);
const session = new server.Session(host, nullCancellationToken, /*useSingleInferredProject*/ false, typingsInstaller, Utils.byteLength, process.hrtime, nullLogger, /*canUseEvents*/ false);
openFilesForSession([moduleFile1, file1Consumer1], session);
// Send an initial compileOnSave request
sendAffectedFileRequestAndCheckResult(session, moduleFile1FileListRequest, [{ projectFileName: configFile.path, files: [moduleFile1, file1Consumer1, file1Consumer2] }]);
// Change file2 content to `let y = Foo();`
const removeFile1Consumer1ImportRequest = makeSessionRequest<server.protocol.ChangeRequestArgs>(server.CommandNames.Change, {
file: file1Consumer1.path,
line: 1,
offset: 1,
endLine: 1,
endOffset: 28,
insertString: ""
});
session.executeCommand(removeFile1Consumer1ImportRequest);
session.executeCommand(changeModuleFile1ShapeRequest1);
sendAffectedFileRequestAndCheckResult(session, moduleFile1FileListRequest, [{ projectFileName: configFile.path, files: [moduleFile1, file1Consumer2] }]);
// Add the import statements back to file2
const addFile2ImportRequest = makeSessionRequest<server.protocol.ChangeRequestArgs>(server.CommandNames.Change, {
file: file1Consumer1.path,
line: 1,
offset: 1,
endLine: 1,
endOffset: 1,
insertString: `import {Foo} from "./moduleFile1";`
});
session.executeCommand(addFile2ImportRequest);
// Change the content of file1 to `export var T2: string;export var T: number;export function Foo() { };`
const changeModuleFile1ShapeRequest2 = makeSessionRequest<server.protocol.ChangeRequestArgs>(server.CommandNames.Change, {
file: moduleFile1.path,
line: 1,
offset: 1,
endLine: 1,
endOffset: 1,
insertString: `export var T2: string;`
});
session.executeCommand(changeModuleFile1ShapeRequest2);
sendAffectedFileRequestAndCheckResult(session, moduleFile1FileListRequest, [{ projectFileName: configFile.path, files: [moduleFile1, file1Consumer1, file1Consumer2] }]);
});
it("should be up-to-date with changes made in non-open files", () => {
const host = createServerHost([moduleFile1, file1Consumer1, file1Consumer2, globalFile3, moduleFile2, configFile, libFile]);
const typingsInstaller = createTestTypingsInstaller(host);
const session = new server.Session(host, nullCancellationToken, /*useSingleInferredProject*/ false, typingsInstaller, Utils.byteLength, process.hrtime, nullLogger, /*canUseEvents*/ false);
openFilesForSession([moduleFile1], session);
// Send an initial compileOnSave request
sendAffectedFileRequestAndCheckResult(session, moduleFile1FileListRequest, [{ projectFileName: configFile.path, files: [moduleFile1, file1Consumer1, file1Consumer2] }]);
file1Consumer1.content = `let y = 10;`;
host.reloadFS([moduleFile1, file1Consumer1, file1Consumer2, configFile, libFile]);
host.triggerFileWatcherCallback(file1Consumer1.path, /*removed*/ false);
session.executeCommand(changeModuleFile1ShapeRequest1);
sendAffectedFileRequestAndCheckResult(session, moduleFile1FileListRequest, [{ projectFileName: configFile.path, files: [moduleFile1, file1Consumer2] }]);
});
it("should be up-to-date with deleted files", () => {
const host = createServerHost([moduleFile1, file1Consumer1, file1Consumer2, globalFile3, moduleFile2, configFile, libFile]);
const typingsInstaller = createTestTypingsInstaller(host);
const session = new server.Session(host, nullCancellationToken, /*useSingleInferredProject*/ false, typingsInstaller, Utils.byteLength, process.hrtime, nullLogger, /*canUseEvents*/ false);
openFilesForSession([moduleFile1], session);
sendAffectedFileRequestAndCheckResult(session, moduleFile1FileListRequest, [{ projectFileName: configFile.path, files: [moduleFile1, file1Consumer1, file1Consumer2] }]);
session.executeCommand(changeModuleFile1ShapeRequest1);
// Delete file1Consumer2
host.reloadFS([moduleFile1, file1Consumer1, configFile, libFile]);
host.triggerFileWatcherCallback(file1Consumer2.path, /*removed*/ true);
sendAffectedFileRequestAndCheckResult(session, moduleFile1FileListRequest, [{ projectFileName: configFile.path, files: [moduleFile1, file1Consumer1] }]);
});
it("should be up-to-date with newly created files", () => {
const host = createServerHost([moduleFile1, file1Consumer1, file1Consumer2, globalFile3, moduleFile2, configFile, libFile]);
const typingsInstaller = createTestTypingsInstaller(host);
const session = new server.Session(host, nullCancellationToken, /*useSingleInferredProject*/ false, typingsInstaller, Utils.byteLength, process.hrtime, nullLogger, /*canUseEvents*/ false);
openFilesForSession([moduleFile1], session);
sendAffectedFileRequestAndCheckResult(session, moduleFile1FileListRequest, [{ projectFileName: configFile.path, files: [moduleFile1, file1Consumer1, file1Consumer2] }]);
const file1Consumer3: FileOrFolder = {
path: "/a/b/file1Consumer3.ts",
content: `import {Foo} from "./moduleFile1"; let y = Foo();`
};
host.reloadFS([moduleFile1, file1Consumer1, file1Consumer2, file1Consumer3, globalFile3, configFile, libFile]);
host.triggerDirectoryWatcherCallback(ts.getDirectoryPath(file1Consumer3.path), file1Consumer3.path);
host.runQueuedTimeoutCallbacks();
session.executeCommand(changeModuleFile1ShapeRequest1);
sendAffectedFileRequestAndCheckResult(session, moduleFile1FileListRequest, [{ projectFileName: configFile.path, files: [moduleFile1, file1Consumer1, file1Consumer2, file1Consumer3] }]);
});
it("should detect changes in non-root files", () => {
moduleFile1 = {
path: "/a/b/moduleFile1.ts",
content: "export function Foo() { };"
};
file1Consumer1 = {
path: "/a/b/file1Consumer1.ts",
content: `import {Foo} from "./moduleFile1"; let y = Foo();`
};
configFile = {
path: "/a/b/tsconfig.json",
content: `{
"compileOnSave": true,
"files": ["${file1Consumer1.path}"]
}`
};
const host = createServerHost([moduleFile1, file1Consumer1, configFile, libFile]);
const typingsInstaller = createTestTypingsInstaller(host);
const session = new server.Session(host, nullCancellationToken, /*useSingleInferredProject*/ false, typingsInstaller, Utils.byteLength, process.hrtime, nullLogger, /*canUseEvents*/ false);
openFilesForSession([moduleFile1, file1Consumer1], session);
sendAffectedFileRequestAndCheckResult(session, moduleFile1FileListRequest, [{ projectFileName: configFile.path, files: [moduleFile1, file1Consumer1] }]);
// change file1 shape now, and verify both files are affected
session.executeCommand(changeModuleFile1ShapeRequest1);
sendAffectedFileRequestAndCheckResult(session, moduleFile1FileListRequest, [{ projectFileName: configFile.path, files: [moduleFile1, file1Consumer1] }]);
// change file1 internal, and verify only file1 is affected
session.executeCommand(changeModuleFile1InternalRequest1);
sendAffectedFileRequestAndCheckResult(session, moduleFile1FileListRequest, [{ projectFileName: configFile.path, files: [moduleFile1] }]);
});
it("should return all files if a global file changed shape", () => {
const host = createServerHost([moduleFile1, file1Consumer1, file1Consumer2, globalFile3, moduleFile2, configFile, libFile]);
const typingsInstaller = createTestTypingsInstaller(host);
const session = new server.Session(host, nullCancellationToken, /*useSingleInferredProject*/ false, typingsInstaller, Utils.byteLength, process.hrtime, nullLogger, /*canUseEvents*/ false);
openFilesForSession([globalFile3], session);
const changeGlobalFile3ShapeRequest = makeSessionRequest<server.protocol.ChangeRequestArgs>(server.CommandNames.Change, {
file: globalFile3.path,
line: 1,
offset: 1,
endLine: 1,
endOffset: 1,
insertString: `var T2: string;`
});
// check after file1 shape changes
session.executeCommand(changeGlobalFile3ShapeRequest);
const globalFile3FileListRequest = makeSessionRequest<server.protocol.FileRequestArgs>(server.CommandNames.CompileOnSaveAffectedFileList, { file: globalFile3.path });
sendAffectedFileRequestAndCheckResult(session, globalFile3FileListRequest, [{ projectFileName: configFile.path, files: [moduleFile1, file1Consumer1, file1Consumer2, globalFile3, moduleFile2] }]);
});
it("should return empty array if CompileOnSave is not enabled", () => {
configFile = {
path: "/a/b/tsconfig.json",
content: `{}`
};
const host = createServerHost([moduleFile1, file1Consumer1, file1Consumer2, configFile, libFile]);
const typingsInstaller = createTestTypingsInstaller(host);
const session = new server.Session(host, nullCancellationToken, /*useSingleInferredProject*/ false, typingsInstaller, Utils.byteLength, process.hrtime, nullLogger, /*canUseEvents*/ false);
openFilesForSession([moduleFile1], session);
sendAffectedFileRequestAndCheckResult(session, moduleFile1FileListRequest, []);
});
it("should always return the file itself if '--isolatedModules' is specified", () => {
configFile = {
path: "/a/b/tsconfig.json",
content: `{
"compileOnSave": true,
"compilerOptions": {
"isolatedModules": true
}
}`
};
const host = createServerHost([moduleFile1, file1Consumer1, configFile, libFile]);
const typingsInstaller = createTestTypingsInstaller(host);
const session = new server.Session(host, nullCancellationToken, /*useSingleInferredProject*/ false, typingsInstaller, Utils.byteLength, process.hrtime, nullLogger, /*canUseEvents*/ false);
openFilesForSession([moduleFile1], session);
const file1ChangeShapeRequest = makeSessionRequest<server.protocol.ChangeRequestArgs>(server.CommandNames.Change, {
file: moduleFile1.path,
line: 1,
offset: 27,
endLine: 1,
endOffset: 27,
insertString: `Point,`
});
session.executeCommand(file1ChangeShapeRequest);
sendAffectedFileRequestAndCheckResult(session, moduleFile1FileListRequest, [{ projectFileName: configFile.path, files: [moduleFile1] }]);
});
it("should always return the file itself if '--out' or '--outFile' is specified", () => {
configFile = {
path: "/a/b/tsconfig.json",
content: `{
"compileOnSave": true,
"compilerOptions": {
"module": "system",
"outFile": "/a/b/out.js"
}
}`
};
const host = createServerHost([moduleFile1, file1Consumer1, configFile, libFile]);
const typingsInstaller = createTestTypingsInstaller(host);
const session = new server.Session(host, nullCancellationToken, /*useSingleInferredProject*/ false, typingsInstaller, Utils.byteLength, process.hrtime, nullLogger, /*canUseEvents*/ false);
openFilesForSession([moduleFile1], session);
const file1ChangeShapeRequest = makeSessionRequest<server.protocol.ChangeRequestArgs>(server.CommandNames.Change, {
file: moduleFile1.path,
line: 1,
offset: 27,
endLine: 1,
endOffset: 27,
insertString: `Point,`
});
session.executeCommand(file1ChangeShapeRequest);
sendAffectedFileRequestAndCheckResult(session, moduleFile1FileListRequest, [{ projectFileName: configFile.path, files: [moduleFile1] }]);
});
it("should return cascaded affected file list", () => {
const file1Consumer1Consumer1: FileOrFolder = {
path: "/a/b/file1Consumer1Consumer1.ts",
content: `import {y} from "./file1Consumer1";`
};
const host = createServerHost([moduleFile1, file1Consumer1, file1Consumer1Consumer1, globalFile3, configFile, libFile]);
const typingsInstaller = createTestTypingsInstaller(host);
const session = new server.Session(host, nullCancellationToken, /*useSingleInferredProject*/ false, typingsInstaller, Utils.byteLength, process.hrtime, nullLogger, /*canUseEvents*/ false);
openFilesForSession([moduleFile1, file1Consumer1], session);
sendAffectedFileRequestAndCheckResult(session, moduleFile1FileListRequest, [{ projectFileName: configFile.path, files: [moduleFile1, file1Consumer1, file1Consumer1Consumer1] }]);
const changeFile1Consumer1ShapeRequest = makeSessionRequest<server.protocol.ChangeRequestArgs>(server.CommandNames.Change, {
file: file1Consumer1.path,
line: 2,
offset: 1,
endLine: 2,
endOffset: 1,
insertString: `export var T: number;`
});
session.executeCommand(changeModuleFile1ShapeRequest1);
session.executeCommand(changeFile1Consumer1ShapeRequest);
sendAffectedFileRequestAndCheckResult(session, moduleFile1FileListRequest, [{ projectFileName: configFile.path, files: [moduleFile1, file1Consumer1, file1Consumer1Consumer1] }]);
});
it("should work fine for files with circular references", () => {
const file1: FileOrFolder = {
path: "/a/b/file1.ts",
content: `
/// <reference path="./file2.ts" />
export var t1 = 10;`
};
const file2: FileOrFolder = {
path: "/a/b/file2.ts",
content: `
/// <reference path="./file1.ts" />
export var t2 = 10;`
};
const host = createServerHost([file1, file2, configFile]);
const typingsInstaller = createTestTypingsInstaller(host);
const session = new server.Session(host, nullCancellationToken, /*useSingleInferredProject*/ false, typingsInstaller, Utils.byteLength, process.hrtime, nullLogger, /*canUseEvents*/ false);
openFilesForSession([file1, file2], session);
const file1AffectedListRequest = makeSessionRequest<server.protocol.FileRequestArgs>(server.CommandNames.CompileOnSaveAffectedFileList, { file: file1.path });
sendAffectedFileRequestAndCheckResult(session, file1AffectedListRequest, [{ projectFileName: configFile.path, files: [file1, file2] }]);
});
it("should return results for all projects if not specifying projectFileName", () => {
const file1: FileOrFolder = { path: "/a/b/file1.ts", content: "export var t = 10;" };
const file2: FileOrFolder = { path: "/a/b/file2.ts", content: `import {t} from "./file1"; var t2 = 11;` };
const file3: FileOrFolder = { path: "/a/c/file2.ts", content: `import {t} from "../b/file1"; var t3 = 11;` };
const configFile1: FileOrFolder = { path: "/a/b/tsconfig.json", content: `{ "compileOnSave": true }` };
const configFile2: FileOrFolder = { path: "/a/c/tsconfig.json", content: `{ "compileOnSave": true }` };
const host = createServerHost([file1, file2, file3, configFile1, configFile2]);
const session = createSession(host);
openFilesForSession([file1, file2, file3], session);
const file1AffectedListRequest = makeSessionRequest<server.protocol.FileRequestArgs>(server.CommandNames.CompileOnSaveAffectedFileList, { file: file1.path });
sendAffectedFileRequestAndCheckResult(session, file1AffectedListRequest, [
{ projectFileName: configFile1.path, files: [file1, file2] },
{ projectFileName: configFile2.path, files: [file1, file3] }
]);
});
it("should detect removed code file", () => {
const referenceFile1: FileOrFolder = {
path: "/a/b/referenceFile1.ts",
content: `
/// <reference path="./moduleFile1.ts" />
export var x = Foo();`
};
const host = createServerHost([moduleFile1, referenceFile1, configFile]);
const session = createSession(host);
openFilesForSession([referenceFile1], session);
host.reloadFS([referenceFile1, configFile]);
host.triggerFileWatcherCallback(moduleFile1.path, /*removed*/ true);
const request = makeSessionRequest<server.protocol.FileRequestArgs>(server.CommandNames.CompileOnSaveAffectedFileList, { file: referenceFile1.path });
sendAffectedFileRequestAndCheckResult(session, request, [
{ projectFileName: configFile.path, files: [referenceFile1] }
]);
const requestForMissingFile = makeSessionRequest<server.protocol.FileRequestArgs>(server.CommandNames.CompileOnSaveAffectedFileList, { file: moduleFile1.path });
sendAffectedFileRequestAndCheckResult(session, requestForMissingFile, []);
});
it("should detect non-existing code file", () => {
const referenceFile1: FileOrFolder = {
path: "/a/b/referenceFile1.ts",
content: `
/// <reference path="./moduleFile2.ts" />
export var x = Foo();`
};
const host = createServerHost([referenceFile1, configFile]);
const session = createSession(host);
openFilesForSession([referenceFile1], session);
const request = makeSessionRequest<server.protocol.FileRequestArgs>(server.CommandNames.CompileOnSaveAffectedFileList, { file: referenceFile1.path });
sendAffectedFileRequestAndCheckResult(session, request, [
{ projectFileName: configFile.path, files: [referenceFile1] }
]);
});
});
});
describe("EmitFile test", () => {
it("should emit specified file", () => {
const file1 = {
path: "/a/b/f1.ts",
content: `export function Foo() { return 10; }`
};
const file2 = {
path: "/a/b/f2.ts",
content: `import {Foo} from "./f1"; let y = Foo();`
};
const configFile = {
path: "/a/b/tsconfig.json",
content: `{}`
};
const host = createServerHost([file1, file2, configFile, libFile]);
const typingsInstaller = createTestTypingsInstaller(host);
const session = new server.Session(host, nullCancellationToken, /*useSingleInferredProject*/ false, typingsInstaller, Utils.byteLength, process.hrtime, nullLogger, /*canUseEvents*/ false);
openFilesForSession([file1, file2], session);
const compileFileRequest = makeSessionRequest<server.protocol.CompileOnSaveEmitFileRequestArgs>(server.CommandNames.CompileOnSaveEmitFile, { file: file1.path, projectFileName: configFile.path });
session.executeCommand(compileFileRequest);
const expectedEmittedFileName = "/a/b/f1.js";
assert.isTrue(host.fileExists(expectedEmittedFileName));
assert.equal(host.readFile(expectedEmittedFileName), `"use strict";\r\nfunction Foo() { return 10; }\r\nexports.Foo = Foo;\r\n`);
});
});
}
@@ -0,0 +1,187 @@
/// <reference path="..\harness.ts" />
/// <reference path="..\virtualFileSystem.ts" />
namespace ts {
const testContents = {
"/dev/tsconfig.json": `{
"extends": "./configs/base",
"files": [
"main.ts",
"supplemental.ts"
]
}`,
"/dev/tsconfig.nostrictnull.json": `{
"extends": "./tsconfig",
"compilerOptions": {
"strictNullChecks": false
}
}`,
"/dev/configs/base.json": `{
"compilerOptions": {
"allowJs": true,
"noImplicitAny": true,
"strictNullChecks": true
}
}`,
"/dev/configs/tests.json": `{
"compilerOptions": {
"preserveConstEnums": true,
"removeComments": false,
"sourceMap": true
},
"exclude": [
"../tests/baselines",
"../tests/scenarios"
],
"include": [
"../tests/**/*.ts"
]
}`,
"/dev/circular.json": `{
"extends": "./circular2",
"compilerOptions": {
"module": "amd"
}
}`,
"/dev/circular2.json": `{
"extends": "./circular",
"compilerOptions": {
"module": "commonjs"
}
}`,
"/dev/missing.json": `{
"extends": "./missing2",
"compilerOptions": {
"types": []
}
}`,
"/dev/failure.json": `{
"extends": "./failure2.json",
"compilerOptions": {
"typeRoots": []
}
}`,
"/dev/failure2.json": `{
"excludes": ["*.js"]
}`,
"/dev/configs/first.json": `{
"extends": "./base",
"compilerOptions": {
"module": "commonjs"
},
"files": ["../main.ts"]
}`,
"/dev/configs/second.json": `{
"extends": "./base",
"compilerOptions": {
"module": "amd"
},
"include": ["../supplemental.*"]
}`,
"/dev/extends.json": `{ "extends": 42 }`,
"/dev/extends2.json": `{ "extends": "configs/base" }`,
"/dev/main.ts": "",
"/dev/supplemental.ts": "",
"/dev/tests/unit/spec.ts": "",
"/dev/tests/utils.ts": "",
"/dev/tests/scenarios/first.json": "",
"/dev/tests/baselines/first/output.ts": ""
};
const caseInsensitiveBasePath = "c:/dev/";
const caseInsensitiveHost = new Utils.MockParseConfigHost(caseInsensitiveBasePath, /*useCaseSensitiveFileNames*/ false, mapObject(testContents, (key, content) => [`c:${key}`, content]));
const caseSensitiveBasePath = "/dev/";
const caseSensitiveHost = new Utils.MockParseConfigHost(caseSensitiveBasePath, /*useCaseSensitiveFileNames*/ true, testContents);
function verifyDiagnostics(actual: Diagnostic[], expected: {code: number, category: DiagnosticCategory, messageText: string}[]) {
assert.isTrue(expected.length === actual.length, `Expected error: ${JSON.stringify(expected)}. Actual error: ${JSON.stringify(actual)}.`);
for (let i = 0; i < actual.length; i++) {
const actualError = actual[i];
const expectedError = expected[i];
assert.equal(actualError.code, expectedError.code, "Error code mismatch");
assert.equal(actualError.category, expectedError.category, "Category mismatch");
assert.equal(flattenDiagnosticMessageText(actualError.messageText, "\n"), expectedError.messageText);
}
}
describe("Configuration Extension", () => {
forEach<[string, string, Utils.MockParseConfigHost], void>([
["under a case insensitive host", caseInsensitiveBasePath, caseInsensitiveHost],
["under a case sensitive host", caseSensitiveBasePath, caseSensitiveHost]
], ([testName, basePath, host]) => {
function testSuccess(name: string, entry: string, expected: CompilerOptions, expectedFiles: string[]) {
it(name, () => {
const {config, error} = ts.readConfigFile(entry, name => host.readFile(name));
assert(config && !error, flattenDiagnosticMessageText(error && error.messageText, "\n"));
const parsed = ts.parseJsonConfigFileContent(config, host, basePath, {}, entry);
assert(!parsed.errors.length, flattenDiagnosticMessageText(parsed.errors[0] && parsed.errors[0].messageText, "\n"));
expected.configFilePath = entry;
assert.deepEqual(parsed.options, expected);
assert.deepEqual(parsed.fileNames, expectedFiles);
});
}
function testFailure(name: string, entry: string, expectedDiagnostics: {code: number, category: DiagnosticCategory, messageText: string}[]) {
it(name, () => {
const {config, error} = ts.readConfigFile(entry, name => host.readFile(name));
assert(config && !error, flattenDiagnosticMessageText(error && error.messageText, "\n"));
const parsed = ts.parseJsonConfigFileContent(config, host, basePath, {}, entry);
verifyDiagnostics(parsed.errors, expectedDiagnostics);
});
}
describe(testName, () => {
testSuccess("can resolve an extension with a base extension", "tsconfig.json", {
allowJs: true,
noImplicitAny: true,
strictNullChecks: true,
}, [
combinePaths(basePath, "main.ts"),
combinePaths(basePath, "supplemental.ts"),
]);
testSuccess("can resolve an extension with a base extension that overrides options", "tsconfig.nostrictnull.json", {
allowJs: true,
noImplicitAny: true,
strictNullChecks: false,
}, [
combinePaths(basePath, "main.ts"),
combinePaths(basePath, "supplemental.ts"),
]);
testFailure("can report errors on circular imports", "circular.json", [
{
code: 18000,
category: DiagnosticCategory.Error,
messageText: `Circularity detected while resolving configuration: ${[combinePaths(basePath, "circular.json"), combinePaths(basePath, "circular2.json"), combinePaths(basePath, "circular.json")].join(" -> ")}`
}
]);
testFailure("can report missing configurations", "missing.json", [{
code: 6096,
category: DiagnosticCategory.Message,
messageText: `File './missing2' does not exist.`
}]);
testFailure("can report errors in extended configs", "failure.json", [{
code: 6114,
category: DiagnosticCategory.Error,
messageText: `Unknown option 'excludes'. Did you mean 'exclude'?`
}]);
testFailure("can error when 'extends' is not a string", "extends.json", [{
code: 5024,
category: DiagnosticCategory.Error,
messageText: `Compiler option 'extends' requires a value of type string.`
}]);
testFailure("can error when 'extends' is neither relative nor rooted.", "extends2.json", [{
code: 18001,
category: DiagnosticCategory.Error,
messageText: `The path in an 'extends' options must be relative or rooted.`
}]);
});
});
});
}
@@ -233,7 +233,7 @@ namespace ts {
file: undefined,
start: 0,
length: 0,
messageText: "Argument for '--lib' option must be: 'es5', 'es6', 'es2015', 'es7', 'es2016', 'es2017', 'dom', 'webworker', 'scripthost', 'es2015.core', 'es2015.collection', 'es2015.generator', 'es2015.iterable', 'es2015.promise', 'es2015.proxy', 'es2015.reflect', 'es2015.symbol', 'es2015.symbol.wellknown', 'es2016.array.include', 'es2017.object', 'es2017.sharedmemory'",
messageText: "Argument for '--lib' option must be: 'es5', 'es6', 'es2015', 'es7', 'es2016', 'es2017', 'dom', 'dom.iterable', 'webworker', 'scripthost', 'es2015.core', 'es2015.collection', 'es2015.generator', 'es2015.iterable', 'es2015.promise', 'es2015.proxy', 'es2015.reflect', 'es2015.symbol', 'es2015.symbol.wellknown', 'es2016.array.include', 'es2017.object', 'es2017.sharedmemory'",
code: Diagnostics.Argument_for_0_option_must_be_Colon_1.code,
category: Diagnostics.Argument_for_0_option_must_be_Colon_1.category
}]
@@ -264,7 +264,7 @@ namespace ts {
file: undefined,
start: 0,
length: 0,
messageText: "Argument for '--lib' option must be: 'es5', 'es6', 'es2015', 'es7', 'es2016', 'es2017', 'dom', 'webworker', 'scripthost', 'es2015.core', 'es2015.collection', 'es2015.generator', 'es2015.iterable', 'es2015.promise', 'es2015.proxy', 'es2015.reflect', 'es2015.symbol', 'es2015.symbol.wellknown', 'es2016.array.include', 'es2017.object', 'es2017.sharedmemory'",
messageText: "Argument for '--lib' option must be: 'es5', 'es6', 'es2015', 'es7', 'es2016', 'es2017', 'dom', 'dom.iterable', 'webworker', 'scripthost', 'es2015.core', 'es2015.collection', 'es2015.generator', 'es2015.iterable', 'es2015.promise', 'es2015.proxy', 'es2015.reflect', 'es2015.symbol', 'es2015.symbol.wellknown', 'es2016.array.include', 'es2017.object', 'es2017.sharedmemory'",
code: Diagnostics.Argument_for_0_option_must_be_Colon_1.code,
category: Diagnostics.Argument_for_0_option_must_be_Colon_1.category
}]
@@ -295,7 +295,7 @@ namespace ts {
file: undefined,
start: 0,
length: 0,
messageText: "Argument for '--lib' option must be: 'es5', 'es6', 'es2015', 'es7', 'es2016', 'es2017', 'dom', 'webworker', 'scripthost', 'es2015.core', 'es2015.collection', 'es2015.generator', 'es2015.iterable', 'es2015.promise', 'es2015.proxy', 'es2015.reflect', 'es2015.symbol', 'es2015.symbol.wellknown', 'es2016.array.include', 'es2017.object', 'es2017.sharedmemory'",
messageText: "Argument for '--lib' option must be: 'es5', 'es6', 'es2015', 'es7', 'es2016', 'es2017', 'dom', 'dom.iterable', 'webworker', 'scripthost', 'es2015.core', 'es2015.collection', 'es2015.generator', 'es2015.iterable', 'es2015.promise', 'es2015.proxy', 'es2015.reflect', 'es2015.symbol', 'es2015.symbol.wellknown', 'es2016.array.include', 'es2017.object', 'es2017.sharedmemory'",
code: Diagnostics.Argument_for_0_option_must_be_Colon_1.code,
category: Diagnostics.Argument_for_0_option_must_be_Colon_1.category
}]
@@ -326,7 +326,7 @@ namespace ts {
file: undefined,
start: 0,
length: 0,
messageText: "Argument for '--lib' option must be: 'es5', 'es6', 'es2015', 'es7', 'es2016', 'es2017', 'dom', 'webworker', 'scripthost', 'es2015.core', 'es2015.collection', 'es2015.generator', 'es2015.iterable', 'es2015.promise', 'es2015.proxy', 'es2015.reflect', 'es2015.symbol', 'es2015.symbol.wellknown', 'es2016.array.include', 'es2017.object', 'es2017.sharedmemory'",
messageText: "Argument for '--lib' option must be: 'es5', 'es6', 'es2015', 'es7', 'es2016', 'es2017', 'dom', 'dom.iterable', 'webworker', 'scripthost', 'es2015.core', 'es2015.collection', 'es2015.generator', 'es2015.iterable', 'es2015.promise', 'es2015.proxy', 'es2015.reflect', 'es2015.symbol', 'es2015.symbol.wellknown', 'es2016.array.include', 'es2017.object', 'es2017.sharedmemory'",
code: Diagnostics.Argument_for_0_option_must_be_Colon_1.code,
category: Diagnostics.Argument_for_0_option_must_be_Colon_1.category
}]
@@ -403,6 +403,8 @@ namespace ts {
{
compilerOptions: <CompilerOptions>{
allowJs: true,
maxNodeModuleJsDepth: 2,
allowSyntheticDefaultImports: true,
module: ModuleKind.CommonJS,
target: ScriptTarget.ES5,
noImplicitAny: false,
@@ -429,6 +431,8 @@ namespace ts {
{
compilerOptions: <CompilerOptions>{
allowJs: false,
maxNodeModuleJsDepth: 2,
allowSyntheticDefaultImports: true,
module: ModuleKind.CommonJS,
target: ScriptTarget.ES5,
noImplicitAny: false,
@@ -450,7 +454,9 @@ namespace ts {
{
compilerOptions:
{
allowJs: true
allowJs: true,
maxNodeModuleJsDepth: 2,
allowSyntheticDefaultImports: true
},
errors: [{
file: undefined,
@@ -469,7 +475,9 @@ namespace ts {
{
compilerOptions:
{
allowJs: true
allowJs: true,
maxNodeModuleJsDepth: 2,
allowSyntheticDefaultImports: true
},
errors: <Diagnostic[]>[]
}
@@ -0,0 +1,44 @@
/// <reference path="..\harness.ts" />
/// <reference path="..\..\compiler\commandLineParser.ts" />
namespace ts {
describe("initTSConfig", () => {
function initTSConfigCorrectly(name: string, commandLinesArgs: string[]) {
describe(name, () => {
const commandLine = parseCommandLine(commandLinesArgs);
const initResult = generateTSConfig(commandLine.options, commandLine.fileNames);
const outputFileName = `tsConfig/${name.replace(/[^a-z0-9\-. ]/ig, "")}/tsconfig.json`;
it(`Correct output for ${outputFileName}`, () => {
Harness.Baseline.runBaseline(outputFileName, () => {
if (initResult) {
return JSON.stringify(initResult, undefined, 4);
}
else {
// This can happen if compiler recieve invalid compiler-options
/* tslint:disable:no-null-keyword */
return null;
/* tslint:enable:no-null-keyword */
}
});
});
});
}
initTSConfigCorrectly("Default initialized TSConfig", ["--init"]);
initTSConfigCorrectly("Initialized TSConfig with files options", ["--init", "file0.st", "file1.ts", "file2.ts"]);
initTSConfigCorrectly("Initialized TSConfig with boolean value compiler options", ["--init", "--noUnusedLocals"]);
initTSConfigCorrectly("Initialized TSConfig with enum value compiler options", ["--init", "--target", "es5", "--jsx", "react"]);
initTSConfigCorrectly("Initialized TSConfig with list compiler options", ["--init", "--types", "jquery,mocha"]);
initTSConfigCorrectly("Initialized TSConfig with list compiler options with enum value", ["--init", "--lib", "es5,es2015.core"]);
initTSConfigCorrectly("Initialized TSConfig with incorrect compiler option", ["--init", "--someNonExistOption"]);
initTSConfigCorrectly("Initialized TSConfig with incorrect compiler option value", ["--init", "--lib", "nonExistLib,es5,es2015.promise"]);
});
}
File diff suppressed because it is too large Load Diff
+4 -4
View File
@@ -446,8 +446,8 @@ export = C;
"/a/B/c/moduleB.ts": `import a = require("./moduleC")`,
"/a/B/c/moduleC.ts": "export var x",
"/a/B/c/moduleD.ts": `
import a = require("./moduleA.ts");
import b = require("./moduleB.ts");
import a = require("./moduleA");
import b = require("./moduleB");
`
});
test(files, { module: ts.ModuleKind.CommonJS, forceConsistentCasingInFileNames: true }, "/a/B/c", /*useCaseSensitiveFileNames*/ false, ["moduleD.ts"], [1149]);
@@ -458,8 +458,8 @@ import b = require("./moduleB.ts");
"/a/B/c/moduleB.ts": `import a = require("./moduleC")`,
"/a/B/c/moduleC.ts": "export var x",
"/a/B/c/moduleD.ts": `
import a = require("./moduleA.ts");
import b = require("./moduleB.ts");
import a = require("./moduleA");
import b = require("./moduleB");
`
});
test(files, { module: ts.ModuleKind.CommonJS, forceConsistentCasingInFileNames: true }, "/a/B/c", /*useCaseSensitiveFileNames*/ false, ["moduleD.ts"], []);
+186
View File
@@ -0,0 +1,186 @@
/// <reference path="../harness.ts" />
/// <reference path="./tsserverProjectSystem.ts" />
/// <reference path="../../server/typingsInstaller/typingsInstaller.ts" />
namespace ts.projectSystem {
describe("Project errors", () => {
function checkProjectErrors(projectFiles: server.ProjectFilesWithTSDiagnostics, expectedErrors: string[]) {
assert.isTrue(projectFiles !== undefined, "missing project files");
const errors = projectFiles.projectErrors;
assert.equal(errors ? errors.length : 0, expectedErrors.length, `expected ${expectedErrors.length} error in the list`);
if (expectedErrors.length) {
for (let i = 0; i < errors.length; i++) {
const actualMessage = flattenDiagnosticMessageText(errors[i].messageText, "\n");
const expectedMessage = expectedErrors[i];
assert.isTrue(actualMessage.indexOf(expectedMessage) === 0, `error message does not match, expected ${actualMessage} to start with ${expectedMessage}`);
}
}
}
it("external project - diagnostics for missing files", () => {
const file1 = {
path: "/a/b/app.ts",
content: ""
};
const file2 = {
path: "/a/b/lib.ts",
content: ""
};
// only file1 exists - expect error
const host = createServerHost([file1]);
const projectService = createProjectService(host);
const projectFileName = "/a/b/test.csproj";
{
projectService.openExternalProject({
projectFileName,
options: {},
rootFiles: toExternalFiles([file1.path, file2.path])
});
projectService.checkNumberOfProjects({ externalProjects: 1 });
const knownProjects = projectService.synchronizeProjectList([]);
checkProjectErrors(knownProjects[0], ["File '/a/b/lib.ts' not found."]);
}
// only file2 exists - expect error
host.reloadFS([file2]);
{
projectService.openExternalProject({
projectFileName,
options: {},
rootFiles: toExternalFiles([file1.path, file2.path])
});
projectService.checkNumberOfProjects({ externalProjects: 1 });
const knownProjects = projectService.synchronizeProjectList([]);
checkProjectErrors(knownProjects[0], ["File '/a/b/app.ts' not found."]);
}
// both files exist - expect no errors
host.reloadFS([file1, file2]);
{
projectService.openExternalProject({
projectFileName,
options: {},
rootFiles: toExternalFiles([file1.path, file2.path])
});
projectService.checkNumberOfProjects({ externalProjects: 1 });
const knownProjects = projectService.synchronizeProjectList([]);
checkProjectErrors(knownProjects[0], []);
}
});
it("configured projects - diagnostics for missing files", () => {
const file1 = {
path: "/a/b/app.ts",
content: ""
};
const file2 = {
path: "/a/b/lib.ts",
content: ""
};
const config = {
path: "/a/b/tsconfig.json",
content: JSON.stringify({ files: [file1, file2].map(f => getBaseFileName(f.path)) })
};
const host = createServerHost([file1, config]);
const projectService = createProjectService(host);
projectService.openClientFile(file1.path);
projectService.checkNumberOfProjects({ configuredProjects: 1 });
checkProjectErrors(projectService.synchronizeProjectList([])[0], ["File '/a/b/lib.ts' not found."]);
host.reloadFS([file1, file2, config]);
projectService.openClientFile(file1.path);
projectService.checkNumberOfProjects({ configuredProjects: 1 });
checkProjectErrors(projectService.synchronizeProjectList([])[0], []);
});
it("configured projects - diagnostics for corrupted config 1", () => {
const file1 = {
path: "/a/b/app.ts",
content: ""
};
const file2 = {
path: "/a/b/lib.ts",
content: ""
};
const correctConfig = {
path: "/a/b/tsconfig.json",
content: JSON.stringify({ files: [file1, file2].map(f => getBaseFileName(f.path)) })
};
const corruptedConfig = {
path: correctConfig.path,
content: correctConfig.content.substr(1)
};
const host = createServerHost([file1, file2, corruptedConfig]);
const projectService = createProjectService(host);
projectService.openClientFile(file1.path);
{
projectService.checkNumberOfProjects({ configuredProjects: 1 });
const configuredProject = forEach(projectService.synchronizeProjectList([]), f => f.info.projectName === corruptedConfig.path && f);
assert.isTrue(configuredProject !== undefined, "should find configured project");
checkProjectErrors(configuredProject, [
"')' expected.",
"Declaration or statement expected.",
"Declaration or statement expected.",
"Failed to parse file '/a/b/tsconfig.json'"
]);
}
// fix config and trigger watcher
host.reloadFS([file1, file2, correctConfig]);
host.triggerFileWatcherCallback(correctConfig.path, /*false*/);
{
projectService.checkNumberOfProjects({ configuredProjects: 1 });
const configuredProject = forEach(projectService.synchronizeProjectList([]), f => f.info.projectName === corruptedConfig.path && f);
assert.isTrue(configuredProject !== undefined, "should find configured project");
checkProjectErrors(configuredProject, []);
}
});
it("configured projects - diagnostics for corrupted config 2", () => {
const file1 = {
path: "/a/b/app.ts",
content: ""
};
const file2 = {
path: "/a/b/lib.ts",
content: ""
};
const correctConfig = {
path: "/a/b/tsconfig.json",
content: JSON.stringify({ files: [file1, file2].map(f => getBaseFileName(f.path)) })
};
const corruptedConfig = {
path: correctConfig.path,
content: correctConfig.content.substr(1)
};
const host = createServerHost([file1, file2, correctConfig]);
const projectService = createProjectService(host);
projectService.openClientFile(file1.path);
{
projectService.checkNumberOfProjects({ configuredProjects: 1 });
const configuredProject = forEach(projectService.synchronizeProjectList([]), f => f.info.projectName === corruptedConfig.path && f);
assert.isTrue(configuredProject !== undefined, "should find configured project");
checkProjectErrors(configuredProject, []);
}
// break config and trigger watcher
host.reloadFS([file1, file2, corruptedConfig]);
host.triggerFileWatcherCallback(corruptedConfig.path, /*false*/);
{
projectService.checkNumberOfProjects({ configuredProjects: 1 });
const configuredProject = forEach(projectService.synchronizeProjectList([]), f => f.info.projectName === corruptedConfig.path && f);
assert.isTrue(configuredProject !== undefined, "should find configured project");
checkProjectErrors(configuredProject, [
"')' expected.",
"Declaration or statement expected.",
"Declaration or statement expected.",
"Failed to parse file '/a/b/tsconfig.json'"
]);
}
});
});
}
@@ -1,79 +0,0 @@
class a {
constructor ( n : number ) ;
constructor ( s : string ) ;
constructor ( ns : any ) {
}
public pgF ( ) { } ;
public pv ;
public get d ( ) {
return 30 ;
}
public set d ( ) {
}
public static get p2 ( ) {
return { x : 30 , y : 40 } ;
}
private static d2 ( ) {
}
private static get p3 ( ) {
return "string" ;
}
private pv3 ;
private foo ( n : number ) : string ;
private foo ( s : string ) : string ;
private foo ( ns : any ) {
return ns.toString ( ) ;
}
}
class b extends a {
}
class m1b {
}
interface m1ib {
}
class c extends m1b {
}
class ib2 implements m1ib {
}
declare class aAmbient {
constructor ( n : number ) ;
constructor ( s : string ) ;
public pgF ( ) : void ;
public pv ;
public d : number ;
static p2 : { x : number ; y : number ; } ;
static d2 ( ) ;
static p3 ;
private pv3 ;
private foo ( s ) ;
}
class d {
private foo ( n : number ) : string ;
private foo ( ns : any ) {
return ns.toString ( ) ;
}
private foo ( s : string ) : string ;
}
class e {
private foo ( ns : any ) {
return ns.toString ( ) ;
}
private foo ( s : string ) : string ;
private foo ( n : number ) : string ;
}
@@ -1,79 +0,0 @@
class a {
constructor(n: number);
constructor(s: string);
constructor(ns: any) {
}
public pgF() { };
public pv;
public get d() {
return 30;
}
public set d() {
}
public static get p2() {
return { x: 30, y: 40 };
}
private static d2() {
}
private static get p3() {
return "string";
}
private pv3;
private foo(n: number): string;
private foo(s: string): string;
private foo(ns: any) {
return ns.toString();
}
}
class b extends a {
}
class m1b {
}
interface m1ib {
}
class c extends m1b {
}
class ib2 implements m1ib {
}
declare class aAmbient {
constructor(n: number);
constructor(s: string);
public pgF(): void;
public pv;
public d: number;
static p2: { x: number; y: number; };
static d2();
static p3;
private pv3;
private foo(s);
}
class d {
private foo(n: number): string;
private foo(ns: any) {
return ns.toString();
}
private foo(s: string): string;
}
class e {
private foo(ns: any) {
return ns.toString();
}
private foo(s: string): string;
private foo(n: number): string;
}
@@ -1,4 +0,0 @@
class foo {
constructor (n?: number, m? = 5, o?: string = "") { }
x:number = 1?2:3;
}
@@ -1,4 +0,0 @@
class foo {
constructor(n?: number, m? = 5, o?: string = "") { }
x: number = 1 ? 2 : 3;
}
@@ -1,3 +0,0 @@
$ ( document ) . ready ( function ( ) {
alert ( 'i am ready' ) ;
} );
@@ -1,3 +0,0 @@
$(document).ready(function() {
alert('i am ready');
});
@@ -1,10 +0,0 @@
function foo ( x : { } ) { }
foo ( { } ) ;
interface bar {
x : { } ;
y : ( ) => { } ;
}
@@ -1,10 +0,0 @@
function foo(x: {}) { }
foo({});
interface bar {
x: {};
y: () => {};
}
@@ -1,112 +0,0 @@
// valid
( ) => 1 ;
( arg ) => 2 ;
arg => 2 ;
( arg = 1 ) => 3 ;
( arg ? ) => 4 ;
( arg : number ) => 5 ;
( arg : number = 0 ) => 6 ;
( arg ? : number ) => 7 ;
( ... arg : number [ ] ) => 8 ;
( arg1 , arg2 ) => 12 ;
( arg1 = 1 , arg2 =3 ) => 13 ;
( arg1 ? , arg2 ? ) => 14 ;
( arg1 : number , arg2 : number ) => 15 ;
( arg1 : number = 0 , arg2 : number = 1 ) => 16 ;
( arg1 ? : number , arg2 ? : number ) => 17 ;
( arg1 , ... arg2 : number [ ] ) => 18 ;
( arg1 , arg2 ? : number ) => 19 ;
// in paren
( ( ) => 21 ) ;
( ( arg ) => 22 ) ;
( ( arg = 1 ) => 23 ) ;
( ( arg ? ) => 24 ) ;
( ( arg : number ) => 25 ) ;
( ( arg : number = 0 ) => 26 ) ;
( ( arg ? : number ) => 27 ) ;
( ( ... arg : number [ ] ) => 28 ) ;
// in multiple paren
( ( ( ( ( arg ) => { return 32 ; } ) ) ) ) ;
// in ternary exression
false ? ( ) => 41 : null ;
false ? ( arg ) => 42 : null ;
false ? ( arg = 1 ) => 43 : null ;
false ? ( arg ? ) => 44 : null ;
false ? ( arg : number ) => 45 : null ;
false ? ( arg ? : number ) => 46 : null ;
false ? ( arg ? : number = 0 ) => 47 : null ;
false ? ( ... arg : number [ ] ) => 48 : null ;
// in ternary exression within paren
false ? ( ( ) => 51 ) : null ;
false ? ( ( arg ) => 52 ) : null ;
false ? ( ( arg = 1 ) => 53 ) : null ;
false ? ( ( arg ? ) => 54 ) : null ;
false ? ( ( arg : number ) => 55 ) : null ;
false ? ( ( arg ? : number ) => 56 ) : null ;
false ? ( ( arg ? : number = 0 ) => 57 ) : null ;
false ? ( ( ... arg : number [ ] ) => 58 ) : null ;
// ternary exression's else clause
false ? null : ( ) => 61 ;
false ? null : ( arg ) => 62 ;
false ? null : ( arg = 1 ) => 63 ;
false ? null : ( arg ? ) => 64 ;
false ? null : ( arg : number ) => 65 ;
false ? null : ( arg ? : number ) => 66 ;
false ? null : ( arg ? : number = 0 ) => 67 ;
false ? null : ( ... arg : number [ ] ) => 68 ;
// nested ternary expressions
( a ? ) => { return a ; } ? ( b ? ) => { return b ; } : ( c ? ) => { return c ; } ;
//multiple levels
( a ? ) => { return a ; } ? ( b ) => ( c ) => 81 : ( c ) => ( d ) => 82 ;
// In Expressions
( ( arg ) => 90 ) instanceof Function ;
( ( arg = 1 ) => 91 ) instanceof Function ;
( ( arg ? ) => 92 ) instanceof Function ;
( ( arg : number ) => 93 ) instanceof Function ;
( ( arg : number = 1 ) => 94 ) instanceof Function ;
( ( arg ? : number ) => 95 ) instanceof Function ;
( ( ... arg : number [ ] ) => 96 ) instanceof Function ;
'' + ( arg ) => 100 ;
( ( arg ) => 0 ) + '' + ( arg ) => 101 ;
( ( arg = 1 ) => 0 ) + '' + ( arg = 2 ) => 102 ;
( ( arg ? ) => 0 ) + '' + ( arg ? ) => 103 ;
( ( arg : number ) => 0 ) + '' + ( arg : number ) => 104 ;
( ( arg : number = 1 ) => 0 ) + '' + ( arg : number = 2 ) => 105 ;
( ( arg ? : number = 1 ) => 0 ) + '' + ( arg ? : number = 2 ) => 106 ;
( ( ... arg : number [ ] ) => 0 ) + '' + ( ... arg : number [ ] ) => 107 ;
( ( arg1 , arg2 ? ) => 0 ) + '' + ( arg1 , arg2 ? ) => 108 ;
( ( arg1 , ... arg2 : number [ ] ) => 0 ) + '' + ( arg1 , ... arg2 : number [ ] ) => 108 ;
// Function Parameters
function foo ( ... arg : any [ ] ) { }
foo (
( a ) => 110 ,
( ( a ) => 111 ) ,
( a ) => {
return 112 ;
} ,
( a ? ) => 113 ,
( a , b ? ) => 114 ,
( a : number ) => 115 ,
( a : number = 0 ) => 116 ,
( a = 0 ) => 117 ,
( a ? : number = 0 ) => 118 ,
( a ? , b ? : number = 0 ) => 118 ,
( ... a : number [ ] ) => 119 ,
( a , b ? = 0 , ... c : number [ ] ) => 120 ,
( a ) => ( b ) => ( c ) => 121 ,
false ? ( a ) => 0 : ( b ) => 122
) ;
@@ -1,112 +0,0 @@
// valid
() => 1;
(arg) => 2;
arg => 2;
(arg = 1) => 3;
(arg?) => 4;
(arg: number) => 5;
(arg: number = 0) => 6;
(arg?: number) => 7;
(...arg: number[]) => 8;
(arg1, arg2) => 12;
(arg1 = 1, arg2 = 3) => 13;
(arg1?, arg2?) => 14;
(arg1: number, arg2: number) => 15;
(arg1: number = 0, arg2: number = 1) => 16;
(arg1?: number, arg2?: number) => 17;
(arg1, ...arg2: number[]) => 18;
(arg1, arg2?: number) => 19;
// in paren
(() => 21);
((arg) => 22);
((arg = 1) => 23);
((arg?) => 24);
((arg: number) => 25);
((arg: number = 0) => 26);
((arg?: number) => 27);
((...arg: number[]) => 28);
// in multiple paren
(((((arg) => { return 32; }))));
// in ternary exression
false ? () => 41 : null;
false ? (arg) => 42 : null;
false ? (arg = 1) => 43 : null;
false ? (arg?) => 44 : null;
false ? (arg: number) => 45 : null;
false ? (arg?: number) => 46 : null;
false ? (arg?: number = 0) => 47 : null;
false ? (...arg: number[]) => 48 : null;
// in ternary exression within paren
false ? (() => 51) : null;
false ? ((arg) => 52) : null;
false ? ((arg = 1) => 53) : null;
false ? ((arg?) => 54) : null;
false ? ((arg: number) => 55) : null;
false ? ((arg?: number) => 56) : null;
false ? ((arg?: number = 0) => 57) : null;
false ? ((...arg: number[]) => 58) : null;
// ternary exression's else clause
false ? null : () => 61;
false ? null : (arg) => 62;
false ? null : (arg = 1) => 63;
false ? null : (arg?) => 64;
false ? null : (arg: number) => 65;
false ? null : (arg?: number) => 66;
false ? null : (arg?: number = 0) => 67;
false ? null : (...arg: number[]) => 68;
// nested ternary expressions
(a?) => { return a; } ? (b?) => { return b; } : (c?) => { return c; };
//multiple levels
(a?) => { return a; } ? (b) => (c) => 81 : (c) => (d) => 82;
// In Expressions
((arg) => 90) instanceof Function;
((arg = 1) => 91) instanceof Function;
((arg?) => 92) instanceof Function;
((arg: number) => 93) instanceof Function;
((arg: number = 1) => 94) instanceof Function;
((arg?: number) => 95) instanceof Function;
((...arg: number[]) => 96) instanceof Function;
'' + (arg) => 100;
((arg) => 0) + '' + (arg) => 101;
((arg = 1) => 0) + '' + (arg = 2) => 102;
((arg?) => 0) + '' + (arg?) => 103;
((arg: number) => 0) + '' + (arg: number) => 104;
((arg: number = 1) => 0) + '' + (arg: number = 2) => 105;
((arg?: number = 1) => 0) + '' + (arg?: number = 2) => 106;
((...arg: number[]) => 0) + '' + (...arg: number[]) => 107;
((arg1, arg2?) => 0) + '' + (arg1, arg2?) => 108;
((arg1, ...arg2: number[]) => 0) + '' + (arg1, ...arg2: number[]) => 108;
// Function Parameters
function foo(...arg: any[]) { }
foo(
(a) => 110,
((a) => 111),
(a) => {
return 112;
},
(a?) => 113,
(a, b?) => 114,
(a: number) => 115,
(a: number = 0) => 116,
(a = 0) => 117,
(a?: number = 0) => 118,
(a?, b?: number = 0) => 118,
(...a: number[]) => 119,
(a, b? = 0, ...c: number[]) => 120,
(a) => (b) => (c) => 121,
false ? (a) => 0 : (b) => 122
);
@@ -1,2 +0,0 @@
if(false){debugger;}
if ( false ) { debugger ; }
@@ -1,2 +0,0 @@
if (false) { debugger; }
if (false) { debugger; }
@@ -1,13 +0,0 @@
var fun1 = function ( ) {
var x = 'foo' ,
z = 'bar' ;
return x ;
},
fun2 = ( function ( f ) {
var fun = function ( ) {
console . log ( f ( ) ) ;
},
x = 'Foo' ;
return fun ;
} ( fun1 ) ) ;
@@ -1,13 +0,0 @@
var fun1 = function() {
var x = 'foo',
z = 'bar';
return x;
},
fun2 = (function(f) {
var fun = function() {
console.log(f());
},
x = 'Foo';
return fun;
} (fun1));
@@ -1,3 +0,0 @@
export class A {
}
@@ -1,6 +0,0 @@
module Foo {
}
import bar = Foo;
import bar2=Foo;
@@ -1,6 +0,0 @@
module Foo {
}
import bar = Foo;
import bar2 = Foo;
@@ -1,95 +0,0 @@
var a;var c , b;var $d
var $e
var f
a++;b++;
function f ( ) {
for (i = 0; i < 10; i++) {
k = abc + 123 ^ d;
a = XYZ[m (a[b[c][d]])];
break;
switch ( variable){
case 1: abc += 425;
break;
case 404 : a [x--/2]%=3 ;
break ;
case vari : v[--x ] *=++y*( m + n / k[z]);
for (a in b){
for (a = 0; a < 10; ++a) {
a++;--a;
if (a == b) {
a++;b--;
}
else
if (a == c){
++a;
(--c)+=d;
$c = $a + --$b;
}
if (a == b)
if (a != b) {
if (a !== b)
if (a === b)
--a;
else
--a;
else {
a--;++b;
a++
}
}
}
for (x in y) {
m-=m;
k=1+2+3+4;
}
}
break;
}
}
var a ={b:function(){}};
return {a:1,b:2}
}
var z = 1;
for (i = 0; i < 10; i++)
for (j = 0; j < 10; j++)
for (k = 0; k < 10; ++k) {
z++;
}
for (k = 0; k < 10; k += 2) {
z++;
}
$(document).ready ();
function pageLoad() {
$('#TextBox1' ) . unbind ( ) ;
$('#TextBox1' ) . datepicker ( ) ;
}
function pageLoad ( ) {
var webclass=[
{ 'student' :
{ 'id': '1', 'name': 'Linda Jones', 'legacySkill': 'Access, VB 5.0' }
} ,
{ 'student':
{'id':'2','name':'Adam Davidson','legacySkill':'Cobol,MainFrame'}
} ,
{ 'student':
{ 'id':'3','name':'Charles Boyer' ,'legacySkill':'HTML, XML'}
}
];
$create(Sys.UI.DataView,{data:webclass},null,null,$get('SList'));
}
$( document ).ready(function(){
alert('hello');
} ) ;
@@ -1,98 +0,0 @@
var a; var c, b; var $d
var $e
var f
a++; b++;
function f() {
for (i = 0; i < 10; i++) {
k = abc + 123 ^ d;
a = XYZ[m(a[b[c][d]])];
break;
switch (variable) {
case 1: abc += 425;
break;
case 404: a[x-- / 2] %= 3;
break;
case vari: v[--x] *= ++y * (m + n / k[z]);
for (a in b) {
for (a = 0; a < 10; ++a) {
a++; --a;
if (a == b) {
a++; b--;
}
else
if (a == c) {
++a;
(--c) += d;
$c = $a + --$b;
}
if (a == b)
if (a != b) {
if (a !== b)
if (a === b)
--a;
else
--a;
else {
a--; ++b;
a++
}
}
}
for (x in y) {
m -= m;
k = 1 + 2 + 3 + 4;
}
}
break;
}
}
var a = { b: function() { } };
return { a: 1, b: 2 }
}
var z = 1;
for (i = 0; i < 10; i++)
for (j = 0; j < 10; j++)
for (k = 0; k < 10; ++k) {
z++;
}
for (k = 0; k < 10; k += 2) {
z++;
}
$(document).ready();
function pageLoad() {
$('#TextBox1').unbind();
$('#TextBox1').datepicker();
}
function pageLoad() {
var webclass = [
{
'student':
{ 'id': '1', 'name': 'Linda Jones', 'legacySkill': 'Access, VB 5.0' }
},
{
'student':
{ 'id': '2', 'name': 'Adam Davidson', 'legacySkill': 'Cobol,MainFrame' }
},
{
'student':
{ 'id': '3', 'name': 'Charles Boyer', 'legacySkill': 'HTML, XML' }
}
];
$create(Sys.UI.DataView, { data: webclass }, null, null, $get('SList'));
}
$(document).ready(function() {
alert('hello');
});
@@ -1,3 +0,0 @@
module Foo {
export module A . B . C { }
}
@@ -1,3 +0,0 @@
module Foo {
export module A.B.C { }
}
@@ -1,76 +0,0 @@
module mod1 {
export class b {
}
class d {
}
export interface ib {
}
}
module m2 {
export module m3 {
export class c extends mod1.b {
}
export class ib2 implements mod1.ib {
}
}
}
class c extends mod1.b {
}
class ib2 implements mod1.ib {
}
declare export module "m4" {
export class d {
} ;
var x : d ;
export function foo ( ) : d ;
}
import m4 = module ( "m4" ) ;
export var x4 = m4.x ;
export var d4 = m4.d ;
export var f4 = m4.foo ( ) ;
export module m1 {
declare export module "m2" {
export class d {
} ;
var x: d ;
export function foo ( ) : d ;
}
import m2 = module ( "m2" ) ;
import m3 = module ( "m4" ) ;
export var x2 = m2.x ;
export var d2 = m2.d ;
export var f2 = m2.foo ( ) ;
export var x3 = m3.x ;
export var d3 = m3.d ;
export var f3 = m3.foo ( ) ;
}
export var x2 = m1.m2.x ;
export var d2 = m1.m2.d ;
export var f2 = m1.m2.foo ( ) ;
export var x3 = m1.m3.x ;
export var d3 = m1.m3.d ;
export var f3 = m1.m3.foo ( ) ;
export module m5 {
export var x2 = m1.m2.x ;
export var d2 = m1.m2.d ;
export var f2 = m1.m2.foo ( ) ;
export var x3 = m1.m3.x ;
export var d3 = m1.m3.d ;
export var f3 = m1.m3.foo ( ) ;
}
@@ -1,76 +0,0 @@
module mod1 {
export class b {
}
class d {
}
export interface ib {
}
}
module m2 {
export module m3 {
export class c extends mod1.b {
}
export class ib2 implements mod1.ib {
}
}
}
class c extends mod1.b {
}
class ib2 implements mod1.ib {
}
declare export module "m4" {
export class d {
};
var x: d;
export function foo(): d;
}
import m4 = module("m4");
export var x4 = m4.x;
export var d4 = m4.d;
export var f4 = m4.foo();
export module m1 {
declare export module "m2" {
export class d {
};
var x: d;
export function foo(): d;
}
import m2 = module("m2");
import m3 = module("m4");
export var x2 = m2.x;
export var d2 = m2.d;
export var f2 = m2.foo();
export var x3 = m3.x;
export var d3 = m3.d;
export var f3 = m3.foo();
}
export var x2 = m1.m2.x;
export var d2 = m1.m2.d;
export var f2 = m1.m2.foo();
export var x3 = m1.m3.x;
export var d3 = m1.m3.d;
export var f3 = m1.m3.foo();
export module m5 {
export var x2 = m1.m2.x;
export var d2 = m1.m2.d;
export var f2 = m1.m2.foo();
export var x3 = m1.m3.x;
export var d3 = m1.m3.d;
export var f3 = m1.m3.foo();
}
@@ -1,27 +0,0 @@
var x = {foo: 1,
bar: "tt",
boo: 1 + 5};
var x2 = {foo: 1,
bar: "tt",boo:1+5};
function Foo() {
var typeICalc = {
clear: {
"()": [1, 2, 3]
}
}
}
// Rule for object literal members for the "value" of the memebr to follow the indent
// of the member, i.e. the relative position of the value is maintained when the member
// is indented.
var x2 = {
foo:
3,
'bar':
{ a: 1, b : 2}
};
var x={ };
var y = {};
@@ -1,31 +0,0 @@
var x = {
foo: 1,
bar: "tt",
boo: 1 + 5
};
var x2 = {
foo: 1,
bar: "tt", boo: 1 + 5
};
function Foo() {
var typeICalc = {
clear: {
"()": [1, 2, 3]
}
}
}
// Rule for object literal members for the "value" of the memebr to follow the indent
// of the member, i.e. the relative position of the value is maintained when the member
// is indented.
var x2 = {
foo:
3,
'bar':
{ a: 1, b: 2 }
};
var x = {};
var y = {};
@@ -1,32 +0,0 @@
function f( ) {
var x = 3;
var z = 2 ;
a = z ++ - 2 * x ;
for ( ; ; ) {
a+=(g +g)*a%t;
b -- ;
}
switch ( a )
{
case 1 : {
a ++ ;
b--;
if(a===a)
return;
else
{
for(a in b)
if(a!=a)
{
for(a in b)
{
a++;
}
}
}
}
default:
break;
}
}
@@ -1,28 +0,0 @@
function f() {
var x = 3;
var z = 2;
a = z++ - 2 * x;
for (; ;) {
a += (g + g) * a % t;
b--;
}
switch (a) {
case 1: {
a++;
b--;
if (a === a)
return;
else {
for (a in b)
if (a != a) {
for (a in b) {
a++;
}
}
}
}
default:
break;
}
}
@@ -1 +0,0 @@
var a=b+c^d-e*++f;
@@ -1 +0,0 @@
var a = b + c ^ d - e * ++f;
@@ -1 +0,0 @@
class test { constructor () { } }
@@ -1 +0,0 @@
class test { constructor() { } }
@@ -1,10 +0,0 @@
module Tools {
export enum NodeType {
Error,
Comment,
}
export enum foob
{
Blah=1, Bleah=2
}
}
@@ -1,9 +0,0 @@
module Tools {
export enum NodeType {
Error,
Comment,
}
export enum foob {
Blah = 1, Bleah = 2
}
}
@@ -1,65 +0,0 @@
module MyModule
{
module A.B.C {
module F {
}
}
interface Blah
{
boo: string;
}
class Foo
{
}
class Foo2 {
public foo():number {
return 5 * 6;
}
public foo2() {
if (1 === 2)
{
var y : number= 76;
return y;
}
while (2 == 3) {
if ( y == null ) {
}
}
}
public foo3() {
if (1 === 2)
//comment preventing line merging
{
var y = 76;
return y;
}
}
}
}
function foo(a:number, b:number):number
{
return 0;
}
function bar(a:number, b:number) :number[] {
return [];
}
module BugFix3 {
declare var f: {
(): any;
(x: number): string;
foo: number;
};
}
@@ -1,58 +0,0 @@
module MyModule {
module A.B.C {
module F {
}
}
interface Blah {
boo: string;
}
class Foo {
}
class Foo2 {
public foo(): number {
return 5 * 6;
}
public foo2() {
if (1 === 2) {
var y: number = 76;
return y;
}
while (2 == 3) {
if (y == null) {
}
}
}
public foo3() {
if (1 === 2)
//comment preventing line merging
{
var y = 76;
return y;
}
}
}
}
function foo(a: number, b: number): number {
return 0;
}
function bar(a: number, b: number): number[] {
return [];
}
module BugFix3 {
declare var f: {
(): any;
(x: number): string;
foo: number;
};
}
@@ -1,17 +0,0 @@
function f(a,b,c,d){
for(var i=0;i<10;i++){
var a=0;
var b=a+a+a*a%a/2-1;
b+=a;
++b;
f(a,b,c,d);
if(1===1){
var m=function(e,f){
return e^f;
}
}
}
}
for (var i = 0 ; i < this.foo(); i++) {
}
@@ -1,17 +0,0 @@
function f(a, b, c, d) {
for (var i = 0; i < 10; i++) {
var a = 0;
var b = a + a + a * a % a / 2 - 1;
b += a;
++b;
f(a, b, c, d);
if (1 === 1) {
var m = function(e, f) {
return e ^ f;
}
}
}
}
for (var i = 0 ; i < this.foo(); i++) {
}
@@ -1,9 +0,0 @@
with (foo.bar)
{
}
with (bar.blah)
{
}
@@ -1,6 +0,0 @@
with (foo.bar) {
}
with (bar.blah) {
}
+10 -5
View File
@@ -18,20 +18,25 @@ namespace ts.server {
createDirectory(): void {},
getExecutingFilePath(): string { return void 0; },
getCurrentDirectory(): string { return void 0; },
getEnvironmentVariable(name: string): string { return ""; },
readDirectory(): string[] { return []; },
exit(): void { },
setTimeout(callback, ms, ...args) { return 0; },
clearTimeout(timeoutId) { }
clearTimeout(timeoutId) { },
setImmediate: () => 0,
clearImmediate() {}
};
const nullCancellationToken: HostCancellationToken = { isCancellationRequested: () => false };
const mockLogger: Logger = {
close(): void {},
isVerbose(): boolean { return false; },
hasLevel(): boolean { return false; },
loggingEnabled(): boolean { return false; },
perftrc(s: string): void {},
info(s: string): void {},
startGroup(): void {},
endGroup(): void {},
msg(s: string, type?: string): void {},
getLogFileName: (): string => undefined
};
describe("the Session class", () => {
@@ -39,7 +44,7 @@ namespace ts.server {
let lastSent: protocol.Message;
beforeEach(() => {
session = new Session(mockHost, Utils.byteLength, process.hrtime, mockLogger);
session = new Session(mockHost, nullCancellationToken, /*useOneInferredProject*/ false, /*typingsInstaller*/ undefined, Utils.byteLength, process.hrtime, mockLogger, /*canUseEvents*/ true);
session.send = (msg: protocol.Message) => {
lastSent = msg;
};
@@ -264,7 +269,7 @@ namespace ts.server {
lastSent: protocol.Message;
customHandler = "testhandler";
constructor() {
super(mockHost, Utils.byteLength, process.hrtime, mockLogger);
super(mockHost, nullCancellationToken, /*useOneInferredProject*/ false, /*typingsInstaller*/ undefined, Utils.byteLength, process.hrtime, mockLogger, /*canUseEvents*/ true);
this.addProtocolHandler(this.customHandler, () => {
return { response: undefined, responseRequired: true };
});
@@ -322,7 +327,7 @@ namespace ts.server {
class InProcSession extends Session {
private queue: protocol.Request[] = [];
constructor(private client: InProcClient) {
super(mockHost, Utils.byteLength, process.hrtime, mockLogger);
super(mockHost, nullCancellationToken, /*useOneInferredProject*/ false, /*typingsInstaller*/ undefined, Utils.byteLength, process.hrtime, mockLogger, /*canUseEvents*/ true);
this.addProtocolHandler("echo", (req: protocol.Request) => ({
response: req.arguments,
responseRequired: true
+4 -5
View File
@@ -62,7 +62,7 @@ namespace ts {
});
it("Correct errors for " + justName, () => {
Harness.Baseline.runBaseline("Correct errors", justName.replace(/\.tsx?$/, ".errors.txt"), () => {
Harness.Baseline.runBaseline(justName.replace(/\.tsx?$/, ".errors.txt"), () => {
if (transpileResult.diagnostics.length === 0) {
/* tslint:disable:no-null-keyword */
return null;
@@ -75,7 +75,7 @@ namespace ts {
if (canUseOldTranspile) {
it("Correct errors (old transpile) for " + justName, () => {
Harness.Baseline.runBaseline("Correct errors", justName.replace(/\.tsx?$/, ".oldTranspile.errors.txt"), () => {
Harness.Baseline.runBaseline(justName.replace(/\.tsx?$/, ".oldTranspile.errors.txt"), () => {
if (oldTranspileDiagnostics.length === 0) {
/* tslint:disable:no-null-keyword */
return null;
@@ -88,7 +88,7 @@ namespace ts {
}
it("Correct output for " + justName, () => {
Harness.Baseline.runBaseline("Correct output", justName.replace(/\.tsx?$/, ".js"), () => {
Harness.Baseline.runBaseline(justName.replace(/\.tsx?$/, ".js"), () => {
if (transpileResult.outputText) {
return transpileResult.outputText;
}
@@ -101,10 +101,9 @@ namespace ts {
});
});
if (canUseOldTranspile) {
it("Correct output (old transpile) for " + justName, () => {
Harness.Baseline.runBaseline("Correct output", justName.replace(/\.tsx?$/, ".oldTranspile.js"), () => {
Harness.Baseline.runBaseline(justName.replace(/\.tsx?$/, ".oldTranspile.js"), () => {
return oldTranspileResult;
});
});
+23 -2
View File
@@ -151,7 +151,7 @@ namespace ts {
);
});
it("always exclude outDir", () => {
it("exclude outDir unless overridden", () => {
const tsconfigWithoutExclude =
`{
"compilerOptions": {
@@ -169,7 +169,7 @@ namespace ts {
const allFiles = ["/bin/a.ts", "/b.ts"];
const expectedFiles = ["/b.ts"];
assertParseFileList(tsconfigWithoutExclude, "tsconfig.json", rootDir, allFiles, expectedFiles);
assertParseFileList(tsconfigWithExclude, "tsconfig.json", rootDir, allFiles, expectedFiles);
assertParseFileList(tsconfigWithExclude, "tsconfig.json", rootDir, allFiles, allFiles);
});
it("implicitly exclude common package folders", () => {
@@ -181,5 +181,26 @@ namespace ts {
["/d.ts", "/folder/e.ts"]
);
});
it("parse and re-emit tsconfig.json file with diagnostics", () => {
const content = `{
"compilerOptions": {
"allowJs": true
// Some comments
"outDir": "bin"
}
"files": ["file1.ts"]
}`;
const { configJsonObject, diagnostics } = sanitizeConfigFile("config.json", content);
const expectedResult = {
compilerOptions: {
allowJs: true,
outDir: "bin"
},
files: ["file1.ts"]
};
assert.isTrue(diagnostics.length === 2);
assert.equal(JSON.stringify(configJsonObject), JSON.stringify(expectedResult));
});
});
}
File diff suppressed because it is too large Load Diff
+723
View File
@@ -0,0 +1,723 @@
/// <reference path="../harness.ts" />
/// <reference path="./tsserverProjectSystem.ts" />
/// <reference path="../../server/typingsInstaller/typingsInstaller.ts" />
namespace ts.projectSystem {
import TI = server.typingsInstaller;
interface InstallerParams {
globalTypingsCacheLocation?: string;
throttleLimit?: number;
}
class Installer extends TestTypingsInstaller {
constructor(host: server.ServerHost, p?: InstallerParams) {
super(
(p && p.globalTypingsCacheLocation) || "/a/data",
(p && p.throttleLimit) || 5,
host);
}
installAll(expectedView: typeof TI.NpmViewRequest[], expectedInstall: typeof TI.NpmInstallRequest[]) {
this.checkPendingCommands(expectedView);
this.executePendingCommands();
this.checkPendingCommands(expectedInstall);
this.executePendingCommands();
}
}
describe("typingsInstaller", () => {
function executeCommand(self: Installer, host: TestServerHost, installedTypings: string[], typingFiles: FileOrFolder[], requestKind: TI.RequestKind, cb: TI.RequestCompletedAction): void {
switch (requestKind) {
case TI.NpmInstallRequest:
self.addPostExecAction(requestKind, installedTypings, (err, stdout, stderr) => {
for (const file of typingFiles) {
host.createFileOrFolder(file, /*createParentDirectory*/ true);
}
cb(err, stdout, stderr);
});
break;
case TI.NpmViewRequest:
self.addPostExecAction(requestKind, installedTypings, cb);
break;
default:
assert.isTrue(false, `unexpected request kind ${requestKind}`);
break;
}
}
it("configured projects (typings installed) 1", () => {
const file1 = {
path: "/a/b/app.js",
content: ""
};
const tsconfig = {
path: "/a/b/tsconfig.json",
content: JSON.stringify({
compilerOptions: {
allowJs: true
},
typingOptions: {
enableAutoDiscovery: true
}
})
};
const packageJson = {
path: "/a/b/package.json",
content: JSON.stringify({
name: "test",
dependencies: {
jquery: "^3.1.0"
}
})
};
const jquery = {
path: "/a/data/node_modules/@types/jquery/index.d.ts",
content: "declare const $: { x: number }"
};
const host = createServerHost([file1, tsconfig, packageJson]);
const installer = new (class extends Installer {
constructor() {
super(host);
}
runCommand(requestKind: TI.RequestKind, requestId: number, command: string, cwd: string, cb: server.typingsInstaller.RequestCompletedAction) {
const installedTypings = ["@types/jquery"];
const typingFiles = [jquery];
executeCommand(this, host, installedTypings, typingFiles, requestKind, cb);
}
})();
const projectService = createProjectService(host, { useSingleInferredProject: true, typingsInstaller: installer });
projectService.openClientFile(file1.path);
checkNumberOfProjects(projectService, { configuredProjects: 1 });
const p = projectService.configuredProjects[0];
checkProjectActualFiles(p, [file1.path]);
installer.installAll([TI.NpmViewRequest], [TI.NpmInstallRequest]);
checkNumberOfProjects(projectService, { configuredProjects: 1 });
checkProjectActualFiles(p, [file1.path, jquery.path]);
});
it("inferred project (typings installed)", () => {
const file1 = {
path: "/a/b/app.js",
content: ""
};
const packageJson = {
path: "/a/b/package.json",
content: JSON.stringify({
name: "test",
dependencies: {
jquery: "^3.1.0"
}
})
};
const jquery = {
path: "/a/data/node_modules/@types/jquery/index.d.ts",
content: "declare const $: { x: number }"
};
const host = createServerHost([file1, packageJson]);
const installer = new (class extends Installer {
constructor() {
super(host);
}
runCommand(requestKind: TI.RequestKind, requestId: number, command: string, cwd: string, cb: server.typingsInstaller.RequestCompletedAction) {
const installedTypings = ["@types/jquery"];
const typingFiles = [jquery];
executeCommand(this, host, installedTypings, typingFiles, requestKind, cb);
}
})();
const projectService = createProjectService(host, { useSingleInferredProject: true, typingsInstaller: installer });
projectService.openClientFile(file1.path);
checkNumberOfProjects(projectService, { inferredProjects: 1 });
const p = projectService.inferredProjects[0];
checkProjectActualFiles(p, [file1.path]);
installer.installAll([TI.NpmViewRequest], [TI.NpmInstallRequest]);
checkNumberOfProjects(projectService, { inferredProjects: 1 });
checkProjectActualFiles(p, [file1.path, jquery.path]);
});
it("external project - no typing options, no .d.ts/js files", () => {
const file1 = {
path: "/a/b/app.ts",
content: ""
};
const host = createServerHost([file1]);
const installer = new (class extends Installer {
constructor() {
super(host);
}
enqueueInstallTypingsRequest() {
assert(false, "auto discovery should not be enabled");
}
})();
const projectFileName = "/a/app/test.csproj";
const projectService = createProjectService(host, { typingsInstaller: installer });
projectService.openExternalProject({
projectFileName,
options: {},
rootFiles: [toExternalFile(file1.path)]
});
installer.checkPendingCommands([]);
// by default auto discovery will kick in if project contain only .js/.d.ts files
// in this case project contain only ts files - no auto discovery
projectService.checkNumberOfProjects({ externalProjects: 1 });
});
it("external project - no autoDiscovery in typing options, no .d.ts/js files", () => {
const file1 = {
path: "/a/b/app.ts",
content: ""
};
const host = createServerHost([file1]);
const installer = new (class extends Installer {
constructor() {
super(host);
}
enqueueInstallTypingsRequest() {
assert(false, "auto discovery should not be enabled");
}
})();
const projectFileName = "/a/app/test.csproj";
const projectService = createProjectService(host, { typingsInstaller: installer });
projectService.openExternalProject({
projectFileName,
options: {},
rootFiles: [toExternalFile(file1.path)],
typingOptions: { include: ["jquery"] }
});
installer.checkPendingCommands([]);
// by default auto discovery will kick in if project contain only .js/.d.ts files
// in this case project contain only ts files - no auto discovery even if typing options is set
projectService.checkNumberOfProjects({ externalProjects: 1 });
});
it("external project - autoDiscovery = true, no .d.ts/js files", () => {
const file1 = {
path: "/a/b/app.ts",
content: ""
};
const jquery = {
path: "/a/data/node_modules/@types/jquery/index.d.ts",
content: "declare const $: { x: number }"
};
const host = createServerHost([file1]);
let enqueueIsCalled = false;
const installer = new (class extends Installer {
constructor() {
super(host);
}
enqueueInstallTypingsRequest(project: server.Project, typingOptions: TypingOptions) {
enqueueIsCalled = true;
super.enqueueInstallTypingsRequest(project, typingOptions);
}
runCommand(requestKind: TI.RequestKind, requestId: number, command: string, cwd: string, cb: TI.RequestCompletedAction): void {
const installedTypings = ["@types/jquery"];
const typingFiles = [jquery];
executeCommand(this, host, installedTypings, typingFiles, requestKind, cb);
}
})();
const projectFileName = "/a/app/test.csproj";
const projectService = createProjectService(host, { typingsInstaller: installer });
projectService.openExternalProject({
projectFileName,
options: {},
rootFiles: [toExternalFile(file1.path)],
typingOptions: { enableAutoDiscovery: true, include: ["node"] }
});
assert.isTrue(enqueueIsCalled, "expected enqueueIsCalled to be true");
installer.installAll([TI.NpmViewRequest], [TI.NpmInstallRequest]);
// autoDiscovery is set in typing options - use it even if project contains only .ts files
projectService.checkNumberOfProjects({ externalProjects: 1 });
});
it("external project - no typing options, with only js, jsx, d.ts files", () => {
// Tests:
// 1. react typings are installed for .jsx
// 2. loose files names are matched against safe list for typings if
// this is a JS project (only js, jsx, d.ts files are present)
const file1 = {
path: "/a/b/lodash.js",
content: ""
};
const file2 = {
path: "/a/b/file2.jsx",
content: ""
};
const file3 = {
path: "/a/b/file3.d.ts",
content: ""
};
const react = {
path: "/a/data/node_modules/@types/react/index.d.ts",
content: "declare const react: { x: number }"
};
const lodash = {
path: "/a/data/node_modules/@types/lodash/index.d.ts",
content: "declare const lodash: { x: number }"
};
const host = createServerHost([file1, file2, file3]);
const installer = new (class extends Installer {
constructor() {
super(host);
}
runCommand(requestKind: TI.RequestKind, requestId: number, command: string, cwd: string, cb: TI.RequestCompletedAction): void {
const installedTypings = ["@types/lodash", "@types/react"];
const typingFiles = [lodash, react];
executeCommand(this, host, installedTypings, typingFiles, requestKind, cb);
}
})();
const projectFileName = "/a/app/test.csproj";
const projectService = createProjectService(host, { typingsInstaller: installer });
projectService.openExternalProject({
projectFileName,
options: { allowJS: true, moduleResolution: ModuleResolutionKind.NodeJs },
rootFiles: [toExternalFile(file1.path), toExternalFile(file2.path), toExternalFile(file3.path)],
typingOptions: {}
});
const p = projectService.externalProjects[0];
projectService.checkNumberOfProjects({ externalProjects: 1 });
checkProjectActualFiles(p, [file1.path, file2.path, file3.path]);
installer.installAll([TI.NpmViewRequest, TI.NpmViewRequest], [TI.NpmInstallRequest], );
checkNumberOfProjects(projectService, { externalProjects: 1 });
checkProjectActualFiles(p, [file1.path, file2.path, file3.path, lodash.path, react.path]);
});
it("external project - no typing options, with js & ts files", () => {
// Tests:
// 1. No typings are included for JS projects when the project contains ts files
const file1 = {
path: "/a/b/jquery.js",
content: ""
};
const file2 = {
path: "/a/b/file2.ts",
content: ""
};
const host = createServerHost([file1, file2]);
let enqueueIsCalled = false;
const installer = new (class extends Installer {
constructor() {
super(host);
}
enqueueInstallTypingsRequest(project: server.Project, typingOptions: TypingOptions) {
enqueueIsCalled = true;
super.enqueueInstallTypingsRequest(project, typingOptions);
}
runCommand(requestKind: TI.RequestKind, requestId: number, command: string, cwd: string, cb: TI.RequestCompletedAction): void {
const installedTypings: string[] = [];
const typingFiles: FileOrFolder[] = [];
executeCommand(this, host, installedTypings, typingFiles, requestKind, cb);
}
})();
const projectFileName = "/a/app/test.csproj";
const projectService = createProjectService(host, { typingsInstaller: installer });
projectService.openExternalProject({
projectFileName,
options: { allowJS: true, moduleResolution: ModuleResolutionKind.NodeJs },
rootFiles: [toExternalFile(file1.path), toExternalFile(file2.path)],
typingOptions: {}
});
const p = projectService.externalProjects[0];
projectService.checkNumberOfProjects({ externalProjects: 1 });
checkProjectActualFiles(p, [file1.path, file2.path]);
installer.checkPendingCommands([]);
checkNumberOfProjects(projectService, { externalProjects: 1 });
checkProjectActualFiles(p, [file1.path, file2.path]);
});
it("external project - with typing options, with only js, d.ts files", () => {
// Tests:
// 1. Safelist matching, typing options includes/excludes and package.json typings are all acquired
// 2. Types for safelist matches are not included when they also appear in the typing option exclude list
// 3. Multiple includes and excludes are respected in typing options
const file1 = {
path: "/a/b/lodash.js",
content: ""
};
const file2 = {
path: "/a/b/commander.js",
content: ""
};
const file3 = {
path: "/a/b/file3.d.ts",
content: ""
};
const packageJson = {
path: "/a/b/package.json",
content: JSON.stringify({
name: "test",
dependencies: {
express: "^3.1.0"
}
})
};
const commander = {
path: "/a/data/node_modules/@types/commander/index.d.ts",
content: "declare const commander: { x: number }"
};
const express = {
path: "/a/data/node_modules/@types/express/index.d.ts",
content: "declare const express: { x: number }"
};
const jquery = {
path: "/a/data/node_modules/@types/jquery/index.d.ts",
content: "declare const jquery: { x: number }"
};
const moment = {
path: "/a/data/node_modules/@types/moment/index.d.ts",
content: "declare const moment: { x: number }"
};
const host = createServerHost([file1, file2, file3, packageJson]);
const installer = new (class extends Installer {
constructor() {
super(host);
}
runCommand(requestKind: TI.RequestKind, requestId: number, command: string, cwd: string, cb: TI.RequestCompletedAction): void {
const installedTypings = ["@types/commander", "@types/express", "@types/jquery", "@types/moment"];
const typingFiles = [commander, express, jquery, moment];
executeCommand(this, host, installedTypings, typingFiles, requestKind, cb);
}
})();
const projectFileName = "/a/app/test.csproj";
const projectService = createProjectService(host, { typingsInstaller: installer });
projectService.openExternalProject({
projectFileName,
options: { allowJS: true, moduleResolution: ModuleResolutionKind.NodeJs },
rootFiles: [toExternalFile(file1.path), toExternalFile(file2.path), toExternalFile(file3.path)],
typingOptions: { include: ["jquery", "moment"], exclude: ["lodash"] }
});
const p = projectService.externalProjects[0];
projectService.checkNumberOfProjects({ externalProjects: 1 });
checkProjectActualFiles(p, [file1.path, file2.path, file3.path]);
installer.installAll(
[TI.NpmViewRequest, TI.NpmViewRequest, TI.NpmViewRequest, TI.NpmViewRequest],
[TI.NpmInstallRequest]
);
checkNumberOfProjects(projectService, { externalProjects: 1 });
checkProjectActualFiles(p, [file1.path, file2.path, file3.path, commander.path, express.path, jquery.path, moment.path]);
});
it("Throttle - delayed typings to install", () => {
const lodashJs = {
path: "/a/b/lodash.js",
content: ""
};
const commanderJs = {
path: "/a/b/commander.js",
content: ""
};
const file3 = {
path: "/a/b/file3.d.ts",
content: ""
};
const packageJson = {
path: "/a/b/package.json",
content: JSON.stringify({
name: "test",
dependencies: {
express: "^3.1.0"
}
})
};
const commander = {
path: "/a/data/node_modules/@types/commander/index.d.ts",
content: "declare const commander: { x: number }"
};
const express = {
path: "/a/data/node_modules/@types/express/index.d.ts",
content: "declare const express: { x: number }"
};
const jquery = {
path: "/a/data/node_modules/@types/jquery/index.d.ts",
content: "declare const jquery: { x: number }"
};
const moment = {
path: "/a/data/node_modules/@types/moment/index.d.ts",
content: "declare const moment: { x: number }"
};
const lodash = {
path: "/a/data/node_modules/@types/lodash/index.d.ts",
content: "declare const lodash: { x: number }"
};
const typingFiles = [commander, express, jquery, moment, lodash];
const host = createServerHost([lodashJs, commanderJs, file3, packageJson]);
const installer = new (class extends Installer {
constructor() {
super(host, { throttleLimit: 3 });
}
runCommand(requestKind: TI.RequestKind, requestId: number, command: string, cwd: string, cb: TI.RequestCompletedAction): void {
const installedTypings = ["@types/commander", "@types/express", "@types/jquery", "@types/moment", "@types/lodash"];
executeCommand(this, host, installedTypings, typingFiles, requestKind, cb);
}
})();
const projectFileName = "/a/app/test.csproj";
const projectService = createProjectService(host, { typingsInstaller: installer });
projectService.openExternalProject({
projectFileName,
options: { allowJS: true, moduleResolution: ModuleResolutionKind.NodeJs },
rootFiles: [toExternalFile(lodashJs.path), toExternalFile(commanderJs.path), toExternalFile(file3.path)],
typingOptions: { include: ["jquery", "moment"] }
});
const p = projectService.externalProjects[0];
projectService.checkNumberOfProjects({ externalProjects: 1 });
checkProjectActualFiles(p, [lodashJs.path, commanderJs.path, file3.path]);
// expected 3 view requests in the queue
installer.checkPendingCommands([TI.NpmViewRequest, TI.NpmViewRequest, TI.NpmViewRequest]);
assert.equal(installer.pendingRunRequests.length, 2, "expected 2 pending requests");
// push view requests
installer.executePendingCommands();
// expected 2 remaining view requests in the queue
installer.checkPendingCommands([TI.NpmViewRequest, TI.NpmViewRequest]);
// push view requests
installer.executePendingCommands();
// expected one install request
installer.checkPendingCommands([TI.NpmInstallRequest]);
installer.executePendingCommands();
// expected all typings file to exist
for (const f of typingFiles) {
assert.isTrue(host.fileExists(f.path), `expected file ${f.path} to exist`);
}
checkNumberOfProjects(projectService, { externalProjects: 1 });
checkProjectActualFiles(p, [lodashJs.path, commanderJs.path, file3.path, commander.path, express.path, jquery.path, moment.path, lodash.path]);
});
it("Throttle - delayed run install requests", () => {
const lodashJs = {
path: "/a/b/lodash.js",
content: ""
};
const commanderJs = {
path: "/a/b/commander.js",
content: ""
};
const file3 = {
path: "/a/b/file3.d.ts",
content: ""
};
const commander = {
path: "/a/data/node_modules/@types/commander/index.d.ts",
content: "declare const commander: { x: number }",
typings: "@types/commander"
};
const jquery = {
path: "/a/data/node_modules/@types/jquery/index.d.ts",
content: "declare const jquery: { x: number }",
typings: "@types/jquery"
};
const lodash = {
path: "/a/data/node_modules/@types/lodash/index.d.ts",
content: "declare const lodash: { x: number }",
typings: "@types/lodash"
};
const cordova = {
path: "/a/data/node_modules/@types/cordova/index.d.ts",
content: "declare const cordova: { x: number }",
typings: "@types/cordova"
};
const grunt = {
path: "/a/data/node_modules/@types/grunt/index.d.ts",
content: "declare const grunt: { x: number }",
typings: "@types/grunt"
};
const gulp = {
path: "/a/data/node_modules/@types/gulp/index.d.ts",
content: "declare const gulp: { x: number }",
typings: "@types/gulp"
};
const host = createServerHost([lodashJs, commanderJs, file3]);
const installer = new (class extends Installer {
constructor() {
super(host, { throttleLimit: 3 });
}
runCommand(requestKind: TI.RequestKind, requestId: number, command: string, cwd: string, cb: TI.RequestCompletedAction): void {
if (requestKind === TI.NpmInstallRequest) {
let typingFiles: (FileOrFolder & { typings: string}) [] = [];
if (command.indexOf("commander") >= 0) {
typingFiles = [commander, jquery, lodash, cordova];
}
else {
typingFiles = [grunt, gulp];
}
executeCommand(this, host, typingFiles.map(f => f.typings), typingFiles, requestKind, cb);
}
else {
executeCommand(this, host, [], [], requestKind, cb);
}
}
})();
// Create project #1 with 4 typings
const projectService = createProjectService(host, { typingsInstaller: installer });
const projectFileName1 = "/a/app/test1.csproj";
projectService.openExternalProject({
projectFileName: projectFileName1,
options: { allowJS: true, moduleResolution: ModuleResolutionKind.NodeJs },
rootFiles: [toExternalFile(lodashJs.path), toExternalFile(commanderJs.path), toExternalFile(file3.path)],
typingOptions: { include: ["jquery", "cordova" ] }
});
installer.checkPendingCommands([TI.NpmViewRequest, TI.NpmViewRequest, TI.NpmViewRequest]);
assert.equal(installer.pendingRunRequests.length, 1, "expect one throttled request");
// Create project #2 with 2 typings
const projectFileName2 = "/a/app/test2.csproj";
projectService.openExternalProject({
projectFileName: projectFileName2,
options: { allowJS: true, moduleResolution: ModuleResolutionKind.NodeJs },
rootFiles: [toExternalFile(file3.path)],
typingOptions: { include: ["grunt", "gulp"] }
});
assert.equal(installer.pendingRunRequests.length, 3, "expect three throttled request");
const p1 = projectService.externalProjects[0];
const p2 = projectService.externalProjects[1];
projectService.checkNumberOfProjects({ externalProjects: 2 });
checkProjectActualFiles(p1, [lodashJs.path, commanderJs.path, file3.path]);
checkProjectActualFiles(p2, [file3.path]);
installer.executePendingCommands();
// expected one view request from the first project and two - from the second one
installer.checkPendingCommands([TI.NpmViewRequest, TI.NpmViewRequest, TI.NpmViewRequest]);
assert.equal(installer.pendingRunRequests.length, 0, "expected no throttled requests");
installer.executePendingCommands();
// should be two install requests from both projects
installer.checkPendingCommands([TI.NpmInstallRequest, TI.NpmInstallRequest]);
installer.executePendingCommands();
checkProjectActualFiles(p1, [lodashJs.path, commanderJs.path, file3.path, commander.path, jquery.path, lodash.path, cordova.path]);
checkProjectActualFiles(p2, [file3.path, grunt.path, gulp.path ]);
});
it("configured projects discover from node_modules", () => {
const app = {
path: "/app.js",
content: ""
};
const jsconfig = {
path: "/jsconfig.json",
content: JSON.stringify({})
};
const jquery = {
path: "/node_modules/jquery/index.js",
content: ""
};
const jqueryPackage = {
path: "/node_modules/jquery/package.json",
content: JSON.stringify({ name: "jquery" })
};
const jqueryDTS = {
path: "/tmp/node_modules/@types/jquery/index.d.ts",
content: ""
};
const host = createServerHost([app, jsconfig, jquery, jqueryPackage]);
const installer = new (class extends Installer {
constructor() {
super(host, { globalTypingsCacheLocation: "/tmp" });
}
runCommand(requestKind: TI.RequestKind, requestId: number, command: string, cwd: string, cb: server.typingsInstaller.RequestCompletedAction) {
const installedTypings = ["@types/jquery"];
const typingFiles = [jqueryDTS];
executeCommand(this, host, installedTypings, typingFiles, requestKind, cb);
}
})();
const projectService = createProjectService(host, { useSingleInferredProject: true, typingsInstaller: installer });
projectService.openClientFile(app.path);
checkNumberOfProjects(projectService, { configuredProjects: 1 });
const p = projectService.configuredProjects[0];
checkProjectActualFiles(p, [app.path]);
installer.installAll([TI.NpmViewRequest], [TI.NpmInstallRequest]);
checkNumberOfProjects(projectService, { configuredProjects: 1 });
checkProjectActualFiles(p, [app.path, jqueryDTS.path]);
});
it("configured projects discover from bower.josn", () => {
const app = {
path: "/app.js",
content: ""
};
const jsconfig = {
path: "/jsconfig.json",
content: JSON.stringify({})
};
const bowerJson = {
path: "/bower.json",
content: JSON.stringify({
"dependencies": {
"jquery": "^3.1.0"
}
})
};
const jqueryDTS = {
path: "/tmp/node_modules/@types/jquery/index.d.ts",
content: ""
};
const host = createServerHost([app, jsconfig, bowerJson]);
const installer = new (class extends Installer {
constructor() {
super(host, { globalTypingsCacheLocation: "/tmp" });
}
runCommand(requestKind: TI.RequestKind, requestId: number, command: string, cwd: string, cb: server.typingsInstaller.RequestCompletedAction) {
const installedTypings = ["@types/jquery"];
const typingFiles = [jqueryDTS];
executeCommand(this, host, installedTypings, typingFiles, requestKind, cb);
}
})();
const projectService = createProjectService(host, { useSingleInferredProject: true, typingsInstaller: installer });
projectService.openClientFile(app.path);
checkNumberOfProjects(projectService, { configuredProjects: 1 });
const p = projectService.configuredProjects[0];
checkProjectActualFiles(p, [app.path]);
installer.installAll([TI.NpmViewRequest], [TI.NpmInstallRequest]);
checkNumberOfProjects(projectService, { configuredProjects: 1 });
checkProjectActualFiles(p, [app.path, jqueryDTS.path]);
});
});
}
+54 -23
View File
@@ -10,13 +10,13 @@ namespace Utils {
this.name = name;
}
isDirectory() { return false; }
isFile() { return false; }
isFileSystem() { return false; }
isDirectory(): this is VirtualDirectory { return false; }
isFile(): this is VirtualFile { return false; }
isFileSystem(): this is VirtualFileSystem { return false; }
}
export class VirtualFile extends VirtualFileSystemEntry {
content: string;
content?: Harness.LanguageService.ScriptInfo;
isFile() { return true; }
}
@@ -73,7 +73,7 @@ namespace Utils {
}
}
addFile(name: string, content?: string): VirtualFile {
addFile(name: string, content?: Harness.LanguageService.ScriptInfo): VirtualFile {
const entry = this.getFileSystemEntry(name);
if (entry === undefined) {
const file = new VirtualFile(this.fileSystem, name);
@@ -82,9 +82,8 @@ namespace Utils {
return file;
}
else if (entry.isFile()) {
const file = <VirtualFile>entry;
file.content = content;
return file;
entry.content = content;
return entry;
}
else {
return undefined;
@@ -111,6 +110,7 @@ namespace Utils {
getFileSystemEntries() { return this.root.getFileSystemEntries(); }
addDirectory(path: string) {
path = ts.normalizePath(path);
const components = ts.getNormalizedPathComponents(path, this.currentDirectory);
let directory: VirtualDirectory = this.root;
for (const component of components) {
@@ -123,8 +123,8 @@ namespace Utils {
return directory;
}
addFile(path: string, content?: string) {
const absolutePath = ts.getNormalizedAbsolutePath(path, this.currentDirectory);
addFile(path: string, content?: Harness.LanguageService.ScriptInfo) {
const absolutePath = ts.normalizePath(ts.getNormalizedAbsolutePath(path, this.currentDirectory));
const fileName = ts.getBaseFileName(path);
const directoryPath = ts.getDirectoryPath(absolutePath);
const directory = this.addDirectory(directoryPath);
@@ -141,6 +141,7 @@ namespace Utils {
}
traversePath(path: string) {
path = ts.normalizePath(path);
let directory: VirtualDirectory = this.root;
for (const component of ts.getNormalizedPathComponents(path, this.currentDirectory)) {
const entry = directory.getFileSystemEntry(component);
@@ -157,20 +158,12 @@ namespace Utils {
return directory;
}
}
export class MockParseConfigHost extends VirtualFileSystem implements ts.ParseConfigHost {
constructor(currentDirectory: string, ignoreCase: boolean, files: string[]) {
super(currentDirectory, ignoreCase);
for (const file of files) {
this.addFile(file);
}
}
readDirectory(path: string, extensions: string[], excludes: string[], includes: string[]) {
return ts.matchFiles(path, extensions, excludes, includes, this.useCaseSensitiveFileNames, this.currentDirectory, (path: string) => this.getAccessibleFileSystemEntries(path));
}
/**
* Reads the directory at the given path and retrieves a list of file names and a list
* of directory names within it. Suitable for use with ts.matchFiles()
* @param path The path to the directory to be read
*/
getAccessibleFileSystemEntries(path: string) {
const entry = this.traversePath(path);
if (entry && entry.isDirectory()) {
@@ -182,5 +175,43 @@ namespace Utils {
}
return { files: [], directories: [] };
}
getAllFileEntries() {
const fileEntries: VirtualFile[] = [];
getFilesRecursive(this.root, fileEntries);
return fileEntries;
function getFilesRecursive(dir: VirtualDirectory, result: VirtualFile[]) {
const files = dir.getFiles();
const dirs = dir.getDirectories();
for (const file of files) {
result.push(file);
}
for (const subDir of dirs) {
getFilesRecursive(subDir, result);
}
}
}
}
export class MockParseConfigHost extends VirtualFileSystem implements ts.ParseConfigHost {
constructor(currentDirectory: string, ignoreCase: boolean, files: ts.MapLike<string> | string[]) {
super(currentDirectory, ignoreCase);
const fileNames = (files instanceof Array) ? files : ts.getOwnKeys(files);
for (const file of fileNames) {
this.addFile(file, new Harness.LanguageService.ScriptInfo(file, (files as ts.MapLike<string>)[file], /*isRootFile*/false));
}
}
readFile(path: string): string {
const value = this.traversePath(path);
if (value && value.isFile()) {
return value.content.content;
}
}
readDirectory(path: string, extensions: string[], excludes: string[], includes: string[]) {
return ts.matchFiles(path, extensions, excludes, includes, this.useCaseSensitiveFileNames, this.currentDirectory, (path: string) => this.getAccessibleFileSystemEntries(path));
}
}
}
+246 -205
View File
@@ -126,6 +126,7 @@ interface KeyAlgorithm {
}
interface KeyboardEventInit extends EventModifierInit {
code?: string;
key?: string;
location?: number;
repeat?: boolean;
@@ -2288,7 +2289,7 @@ declare var DeviceRotationRate: {
new(): DeviceRotationRate;
}
interface Document extends Node, GlobalEventHandlers, NodeSelector, DocumentEvent {
interface Document extends Node, GlobalEventHandlers, NodeSelector, DocumentEvent, ParentNode {
/**
* Sets or gets the URL for the current document.
*/
@@ -3351,7 +3352,7 @@ declare var Document: {
new(): Document;
}
interface DocumentFragment extends Node, NodeSelector {
interface DocumentFragment extends Node, NodeSelector, ParentNode {
addEventListener(type: string, listener: EventListenerOrEventListenerObject, useCapture?: boolean): void;
}
@@ -3420,7 +3421,7 @@ declare var EXT_texture_filter_anisotropic: {
readonly TEXTURE_MAX_ANISOTROPY_EXT: number;
}
interface Element extends Node, GlobalEventHandlers, ElementTraversal, NodeSelector, ChildNode {
interface Element extends Node, GlobalEventHandlers, ElementTraversal, NodeSelector, ChildNode, ParentNode {
readonly classList: DOMTokenList;
className: string;
readonly clientHeight: number;
@@ -3675,6 +3676,16 @@ interface Element extends Node, GlobalEventHandlers, ElementTraversal, NodeSelec
getElementsByClassName(classNames: string): NodeListOf<Element>;
matches(selector: string): boolean;
closest(selector: string): Element | null;
scrollIntoView(arg?: boolean | ScrollIntoViewOptions): void;
scroll(options?: ScrollToOptions): void;
scroll(x: number, y: number): void;
scrollTo(options?: ScrollToOptions): void;
scrollTo(x: number, y: number): void;
scrollBy(options?: ScrollToOptions): void;
scrollBy(x: number, y: number): void;
insertAdjacentElement(position: string, insertedElement: Element): Element | null;
insertAdjacentHTML(where: string, html: string): void;
insertAdjacentText(where: string, text: string): void;
addEventListener(type: "MSGestureChange", listener: (this: this, ev: MSGestureEvent) => any, useCapture?: boolean): void;
addEventListener(type: "MSGestureDoubleTap", listener: (this: this, ev: MSGestureEvent) => any, useCapture?: boolean): void;
addEventListener(type: "MSGestureEnd", listener: (this: this, ev: MSGestureEvent) => any, useCapture?: boolean): void;
@@ -4446,7 +4457,7 @@ interface HTMLCanvasElement extends HTMLElement {
* @param type The standard MIME type for the image format to return. If you do not specify this parameter, the default value is a PNG format image.
*/
toDataURL(type?: string, ...args: any[]): string;
toBlob(callback: (result: Blob | null) => void, ... arguments: any[]): void;
toBlob(callback: (result: Blob | null) => void, type?: string, ...arguments: any[]): void;
}
declare var HTMLCanvasElement: {
@@ -4621,11 +4632,7 @@ interface HTMLElement extends Element {
click(): void;
dragDrop(): boolean;
focus(): void;
insertAdjacentElement(position: string, insertedElement: Element): Element;
insertAdjacentHTML(where: string, html: string): void;
insertAdjacentText(where: string, text: string): void;
msGetInputContext(): MSInputMethodContext;
scrollIntoView(top?: boolean): void;
setActive(): void;
addEventListener(type: "MSContentZoom", listener: (this: this, ev: UIEvent) => any, useCapture?: boolean): void;
addEventListener(type: "MSGestureChange", listener: (this: this, ev: MSGestureEvent) => any, useCapture?: boolean): void;
@@ -5890,6 +5897,7 @@ interface HTMLLinkElement extends HTMLElement, LinkStyle {
*/
type: string;
import?: Document;
integrity: string;
addEventListener(type: string, listener: EventListenerOrEventListenerObject, useCapture?: boolean): void;
}
@@ -6178,7 +6186,7 @@ interface HTMLMediaElement extends HTMLElement {
*/
canPlayType(type: string): string;
/**
* Fires immediately after the client loads the object.
* Resets the audio or video object and loads a new media resource.
*/
load(): void;
/**
@@ -6751,6 +6759,7 @@ interface HTMLScriptElement extends HTMLElement {
* Sets or retrieves the MIME type for the associated scripting engine.
*/
type: string;
integrity: string;
}
declare var HTMLScriptElement: {
@@ -7527,11 +7536,12 @@ declare var HashChangeEvent: {
interface History {
readonly length: number;
readonly state: any;
back(distance?: any): void;
forward(distance?: any): void;
go(delta?: any): void;
pushState(statedata: any, title?: string, url?: string): void;
replaceState(statedata: any, title?: string, url?: string): void;
scrollRestoration: ScrollRestoration;
back(): void;
forward(): void;
go(delta?: number): void;
pushState(data: any, title: string, url?: string | null): void;
replaceState(data: any, title: string, url?: string | null): void;
}
declare var History: {
@@ -7756,6 +7766,7 @@ interface KeyboardEvent extends UIEvent {
readonly repeat: boolean;
readonly shiftKey: boolean;
readonly which: number;
readonly code: string;
getModifierState(keyArg: string): boolean;
initKeyboardEvent(typeArg: string, canBubbleArg: boolean, cancelableArg: boolean, viewArg: Window, keyArg: string, locationArg: number, modifiersListArg: string, repeat: boolean, locale: string): void;
readonly DOM_KEY_LOCATION_JOYSTICK: number;
@@ -9128,6 +9139,7 @@ interface PerformanceTiming {
readonly responseStart: number;
readonly unloadEventEnd: number;
readonly unloadEventStart: number;
readonly secureConnectionStart: number;
toJSON(): any;
}
@@ -11405,8 +11417,8 @@ declare var StereoPannerNode: {
interface Storage {
readonly length: number;
clear(): void;
getItem(key: string): string;
key(index: number): string;
getItem(key: string): string | null;
key(index: number): string | null;
removeItem(key: string): void;
setItem(key: string, data: string): void;
[key: string]: any;
@@ -12947,7 +12959,7 @@ interface Window extends EventTarget, WindowTimers, WindowSessionStorage, Window
onunload: (this: this, ev: Event) => any;
onvolumechange: (this: this, ev: Event) => any;
onwaiting: (this: this, ev: Event) => any;
readonly opener: Window;
opener: any;
orientation: string | number;
readonly outerHeight: number;
readonly outerWidth: number;
@@ -13002,6 +13014,9 @@ interface Window extends EventTarget, WindowTimers, WindowSessionStorage, Window
webkitConvertPointFromNodeToPage(node: Node, pt: WebKitPoint): WebKitPoint;
webkitConvertPointFromPageToNode(node: Node, pt: WebKitPoint): WebKitPoint;
webkitRequestAnimationFrame(callback: FrameRequestCallback): number;
scroll(options?: ScrollToOptions): void;
scrollTo(options?: ScrollToOptions): void;
scrollBy(options?: ScrollToOptions): void;
addEventListener(type: "MSGestureChange", listener: (this: this, ev: MSGestureEvent) => any, useCapture?: boolean): void;
addEventListener(type: "MSGestureDoubleTap", listener: (this: this, ev: MSGestureEvent) => any, useCapture?: boolean): void;
addEventListener(type: "MSGestureEnd", listener: (this: this, ev: MSGestureEvent) => any, useCapture?: boolean): void;
@@ -13098,7 +13113,6 @@ interface Window extends EventTarget, WindowTimers, WindowSessionStorage, Window
addEventListener(type: "waiting", listener: (this: this, ev: Event) => any, useCapture?: boolean): void;
addEventListener(type: "wheel", listener: (this: this, ev: WheelEvent) => any, useCapture?: boolean): void;
addEventListener(type: string, listener: EventListenerOrEventListenerObject, useCapture?: boolean): void;
[index: number]: Window;
}
declare var Window: {
@@ -13129,7 +13143,7 @@ declare var XMLDocument: {
}
interface XMLHttpRequest extends EventTarget, XMLHttpRequestEventTarget {
onreadystatechange: (this: this, ev: ProgressEvent) => any;
onreadystatechange: (this: this, ev: Event) => any;
readonly readyState: number;
readonly response: any;
readonly responseText: string;
@@ -13156,13 +13170,13 @@ interface XMLHttpRequest extends EventTarget, XMLHttpRequestEventTarget {
readonly LOADING: number;
readonly OPENED: number;
readonly UNSENT: number;
addEventListener(type: "abort", listener: (this: this, ev: Event) => any, useCapture?: boolean): void;
addEventListener(type: "error", listener: (this: this, ev: ErrorEvent) => any, useCapture?: boolean): void;
addEventListener(type: "load", listener: (this: this, ev: Event) => any, useCapture?: boolean): void;
addEventListener(type: "abort", listener: (this: this, ev: ProgressEvent) => any, useCapture?: boolean): void;
addEventListener(type: "error", listener: (this: this, ev: ProgressEvent) => any, useCapture?: boolean): void;
addEventListener(type: "load", listener: (this: this, ev: ProgressEvent) => any, useCapture?: boolean): void;
addEventListener(type: "loadend", listener: (this: this, ev: ProgressEvent) => any, useCapture?: boolean): void;
addEventListener(type: "loadstart", listener: (this: this, ev: Event) => any, useCapture?: boolean): void;
addEventListener(type: "loadstart", listener: (this: this, ev: ProgressEvent) => any, useCapture?: boolean): void;
addEventListener(type: "progress", listener: (this: this, ev: ProgressEvent) => any, useCapture?: boolean): void;
addEventListener(type: "readystatechange", listener: (this: this, ev: ProgressEvent) => any, useCapture?: boolean): void;
addEventListener(type: "readystatechange", listener: (this: this, ev: Event) => any, useCapture?: boolean): void;
addEventListener(type: "timeout", listener: (this: this, ev: ProgressEvent) => any, useCapture?: boolean): void;
addEventListener(type: string, listener: EventListenerOrEventListenerObject, useCapture?: boolean): void;
}
@@ -13503,183 +13517,183 @@ interface NavigatorUserMedia {
}
interface NodeSelector {
querySelector(selectors: "a"): HTMLAnchorElement;
querySelector(selectors: "abbr"): HTMLElement;
querySelector(selectors: "acronym"): HTMLElement;
querySelector(selectors: "address"): HTMLElement;
querySelector(selectors: "applet"): HTMLAppletElement;
querySelector(selectors: "area"): HTMLAreaElement;
querySelector(selectors: "article"): HTMLElement;
querySelector(selectors: "aside"): HTMLElement;
querySelector(selectors: "audio"): HTMLAudioElement;
querySelector(selectors: "b"): HTMLElement;
querySelector(selectors: "base"): HTMLBaseElement;
querySelector(selectors: "basefont"): HTMLBaseFontElement;
querySelector(selectors: "bdo"): HTMLElement;
querySelector(selectors: "big"): HTMLElement;
querySelector(selectors: "blockquote"): HTMLQuoteElement;
querySelector(selectors: "body"): HTMLBodyElement;
querySelector(selectors: "br"): HTMLBRElement;
querySelector(selectors: "button"): HTMLButtonElement;
querySelector(selectors: "canvas"): HTMLCanvasElement;
querySelector(selectors: "caption"): HTMLTableCaptionElement;
querySelector(selectors: "center"): HTMLElement;
querySelector(selectors: "circle"): SVGCircleElement;
querySelector(selectors: "cite"): HTMLElement;
querySelector(selectors: "clippath"): SVGClipPathElement;
querySelector(selectors: "code"): HTMLElement;
querySelector(selectors: "col"): HTMLTableColElement;
querySelector(selectors: "colgroup"): HTMLTableColElement;
querySelector(selectors: "datalist"): HTMLDataListElement;
querySelector(selectors: "dd"): HTMLElement;
querySelector(selectors: "defs"): SVGDefsElement;
querySelector(selectors: "del"): HTMLModElement;
querySelector(selectors: "desc"): SVGDescElement;
querySelector(selectors: "dfn"): HTMLElement;
querySelector(selectors: "dir"): HTMLDirectoryElement;
querySelector(selectors: "div"): HTMLDivElement;
querySelector(selectors: "dl"): HTMLDListElement;
querySelector(selectors: "dt"): HTMLElement;
querySelector(selectors: "ellipse"): SVGEllipseElement;
querySelector(selectors: "em"): HTMLElement;
querySelector(selectors: "embed"): HTMLEmbedElement;
querySelector(selectors: "feblend"): SVGFEBlendElement;
querySelector(selectors: "fecolormatrix"): SVGFEColorMatrixElement;
querySelector(selectors: "fecomponenttransfer"): SVGFEComponentTransferElement;
querySelector(selectors: "fecomposite"): SVGFECompositeElement;
querySelector(selectors: "feconvolvematrix"): SVGFEConvolveMatrixElement;
querySelector(selectors: "fediffuselighting"): SVGFEDiffuseLightingElement;
querySelector(selectors: "fedisplacementmap"): SVGFEDisplacementMapElement;
querySelector(selectors: "fedistantlight"): SVGFEDistantLightElement;
querySelector(selectors: "feflood"): SVGFEFloodElement;
querySelector(selectors: "fefunca"): SVGFEFuncAElement;
querySelector(selectors: "fefuncb"): SVGFEFuncBElement;
querySelector(selectors: "fefuncg"): SVGFEFuncGElement;
querySelector(selectors: "fefuncr"): SVGFEFuncRElement;
querySelector(selectors: "fegaussianblur"): SVGFEGaussianBlurElement;
querySelector(selectors: "feimage"): SVGFEImageElement;
querySelector(selectors: "femerge"): SVGFEMergeElement;
querySelector(selectors: "femergenode"): SVGFEMergeNodeElement;
querySelector(selectors: "femorphology"): SVGFEMorphologyElement;
querySelector(selectors: "feoffset"): SVGFEOffsetElement;
querySelector(selectors: "fepointlight"): SVGFEPointLightElement;
querySelector(selectors: "fespecularlighting"): SVGFESpecularLightingElement;
querySelector(selectors: "fespotlight"): SVGFESpotLightElement;
querySelector(selectors: "fetile"): SVGFETileElement;
querySelector(selectors: "feturbulence"): SVGFETurbulenceElement;
querySelector(selectors: "fieldset"): HTMLFieldSetElement;
querySelector(selectors: "figcaption"): HTMLElement;
querySelector(selectors: "figure"): HTMLElement;
querySelector(selectors: "filter"): SVGFilterElement;
querySelector(selectors: "font"): HTMLFontElement;
querySelector(selectors: "footer"): HTMLElement;
querySelector(selectors: "foreignobject"): SVGForeignObjectElement;
querySelector(selectors: "form"): HTMLFormElement;
querySelector(selectors: "frame"): HTMLFrameElement;
querySelector(selectors: "frameset"): HTMLFrameSetElement;
querySelector(selectors: "g"): SVGGElement;
querySelector(selectors: "h1"): HTMLHeadingElement;
querySelector(selectors: "h2"): HTMLHeadingElement;
querySelector(selectors: "h3"): HTMLHeadingElement;
querySelector(selectors: "h4"): HTMLHeadingElement;
querySelector(selectors: "h5"): HTMLHeadingElement;
querySelector(selectors: "h6"): HTMLHeadingElement;
querySelector(selectors: "head"): HTMLHeadElement;
querySelector(selectors: "header"): HTMLElement;
querySelector(selectors: "hgroup"): HTMLElement;
querySelector(selectors: "hr"): HTMLHRElement;
querySelector(selectors: "html"): HTMLHtmlElement;
querySelector(selectors: "i"): HTMLElement;
querySelector(selectors: "iframe"): HTMLIFrameElement;
querySelector(selectors: "image"): SVGImageElement;
querySelector(selectors: "img"): HTMLImageElement;
querySelector(selectors: "input"): HTMLInputElement;
querySelector(selectors: "ins"): HTMLModElement;
querySelector(selectors: "isindex"): HTMLUnknownElement;
querySelector(selectors: "kbd"): HTMLElement;
querySelector(selectors: "keygen"): HTMLElement;
querySelector(selectors: "label"): HTMLLabelElement;
querySelector(selectors: "legend"): HTMLLegendElement;
querySelector(selectors: "li"): HTMLLIElement;
querySelector(selectors: "line"): SVGLineElement;
querySelector(selectors: "lineargradient"): SVGLinearGradientElement;
querySelector(selectors: "link"): HTMLLinkElement;
querySelector(selectors: "listing"): HTMLPreElement;
querySelector(selectors: "map"): HTMLMapElement;
querySelector(selectors: "mark"): HTMLElement;
querySelector(selectors: "marker"): SVGMarkerElement;
querySelector(selectors: "marquee"): HTMLMarqueeElement;
querySelector(selectors: "mask"): SVGMaskElement;
querySelector(selectors: "menu"): HTMLMenuElement;
querySelector(selectors: "meta"): HTMLMetaElement;
querySelector(selectors: "metadata"): SVGMetadataElement;
querySelector(selectors: "meter"): HTMLMeterElement;
querySelector(selectors: "nav"): HTMLElement;
querySelector(selectors: "nextid"): HTMLUnknownElement;
querySelector(selectors: "nobr"): HTMLElement;
querySelector(selectors: "noframes"): HTMLElement;
querySelector(selectors: "noscript"): HTMLElement;
querySelector(selectors: "object"): HTMLObjectElement;
querySelector(selectors: "ol"): HTMLOListElement;
querySelector(selectors: "optgroup"): HTMLOptGroupElement;
querySelector(selectors: "option"): HTMLOptionElement;
querySelector(selectors: "p"): HTMLParagraphElement;
querySelector(selectors: "param"): HTMLParamElement;
querySelector(selectors: "path"): SVGPathElement;
querySelector(selectors: "pattern"): SVGPatternElement;
querySelector(selectors: "picture"): HTMLPictureElement;
querySelector(selectors: "plaintext"): HTMLElement;
querySelector(selectors: "polygon"): SVGPolygonElement;
querySelector(selectors: "polyline"): SVGPolylineElement;
querySelector(selectors: "pre"): HTMLPreElement;
querySelector(selectors: "progress"): HTMLProgressElement;
querySelector(selectors: "q"): HTMLQuoteElement;
querySelector(selectors: "radialgradient"): SVGRadialGradientElement;
querySelector(selectors: "rect"): SVGRectElement;
querySelector(selectors: "rt"): HTMLElement;
querySelector(selectors: "ruby"): HTMLElement;
querySelector(selectors: "s"): HTMLElement;
querySelector(selectors: "samp"): HTMLElement;
querySelector(selectors: "script"): HTMLScriptElement;
querySelector(selectors: "section"): HTMLElement;
querySelector(selectors: "select"): HTMLSelectElement;
querySelector(selectors: "small"): HTMLElement;
querySelector(selectors: "source"): HTMLSourceElement;
querySelector(selectors: "span"): HTMLSpanElement;
querySelector(selectors: "stop"): SVGStopElement;
querySelector(selectors: "strike"): HTMLElement;
querySelector(selectors: "strong"): HTMLElement;
querySelector(selectors: "style"): HTMLStyleElement;
querySelector(selectors: "sub"): HTMLElement;
querySelector(selectors: "sup"): HTMLElement;
querySelector(selectors: "svg"): SVGSVGElement;
querySelector(selectors: "switch"): SVGSwitchElement;
querySelector(selectors: "symbol"): SVGSymbolElement;
querySelector(selectors: "table"): HTMLTableElement;
querySelector(selectors: "tbody"): HTMLTableSectionElement;
querySelector(selectors: "td"): HTMLTableDataCellElement;
querySelector(selectors: "template"): HTMLTemplateElement;
querySelector(selectors: "text"): SVGTextElement;
querySelector(selectors: "textpath"): SVGTextPathElement;
querySelector(selectors: "textarea"): HTMLTextAreaElement;
querySelector(selectors: "tfoot"): HTMLTableSectionElement;
querySelector(selectors: "th"): HTMLTableHeaderCellElement;
querySelector(selectors: "thead"): HTMLTableSectionElement;
querySelector(selectors: "title"): HTMLTitleElement;
querySelector(selectors: "tr"): HTMLTableRowElement;
querySelector(selectors: "track"): HTMLTrackElement;
querySelector(selectors: "tspan"): SVGTSpanElement;
querySelector(selectors: "tt"): HTMLElement;
querySelector(selectors: "u"): HTMLElement;
querySelector(selectors: "ul"): HTMLUListElement;
querySelector(selectors: "use"): SVGUseElement;
querySelector(selectors: "var"): HTMLElement;
querySelector(selectors: "video"): HTMLVideoElement;
querySelector(selectors: "view"): SVGViewElement;
querySelector(selectors: "wbr"): HTMLElement;
querySelector(selectors: "x-ms-webview"): MSHTMLWebViewElement;
querySelector(selectors: "xmp"): HTMLPreElement;
querySelector(selectors: string): Element;
querySelector(selectors: "a"): HTMLAnchorElement | null;
querySelector(selectors: "abbr"): HTMLElement | null;
querySelector(selectors: "acronym"): HTMLElement | null;
querySelector(selectors: "address"): HTMLElement | null;
querySelector(selectors: "applet"): HTMLAppletElement | null;
querySelector(selectors: "area"): HTMLAreaElement | null;
querySelector(selectors: "article"): HTMLElement | null;
querySelector(selectors: "aside"): HTMLElement | null;
querySelector(selectors: "audio"): HTMLAudioElement | null;
querySelector(selectors: "b"): HTMLElement | null;
querySelector(selectors: "base"): HTMLBaseElement | null;
querySelector(selectors: "basefont"): HTMLBaseFontElement | null;
querySelector(selectors: "bdo"): HTMLElement | null;
querySelector(selectors: "big"): HTMLElement | null;
querySelector(selectors: "blockquote"): HTMLQuoteElement | null;
querySelector(selectors: "body"): HTMLBodyElement | null;
querySelector(selectors: "br"): HTMLBRElement | null;
querySelector(selectors: "button"): HTMLButtonElement | null;
querySelector(selectors: "canvas"): HTMLCanvasElement | null;
querySelector(selectors: "caption"): HTMLTableCaptionElement | null;
querySelector(selectors: "center"): HTMLElement | null;
querySelector(selectors: "circle"): SVGCircleElement | null;
querySelector(selectors: "cite"): HTMLElement | null;
querySelector(selectors: "clippath"): SVGClipPathElement | null;
querySelector(selectors: "code"): HTMLElement | null;
querySelector(selectors: "col"): HTMLTableColElement | null;
querySelector(selectors: "colgroup"): HTMLTableColElement | null;
querySelector(selectors: "datalist"): HTMLDataListElement | null;
querySelector(selectors: "dd"): HTMLElement | null;
querySelector(selectors: "defs"): SVGDefsElement | null;
querySelector(selectors: "del"): HTMLModElement | null;
querySelector(selectors: "desc"): SVGDescElement | null;
querySelector(selectors: "dfn"): HTMLElement | null;
querySelector(selectors: "dir"): HTMLDirectoryElement | null;
querySelector(selectors: "div"): HTMLDivElement | null;
querySelector(selectors: "dl"): HTMLDListElement | null;
querySelector(selectors: "dt"): HTMLElement | null;
querySelector(selectors: "ellipse"): SVGEllipseElement | null;
querySelector(selectors: "em"): HTMLElement | null;
querySelector(selectors: "embed"): HTMLEmbedElement | null;
querySelector(selectors: "feblend"): SVGFEBlendElement | null;
querySelector(selectors: "fecolormatrix"): SVGFEColorMatrixElement | null;
querySelector(selectors: "fecomponenttransfer"): SVGFEComponentTransferElement | null;
querySelector(selectors: "fecomposite"): SVGFECompositeElement | null;
querySelector(selectors: "feconvolvematrix"): SVGFEConvolveMatrixElement | null;
querySelector(selectors: "fediffuselighting"): SVGFEDiffuseLightingElement | null;
querySelector(selectors: "fedisplacementmap"): SVGFEDisplacementMapElement | null;
querySelector(selectors: "fedistantlight"): SVGFEDistantLightElement | null;
querySelector(selectors: "feflood"): SVGFEFloodElement | null;
querySelector(selectors: "fefunca"): SVGFEFuncAElement | null;
querySelector(selectors: "fefuncb"): SVGFEFuncBElement | null;
querySelector(selectors: "fefuncg"): SVGFEFuncGElement | null;
querySelector(selectors: "fefuncr"): SVGFEFuncRElement | null;
querySelector(selectors: "fegaussianblur"): SVGFEGaussianBlurElement | null;
querySelector(selectors: "feimage"): SVGFEImageElement | null;
querySelector(selectors: "femerge"): SVGFEMergeElement | null;
querySelector(selectors: "femergenode"): SVGFEMergeNodeElement | null;
querySelector(selectors: "femorphology"): SVGFEMorphologyElement | null;
querySelector(selectors: "feoffset"): SVGFEOffsetElement | null;
querySelector(selectors: "fepointlight"): SVGFEPointLightElement | null;
querySelector(selectors: "fespecularlighting"): SVGFESpecularLightingElement | null;
querySelector(selectors: "fespotlight"): SVGFESpotLightElement | null;
querySelector(selectors: "fetile"): SVGFETileElement | null;
querySelector(selectors: "feturbulence"): SVGFETurbulenceElement | null;
querySelector(selectors: "fieldset"): HTMLFieldSetElement | null;
querySelector(selectors: "figcaption"): HTMLElement | null;
querySelector(selectors: "figure"): HTMLElement | null;
querySelector(selectors: "filter"): SVGFilterElement | null;
querySelector(selectors: "font"): HTMLFontElement | null;
querySelector(selectors: "footer"): HTMLElement | null;
querySelector(selectors: "foreignobject"): SVGForeignObjectElement | null;
querySelector(selectors: "form"): HTMLFormElement | null;
querySelector(selectors: "frame"): HTMLFrameElement | null;
querySelector(selectors: "frameset"): HTMLFrameSetElement | null;
querySelector(selectors: "g"): SVGGElement | null;
querySelector(selectors: "h1"): HTMLHeadingElement | null;
querySelector(selectors: "h2"): HTMLHeadingElement | null;
querySelector(selectors: "h3"): HTMLHeadingElement | null;
querySelector(selectors: "h4"): HTMLHeadingElement | null;
querySelector(selectors: "h5"): HTMLHeadingElement | null;
querySelector(selectors: "h6"): HTMLHeadingElement | null;
querySelector(selectors: "head"): HTMLHeadElement | null;
querySelector(selectors: "header"): HTMLElement | null;
querySelector(selectors: "hgroup"): HTMLElement | null;
querySelector(selectors: "hr"): HTMLHRElement | null;
querySelector(selectors: "html"): HTMLHtmlElement | null;
querySelector(selectors: "i"): HTMLElement | null;
querySelector(selectors: "iframe"): HTMLIFrameElement | null;
querySelector(selectors: "image"): SVGImageElement | null;
querySelector(selectors: "img"): HTMLImageElement | null;
querySelector(selectors: "input"): HTMLInputElement | null;
querySelector(selectors: "ins"): HTMLModElement | null;
querySelector(selectors: "isindex"): HTMLUnknownElement | null;
querySelector(selectors: "kbd"): HTMLElement | null;
querySelector(selectors: "keygen"): HTMLElement | null;
querySelector(selectors: "label"): HTMLLabelElement | null;
querySelector(selectors: "legend"): HTMLLegendElement | null;
querySelector(selectors: "li"): HTMLLIElement | null;
querySelector(selectors: "line"): SVGLineElement | null;
querySelector(selectors: "lineargradient"): SVGLinearGradientElement | null;
querySelector(selectors: "link"): HTMLLinkElement | null;
querySelector(selectors: "listing"): HTMLPreElement | null;
querySelector(selectors: "map"): HTMLMapElement | null;
querySelector(selectors: "mark"): HTMLElement | null;
querySelector(selectors: "marker"): SVGMarkerElement | null;
querySelector(selectors: "marquee"): HTMLMarqueeElement | null;
querySelector(selectors: "mask"): SVGMaskElement | null;
querySelector(selectors: "menu"): HTMLMenuElement | null;
querySelector(selectors: "meta"): HTMLMetaElement | null;
querySelector(selectors: "metadata"): SVGMetadataElement | null;
querySelector(selectors: "meter"): HTMLMeterElement | null;
querySelector(selectors: "nav"): HTMLElement | null;
querySelector(selectors: "nextid"): HTMLUnknownElement | null;
querySelector(selectors: "nobr"): HTMLElement | null;
querySelector(selectors: "noframes"): HTMLElement | null;
querySelector(selectors: "noscript"): HTMLElement | null;
querySelector(selectors: "object"): HTMLObjectElement | null;
querySelector(selectors: "ol"): HTMLOListElement | null;
querySelector(selectors: "optgroup"): HTMLOptGroupElement | null;
querySelector(selectors: "option"): HTMLOptionElement | null;
querySelector(selectors: "p"): HTMLParagraphElement | null;
querySelector(selectors: "param"): HTMLParamElement | null;
querySelector(selectors: "path"): SVGPathElement | null;
querySelector(selectors: "pattern"): SVGPatternElement | null;
querySelector(selectors: "picture"): HTMLPictureElement | null;
querySelector(selectors: "plaintext"): HTMLElement | null;
querySelector(selectors: "polygon"): SVGPolygonElement | null;
querySelector(selectors: "polyline"): SVGPolylineElement | null;
querySelector(selectors: "pre"): HTMLPreElement | null;
querySelector(selectors: "progress"): HTMLProgressElement | null;
querySelector(selectors: "q"): HTMLQuoteElement | null;
querySelector(selectors: "radialgradient"): SVGRadialGradientElement | null;
querySelector(selectors: "rect"): SVGRectElement | null;
querySelector(selectors: "rt"): HTMLElement | null;
querySelector(selectors: "ruby"): HTMLElement | null;
querySelector(selectors: "s"): HTMLElement | null;
querySelector(selectors: "samp"): HTMLElement | null;
querySelector(selectors: "script"): HTMLScriptElement | null;
querySelector(selectors: "section"): HTMLElement | null;
querySelector(selectors: "select"): HTMLSelectElement | null;
querySelector(selectors: "small"): HTMLElement | null;
querySelector(selectors: "source"): HTMLSourceElement | null;
querySelector(selectors: "span"): HTMLSpanElement | null;
querySelector(selectors: "stop"): SVGStopElement | null;
querySelector(selectors: "strike"): HTMLElement | null;
querySelector(selectors: "strong"): HTMLElement | null;
querySelector(selectors: "style"): HTMLStyleElement | null;
querySelector(selectors: "sub"): HTMLElement | null;
querySelector(selectors: "sup"): HTMLElement | null;
querySelector(selectors: "svg"): SVGSVGElement | null;
querySelector(selectors: "switch"): SVGSwitchElement | null;
querySelector(selectors: "symbol"): SVGSymbolElement | null;
querySelector(selectors: "table"): HTMLTableElement | null;
querySelector(selectors: "tbody"): HTMLTableSectionElement | null;
querySelector(selectors: "td"): HTMLTableDataCellElement | null;
querySelector(selectors: "template"): HTMLTemplateElement | null;
querySelector(selectors: "text"): SVGTextElement | null;
querySelector(selectors: "textpath"): SVGTextPathElement | null;
querySelector(selectors: "textarea"): HTMLTextAreaElement | null;
querySelector(selectors: "tfoot"): HTMLTableSectionElement | null;
querySelector(selectors: "th"): HTMLTableHeaderCellElement | null;
querySelector(selectors: "thead"): HTMLTableSectionElement | null;
querySelector(selectors: "title"): HTMLTitleElement | null;
querySelector(selectors: "tr"): HTMLTableRowElement | null;
querySelector(selectors: "track"): HTMLTrackElement | null;
querySelector(selectors: "tspan"): SVGTSpanElement | null;
querySelector(selectors: "tt"): HTMLElement | null;
querySelector(selectors: "u"): HTMLElement | null;
querySelector(selectors: "ul"): HTMLUListElement | null;
querySelector(selectors: "use"): SVGUseElement | null;
querySelector(selectors: "var"): HTMLElement | null;
querySelector(selectors: "video"): HTMLVideoElement | null;
querySelector(selectors: "view"): SVGViewElement | null;
querySelector(selectors: "wbr"): HTMLElement | null;
querySelector(selectors: "x-ms-webview"): MSHTMLWebViewElement | null;
querySelector(selectors: "xmp"): HTMLPreElement | null;
querySelector(selectors: string): Element | null;
querySelectorAll(selectors: "a"): NodeListOf<HTMLAnchorElement>;
querySelectorAll(selectors: "abbr"): NodeListOf<HTMLElement>;
querySelectorAll(selectors: "acronym"): NodeListOf<HTMLElement>;
@@ -14029,6 +14043,20 @@ interface ProgressEventInit extends EventInit {
total?: number;
}
interface ScrollOptions {
behavior?: ScrollBehavior;
}
interface ScrollToOptions extends ScrollOptions {
left?: number;
top?: number;
}
interface ScrollIntoViewOptions extends ScrollOptions {
block?: ScrollLogicalPosition;
inline?: ScrollLogicalPosition;
}
interface ClipboardEventInit extends EventInit {
data?: string;
dataType?: string;
@@ -14072,7 +14100,7 @@ interface EcdsaParams extends Algorithm {
}
interface EcKeyGenParams extends Algorithm {
typedCurve: string;
namedCurve: string;
}
interface EcKeyAlgorithm extends KeyAlgorithm {
@@ -14208,6 +14236,13 @@ interface JsonWebKey {
k?: string;
}
interface ParentNode {
readonly children: HTMLCollection;
readonly firstElementChild: Element;
readonly lastElementChild: Element;
readonly childElementCount: number;
}
declare type EventListenerOrEventListenerObject = EventListener | EventListenerObject;
interface ErrorEventHandler {
@@ -14278,7 +14313,7 @@ declare var location: Location;
declare var locationbar: BarProp;
declare var menubar: BarProp;
declare var msCredentials: MSCredentials;
declare var name: string;
declare const name: never;
declare var navigator: Navigator;
declare var offscreenBuffering: string | boolean;
declare var onabort: (this: Window, ev: UIEvent) => any;
@@ -14372,7 +14407,7 @@ declare var ontouchstart: (ev: TouchEvent) => any;
declare var onunload: (this: Window, ev: Event) => any;
declare var onvolumechange: (this: Window, ev: Event) => any;
declare var onwaiting: (this: Window, ev: Event) => any;
declare var opener: Window;
declare var opener: any;
declare var orientation: string | number;
declare var outerHeight: number;
declare var outerWidth: number;
@@ -14425,6 +14460,9 @@ declare function webkitCancelAnimationFrame(handle: number): void;
declare function webkitConvertPointFromNodeToPage(node: Node, pt: WebKitPoint): WebKitPoint;
declare function webkitConvertPointFromPageToNode(node: Node, pt: WebKitPoint): WebKitPoint;
declare function webkitRequestAnimationFrame(callback: FrameRequestCallback): number;
declare function scroll(options?: ScrollToOptions): void;
declare function scrollTo(options?: ScrollToOptions): void;
declare function scrollBy(options?: ScrollToOptions): void;
declare function toString(): string;
declare function addEventListener(type: string, listener?: EventListenerOrEventListenerObject, useCapture?: boolean): void;
declare function dispatchEvent(evt: Event): boolean;
@@ -14580,6 +14618,9 @@ type MSOutboundPayload = MSVideoSendPayload | MSAudioSendPayload;
type RTCIceGatherCandidate = RTCIceCandidate | RTCIceCandidateComplete;
type RTCTransport = RTCDtlsTransport | RTCSrtpSdesTransport;
type payloadtype = number;
type ScrollBehavior = "auto" | "instant" | "smooth";
type ScrollLogicalPosition = "start" | "center" | "end" | "nearest";
type IDBValidKey = number | string | Date | IDBArrayKey;
type BufferSource = ArrayBuffer | ArrayBufferView;
type MouseWheelEvent = WheelEvent;
type MouseWheelEvent = WheelEvent;
type ScrollRestoration = "auto" | "manual";
+1 -1
View File
@@ -1,4 +1,4 @@
/// <reference path="lib.dom.generated.d.ts" />
/// <reference path="lib.dom.d.ts" />
interface DOMTokenList {
[Symbol.iterator](): IterableIterator<string>;

Some files were not shown because too many files have changed in this diff Show More