react-native-builder-bob
Version:
CLI to build JavaScript files for React Native libraries
218 lines (214 loc) • 10.5 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = compile;
var _path = _interopRequireDefault(require("path"));
var _fsExtra = _interopRequireDefault(require("fs-extra"));
var _kleur = _interopRequireDefault(require("kleur"));
var babel = _interopRequireWildcard(require("@babel/core"));
var _glob = require("glob");
var _isCodegenSpec = require("./isCodegenSpec");
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
const sourceExt = /\.([cm])?[jt]sx?$/;
async function compile({
root,
source,
output,
esm = false,
babelrc = false,
configFile = false,
exclude,
modules,
copyFlow,
sourceMaps = true,
report,
jsxRuntime = 'automatic',
variants
}) {
const files = (0, _glob.globSync)('**/*', {
cwd: source,
absolute: true,
nodir: true,
ignore: exclude
});
report.info(`Compiling ${_kleur.default.blue(String(files.length))} files in ${_kleur.default.blue(_path.default.relative(root, source))} with ${_kleur.default.blue('babel')}`);
const pkg = JSON.parse(await _fsExtra.default.readFile(_path.default.join(root, 'package.json'), 'utf-8'));
if (copyFlow) {
if (!Object.keys(pkg.devDependencies || {}).includes('flow-bin')) {
report.warn(`The ${_kleur.default.blue('copyFlow')} option was specified, but couldn't find ${_kleur.default.blue('flow-bin')} in ${_kleur.default.blue('package.json')}.\nIf the project is using ${_kleur.default.blue('flow')}, then make sure you have added ${_kleur.default.blue('flow-bin')} to your ${_kleur.default.blue('devDependencies')}, otherwise remove the ${_kleur.default.blue('copyFlow')} option.`);
}
}
await _fsExtra.default.mkdirp(output);
// Imports are not rewritten to include the extension if `esm` is not enabled
// Ideally we should always treat ESM syntax as CommonJS if `esm` is not enabled
// This would maintain compatibility for legacy setups where `import`/`export` didn't require file extensions
// However NextJS has non-standard behavior and breaks if we add `type: 'commonjs'` for code with import/export
// So we skip generating `package.json` if `esm` is not enabled and `modules` is not `commonjs`
// This means that user can't use `type: 'module'` in root `package.json` without enabling `esm` for `module` target
if (esm || modules === 'commonjs') {
await _fsExtra.default.writeJSON(_path.default.join(output, 'package.json'), {
type: modules === 'commonjs' ? 'commonjs' : 'module'
});
}
await Promise.all(files.map(async filepath => {
const outputFilename = _path.default.join(output, _path.default.relative(source, filepath)).replace(sourceExt, '.$1js');
await _fsExtra.default.mkdirp(_path.default.dirname(outputFilename));
if (!sourceExt.test(filepath)) {
// Copy files which aren't source code
await _fsExtra.default.copy(filepath, outputFilename);
return;
}
const content = await _fsExtra.default.readFile(filepath, 'utf-8');
// If codegen is used in the app, then we need to preserve TypeScript source
// So we copy the file as is instead of transforming it
const codegenEnabled = 'codegenConfig' in pkg;
if (codegenEnabled && (0, _isCodegenSpec.isCodegenSpec)(filepath)) {
await _fsExtra.default.copy(filepath, _path.default.join(output, _path.default.relative(source, filepath)));
return;
}
const result = await babel.transformAsync(content, {
caller: {
name: 'react-native-builder-bob',
supportsStaticESM: /\.m[jt]s$/.test(filepath) ||
// If a file is explicitly marked as ESM, then preserve the syntax
modules === 'preserve' ? true : false,
rewriteImportExtensions: esm,
jsxRuntime,
codegenEnabled
},
cwd: root,
babelrc: babelrc,
configFile: configFile,
sourceMaps,
sourceRoot: _path.default.relative(_path.default.dirname(outputFilename), source),
sourceFileName: _path.default.relative(source, filepath),
filename: filepath,
...(babelrc || configFile ? null : {
presets: [require.resolve('../../babel-preset')]
})
});
if (result == null) {
throw new Error('Output code was null');
}
let code = result.code || '';
if (sourceMaps && result.map) {
const mapFilename = outputFilename + '.map';
code += '\n//# sourceMappingURL=' + _path.default.basename(mapFilename);
// Don't inline the source code, it can be retrieved from the source file
result.map.sourcesContent = undefined;
await _fsExtra.default.writeJSON(mapFilename, result.map);
}
await _fsExtra.default.writeFile(outputFilename, code);
if (copyFlow) {
await _fsExtra.default.copy(filepath, outputFilename + '.flow');
}
}));
report.success(`Wrote files to ${_kleur.default.blue(_path.default.relative(root, output))}`);
const getGeneratedEntryPath = async () => {
if (pkg.source) {
for (const ext of ['.js', '.cjs', '.mjs']) {
const indexName =
// The source field may not have an extension, so we add it instead of replacing directly
_path.default.basename(pkg.source).replace(sourceExt, '') + ext;
const potentialPath = _path.default.join(output, _path.default.dirname(_path.default.relative(source, _path.default.join(root, pkg.source))), indexName);
if (await _fsExtra.default.pathExists(potentialPath)) {
return _path.default.relative(root, potentialPath);
}
}
}
return null;
};
const fields = [];
if (variants.commonjs && variants.module) {
if (modules === 'commonjs') {
fields.push({
name: 'main',
value: pkg.main
});
} else {
fields.push({
name: 'module',
value: pkg.module
});
}
} else {
fields.push({
name: 'main',
value: pkg.main
});
}
if (esm) {
if (variants.commonjs && variants.module) {
if (modules === 'commonjs') {
fields.push(typeof pkg.exports?.['.']?.require === 'string' ? {
name: "exports['.'].require",
value: pkg.exports?.['.']?.require
} : {
name: "exports['.'].require.default",
value: pkg.exports?.['.']?.require?.default
});
} else {
fields.push(typeof pkg.exports?.['.']?.import === 'string' ? {
name: "exports['.'].import",
value: pkg.exports?.['.']?.import
} : {
name: "exports['.'].import.default",
value: pkg.exports?.['.']?.import?.default
});
}
} else {
fields.push({
name: "exports['.'].default",
value: pkg.exports?.['.']?.default
});
}
} else {
if (modules === 'commonjs' && pkg.exports?.['.']?.require) {
report.warn(`The ${_kleur.default.blue('esm')} option is disabled, but the ${_kleur.default.blue("exports['.'].require")} field is set in ${_kleur.default.blue('package.json')}. This is likely a mistake.`);
} else if (modules === 'preserve' && pkg.exports?.['.']?.import) {
report.warn(`The ${_kleur.default.blue('esm')} option is disabled, but the ${_kleur.default.blue("exports['.'].import")} field is set in ${_kleur.default.blue('package.json')}. This is likely a mistake.`);
}
}
const generatedEntryPath = await getGeneratedEntryPath();
if (fields.some(field => field.value)) {
for (const {
name,
value
} of fields) {
if (!value) {
continue;
}
if (name.startsWith('exports') && value && !/^\.\//.test(value)) {
report.error(`The ${_kleur.default.blue(name)} field in ${_kleur.default.blue(`package.json`)} should be a relative path starting with ${_kleur.default.blue('./')}. Found: ${_kleur.default.blue(value)}`);
throw new Error(`Found incorrect path in '${name}' field.`);
}
try {
require.resolve(_path.default.join(root, value));
} catch (e) {
if (e != null && typeof e === 'object' && 'code' in e && e.code === 'MODULE_NOT_FOUND') {
if (!generatedEntryPath) {
report.warn(`Failed to detect the entry point for the generated files. Make sure you have a valid ${_kleur.default.blue('source')} field in your ${_kleur.default.blue('package.json')}.`);
}
report.error(`The ${_kleur.default.blue(name)} field in ${_kleur.default.blue('package.json')} points to a non-existent file: ${_kleur.default.blue(value)}.\nVerify the path points to the correct file under ${_kleur.default.blue(_path.default.relative(root, output))}${generatedEntryPath ? ` (found ${_kleur.default.blue(generatedEntryPath)}).` : '.'}`);
throw new Error(`Found incorrect path in '${name}' field.`, {
cause: e
});
}
throw e;
}
}
if (generatedEntryPath) {
if (modules === 'commonjs' && pkg.exports?.['.']?.import === `./${generatedEntryPath}`) {
report.warn(`The the ${_kleur.default.blue("exports['.'].import")} field points to a CommonJS module. This is likely a mistake.`);
} else if (modules === 'preserve' && pkg.exports?.['.']?.require === `./${generatedEntryPath}`) {
report.warn(`The the ${_kleur.default.blue("exports['.'].import")} field points to a ES module. This is likely a mistake.`);
}
}
} else {
report.warn(`No ${fields.map(field => _kleur.default.blue(field.name)).join(' or ')} field found in ${_kleur.default.blue('package.json')}. Consider ${generatedEntryPath ? `pointing to ${_kleur.default.blue(generatedEntryPath)}` : `adding ${fields.length > 1 ? 'them' : 'it'}`} so that consumers of your package can import your package.`);
}
}
//# sourceMappingURL=compile.js.map