Skip to content

Commit 52a0d6b

Browse files
authored
Make Flow work with your editor (facebook#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.
1 parent cfefc81 commit 52a0d6b

File tree

3 files changed

+57
-14
lines changed

3 files changed

+57
-14
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
.DS_STORE
22
node_modules
33
scripts/flow/*/.flowconfig
4+
.flowconfig
45
*~
56
*.pyc
67
.grunt

scripts/flow/config/flowconfig

+9-8
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
[ignore]
22
.*/scripts/bench/.*
3+
.*/build/.*
34

45
# These shims are copied into external projects:
56
.*/rollup/shims/facebook-www/.*
@@ -20,16 +21,16 @@
2021
%REACT_RENDERER_FLOW_IGNORES%
2122

2223
[include]
23-
../../../node_modules/
24-
../../../packages/
25-
../../../scripts/
24+
./node_modules/
25+
./packages/
26+
./scripts/
2627

2728
[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
3334

3435
[lints]
3536
untyped-type-import=error

scripts/flow/runFlow.js

+47-6
Original file line numberDiff line numberDiff line change
@@ -8,24 +8,65 @@
88
'use strict';
99

1010
const chalk = require('chalk');
11-
const spawn = require('child_process').spawn;
11+
const {spawn} = require('child_process');
12+
const fs = require('fs');
1213

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.
1319
require('./createFlowConfigs');
1420

1521
async function runFlow(renderer, args) {
1622
return new Promise(resolve => {
17-
console.log(
18-
'Running Flow on the ' + chalk.yellow(renderer) + ' renderer...',
19-
);
2023
let cmd = __dirname + '/../../node_modules/.bin/flow';
2124
if (process.platform === 'win32') {
2225
cmd = cmd.replace(/\//g, '\\') + '.cmd';
2326
}
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+
2467
spawn(cmd, args, {
2568
// Allow colors to pass through:
2669
stdio: 'inherit',
27-
// Use a specific renderer config:
28-
cwd: process.cwd() + '/scripts/flow/' + renderer + '/',
2970
}).on('close', function(code) {
3071
if (code !== 0) {
3172
console.error(

0 commit comments

Comments
 (0)