Skip to content

Commit 1703972

Browse files
committed
Preserve copyright comments when generating d.ts files
1 parent 5cbcafa commit 1703972

11 files changed

+435
-64
lines changed

src/compiler/declarationEmitter.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -468,6 +468,7 @@ namespace ts {
468468
function emitSourceFile(node: SourceFile) {
469469
currentSourceFile = node;
470470
enclosingDeclaration = node;
471+
emitDetachedComments(currentSourceFile, writer, writeCommentRange, node, newLine, true /* remove comments */);
471472
emitLines(node.statements);
472473
}
473474

src/compiler/emitter.ts

Lines changed: 12 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -4907,7 +4907,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
49074907

49084908
increaseIndent();
49094909
let outPos = writer.getTextPos();
4910-
emitDetachedComments(node.body);
4910+
emitDetachedCommentsAndUpdateCommentsInfo(node.body);
49114911
emitFunctionBodyPreamble(node);
49124912
let preambleEmitted = writer.getTextPos() !== outPos;
49134913
decreaseIndent();
@@ -4952,7 +4952,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
49524952
let initialTextPos = writer.getTextPos();
49534953

49544954
increaseIndent();
4955-
emitDetachedComments(body.statements);
4955+
emitDetachedCommentsAndUpdateCommentsInfo(body.statements);
49564956

49574957
// Emit all the directive prologues (like "use strict"). These have to come before
49584958
// any other preamble code we write (like parameter initializers).
@@ -5274,7 +5274,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
52745274
// Emit all the directive prologues (like "use strict"). These have to come before
52755275
// any other preamble code we write (like parameter initializers).
52765276
startIndex = emitDirectivePrologues(ctor.body.statements, /*startWithNewLine*/ true);
5277-
emitDetachedComments(ctor.body.statements);
5277+
emitDetachedCommentsAndUpdateCommentsInfo(ctor.body.statements);
52785278
}
52795279
emitCaptureThisForNodeIfNecessary(node);
52805280
let superCall: ExpressionStatement;
@@ -7652,7 +7652,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
76527652
// Start new file on new line
76537653
writeLine();
76547654
emitShebang();
7655-
emitDetachedComments(node);
7655+
emitDetachedCommentsAndUpdateCommentsInfo(node);
76567656

76577657
if (isExternalModule(node) || compilerOptions.isolatedModules) {
76587658
let emitModule = moduleEmitDelegates[modulekind] || moduleEmitDelegates[ModuleKind.CommonJS];
@@ -7948,11 +7948,6 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
79487948
return leadingComments;
79497949
}
79507950

7951-
function isPinnedComments(comment: CommentRange) {
7952-
return currentSourceFile.text.charCodeAt(comment.pos + 1) === CharacterCodes.asterisk &&
7953-
currentSourceFile.text.charCodeAt(comment.pos + 2) === CharacterCodes.exclamation;
7954-
}
7955-
79567951
/**
79577952
* Determine if the given comment is a triple-slash
79587953
*
@@ -8086,62 +8081,15 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
80868081
emitComments(currentSourceFile, writer, leadingComments, /*trailingSeparator*/ true, newLine, writeComment);
80878082
}
80888083

8089-
function emitDetachedComments(node: TextRange) {
8090-
let leadingComments: CommentRange[];
8091-
if (compilerOptions.removeComments) {
8092-
// removeComments is true, only reserve pinned comment at the top of file
8093-
// For example:
8094-
// /*! Pinned Comment */
8095-
//
8096-
// var x = 10;
8097-
if (node.pos === 0) {
8098-
leadingComments = filter(getLeadingCommentRanges(currentSourceFile.text, node.pos), isPinnedComments);
8099-
}
8100-
}
8101-
else {
8102-
// removeComments is false, just get detached as normal and bypass the process to filter comment
8103-
leadingComments = getLeadingCommentRanges(currentSourceFile.text, node.pos);
8104-
}
8105-
8106-
if (leadingComments) {
8107-
let detachedComments: CommentRange[] = [];
8108-
let lastComment: CommentRange;
8109-
8110-
forEach(leadingComments, comment => {
8111-
if (lastComment) {
8112-
let lastCommentLine = getLineOfLocalPosition(currentSourceFile, lastComment.end);
8113-
let commentLine = getLineOfLocalPosition(currentSourceFile, comment.pos);
8084+
function emitDetachedCommentsAndUpdateCommentsInfo(node: TextRange) {
8085+
let currentDetachedCommentInfo = emitDetachedComments(currentSourceFile, writer, writeComment, node, newLine, compilerOptions.removeComments);
81148086

8115-
if (commentLine >= lastCommentLine + 2) {
8116-
// There was a blank line between the last comment and this comment. This
8117-
// comment is not part of the copyright comments. Return what we have so
8118-
// far.
8119-
return detachedComments;
8120-
}
8121-
}
8122-
8123-
detachedComments.push(comment);
8124-
lastComment = comment;
8125-
});
8126-
8127-
if (detachedComments.length) {
8128-
// All comments look like they could have been part of the copyright header. Make
8129-
// sure there is at least one blank line between it and the node. If not, it's not
8130-
// a copyright header.
8131-
let lastCommentLine = getLineOfLocalPosition(currentSourceFile, lastOrUndefined(detachedComments).end);
8132-
let nodeLine = getLineOfLocalPosition(currentSourceFile, skipTrivia(currentSourceFile.text, node.pos));
8133-
if (nodeLine >= lastCommentLine + 2) {
8134-
// Valid detachedComments
8135-
emitNewLineBeforeLeadingComments(currentSourceFile, writer, node, leadingComments);
8136-
emitComments(currentSourceFile, writer, detachedComments, /*trailingSeparator*/ true, newLine, writeComment);
8137-
let currentDetachedCommentInfo = { nodePos: node.pos, detachedCommentEndPos: lastOrUndefined(detachedComments).end };
8138-
if (detachedCommentsInfo) {
8139-
detachedCommentsInfo.push(currentDetachedCommentInfo);
8140-
}
8141-
else {
8142-
detachedCommentsInfo = [currentDetachedCommentInfo];
8143-
}
8144-
}
8087+
if (currentDetachedCommentInfo) {
8088+
if (detachedCommentsInfo) {
8089+
detachedCommentsInfo.push(currentDetachedCommentInfo);
8090+
}
8091+
else {
8092+
detachedCommentsInfo = [currentDetachedCommentInfo];
81458093
}
81468094
}
81478095
}

src/compiler/utilities.ts

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1904,6 +1904,74 @@ namespace ts {
19041904
});
19051905
}
19061906

1907+
/**
1908+
* Detached comment is a comment at the top of file or function body that is separated from
1909+
* the next statement by space.
1910+
*/
1911+
export function emitDetachedComments(currentSourceFile: SourceFile, writer: EmitTextWriter,
1912+
writeComment: (currentSourceFile: SourceFile, writer: EmitTextWriter, comment: CommentRange, newLine: string) => void,
1913+
node: TextRange, newLine: string, removeComments: boolean) {
1914+
let leadingComments: CommentRange[];
1915+
let currentDetachedCommentInfo: {nodePos: number, detachedCommentEndPos: number};
1916+
if (removeComments) {
1917+
// removeComments is true, only reserve pinned comment at the top of file
1918+
// For example:
1919+
// /*! Pinned Comment */
1920+
//
1921+
// var x = 10;
1922+
if (node.pos === 0) {
1923+
leadingComments = filter(getLeadingCommentRanges(currentSourceFile.text, node.pos), isPinnedComment);
1924+
}
1925+
}
1926+
else {
1927+
// removeComments is false, just get detached as normal and bypass the process to filter comment
1928+
leadingComments = getLeadingCommentRanges(currentSourceFile.text, node.pos);
1929+
}
1930+
1931+
if (leadingComments) {
1932+
let detachedComments: CommentRange[] = [];
1933+
let lastComment: CommentRange;
1934+
1935+
for (let comment of leadingComments) {
1936+
if (lastComment) {
1937+
let lastCommentLine = getLineOfLocalPosition(currentSourceFile, lastComment.end);
1938+
let commentLine = getLineOfLocalPosition(currentSourceFile, comment.pos);
1939+
1940+
if (commentLine >= lastCommentLine + 2) {
1941+
// There was a blank line between the last comment and this comment. This
1942+
// comment is not part of the copyright comments. Return what we have so
1943+
// far.
1944+
break;
1945+
}
1946+
}
1947+
1948+
detachedComments.push(comment);
1949+
lastComment = comment;
1950+
}
1951+
1952+
if (detachedComments.length) {
1953+
// All comments look like they could have been part of the copyright header. Make
1954+
// sure there is at least one blank line between it and the node. If not, it's not
1955+
// a copyright header.
1956+
let lastCommentLine = getLineOfLocalPosition(currentSourceFile, lastOrUndefined(detachedComments).end);
1957+
let nodeLine = getLineOfLocalPosition(currentSourceFile, skipTrivia(currentSourceFile.text, node.pos));
1958+
if (nodeLine >= lastCommentLine + 2) {
1959+
// Valid detachedComments
1960+
emitNewLineBeforeLeadingComments(currentSourceFile, writer, node, leadingComments);
1961+
emitComments(currentSourceFile, writer, detachedComments, /*trailingSeparator*/ true, newLine, writeComment);
1962+
currentDetachedCommentInfo = { nodePos: node.pos, detachedCommentEndPos: lastOrUndefined(detachedComments).end };
1963+
}
1964+
}
1965+
}
1966+
1967+
return currentDetachedCommentInfo;
1968+
1969+
function isPinnedComment(comment: CommentRange) {
1970+
return currentSourceFile.text.charCodeAt(comment.pos + 1) === CharacterCodes.asterisk &&
1971+
currentSourceFile.text.charCodeAt(comment.pos + 2) === CharacterCodes.exclamation;
1972+
}
1973+
}
1974+
19071975
export function writeCommentRange(currentSourceFile: SourceFile, writer: EmitTextWriter, comment: CommentRange, newLine: string) {
19081976
if (currentSourceFile.text.charCodeAt(comment.pos + 1) === CharacterCodes.asterisk) {
19091977
let firstCommentLineAndCharacter = getLineAndCharacterOfPosition(currentSourceFile, comment.pos);
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
//// [tests/cases/compiler/declarationEmitDetachedComment1.ts] ////
2+
3+
//// [test1.ts]
4+
5+
/*! Copyright 2015 MyCompany Inc. */
6+
7+
/**
8+
* Hello class
9+
*/
10+
class Hello {
11+
12+
}
13+
14+
//// [test2.ts]
15+
/* A comment at the top of the file. */
16+
17+
/**
18+
* Hi class
19+
*/
20+
class Hi {
21+
22+
}
23+
24+
//// [test3.ts]
25+
// A one-line comment at the top of the file.
26+
27+
/**
28+
* Hola class
29+
*/
30+
class Hola {
31+
32+
}
33+
34+
35+
//// [test1.js]
36+
/*! Copyright 2015 MyCompany Inc. */
37+
/**
38+
* Hello class
39+
*/
40+
var Hello = (function () {
41+
function Hello() {
42+
}
43+
return Hello;
44+
})();
45+
//// [test2.js]
46+
/* A comment at the top of the file. */
47+
/**
48+
* Hi class
49+
*/
50+
var Hi = (function () {
51+
function Hi() {
52+
}
53+
return Hi;
54+
})();
55+
//// [test3.js]
56+
// A one-line comment at the top of the file.
57+
/**
58+
* Hola class
59+
*/
60+
var Hola = (function () {
61+
function Hola() {
62+
}
63+
return Hola;
64+
})();
65+
66+
67+
//// [test1.d.ts]
68+
/*! Copyright 2015 MyCompany Inc. */
69+
/**
70+
* Hello class
71+
*/
72+
declare class Hello {
73+
}
74+
//// [test2.d.ts]
75+
/**
76+
* Hi class
77+
*/
78+
declare class Hi {
79+
}
80+
//// [test3.d.ts]
81+
/**
82+
* Hola class
83+
*/
84+
declare class Hola {
85+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
=== tests/cases/compiler/test1.ts ===
2+
3+
/*! Copyright 2015 MyCompany Inc. */
4+
5+
/**
6+
* Hello class
7+
*/
8+
class Hello {
9+
>Hello : Symbol(Hello, Decl(test1.ts, 0, 0))
10+
11+
}
12+
13+
=== tests/cases/compiler/test2.ts ===
14+
/* A comment at the top of the file. */
15+
16+
/**
17+
* Hi class
18+
*/
19+
class Hi {
20+
>Hi : Symbol(Hi, Decl(test2.ts, 0, 0))
21+
22+
}
23+
24+
=== tests/cases/compiler/test3.ts ===
25+
// A one-line comment at the top of the file.
26+
27+
/**
28+
* Hola class
29+
*/
30+
class Hola {
31+
>Hola : Symbol(Hola, Decl(test3.ts, 0, 0))
32+
33+
}
34+
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
=== tests/cases/compiler/test1.ts ===
2+
3+
/*! Copyright 2015 MyCompany Inc. */
4+
5+
/**
6+
* Hello class
7+
*/
8+
class Hello {
9+
>Hello : Hello
10+
11+
}
12+
13+
=== tests/cases/compiler/test2.ts ===
14+
/* A comment at the top of the file. */
15+
16+
/**
17+
* Hi class
18+
*/
19+
class Hi {
20+
>Hi : Hi
21+
22+
}
23+
24+
=== tests/cases/compiler/test3.ts ===
25+
// A one-line comment at the top of the file.
26+
27+
/**
28+
* Hola class
29+
*/
30+
class Hola {
31+
>Hola : Hola
32+
33+
}
34+

0 commit comments

Comments
 (0)