diff --git a/src/compiler/core.ts b/src/compiler/core.ts index c93f3aebc2d42..79f0251c5651f 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -860,4 +860,4 @@ namespace ts { } return copiedList; } -} +} diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 48746d828f5a7..d2f9abc7354f5 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -549,7 +549,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi [ModuleKind.CommonJS]() {}, }; - + return doEmit; function doEmit(jsFilePath: string, sourceMapFilePath: string, sourceFiles: SourceFile[], isBundledEmit: boolean) { @@ -5991,9 +5991,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } // Clone the type name and parent it to a location outside of the current declaration. - const typeName = cloneEntityName(node.typeName); - typeName.parent = location; - + const typeName = cloneEntityName(node.typeName, location); const result = resolver.getTypeReferenceSerializationKind(typeName); switch (result) { case TypeReferenceSerializationKind.Unknown: diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index eef6dbe970250..1884cee15161d 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -1580,20 +1580,60 @@ namespace ts { return isFunctionLike(n) || n.kind === SyntaxKind.ModuleDeclaration || n.kind === SyntaxKind.SourceFile; } - export function cloneEntityName(node: EntityName): EntityName { - if (node.kind === SyntaxKind.Identifier) { - const clone = createSynthesizedNode(SyntaxKind.Identifier); - clone.text = (node).text; - return clone; + /** + * Creates a shallow, memberwise clone of a node. The "kind", "pos", "end", "flags", and "parent" + * properties are excluded by default, and can be provided via the "location", "flags", and + * "parent" parameters. + * @param node The node to clone. + * @param location An optional TextRange to use to supply the new position. + * @param flags The NodeFlags to use for the cloned node. + * @param parent The parent for the new node. + */ + export function cloneNode(node: T, location?: TextRange, flags?: NodeFlags, parent?: Node): T { + // We don't use "clone" from core.ts here, as we need to preserve the prototype chain of + // the original node. We also need to exclude specific properties and only include own- + // properties (to skip members already defined on the shared prototype). + const clone = location !== undefined + ? createNode(node.kind, location.pos, location.end) + : createSynthesizedNode(node.kind); + + for (const key in node) { + if (clone.hasOwnProperty(key) || !node.hasOwnProperty(key)) { + continue; + } + + (clone)[key] = (node)[key]; } - else { - const clone = createSynthesizedNode(SyntaxKind.QualifiedName); - clone.left = cloneEntityName((node).left); - clone.left.parent = clone; - clone.right = cloneEntityName((node).right); - clone.right.parent = clone; - return clone; + + if (flags !== undefined) { + clone.flags = flags; + } + + if (parent !== undefined) { + clone.parent = parent; } + + return clone; + } + + /** + * Creates a deep clone of an EntityName, with new parent pointers. + * @param node The EntityName to clone. + * @param parent The parent for the cloned node. + */ + export function cloneEntityName(node: EntityName, parent?: Node): EntityName { + const clone = cloneNode(node, node, node.flags, parent); + if (isQualifiedName(clone)) { + const { left, right } = clone; + clone.left = cloneEntityName(left, clone); + clone.right = cloneNode(right, right, right.flags, parent); + } + + return clone; + } + + export function isQualifiedName(node: Node): node is QualifiedName { + return node.kind === SyntaxKind.QualifiedName; } export function nodeIsSynthesized(node: Node): boolean { @@ -1936,7 +1976,7 @@ namespace ts { const bundledSources = filter(host.getSourceFiles(), sourceFile => !isDeclarationFile(sourceFile) && // Not a declaration file (!isExternalModule(sourceFile) || // non module file - (getEmitModuleKind(options) && isExternalModule(sourceFile)))); // module that can emit - note falsy value from getEmitModuleKind means the module kind that shouldn't be emitted + (getEmitModuleKind(options) && isExternalModule(sourceFile)))); // module that can emit - note falsy value from getEmitModuleKind means the module kind that shouldn't be emitted if (bundledSources.length) { const jsFilePath = options.outFile || options.out; const emitFileNames: EmitFileNames = {