Skip to content

Commit f503169

Browse files
committed
Merge pull request #5472 from MartyIX/issue-5183
Fix copyright comments are not preserved when generating d.ts files
2 parents cc64210 + 1703972 commit f503169

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
@@ -4899,7 +4899,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
48994899

49004900
increaseIndent();
49014901
let outPos = writer.getTextPos();
4902-
emitDetachedComments(node.body);
4902+
emitDetachedCommentsAndUpdateCommentsInfo(node.body);
49034903
emitFunctionBodyPreamble(node);
49044904
let preambleEmitted = writer.getTextPos() !== outPos;
49054905
decreaseIndent();
@@ -4944,7 +4944,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
49444944
let initialTextPos = writer.getTextPos();
49454945

49464946
increaseIndent();
4947-
emitDetachedComments(body.statements);
4947+
emitDetachedCommentsAndUpdateCommentsInfo(body.statements);
49484948

49494949
// Emit all the directive prologues (like "use strict"). These have to come before
49504950
// any other preamble code we write (like parameter initializers).
@@ -5266,7 +5266,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
52665266
// Emit all the directive prologues (like "use strict"). These have to come before
52675267
// any other preamble code we write (like parameter initializers).
52685268
startIndex = emitDirectivePrologues(ctor.body.statements, /*startWithNewLine*/ true);
5269-
emitDetachedComments(ctor.body.statements);
5269+
emitDetachedCommentsAndUpdateCommentsInfo(ctor.body.statements);
52705270
}
52715271
emitCaptureThisForNodeIfNecessary(node);
52725272
let superCall: ExpressionStatement;
@@ -7644,7 +7644,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
76447644
// Start new file on new line
76457645
writeLine();
76467646
emitShebang();
7647-
emitDetachedComments(node);
7647+
emitDetachedCommentsAndUpdateCommentsInfo(node);
76487648

76497649
if (isExternalModule(node) || compilerOptions.isolatedModules) {
76507650
let emitModule = moduleEmitDelegates[modulekind] || moduleEmitDelegates[ModuleKind.CommonJS];
@@ -7940,11 +7940,6 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
79407940
return leadingComments;
79417941
}
79427942

7943-
function isPinnedComments(comment: CommentRange) {
7944-
return currentSourceFile.text.charCodeAt(comment.pos + 1) === CharacterCodes.asterisk &&
7945-
currentSourceFile.text.charCodeAt(comment.pos + 2) === CharacterCodes.exclamation;
7946-
}
7947-
79487943
/**
79497944
* Determine if the given comment is a triple-slash
79507945
*
@@ -8078,62 +8073,15 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
80788073
emitComments(currentSourceFile, writer, leadingComments, /*trailingSeparator*/ true, newLine, writeComment);
80798074
}
80808075

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

8107-
if (commentLine >= lastCommentLine + 2) {
8108-
// There was a blank line between the last comment and this comment. This
8109-
// comment is not part of the copyright comments. Return what we have so
8110-
// far.
8111-
return detachedComments;
8112-
}
8113-
}
8114-
8115-
detachedComments.push(comment);
8116-
lastComment = comment;
8117-
});
8118-
8119-
if (detachedComments.length) {
8120-
// All comments look like they could have been part of the copyright header. Make
8121-
// sure there is at least one blank line between it and the node. If not, it's not
8122-
// a copyright header.
8123-
let lastCommentLine = getLineOfLocalPosition(currentSourceFile, lastOrUndefined(detachedComments).end);
8124-
let nodeLine = getLineOfLocalPosition(currentSourceFile, skipTrivia(currentSourceFile.text, node.pos));
8125-
if (nodeLine >= lastCommentLine + 2) {
8126-
// Valid detachedComments
8127-
emitNewLineBeforeLeadingComments(currentSourceFile, writer, node, leadingComments);
8128-
emitComments(currentSourceFile, writer, detachedComments, /*trailingSeparator*/ true, newLine, writeComment);
8129-
let currentDetachedCommentInfo = { nodePos: node.pos, detachedCommentEndPos: lastOrUndefined(detachedComments).end };
8130-
if (detachedCommentsInfo) {
8131-
detachedCommentsInfo.push(currentDetachedCommentInfo);
8132-
}
8133-
else {
8134-
detachedCommentsInfo = [currentDetachedCommentInfo];
8135-
}
8136-
}
8079+
if (currentDetachedCommentInfo) {
8080+
if (detachedCommentsInfo) {
8081+
detachedCommentsInfo.push(currentDetachedCommentInfo);
8082+
}
8083+
else {
8084+
detachedCommentsInfo = [currentDetachedCommentInfo];
81378085
}
81388086
}
81398087
}

src/compiler/utilities.ts

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1912,6 +1912,74 @@ namespace ts {
19121912
});
19131913
}
19141914

1915+
/**
1916+
* Detached comment is a comment at the top of file or function body that is separated from
1917+
* the next statement by space.
1918+
*/
1919+
export function emitDetachedComments(currentSourceFile: SourceFile, writer: EmitTextWriter,
1920+
writeComment: (currentSourceFile: SourceFile, writer: EmitTextWriter, comment: CommentRange, newLine: string) => void,
1921+
node: TextRange, newLine: string, removeComments: boolean) {
1922+
let leadingComments: CommentRange[];
1923+
let currentDetachedCommentInfo: {nodePos: number, detachedCommentEndPos: number};
1924+
if (removeComments) {
1925+
// removeComments is true, only reserve pinned comment at the top of file
1926+
// For example:
1927+
// /*! Pinned Comment */
1928+
//
1929+
// var x = 10;
1930+
if (node.pos === 0) {
1931+
leadingComments = filter(getLeadingCommentRanges(currentSourceFile.text, node.pos), isPinnedComment);
1932+
}
1933+
}
1934+
else {
1935+
// removeComments is false, just get detached as normal and bypass the process to filter comment
1936+
leadingComments = getLeadingCommentRanges(currentSourceFile.text, node.pos);
1937+
}
1938+
1939+
if (leadingComments) {
1940+
let detachedComments: CommentRange[] = [];
1941+
let lastComment: CommentRange;
1942+
1943+
for (let comment of leadingComments) {
1944+
if (lastComment) {
1945+
let lastCommentLine = getLineOfLocalPosition(currentSourceFile, lastComment.end);
1946+
let commentLine = getLineOfLocalPosition(currentSourceFile, comment.pos);
1947+
1948+
if (commentLine >= lastCommentLine + 2) {
1949+
// There was a blank line between the last comment and this comment. This
1950+
// comment is not part of the copyright comments. Return what we have so
1951+
// far.
1952+
break;
1953+
}
1954+
}
1955+
1956+
detachedComments.push(comment);
1957+
lastComment = comment;
1958+
}
1959+
1960+
if (detachedComments.length) {
1961+
// All comments look like they could have been part of the copyright header. Make
1962+
// sure there is at least one blank line between it and the node. If not, it's not
1963+
// a copyright header.
1964+
let lastCommentLine = getLineOfLocalPosition(currentSourceFile, lastOrUndefined(detachedComments).end);
1965+
let nodeLine = getLineOfLocalPosition(currentSourceFile, skipTrivia(currentSourceFile.text, node.pos));
1966+
if (nodeLine >= lastCommentLine + 2) {
1967+
// Valid detachedComments
1968+
emitNewLineBeforeLeadingComments(currentSourceFile, writer, node, leadingComments);
1969+
emitComments(currentSourceFile, writer, detachedComments, /*trailingSeparator*/ true, newLine, writeComment);
1970+
currentDetachedCommentInfo = { nodePos: node.pos, detachedCommentEndPos: lastOrUndefined(detachedComments).end };
1971+
}
1972+
}
1973+
}
1974+
1975+
return currentDetachedCommentInfo;
1976+
1977+
function isPinnedComment(comment: CommentRange) {
1978+
return currentSourceFile.text.charCodeAt(comment.pos + 1) === CharacterCodes.asterisk &&
1979+
currentSourceFile.text.charCodeAt(comment.pos + 2) === CharacterCodes.exclamation;
1980+
}
1981+
}
1982+
19151983
export function writeCommentRange(currentSourceFile: SourceFile, writer: EmitTextWriter, comment: CommentRange, newLine: string) {
19161984
if (currentSourceFile.text.charCodeAt(comment.pos + 1) === CharacterCodes.asterisk) {
19171985
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)