const { join, dirname, basename } = require("path") const { readFileSync, existsSync, mkdirSync, createWriteStream, readdirSync, lstatSync, rmSync } = require('fs') const { spawn } = require('child_process') const esbuild = require('esbuild') const { copy } = require('esbuild-plugin-copy') async function execProcess(cmd, logFile, args, buildDir= '.build') { let compileRoot = dirname(dirname(process.argv[1])) console.log('Running from',) console.log("Compiling...\n", process.cwd(), args) if (!existsSync(join(process.cwd(), buildDir))) { mkdirSync(join(process.cwd(), buildDir)) } const compileOut = spawn(cmd, args) const stdoutFilePath = `${buildDir}/${logFile}.log` const stderrFilePath = `${buildDir}/${logFile}-err.log` const outPromise = new Promise((resolve) => { if (compileOut.stdout != null) { let outPipe = createWriteStream(stdoutFilePath) compileOut.stdout.pipe(outPipe) compileOut.stdout.on('end', function (data) { outPipe.close() resolve() }) } else { resolve() } }) const errPromise = new Promise((resolve) => { if (compileOut.stderr != null) { let outPipe = createWriteStream(stderrFilePath) compileOut.stderr.pipe(outPipe) compileOut.stderr.on('end', function (data) { outPipe.close() resolve() }) } else { resolve() } }) let editCode = 0 const closePromise = new Promise(resolve => { compileOut.on('close', (code) => { editCode = code resolve() }) compileOut.on('error', (err) => { console.error(err) resolve() }) }) await Promise.all([outPromise, errPromise, closePromise]) if (editCode !== 0) { const data = readFileSync(stdoutFilePath) const errData = readFileSync(stderrFilePath) console.error('\n' + data.toString() + '\n' + errData.toString()) process.exit(editCode) } } let args = process.argv.splice(2) function collectFiles(source) { const result = [] const files = readdirSync(source) for (const f of files) { const sourceFile = join(source, f) if (lstatSync(sourceFile).isDirectory()) { result.push(...collectFiles(sourceFile)) } else { let ext = basename(sourceFile) if (!ext.endsWith('.ts') && !ext.endsWith('.js') && !ext.endsWith('.svelte')) { continue } result.push(sourceFile) } } return result } switch (args[0]) { case 'ui': { console.log('Nothing to compile to UI') break } case 'transpile': { const filesToTranspile = collectFiles(join(process.cwd(), args[1])) let st = Date.now() performESBuild(filesToTranspile) .then(() => { console.log("Transpile time: ", Date.now() - st) }) break } case 'validate': { let st = Date.now() validateTSC(st).then(() => { console.log("Validate time: ", Date.now() - st) }) break } default: { let st = Date.now() const filesToTranspile = collectFiles(join(process.cwd(), 'src')) Promise.all( [ performESBuild(filesToTranspile), validateTSC() ] ) .then(() => { console.log("Full build time: ", Date.now() - st) }) break } } async function performESBuild(filesToTranspile) { await esbuild.build({ entryPoints: filesToTranspile, bundle: false, minify: false, outdir: 'lib', keepNames: true, sourcemap: 'external', allowOverwrite: true, format: 'cjs', plugins: [ copy({ // this is equal to process.cwd(), which means we use cwd path as base path to resolve `to` path // if not specified, this plugin uses ESBuild.build outdir/outfile options as base path. resolveFrom: 'cwd', assets: { from: [args[1] + '/**/*.json'], to: ['./lib'], }, watch: true, }) ] }) } async function validateTSC(st) { if (existsSync(join(process.cwd(), 'types'))) { rmSync(join(process.cwd(), 'types'), { recursive: true }) } if (existsSync(join(process.cwd(), '.validate'))) { rmSync(join(process.cwd(), '.validate'), { recursive: true }) } await execProcess( 'tsc', 'validate', [ '-pretty', "--emitDeclarationOnly", "--incremental", "--tsBuildInfoFile", ".validate/tsBuildInfoFile.info", "--declarationDir", "types", ...args.splice(1) ], '.validate') }