diff --git a/packages/babel-core/README.md b/packages/babel-core/README.md index a845fbfdd7..ad02a68c32 100644 --- a/packages/babel-core/README.md +++ b/packages/babel-core/README.md @@ -116,5 +116,5 @@ Following is a table of the options you can use: | `sourceMaps` | `false` | If truthy, adds a `map` property to returned output. If set to `"inline"`, a comment with a sourceMappingURL directive is added to the bottom of the returned code. If set to `"both"` then a `map` property is returned as well as a source map comment appended. **This does not emit sourcemap files by itself!** To have sourcemaps emitted using the CLI, you must pass it the `--source-maps` option | | `sourceMapTarget` | `(filenameRelative)` | Set `file` on returned source map | | `sourceRoot` | `(moduleRoot)` | The root from which all sources are relative | -| `sourceType` | `"module"` | Indicate the mode the code should be parsed in. Can be either "script" or "module" | +| `sourceType` | `"module"` | Indicate the mode the code should be parsed in. Can be one of "script", "module", or "unambiguous". `"unambiguous"` will make Babel attempt to _guess_, based on the presence of ES6 `import` or `export` statements. Files with ES6 `import`s and `export`s are considered `"module"` and are otherwise `"script"`. | | `wrapPluginVisitorMethod`| `null` | An optional callback that can be used to wrap visitor methods. **NOTE:** This is useful for things like introspection, and not really needed for implementing anything. Called as `wrapPluginVisitorMethod(pluginAlias, visitorType, callback)`. diff --git a/packages/babel-core/src/config/option-assertions.js b/packages/babel-core/src/config/option-assertions.js index bc0ce7fae6..693b4fea92 100644 --- a/packages/babel-core/src/config/option-assertions.js +++ b/packages/babel-core/src/config/option-assertions.js @@ -46,8 +46,15 @@ export function assertSourceType( key: string, value: mixed, ): SourceTypeOption | void { - if (value !== undefined && value !== "module" && value !== "script") { - throw new Error(`.${key} must be "module", "script", or undefined`); + if ( + value !== undefined && + value !== "module" && + value !== "script" && + value !== "unambiguous" + ) { + throw new Error( + `.${key} must be "module", "script", "unambiguous", or undefined`, + ); } return value; } diff --git a/packages/babel-core/src/config/options.js b/packages/babel-core/src/config/options.js index e47babff92..aa915cc94c 100644 --- a/packages/babel-core/src/config/options.js +++ b/packages/babel-core/src/config/options.js @@ -185,7 +185,7 @@ export type PluginItem = PluginTarget | [PluginTarget, {} | void]; export type PluginList = $ReadOnlyArray; export type SourceMapsOption = boolean | "inline" | "both"; -export type SourceTypeOption = "module" | "script"; +export type SourceTypeOption = "module" | "script" | "unambiguous"; export type CompactOption = boolean | "auto"; export type RootInputSourceMapOption = {} | boolean; diff --git a/packages/babylon/README.md b/packages/babylon/README.md index 794e9e70cb..78dd00be33 100644 --- a/packages/babylon/README.md +++ b/packages/babylon/README.md @@ -39,7 +39,7 @@ mind. When in doubt, use `.parse()`. - **allowSuperOutsideMethod**: TODO - **sourceType**: Indicate the mode the code should be parsed in. Can be - either `"script"` or `"module"`. + one of `"script"`, `"module"`, or `"unambiguous"`. Defaults to `"script"`. `"unambiguous"` will make Babylon attempt to _guess_, based on the presence of ES6 `import` or `export` statements. Files with ES6 `import`s and `export`s are considered `"module"` and are otherwise `"script"`. - **sourceFilename**: Correlate output AST nodes with their source filename. Useful when generating code and source maps from the ASTs of multiple input files. diff --git a/packages/babylon/src/index.js b/packages/babylon/src/index.js index 882834dd47..7071a312b3 100755 --- a/packages/babylon/src/index.js +++ b/packages/babylon/src/index.js @@ -18,7 +18,27 @@ plugins.jsx = jsxPlugin; plugins.typescript = typescriptPlugin; export function parse(input: string, options?: Options): File { - return getParser(options, input).parse(); + if (options && options.sourceType === "unambiguous") { + options = Object.assign({}, options); + try { + options.sourceType = "module"; + const ast = getParser(options, input).parse(); + + // Rather than try to parse as a script first, we opt to parse as a module and convert back + // to a script where possible to avoid having to do a full re-parse of the input content. + if (!hasModuleSyntax(ast)) ast.program.sourceType = "script"; + return ast; + } catch (moduleError) { + try { + options.sourceType = "script"; + return getParser(options, input).parse(); + } catch (scriptError) {} + + throw moduleError; + } + } else { + return getParser(options, input).parse(); + } } export function parseExpression(input: string, options?: Options): Expression { @@ -91,3 +111,16 @@ function getParserClass( } return cls; } + +function hasModuleSyntax(ast) { + return ast.program.body.some( + child => + (child.type === "ImportDeclaration" && + (!child.importKind || child.importKind === "value")) || + (child.type === "ExportNamedDeclaration" && + (!child.exportKind || child.exportKind === "value")) || + (child.type === "ExportAllDeclaration" && + (!child.exportKind || child.exportKind === "value")) || + child.type === "ExportDefaultDeclaration", + ); +} diff --git a/packages/babylon/test/fixtures/core/sourcetype-unambiguous/commonjs/actual.js b/packages/babylon/test/fixtures/core/sourcetype-unambiguous/commonjs/actual.js new file mode 100644 index 0000000000..99bf8b9add --- /dev/null +++ b/packages/babylon/test/fixtures/core/sourcetype-unambiguous/commonjs/actual.js @@ -0,0 +1 @@ +var foo = require("foo"); diff --git a/packages/babylon/test/fixtures/core/sourcetype-unambiguous/commonjs/expected.json b/packages/babylon/test/fixtures/core/sourcetype-unambiguous/commonjs/expected.json new file mode 100644 index 0000000000..4025a67dc6 --- /dev/null +++ b/packages/babylon/test/fixtures/core/sourcetype-unambiguous/commonjs/expected.json @@ -0,0 +1,138 @@ +{ + "type": "File", + "start": 0, + "end": 25, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 25 + } + }, + "program": { + "type": "Program", + "start": 0, + "end": 25, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 25 + } + }, + "sourceType": "script", + "body": [ + { + "type": "VariableDeclaration", + "start": 0, + "end": 25, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 25 + } + }, + "declarations": [ + { + "type": "VariableDeclarator", + "start": 4, + "end": 24, + "loc": { + "start": { + "line": 1, + "column": 4 + }, + "end": { + "line": 1, + "column": 24 + } + }, + "id": { + "type": "Identifier", + "start": 4, + "end": 7, + "loc": { + "start": { + "line": 1, + "column": 4 + }, + "end": { + "line": 1, + "column": 7 + }, + "identifierName": "foo" + }, + "name": "foo" + }, + "init": { + "type": "CallExpression", + "start": 10, + "end": 24, + "loc": { + "start": { + "line": 1, + "column": 10 + }, + "end": { + "line": 1, + "column": 24 + } + }, + "callee": { + "type": "Identifier", + "start": 10, + "end": 17, + "loc": { + "start": { + "line": 1, + "column": 10 + }, + "end": { + "line": 1, + "column": 17 + }, + "identifierName": "require" + }, + "name": "require" + }, + "arguments": [ + { + "type": "StringLiteral", + "start": 18, + "end": 23, + "loc": { + "start": { + "line": 1, + "column": 18 + }, + "end": { + "line": 1, + "column": 23 + } + }, + "extra": { + "rawValue": "foo", + "raw": "\"foo\"" + }, + "value": "foo" + } + ] + } + } + ], + "kind": "var" + } + ], + "directives": [] + } +} \ No newline at end of file diff --git a/packages/babylon/test/fixtures/core/sourcetype-unambiguous/commonjs/options.json b/packages/babylon/test/fixtures/core/sourcetype-unambiguous/commonjs/options.json new file mode 100644 index 0000000000..e69c4497f5 --- /dev/null +++ b/packages/babylon/test/fixtures/core/sourcetype-unambiguous/commonjs/options.json @@ -0,0 +1,3 @@ +{ + "sourceType": "unambiguous" +} diff --git a/packages/babylon/test/fixtures/core/sourcetype-unambiguous/flow/actual.js b/packages/babylon/test/fixtures/core/sourcetype-unambiguous/flow/actual.js new file mode 100644 index 0000000000..c1d866f7a4 --- /dev/null +++ b/packages/babylon/test/fixtures/core/sourcetype-unambiguous/flow/actual.js @@ -0,0 +1,3 @@ +import type { Foo } from "bar"; +export type { Foo }; +export type * from "bar"; diff --git a/packages/babylon/test/fixtures/core/sourcetype-unambiguous/flow/expected.json b/packages/babylon/test/fixtures/core/sourcetype-unambiguous/flow/expected.json new file mode 100644 index 0000000000..3ae2f3a00b --- /dev/null +++ b/packages/babylon/test/fixtures/core/sourcetype-unambiguous/flow/expected.json @@ -0,0 +1,227 @@ +{ + "type": "File", + "start": 0, + "end": 78, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 3, + "column": 25 + } + }, + "program": { + "type": "Program", + "start": 0, + "end": 78, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 3, + "column": 25 + } + }, + "sourceType": "script", + "body": [ + { + "type": "ImportDeclaration", + "start": 0, + "end": 31, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 31 + } + }, + "specifiers": [ + { + "type": "ImportSpecifier", + "start": 14, + "end": 17, + "loc": { + "start": { + "line": 1, + "column": 14 + }, + "end": { + "line": 1, + "column": 17 + } + }, + "imported": { + "type": "Identifier", + "start": 14, + "end": 17, + "loc": { + "start": { + "line": 1, + "column": 14 + }, + "end": { + "line": 1, + "column": 17 + }, + "identifierName": "Foo" + }, + "name": "Foo" + }, + "importKind": null, + "local": { + "type": "Identifier", + "start": 14, + "end": 17, + "loc": { + "start": { + "line": 1, + "column": 14 + }, + "end": { + "line": 1, + "column": 17 + }, + "identifierName": "Foo" + }, + "name": "Foo" + } + } + ], + "importKind": "type", + "source": { + "type": "StringLiteral", + "start": 25, + "end": 30, + "loc": { + "start": { + "line": 1, + "column": 25 + }, + "end": { + "line": 1, + "column": 30 + } + }, + "extra": { + "rawValue": "bar", + "raw": "\"bar\"" + }, + "value": "bar" + } + }, + { + "type": "ExportNamedDeclaration", + "start": 32, + "end": 52, + "loc": { + "start": { + "line": 2, + "column": 0 + }, + "end": { + "line": 2, + "column": 20 + } + }, + "specifiers": [ + { + "type": "ExportSpecifier", + "start": 46, + "end": 49, + "loc": { + "start": { + "line": 2, + "column": 14 + }, + "end": { + "line": 2, + "column": 17 + } + }, + "local": { + "type": "Identifier", + "start": 46, + "end": 49, + "loc": { + "start": { + "line": 2, + "column": 14 + }, + "end": { + "line": 2, + "column": 17 + }, + "identifierName": "Foo" + }, + "name": "Foo" + }, + "exported": { + "type": "Identifier", + "start": 46, + "end": 49, + "loc": { + "start": { + "line": 2, + "column": 14 + }, + "end": { + "line": 2, + "column": 17 + }, + "identifierName": "Foo" + }, + "name": "Foo" + } + } + ], + "source": null, + "exportKind": "type", + "declaration": null + }, + { + "type": "ExportAllDeclaration", + "start": 53, + "end": 78, + "loc": { + "start": { + "line": 3, + "column": 0 + }, + "end": { + "line": 3, + "column": 25 + } + }, + "exportKind": "type", + "source": { + "type": "StringLiteral", + "start": 72, + "end": 77, + "loc": { + "start": { + "line": 3, + "column": 19 + }, + "end": { + "line": 3, + "column": 24 + } + }, + "extra": { + "rawValue": "bar", + "raw": "\"bar\"" + }, + "value": "bar" + } + } + ], + "directives": [] + } +} \ No newline at end of file diff --git a/packages/babylon/test/fixtures/core/sourcetype-unambiguous/flow/options.json b/packages/babylon/test/fixtures/core/sourcetype-unambiguous/flow/options.json new file mode 100644 index 0000000000..cd0c1cac56 --- /dev/null +++ b/packages/babylon/test/fixtures/core/sourcetype-unambiguous/flow/options.json @@ -0,0 +1,4 @@ +{ + "sourceType": "unambiguous", + "plugins": ["flow"] +} diff --git a/packages/babylon/test/fixtures/core/sourcetype-unambiguous/module-export-all/actual.js b/packages/babylon/test/fixtures/core/sourcetype-unambiguous/module-export-all/actual.js new file mode 100644 index 0000000000..9ec8f63ab2 --- /dev/null +++ b/packages/babylon/test/fixtures/core/sourcetype-unambiguous/module-export-all/actual.js @@ -0,0 +1 @@ +export * from "foo"; diff --git a/packages/babylon/test/fixtures/core/sourcetype-unambiguous/module-export-all/expected.json b/packages/babylon/test/fixtures/core/sourcetype-unambiguous/module-export-all/expected.json new file mode 100644 index 0000000000..e3e870a7bf --- /dev/null +++ b/packages/babylon/test/fixtures/core/sourcetype-unambiguous/module-export-all/expected.json @@ -0,0 +1,69 @@ +{ + "type": "File", + "start": 0, + "end": 20, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 20 + } + }, + "program": { + "type": "Program", + "start": 0, + "end": 20, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 20 + } + }, + "sourceType": "module", + "body": [ + { + "type": "ExportAllDeclaration", + "start": 0, + "end": 20, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 20 + } + }, + "source": { + "type": "StringLiteral", + "start": 14, + "end": 19, + "loc": { + "start": { + "line": 1, + "column": 14 + }, + "end": { + "line": 1, + "column": 19 + } + }, + "extra": { + "rawValue": "foo", + "raw": "\"foo\"" + }, + "value": "foo" + } + } + ], + "directives": [] + } +} \ No newline at end of file diff --git a/packages/babylon/test/fixtures/core/sourcetype-unambiguous/module-export-all/options.json b/packages/babylon/test/fixtures/core/sourcetype-unambiguous/module-export-all/options.json new file mode 100644 index 0000000000..e69c4497f5 --- /dev/null +++ b/packages/babylon/test/fixtures/core/sourcetype-unambiguous/module-export-all/options.json @@ -0,0 +1,3 @@ +{ + "sourceType": "unambiguous" +} diff --git a/packages/babylon/test/fixtures/core/sourcetype-unambiguous/module-export-default/actual.js b/packages/babylon/test/fixtures/core/sourcetype-unambiguous/module-export-default/actual.js new file mode 100644 index 0000000000..ff8b4c5632 --- /dev/null +++ b/packages/babylon/test/fixtures/core/sourcetype-unambiguous/module-export-default/actual.js @@ -0,0 +1 @@ +export default {}; diff --git a/packages/babylon/test/fixtures/core/sourcetype-unambiguous/module-export-default/expected.json b/packages/babylon/test/fixtures/core/sourcetype-unambiguous/module-export-default/expected.json new file mode 100644 index 0000000000..5373472b0e --- /dev/null +++ b/packages/babylon/test/fixtures/core/sourcetype-unambiguous/module-export-default/expected.json @@ -0,0 +1,65 @@ +{ + "type": "File", + "start": 0, + "end": 18, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 18 + } + }, + "program": { + "type": "Program", + "start": 0, + "end": 18, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 18 + } + }, + "sourceType": "module", + "body": [ + { + "type": "ExportDefaultDeclaration", + "start": 0, + "end": 18, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 18 + } + }, + "declaration": { + "type": "ObjectExpression", + "start": 15, + "end": 17, + "loc": { + "start": { + "line": 1, + "column": 15 + }, + "end": { + "line": 1, + "column": 17 + } + }, + "properties": [] + } + } + ], + "directives": [] + } +} \ No newline at end of file diff --git a/packages/babylon/test/fixtures/core/sourcetype-unambiguous/module-export-default/options.json b/packages/babylon/test/fixtures/core/sourcetype-unambiguous/module-export-default/options.json new file mode 100644 index 0000000000..e69c4497f5 --- /dev/null +++ b/packages/babylon/test/fixtures/core/sourcetype-unambiguous/module-export-default/options.json @@ -0,0 +1,3 @@ +{ + "sourceType": "unambiguous" +} diff --git a/packages/babylon/test/fixtures/core/sourcetype-unambiguous/module-export-named/actual.js b/packages/babylon/test/fixtures/core/sourcetype-unambiguous/module-export-named/actual.js new file mode 100644 index 0000000000..ac17756121 --- /dev/null +++ b/packages/babylon/test/fixtures/core/sourcetype-unambiguous/module-export-named/actual.js @@ -0,0 +1 @@ +export function fn(){} diff --git a/packages/babylon/test/fixtures/core/sourcetype-unambiguous/module-export-named/expected.json b/packages/babylon/test/fixtures/core/sourcetype-unambiguous/module-export-named/expected.json new file mode 100644 index 0000000000..01e29c42d4 --- /dev/null +++ b/packages/babylon/test/fixtures/core/sourcetype-unambiguous/module-export-named/expected.json @@ -0,0 +1,104 @@ +{ + "type": "File", + "start": 0, + "end": 22, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 22 + } + }, + "program": { + "type": "Program", + "start": 0, + "end": 22, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 22 + } + }, + "sourceType": "module", + "body": [ + { + "type": "ExportNamedDeclaration", + "start": 0, + "end": 22, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 22 + } + }, + "specifiers": [], + "source": null, + "declaration": { + "type": "FunctionDeclaration", + "start": 7, + "end": 22, + "loc": { + "start": { + "line": 1, + "column": 7 + }, + "end": { + "line": 1, + "column": 22 + } + }, + "id": { + "type": "Identifier", + "start": 16, + "end": 18, + "loc": { + "start": { + "line": 1, + "column": 16 + }, + "end": { + "line": 1, + "column": 18 + }, + "identifierName": "fn" + }, + "name": "fn" + }, + "generator": false, + "expression": false, + "async": false, + "params": [], + "body": { + "type": "BlockStatement", + "start": 20, + "end": 22, + "loc": { + "start": { + "line": 1, + "column": 20 + }, + "end": { + "line": 1, + "column": 22 + } + }, + "body": [], + "directives": [] + } + } + } + ], + "directives": [] + } +} \ No newline at end of file diff --git a/packages/babylon/test/fixtures/core/sourcetype-unambiguous/module-export-named/options.json b/packages/babylon/test/fixtures/core/sourcetype-unambiguous/module-export-named/options.json new file mode 100644 index 0000000000..e69c4497f5 --- /dev/null +++ b/packages/babylon/test/fixtures/core/sourcetype-unambiguous/module-export-named/options.json @@ -0,0 +1,3 @@ +{ + "sourceType": "unambiguous" +} diff --git a/packages/babylon/test/fixtures/core/sourcetype-unambiguous/module-import/actual.js b/packages/babylon/test/fixtures/core/sourcetype-unambiguous/module-import/actual.js new file mode 100644 index 0000000000..c0748305d5 --- /dev/null +++ b/packages/babylon/test/fixtures/core/sourcetype-unambiguous/module-import/actual.js @@ -0,0 +1 @@ +import "foo"; diff --git a/packages/babylon/test/fixtures/core/sourcetype-unambiguous/module-import/expected.json b/packages/babylon/test/fixtures/core/sourcetype-unambiguous/module-import/expected.json new file mode 100644 index 0000000000..1d1c75f23e --- /dev/null +++ b/packages/babylon/test/fixtures/core/sourcetype-unambiguous/module-import/expected.json @@ -0,0 +1,70 @@ +{ + "type": "File", + "start": 0, + "end": 13, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 13 + } + }, + "program": { + "type": "Program", + "start": 0, + "end": 13, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 13 + } + }, + "sourceType": "module", + "body": [ + { + "type": "ImportDeclaration", + "start": 0, + "end": 13, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 13 + } + }, + "specifiers": [], + "source": { + "type": "StringLiteral", + "start": 7, + "end": 12, + "loc": { + "start": { + "line": 1, + "column": 7 + }, + "end": { + "line": 1, + "column": 12 + } + }, + "extra": { + "rawValue": "foo", + "raw": "\"foo\"" + }, + "value": "foo" + } + } + ], + "directives": [] + } +} \ No newline at end of file diff --git a/packages/babylon/test/fixtures/core/sourcetype-unambiguous/module-import/options.json b/packages/babylon/test/fixtures/core/sourcetype-unambiguous/module-import/options.json new file mode 100644 index 0000000000..e69c4497f5 --- /dev/null +++ b/packages/babylon/test/fixtures/core/sourcetype-unambiguous/module-import/options.json @@ -0,0 +1,3 @@ +{ + "sourceType": "unambiguous" +}