|
| 1 | +import * as types from "../shared/types" |
| 2 | +import * as common from "../shared/common" |
| 3 | +import * as ourselves from "./wasm" |
| 4 | + |
| 5 | +declare const ESBUILD_VERSION: string; |
| 6 | +declare let WEB_WORKER_SOURCE_CODE: string |
| 7 | +declare let WEB_WORKER_FUNCTION: (postMessage: (data: Uint8Array) => void) => (event: { data: Uint8Array | ArrayBuffer | WebAssembly.Module }) => void |
| 8 | + |
| 9 | +export let version = ESBUILD_VERSION |
| 10 | + |
| 11 | +export let build: typeof types.build = (options: types.BuildOptions): Promise<any> => |
| 12 | + ensureServiceIsRunning().then(service => |
| 13 | + service.build(options)) |
| 14 | + |
| 15 | +export const serve: typeof types.serve = () => { |
| 16 | + throw new Error(`The "serve" API does not work in Deno via WebAssembly`) |
| 17 | +} |
| 18 | + |
| 19 | +export const transform: typeof types.transform = (input, options) => |
| 20 | + ensureServiceIsRunning().then(service => |
| 21 | + service.transform(input, options)) |
| 22 | + |
| 23 | +export const formatMessages: typeof types.formatMessages = (messages, options) => |
| 24 | + ensureServiceIsRunning().then(service => |
| 25 | + service.formatMessages(messages, options)) |
| 26 | + |
| 27 | +export const analyzeMetafile: typeof types.analyzeMetafile = (metafile, options) => |
| 28 | + ensureServiceIsRunning().then(service => |
| 29 | + service.analyzeMetafile(metafile, options)) |
| 30 | + |
| 31 | +export const buildSync: typeof types.buildSync = () => { |
| 32 | + throw new Error(`The "buildSync" API does not work in Deno`) |
| 33 | +} |
| 34 | + |
| 35 | +export const transformSync: typeof types.transformSync = () => { |
| 36 | + throw new Error(`The "transformSync" API does not work in Deno`) |
| 37 | +} |
| 38 | + |
| 39 | +export const formatMessagesSync: typeof types.formatMessagesSync = () => { |
| 40 | + throw new Error(`The "formatMessagesSync" API does not work in Deno`) |
| 41 | +} |
| 42 | + |
| 43 | +export const analyzeMetafileSync: typeof types.analyzeMetafileSync = () => { |
| 44 | + throw new Error(`The "analyzeMetafileSync" API does not work in Deno`) |
| 45 | +} |
| 46 | + |
| 47 | +export const stop = () => { |
| 48 | + if (stopService) stopService() |
| 49 | +} |
| 50 | + |
| 51 | +interface Service { |
| 52 | + build: typeof types.build |
| 53 | + transform: typeof types.transform |
| 54 | + formatMessages: typeof types.formatMessages |
| 55 | + analyzeMetafile: typeof types.analyzeMetafile |
| 56 | +} |
| 57 | + |
| 58 | +let initializePromise: Promise<Service> | undefined; |
| 59 | +let stopService: (() => void) | undefined |
| 60 | + |
| 61 | +let ensureServiceIsRunning = (): Promise<Service> => { |
| 62 | + return initializePromise || startRunningService('', undefined, true) |
| 63 | +} |
| 64 | + |
| 65 | +export const initialize: typeof types.initialize = async (options) => { |
| 66 | + options = common.validateInitializeOptions(options || {}) |
| 67 | + let wasmURL = options.wasmURL; |
| 68 | + let wasmModule = options.wasmModule; |
| 69 | + let useWorker = options.worker !== false; |
| 70 | + if (initializePromise) throw new Error('Cannot call "initialize" more than once'); |
| 71 | + initializePromise = startRunningService(wasmURL || '', wasmModule, useWorker); |
| 72 | + initializePromise.catch(() => { |
| 73 | + // Let the caller try again if this fails |
| 74 | + initializePromise = void 0; |
| 75 | + }); |
| 76 | + await initializePromise; |
| 77 | +} |
| 78 | + |
| 79 | +const startRunningService = async (wasmURL: string, wasmModule: WebAssembly.Module | undefined, useWorker: boolean): Promise<Service> => { |
| 80 | + let wasm: WebAssembly.Module; |
| 81 | + if (wasmModule) { |
| 82 | + wasm = wasmModule; |
| 83 | + } else { |
| 84 | + if (!wasmURL) wasmURL = new URL('esbuild.wasm', import.meta.url).href |
| 85 | + wasm = await WebAssembly.compileStreaming(fetch(wasmURL)) |
| 86 | + } |
| 87 | + |
| 88 | + let worker: { |
| 89 | + onmessage: ((event: any) => void) | null |
| 90 | + postMessage: (data: Uint8Array | ArrayBuffer | WebAssembly.Module) => void |
| 91 | + terminate: () => void |
| 92 | + } |
| 93 | + |
| 94 | + if (useWorker) { |
| 95 | + // Run esbuild off the main thread |
| 96 | + let blob = new Blob([`onmessage=${WEB_WORKER_SOURCE_CODE}(postMessage)`], { type: 'text/javascript' }) |
| 97 | + worker = new Worker(URL.createObjectURL(blob), { type: 'module' }) |
| 98 | + } else { |
| 99 | + // Run esbuild on the main thread |
| 100 | + let onmessage = WEB_WORKER_FUNCTION((data: Uint8Array) => worker.onmessage!({ data })) |
| 101 | + worker = { |
| 102 | + onmessage: null, |
| 103 | + postMessage: data => setTimeout(() => onmessage({ data })), |
| 104 | + terminate() { |
| 105 | + }, |
| 106 | + } |
| 107 | + } |
| 108 | + |
| 109 | + worker.postMessage(wasm) |
| 110 | + worker.onmessage = ({ data }) => readFromStdout(data) |
| 111 | + |
| 112 | + let { readFromStdout, service } = common.createChannel({ |
| 113 | + writeToStdin(bytes) { |
| 114 | + worker.postMessage(bytes) |
| 115 | + }, |
| 116 | + isSync: false, |
| 117 | + isWriteUnavailable: true, |
| 118 | + esbuild: ourselves, |
| 119 | + }) |
| 120 | + |
| 121 | + stopService = () => { |
| 122 | + worker.terminate() |
| 123 | + initializePromise = undefined |
| 124 | + stopService = undefined |
| 125 | + } |
| 126 | + |
| 127 | + return { |
| 128 | + build: (options: types.BuildOptions): Promise<any> => |
| 129 | + new Promise<types.BuildResult>((resolve, reject) => |
| 130 | + service.buildOrServe({ |
| 131 | + callName: 'build', |
| 132 | + refs: null, |
| 133 | + serveOptions: null, |
| 134 | + options, |
| 135 | + isTTY: false, |
| 136 | + defaultWD: '/', |
| 137 | + callback: (err, res) => err ? reject(err) : resolve(res as types.BuildResult), |
| 138 | + })), |
| 139 | + transform: (input, options) => |
| 140 | + new Promise((resolve, reject) => |
| 141 | + service.transform({ |
| 142 | + callName: 'transform', |
| 143 | + refs: null, |
| 144 | + input, |
| 145 | + options: options || {}, |
| 146 | + isTTY: false, |
| 147 | + fs: { |
| 148 | + readFile(_, callback) { callback(new Error('Internal error'), null); }, |
| 149 | + writeFile(_, callback) { callback(null); }, |
| 150 | + }, |
| 151 | + callback: (err, res) => err ? reject(err) : resolve(res!), |
| 152 | + })), |
| 153 | + formatMessages: (messages, options) => |
| 154 | + new Promise((resolve, reject) => |
| 155 | + service.formatMessages({ |
| 156 | + callName: 'formatMessages', |
| 157 | + refs: null, |
| 158 | + messages, |
| 159 | + options, |
| 160 | + callback: (err, res) => err ? reject(err) : resolve(res!), |
| 161 | + })), |
| 162 | + analyzeMetafile: (metafile, options) => |
| 163 | + new Promise((resolve, reject) => |
| 164 | + service.analyzeMetafile({ |
| 165 | + callName: 'analyzeMetafile', |
| 166 | + refs: null, |
| 167 | + metafile: typeof metafile === 'string' ? metafile : JSON.stringify(metafile), |
| 168 | + options, |
| 169 | + callback: (err, res) => err ? reject(err) : resolve(res!), |
| 170 | + })), |
| 171 | + } |
| 172 | +} |
0 commit comments