/* eslint-env node */ /* eslint-disable no-console */ // Build JS and CSS using esbuild and Lightning CSS import { promises as fs } from 'node:fs'; import path from 'node:path'; import browserslist from 'browserslist'; import esbuild from 'esbuild'; import * as lightningcss from 'lightningcss'; // Packages to build but exclude from bundle const externalPackages = [ 'chart.js/auto', 'alpinejs/dist/cdn.min.js', '@alpinejs/collapse/dist/cdn.min.js', 'xterm', 'xterm-addon-webgl', 'xterm-addon-canvas', ]; // Build main bundle async function buildJS() { const inputPath = './web/js/src/index.js'; try { await esbuild.build({ entryPoints: [inputPath], outfile: './web/js/dist/main.min.js', bundle: true, minify: true, sourcemap: true, external: externalPackages, }); console.log('✅ JavaScript build completed for', inputPath); } catch (error) { console.error('❌ Error building JavaScript:', error); process.exit(1); } } // Build external packages async function buildExternalJS() { try { const buildPromises = externalPackages.map(async (pkg) => { const outputPath = getOutputPath(pkg); await esbuild.build({ entryPoints: [pkg], outfile: outputPath, bundle: true, minify: true, format: 'esm', }); console.log(`✅ Dependency build completed for ${pkg}`); }); await Promise.all(buildPromises); } catch (error) { console.error('❌ Error building external packages:', error); process.exit(1); } } function getOutputPath(pkg) { let pkgName; if (pkg.startsWith('alpinejs')) { pkgName = 'alpinejs'; } else if (pkg.startsWith('@alpinejs/collapse')) { pkgName = 'alpinejs-collapse'; } else { pkgName = pkg.replace(/\//g, '-'); } return `./web/js/dist/${pkgName}.min.js`; } // Process a CSS file async function processCSS(inputFile, outputFile) { try { await ensureDir(path.dirname(outputFile)); const css = await fs.readFile(inputFile); const bundle = await lightningcss.bundleAsync({ filename: inputFile, sourceMap: true, code: Buffer.from(css), minify: true, targets: lightningcss.browserslistToTargets(browserslist()), drafts: { customMedia: true, nesting: true }, visitor: { Url: (node) => { // Fix relative paths for webfonts if (node.url.startsWith('../webfonts/')) { return { url: node.url.replace('../webfonts/', '/webfonts/'), loc: node.loc, }; } return node; }, }, resolver: { resolve(specifier, from) { if (!specifier.endsWith('.css')) { specifier += '.css'; } if (specifier.startsWith('node:')) { return `node_modules/${specifier.replace('node:', '')}`; } return `${path.dirname(from)}/${specifier}`; }, }, }); await fs.writeFile(outputFile, bundle.code); await fs.writeFile(`${outputFile}.map`, bundle.map); console.log(`✅ CSS build completed for ${inputFile}`); } catch (error) { console.error(`❌ Error processing CSS for ${inputFile}:`, error); process.exit(1); } } // Build CSS async function buildCSS() { const themesSourcePath = './web/css/src/themes/'; const cssEntries = await fs.readdir(themesSourcePath); const cssBuildPromises = cssEntries .filter((entry) => path.extname(entry) === '.css') .map(async (entry) => { const entryName = entry.replace('.css', '.min.css'); const inputPath = path.join(themesSourcePath, entry); const outputPath = `./web/css/themes/${entryName}`; await processCSS(inputPath, outputPath); }); await Promise.all(cssBuildPromises); } // Ensure a directory exists async function ensureDir(dir) { try { await fs.mkdir(dir, { recursive: true }); } catch (error) { if (error.code !== 'EEXIST') { throw error; } } } // Build all assets async function build() { console.log('🚀 Building JS and CSS...'); await buildJS(); await buildExternalJS(); await buildCSS(); console.log('🎉 Build completed.'); } // Execute build build();