Files
john-nuvio/build.js
T
2026-07-20 17:28:26 +03:00

198 lines
6.6 KiB
JavaScript

#!/usr/bin/env node
/**
* Build script for nuvio-providers
*
* Bundles each provider from src/<provider>/ into a single file at providers/<provider>.js
*
* Usage:
* node build.js # Build all providers
* node build.js vixsrc # Build only vixsrc
* node build.js --minify # Build all with minification
* node build.js --watch # Watch mode (requires nodemon)
* node build.js --transpile # Transpile async/await in providers/
*/
const esbuild = require('esbuild');
const fs = require('fs');
const path = require('path');
const srcDir = path.join(__dirname, 'src');
const outDir = path.join(__dirname, 'providers');
// Modules that the Nuvio app provides - don't bundle these
const EXTERNAL_MODULES = [
'cheerio-without-node-native',
'react-native-cheerio',
'cheerio',
'crypto-js',
'axios'
];
// Get provider names from command line or discover all
function getProvidersToBuild() {
const args = process.argv.slice(2).filter(arg => !arg.startsWith('-'));
if (args.length > 0) {
return args;
}
// Discover all provider folders in src/
if (!fs.existsSync(srcDir)) {
console.error('❌ src/ directory not found. Create provider folders in src/<provider>/');
process.exit(1);
}
return fs.readdirSync(srcDir, { withFileTypes: true })
.filter(d => d.isDirectory())
.map(d => d.name);
}
async function buildProvider(providerName, options = {}) {
const providerDir = path.join(srcDir, providerName);
const entryPoint = path.join(providerDir, 'index.js');
const outFile = path.join(outDir, `${providerName}.js`);
if (!fs.existsSync(entryPoint)) {
console.warn(`⚠️ Skipping ${providerName}: no src/${providerName}/index.js found`);
return false;
}
try {
const result = await esbuild.build({
entryPoints: [entryPoint],
bundle: true,
outfile: outFile,
format: 'cjs', // CommonJS for module.exports compatibility
platform: 'neutral', // Works in both browser and node-like environments
// Both Nuvio runtimes support native async/await. Keeping it avoids
// esbuild's generator helper, which older NuvioTV builds can reject.
target: 'es2017',
minify: options.minify || false, // Minify if --minify flag is set
sourcemap: false,
external: EXTERNAL_MODULES,
banner: {
// NuvioTV embeds the bundle in its own function wrapper. Keep the
// leading comment on one line so it cannot span wrapper boundaries.
js: `// ${providerName} - bundled from src/${providerName}/`
},
logLevel: 'warning'
});
const stats = fs.statSync(outFile);
const sizeKB = (stats.size / 1024).toFixed(1);
const minifyIndicator = options.minify ? ' (minified)' : '';
console.log(`✅ ${providerName}.js (${sizeKB} KB)${minifyIndicator}`);
return true;
} catch (err) {
console.error(`❌ Failed to build ${providerName}:`, err.message);
return false;
}
}
// Transpile a single file in providers/ (for developers writing single-file providers with async)
async function transpileSingleFile(filename) {
const inputPath = path.join(outDir, filename);
if (!fs.existsSync(inputPath)) {
console.warn(`⚠️ File not found: providers/${filename}`);
return false;
}
// Read original file
const originalContent = fs.readFileSync(inputPath, 'utf-8');
// Check if it needs transpilation (has async/await)
if (!originalContent.includes('async ') && !originalContent.includes('await ')) {
console.log(`⏭️ ${filename} - no async/await, skipping`);
return true;
}
try {
const result = await esbuild.transform(originalContent, {
loader: 'js',
// Match bundled providers and retain native async/await for both apps.
target: 'es2017',
format: 'cjs'
});
// Write transpiled content back
fs.writeFileSync(inputPath, result.code);
const stats = fs.statSync(inputPath);
const sizeKB = (stats.size / 1024).toFixed(1);
console.log(`✅ ${filename} transpiled (${sizeKB} KB)`);
return true;
} catch (err) {
console.error(`❌ Failed to transpile ${filename}:`, err.message);
return false;
}
}
async function main() {
const args = process.argv.slice(2);
const shouldMinify = args.includes('--minify');
// Handle --transpile flag for single-file providers
if (args.includes('--transpile')) {
const files = args.filter(a => a !== '--transpile' && !a.startsWith('-'));
if (files.length === 0) {
// Transpile all .js files in providers/ that aren't from src/
const srcProviders = fs.existsSync(srcDir)
? fs.readdirSync(srcDir, { withFileTypes: true })
.filter(d => d.isDirectory())
.map(d => d.name + '.js')
: [];
const allProviderFiles = fs.readdirSync(outDir)
.filter(f => f.endsWith('.js') && !srcProviders.includes(f));
console.log(`\n🔄 Transpiling ${allProviderFiles.length} single-file provider(s)...\n`);
for (const file of allProviderFiles) {
await transpileSingleFile(file);
}
} else {
console.log(`\n🔄 Transpiling ${files.length} file(s)...\n`);
for (const file of files) {
const filename = file.endsWith('.js') ? file : file + '.js';
await transpileSingleFile(filename);
}
}
return;
}
const providers = getProvidersToBuild();
if (providers.length === 0) {
console.log('No providers found in src/ directory.');
console.log('Create a provider: mkdir -p src/myprovider && touch src/myprovider/index.js');
return;
}
const minifyLabel = shouldMinify ? ' (minified)' : '';
console.log(`\n📦 Building ${providers.length} provider(s)${minifyLabel}...\n`);
// Ensure output directory exists
if (!fs.existsSync(outDir)) {
fs.mkdirSync(outDir, { recursive: true });
}
let success = 0;
let failed = 0;
for (const provider of providers) {
const result = await buildProvider(provider, { minify: shouldMinify });
if (result) success++;
else failed++;
}
console.log(`\n✨ Done! ${success} built, ${failed} skipped/failed\n`);
}
main().catch(err => {
console.error('Build failed:', err);
process.exit(1);
});