UNPKG

@bytecodealliance/jco

Version:

JavaScript tooling for working with WebAssembly Components

124 lines (108 loc) 4.41 kB
import { stat, readFile, writeFile } from 'node:fs/promises'; import { resolve, basename } from 'node:path'; import { styleText } from '../common.js'; /** All features that can be enabled/disabled */ const ALL_FEATURES = ['clocks', 'http', 'random', 'stdio', 'fetch-event']; /** Features that should be used for --debug mode */ const DEBUG_FEATURES = ['stdio']; export async function componentize(jsSource, opts) { const { componentize: componentizeFn } = await eval( 'import("@bytecodealliance/componentize-js")' ); const { disableFeatures, enableFeatures } = calculateFeatureSet(opts); const source = await readFile(jsSource, 'utf8'); const witPath = resolve(opts.wit); const sourceName = basename(jsSource); let component; try { const result = await componentizeFn(source, { enableAot: opts.aot, aotMinStackSizeBytes: opts.aotMinStackSizeBytes, wevalBin: opts.wevalBin, sourceName, witPath, worldName: opts.worldName, disableFeatures, enableFeatures, preview2Adapter: opts.preview2Adapter, debugBuild: opts.debugStarlingmonkeyBuild, engine: opts.engine, debug: { bindings: opts.debugBindings, bindingsDir: opts.debugBindingsDir, binary: opts.debugBinary, binaryPath: opts.debugBinaryPath, enableWizerLogging: opts.debugEnableWizerLogging, }, }); if (result.debug) { console.error( `${styleText('cyan', 'DEBUG')} Debug output\n${JSON.stringify(debug, null, 2)}\n` ); } component = result.component; } catch (err) { // Detect package resolution issues that usually mean a misconfigured "witPath" if (err.toString().includes('no known packages')) { const isFile = await stat(witPath).then((s) => s.isFile()); if (isFile) { const hint = await printWITPathHint(witPath); if (err.message) { err.message += `\n${hint}`; } } } throw err; } await writeFile(opts.out, component); console.log(`${styleText('green', 'OK')} Successfully written ${styleText('bold', opts.out)}.`); } /** * Print a hint about the witPath option that may be incorrect * * @param {string} witPath - witPath option that was used (which is a path that resolves to a file or directory) * @returns {string} user-visible, highlighted output that can be printed */ async function printWITPathHint(witPath) { const warningPrefix = styleText(['yellow', 'bold'], 'warning'); const pathMeta = await stat(witPath); let output = '\n'; if (!pathMeta.isFile() && !pathMeta.isDirectory()) { output += `${warningPrefix} The supplited WIT path [${witPath}] is neither a file or directory.\n`; return output; } output += `${warningPrefix} Your WIT path option [${witPath}] may be incorrect\n`; output += `${warningPrefix} When using a world with dependencies, you must pass the enclosing WIT folder, not a single file.\n`; output += `${warningPrefix} (e.g. 'wit/', rather than 'wit/component.wit').\n`; return output; } /** * Build set of disabled features * * At present, `componentize-js` does not use enabled features but exclusively * takes into account disabled features. * * @param {{ debug: boolean, disable: string[], enable: string[] }} opts * @returns {{ disableFeatures: string[], enableFeatures: string[] }} */ function calculateFeatureSet(opts) { const disableFeatures = new Set(opts?.debug ? DEBUG_FEATURES : []); const disable = opts?.disable ?? []; const enable = opts?.enable ?? []; // Process disabled features if (disable.includes('all')) { ALL_FEATURES.forEach((v) => disableFeatures.add(v)); } else { disable.forEach((v) => disableFeatures.add(v)); } // Process enabled features if (enable.includes('all')) { ALL_FEATURES.forEach((v) => disableFeatures.delete(v)); } else { enable.forEach((v) => disableFeatures.delete(v)); } return { disableFeatures: [...disableFeatures], enableFeatures: ALL_FEATURES.filter((v) => !disableFeatures.has(v)), }; }