Skip to content

Commit

Permalink
chore: setup monorepo with Lerna (react-native-community#109)
Browse files Browse the repository at this point in the history
* chore: setup monorepo with lerna

* chore: move flow to root

* fix: lint

* chore: add watch command
  • Loading branch information
thymikee authored and Esemesek committed Jan 23, 2019
1 parent 861b0c3 commit 61bc288
Show file tree
Hide file tree
Showing 11 changed files with 2,625 additions and 321 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ node_modules
package-lock.json
npm-debug.log
build/
.eslintcache
11 changes: 11 additions & 0 deletions babel.config.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,14 @@
module.exports = {
babelrcRoots: ['packages/*'],
presets: [
[
require.resolve('@babel/preset-env'),
{
targets: { node: 8 },
useBuiltIns: 'entry',
},
],
require.resolve('@babel/preset-flow'),
],
plugins: [require.resolve('@babel/plugin-transform-strict-mode')],
};
6 changes: 6 additions & 0 deletions lerna.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"lerna": "3.10.6",
"version": "independent",
"npmClient": "yarn",
"useWorkspaces": true
}
32 changes: 27 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,35 @@
"packages/*"
],
"scripts": {
"build": "node ./scripts/build.js",
"build-clean": "rm -rf ./packages/*/build",
"watch": "node ./scripts/watch.js",
"test": "jest",
"lint": "eslint packages",
"flow-check": "flow check"
"lint": "eslint . --cache --report-unused-disable-directives",
"flow-check": "flow check",
"postinstall": "yarn build",
"publish": "yarn build-clean && yarn build && lerna publish"
},
"devDependencies": {
"@babel/core": "^7.2.2",
"dependencies": {
"@babel/core": "^7.0.0",
"@babel/plugin-transform-strict-mode": "^7.0.0",
"@babel/preset-env": "^7.0.0",
"@babel/preset-flow": "^7.0.0",
"@callstack/eslint-config": "^3.0.2",
"@commitlint/cli": "^7.2.1",
"@commitlint/config-conventional": "^7.1.2",
"chalk": "^2.4.2",
"eslint": "^5.10.0",
"eslint-plugin-prettier": "^3.0.1",
"flow-bin": "^0.87.0",
"glob": "^7.1.3",
"husky": "^1.3.1",
"jest": "^24.0.0-alpha.12"
"jest": "^24.0.0-alpha.12",
"lerna": "^3.10.6",
"micromatch": "^3.1.10",
"mkdirp": "^0.5.1",
"prettier": "^1.16.0",
"string-length": "^2.0.0"
},
"eslintConfig": {
"extends": "@callstack",
Expand All @@ -24,6 +41,11 @@
"no-console": 0
}
},
"prettier": {
"proseWrap": "never",
"singleQuote": true,
"trailingComma": "es5"
},
"husky": {
"hooks": {
"commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
Expand Down
15 changes: 0 additions & 15 deletions packages/cli/.babelrc.js

This file was deleted.

10 changes: 0 additions & 10 deletions packages/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,6 @@
"type": "git",
"url": "https://github.com/react-native-community/react-native-cli.git"
},
"scripts": {
"prepublish": "yarn build",
"build": "rm -rf build; babel src --copy-files --out-dir build --ignore '**/__tests__/**','**/__mocks__/**','**/__fixtures__/**'"
},
"jest": {
"displayName": "cli",
"testPathIgnorePatterns": [
Expand Down Expand Up @@ -66,12 +62,6 @@
"xmldoc": "^0.4.0"
},
"devDependencies": {
"@babel/cli": "^7.2.3",
"@babel/core": "^7.1.5",
"@babel/plugin-transform-strict-mode": "^7.2.0",
"@babel/preset-env": "^7.1.5",
"@babel/preset-flow": "^7.0.0",
"flow-bin": "0.87.0",
"react-native": "^0.57.0"
},
"peerDependencies": {
Expand Down
5 changes: 1 addition & 4 deletions packages/cli/src/util/loadMetroConfig.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
/**
* @flow
*/
/* eslint-disable no-param-reassign */
/**
* Configuration file of Metro.
* @flow
*/
import type { ConfigT } from 'metro-config/src/configTypes.flow';

Expand Down
132 changes: 132 additions & 0 deletions scripts/build.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

/**
* script to build (transpile) files.
* By default it transpiles js files for all packages and writes them
* into `build/` directory.
* Non-js files not matching IGNORE_PATTERN will be copied without transpiling.
*
* Example:
* node ./scripts/build.js
* node ./scripts/build.js /users/123/jest/packages/jest-111/src/111.js
*
* NOTE: this script is node@4 compatible
*/

const fs = require('fs');
const path = require('path');
const glob = require('glob');
const mkdirp = require('mkdirp');
const babel = require('@babel/core');
const chalk = require('chalk');
const micromatch = require('micromatch');
const prettier = require('prettier');
const stringLength = require('string-length');
const { PACKAGES_DIR, getPackages } = require('./helpers');

const OK = chalk.reset.inverse.bold.green(' DONE ');
const SRC_DIR = 'src';
const BUILD_DIR = 'build';
const JS_FILES_PATTERN = '**/*.js';
const IGNORE_PATTERN = '**/__{tests,mocks,fixtures}__/**';

const transformOptions = require('../babel.config.js');

const prettierConfig = prettier.resolveConfig.sync(__filename);
prettierConfig.trailingComma = 'none';
prettierConfig.parser = 'babel';

const adjustToTerminalWidth = str => {
const columns = process.stdout.columns || 80;
const WIDTH = columns - stringLength(OK) + 1;
const strs = str.match(new RegExp(`(.{1,${WIDTH}})`, 'g'));
let lastString = strs[strs.length - 1];
if (lastString.length < WIDTH) {
lastString += Array(WIDTH - lastString.length).join(chalk.dim('.'));
}
return strs
.slice(0, -1)
.concat(lastString)
.join('\n');
};

function getPackageName(file) {
return path.relative(PACKAGES_DIR, file).split(path.sep)[0];
}

function getBuildPath(file, buildFolder) {
const pkgName = getPackageName(file);
const pkgSrcPath = path.resolve(PACKAGES_DIR, pkgName, SRC_DIR);
const pkgBuildPath = path.resolve(PACKAGES_DIR, pkgName, buildFolder);
const relativeToSrcPath = path.relative(pkgSrcPath, file);
return path.resolve(pkgBuildPath, relativeToSrcPath);
}

function buildNodePackage(p) {
const srcDir = path.resolve(p, SRC_DIR);
const pattern = path.resolve(srcDir, '**/*');
const files = glob.sync(pattern, {
nodir: true,
});

process.stdout.write(adjustToTerminalWidth(`${path.basename(p)}\n`));

files.forEach(file => buildFile(file, true));
process.stdout.write(`${OK}\n`);
}

function buildFile(file, silent) {
const destPath = getBuildPath(file, BUILD_DIR);

if (micromatch.isMatch(file, IGNORE_PATTERN)) {
silent ||
process.stdout.write(
`${chalk.dim(' \u2022 ') +
path.relative(PACKAGES_DIR, file)} (ignore)\n`
);
return;
}

mkdirp.sync(path.dirname(destPath), '777');

if (!micromatch.isMatch(file, JS_FILES_PATTERN)) {
fs.createReadStream(file).pipe(fs.createWriteStream(destPath));
silent ||
process.stdout.write(
`${chalk.red(' \u2022 ') +
path.relative(PACKAGES_DIR, file) +
chalk.red(' \u21D2 ') +
path.relative(PACKAGES_DIR, destPath)} (copy)\n`
);
} else {
const options = Object.assign({}, transformOptions);
const transformed = babel.transformFileSync(file, options).code;
const prettyCode = prettier.format(transformed, prettierConfig);

fs.writeFileSync(destPath, prettyCode);

silent ||
process.stdout.write(
`${chalk.green(' \u2022 ') +
path.relative(PACKAGES_DIR, file) +
chalk.green(' \u21D2 ') +
path.relative(PACKAGES_DIR, destPath)}\n`
);
}
}

const files = process.argv.slice(2);

if (files.length) {
files.forEach(buildFile);
} else {
const packages = getPackages();
process.stdout.write(chalk.inverse(' Building packages \n'));
packages.forEach(buildNodePackage);
process.stdout.write('\n');
}
16 changes: 16 additions & 0 deletions scripts/helpers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
const fs = require('fs');
const path = require('path');

const PACKAGES_DIR = path.resolve(__dirname, '../packages');

function getPackages() {
return fs
.readdirSync(PACKAGES_DIR)
.map(file => path.resolve(PACKAGES_DIR, file))
.filter(f => fs.lstatSync(path.resolve(f)).isDirectory());
}

module.exports = {
getPackages,
PACKAGES_DIR,
};
75 changes: 75 additions & 0 deletions scripts/watch.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

/**
* Watch files for changes and rebuild (copy from 'src/' to `build/`) if changed
*/

const fs = require('fs');
const { execSync } = require('child_process');
const path = require('path');
const chalk = require('chalk');
const { getPackages } = require('./helpers');

const BUILD_CMD = `node ${path.resolve(__dirname, './build.js')}`;

let filesToBuild = new Map();

const exists = filename => {
try {
return fs.statSync(filename).isFile();
} catch (e) {
// omit
}
return false;
};
const rebuild = filename => filesToBuild.set(filename, true);

getPackages().forEach(p => {
const srcDir = path.resolve(p, 'src');
try {
fs.accessSync(srcDir, fs.F_OK);
fs.watch(path.resolve(p, 'src'), { recursive: true }, (event, filename) => {
const filePath = path.resolve(srcDir, filename);

if ((event === 'change' || event === 'rename') && exists(filePath)) {
console.log(chalk.green('->'), `${event}: ${filename}`);
rebuild(filePath);
} else {
const buildFile = path.resolve(srcDir, '..', 'build', filename);
try {
fs.unlinkSync(buildFile);
process.stdout.write(
`${chalk.red(' \u2022 ') +
path.relative(
path.resolve(srcDir, '..', '..'),
buildFile
)} (deleted)\n`
);
} catch (e) {
// omit
}
}
});
} catch (e) {
// doesn't exist
}
});

setInterval(() => {
const files = Array.from(filesToBuild.keys());
if (files.length) {
filesToBuild = new Map();
try {
execSync(`${BUILD_CMD} ${files.join(' ')}`, { stdio: [0, 1, 2] });
} catch (e) {
// omit
}
}
}, 100);

console.log(chalk.red('->'), chalk.cyan('Watching for changes...'));
Loading

0 comments on commit 61bc288

Please sign in to comment.