UNPKG

@zeix/ui-element

Version:

UIElement - a HTML-first library for reactive Web Components

272 lines (227 loc) โ€ข 6.76 kB
import { execSync } from 'child_process' import { mkdir, readdir, unlink, writeFile } from 'fs/promises' import { join } from 'path' import { ASSETS_DIR, generateAssetHash } from './config' // Clean up old versioned assets const cleanOldVersionedAssets = async (): Promise<void> => { try { const files = await readdir(ASSETS_DIR) const versionedFiles = files.filter(file => /^main\.[a-f0-9]+\.(css|js|js\.map)$/.test(file), ) if (versionedFiles.length > 0) { console.log( `๐Ÿงน Cleaning ${versionedFiles.length} old versioned assets...`, ) await Promise.all( versionedFiles.map(file => unlink(join(ASSETS_DIR, file)).catch(() => { // File might not exist, ignore }), ), ) } } catch (error) { // Directory might not exist yet, that's fine if ((error as NodeJS.ErrnoException).code !== 'ENOENT') { console.warn('โš ๏ธ Warning: Could not clean old assets:', error) } } } // Ensure assets directory exists const ensureAssetsDir = async (): Promise<void> => { try { await mkdir(ASSETS_DIR, { recursive: true }) } catch (error) { if ((error as NodeJS.ErrnoException).code !== 'EEXIST') { throw error } } } // Build CSS using lightningcss (leveraging existing setup) const buildCSSWithLightning = async (): Promise<void> => { console.log('๐Ÿ”„ Building CSS with lightningcss...') try { // Use the existing lightningcss command from package.json execSync( 'lightningcss --minify --bundle --targets ">= 0.25%" docs-src/main.css -o ./docs/assets/main.css', { stdio: 'inherit', }, ) console.log('โœ… CSS built with lightningcss') } catch (error) { console.error('โŒ Failed to build CSS with lightningcss:', error) throw error } } // Create optimized CSS assets export const buildOptimizedCSS = async (): Promise<{ mainCSSPath: string mainCSSHash: string }> => { await ensureAssetsDir() // First build the main CSS using lightningcss await buildCSSWithLightning() const originalCSSPath = join(ASSETS_DIR, 'main.css') // Generate content hash for the main CSS file const mainCSSHash = generateAssetHash(originalCSSPath) // Create versioned filename for main CSS const versionedCSSPath = join(ASSETS_DIR, `main.${mainCSSHash}.css`) const mainCSSPath = `assets/main.${mainCSSHash}.css` // Copy main CSS to versioned filename execSync(`cp "${originalCSSPath}" "${versionedCSSPath}"`) console.log(`โœ… Built versioned CSS: ${versionedCSSPath}`) return { mainCSSPath, mainCSSHash, } } // Build JavaScript using bun (leveraging existing setup) const buildJSWithBun = async (): Promise<void> => { console.log('๐Ÿ”„ Building JS with bun...') try { // Use the existing bun command from package.json execSync( 'bun build docs-src/main.ts --outdir ./docs/assets/ --minify --define process.env.DEV_MODE=false --sourcemap=external', { stdio: 'inherit', }, ) console.log('โœ… JS built with bun') } catch (error) { console.error('โŒ Failed to build JS with bun:', error) throw error } } // Create optimized JS assets export const buildOptimizedJS = async (): Promise<{ mainJSPath: string mainJSHash: string }> => { await ensureAssetsDir() // First build the main JS using bun await buildJSWithBun() const originalJSPath = join(ASSETS_DIR, 'main.js') // Generate content hash for the main JS file const mainJSHash = generateAssetHash(originalJSPath) // Create versioned filename for main JS const versionedJSPath = join(ASSETS_DIR, `main.${mainJSHash}.js`) const mainJSPath = `assets/main.${mainJSHash}.js` // Copy main JS to versioned filename execSync(`cp "${originalJSPath}" "${versionedJSPath}"`) // Also copy sourcemap if it exists try { const sourceMapPath = join(ASSETS_DIR, 'main.js.map') const versionedSourceMapPath = join( ASSETS_DIR, `main.${mainJSHash}.js.map`, ) execSync(`cp "${sourceMapPath}" "${versionedSourceMapPath}"`) console.log( `โœ… Built versioned JS sourcemap: ${versionedSourceMapPath}`, ) } catch { // Sourcemap might not exist, that's okay } console.log(`โœ… Built versioned JS: ${versionedJSPath}`) return { mainJSPath, mainJSHash, } } // Generate service worker for advanced caching export const generateServiceWorker = async ( cssHash: string, jsHash: string, ): Promise<void> => { const swContent = `// UIElement Docs Service Worker const CACHE_NAME = 'ui-element-docs-v${Date.now()}'; // Assets to cache on install const STATIC_ASSETS = [ '/', '/index.html', '/assets/main.${cssHash}.css', '/assets/main.${jsHash}.js', ]; self.addEventListener('install', (event) => { event.waitUntil( caches.open(CACHE_NAME) .then(cache => cache.addAll(STATIC_ASSETS)) .then(() => self.skipWaiting()) ); }); self.addEventListener('activate', (event) => { event.waitUntil( caches.keys() .then(cacheNames => { return Promise.all( cacheNames .filter(cacheName => cacheName !== CACHE_NAME) .map(cacheName => caches.delete(cacheName)) ); }) .then(() => self.clients.claim()) ); }); self.addEventListener('fetch', (event) => { const { request } = event; // Only handle GET requests if (request.method !== 'GET') return; // Skip cross-origin requests if (!request.url.startsWith(self.location.origin)) return; event.respondWith( caches.match(request) .then(cachedResponse => { if (cachedResponse) { return cachedResponse; } return fetch(request) .then(response => { // Don't cache non-successful responses if (!response.ok) return response; // Clone the response const responseToCache = response.clone(); // Cache static assets if (request.url.includes('/assets/') || request.url.endsWith('.html')) { caches.open(CACHE_NAME) .then(cache => cache.put(request, responseToCache)); } return response; }); }) ); });` const swPath = join('./docs', 'sw.js') await writeFile(swPath, swContent, 'utf8') console.log(`โœ… Generated service worker: ${swPath}`) } // Main build function export const buildOptimizedAssets = async (): Promise<{ css: { mainCSSPath: string mainCSSHash: string } js: { mainJSPath: string mainJSHash: string } }> => { console.log('๐Ÿ”„ Building optimized assets...') // Clean up old versioned assets first await cleanOldVersionedAssets() const [cssAssets, jsAssets] = await Promise.all([ buildOptimizedCSS(), buildOptimizedJS(), ]) // Generate service worker with the hashes await generateServiceWorker(cssAssets.mainCSSHash, jsAssets.mainJSHash) console.log('โœจ Optimized assets built successfully!') return { css: cssAssets, js: jsAssets, } } if (import.meta.main) { buildOptimizedAssets().catch(console.error) }