diff --git a/packages/babel-plugin-transform-flow-strip-types/README.md b/packages/babel-plugin-transform-flow-strip-types/README.md index 6e279dbfae..9248839270 100644 --- a/packages/babel-plugin-transform-flow-strip-types/README.md +++ b/packages/babel-plugin-transform-flow-strip-types/README.md @@ -47,3 +47,13 @@ require("babel-core").transform("code", { plugins: ["transform-flow-strip-types"] }); ``` + +## Options + +### `requireDirective` + +`boolean`, defaults to `false`. + +Setting this to true will only strip annotations and declarations from files +that contain the `// @flow` directive. It will also throw errors for any Flow +annotations found in files without the directive. diff --git a/packages/babel-plugin-transform-flow-strip-types/src/index.js b/packages/babel-plugin-transform-flow-strip-types/src/index.js index aafdb20590..1bec2a5745 100644 --- a/packages/babel-plugin-transform-flow-strip-types/src/index.js +++ b/packages/babel-plugin-transform-flow-strip-types/src/index.js @@ -3,13 +3,20 @@ import syntaxFlow from "babel-plugin-syntax-flow"; export default function({ types: t }) { const FLOW_DIRECTIVE = "@flow"; + let skipStrip = false; + return { inherits: syntaxFlow, visitor: { - Program(path, { file: { ast: { comments } } }) { + Program(path, { file: { ast: { comments } }, opts }) { + skipStrip = false; + let directiveFound = false; + for (const comment of (comments: Array)) { if (comment.value.indexOf(FLOW_DIRECTIVE) >= 0) { + directiveFound = true; + // remove flow directive comment.value = comment.value.replace(FLOW_DIRECTIVE, ""); @@ -17,9 +24,13 @@ export default function({ types: t }) { if (!comment.value.replace(/\*/g, "").trim()) comment.ignore = true; } } - }, + if (!directiveFound && opts.requireDirective) { + skipStrip = true; + } + }, ImportDeclaration(path) { + if (skipStrip) return; if (!path.node.specifiers.length) return; let typeCount = 0; @@ -36,16 +47,25 @@ export default function({ types: t }) { }, Flow(path) { + if (skipStrip) { + throw path.buildCodeFrameError( + "A @flow directive is required when using Flow annotations with " + + "babel-preset-react or the `requireDirective` option.", + ); + } + path.remove(); }, ClassProperty(path) { + if (skipStrip) return; path.node.variance = null; path.node.typeAnnotation = null; if (!path.node.value) path.remove(); }, Class(path) { + if (skipStrip) return; path.node.implements = null; // We do this here instead of in a `ClassProperty` visitor because the class transform @@ -59,10 +79,12 @@ export default function({ types: t }) { }, AssignmentPattern({ node }) { + if (skipStrip) return; node.left.optional = false; }, Function({ node }) { + if (skipStrip) return; for (let i = 0; i < node.params.length; i++) { const param = node.params[i]; param.optional = false; @@ -75,6 +97,7 @@ export default function({ types: t }) { }, TypeCastExpression(path) { + if (skipStrip) return; let { node } = path; do { node = node.expression; diff --git a/packages/babel-plugin-transform-flow-strip-types/test/fixtures/requireDirective/options.json b/packages/babel-plugin-transform-flow-strip-types/test/fixtures/requireDirective/options.json new file mode 100644 index 0000000000..b44aa63984 --- /dev/null +++ b/packages/babel-plugin-transform-flow-strip-types/test/fixtures/requireDirective/options.json @@ -0,0 +1,3 @@ +{ + "plugins": [["transform-flow-strip-types", { "requireDirective": true }]] +} diff --git a/packages/babel-plugin-transform-flow-strip-types/test/fixtures/requireDirective/true-does-not-throw-with-directive/actual.js b/packages/babel-plugin-transform-flow-strip-types/test/fixtures/requireDirective/true-does-not-throw-with-directive/actual.js new file mode 100644 index 0000000000..88aebda63c --- /dev/null +++ b/packages/babel-plugin-transform-flow-strip-types/test/fixtures/requireDirective/true-does-not-throw-with-directive/actual.js @@ -0,0 +1 @@ +function foo(numVal, strVal) {} diff --git a/packages/babel-plugin-transform-flow-strip-types/test/fixtures/requireDirective/true-does-not-throw-with-directive/expected.js b/packages/babel-plugin-transform-flow-strip-types/test/fixtures/requireDirective/true-does-not-throw-with-directive/expected.js new file mode 100644 index 0000000000..88aebda63c --- /dev/null +++ b/packages/babel-plugin-transform-flow-strip-types/test/fixtures/requireDirective/true-does-not-throw-with-directive/expected.js @@ -0,0 +1 @@ +function foo(numVal, strVal) {} diff --git a/packages/babel-plugin-transform-flow-strip-types/test/fixtures/requireDirective/true-strips-with-directive/actual.js b/packages/babel-plugin-transform-flow-strip-types/test/fixtures/requireDirective/true-strips-with-directive/actual.js new file mode 100644 index 0000000000..58edb63680 --- /dev/null +++ b/packages/babel-plugin-transform-flow-strip-types/test/fixtures/requireDirective/true-strips-with-directive/actual.js @@ -0,0 +1,3 @@ +// @flow + +function foo(numVal: number, strVal: string) {} diff --git a/packages/babel-plugin-transform-flow-strip-types/test/fixtures/requireDirective/true-strips-with-directive/expected.js b/packages/babel-plugin-transform-flow-strip-types/test/fixtures/requireDirective/true-strips-with-directive/expected.js new file mode 100644 index 0000000000..a828af11ee --- /dev/null +++ b/packages/babel-plugin-transform-flow-strip-types/test/fixtures/requireDirective/true-strips-with-directive/expected.js @@ -0,0 +1 @@ +function foo(numVal, strVal) {} \ No newline at end of file diff --git a/packages/babel-plugin-transform-flow-strip-types/test/fixtures/requireDirective/true-throws-if-found-with-directive/actual.js b/packages/babel-plugin-transform-flow-strip-types/test/fixtures/requireDirective/true-throws-if-found-with-directive/actual.js new file mode 100644 index 0000000000..6bd1f1803c --- /dev/null +++ b/packages/babel-plugin-transform-flow-strip-types/test/fixtures/requireDirective/true-throws-if-found-with-directive/actual.js @@ -0,0 +1 @@ +function foo(numVal: number, strVal: string) {} diff --git a/packages/babel-plugin-transform-flow-strip-types/test/fixtures/requireDirective/true-throws-if-found-with-directive/options.json b/packages/babel-plugin-transform-flow-strip-types/test/fixtures/requireDirective/true-throws-if-found-with-directive/options.json new file mode 100644 index 0000000000..03baa59e36 --- /dev/null +++ b/packages/babel-plugin-transform-flow-strip-types/test/fixtures/requireDirective/true-throws-if-found-with-directive/options.json @@ -0,0 +1,3 @@ +{ + "throws": "A @flow directive is required when using Flow annotations with babel-preset-react or the `requireDirective` option." +} diff --git a/packages/babel-preset-react/README.md b/packages/babel-preset-react/README.md index 591a59547b..049cad9add 100644 --- a/packages/babel-preset-react/README.md +++ b/packages/babel-preset-react/README.md @@ -2,13 +2,28 @@ > Babel preset for all React plugins. -This preset includes the following plugins/presets: +This preset always includes the following plugins: -- [preset-flow](https://babeljs.io/docs/plugins/preset-flow/) - [syntax-jsx](https://babeljs.io/docs/plugins/syntax-jsx/) +- [transform-flow-strip-types](https://babeljs.io/docs/plugins/transform-flow-strip-types/) - [transform-react-jsx](https://babeljs.io/docs/plugins/transform-react-jsx/) - [transform-react-display-name](https://babeljs.io/docs/plugins/transform-react-display-name/) +And with the `development` option: + +- [transform-react-jsx-self](https://babeljs.io/docs/plugins/transform-react-jsx-self/) +- [transform-react-jsx-source](https://babeljs.io/docs/plugins/transform-react-jsx-source/) + +Note: This preset sets the `requireDirective` option on +`transform-flow-strip-types`. This means Flow annotations and declarations +will _only_ be removed in files that have a `// @flow ` directive. It will also +throw errors for any Flow annotations found in files without the directive. + +```js +// @flow +function foo(numVal: number, strVal: string) {} +``` + ## Install > You can also check out the React [Getting Started page](https://facebook.github.io/react/docs/hello-world.html) @@ -54,7 +69,7 @@ View the output ### Via CLI ```sh -babel script.js --presets react +babel script.js --presets react ``` ### Via Node API @@ -64,3 +79,42 @@ require("babel-core").transform("code", { presets: ["react"] }); ``` + +## Options + +### `development` + +`boolean`, defaults to `false`. + +Toggles plugins that aid in development, such as [`babel-plugin-transform-react-jsx-self`](https://babeljs.io/docs/plugins/transform-react-jsx-self/) and [`babel-plugin-transform-react-jsx-source`](https://babeljs.io/docs/plugins/transform-react-jsx-source/). + +This is useful when combined with either a `babelrc.js` or [env option in a .babelrc](https://babeljs.io/docs/usage/babelrc/#env-option) configuration: + +#### babelrc.js + +```js +module.exports = { + presets: [ + ["react", { + development: process.env.BABEL_ENV === "development" + }], + ], +} +``` + +#### .babelrc + +> Note: the `env` option will likely get deprecated soon + +```json +{ + "presets": ["react"], + "env": { + "development": { + "presets": [ + ["react", { "development": true }] + ] + } + } +} +``` diff --git a/packages/babel-preset-react/package.json b/packages/babel-preset-react/package.json index 6616bccae6..ab3feff62e 100644 --- a/packages/babel-preset-react/package.json +++ b/packages/babel-preset-react/package.json @@ -9,10 +9,14 @@ "main": "lib/index.js", "dependencies": { "babel-plugin-syntax-jsx": "7.0.0-alpha.18", + "babel-plugin-transform-flow-strip-types": "7.0.0-alpha.18", "babel-plugin-transform-react-display-name": "7.0.0-alpha.18", "babel-plugin-transform-react-jsx": "7.0.0-alpha.18", "babel-plugin-transform-react-jsx-self": "7.0.0-alpha.18", - "babel-plugin-transform-react-jsx-source": "7.0.0-alpha.18", - "babel-preset-flow": "7.0.0-alpha.18" + "babel-plugin-transform-react-jsx-source": "7.0.0-alpha.18" + }, + "devDependencies": { + "babel-helper-plugin-test-runner": "7.0.0-alpha.18", + "babel-helper-transform-fixture-test-runner": "7.0.0-alpha.18" } } diff --git a/packages/babel-preset-react/src/index.js b/packages/babel-preset-react/src/index.js index f1d1c1cc6a..e2d308f4dd 100644 --- a/packages/babel-preset-react/src/index.js +++ b/packages/babel-preset-react/src/index.js @@ -1,11 +1,26 @@ -import presetFlow from "babel-preset-flow"; +import transformFlowStripTypes from "babel-plugin-transform-flow-strip-types"; import transformReactJSX from "babel-plugin-transform-react-jsx"; import transformSyntaxJSX from "babel-plugin-syntax-jsx"; import transformReactDisplayName from "babel-plugin-transform-react-display-name"; +import transformReactJSXSource from "babel-plugin-transform-react-jsx-source"; +import transformReactJSXSelf from "babel-plugin-transform-react-jsx-self"; + +export default function(context, opts = {}) { + const development = opts.development || false; + + if (typeof development !== "boolean") { + throw new Error("Preset react 'development' option must be a boolean."); + } -export default function() { return { - presets: [presetFlow], - plugins: [transformReactJSX, transformSyntaxJSX, transformReactDisplayName], + plugins: [ + transformReactJSX, + transformSyntaxJSX, + transformReactDisplayName, + + development && transformReactJSXSource, + development && transformReactJSXSelf, + [transformFlowStripTypes, { requireDirective: true }], + ].filter(Boolean), }; } diff --git a/packages/babel-preset-react/test/fixtures/flow/options.json b/packages/babel-preset-react/test/fixtures/flow/options.json new file mode 100644 index 0000000000..2e9146fee1 --- /dev/null +++ b/packages/babel-preset-react/test/fixtures/flow/options.json @@ -0,0 +1,5 @@ +{ + "presets": [ + "react" + ] +} diff --git a/packages/babel-preset-react/test/fixtures/flow/strip-with-directive/actual.js b/packages/babel-preset-react/test/fixtures/flow/strip-with-directive/actual.js new file mode 100644 index 0000000000..1b44b40442 --- /dev/null +++ b/packages/babel-preset-react/test/fixtures/flow/strip-with-directive/actual.js @@ -0,0 +1,2 @@ +// @flow +function foo(numVal: number, strVal: string) {} diff --git a/packages/babel-preset-react/test/fixtures/flow/strip-with-directive/expected.js b/packages/babel-preset-react/test/fixtures/flow/strip-with-directive/expected.js new file mode 100644 index 0000000000..a828af11ee --- /dev/null +++ b/packages/babel-preset-react/test/fixtures/flow/strip-with-directive/expected.js @@ -0,0 +1 @@ +function foo(numVal, strVal) {} \ No newline at end of file diff --git a/packages/babel-preset-react/test/fixtures/flow/throws-without-directive/actual.js b/packages/babel-preset-react/test/fixtures/flow/throws-without-directive/actual.js new file mode 100644 index 0000000000..6bd1f1803c --- /dev/null +++ b/packages/babel-preset-react/test/fixtures/flow/throws-without-directive/actual.js @@ -0,0 +1 @@ +function foo(numVal: number, strVal: string) {} diff --git a/packages/babel-preset-react/test/fixtures/flow/throws-without-directive/expected.js b/packages/babel-preset-react/test/fixtures/flow/throws-without-directive/expected.js new file mode 100644 index 0000000000..6bd1f1803c --- /dev/null +++ b/packages/babel-preset-react/test/fixtures/flow/throws-without-directive/expected.js @@ -0,0 +1 @@ +function foo(numVal: number, strVal: string) {} diff --git a/packages/babel-preset-react/test/fixtures/flow/throws-without-directive/options.json b/packages/babel-preset-react/test/fixtures/flow/throws-without-directive/options.json new file mode 100644 index 0000000000..03baa59e36 --- /dev/null +++ b/packages/babel-preset-react/test/fixtures/flow/throws-without-directive/options.json @@ -0,0 +1,3 @@ +{ + "throws": "A @flow directive is required when using Flow annotations with babel-preset-react or the `requireDirective` option." +} diff --git a/packages/babel-preset-react/test/fixtures/preset-options/development/exec.js b/packages/babel-preset-react/test/fixtures/preset-options/development/exec.js new file mode 100644 index 0000000000..4fc1a143a9 --- /dev/null +++ b/packages/babel-preset-react/test/fixtures/preset-options/development/exec.js @@ -0,0 +1,19 @@ +const actual = transform( + '', + Object.assign({}, opts, { filename: '/fake/path/mock.js' }) +).code; + +const expected = multiline([ + 'var _jsxFileName = "/fake/path/mock.js";', + 'React.createElement(Foo, {', + ' bar: "baz",', + ' __source: {', + ' fileName: _jsxFileName,', + ' lineNumber: 1', + ' },', + ' __self: this', + '});', +]); + + +assert.equal(actual, expected); diff --git a/packages/babel-preset-react/test/fixtures/preset-options/development/options.json b/packages/babel-preset-react/test/fixtures/preset-options/development/options.json new file mode 100644 index 0000000000..5ce6ee2080 --- /dev/null +++ b/packages/babel-preset-react/test/fixtures/preset-options/development/options.json @@ -0,0 +1,5 @@ +{ + "presets": [ + ["react", { "development": true }] + ] +} diff --git a/packages/babel-preset-react/test/fixtures/preset-options/empty-options/actual.js b/packages/babel-preset-react/test/fixtures/preset-options/empty-options/actual.js new file mode 100644 index 0000000000..378e2ae67b --- /dev/null +++ b/packages/babel-preset-react/test/fixtures/preset-options/empty-options/actual.js @@ -0,0 +1 @@ + diff --git a/packages/babel-preset-react/test/fixtures/preset-options/empty-options/expected.js b/packages/babel-preset-react/test/fixtures/preset-options/empty-options/expected.js new file mode 100644 index 0000000000..835263a79b --- /dev/null +++ b/packages/babel-preset-react/test/fixtures/preset-options/empty-options/expected.js @@ -0,0 +1,3 @@ +React.createElement(Foo, { + bar: "baz" +}); diff --git a/packages/babel-preset-react/test/fixtures/preset-options/empty-options/options.json b/packages/babel-preset-react/test/fixtures/preset-options/empty-options/options.json new file mode 100644 index 0000000000..2f5fa2457b --- /dev/null +++ b/packages/babel-preset-react/test/fixtures/preset-options/empty-options/options.json @@ -0,0 +1,5 @@ +{ + "presets": [ + ["react", {}] + ] +} diff --git a/packages/babel-preset-react/test/fixtures/preset-options/no-options/actual.js b/packages/babel-preset-react/test/fixtures/preset-options/no-options/actual.js new file mode 100644 index 0000000000..378e2ae67b --- /dev/null +++ b/packages/babel-preset-react/test/fixtures/preset-options/no-options/actual.js @@ -0,0 +1 @@ + diff --git a/packages/babel-preset-react/test/fixtures/preset-options/no-options/expected.js b/packages/babel-preset-react/test/fixtures/preset-options/no-options/expected.js new file mode 100644 index 0000000000..835263a79b --- /dev/null +++ b/packages/babel-preset-react/test/fixtures/preset-options/no-options/expected.js @@ -0,0 +1,3 @@ +React.createElement(Foo, { + bar: "baz" +}); diff --git a/packages/babel-preset-react/test/fixtures/preset-options/no-options/options.json b/packages/babel-preset-react/test/fixtures/preset-options/no-options/options.json new file mode 100644 index 0000000000..2e9146fee1 --- /dev/null +++ b/packages/babel-preset-react/test/fixtures/preset-options/no-options/options.json @@ -0,0 +1,5 @@ +{ + "presets": [ + "react" + ] +} diff --git a/packages/babel-preset-react/test/index.js b/packages/babel-preset-react/test/index.js new file mode 100644 index 0000000000..eaa4969a9b --- /dev/null +++ b/packages/babel-preset-react/test/index.js @@ -0,0 +1,20 @@ +import react from "../lib"; +import { expect } from "chai"; + +describe("react preset", () => { + it("doesn't throw with no options passed", () => { + expect(() => { + react(null); + }).not.to.throw(); + }); + + describe("options", () => { + describe("development", () => { + it("throws on non-boolean value", () => { + expect(function() { + react(null, { development: 1 }); + }).to.throw(/must be a boolean/); + }); + }); + }); +}); diff --git a/packages/babel-preset-react/test/preset-options.js b/packages/babel-preset-react/test/preset-options.js new file mode 100644 index 0000000000..09cfbc31f5 --- /dev/null +++ b/packages/babel-preset-react/test/preset-options.js @@ -0,0 +1,3 @@ +import runner from "babel-helper-plugin-test-runner"; + +runner(__dirname);