Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
274 changes: 177 additions & 97 deletions src/esbuild.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,74 @@ import * as console from "node:console"

import { copyPaths, copyWasms, copyLocales, setupLocaleWatcher } from "@roo-code/build"

// Lock file to prevent concurrent execution
const LOCK_FILE = path.join(process.cwd(), '.esbuild.lock')
const MAX_WAIT_TIME = 30000 // 30 seconds
const POLL_INTERVAL = 100 // 100ms

async function acquireLock() {
const startTime = Date.now()

while (Date.now() - startTime < MAX_WAIT_TIME) {
try {
// Try to create lock file exclusively
fs.writeFileSync(LOCK_FILE, process.pid.toString(), { flag: 'wx' })
return true
} catch (error) {
if (error.code === 'EEXIST') {
// Lock file exists, check if the process is still running
try {
const lockPid = fs.readFileSync(LOCK_FILE, 'utf8').trim()
const pid = parseInt(lockPid, 10)

// Check if process is still running
try {
process.kill(pid, 0) // Signal 0 checks if process exists
// Process is still running, wait
await new Promise(resolve => setTimeout(resolve, POLL_INTERVAL))
continue
} catch (killError) {
// Process is not running, remove stale lock
fs.unlinkSync(LOCK_FILE)
continue
}
} catch (readError) {
// Can't read lock file, try to remove it
try {
fs.unlinkSync(LOCK_FILE)
} catch (unlinkError) {
// Ignore unlink errors
}
continue
}
} else {
throw error
}
}
}

throw new Error(`Failed to acquire lock after ${MAX_WAIT_TIME}ms`)
}

function releaseLock() {
try {
fs.unlinkSync(LOCK_FILE)
} catch (error) {
// Ignore errors when releasing lock
}
}

// Ensure lock is released on process exit
process.on('exit', releaseLock)
process.on('SIGINT', () => {
releaseLock()
process.exit(0)
})
process.on('SIGTERM', () => {
releaseLock()
process.exit(0)
})

const __filename = fileURLToPath(import.meta.url)
const __dirname = path.dirname(__filename)

Expand All @@ -17,117 +85,129 @@ async function main() {
const minify = production
const sourcemap = !production

/**
* @type {import('esbuild').BuildOptions}
*/
const buildOptions = {
bundle: true,
minify,
sourcemap,
logLevel: "silent",
format: "cjs",
sourcesContent: false,
platform: "node",
}
// Acquire lock to prevent concurrent execution
console.log(`[${name}] Acquiring build lock...`)
await acquireLock()
console.log(`[${name}] Build lock acquired`)

const srcDir = __dirname
const buildDir = __dirname
const distDir = path.join(buildDir, "dist")
try {
/**
* @type {import('esbuild').BuildOptions}
*/
const buildOptions = {
bundle: true,
minify,
sourcemap,
logLevel: "silent",
format: "cjs",
sourcesContent: false,
platform: "node",
}

if (fs.existsSync(distDir)) {
console.log(`[${name}] Cleaning dist directory: ${distDir}`)
fs.rmSync(distDir, { recursive: true, force: true })
}
const srcDir = __dirname
const buildDir = __dirname
const distDir = path.join(buildDir, "dist")

if (fs.existsSync(distDir)) {
console.log(`[${name}] Cleaning dist directory: ${distDir}`)
fs.rmSync(distDir, { recursive: true, force: true })
}

/**
* @type {import('esbuild').Plugin[]}
*/
const plugins = [
{
name: "copyFiles",
setup(build) {
build.onEnd(() => {
copyPaths(
[
["../README.md", "README.md"],
["../CHANGELOG.md", "CHANGELOG.md"],
["../LICENSE", "LICENSE"],
["../.env", ".env", { optional: true }],
["node_modules/vscode-material-icons/generated", "assets/vscode-material-icons"],
["../webview-ui/audio", "webview-ui/audio"],
],
srcDir,
buildDir,
)
})
/**
* @type {import('esbuild').Plugin[]}
*/
const plugins = [
{
name: "copyFiles",
setup(build) {
build.onEnd(() => {
copyPaths(
[
["../README.md", "README.md"],
["../CHANGELOG.md", "CHANGELOG.md"],
["../LICENSE", "LICENSE"],
["../.env", ".env", { optional: true }],
["node_modules/vscode-material-icons/generated", "assets/vscode-material-icons"],
["../webview-ui/audio", "webview-ui/audio"],
],
srcDir,
buildDir,
)
})
},
},
},
{
name: "copyWasms",
setup(build) {
build.onEnd(() => copyWasms(srcDir, distDir))
{
name: "copyWasms",
setup(build) {
build.onEnd(() => copyWasms(srcDir, distDir))
},
},
},
{
name: "copyLocales",
setup(build) {
build.onEnd(() => copyLocales(srcDir, distDir))
{
name: "copyLocales",
setup(build) {
build.onEnd(() => copyLocales(srcDir, distDir))
},
},
},
{
name: "esbuild-problem-matcher",
setup(build) {
build.onStart(() => console.log("[esbuild-problem-matcher#onStart]"))
build.onEnd((result) => {
result.errors.forEach(({ text, location }) => {
console.error(`✘ [ERROR] ${text}`)
if (location && location.file) {
console.error(` ${location.file}:${location.line}:${location.column}:`)
}
})
{
name: "esbuild-problem-matcher",
setup(build) {
build.onStart(() => console.log("[esbuild-problem-matcher#onStart]"))
build.onEnd((result) => {
result.errors.forEach(({ text, location }) => {
console.error(`✘ [ERROR] ${text}`)
if (location && location.file) {
console.error(` ${location.file}:${location.line}:${location.column}:`)
}
})

console.log("[esbuild-problem-matcher#onEnd]")
})
console.log("[esbuild-problem-matcher#onEnd]")
})
},
},
},
]

/**
* @type {import('esbuild').BuildOptions}
*/
const extensionConfig = {
...buildOptions,
plugins,
entryPoints: ["extension.ts"],
outfile: "dist/extension.js",
external: ["vscode"],
}
]

/**
* @type {import('esbuild').BuildOptions}
*/
const workerConfig = {
...buildOptions,
entryPoints: ["workers/countTokens.ts"],
outdir: "dist/workers",
}
/**
* @type {import('esbuild').BuildOptions}
*/
const extensionConfig = {
...buildOptions,
plugins,
entryPoints: ["extension.ts"],
outfile: "dist/extension.js",
external: ["vscode"],
}

/**
* @type {import('esbuild').BuildOptions}
*/
const workerConfig = {
...buildOptions,
entryPoints: ["workers/countTokens.ts"],
outdir: "dist/workers",
}

const [extensionCtx, workerCtx] = await Promise.all([
esbuild.context(extensionConfig),
esbuild.context(workerConfig),
])

const [extensionCtx, workerCtx] = await Promise.all([
esbuild.context(extensionConfig),
esbuild.context(workerConfig),
])

if (watch) {
await Promise.all([extensionCtx.watch(), workerCtx.watch()])
copyLocales(srcDir, distDir)
setupLocaleWatcher(srcDir, distDir)
} else {
await Promise.all([extensionCtx.rebuild(), workerCtx.rebuild()])
await Promise.all([extensionCtx.dispose(), workerCtx.dispose()])
if (watch) {
await Promise.all([extensionCtx.watch(), workerCtx.watch()])
copyLocales(srcDir, distDir)
setupLocaleWatcher(srcDir, distDir)
} else {
await Promise.all([extensionCtx.rebuild(), workerCtx.rebuild()])
await Promise.all([extensionCtx.dispose(), workerCtx.dispose()])
}
} finally {
// Always release the lock
releaseLock()
console.log(`[${name}] Build lock released`)
}
}

main().catch((e) => {
console.error(e)
releaseLock()
process.exit(1)
})
Loading