Files
client/build-ndk-bundle.js
2026-04-17 16:52:51 -04:00

214 lines
7.4 KiB
JavaScript

/**
* NDK Browser Bundle Builder
*
* Creates a browser-compatible IIFE bundle of NDK with:
* - Core NDK functionality
* - Browser cache
* - SQLite WASM cache
* - Sessions
* - Messages
* - Web of Trust (WoT)
* - Blossom
* - Uses the existing window.NostrTools from nostr.bundle.js
* - Exposes NDK as window.NDK
*/
const esbuild = require('esbuild');
const path = require('path');
const fs = require('fs');
async function buildNDKBundle() {
console.log('🔧 Building NDK browser bundle with additional packages...');
console.log('📦 Including: core, wallet, cache-browser, cache-dexie, cache-sqlite-wasm, sessions, messages, wot, blossom');
const __dirname = path.resolve();
// Create a virtual entry point that imports all packages using absolute paths
const entryContent = `
// Import the default NDK class
import NDK from '${path.join(__dirname, 'ndk/core/src/index.ts')}';
// Import default exports from cache packages
import NDKCacheBrowser from '${path.join(__dirname, 'ndk/cache-browser/src/index.ts')}';
import NDKCacheAdapterDexie from '${path.join(__dirname, 'ndk/cache-dexie/src/index.ts')}';
import NDKCacheAdapterSqliteWasm from '${path.join(__dirname, 'ndk/cache-sqlite-wasm/src/index.ts')}';
import { CashuMint, CashuWallet, getDecodedToken, getEncodedTokenV4 } from '${path.join(__dirname, 'ndk/node_modules/@cashu/cashu-ts/lib/cashu-ts.es.js')}';
// Re-export everything as named exports
export * from '${path.join(__dirname, 'ndk/core/src/index.ts')}';
export * from '${path.join(__dirname, 'ndk/cache-browser/src/index.ts')}';
export * from '${path.join(__dirname, 'ndk/cache-dexie/src/index.ts')}';
export * from '${path.join(__dirname, 'ndk/cache-sqlite-wasm/src/index.ts')}';
export * from '${path.join(__dirname, 'ndk/sessions/src/index.ts')}';
export * from '${path.join(__dirname, 'ndk/messages/src/index.ts')}';
export * from '${path.join(__dirname, 'ndk/wot/src/index.ts')}';
export * from '${path.join(__dirname, 'ndk/blossom/src/index.ts')}';
export * from '${path.join(__dirname, 'ndk/wallet/src/index.ts')}';
// Re-export default exports as named exports
export {
NDKCacheBrowser,
NDKCacheAdapterDexie,
NDKCacheAdapterSqliteWasm,
CashuMint,
CashuWallet,
getDecodedToken,
getEncodedTokenV4
};
// Export the main NDK class as default
export default NDK;
`;
// Write the virtual entry point
const entryPath = path.join(__dirname, 'build/ndk-entry.js');
fs.mkdirSync('build', { recursive: true });
fs.writeFileSync(entryPath, entryContent);
try {
const result = await esbuild.build({
entryPoints: [entryPath],
bundle: true,
format: 'iife',
globalName: 'NDK',
outfile: 'build/ndk-core.bundle.js',
platform: 'browser',
target: 'es2020',
minify: false, // Set to true for production
sourcemap: true,
metafile: true, // Enable metafile for analysis
// Plugin to map nostr-tools imports to window.NostrTools
plugins: [{
name: 'nostr-tools-external',
setup(build) {
// Intercept all nostr-tools imports (including subpaths like nostr-tools/nip49)
build.onResolve({ filter: /^nostr-tools/ }, args => {
return {
path: args.path,
namespace: 'nostr-tools-external'
};
});
// Replace with window.NostrTools reference
build.onLoad({ filter: /.*/, namespace: 'nostr-tools-external' }, (args) => {
// Extract subpath if present (e.g., "nostr-tools/nip49" -> "nip49")
const subpath = args.path.replace(/^nostr-tools\/?/, '');
return {
contents: `
// Map nostr-tools to window.NostrTools
if (typeof window === 'undefined' || !window.NostrTools) {
throw new Error('NDK: nostr.bundle.js must be loaded before ndk-core.bundle.js');
}
${subpath ? `
// Access subpath: ${subpath}
if (!window.NostrTools.${subpath}) {
console.warn('nostr-tools/${subpath} not found in window.NostrTools');
module.exports = {};
} else {
module.exports = window.NostrTools.${subpath};
}
` : `
module.exports = window.NostrTools;
`}
`,
loader: 'js'
};
});
}
}],
// Define environment variables for browser
define: {
'process.env.NODE_ENV': '"production"',
'global': 'window'
},
// Banner to add at the top of the bundle
banner: {
js: `/**
* NDK (Nostr Development Kit) - Browser Bundle
* Version: 3.0.0-beta.64
*
* IMPORTANT: This bundle requires nostr.bundle.js to be loaded first!
*
* Usage:
* <script src="nostr.bundle.js"></script>
* <script src="ndk-core.bundle.js"></script>
*
* Then access via: window.NDK
*
* Generated: ${new Date().toISOString()}
*/
`
}
});
// Save metafile for analysis
fs.writeFileSync('build/meta.json', JSON.stringify(result.metafile, null, 2));
const stats = fs.statSync('build/ndk-core.bundle.js');
const sizeKB = (stats.size / 1024).toFixed(2);
console.log('✅ NDK bundle created successfully!');
console.log(`📏 Bundle size: ${sizeKB} KB`);
console.log('📄 Output: build/ndk-core.bundle.js');
console.log('📄 Source map: build/ndk-core.bundle.js.map');
console.log('');
// Analyze bundle composition
console.log('📊 Bundle Composition Analysis:');
console.log('');
const inputs = result.metafile.inputs;
const packageSizes = {};
for (const [file, info] of Object.entries(inputs)) {
let packageName = 'other';
if (file.includes('ndk/core/')) packageName = 'core';
else if (file.includes('ndk/cache-browser/')) packageName = 'cache-browser';
else if (file.includes('ndk/cache-dexie/')) packageName = 'cache-dexie';
else if (file.includes('ndk/cache-sqlite-wasm/')) packageName = 'cache-sqlite-wasm';
else if (file.includes('ndk/sessions/')) packageName = 'sessions';
else if (file.includes('ndk/messages/')) packageName = 'messages';
else if (file.includes('ndk/wot/')) packageName = 'wot';
else if (file.includes('ndk/blossom/')) packageName = 'blossom';
else if (file.includes('node_modules/@noble/')) packageName = 'noble-crypto';
else if (file.includes('node_modules/')) packageName = 'dependencies';
if (!packageSizes[packageName]) packageSizes[packageName] = 0;
packageSizes[packageName] += info.bytes;
}
// Sort by size
const sorted = Object.entries(packageSizes)
.sort((a, b) => b[1] - a[1])
.map(([name, bytes]) => ({
name,
kb: (bytes / 1024).toFixed(2),
percent: ((bytes / stats.size) * 100).toFixed(1)
}));
for (const item of sorted) {
const bar = '█'.repeat(Math.floor(item.percent / 2));
console.log(` ${item.name.padEnd(20)} ${item.kb.padStart(8)} KB ${item.percent.padStart(5)}% ${bar}`);
}
console.log('');
console.log('📋 Usage:');
console.log(' 1. Load nostr.bundle.js first');
console.log(' 2. Load ndk-core.bundle.js second');
console.log(' 3. Access via window.NDK');
} catch (error) {
console.error('❌ Build failed:', error);
process.exit(1);
}
}
// Run the build
buildNDKBundle();