hermes-transform 0.11.0 → 0.12.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.
- package/dist/detachedNode.js.flow +1 -0
- package/dist/generated/node-types.js +5 -4
- package/dist/generated/node-types.js.flow +9 -7
- package/dist/transform/TransformContext.js +4 -0
- package/dist/transform/TransformContext.js.flow +4 -0
- package/dist/transform/mutations/RemoveNode.js +12 -1
- package/dist/transform/mutations/RemoveNode.js.flow +19 -0
- package/dist/transform/parse.js +3 -2
- package/dist/transform/parse.js.flow +2 -1
- package/dist/transform/print.js +52 -102
- package/dist/transform/print.js.flow +58 -120
- package/dist/transform/transform.js +2 -2
- package/dist/transform/transform.js.flow +3 -3
- package/package.json +5 -5
- package/dist/transform/comments/prettier/common/util.js.flow +0 -349
- package/dist/transform/comments/prettier/language-js/comments.js.flow +0 -950
- package/dist/transform/comments/prettier/language-js/loc.js.flow +0 -41
- package/dist/transform/comments/prettier/language-js/printer-estree.js.flow +0 -37
- package/dist/transform/comments/prettier/language-js/utils.js.flow +0 -135
- package/dist/transform/comments/prettier/main/comments.js.flow +0 -437
- package/dist/transform/comments/prettier/utils/get-last.js.flow +0 -14
|
@@ -11,9 +11,9 @@
|
|
|
11
11
|
'use strict';
|
|
12
12
|
|
|
13
13
|
import type {MaybeDetachedNode} from '../detachedNode';
|
|
14
|
-
import type {Program
|
|
14
|
+
import type {Program} from 'hermes-estree';
|
|
15
15
|
|
|
16
|
-
import {
|
|
16
|
+
import {mutateESTreeASTForPrettier} from 'hermes-parser';
|
|
17
17
|
import * as prettier from 'prettier';
|
|
18
18
|
import {
|
|
19
19
|
addCommentsToNode,
|
|
@@ -21,12 +21,12 @@ import {
|
|
|
21
21
|
} from './comments/comments';
|
|
22
22
|
import type {VisitorKeysType} from 'hermes-parser';
|
|
23
23
|
|
|
24
|
-
export function print(
|
|
24
|
+
export async function print(
|
|
25
25
|
ast: MaybeDetachedNode<Program>,
|
|
26
26
|
originalCode: string,
|
|
27
27
|
prettierOptions: {...} = {},
|
|
28
28
|
visitorKeys?: ?VisitorKeysType,
|
|
29
|
-
): string {
|
|
29
|
+
): Promise<string> {
|
|
30
30
|
// $FlowExpectedError[incompatible-type] This is now safe to access.
|
|
31
31
|
const program: Program = ast;
|
|
32
32
|
|
|
@@ -44,7 +44,7 @@ export function print(
|
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
// Fix up the AST to match what prettier expects.
|
|
47
|
-
|
|
47
|
+
mutateESTreeASTForPrettier(program, visitorKeys);
|
|
48
48
|
|
|
49
49
|
// we need to delete the comments prop or else prettier will do
|
|
50
50
|
// its own attachment pass after the mutation and duplicate the
|
|
@@ -52,125 +52,63 @@ export function print(
|
|
|
52
52
|
// $FlowExpectedError[cannot-write]
|
|
53
53
|
delete program.comments;
|
|
54
54
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
55
|
+
switch (getPrettierMajorVersion()) {
|
|
56
|
+
case '3': {
|
|
57
|
+
// Lazy require this module as it only exists in prettier v3.
|
|
58
|
+
const prettierFlowPlugin = require('prettier/plugins/flow');
|
|
59
|
+
return prettier.format(
|
|
60
|
+
originalCode,
|
|
61
|
+
// $FlowExpectedError[incompatible-exact] - we don't want to create a dependency on the prettier types
|
|
62
|
+
{
|
|
63
|
+
...prettierOptions,
|
|
64
|
+
parser: 'flow',
|
|
65
|
+
requirePragma: false,
|
|
66
|
+
plugins: [
|
|
67
|
+
{
|
|
68
|
+
parsers: {
|
|
69
|
+
flow: {
|
|
70
|
+
...prettierFlowPlugin.parsers.flow,
|
|
71
|
+
parse() {
|
|
72
|
+
return program;
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
],
|
|
78
|
+
},
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
case '2': {
|
|
82
|
+
return prettier.format(
|
|
83
|
+
originalCode,
|
|
84
|
+
// $FlowExpectedError[incompatible-exact] - we don't want to create a dependency on the prettier types
|
|
85
|
+
{
|
|
86
|
+
...prettierOptions,
|
|
87
|
+
parser() {
|
|
88
|
+
return program;
|
|
89
|
+
},
|
|
90
|
+
},
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
case 'UNSUPPORTED':
|
|
94
|
+
default: {
|
|
95
|
+
throw new Error(
|
|
96
|
+
`Unknown or unsupported prettier version of "${prettier.version}". Only major versions 3 or 2 of prettier are supported.`,
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
65
100
|
}
|
|
66
101
|
|
|
67
|
-
function
|
|
68
|
-
|
|
69
|
-
visitorKeys: ?VisitorKeysType,
|
|
70
|
-
): void {
|
|
71
|
-
SimpleTraverser.traverse(rootNode, {
|
|
72
|
-
enter(node) {
|
|
73
|
-
// prettier fully expects the parent pointers are NOT set and
|
|
74
|
-
// certain cases can crash due to prettier infinite-looping
|
|
75
|
-
// whilst naively traversing the parent property
|
|
76
|
-
// https://github.com/prettier/prettier/issues/11793
|
|
77
|
-
// $FlowExpectedError[cannot-write]
|
|
78
|
-
delete node.parent;
|
|
79
|
-
|
|
80
|
-
// prettier currently relies on the AST being in the old-school, deprecated AST format for optional chaining
|
|
81
|
-
// so we have to apply their transform to our AST so it can actually format it.
|
|
82
|
-
if (node.type === 'ChainExpression') {
|
|
83
|
-
const newNode = transformChainExpression(node.expression);
|
|
84
|
-
|
|
85
|
-
// Clear out existing properties
|
|
86
|
-
for (const k of Object.keys(node)) {
|
|
87
|
-
// $FlowExpectedError[prop-missing]
|
|
88
|
-
delete node[k];
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
// Traverse `newNode` and its children.
|
|
92
|
-
mutateASTForPrettier(newNode, visitorKeys);
|
|
102
|
+
function getPrettierMajorVersion(): '3' | '2' | 'UNSUPPORTED' {
|
|
103
|
+
const {version} = prettier;
|
|
93
104
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
Object.assign(node, newNode);
|
|
98
|
-
|
|
99
|
-
// Skip traversing the existing nodes since we are replacing them.
|
|
100
|
-
throw SimpleTraverser.Skip;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
// Prettier currently relies on comparing the `node` vs `node.value` start positions to know if an
|
|
104
|
-
// `ObjectTypeProperty` is a method or not (instead of using the `node.method` boolean). To correctly print
|
|
105
|
-
// the node when its not a method we need the start position to be different from the `node.value`s start
|
|
106
|
-
// position.
|
|
107
|
-
if (node.type === 'ObjectTypeProperty') {
|
|
108
|
-
if (
|
|
109
|
-
node.method === false &&
|
|
110
|
-
node.kind === 'init' &&
|
|
111
|
-
node.range[0] === 1 &&
|
|
112
|
-
node.value.range[0] === 1
|
|
113
|
-
) {
|
|
114
|
-
// $FlowExpectedError[cannot-write]
|
|
115
|
-
// $FlowExpectedError[cannot-spread-interface]
|
|
116
|
-
node.value = {
|
|
117
|
-
...node.value,
|
|
118
|
-
range: [2, node.value.range[1]],
|
|
119
|
-
};
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
// Prettier currently relies on comparing the the start positions to know if the import/export specifier should have a
|
|
124
|
-
// rename (eg `Name` vs `Name as Name`) when the name is exactly the same
|
|
125
|
-
// So we need to ensure that the range is always the same to avoid the useless code printing
|
|
126
|
-
if (node.type === 'ImportSpecifier') {
|
|
127
|
-
if (node.local.name === node.imported.name) {
|
|
128
|
-
if (node.local.range == null) {
|
|
129
|
-
// for our TS-ast printing which has no locs
|
|
130
|
-
// $FlowExpectedError[cannot-write]
|
|
131
|
-
node.local.range = [0, 0];
|
|
132
|
-
}
|
|
133
|
-
// $FlowExpectedError[cannot-write]
|
|
134
|
-
node.imported.range = [...node.local.range];
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
if (node.type === 'ExportSpecifier') {
|
|
138
|
-
if (node.local.name === node.exported.name) {
|
|
139
|
-
if (node.local.range == null) {
|
|
140
|
-
// for our TS-ast printing which has no locs
|
|
141
|
-
// $FlowExpectedError[cannot-write]
|
|
142
|
-
node.local.range = [0, 0];
|
|
143
|
-
}
|
|
144
|
-
// $FlowExpectedError[cannot-write]
|
|
145
|
-
node.exported.range = [...node.local.range];
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
},
|
|
149
|
-
leave() {},
|
|
150
|
-
visitorKeys,
|
|
151
|
-
});
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
// https://github.com/prettier/prettier/blob/d962466a828f8ef51435e3e8840178d90b7ec6cd/src/language-js/parse/postprocess/index.js#L161-L182
|
|
155
|
-
function transformChainExpression(node: ESNode): ESNode {
|
|
156
|
-
switch (node.type) {
|
|
157
|
-
case 'CallExpression':
|
|
158
|
-
// $FlowExpectedError[cannot-spread-interface]
|
|
159
|
-
return {
|
|
160
|
-
...node,
|
|
161
|
-
type: 'OptionalCallExpression',
|
|
162
|
-
callee: transformChainExpression(node.callee),
|
|
163
|
-
};
|
|
105
|
+
if (version.startsWith('3.')) {
|
|
106
|
+
return '3';
|
|
107
|
+
}
|
|
164
108
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
return {
|
|
168
|
-
...node,
|
|
169
|
-
type: 'OptionalMemberExpression',
|
|
170
|
-
object: transformChainExpression(node.object),
|
|
171
|
-
};
|
|
172
|
-
// No default
|
|
109
|
+
if (version.startsWith('2.')) {
|
|
110
|
+
return '2';
|
|
173
111
|
}
|
|
174
112
|
|
|
175
|
-
return
|
|
113
|
+
return 'UNSUPPORTED';
|
|
176
114
|
}
|
|
@@ -20,8 +20,8 @@ var _parse = require("./parse");
|
|
|
20
20
|
|
|
21
21
|
var _print = require("./print");
|
|
22
22
|
|
|
23
|
-
function transform(originalCode, visitors, prettierOptions = {}) {
|
|
24
|
-
const parseResult = (0, _parse.parse)(originalCode);
|
|
23
|
+
async function transform(originalCode, visitors, prettierOptions = {}) {
|
|
24
|
+
const parseResult = await (0, _parse.parse)(originalCode);
|
|
25
25
|
const {
|
|
26
26
|
ast,
|
|
27
27
|
astWasMutated,
|
|
@@ -19,12 +19,12 @@ import {print} from './print';
|
|
|
19
19
|
|
|
20
20
|
export type TransformVisitor = Visitor<TransformContextAdditions>;
|
|
21
21
|
|
|
22
|
-
export function transform(
|
|
22
|
+
export async function transform(
|
|
23
23
|
originalCode: string,
|
|
24
24
|
visitors: TransformVisitor,
|
|
25
25
|
prettierOptions: {...} = {},
|
|
26
|
-
): string {
|
|
27
|
-
const parseResult = parse(originalCode);
|
|
26
|
+
): Promise<string> {
|
|
27
|
+
const parseResult = await parse(originalCode);
|
|
28
28
|
|
|
29
29
|
const {ast, astWasMutated, mutatedCode} = transformAST(parseResult, visitors);
|
|
30
30
|
if (!astWasMutated) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hermes-transform",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.0",
|
|
4
4
|
"description": "Tools built on top of Hermes-ESTree to enable codebase transformation",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"license": "MIT",
|
|
@@ -12,12 +12,12 @@
|
|
|
12
12
|
"@babel/code-frame": "^7.16.0",
|
|
13
13
|
"esquery": "^1.4.0",
|
|
14
14
|
"flow-enums-runtime": "^0.0.6",
|
|
15
|
-
"hermes-eslint": "0.
|
|
16
|
-
"hermes-estree": "0.
|
|
17
|
-
"hermes-parser": "0.
|
|
15
|
+
"hermes-eslint": "0.12.0",
|
|
16
|
+
"hermes-estree": "0.12.0",
|
|
17
|
+
"hermes-parser": "0.12.0"
|
|
18
18
|
},
|
|
19
19
|
"peerDependencies": {
|
|
20
|
-
"prettier": "^2.7.1"
|
|
20
|
+
"prettier": "^3.0.0 || ^2.7.1"
|
|
21
21
|
},
|
|
22
22
|
"files": [
|
|
23
23
|
"dist",
|
|
@@ -1,349 +0,0 @@
|
|
|
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
|
-
|
|
10
|
-
'use strict';
|
|
11
|
-
|
|
12
|
-
const stringWidth = require('string-width');
|
|
13
|
-
const getLast = require('../utils/get-last.js');
|
|
14
|
-
|
|
15
|
-
const notAsciiRegex = /[^\x20-\x7F]/;
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* @typedef {{backwards?: boolean}} SkipOptions
|
|
19
|
-
*/
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* @param {string | RegExp} chars
|
|
23
|
-
* @returns {(text: string, index: number | false, opts?: SkipOptions) => number | false}
|
|
24
|
-
*/
|
|
25
|
-
function skip(chars) {
|
|
26
|
-
return (text, index, opts) => {
|
|
27
|
-
const backwards = opts && opts.backwards;
|
|
28
|
-
|
|
29
|
-
// Allow `skip` functions to be threaded together without having
|
|
30
|
-
// to check for failures (did someone say monads?).
|
|
31
|
-
/* istanbul ignore next */
|
|
32
|
-
if (index === false) {
|
|
33
|
-
return false;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
const {length} = text;
|
|
37
|
-
let cursor = index;
|
|
38
|
-
while (cursor >= 0 && cursor < length) {
|
|
39
|
-
const c = text.charAt(cursor);
|
|
40
|
-
if (chars instanceof RegExp) {
|
|
41
|
-
if (!chars.test(c)) {
|
|
42
|
-
return cursor;
|
|
43
|
-
}
|
|
44
|
-
} else if (!chars.includes(c)) {
|
|
45
|
-
return cursor;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
backwards ? cursor-- : cursor++;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
if (cursor === -1 || cursor === length) {
|
|
52
|
-
// If we reached the beginning or end of the file, return the
|
|
53
|
-
// out-of-bounds cursor. It's up to the caller to handle this
|
|
54
|
-
// correctly. We don't want to indicate `false` though if it
|
|
55
|
-
// actually skipped valid characters.
|
|
56
|
-
return cursor;
|
|
57
|
-
}
|
|
58
|
-
return false;
|
|
59
|
-
};
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* @type {(text: string, index: number | false, opts?: SkipOptions) => number | false}
|
|
64
|
-
*/
|
|
65
|
-
const skipWhitespace = skip(/\s/);
|
|
66
|
-
/**
|
|
67
|
-
* @type {(text: string, index: number | false, opts?: SkipOptions) => number | false}
|
|
68
|
-
*/
|
|
69
|
-
const skipSpaces = skip(' \t');
|
|
70
|
-
/**
|
|
71
|
-
* @type {(text: string, index: number | false, opts?: SkipOptions) => number | false}
|
|
72
|
-
*/
|
|
73
|
-
const skipToLineEnd = skip(',; \t');
|
|
74
|
-
/**
|
|
75
|
-
* @type {(text: string, index: number | false, opts?: SkipOptions) => number | false}
|
|
76
|
-
*/
|
|
77
|
-
const skipEverythingButNewLine = skip(/[^\n\r]/);
|
|
78
|
-
|
|
79
|
-
/**
|
|
80
|
-
* @param {string} text
|
|
81
|
-
* @param {number | false} index
|
|
82
|
-
* @returns {number | false}
|
|
83
|
-
*/
|
|
84
|
-
function skipInlineComment(text, index) {
|
|
85
|
-
/* istanbul ignore next */
|
|
86
|
-
if (index === false) {
|
|
87
|
-
return false;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
if (text.charAt(index) === '/' && text.charAt(index + 1) === '*') {
|
|
91
|
-
for (let i = index + 2; i < text.length; ++i) {
|
|
92
|
-
if (text.charAt(i) === '*' && text.charAt(i + 1) === '/') {
|
|
93
|
-
return i + 2;
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
return index;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
/**
|
|
101
|
-
* @param {string} text
|
|
102
|
-
* @param {number | false} index
|
|
103
|
-
* @returns {number | false}
|
|
104
|
-
*/
|
|
105
|
-
function skipTrailingComment(text, index) {
|
|
106
|
-
/* istanbul ignore next */
|
|
107
|
-
if (index === false) {
|
|
108
|
-
return false;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
if (text.charAt(index) === '/' && text.charAt(index + 1) === '/') {
|
|
112
|
-
return skipEverythingButNewLine(text, index);
|
|
113
|
-
}
|
|
114
|
-
return index;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
// This one doesn't use the above helper function because it wants to
|
|
118
|
-
// test \r\n in order and `skip` doesn't support ordering and we only
|
|
119
|
-
// want to skip one newline. It's simple to implement.
|
|
120
|
-
/**
|
|
121
|
-
* @param {string} text
|
|
122
|
-
* @param {number | false} index
|
|
123
|
-
* @param {SkipOptions=} opts
|
|
124
|
-
* @returns {number | false}
|
|
125
|
-
*/
|
|
126
|
-
function skipNewline(text, index, opts) {
|
|
127
|
-
const backwards = opts && opts.backwards;
|
|
128
|
-
if (index === false) {
|
|
129
|
-
return false;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
const atIndex = text.charAt(index);
|
|
133
|
-
if (backwards) {
|
|
134
|
-
// We already replace `\r\n` with `\n` before parsing
|
|
135
|
-
/* istanbul ignore next */
|
|
136
|
-
if (text.charAt(index - 1) === '\r' && atIndex === '\n') {
|
|
137
|
-
return index - 2;
|
|
138
|
-
}
|
|
139
|
-
if (
|
|
140
|
-
atIndex === '\n' ||
|
|
141
|
-
atIndex === '\r' ||
|
|
142
|
-
atIndex === '\u2028' ||
|
|
143
|
-
atIndex === '\u2029'
|
|
144
|
-
) {
|
|
145
|
-
return index - 1;
|
|
146
|
-
}
|
|
147
|
-
} else {
|
|
148
|
-
// We already replace `\r\n` with `\n` before parsing
|
|
149
|
-
/* istanbul ignore next */
|
|
150
|
-
if (atIndex === '\r' && text.charAt(index + 1) === '\n') {
|
|
151
|
-
return index + 2;
|
|
152
|
-
}
|
|
153
|
-
if (
|
|
154
|
-
atIndex === '\n' ||
|
|
155
|
-
atIndex === '\r' ||
|
|
156
|
-
atIndex === '\u2028' ||
|
|
157
|
-
atIndex === '\u2029'
|
|
158
|
-
) {
|
|
159
|
-
return index + 1;
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
return index;
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
/**
|
|
167
|
-
* @param {string} text
|
|
168
|
-
* @param {number} index
|
|
169
|
-
* @param {SkipOptions=} opts
|
|
170
|
-
* @returns {boolean}
|
|
171
|
-
*/
|
|
172
|
-
function hasNewline(text, index, opts = {}) {
|
|
173
|
-
const idx = skipSpaces(text, opts.backwards ? index - 1 : index, opts);
|
|
174
|
-
const idx2 = skipNewline(text, idx, opts);
|
|
175
|
-
return idx !== idx2;
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
/**
|
|
179
|
-
* @param {string} text
|
|
180
|
-
* @param {number} start
|
|
181
|
-
* @param {number} end
|
|
182
|
-
* @returns {boolean}
|
|
183
|
-
*/
|
|
184
|
-
function hasNewlineInRange(text, start, end) {
|
|
185
|
-
for (let i = start; i < end; ++i) {
|
|
186
|
-
if (text.charAt(i) === '\n') {
|
|
187
|
-
return true;
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
return false;
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
/**
|
|
194
|
-
* @param {string} text
|
|
195
|
-
* @param {number} index
|
|
196
|
-
* @returns {boolean}
|
|
197
|
-
*/
|
|
198
|
-
function isNextLineEmptyAfterIndex(text, index) {
|
|
199
|
-
/** @type {number | false} */
|
|
200
|
-
let oldIdx = null;
|
|
201
|
-
/** @type {number | false} */
|
|
202
|
-
let idx = index;
|
|
203
|
-
while (idx !== oldIdx) {
|
|
204
|
-
// We need to skip all the potential trailing inline comments
|
|
205
|
-
oldIdx = idx;
|
|
206
|
-
idx = skipToLineEnd(text, idx);
|
|
207
|
-
idx = skipInlineComment(text, idx);
|
|
208
|
-
idx = skipSpaces(text, idx);
|
|
209
|
-
}
|
|
210
|
-
idx = skipTrailingComment(text, idx);
|
|
211
|
-
idx = skipNewline(text, idx);
|
|
212
|
-
return idx !== false && hasNewline(text, idx);
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
/**
|
|
216
|
-
* @param {string} text
|
|
217
|
-
* @param {number} idx
|
|
218
|
-
* @returns {number | false}
|
|
219
|
-
*/
|
|
220
|
-
function getNextNonSpaceNonCommentCharacterIndexWithStartIndex(text, idx) {
|
|
221
|
-
/** @type {number | false} */
|
|
222
|
-
let oldIdx = null;
|
|
223
|
-
/** @type {number | false} */
|
|
224
|
-
let nextIdx = idx;
|
|
225
|
-
while (nextIdx !== oldIdx) {
|
|
226
|
-
oldIdx = nextIdx;
|
|
227
|
-
nextIdx = skipSpaces(text, nextIdx);
|
|
228
|
-
nextIdx = skipInlineComment(text, nextIdx);
|
|
229
|
-
nextIdx = skipTrailingComment(text, nextIdx);
|
|
230
|
-
nextIdx = skipNewline(text, nextIdx);
|
|
231
|
-
}
|
|
232
|
-
return nextIdx;
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
/**
|
|
236
|
-
* @template N
|
|
237
|
-
* @param {string} text
|
|
238
|
-
* @param {N} node
|
|
239
|
-
* @param {(node: N) => number} locEnd
|
|
240
|
-
* @returns {number | false}
|
|
241
|
-
*/
|
|
242
|
-
function getNextNonSpaceNonCommentCharacterIndex(text, node, locEnd) {
|
|
243
|
-
return getNextNonSpaceNonCommentCharacterIndexWithStartIndex(
|
|
244
|
-
text,
|
|
245
|
-
locEnd(node),
|
|
246
|
-
);
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
/**
|
|
250
|
-
* @template N
|
|
251
|
-
* @param {string} text
|
|
252
|
-
* @param {N} node
|
|
253
|
-
* @param {(node: N) => number} locEnd
|
|
254
|
-
* @returns {string}
|
|
255
|
-
*/
|
|
256
|
-
function getNextNonSpaceNonCommentCharacter(text, node, locEnd) {
|
|
257
|
-
return text.charAt(
|
|
258
|
-
// @ts-expect-error => TBD: can return false, should we define a fallback?
|
|
259
|
-
getNextNonSpaceNonCommentCharacterIndex(text, node, locEnd),
|
|
260
|
-
);
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
/**
|
|
264
|
-
* @param {string} text
|
|
265
|
-
* @returns {number}
|
|
266
|
-
*/
|
|
267
|
-
function getStringWidth(text) {
|
|
268
|
-
if (!text) {
|
|
269
|
-
return 0;
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
// shortcut to avoid needless string `RegExp`s, replacements, and allocations within `string-width`
|
|
273
|
-
if (!notAsciiRegex.test(text)) {
|
|
274
|
-
return text.length;
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
return stringWidth(text);
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
function addCommentHelper(node, comment) {
|
|
281
|
-
const comments = node.comments || (node.comments = []);
|
|
282
|
-
comments.push(comment);
|
|
283
|
-
comment.printed = false;
|
|
284
|
-
comment.nodeDescription = describeNodeForDebugging(node);
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
function addLeadingComment(node, comment) {
|
|
288
|
-
comment.leading = true;
|
|
289
|
-
comment.trailing = false;
|
|
290
|
-
addCommentHelper(node, comment);
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
function addDanglingComment(node, comment, marker) {
|
|
294
|
-
comment.leading = false;
|
|
295
|
-
comment.trailing = false;
|
|
296
|
-
if (marker) {
|
|
297
|
-
comment.marker = marker;
|
|
298
|
-
}
|
|
299
|
-
addCommentHelper(node, comment);
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
function addTrailingComment(node, comment) {
|
|
303
|
-
comment.leading = false;
|
|
304
|
-
comment.trailing = true;
|
|
305
|
-
addCommentHelper(node, comment);
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
/**
|
|
309
|
-
* @param {any} object
|
|
310
|
-
* @returns {object is Array<any>}
|
|
311
|
-
*/
|
|
312
|
-
function isNonEmptyArray(object) {
|
|
313
|
-
return Array.isArray(object) && object.length > 0;
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
function describeNodeForDebugging(node) {
|
|
317
|
-
const nodeType = node.type || node.kind || '(unknown type)';
|
|
318
|
-
let nodeName = String(
|
|
319
|
-
node.name ||
|
|
320
|
-
(node.id && (typeof node.id === 'object' ? node.id.name : node.id)) ||
|
|
321
|
-
(node.key && (typeof node.key === 'object' ? node.key.name : node.key)) ||
|
|
322
|
-
(node.value &&
|
|
323
|
-
(typeof node.value === 'object' ? '' : String(node.value))) ||
|
|
324
|
-
node.operator ||
|
|
325
|
-
'',
|
|
326
|
-
);
|
|
327
|
-
if (nodeName.length > 20) {
|
|
328
|
-
nodeName = nodeName.slice(0, 19) + '…';
|
|
329
|
-
}
|
|
330
|
-
return nodeType + (nodeName ? ' ' + nodeName : '');
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
module.exports = {
|
|
334
|
-
getStringWidth,
|
|
335
|
-
getLast,
|
|
336
|
-
getNextNonSpaceNonCommentCharacterIndexWithStartIndex,
|
|
337
|
-
getNextNonSpaceNonCommentCharacterIndex,
|
|
338
|
-
getNextNonSpaceNonCommentCharacter,
|
|
339
|
-
skipWhitespace,
|
|
340
|
-
skipSpaces,
|
|
341
|
-
skipNewline,
|
|
342
|
-
isNextLineEmptyAfterIndex,
|
|
343
|
-
hasNewline,
|
|
344
|
-
hasNewlineInRange,
|
|
345
|
-
addLeadingComment,
|
|
346
|
-
addDanglingComment,
|
|
347
|
-
addTrailingComment,
|
|
348
|
-
isNonEmptyArray,
|
|
349
|
-
};
|