Add shared monorepo build setup (#38718)

Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/38718

> NOTE: Replaces https://github.com/facebook/react-native/pull/38240

## Context

RFC: Decoupling Flipper from React Native core: https://github.com/react-native-community/discussions-and-proposals/pull/641

## Changes

To support incoming new React Native packages around debugging (including migrating over [`react-native-community/cli-plugin-metro`](https://github.com/react-native-community/cli/tree/main/packages/cli-plugin-metro)) — which target Node.js and require a build step, this PR adds a minimal shared build setup across the `react-native` monorepo.

The setup is closely inspired/based on the build scripts in Jest, Metro, and React Native CLI — and is a simple set of script wrappers around Babel. These are available as build commands at the root of the repo:

- `yarn build` — Builds all configured packages. Functionally, this:
  - Outputs a `dist/` directory with built files.
  - Rewrites package.json `"exports"` to update every `./src/*` reference to `./dist/*` (source of truth).
- `scripts/build/babel-register.js` — Allows running all Node.js entry points from source, similar to the current setup in [facebook/metro](https://github.com/facebook/metro). (Example entry point file in this PR: `packages/dev-middleware/src/index.js`)

Build configuration (i.e. Babel config) is shared as a set standard across the monorepo, and **packages are opted-in to requiring a build**, configured in `scripts/build.config.js`.

```
const buildConfig /*: BuildConfig */ = {
  // The packages to include for build and their build options
  packages: {
    'dev-middleware': {target: 'node'},
  },
};
```

For now, there is a single `target: 'node'` option — this is necessary as `react-native`, unlike the above other projects, is a repository with packages targeting several runtimes. We may, in future, introduce a build step for other, non-Node, packages — which may be useful for things such as auto-generated TypeScript definitions.

 {F1043312771}

**Differences from the Metro setup**

- References (and compiles out) repo-local `scripts/build/babel-register.js` — removing need for an npm-published dependency.

## Current integration points

- **CircleCI** — `yarn build` is added to the `build_npm_package` and `find_and_publish_bumped_packages` jobs.

**New Node.js package(s) are not load bearing quite yet**: There are not yet any built packages added to the dependencies of `packages/react-native/`, so this will be further tested in a later PR (and is actively being done in an internal commit stack).

### Alternative designs

**Per-package config file**

Replace `scripts/build/config.js` with a package-defined key in in `package.json`, similar to Jest's [`publishConfig`](1f019afdcd/packages/jest-cli/package.json (L87C3-L89C4)).

```
"buildConfig": {
  "type": "node"
},
```

This would be the only customisation required, with a single Babel config still standardised. Another option this might receive in future is `enableTypeScriptCodgeen`.

**Rollup**

More sophisticated build tool for Node.js, used by the React codebase (albeit within a custom script setup as well).

**Lerna and Nx**

- Most sophisticated setup enabling caching and optimised cloud runs.
- Probably the most likely thing we'll move towards at a later stage.

Changelog: [Internal]

Reviewed By: NickGerleman

Differential Revision: D47760330

fbshipit-source-id: 38ec94708ce3d9946a197d80885781e9707c5841
This commit is contained in:
Alex Hunt 2023-08-03 04:42:30 -07:00 committed by Facebook GitHub Bot
parent 0fb71630c7
commit cd8f5d176a
13 changed files with 503 additions and 35 deletions

View File

@ -243,6 +243,12 @@ commands:
- ~/.cache/yarn
key: << parameters.yarn_base_cache_key >>-{{ arch }}-{{ checksum "yarn.lock" }}
build_packages:
steps:
- run:
name: Build packages
command: yarn build
brew_install:
parameters:
package:
@ -1775,6 +1781,7 @@ jobs:
cp $HERMES_WS_DIR/hermes-runtime-darwin/hermes-ios-release.tar.gz ./packages/react-native/ReactAndroid/external-artifacts/artifacts/hermes-ios-release.tar.gz
- run_yarn
- build_packages
- download_gradle_dependencies
# START: Stables and nightlies
@ -1870,6 +1877,7 @@ jobs:
steps:
- checkout
- run_yarn
- build_packages
- run:
name: Set NPM auth token
command: echo "//registry.npmjs.org/:_authToken=${CIRCLE_NPM_TOKEN}" > ~/.npmrc

40
flow-typed/npm/parseargs_v0.11.x.js vendored Normal file
View File

@ -0,0 +1,40 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict
* @format
* @oncall react_native
*/
declare module '@pkgjs/parseargs' {
declare type ParseArgsOptionConfig = {
type: 'string' | 'boolean',
short?: string,
multiple?: boolean,
};
declare type ParseArgsOptionsConfig = {
[longOption: string]: ParseArgsOptionConfig,
};
declare export type ParseArgsConfig = {
strict?: boolean,
allowPositionals?: boolean,
tokens?: boolean,
options?: ParseArgsOptionsConfig,
args?: string[],
};
declare type ParsedResults = {
values: {
[longOption: string]: void | string | boolean | Array<string | boolean>,
},
positionals: string[],
...
};
declare export function parseArgs(config: ParseArgsConfig): ParsedResults;
}

View File

@ -13,6 +13,8 @@
"scripts": {
"start": "cd packages/rn-tester && npm run start",
"android": "cd packages/rn-tester && npm run android",
"build": "node ./scripts/build/build.js",
"clean": "node ./scripts/build/clean.js",
"test": "jest",
"test-ci": "jest --maxWorkers=2 --ci --reporters=\"default\" --reporters=\"jest-junit\"",
"flow": "flow",
@ -45,14 +47,20 @@
"@babel/eslint-parser": "^7.20.0",
"@babel/generator": "^7.20.0",
"@babel/plugin-transform-regenerator": "^7.20.0",
"@babel/preset-env": "^7.20.0",
"@babel/preset-flow": "^7.20.0",
"@definitelytyped/dtslint": "^0.0.127",
"@jest/create-cache-key-function": "^29.2.1",
"@pkgjs/parseargs": "^0.11.0",
"@react-native/metro-babel-transformer": "^0.73.11",
"@react-native/metro-config": "^0.73.0",
"@types/react": "^18.0.18",
"@typescript-eslint/parser": "^5.57.1",
"async": "^3.2.2",
"babel-plugin-minify-dead-code-elimination": "^0.5.2",
"babel-plugin-transform-define": "^2.1.2",
"babel-plugin-transform-flow-enums": "^0.0.2",
"chalk": "^4.0.0",
"clang-format": "^1.8.0",
"connect": "^3.6.5",
"eslint": "^8.19.0",
@ -70,6 +78,7 @@
"eslint-plugin-redundant-undefined": "^0.4.0",
"eslint-plugin-relay": "^1.8.3",
"flow-bin": "^0.213.1",
"glob": "^7.1.1",
"hermes-eslint": "0.15.0",
"inquirer": "^7.1.0",
"jest": "^29.2.1",
@ -77,6 +86,7 @@
"jscodeshift": "^0.14.0",
"metro-babel-register": "0.77.0",
"metro-memory-fs": "0.77.0",
"micromatch": "^4.0.4",
"mkdirp": "^0.5.1",
"mock-fs": "^5.1.4",
"prettier": "2.8.8",

View File

@ -1,13 +0,0 @@
{
"presets": [
"@babel/preset-flow",
[
"@babel/preset-env",
{
"targets": {
"node": "16"
}
}
]
]
}

View File

@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict
* @flow
* @format
* @oncall react_native
*/

View File

@ -14,29 +14,19 @@
"directory": "packages/dev-middleware"
},
"license": "MIT",
"exports": "./dist/index.js",
"exports": {
".": "./src/index.js",
"./package.json": "./package.json"
},
"files": [
"dist"
],
"scripts": {
"build": "yarn clean && babel src --out-dir dist",
"dev": "babel src --out-dir dist --source-maps --watch",
"clean": "rimraf dist",
"prepare": "yarn build"
},
"dependencies": {
"chrome-launcher": "^0.15.2",
"connect": "^3.6.5",
"node-fetch": "^2.2.0",
"temp-dir": "^2.0.0"
},
"devDependencies": {
"@babel/cli": "^7.20.0",
"@babel/core": "^7.20.0",
"@babel/preset-env": "^7.20.0",
"@babel/preset-flow": "^7.20.0",
"rimraf": "^3.0.2"
},
"engines": {
"node": ">=18"
}

View File

@ -0,0 +1,12 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict
* @format
* @oncall react_native
*/
export {default as createDevMiddleware} from './createDevMiddleware';

View File

@ -4,9 +4,17 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict
* @flow
* @format
* @oncall react_native
*/
export {default as createDevMiddleware} from './createDevMiddleware';
/*::
export type * from './index.flow';
*/
if (!process.env.BUILD_EXCLUDE_BABEL_REGISTER) {
require('../../../scripts/build/babel-register').registerForMonorepo();
}
module.exports = require('./index.flow');

View File

@ -0,0 +1,54 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
* @format
* @oncall react_native
*/
const path = require('path');
const {PACKAGES_DIR} = require('./build');
const {buildConfig, getBabelConfig} = require('./config');
let isRegisteredForMonorepo = false;
/**
* Calling this function enables all Node.js packages to run from source when
* developing in the React Native repo.
*
* A call should located in each entry point file in a package (i.e. for all
* paths in "exports"), inside a special `if` condition that will be compiled
* away on build.
*
* if (!process.env.BUILD_EXCLUDE_BABEL_REGISTER) {
* require('../../../scripts/build/babel-register').registerForMonorepo();
* }
*/
function registerForMonorepo() {
if (isRegisteredForMonorepo) {
return;
}
for (const [packageName, {target}] of Object.entries(buildConfig.packages)) {
if (target === 'node') {
registerPackage(packageName);
}
}
isRegisteredForMonorepo = true;
}
function registerPackage(packageName /*: string */) {
const packageDir = path.join(PACKAGES_DIR, packageName);
require('@babel/register')({
...getBabelConfig(packageName),
root: packageDir,
ignore: [/\/node_modules\//],
});
}
module.exports = {registerForMonorepo};

186
scripts/build/build.js Normal file
View File

@ -0,0 +1,186 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
* @format
* @oncall react_native
*/
const babel = require('@babel/core');
const {parseArgs} = require('@pkgjs/parseargs');
const chalk = require('chalk');
const glob = require('glob');
const micromatch = require('micromatch');
const fs = require('fs');
const path = require('path');
const prettier = require('prettier');
const {buildConfig, getBabelConfig} = require('./config');
const PACKAGES_DIR /*: string */ = path.resolve(__dirname, '../../packages');
const SRC_DIR = 'src';
const BUILD_DIR = 'dist';
const JS_FILES_PATTERN = '**/*.js';
const IGNORE_PATTERN = '**/__{tests,mocks,fixtures}__/**';
const config = {
allowPositionals: true,
options: {
help: {type: 'boolean'},
},
};
function build() {
const {
positionals: packageNames,
values: {help},
} = parseArgs(config);
if (help) {
console.log(`
Usage: node ./scripts/build/build.js <packages>
Build packages (shared monorepo build setup).
By default, builds all packages defined in ./scripts/build/config.js. If a
a package list is provided, builds only those specified.
`);
process.exitCode = 0;
return;
}
console.log('\n' + chalk.bold.inverse('Building packages') + '\n');
if (packageNames.length) {
packageNames
.filter(packageName => packageName in buildConfig.packages)
.forEach(buildPackage);
} else {
Object.keys(buildConfig.packages).forEach(buildPackage);
}
process.exitCode = 0;
}
function buildPackage(packageName /*: string */) {
const files = glob.sync(
path.resolve(PACKAGES_DIR, packageName, SRC_DIR, '**/*'),
{nodir: true},
);
const packageJsonPath = path.join(PACKAGES_DIR, packageName, 'package.json');
process.stdout.write(
`${packageName} ${chalk.dim('.').repeat(72 - packageName.length)} `,
);
files.forEach(file => buildFile(path.normalize(file), true));
rewritePackageExports(packageJsonPath);
process.stdout.write(chalk.reset.inverse.bold.green(' DONE ') + '\n');
}
function buildFile(file /*: string */, silent /*: boolean */ = false) {
const packageName = getPackageName(file);
const buildPath = getBuildPath(file);
const logResult = ({copied, desc} /*: {copied: boolean, desc?: string} */) =>
silent ||
console.log(
chalk.dim(' - ') +
path.relative(PACKAGES_DIR, file) +
(copied ? ' -> ' + path.relative(PACKAGES_DIR, buildPath) : ' ') +
(desc != null ? ' (' + desc + ')' : ''),
);
if (micromatch.isMatch(file, IGNORE_PATTERN)) {
logResult({copied: false, desc: 'ignore'});
return;
}
fs.mkdirSync(path.dirname(buildPath), {recursive: true});
if (!micromatch.isMatch(file, JS_FILES_PATTERN)) {
fs.copyFileSync(file, buildPath);
logResult({copied: true, desc: 'copy'});
} else {
const transformed = prettier.format(
babel.transformFileSync(file, getBabelConfig(packageName)).code,
{parser: 'babel'},
);
fs.writeFileSync(buildPath, transformed);
if (/@flow/.test(fs.readFileSync(file, 'utf-8'))) {
fs.copyFileSync(file, buildPath + '.flow');
}
logResult({copied: true});
}
}
function getPackageName(file /*: string */) /*: string */ {
return path.relative(PACKAGES_DIR, file).split(path.sep)[0];
}
function getBuildPath(file /*: string */) /*: string */ {
const packageDir = path.join(PACKAGES_DIR, getPackageName(file));
return path.join(
packageDir,
file.replace(path.join(packageDir, SRC_DIR), BUILD_DIR),
);
}
function rewritePackageExports(packageJsonPath /*: string */) {
const pkg = JSON.parse(fs.readFileSync(packageJsonPath, {encoding: 'utf8'}));
if (pkg.exports == null) {
return;
}
pkg.exports = rewriteExportsField(pkg.exports);
fs.writeFileSync(
packageJsonPath,
prettier.format(JSON.stringify(pkg), {parser: 'json'}),
);
}
/*::
type ExportsField = {
[subpath: string]: ExportsField | string,
} | string;
*/
function rewriteExportsField(
exportsField /*: ExportsField */,
) /*: ExportsField */ {
if (typeof exportsField === 'string') {
return rewriteExportsTarget(exportsField);
}
for (const key in exportsField) {
if (typeof exportsField[key] === 'string') {
exportsField[key] = rewriteExportsTarget(exportsField[key]);
} else if (typeof exportsField[key] === 'object') {
exportsField[key] = rewriteExportsField(exportsField[key]);
}
}
return exportsField;
}
function rewriteExportsTarget(target /*: string */) /*: string */ {
return target.replace('./' + SRC_DIR + '/', './' + BUILD_DIR + '/');
}
module.exports = {
buildFile,
getBuildPath,
BUILD_DIR,
PACKAGES_DIR,
SRC_DIR,
};
if (require.main === module) {
build();
}

51
scripts/build/clean.js Normal file
View File

@ -0,0 +1,51 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
* @format
* @oncall react_native
*/
const {parseArgs} = require('@pkgjs/parseargs');
const fs = require('fs');
const path = require('path');
const {BUILD_DIR, PACKAGES_DIR} = require('./build');
const {buildConfig} = require('./config');
const config = {
options: {
help: {type: 'boolean'},
},
};
function clean() {
const {
values: {help},
} = parseArgs(config);
if (help) {
console.log(`
Usage: node ./scripts/build/clean.js
Clean build directories for all packages defined in ./scripts/build/config.js.
`);
process.exitCode = 0;
return;
}
for (const packageName of Object.keys(buildConfig.packages)) {
fs.rmSync(path.join(PACKAGES_DIR, packageName, BUILD_DIR), {
force: true,
recursive: true,
});
}
process.exitCode = 0;
}
if (require.main === module) {
clean();
}

71
scripts/build/config.js Normal file
View File

@ -0,0 +1,71 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
* @format
* @oncall react_native
*/
/*::
import type {BabelCoreOptions} from '@babel/core';
export type BuildOptions = $ReadOnly<{
target: 'node',
}>;
export type BuildConfig = $ReadOnly<{
packages: $ReadOnly<{[packageName: string]: BuildOptions}>,
}>;
*/
const TARGET_NODE_VERSION = '18';
const buildConfig /*: BuildConfig */ = {
// The packages to include for build and their build options
packages: {
'dev-middleware': {target: 'node'},
},
};
function getBabelConfig(
packageName /*: $Keys<BuildConfig['packages']> */,
) /*: BabelCoreOptions */ {
const {target} = buildConfig.packages[packageName];
switch (target) {
case 'node':
return {
presets: [
'@babel/preset-flow',
[
'@babel/preset-env',
{
targets: {
node: TARGET_NODE_VERSION,
},
},
],
],
plugins: [
[
'transform-define',
{
'process.env.BUILD_EXCLUDE_BABEL_REGISTER': true,
},
],
[
'minify-dead-code-elimination',
{keepFnName: true, keepFnArgs: true, keepClassName: true},
],
],
};
}
}
module.exports = {
buildConfig,
getBabelConfig,
};

View File

@ -305,9 +305,9 @@
type-fest "3.11.1"
"@babel/cli@^7.20.0":
version "7.21.5"
resolved "https://registry.yarnpkg.com/@babel/cli/-/cli-7.21.5.tgz#a685a5b50b785f2edfbf6e042c1265c653547d9d"
integrity sha512-TOKytQ9uQW9c4np8F+P7ZfPINy5Kv+pizDIUwSVH8X5zHgYHV4AA8HE5LA450xXeu4jEfmUckTYvv1I4S26M/g==
version "7.22.6"
resolved "https://registry.yarnpkg.com/@babel/cli/-/cli-7.22.6.tgz#63f5be2a0abd587ccfbdc93424fa85f43142cc53"
integrity sha512-Be3/RfEDmkMRGT1+ru5nTkfcvWz5jDOYg1V9rXqTz2u9Qt96O1ryboGvxVBp7wOnYWDB8DNHIWb6DThrpudfOw==
dependencies:
"@jridgewell/trace-mapping" "^0.3.17"
commander "^4.0.1"
@ -3563,7 +3563,7 @@ ansi-styles@^6.1.0:
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5"
integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==
anymatch@^3.0.3, anymatch@~3.1.2:
anymatch@^3.0.3:
version "3.1.2"
resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716"
integrity sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==
@ -3571,6 +3571,14 @@ anymatch@^3.0.3, anymatch@~3.1.2:
normalize-path "^3.0.0"
picomatch "^2.0.4"
anymatch@~3.1.2:
version "3.1.3"
resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e"
integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==
dependencies:
normalize-path "^3.0.0"
picomatch "^2.0.4"
appdirsjs@^1.2.4:
version "1.2.4"
resolved "https://registry.yarnpkg.com/appdirsjs/-/appdirsjs-1.2.4.tgz#3ab582acc9fdfaaa0c1f81b3a25422ad4d95f9d4"
@ -4073,6 +4081,21 @@ babel-core@^7.0.0-bridge.0:
resolved "https://registry.yarnpkg.com/babel-core/-/babel-core-7.0.0-bridge.0.tgz#95a492ddd90f9b4e9a4a1da14eb335b87b634ece"
integrity sha512-poPX9mZH/5CSanm50Q+1toVci6pv5KSRv/5TWCwtzQS5XEwn40BcCrgIeMFWP9CKKIniKXNxoIOnOq4VVlGXhg==
babel-helper-evaluate-path@^0.5.0:
version "0.5.0"
resolved "https://registry.yarnpkg.com/babel-helper-evaluate-path/-/babel-helper-evaluate-path-0.5.0.tgz#a62fa9c4e64ff7ea5cea9353174ef023a900a67c"
integrity sha512-mUh0UhS607bGh5wUMAQfOpt2JX2ThXMtppHRdRU1kL7ZLRWIXxoV2UIV1r2cAeeNeU1M5SB5/RSUgUxrK8yOkA==
babel-helper-mark-eval-scopes@^0.4.3:
version "0.4.3"
resolved "https://registry.yarnpkg.com/babel-helper-mark-eval-scopes/-/babel-helper-mark-eval-scopes-0.4.3.tgz#d244a3bef9844872603ffb46e22ce8acdf551562"
integrity sha512-+d/mXPP33bhgHkdVOiPkmYoeXJ+rXRWi7OdhwpyseIqOS8CmzHQXHUp/+/Qr8baXsT0kjGpMHHofHs6C3cskdA==
babel-helper-remove-or-void@^0.4.3:
version "0.4.3"
resolved "https://registry.yarnpkg.com/babel-helper-remove-or-void/-/babel-helper-remove-or-void-0.4.3.tgz#a4f03b40077a0ffe88e45d07010dee241ff5ae60"
integrity sha512-eYNceYtcGKpifHDir62gHJadVXdg9fAhuZEXiRQnJJ4Yi4oUTpqpNY//1pM4nVyjjDMPYaC2xSf0I+9IqVzwdA==
babel-jest@^29.2.1:
version "29.2.1"
resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-29.2.1.tgz#213c47e28072de11bdb98c9d29b89f2ab99664f1"
@ -4107,6 +4130,16 @@ babel-plugin-jest-hoist@^29.2.0:
"@types/babel__core" "^7.1.14"
"@types/babel__traverse" "^7.0.6"
babel-plugin-minify-dead-code-elimination@^0.5.2:
version "0.5.2"
resolved "https://registry.yarnpkg.com/babel-plugin-minify-dead-code-elimination/-/babel-plugin-minify-dead-code-elimination-0.5.2.tgz#f386ceec77a80cc4e76022a04c21b7d68e0aa5eb"
integrity sha512-krq9Lwi0QIzyAlcNBXTL4usqUvevB4BzktdEsb8srcXC1AaYqRJiAQw6vdKdJSaXbz6snBvziGr6ch/aoRCfpA==
dependencies:
babel-helper-evaluate-path "^0.5.0"
babel-helper-mark-eval-scopes "^0.4.3"
babel-helper-remove-or-void "^0.4.3"
lodash "^4.17.11"
babel-plugin-polyfill-corejs2@^0.3.3:
version "0.3.3"
resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.3.tgz#5d1bd3836d0a19e1b84bbf2d9640ccb6f951c122"
@ -4141,6 +4174,14 @@ babel-plugin-syntax-trailing-function-commas@^7.0.0-beta.0:
resolved "https://registry.yarnpkg.com/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-7.0.0-beta.0.tgz#aa213c1435e2bffeb6fca842287ef534ad05d5cf"
integrity sha512-Xj9XuRuz3nTSbaTXWv3itLOcxyF4oPD8douBBmj7U9BBC6nEBYfyOJYQMf/8PJAFotC62UY5dFfIGEPr7WswzQ==
babel-plugin-transform-define@^2.1.2:
version "2.1.2"
resolved "https://registry.yarnpkg.com/babel-plugin-transform-define/-/babel-plugin-transform-define-2.1.2.tgz#d23e692cf14c68af90ca81500cd953f84678ca28"
integrity sha512-kiIiwIRiOVvSChdXg0efxsP+I7e7kLQUoZDHY+gbDIq7r4FcvXlYClWbbDzoeTGxDkiuivQvbQ4uOEaTIGHoKw==
dependencies:
lodash "^4.17.11"
traverse "0.6.6"
babel-plugin-transform-flow-enums@^0.0.2:
version "0.0.2"
resolved "https://registry.yarnpkg.com/babel-plugin-transform-flow-enums/-/babel-plugin-transform-flow-enums-0.0.2.tgz#d1d0cc9bdc799c850ca110d0ddc9f21b9ec3ef25"
@ -4849,7 +4890,12 @@ content-type@~1.0.4, content-type@~1.0.5:
resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.5.tgz#8b773162656d1d1086784c8f23a54ce6d73d7918"
integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==
convert-source-map@^1.1.0, convert-source-map@^1.4.0, convert-source-map@^1.6.0, convert-source-map@^1.7.0:
convert-source-map@^1.1.0:
version "1.9.0"
resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.9.0.tgz#7faae62353fb4213366d0ca98358d22e8368b05f"
integrity sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==
convert-source-map@^1.4.0, convert-source-map@^1.6.0, convert-source-map@^1.7.0:
version "1.8.0"
resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.8.0.tgz#f3373c32d21b4d780dd8004514684fb791ca4369"
integrity sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==
@ -10707,6 +10753,11 @@ tr46@~0.0.3:
resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a"
integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==
traverse@0.6.6:
version "0.6.6"
resolved "https://registry.yarnpkg.com/traverse/-/traverse-0.6.6.tgz#cbdf560fd7b9af632502fed40f918c157ea97137"
integrity sha512-kdf4JKs8lbARxWdp7RKdNzoJBhGUcIalSYibuGyHJbmk40pOysQ0+QPvlkCOICOivDWU2IJo2rkrxyTK2AH4fw==
triple-beam@^1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/triple-beam/-/triple-beam-1.3.0.tgz#a595214c7298db8339eeeee083e4d10bd8cb8dd9"