2022-10-10 12:50:21 +00:00
|
|
|
// import { esbuild } from "../../mod.ts";
|
|
|
|
import * as esbuildWasm from "https://deno.land/x/esbuild@v0.14.54/wasm.js";
|
|
|
|
import * as esbuildNative from "https://deno.land/x/esbuild@v0.14.54/mod.js";
|
|
|
|
|
|
|
|
export const esbuild: typeof esbuildWasm = Deno.run === undefined
|
|
|
|
? esbuildWasm
|
|
|
|
: esbuildNative;
|
|
|
|
|
2022-10-12 09:47:13 +00:00
|
|
|
import { path } from "./deps.ts";
|
|
|
|
import { denoPlugin } from "./forked/esbuild_deno_loader/mod.ts";
|
|
|
|
import { patchDenoLibJS } from "./hack.ts";
|
2022-10-13 13:16:18 +00:00
|
|
|
import { Manifest } from "./types.ts";
|
2022-10-10 12:50:21 +00:00
|
|
|
|
|
|
|
export type CompileOptions = {
|
|
|
|
debug?: boolean;
|
2022-10-13 13:16:18 +00:00
|
|
|
imports?: Manifest<any>[];
|
2022-10-10 12:50:21 +00:00
|
|
|
importMap?: URL;
|
2022-10-13 13:16:18 +00:00
|
|
|
// Reload plug import cache
|
|
|
|
reload?: boolean;
|
|
|
|
// Print info on bundle size
|
|
|
|
info?: boolean;
|
2022-10-10 12:50:21 +00:00
|
|
|
};
|
|
|
|
|
2022-10-13 13:16:18 +00:00
|
|
|
function esBuildExternals(imports?: Manifest<any>[]) {
|
|
|
|
if (!imports) {
|
|
|
|
return [];
|
|
|
|
}
|
|
|
|
const externals: string[] = [];
|
|
|
|
for (const manifest of imports) {
|
|
|
|
for (const dep of Object.keys(manifest.dependencies || {})) {
|
|
|
|
if (!externals.includes(dep)) {
|
|
|
|
externals.push(dep);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return externals;
|
|
|
|
}
|
|
|
|
|
2022-10-10 12:50:21 +00:00
|
|
|
export async function compile(
|
|
|
|
filePath: string,
|
|
|
|
functionName: string | undefined = undefined,
|
|
|
|
options: CompileOptions = {},
|
|
|
|
): Promise<string> {
|
|
|
|
const outFile = await Deno.makeTempFile({ suffix: ".js" });
|
|
|
|
let inFile = filePath;
|
|
|
|
|
|
|
|
if (functionName) {
|
|
|
|
// Generate a new file importing just this one function and exporting it
|
|
|
|
inFile = await Deno.makeTempFile({ suffix: ".ts" });
|
|
|
|
await Deno.writeTextFile(
|
|
|
|
inFile,
|
2023-01-01 19:28:25 +00:00
|
|
|
`import {${functionName}} from "file://${
|
|
|
|
// Replacaing \ with / for Windows
|
|
|
|
path.resolve(filePath).replaceAll("\\", "\\\\")
|
2022-10-10 12:50:21 +00:00
|
|
|
}";export default ${functionName};`,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
// console.log("External modules", excludeModules);
|
|
|
|
|
|
|
|
try {
|
|
|
|
// TODO: Figure out how to make source maps work correctly with eval() code
|
|
|
|
const result = await esbuild.build({
|
|
|
|
entryPoints: [path.basename(inFile)],
|
|
|
|
bundle: true,
|
|
|
|
format: "iife",
|
|
|
|
globalName: "mod",
|
|
|
|
platform: "browser",
|
|
|
|
sourcemap: false, //debug ? "inline" : false,
|
|
|
|
minify: !options.debug,
|
|
|
|
outfile: outFile,
|
2022-10-13 13:16:18 +00:00
|
|
|
metafile: options.info,
|
|
|
|
external: esBuildExternals(options.imports),
|
2022-10-10 12:50:21 +00:00
|
|
|
treeShaking: true,
|
|
|
|
plugins: [
|
|
|
|
denoPlugin({
|
2022-10-12 09:47:13 +00:00
|
|
|
// TODO do this differently
|
2022-10-10 12:50:21 +00:00
|
|
|
importMapURL: options.importMap ||
|
|
|
|
new URL("./../import_map.json", import.meta.url),
|
2022-12-05 10:41:28 +00:00
|
|
|
loader: "native",
|
2022-10-10 12:50:21 +00:00
|
|
|
}),
|
|
|
|
],
|
|
|
|
absWorkingDir: path.resolve(path.dirname(inFile)),
|
|
|
|
});
|
|
|
|
|
2022-10-13 13:16:18 +00:00
|
|
|
if (options.info) {
|
|
|
|
const text = await esbuild.analyzeMetafile(result.metafile!);
|
2022-10-10 12:50:21 +00:00
|
|
|
console.log("Bundle info for", functionName, text);
|
|
|
|
}
|
|
|
|
|
|
|
|
let jsCode = await Deno.readTextFile(outFile);
|
|
|
|
jsCode = patchDenoLibJS(jsCode);
|
|
|
|
await Deno.remove(outFile);
|
|
|
|
return `(() => { ${jsCode} return mod;})()`;
|
|
|
|
} finally {
|
|
|
|
if (inFile !== filePath) {
|
|
|
|
await Deno.remove(inFile);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export async function compileModule(
|
|
|
|
cwd: string,
|
|
|
|
moduleName: string,
|
|
|
|
options: CompileOptions = {},
|
|
|
|
): Promise<string> {
|
|
|
|
const inFile = path.resolve(cwd, "_in.ts");
|
|
|
|
await Deno.writeTextFile(inFile, `export * from "${moduleName}";`);
|
|
|
|
const code = await compile(inFile, undefined, options);
|
|
|
|
await Deno.remove(inFile);
|
|
|
|
return code;
|
|
|
|
}
|
|
|
|
|
|
|
|
export async function sandboxCompile(
|
|
|
|
filename: string,
|
|
|
|
code: string,
|
|
|
|
functionName?: string,
|
|
|
|
options: CompileOptions = {},
|
|
|
|
): Promise<string> {
|
|
|
|
const tmpDir = await Deno.makeTempDir();
|
|
|
|
|
|
|
|
await Deno.writeTextFile(`${tmpDir}/${filename}`, code);
|
|
|
|
const jsCode = await compile(
|
|
|
|
`${tmpDir}/${filename}`,
|
|
|
|
functionName,
|
|
|
|
options,
|
|
|
|
);
|
|
|
|
await Deno.remove(tmpDir, { recursive: true });
|
|
|
|
return jsCode;
|
|
|
|
}
|
|
|
|
|
|
|
|
export async function sandboxCompileModule(
|
|
|
|
moduleUrl: string,
|
|
|
|
options: CompileOptions = {},
|
|
|
|
): Promise<string> {
|
|
|
|
await Deno.writeTextFile(
|
|
|
|
"_mod.ts",
|
|
|
|
`module.exports = require("${moduleUrl}");`,
|
|
|
|
);
|
|
|
|
const code = await compile("_mod.ts", undefined, options);
|
|
|
|
await Deno.remove("_mod.ts");
|
|
|
|
return code;
|
|
|
|
}
|