hermes-transform 0.28.1 → 0.29.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/detachedNode.js +1 -0
- package/dist/detachedNode.js.flow +1 -0
- package/dist/generated/node-types.js +1 -12
- package/dist/generated/node-types.js.flow +2 -19
- package/dist/generated/special-case-node-types/misc.js +11 -0
- package/dist/generated/special-case-node-types/misc.js.flow +18 -0
- package/dist/src/detachedNode.js +178 -0
- package/dist/src/generated/node-types.js +2212 -0
- package/dist/src/generated/special-case-node-types/Comment.js +36 -0
- package/dist/src/generated/special-case-node-types/DeclareExportDeclaration.js +58 -0
- package/dist/src/generated/special-case-node-types/DeclareHook.js +33 -0
- package/dist/src/generated/special-case-node-types/ExportNamedDeclaration.js +44 -0
- package/dist/src/generated/special-case-node-types/Literal.js +97 -0
- package/dist/src/generated/special-case-node-types/ObjectTypeProperty.js +74 -0
- package/dist/src/generated/special-case-node-types/Property.js +136 -0
- package/dist/src/generated/special-case-node-types/misc.js +158 -0
- package/dist/src/generated/special-case-node-types.js +69 -0
- package/dist/src/index.js +53 -0
- package/dist/src/transform/Errors.js +43 -0
- package/dist/src/transform/MutationContext.js +81 -0
- package/dist/src/transform/TransformContext.js +213 -0
- package/dist/src/transform/comments/comments.js +308 -0
- package/dist/src/transform/comments/prettier/common/util.js +364 -0
- package/dist/src/transform/comments/prettier/language-js/comments.js +788 -0
- package/dist/src/transform/comments/prettier/language-js/loc.js +42 -0
- package/dist/src/transform/comments/prettier/language-js/printer-estree.js +32 -0
- package/dist/src/transform/comments/prettier/language-js/utils.js +119 -0
- package/dist/src/transform/comments/prettier/main/comments.js +440 -0
- package/dist/src/transform/comments/prettier/utils/get-last.js +13 -0
- package/dist/src/transform/mutations/AddComments.js +43 -0
- package/dist/src/transform/mutations/CloneCommentsTo.js +31 -0
- package/dist/src/transform/mutations/InsertStatement.js +91 -0
- package/dist/src/transform/mutations/ModifyNodeInPlace.js +59 -0
- package/dist/src/transform/mutations/RemoveComment.js +78 -0
- package/dist/src/transform/mutations/RemoveNode.js +205 -0
- package/dist/src/transform/mutations/RemoveStatement.js +59 -0
- package/dist/src/transform/mutations/ReplaceNode.js +92 -0
- package/dist/src/transform/mutations/ReplaceStatementWithMany.js +79 -0
- package/dist/src/transform/mutations/utils/getStatementParent.js +143 -0
- package/dist/src/transform/mutations/utils/isValidModuleDeclarationParent.js +43 -0
- package/dist/src/transform/parse.js +56 -0
- package/dist/src/transform/print.js +134 -0
- package/dist/src/transform/transform.js +36 -0
- package/dist/src/transform/transformAST.js +134 -0
- package/dist/src/traverse/NodeEventGenerator.js +353 -0
- package/dist/src/traverse/SafeEmitter.js +52 -0
- package/dist/src/traverse/esquery.js +37 -0
- package/dist/src/traverse/traverse.js +150 -0
- package/dist/transform/comments/prettier/language-js/comments.js +29 -1
- package/dist/transform/mutations/utils/getStatementParent.js.flow +7 -5
- package/package.json +5 -5
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.CommentPlacement = void 0;
|
|
7
|
+
exports.addComment = addComment;
|
|
8
|
+
exports.appendCommentToSource = appendCommentToSource;
|
|
9
|
+
exports.attachComments = attachComments;
|
|
10
|
+
exports.cloneComment = cloneComment;
|
|
11
|
+
exports.cloneCommentWithMarkers = cloneCommentWithMarkers;
|
|
12
|
+
exports.cloneCommentsToNewNode = cloneCommentsToNewNode;
|
|
13
|
+
exports.cloneJSDocCommentsToNewNode = cloneJSDocCommentsToNewNode;
|
|
14
|
+
exports.getCommentsForNode = getCommentsForNode;
|
|
15
|
+
exports.getLeadingCommentsForNode = getLeadingCommentsForNode;
|
|
16
|
+
exports.getTrailingCommentsForNode = getTrailingCommentsForNode;
|
|
17
|
+
exports.isAttachedComment = isAttachedComment;
|
|
18
|
+
exports.isLeadingComment = isLeadingComment;
|
|
19
|
+
exports.isTrailingComment = isTrailingComment;
|
|
20
|
+
exports.makeCommentOwnLine = makeCommentOwnLine;
|
|
21
|
+
exports.moveCommentsToNewNode = moveCommentsToNewNode;
|
|
22
|
+
exports.mutateESTreeASTCommentsForPrettier = mutateESTreeASTCommentsForPrettier;
|
|
23
|
+
exports.setCommentsOnNode = setCommentsOnNode;
|
|
24
|
+
|
|
25
|
+
var _comments = require("./prettier/main/comments");
|
|
26
|
+
|
|
27
|
+
var _loc = require("./prettier/language-js/loc");
|
|
28
|
+
|
|
29
|
+
var _printerEstree = _interopRequireDefault(require("./prettier/language-js/printer-estree"));
|
|
30
|
+
|
|
31
|
+
var _util = require("./prettier/common/util");
|
|
32
|
+
|
|
33
|
+
var _hermesEstree = require("hermes-estree");
|
|
34
|
+
|
|
35
|
+
var _os = require("os");
|
|
36
|
+
|
|
37
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
41
|
+
*
|
|
42
|
+
* This source code is licensed under the MIT license found in the
|
|
43
|
+
* LICENSE file in the root directory of this source tree.
|
|
44
|
+
*
|
|
45
|
+
*
|
|
46
|
+
* @format
|
|
47
|
+
*/
|
|
48
|
+
// $FlowExpectedError[untyped-import]
|
|
49
|
+
// $FlowExpectedError[untyped-import]
|
|
50
|
+
// $FlowExpectedError[untyped-import]
|
|
51
|
+
const CommentPlacement = require("flow-enums-runtime").Mirrored(["LEADING_OWN_LINE", "LEADING_INLINE", "TRAILING_OWN_LINE", "TRAILING_INLINE"]);
|
|
52
|
+
|
|
53
|
+
exports.CommentPlacement = CommentPlacement;
|
|
54
|
+
|
|
55
|
+
function attachComments(comments, ast, text) {
|
|
56
|
+
(0, _comments.attach)(comments, ast, text, {
|
|
57
|
+
locStart: _loc.locStart,
|
|
58
|
+
locEnd: _loc.locEnd,
|
|
59
|
+
printer: _printerEstree.default
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function mutateESTreeASTCommentsForPrettier(program, text) {
|
|
64
|
+
let code = text; // we need to delete the comments prop or else prettier will do
|
|
65
|
+
// its own attachment pass after the mutation and duplicate the
|
|
66
|
+
// comments on each node, borking the output
|
|
67
|
+
// $FlowExpectedError[cannot-write]
|
|
68
|
+
|
|
69
|
+
delete program.comments; // The docblock comment is never attached to any AST nodes, since its technically
|
|
70
|
+
// attached to the program. However this is specific to our AST and in order for
|
|
71
|
+
// prettier to correctly print it we need to attach it to the first node in the
|
|
72
|
+
// program body.
|
|
73
|
+
|
|
74
|
+
if (program.docblock != null && program.docblock.comment != null) {
|
|
75
|
+
const docblockComment = program.docblock.comment;
|
|
76
|
+
const isDocblockCommentNew = !isAttachedComment(docblockComment);
|
|
77
|
+
|
|
78
|
+
if (isDocblockCommentNew) {
|
|
79
|
+
// $FlowExpectedError[prop-missing]
|
|
80
|
+
docblockComment.printed = false; // $FlowExpectedError[prop-missing]
|
|
81
|
+
|
|
82
|
+
docblockComment.leading = true; // $FlowExpectedError[prop-missing]
|
|
83
|
+
|
|
84
|
+
docblockComment.trailing = false;
|
|
85
|
+
} // If we have a first node in the program body, attach the comment to that
|
|
86
|
+
// otherwise set it on the program.
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
if (program.body.length > 0) {
|
|
90
|
+
const firstStatement = program.body[0];
|
|
91
|
+
const leadingComments = getLeadingCommentsForNode(firstStatement);
|
|
92
|
+
|
|
93
|
+
if (!leadingComments.includes(docblockComment)) {
|
|
94
|
+
setCommentsOnNode(firstStatement, [docblockComment, ...getCommentsForNode(firstStatement)]);
|
|
95
|
+
|
|
96
|
+
if (isDocblockCommentNew) {
|
|
97
|
+
// Add markers to the code block to ensure docblock comment is printed on its
|
|
98
|
+
// own line.
|
|
99
|
+
code = makeCommentOwnLine(code, docblockComment);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
} else {
|
|
103
|
+
setCommentsOnNode(program, [docblockComment]);
|
|
104
|
+
}
|
|
105
|
+
} // Should not be needed anymore
|
|
106
|
+
// $FlowExpectedError[cannot-write]
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
delete program.docblock;
|
|
110
|
+
return code;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function moveCommentsToNewNode(oldNode, newNode) {
|
|
114
|
+
setCommentsOnNode(newNode, getCommentsForNode(oldNode));
|
|
115
|
+
setCommentsOnNode(oldNode, []);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function cloneCommentsToNewNode(oldNode, newNode) {
|
|
119
|
+
setCommentsOnNode(newNode, getCommentsForNode(oldNode).map(comment => cloneCommentWithMarkers(comment)));
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function cloneJSDocCommentsToNewNode(oldNode, newNode) {
|
|
123
|
+
const comments = getCommentsForNode(oldNode).filter(comment => {
|
|
124
|
+
return (0, _hermesEstree.isBlockComment)(comment) && // JSDoc comments always start with an extra asterisk
|
|
125
|
+
comment.value.startsWith('*');
|
|
126
|
+
});
|
|
127
|
+
setCommentsOnNode(newNode, [...getCommentsForNode(newNode), ...comments.map(cloneCommentWithMarkers)]);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function setCommentsOnNode(node, comments) {
|
|
131
|
+
// $FlowExpectedError - this property is secretly added by prettier.
|
|
132
|
+
node.comments = comments;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function getCommentsForNode(node) {
|
|
136
|
+
var _node$comments;
|
|
137
|
+
|
|
138
|
+
// $FlowExpectedError - this property is secretly added by prettier.
|
|
139
|
+
return (_node$comments = node.comments) != null ? _node$comments : [];
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function isAttachedComment(comment) {
|
|
143
|
+
// Prettier adds this property to comments that have been attached.
|
|
144
|
+
// $FlowExpectedError[prop-missing] - this property is secretly added by prettier.
|
|
145
|
+
return comment.printed === false;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function isLeadingComment(comment) {
|
|
149
|
+
// $FlowExpectedError - this property is secretly added by prettier.
|
|
150
|
+
return comment.leading === true;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function isTrailingComment(comment) {
|
|
154
|
+
// $FlowExpectedError - this property is secretly added by prettier.
|
|
155
|
+
return comment.trailing === true;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function getLeadingCommentsForNode(node) {
|
|
159
|
+
return getCommentsForNode(node).filter(isLeadingComment);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function getTrailingCommentsForNode(node) {
|
|
163
|
+
return getCommentsForNode(node).filter(isTrailingComment);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function addComment(node, comment, placement) {
|
|
167
|
+
switch (placement) {
|
|
168
|
+
case CommentPlacement.LEADING_OWN_LINE:
|
|
169
|
+
case CommentPlacement.LEADING_INLINE:
|
|
170
|
+
{
|
|
171
|
+
(0, _util.addLeadingComment)(node, comment);
|
|
172
|
+
break;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
case CommentPlacement.TRAILING_OWN_LINE:
|
|
176
|
+
case CommentPlacement.TRAILING_INLINE:
|
|
177
|
+
{
|
|
178
|
+
(0, _util.addTrailingComment)(node, comment);
|
|
179
|
+
break;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function cloneComment(comment) {
|
|
185
|
+
// $FlowExpectedError[incompatible-return]
|
|
186
|
+
return {
|
|
187
|
+
type: comment.type,
|
|
188
|
+
value: comment.value,
|
|
189
|
+
loc: comment.loc,
|
|
190
|
+
range: comment.range
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function cloneCommentWithMarkers(comment) {
|
|
195
|
+
// $FlowExpectedError[incompatible-return]
|
|
196
|
+
return {
|
|
197
|
+
type: comment.type,
|
|
198
|
+
value: comment.value,
|
|
199
|
+
loc: comment.loc,
|
|
200
|
+
range: comment.range,
|
|
201
|
+
leading: isLeadingComment(comment),
|
|
202
|
+
trailing: isTrailingComment(comment)
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function getFirstNewlineIndex(code) {
|
|
207
|
+
return code.search(/\r\n|\n|\r/);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
function getFirstNonWhitespaceIndex(code) {
|
|
211
|
+
return code.search(/\S/);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function makeCommentOwnLine(code, comment) {
|
|
215
|
+
let newCode = code; // Since we always want a line break we need to ensure a newline is found when
|
|
216
|
+
// searching out from either side of the comment range.
|
|
217
|
+
|
|
218
|
+
let firstNewline = getFirstNewlineIndex(code);
|
|
219
|
+
|
|
220
|
+
if (firstNewline === -1) {
|
|
221
|
+
// No newline in file, lets add one.
|
|
222
|
+
newCode += _os.EOL;
|
|
223
|
+
firstNewline = newCode.length;
|
|
224
|
+
} // Prettier only uses these ranges for detecting whitespace, so this nonsensical
|
|
225
|
+
// range is safe.
|
|
226
|
+
// $FlowExpectedError[cannot-write]
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
comment.range = [firstNewline + 1, firstNewline];
|
|
230
|
+
return newCode;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
function appendCommentToSource(code, comment, placement) {
|
|
234
|
+
let newCode = code;
|
|
235
|
+
|
|
236
|
+
switch (comment.type) {
|
|
237
|
+
case 'Block':
|
|
238
|
+
{
|
|
239
|
+
// Prettier decides if a newline is necessary between the comment and its node by looking
|
|
240
|
+
// to see if a newline seperates them in the source text. We can trick prettier into
|
|
241
|
+
// formatting how we want for new comments by placing the range such that a newline
|
|
242
|
+
// will (OWN_LINE) or will not (INLINE) be found when searching from the specified range
|
|
243
|
+
// position.
|
|
244
|
+
switch (placement) {
|
|
245
|
+
case CommentPlacement.LEADING_OWN_LINE:
|
|
246
|
+
case CommentPlacement.TRAILING_OWN_LINE:
|
|
247
|
+
{
|
|
248
|
+
newCode = makeCommentOwnLine(code, comment);
|
|
249
|
+
break;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
case CommentPlacement.LEADING_INLINE:
|
|
253
|
+
case CommentPlacement.TRAILING_INLINE:
|
|
254
|
+
{
|
|
255
|
+
// Since we don't want a line break we need to ensure a non whitespace char is
|
|
256
|
+
// always found before a newline when searching out from either side of the
|
|
257
|
+
// comment range.
|
|
258
|
+
let firstNonWhitespace = getFirstNonWhitespaceIndex(code);
|
|
259
|
+
|
|
260
|
+
if (firstNonWhitespace === -1) {
|
|
261
|
+
// No non whitespace chars in file, lets add an identifiable statement for prettier to find.
|
|
262
|
+
newCode += '$FORCE_INLINE_ON_EMPTY_FILE_TOKEN$;';
|
|
263
|
+
firstNonWhitespace = newCode.length;
|
|
264
|
+
break;
|
|
265
|
+
} // $FlowExpectedError[cannot-write]
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
comment.range = [firstNonWhitespace + 1, firstNonWhitespace];
|
|
269
|
+
break;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
break;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
case 'Line':
|
|
277
|
+
{
|
|
278
|
+
// For `Line` comments prettier slices comments directly from the source code when printing
|
|
279
|
+
// https://github.com/prettier/prettier/blob/5f0ee39fa03532c85bd1c35291450fe7ac3667b3/src/language-js/print/comment.js#L15-L20
|
|
280
|
+
// this means that we need to have any appended comments directly in the
|
|
281
|
+
// source code or else prettier will slice nothing and bork up the transform
|
|
282
|
+
const commentText = `//${comment.value}`;
|
|
283
|
+
const lastChar = newCode[newCode.length - 1];
|
|
284
|
+
|
|
285
|
+
if (lastChar !== '\n' && lastChar !== '\r') {
|
|
286
|
+
newCode += _os.EOL;
|
|
287
|
+
} // Line comments cannot be inline before a node so we only place trailing Line comments inline.
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
if (placement === CommentPlacement.TRAILING_INLINE) {
|
|
291
|
+
// Prettier determines an "end of line" comment by walking backwards from
|
|
292
|
+
// the comment start range through the source code to see if it finds a non
|
|
293
|
+
// newline token. In order to trick prettier for new comments we need to
|
|
294
|
+
// insert fake source code for it to find.
|
|
295
|
+
newCode += '$FORCE_END_OF_LINE_COMMENT_TOKEN$;';
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
const start = newCode.length;
|
|
299
|
+
newCode += commentText;
|
|
300
|
+
const end = newCode.length; // $FlowExpectedError[cannot-write]
|
|
301
|
+
|
|
302
|
+
comment.range = [start, end];
|
|
303
|
+
break;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
return newCode;
|
|
308
|
+
}
|
|
@@ -0,0 +1,364 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*
|
|
7
|
+
* @format
|
|
8
|
+
*/
|
|
9
|
+
'use strict';
|
|
10
|
+
|
|
11
|
+
const stringWidth = require('string-width');
|
|
12
|
+
|
|
13
|
+
const getLast = require('../utils/get-last.js');
|
|
14
|
+
|
|
15
|
+
const notAsciiRegex = /[^\x20-\x7F]/;
|
|
16
|
+
/**
|
|
17
|
+
* @typedef {{backwards?: boolean}} SkipOptions
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* @param {string | RegExp} chars
|
|
22
|
+
* @returns {(text: string, index: number | false, opts?: SkipOptions) => number | false}
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
function skip(chars) {
|
|
26
|
+
return (text, index, opts) => {
|
|
27
|
+
const backwards = opts && opts.backwards; // Allow `skip` functions to be threaded together without having
|
|
28
|
+
// to check for failures (did someone say monads?).
|
|
29
|
+
|
|
30
|
+
/* istanbul ignore next */
|
|
31
|
+
|
|
32
|
+
if (index === false) {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const {
|
|
37
|
+
length
|
|
38
|
+
} = text;
|
|
39
|
+
let cursor = index;
|
|
40
|
+
|
|
41
|
+
while (cursor >= 0 && cursor < length) {
|
|
42
|
+
const c = text.charAt(cursor);
|
|
43
|
+
|
|
44
|
+
if (chars instanceof RegExp) {
|
|
45
|
+
if (!chars.test(c)) {
|
|
46
|
+
return cursor;
|
|
47
|
+
}
|
|
48
|
+
} else if (!chars.includes(c)) {
|
|
49
|
+
return cursor;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
backwards ? cursor-- : cursor++;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (cursor === -1 || cursor === length) {
|
|
56
|
+
// If we reached the beginning or end of the file, return the
|
|
57
|
+
// out-of-bounds cursor. It's up to the caller to handle this
|
|
58
|
+
// correctly. We don't want to indicate `false` though if it
|
|
59
|
+
// actually skipped valid characters.
|
|
60
|
+
return cursor;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return false;
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* @type {(text: string, index: number | false, opts?: SkipOptions) => number | false}
|
|
68
|
+
*/
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
const skipWhitespace = skip(/\s/);
|
|
72
|
+
/**
|
|
73
|
+
* @type {(text: string, index: number | false, opts?: SkipOptions) => number | false}
|
|
74
|
+
*/
|
|
75
|
+
|
|
76
|
+
const skipSpaces = skip(' \t');
|
|
77
|
+
/**
|
|
78
|
+
* @type {(text: string, index: number | false, opts?: SkipOptions) => number | false}
|
|
79
|
+
*/
|
|
80
|
+
|
|
81
|
+
const skipToLineEnd = skip(',; \t');
|
|
82
|
+
/**
|
|
83
|
+
* @type {(text: string, index: number | false, opts?: SkipOptions) => number | false}
|
|
84
|
+
*/
|
|
85
|
+
|
|
86
|
+
const skipEverythingButNewLine = skip(/[^\n\r]/);
|
|
87
|
+
/**
|
|
88
|
+
* @param {string} text
|
|
89
|
+
* @param {number | false} index
|
|
90
|
+
* @returns {number | false}
|
|
91
|
+
*/
|
|
92
|
+
|
|
93
|
+
function skipInlineComment(text, index) {
|
|
94
|
+
/* istanbul ignore next */
|
|
95
|
+
if (index === false) {
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (text.charAt(index) === '/' && text.charAt(index + 1) === '*') {
|
|
100
|
+
for (let i = index + 2; i < text.length; ++i) {
|
|
101
|
+
if (text.charAt(i) === '*' && text.charAt(i + 1) === '/') {
|
|
102
|
+
return i + 2;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return index;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* @param {string} text
|
|
111
|
+
* @param {number | false} index
|
|
112
|
+
* @returns {number | false}
|
|
113
|
+
*/
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
function skipTrailingComment(text, index) {
|
|
117
|
+
/* istanbul ignore next */
|
|
118
|
+
if (index === false) {
|
|
119
|
+
return false;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (text.charAt(index) === '/' && text.charAt(index + 1) === '/') {
|
|
123
|
+
return skipEverythingButNewLine(text, index);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return index;
|
|
127
|
+
} // This one doesn't use the above helper function because it wants to
|
|
128
|
+
// test \r\n in order and `skip` doesn't support ordering and we only
|
|
129
|
+
// want to skip one newline. It's simple to implement.
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* @param {string} text
|
|
133
|
+
* @param {number | false} index
|
|
134
|
+
* @param {SkipOptions=} opts
|
|
135
|
+
* @returns {number | false}
|
|
136
|
+
*/
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
function skipNewline(text, index, opts) {
|
|
140
|
+
const backwards = opts && opts.backwards;
|
|
141
|
+
|
|
142
|
+
if (index === false) {
|
|
143
|
+
return false;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const atIndex = text.charAt(index);
|
|
147
|
+
|
|
148
|
+
if (backwards) {
|
|
149
|
+
// We already replace `\r\n` with `\n` before parsing
|
|
150
|
+
|
|
151
|
+
/* istanbul ignore next */
|
|
152
|
+
if (text.charAt(index - 1) === '\r' && atIndex === '\n') {
|
|
153
|
+
return index - 2;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if (atIndex === '\n' || atIndex === '\r' || atIndex === '\u2028' || atIndex === '\u2029') {
|
|
157
|
+
return index - 1;
|
|
158
|
+
}
|
|
159
|
+
} else {
|
|
160
|
+
// We already replace `\r\n` with `\n` before parsing
|
|
161
|
+
|
|
162
|
+
/* istanbul ignore next */
|
|
163
|
+
if (atIndex === '\r' && text.charAt(index + 1) === '\n') {
|
|
164
|
+
return index + 2;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
if (atIndex === '\n' || atIndex === '\r' || atIndex === '\u2028' || atIndex === '\u2029') {
|
|
168
|
+
return index + 1;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
return index;
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* @param {string} text
|
|
176
|
+
* @param {number} index
|
|
177
|
+
* @param {SkipOptions=} opts
|
|
178
|
+
* @returns {boolean}
|
|
179
|
+
*/
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
function hasNewline(text, index, opts = {}) {
|
|
183
|
+
const idx = skipSpaces(text, opts.backwards ? index - 1 : index, opts);
|
|
184
|
+
const idx2 = skipNewline(text, idx, opts);
|
|
185
|
+
return idx !== idx2;
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* @param {string} text
|
|
189
|
+
* @param {number} start
|
|
190
|
+
* @param {number} end
|
|
191
|
+
* @returns {boolean}
|
|
192
|
+
*/
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
function hasNewlineInRange(text, start, end) {
|
|
196
|
+
for (let i = start; i < end; ++i) {
|
|
197
|
+
if (text.charAt(i) === '\n') {
|
|
198
|
+
return true;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
return false;
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* @param {string} text
|
|
206
|
+
* @param {number} index
|
|
207
|
+
* @returns {boolean}
|
|
208
|
+
*/
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
function isNextLineEmptyAfterIndex(text, index) {
|
|
212
|
+
/** @type {number | false} */
|
|
213
|
+
let oldIdx = null;
|
|
214
|
+
/** @type {number | false} */
|
|
215
|
+
|
|
216
|
+
let idx = index;
|
|
217
|
+
|
|
218
|
+
while (idx !== oldIdx) {
|
|
219
|
+
// We need to skip all the potential trailing inline comments
|
|
220
|
+
oldIdx = idx;
|
|
221
|
+
idx = skipToLineEnd(text, idx);
|
|
222
|
+
idx = skipInlineComment(text, idx);
|
|
223
|
+
idx = skipSpaces(text, idx);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
idx = skipTrailingComment(text, idx);
|
|
227
|
+
idx = skipNewline(text, idx);
|
|
228
|
+
return idx !== false && hasNewline(text, idx);
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* @param {string} text
|
|
232
|
+
* @param {number} idx
|
|
233
|
+
* @returns {number | false}
|
|
234
|
+
*/
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
function getNextNonSpaceNonCommentCharacterIndexWithStartIndex(text, idx) {
|
|
238
|
+
/** @type {number | false} */
|
|
239
|
+
let oldIdx = null;
|
|
240
|
+
/** @type {number | false} */
|
|
241
|
+
|
|
242
|
+
let nextIdx = idx;
|
|
243
|
+
|
|
244
|
+
while (nextIdx !== oldIdx) {
|
|
245
|
+
oldIdx = nextIdx;
|
|
246
|
+
nextIdx = skipSpaces(text, nextIdx);
|
|
247
|
+
nextIdx = skipInlineComment(text, nextIdx);
|
|
248
|
+
nextIdx = skipTrailingComment(text, nextIdx);
|
|
249
|
+
nextIdx = skipNewline(text, nextIdx);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
return nextIdx;
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* @template N
|
|
256
|
+
* @param {string} text
|
|
257
|
+
* @param {N} node
|
|
258
|
+
* @param {(node: N) => number} locEnd
|
|
259
|
+
* @returns {number | false}
|
|
260
|
+
*/
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
function getNextNonSpaceNonCommentCharacterIndex(text, node, locEnd) {
|
|
264
|
+
return getNextNonSpaceNonCommentCharacterIndexWithStartIndex(text, locEnd(node));
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* @template N
|
|
268
|
+
* @param {string} text
|
|
269
|
+
* @param {N} node
|
|
270
|
+
* @param {(node: N) => number} locEnd
|
|
271
|
+
* @returns {string}
|
|
272
|
+
*/
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
function getNextNonSpaceNonCommentCharacter(text, node, locEnd) {
|
|
276
|
+
return text.charAt( // @ts-expect-error => TBD: can return false, should we define a fallback?
|
|
277
|
+
getNextNonSpaceNonCommentCharacterIndex(text, node, locEnd));
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* @param {string} text
|
|
281
|
+
* @returns {number}
|
|
282
|
+
*/
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
function getStringWidth(text) {
|
|
286
|
+
if (!text) {
|
|
287
|
+
return 0;
|
|
288
|
+
} // shortcut to avoid needless string `RegExp`s, replacements, and allocations within `string-width`
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
if (!notAsciiRegex.test(text)) {
|
|
292
|
+
return text.length;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
return stringWidth(text);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
function addCommentHelper(node, comment) {
|
|
299
|
+
const comments = node.comments || (node.comments = []);
|
|
300
|
+
comments.push(comment);
|
|
301
|
+
comment.printed = false;
|
|
302
|
+
comment.nodeDescription = describeNodeForDebugging(node);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
function addLeadingComment(node, comment) {
|
|
306
|
+
comment.leading = true;
|
|
307
|
+
comment.trailing = false;
|
|
308
|
+
addCommentHelper(node, comment);
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
function addDanglingComment(node, comment, marker) {
|
|
312
|
+
comment.leading = false;
|
|
313
|
+
comment.trailing = false;
|
|
314
|
+
|
|
315
|
+
if (marker) {
|
|
316
|
+
comment.marker = marker;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
addCommentHelper(node, comment);
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
function addTrailingComment(node, comment) {
|
|
323
|
+
comment.leading = false;
|
|
324
|
+
comment.trailing = true;
|
|
325
|
+
addCommentHelper(node, comment);
|
|
326
|
+
}
|
|
327
|
+
/**
|
|
328
|
+
* @param {any} object
|
|
329
|
+
* @returns {object is Array<any>}
|
|
330
|
+
*/
|
|
331
|
+
|
|
332
|
+
|
|
333
|
+
function isNonEmptyArray(object) {
|
|
334
|
+
return Array.isArray(object) && object.length > 0;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
function describeNodeForDebugging(node) {
|
|
338
|
+
const nodeType = node.type || node.kind || '(unknown type)';
|
|
339
|
+
let nodeName = String(node.name || node.id && (typeof node.id === 'object' ? node.id.name : node.id) || node.key && (typeof node.key === 'object' ? node.key.name : node.key) || node.value && (typeof node.value === 'object' ? '' : String(node.value)) || node.operator || '');
|
|
340
|
+
|
|
341
|
+
if (nodeName.length > 20) {
|
|
342
|
+
nodeName = nodeName.slice(0, 19) + '…';
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
return nodeType + (nodeName ? ' ' + nodeName : '');
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
module.exports = {
|
|
349
|
+
getStringWidth,
|
|
350
|
+
getLast,
|
|
351
|
+
getNextNonSpaceNonCommentCharacterIndexWithStartIndex,
|
|
352
|
+
getNextNonSpaceNonCommentCharacterIndex,
|
|
353
|
+
getNextNonSpaceNonCommentCharacter,
|
|
354
|
+
skipWhitespace,
|
|
355
|
+
skipSpaces,
|
|
356
|
+
skipNewline,
|
|
357
|
+
isNextLineEmptyAfterIndex,
|
|
358
|
+
hasNewline,
|
|
359
|
+
hasNewlineInRange,
|
|
360
|
+
addLeadingComment,
|
|
361
|
+
addDanglingComment,
|
|
362
|
+
addTrailingComment,
|
|
363
|
+
isNonEmptyArray
|
|
364
|
+
};
|