Scripts
Scripts let you run arbitrary Node.js code on the IDE host machine. They live in a scripts/ folder in your project, are copied into the extension output during unextension sync, and are called from the webview via runScript.
Scripts run with full Node.js access — filesystem, network, child processes, installed packages — with no sandbox.
Add scriptsDir to your unextension.config.ts:
import { defineConfig } from '@unextension/cli'
export default defineConfig({ name: 'my-extension', // ... scriptsDir: './dist/scripts', // compiled output, not source})Approach 1 — TypeScript with esbuild (recommended)
Section titled “Approach 1 — TypeScript with esbuild (recommended)”Write scripts in TypeScript and bundle them into self-contained files using esbuild. Each script is bundled with all its imports inlined, so no node_modules is needed at runtime inside the extension.
Project structure
Section titled “Project structure”my-extension/ scripts/ build.mjs ← esbuild bundler hello.ts ← your script source dist/ scripts/ hello.js ← bundled output (copied to extension)Install esbuild
Section titled “Install esbuild”pnpm add -D esbuild @types/nodescripts/build.mjs
Section titled “scripts/build.mjs”import { build } from 'esbuild'import { readdirSync } from 'node:fs'import { join, dirname } from 'node:path'import { fileURLToPath } from 'node:url'
const scriptsDir = dirname(fileURLToPath(import.meta.url))const outDir = join(scriptsDir, '../dist/scripts')
const entries = readdirSync(scriptsDir) .filter((f) => f.endsWith('.ts') && f !== 'build.mjs') .map((f) => join(scriptsDir, f))
if (entries.length > 0) { await build({ entryPoints: entries, outdir: outDir, bundle: true, // inline all imports — no node_modules needed at runtime platform: 'node', target: 'node18', format: 'cjs', external: [], })}package.json build script
Section titled “package.json build script”{ "scripts": { "build": "vite build && node scripts/build.mjs && unextension sync" }}Writing a script with createScript
Section titled “Writing a script with createScript”Use createScript from @unextension/bridge/script to handle the payload/result boilerplate:
import { createScript } from '@unextension/bridge/script'
createScript(async (payload: { name: string }) => { return { greeting: `Hello, ${payload.name}!`, nodeVersion: process.version, cwd: process.cwd(), }})Call it from the webview:
import { runScript } from '@unextension/bridge'
const result = await runScript('hello', { name: 'World' })console.log(result.result) // { greeting: 'Hello, World!', nodeVersion: 'v20.x.x', ... }Approach 2 — Plain JavaScript
Section titled “Approach 2 — Plain JavaScript”If you don’t need TypeScript, write scripts as plain .js files and point scriptsDir directly at your scripts folder:
export default defineConfig({ scriptsDir: './scripts',})Contract
Section titled “Contract”Scripts communicate via:
- Input —
process.env.UNEXTENSION_PAYLOAD— a JSON string of the payload passed torunScript - Output — write a JSON string to
process.stdout— this becomesresult.result - Errors — write to
process.stderrand/or exit with a non-zero code
const payload = JSON.parse(process.env.UNEXTENSION_PAYLOAD ?? 'null')
const result = { greeting: `Hello, ${payload?.name ?? 'World'}!`, nodeVersion: process.version,}
process.stdout.write(JSON.stringify(result))createScript without TypeScript
Section titled “createScript without TypeScript”You can also use createScript in plain JS:
const { createScript } = require('@unextension/bridge/script')
createScript(async (payload) => { return { greeting: `Hello, ${payload?.name ?? 'World'}!`, nodeVersion: process.version, }})createScript API
Section titled “createScript API”import { createScript } from '@unextension/bridge/script'
createScript<TPayload, TResult>( handler: (payload: TPayload) => TResult | Promise<TResult>): Promise<void>| Parameter | Type | Description |
|---|---|---|
handler | (payload: TPayload) => TResult | Promise<TResult> | Your script logic. Return any JSON-serializable value. |
createScript handles:
- Parsing
UNEXTENSION_PAYLOADfrom the environment - Calling your handler with the typed payload
- Writing the result as JSON to stdout
- Catching errors, writing to stderr, and exiting with code
1