react-native-builder-bob
Version:
CLI to build JavaScript files for React Native libraries
215 lines (214 loc) • 10.7 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = build;
var _del = _interopRequireDefault(require("del"));
var _fsExtra = _interopRequireDefault(require("fs-extra"));
var _json = _interopRequireDefault(require("json5"));
var _kleur = _interopRequireDefault(require("kleur"));
var _os = require("os");
var _path = _interopRequireDefault(require("path"));
var _which = _interopRequireDefault(require("which"));
var _spawn = require("../utils/spawn");
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
async function build({
source,
root,
output,
report,
options,
variants,
esm
}) {
report.info(`Cleaning up previous build at ${_kleur.default.blue(_path.default.relative(root, output))}`);
await (0, _del.default)([output]);
report.info(`Generating type definitions with ${_kleur.default.blue('tsc')}`);
const project = options?.project ? options.project : 'tsconfig.json';
const tsconfig = _path.default.join(root, project);
try {
if (await _fsExtra.default.pathExists(tsconfig)) {
try {
const config = _json.default.parse(await _fsExtra.default.readFile(tsconfig, 'utf-8'));
if (config.compilerOptions) {
const conflicts = [];
if (config.compilerOptions.declarationDir) {
conflicts.push('compilerOptions.declarationDir');
}
if (config.compilerOptions.outDir && _path.default.join(root, config.compilerOptions.outDir) !== output) {
conflicts.push('compilerOptions.outDir');
}
if (conflicts.length) {
report.warn(`Found following options in the config file which can conflict with the CLI options. Please remove them from ${_kleur.default.blue(project)}:${conflicts.reduce((acc, curr) => acc + `\n${_kleur.default.gray('-')} ${_kleur.default.yellow(curr)}`, '')}`);
}
}
} catch (e) {
report.warn(`Couldn't parse ${_kleur.default.blue(project)}. There might be validation errors.`);
}
} else {
throw new Error(`Couldn't find a ${_kleur.default.blue(project)} in the project root.`);
}
let tsc;
if (options?.tsc) {
tsc = _path.default.resolve(root, options.tsc);
if (!(await _fsExtra.default.pathExists(tsc))) {
throw new Error(`The ${_kleur.default.blue('tsc')} binary doesn't seem to be installed at ${_kleur.default.blue(tsc)}. Please specify the correct path in options or remove it to use the workspace's version.`);
}
} else {
const execpath = process.env.npm_execpath;
const cli = execpath?.split(_path.default.sep).pop()?.includes('yarn') ? 'yarn' : 'npm';
if (cli === 'yarn') {
const result = await (0, _spawn.spawn)('yarn', ['bin', 'tsc'], {
cwd: root
});
tsc = result.trim();
} else {
tsc = _path.default.resolve(root, 'node_modules', '.bin', 'tsc');
}
if ((0, _os.platform)() === 'win32' && !tsc.endsWith('.cmd')) {
tsc += '.cmd';
}
}
if (!(await _fsExtra.default.pathExists(tsc))) {
try {
tsc = await (0, _which.default)('tsc');
if (await _fsExtra.default.pathExists(tsc)) {
report.warn(`Failed to locate ${_kleur.default.blue('tsc')} in the workspace. Falling back to the binary found in ${_kleur.default.blue('PATH')} at ${_kleur.default.blue(tsc)}. Consider adding ${_kleur.default.blue('typescript')} to your ${_kleur.default.blue('devDependencies')} or specifying the ${_kleur.default.blue('tsc')} option for the typescript target.`);
}
} catch (e) {
// Ignore
}
}
if (tsc == null || !(await _fsExtra.default.pathExists(tsc))) {
throw new Error(`The ${_kleur.default.blue('tsc')} binary doesn't seem to be installed under ${_kleur.default.blue('node_modules')} or present in $PATH. Make sure you have added ${_kleur.default.blue('typescript')} to your ${_kleur.default.blue('devDependencies')} or specify the ${_kleur.default.blue('tsc')} option for typescript.`);
}
const outputs = {};
if (esm && variants.commonjs && variants.module) {
outputs.commonjs = _path.default.join(output, 'commonjs');
outputs.module = _path.default.join(output, 'module');
} else if (variants.commonjs) {
outputs.commonjs = output;
} else {
outputs.module = output;
}
const outDir = outputs.commonjs ?? outputs.module;
if (outDir == null) {
throw new Error('Neither commonjs nor module output is enabled.');
}
const tsbuildinfo = _path.default.join(outDir, project.replace(/\.json$/, '.tsbuildinfo'));
try {
await (0, _del.default)([tsbuildinfo]);
} catch (e) {
// Ignore
}
await (0, _spawn.spawn)(tsc, ['--pretty', '--declaration', '--declarationMap', '--noEmit', 'false', '--emitDeclarationOnly', '--project', project, '--outDir', outDir], {
cwd: root
});
try {
await (0, _del.default)([tsbuildinfo]);
} catch (e) {
// Ignore
}
if (esm) {
if (outputs?.commonjs && outputs?.module) {
// When ESM compatible output is enabled and commonjs build is present, we need to generate 2 builds for commonjs and esm
// In this case we copy the already generated types, and add `package.json` with `type` field
await _fsExtra.default.copy(outputs.commonjs, outputs.module);
await _fsExtra.default.writeJSON(_path.default.join(outputs.commonjs, 'package.json'), {
type: 'commonjs'
});
await _fsExtra.default.writeJSON(_path.default.join(outputs.module, 'package.json'), {
type: 'module'
});
} else if (outputs?.commonjs) {
await _fsExtra.default.writeJSON(_path.default.join(outputs.commonjs, 'package.json'), {
type: 'commonjs'
});
} else if (outputs?.module) {
await _fsExtra.default.writeJSON(_path.default.join(outputs.module, 'package.json'), {
type: 'module'
});
}
}
report.success(`Wrote definition files to ${_kleur.default.blue(_path.default.relative(root, output))}`);
const pkg = JSON.parse(await _fsExtra.default.readFile(_path.default.join(root, 'package.json'), 'utf-8'));
const fields = [{
name: 'types',
value: pkg.types,
output: outputs.commonjs,
error: false,
message: undefined
}, ...(pkg.exports?.['.']?.types ? [{
name: "exports['.'].types",
value: pkg.exports?.['.']?.types,
output: outDir,
error: Boolean(pkg.exports?.['.']?.import && pkg.exports?.['.']?.require),
message: `using ${_kleur.default.blue("exports['.'].import")} and ${_kleur.default.blue("exports['.'].require")}. Specify ${_kleur.default.blue("exports['.'].import.types")} and ${_kleur.default.blue("exports['.'].require.types")} instead.`
}] : []), {
name: "exports['.'].import.types",
value: pkg.exports?.['.']?.import?.types,
output: outputs.module,
error: !esm,
message: `the ${_kleur.default.blue('esm')} option is not enabled for the ${_kleur.default.blue('module')} target`
}, {
name: "exports['.'].require.types",
value: pkg.exports?.['.']?.require?.types,
output: outputs.commonjs,
error: false,
message: undefined
}];
const getGeneratedTypesPath = async field => {
if (!field.output || field.error) {
return null;
}
if (pkg.source) {
const indexDTsName = _path.default.basename(pkg.source).replace(/\.(jsx?|tsx?)$/, '') + '.d.ts';
const potentialPaths = [_path.default.join(field.output, _path.default.dirname(pkg.source), indexDTsName), _path.default.join(field.output, _path.default.relative(source, _path.default.join(root, _path.default.dirname(pkg.source))), indexDTsName)];
for (const potentialPath of potentialPaths) {
if (await _fsExtra.default.pathExists(potentialPath)) {
return _path.default.relative(root, potentialPath);
}
}
}
return null;
};
const invalidFieldNames = (await Promise.all(fields.map(async field => {
if (field.error) {
if (field.value) {
report.warn(`The ${_kleur.default.blue(field.name)} field in ${_kleur.default.blue(`package.json`)} should not be set when ${String(field.message)}.`);
}
return null;
}
if (field.name.startsWith('exports') && field.value && !/^\.\//.test(field.value)) {
report.error(`The ${_kleur.default.blue(field.name)} field in ${_kleur.default.blue(`package.json`)} should be a relative path starting with ${_kleur.default.blue('./')}. Found: ${_kleur.default.blue(field.value)}`);
return field.name;
}
if (field.value && !(await _fsExtra.default.pathExists(_path.default.join(root, field.value)))) {
const generatedTypesPath = await getGeneratedTypesPath(field);
report.error(`The ${_kleur.default.blue(field.name)} field in ${_kleur.default.blue('package.json')} points to a non-existent file: ${_kleur.default.blue(field.value)}.\nVerify the path points to the correct file under ${_kleur.default.blue(_path.default.relative(root, output))}${generatedTypesPath ? ` (found ${_kleur.default.blue(generatedTypesPath)}).` : '.'}`);
return field.name;
}
return null;
}))).filter(name => name != null);
if (invalidFieldNames.length) {
throw new Error(`Found errors for fields: ${invalidFieldNames.join(', ')}.`);
}
const validFields = fields.filter(field => !field.error);
if (validFields.every(field => field.value == null)) {
const suggestedTypesPaths = (await Promise.all(validFields.map(async field => getGeneratedTypesPath(field)))).filter(path => path != null).filter((path, i, self) => self.indexOf(path) === i);
report.warn(`No ${validFields.map(field => _kleur.default.blue(field.name)).join(' or ')} field found in ${_kleur.default.blue('package.json')}. Consider ${suggestedTypesPaths.length ? `pointing to ${suggestedTypesPaths.map(path => _kleur.default.blue(path)).join(' or ')}` : `adding ${validFields.length > 1 ? 'them' : 'it'}`} so that consumers of your package can use the typescript definitions.`);
}
} catch (e) {
if (e != null && typeof e === 'object') {
if ('stdout' in e && e.stdout != null) {
report.error(`Errors found when building definition files:\n${e.stdout.toString()}`);
} else if ('message' in e && typeof e.message === 'string') {
report.error(e.message);
}
}
throw new Error('Failed to build definition files.', {
cause: e
});
}
}
//# sourceMappingURL=typescript.js.map