@samitouri / QOS-React-2 / commits / 52a0d6b5ab

Make Flow work with your editor (#18664)

We typecheck the reconciler against each one of our host configs. `yarn flow dom` checks it against the DOM renderer, `yarn flow native` checks it against the native renderer, and so on. To do this, we generate separate flowconfig files. Currently, there is no root-level host config, so running Flow directly via `flow` CLI doesn't work. You have to use the `yarn flow` command and pick a specific renderer. A drawback of this design, though, is that our Flow setup doesn't work with other tooling. Namely, editor integrations. I think the intent of this was maybe so you don't run Flow against a renderer than you intended, see it pass, and wrongly think you fixed all the errors. However, since they all run in CI, I don't think this is a big deal. In practice, I nearly always run Flow against the same renderer (DOM), and I'm guessing that's the most common workflow for others, too. So what I've done in this commit is modify the `yarn flow` command to copy the generated `.flowconfig` file into the root directory. The editor integration will pick this up and show Flow information for whatever was the last renderer you checked. Everything else about the setup is the same, and all the renderers will continue to be checked by CI.

Andrew Clark committed Apr 18, 2020 at 10:24 UTC 52a0d6b5ab73251ba646e16b08d49d5e870e494e
3 files changed +57 -14
.gitignore
+1
@@ -1,6 +1,7 @@
1 .DS_STORE
2 node_modules
3 scripts/flow/*/.flowconfig
4 +.flowconfig
5 *~
6 *.pyc
7 .grunt
scripts/flow/config/flowconfig
+9 -8
@@ -1,5 +1,6 @@
1 [ignore]
2 .*/scripts/bench/.*
3 +.*/build/.*
4
5 # These shims are copied into external projects:
6 .*/rollup/shims/facebook-www/.*
@@ -20,16 +21,16 @@
21 %REACT_RENDERER_FLOW_IGNORES%
22
23 [include]
23 -../../../node_modules/
24 -../../../packages/
25 -../../../scripts/
24 +./node_modules/
25 +./packages/
26 +./scripts/
27
28 [libs]
28 -../../../node_modules/fbjs/flow/lib/dev.js
29 -../environment.js
30 -../react-devtools.js
31 -../react-native-host-hooks.js
32 -../react-relay-hooks.js
29 +./node_modules/fbjs/flow/lib/dev.js
30 +./scripts/flow/environment.js
31 +./scripts/flow/react-devtools.js
32 +./scripts/flow/react-native-host-hooks.js
33 +./scripts/flow/react-relay-hooks.js
34
35 [lints]
36 untyped-type-import=error
scripts/flow/runFlow.js
+47 -6
@@ -8,24 +8,65 @@
8 'use strict';
9
10 const chalk = require('chalk');
11 -const spawn = require('child_process').spawn;
11 +const {spawn} = require('child_process');
12 +const fs = require('fs');
13
14 +// TODO: This generates all the renderer configs at once. Originally this was
15 +// to allow the possibility of running multiple Flow processes in parallel, but
16 +// that never happened. If we did, we'd probably do this in CI, anyway, and run
17 +// on multiple machines. So instead we could remove this intermediate step and
18 +// generate only the config for the specified renderer.
19 require('./createFlowConfigs');
20
21 async function runFlow(renderer, args) {
22 return new Promise(resolve => {
17 - console.log(
18 - 'Running Flow on the ' + chalk.yellow(renderer) + ' renderer...',
19 - );
23 let cmd = __dirname + '/../../node_modules/.bin/flow';
24 if (process.platform === 'win32') {
25 cmd = cmd.replace(/\//g, '\\') + '.cmd';
26 }
27 +
28 + // Copy renderer flowconfig file to the root of the project so that it
29 + // works with editor integrations. This means that the Flow config used by
30 + // the editor will correspond to the last renderer you checked.
31 + const srcPath =
32 + process.cwd() + '/scripts/flow/' + renderer + '/.flowconfig';
33 + const srcStat = fs.statSync(__dirname + '/config/flowconfig');
34 + const destPath = './.flowconfig';
35 + if (fs.existsSync(destPath)) {
36 + const oldConfig = fs.readFileSync(destPath) + '';
37 + const newConfig = fs.readFileSync(srcPath) + '';
38 + if (oldConfig !== newConfig) {
39 + // Use the mtime to detect if the file was manually edited. If so,
40 + // log an error.
41 + const destStat = fs.statSync(destPath);
42 + if (destStat.mtimeMs - srcStat.mtimeMs > 1) {
43 + console.error(
44 + chalk.red(
45 + 'Detected manual changes to .flowconfig, which is a generated ' +
46 + 'file. These changes have been discarded.\n\n' +
47 + 'To change the Flow config, edit the template in ' +
48 + 'scripts/flow/config/flowconfig. Then run this command again.\n',
49 + ),
50 + );
51 + }
52 + fs.unlinkSync(destPath);
53 + fs.copyFileSync(srcPath, destPath);
54 + // Set the mtime of the copied file to be same as the original file,
55 + // so that the ahove check works.
56 + fs.utimesSync(destPath, srcStat.atime, srcStat.mtime);
57 + }
58 + } else {
59 + fs.copyFileSync(srcPath, destPath);
60 + fs.utimesSync(destPath, srcStat.atime, srcStat.mtime);
61 + }
62 +
63 + console.log(
64 + 'Running Flow on the ' + chalk.yellow(renderer) + ' renderer...',
65 + );
66 +
67 spawn(cmd, args, {
68 // Allow colors to pass through:
69 stdio: 'inherit',
27 - // Use a specific renderer config:
28 - cwd: process.cwd() + '/scripts/flow/' + renderer + '/',
70 }).on('close', function(code) {
71 if (code !== 0) {
72 console.error(