Skip to content

Commit

Permalink
feat(bundle): add an imports replacement plugin
Browse files Browse the repository at this point in the history
  • Loading branch information
lowlighter committed Dec 3, 2024
1 parent 71576e8 commit 2a82485
Show file tree
Hide file tree
Showing 5 changed files with 56 additions and 3 deletions.
34 changes: 31 additions & 3 deletions bundle/ts/bundle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

// Imports
import * as esbuild from "esbuild"
import { denoPlugins as plugins } from "@luca/esbuild-deno-loader"
import { denoLoaderPlugin, denoResolverPlugin } from "@luca/esbuild-deno-loader"
import { encodeBase64 } from "@std/encoding/base64"
import { minify as terser } from "terser"
import { fromFileUrl } from "@std/path/from-file-url"
Expand Down Expand Up @@ -38,12 +38,16 @@ import { delay } from "@std/async/delay"
* console.log(await bundle(`console.log("Hello world")`))
* ```
*/
export async function bundle(input: URL | string, { minify = "terser", format = "esm", debug = false, banner = "", shadow = true, config, exports, raw } = {} as options): Promise<string> {
export async function bundle(input: URL | string, { minify = "terser", format = "esm", debug = false, banner = "", shadow = true, config, exports, raw, overrides } = {} as options): Promise<string> {
const url = input instanceof URL ? input : new URL(`data:application/typescript;base64,${encodeBase64(input)}`)
let code = ""
try {
const { outputFiles: [{ text: output }] } = await esbuild.build({
plugins: [...plugins({ configPath: config ? fromFileUrl(config) : undefined })],
plugins: [
overrides?.imports ? overridesImports({ imports: overrides.imports }) : null,
denoResolverPlugin({ configPath: config ? fromFileUrl(config) : undefined }),
denoLoaderPlugin({ configPath: config ? fromFileUrl(config) : undefined }),
].filter((plugin): plugin is esbuild.Plugin => Boolean(plugin)),
entryPoints: [url.href],
format,
globalName: exports,
Expand Down Expand Up @@ -95,4 +99,28 @@ export type options = {
banner?: string
shadow?: boolean
raw?: Record<PropertyKey, unknown>
overrides?: {
imports?: Record<string, string>
}
}

/** Override imports. */
function overridesImports(options: { imports: NonNullable<NonNullable<options["overrides"]>["imports"]> }): esbuild.Plugin {
return ({
name: "libs-bundler-overrides-imports",
setup(build) {
build.onResolve({ filter: /.*/ }, (args) => {
if (!(args.path in options.imports)) {
return null
}
const url = new URL(options.imports[args.path])
if (url.protocol === "file:") {
return { path: fromFileUrl(url), namespace: "file" }
}
const namespace = url.protocol.slice(0, -1)
const path = url.href.slice(namespace.length + 1)
return { path, namespace }
})
},
}) as esbuild.Plugin
}
13 changes: 13 additions & 0 deletions bundle/ts/bundle_test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { bundle } from "./bundle.ts"
import { expect, test } from "@libs/testing"
import { fromFileUrl } from "@std/path"

const base = new URL("testing/", import.meta.url)
const config = new URL("deno.jsonc", base)
Expand Down Expand Up @@ -53,3 +54,15 @@ test("`bundle()` handles specifiers", async () => {
const url = new URL("test_specifiers.ts", base)
await expect(bundle(url)).resolves.toContain("success")
}, { permissions: { read: true, net: ["jsr.io"], env: true, write: true, run: true } })

test("`bundle()` handles imports replacements (file scheme)", async () => {
const url = new URL("test_overrides_imports.ts", base)
const replaced = import.meta.resolve("./testing/test_overrides_imports_func_success.ts")
await expect(bundle(url, { overrides: { imports: { "./test_overrides_imports_func_failure.ts": replaced } } })).resolves.toContain("success")
}, { permissions: { read: true, net: ["jsr.io"], env: true, write: true, run: true } })

test("`bundle()` handles imports replacements (non-file scheme)", async () => {
const url = new URL("test_overrides_imports.ts", base)
const replaced = `data:text/typescript;base64,${btoa(await Deno.readTextFile(fromFileUrl(import.meta.resolve("./testing/test_overrides_imports_func_success.ts"))))}`
await expect(bundle(url, { overrides: { imports: { "./test_overrides_imports_func_failure.ts": replaced } } })).resolves.toContain("success")
}, { permissions: { read: true, net: ["jsr.io"], env: true, write: true, run: true } })
4 changes: 4 additions & 0 deletions bundle/ts/testing/test_overrides_imports.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// deno-lint-ignore-file no-console
// Example module with overrides imports
import { ok } from "./test_overrides_imports_func_failure.ts"
console.log(ok)
4 changes: 4 additions & 0 deletions bundle/ts/testing/test_overrides_imports_func_failure.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// Example module with overrides imports
export function ok() {
return "failure"
}
4 changes: 4 additions & 0 deletions bundle/ts/testing/test_overrides_imports_func_success.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// Example module with overrides imports
export function ok() {
return "success"
}

0 comments on commit 2a82485

Please sign in to comment.