@react-native-community/bob
Version:
CLI to build JavaScript files for React Native libraries
395 lines (324 loc) • 12.9 kB
JavaScript
var _path = _interopRequireDefault(require("path"));
var _fsExtra = _interopRequireDefault(require("fs-extra"));
var _chalk = _interopRequireDefault(require("chalk"));
var _dedent = _interopRequireDefault(require("dedent"));
var _yargs = _interopRequireDefault(require("yargs"));
var _cosmiconfig = require("cosmiconfig");
var _isGitDirty = _interopRequireDefault(require("is-git-dirty"));
var _create = _interopRequireWildcard(require("./create"));
var _prompts = _interopRequireDefault(require("./utils/prompts"));
var logger = _interopRequireWildcard(require("./utils/logger"));
var _aar = _interopRequireDefault(require("./targets/aar"));
var _commonjs = _interopRequireDefault(require("./targets/commonjs"));
var _module = _interopRequireDefault(require("./targets/module"));
var _typescript = _interopRequireDefault(require("./targets/typescript"));
function _getRequireWildcardCache() { if (typeof WeakMap !== "function") return null; var cache = new WeakMap(); _getRequireWildcardCache = function () { return cache; }; return cache; }
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
// eslint-disable-next-line import/no-commonjs
const {
name,
version
} = require('../package.json');
const root = process.cwd();
const explorer = (0, _cosmiconfig.cosmiconfigSync)(name, {
searchPlaces: ['package.json', `bob.config.js`]
});
const FLOW_PRGAMA_REGEX = /\*?\s*@(flow)\b/m; // eslint-disable-next-line babel/no-unused-expressions
_yargs.default.command('create <name>', 'create a react native library', _create.args, _create.default).command('init', 'configure the package to use bob', {}, async () => {
var _pkg$scripts;
const pak = _path.default.join(root, 'package.json');
if ((0, _isGitDirty.default)()) {
const {
shouldContinue
} = await (0, _prompts.default)({
type: 'confirm',
name: 'shouldContinue',
message: `The working directory is not clean. You should commit or stash your changes before configuring bob. Continue anyway?`,
initial: false
});
if (!shouldContinue) {
process.exit(1);
}
}
if (!(await _fsExtra.default.pathExists(pak))) {
logger.exit(`Couldn't find a 'package.json' file in '${root}'. Are you in a project folder?`);
}
const {
source
} = await (0, _prompts.default)({
type: 'text',
name: 'source',
message: 'Where are your source files?',
initial: 'src',
validate: input => Boolean(input)
});
let entryFile;
if (await _fsExtra.default.pathExists(_path.default.join(root, source, 'index.js'))) {
entryFile = 'index.js';
} else if (await _fsExtra.default.pathExists(_path.default.join(root, source, 'index.ts'))) {
entryFile = 'index.ts';
} else if (await _fsExtra.default.pathExists(_path.default.join(root, source, 'index.tsx'))) {
entryFile = 'index.tsx';
}
if (!entryFile) {
logger.exit(`Couldn't find a 'index.js'. 'index.ts' or 'index.tsx' file under '${source}'. Please re-run the CLI after creating it.`);
return;
}
const pkg = JSON.parse(await _fsExtra.default.readFile(pak, 'utf-8'));
pkg.devDependencies = Object.fromEntries([...Object.entries(pkg.devDependencies || {}), [name, `^${version}`]].sort(([a], [b]) => a.localeCompare(b)));
const questions = [{
type: 'text',
name: 'output',
message: 'Where do you want to generate the output files?',
initial: 'lib',
validate: input => Boolean(input)
}, {
type: 'multiselect',
name: 'targets',
message: 'Which targets do you want to build?',
choices: [{
title: 'commonjs - for running in Node (tests, SSR etc.)',
value: 'commonjs',
selected: true
}, {
title: 'module - for bundlers (metro, webpack etc.)',
value: 'module',
selected: true
}, {
title: 'typescript - declaration files for typechecking',
value: 'typescript',
selected: /\.tsx?$/.test(entryFile)
}, {
title: 'aar - bundle android code to a binary',
value: 'aar',
selected: false
}],
validate: input => Boolean(input.length)
}];
if (entryFile.endsWith('.js') && FLOW_PRGAMA_REGEX.test(await _fsExtra.default.readFile(_path.default.join(root, source, entryFile), 'utf-8'))) {
questions.push({
type: 'confirm',
name: 'flow',
message: 'Do you want to publish definitions for flow?',
initial: Object.keys(pkg.devDependencies || {}).includes('flow-bin')
});
}
const {
output,
targets,
flow
} = await (0, _prompts.default)(questions);
const target = targets[0] === 'commonjs' || targets[0] === 'module' ? targets[0] : undefined;
const entries = {
'main': target ? _path.default.join(output, target, 'index.js') : _path.default.join(source, entryFile),
'react-native': _path.default.join(source, entryFile)
};
if (targets.includes('module')) {
entries.module = _path.default.join(output, 'module', 'index.js');
}
if (targets.includes('typescript')) {
entries.types = _path.default.join(output, 'typescript', source, 'index.d.ts');
if (!(await _fsExtra.default.pathExists(_path.default.join(root, 'tsconfig.json')))) {
const {
tsconfig
} = await (0, _prompts.default)({
type: 'confirm',
name: 'tsconfig',
message: `You have enabled 'typescript' compilation, but we couldn't find a 'tsconfig.json' in project root. Generate one?`,
initial: true
});
if (tsconfig) {
await _fsExtra.default.writeFile(_path.default.join(root, 'tsconfig.json'), JSON.stringify({
compilerOptions: {
allowUnreachableCode: false,
allowUnusedLabels: false,
esModuleInterop: true,
forceConsistentCasingInFileNames: true,
jsx: 'react',
lib: ['esnext'],
module: 'esnext',
moduleResolution: 'node',
noFallthroughCasesInSwitch: true,
noImplicitReturns: true,
noImplicitUseStrict: false,
noStrictGenericChecks: false,
noUnusedLocals: true,
noUnusedParameters: true,
resolveJsonModule: true,
skipLibCheck: true,
strict: true,
target: 'esnext'
}
}, null, 2));
}
}
}
const prepare = 'bob build';
const files = [source, output, '!**/__tests__', '!**/__fixtures__', '!**/__mocks__'];
for (const key in entries) {
const entry = entries[key];
if (pkg[key] && pkg[key] !== entry) {
const {
replace
} = await (0, _prompts.default)({
type: 'confirm',
name: 'replace',
message: `Your package.json has the '${key}' field set to '${pkg[key]}'. Do you want to replace it with '${entry}'?`,
initial: true
});
if (replace) {
pkg[key] = entry;
}
} else {
pkg[key] = entry;
}
}
if ((_pkg$scripts = pkg.scripts) !== null && _pkg$scripts !== void 0 && _pkg$scripts.prepare && pkg.scripts.prepare !== prepare) {
const {
replace
} = await (0, _prompts.default)({
type: 'confirm',
name: 'replace',
message: `Your package.json has the 'scripts.prepare' field set to '${pkg.scripts.prepare}'. Do you want to replace it with '${prepare}'?`,
initial: true
});
if (replace) {
pkg.scripts.prepare = prepare;
}
} else {
pkg.scripts = pkg.scripts || {};
pkg.scripts.prepare = prepare;
}
if (pkg.files && JSON.stringify(pkg.files.slice().sort()) !== JSON.stringify(files.slice().sort())) {
const {
update
} = await (0, _prompts.default)({
type: 'confirm',
name: 'update',
message: `Your package.json already has a 'files' field. Do you want to update it?`,
initial: true
});
if (update) {
pkg.files = [...files, ...pkg.files.filter(file => !files.includes(file.replace(/\/$/g, '')))];
}
} else {
pkg.files = files;
}
pkg[name] = {
source,
output,
targets: targets.map(t => {
if (t === target && flow) {
return [t, {
flow
}];
}
return t;
})
};
if (pkg.jest) {
const entry = `<rootDir>/${output}/`;
if (pkg.jest.modulePathIgnorePatterns) {
const {
modulePathIgnorePatterns
} = pkg.jest;
if (!modulePathIgnorePatterns.includes(entry)) {
modulePathIgnorePatterns.push(entry);
}
} else {
pkg.jest.modulePathIgnorePatterns = [entry];
}
}
pkg.eslintIgnore = pkg.eslintIgnore || ['node_modules/'];
if (!pkg.eslintIgnore.includes(`${output}/`)) {
pkg.eslintIgnore.push(`${output}/`);
}
await _fsExtra.default.writeFile(pak, JSON.stringify(pkg, null, 2));
const ignorefiles = [_path.default.join(root, '.gitignore'), _path.default.join(root, '.eslintignore')];
for (const ignorefile of ignorefiles) {
if (await _fsExtra.default.pathExists(ignorefile)) {
const content = await _fsExtra.default.readFile(ignorefile, 'utf-8');
if (!content.split('\n').includes(`${output}/`)) {
await _fsExtra.default.writeFile(ignorefile, `${content}\n# generated by bob\n${output}/\n`);
}
}
}
console.log((0, _dedent.default)((0, _chalk.default)`
Project {yellow ${pkg.name}} configured successfully!
{magenta {bold Perform last steps} by running}{gray :}
{gray $} yarn
{yellow Good luck!}
`));
}).command('build', 'build files for publishing', {}, async argv => {
var _options$targets;
const result = explorer.search();
if (!(result !== null && result !== void 0 && result.config)) {
logger.exit(`No configuration found. Run '${argv.$0} init' to create one automatically.`);
}
const options = result.config;
if (!((_options$targets = options.targets) !== null && _options$targets !== void 0 && _options$targets.length)) {
logger.exit(`No targets found in the configuration in '${_path.default.relative(root, result.filepath)}'.`);
}
const source = options.source;
if (!source) {
logger.exit(`No source option found in the configuration in '${_path.default.relative(root, result.filepath)}'.`);
}
const output = options.output;
if (!output) {
logger.exit(`No source option found in the configuration in '${_path.default.relative(root, result.filepath)}'.`);
}
const report = {
info: logger.info,
warn: logger.warn,
error: logger.error,
success: logger.success
};
for (const target of options.targets) {
const targetName = Array.isArray(target) ? target[0] : target;
const targetOptions = Array.isArray(target) ? target[1] : undefined;
report.info(`Building target ${_chalk.default.blue(targetName)}`);
switch (targetName) {
case 'aar':
await (0, _aar.default)({
root,
source: _path.default.resolve(root, source),
output: _path.default.resolve(root, output, 'aar'),
options: targetOptions,
report
});
break;
case 'commonjs':
await (0, _commonjs.default)({
root,
source: _path.default.resolve(root, source),
output: _path.default.resolve(root, output, 'commonjs'),
options: targetOptions,
report
});
break;
case 'module':
await (0, _module.default)({
root,
source: _path.default.resolve(root, source),
output: _path.default.resolve(root, output, 'module'),
options: targetOptions,
report
});
break;
case 'typescript':
await (0, _typescript.default)({
root,
source: _path.default.resolve(root, source),
output: _path.default.resolve(root, output, 'typescript'),
options: targetOptions,
report
});
break;
default:
logger.exit(`Invalid target ${_chalk.default.blue(targetName)}.`);
}
}
}).demandCommand().recommendCommands().strict().argv;
//# sourceMappingURL=cli.js.map
;