#!/usr/bin/env node /** * Build script for nuvio-providers * * Bundles each provider from src// into a single file at providers/.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//'); 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); });