hermes-transform 0.28.0 → 0.29.0

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.
Files changed (51) hide show
  1. package/dist/detachedNode.js +1 -0
  2. package/dist/detachedNode.js.flow +1 -0
  3. package/dist/generated/node-types.js +1 -12
  4. package/dist/generated/node-types.js.flow +2 -19
  5. package/dist/generated/special-case-node-types/misc.js +11 -0
  6. package/dist/generated/special-case-node-types/misc.js.flow +18 -0
  7. package/dist/src/detachedNode.js +178 -0
  8. package/dist/src/generated/node-types.js +2212 -0
  9. package/dist/src/generated/special-case-node-types/Comment.js +36 -0
  10. package/dist/src/generated/special-case-node-types/DeclareExportDeclaration.js +58 -0
  11. package/dist/src/generated/special-case-node-types/DeclareHook.js +33 -0
  12. package/dist/src/generated/special-case-node-types/ExportNamedDeclaration.js +44 -0
  13. package/dist/src/generated/special-case-node-types/Literal.js +97 -0
  14. package/dist/src/generated/special-case-node-types/ObjectTypeProperty.js +74 -0
  15. package/dist/src/generated/special-case-node-types/Property.js +136 -0
  16. package/dist/src/generated/special-case-node-types/misc.js +158 -0
  17. package/dist/src/generated/special-case-node-types.js +69 -0
  18. package/dist/src/index.js +53 -0
  19. package/dist/src/transform/Errors.js +43 -0
  20. package/dist/src/transform/MutationContext.js +81 -0
  21. package/dist/src/transform/TransformContext.js +213 -0
  22. package/dist/src/transform/comments/comments.js +308 -0
  23. package/dist/src/transform/comments/prettier/common/util.js +364 -0
  24. package/dist/src/transform/comments/prettier/language-js/comments.js +788 -0
  25. package/dist/src/transform/comments/prettier/language-js/loc.js +42 -0
  26. package/dist/src/transform/comments/prettier/language-js/printer-estree.js +32 -0
  27. package/dist/src/transform/comments/prettier/language-js/utils.js +119 -0
  28. package/dist/src/transform/comments/prettier/main/comments.js +440 -0
  29. package/dist/src/transform/comments/prettier/utils/get-last.js +13 -0
  30. package/dist/src/transform/mutations/AddComments.js +43 -0
  31. package/dist/src/transform/mutations/CloneCommentsTo.js +31 -0
  32. package/dist/src/transform/mutations/InsertStatement.js +91 -0
  33. package/dist/src/transform/mutations/ModifyNodeInPlace.js +59 -0
  34. package/dist/src/transform/mutations/RemoveComment.js +78 -0
  35. package/dist/src/transform/mutations/RemoveNode.js +205 -0
  36. package/dist/src/transform/mutations/RemoveStatement.js +59 -0
  37. package/dist/src/transform/mutations/ReplaceNode.js +92 -0
  38. package/dist/src/transform/mutations/ReplaceStatementWithMany.js +79 -0
  39. package/dist/src/transform/mutations/utils/getStatementParent.js +143 -0
  40. package/dist/src/transform/mutations/utils/isValidModuleDeclarationParent.js +43 -0
  41. package/dist/src/transform/parse.js +56 -0
  42. package/dist/src/transform/print.js +134 -0
  43. package/dist/src/transform/transform.js +36 -0
  44. package/dist/src/transform/transformAST.js +134 -0
  45. package/dist/src/traverse/NodeEventGenerator.js +353 -0
  46. package/dist/src/traverse/SafeEmitter.js +52 -0
  47. package/dist/src/traverse/esquery.js +37 -0
  48. package/dist/src/traverse/traverse.js +150 -0
  49. package/dist/transform/comments/prettier/language-js/comments.js +29 -1
  50. package/dist/transform/mutations/utils/getStatementParent.js.flow +7 -5
  51. package/package.json +5 -5
@@ -0,0 +1,42 @@
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 {
12
+ isNonEmptyArray
13
+ } = require('../common/util.js');
14
+ /**
15
+ * @typedef {import("./types/estree").Node} Node
16
+ */
17
+
18
+
19
+ function locStart(node, opts) {
20
+ const {
21
+ ignoreDecorators
22
+ } = opts || {}; // Handle nodes with decorators. They should start at the first decorator
23
+
24
+ if (!ignoreDecorators) {
25
+ const decorators = node.declaration && node.declaration.decorators || node.decorators;
26
+
27
+ if (isNonEmptyArray(decorators)) {
28
+ return locStart(decorators[0]);
29
+ }
30
+ }
31
+
32
+ return node.range ? node.range[0] : node.start;
33
+ }
34
+
35
+ function locEnd(node) {
36
+ return node.range ? node.range[1] : node.end;
37
+ }
38
+
39
+ module.exports = {
40
+ locStart,
41
+ locEnd
42
+ };
@@ -0,0 +1,32 @@
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 handleComments = require('./comments.js');
12
+
13
+ const {
14
+ isBlockComment,
15
+ isLineComment
16
+ } = require('./utils.js');
17
+
18
+ function canAttachComment(node) {
19
+ return node.type && !isBlockComment(node) && !isLineComment(node) && node.type !== 'EmptyStatement' && node.type !== 'TemplateElement' && node.type !== 'Import' && // `babel-ts` don't have similar node for `class Foo { bar() /* bat */; }`
20
+ node.type !== 'TSEmptyBodyFunctionExpression';
21
+ }
22
+
23
+ module.exports = {
24
+ canAttachComment,
25
+ handleComments: {
26
+ avoidAstMutation: true,
27
+ ownLine: handleComments.handleOwnLineComment,
28
+ endOfLine: handleComments.handleEndOfLineComment,
29
+ remaining: handleComments.handleRemainingComment
30
+ },
31
+ getCommentChildNodes: handleComments.getCommentChildNodes
32
+ };
@@ -0,0 +1,119 @@
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
+ * @param {Comment} comment
12
+ * @returns {boolean}
13
+ */
14
+
15
+ function isBlockComment(comment) {
16
+ return comment.type === 'Block' || comment.type === 'CommentBlock' || // `meriyah`
17
+ comment.type === 'MultiLine';
18
+ }
19
+ /**
20
+ * @param {Comment} comment
21
+ * @returns {boolean}
22
+ */
23
+
24
+
25
+ function isLineComment(comment) {
26
+ return comment.type === 'Line' || comment.type === 'CommentLine' || // `meriyah` has `SingleLine`, `HashbangComment`, `HTMLOpen`, and `HTMLClose`
27
+ comment.type === 'SingleLine' || comment.type === 'HashbangComment' || comment.type === 'HTMLOpen' || comment.type === 'HTMLClose';
28
+ }
29
+ /**
30
+ * @param {Node} node
31
+ * @returns {boolean}
32
+ */
33
+
34
+
35
+ function isCallExpression(node) {
36
+ return node && (node.type === 'CallExpression' || node.type === 'OptionalCallExpression');
37
+ }
38
+ /**
39
+ * @param {Node} node
40
+ * @returns {boolean}
41
+ */
42
+
43
+
44
+ function isMemberExpression(node) {
45
+ return node && (node.type === 'MemberExpression' || node.type === 'OptionalMemberExpression');
46
+ }
47
+
48
+ const functionParametersCache = new WeakMap();
49
+
50
+ function getFunctionParameters(node) {
51
+ if (functionParametersCache.has(node)) {
52
+ return functionParametersCache.get(node);
53
+ }
54
+
55
+ const parameters = [];
56
+
57
+ if (node.this) {
58
+ parameters.push(node.this);
59
+ } // `params` vs `parameters` - see https://github.com/babel/babel/issues/9231
60
+
61
+
62
+ if (Array.isArray(node.parameters)) {
63
+ parameters.push(...node.parameters);
64
+ } else if (Array.isArray(node.params)) {
65
+ parameters.push(...node.params);
66
+ }
67
+
68
+ if (node.rest) {
69
+ parameters.push(node.rest);
70
+ }
71
+
72
+ functionParametersCache.set(node, parameters);
73
+ return parameters;
74
+ }
75
+
76
+ const callArgumentsCache = new WeakMap();
77
+
78
+ function getCallArguments(node) {
79
+ if (callArgumentsCache.has(node)) {
80
+ return callArgumentsCache.get(node);
81
+ }
82
+
83
+ let args = node.arguments;
84
+
85
+ if (node.type === 'ImportExpression') {
86
+ args = [node.source];
87
+
88
+ if (node.attributes) {
89
+ args.push(node.attributes);
90
+ }
91
+ }
92
+
93
+ callArgumentsCache.set(node, args);
94
+ return args;
95
+ }
96
+
97
+ function isPrettierIgnoreComment(comment) {
98
+ return comment.value.trim() === 'prettier-ignore' && !comment.unignore;
99
+ }
100
+
101
+ function isCallLikeExpression(node) {
102
+ return isCallExpression(node) || node.type === 'NewExpression' || node.type === 'ImportExpression';
103
+ }
104
+
105
+ function isObjectProperty(node) {
106
+ return node && (node.type === 'ObjectProperty' || node.type === 'Property' && !node.method && node.kind === 'init');
107
+ }
108
+
109
+ module.exports = {
110
+ getFunctionParameters,
111
+ getCallArguments,
112
+ isBlockComment,
113
+ isCallLikeExpression,
114
+ isLineComment,
115
+ isPrettierIgnoreComment,
116
+ isCallExpression,
117
+ isMemberExpression,
118
+ isObjectProperty
119
+ };
@@ -0,0 +1,440 @@
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 assert = require('assert');
12
+
13
+ const {
14
+ hasNewline,
15
+ addLeadingComment,
16
+ addDanglingComment,
17
+ addTrailingComment
18
+ } = require('../common/util.js');
19
+
20
+ const childNodesCache = new WeakMap();
21
+
22
+ function getSortedChildNodes(node, options, resultArray) {
23
+ if (!node) {
24
+ return;
25
+ }
26
+
27
+ const {
28
+ printer,
29
+ locStart,
30
+ locEnd
31
+ } = options;
32
+
33
+ if (resultArray) {
34
+ if (printer.canAttachComment && printer.canAttachComment(node)) {
35
+ // This reverse insertion sort almost always takes constant
36
+ // time because we almost always (maybe always?) append the
37
+ // nodes in order anyway.
38
+ let i;
39
+
40
+ for (i = resultArray.length - 1; i >= 0; --i) {
41
+ if (locStart(resultArray[i]) <= locStart(node) && locEnd(resultArray[i]) <= locEnd(node)) {
42
+ break;
43
+ }
44
+ }
45
+
46
+ resultArray.splice(i + 1, 0, node);
47
+ return;
48
+ }
49
+ } else if (childNodesCache.has(node)) {
50
+ return childNodesCache.get(node);
51
+ }
52
+
53
+ const childNodes = printer.getCommentChildNodes && printer.getCommentChildNodes(node, options) || typeof node === 'object' && Object.entries(node).filter(([key]) => key !== 'enclosingNode' && key !== 'precedingNode' && key !== 'followingNode' && key !== 'tokens' && key !== 'comments' && key !== 'parent').map(([, value]) => value);
54
+
55
+ if (!childNodes) {
56
+ return;
57
+ }
58
+
59
+ if (!resultArray) {
60
+ resultArray = [];
61
+ childNodesCache.set(node, resultArray);
62
+ }
63
+
64
+ for (const childNode of childNodes) {
65
+ getSortedChildNodes(childNode, options, resultArray);
66
+ }
67
+
68
+ return resultArray;
69
+ } // As efficiently as possible, decorate the comment object with
70
+ // .precedingNode, .enclosingNode, and/or .followingNode properties, at
71
+ // least one of which is guaranteed to be defined.
72
+
73
+
74
+ function decorateComment(node, comment, options, enclosingNode) {
75
+ const {
76
+ locStart,
77
+ locEnd
78
+ } = options;
79
+ const commentStart = locStart(comment);
80
+ const commentEnd = locEnd(comment);
81
+ const childNodes = getSortedChildNodes(node, options);
82
+ let precedingNode;
83
+ let followingNode; // Time to dust off the old binary search robes and wizard hat.
84
+
85
+ let left = 0;
86
+ let right = childNodes.length;
87
+
88
+ while (left < right) {
89
+ const middle = left + right >> 1;
90
+ const child = childNodes[middle];
91
+ const start = locStart(child);
92
+ const end = locEnd(child); // The comment is completely contained by this child node.
93
+
94
+ if (start <= commentStart && commentEnd <= end) {
95
+ // Abandon the binary search at this level.
96
+ return decorateComment(child, comment, options, child);
97
+ }
98
+
99
+ if (end <= commentStart) {
100
+ // This child node falls completely before the comment.
101
+ // Because we will never consider this node or any nodes
102
+ // before it again, this node must be the closest preceding
103
+ // node we have encountered so far.
104
+ precedingNode = child;
105
+ left = middle + 1;
106
+ continue;
107
+ }
108
+
109
+ if (commentEnd <= start) {
110
+ // This child node falls completely after the comment.
111
+ // Because we will never consider this node or any nodes after
112
+ // it again, this node must be the closest following node we
113
+ // have encountered so far.
114
+ followingNode = child;
115
+ right = middle;
116
+ continue;
117
+ }
118
+ /* istanbul ignore next */
119
+
120
+
121
+ throw new Error('Comment location overlaps with node location');
122
+ } // We don't want comments inside of different expressions inside of the same
123
+ // template literal to move to another expression.
124
+
125
+
126
+ if (enclosingNode && enclosingNode.type === 'TemplateLiteral') {
127
+ const {
128
+ quasis
129
+ } = enclosingNode;
130
+ const commentIndex = findExpressionIndexForComment(quasis, comment, options);
131
+
132
+ if (precedingNode && findExpressionIndexForComment(quasis, precedingNode, options) !== commentIndex) {
133
+ precedingNode = null;
134
+ }
135
+
136
+ if (followingNode && findExpressionIndexForComment(quasis, followingNode, options) !== commentIndex) {
137
+ followingNode = null;
138
+ }
139
+ }
140
+
141
+ return {
142
+ enclosingNode,
143
+ precedingNode,
144
+ followingNode
145
+ };
146
+ }
147
+
148
+ const returnFalse = () => false;
149
+
150
+ function attach(comments, ast, text, options) {
151
+ if (!Array.isArray(comments)) {
152
+ return;
153
+ }
154
+
155
+ const tiesToBreak = [];
156
+ const {
157
+ locStart,
158
+ locEnd,
159
+ printer: {
160
+ handleComments = {}
161
+ }
162
+ } = options; // TODO: Make this as default behavior
163
+
164
+ const {
165
+ avoidAstMutation,
166
+ ownLine: handleOwnLineComment = returnFalse,
167
+ endOfLine: handleEndOfLineComment = returnFalse,
168
+ remaining: handleRemainingComment = returnFalse
169
+ } = handleComments;
170
+ const decoratedComments = comments.map((comment, index) => ({ ...decorateComment(ast, comment, options),
171
+ comment,
172
+ text,
173
+ options,
174
+ ast,
175
+ isLastComment: comments.length - 1 === index
176
+ }));
177
+
178
+ for (const [index, context] of decoratedComments.entries()) {
179
+ const {
180
+ comment,
181
+ precedingNode,
182
+ enclosingNode,
183
+ followingNode,
184
+ text,
185
+ options,
186
+ ast,
187
+ isLastComment
188
+ } = context;
189
+
190
+ if (options.parser === 'json' || options.parser === 'json5' || options.parser === '__js_expression' || options.parser === '__vue_expression') {
191
+ if (locStart(comment) - locStart(ast) <= 0) {
192
+ addLeadingComment(ast, comment);
193
+ continue;
194
+ }
195
+
196
+ if (locEnd(comment) - locEnd(ast) >= 0) {
197
+ addTrailingComment(ast, comment);
198
+ continue;
199
+ }
200
+ }
201
+
202
+ let args;
203
+
204
+ if (avoidAstMutation) {
205
+ args = [context];
206
+ } else {
207
+ comment.enclosingNode = enclosingNode;
208
+ comment.precedingNode = precedingNode;
209
+ comment.followingNode = followingNode;
210
+ args = [comment, text, options, ast, isLastComment];
211
+ }
212
+
213
+ if (isOwnLineComment(text, options, decoratedComments, index)) {
214
+ comment.placement = 'ownLine'; // If a comment exists on its own line, prefer a leading comment.
215
+ // We also need to check if it's the first line of the file.
216
+
217
+ if (handleOwnLineComment(...args)) {// We're good
218
+ } else if (followingNode) {
219
+ // Always a leading comment.
220
+ addLeadingComment(followingNode, comment);
221
+ } else if (precedingNode) {
222
+ addTrailingComment(precedingNode, comment);
223
+ } else if (enclosingNode) {
224
+ addDanglingComment(enclosingNode, comment);
225
+ } else {
226
+ // There are no nodes, let's attach it to the root of the ast
227
+
228
+ /* istanbul ignore next */
229
+ addDanglingComment(ast, comment);
230
+ }
231
+ } else if (isEndOfLineComment(text, options, decoratedComments, index)) {
232
+ comment.placement = 'endOfLine';
233
+
234
+ if (handleEndOfLineComment(...args)) {// We're good
235
+ } else if (precedingNode) {
236
+ // There is content before this comment on the same line, but
237
+ // none after it, so prefer a trailing comment of the previous node.
238
+ addTrailingComment(precedingNode, comment);
239
+ } else if (followingNode) {
240
+ addLeadingComment(followingNode, comment);
241
+ } else if (enclosingNode) {
242
+ addDanglingComment(enclosingNode, comment);
243
+ } else {
244
+ // There are no nodes, let's attach it to the root of the ast
245
+
246
+ /* istanbul ignore next */
247
+ addDanglingComment(ast, comment);
248
+ }
249
+ } else {
250
+ comment.placement = 'remaining';
251
+
252
+ if (handleRemainingComment(...args)) {// We're good
253
+ } else if (precedingNode && followingNode) {
254
+ // Otherwise, text exists both before and after the comment on
255
+ // the same line. If there is both a preceding and following
256
+ // node, use a tie-breaking algorithm to determine if it should
257
+ // be attached to the next or previous node. In the last case,
258
+ // simply attach the right node;
259
+ const tieCount = tiesToBreak.length;
260
+
261
+ if (tieCount > 0) {
262
+ const lastTie = tiesToBreak[tieCount - 1];
263
+
264
+ if (lastTie.followingNode !== followingNode) {
265
+ breakTies(tiesToBreak, text, options);
266
+ }
267
+ }
268
+
269
+ tiesToBreak.push(context);
270
+ } else if (precedingNode) {
271
+ addTrailingComment(precedingNode, comment);
272
+ } else if (followingNode) {
273
+ addLeadingComment(followingNode, comment);
274
+ } else if (enclosingNode) {
275
+ addDanglingComment(enclosingNode, comment);
276
+ } else {
277
+ // There are no nodes, let's attach it to the root of the ast
278
+
279
+ /* istanbul ignore next */
280
+ addDanglingComment(ast, comment);
281
+ }
282
+ }
283
+ }
284
+
285
+ breakTies(tiesToBreak, text, options);
286
+
287
+ if (!avoidAstMutation) {
288
+ for (const comment of comments) {
289
+ // These node references were useful for breaking ties, but we
290
+ // don't need them anymore, and they create cycles in the AST that
291
+ // may lead to infinite recursion if we don't delete them here.
292
+ delete comment.precedingNode;
293
+ delete comment.enclosingNode;
294
+ delete comment.followingNode;
295
+ }
296
+ }
297
+ }
298
+
299
+ const isAllEmptyAndNoLineBreak = text => !/[\S\n\u2028\u2029]/.test(text);
300
+
301
+ function isOwnLineComment(text, options, decoratedComments, commentIndex) {
302
+ const {
303
+ comment,
304
+ precedingNode
305
+ } = decoratedComments[commentIndex];
306
+ const {
307
+ locStart,
308
+ locEnd
309
+ } = options;
310
+ let start = locStart(comment);
311
+
312
+ if (precedingNode) {
313
+ // Find first comment on the same line
314
+ for (let index = commentIndex - 1; index >= 0; index--) {
315
+ const {
316
+ comment,
317
+ precedingNode: currentCommentPrecedingNode
318
+ } = decoratedComments[index];
319
+
320
+ if (currentCommentPrecedingNode !== precedingNode || !isAllEmptyAndNoLineBreak(text.slice(locEnd(comment), start))) {
321
+ break;
322
+ }
323
+
324
+ start = locStart(comment);
325
+ }
326
+ }
327
+
328
+ return hasNewline(text, start, {
329
+ backwards: true
330
+ });
331
+ }
332
+
333
+ function isEndOfLineComment(text, options, decoratedComments, commentIndex) {
334
+ const {
335
+ comment,
336
+ followingNode
337
+ } = decoratedComments[commentIndex];
338
+ const {
339
+ locStart,
340
+ locEnd
341
+ } = options;
342
+ let end = locEnd(comment);
343
+
344
+ if (followingNode) {
345
+ // Find last comment on the same line
346
+ for (let index = commentIndex + 1; index < decoratedComments.length; index++) {
347
+ const {
348
+ comment,
349
+ followingNode: currentCommentFollowingNode
350
+ } = decoratedComments[index];
351
+
352
+ if (currentCommentFollowingNode !== followingNode || !isAllEmptyAndNoLineBreak(text.slice(end, locStart(comment)))) {
353
+ break;
354
+ }
355
+
356
+ end = locEnd(comment);
357
+ }
358
+ }
359
+
360
+ return hasNewline(text, end);
361
+ }
362
+
363
+ function breakTies(tiesToBreak, text, options) {
364
+ const tieCount = tiesToBreak.length;
365
+
366
+ if (tieCount === 0) {
367
+ return;
368
+ }
369
+
370
+ const {
371
+ precedingNode,
372
+ followingNode,
373
+ enclosingNode
374
+ } = tiesToBreak[0];
375
+ const gapRegExp = options.printer.getGapRegex && options.printer.getGapRegex(enclosingNode) || /^[\s(]*$/;
376
+ let gapEndPos = options.locStart(followingNode); // Iterate backwards through tiesToBreak, examining the gaps
377
+ // between the tied comments. In order to qualify as leading, a
378
+ // comment must be separated from followingNode by an unbroken series of
379
+ // gaps (or other comments). Gaps should only contain whitespace or open
380
+ // parentheses.
381
+
382
+ let indexOfFirstLeadingComment;
383
+
384
+ for (indexOfFirstLeadingComment = tieCount; indexOfFirstLeadingComment > 0; --indexOfFirstLeadingComment) {
385
+ const {
386
+ comment,
387
+ precedingNode: currentCommentPrecedingNode,
388
+ followingNode: currentCommentFollowingNode
389
+ } = tiesToBreak[indexOfFirstLeadingComment - 1];
390
+ assert.strictEqual(currentCommentPrecedingNode, precedingNode);
391
+ assert.strictEqual(currentCommentFollowingNode, followingNode);
392
+ const gap = text.slice(options.locEnd(comment), gapEndPos);
393
+
394
+ if (gapRegExp.test(gap)) {
395
+ gapEndPos = options.locStart(comment);
396
+ } else {
397
+ // The gap string contained something other than whitespace or open
398
+ // parentheses.
399
+ break;
400
+ }
401
+ }
402
+
403
+ for (const [i, {
404
+ comment
405
+ }] of tiesToBreak.entries()) {
406
+ if (i < indexOfFirstLeadingComment) {
407
+ addTrailingComment(precedingNode, comment);
408
+ } else {
409
+ addLeadingComment(followingNode, comment);
410
+ }
411
+ }
412
+
413
+ for (const node of [precedingNode, followingNode]) {
414
+ if (node.comments && node.comments.length > 1) {
415
+ node.comments.sort((a, b) => options.locStart(a) - options.locStart(b));
416
+ }
417
+ }
418
+
419
+ tiesToBreak.length = 0;
420
+ }
421
+
422
+ function findExpressionIndexForComment(quasis, comment, options) {
423
+ const startPos = options.locStart(comment) - 1;
424
+
425
+ for (let i = 1; i < quasis.length; ++i) {
426
+ if (startPos < options.locStart(quasis[i])) {
427
+ return i - 1;
428
+ }
429
+ } // We haven't found it, it probably means that some of the locations are off.
430
+ // Let's just return the first one.
431
+
432
+ /* istanbul ignore next */
433
+
434
+
435
+ return 0;
436
+ }
437
+
438
+ module.exports = {
439
+ attach
440
+ };
@@ -0,0 +1,13 @@
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 getLast = arr => arr[arr.length - 1];
12
+
13
+ module.exports = getLast;
@@ -0,0 +1,43 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.createAddCommentsMutation = createAddCommentsMutation;
7
+ exports.performAddCommentsMutation = performAddCommentsMutation;
8
+
9
+ var _comments = require("../comments/comments");
10
+
11
+ /**
12
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
13
+ *
14
+ * This source code is licensed under the MIT license found in the
15
+ * LICENSE file in the root directory of this source tree.
16
+ *
17
+ *
18
+ * @format
19
+ */
20
+ function createAddCommentsMutation(node, comments) {
21
+ if (comments.length === 0) {
22
+ return null;
23
+ }
24
+
25
+ return {
26
+ type: 'addComments',
27
+ comments,
28
+ node
29
+ };
30
+ }
31
+
32
+ function performAddCommentsMutation(mutationContext, mutation) {
33
+ for (const {
34
+ comment: originalComment,
35
+ placement
36
+ } of mutation.comments) {
37
+ const comment = (0, _comments.cloneComment)(originalComment);
38
+ mutationContext.appendCommentToSource(comment, placement);
39
+ (0, _comments.addComment)(mutation.node, comment, placement);
40
+ }
41
+
42
+ return null;
43
+ }