import type { ChildProcess } from 'node:child_process'; import { spawn } from 'node:child_process'; import { existsSync } from 'node:fs'; import path from 'node:path'; import * as p from '@clack/prompts'; import { config } from 'dotenv'; import { type BuildFlags, projectRoot } from '../config/index.js'; import { watchI18N } from '../util/i18n.js'; const cwd = path.resolve(projectRoot, 'packages/frontend/core'); const flags: BuildFlags = { distribution: 'browser', mode: 'development', channel: 'canary', coverage: process.env.COVERAGE === 'true', localBlockSuite: undefined, }; if (process.argv.includes('--static')) { await awaitChildProcess( spawn( 'node', [ '--loader', 'ts-node/esm/transpile-only', '../../../node_modules/webpack/bin/webpack.js', 'serve', '--mode', 'development', '--no-client-overlay', '--no-live-reload', '--env', 'flags=' + Buffer.from(JSON.stringify(flags), 'utf-8').toString('hex'), ].filter((v): v is string => !!v), { cwd, stdio: 'inherit', shell: true, env: process.env, } ) ); process.exit(0); } const files = ['.env', '.env.local']; for (const file of files) { if (existsSync(path.resolve(projectRoot, file))) { config({ path: path.resolve(projectRoot, file), }); console.log(`${file} loaded`); break; } } const buildFlags = await p.group( { distribution: () => p.select({ message: 'Distribution', options: [ { value: 'browser', }, { value: 'desktop', }, ], initialValue: 'browser', }), mode: () => p.select({ message: 'Mode', options: [ { value: 'development', }, { value: 'production', }, ], initialValue: 'development', }), channel: () => p.select({ message: 'Channel', options: [ { value: 'canary', }, { value: 'beta', }, { value: 'stable', }, ], initialValue: 'canary', }), coverage: () => p.confirm({ message: 'Enable coverage', initialValue: process.env.COVERAGE === 'true', }), debugBlockSuite: () => p.confirm({ message: 'Debug blocksuite locally?', initialValue: false, }), }, { onCancel: () => { p.cancel('Operation cancelled.'); process.exit(0); }, } ); if (buildFlags.debugBlockSuite) { const { config } = await import('dotenv'); const envLocal = config({ path: path.resolve(cwd, '.env.local'), }); const localBlockSuite = await p.text({ message: 'local blocksuite PATH', initialValue: envLocal.error ? undefined : envLocal.parsed?.LOCAL_BLOCK_SUITE, }); if (typeof localBlockSuite !== 'string') { throw new Error('local blocksuite PATH is required'); } if (!existsSync(localBlockSuite)) { throw new Error(`local blocksuite not found: ${localBlockSuite}`); } flags.localBlockSuite = localBlockSuite; } flags.distribution = buildFlags.distribution as any; flags.mode = buildFlags.mode as any; flags.channel = buildFlags.channel as any; flags.coverage = buildFlags.coverage; watchI18N(); function awaitChildProcess(child: ChildProcess): Promise { return new Promise((resolve, reject) => { const handleExitCode = (code: number | null) => { if (code) { reject( new Error( `Child process at ${ (child as any).cwd } fails: ${child.spawnargs.join(' ')}` ) ); } else { resolve(0); } }; child.on('error', () => handleExitCode(child.exitCode)); child.on('exit', code => handleExitCode(code)); }); } try { // Build:infra await awaitChildProcess( spawn('yarn', ['build:infra'], { cwd, stdio: 'inherit', shell: true, env: process.env, }) ); // Build:plugins await awaitChildProcess( spawn('yarn', ['build:plugins'], { cwd, stdio: 'inherit', shell: true, env: process.env, }) ); // Start webpack await awaitChildProcess( spawn( 'node', [ '--loader', 'ts-node/esm/transpile-only', '../../../node_modules/webpack/bin/webpack.js', flags.mode === 'development' ? 'serve' : undefined, '--mode', flags.mode === 'development' ? 'development' : 'production', '--env', 'flags=' + Buffer.from(JSON.stringify(flags), 'utf-8').toString('hex'), ].filter((v): v is string => !!v), { cwd, stdio: 'inherit', shell: true, env: process.env, } ) ); } catch (error) { console.error('Error during build:', error); process.exit(1); }