sb-babel-cli
Version:
A smarter babel-cli
172 lines (171 loc) • 7.34 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.loadConfigFromRoot = exports.validateConfig = exports.getBabelCore = exports.logError = exports.getCacheDB = exports.getSha1 = exports.posixifyPath = exports.CLIError = void 0;
const fs_1 = __importDefault(require("fs"));
const lowdb_1 = __importDefault(require("lowdb"));
const path_1 = __importDefault(require("path"));
const crypto_1 = __importDefault(require("crypto"));
const make_dir_1 = __importDefault(require("make-dir"));
const debounce_1 = __importDefault(require("lodash/debounce"));
const resolve_from_1 = __importDefault(require("resolve-from"));
const FileAsync_1 = __importDefault(require("lowdb/adapters/FileAsync"));
class CLIError extends Error {
}
exports.CLIError = CLIError;
function posixifyPath(filePath) {
return filePath.split(path_1.default.sep).join(path_1.default.posix.sep);
}
exports.posixifyPath = posixifyPath;
function getSha1(contents) {
const hash = crypto_1.default.createHash('sha1');
hash.update(contents);
return hash.digest('hex');
}
exports.getSha1 = getSha1;
async function getCacheDB(projectPath, loadState, cacheDirectory) {
const configPath = path_1.default.join(cacheDirectory, '.sb-babel-cli', `cache-timestamps-${getSha1(projectPath)}`);
await make_dir_1.default(path_1.default.dirname(configPath));
const adapter = new FileAsync_1.default(configPath, {
serialize: JSON.stringify,
});
adapter.write = debounce_1.default(adapter.write, 1000);
const db = await lowdb_1.default(adapter);
if (!loadState) {
db.setState({});
}
return db;
}
exports.getCacheDB = getCacheDB;
function logError(error) {
if (error instanceof CLIError) {
console.error('ERROR', error.message);
}
else {
console.error(error);
}
}
exports.logError = logError;
let babelCore;
function getBabelCore(projectPath) {
if (babelCore == null) {
let resolved = null;
try {
resolved = resolve_from_1.default(projectPath, '@babel/core');
}
catch (_) {
/* No Op */
}
if (!resolved) {
throw new CLIError('Unable to find @babel/core in your project');
}
// eslint-disable-next-line global-require,import/no-dynamic-require,@typescript-eslint/no-var-requires
babelCore = require(resolved);
}
return babelCore;
}
exports.getBabelCore = getBabelCore;
const EXCLUDED_CONFIG_KEYS = ['rootDirectory', 'sourceDirectory', 'loadConfig', 'specifiedArgs'];
function validateConfig(config, sourceConfig) {
const issues = [];
if (typeof config !== 'object' || config == null || Array.isArray(config)) {
issues.push('Config root is malformed');
}
else {
const validKeys = Object.keys(sourceConfig);
EXCLUDED_CONFIG_KEYS.forEach((item) => {
validKeys.splice(validKeys.indexOf(item), 1);
});
const receivedKeys = Object.keys(config);
const extraKeys = receivedKeys.filter((item) => !validKeys.includes(item));
if (extraKeys.length > 0) {
issues.push(`Unknown config items: ${extraKeys.join(', ')}`);
}
if (config.watch != null && typeof config.watch !== 'boolean') {
issues.push('config.watch must be a valid boolean');
}
if (config.ignored != null &&
(!Array.isArray(config.ignored) || !config.ignored.every((item) => typeof item === 'string'))) {
issues.push('config.ignored must be a valid array of strings');
}
if (config.ignoredForRestart != null &&
(!Array.isArray(config.ignoredForRestart) || !config.ignoredForRestart.every((item) => typeof item === 'string'))) {
issues.push('config.ignoredForRestart must be a valid array of strings');
}
if (config.sourceMaps != null && typeof config.sourceMaps !== 'boolean' && config.sourceMaps !== 'inline') {
issues.push('config.sourceMaps must either be a boolean or "inline"');
}
if (config.resetCache != null && typeof config.resetCache !== 'boolean') {
issues.push('config.resetCache must be a valid boolean');
}
if (config.keepExtraFiles != null && typeof config.keepExtraFiles !== 'boolean') {
issues.push('config.keepExtraFiles must be a valid boolean');
}
if (config.execute != null && typeof config.execute !== 'string') {
issues.push('config.execute must be a valid string');
}
if (config.executeDelay != null && typeof config.executeDelay !== 'number') {
issues.push('config.executeDelay must be a valid number');
}
if (config.extensions != null &&
(!Array.isArray(config.extensions) || !config.extensions.every((item) => typeof item === 'string'))) {
issues.push('config.extensions must be a valid array of strings');
}
if (config.printConfig != null && typeof config.printConfig !== 'boolean') {
issues.push('config.printConfig must be a valid boolean');
}
if (config.nodeArgs != null &&
(!Array.isArray(config.nodeArgs) || !config.nodeArgs.every((item) => typeof item === 'string'))) {
issues.push('config.nodeArgs must be a valid array of strings');
}
if (config.programArgs != null &&
(!Array.isArray(config.programArgs) || !config.programArgs.every((item) => typeof item === 'string'))) {
issues.push('config.programArgs must be a valid array of strings');
}
}
return issues;
}
exports.validateConfig = validateConfig;
async function loadConfigFromRoot(rootDirectory, config) {
const manifestPath = path_1.default.join(rootDirectory, 'package.json');
try {
await fs_1.default.promises.access(manifestPath, fs_1.default.constants.R_OK);
}
catch (_) {
// Manifest does not exist
return config;
}
let parsed = {};
try {
parsed = JSON.parse(await fs_1.default.promises.readFile(manifestPath, 'utf8'));
}
catch (_) {
// Manifest is not a valid JSON
console.warn(`WARNING: Manifest file at ${manifestPath} is malformed`);
return config;
}
if (typeof parsed !== 'object' || parsed == null) {
// Malformed manifest structure
return config;
}
const manifestConfig = parsed['sb-babel-cli'];
if (manifestConfig != null) {
const issues = validateConfig(manifestConfig, config);
if (issues.length > 0) {
console.warn(`WARNING: Malformed sb-babel-cli config in manifest at ${manifestPath}`);
console.log(issues.map((item) => ` - ${item}`).join('\n'));
return config;
}
const newConfig = { ...manifestConfig };
Object.keys(config).forEach((key) => {
if (config.specifiedArgs.includes(key) || EXCLUDED_CONFIG_KEYS.includes(key) || !(key in newConfig)) {
newConfig[key] = config[key];
}
});
return newConfig;
}
return config;
}
exports.loadConfigFromRoot = loadConfigFromRoot;