hermes-transform 0.6.0 → 0.7.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/index.js.flow +2 -1
- package/dist/transform/MutationContext.js +2 -2
- package/dist/transform/MutationContext.js.flow +3 -2
- package/dist/transform/TransformContext.js +22 -32
- package/dist/transform/TransformContext.js.flow +72 -70
- package/dist/transform/comments/comments.js +112 -25
- package/dist/transform/comments/comments.js.flow +105 -28
- package/dist/transform/getTransformedAST.js +5 -12
- package/dist/transform/getTransformedAST.js.flow +13 -13
- package/dist/transform/mutations/{AddLeadingComments.js → AddComments.js} +11 -8
- package/dist/transform/mutations/AddComments.js.flow +50 -0
- package/dist/transform/mutations/CloneCommentsTo.js +1 -2
- package/dist/transform/mutations/CloneCommentsTo.js.flow +1 -2
- package/dist/transform/mutations/InsertStatement.js +1 -1
- package/dist/transform/mutations/InsertStatement.js.flow +1 -1
- package/dist/transform/mutations/utils/arrayUtils.js +14 -0
- package/dist/transform/mutations/utils/arrayUtils.js.flow +15 -0
- package/dist/transform/transform.js.flow +2 -2
- package/dist/traverse/traverse.js +27 -3
- package/dist/traverse/traverse.js.flow +62 -9
- package/package.json +4 -3
- package/dist/transform/mutations/AddLeadingComments.js.flow +0 -49
- package/dist/transform/mutations/AddTrailingComments.js +0 -40
- package/dist/transform/mutations/AddTrailingComments.js.flow +0 -49
|
@@ -23,9 +23,17 @@ import {
|
|
|
23
23
|
// $FlowExpectedError[untyped-import]
|
|
24
24
|
addTrailingComment as untypedAddTrailingComment,
|
|
25
25
|
} from './prettier/common/util';
|
|
26
|
+
import {EOL} from 'os';
|
|
26
27
|
|
|
27
28
|
export type Options = $ReadOnly<{}>;
|
|
28
29
|
|
|
30
|
+
export enum CommentPlacement {
|
|
31
|
+
LEADING_OWN_LINE,
|
|
32
|
+
LEADING_INLINE,
|
|
33
|
+
TRAILING_OWN_LINE,
|
|
34
|
+
TRAILING_INLINE,
|
|
35
|
+
}
|
|
36
|
+
|
|
29
37
|
export function attachComments(
|
|
30
38
|
comments: $ReadOnlyArray<Comment>,
|
|
31
39
|
ast: Program,
|
|
@@ -80,18 +88,23 @@ export function isTrailingComment(comment: Comment): boolean {
|
|
|
80
88
|
return comment.trailing === true;
|
|
81
89
|
}
|
|
82
90
|
|
|
83
|
-
export function
|
|
84
|
-
node: ESNode | DetachedNode<ESNode>,
|
|
85
|
-
comment: Comment,
|
|
86
|
-
): void {
|
|
87
|
-
untypedAddLeadingComment(node, comment);
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
export function addTrailingComment(
|
|
91
|
+
export function addComment(
|
|
91
92
|
node: ESNode | DetachedNode<ESNode>,
|
|
92
93
|
comment: Comment,
|
|
94
|
+
placement: CommentPlacement,
|
|
93
95
|
): void {
|
|
94
|
-
|
|
96
|
+
switch (placement) {
|
|
97
|
+
case CommentPlacement.LEADING_OWN_LINE:
|
|
98
|
+
case CommentPlacement.LEADING_INLINE: {
|
|
99
|
+
untypedAddLeadingComment(node, comment);
|
|
100
|
+
break;
|
|
101
|
+
}
|
|
102
|
+
case CommentPlacement.TRAILING_OWN_LINE:
|
|
103
|
+
case CommentPlacement.TRAILING_INLINE: {
|
|
104
|
+
untypedAddTrailingComment(node, comment);
|
|
105
|
+
break;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
95
108
|
}
|
|
96
109
|
|
|
97
110
|
export function cloneComment<T: Comment>(comment: T): T {
|
|
@@ -116,30 +129,94 @@ export function cloneCommentWithMarkers<T: Comment>(comment: T): T {
|
|
|
116
129
|
};
|
|
117
130
|
}
|
|
118
131
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
132
|
+
function getFirstNewlineIndex(code: string): number {
|
|
133
|
+
return code.search(/\r\n|\n|\r/);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function getFirstNonWhitespaceIndex(code: string): number {
|
|
137
|
+
return code.search(/\S/);
|
|
138
|
+
}
|
|
124
139
|
|
|
125
|
-
|
|
140
|
+
export function appendCommentToSource(
|
|
141
|
+
code: string,
|
|
142
|
+
comment: Comment,
|
|
143
|
+
placement: CommentPlacement,
|
|
144
|
+
): string {
|
|
145
|
+
let newCode = code;
|
|
126
146
|
switch (comment.type) {
|
|
127
|
-
case 'Block':
|
|
128
|
-
|
|
147
|
+
case 'Block': {
|
|
148
|
+
// Prettier decides if a newline is necessary between the comment and its node by looking
|
|
149
|
+
// to see if a newline seperates them in the source text. We can trick prettier into
|
|
150
|
+
// formatting how we want for new comments by placing the range such that a newline
|
|
151
|
+
// will (OWN_LINE) or will not (INLINE) be found when searching from the specified range
|
|
152
|
+
// position.
|
|
153
|
+
switch (placement) {
|
|
154
|
+
case CommentPlacement.LEADING_OWN_LINE:
|
|
155
|
+
case CommentPlacement.TRAILING_OWN_LINE: {
|
|
156
|
+
// Since we always want a line break we need to ensure a newline is found when
|
|
157
|
+
// searching out from either side of the comment range.
|
|
158
|
+
let firstNewline = getFirstNewlineIndex(code);
|
|
159
|
+
if (firstNewline === -1) {
|
|
160
|
+
// No newline in file, lets add one.
|
|
161
|
+
newCode += EOL;
|
|
162
|
+
firstNewline = newCode.length;
|
|
163
|
+
}
|
|
164
|
+
// Prettier only uses these ranges for detecting whitespace, so this nonsensical
|
|
165
|
+
// range is safe.
|
|
166
|
+
// $FlowExpectedError[cannot-write]
|
|
167
|
+
comment.range = [firstNewline + 1, firstNewline];
|
|
168
|
+
break;
|
|
169
|
+
}
|
|
170
|
+
case CommentPlacement.LEADING_INLINE:
|
|
171
|
+
case CommentPlacement.TRAILING_INLINE: {
|
|
172
|
+
// Since we don't want a line break we need to ensure a non whitespace char is
|
|
173
|
+
// always found before a newline when searching out from either side of the
|
|
174
|
+
// comment range.
|
|
175
|
+
let firstNonWhitespace = getFirstNonWhitespaceIndex(code);
|
|
176
|
+
if (firstNonWhitespace === -1) {
|
|
177
|
+
// No non whitespace chars in file, lets add an identifiable statement for prettier to find.
|
|
178
|
+
newCode += '$FORCE_INLINE_ON_EMPTY_FILE_TOKEN$;';
|
|
179
|
+
firstNonWhitespace = newCode.length;
|
|
180
|
+
break;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// $FlowExpectedError[cannot-write]
|
|
184
|
+
comment.range = [firstNonWhitespace + 1, firstNonWhitespace];
|
|
185
|
+
break;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
129
188
|
break;
|
|
130
|
-
|
|
131
|
-
|
|
189
|
+
}
|
|
190
|
+
case 'Line': {
|
|
191
|
+
// For `Line` comments prettier slices comments directly from the source code when printing
|
|
192
|
+
// https://github.com/prettier/prettier/blob/5f0ee39fa03532c85bd1c35291450fe7ac3667b3/src/language-js/print/comment.js#L15-L20
|
|
193
|
+
// this means that we need to have any appended comments directly in the
|
|
194
|
+
// source code or else prettier will slice nothing and bork up the transform
|
|
195
|
+
const commentText = `//${comment.value}`;
|
|
196
|
+
|
|
197
|
+
const lastChar = newCode[newCode.length - 1];
|
|
198
|
+
if (lastChar !== '\n' && lastChar !== '\r') {
|
|
199
|
+
newCode += EOL;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// Line comments cannot be inline before a node so we only place trailing Line comments inline.
|
|
203
|
+
if (placement === CommentPlacement.TRAILING_INLINE) {
|
|
204
|
+
// Prettier determines an "end of line" comment by walking backwards from
|
|
205
|
+
// the comment start range through the source code to see if it finds a non
|
|
206
|
+
// newline token. In order to trick prettier for new comments we need to
|
|
207
|
+
// insert fake source code for it to find.
|
|
208
|
+
newCode += '$FORCE_END_OF_LINE_COMMENT_TOKEN$;';
|
|
209
|
+
}
|
|
210
|
+
const start = newCode.length;
|
|
211
|
+
newCode += commentText;
|
|
212
|
+
const end = newCode.length;
|
|
213
|
+
|
|
214
|
+
// $FlowExpectedError[cannot-write]
|
|
215
|
+
comment.range = [start, end];
|
|
216
|
+
|
|
132
217
|
break;
|
|
218
|
+
}
|
|
133
219
|
}
|
|
134
220
|
|
|
135
|
-
let newCode = code;
|
|
136
|
-
newCode += '\n';
|
|
137
|
-
const start = newCode.length;
|
|
138
|
-
newCode += commentText;
|
|
139
|
-
const end = newCode.length;
|
|
140
|
-
|
|
141
|
-
// $FlowExpectedError[cannot-write]
|
|
142
|
-
comment.range = [start, end];
|
|
143
|
-
|
|
144
221
|
return newCode;
|
|
145
222
|
}
|
|
@@ -26,9 +26,7 @@ var _TransformContext = require("./TransformContext");
|
|
|
26
26
|
|
|
27
27
|
var _comments = require("./comments/comments");
|
|
28
28
|
|
|
29
|
-
var
|
|
30
|
-
|
|
31
|
-
var _AddTrailingComments = require("./mutations/AddTrailingComments");
|
|
29
|
+
var _AddComments = require("./mutations/AddComments");
|
|
32
30
|
|
|
33
31
|
var _CloneCommentsTo = require("./mutations/CloneCommentsTo");
|
|
34
32
|
|
|
@@ -55,8 +53,8 @@ function getTransformedAST(code, visitors) {
|
|
|
55
53
|
|
|
56
54
|
(0, _comments.attachComments)(ast.comments, ast, code); // traverse the AST and colllect the mutations
|
|
57
55
|
|
|
58
|
-
const transformContext = (0, _TransformContext.getTransformContext)(
|
|
59
|
-
(0, _traverse.traverseWithContext)(ast, scopeManager, () => transformContext, visitors); // apply the mutations to the AST
|
|
56
|
+
const transformContext = (0, _TransformContext.getTransformContext)();
|
|
57
|
+
(0, _traverse.traverseWithContext)(code, ast, scopeManager, () => transformContext, visitors); // apply the mutations to the AST
|
|
60
58
|
|
|
61
59
|
const mutationContext = new _MutationContext.MutationContext(code);
|
|
62
60
|
const removeCommentMutations = [];
|
|
@@ -96,14 +94,9 @@ function getTransformedAST(code, visitors) {
|
|
|
96
94
|
return null;
|
|
97
95
|
}
|
|
98
96
|
|
|
99
|
-
case '
|
|
100
|
-
{
|
|
101
|
-
return (0, _AddLeadingComments.performAddLeadingCommentsMutation)(mutationContext, mutation);
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
case 'addTrailingComments':
|
|
97
|
+
case 'addComments':
|
|
105
98
|
{
|
|
106
|
-
return (0,
|
|
99
|
+
return (0, _AddComments.performAddCommentsMutation)(mutationContext, mutation);
|
|
107
100
|
}
|
|
108
101
|
|
|
109
102
|
case 'cloneCommentsTo':
|
|
@@ -11,8 +11,7 @@
|
|
|
11
11
|
'use strict';
|
|
12
12
|
|
|
13
13
|
import type {ESNode, Program} from 'hermes-estree';
|
|
14
|
-
import type {
|
|
15
|
-
import type {TransformContext} from './TransformContext';
|
|
14
|
+
import type {TransformVisitor} from './transform';
|
|
16
15
|
import type {RemoveCommentMutation} from './mutations/RemoveComment';
|
|
17
16
|
|
|
18
17
|
import {parseForESLint} from 'hermes-eslint';
|
|
@@ -21,8 +20,7 @@ import {traverseWithContext} from '../traverse/traverse';
|
|
|
21
20
|
import {MutationContext} from './MutationContext';
|
|
22
21
|
import {getTransformContext} from './TransformContext';
|
|
23
22
|
import {attachComments} from './comments/comments';
|
|
24
|
-
import {
|
|
25
|
-
import {performAddTrailingCommentsMutation} from './mutations/AddTrailingComments';
|
|
23
|
+
import {performAddCommentsMutation} from './mutations/AddComments';
|
|
26
24
|
import {performCloneCommentsToMutation} from './mutations/CloneCommentsTo';
|
|
27
25
|
import {performInsertStatementMutation} from './mutations/InsertStatement';
|
|
28
26
|
import {performRemoveCommentMutations} from './mutations/RemoveComment';
|
|
@@ -33,7 +31,7 @@ import {performReplaceStatementWithManyMutation} from './mutations/ReplaceStatem
|
|
|
33
31
|
|
|
34
32
|
export function getTransformedAST(
|
|
35
33
|
code: string,
|
|
36
|
-
visitors:
|
|
34
|
+
visitors: TransformVisitor,
|
|
37
35
|
): {
|
|
38
36
|
ast: Program,
|
|
39
37
|
astWasMutated: boolean,
|
|
@@ -48,8 +46,14 @@ export function getTransformedAST(
|
|
|
48
46
|
attachComments(ast.comments, ast, code);
|
|
49
47
|
|
|
50
48
|
// traverse the AST and colllect the mutations
|
|
51
|
-
const transformContext = getTransformContext(
|
|
52
|
-
traverseWithContext(
|
|
49
|
+
const transformContext = getTransformContext();
|
|
50
|
+
traverseWithContext(
|
|
51
|
+
code,
|
|
52
|
+
ast,
|
|
53
|
+
scopeManager,
|
|
54
|
+
() => transformContext,
|
|
55
|
+
visitors,
|
|
56
|
+
);
|
|
53
57
|
|
|
54
58
|
// apply the mutations to the AST
|
|
55
59
|
const mutationContext = new MutationContext(code);
|
|
@@ -88,12 +92,8 @@ export function getTransformedAST(
|
|
|
88
92
|
return null;
|
|
89
93
|
}
|
|
90
94
|
|
|
91
|
-
case '
|
|
92
|
-
return
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
case 'addTrailingComments': {
|
|
96
|
-
return performAddTrailingCommentsMutation(mutationContext, mutation);
|
|
95
|
+
case 'addComments': {
|
|
96
|
+
return performAddCommentsMutation(mutationContext, mutation);
|
|
97
97
|
}
|
|
98
98
|
|
|
99
99
|
case 'cloneCommentsTo': {
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports.
|
|
7
|
-
exports.
|
|
6
|
+
exports.createAddCommentsMutation = createAddCommentsMutation;
|
|
7
|
+
exports.performAddCommentsMutation = performAddCommentsMutation;
|
|
8
8
|
|
|
9
9
|
var _comments = require("../comments/comments");
|
|
10
10
|
|
|
@@ -17,23 +17,26 @@ var _comments = require("../comments/comments");
|
|
|
17
17
|
*
|
|
18
18
|
* @format
|
|
19
19
|
*/
|
|
20
|
-
function
|
|
20
|
+
function createAddCommentsMutation(node, comments) {
|
|
21
21
|
if (comments.length === 0) {
|
|
22
22
|
return null;
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
return {
|
|
26
|
-
type: '
|
|
26
|
+
type: 'addComments',
|
|
27
27
|
comments,
|
|
28
28
|
node
|
|
29
29
|
};
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
function
|
|
33
|
-
for (const
|
|
32
|
+
function performAddCommentsMutation(mutationContext, mutation) {
|
|
33
|
+
for (const {
|
|
34
|
+
comment: originalComment,
|
|
35
|
+
placement
|
|
36
|
+
} of mutation.comments) {
|
|
34
37
|
const comment = (0, _comments.cloneComment)(originalComment);
|
|
35
|
-
mutationContext.appendCommentToSource(comment);
|
|
36
|
-
(0, _comments.
|
|
38
|
+
mutationContext.appendCommentToSource(comment, placement);
|
|
39
|
+
(0, _comments.addComment)(mutation.node, comment, placement);
|
|
37
40
|
}
|
|
38
41
|
|
|
39
42
|
return null;
|
|
@@ -0,0 +1,50 @@
|
|
|
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
|
+
* @flow strict-local
|
|
8
|
+
* @format
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import type {Comment, ESNode} from 'hermes-estree';
|
|
12
|
+
import type {DetachedNode} from '../../detachedNode';
|
|
13
|
+
import type {MutationContext} from '../MutationContext';
|
|
14
|
+
import type {CommentPlacement} from '../comments/comments';
|
|
15
|
+
|
|
16
|
+
import {addComment, cloneComment} from '../comments/comments';
|
|
17
|
+
|
|
18
|
+
export type AddCommentsMutation = $ReadOnly<{
|
|
19
|
+
type: 'addComments',
|
|
20
|
+
comments: $ReadOnlyArray<{comment: Comment, placement: CommentPlacement}>,
|
|
21
|
+
node: ESNode | DetachedNode<ESNode>,
|
|
22
|
+
}>;
|
|
23
|
+
|
|
24
|
+
export function createAddCommentsMutation(
|
|
25
|
+
node: AddCommentsMutation['node'],
|
|
26
|
+
comments: AddCommentsMutation['comments'],
|
|
27
|
+
): ?AddCommentsMutation {
|
|
28
|
+
if (comments.length === 0) {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return {
|
|
33
|
+
type: 'addComments',
|
|
34
|
+
comments,
|
|
35
|
+
node,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function performAddCommentsMutation(
|
|
40
|
+
mutationContext: MutationContext,
|
|
41
|
+
mutation: AddCommentsMutation,
|
|
42
|
+
): null {
|
|
43
|
+
for (const {comment: originalComment, placement} of mutation.comments) {
|
|
44
|
+
const comment = cloneComment(originalComment);
|
|
45
|
+
mutationContext.appendCommentToSource(comment, placement);
|
|
46
|
+
addComment(mutation.node, comment, placement);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
@@ -25,12 +25,11 @@ function createCloneCommentsToMutation(target, destination) {
|
|
|
25
25
|
};
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
function performCloneCommentsToMutation(
|
|
28
|
+
function performCloneCommentsToMutation(_mutationContext, mutation) {
|
|
29
29
|
const newComments = [];
|
|
30
30
|
|
|
31
31
|
for (const originalComment of (0, _comments.getCommentsForNode)(mutation.target)) {
|
|
32
32
|
const comment = (0, _comments.cloneCommentWithMarkers)(originalComment);
|
|
33
|
-
mutationContext.appendCommentToSource(comment);
|
|
34
33
|
newComments.push(comment);
|
|
35
34
|
}
|
|
36
35
|
|
|
@@ -36,13 +36,12 @@ export function createCloneCommentsToMutation(
|
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
export function performCloneCommentsToMutation(
|
|
39
|
-
|
|
39
|
+
_mutationContext: MutationContext,
|
|
40
40
|
mutation: CloneCommentsToMutation,
|
|
41
41
|
): null {
|
|
42
42
|
const newComments = [];
|
|
43
43
|
for (const originalComment of getCommentsForNode(mutation.target)) {
|
|
44
44
|
const comment = cloneCommentWithMarkers(originalComment);
|
|
45
|
-
mutationContext.appendCommentToSource(comment);
|
|
46
45
|
newComments.push(comment);
|
|
47
46
|
}
|
|
48
47
|
addCommentsToNode(mutation.destination, newComments);
|
|
@@ -57,7 +57,7 @@ function performInsertStatementMutation(mutationContext, mutation) {
|
|
|
57
57
|
switch (mutation.side) {
|
|
58
58
|
case 'before':
|
|
59
59
|
{
|
|
60
|
-
parent[insertionParent.key] = (0, _arrayUtils.insertInArray)(parent[insertionParent.key], insertionParent.targetIndex
|
|
60
|
+
parent[insertionParent.key] = (0, _arrayUtils.insertInArray)(parent[insertionParent.key], insertionParent.targetIndex, mutation.nodesToInsert);
|
|
61
61
|
break;
|
|
62
62
|
}
|
|
63
63
|
|
|
@@ -16,14 +16,28 @@ exports.replaceInArray = replaceInArray;
|
|
|
16
16
|
*
|
|
17
17
|
* @format
|
|
18
18
|
*/
|
|
19
|
+
function assertArrayBounds(array, index) {
|
|
20
|
+
if (index < 0 || index >= array.length) {
|
|
21
|
+
throw new Error(`Invalid Mutation: Tried to mutate an elements array with an out of bounds index. Index: ${index}, Array Size: ${array.length}`);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
19
25
|
function insertInArray(array, index, elements) {
|
|
26
|
+
if (index === array.length) {
|
|
27
|
+
// Support the insert at end of array case.
|
|
28
|
+
return array.concat(elements);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
assertArrayBounds(array, index);
|
|
20
32
|
return array.slice(0, index).concat(elements).concat(array.slice(index));
|
|
21
33
|
}
|
|
22
34
|
|
|
23
35
|
function removeFromArray(array, index) {
|
|
36
|
+
assertArrayBounds(array, index);
|
|
24
37
|
return [...array.slice(0, index), ...array.slice(index + 1)];
|
|
25
38
|
}
|
|
26
39
|
|
|
27
40
|
function replaceInArray(array, index, elements) {
|
|
41
|
+
assertArrayBounds(array, index);
|
|
28
42
|
return array.slice(0, index).concat(elements).concat(array.slice(index + 1));
|
|
29
43
|
}
|
|
@@ -8,11 +8,24 @@
|
|
|
8
8
|
* @format
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
+
function assertArrayBounds<T>(array: $ReadOnlyArray<T>, index: number): void {
|
|
12
|
+
if (index < 0 || index >= array.length) {
|
|
13
|
+
throw new Error(
|
|
14
|
+
`Invalid Mutation: Tried to mutate an elements array with an out of bounds index. Index: ${index}, Array Size: ${array.length}`,
|
|
15
|
+
);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
11
19
|
export function insertInArray<T>(
|
|
12
20
|
array: $ReadOnlyArray<T>,
|
|
13
21
|
index: number,
|
|
14
22
|
elements: $ReadOnlyArray<T>,
|
|
15
23
|
): Array<T> {
|
|
24
|
+
if (index === array.length) {
|
|
25
|
+
// Support the insert at end of array case.
|
|
26
|
+
return array.concat(elements);
|
|
27
|
+
}
|
|
28
|
+
assertArrayBounds(array, index);
|
|
16
29
|
return array.slice(0, index).concat(elements).concat(array.slice(index));
|
|
17
30
|
}
|
|
18
31
|
|
|
@@ -20,6 +33,7 @@ export function removeFromArray<T>(
|
|
|
20
33
|
array: $ReadOnlyArray<T>,
|
|
21
34
|
index: number,
|
|
22
35
|
): Array<T> {
|
|
36
|
+
assertArrayBounds(array, index);
|
|
23
37
|
return [...array.slice(0, index), ...array.slice(index + 1)];
|
|
24
38
|
}
|
|
25
39
|
|
|
@@ -28,6 +42,7 @@ export function replaceInArray<T>(
|
|
|
28
42
|
index: number,
|
|
29
43
|
elements: $ReadOnlyArray<T>,
|
|
30
44
|
): Array<T> {
|
|
45
|
+
assertArrayBounds(array, index);
|
|
31
46
|
return array
|
|
32
47
|
.slice(0, index)
|
|
33
48
|
.concat(elements)
|
|
@@ -11,13 +11,13 @@
|
|
|
11
11
|
'use strict';
|
|
12
12
|
|
|
13
13
|
import type {Visitor} from '../traverse/traverse';
|
|
14
|
-
import type {
|
|
14
|
+
import type {TransformContextAdditions} from './TransformContext';
|
|
15
15
|
|
|
16
16
|
import * as prettier from 'prettier';
|
|
17
17
|
import {getTransformedAST} from './getTransformedAST';
|
|
18
18
|
import {SimpleTraverser} from '../traverse/SimpleTraverser';
|
|
19
19
|
|
|
20
|
-
export type TransformVisitor = Visitor<
|
|
20
|
+
export type TransformVisitor = Visitor<TransformContextAdditions>;
|
|
21
21
|
|
|
22
22
|
export function transform(
|
|
23
23
|
originalCode: string,
|
|
@@ -6,6 +6,8 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
6
6
|
exports.traverse = traverse;
|
|
7
7
|
exports.traverseWithContext = traverseWithContext;
|
|
8
8
|
|
|
9
|
+
var _codeFrame = require("@babel/code-frame");
|
|
10
|
+
|
|
9
11
|
var _NodeEventGenerator = require("./NodeEventGenerator");
|
|
10
12
|
|
|
11
13
|
var _SafeEmitter = require("./SafeEmitter");
|
|
@@ -28,7 +30,7 @@ var _SimpleTraverser = require("./SimpleTraverser");
|
|
|
28
30
|
* @param scopeManager the eslint-scope compatible scope manager instance calculated using the ast
|
|
29
31
|
* @param additionalContext a callback function which returns additional context members to add to the context provided to the visitor
|
|
30
32
|
*/
|
|
31
|
-
function traverseWithContext(ast, scopeManager, additionalContext, visitor) {
|
|
33
|
+
function traverseWithContext(code, ast, scopeManager, additionalContext, visitor) {
|
|
32
34
|
const emitter = new _SafeEmitter.SafeEmitter();
|
|
33
35
|
const nodeQueue = [];
|
|
34
36
|
let currentNode = ast; // set parent pointers and build up the traversal queue
|
|
@@ -72,6 +74,28 @@ function traverseWithContext(ast, scopeManager, additionalContext, visitor) {
|
|
|
72
74
|
};
|
|
73
75
|
|
|
74
76
|
const traversalContextBase = Object.freeze({
|
|
77
|
+
buildCodeFrame: (node, message) => {
|
|
78
|
+
// babel uses 1-indexed columns
|
|
79
|
+
const locForBabel = {
|
|
80
|
+
start: {
|
|
81
|
+
line: node.loc.start.line,
|
|
82
|
+
column: node.loc.start.column + 1
|
|
83
|
+
},
|
|
84
|
+
end: {
|
|
85
|
+
line: node.loc.end.line,
|
|
86
|
+
column: node.loc.end.column + 1
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
return (0, _codeFrame.codeFrameColumns)(code, locForBabel, {
|
|
90
|
+
linesAbove: 0,
|
|
91
|
+
linesBelow: 0,
|
|
92
|
+
highlightCode: process.env.NODE_ENV !== 'test',
|
|
93
|
+
message: message
|
|
94
|
+
});
|
|
95
|
+
},
|
|
96
|
+
buildSimpleCodeFrame: (node, message) => {
|
|
97
|
+
return `[${node.type}:${node.loc.start.line}:${node.loc.start.column}] ${message}`;
|
|
98
|
+
},
|
|
75
99
|
getDeclaredVariables: node => scopeManager.getDeclaredVariables(node),
|
|
76
100
|
getBinding: name => {
|
|
77
101
|
let currentScope = getScope();
|
|
@@ -122,6 +146,6 @@ function traverseWithContext(ast, scopeManager, additionalContext, visitor) {
|
|
|
122
146
|
});
|
|
123
147
|
}
|
|
124
148
|
|
|
125
|
-
function traverse(ast, scopeManager, visitor) {
|
|
126
|
-
traverseWithContext(ast, scopeManager, () => {}, visitor);
|
|
149
|
+
function traverse(code, ast, scopeManager, visitor) {
|
|
150
|
+
traverseWithContext(code, ast, scopeManager, () => {}, visitor);
|
|
127
151
|
}
|
|
@@ -12,11 +12,12 @@ import type {ESNode, ESQueryNodeSelectors, Program} from 'hermes-estree';
|
|
|
12
12
|
import type {ScopeManager, Scope, Variable} from 'hermes-eslint';
|
|
13
13
|
import type {EmitterListener} from './SafeEmitter';
|
|
14
14
|
|
|
15
|
+
import {codeFrameColumns} from '@babel/code-frame';
|
|
15
16
|
import {NodeEventGenerator} from './NodeEventGenerator';
|
|
16
17
|
import {SafeEmitter} from './SafeEmitter';
|
|
17
18
|
import {SimpleTraverser} from './SimpleTraverser';
|
|
18
19
|
|
|
19
|
-
export type
|
|
20
|
+
export type TraversalContextBase = $ReadOnly<{
|
|
20
21
|
/**
|
|
21
22
|
* Gets the variables that were declared by the given node.
|
|
22
23
|
*/
|
|
@@ -31,13 +32,36 @@ export type TraversalContext<T = void> = $ReadOnly<{
|
|
|
31
32
|
* Defaults to the currently traversed node.
|
|
32
33
|
*/
|
|
33
34
|
getScope: (node?: ESNode) => Scope,
|
|
34
|
-
|
|
35
|
+
/**
|
|
36
|
+
* Creates a full code frame for the node along with the message.
|
|
37
|
+
*
|
|
38
|
+
* i.e. `context.buildCodeFrame(node, 'foo')` will create a string like:
|
|
39
|
+
* ```
|
|
40
|
+
* 56 | function () {
|
|
41
|
+
* | ^^^^^^^^^^^^^
|
|
42
|
+
* 57 | }.bind(this)
|
|
43
|
+
* | ^^ foo
|
|
44
|
+
* ```
|
|
45
|
+
*/
|
|
46
|
+
buildCodeFrame: (node: ESNode, message: string) => string,
|
|
47
|
+
/**
|
|
48
|
+
* Creates a simple code frame for the node along with the message.
|
|
49
|
+
* Use this if you want a condensed marker for your message.
|
|
50
|
+
*
|
|
51
|
+
* i.e. `context.logWithNode(node, 'foo')` will create a string like:
|
|
52
|
+
* ```
|
|
53
|
+
* [FunctionExpression:56:44] foo
|
|
54
|
+
* ```
|
|
55
|
+
* (where 56:44 represents L56, Col44)
|
|
56
|
+
*/
|
|
57
|
+
buildSimpleCodeFrame: (node: ESNode, message: string) => string,
|
|
58
|
+
}>;
|
|
59
|
+
export type TraversalContext<T> = $ReadOnly<{
|
|
60
|
+
...TraversalContextBase,
|
|
35
61
|
...T,
|
|
36
62
|
}>;
|
|
37
63
|
|
|
38
|
-
export type Visitor<T =
|
|
39
|
-
context: TraversalContext<T>,
|
|
40
|
-
) => ESQueryNodeSelectors;
|
|
64
|
+
export type Visitor<T> = (context: TraversalContext<T>) => ESQueryNodeSelectors;
|
|
41
65
|
|
|
42
66
|
/**
|
|
43
67
|
* Traverse the AST with additional context members provided by `additionalContext`.
|
|
@@ -45,10 +69,11 @@ export type Visitor<T = void> = (
|
|
|
45
69
|
* @param scopeManager the eslint-scope compatible scope manager instance calculated using the ast
|
|
46
70
|
* @param additionalContext a callback function which returns additional context members to add to the context provided to the visitor
|
|
47
71
|
*/
|
|
48
|
-
export function traverseWithContext<T =
|
|
72
|
+
export function traverseWithContext<T = TraversalContextBase>(
|
|
73
|
+
code: string,
|
|
49
74
|
ast: Program,
|
|
50
75
|
scopeManager: ScopeManager,
|
|
51
|
-
additionalContext:
|
|
76
|
+
additionalContext: TraversalContextBase => T,
|
|
52
77
|
visitor: Visitor<T>,
|
|
53
78
|
): void {
|
|
54
79
|
const emitter = new SafeEmitter();
|
|
@@ -86,9 +111,34 @@ export function traverseWithContext<T = void>(
|
|
|
86
111
|
return scopeManager.scopes[0];
|
|
87
112
|
};
|
|
88
113
|
|
|
89
|
-
const traversalContextBase:
|
|
114
|
+
const traversalContextBase: TraversalContextBase = Object.freeze({
|
|
115
|
+
buildCodeFrame: (node: ESNode, message: string): string => {
|
|
116
|
+
// babel uses 1-indexed columns
|
|
117
|
+
const locForBabel = {
|
|
118
|
+
start: {
|
|
119
|
+
line: node.loc.start.line,
|
|
120
|
+
column: node.loc.start.column + 1,
|
|
121
|
+
},
|
|
122
|
+
end: {
|
|
123
|
+
line: node.loc.end.line,
|
|
124
|
+
column: node.loc.end.column + 1,
|
|
125
|
+
},
|
|
126
|
+
};
|
|
127
|
+
return codeFrameColumns(code, locForBabel, {
|
|
128
|
+
linesAbove: 0,
|
|
129
|
+
linesBelow: 0,
|
|
130
|
+
highlightCode: process.env.NODE_ENV !== 'test',
|
|
131
|
+
message: message,
|
|
132
|
+
});
|
|
133
|
+
},
|
|
134
|
+
|
|
135
|
+
buildSimpleCodeFrame: (node: ESNode, message: string): string => {
|
|
136
|
+
return `[${node.type}:${node.loc.start.line}:${node.loc.start.column}] ${message}`;
|
|
137
|
+
},
|
|
138
|
+
|
|
90
139
|
getDeclaredVariables: (node: ESNode) =>
|
|
91
140
|
scopeManager.getDeclaredVariables(node),
|
|
141
|
+
|
|
92
142
|
getBinding: (name: string) => {
|
|
93
143
|
let currentScope = getScope();
|
|
94
144
|
|
|
@@ -103,8 +153,10 @@ export function traverseWithContext<T = void>(
|
|
|
103
153
|
|
|
104
154
|
return null;
|
|
105
155
|
},
|
|
156
|
+
|
|
106
157
|
getScope,
|
|
107
158
|
});
|
|
159
|
+
|
|
108
160
|
const traversalContext: TraversalContext<T> = Object.freeze({
|
|
109
161
|
...traversalContextBase,
|
|
110
162
|
...additionalContext(traversalContextBase),
|
|
@@ -141,9 +193,10 @@ export function traverseWithContext<T = void>(
|
|
|
141
193
|
}
|
|
142
194
|
|
|
143
195
|
export function traverse(
|
|
196
|
+
code: string,
|
|
144
197
|
ast: Program,
|
|
145
198
|
scopeManager: ScopeManager,
|
|
146
199
|
visitor: Visitor<void>,
|
|
147
200
|
): void {
|
|
148
|
-
traverseWithContext(ast, scopeManager, () => {}, visitor);
|
|
201
|
+
traverseWithContext(code, ast, scopeManager, () => {}, visitor);
|
|
149
202
|
}
|