From e2add2cc7f7592596c685a026d6bea201f2822f6 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Thu, 17 May 2018 18:07:28 +0100 Subject: [PATCH 1/6] Generate Flow config on install We'll need to do pre-renderer Flow passes with different configs. This is the first step to get it working. We only want the original version checked in. --- .gitignore | 3 ++- package.json | 2 +- scripts/flow/createFlowConfig.js | 25 +++++++++++++++++++++++++ .flowconfig => scripts/flow/flowconfig | 2 +- 4 files changed, 29 insertions(+), 3 deletions(-) create mode 100644 scripts/flow/createFlowConfig.js rename .flowconfig => scripts/flow/flowconfig (99%) diff --git a/.gitignore b/.gitignore index 53fe182d972..9beafb9a0bf 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ .DS_STORE node_modules +.flowconfig *~ *.pyc .grunt @@ -21,4 +22,4 @@ chrome-user-data *.iml .vscode *.swp -*.swo \ No newline at end of file +*.swo diff --git a/package.json b/package.json index 0d70a61bb12..a5a6a25bd93 100644 --- a/package.json +++ b/package.json @@ -110,7 +110,7 @@ "linc": "node ./scripts/tasks/linc.js", "lint": "node ./scripts/tasks/eslint.js", "lint-build": "node ./scripts/rollup/validate/index.js", - "postinstall": "node node_modules/fbjs-scripts/node/check-dev-engines.js package.json", + "postinstall": "node node_modules/fbjs-scripts/node/check-dev-engines.js package.json && node ./scripts/flow/createFlowConfig.js", "debug-test": "cross-env NODE_ENV=development node --inspect-brk node_modules/.bin/jest --config ./scripts/jest/config.source.js --runInBand", "test": "cross-env NODE_ENV=development jest --config ./scripts/jest/config.source.js", "test-prod": "cross-env NODE_ENV=production jest --config ./scripts/jest/config.source.js", diff --git a/scripts/flow/createFlowConfig.js b/scripts/flow/createFlowConfig.js new file mode 100644 index 00000000000..e3eec2be44c --- /dev/null +++ b/scripts/flow/createFlowConfig.js @@ -0,0 +1,25 @@ +'use strict'; + +const chalk = require('chalk'); +const fs = require('fs'); + +const config = fs.readFileSync(__dirname + '/flowconfig'); +const disclaimer = ` +# --------------------------------------------------------# +# NOTE: this file is generated. # +# If you want to edit it, open ./scripts/flow/flowconfig. # +# Then run Yarn for changes to take effect. # +# --------------------------------------------------------# +`.trim(); + +fs.writeFileSync( + process.cwd() + '/.flowconfig', + ` +${disclaimer} + +${config} + +${disclaimer} +`.trim(), +); +console.log(chalk.dim('Wrote the Flow configuration to .flowconfig')); diff --git a/.flowconfig b/scripts/flow/flowconfig similarity index 99% rename from .flowconfig rename to scripts/flow/flowconfig index 634625671f9..e72729c39ca 100644 --- a/.flowconfig +++ b/scripts/flow/flowconfig @@ -42,4 +42,4 @@ suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy suppress_comment=\\(.\\|\n\\)*\\$FlowExpectedError [version] -^0.61.0 +^0.61.0 \ No newline at end of file From 42b3fe0f1fb7d79191c5495a04dd38a54524e5bc Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Thu, 17 May 2018 19:27:59 +0100 Subject: [PATCH 2/6] Create multiple Flow configs from a template --- .gitignore | 2 +- package.json | 2 +- scripts/flow/{ => config}/flowconfig | 28 ++-- scripts/flow/createFlowConfig.js | 25 ---- scripts/flow/createFlowConfigs.js | 43 +++++++ scripts/flow/environment.js | 154 ++++++++++++++++++++++ scripts/flow/react-native-host-hooks.js | 162 ------------------------ scripts/tasks/flow-ci.js | 6 +- scripts/tasks/flow.js | 6 +- 9 files changed, 221 insertions(+), 207 deletions(-) rename scripts/flow/{ => config}/flowconfig (53%) delete mode 100644 scripts/flow/createFlowConfig.js create mode 100644 scripts/flow/createFlowConfigs.js delete mode 100644 scripts/flow/react-native-host-hooks.js diff --git a/.gitignore b/.gitignore index 9beafb9a0bf..bf3af85e9eb 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,6 @@ .DS_STORE node_modules -.flowconfig +scripts/flow/*/.flowconfig *~ *.pyc .grunt diff --git a/package.json b/package.json index a5a6a25bd93..520222adcf9 100644 --- a/package.json +++ b/package.json @@ -110,7 +110,7 @@ "linc": "node ./scripts/tasks/linc.js", "lint": "node ./scripts/tasks/eslint.js", "lint-build": "node ./scripts/rollup/validate/index.js", - "postinstall": "node node_modules/fbjs-scripts/node/check-dev-engines.js package.json && node ./scripts/flow/createFlowConfig.js", + "postinstall": "node node_modules/fbjs-scripts/node/check-dev-engines.js package.json && node ./scripts/flow/createFlowConfigs.js", "debug-test": "cross-env NODE_ENV=development node --inspect-brk node_modules/.bin/jest --config ./scripts/jest/config.source.js --runInBand", "test": "cross-env NODE_ENV=development jest --config ./scripts/jest/config.source.js", "test-prod": "cross-env NODE_ENV=production jest --config ./scripts/jest/config.source.js", diff --git a/scripts/flow/flowconfig b/scripts/flow/config/flowconfig similarity index 53% rename from scripts/flow/flowconfig rename to scripts/flow/config/flowconfig index e72729c39ca..4fd71c24a0f 100644 --- a/scripts/flow/flowconfig +++ b/scripts/flow/config/flowconfig @@ -1,25 +1,25 @@ [ignore] - -/fixtures/.* -/build/.* -/scripts/bench/.* +.*/scripts/bench/.* # These shims are copied into external projects: -/scripts/rollup/shims/facebook-www/.* -/scripts/rollup/shims/react-native/.* +.*/rollup/shims/facebook-www/.* +.*/rollup/shims/react-native/.* -/.*/node_modules/y18n/.* -/node_modules/chrome-devtools-frontend/.* -/node_modules/devtools-timeline-model/.* -/node_modules/create-react-class/.* -/.*/__mocks__/.* -/.*/__tests__/.* +.*/node_modules/y18n/.* +.*/node_modules/chrome-devtools-frontend/.* +.*/node_modules/devtools-timeline-model/.* +.*/node_modules/create-react-class/.* +.*/__mocks__/.* +.*/__tests__/.* [include] +../../../node_modules/ +../../../packages/ +../../../scripts/ [libs] -./node_modules/fbjs/flow/lib/dev.js -./scripts/flow +../../../node_modules/fbjs/flow/lib/dev.js +../environment.js [lints] untyped-type-import=error diff --git a/scripts/flow/createFlowConfig.js b/scripts/flow/createFlowConfig.js deleted file mode 100644 index e3eec2be44c..00000000000 --- a/scripts/flow/createFlowConfig.js +++ /dev/null @@ -1,25 +0,0 @@ -'use strict'; - -const chalk = require('chalk'); -const fs = require('fs'); - -const config = fs.readFileSync(__dirname + '/flowconfig'); -const disclaimer = ` -# --------------------------------------------------------# -# NOTE: this file is generated. # -# If you want to edit it, open ./scripts/flow/flowconfig. # -# Then run Yarn for changes to take effect. # -# --------------------------------------------------------# -`.trim(); - -fs.writeFileSync( - process.cwd() + '/.flowconfig', - ` -${disclaimer} - -${config} - -${disclaimer} -`.trim(), -); -console.log(chalk.dim('Wrote the Flow configuration to .flowconfig')); diff --git a/scripts/flow/createFlowConfigs.js b/scripts/flow/createFlowConfigs.js new file mode 100644 index 00000000000..647b953eabc --- /dev/null +++ b/scripts/flow/createFlowConfigs.js @@ -0,0 +1,43 @@ +'use strict'; + +const chalk = require('chalk'); +const fs = require('fs'); + +const config = fs.readFileSync(__dirname + '/config/flowconfig'); + +function writeConfig(folder) { + const disclaimer = ` +# ---------------------------------------------------------------# +# NOTE: this file is generated. # +# If you want to edit it, open ./scripts/flow/config/flowconfig. # +# Then run Yarn for changes to take effect. # +# ---------------------------------------------------------------# + `.trim(); + + const configFile = folder + '/.flowconfig'; + let oldConfig; + try { + oldConfig = fs.readFileSync(configFile); + } catch (err) { + oldConfig = null; + } + const newConfig = ` +${disclaimer} +${config} +${disclaimer} +`.trim(); + if (newConfig !== oldConfig) { + fs.writeFileSync(configFile, newConfig); + } +} + +// Write multiple configs in different folders +// so that we can run those checks in parallel if we want. +writeConfig(__dirname + '/dom'); +writeConfig(__dirname + '/fabric'); +writeConfig(__dirname + '/native'); +writeConfig(__dirname + '/test'); + +console.log( + chalk.dim('Wrote the Flow configurations to ./scripts/flow/*/.flowconfig'), +); diff --git a/scripts/flow/environment.js b/scripts/flow/environment.js index e7f477360e9..b5ff278bb2c 100644 --- a/scripts/flow/environment.js +++ b/scripts/flow/environment.js @@ -9,6 +9,11 @@ /* eslint-disable */ +import type { + ReactNativeBaseComponentViewConfig, + ViewConfigGetter, +} from 'react-native-renderer/src/ReactNativeTypes'; + declare var __REACT_DEVTOOLS_GLOBAL_HOOK__: any; /*?{ inject: ?((stuff: Object) => void) };*/ @@ -32,3 +37,152 @@ declare module 'EventListener' { capture: (target: Element, type: string, callback: Function) => mixed, }; } + +// React Native host hooks + +declare module 'deepDiffer' { + declare module.exports: (one: any, two: any) => boolean; +} +declare module 'deepFreezeAndThrowOnMutationInDev' { + declare module.exports: (obj: T) => T; +} +declare module 'flattenStyle' { +} +declare module 'InitializeCore' { +} +declare module 'RCTEventEmitter' { + declare function register(mixed): void; +} +declare module 'TextInputState' { + declare function blurTextInput(object: any): void; + declare function focusTextInput(object: any): void; +} +declare module 'ExceptionsManager' { + declare function handleException(error: Error, isFatal: boolean): void; +} +declare module 'Platform' { + declare var OS: string; +} +declare module 'UIManager' { + declare var customBubblingEventTypes: Object; + declare var customDirectEventTypes: Object; + declare function createView( + reactTag: number, + viewName: string, + rootTag: number, + props: ?Object, + ): void; + declare function manageChildren( + containerTag: number, + moveFromIndices: Array, + moveToIndices: Array, + addChildReactTags: Array, + addAtIndices: Array, + removeAtIndices: Array, + ): void; + declare function measure(hostComponent: mixed, callback: Function): void; + declare function measureInWindow( + nativeTag: ?number, + callback: Function, + ): void; + declare function measureLayout( + nativeTag: mixed, + nativeNode: number, + onFail: Function, + onSuccess: Function, + ): void; + declare function removeRootView(containerTag: number): void; + declare function removeSubviewsFromContainerWithID(containerId: number): void; + declare function replaceExistingNonRootView(): void; + declare function setChildren( + containerTag: number, + reactTags: Array, + ): void; + declare function updateView( + reactTag: number, + viewName: string, + props: ?Object, + ): void; + declare function __takeSnapshot( + view?: 'window' | Element | number, + options?: { + width?: number, + height?: number, + format?: 'png' | 'jpeg', + quality?: number, + }, + ): Promise; + declare function setJSResponder( + reactTag: number, + blockNativeResponder: boolean, + ): void; + declare function clearJSResponder(): void; +} + +declare module 'FabricUIManager' { + declare function createNode( + reactTag: number, + viewName: string, + rootTag: number, + props: ?Object, + instanceHandle: Object, + ): Object; + declare function cloneNode(node: Object, instanceHandle: Object): Object; + declare function cloneNodeWithNewChildren( + node: Object, + instanceHandle: Object, + ): Object; + declare function cloneNodeWithNewProps( + node: Object, + newProps: ?Object, + instanceHandle: Object, + ): Object; + declare function cloneNodeWithNewChildrenAndProps( + node: Object, + newProps: ?Object, + instanceHandle: Object, + ): Object; + declare function appendChild(node: Object, childNode: Object): void; + + declare function createChildSet(rootTag: number): Object; + declare function appendChildToSet(childSet: Object, childNode: Object): void; + declare function completeRoot(rootTag: number, childSet: Object): void; +} + +declare module 'View' { + declare module.exports: typeof React$Component; +} + +declare module 'RTManager' { + declare function createNode( + tag: number, + classType: string, + props: ?Object, + ): void; + + declare function beginUpdates(): void; + + declare function appendChildToContext( + contextTag: number, + childTag: number, + ): void; + declare function appendChild(parentTag: number, childTag: number): void; + declare function prependChild(childTag: number, beforeTag: number): void; + declare function deleteChild(childTag: number): void; + declare function updateNode(tag: number, props: ?Object): void; + + declare function completeUpdates(): void; +} + +declare module 'BatchedBridge' { + declare function registerCallableModule(name: string, module: Object): void; +} + +declare module 'ReactNativeViewConfigRegistry' { + declare var customBubblingEventTypes: Object; + declare var customDirectEventTypes: Object; + declare var eventTypes: Object; + + declare function register(name: string, callback: ViewConfigGetter): string; + declare function get(name: string): ReactNativeBaseComponentViewConfig; +} diff --git a/scripts/flow/react-native-host-hooks.js b/scripts/flow/react-native-host-hooks.js deleted file mode 100644 index 5d3971b1c32..00000000000 --- a/scripts/flow/react-native-host-hooks.js +++ /dev/null @@ -1,162 +0,0 @@ -/** - * Copyright (c) 2015-present, Facebook, Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * @flow - */ - -/* eslint-disable */ - -import type { - ReactNativeBaseComponentViewConfig, - ViewConfigGetter, -} from 'react-native-renderer/src/ReactNativeTypes'; - -declare module 'deepDiffer' { - declare module.exports: (one: any, two: any) => boolean; -} -declare module 'deepFreezeAndThrowOnMutationInDev' { - declare module.exports: (obj: T) => T; -} -declare module 'flattenStyle' { -} -declare module 'InitializeCore' { -} -declare module 'RCTEventEmitter' { - declare function register(mixed): void; -} -declare module 'TextInputState' { - declare function blurTextInput(object: any): void; - declare function focusTextInput(object: any): void; -} -declare module 'ExceptionsManager' { - declare function handleException(error: Error, isFatal: boolean): void; -} -declare module 'Platform' { - declare var OS: string; -} -declare module 'UIManager' { - declare var customBubblingEventTypes: Object; - declare var customDirectEventTypes: Object; - declare function createView( - reactTag: number, - viewName: string, - rootTag: number, - props: ?Object, - ): void; - declare function manageChildren( - containerTag: number, - moveFromIndices: Array, - moveToIndices: Array, - addChildReactTags: Array, - addAtIndices: Array, - removeAtIndices: Array, - ): void; - declare function measure(hostComponent: mixed, callback: Function): void; - declare function measureInWindow( - nativeTag: ?number, - callback: Function, - ): void; - declare function measureLayout( - nativeTag: mixed, - nativeNode: number, - onFail: Function, - onSuccess: Function, - ): void; - declare function removeRootView(containerTag: number): void; - declare function removeSubviewsFromContainerWithID(containerId: number): void; - declare function replaceExistingNonRootView(): void; - declare function setChildren( - containerTag: number, - reactTags: Array, - ): void; - declare function updateView( - reactTag: number, - viewName: string, - props: ?Object, - ): void; - declare function __takeSnapshot( - view?: 'window' | Element | number, - options?: { - width?: number, - height?: number, - format?: 'png' | 'jpeg', - quality?: number, - }, - ): Promise; - declare function setJSResponder( - reactTag: number, - blockNativeResponder: boolean, - ): void; - declare function clearJSResponder(): void; -} - -declare module 'FabricUIManager' { - declare function createNode( - reactTag: number, - viewName: string, - rootTag: number, - props: ?Object, - instanceHandle: Object, - ): Object; - declare function cloneNode(node: Object, instanceHandle: Object): Object; - declare function cloneNodeWithNewChildren( - node: Object, - instanceHandle: Object, - ): Object; - declare function cloneNodeWithNewProps( - node: Object, - newProps: ?Object, - instanceHandle: Object, - ): Object; - declare function cloneNodeWithNewChildrenAndProps( - node: Object, - newProps: ?Object, - instanceHandle: Object, - ): Object; - declare function appendChild(node: Object, childNode: Object): void; - - declare function createChildSet(rootTag: number): Object; - declare function appendChildToSet(childSet: Object, childNode: Object): void; - declare function completeRoot(rootTag: number, childSet: Object): void; -} - -declare module 'View' { - declare module.exports: typeof React$Component; -} - -declare module 'RTManager' { - declare function createNode( - tag: number, - classType: string, - props: ?Object, - ): void; - - declare function beginUpdates(): void; - - declare function appendChildToContext( - contextTag: number, - childTag: number, - ): void; - declare function appendChild(parentTag: number, childTag: number): void; - declare function prependChild(childTag: number, beforeTag: number): void; - declare function deleteChild(childTag: number): void; - declare function updateNode(tag: number, props: ?Object): void; - - declare function completeUpdates(): void; -} - -declare module 'BatchedBridge' { - declare function registerCallableModule(name: string, module: Object): void; -} - -declare module 'ReactNativeViewConfigRegistry' { - declare var customBubblingEventTypes: Object; - declare var customDirectEventTypes: Object; - declare var eventTypes: Object; - - declare function register(name: string, callback: ViewConfigGetter): string; - declare function get(name: string): ReactNativeBaseComponentViewConfig; -} diff --git a/scripts/tasks/flow-ci.js b/scripts/tasks/flow-ci.js index d3881e03697..c8d70ff9bdd 100644 --- a/scripts/tasks/flow-ci.js +++ b/scripts/tasks/flow-ci.js @@ -7,7 +7,8 @@ 'use strict'; -const path = require('path'); +require('../flow/createFlowConfigs'); + const spawn = require('child_process').spawn; const extension = process.platform === 'win32' ? '.cmd' : ''; @@ -15,9 +16,10 @@ const extension = process.platform === 'win32' ? '.cmd' : ''; // This script forces a complete check. // Use it for the CI. -spawn(path.join('node_modules', '.bin', 'flow' + extension), ['check', '.'], { +spawn('../../../node_modules/.bin/flow' + extension, ['check', '.'], { // Allow colors to pass through stdio: 'inherit', + cwd: process.cwd() + '/scripts/flow/dom/', }).on('close', function(code) { if (code !== 0) { console.error('Flow failed'); diff --git a/scripts/tasks/flow.js b/scripts/tasks/flow.js index a7b643712b3..c68f219ad85 100644 --- a/scripts/tasks/flow.js +++ b/scripts/tasks/flow.js @@ -7,7 +7,8 @@ 'use strict'; -const path = require('path'); +require('../flow/createFlowConfigs'); + const spawn = require('child_process').spawn; const extension = process.platform === 'win32' ? '.cmd' : ''; @@ -15,9 +16,10 @@ const extension = process.platform === 'win32' ? '.cmd' : ''; // This script is using `flow status` for a quick check with a server. // Use it for local development. -spawn(path.join('node_modules', '.bin', 'flow' + extension), ['status', '.'], { +spawn('../../../node_modules/.bin/flow' + extension, ['status', '.'], { // Allow colors to pass through stdio: 'inherit', + cwd: process.cwd() + '/scripts/flow/dom/', }).on('close', function(code) { if (code !== 0) { console.error('Flow failed'); From 117222140e315cc2eeec5cefba5df8b737a780ab Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Thu, 17 May 2018 19:45:11 +0100 Subject: [PATCH 3/6] Run Flow per renderer --- scripts/flow/createFlowConfigs.js | 19 ++++++++-------- scripts/flow/runFlow.js | 37 +++++++++++++++++++++++++++++++ scripts/flow/typedRenderers.js | 3 +++ scripts/tasks/flow-ci.js | 28 +++++++++-------------- scripts/tasks/flow.js | 33 ++++++++++++++------------- 5 files changed, 77 insertions(+), 43 deletions(-) create mode 100644 scripts/flow/runFlow.js create mode 100644 scripts/flow/typedRenderers.js diff --git a/scripts/flow/createFlowConfigs.js b/scripts/flow/createFlowConfigs.js index 647b953eabc..ab31dc4cbbb 100644 --- a/scripts/flow/createFlowConfigs.js +++ b/scripts/flow/createFlowConfigs.js @@ -2,10 +2,14 @@ const chalk = require('chalk'); const fs = require('fs'); +const mkdirp = require('mkdirp'); +const {typedRenderers} = require('./typedRenderers'); const config = fs.readFileSync(__dirname + '/config/flowconfig'); function writeConfig(folder) { + mkdirp.sync(folder); + const disclaimer = ` # ---------------------------------------------------------------# # NOTE: this file is generated. # @@ -17,7 +21,7 @@ function writeConfig(folder) { const configFile = folder + '/.flowconfig'; let oldConfig; try { - oldConfig = fs.readFileSync(configFile); + oldConfig = fs.readFileSync(configFile).toString(); } catch (err) { oldConfig = null; } @@ -26,18 +30,15 @@ ${disclaimer} ${config} ${disclaimer} `.trim(); + if (newConfig !== oldConfig) { fs.writeFileSync(configFile, newConfig); + console.log(chalk.dim('Wrote a Flow config to ' + configFile)); } } // Write multiple configs in different folders // so that we can run those checks in parallel if we want. -writeConfig(__dirname + '/dom'); -writeConfig(__dirname + '/fabric'); -writeConfig(__dirname + '/native'); -writeConfig(__dirname + '/test'); - -console.log( - chalk.dim('Wrote the Flow configurations to ./scripts/flow/*/.flowconfig'), -); +typedRenderers.forEach(renderer => { + writeConfig(__dirname + '/' + renderer); +}); diff --git a/scripts/flow/runFlow.js b/scripts/flow/runFlow.js new file mode 100644 index 00000000000..92c8f20f17e --- /dev/null +++ b/scripts/flow/runFlow.js @@ -0,0 +1,37 @@ +'use strict'; + +const chalk = require('chalk'); +const spawn = require('child_process').spawn; +const extension = process.platform === 'win32' ? '.cmd' : ''; + +require('./createFlowConfigs'); + +async function runFlow(renderer, args) { + return new Promise(resolve => { + console.log( + 'Running Flow for the ' + chalk.cyan(renderer) + ' renderer...', + ); + spawn('../../../node_modules/.bin/flow' + extension, args, { + // Allow colors to pass through: + stdio: 'inherit', + // Use a specific renderer config: + cwd: process.cwd() + '/scripts/flow/' + renderer + '/', + }).on('close', function(code) { + if (code !== 0) { + console.error( + 'Flow failed for the ' + chalk.red(renderer) + ' renderer', + ); + console.log(); + process.exit(code); + } else { + console.log( + 'Flow passed for the ' + chalk.green(renderer) + ' renderer', + ); + console.log(); + resolve(); + } + }); + }); +} + +module.exports = runFlow; diff --git a/scripts/flow/typedRenderers.js b/scripts/flow/typedRenderers.js new file mode 100644 index 00000000000..ded6a1200df --- /dev/null +++ b/scripts/flow/typedRenderers.js @@ -0,0 +1,3 @@ +'use strict'; + +exports.typedRenderers = ['dom', 'fabric', 'native', 'test']; diff --git a/scripts/tasks/flow-ci.js b/scripts/tasks/flow-ci.js index c8d70ff9bdd..ae4ff292812 100644 --- a/scripts/tasks/flow-ci.js +++ b/scripts/tasks/flow-ci.js @@ -7,25 +7,17 @@ 'use strict'; -require('../flow/createFlowConfigs'); - -const spawn = require('child_process').spawn; - -const extension = process.platform === 'win32' ? '.cmd' : ''; +process.on('unhandledRejection', err => { + throw err; +}); -// This script forces a complete check. -// Use it for the CI. +const runFlow = require('../flow/runFlow'); +const {typedRenderers} = require('../flow/typedRenderers'); -spawn('../../../node_modules/.bin/flow' + extension, ['check', '.'], { - // Allow colors to pass through - stdio: 'inherit', - cwd: process.cwd() + '/scripts/flow/dom/', -}).on('close', function(code) { - if (code !== 0) { - console.error('Flow failed'); - } else { - console.log('Flow passed'); +async function checkAll() { + for (let renderer of typedRenderers) { + await runFlow(renderer, ['check']); } +} - process.exit(code); -}); +checkAll(); diff --git a/scripts/tasks/flow.js b/scripts/tasks/flow.js index c68f219ad85..a16ee372aee 100644 --- a/scripts/tasks/flow.js +++ b/scripts/tasks/flow.js @@ -7,25 +7,26 @@ 'use strict'; -require('../flow/createFlowConfigs'); - -const spawn = require('child_process').spawn; +process.on('unhandledRejection', err => { + throw err; +}); -const extension = process.platform === 'win32' ? '.cmd' : ''; +const runFlow = require('../flow/runFlow'); +const {typedRenderers} = require('../flow/typedRenderers'); // This script is using `flow status` for a quick check with a server. // Use it for local development. -spawn('../../../node_modules/.bin/flow' + extension, ['status', '.'], { - // Allow colors to pass through - stdio: 'inherit', - cwd: process.cwd() + '/scripts/flow/dom/', -}).on('close', function(code) { - if (code !== 0) { - console.error('Flow failed'); - } else { - console.log('Flow passed'); - } +const primaryRenderer = process.argv[2]; +if (typedRenderers.indexOf(primaryRenderer) === -1) { + console.error( + 'You need to pass a primary renderer to yarn flow. For example:' + ); + typedRenderers.forEach(renderer => { + console.log(' * yarn flow ' + renderer); + }); + console.log(); + process.exit(1); +} - process.exit(code); -}); +runFlow(primaryRenderer, ['status']); From cd8890f71fd7a457075b7ac1f2fd6fb113b70d45 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Thu, 17 May 2018 20:13:11 +0100 Subject: [PATCH 4/6] Lint --- scripts/tasks/flow-ci.js | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/tasks/flow-ci.js b/scripts/tasks/flow-ci.js index ae4ff292812..1d7444eb7ab 100644 --- a/scripts/tasks/flow-ci.js +++ b/scripts/tasks/flow-ci.js @@ -15,6 +15,7 @@ const runFlow = require('../flow/runFlow'); const {typedRenderers} = require('../flow/typedRenderers'); async function checkAll() { + // eslint-disable-next-line no-for-of-loops/no-for-of-loops for (let renderer of typedRenderers) { await runFlow(renderer, ['check']); } From adf7f9249102ac6333b5094f1ac58dc84301f7fc Mon Sep 17 00:00:00 2001 From: Dan Date: Thu, 17 May 2018 20:27:59 +0100 Subject: [PATCH 5/6] Revert the environment consolidation I thought this would be a bit cleaner at first because we now have non-environment files in this directory. But Sebastian is changing these files at the same time so I want to avoid conflicts and keep the PR more tightly scoped. Undo. --- scripts/flow/config/flowconfig | 1 + scripts/flow/environment.js | 156 +---------------------- scripts/flow/react-native-host-hooks.js | 162 ++++++++++++++++++++++++ 3 files changed, 164 insertions(+), 155 deletions(-) create mode 100644 scripts/flow/react-native-host-hooks.js diff --git a/scripts/flow/config/flowconfig b/scripts/flow/config/flowconfig index 4fd71c24a0f..6484737582b 100644 --- a/scripts/flow/config/flowconfig +++ b/scripts/flow/config/flowconfig @@ -20,6 +20,7 @@ [libs] ../../../node_modules/fbjs/flow/lib/dev.js ../environment.js +../react-native-host-hooks.js [lints] untyped-type-import=error diff --git a/scripts/flow/environment.js b/scripts/flow/environment.js index b5ff278bb2c..3030c452659 100644 --- a/scripts/flow/environment.js +++ b/scripts/flow/environment.js @@ -9,11 +9,6 @@ /* eslint-disable */ -import type { - ReactNativeBaseComponentViewConfig, - ViewConfigGetter, -} from 'react-native-renderer/src/ReactNativeTypes'; - declare var __REACT_DEVTOOLS_GLOBAL_HOOK__: any; /*?{ inject: ?((stuff: Object) => void) };*/ @@ -36,153 +31,4 @@ declare module 'EventListener' { listen: (target: Element, type: string, callback: Function) => mixed, capture: (target: Element, type: string, callback: Function) => mixed, }; -} - -// React Native host hooks - -declare module 'deepDiffer' { - declare module.exports: (one: any, two: any) => boolean; -} -declare module 'deepFreezeAndThrowOnMutationInDev' { - declare module.exports: (obj: T) => T; -} -declare module 'flattenStyle' { -} -declare module 'InitializeCore' { -} -declare module 'RCTEventEmitter' { - declare function register(mixed): void; -} -declare module 'TextInputState' { - declare function blurTextInput(object: any): void; - declare function focusTextInput(object: any): void; -} -declare module 'ExceptionsManager' { - declare function handleException(error: Error, isFatal: boolean): void; -} -declare module 'Platform' { - declare var OS: string; -} -declare module 'UIManager' { - declare var customBubblingEventTypes: Object; - declare var customDirectEventTypes: Object; - declare function createView( - reactTag: number, - viewName: string, - rootTag: number, - props: ?Object, - ): void; - declare function manageChildren( - containerTag: number, - moveFromIndices: Array, - moveToIndices: Array, - addChildReactTags: Array, - addAtIndices: Array, - removeAtIndices: Array, - ): void; - declare function measure(hostComponent: mixed, callback: Function): void; - declare function measureInWindow( - nativeTag: ?number, - callback: Function, - ): void; - declare function measureLayout( - nativeTag: mixed, - nativeNode: number, - onFail: Function, - onSuccess: Function, - ): void; - declare function removeRootView(containerTag: number): void; - declare function removeSubviewsFromContainerWithID(containerId: number): void; - declare function replaceExistingNonRootView(): void; - declare function setChildren( - containerTag: number, - reactTags: Array, - ): void; - declare function updateView( - reactTag: number, - viewName: string, - props: ?Object, - ): void; - declare function __takeSnapshot( - view?: 'window' | Element | number, - options?: { - width?: number, - height?: number, - format?: 'png' | 'jpeg', - quality?: number, - }, - ): Promise; - declare function setJSResponder( - reactTag: number, - blockNativeResponder: boolean, - ): void; - declare function clearJSResponder(): void; -} - -declare module 'FabricUIManager' { - declare function createNode( - reactTag: number, - viewName: string, - rootTag: number, - props: ?Object, - instanceHandle: Object, - ): Object; - declare function cloneNode(node: Object, instanceHandle: Object): Object; - declare function cloneNodeWithNewChildren( - node: Object, - instanceHandle: Object, - ): Object; - declare function cloneNodeWithNewProps( - node: Object, - newProps: ?Object, - instanceHandle: Object, - ): Object; - declare function cloneNodeWithNewChildrenAndProps( - node: Object, - newProps: ?Object, - instanceHandle: Object, - ): Object; - declare function appendChild(node: Object, childNode: Object): void; - - declare function createChildSet(rootTag: number): Object; - declare function appendChildToSet(childSet: Object, childNode: Object): void; - declare function completeRoot(rootTag: number, childSet: Object): void; -} - -declare module 'View' { - declare module.exports: typeof React$Component; -} - -declare module 'RTManager' { - declare function createNode( - tag: number, - classType: string, - props: ?Object, - ): void; - - declare function beginUpdates(): void; - - declare function appendChildToContext( - contextTag: number, - childTag: number, - ): void; - declare function appendChild(parentTag: number, childTag: number): void; - declare function prependChild(childTag: number, beforeTag: number): void; - declare function deleteChild(childTag: number): void; - declare function updateNode(tag: number, props: ?Object): void; - - declare function completeUpdates(): void; -} - -declare module 'BatchedBridge' { - declare function registerCallableModule(name: string, module: Object): void; -} - -declare module 'ReactNativeViewConfigRegistry' { - declare var customBubblingEventTypes: Object; - declare var customDirectEventTypes: Object; - declare var eventTypes: Object; - - declare function register(name: string, callback: ViewConfigGetter): string; - declare function get(name: string): ReactNativeBaseComponentViewConfig; -} +} \ No newline at end of file diff --git a/scripts/flow/react-native-host-hooks.js b/scripts/flow/react-native-host-hooks.js new file mode 100644 index 00000000000..b5ae015a49e --- /dev/null +++ b/scripts/flow/react-native-host-hooks.js @@ -0,0 +1,162 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow + */ + +/* eslint-disable */ + +import type { + ReactNativeBaseComponentViewConfig, + ViewConfigGetter, +} from 'react-native-renderer/src/ReactNativeTypes'; + +declare module 'deepDiffer' { + declare module.exports: (one: any, two: any) => boolean; +} +declare module 'deepFreezeAndThrowOnMutationInDev' { + declare module.exports: (obj: T) => T; +} +declare module 'flattenStyle' { +} +declare module 'InitializeCore' { +} +declare module 'RCTEventEmitter' { + declare function register(mixed): void; +} +declare module 'TextInputState' { + declare function blurTextInput(object: any): void; + declare function focusTextInput(object: any): void; +} +declare module 'ExceptionsManager' { + declare function handleException(error: Error, isFatal: boolean): void; +} +declare module 'Platform' { + declare var OS: string; +} +declare module 'UIManager' { + declare var customBubblingEventTypes: Object; + declare var customDirectEventTypes: Object; + declare function createView( + reactTag: number, + viewName: string, + rootTag: number, + props: ?Object, + ): void; + declare function manageChildren( + containerTag: number, + moveFromIndices: Array, + moveToIndices: Array, + addChildReactTags: Array, + addAtIndices: Array, + removeAtIndices: Array, + ): void; + declare function measure(hostComponent: mixed, callback: Function): void; + declare function measureInWindow( + nativeTag: ?number, + callback: Function, + ): void; + declare function measureLayout( + nativeTag: mixed, + nativeNode: number, + onFail: Function, + onSuccess: Function, + ): void; + declare function removeRootView(containerTag: number): void; + declare function removeSubviewsFromContainerWithID(containerId: number): void; + declare function replaceExistingNonRootView(): void; + declare function setChildren( + containerTag: number, + reactTags: Array, + ): void; + declare function updateView( + reactTag: number, + viewName: string, + props: ?Object, + ): void; + declare function __takeSnapshot( + view?: 'window' | Element | number, + options?: { + width?: number, + height?: number, + format?: 'png' | 'jpeg', + quality?: number, + }, + ): Promise; + declare function setJSResponder( + reactTag: number, + blockNativeResponder: boolean, + ): void; + declare function clearJSResponder(): void; +} + +declare module 'FabricUIManager' { + declare function createNode( + reactTag: number, + viewName: string, + rootTag: number, + props: ?Object, + instanceHandle: Object, + ): Object; + declare function cloneNode(node: Object, instanceHandle: Object): Object; + declare function cloneNodeWithNewChildren( + node: Object, + instanceHandle: Object, + ): Object; + declare function cloneNodeWithNewProps( + node: Object, + newProps: ?Object, + instanceHandle: Object, + ): Object; + declare function cloneNodeWithNewChildrenAndProps( + node: Object, + newProps: ?Object, + instanceHandle: Object, + ): Object; + declare function appendChild(node: Object, childNode: Object): void; + + declare function createChildSet(rootTag: number): Object; + declare function appendChildToSet(childSet: Object, childNode: Object): void; + declare function completeRoot(rootTag: number, childSet: Object): void; +} + +declare module 'View' { + declare module.exports: typeof React$Component; +} + +declare module 'RTManager' { + declare function createNode( + tag: number, + classType: string, + props: ?Object, + ): void; + + declare function beginUpdates(): void; + + declare function appendChildToContext( + contextTag: number, + childTag: number, + ): void; + declare function appendChild(parentTag: number, childTag: number): void; + declare function prependChild(childTag: number, beforeTag: number): void; + declare function deleteChild(childTag: number): void; + declare function updateNode(tag: number, props: ?Object): void; + + declare function completeUpdates(): void; +} + +declare module 'BatchedBridge' { + declare function registerCallableModule(name: string, module: Object): void; +} + +declare module 'ReactNativeViewConfigRegistry' { + declare var customBubblingEventTypes: Object; + declare var customDirectEventTypes: Object; + declare var eventTypes: Object; + + declare function register(name: string, callback: ViewConfigGetter): string; + declare function get(name: string): ReactNativeBaseComponentViewConfig; +} \ No newline at end of file From b37441823c19eba1980844f9419dfcf68badd212 Mon Sep 17 00:00:00 2001 From: Dan Date: Thu, 17 May 2018 20:34:34 +0100 Subject: [PATCH 6/6] Misc --- scripts/flow/environment.js | 2 +- scripts/flow/react-native-host-hooks.js | 2 +- scripts/tasks/flow.js | 22 +++++++++++++++++++--- 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/scripts/flow/environment.js b/scripts/flow/environment.js index 3030c452659..e7f477360e9 100644 --- a/scripts/flow/environment.js +++ b/scripts/flow/environment.js @@ -31,4 +31,4 @@ declare module 'EventListener' { listen: (target: Element, type: string, callback: Function) => mixed, capture: (target: Element, type: string, callback: Function) => mixed, }; -} \ No newline at end of file +} diff --git a/scripts/flow/react-native-host-hooks.js b/scripts/flow/react-native-host-hooks.js index b5ae015a49e..5d3971b1c32 100644 --- a/scripts/flow/react-native-host-hooks.js +++ b/scripts/flow/react-native-host-hooks.js @@ -159,4 +159,4 @@ declare module 'ReactNativeViewConfigRegistry' { declare function register(name: string, callback: ViewConfigGetter): string; declare function get(name: string): ReactNativeBaseComponentViewConfig; -} \ No newline at end of file +} diff --git a/scripts/tasks/flow.js b/scripts/tasks/flow.js index a16ee372aee..4c587432c1b 100644 --- a/scripts/tasks/flow.js +++ b/scripts/tasks/flow.js @@ -11,6 +11,7 @@ process.on('unhandledRejection', err => { throw err; }); +const chalk = require('chalk'); const runFlow = require('../flow/runFlow'); const {typedRenderers} = require('../flow/typedRenderers'); @@ -19,13 +20,28 @@ const {typedRenderers} = require('../flow/typedRenderers'); const primaryRenderer = process.argv[2]; if (typedRenderers.indexOf(primaryRenderer) === -1) { - console.error( - 'You need to pass a primary renderer to yarn flow. For example:' + console.log( + 'The ' + + chalk.red('yarn flow') + + ' command now requires you to pick a primary renderer:' ); + console.log(); typedRenderers.forEach(renderer => { - console.log(' * yarn flow ' + renderer); + console.log(' * ' + chalk.cyan('yarn flow ' + renderer)); }); console.log(); + console.log('If you are not sure, run ' + chalk.green('yarn flow dom') + '.'); + console.log( + 'This will still typecheck non-DOM packages, although less precisely.' + ); + console.log(); + console.log('Note that checks for all renderers will run on CI.'); + console.log( + 'You can also do this locally with ' + + chalk.cyan('yarn flow-ci') + + ' but it will be slow.' + ); + console.log(); process.exit(1); }