Skip to content

Commit c94dcbd

Browse files
committed
Merge pull request #5726 from Microsoft/cloneNode
Generic algorithm to create a shallow, memberwise clone of a node.
2 parents 883b8d9 + 4edf330 commit c94dcbd

File tree

3 files changed

+56
-18
lines changed

3 files changed

+56
-18
lines changed

src/compiler/core.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -860,4 +860,4 @@ namespace ts {
860860
}
861861
return copiedList;
862862
}
863-
}
863+
}

src/compiler/emitter.ts

+2-4
Original file line numberDiff line numberDiff line change
@@ -549,7 +549,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
549549
[ModuleKind.CommonJS]() {},
550550
};
551551

552-
552+
553553
return doEmit;
554554

555555
function doEmit(jsFilePath: string, sourceMapFilePath: string, sourceFiles: SourceFile[], isBundledEmit: boolean) {
@@ -5991,9 +5991,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
59915991
}
59925992

59935993
// Clone the type name and parent it to a location outside of the current declaration.
5994-
const typeName = cloneEntityName(node.typeName);
5995-
typeName.parent = location;
5996-
5994+
const typeName = cloneEntityName(node.typeName, location);
59975995
const result = resolver.getTypeReferenceSerializationKind(typeName);
59985996
switch (result) {
59995997
case TypeReferenceSerializationKind.Unknown:

src/compiler/utilities.ts

+53-13
Original file line numberDiff line numberDiff line change
@@ -1580,20 +1580,60 @@ namespace ts {
15801580
return isFunctionLike(n) || n.kind === SyntaxKind.ModuleDeclaration || n.kind === SyntaxKind.SourceFile;
15811581
}
15821582

1583-
export function cloneEntityName(node: EntityName): EntityName {
1584-
if (node.kind === SyntaxKind.Identifier) {
1585-
const clone = <Identifier>createSynthesizedNode(SyntaxKind.Identifier);
1586-
clone.text = (<Identifier>node).text;
1587-
return clone;
1583+
/**
1584+
* Creates a shallow, memberwise clone of a node. The "kind", "pos", "end", "flags", and "parent"
1585+
* properties are excluded by default, and can be provided via the "location", "flags", and
1586+
* "parent" parameters.
1587+
* @param node The node to clone.
1588+
* @param location An optional TextRange to use to supply the new position.
1589+
* @param flags The NodeFlags to use for the cloned node.
1590+
* @param parent The parent for the new node.
1591+
*/
1592+
export function cloneNode<T extends Node>(node: T, location?: TextRange, flags?: NodeFlags, parent?: Node): T {
1593+
// We don't use "clone" from core.ts here, as we need to preserve the prototype chain of
1594+
// the original node. We also need to exclude specific properties and only include own-
1595+
// properties (to skip members already defined on the shared prototype).
1596+
const clone = location !== undefined
1597+
? <T>createNode(node.kind, location.pos, location.end)
1598+
: <T>createSynthesizedNode(node.kind);
1599+
1600+
for (const key in node) {
1601+
if (clone.hasOwnProperty(key) || !node.hasOwnProperty(key)) {
1602+
continue;
1603+
}
1604+
1605+
(<any>clone)[key] = (<any>node)[key];
15881606
}
1589-
else {
1590-
const clone = <QualifiedName>createSynthesizedNode(SyntaxKind.QualifiedName);
1591-
clone.left = cloneEntityName((<QualifiedName>node).left);
1592-
clone.left.parent = clone;
1593-
clone.right = <Identifier>cloneEntityName((<QualifiedName>node).right);
1594-
clone.right.parent = clone;
1595-
return clone;
1607+
1608+
if (flags !== undefined) {
1609+
clone.flags = flags;
1610+
}
1611+
1612+
if (parent !== undefined) {
1613+
clone.parent = parent;
15961614
}
1615+
1616+
return clone;
1617+
}
1618+
1619+
/**
1620+
* Creates a deep clone of an EntityName, with new parent pointers.
1621+
* @param node The EntityName to clone.
1622+
* @param parent The parent for the cloned node.
1623+
*/
1624+
export function cloneEntityName(node: EntityName, parent?: Node): EntityName {
1625+
const clone = cloneNode(node, node, node.flags, parent);
1626+
if (isQualifiedName(clone)) {
1627+
const { left, right } = clone;
1628+
clone.left = cloneEntityName(left, clone);
1629+
clone.right = cloneNode(right, right, right.flags, parent);
1630+
}
1631+
1632+
return clone;
1633+
}
1634+
1635+
export function isQualifiedName(node: Node): node is QualifiedName {
1636+
return node.kind === SyntaxKind.QualifiedName;
15971637
}
15981638

15991639
export function nodeIsSynthesized(node: Node): boolean {
@@ -1936,7 +1976,7 @@ namespace ts {
19361976
const bundledSources = filter(host.getSourceFiles(),
19371977
sourceFile => !isDeclarationFile(sourceFile) && // Not a declaration file
19381978
(!isExternalModule(sourceFile) || // non module file
1939-
(getEmitModuleKind(options) && isExternalModule(sourceFile)))); // module that can emit - note falsy value from getEmitModuleKind means the module kind that shouldn't be emitted
1979+
(getEmitModuleKind(options) && isExternalModule(sourceFile)))); // module that can emit - note falsy value from getEmitModuleKind means the module kind that shouldn't be emitted
19401980
if (bundledSources.length) {
19411981
const jsFilePath = options.outFile || options.out;
19421982
const emitFileNames: EmitFileNames = {

0 commit comments

Comments
 (0)