Spaces:
Running
on
CPU Upgrade
Running
on
CPU Upgrade
| /** | |
| * Export bundle: TXT + DOCX + screenshots β ZIP archive | |
| * | |
| * Runs the three export steps in sequence, gathers all outputs | |
| * into a single folder, and creates a .zip archive. | |
| * | |
| * Usage: | |
| * node scripts/export-bundle.mjs | |
| * npm run export:bundle | |
| * | |
| * Output: dist/{slug}-bundle.zip | |
| */ | |
| import { spawn } from 'node:child_process'; | |
| import { resolve, join, basename } from 'node:path'; | |
| import { promises as fs } from 'node:fs'; | |
| import { createWriteStream } from 'node:fs'; | |
| import { pipeline } from 'node:stream/promises'; | |
| import { Readable } from 'node:stream'; | |
| import process from 'node:process'; | |
| import { execSync } from 'node:child_process'; | |
| const cwd = process.cwd(); | |
| // βββ Helpers ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| function run(command, args = [], options = {}) { | |
| return new Promise((resolvePromise, reject) => { | |
| console.log(` $ ${command} ${args.join(' ')}`); | |
| const child = spawn(command, args, { stdio: 'inherit', shell: false, ...options }); | |
| child.on('error', reject); | |
| child.on('close', (code) => { | |
| if (code === 0) resolvePromise(); | |
| else reject(new Error(`"${command} ${args.join(' ')}" exited with code ${code}`)); | |
| }); | |
| }); | |
| } | |
| async function exists(p) { | |
| try { await fs.access(p); return true; } catch { return false; } | |
| } | |
| async function copyDir(src, dest) { | |
| await fs.mkdir(dest, { recursive: true }); | |
| const entries = await fs.readdir(src, { withFileTypes: true }); | |
| for (const entry of entries) { | |
| const srcPath = join(src, entry.name); | |
| const destPath = join(dest, entry.name); | |
| if (entry.isDirectory()) { | |
| await copyDir(srcPath, destPath); | |
| } else { | |
| await fs.copyFile(srcPath, destPath); | |
| } | |
| } | |
| } | |
| async function findFiles(dir, pattern) { | |
| const results = []; | |
| try { | |
| const entries = await fs.readdir(dir, { withFileTypes: true }); | |
| for (const entry of entries) { | |
| if (entry.isFile() && pattern.test(entry.name)) { | |
| results.push(join(dir, entry.name)); | |
| } | |
| } | |
| } catch { /* dir may not exist */ } | |
| return results; | |
| } | |
| // βββ Main βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| async function main() { | |
| const distDir = resolve(cwd, 'dist'); | |
| const screenshotsDir = resolve(cwd, 'screenshots'); | |
| console.log('π¦ Export Bundle: TXT + DOCX + Images β ZIP\n'); | |
| // ββ Step 1: Export TXT ββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| console.log('β'.repeat(60)); | |
| console.log('1/3 Exporting TXTβ¦'); | |
| console.log('β'.repeat(60)); | |
| await run('node', ['scripts/export-txt.mjs']); | |
| console.log(); | |
| // Find the generated TXT file | |
| const txtFiles = await findFiles(distDir, /\.txt$/); | |
| if (txtFiles.length === 0) { | |
| console.error('β No TXT file found in dist/ after export:txt'); | |
| process.exit(1); | |
| } | |
| const txtPath = txtFiles[0]; | |
| const slug = basename(txtPath, '.txt'); | |
| console.log(` β Found: ${basename(txtPath)}`); | |
| // ββ Step 2: Export DOCX βββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| console.log('\n' + 'β'.repeat(60)); | |
| console.log('2/3 Exporting DOCXβ¦'); | |
| console.log('β'.repeat(60)); | |
| await run('node', ['scripts/export-docx.mjs', `--input=${txtPath}`]); | |
| console.log(); | |
| const docxPath = txtPath.replace('.txt', '.docx'); | |
| if (!(await exists(docxPath))) { | |
| console.error('β DOCX file not found after export:docx'); | |
| process.exit(1); | |
| } | |
| console.log(` β Found: ${basename(docxPath)}`); | |
| // ββ Step 3: Export Images βββββββββββββββββββββββββββββββββββββββββββββββββ | |
| console.log('\n' + 'β'.repeat(60)); | |
| console.log('3/3 Exporting screenshotsβ¦'); | |
| console.log('β'.repeat(60)); | |
| await run('node', ['scripts/screenshot-elements.mjs']); | |
| console.log(); | |
| if (!(await exists(screenshotsDir))) { | |
| console.error('β screenshots/ folder not found after export:images'); | |
| process.exit(1); | |
| } | |
| // Count screenshots | |
| const pngFiles = await findFiles(screenshotsDir, /\.png$/); | |
| console.log(` β Found: ${pngFiles.length} screenshots`); | |
| // ββ Step 4: Assemble bundle folder ββββββββββββββββββββββββββββββββββββββββ | |
| console.log('\n' + 'β'.repeat(60)); | |
| console.log('4/4 Creating archiveβ¦'); | |
| console.log('β'.repeat(60)); | |
| const bundleDir = resolve(distDir, `${slug}-bundle`); | |
| const imagesDir = join(bundleDir, 'images'); | |
| // Clean previous bundle | |
| await fs.rm(bundleDir, { recursive: true, force: true }); | |
| await fs.mkdir(imagesDir, { recursive: true }); | |
| // Copy TXT | |
| await fs.copyFile(txtPath, join(bundleDir, basename(txtPath))); | |
| console.log(` β ${basename(txtPath)}`); | |
| // Copy DOCX | |
| await fs.copyFile(docxPath, join(bundleDir, basename(docxPath))); | |
| console.log(` β ${basename(docxPath)}`); | |
| // Copy screenshots | |
| for (const png of pngFiles) { | |
| await fs.copyFile(png, join(imagesDir, basename(png))); | |
| } | |
| console.log(` β images/ (${pngFiles.length} files)`); | |
| // ββ Step 5: Create ZIP ββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| const zipPath = resolve(distDir, `${slug}-bundle.zip`); | |
| // Remove old zip | |
| try { await fs.unlink(zipPath); } catch { /* ok */ } | |
| // Use system zip command (available on macOS/Linux) | |
| execSync( | |
| `cd "${distDir}" && zip -r "${basename(zipPath)}" "${basename(bundleDir)}"`, | |
| { stdio: 'inherit' }, | |
| ); | |
| const zipStat = await fs.stat(zipPath); | |
| const sizeMB = (zipStat.size / 1024 / 1024).toFixed(1); | |
| // Clean up bundle folder (keep only the zip) | |
| await fs.rm(bundleDir, { recursive: true, force: true }); | |
| console.log(`\n${'β'.repeat(60)}`); | |
| console.log(`π Bundle created: dist/${basename(zipPath)} (${sizeMB} MB)`); | |
| console.log(` Contents: ${basename(txtPath)}, ${basename(docxPath)}, ${pngFiles.length} images`); | |
| console.log('β'.repeat(60)); | |
| // Also copy to public/ | |
| const publicZip = resolve(cwd, 'public', basename(zipPath)); | |
| await fs.mkdir(resolve(cwd, 'public'), { recursive: true }); | |
| await fs.copyFile(zipPath, publicZip); | |
| console.log(` β Copied to public/${basename(zipPath)}`); | |
| } | |
| main().catch((err) => { | |
| console.error('β Bundle failed:', err.message); | |
| process.exit(1); | |
| }); | |