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.
Files changed (62) hide show
  1. package/dist/detachedNode.js +9 -27
  2. package/dist/detachedNode.js.flow +18 -31
  3. package/dist/generated/TransformCloneSignatures.js.flow +18 -0
  4. package/dist/generated/TransformModifySignatures.js.flow +14 -0
  5. package/dist/generated/TransformReplaceSignatures.js.flow +15 -1
  6. package/dist/generated/node-types.js +253 -59
  7. package/dist/generated/node-types.js.flow +257 -102
  8. package/dist/generated/special-case-node-types/Comment.js +36 -0
  9. package/dist/generated/special-case-node-types/Comment.js.flow +36 -0
  10. package/dist/generated/special-case-node-types/DeclareExportDeclaration.js +55 -0
  11. package/dist/generated/special-case-node-types/DeclareExportDeclaration.js.flow +97 -0
  12. package/dist/generated/special-case-node-types/ExportNamedDeclaration.js +42 -0
  13. package/dist/generated/special-case-node-types/ExportNamedDeclaration.js.flow +75 -0
  14. package/dist/generated/special-case-node-types/Literal.js +97 -0
  15. package/dist/generated/special-case-node-types/Literal.js.flow +139 -0
  16. package/dist/generated/special-case-node-types/ObjectTypeProperty.js +73 -0
  17. package/dist/generated/special-case-node-types/ObjectTypeProperty.js.flow +107 -0
  18. package/dist/generated/special-case-node-types/Property.js +136 -0
  19. package/dist/generated/special-case-node-types/Property.js.flow +237 -0
  20. package/dist/generated/special-case-node-types/misc.js +119 -0
  21. package/dist/generated/special-case-node-types/misc.js.flow +205 -0
  22. package/dist/generated/special-case-node-types.js +42 -174
  23. package/dist/generated/special-case-node-types.js.flow +7 -274
  24. package/dist/index.js +19 -3
  25. package/dist/index.js.flow +6 -2
  26. package/dist/transform/TransformContext.js +1 -1
  27. package/dist/transform/TransformContext.js.flow +2 -2
  28. package/dist/transform/comments/comments.js +11 -0
  29. package/dist/transform/comments/comments.js.flow +16 -1
  30. package/dist/transform/comments/prettier/main/comments.js +1 -1
  31. package/dist/transform/comments/prettier/main/comments.js.flow +2 -1
  32. package/dist/transform/mutations/InsertStatement.js +3 -3
  33. package/dist/transform/mutations/InsertStatement.js.flow +3 -3
  34. package/dist/transform/mutations/RemoveComment.js +3 -3
  35. package/dist/transform/mutations/RemoveComment.js.flow +3 -5
  36. package/dist/transform/mutations/RemoveNode.js +2 -2
  37. package/dist/transform/mutations/RemoveNode.js.flow +2 -2
  38. package/dist/transform/mutations/RemoveStatement.js +2 -2
  39. package/dist/transform/mutations/RemoveStatement.js.flow +2 -2
  40. package/dist/transform/mutations/ReplaceNode.js +4 -6
  41. package/dist/transform/mutations/ReplaceNode.js.flow +2 -3
  42. package/dist/transform/mutations/ReplaceStatementWithMany.js +2 -2
  43. package/dist/transform/mutations/ReplaceStatementWithMany.js.flow +2 -2
  44. package/dist/transform/mutations/utils/getStatementParent.js +3 -2
  45. package/dist/transform/mutations/utils/getStatementParent.js.flow +5 -2
  46. package/dist/transform/parse.js +55 -0
  47. package/dist/transform/parse.js.flow +55 -0
  48. package/dist/transform/print.js +160 -0
  49. package/dist/transform/print.js.flow +176 -0
  50. package/dist/transform/transform.js +6 -67
  51. package/dist/transform/transform.js.flow +6 -69
  52. package/dist/transform/{getTransformedAST.js → transformAST.js} +7 -31
  53. package/dist/transform/{getTransformedAST.js.flow → transformAST.js.flow} +7 -32
  54. package/dist/traverse/traverse.js +36 -35
  55. package/dist/traverse/traverse.js.flow +45 -26
  56. package/package.json +5 -4
  57. package/dist/getVisitorKeys.js +0 -33
  58. package/dist/getVisitorKeys.js.flow +0 -31
  59. package/dist/transform/mutations/utils/arrayUtils.js +0 -43
  60. package/dist/transform/mutations/utils/arrayUtils.js.flow +0 -50
  61. package/dist/traverse/SimpleTraverser.js +0 -118
  62. 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 _arrayUtils = require("./utils/arrayUtils");
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] = (0, _arrayUtils.replaceInArray)(parent[replacementParent.key], replacementParent.targetIndex, [mutation.nodeToReplaceWith]);
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, _getVisitorKeys.getVisitorKeys)(parent)) {
65
- if ((0, _getVisitorKeys.isNode)( // $FlowExpectedError[prop-missing]
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 {replaceInArray} from './utils/arrayUtils';
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 _arrayUtils = require("./utils/arrayUtils");
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] = (0, _arrayUtils.replaceInArray)(parent[replacementParent.key], replacementParent.targetIndex, mutation.nodesToReplaceWith);
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 {replaceInArray} from './utils/arrayUtils';
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
- parent, 'left', 'right');
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 prettier = _interopRequireWildcard(require("prettier"));
17
+ var _transformAST = require("./transformAST");
18
18
 
19
- var _getTransformedAST = require("./getTransformedAST");
19
+ var _parse = require("./parse");
20
20
 
21
- var _SimpleTraverser = require("../traverse/SimpleTraverser");
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, _getTransformedAST.getTransformedAST)(originalCode, visitors);
29
+ } = (0, _transformAST.transformAST)(parseResult, visitors);
33
30
 
34
31
  if (!astWasMutated) {
35
32
  return originalCode;
36
33
  }
37
34
 
38
- _SimpleTraverser.SimpleTraverser.traverse(ast, {
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
  }