hermes-transform 0.9.0 → 0.10.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 +9 -27
- package/dist/detachedNode.js.flow +18 -31
- package/dist/generated/TransformCloneSignatures.js.flow +18 -0
- package/dist/generated/TransformModifySignatures.js.flow +14 -0
- package/dist/generated/TransformReplaceSignatures.js.flow +15 -1
- package/dist/generated/node-types.js +253 -59
- package/dist/generated/node-types.js.flow +257 -102
- package/dist/generated/special-case-node-types/Comment.js +36 -0
- package/dist/generated/special-case-node-types/Comment.js.flow +36 -0
- package/dist/generated/special-case-node-types/DeclareExportDeclaration.js +55 -0
- package/dist/generated/special-case-node-types/DeclareExportDeclaration.js.flow +97 -0
- package/dist/generated/special-case-node-types/ExportNamedDeclaration.js +42 -0
- package/dist/generated/special-case-node-types/ExportNamedDeclaration.js.flow +75 -0
- package/dist/generated/special-case-node-types/Literal.js +97 -0
- package/dist/generated/special-case-node-types/Literal.js.flow +139 -0
- package/dist/generated/special-case-node-types/ObjectTypeProperty.js +73 -0
- package/dist/generated/special-case-node-types/ObjectTypeProperty.js.flow +107 -0
- package/dist/generated/special-case-node-types/Property.js +136 -0
- package/dist/generated/special-case-node-types/Property.js.flow +237 -0
- package/dist/generated/special-case-node-types/misc.js +119 -0
- package/dist/generated/special-case-node-types/misc.js.flow +205 -0
- package/dist/generated/special-case-node-types.js +42 -174
- package/dist/generated/special-case-node-types.js.flow +7 -274
- package/dist/index.js +19 -3
- package/dist/index.js.flow +6 -2
- package/dist/transform/TransformContext.js +1 -1
- package/dist/transform/TransformContext.js.flow +2 -2
- package/dist/transform/comments/comments.js +11 -0
- package/dist/transform/comments/comments.js.flow +16 -1
- package/dist/transform/comments/prettier/main/comments.js +1 -1
- package/dist/transform/comments/prettier/main/comments.js.flow +2 -1
- package/dist/transform/mutations/InsertStatement.js +3 -3
- package/dist/transform/mutations/InsertStatement.js.flow +3 -3
- package/dist/transform/mutations/RemoveComment.js +3 -3
- package/dist/transform/mutations/RemoveComment.js.flow +3 -5
- package/dist/transform/mutations/RemoveNode.js +2 -2
- package/dist/transform/mutations/RemoveNode.js.flow +2 -2
- package/dist/transform/mutations/RemoveStatement.js +2 -2
- package/dist/transform/mutations/RemoveStatement.js.flow +2 -2
- package/dist/transform/mutations/ReplaceNode.js +4 -6
- package/dist/transform/mutations/ReplaceNode.js.flow +2 -3
- package/dist/transform/mutations/ReplaceStatementWithMany.js +2 -2
- package/dist/transform/mutations/ReplaceStatementWithMany.js.flow +2 -2
- package/dist/transform/mutations/utils/getStatementParent.js +3 -2
- package/dist/transform/mutations/utils/getStatementParent.js.flow +5 -2
- package/dist/transform/parse.js +55 -0
- package/dist/transform/parse.js.flow +55 -0
- package/dist/transform/print.js +160 -0
- package/dist/transform/print.js.flow +176 -0
- package/dist/transform/transform.js +6 -67
- package/dist/transform/transform.js.flow +6 -69
- package/dist/transform/{getTransformedAST.js → transformAST.js} +7 -31
- package/dist/transform/{getTransformedAST.js.flow → transformAST.js.flow} +7 -32
- package/dist/traverse/traverse.js +36 -35
- package/dist/traverse/traverse.js.flow +45 -26
- package/package.json +5 -4
- package/dist/getVisitorKeys.js +0 -33
- package/dist/getVisitorKeys.js.flow +0 -31
- package/dist/transform/mutations/utils/arrayUtils.js +0 -43
- package/dist/transform/mutations/utils/arrayUtils.js.flow +0 -50
- package/dist/traverse/SimpleTraverser.js +0 -118
- package/dist/traverse/SimpleTraverser.js.flow +0 -112
|
@@ -6,7 +6,7 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
6
6
|
exports.createReplaceNodeMutation = createReplaceNodeMutation;
|
|
7
7
|
exports.performReplaceNodeMutation = performReplaceNodeMutation;
|
|
8
8
|
|
|
9
|
-
var
|
|
9
|
+
var _hermesParser = require("hermes-parser");
|
|
10
10
|
|
|
11
11
|
var _comments = require("../comments/comments");
|
|
12
12
|
|
|
@@ -14,8 +14,6 @@ var _Errors = require("../Errors");
|
|
|
14
14
|
|
|
15
15
|
var _detachedNode = require("../../detachedNode");
|
|
16
16
|
|
|
17
|
-
var _getVisitorKeys = require("../../getVisitorKeys");
|
|
18
|
-
|
|
19
17
|
/**
|
|
20
18
|
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
21
19
|
*
|
|
@@ -46,7 +44,7 @@ function performReplaceNodeMutation(mutationContext, mutation) {
|
|
|
46
44
|
|
|
47
45
|
if (replacementParent.type === 'array') {
|
|
48
46
|
const parent = replacementParent.parent;
|
|
49
|
-
parent[replacementParent.key] =
|
|
47
|
+
parent[replacementParent.key] = _hermesParser.astArrayMutationHelpers.replaceInArray(parent[replacementParent.key], replacementParent.targetIndex, [mutation.nodeToReplaceWith]);
|
|
50
48
|
} else {
|
|
51
49
|
replacementParent.parent[replacementParent.key] = mutation.nodeToReplaceWith;
|
|
52
50
|
}
|
|
@@ -61,8 +59,8 @@ function performReplaceNodeMutation(mutationContext, mutation) {
|
|
|
61
59
|
function getParentKey(target) {
|
|
62
60
|
const parent = target.parent;
|
|
63
61
|
|
|
64
|
-
for (const key of (0,
|
|
65
|
-
if ((0,
|
|
62
|
+
for (const key of (0, _hermesParser.getVisitorKeys)(parent)) {
|
|
63
|
+
if ((0, _hermesParser.isNode)( // $FlowExpectedError[prop-missing]
|
|
66
64
|
parent[key])) {
|
|
67
65
|
if (parent[key] === target) {
|
|
68
66
|
return {
|
|
@@ -12,11 +12,10 @@ import type {ESNode} from 'hermes-estree';
|
|
|
12
12
|
import type {MutationContext} from '../MutationContext';
|
|
13
13
|
import type {DetachedNode} from '../../detachedNode';
|
|
14
14
|
|
|
15
|
-
import {
|
|
15
|
+
import {getVisitorKeys, isNode, astArrayMutationHelpers} from 'hermes-parser';
|
|
16
16
|
import {moveCommentsToNewNode} from '../comments/comments';
|
|
17
17
|
import {InvalidReplacementError} from '../Errors';
|
|
18
18
|
import {getOriginalNode} from '../../detachedNode';
|
|
19
|
-
import {getVisitorKeys, isNode} from '../../getVisitorKeys';
|
|
20
19
|
|
|
21
20
|
export type ReplaceNodeMutation = $ReadOnly<{
|
|
22
21
|
type: 'replaceNode',
|
|
@@ -56,7 +55,7 @@ export function performReplaceNodeMutation(
|
|
|
56
55
|
const parent: interface {
|
|
57
56
|
[string]: $ReadOnlyArray<DetachedNode<ESNode>>,
|
|
58
57
|
} = replacementParent.parent;
|
|
59
|
-
parent[replacementParent.key] = replaceInArray(
|
|
58
|
+
parent[replacementParent.key] = astArrayMutationHelpers.replaceInArray(
|
|
60
59
|
parent[replacementParent.key],
|
|
61
60
|
replacementParent.targetIndex,
|
|
62
61
|
[mutation.nodeToReplaceWith],
|
|
@@ -6,7 +6,7 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
6
6
|
exports.createReplaceStatementWithManyMutation = createReplaceStatementWithManyMutation;
|
|
7
7
|
exports.performReplaceStatementWithManyMutation = performReplaceStatementWithManyMutation;
|
|
8
8
|
|
|
9
|
-
var
|
|
9
|
+
var _hermesParser = require("hermes-parser");
|
|
10
10
|
|
|
11
11
|
var _getStatementParent = require("./utils/getStatementParent");
|
|
12
12
|
|
|
@@ -63,7 +63,7 @@ function performReplaceStatementWithManyMutation(mutationContext, mutation) {
|
|
|
63
63
|
|
|
64
64
|
if (replacementParent.type === 'array') {
|
|
65
65
|
const parent = replacementParent.parent;
|
|
66
|
-
parent[replacementParent.key] =
|
|
66
|
+
parent[replacementParent.key] = _hermesParser.astArrayMutationHelpers.replaceInArray(parent[replacementParent.key], replacementParent.targetIndex, mutation.nodesToReplaceWith);
|
|
67
67
|
return replacementParent.parent;
|
|
68
68
|
}
|
|
69
69
|
|
|
@@ -12,7 +12,7 @@ import type {ESNode, ModuleDeclaration, Statement} from 'hermes-estree';
|
|
|
12
12
|
import type {MutationContext} from '../MutationContext';
|
|
13
13
|
import type {DetachedNode} from '../../detachedNode';
|
|
14
14
|
|
|
15
|
-
import {
|
|
15
|
+
import {astArrayMutationHelpers} from 'hermes-parser';
|
|
16
16
|
import {getStatementParent} from './utils/getStatementParent';
|
|
17
17
|
import {isValidModuleDeclarationParent} from './utils/isValidModuleDeclarationParent';
|
|
18
18
|
import {moveCommentsToNewNode} from '../comments/comments';
|
|
@@ -78,7 +78,7 @@ export function performReplaceStatementWithManyMutation(
|
|
|
78
78
|
const parent: interface {
|
|
79
79
|
[string]: $ReadOnlyArray<DetachedNode<Statement | ModuleDeclaration>>,
|
|
80
80
|
} = replacementParent.parent;
|
|
81
|
-
parent[replacementParent.key] = replaceInArray(
|
|
81
|
+
parent[replacementParent.key] = astArrayMutationHelpers.replaceInArray(
|
|
82
82
|
parent[replacementParent.key],
|
|
83
83
|
replacementParent.targetIndex,
|
|
84
84
|
mutation.nodesToReplaceWith,
|
|
@@ -97,8 +97,9 @@ function getStatementParent(target) {
|
|
|
97
97
|
case 'ForInStatement':
|
|
98
98
|
case 'ForOfStatement':
|
|
99
99
|
{
|
|
100
|
-
assertValidStatementLocation( // $FlowExpectedError[prop-missing] - flow does not track properties from parent interface
|
|
101
|
-
|
|
100
|
+
assertValidStatementLocation(parent, // $FlowExpectedError[prop-missing] - flow does not track properties from parent interface
|
|
101
|
+
'left', // $FlowExpectedError[prop-missing] - flow does not track properties from parent interface
|
|
102
|
+
'right');
|
|
102
103
|
return {
|
|
103
104
|
type: 'single',
|
|
104
105
|
parent,
|
|
@@ -9,6 +9,8 @@
|
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
import type {
|
|
12
|
+
ForInStatement,
|
|
13
|
+
ForOfStatement,
|
|
12
14
|
ModuleDeclaration,
|
|
13
15
|
Statement,
|
|
14
16
|
StatementParentArray,
|
|
@@ -92,10 +94,11 @@ export function getStatementParent(
|
|
|
92
94
|
|
|
93
95
|
case 'ForInStatement':
|
|
94
96
|
case 'ForOfStatement': {
|
|
95
|
-
assertValidStatementLocation(
|
|
96
|
-
// $FlowExpectedError[prop-missing] - flow does not track properties from parent interface
|
|
97
|
+
assertValidStatementLocation<ForInStatement | ForOfStatement>(
|
|
97
98
|
parent,
|
|
99
|
+
// $FlowExpectedError[prop-missing] - flow does not track properties from parent interface
|
|
98
100
|
'left',
|
|
101
|
+
// $FlowExpectedError[prop-missing] - flow does not track properties from parent interface
|
|
99
102
|
'right',
|
|
100
103
|
);
|
|
101
104
|
return {type: 'single', parent, key: 'body'};
|
|
@@ -0,0 +1,55 @@
|
|
|
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
|
+
*
|
|
8
|
+
* @format
|
|
9
|
+
*/
|
|
10
|
+
'use strict';
|
|
11
|
+
|
|
12
|
+
Object.defineProperty(exports, "__esModule", {
|
|
13
|
+
value: true
|
|
14
|
+
});
|
|
15
|
+
exports.parse = parse;
|
|
16
|
+
|
|
17
|
+
var _comments = require("./comments/comments");
|
|
18
|
+
|
|
19
|
+
var _hermesEslint = require("hermes-eslint");
|
|
20
|
+
|
|
21
|
+
function parse(code) {
|
|
22
|
+
const {
|
|
23
|
+
ast,
|
|
24
|
+
scopeManager
|
|
25
|
+
} = (0, _hermesEslint.parseForESLint)(code, {
|
|
26
|
+
sourceType: 'module'
|
|
27
|
+
}); // Don't include the docblock comment in the comment list as we don't want to attach it
|
|
28
|
+
// as it should be maintained at the top of the file as nodes are moved around.
|
|
29
|
+
|
|
30
|
+
let comments = ast.comments;
|
|
31
|
+
|
|
32
|
+
if (ast.docblock != null && ast.docblock.comment === comments[0]) {
|
|
33
|
+
const [first, ...nonDocblockComments] = comments; // Since we will not be attaching this comment automatically we need to add the
|
|
34
|
+
// properties prettier expects for printing.
|
|
35
|
+
// $FlowExpectedError[prop-missing]
|
|
36
|
+
|
|
37
|
+
first.placement = 'endOfLine'; // $FlowExpectedError[prop-missing]
|
|
38
|
+
|
|
39
|
+
first.leading = true; // $FlowExpectedError[prop-missing]
|
|
40
|
+
|
|
41
|
+
first.trailing = false; // $FlowExpectedError[prop-missing]
|
|
42
|
+
|
|
43
|
+
first.printed = false;
|
|
44
|
+
comments = nonDocblockComments;
|
|
45
|
+
} // attach comments before mutation. this will ensure that as nodes are
|
|
46
|
+
// cloned / moved around - comments remain in the correct place with respect to the node
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
(0, _comments.attachComments)(comments, ast, code);
|
|
50
|
+
return {
|
|
51
|
+
ast,
|
|
52
|
+
scopeManager,
|
|
53
|
+
code
|
|
54
|
+
};
|
|
55
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
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
|
+
'use strict';
|
|
12
|
+
|
|
13
|
+
import type {Program} from 'hermes-estree';
|
|
14
|
+
import type {ScopeManager} from 'hermes-eslint';
|
|
15
|
+
|
|
16
|
+
import {attachComments} from './comments/comments';
|
|
17
|
+
import {parseForESLint} from 'hermes-eslint';
|
|
18
|
+
|
|
19
|
+
export type ParseResult = {
|
|
20
|
+
ast: Program,
|
|
21
|
+
scopeManager: ScopeManager,
|
|
22
|
+
code: string,
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export function parse(code: string): ParseResult {
|
|
26
|
+
const {ast, scopeManager} = parseForESLint(code, {
|
|
27
|
+
sourceType: 'module',
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
// Don't include the docblock comment in the comment list as we don't want to attach it
|
|
31
|
+
// as it should be maintained at the top of the file as nodes are moved around.
|
|
32
|
+
let comments = ast.comments;
|
|
33
|
+
if (ast.docblock != null && ast.docblock.comment === comments[0]) {
|
|
34
|
+
const [first, ...nonDocblockComments] = comments;
|
|
35
|
+
|
|
36
|
+
// Since we will not be attaching this comment automatically we need to add the
|
|
37
|
+
// properties prettier expects for printing.
|
|
38
|
+
// $FlowExpectedError[prop-missing]
|
|
39
|
+
first.placement = 'endOfLine';
|
|
40
|
+
// $FlowExpectedError[prop-missing]
|
|
41
|
+
first.leading = true;
|
|
42
|
+
// $FlowExpectedError[prop-missing]
|
|
43
|
+
first.trailing = false;
|
|
44
|
+
// $FlowExpectedError[prop-missing]
|
|
45
|
+
first.printed = false;
|
|
46
|
+
|
|
47
|
+
comments = nonDocblockComments;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// attach comments before mutation. this will ensure that as nodes are
|
|
51
|
+
// cloned / moved around - comments remain in the correct place with respect to the node
|
|
52
|
+
attachComments(comments, ast, code);
|
|
53
|
+
|
|
54
|
+
return {ast, scopeManager, code};
|
|
55
|
+
}
|
|
@@ -0,0 +1,160 @@
|
|
|
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
|
+
*
|
|
8
|
+
* @format
|
|
9
|
+
*/
|
|
10
|
+
'use strict';
|
|
11
|
+
|
|
12
|
+
Object.defineProperty(exports, "__esModule", {
|
|
13
|
+
value: true
|
|
14
|
+
});
|
|
15
|
+
exports.print = print;
|
|
16
|
+
|
|
17
|
+
var _hermesParser = require("hermes-parser");
|
|
18
|
+
|
|
19
|
+
var prettier = _interopRequireWildcard(require("prettier"));
|
|
20
|
+
|
|
21
|
+
var _comments = require("./comments/comments");
|
|
22
|
+
|
|
23
|
+
function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
|
|
24
|
+
|
|
25
|
+
function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
|
|
26
|
+
|
|
27
|
+
function print(ast, originalCode, prettierOptions = {}, visitorKeys) {
|
|
28
|
+
// $FlowExpectedError[incompatible-type] This is now safe to access.
|
|
29
|
+
const program = ast; // The docblock comment is never attached to any AST nodes, since its technically
|
|
30
|
+
// attached to the program. However this is specific to our AST and in order for
|
|
31
|
+
// prettier to correctly print it we need to attach it to the first node in the
|
|
32
|
+
// program body.
|
|
33
|
+
|
|
34
|
+
if (program.docblock != null && program.body.length > 0) {
|
|
35
|
+
const firstNode = program.body[0];
|
|
36
|
+
const docblockComment = program.docblock.comment;
|
|
37
|
+
const leadingComments = (0, _comments.getLeadingCommentsForNode)(firstNode);
|
|
38
|
+
|
|
39
|
+
if (!leadingComments.includes(docblockComment)) {
|
|
40
|
+
(0, _comments.addCommentsToNode)(firstNode, [docblockComment], 'leading');
|
|
41
|
+
}
|
|
42
|
+
} // Fix up the AST to match what prettier expects.
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
mutateASTForPrettier(program, visitorKeys); // we need to delete the comments prop or else prettier will do
|
|
46
|
+
// its own attachment pass after the mutation and duplicate the
|
|
47
|
+
// comments on each node, borking the output
|
|
48
|
+
// $FlowExpectedError[cannot-write]
|
|
49
|
+
|
|
50
|
+
delete program.comments;
|
|
51
|
+
return prettier.format(originalCode, // $FlowExpectedError[incompatible-exact] - we don't want to create a dependency on the prettier types
|
|
52
|
+
{ ...prettierOptions,
|
|
53
|
+
|
|
54
|
+
parser() {
|
|
55
|
+
return program;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function mutateASTForPrettier(rootNode, visitorKeys) {
|
|
62
|
+
_hermesParser.SimpleTraverser.traverse(rootNode, {
|
|
63
|
+
enter(node) {
|
|
64
|
+
// prettier fully expects the parent pointers are NOT set and
|
|
65
|
+
// certain cases can crash due to prettier infinite-looping
|
|
66
|
+
// whilst naively traversing the parent property
|
|
67
|
+
// https://github.com/prettier/prettier/issues/11793
|
|
68
|
+
// $FlowExpectedError[cannot-write]
|
|
69
|
+
delete node.parent; // prettier currently relies on the AST being in the old-school, deprecated AST format for optional chaining
|
|
70
|
+
// so we have to apply their transform to our AST so it can actually format it.
|
|
71
|
+
|
|
72
|
+
if (node.type === 'ChainExpression') {
|
|
73
|
+
const newNode = transformChainExpression(node.expression); // Clear out existing properties
|
|
74
|
+
|
|
75
|
+
for (const k of Object.keys(node)) {
|
|
76
|
+
// $FlowExpectedError[prop-missing]
|
|
77
|
+
delete node[k];
|
|
78
|
+
} // Traverse `newNode` and its children.
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
mutateASTForPrettier(newNode, visitorKeys); // Overwrite `node` to match `newNode` while retaining the reference.
|
|
82
|
+
// $FlowExpectedError[prop-missing]
|
|
83
|
+
// $FlowExpectedError[cannot-write]
|
|
84
|
+
|
|
85
|
+
Object.assign(node, newNode); // Skip traversing the existing nodes since we are replacing them.
|
|
86
|
+
|
|
87
|
+
throw _hermesParser.SimpleTraverser.Skip;
|
|
88
|
+
} // Prettier currently relies on comparing the `node` vs `node.value` start positions to know if an
|
|
89
|
+
// `ObjectTypeProperty` is a method or not (instead of using the `node.method` boolean). To correctly print
|
|
90
|
+
// the node when its not a method we need the start position to be different from the `node.value`s start
|
|
91
|
+
// position.
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
if (node.type === 'ObjectTypeProperty') {
|
|
95
|
+
if (node.method === false && node.kind === 'init' && node.range[0] === 1 && node.value.range[0] === 1) {
|
|
96
|
+
// $FlowExpectedError[cannot-write]
|
|
97
|
+
// $FlowExpectedError[cannot-spread-interface]
|
|
98
|
+
node.value = { ...node.value,
|
|
99
|
+
range: [2, node.value.range[1]]
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
} // Prettier currently relies on comparing the the start positions to know if the import/export specifier should have a
|
|
103
|
+
// rename (eg `Name` vs `Name as Name`) when the name is exactly the same
|
|
104
|
+
// So we need to ensure that the range is always the same to avoid the useless code printing
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
if (node.type === 'ImportSpecifier') {
|
|
108
|
+
if (node.local.name === node.imported.name) {
|
|
109
|
+
if (node.local.range == null) {
|
|
110
|
+
// for our TS-ast printing which has no locs
|
|
111
|
+
// $FlowExpectedError[cannot-write]
|
|
112
|
+
node.local.range = [0, 0];
|
|
113
|
+
} // $FlowExpectedError[cannot-write]
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
node.imported.range = [...node.local.range];
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (node.type === 'ExportSpecifier') {
|
|
121
|
+
if (node.local.name === node.exported.name) {
|
|
122
|
+
if (node.local.range == null) {
|
|
123
|
+
// for our TS-ast printing which has no locs
|
|
124
|
+
// $FlowExpectedError[cannot-write]
|
|
125
|
+
node.local.range = [0, 0];
|
|
126
|
+
} // $FlowExpectedError[cannot-write]
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
node.exported.range = [...node.local.range];
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
},
|
|
133
|
+
|
|
134
|
+
leave() {},
|
|
135
|
+
|
|
136
|
+
visitorKeys
|
|
137
|
+
});
|
|
138
|
+
} // https://github.com/prettier/prettier/blob/d962466a828f8ef51435e3e8840178d90b7ec6cd/src/language-js/parse/postprocess/index.js#L161-L182
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
function transformChainExpression(node) {
|
|
142
|
+
switch (node.type) {
|
|
143
|
+
case 'CallExpression':
|
|
144
|
+
// $FlowExpectedError[cannot-spread-interface]
|
|
145
|
+
return { ...node,
|
|
146
|
+
type: 'OptionalCallExpression',
|
|
147
|
+
callee: transformChainExpression(node.callee)
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
case 'MemberExpression':
|
|
151
|
+
// $FlowExpectedError[cannot-spread-interface]
|
|
152
|
+
return { ...node,
|
|
153
|
+
type: 'OptionalMemberExpression',
|
|
154
|
+
object: transformChainExpression(node.object)
|
|
155
|
+
};
|
|
156
|
+
// No default
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return node;
|
|
160
|
+
}
|
|
@@ -0,0 +1,176 @@
|
|
|
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
|
+
'use strict';
|
|
12
|
+
|
|
13
|
+
import type {MaybeDetachedNode} from '../detachedNode';
|
|
14
|
+
import type {Program, ESNode} from 'hermes-estree';
|
|
15
|
+
|
|
16
|
+
import {SimpleTraverser} from 'hermes-parser';
|
|
17
|
+
import * as prettier from 'prettier';
|
|
18
|
+
import {
|
|
19
|
+
addCommentsToNode,
|
|
20
|
+
getLeadingCommentsForNode,
|
|
21
|
+
} from './comments/comments';
|
|
22
|
+
import type {VisitorKeysType} from 'hermes-parser';
|
|
23
|
+
|
|
24
|
+
export function print(
|
|
25
|
+
ast: MaybeDetachedNode<Program>,
|
|
26
|
+
originalCode: string,
|
|
27
|
+
prettierOptions: {...} = {},
|
|
28
|
+
visitorKeys?: ?VisitorKeysType,
|
|
29
|
+
): string {
|
|
30
|
+
// $FlowExpectedError[incompatible-type] This is now safe to access.
|
|
31
|
+
const program: Program = ast;
|
|
32
|
+
|
|
33
|
+
// The docblock comment is never attached to any AST nodes, since its technically
|
|
34
|
+
// attached to the program. However this is specific to our AST and in order for
|
|
35
|
+
// prettier to correctly print it we need to attach it to the first node in the
|
|
36
|
+
// program body.
|
|
37
|
+
if (program.docblock != null && program.body.length > 0) {
|
|
38
|
+
const firstNode = program.body[0];
|
|
39
|
+
const docblockComment = program.docblock.comment;
|
|
40
|
+
const leadingComments = getLeadingCommentsForNode(firstNode);
|
|
41
|
+
if (!leadingComments.includes(docblockComment)) {
|
|
42
|
+
addCommentsToNode(firstNode, [docblockComment], 'leading');
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Fix up the AST to match what prettier expects.
|
|
47
|
+
mutateASTForPrettier(program, visitorKeys);
|
|
48
|
+
|
|
49
|
+
// we need to delete the comments prop or else prettier will do
|
|
50
|
+
// its own attachment pass after the mutation and duplicate the
|
|
51
|
+
// comments on each node, borking the output
|
|
52
|
+
// $FlowExpectedError[cannot-write]
|
|
53
|
+
delete program.comments;
|
|
54
|
+
|
|
55
|
+
return prettier.format(
|
|
56
|
+
originalCode,
|
|
57
|
+
// $FlowExpectedError[incompatible-exact] - we don't want to create a dependency on the prettier types
|
|
58
|
+
{
|
|
59
|
+
...prettierOptions,
|
|
60
|
+
parser() {
|
|
61
|
+
return program;
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function mutateASTForPrettier(
|
|
68
|
+
rootNode: ESNode,
|
|
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);
|
|
93
|
+
|
|
94
|
+
// Overwrite `node` to match `newNode` while retaining the reference.
|
|
95
|
+
// $FlowExpectedError[prop-missing]
|
|
96
|
+
// $FlowExpectedError[cannot-write]
|
|
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
|
+
};
|
|
164
|
+
|
|
165
|
+
case 'MemberExpression':
|
|
166
|
+
// $FlowExpectedError[cannot-spread-interface]
|
|
167
|
+
return {
|
|
168
|
+
...node,
|
|
169
|
+
type: 'OptionalMemberExpression',
|
|
170
|
+
object: transformChainExpression(node.object),
|
|
171
|
+
};
|
|
172
|
+
// No default
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
return node;
|
|
176
|
+
}
|
|
@@ -14,84 +14,23 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
14
14
|
});
|
|
15
15
|
exports.transform = transform;
|
|
16
16
|
|
|
17
|
-
var
|
|
17
|
+
var _transformAST = require("./transformAST");
|
|
18
18
|
|
|
19
|
-
var
|
|
19
|
+
var _parse = require("./parse");
|
|
20
20
|
|
|
21
|
-
var
|
|
22
|
-
|
|
23
|
-
function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
|
|
24
|
-
|
|
25
|
-
function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
|
|
21
|
+
var _print = require("./print");
|
|
26
22
|
|
|
27
23
|
function transform(originalCode, visitors, prettierOptions = {}) {
|
|
24
|
+
const parseResult = (0, _parse.parse)(originalCode);
|
|
28
25
|
const {
|
|
29
26
|
ast,
|
|
30
27
|
astWasMutated,
|
|
31
28
|
mutatedCode
|
|
32
|
-
} = (0,
|
|
29
|
+
} = (0, _transformAST.transformAST)(parseResult, visitors);
|
|
33
30
|
|
|
34
31
|
if (!astWasMutated) {
|
|
35
32
|
return originalCode;
|
|
36
33
|
}
|
|
37
34
|
|
|
38
|
-
|
|
39
|
-
enter(node) {
|
|
40
|
-
// prettier fully expects the parent pointers are NOT set and
|
|
41
|
-
// certain cases can crash due to prettier infinite-looping
|
|
42
|
-
// whilst naively traversing the parent property
|
|
43
|
-
// https://github.com/prettier/prettier/issues/11793
|
|
44
|
-
// $FlowExpectedError[cannot-write]
|
|
45
|
-
delete node.parent; // prettier currently relies on the AST being in the old-school, deprecated AST format for optional chaining
|
|
46
|
-
// so we have to apply their transform to our AST so it can actually format it.
|
|
47
|
-
|
|
48
|
-
if (node.type === 'ChainExpression') {
|
|
49
|
-
const newNode = transformChainExpression(node.expression); // $FlowExpectedError[cannot-write]
|
|
50
|
-
|
|
51
|
-
delete node.expression; // $FlowExpectedError[prop-missing]
|
|
52
|
-
// $FlowExpectedError[cannot-write]
|
|
53
|
-
|
|
54
|
-
Object.assign(node, newNode);
|
|
55
|
-
}
|
|
56
|
-
},
|
|
57
|
-
|
|
58
|
-
leave() {}
|
|
59
|
-
|
|
60
|
-
}); // we need to delete the comments prop or else prettier will do
|
|
61
|
-
// its own attachment pass after the mutation and duplicate the
|
|
62
|
-
// comments on each node, borking the output
|
|
63
|
-
// $FlowExpectedError[cannot-write]
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
delete ast.comments;
|
|
67
|
-
return prettier.format(mutatedCode, // $FlowExpectedError[incompatible-exact] - we don't want to create a dependency on the prettier types
|
|
68
|
-
{ ...prettierOptions,
|
|
69
|
-
|
|
70
|
-
parser() {
|
|
71
|
-
return ast;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
});
|
|
75
|
-
} // https://github.com/prettier/prettier/blob/d962466a828f8ef51435e3e8840178d90b7ec6cd/src/language-js/parse/postprocess/index.js#L161-L182
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
function transformChainExpression(node) {
|
|
79
|
-
switch (node.type) {
|
|
80
|
-
case 'CallExpression':
|
|
81
|
-
// $FlowExpectedError[cannot-write]
|
|
82
|
-
node.type = 'OptionalCallExpression'; // $FlowExpectedError[cannot-write]
|
|
83
|
-
|
|
84
|
-
node.callee = transformChainExpression(node.callee);
|
|
85
|
-
break;
|
|
86
|
-
|
|
87
|
-
case 'MemberExpression':
|
|
88
|
-
// $FlowExpectedError[cannot-write]
|
|
89
|
-
node.type = 'OptionalMemberExpression'; // $FlowExpectedError[cannot-write]
|
|
90
|
-
|
|
91
|
-
node.object = transformChainExpression(node.object);
|
|
92
|
-
break;
|
|
93
|
-
// No default
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
return node;
|
|
35
|
+
return (0, _print.print)(ast, mutatedCode, prettierOptions);
|
|
97
36
|
}
|