Skip to content

Commit

Permalink
feat: support embedded import map expansion (#111)
Browse files Browse the repository at this point in the history
This supports the embedded import map expansion feature from Deno 1.40: https://deno.com/blog/v1.40#simpler-imports-in-denojson
  • Loading branch information
lucacasonato authored Feb 6, 2024
1 parent 6c8f898 commit 04feffe
Show file tree
Hide file tree
Showing 6 changed files with 101 additions and 2 deletions.
27 changes: 27 additions & 0 deletions mod_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -674,6 +674,33 @@ Deno.test("bundle config ref import map", async (t) => {
});
});

Deno.test("bundle config inline import map with expansion", async (t) => {
await testLoader(t, LOADERS, async (esbuild, loader) => {
const configPath = join(
Deno.cwd(),
"testdata",
"config_inline_expansion.json",
);
const res = await esbuild.build({
...DEFAULT_OPTS,
plugins: [
...denoPlugins({ configPath, loader }),
],
bundle: true,
platform: "neutral",
entryPoints: ["./testdata/mapped_jsr.js"],
});
assertEquals(res.warnings, []);
assertEquals(res.errors, []);
assertEquals(res.outputFiles.length, 1);
const output = res.outputFiles[0];
assertEquals(output.path, "<stdout>");
const dataURL = `data:application/javascript;base64,${btoa(output.text)}`;
const ns = await import(dataURL);
assertEquals(ns.join("a", "b"), join("a", "b"));
});
});

const COMPUTED_PLUGIN: esbuild.Plugin = {
name: "computed",
setup(build) {
Expand Down
7 changes: 6 additions & 1 deletion src/plugin_deno_resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@ import {
SpecifierMap,
toFileUrl,
} from "../deps.ts";
import { readDenoConfig, urlToEsbuildResolution } from "./shared.ts";
import {
expandEmbeddedImportMap,
readDenoConfig,
urlToEsbuildResolution,
} from "./shared.ts";

export type { ImportMap, Scopes, SpecifierMap };

Expand Down Expand Up @@ -76,6 +80,7 @@ export function denoResolverPlugin(
imports: config.imports,
scopes: config.scopes,
} as ImportMap;
expandEmbeddedImportMap(configImportMap);
importMap = resolveImportMap(
configImportMap,
toFileUrl(options.configPath),
Expand Down
33 changes: 32 additions & 1 deletion src/shared.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
import { esbuild, extname, fromFileUrl, JSONC, toFileUrl } from "../deps.ts";
import {
esbuild,
extname,
fromFileUrl,
ImportMap,
JSONC,
toFileUrl,
} from "../deps.ts";
import { MediaType } from "./deno.ts";

export interface Loader {
Expand Down Expand Up @@ -325,3 +332,27 @@ export function parseJsrSpecifier(specifier: URL): JsrSpecifier {
path: pathStartIndex === path.length ? null : path.slice(pathStartIndex),
};
}

// For all pairs in `imports` where the specifier does not end in a /, and the
// target starts with `jsr:` or `npm:`, and no entry exists for `${specifier}/`,
// add an entry for `${specifier}/` pointing to the target with a / appended,
// and a `/` appended to the scheme, if none is present there.
export function expandEmbeddedImportMap(importMap: ImportMap) {
if (importMap.imports !== undefined) {
const newImports: [string, string | null][] = [];
for (const [specifier, target] of Object.entries(importMap.imports)) {
newImports.push([specifier, target]);
if (
!specifier.endsWith("/") && target &&
(target.startsWith("jsr:") || target.startsWith("npm:")) &&
!importMap.imports[specifier + "/"]
) {
const newSpecifier = specifier + "/";
const newTarget = target.slice(0, 4) + "/" +
target.slice(target[4] === "/" ? 5 : 4) + "/";
newImports.push([newSpecifier, newTarget]);
}
}
importMap.imports = Object.fromEntries(newImports);
}
}
29 changes: 29 additions & 0 deletions src/shared_test.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import {
expandEmbeddedImportMap,
NpmSpecifier,
parseJsrSpecifier,
parseNpmSpecifier,
} from "./shared.ts";
import { assertEquals, assertThrows } from "../test_deps.ts";
import { JsrSpecifier } from "./shared.ts";
import { ImportMap } from "../deps.ts";

interface NpmSpecifierTestCase extends NpmSpecifier {
specifier: string;
Expand Down Expand Up @@ -190,3 +192,30 @@ Deno.test("parseJsrSpecifier", async (t) => {
});
}
});

Deno.test("expandEmbeddedImportMap", () => {
const importMap: ImportMap = {
imports: {
"@std/path": "jsr:@std/[email protected]",
"preact": "npm:preact@^10.0.0",
"preact2": "npm:/preact2@^10.0.0",

"preact-render-to-string": "jsr:preact-render-to-string",
"preact-render-to-string/": "jsr:preact-render-to-string2/",
},
};
expandEmbeddedImportMap(importMap);
assertEquals(importMap, {
imports: {
"@std/path": "jsr:@std/[email protected]",
"@std/path/": "jsr:/@std/[email protected]/",
"preact": "npm:preact@^10.0.0",
"preact/": "npm:/preact@^10.0.0/",
"preact2": "npm:/preact2@^10.0.0",
"preact2/": "npm:/preact2@^10.0.0/",

"preact-render-to-string": "jsr:preact-render-to-string",
"preact-render-to-string/": "jsr:preact-render-to-string2/",
},
});
});
6 changes: 6 additions & 0 deletions testdata/config_inline_expansion.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"lock": "./jsr/deno.lock",
"imports": {
"@std/path": "jsr:@std/path@^0.213"
}
}
1 change: 1 addition & 0 deletions testdata/mapped_jsr.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "@std/path/join";

0 comments on commit 04feffe

Please sign in to comment.