204 lines
6.7 KiB
TypeScript
204 lines
6.7 KiB
TypeScript
import type * as types from "../shared/types"
|
|
import * as common from "../shared/common"
|
|
import * as ourselves from "./wasm"
|
|
|
|
interface Go {
|
|
_scheduledTimeouts: Map<number, ReturnType<typeof setTimeout>>
|
|
}
|
|
|
|
declare const ESBUILD_VERSION: string
|
|
declare let WEB_WORKER_SOURCE_CODE: string
|
|
declare let WEB_WORKER_FUNCTION: (postMessage: (data: Uint8Array) => void) => (event: { data: Uint8Array | ArrayBuffer | WebAssembly.Module }) => Go
|
|
|
|
export let version = ESBUILD_VERSION
|
|
|
|
export let build: typeof types.build = (options: types.BuildOptions) =>
|
|
ensureServiceIsRunning().then(service =>
|
|
service.build(options))
|
|
|
|
export const context: typeof types.context = (options: types.BuildOptions) =>
|
|
ensureServiceIsRunning().then(service =>
|
|
service.context(options))
|
|
|
|
export const transform: typeof types.transform = (input: string | Uint8Array, options?: types.TransformOptions) =>
|
|
ensureServiceIsRunning().then(service =>
|
|
service.transform(input, options))
|
|
|
|
export const formatMessages: typeof types.formatMessages = (messages, options) =>
|
|
ensureServiceIsRunning().then(service =>
|
|
service.formatMessages(messages, options))
|
|
|
|
export const analyzeMetafile: typeof types.analyzeMetafile = (metafile, options) =>
|
|
ensureServiceIsRunning().then(service =>
|
|
service.analyzeMetafile(metafile, options))
|
|
|
|
export const buildSync: typeof types.buildSync = () => {
|
|
throw new Error(`The "buildSync" API does not work in Deno`)
|
|
}
|
|
|
|
export const transformSync: typeof types.transformSync = () => {
|
|
throw new Error(`The "transformSync" API does not work in Deno`)
|
|
}
|
|
|
|
export const formatMessagesSync: typeof types.formatMessagesSync = () => {
|
|
throw new Error(`The "formatMessagesSync" API does not work in Deno`)
|
|
}
|
|
|
|
export const analyzeMetafileSync: typeof types.analyzeMetafileSync = () => {
|
|
throw new Error(`The "analyzeMetafileSync" API does not work in Deno`)
|
|
}
|
|
|
|
export const stop = () => {
|
|
if (stopService) stopService()
|
|
return Promise.resolve()
|
|
}
|
|
|
|
interface Service {
|
|
build: typeof types.build
|
|
context: typeof types.context
|
|
transform: typeof types.transform
|
|
formatMessages: typeof types.formatMessages
|
|
analyzeMetafile: typeof types.analyzeMetafile
|
|
}
|
|
|
|
let initializePromise: Promise<Service> | undefined
|
|
let stopService: (() => void) | undefined
|
|
|
|
let ensureServiceIsRunning = (): Promise<Service> => {
|
|
return initializePromise || startRunningService('esbuild.wasm', undefined, true)
|
|
}
|
|
|
|
export const initialize: typeof types.initialize = async (options) => {
|
|
options = common.validateInitializeOptions(options || {})
|
|
let wasmURL = options.wasmURL
|
|
let wasmModule = options.wasmModule
|
|
let useWorker = options.worker !== false
|
|
if (initializePromise) throw new Error('Cannot call "initialize" more than once')
|
|
initializePromise = startRunningService(wasmURL || 'esbuild.wasm', wasmModule, useWorker)
|
|
initializePromise.catch(() => {
|
|
// Let the caller try again if this fails
|
|
initializePromise = void 0
|
|
})
|
|
await initializePromise
|
|
}
|
|
|
|
const startRunningService = async (wasmURL: string | URL, wasmModule: WebAssembly.Module | undefined, useWorker: boolean): Promise<Service> => {
|
|
let worker: {
|
|
onmessage?: ((event: any) => void) | null | undefined
|
|
postMessage: (data: Uint8Array | ArrayBuffer | WebAssembly.Module) => void
|
|
terminate: () => void
|
|
}
|
|
|
|
if (useWorker) {
|
|
// Run esbuild off the main thread
|
|
let blob = new Blob([`onmessage=${WEB_WORKER_SOURCE_CODE}(postMessage)`], { type: 'text/javascript' })
|
|
worker = new Worker(URL.createObjectURL(blob), { type: 'module' })
|
|
} else {
|
|
// Run esbuild on the main thread
|
|
let onmessage = WEB_WORKER_FUNCTION((data: Uint8Array) => worker.onmessage!({ data }))
|
|
let go: Go | undefined
|
|
worker = {
|
|
onmessage: null,
|
|
postMessage: data => setTimeout(() => go = onmessage({ data })),
|
|
terminate() {
|
|
if (go)
|
|
for (let timeout of go._scheduledTimeouts.values())
|
|
clearTimeout(timeout)
|
|
},
|
|
}
|
|
}
|
|
|
|
let firstMessageResolve: (value: void) => void
|
|
let firstMessageReject: (error: any) => void
|
|
|
|
const firstMessagePromise = new Promise((resolve, reject) => {
|
|
firstMessageResolve = resolve
|
|
firstMessageReject = reject
|
|
})
|
|
|
|
worker.onmessage = ({ data: error }) => {
|
|
worker.onmessage = ({ data }) => readFromStdout(data)
|
|
if (error) firstMessageReject(error)
|
|
else firstMessageResolve()
|
|
}
|
|
|
|
worker.postMessage(wasmModule || new URL(wasmURL, import.meta.url).toString())
|
|
|
|
let { readFromStdout, service } = common.createChannel({
|
|
writeToStdin(bytes) {
|
|
worker.postMessage(bytes)
|
|
},
|
|
isSync: false,
|
|
hasFS: false,
|
|
esbuild: ourselves,
|
|
})
|
|
|
|
// This will throw if WebAssembly module instantiation fails
|
|
await firstMessagePromise
|
|
|
|
stopService = () => {
|
|
worker.terminate()
|
|
initializePromise = undefined
|
|
stopService = undefined
|
|
}
|
|
|
|
return {
|
|
build: (options: types.BuildOptions) =>
|
|
new Promise<types.BuildResult>((resolve, reject) =>
|
|
service.buildOrContext({
|
|
callName: 'build',
|
|
refs: null,
|
|
options,
|
|
isTTY: false,
|
|
defaultWD: '/',
|
|
callback: (err, res) => err ? reject(err) : resolve(res as types.BuildResult),
|
|
})),
|
|
|
|
context: (options: types.BuildOptions) =>
|
|
new Promise<types.BuildContext>((resolve, reject) =>
|
|
service.buildOrContext({
|
|
callName: 'context',
|
|
refs: null,
|
|
options,
|
|
isTTY: false,
|
|
defaultWD: '/',
|
|
callback: (err, res) => err ? reject(err) : resolve(res as types.BuildContext),
|
|
})),
|
|
|
|
transform: (input: string | Uint8Array, options?: types.TransformOptions) =>
|
|
new Promise<types.TransformResult>((resolve, reject) =>
|
|
service.transform({
|
|
callName: 'transform',
|
|
refs: null,
|
|
input,
|
|
options: options || {},
|
|
isTTY: false,
|
|
fs: {
|
|
readFile(_, callback) { callback(new Error('Internal error'), null); },
|
|
writeFile(_, callback) { callback(null); },
|
|
},
|
|
callback: (err, res) => err ? reject(err) : resolve(res!),
|
|
})),
|
|
|
|
formatMessages: (messages, options) =>
|
|
new Promise((resolve, reject) =>
|
|
service.formatMessages({
|
|
callName: 'formatMessages',
|
|
refs: null,
|
|
messages,
|
|
options,
|
|
callback: (err, res) => err ? reject(err) : resolve(res!),
|
|
})),
|
|
|
|
analyzeMetafile: (metafile, options) =>
|
|
new Promise((resolve, reject) =>
|
|
service.analyzeMetafile({
|
|
callName: 'analyzeMetafile',
|
|
refs: null,
|
|
metafile: typeof metafile === 'string' ? metafile : JSON.stringify(metafile),
|
|
options,
|
|
callback: (err, res) => err ? reject(err) : resolve(res!),
|
|
})),
|
|
}
|
|
}
|