261 lines
10 KiB
JavaScript
261 lines
10 KiB
JavaScript
const decoder = new TextDecoder();
|
|
const encoder = new TextEncoder();
|
|
/**
|
|
* Creates host for host formatting.
|
|
*/
|
|
export function createHost() {
|
|
let instance;
|
|
let hostFormatter = (_filePath, fileText, _overrideConfig) => fileText;
|
|
let overrideConfig = {};
|
|
let filePath = "";
|
|
let formattedText = "";
|
|
let errorText = "";
|
|
return {
|
|
setInstance(wasmInstance) {
|
|
instance = wasmInstance;
|
|
},
|
|
setHostFormatter(formatWithHost) {
|
|
hostFormatter = formatWithHost;
|
|
},
|
|
createImportObject() {
|
|
let sharedBuffer = new Uint8Array(0);
|
|
let sharedBufferIndex = 0;
|
|
const resetSharedBuffer = (length) => {
|
|
sharedBuffer = new Uint8Array(length);
|
|
sharedBufferIndex = 0;
|
|
};
|
|
return {
|
|
dprint: {
|
|
"host_clear_bytes": (length) => {
|
|
resetSharedBuffer(length);
|
|
},
|
|
"host_read_buffer": (pointer, length) => {
|
|
sharedBuffer.set(getWasmBufferAtPointer(instance, pointer, length), sharedBufferIndex);
|
|
sharedBufferIndex += length;
|
|
},
|
|
"host_write_buffer": (pointer, index, length) => {
|
|
getWasmBufferAtPointer(instance, pointer, length).set(sharedBuffer.slice(index, index + length));
|
|
},
|
|
"host_take_file_path": () => {
|
|
filePath = decoder.decode(sharedBuffer);
|
|
resetSharedBuffer(0);
|
|
},
|
|
"host_take_override_config": () => {
|
|
overrideConfig = JSON.parse(decoder.decode(sharedBuffer));
|
|
resetSharedBuffer(0);
|
|
},
|
|
"host_format": () => {
|
|
const fileText = decoder.decode(sharedBuffer);
|
|
try {
|
|
formattedText = hostFormatter(filePath, fileText, overrideConfig);
|
|
return fileText === formattedText ? 0 : 1;
|
|
}
|
|
catch (error) {
|
|
errorText = String(error);
|
|
return 2;
|
|
}
|
|
},
|
|
"host_get_formatted_text": () => {
|
|
sharedBuffer = encoder.encode(formattedText);
|
|
sharedBufferIndex = 0;
|
|
return sharedBuffer.length;
|
|
},
|
|
"host_get_error_text": () => {
|
|
sharedBuffer = encoder.encode(errorText);
|
|
sharedBufferIndex = 0;
|
|
return sharedBuffer.length;
|
|
},
|
|
},
|
|
};
|
|
},
|
|
};
|
|
}
|
|
/**
|
|
* Creates a formatter from the specified streaming source.
|
|
* @remarks This is the most efficient way to create a formatter.
|
|
* @param response - The streaming source to create the formatter from.
|
|
*/
|
|
export async function createStreaming(responsePromise) {
|
|
const response = await responsePromise;
|
|
if (response.status !== 200) {
|
|
throw new Error(`Unexpected status code: ${response.status}\n${await response.text()}`);
|
|
}
|
|
if (typeof WebAssembly.instantiateStreaming === "function"
|
|
&& response.headers.get("content-type") === "application/wasm") {
|
|
const host = createHost();
|
|
const { instance } = await WebAssembly
|
|
// deno-lint-ignore no-explicit-any
|
|
.instantiateStreaming(response, host.createImportObject());
|
|
return createFromInstance(instance, host);
|
|
}
|
|
else {
|
|
// fallback for node.js or when the content type isn't application/wasm
|
|
return response.arrayBuffer()
|
|
.then((buffer) => createFromBuffer(buffer));
|
|
}
|
|
}
|
|
/**
|
|
* Creates a formatter from the specified wasm module bytes.
|
|
* @param wasmModuleBuffer - The buffer of the wasm module.
|
|
*/
|
|
export function createFromBuffer(wasmModuleBuffer) {
|
|
const host = createHost();
|
|
const wasmModule = new WebAssembly.Module(wasmModuleBuffer);
|
|
const wasmInstance = new WebAssembly.Instance(wasmModule, host.createImportObject());
|
|
return createFromInstance(wasmInstance, host);
|
|
}
|
|
/**
|
|
* Creates a formatter from the specified wasm instance.
|
|
* @param wasmInstance - The WebAssembly instance.
|
|
* @param host- Formatting host.
|
|
*/
|
|
export function createFromInstance(wasmInstance, host) {
|
|
host.setInstance(wasmInstance);
|
|
// deno-lint-ignore no-explicit-any
|
|
const wasmExports = wasmInstance.exports;
|
|
const {
|
|
// deno-lint-ignore camelcase
|
|
get_plugin_schema_version,
|
|
// deno-lint-ignore camelcase
|
|
set_file_path,
|
|
// deno-lint-ignore camelcase
|
|
set_override_config,
|
|
// deno-lint-ignore camelcase
|
|
get_formatted_text, format,
|
|
// deno-lint-ignore camelcase
|
|
get_error_text,
|
|
// deno-lint-ignore camelcase
|
|
get_plugin_info,
|
|
// deno-lint-ignore camelcase
|
|
get_resolved_config,
|
|
// deno-lint-ignore camelcase
|
|
get_config_diagnostics,
|
|
// deno-lint-ignore camelcase
|
|
set_global_config,
|
|
// deno-lint-ignore camelcase
|
|
set_plugin_config,
|
|
// deno-lint-ignore camelcase
|
|
get_license_text,
|
|
// deno-lint-ignore camelcase
|
|
reset_config, } = wasmExports;
|
|
const pluginSchemaVersion = get_plugin_schema_version();
|
|
const expectedPluginSchemaVersion = 3;
|
|
if (pluginSchemaVersion !== 2
|
|
&& pluginSchemaVersion !== expectedPluginSchemaVersion) {
|
|
throw new Error(`Not compatible plugin. `
|
|
+ `Expected schema ${expectedPluginSchemaVersion}, `
|
|
+ `but plugin had ${pluginSchemaVersion}.`);
|
|
}
|
|
let configSet = false;
|
|
return {
|
|
setConfig(globalConfig, pluginConfig) {
|
|
setConfig(globalConfig, pluginConfig);
|
|
},
|
|
getConfigDiagnostics() {
|
|
setConfigIfNotSet();
|
|
const length = get_config_diagnostics();
|
|
return JSON.parse(receiveString(wasmInstance, length));
|
|
},
|
|
getResolvedConfig() {
|
|
setConfigIfNotSet();
|
|
const length = get_resolved_config();
|
|
return JSON.parse(receiveString(wasmInstance, length));
|
|
},
|
|
getPluginInfo() {
|
|
const length = get_plugin_info();
|
|
const pluginInfo = JSON.parse(receiveString(wasmInstance, length));
|
|
pluginInfo.fileNames = pluginInfo.fileNames ?? [];
|
|
return pluginInfo;
|
|
},
|
|
getLicenseText() {
|
|
const length = get_license_text();
|
|
return receiveString(wasmInstance, length);
|
|
},
|
|
formatText(filePath, fileText, overrideConfig, formatWithHost) {
|
|
if (formatWithHost) {
|
|
host.setHostFormatter(formatWithHost);
|
|
}
|
|
setConfigIfNotSet();
|
|
if (overrideConfig != null) {
|
|
if (pluginSchemaVersion === 2) {
|
|
throw new Error("Cannot set the override configuration for this old plugin.");
|
|
}
|
|
sendString(wasmInstance, JSON.stringify(overrideConfig));
|
|
set_override_config();
|
|
}
|
|
sendString(wasmInstance, filePath);
|
|
set_file_path();
|
|
sendString(wasmInstance, fileText);
|
|
const responseCode = format();
|
|
switch (responseCode) {
|
|
case 0: // no change
|
|
return fileText;
|
|
case 1: // change
|
|
return receiveString(wasmInstance, get_formatted_text());
|
|
case 2: // error
|
|
throw new Error(receiveString(wasmInstance, get_error_text()));
|
|
default:
|
|
throw new Error(`Unexpected response code: ${responseCode}`);
|
|
}
|
|
},
|
|
};
|
|
function setConfigIfNotSet() {
|
|
if (!configSet) {
|
|
setConfig({}, {});
|
|
}
|
|
}
|
|
function setConfig(globalConfig, pluginConfig) {
|
|
if (reset_config != null) {
|
|
reset_config();
|
|
}
|
|
sendString(wasmInstance, JSON.stringify(globalConfig));
|
|
set_global_config();
|
|
sendString(wasmInstance, JSON.stringify(pluginConfig));
|
|
set_plugin_config();
|
|
configSet = true;
|
|
}
|
|
}
|
|
function sendString(wasmInstance, text) {
|
|
// deno-lint-ignore no-explicit-any
|
|
const exports = wasmInstance.exports;
|
|
const encodedText = encoder.encode(text);
|
|
const length = encodedText.length;
|
|
const memoryBufferSize = exports.get_wasm_memory_buffer_size();
|
|
const memoryBufferPointer = getWasmMemoryBufferPointer(wasmInstance);
|
|
exports.clear_shared_bytes(length);
|
|
let index = 0;
|
|
while (index < length) {
|
|
const writeCount = Math.min(length - index, memoryBufferSize);
|
|
const wasmBuffer = getWasmBufferAtPointer(wasmInstance, memoryBufferPointer, writeCount);
|
|
wasmBuffer.set(encodedText.slice(index, index + writeCount));
|
|
exports.add_to_shared_bytes_from_buffer(writeCount);
|
|
index += writeCount;
|
|
}
|
|
return length;
|
|
}
|
|
function receiveString(wasmInstance, length) {
|
|
// deno-lint-ignore no-explicit-any
|
|
const exports = wasmInstance.exports;
|
|
const memoryBufferSize = exports.get_wasm_memory_buffer_size();
|
|
const memoryBufferPointer = getWasmMemoryBufferPointer(wasmInstance);
|
|
const buffer = new Uint8Array(length);
|
|
let index = 0;
|
|
while (index < length) {
|
|
const readCount = Math.min(length - index, memoryBufferSize);
|
|
exports.set_buffer_with_shared_bytes(index, readCount);
|
|
const wasmBuffer = getWasmBufferAtPointer(wasmInstance, memoryBufferPointer, readCount);
|
|
buffer.set(wasmBuffer, index);
|
|
index += readCount;
|
|
}
|
|
return decoder.decode(buffer);
|
|
}
|
|
function getWasmMemoryBufferPointer(wasmInstance) {
|
|
// deno-lint-ignore no-explicit-any
|
|
return wasmInstance.exports.get_wasm_memory_buffer();
|
|
}
|
|
function getWasmBufferAtPointer(wasmInstance, pointer, length) {
|
|
return new Uint8Array(
|
|
// deno-lint-ignore no-explicit-any
|
|
wasmInstance.exports.memory.buffer, pointer, length);
|
|
}
|