igloo-code-compiler
v0.1.2
Published
Browser-side build SDK — compile frontend projects via a hosted iframe
Downloads
404
Maintainers
Readme
igloo-code-compiler
Browser-side frontend build SDK. Wraps the code-compiler service into a clean TypeScript API — no postMessage boilerplate required.
How it works
Your app
└─ CodeCompiler SDK
└─ Hidden iframe ←postMessage→ code-compiler service
└─ WebContainer (Node.js in Browser)
└─ npm install + npm run buildThe SDK creates an invisible <iframe> pointing to your deployed code-compiler instance. All postMessage communication (COMPILE_READY / COMPILE_REQUEST / COMPILE_SUCCESS, etc.) is handled automatically — you just call compile().
Installation
npm install igloo-code-compiler
# or
pnpm add igloo-code-compilerQuick start
import { CodeCompiler } from 'igloo-code-compiler'
const compiler = new CodeCompiler({
endpoint: 'https://compiler.example.com',
})
// Stream build logs
compiler.on('progress', (msg) => console.log('[Build]', msg))
// Compile and get output files
const outputFiles = await compiler.compile({
files: {
'package.json': JSON.stringify({
name: 'my-app',
scripts: { build: 'vite build' },
dependencies: { react: '^18.0.0', 'react-dom': '^18.0.0' },
devDependencies: { vite: '^5.0.0', '@vitejs/plugin-react': '^4.0.0' },
}),
'vite.config.js': `
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
export default defineConfig({ plugins: [react()] })
`,
'index.html': `<!doctype html><html><body>
<div id="root"></div>
<script type="module" src="/src/main.jsx"></script>
</body></html>`,
'src/main.jsx': `
import React from 'react'
import ReactDOM from 'react-dom/client'
ReactDOM.createRoot(document.getElementById('root')).render(<h1>Hello</h1>)
`,
},
})
// outputFiles: { 'index.html': '...', 'assets/index.js': '...', ... }
console.log('Build output:', Object.keys(outputFiles))
compiler.destroy()API
new CodeCompiler(options)
| Option | Type | Required | Description |
| -------------- | ------------------------------ | -------- | --------------------------------------------------------------------------------- |
| endpoint | string | ✅ | URL of the deployed code-compiler service, e.g. 'https://compiler.example.com' |
| container | HTMLElement | — | Mount the iframe into this element (default: hidden, appended to document.body) |
| iframeStyle | Partial<CSSStyleDeclaration> | — | Custom iframe CSS (only applies when container is not set) |
| readyTimeout | number | — | Timeout (ms) waiting for the compiler to become ready; default 180000 |
.compile(options): Promise<OutputFiles>
Triggers one compilation and returns a file map of the build output ({ [relativePath]: content }).
| Option | Type | Description |
| -------------------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------- |
| files | FileMap | Flat file map (mutually exclusive with zip). Keys are relative paths, values are file content strings. Must include package.json. |
| zip | string | Base64-encoded zip (mutually exclusive with files). Accepts with or without a data: URL prefix. |
| buildCommand | string | Build command; default 'npm run build' |
| installCommand | string | Install command; default: auto-detected from lock file |
| skipTsc | boolean | Strip tsc/vue-tsc from the build script; default true |
| timeout | number | Compilation timeout (ms); default 600000 (10 minutes) |
| autoUpload | boolean | Auto-publish to the server after a successful build; default false |
| publishProjectName | string | Project name for publishing (required when autoUpload is true) |
| publishAuthToken | string | Bearer token for the publish API (optional) |
When both files and zip are provided, zip takes priority.
.ready(): Promise<void>
Waits for the compiler iframe to initialize. compile() already calls this internally — you rarely need to call it manually.
.upload(options): Promise<UploadResult>
Uploads the output from the previous compilation without re-compiling. Must be called after a successful compile().
| Option | Type | Description |
| -------------------- | -------- | ------------------------------------ |
| publishProjectName | string | Project name (required) |
| publishAuthToken | string | Bearer token (optional) |
| timeout | number | Upload timeout (ms); default 60000 |
.on(event, listener) / .off(event, listener) / .once(event, listener)
| Event | Argument | Description |
| ------------------- | ---------------------- | ------------------------ |
| 'ready' | — | Compiler is ready |
| 'progress' | message: string | Real-time build log line |
| 'success' | files: OutputFiles | Build succeeded |
| 'error' | error: Error | Build failed |
| 'upload-progress' | message: string | Upload progress |
| 'upload-success' | result: UploadResult | Upload succeeded |
| 'upload-error' | error: Error | Upload failed |
.destroy()
Destroys the instance: removes the iframe and stops listening for messages. Any in-progress compile() call rejects immediately.
Properties
| Property | Type | Description |
| -------------- | --------- | ------------------------------------------ |
| .ready_state | boolean | Whether COMPILE_READY has been received |
| .compiling | boolean | Whether a compilation is currently running |
| .uploading | boolean | Whether an upload is currently running |
Publishing build output
Two ways to publish after a successful build:
Option 1 — Auto-publish (autoUpload)
const files = await compiler.compile({
files: { ... },
autoUpload: true,
publishProjectName: 'my-app',
publishAuthToken: 'Bearer xxx', // optional
})
compiler.on('upload-progress', (msg) => console.log('[Upload]', msg))
compiler.on('upload-success', (result) => {
console.log('Live URL:', result.url) // e.g. 'my-app.example.com'
console.log('HTML file:', result.s3Url)
})
compiler.on('upload-error', (err) => console.error('Upload failed:', err.message))Option 2 — Manual upload (upload())
Use this when you want to inspect the output first, or let the user confirm before publishing.
import { CodeCompiler } from 'igloo-code-compiler'
const compiler = new CodeCompiler({ endpoint: 'https://compiler.example.com' })
// Step 1: compile
const files = await compiler.compile({ files: { ... } })
console.log('Build done, files:', Object.keys(files))
// Step 2: upload (reuses the same build output — no re-compilation)
compiler.on('upload-progress', (msg) => console.log('[Upload]', msg))
const result = await compiler.upload({ publishProjectName: 'my-app' })
console.log('Live URL:', result.url)
compiler.destroy()Upload result
| Field | Type | Description |
| ------------- | -------- | ---------------------------------------------------- |
| projectName | string | Published project name |
| url | string | Accessible project domain, e.g. my-app.example.com |
| s3Url | string | Direct S3/CDN link to the HTML file |
Passing a ZIP archive
When the file count is large, or you already have a zip on hand:
// Fetch a zip from an API
const response = await fetch('/api/project/my-app.zip')
const buffer = await response.arrayBuffer()
const bytes = new Uint8Array(buffer)
let binary = ''
bytes.forEach(b => (binary += String.fromCharCode(b)))
const base64 = btoa(binary)
const files = await compiler.compile({ zip: base64 })Error handling
import { CodeCompiler, CompileError, CompileTimeoutError } from 'igloo-code-compiler'
try {
const files = await compiler.compile({ files: { ... } })
} catch (err) {
if (err instanceof CompileTimeoutError) {
console.error('Build timed out')
} else if (err instanceof CompileError) {
console.error('Build failed:', err.message)
} else {
throw err
}
}React hook example
import { useEffect, useRef, useState } from 'react'
import { CodeCompiler, type OutputFiles } from 'igloo-code-compiler'
export function useCompiler(endpoint: string) {
const compilerRef = useRef<CodeCompiler | null>(null)
const [logs, setLogs] = useState<string[]>([])
useEffect(() => {
const compiler = new CodeCompiler({ endpoint })
compiler.on('progress', (msg) => setLogs((prev) => [...prev, msg]))
compilerRef.current = compiler
return () => compiler.destroy()
}, [endpoint])
const compile = async (files: Record<string, string>): Promise<OutputFiles> => {
if (!compilerRef.current) throw new Error('Compiler not initialized')
return compilerRef.current.compile({ files })
}
return { compile, logs }
}Notes
- Browser-only: The SDK depends on
window,document, andpostMessage— it does not support SSR or Node.js environments. - One compilation at a time: A single instance can only run one
compile()call concurrently. Create multiple instances for parallel builds. - COOP/COEP headers: The code-compiler service must serve with
Cross-Origin-Opener-PolicyandCross-Origin-Embedder-Policyheaders. The service handles this automatically via its built-in Service Worker — no configuration required on your parent page. - First build is slower: The WebContainer runtime (~10 MB) is downloaded from CDN on first use. Call
new CodeCompiler(...)orcompiler.ready()early to warm up the instance.
