Merge branch 'master' into refactor-jsdoc-types-to-typescript

This commit is contained in:
Nathan Shively-Sanders
2017-10-12 14:18:28 -07:00
89 changed files with 1922 additions and 83 deletions
+28 -28
View File
@@ -15675,34 +15675,32 @@ namespace ts {
return getInferredTypes(context);
}
function checkTypeArguments(signature: Signature, typeArgumentNodes: ReadonlyArray<TypeNode>, typeArgumentTypes: Type[], reportErrors: boolean, headMessage?: DiagnosticMessage): boolean {
function checkTypeArguments(signature: Signature, typeArgumentNodes: ReadonlyArray<TypeNode>, reportErrors: boolean, headMessage?: DiagnosticMessage): Type[] | false {
const isJavascript = isInJavaScriptFile(signature.declaration);
const typeParameters = signature.typeParameters;
let typeArgumentsAreAssignable = true;
const typeArgumentTypes = fillMissingTypeArguments(map(typeArgumentNodes, getTypeFromTypeNode), typeParameters, getMinTypeArgumentCount(typeParameters), isJavascript);
let mapper: TypeMapper;
for (let i = 0; i < typeArgumentNodes.length; i++) {
if (typeArgumentsAreAssignable /* so far */) {
const constraint = getConstraintOfTypeParameter(typeParameters[i]);
if (constraint) {
let errorInfo: DiagnosticMessageChain;
let typeArgumentHeadMessage = Diagnostics.Type_0_does_not_satisfy_the_constraint_1;
if (reportErrors && headMessage) {
errorInfo = chainDiagnosticMessages(errorInfo, typeArgumentHeadMessage);
typeArgumentHeadMessage = headMessage;
}
if (!mapper) {
mapper = createTypeMapper(typeParameters, typeArgumentTypes);
}
const typeArgument = typeArgumentTypes[i];
typeArgumentsAreAssignable = checkTypeAssignableTo(
typeArgument,
getTypeWithThisArgument(instantiateType(constraint, mapper), typeArgument),
reportErrors ? typeArgumentNodes[i] : undefined,
typeArgumentHeadMessage,
errorInfo);
}
Debug.assert(typeParameters[i] !== undefined, "Should not call checkTypeArguments with too many type arguments");
const constraint = getConstraintOfTypeParameter(typeParameters[i]);
if (!constraint) continue;
const errorInfo = reportErrors && headMessage && chainDiagnosticMessages(/*details*/ undefined, Diagnostics.Type_0_does_not_satisfy_the_constraint_1);
const typeArgumentHeadMessage = headMessage || Diagnostics.Type_0_does_not_satisfy_the_constraint_1;
if (!mapper) {
mapper = createTypeMapper(typeParameters, typeArgumentTypes);
}
const typeArgument = typeArgumentTypes[i];
if (!checkTypeAssignableTo(
typeArgument,
getTypeWithThisArgument(instantiateType(constraint, mapper), typeArgument),
reportErrors ? typeArgumentNodes[i] : undefined,
typeArgumentHeadMessage,
errorInfo)) {
return false;
}
}
return typeArgumentsAreAssignable;
return typeArgumentTypes;
}
/**
@@ -16235,8 +16233,7 @@ namespace ts {
checkApplicableSignature(node, args, candidateForArgumentError, assignableRelation, /*excludeArgument*/ undefined, /*reportErrors*/ true);
}
else if (candidateForTypeArgumentError) {
const typeArguments = (<CallExpression>node).typeArguments;
checkTypeArguments(candidateForTypeArgumentError, typeArguments, map(typeArguments, getTypeFromTypeNode), /*reportErrors*/ true, fallbackError);
checkTypeArguments(candidateForTypeArgumentError, (node as CallExpression).typeArguments, /*reportErrors*/ true, fallbackError);
}
else if (typeArguments && every(signatures, sig => length(sig.typeParameters) !== typeArguments.length)) {
let min = Number.POSITIVE_INFINITY;
@@ -16335,10 +16332,12 @@ namespace ts {
candidate = originalCandidate;
if (candidate.typeParameters) {
let typeArgumentTypes: Type[];
const isJavascript = isInJavaScriptFile(candidate.declaration);
if (typeArguments) {
typeArgumentTypes = fillMissingTypeArguments(map(typeArguments, getTypeFromTypeNode), candidate.typeParameters, getMinTypeArgumentCount(candidate.typeParameters), isJavascript);
if (!checkTypeArguments(candidate, typeArguments, typeArgumentTypes, /*reportErrors*/ false)) {
const typeArgumentResult = checkTypeArguments(candidate, typeArguments, /*reportErrors*/ false);
if (typeArgumentResult) {
typeArgumentTypes = typeArgumentResult;
}
else {
candidateForTypeArgumentError = originalCandidate;
break;
}
@@ -16346,6 +16345,7 @@ namespace ts {
else {
typeArgumentTypes = inferTypeArguments(node, candidate, args, excludeArgument, inferenceContext);
}
const isJavascript = isInJavaScriptFile(candidate.declaration);
candidate = getSignatureInstantiation(candidate, typeArgumentTypes, isJavascript);
}
if (!checkApplicableSignature(node, args, candidate, relation, excludeArgument, /*reportErrors*/ false)) {
+10
View File
@@ -2612,6 +2612,16 @@ namespace ts {
return node;
}
/**
* Sets flags that control emit behavior of a node.
*/
/* @internal */
export function addEmitFlags<T extends Node>(node: T, emitFlags: EmitFlags) {
const emitNode = getOrCreateEmitNode(node);
emitNode.flags = emitNode.flags | emitFlags;
return node;
}
/**
* Gets a custom text range to use when emitting source maps.
*/
+1 -2
View File
@@ -1112,7 +1112,7 @@ namespace ts {
}
function visitCallExpression(node: CallExpression) {
if (forEach(node.arguments, containsYield)) {
if (!isImportCall(node) && forEach(node.arguments, containsYield)) {
// [source]
// a.b(1, yield, 2);
//
@@ -1123,7 +1123,6 @@ namespace ts {
// .yield resumeLabel
// .mark resumeLabel
// _b.apply(_a, _c.concat([%sent%, 2]));
const { target, thisArg } = createCallBinding(node.expression, hoistVariableDeclaration, languageVersion, /*cacheIdentifiers*/ true);
return setOriginalNode(
createFunctionApply(
+30 -16
View File
@@ -21,7 +21,8 @@ namespace ts {
const {
startLexicalEnvironment,
endLexicalEnvironment
endLexicalEnvironment,
hoistVariableDeclaration
} = context;
const compilerOptions = context.getCompilerOptions();
@@ -519,18 +520,20 @@ namespace ts {
}
function visitImportCallExpression(node: ImportCall): Expression {
const argument = visitNode(firstOrUndefined(node.arguments), importCallExpressionVisitor);
const containsLexicalThis = !!(node.transformFlags & TransformFlags.ContainsLexicalThis);
switch (compilerOptions.module) {
case ModuleKind.AMD:
return transformImportCallExpressionAMD(node);
return createImportCallExpressionAMD(argument, containsLexicalThis);
case ModuleKind.UMD:
return transformImportCallExpressionUMD(node);
return createImportCallExpressionUMD(argument, containsLexicalThis);
case ModuleKind.CommonJS:
default:
return transformImportCallExpressionCommonJS(node);
return createImportCallExpressionCommonJS(argument, containsLexicalThis);
}
}
function transformImportCallExpressionUMD(node: ImportCall): Expression {
function createImportCallExpressionUMD(arg: Expression | undefined, containsLexicalThis: boolean): Expression {
// (function (factory) {
// ... (regular UMD)
// }
@@ -545,14 +548,25 @@ namespace ts {
// : new Promise(function (_a, _b) { require([x], _a, _b); }); /*Amd Require*/
// });
needUMDDynamicImportHelper = true;
return createConditional(
/*condition*/ createIdentifier("__syncRequire"),
/*whenTrue*/ transformImportCallExpressionCommonJS(node),
/*whenFalse*/ transformImportCallExpressionAMD(node)
);
if (isSimpleCopiableExpression(arg)) {
const argClone = isGeneratedIdentifier(arg) ? arg : isStringLiteral(arg) ? createLiteral(arg) : setEmitFlags(setTextRange(getSynthesizedClone(arg), arg), EmitFlags.NoComments);
return createConditional(
/*condition*/ createIdentifier("__syncRequire"),
/*whenTrue*/ createImportCallExpressionCommonJS(arg, containsLexicalThis),
/*whenFalse*/ createImportCallExpressionAMD(argClone, containsLexicalThis)
);
}
else {
const temp = createTempVariable(hoistVariableDeclaration);
return createComma(createAssignment(temp, arg), createConditional(
/*condition*/ createIdentifier("__syncRequire"),
/*whenTrue*/ createImportCallExpressionCommonJS(temp, containsLexicalThis),
/*whenFalse*/ createImportCallExpressionAMD(temp, containsLexicalThis)
));
}
}
function transformImportCallExpressionAMD(node: ImportCall): Expression {
function createImportCallExpressionAMD(arg: Expression | undefined, containsLexicalThis: boolean): Expression {
// improt("./blah")
// emit as
// define(["require", "exports", "blah"], function (require, exports) {
@@ -570,7 +584,7 @@ namespace ts {
createCall(
createIdentifier("require"),
/*typeArguments*/ undefined,
[createArrayLiteral([firstOrUndefined(node.arguments) || createOmittedExpression()]), resolve, reject]
[createArrayLiteral([arg || createOmittedExpression()]), resolve, reject]
)
)
]);
@@ -598,7 +612,7 @@ namespace ts {
// if there is a lexical 'this' in the import call arguments, ensure we indicate
// that this new function expression indicates it captures 'this' so that the
// es2015 transformer will properly substitute 'this' with '_this'.
if (node.transformFlags & TransformFlags.ContainsLexicalThis) {
if (containsLexicalThis) {
setEmitFlags(func, EmitFlags.CapturesThis);
}
}
@@ -606,14 +620,14 @@ namespace ts {
return createNew(createIdentifier("Promise"), /*typeArguments*/ undefined, [func]);
}
function transformImportCallExpressionCommonJS(node: ImportCall): Expression {
function createImportCallExpressionCommonJS(arg: Expression | undefined, containsLexicalThis: boolean): Expression {
// import("./blah")
// emit as
// Promise.resolve().then(function () { return require(x); }) /*CommonJs Require*/
// We have to wrap require in then callback so that require is done in asynchronously
// if we simply do require in resolve callback in Promise constructor. We will execute the loading immediately
const promiseResolveCall = createCall(createPropertyAccess(createIdentifier("Promise"), "resolve"), /*typeArguments*/ undefined, /*argumentsArray*/ []);
const requireCall = createCall(createIdentifier("require"), /*typeArguments*/ undefined, node.arguments);
const requireCall = createCall(createIdentifier("require"), /*typeArguments*/ undefined, arg ? [arg] : []);
let func: FunctionExpression | ArrowFunction;
if (languageVersion >= ScriptTarget.ES2015) {
@@ -638,7 +652,7 @@ namespace ts {
// if there is a lexical 'this' in the import call arguments, ensure we indicate
// that this new function expression indicates it captures 'this' so that the
// es2015 transformer will properly substitute 'this' with '_this'.
if (node.transformFlags & TransformFlags.ContainsLexicalThis) {
if (containsLexicalThis) {
setEmitFlags(func, EmitFlags.CapturesThis);
}
}
+1 -1
View File
@@ -1495,7 +1495,7 @@ namespace ts {
createIdentifier("import")
),
/*typeArguments*/ undefined,
node.arguments
some(node.arguments) ? [visitNode(node.arguments[0], destructuringAndImportCallVisitor)] : []
);
}
+13
View File
@@ -178,4 +178,17 @@ namespace ts {
}
return values;
}
/**
* Used in the module transformer to check if an expression is reasonably without sideeffect,
* and thus better to copy into multiple places rather than to cache in a temporary variable
* - this is mostly subjective beyond the requirement that the expression not be sideeffecting
*/
export function isSimpleCopiableExpression(expression: Expression) {
return expression.kind === SyntaxKind.StringLiteral ||
expression.kind === SyntaxKind.NumericLiteral ||
expression.kind === SyntaxKind.NoSubstitutionTemplateLiteral ||
isKeyword(expression.kind) ||
isIdentifier(expression);
}
}
+32
View File
@@ -223,6 +223,14 @@ const f = () => {
testExtractConstant("extractConstant_ArrowFunction_Expression",
`const f = () => [#|2 + 1|];`);
testExtractConstant("extractConstant_PreserveTrivia", `
// a
var q = /*b*/ //c
/*d*/ [#|1 /*e*/ //f
/*g*/ + /*h*/ //i
/*j*/ 2|] /*k*/ //l
/*m*/; /*n*/ //o`);
testExtractConstantFailed("extractConstant_Void", `
function f(): void { }
[#|f();|]`);
@@ -230,6 +238,30 @@ function f(): void { }
testExtractConstantFailed("extractConstant_Never", `
function f(): never { }
[#|f();|]`);
testExtractConstant("extractConstant_This_Constructor", `
class C {
constructor() {
[#|this.m2()|];
}
m2() { return 1; }
}`);
testExtractConstant("extractConstant_This_Method", `
class C {
m1() {
[#|this.m2()|];
}
m2() { return 1; }
}`);
testExtractConstant("extractConstant_This_Property", `
namespace N { // Force this test to be TS-only
class C {
x = 1;
y = [#|this.x|];
}
}`);
});
function testExtractConstant(caption: string, text: string) {
@@ -532,6 +532,14 @@ function f() {
[#|let x;|]
return { x };
}`);
testExtractFunction("extractFunction_PreserveTrivia", `
// a
var q = /*b*/ //c
/*d*/ [#|1 /*e*/ //f
/*g*/ + /*h*/ //i
/*j*/ 2|] /*k*/ //l
/*m*/; /*n*/ //o`);
});
function testExtractFunction(caption: string, text: string) {
+27
View File
@@ -152,6 +152,16 @@ namespace ts {
}
}
`);
testExtractRange(`
function f(x: number) {
[#|[$|try {
x++;
}
finally {
return 1;
}|]|]
}
`);
});
testExtractRangeFailed("extractRangeFailed1",
@@ -313,6 +323,23 @@ switch (x) {
refactor.extractSymbol.Messages.CannotExtractRange.message
]);
testExtractRangeFailed("extractRangeFailed11",
`
function f(x: number) {
while (true) {
[#|try {
x++;
}
finally {
break;
}|]
}
}
`,
[
refactor.extractSymbol.Messages.CannotExtractRangeContainingConditionalBreakOrContinueStatements.message
]);
testExtractRangeFailed("extract-method-not-for-token-expression-statement", `[#|a|]`, [refactor.extractSymbol.Messages.CannotExtractIdentifier.message]);
});
}
+112 -5
View File
@@ -281,11 +281,11 @@ namespace ts.projectSystem {
checkNumberOfProjects(this, count);
}
}
export function createProjectService(host: server.ServerHost, parameters: CreateProjectServiceParameters = {}) {
export function createProjectService(host: server.ServerHost, parameters: CreateProjectServiceParameters = {}, options?: Partial<server.ProjectServiceOptions>) {
const cancellationToken = parameters.cancellationToken || server.nullCancellationToken;
const logger = parameters.logger || nullLogger;
const useSingleInferredProject = parameters.useSingleInferredProject !== undefined ? parameters.useSingleInferredProject : false;
return new TestProjectService(host, logger, cancellationToken, useSingleInferredProject, parameters.typingsInstaller, parameters.eventHandler);
return new TestProjectService(host, logger, cancellationToken, useSingleInferredProject, parameters.typingsInstaller, parameters.eventHandler, options);
}
export function checkNumberOfConfiguredProjects(projectService: server.ProjectService, expected: number) {
@@ -3784,6 +3784,113 @@ namespace ts.projectSystem {
assert.equal(projectService.inferredProjects[1].getCompilationSettings().target, ScriptTarget.ESNext);
assert.equal(projectService.inferredProjects[2].getCompilationSettings().target, ScriptTarget.ES2015);
});
function checkInferredProject(inferredProject: server.InferredProject, actualFiles: FileOrFolder[], target: ScriptTarget) {
checkProjectActualFiles(inferredProject, actualFiles.map(f => f.path));
assert.equal(inferredProject.getCompilationSettings().target, target);
}
function verifyProjectRootWithCaseSensitivity(useCaseSensitiveFileNames: boolean) {
const files: [FileOrFolder, FileOrFolder, FileOrFolder, FileOrFolder] = [
{ path: "/a/file1.ts", content: "let x = 1;" },
{ path: "/A/file2.ts", content: "let y = 2;" },
{ path: "/b/file2.ts", content: "let x = 3;" },
{ path: "/c/file3.ts", content: "let z = 4;" }
];
const host = createServerHost(files, { useCaseSensitiveFileNames });
const projectService = createProjectService(host, { useSingleInferredProject: true, }, { useInferredProjectPerProjectRoot: true });
projectService.setCompilerOptionsForInferredProjects({
allowJs: true,
target: ScriptTarget.ESNext
});
projectService.setCompilerOptionsForInferredProjects({
allowJs: true,
target: ScriptTarget.ES2015
}, "/a");
openClientFiles(["/a", "/a", "/b", undefined]);
verifyInferredProjectsState([
[[files[3]], ScriptTarget.ESNext],
[[files[0], files[1]], ScriptTarget.ES2015],
[[files[2]], ScriptTarget.ESNext]
]);
closeClientFiles();
openClientFiles(["/a", "/A", "/b", undefined]);
if (useCaseSensitiveFileNames) {
verifyInferredProjectsState([
[[files[3]], ScriptTarget.ESNext],
[[files[0]], ScriptTarget.ES2015],
[[files[1]], ScriptTarget.ESNext],
[[files[2]], ScriptTarget.ESNext]
]);
}
else {
verifyInferredProjectsState([
[[files[3]], ScriptTarget.ESNext],
[[files[0], files[1]], ScriptTarget.ES2015],
[[files[2]], ScriptTarget.ESNext]
]);
}
closeClientFiles();
projectService.setCompilerOptionsForInferredProjects({
allowJs: true,
target: ScriptTarget.ES2017
}, "/A");
openClientFiles(["/a", "/a", "/b", undefined]);
verifyInferredProjectsState([
[[files[3]], ScriptTarget.ESNext],
[[files[0], files[1]], useCaseSensitiveFileNames ? ScriptTarget.ES2015 : ScriptTarget.ES2017],
[[files[2]], ScriptTarget.ESNext]
]);
closeClientFiles();
openClientFiles(["/a", "/A", "/b", undefined]);
if (useCaseSensitiveFileNames) {
verifyInferredProjectsState([
[[files[3]], ScriptTarget.ESNext],
[[files[0]], ScriptTarget.ES2015],
[[files[1]], ScriptTarget.ES2017],
[[files[2]], ScriptTarget.ESNext]
]);
}
else {
verifyInferredProjectsState([
[[files[3]], ScriptTarget.ESNext],
[[files[0], files[1]], ScriptTarget.ES2017],
[[files[2]], ScriptTarget.ESNext]
]);
}
closeClientFiles();
function openClientFiles(projectRoots: [string | undefined, string | undefined, string | undefined, string | undefined]) {
files.forEach((file, index) => {
projectService.openClientFile(file.path, file.content, ScriptKind.JS, projectRoots[index]);
});
}
function closeClientFiles() {
files.forEach(file => projectService.closeClientFile(file.path));
}
function verifyInferredProjectsState(expected: [FileOrFolder[], ScriptTarget][]) {
checkNumberOfProjects(projectService, { inferredProjects: expected.length });
projectService.inferredProjects.forEach((p, index) => {
const [actualFiles, target] = expected[index];
checkInferredProject(p, actualFiles, target);
});
}
}
it("inferred projects per project root with case sensitive system", () => {
verifyProjectRootWithCaseSensitivity(/*useCaseSensitiveFileNames*/ true);
});
it("inferred projects per project root with case insensitive system", () => {
verifyProjectRootWithCaseSensitivity(/*useCaseSensitiveFileNames*/ false);
});
});
describe("No overwrite emit error", () => {
@@ -4412,9 +4519,9 @@ namespace ts.projectSystem {
fileName: "/a.ts",
textChanges: [
{
start: { line: 2, offset: 1 },
end: { line: 3, offset: 1 },
newText: " newFunction();\n",
start: { line: 2, offset: 3 },
end: { line: 2, offset: 5 },
newText: "newFunction();",
},
{
start: { line: 3, offset: 2 },
+8 -7
View File
@@ -590,9 +590,9 @@ namespace ts.server {
// always set 'allowNonTsExtensions' for inferred projects since user cannot configure it from the outside
// previously we did not expose a way for user to change these settings and this option was enabled by default
compilerOptions.allowNonTsExtensions = true;
if (projectRootPath) {
this.compilerOptionsForInferredProjectsPerProjectRoot.set(projectRootPath, compilerOptions);
const canonicalProjectRootPath = projectRootPath && this.toCanonicalFileName(projectRootPath);
if (canonicalProjectRootPath) {
this.compilerOptionsForInferredProjectsPerProjectRoot.set(canonicalProjectRootPath, compilerOptions);
}
else {
this.compilerOptionsForInferredProjects = compilerOptions;
@@ -608,9 +608,9 @@ namespace ts.server {
// root path
// - Inferred projects with a projectRootPath, if the new options apply to that
// project root path.
if (projectRootPath ?
project.projectRootPath === projectRootPath :
!project.projectRootPath || !this.compilerOptionsForInferredProjectsPerProjectRoot.has(project.projectRootPath)) {
if (canonicalProjectRootPath ?
project.projectRootPath === canonicalProjectRootPath :
!project.projectRootPath || !this.compilerOptionsForInferredProjectsPerProjectRoot.has(project.projectRootPath)) {
project.setCompilerOptions(compilerOptions);
project.compileOnSaveEnabled = compilerOptions.compileOnSave;
project.markAsDirty();
@@ -1599,9 +1599,10 @@ namespace ts.server {
}
if (projectRootPath) {
const canonicalProjectRootPath = this.toCanonicalFileName(projectRootPath);
// if we have an explicit project root path, find (or create) the matching inferred project.
for (const project of this.inferredProjects) {
if (project.projectRootPath === projectRootPath) {
if (project.projectRootPath === canonicalProjectRootPath) {
return project;
}
}
+5 -1
View File
@@ -1047,12 +1047,15 @@ namespace ts.server {
super.setCompilerOptions(newOptions);
}
/** this is canonical project root path */
readonly projectRootPath: string | undefined;
/*@internal*/
constructor(
projectService: ProjectService,
documentRegistry: DocumentRegistry,
compilerOptions: CompilerOptions,
readonly projectRootPath: string | undefined,
projectRootPath: string | undefined,
currentDirectory: string | undefined) {
super(InferredProject.newName(),
ProjectKind.Inferred,
@@ -1064,6 +1067,7 @@ namespace ts.server {
/*compileOnSaveEnabled*/ false,
projectService.host,
currentDirectory);
this.projectRootPath = projectRootPath && projectService.toCanonicalFileName(projectRootPath);
}
addRoot(info: ScriptInfo) {
+12 -11
View File
@@ -476,7 +476,10 @@ namespace ts.refactor.extractSymbol {
// if range uses this as keyword or as type inside the class then it can only be extracted to a method of the containing class
const containingClass = getContainingClass(current);
if (containingClass) {
return [containingClass];
const containingFunction = findAncestor(current, isFunctionLikeDeclaration);
return containingFunction
? [containingFunction, containingClass]
: [containingClass];
}
}
@@ -737,6 +740,8 @@ namespace ts.refactor.extractSymbol {
}
const { body, returnValueProperty } = transformFunctionBody(node, exposedVariableDeclarations, writes, substitutions, !!(range.facts & RangeFacts.HasReturn));
suppressLeadingAndTrailingTrivia(body);
let newFunction: MethodDeclaration | FunctionDeclaration;
if (isClassLike(scope)) {
@@ -923,15 +928,10 @@ namespace ts.refactor.extractSymbol {
}
}
if (isReadonlyArray(range.range)) {
changeTracker.replaceNodesWithNodes(context.file, range.range, newNodes, {
nodeSeparator: context.newLineCharacter,
suffix: context.newLineCharacter // insert newline only when replacing statements
});
}
else {
changeTracker.replaceNodeWithNodes(context.file, range.range, newNodes, { nodeSeparator: context.newLineCharacter });
}
const replacementRange = isReadonlyArray(range.range)
? { pos: first(range.range).getStart(), end: last(range.range).end }
: { pos: range.range.getStart(), end: range.range.end };
changeTracker.replaceRangeWithNodes(context.file, replacementRange, newNodes, { nodeSeparator: context.newLineCharacter });
const edits = changeTracker.getChanges();
const renameRange = isReadonlyArray(range.range) ? first(range.range) : range.range;
@@ -979,6 +979,7 @@ namespace ts.refactor.extractSymbol {
: checker.typeToTypeNode(checker.getContextualType(node), scope, NodeBuilderFlags.NoTruncation);
const initializer = transformConstantInitializer(node, substitutions);
suppressLeadingAndTrailingTrivia(initializer);
const changeTracker = textChanges.ChangeTracker.fromContext(context);
@@ -1011,7 +1012,7 @@ namespace ts.refactor.extractSymbol {
changeTracker.insertNodeBefore(context.file, nodeToInsertBefore, newVariable, { suffix: context.newLineCharacter + context.newLineCharacter });
// Consume
changeTracker.replaceNodeWithNodes(context.file, node, [localReference], { nodeSeparator: context.newLineCharacter });
changeTracker.replaceRange(context.file, { pos: node.getStart(), end: node.end }, localReference);
}
else {
const newVariableDeclaration = createVariableDeclaration(localNameText, variableType, initializer);
+35
View File
@@ -1369,4 +1369,39 @@ namespace ts {
return visited;
}
/**
* Sets EmitFlags to suppress leading and trailing trivia on the node.
*/
/* @internal */
export function suppressLeadingAndTrailingTrivia(node: Node) {
Debug.assert(node !== undefined);
suppressLeading(node);
suppressTrailing(node);
function suppressLeading(node: Node) {
addEmitFlags(node, EmitFlags.NoLeadingComments);
const firstChild = forEachChild(node, child => child);
firstChild && suppressLeading(firstChild);
}
function suppressTrailing(node: Node) {
addEmitFlags(node, EmitFlags.NoTrailingComments);
let lastChild: Node = undefined;
forEachChild(
node,
child => (lastChild = child, undefined),
children => {
// As an optimization, jump straight to the end of the list.
if (children.length) {
lastChild = last(children);
}
return undefined;
});
lastChild && suppressTrailing(lastChild);
}
}
}