flow-parser 0.317.0 → 0.319.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 (102) hide show
  1. package/oxidized/FlowParser.js +249 -0
  2. package/oxidized/FlowParser.js.flow +457 -0
  3. package/oxidized/FlowParserDeserializer.js +209 -0
  4. package/oxidized/FlowParserDeserializer.js.flow +323 -0
  5. package/oxidized/FlowParserNodeDeserializers.js +1767 -0
  6. package/oxidized/FlowParserNodeDeserializers.js.flow +18 -0
  7. package/oxidized/FlowParserWASM.js +1 -0
  8. package/oxidized/FlowParserWASM.js.flow +119 -0
  9. package/oxidized/HermesAST.js.flow +57 -0
  10. package/oxidized/HermesParserDecodeUTF8String.js +45 -0
  11. package/oxidized/HermesParserDecodeUTF8String.js.flow +65 -0
  12. package/oxidized/ParserOptions.js +8 -0
  13. package/oxidized/ParserOptions.js.flow +55 -0
  14. package/oxidized/babel/TransformESTreeToBabel.js +1055 -0
  15. package/oxidized/babel/TransformESTreeToBabel.js.flow +1349 -0
  16. package/oxidized/estree/StripFlowTypes.js +158 -0
  17. package/oxidized/estree/StripFlowTypes.js.flow +158 -0
  18. package/oxidized/estree/StripFlowTypesForBabel.js +176 -0
  19. package/oxidized/estree/StripFlowTypesForBabel.js.flow +216 -0
  20. package/oxidized/estree/TransformComponentSyntax.js +751 -0
  21. package/oxidized/estree/TransformComponentSyntax.js.flow +864 -0
  22. package/oxidized/estree/TransformEnumSyntax.js +92 -0
  23. package/oxidized/estree/TransformEnumSyntax.js.flow +125 -0
  24. package/oxidized/estree/TransformMatchSyntax.js +990 -0
  25. package/oxidized/estree/TransformMatchSyntax.js.flow +991 -0
  26. package/oxidized/estree/TransformRecordSyntax.js +283 -0
  27. package/oxidized/estree/TransformRecordSyntax.js.flow +310 -0
  28. package/oxidized/generated/ESTreeVisitorKeys.js +234 -0
  29. package/oxidized/generated/ESTreeVisitorKeys.js.flow +22 -0
  30. package/oxidized/getModuleDocblock.js +87 -0
  31. package/oxidized/getModuleDocblock.js.flow +119 -0
  32. package/oxidized/index.js +220 -0
  33. package/oxidized/index.js.flow +183 -0
  34. package/oxidized/oxidized-src/FlowParser.js +249 -0
  35. package/oxidized/oxidized-src/FlowParserDeserializer.js +209 -0
  36. package/oxidized/oxidized-src/FlowParserNodeDeserializers.js +1767 -0
  37. package/oxidized/oxidized-src/HermesParserDecodeUTF8String.js +45 -0
  38. package/oxidized/oxidized-src/ParserOptions.js +8 -0
  39. package/oxidized/oxidized-src/babel/TransformESTreeToBabel.js +1055 -0
  40. package/oxidized/oxidized-src/estree/StripFlowTypes.js +158 -0
  41. package/oxidized/oxidized-src/estree/StripFlowTypesForBabel.js +176 -0
  42. package/oxidized/oxidized-src/estree/TransformComponentSyntax.js +751 -0
  43. package/oxidized/oxidized-src/estree/TransformEnumSyntax.js +92 -0
  44. package/oxidized/oxidized-src/estree/TransformMatchSyntax.js +990 -0
  45. package/oxidized/oxidized-src/estree/TransformRecordSyntax.js +283 -0
  46. package/oxidized/oxidized-src/generated/ESTreeVisitorKeys.js +234 -0
  47. package/oxidized/oxidized-src/getModuleDocblock.js +87 -0
  48. package/oxidized/oxidized-src/index.js +220 -0
  49. package/oxidized/oxidized-src/transform/SimpleTransform.js +95 -0
  50. package/oxidized/oxidized-src/transform/astArrayMutationHelpers.js +52 -0
  51. package/oxidized/oxidized-src/transform/astNodeMutationHelpers.js +142 -0
  52. package/oxidized/oxidized-src/transform/print/comments/comments.js +245 -0
  53. package/oxidized/oxidized-src/transform/print/comments/prettier/common/util.js +235 -0
  54. package/oxidized/oxidized-src/transform/print/comments/prettier/language-js/comments.js +663 -0
  55. package/oxidized/oxidized-src/transform/print/comments/prettier/language-js/loc.js +30 -0
  56. package/oxidized/oxidized-src/transform/print/comments/prettier/language-js/printer-estree.js +23 -0
  57. package/oxidized/oxidized-src/transform/print/comments/prettier/language-js/utils.js +89 -0
  58. package/oxidized/oxidized-src/transform/print/comments/prettier/main/comments.js +374 -0
  59. package/oxidized/oxidized-src/transform/print/comments/prettier/utils/get-last.js +5 -0
  60. package/oxidized/oxidized-src/transform/print/detachedNodeTypes.js +1 -0
  61. package/oxidized/oxidized-src/transform/print/print.js +68 -0
  62. package/oxidized/oxidized-src/traverse/SimpleTraverser.js +90 -0
  63. package/oxidized/oxidized-src/traverse/getVisitorKeys.js +29 -0
  64. package/oxidized/oxidized-src/utils/Builders.js +181 -0
  65. package/oxidized/oxidized-src/utils/GenID.js +37 -0
  66. package/oxidized/oxidized-src/utils/createSyntaxError.js +15 -0
  67. package/oxidized/oxidized-src/utils/isReservedWord.js +53 -0
  68. package/oxidized/oxidized-src/utils/mutateESTreeASTForPrettier.js +85 -0
  69. package/oxidized/transform/SimpleTransform.js +95 -0
  70. package/oxidized/transform/SimpleTransform.js.flow +168 -0
  71. package/oxidized/transform/astArrayMutationHelpers.js +52 -0
  72. package/oxidized/transform/astArrayMutationHelpers.js.flow +71 -0
  73. package/oxidized/transform/astNodeMutationHelpers.js +142 -0
  74. package/oxidized/transform/astNodeMutationHelpers.js.flow +252 -0
  75. package/oxidized/transform/print/comments/comments.js +245 -0
  76. package/oxidized/transform/print/comments/comments.js.flow +330 -0
  77. package/oxidized/transform/print/comments/prettier/common/util.js +235 -0
  78. package/oxidized/transform/print/comments/prettier/language-js/comments.js +663 -0
  79. package/oxidized/transform/print/comments/prettier/language-js/loc.js +30 -0
  80. package/oxidized/transform/print/comments/prettier/language-js/printer-estree.js +23 -0
  81. package/oxidized/transform/print/comments/prettier/language-js/utils.js +89 -0
  82. package/oxidized/transform/print/comments/prettier/main/comments.js +374 -0
  83. package/oxidized/transform/print/comments/prettier/utils/get-last.js +5 -0
  84. package/oxidized/transform/print/detachedNodeTypes.js +1 -0
  85. package/oxidized/transform/print/detachedNodeTypes.js.flow +24 -0
  86. package/oxidized/transform/print/print.js +68 -0
  87. package/oxidized/transform/print/print.js.flow +87 -0
  88. package/oxidized/traverse/SimpleTraverser.js +90 -0
  89. package/oxidized/traverse/SimpleTraverser.js.flow +133 -0
  90. package/oxidized/traverse/getVisitorKeys.js +29 -0
  91. package/oxidized/traverse/getVisitorKeys.js.flow +36 -0
  92. package/oxidized/utils/Builders.js +181 -0
  93. package/oxidized/utils/Builders.js.flow +218 -0
  94. package/oxidized/utils/GenID.js +37 -0
  95. package/oxidized/utils/GenID.js.flow +39 -0
  96. package/oxidized/utils/createSyntaxError.js +15 -0
  97. package/oxidized/utils/createSyntaxError.js.flow +24 -0
  98. package/oxidized/utils/isReservedWord.js +53 -0
  99. package/oxidized/utils/isReservedWord.js.flow +57 -0
  100. package/oxidized/utils/mutateESTreeASTForPrettier.js +85 -0
  101. package/oxidized/utils/mutateESTreeASTForPrettier.js.flow +130 -0
  102. package/package.json +16 -6
@@ -0,0 +1,249 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.parse = parse;
7
+
8
+ var _FlowParserDeserializer = _interopRequireDefault(require("./FlowParserDeserializer"));
9
+
10
+ var _FlowParserWASM = _interopRequireDefault(require("./FlowParserWASM"));
11
+
12
+ var _getModuleDocblock = require("./getModuleDocblock");
13
+
14
+ function _interopRequireDefault(obj) {
15
+ return obj && obj.__esModule ? obj : {
16
+ default: obj
17
+ };
18
+ }
19
+
20
+ let FlowParserWASM;
21
+ let flowParse;
22
+ let flowParseResult_free;
23
+ let flowParseResult_getError;
24
+ let flowParseResult_getErrorLine;
25
+ let flowParseResult_getErrorColumn;
26
+ let flowParseResult_getProgramBuffer;
27
+ let flowParseResult_getPositionBuffer;
28
+ let flowParseResult_getPositionBufferSize;
29
+ let flowParseResult_getStringBuffer;
30
+
31
+ function initFlowParserWASM() {
32
+ if (FlowParserWASM != null) {
33
+ return;
34
+ }
35
+
36
+ FlowParserWASM = (0, _FlowParserWASM.default)({
37
+ quit(_status, toThrow) {
38
+ throw toThrow;
39
+ }
40
+
41
+ });
42
+ flowParse = FlowParserWASM.cwrap('hermesParse', 'number', ['number', 'number', 'number', 'number', 'number', 'number', 'number', 'number', 'number', 'number', 'number', 'number', 'number', 'number', 'number', 'number']);
43
+ flowParseResult_free = FlowParserWASM.cwrap('hermesParseResult_free', 'void', ['number']);
44
+ flowParseResult_getError = FlowParserWASM.cwrap('hermesParseResult_getError', 'string', ['number']);
45
+ flowParseResult_getErrorLine = FlowParserWASM.cwrap('hermesParseResult_getErrorLine', 'number', ['number']);
46
+ flowParseResult_getErrorColumn = FlowParserWASM.cwrap('hermesParseResult_getErrorColumn', 'number', ['number']);
47
+ flowParseResult_getProgramBuffer = FlowParserWASM.cwrap('hermesParseResult_getProgramBuffer', 'number', ['number']);
48
+ flowParseResult_getPositionBuffer = FlowParserWASM.cwrap('hermesParseResult_getPositionBuffer', 'number', ['number']);
49
+ flowParseResult_getPositionBufferSize = FlowParserWASM.cwrap('hermesParseResult_getPositionBufferSize', 'number', ['number']);
50
+ flowParseResult_getStringBuffer = FlowParserWASM.cwrap('hermesParseResult_getStringBuffer', 'number', ['number']);
51
+ }
52
+
53
+ function copyToHeap(buffer, addr) {
54
+ FlowParserWASM.HEAP8.set(buffer, addr);
55
+ FlowParserWASM.HEAP8[addr + buffer.length] = 0;
56
+ }
57
+
58
+ function flag(v) {
59
+ return v === true ? 1 : 0;
60
+ }
61
+
62
+ function parse(source, options) {
63
+ initFlowParserWASM();
64
+ const sourceBuffer = Buffer.from(source, 'utf8');
65
+ const filename = typeof options.sourceFilename === 'string' ? options.sourceFilename : '';
66
+ const filenameBuffer = filename.length > 0 ? Buffer.from(filename, 'utf8') : null;
67
+ let sourceAddr = 0;
68
+ let filenameAddr = 0;
69
+ let filenameLen = 0;
70
+ let parseResult = 0;
71
+
72
+ try {
73
+ sourceAddr = FlowParserWASM._malloc(sourceBuffer.length + 1);
74
+
75
+ if (!sourceAddr) {
76
+ throw new Error('Parser out of memory');
77
+ }
78
+
79
+ if (filenameBuffer != null) {
80
+ filenameAddr = FlowParserWASM._malloc(filenameBuffer.length + 1);
81
+
82
+ if (!filenameAddr) {
83
+ throw new Error('Parser out of memory');
84
+ }
85
+
86
+ copyToHeap(filenameBuffer, filenameAddr);
87
+ filenameLen = filenameBuffer.length + 1;
88
+ }
89
+
90
+ copyToHeap(sourceBuffer, sourceAddr);
91
+ const enableTypes = options.enableTypes === false ? 0 : 1;
92
+ const sourceTypeCode = options.sourceType === 'script' ? 1 : options.sourceType === 'module' ? 2 : 0;
93
+ parseResult = flowParse(sourceAddr, sourceBuffer.length + 1, filenameAddr, filenameLen, flag(options.enableExperimentalComponentSyntax), flag(options.enableExperimentalFlowMatchSyntax), flag(options.enableExperimentalDecorators), flag(options.tokens), flag(options.allowReturnOutsideFunction), flag(options.assertOperator), flag(options.enableEnums), flag(options.enableRecords), enableTypes, sourceTypeCode, flag(options.enableTypesPragmaDetection), 0);
94
+ const programBuffer = flowParseResult_getProgramBuffer(parseResult);
95
+
96
+ if (!programBuffer) {
97
+ const err = flowParseResult_getError(parseResult);
98
+ const syntaxError = new SyntaxError(err || 'unknown parse error');
99
+ syntaxError.loc = {
100
+ line: flowParseResult_getErrorLine(parseResult),
101
+ column: flowParseResult_getErrorColumn(parseResult)
102
+ };
103
+ throw syntaxError;
104
+ }
105
+
106
+ const deserializer = new _FlowParserDeserializer.default(programBuffer, flowParseResult_getPositionBuffer(parseResult), flowParseResult_getPositionBufferSize(parseResult), flowParseResult_getStringBuffer(parseResult), FlowParserWASM, options);
107
+ const ast = deserializer.deserialize();
108
+
109
+ if (ast.type !== 'Program') {
110
+ throw new Error(`Expected Program, got ${ast.type}`);
111
+ }
112
+
113
+ const sourceFilename = typeof options.sourceFilename === 'string' ? options.sourceFilename : null;
114
+ const visitedNodes = new WeakSet();
115
+ const locRanges = new WeakMap();
116
+
117
+ function fixLocs(node) {
118
+ if (node == null || typeof node !== 'object') {
119
+ return;
120
+ }
121
+
122
+ const wireNode = node;
123
+
124
+ if (visitedNodes.has(wireNode)) {
125
+ return;
126
+ }
127
+
128
+ visitedNodes.add(wireNode);
129
+
130
+ if (wireNode.loc != null) {
131
+ const loc = wireNode.loc;
132
+ let range = locRanges.get(loc);
133
+
134
+ if (range == null) {
135
+ const rangeStart = loc.rangeStart;
136
+ const rangeEnd = loc.rangeEnd;
137
+
138
+ if (rangeStart == null || rangeEnd == null) {
139
+ throw new Error('Expected serialized source range');
140
+ }
141
+
142
+ loc.source = sourceFilename;
143
+ range = [rangeStart, rangeEnd];
144
+ locRanges.set(loc, range);
145
+ delete loc.rangeStart;
146
+ delete loc.rangeEnd;
147
+ }
148
+
149
+ wireNode.range = range;
150
+ }
151
+
152
+ for (const key of Object.keys(wireNode)) {
153
+ const val = wireNode[key];
154
+
155
+ if (Array.isArray(val)) {
156
+ for (const child of val) {
157
+ fixLocs(child);
158
+ }
159
+ } else {
160
+ fixLocs(val);
161
+ }
162
+ }
163
+ }
164
+
165
+ fixLocs(ast);
166
+
167
+ if (options.throwOnParseErrors !== false) {
168
+ const errors = ast.errors;
169
+
170
+ if (errors != null && errors.length > 0) {
171
+ const first = errors[0];
172
+ const start = first.loc.start;
173
+
174
+ if (start == null) {
175
+ throw new Error('Expected serialized parser error start location');
176
+ }
177
+
178
+ const line = start.line;
179
+ const column = start.column;
180
+ const syntaxError = new SyntaxError(`${first.message} (${line}:${column})`);
181
+ syntaxError.loc = {
182
+ line,
183
+ column
184
+ };
185
+ throw syntaxError;
186
+ }
187
+ }
188
+
189
+ if (options.sourceType === 'script' || options.sourceType === 'module') {
190
+ ast.sourceType = options.sourceType;
191
+ } else {
192
+ ast.sourceType = detectSourceType(ast);
193
+ }
194
+
195
+ ast.docblock = (0, _getModuleDocblock.getModuleDocblock)(ast);
196
+ return asProgram(ast);
197
+ } finally {
198
+ if (parseResult !== 0) {
199
+ flowParseResult_free(parseResult);
200
+ }
201
+
202
+ if (sourceAddr !== 0) {
203
+ FlowParserWASM._free(sourceAddr);
204
+ }
205
+
206
+ if (filenameAddr !== 0) {
207
+ FlowParserWASM._free(filenameAddr);
208
+ }
209
+ }
210
+ }
211
+
212
+ function asProgram(ast) {
213
+ const program = ast;
214
+ return program;
215
+ }
216
+
217
+ function detectSourceType(program) {
218
+ if (!Array.isArray(program.body)) {
219
+ return 'script';
220
+ }
221
+
222
+ for (const stmt of program.body) {
223
+ if (stmt == null) {
224
+ continue;
225
+ }
226
+
227
+ switch (stmt.type) {
228
+ case 'ImportDeclaration':
229
+ if (stmt.importKind === 'value' || stmt.importKind == null) {
230
+ return 'module';
231
+ }
232
+
233
+ break;
234
+
235
+ case 'ExportDefaultDeclaration':
236
+ return 'module';
237
+
238
+ case 'ExportNamedDeclaration':
239
+ case 'ExportAllDeclaration':
240
+ if (stmt.exportKind === 'value' || stmt.exportKind == null) {
241
+ return 'module';
242
+ }
243
+
244
+ break;
245
+ }
246
+ }
247
+
248
+ return 'script';
249
+ }
@@ -0,0 +1,457 @@
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
8
+ * @format
9
+ */
10
+
11
+ 'use strict';
12
+
13
+ import type {Program} from 'flow-estree';
14
+ import type {ParserOptions} from './ParserOptions';
15
+ import type {FlowParserProgram} from './FlowParserDeserializer';
16
+ import type {FlowParserWASM as FlowParserWASMType} from './FlowParserWASM';
17
+
18
+ import FlowParserDeserializer from './FlowParserDeserializer';
19
+ import FlowParserWASMModule from './FlowParserWASM';
20
+ import {getModuleDocblock} from './getModuleDocblock';
21
+
22
+ type FlowParse = (
23
+ number,
24
+ number,
25
+ number,
26
+ number,
27
+ number,
28
+ number,
29
+ number,
30
+ number,
31
+ number,
32
+ number,
33
+ number,
34
+ number,
35
+ number,
36
+ number,
37
+ number,
38
+ number,
39
+ ) => number;
40
+
41
+ type WireSourceLocation = {
42
+ source?: ?string,
43
+ start?: unknown,
44
+ end?: unknown,
45
+ rangeStart?: number,
46
+ rangeEnd?: number,
47
+ [string]: unknown,
48
+ };
49
+
50
+ type WireNode = {
51
+ loc?: WireSourceLocation,
52
+ range?: [number, number],
53
+ [string]: unknown,
54
+ };
55
+
56
+ let FlowParserWASM: FlowParserWASMType;
57
+ let flowParse: FlowParse;
58
+ let flowParseResult_free: number => void;
59
+ let flowParseResult_getError: number => string;
60
+ let flowParseResult_getErrorLine: number => number;
61
+ let flowParseResult_getErrorColumn: number => number;
62
+ let flowParseResult_getProgramBuffer: number => number;
63
+ let flowParseResult_getPositionBuffer: number => number;
64
+ let flowParseResult_getPositionBufferSize: number => number;
65
+ let flowParseResult_getStringBuffer: number => number;
66
+
67
+ /**
68
+ * Init the WASM wrapper code generated by `emscripten` for the
69
+ * Flow Rust parser WASM binary.
70
+ */
71
+ function initFlowParserWASM(): void {
72
+ if (FlowParserWASM != null) {
73
+ return;
74
+ }
75
+
76
+ FlowParserWASM = FlowParserWASMModule({
77
+ quit(_status, toThrow) {
78
+ throw toThrow;
79
+ },
80
+ });
81
+
82
+ // The Flow Rust WASM binary exports an extended C ABI: the original
83
+ // Hermes signature plus a (sourceFilename, sourceFilenameSize) pair that
84
+ // lets the parser switch on file extension (.flow / .d.ts / .d.cts /
85
+ // .d.mts) for ambient-context handling, plus an explicit
86
+ // `enableDecorators` flag (Stage-1 decorator syntax) so fixtures that
87
+ // disable decorators can exercise the unexpected-`@` error path, an
88
+ // `assertOperator` flag to enable Flow's `expr!` non-null assertion,
89
+ // `enableEnums` / `enableRecords` flags that mirror OCaml's
90
+ // `default_parse_options` (both off by default; fixtures that use enum
91
+ // or record syntax opt in via their `.options.json`), an `enableTypes`
92
+ // flag that mirrors OCaml's `types` option (on by default; fixtures that
93
+ // need to exercise the no-type-grammar path opt out via `types: false`),
94
+ // a `sourceType` integer (0=unspecified, 1=script, 2=module), and a final
95
+ // zero to keep Flow's legacy comment syntax disabled. The Rust parser does
96
+ // docblock `@flow` pragma detection internally when
97
+ // `enableTypesPragmaDetection` is set; the JS adapter `parse()` resolves
98
+ // `flow: 'detect'` to that flag.
99
+ flowParse = FlowParserWASM.cwrap('hermesParse', 'number', [
100
+ 'number',
101
+ 'number',
102
+ 'number',
103
+ 'number',
104
+ 'number',
105
+ 'number',
106
+ 'number',
107
+ 'number',
108
+ 'number',
109
+ 'number',
110
+ 'number',
111
+ 'number',
112
+ 'number',
113
+ 'number',
114
+ 'number',
115
+ 'number',
116
+ ]);
117
+
118
+ flowParseResult_free = FlowParserWASM.cwrap(
119
+ 'hermesParseResult_free',
120
+ 'void',
121
+ ['number'],
122
+ );
123
+
124
+ flowParseResult_getError = FlowParserWASM.cwrap(
125
+ 'hermesParseResult_getError',
126
+ 'string',
127
+ ['number'],
128
+ );
129
+
130
+ flowParseResult_getErrorLine = FlowParserWASM.cwrap(
131
+ 'hermesParseResult_getErrorLine',
132
+ 'number',
133
+ ['number'],
134
+ );
135
+
136
+ flowParseResult_getErrorColumn = FlowParserWASM.cwrap(
137
+ 'hermesParseResult_getErrorColumn',
138
+ 'number',
139
+ ['number'],
140
+ );
141
+
142
+ flowParseResult_getProgramBuffer = FlowParserWASM.cwrap(
143
+ 'hermesParseResult_getProgramBuffer',
144
+ 'number',
145
+ ['number'],
146
+ );
147
+
148
+ flowParseResult_getPositionBuffer = FlowParserWASM.cwrap(
149
+ 'hermesParseResult_getPositionBuffer',
150
+ 'number',
151
+ ['number'],
152
+ );
153
+
154
+ flowParseResult_getPositionBufferSize = FlowParserWASM.cwrap(
155
+ 'hermesParseResult_getPositionBufferSize',
156
+ 'number',
157
+ ['number'],
158
+ );
159
+
160
+ flowParseResult_getStringBuffer = FlowParserWASM.cwrap(
161
+ 'hermesParseResult_getStringBuffer',
162
+ 'number',
163
+ ['number'],
164
+ );
165
+ }
166
+
167
+ // Copy a string into the WASM heap and null-terminate
168
+ function copyToHeap(buffer: Buffer, addr: number): void {
169
+ FlowParserWASM.HEAP8.set(buffer, addr);
170
+ FlowParserWASM.HEAP8[addr + buffer.length] = 0;
171
+ }
172
+
173
+ // Coerce any option value to the 0/1 expected by the C ABI. Anything other
174
+ // than the literal boolean `true` is treated as `false` so callers can't
175
+ // accidentally pass `undefined`/`null`/strings and get NaN at the cwrap
176
+ // boundary.
177
+ function flag(v: unknown): 0 | 1 {
178
+ return v === true ? 1 : 0;
179
+ }
180
+
181
+ function parse(source: string, options: ParserOptions): Program {
182
+ initFlowParserWASM();
183
+
184
+ const sourceBuffer = Buffer.from(source, 'utf8');
185
+ // Plumb the source filename into WASM so the parser can inspect file
186
+ // extensions (.flow / .d.ts / .d.cts / .d.mts) for ambient context. When
187
+ // no filename is provided we pass NULL/0 — the parser falls back to its
188
+ // non-ambient defaults.
189
+ const filename =
190
+ typeof options.sourceFilename === 'string' ? options.sourceFilename : '';
191
+ const filenameBuffer =
192
+ filename.length > 0 ? Buffer.from(filename, 'utf8') : null;
193
+
194
+ // All wasm allocations live inside a single try/finally so a throw from
195
+ // any malloc, copyToHeap, or the parse call itself frees everything we
196
+ // managed to allocate. Earlier versions allocated and partially populated
197
+ // before entering the try, so a throw from copyToHeap leaked the wasm
198
+ // address back to the heap.
199
+ let sourceAddr = 0;
200
+ let filenameAddr = 0;
201
+ let filenameLen = 0;
202
+ let parseResult = 0;
203
+ try {
204
+ sourceAddr = FlowParserWASM._malloc(sourceBuffer.length + 1);
205
+ if (!sourceAddr) {
206
+ throw new Error('Parser out of memory');
207
+ }
208
+ if (filenameBuffer != null) {
209
+ filenameAddr = FlowParserWASM._malloc(filenameBuffer.length + 1);
210
+ if (!filenameAddr) {
211
+ throw new Error('Parser out of memory');
212
+ }
213
+ copyToHeap(filenameBuffer, filenameAddr);
214
+ filenameLen = filenameBuffer.length + 1;
215
+ }
216
+ copyToHeap(sourceBuffer, sourceAddr);
217
+
218
+ // `enableTypes` mirrors OCaml's `types` option, which defaults to true.
219
+ // Fixtures opt out via `types: false` to exercise the no-type-grammar
220
+ // path (e.g. ts_syntax fixtures testing that `as`/`satisfies` are not
221
+ // parsed when types are disabled).
222
+ const enableTypes = options.enableTypes === false ? 0 : 1;
223
+ // `sourceType` is sent to Rust as an integer code:
224
+ // 0 = unspecified / parser default
225
+ // 1 = 'script'
226
+ // 2 = 'module'
227
+ // All three map to the same parse on the Rust side: flow_parser has no
228
+ // script/module gate, top-level imports/exports are always accepted,
229
+ // and top-level `await` is always rejected (matching OCaml flow_parser,
230
+ // which also pins `allow_await:false` at the top level). The integer
231
+ // round-trips for diagnostics; semantics are identical.
232
+ const sourceTypeCode =
233
+ options.sourceType === 'script'
234
+ ? 1
235
+ : options.sourceType === 'module'
236
+ ? 2
237
+ : 0;
238
+ parseResult = flowParse(
239
+ sourceAddr,
240
+ sourceBuffer.length + 1,
241
+ filenameAddr,
242
+ filenameLen,
243
+ flag(options.enableExperimentalComponentSyntax),
244
+ flag(options.enableExperimentalFlowMatchSyntax),
245
+ // Decorators default to disabled — OCaml's `default_parse_options`
246
+ // sets `esproposal_decorators = false`. Opt in via
247
+ // `enableExperimentalDecorators: true`.
248
+ flag(options.enableExperimentalDecorators),
249
+ flag(options.tokens),
250
+ flag(options.allowReturnOutsideFunction),
251
+ // `assertOperator` enables Flow's `expr!` non-null assertion.
252
+ flag(options.assertOperator),
253
+ // `enableEnums` / `enableRecords` mirror OCaml's `default_parse_options`
254
+ // (both default to false; fixtures opt in via their `.options.json`).
255
+ flag(options.enableEnums),
256
+ flag(options.enableRecords),
257
+ enableTypes,
258
+ sourceTypeCode,
259
+ // When set, the Rust parser scans the docblock for `@flow`. Files with
260
+ // the pragma parse all Flow syntax; files without it still parse
261
+ // unambiguous Flow syntax such as `import type`.
262
+ // The JS public `parse()` in src/index.js sets this when the caller
263
+ // requests `flow: 'detect'`; raw direct callers of `FlowParser.parse`
264
+ // (e.g. the wasm fixture runner) leave it off and supply
265
+ // `enableTypes` directly.
266
+ flag(options.enableTypesPragmaDetection),
267
+ 0,
268
+ );
269
+
270
+ // The Rust parser always returns a (possibly partial) AST plus an
271
+ // error list, mirroring the OCaml behaviour. Errors are surfaced as
272
+ // `program.errors` by the deserializer; only fall back to throwing if
273
+ // we got back no program buffer at all (e.g. invalid UTF-8).
274
+ const programBuffer = flowParseResult_getProgramBuffer(parseResult);
275
+ if (!programBuffer) {
276
+ const err = flowParseResult_getError(parseResult);
277
+ const syntaxError = new SyntaxError(err || 'unknown parse error');
278
+ // $FlowExpectedError[prop-missing] SyntaxError loc is a parser extension.
279
+ syntaxError.loc = {
280
+ line: flowParseResult_getErrorLine(parseResult),
281
+ column: flowParseResult_getErrorColumn(parseResult),
282
+ };
283
+ throw syntaxError;
284
+ }
285
+
286
+ const deserializer = new FlowParserDeserializer(
287
+ programBuffer,
288
+ flowParseResult_getPositionBuffer(parseResult),
289
+ flowParseResult_getPositionBufferSize(parseResult),
290
+ flowParseResult_getStringBuffer(parseResult),
291
+ FlowParserWASM,
292
+ options,
293
+ );
294
+ const ast = deserializer.deserialize();
295
+ if (ast.type !== 'Program') {
296
+ throw new Error(`Expected Program, got ${ast.type}`);
297
+ }
298
+
299
+ // Wire→ESTree loc/range normalization. The wire format carries
300
+ // `rangeStart` / `rangeEnd` on the loc object; ESTree consumers expect a
301
+ // `range: [start, end]` array on the *node* itself, plus a clean
302
+ // `loc: { source, start, end }`. This walk is the parser-level analog of
303
+ // upstream HermesToESTreeAdapter.transform()'s mandatory range conversion
304
+ // (HermesToESTreeAdapter.js:39 — `node.range = [loc.rangeStart, loc.rangeEnd]`).
305
+ // Lives here (not in src/index.js) so callers that bypass the public
306
+ // hermes-parser-compatibility wrapper (e.g. the wasm fixture runner that
307
+ // tests raw Flow ESTree parity with the OCaml parser) still get the
308
+ // canonical ESTree loc/range shape.
309
+ //
310
+ // Track visited nodes so a malformed AST with a cycle doesn't blow the
311
+ // stack; track visited locs separately because two nodes can share a loc
312
+ // reference and we must read `rangeStart`/`rangeEnd` before deleting them.
313
+ const sourceFilename =
314
+ typeof options.sourceFilename === 'string'
315
+ ? options.sourceFilename
316
+ : null;
317
+ const visitedNodes: WeakSet<WireNode> = new WeakSet();
318
+ const locRanges: WeakMap<WireSourceLocation, [number, number]> =
319
+ new WeakMap();
320
+ function fixLocs(node: unknown): void {
321
+ if (node == null || typeof node !== 'object') {
322
+ return;
323
+ }
324
+ // $FlowFixMe[incompatible-variance] Dynamic ESTree serializer object.
325
+ // $FlowFixMe[incompatible-type] Dynamic ESTree serializer object.
326
+ const wireNode: WireNode = node;
327
+ if (visitedNodes.has(wireNode)) {
328
+ return;
329
+ }
330
+ visitedNodes.add(wireNode);
331
+ if (wireNode.loc != null) {
332
+ const loc = wireNode.loc;
333
+ let range = locRanges.get(loc);
334
+ if (range == null) {
335
+ const rangeStart = loc.rangeStart;
336
+ const rangeEnd = loc.rangeEnd;
337
+ if (rangeStart == null || rangeEnd == null) {
338
+ throw new Error('Expected serialized source range');
339
+ }
340
+ loc.source = sourceFilename;
341
+ range = [rangeStart, rangeEnd];
342
+ locRanges.set(loc, range);
343
+ delete loc.rangeStart;
344
+ delete loc.rangeEnd;
345
+ }
346
+ wireNode.range = range;
347
+ }
348
+ for (const key of Object.keys(wireNode)) {
349
+ const val = wireNode[key];
350
+ if (Array.isArray(val)) {
351
+ for (const child of val) {
352
+ fixLocs(child);
353
+ }
354
+ } else {
355
+ fixLocs(val);
356
+ }
357
+ }
358
+ }
359
+ fixLocs(ast);
360
+
361
+ // First-error-throw lives at the wasm boundary so the public
362
+ // src/index.js parse() stays close to upstream. Direct callers (e.g.
363
+ // the wasm fixture runner that wants raw ast.errors instead of a
364
+ // throw) opt out via `throwOnParseErrors: false`. Format mirrors
365
+ // HermesASTAdapter.formatError — `${message} (${line}:${column})`.
366
+ // `allowReturnOutsideFunction` is honored in the Rust parser via
367
+ // ParseOptions; invalid RegExp flags are no longer surfaced as parse
368
+ // errors at the Rust source — both filters that used to live here
369
+ // are gone.
370
+ if (options.throwOnParseErrors !== false) {
371
+ const errors = ast.errors;
372
+ if (errors != null && errors.length > 0) {
373
+ const first = errors[0];
374
+ const start = first.loc.start;
375
+ if (start == null) {
376
+ throw new Error('Expected serialized parser error start location');
377
+ }
378
+ const line = start.line;
379
+ const column = start.column;
380
+ const syntaxError = new SyntaxError(
381
+ `${first.message} (${line}:${column})`,
382
+ );
383
+ // $FlowExpectedError[prop-missing] SyntaxError loc is a parser extension.
384
+ syntaxError.loc = {line, column};
385
+ throw syntaxError;
386
+ }
387
+ }
388
+
389
+ // Category C - G (shit.md): module-vs-script detection mirroring
390
+ // HermesASTAdapter.setModuleSourceType. Walks the top-level program body
391
+ // for import/export forms; a single value-kind import or export is enough
392
+ // to classify the program as a module. Type-only imports and re-exports
393
+ // keep the program in script mode. We check only the top-level body
394
+ // (matching upstream) because nested imports are not legal at runtime.
395
+ // When the caller pinned `sourceType: 'script'` or `'module'` we honor
396
+ // it verbatim; `'unambiguous'` was already deleted from `options` in
397
+ // src/index.js's getOptions so it falls through to detection.
398
+ if (options.sourceType === 'script' || options.sourceType === 'module') {
399
+ ast.sourceType = options.sourceType;
400
+ } else {
401
+ ast.sourceType = detectSourceType(ast);
402
+ }
403
+
404
+ // Category C - I (shit.md): attach the module docblock derived from the
405
+ // leading block comment. Mirrors HermesToESTreeAdapter.mapProgram.
406
+ ast.docblock = getModuleDocblock(ast);
407
+
408
+ return asProgram(ast);
409
+ } finally {
410
+ if (parseResult !== 0) {
411
+ flowParseResult_free(parseResult);
412
+ }
413
+ if (sourceAddr !== 0) {
414
+ FlowParserWASM._free(sourceAddr);
415
+ }
416
+ if (filenameAddr !== 0) {
417
+ FlowParserWASM._free(filenameAddr);
418
+ }
419
+ }
420
+ }
421
+
422
+ function asProgram(ast: FlowParserProgram): Program {
423
+ // FlowParserProgram is the deserialized wire shape. `fixLocs` above mutates
424
+ // that shape into the public ESTree Program shape, including loc/range
425
+ // fields that Flow cannot prove from the serializer protocol.
426
+ const program: $FlowFixMe = ast;
427
+ return program;
428
+ }
429
+
430
+ function detectSourceType(program: FlowParserProgram): 'script' | 'module' {
431
+ if (!Array.isArray(program.body)) {
432
+ return 'script';
433
+ }
434
+ for (const stmt of program.body) {
435
+ if (stmt == null) {
436
+ continue;
437
+ }
438
+ switch (stmt.type) {
439
+ case 'ImportDeclaration':
440
+ if (stmt.importKind === 'value' || stmt.importKind == null) {
441
+ return 'module';
442
+ }
443
+ break;
444
+ case 'ExportDefaultDeclaration':
445
+ return 'module';
446
+ case 'ExportNamedDeclaration':
447
+ case 'ExportAllDeclaration':
448
+ if (stmt.exportKind === 'value' || stmt.exportKind == null) {
449
+ return 'module';
450
+ }
451
+ break;
452
+ }
453
+ }
454
+ return 'script';
455
+ }
456
+
457
+ export {parse};