prepare-package
Version:
Prepare a Node.js package before being published
128 lines (102 loc) • 3.62 kB
JavaScript
const fs = require('fs');
const path = require('path');
const readline = require('readline');
const { default: chalk } = require('chalk');
const logger = require('./logger');
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
function ask(question, defaultValue) {
const suffix = defaultValue ? ` (${chalk.cyan(defaultValue)})` : '';
return new Promise((resolve) => {
rl.question(`${question}${suffix}: `, (answer) => {
resolve(answer.trim() || defaultValue || '');
});
});
}
function askChoice(question, choices, defaultIndex) {
console.log(`\n${question}`);
choices.forEach((choice, i) => {
const marker = i === defaultIndex ? chalk.green('❯') : ' ';
console.log(` ${marker} ${i + 1}) ${choice}`);
});
return new Promise((resolve) => {
rl.question(`Choice (${defaultIndex + 1}): `, (answer) => {
const index = parseInt(answer, 10) - 1;
resolve(choices[Number.isNaN(index) || index < 0 || index >= choices.length ? defaultIndex : index]);
});
});
}
function toTitleCase(name) {
return name
.replace(/[-_@/]/g, ' ')
.replace(/\s+/g, ' ')
.trim()
.split(' ')
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
.join('');
}
async function init() {
const cwd = process.cwd();
const pkgPath = path.resolve(cwd, 'package.json');
if (!fs.existsSync(pkgPath)) {
logger.error('No package.json found in current directory. Run `npm init` first.');
process.exit(1);
}
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
console.log(chalk.bold(`\n prepare-package init\n`));
console.log(` Setting up ${chalk.cyan(pkg.name || 'your package')}...\n`);
// Ask type
const type = await askChoice('Build type:', ['copy', 'bundle'], 0);
// Ask input/output
const input = await ask('Source directory', 'src');
const output = await ask('Output directory', 'dist');
// Build preparePackage config
const config = {
input,
output,
type,
};
// Bundle-specific config
if (type === 'bundle') {
const defaultGlobal = toTitleCase(pkg.name || 'MyLib');
const defaultFileName = `${(pkg.name || 'my-lib').replace(/@.*\//, '')}.min.js`;
const formatsRaw = await ask('Formats (comma-separated)', 'esm, cjs, iife');
const formats = formatsRaw.split(',').map(f => f.trim()).filter(Boolean);
config.build = { formats };
if (formats.includes('iife')) {
const globalName = await ask('IIFE global name', defaultGlobal);
const fileName = await ask('IIFE filename', defaultFileName);
config.build.iife = { globalName, fileName };
}
}
// Update package.json
pkg.preparePackage = config;
pkg.main = pkg.main || `./${output}/index.js`;
if (type === 'bundle') {
pkg.module = pkg.module || `./${output}/index.mjs`;
pkg.exports = pkg.exports || {
'.': {
import: `./${output}/index.mjs`,
require: `./${output}/index.js`,
},
};
}
// Add scripts
pkg.scripts = pkg.scripts || {};
pkg.scripts.prepare = `node -e "require('prepare-package')()"`;
pkg.scripts['prepare:watch'] = `node -e "require('prepare-package/watch')()"`;
// Write
fs.writeFileSync(pkgPath, `${JSON.stringify(pkg, null, 2)}\n`);
console.log(`\n${chalk.green('Done!')} Updated package.json:\n`);
console.log(chalk.gray(JSON.stringify({ preparePackage: config }, null, 2)));
console.log(`\nRun ${chalk.cyan('npm run prepare')} to build.\n`);
rl.close();
}
init().catch((err) => {
logger.error(err.message);
rl.close();
process.exit(1);
});