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,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
+ };