UNPKG

react-native-builder-bob

Version:

CLI to build JavaScript files for React Native libraries

371 lines (365 loc) 12.7 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.init = init; var _path = _interopRequireDefault(require("path")); var _fsExtra = _interopRequireDefault(require("fs-extra")); var _kleur = _interopRequireDefault(require("kleur")); var _dedent = _interopRequireDefault(require("dedent")); var _isGitDirty = _interopRequireDefault(require("is-git-dirty")); var _prompts = _interopRequireDefault(require("./utils/prompts")); var _loadConfig = require("./utils/loadConfig"); var _package = _interopRequireDefault(require("../package.json")); function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; } const FLOW_PRGAMA_REGEX = /\*?\s*@(flow)\b/m; async function init() { const root = process.cwd(); const projectPackagePath = _path.default.resolve(root, 'package.json'); if ((0, _isGitDirty.default)()) { const { shouldContinue } = await (0, _prompts.default)({ type: 'confirm', name: 'shouldContinue', message: `The working directory is not clean.\n You should commit or stash your changes before configuring bob.\n Continue anyway?`, initial: false }); if (!shouldContinue) { process.exit(0); } } if (!(await _fsExtra.default.pathExists(projectPackagePath))) { throw new Error(`Couldn't find a 'package.json' file in '${root}'.\n Are you in a project folder?`); } const pkg = JSON.parse(await _fsExtra.default.readFile(projectPackagePath, 'utf-8')); const result = (0, _loadConfig.loadConfig)(root); if (result?.config && pkg.devDependencies && _package.default.name in pkg.devDependencies) { const { shouldContinue } = await (0, _prompts.default)({ type: 'confirm', name: 'shouldContinue', message: `The project seems to be already configured with bob.\n Do you want to overwrite the existing configuration?`, initial: false }); if (!shouldContinue) { process.exit(0); } } // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion 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) { throw new Error(`Couldn't find a 'index.js'. 'index.ts' or 'index.tsx' file under '${source}'.\n Please re-run the CLI after creating it.`); } pkg.devDependencies = Object.fromEntries([...Object.entries(pkg.devDependencies || {}), [_package.default.name, `^${_package.default.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: 'module - for modern setups', value: 'module', selected: true }, { title: 'commonjs - for legacy setups (Node.js < 20)', value: 'commonjs', selected: false }, { title: 'typescript - declaration files for typechecking', value: 'typescript', selected: /\.tsx?$/.test(entryFile) }], 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') }); } // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion const { output, targets, flow } = await (0, _prompts.default)(questions); const target = targets[0] === 'commonjs' || targets[0] === 'module' ? targets[0] : undefined; const entries = { source: `./${_path.default.join(source, entryFile)}` }; let esm = false; if (targets.includes('module')) { esm = true; entries.module = `./${_path.default.join(output, 'module', 'index.js')}`; } if (targets.includes('commonjs')) { entries.commonjs = `./${_path.default.join(output, 'commonjs', 'index.js')}`; } const types = {}; if (targets.includes('typescript')) { if (targets.includes('commonjs') && targets.includes('module')) { types.require = `./${_path.default.join(output, 'typescript', 'commonjs', source, 'index.d.ts')}`; types.import = `./${_path.default.join(output, 'typescript', 'module', source, 'index.d.ts')}`; } else { types.require = `./${_path.default.join(output, 'typescript', source, 'index.d.ts')}`; types.import = types.require; } 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.\n Generate one?`, initial: true }); if (tsconfig) { await _fsExtra.default.writeJSON(_path.default.join(root, 'tsconfig.json'), { compilerOptions: { rootDir: '.', allowUnreachableCode: false, allowUnusedLabels: false, esModuleInterop: true, forceConsistentCasingInFileNames: true, jsx: 'react-jsx', lib: ['ESNext'], module: 'ESNext', moduleResolution: 'bundler', noFallthroughCasesInSwitch: true, noImplicitReturns: true, noImplicitUseStrict: false, noStrictGenericChecks: false, noUncheckedIndexedAccess: true, noUnusedLocals: true, noUnusedParameters: true, resolveJsonModule: true, skipLibCheck: true, strict: true, target: 'ESNext', verbatimModuleSyntax: true } }, { spaces: 2 }); } } } const prepare = 'bob build'; const files = [source, output, '!**/__tests__', '!**/__fixtures__', '!**/__mocks__']; if (esm) { let replace = false; const exportsField = { '.': {}, './package.json': './package.json' }; const importField = { ...(types.import ? { types: types.import } : null), ...(entries.module ? { default: entries.module } : null) }; const requireField = { ...(types.require ? { types: types.require } : null), ...(entries.commonjs ? { default: entries.commonjs } : null) }; if (targets.includes('commonjs') && targets.includes('module')) { exportsField['.'] = { source: entries.source, import: importField, require: requireField }; } else if (targets.includes('commonjs')) { exportsField['.'] = { source: entries.source, ...requireField }; } else if (targets.includes('module')) { exportsField['.'] = { source: entries.source, ...importField }; } if (pkg.exports && JSON.stringify(pkg.exports) !== JSON.stringify(exportsField)) { replace = (await (0, _prompts.default)({ type: 'confirm', name: 'replace', message: `Your package.json has 'exports' field set.\n Do you want to replace it?`, initial: true })).replace; } else { replace = true; } if (replace) { pkg.exports = exportsField; } } const entryFields = {}; if (targets.includes('commonjs') && targets.includes('module')) { entryFields.main = entries.commonjs; entryFields.module = entries.module; if (targets.includes('typescript')) { entryFields.types = types.require; } } else if (targets.includes('commonjs')) { entryFields.main = entries.commonjs; if (targets.includes('typescript')) { entryFields.types = types.require; } } else if (targets.includes('module')) { entryFields.main = entries.module; if (targets.includes('typescript')) { entryFields.types = types.import; } } for (const key in entryFields) { // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion const entry = entryFields[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 '${String(pkg[key])}'.\n Do you want to replace it with '${String(entry)}'?`, initial: true }); if (replace) { pkg[key] = entry; } } else { pkg[key] = entry; } } if (pkg['react-native'] && (pkg['react-native'].startsWith(source) || pkg['react-native'].startsWith(`./${source}`))) { const { remove } = await (0, _prompts.default)({ type: 'confirm', name: 'remove', message: `Your package.json has the 'react-native' field pointing to source code.\n This can cause problems when customizing babel configuration.\n Do you want to remove it?`, initial: true }); if (remove) { delete pkg['react-native']; } } if (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 '${String(pkg.scripts.prepare)}'.\n 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) { const pkgFiles = pkg.files; if (files?.some(file => !pkgFiles.includes(file))) { const { update } = await (0, _prompts.default)({ type: 'confirm', name: 'update', message: `Your package.json already has a 'files' field.\n Do you want to update it?`, initial: true }); if (update) { pkg.files = [...files, ...pkg.files.filter(file => !files.includes(file))]; } } } else { pkg.files = files; } pkg[_package.default.name] = { source, output, targets: targets.map(t => { if (t === target && flow) { return [t, { copyFlow: true }]; } if (t === 'commonjs' || t === 'module') { return [t, { esm }]; } 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.writeJSON(projectPackagePath, pkg, { spaces: 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`); } } } const packageManager = (await _fsExtra.default.pathExists(_path.default.join(root, 'yarn.lock'))) ? 'yarn' : 'npm'; process.stdout.write((0, _dedent.default)(` Project ${_kleur.default.yellow(pkg.name)} configured successfully! ${_kleur.default.magenta(`${_kleur.default.bold('Perform last steps')} by running`)}${_kleur.default.gray(':')} ${_kleur.default.gray('$')} ${packageManager} install ${_kleur.default.yellow('Good luck!')} `)); } //# sourceMappingURL=init.js.map