jsrunner-bundler
Version:
NPM Bundler (sources & types) for the JavaScript Runner
116 lines (101 loc) • 4.13 kB
JavaScript
// jsrunner-bundler.cjs
// Empaqueta el bundle JS y genera typings como módulos ambient (“declare module 'x' { … }”)
const fs = require('fs');
const path = require('path');
const webpack = require('webpack');
const { execSync } = require('child_process');
// Directorios: 'src' en raíz para el entry, y 'dist' en raíz para salida
const SRC_DIR = path.resolve(__dirname, 'src');
const DIST_DIR = path.resolve(__dirname, 'dist');
const ENTRY_JS = path.join(SRC_DIR, 'index.js');
const OUT_JS = path.join(DIST_DIR, 'jsrunner.js');
const OUT_DTS = path.join(DIST_DIR, 'jsrunner.d.ts');
// Módulos a empaquetar pasados como argumentos
const modules = process.argv.slice(2);
if (!modules.length) {
console.error('❌ Uso: node jsrunner-bundler.cjs <módulo1> [módulo2 …]');
process.exit(1);
}
/**
* Recoge recursivamente todos los archivos .d.ts en un directorio
*/
function collectDtsFiles(dir, out = []) {
for (const name of fs.readdirSync(dir)) {
const full = path.join(dir, name);
const stat = fs.statSync(full);
if (stat.isDirectory()) collectDtsFiles(full, out);
else if (name.endsWith('.d.ts')) out.push(full);
}
return out;
}
/**
* Elimina líneas de import/export from 'x' y export {}
*/
function removeImportsAndExports(src) {
return src
.split('\n')
.filter(line => {
const t = line.trim();
if (/^(import|export)\s.*from\s+['"].+['"];?$/.test(t)) return false;
if (/^export\s+\{\s*\};?$/.test(t)) return false;
return true;
})
.map(line => line.replace(/^export\s+/, ''))
.join('\n');
}
// 1) Instala dependencias locales
console.log('📦 npm install %s', modules.join(' '));
execSync(`npm install ${modules.join(' ')}`, { stdio: 'inherit' });
// 2) Genera entry JS para Webpack en 'src/index.js'
fs.mkdirSync(SRC_DIR, { recursive: true });
const entryContent = modules.map(m => `export * from "${m}";`).join('\n');
fs.writeFileSync(ENTRY_JS, entryContent, 'utf8');
// 3) Ejecuta Webpack
console.log('🧩 Ejecutando Webpack…');
const webpackConfigPath = path.resolve(__dirname, 'webpack.config.js');
if (!fs.existsSync(webpackConfigPath)) {
console.error('❌ No se encontró webpack.config.js');
process.exit(1);
}
const webpackConfig = require(webpackConfigPath);
webpack(webpackConfig, (err, stats) => {
if (err || stats.hasErrors()) {
console.error('❌ Webpack falló:', err || stats.toJson().errors);
process.exit(1);
}
console.log('✅ Bundle generado:', OUT_JS);
// 4) Genera typings .d.ts como un único módulo ambient por paquete
console.log('📄 Generando typings embebidos en un único module...');
let output = `// AUTO-GENERATED: combinadas para [${modules.join(', ')}]\n\n`;
modules.forEach(modName => {
// Resuelve ruta de package.json y root de tipos
const pkgJsonPath = require.resolve(path.join(modName, 'package.json'));
const pkgRoot = path.dirname(pkgJsonPath);
const pkg = require(pkgJsonPath);
let typesRoot = pkg.types || pkg.typings
? path.resolve(pkgRoot, pkg.types || pkg.typings)
: pkgRoot;
if (!fs.statSync(typesRoot).isDirectory()) typesRoot = path.dirname(typesRoot);
// Recopila y concatena todos los .d.ts
const files = collectDtsFiles(typesRoot).sort();
let combined = '';
files.forEach(file => {
const rel = path.relative(typesRoot, file).replace(/\\/g, '/');
let src = fs.readFileSync(file, 'utf8');
src = removeImportsAndExports(src).trim();
combined += `// ----- ${rel} -----\n`;
combined += src + '\n\n';
});
// Envuelve todo en un único declare module
output += `declare module "${modName}" {\n`;
combined.split('\n').forEach(line => {
output += ' ' + line + '\n';
});
output += '}\n\n';
});
// Escribe el archivo resultante
fs.mkdirSync(path.dirname(OUT_DTS), { recursive: true });
fs.writeFileSync(OUT_DTS, output, 'utf8');
console.log('✅ Typings generadas en:', OUT_DTS);
});