diff --git a/tools/hermes-parser/js/babel-plugin-syntax-hermes-parser/__tests__/babel-plugin-syntax-hermes-parser-test.js b/tools/hermes-parser/js/babel-plugin-syntax-hermes-parser/__tests__/babel-plugin-syntax-hermes-parser-test.js index a03108c2b20..2b503f520ae 100644 --- a/tools/hermes-parser/js/babel-plugin-syntax-hermes-parser/__tests__/babel-plugin-syntax-hermes-parser-test.js +++ b/tools/hermes-parser/js/babel-plugin-syntax-hermes-parser/__tests__/babel-plugin-syntax-hermes-parser-test.js @@ -16,22 +16,44 @@ import {transformSync} from '@babel/core'; import hermesParserPlugin from '../src'; import * as HermesParser from 'hermes-parser'; -const MODULE_PREAMBLE = '"use strict";\n\n'; +const MODULE_PREAMBLE = '// @flow\n\n"use strict";\n\n'; +const NON_FLOW_MODULE_PREAMBLE = '"use strict";\n\n'; describe('babel-plugin-syntax-hermes-parser', () => { - test('test basic parsing', () => { - const parseSpy = jest.spyOn(HermesParser, 'parse'); - const code = MODULE_PREAMBLE + 'const a = 1;'; + const parseSpy = jest.spyOn(HermesParser, 'parse'); + + afterEach(() => { + parseSpy.mockClear(); + }); + + test('should parse Flow files', () => { + const code = MODULE_PREAMBLE + 'const a: number = 1;'; + const output = transformSync(code, { + plugins: [hermesParserPlugin], + }); + expect(output.code).toMatchInlineSnapshot(` + ""use strict"; + + const a = 1;" + `); + expect(parseSpy).toBeCalledTimes(1); + }); + + test('should parse files without @flow annotation', () => { + const code = NON_FLOW_MODULE_PREAMBLE + 'const a: number = 1;'; const output = transformSync(code, { plugins: [hermesParserPlugin], }); - expect(output.code).toBe(code); + expect(output.code).toMatchInlineSnapshot(` + ""use strict"; + + const a = 1;" + `); expect(parseSpy).toBeCalledTimes(1); }); - test('test skip TS', () => { - const parseSpy = jest.spyOn(HermesParser, 'parse'); - const code = MODULE_PREAMBLE + 'const a: string = 1;'; + test('should skip TypeScript files', () => { + const code = NON_FLOW_MODULE_PREAMBLE + 'const a: number = 1;'; const output = transformSync(code, { plugins: [hermesParserPlugin], filename: 'foo.ts', @@ -44,8 +66,7 @@ describe('babel-plugin-syntax-hermes-parser', () => { expect(parseSpy).toBeCalledTimes(0); }); - test('test component syntax parsing', () => { - const parseSpy = jest.spyOn(HermesParser, 'parse'); + test('should parse component syntax when enabled', () => { const code = MODULE_PREAMBLE + 'component Foo() {}'; const output = transformSync(code, { plugins: [hermesParserPlugin], @@ -60,4 +81,28 @@ describe('babel-plugin-syntax-hermes-parser', () => { `); expect(parseSpy).toBeCalledTimes(1); }); + + describe("with parseLangTypes = 'flow'", () => { + test('should parse Flow files', () => { + const code = MODULE_PREAMBLE + 'const a: number = 1;'; + const output = transformSync(code, { + plugins: [hermesParserPlugin], + }); + expect(output.code).toMatchInlineSnapshot(` + ""use strict"; + + const a = 1;" + `); + expect(parseSpy).toBeCalledTimes(1); + }); + + test('should skip files without @flow annotation ', () => { + const code = NON_FLOW_MODULE_PREAMBLE + 'class Foo {}'; + const output = transformSync(code, { + plugins: [[hermesParserPlugin, {parseLangTypes: 'flow'}]], + }); + expect(output.code).toBe(code); + expect(parseSpy).toBeCalledTimes(0); + }); + }); }); diff --git a/tools/hermes-parser/js/babel-plugin-syntax-hermes-parser/src/index.js b/tools/hermes-parser/js/babel-plugin-syntax-hermes-parser/src/index.js index a04237fb031..83994fc29b1 100644 --- a/tools/hermes-parser/js/babel-plugin-syntax-hermes-parser/src/index.js +++ b/tools/hermes-parser/js/babel-plugin-syntax-hermes-parser/src/index.js @@ -14,12 +14,28 @@ import type {ParserOptions} from 'hermes-parser'; import * as HermesParser from 'hermes-parser'; +type Options = { + /** + * When set to 'flow', will check files for a `@flow` annotation to apply + * this plugin, otherwise falling back to Babel's parser. + * + * This is independent of `parserOpts.flow`, which may customise 'detect' + * behaviour within hermes-parser (downstream from this plugin). + * + * Defaults to 'all'. + */ + parseLangTypes?: 'flow' | 'all', +}; + export default function BabelPluginSyntaxHermesParser( // $FlowExpectedError[unclear-type] We don't have types for this. api: any, + options: Options, ): $ReadOnly<{...}> { api.assertVersion('^7.0.0 || ^8.0.0-alpha.6'); + const {parseLangTypes = 'all'} = options; + let curParserOpts: ParserOptions = {}; let curFilename: ?string = null; @@ -42,14 +58,20 @@ export default function BabelPluginSyntaxHermesParser( ) { return; } - const opts: ParserOptions = {}; + + const parserOpts: ParserOptions = {}; for (const [key, value] of Object.entries(curParserOpts)) { if (HermesParser.ParserOptionsKeys.has(key)) { // $FlowExpectedError[incompatible-type] - opts[key] = value; + parserOpts[key] = value; } } - return HermesParser.parse(code, {...opts, babel: true}); + + if (parseLangTypes === 'flow' && !/@flow/.test(code)) { + return; + } + + return HermesParser.parse(code, {...parserOpts, babel: true}); }, pre() {