You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: tutorial/02-babel-es6-eslint-flow/README.md
+53-68Lines changed: 53 additions & 68 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -42,7 +42,7 @@ If you try to run `yarn start` now, it should print the correct output, but you
42
42
},
43
43
```
44
44
45
-
**Note**: A `.babelrc` file at the root of your project could also be used instead of the `babel` field of `package.json`. Your root folder will get more and more bloated over time, so keep the Babel config in `package.json` until it grows too large.
45
+
**Note**: A `.babelrc` file at the root of your project could also be used instead of the `babel` field of `package.json`, but since your root folder will get more and more bloated over time, I would recommend to keep the Babel config in `package.json` until it grows too large.
46
46
47
47
- Try running `yarn start` again. The `lib/index.js` file should now have been correctly transformed into ES5 code (`var` everywhere!).
48
48
@@ -55,23 +55,20 @@ We now have the basic compilation working. To make this environment a bit more u
55
55
```json
56
56
"scripts": {
57
57
"start": "yarn run watch",
58
-
"clean": "rimraf lib",
59
-
"prebuild": "yarn run clean",
60
-
"build": "babel src -d lib",
61
-
"lint": "eslint src/**/*.js",
62
58
"watch": "watch 'yarn run main' src --interval=1",
63
-
"main": "yarn run lint && yarn run build && node lib"
59
+
"main": "yarn run lint && yarn run build && node lib",
60
+
"build": "yarn run clean && babel src -d lib",
61
+
"clean": "rimraf lib",
62
+
"lint": "eslint src/**/*.js"
64
63
},
65
64
```
66
65
67
-
### `clean` with `rimraf`, and `prebuild`
66
+
### `clean` with `rimraf`
68
67
69
68
`clean` is a task that simply deletes our entire auto-generated `lib` folder before every `build`. This is typically useful to get rid of old compiled files after renaming or deleting some in `src`, or to make sure the `lib` folder is in sync with the `src` folder if your build fails and you don't notice. We use `rimraf` instead of a plain `rm -rf` in order to support Windows environments as well.
70
69
71
70
- Run `yarn add --dev rimraf`.
72
71
73
-
Tasks that are prefixed by `pre` or `post` will respectively be called after and before said task. `clean` is related to `build` and we want to run it before every build, so it's a good candidate for a `pre` task. `prebuild` simply calls `yarn run clean`.
74
-
75
72
### `main` and `watch`
76
73
77
74
`main` is going to be... well the *main* task of our workflow. It performs every operation needed for the build (many more will be added later), and runs the program.
@@ -86,6 +83,8 @@ Alright, we're now good to go.
86
83
87
84
- Run `yarn start`. It should clean, build, print "Hello ES6" and start watching for changes. Try modifying `src/index.js` to make sure the whole task flow is triggered again.
88
85
86
+
**Note**: It is possible to prefix task names with `pre` or `post` (like `prebuild`), to trigger tasks before and after others. We could have for instance called `clean` in a `prebuild` task instead of chaining the two in one command separated by `&&`. This approach makes command lines shorter but I find that it actually reduces readability to have to jump up and down to look for any existing `pre` and `post` tasks when reading through our tasks.
87
+
89
88
## ES6
90
89
91
90
> 💡 **[ES6](http://es6-features.org/)**: The most significant improvement of the JavaScript language. There are too many ES6 features to list them here but typical ES6 code uses classes with `class`, `const` and `let`, template strings, and arrow functions (`(param) => { console.log('Hi'); }`).
@@ -133,6 +132,7 @@ In `dog.js`, we also replace `module.exports = Dog` by `export default Dog`.
133
132
Note that in `dog.js`, the name `Dog` is only used in the `export`. Therefore it could be possible to export directly an anonymous class like this instead:
134
133
135
134
```javascript
135
+
// Dog
136
136
exportdefaultclass {
137
137
constructor(name) {
138
138
this.name= name;
@@ -144,21 +144,23 @@ export default class {
144
144
}
145
145
```
146
146
147
-
You might now guess that the name 'Dog' used in the `import` in `index.js` is actually completely up to you. This would work just fine:
147
+
You might now guess that the name `Dog` used in the `import` in `index.js` is actually completely up to you. This would work just fine:
148
148
149
149
```javascript
150
150
importCatfrom'./dog'; // Don't do this
151
151
152
152
consttoby=newCat('Toby');
153
153
```
154
154
155
-
Obviously, most of the time you will use the same name as the class / module you're importing.
155
+
Obviously, most of the time you will use the same name as the class / module you're importing. Anyway!
156
156
157
157
-`yarn start` should still print "Wah wah, I am Toby".
158
158
159
159
## ESLint
160
160
161
-
We're going to lint our code to catch potential issues. ESLint is the linter of choice for ES6 code. Instead of configuring the rules we want for our code ourselves, we will use the config created by Airbnb. This config uses a few plugins, so we need to install those as well to use their config.
161
+
> 💡 **[ESLint](http://eslint.org)** is the linter of choice for ES6 code. A linter gives you recommendations about code formatting, which enforces style consistency in your code, and code you share with your team. It's also a great way to learn about JavaScript by making mistakes that ESLint will catch.
162
+
163
+
ESLint works with *rules*, and there are [many of them](http://eslint.org/docs/rules/). Instead of configuring the rules we want for our code ourselves, we will use the config created by Airbnb. This config uses a few plugins, so we need to install those as well to use their config.
162
164
163
165
Check out Airbnb's most recent [instructions](https://www.npmjs.com/package/eslint-config-airbnb) to install the config package and all its dependencies correctly. As of 2016-11-11, they recommend using the following command in your terminal:
164
166
@@ -207,11 +209,37 @@ Here we just tell ESLint that the files we want to lint are all the `.js` files
207
209
208
210
> 💡 **[Flow](https://flowtype.org/)**: A static type checker by Facebook. It detects inconsistent types in your code. For instance, it will give you an error if you try to use a string where should be using a number.
209
211
210
-
- In order for Babel to understand and remove Flow annotations during the transpilation process, install the Flow preset for Babel by running `yarn add --dev babel-preset-flow`. Then, add `"flow"` under `babel.presets` in your `package.json`.
212
+
Right now, our JavaScript code is valid ES6 code. Flow can analyze plain JavaScript to give us some insights, but in order to use its full power, we need to add type annotations in our code, which will make it non-standard. We need to teach Babel and ESLint what those type annotations are in order for these tools to not freak out when parsing our files.
213
+
214
+
- Run `yarn add --dev flow-bin babel-preset-flow babel-eslint eslint-plugin-flowtype`.
215
+
216
+
`flow-bin` is the binary to run Flow in our `scripts` tasks, `babel-preset-flow` is the preset for Babel to understand Flow annotations, `babel-eslint` is a package to tell ESLint *to rely on Babel's parser* instead of its own, and `eslint-plugin-flowtype` is an ESLint plugin to lint Flow annotations.
217
+
218
+
- Update your `package.json` file with the following configuration for `babel` and `eslintConfig`:
219
+
220
+
```json
221
+
"babel": {
222
+
"presets": [
223
+
"latest",
224
+
"flow"
225
+
]
226
+
},
227
+
"eslintConfig": {
228
+
"extends": [
229
+
"airbnb",
230
+
"plugin:flowtype/recommended"
231
+
],
232
+
"plugins": [
233
+
"flowtype"
234
+
]
235
+
},
236
+
```
237
+
238
+
**Note**: The `plugin:flowtype/recommended` contains the instruction for ESLint to use Babel's parser. If you want to be more explicit, feel free to add `"parser": "babel-eslint"` under `eslintConfig`.
211
239
212
-
- Create an empty `.flowconfig` file at the root of your project
240
+
I know this is a lot to take in, so take a minute to think about it. I'm still amazed that it is even possible for ESLint to use Babel's parser to understand Flow annotations. These 2 tools are really incredible for being so modular.
213
241
214
-
-Run `yarn add --dev flow-bin` to install Flow, and create a `typecheck` task:
242
+
-Create a `typecheck` task:
215
243
216
244
```json
217
245
"typecheck": "flow"
@@ -223,9 +251,11 @@ Here we just tell ESLint that the files we want to lint are all the `.js` files
223
251
"main": "yarn run typecheck && yarn run lint && yarn run build && node lib"
224
252
```
225
253
226
-
Alright, we should be able to run Flow now.
254
+
- Create an empty `.flowconfig` file at the root of your project.
255
+
256
+
Alright, we should be all set for the configuration part.
227
257
228
-
- Add Flow annotations to `src/shared/dog.js` like so:
258
+
- Add Flow annotations to `src/dog.js` like so:
229
259
230
260
```javascript
231
261
// @flow
@@ -237,72 +267,27 @@ class Dog {
237
267
this.name= name;
238
268
}
239
269
240
-
bark(): string {
270
+
bark() {
241
271
return`Wah wah, I am ${this.name}`;
242
272
}
243
-
244
-
barkInConsole() {
245
-
/* eslint-disable no-console */
246
-
console.log(this.bark());
247
-
/* eslint-enable no-console */
248
-
}
249
-
250
273
}
251
274
252
275
exportdefaultDog;
253
276
```
254
277
255
278
The `// @flow` comment tells Flow that we want this file to be typechecked. For the rest, Flow annotations are typically a colon after a function parameter or a function name. Check the documentation for more details.
256
279
257
-
Now if you run `yarn start`, Flow will work fine, but ESLint is going to complain about that non-standard syntax we're using. Since Babel's parser is all up-and-running with parsing Flow content thanks to the `babel-preset-flow` plugin we installed, it'd be nice if ESLint could rely on Babel's parser instead of trying to understand Flow annotations on its own. That's actually possible using the `babel-eslint` package. Let's do this.
258
-
259
-
- Run `yarn add --dev babel-eslint`
260
-
261
-
- In `package.json`, under `eslintConfig`, add the following property: `"parser": "babel-eslint"`
280
+
- Add `// @flow` at the top of `index.js` as well.
262
281
263
282
`yarn start` should now both lint and typecheck your code fine.
264
283
265
-
Now that ESLint and Babel are able to share a common parser, we can actually get ESLint to lint our Flow annotations via the `eslint-plugin-flowtype` plugin.
266
-
267
-
- Run `yarn add --dev eslint-plugin-flowtype` and add `"flowtype"` under `eslintConfig.plugins` in `package.json`, and add `"plugin:flowtype/recommended"` under `eslintConfig.extends` in an array next to `"airbnb"`.
268
-
269
-
Now if you type `name:string` as an annotation, ESLint should complain that you forgot a space after the colon for instance.
270
-
271
-
**Note**: The `"parser": "babel-eslint"` property that I made you write in `package.json` is actually included in the `"plugin:flowtype/recommended"` config, so you can now remove it for a more minimal `package.json`. Leaving it there is more explicit though, so that's up to your personal preference. Since this tutorial is about the most minimal setup, I removed it.
272
-
273
-
- You can now add `// @flow` in every `.js` and `.jsx` file under `src`, run `yarn test` or `yarn start`, and add type annotations everywhere Flow asks you to do so.
274
-
275
-
One counterintuitive case is the following, for `src/client/component/message.jsx`:
As you can see, when destructuring function parameters, you must annotate the extracted properties using a sort of object literal notation.
282
-
283
-
Another case you will encounter is that in `src/client/reducers/dog-reducer.js`, Flow will complain about Immutable not having a default export. This issue is discussed in [#863 on Immutable](https://github.com/facebook/immutable-js/issues/863), which highlights 2 workarounds:
284
-
285
-
```javascript
286
-
import { MapasImmutableMap } from'immutable';
287
-
// or
288
-
import*asImmutablefrom'immutable';
289
-
```
290
-
291
-
Until Immutable officially adresses the issue, just pick whichever looks better to you when importing Immutable components. I'm personally going for `import * as Immutable from 'immutable'` since it's shorter and won't require refactoring the code when this issue gets fixed.
292
-
293
-
**Note**: If Flow detects type errors in your `node_modules` folder, add an `[ignore]` section in your `.flowconfig` to ignore the packages causing issues specifically (do not ignore the entire `node_modules` directory). It could look like this:
294
-
295
-
```flowconfig
296
-
[ignore]
297
-
298
-
.*/node_modules/gulp-flowtype/.*
299
-
```
284
+
There are 2 things that I want you to try:
300
285
301
-
In my case, the `linter-flow` plugin for Atom was detecting type errors in the `node_modules/gulp-flowtype` directory, which contains files annotated with `// @flow`.
286
+
- Replace `constructor(name: string)` by `constructor(name: number)`, and run `yarn start`. You should get a **Flow** error telling you that those types are incompatible. That means Flow is set up correctly.
302
287
303
-
You now have bullet-proof code that is linted, typechecked, and tested, good job!
288
+
- Now replace `constructor(name: string)` by `constructor(name:string)`, and run `yarn start`. You should get an **ESLint** error telling you that Flow annotations should have a space after the colon. That means the Flow plugin for ESLint is set up correctly.
304
289
305
-
Back to the [previous section](/tutorial/11-testing-mocha-chai-sinon) or the [table of contents](https://github.com/verekia/js-stack-from-scratch).
290
+
If you got the 2 different errors working, you are all set with Flow and ESLint!
306
291
307
292
Next section: [4 - Express Server](/tutorial/4-express-server)
0 commit comments