Merge pull request #5468 from babel/react-preset

Add requireDirective to strip-flow-types for use in React preset
This commit is contained in:
Henry Zhu 2017-08-04 11:31:12 -04:00 committed by GitHub
commit 8c457e9283
28 changed files with 208 additions and 11 deletions

View File

@ -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.

View File

@ -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<Object>)) {
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;

View File

@ -0,0 +1,3 @@
{
"plugins": [["transform-flow-strip-types", { "requireDirective": true }]]
}

View File

@ -0,0 +1,3 @@
// @flow
function foo(numVal: number, strVal: string) {}

View File

@ -0,0 +1 @@
function foo(numVal, strVal) {}

View File

@ -0,0 +1 @@
function foo(numVal: number, strVal: string) {}

View File

@ -0,0 +1,3 @@
{
"throws": "A @flow directive is required when using Flow annotations with babel-preset-react or the `requireDirective` option."
}

View File

@ -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 }]
]
}
}
}
```

View File

@ -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"
}
}

View File

@ -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),
};
}

View File

@ -0,0 +1,5 @@
{
"presets": [
"react"
]
}

View File

@ -0,0 +1,2 @@
// @flow
function foo(numVal: number, strVal: string) {}

View File

@ -0,0 +1 @@
function foo(numVal, strVal) {}

View File

@ -0,0 +1 @@
function foo(numVal: number, strVal: string) {}

View File

@ -0,0 +1 @@
function foo(numVal: number, strVal: string) {}

View File

@ -0,0 +1,3 @@
{
"throws": "A @flow directive is required when using Flow annotations with babel-preset-react or the `requireDirective` option."
}

View File

@ -0,0 +1,19 @@
const actual = transform(
'<Foo bar="baz" />',
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);

View File

@ -0,0 +1,5 @@
{
"presets": [
["react", { "development": true }]
]
}

View File

@ -0,0 +1 @@
<Foo bar="baz" />

View File

@ -0,0 +1,3 @@
React.createElement(Foo, {
bar: "baz"
});

View File

@ -0,0 +1,5 @@
{
"presets": [
["react", {}]
]
}

View File

@ -0,0 +1 @@
<Foo bar="baz" />

View File

@ -0,0 +1,3 @@
React.createElement(Foo, {
bar: "baz"
});

View File

@ -0,0 +1,5 @@
{
"presets": [
"react"
]
}

View File

@ -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/);
});
});
});
});

View File

@ -0,0 +1,3 @@
import runner from "babel-helper-plugin-test-runner";
runner(__dirname);