Initial commit 🎉
fbshipit-source-id: b6fc29740c6875d2e78953b8a7123890a67930f2 Co-authored-by: Sebastian McKenzie <sebmck@fb.com> Co-authored-by: John Knox <jknox@fb.com> Co-authored-by: Emil Sjölander <emilsj@fb.com> Co-authored-by: Pritesh Nandgaonkar <prit91@fb.com>
This commit is contained in:
205
scripts/build-release.js
Executable file
205
scripts/build-release.js
Executable file
@@ -0,0 +1,205 @@
|
||||
/**
|
||||
* Copyright 2018-present Facebook.
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
* @format
|
||||
*/
|
||||
const path = require('path');
|
||||
const tmp = require('tmp');
|
||||
const fs = require('fs-extra');
|
||||
const builder = require('electron-builder');
|
||||
const Platform = builder.Platform;
|
||||
const metro = require('../static/node_modules/metro');
|
||||
const compilePlugins = require('../static/compilePlugins');
|
||||
|
||||
function generateManifest(versionNumber) {
|
||||
const filePath = path.join(__dirname, '..', 'dist');
|
||||
if (!fs.existsSync(filePath)) {
|
||||
fs.mkdirSync(filePath);
|
||||
}
|
||||
fs.writeFileSync(
|
||||
path.join(__dirname, '../dist/manifest.json'),
|
||||
JSON.stringify({
|
||||
package: 'com.facebook.sonar',
|
||||
version_name: versionNumber,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
function buildFolder() {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('Creating build directory');
|
||||
return new Promise((resolve, reject) => {
|
||||
tmp.dir((err, buildFolder) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else {
|
||||
resolve(buildFolder);
|
||||
}
|
||||
});
|
||||
}).catch(die);
|
||||
}
|
||||
|
||||
function modifyPackageManifest(buildFolder) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('Creating package.json manifest');
|
||||
const manifest = require('../package.json');
|
||||
const manifestStatic = require('../static/package.json');
|
||||
|
||||
// The manifest's dependencies are bundled with the final app by
|
||||
// electron-builder. We want to bundle the dependencies from the static-folder
|
||||
// because all dependencies from the root-folder are already bundled by metro.
|
||||
manifest.dependencies = manifestStatic.dependencies;
|
||||
manifest.main = 'index.js';
|
||||
|
||||
const BUILD_NUMBER_ARG = 'build-number=';
|
||||
const buildNumber = (
|
||||
process.argv.find(arg => arg.startsWith(BUILD_NUMBER_ARG)) || ''
|
||||
).replace(BUILD_NUMBER_ARG, '');
|
||||
if (buildNumber) {
|
||||
manifest.version = [
|
||||
...manifest.version.split('.').slice(0, 2),
|
||||
buildNumber,
|
||||
].join('.');
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
fs.writeFile(
|
||||
path.join(buildFolder, 'package.json'),
|
||||
JSON.stringify(manifest, null, ' '),
|
||||
err => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else {
|
||||
resolve(manifest.version);
|
||||
}
|
||||
},
|
||||
);
|
||||
}).catch(die);
|
||||
}
|
||||
|
||||
function buildDist(buildFolder) {
|
||||
const targetsRaw = [];
|
||||
targetsRaw.push(Platform.MAC.createTarget(['zip']));
|
||||
if (process.argv.slice(2).indexOf('macOnly') === -1) {
|
||||
targetsRaw.push(Platform.LINUX.createTarget(['dir']));
|
||||
targetsRaw.push(Platform.WINDOWS.createTarget(['dir']));
|
||||
}
|
||||
|
||||
if (!targetsRaw.length) {
|
||||
throw new Error('No targets specified. eg. --osx pkg,dmg --linux tar.gz');
|
||||
}
|
||||
|
||||
// merge all target maps into a single map
|
||||
let targetsMerged = [];
|
||||
for (const target of targetsRaw) {
|
||||
targetsMerged = targetsMerged.concat(Array.from(target));
|
||||
}
|
||||
const targets = new Map(targetsMerged);
|
||||
|
||||
const electronDownload = {};
|
||||
if (process.env.electron_config_cache) {
|
||||
electronDownload.cache = process.env.electron_config_cache;
|
||||
}
|
||||
|
||||
return builder
|
||||
.build({
|
||||
appDir: buildFolder,
|
||||
config: {
|
||||
appId: `com.facebook.sonar`,
|
||||
directories: {
|
||||
buildResources: path.join(__dirname, '..', 'static'),
|
||||
output: path.join(__dirname, '..', 'dist'),
|
||||
},
|
||||
electronDownload,
|
||||
npmRebuild: false,
|
||||
asarUnpack: 'PortForwardingMacApp.app/**/*',
|
||||
},
|
||||
projectDir: buildFolder,
|
||||
targets,
|
||||
})
|
||||
.catch(die);
|
||||
}
|
||||
|
||||
function die(err) {
|
||||
console.error(err.stack);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
function compile(buildFolder) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(
|
||||
'Building main bundle',
|
||||
path.join(__dirname, '..', 'src', 'init.js'),
|
||||
);
|
||||
return metro
|
||||
.runBuild({
|
||||
config: {
|
||||
getProjectRoots: () => [path.join(__dirname, '..')],
|
||||
getTransformModulePath: () =>
|
||||
path.join(__dirname, '..', 'static', 'transforms', 'index.js'),
|
||||
},
|
||||
resetCache: true,
|
||||
dev: false,
|
||||
entry: path.join(__dirname, '..', 'src', 'init.js'),
|
||||
out: path.join(buildFolder, 'bundle.js'),
|
||||
})
|
||||
.catch(die);
|
||||
}
|
||||
|
||||
function copyStaticFolder(buildFolder) {
|
||||
return new Promise((resolve, reject) => {
|
||||
fs.copy(
|
||||
path.join(__dirname, '..', 'static'),
|
||||
buildFolder,
|
||||
{
|
||||
dereference: true,
|
||||
},
|
||||
err => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
},
|
||||
);
|
||||
}).catch(die);
|
||||
}
|
||||
|
||||
function compileDefaultPlugins(buildFolder) {
|
||||
const defaultPluginFolder = 'defaultPlugins';
|
||||
const defaultPluginDir = path.join(buildFolder, defaultPluginFolder);
|
||||
return compilePlugins(
|
||||
null,
|
||||
[
|
||||
path.join(__dirname, '..', 'src', 'plugins'),
|
||||
path.join(__dirname, '..', 'src', 'fb', 'plugins'),
|
||||
],
|
||||
defaultPluginDir,
|
||||
).then(defaultPlugins =>
|
||||
fs.writeFileSync(
|
||||
path.join(defaultPluginDir, 'index.json'),
|
||||
JSON.stringify(
|
||||
defaultPlugins.map(plugin => ({
|
||||
...plugin,
|
||||
out: path.join(defaultPluginFolder, path.parse(plugin.out).base),
|
||||
})),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
(async () => {
|
||||
const dir = await buildFolder();
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('Created build directory', dir);
|
||||
await copyStaticFolder(dir);
|
||||
await compileDefaultPlugins(dir);
|
||||
await compile(dir);
|
||||
const versionNumber = await modifyPackageManifest(dir);
|
||||
generateManifest(versionNumber);
|
||||
await buildDist(dir);
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('✨ Done');
|
||||
process.exit();
|
||||
})();
|
||||
30
scripts/eslint.sh
Executable file
30
scripts/eslint.sh
Executable file
@@ -0,0 +1,30 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
# This script is used by `arc lint`.
|
||||
|
||||
THIS_DIR=$(cd -P "$(dirname "${BASH_SOURCE[0]}")" && pwd)
|
||||
ROOT_DIR=$(cd "$THIS_DIR" && hg root)
|
||||
|
||||
cd "$ROOT_DIR/xplat/sonar"
|
||||
|
||||
# Sonar's Electron dependency downloads itself via a post-install script.
|
||||
# When running in Sandcastle or devservers, the module install will fail
|
||||
# because we can't reach the internet. Setting the fwdproxy is dangerous, so
|
||||
# the next best thing is to install the modules with `--ignore-scripts`.
|
||||
# However, we can't run `install-node-modules.sh` like this all of the time.
|
||||
# `install-node-modules.sh` uses its args as keys for the "yarn watchman check"
|
||||
# cache. So if we run `install-node-modules.sh` outside of this script without
|
||||
# the flag, but then this script runs it with the flag, we're going to
|
||||
# invalidate the cache.
|
||||
|
||||
# If `node_modules` exists, we can't tell if it was created with
|
||||
# `--ignore-scripts` or not, so we play it safe, and avoid touching it.
|
||||
if [[ ! -d "node_modules" ]]; then
|
||||
"$ROOT_DIR/xplat/third-party/yarn/install-node-modules.sh" --ignore-scripts
|
||||
fi
|
||||
|
||||
exec \
|
||||
"$ROOT_DIR/xplat/third-party/node/bin/node" \
|
||||
"$ROOT_DIR/xplat/sonar/node_modules/.bin/eslint" \
|
||||
"$@"
|
||||
31
scripts/flow.sh
Executable file
31
scripts/flow.sh
Executable file
@@ -0,0 +1,31 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
# This script is used by `arc lint`.
|
||||
|
||||
THIS_DIR=$(cd -P "$(dirname "${BASH_SOURCE[0]}")" && pwd)
|
||||
ROOT_DIR=$(cd "$THIS_DIR" && hg root)
|
||||
|
||||
cd "$ROOT_DIR/xplat/sonar"
|
||||
|
||||
# Sonar's Electron dependency downloads itself via a post-install script.
|
||||
# When running in Sandcastle or devservers, the module install will fail
|
||||
# because we can't reach the internet. Setting the fwdproxy is dangerous, so
|
||||
# the next best thing is to install the modules with `--ignore-scripts`.
|
||||
# However, we can't run `install-node-modules.sh` like this all of the time.
|
||||
# `install-node-modules.sh` uses its args as keys for the "yarn watchman check"
|
||||
# cache. So if we run `install-node-modules.sh` outside of this script without
|
||||
# the flag, but then this script runs it with the flag, we're going to
|
||||
# invalidate the cache.
|
||||
|
||||
# If `node_modules` exists, we can't tell if it was created with
|
||||
# `--ignore-scripts` or not, so we play it safe, and avoid touching it.
|
||||
if [[ ! -d "node_modules" ]]; then
|
||||
"$ROOT_DIR/xplat/third-party/yarn/install-node-modules.sh" --ignore-scripts
|
||||
fi
|
||||
|
||||
# Prefer the internal version of Flow, which should be in the PATH - but
|
||||
# fallback to the OSS version (this is needed in Sandcastle).
|
||||
FLOW_BINARY="$(which flow 2>/dev/null || echo "$ROOT_DIR/xplat/sonar/node_modules/.bin/flow")"
|
||||
|
||||
exec "$FLOW_BINARY" "$@"
|
||||
29
scripts/install-dependencies.sh
Executable file
29
scripts/install-dependencies.sh
Executable file
@@ -0,0 +1,29 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
main () {
|
||||
local -r THIS_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
|
||||
ROOT_DIR=$(cd "$THIS_DIR" && hg root)
|
||||
source "$ROOT_DIR/xplat/sonar/scripts/setup-env.sh"
|
||||
|
||||
# save current cursor location
|
||||
printf "Ensuring correct dependencies..."
|
||||
|
||||
PREV_DIR="`pwd`"
|
||||
|
||||
# install dependencies
|
||||
cd "$INFINITY_DIR"
|
||||
"$INSTALL_NODE_MODULES"
|
||||
|
||||
# ensure electron gets installed
|
||||
node node_modules/electron/install.js
|
||||
|
||||
# go back
|
||||
cd "$PREV_DIR"
|
||||
|
||||
# remove correct dependencies log
|
||||
printf "\r"
|
||||
}
|
||||
|
||||
main
|
||||
63
scripts/metro-transform.js
Normal file
63
scripts/metro-transform.js
Normal file
@@ -0,0 +1,63 @@
|
||||
/**
|
||||
* Copyright 2018-present Facebook.
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
* @format
|
||||
*/
|
||||
|
||||
const generate = require('babel-generator').default;
|
||||
const babylon = require('babylon');
|
||||
const babel = require('babel-core');
|
||||
const metro = require('metro');
|
||||
|
||||
exports.transform = function({
|
||||
filename,
|
||||
options,
|
||||
src,
|
||||
plugins: defaultPlugins,
|
||||
}) {
|
||||
const presets = [];
|
||||
|
||||
let ast = babylon.parse(src, {
|
||||
filename,
|
||||
plugins: ['jsx', 'flow', 'classProperties', 'objectRestSpread'],
|
||||
sourceType: filename.includes('node_modules') ? 'script' : 'module',
|
||||
});
|
||||
|
||||
// run babel
|
||||
const plugins = [
|
||||
...defaultPlugins,
|
||||
require('./babel-plugins/electron-requires.js'),
|
||||
require('./babel-plugins/dynamic-requires.js'),
|
||||
];
|
||||
if (!filename.includes('node_modules')) {
|
||||
plugins.unshift(require('babel-plugin-transform-es2015-modules-commonjs'));
|
||||
}
|
||||
ast = babel.transformFromAst(ast, src, {
|
||||
babelrc: !filename.includes('node_modules'),
|
||||
code: false,
|
||||
comments: false,
|
||||
compact: false,
|
||||
filename,
|
||||
plugins,
|
||||
presets,
|
||||
sourceMaps: true,
|
||||
}).ast;
|
||||
|
||||
const result = generate(
|
||||
ast,
|
||||
{
|
||||
filename,
|
||||
sourceFileName: filename,
|
||||
sourceMaps: true,
|
||||
},
|
||||
src,
|
||||
);
|
||||
|
||||
return {
|
||||
ast,
|
||||
code: result.code,
|
||||
filename,
|
||||
map: result.rawMappings.map(metro.sourceMaps.compactMapping),
|
||||
};
|
||||
};
|
||||
20
scripts/public-build.json
Normal file
20
scripts/public-build.json
Normal file
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"command": "SandcastleUniversalCommand",
|
||||
"args": {
|
||||
"name": "Release public Sonar build",
|
||||
"oncall": "danielbuechele",
|
||||
"steps": [
|
||||
{
|
||||
"name": "sonar_release_public_build",
|
||||
"required": true,
|
||||
"shell": "cd ../xplat/sonar/scripts && ./public-build.sh"
|
||||
}
|
||||
]
|
||||
},
|
||||
"alias": "sonar_release_public_build",
|
||||
"capabilities": {
|
||||
"vcs": "fbcode-fbsource",
|
||||
"type": "lego"
|
||||
},
|
||||
"hash": "master"
|
||||
}
|
||||
46
scripts/public-build.sh
Executable file
46
scripts/public-build.sh
Executable file
@@ -0,0 +1,46 @@
|
||||
#!/bin/bash
|
||||
TOKEN=$(secrets_tool get SONAR_GITHUB_TOKEN)
|
||||
GITHUB_ORG="facebook"
|
||||
GITHUB_REPO="Sonar"
|
||||
|
||||
cd ../../ || exit
|
||||
|
||||
function jsonValue() {
|
||||
python -c 'import json,sys;obj=json.load(sys.stdin);print obj["'$1'"]' || echo ''
|
||||
}
|
||||
|
||||
git -c http.proxy=fwdproxy:8080 -c https.proxy=fwdproxy:8080 clone https://github.com/facebook/Sonar.git sonar-public
|
||||
cp sonar/scripts/sandcastle-build.sh sonar-public/scripts/sandcastle-build.sh
|
||||
cd sonar-public/scripts && ./sandcastle-build.sh "$(git rev-list HEAD --count || echo 0)"
|
||||
|
||||
VERSION=$(plutil -p ./sonar-public/dist/mac/Sonar.app/Contents/Info.plist | awk '/CFBundleShortVersionString/ {print substr($3, 2, length($3)-2)}')
|
||||
|
||||
RELEASE_JSON=$(curl $(fwdproxy-config curl) --silent --data '{
|
||||
"tag_name": "v'$VERSION'",
|
||||
"target_commitish": "master",
|
||||
"name": "v'$VERSION'",
|
||||
"body": "",
|
||||
"draft": false,
|
||||
"prerelease": false
|
||||
}' https://api.github.com/repos/$GITHUB_ORG/$GITHUB_REPO/releases?access_token=$TOKEN)
|
||||
|
||||
RELEASE_ID=$(echo $RELEASE_JSON | jsonValue id)
|
||||
|
||||
if [ -z "${RELEASE_ID}" ]; then
|
||||
echo $RELEASE_JSON
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Created GitHub release ID: $RELEASE_ID"
|
||||
UPLOAD_URL=$(echo $RELEASE_JSON | jsonValue upload_url| sed -e 's#{?name,label}##')
|
||||
ASSET_JSON=$(curl $(fwdproxy-config curl) --silent $UPLOAD_URL'?access_token='$TOKEN'&name=Sonar.zip' --header 'Content-Type: application/zip' --upload-file ./sonar-public/dist/Sonar.zip -X POST)
|
||||
|
||||
DOWNLOAD_URL=$(echo $ASSET_JSON | jsonValue browser_download_url)
|
||||
|
||||
if [ -z "${DOWNLOAD_URL}" ]; then
|
||||
echo $ASSET_JSON
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Released Sonar v$VERSION"
|
||||
echo "Download: $DOWNLOAD_URL"
|
||||
15
scripts/setup-env.sh
Executable file
15
scripts/setup-env.sh
Executable file
@@ -0,0 +1,15 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
main () {
|
||||
local -r THIS_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
|
||||
ROOT_DIR=$(cd "$THIS_DIR" && hg root)
|
||||
|
||||
source "$ROOT_DIR/xplat/js/env-utils/setup_env_vars.sh"
|
||||
|
||||
export SONAR_DIR="$ROOT_DIR/xplat/infinity"
|
||||
export PATH="$SONAR_DIR/node_modules/.bin:$ROOT_DIR/xplat/third-party/node/bin:$ROOT_DIR/xplat/third-party/yarn:$PATH"
|
||||
}
|
||||
|
||||
main
|
||||
176
scripts/start-dev-server.js
Normal file
176
scripts/start-dev-server.js
Normal file
@@ -0,0 +1,176 @@
|
||||
/**
|
||||
* Copyright 2018-present Facebook.
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
* @format
|
||||
*/
|
||||
const electronBinary = require('electron');
|
||||
const codeFrame = require('babel-code-frame');
|
||||
const socketIo = require('socket.io');
|
||||
const express = require('express');
|
||||
const detect = require('detect-port');
|
||||
const child = require('child_process');
|
||||
const Convert = require('ansi-to-html');
|
||||
const chalk = require('chalk');
|
||||
const http = require('http');
|
||||
const path = require('path');
|
||||
const metro = require('../static/node_modules/metro');
|
||||
const fs = require('fs');
|
||||
|
||||
const convertAnsi = new Convert();
|
||||
|
||||
const DEFAULT_PORT = process.env.PORT || 3000;
|
||||
const STATIC_DIR = path.join(__dirname, '..', 'static');
|
||||
|
||||
function launchElectron({bundleURL, electronURL}) {
|
||||
const args = [
|
||||
path.join(STATIC_DIR, 'index.js'),
|
||||
'--remote-debugging-port=9222',
|
||||
'--dynamicPlugins=~/fbsource/xplat/sonar/src/plugins,~/fbsource/xplat/sonar/src/fb/plugins',
|
||||
];
|
||||
|
||||
const proc = child.spawn(electronBinary, args, {
|
||||
cwd: STATIC_DIR,
|
||||
env: {
|
||||
...process.env,
|
||||
SONAR_ROOT: process.cwd(),
|
||||
BUNDLE_URL: bundleURL,
|
||||
ELECTRON_URL: electronURL,
|
||||
},
|
||||
stdio: 'inherit',
|
||||
});
|
||||
|
||||
proc.on('close', () => {
|
||||
process.exit();
|
||||
});
|
||||
|
||||
process.on('exit', () => {
|
||||
proc.kill();
|
||||
});
|
||||
}
|
||||
|
||||
function startMetroServer(port) {
|
||||
return metro.runServer({
|
||||
port,
|
||||
watch: true,
|
||||
config: {
|
||||
getProjectRoots: () => [path.join(__dirname, '..')],
|
||||
getTransformModulePath: () =>
|
||||
path.join(__dirname, '..', 'static', 'transforms', 'index.js'),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function startAssetServer(port) {
|
||||
const app = express();
|
||||
|
||||
app.use((req, res, next) => {
|
||||
if (knownErrors[req.url] != null) {
|
||||
delete knownErrors[req.url];
|
||||
outputScreen();
|
||||
}
|
||||
next();
|
||||
});
|
||||
|
||||
app.get('/', (req, res) => {
|
||||
fs.readFile(path.join(STATIC_DIR, 'index.dev.html'), (err, content) => {
|
||||
res.end(content);
|
||||
});
|
||||
});
|
||||
|
||||
app.use(express.static(STATIC_DIR));
|
||||
|
||||
app.use(function(err, req, res, next) {
|
||||
knownErrors[req.url] = err;
|
||||
outputScreen();
|
||||
res.status(500).send('Something broke, check the console!');
|
||||
});
|
||||
|
||||
const server = http.createServer(app);
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
server.listen(port, () => resolve(server));
|
||||
});
|
||||
}
|
||||
|
||||
function addWebsocket(server) {
|
||||
const io = socketIo(server);
|
||||
|
||||
// notify connected clients that there's errors in the console
|
||||
io.on('connection', client => {
|
||||
if (hasErrors()) {
|
||||
client.emit('hasErrors', convertAnsi.toHtml(buildErrorScreen()));
|
||||
}
|
||||
});
|
||||
|
||||
// refresh the app on changes to the src folder
|
||||
// this can be removed once metroServer notifies us about file changes
|
||||
fs.watch(path.join(__dirname, '..', 'src'), () => {
|
||||
io.emit('refresh');
|
||||
});
|
||||
|
||||
return io;
|
||||
}
|
||||
|
||||
const knownErrors = {};
|
||||
|
||||
function hasErrors() {
|
||||
return Object.keys(knownErrors).length > 0;
|
||||
}
|
||||
|
||||
function buildErrorScreen() {
|
||||
const lines = [
|
||||
chalk.red(`✖ Found ${Object.keys(knownErrors).length} errors`),
|
||||
'',
|
||||
];
|
||||
|
||||
for (const url in knownErrors) {
|
||||
const err = knownErrors[url];
|
||||
|
||||
if (err.filename != null && err.lineNumber != null && err.column != null) {
|
||||
lines.push(chalk.inverse(err.filename));
|
||||
lines.push();
|
||||
lines.push(err.message);
|
||||
lines.push(
|
||||
codeFrame(
|
||||
fs.readFileSync(err.filename, 'utf8'),
|
||||
err.lineNumber,
|
||||
err.column,
|
||||
),
|
||||
);
|
||||
} else {
|
||||
lines.push(err.stack);
|
||||
}
|
||||
|
||||
lines.push('');
|
||||
}
|
||||
|
||||
return lines.join('\n');
|
||||
}
|
||||
|
||||
function outputScreen(socket) {
|
||||
// output screen
|
||||
if (hasErrors()) {
|
||||
const errorScreen = buildErrorScreen();
|
||||
console.error(errorScreen);
|
||||
|
||||
// notify live clients of errors
|
||||
socket.emit('hasErrors', convertAnsi.toHtml(errorScreen));
|
||||
} else {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(chalk.green('✔ No known errors'));
|
||||
}
|
||||
}
|
||||
|
||||
(async () => {
|
||||
const assetServerPort = await detect(DEFAULT_PORT);
|
||||
const assetServer = await startAssetServer(assetServerPort);
|
||||
const socket = addWebsocket(assetServer);
|
||||
const metroServerPort = await detect(DEFAULT_PORT + 1);
|
||||
await startMetroServer(metroServerPort);
|
||||
outputScreen(socket);
|
||||
launchElectron({
|
||||
bundleURL: `http://localhost:${metroServerPort}/src/init.bundle`,
|
||||
electronURL: `http://localhost:${assetServerPort}/index.dev.html`,
|
||||
});
|
||||
})();
|
||||
48
scripts/yarn-install.js
Normal file
48
scripts/yarn-install.js
Normal file
@@ -0,0 +1,48 @@
|
||||
/**
|
||||
* Copyright 2018-present Facebook.
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
* @format
|
||||
*/
|
||||
|
||||
const glob = require('glob');
|
||||
const path = require('path');
|
||||
const {spawn} = require('child_process');
|
||||
const PACKAGES = ['static', 'src/plugins/*', 'src/fb/plugins/*'];
|
||||
const YARN_PATH =
|
||||
process.argv.length > 2 ? path.join(__dirname, process.argv[2]) : 'yarn';
|
||||
|
||||
Promise.all(
|
||||
PACKAGES.map(
|
||||
pattern =>
|
||||
new Promise((resolve, reject) => {
|
||||
glob(
|
||||
path.join(__dirname, '..', pattern, 'package.json'),
|
||||
(err, matches) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else {
|
||||
resolve(matches);
|
||||
}
|
||||
},
|
||||
);
|
||||
}),
|
||||
),
|
||||
)
|
||||
.then(packages =>
|
||||
Promise.all(
|
||||
packages.reduce((acc, cv) => acc.concat(cv), []).map(
|
||||
pkg =>
|
||||
new Promise(resolve => {
|
||||
const cwd = pkg.replace('/package.json', '');
|
||||
const yarn = spawn(YARN_PATH, ['--mutex', 'file'], {
|
||||
cwd,
|
||||
});
|
||||
yarn.stderr.on('data', e => console.error(e.toString()));
|
||||
yarn.on('close', code => resolve(code));
|
||||
}),
|
||||
),
|
||||
),
|
||||
)
|
||||
// eslint-disable-next-line
|
||||
.then(() => console.log('📦 Installed all dependencies!'));
|
||||
Reference in New Issue
Block a user