Skip to content

Commit d5a8dc8

Browse files
committed
feat(import-map): add support for import maps in user workers
1 parent ef60489 commit d5a8dc8

File tree

9 files changed

+146
-18
lines changed

9 files changed

+146
-18
lines changed

crates/base/src/runtime/mod.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -516,6 +516,11 @@ where
516516
let base_dir_path =
517517
std::env::current_dir().map(|p| p.join(&service_path))?;
518518

519+
let maybe_import_map_path = context
520+
.get("importMapPath")
521+
.and_then(|it| it.as_str())
522+
.map(str::to_string);
523+
519524
let eszip = if let Some(eszip_payload) = maybe_eszip {
520525
eszip_payload
521526
} else {
@@ -563,6 +568,7 @@ where
563568
CacheSetting::Use
564569
};
565570

571+
emitter_factory.set_import_map_path(maybe_import_map_path.clone());
566572
emitter_factory
567573
.set_permissions_options(Some(permissions_options.clone()));
568574

@@ -631,10 +637,6 @@ where
631637
.get("sourceMap")
632638
.and_then(serde_json::Value::as_bool)
633639
.unwrap_or_default();
634-
let maybe_import_map_path = context
635-
.get("importMapPath")
636-
.and_then(|it| it.as_str())
637-
.map(str::to_string);
638640

639641
let rt_provider = create_module_loader_for_standalone_from_eszip_kind(
640642
eszip,
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export function getHelperMessage(): string {
2+
return "import map works!";
3+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"imports": {
3+
"helper-from-import-map": "./helper.ts"
4+
}
5+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import * as path from "jsr:@std/path";
2+
3+
Deno.serve(async (req: Request) => {
4+
const basePath = "test_cases/user-worker-with-import-map";
5+
const url = new URL(req.url);
6+
const { pathname } = url;
7+
8+
const userWorkerPath = path.join(basePath, "user_worker");
9+
if (pathname === "/import_map") {
10+
const worker = await EdgeRuntime.userWorkers.create({
11+
servicePath: userWorkerPath,
12+
forceCreate: true,
13+
context: {
14+
importMapPath: path.join(basePath, "import_map.json"),
15+
},
16+
});
17+
return worker.fetch(req);
18+
}
19+
if (pathname === "/inline_import_map") {
20+
const inlineImportMap = {
21+
imports: {
22+
"helper-from-import-map": `./${path.join(basePath, "helper.ts")}`,
23+
},
24+
};
25+
const importMapPath = `data:${encodeURIComponent(JSON.stringify(inlineImportMap))}`;
26+
27+
const worker = await EdgeRuntime.userWorkers.create({
28+
servicePath: userWorkerPath,
29+
forceCreate: true,
30+
context: {
31+
importMapPath: importMapPath,
32+
},
33+
});
34+
return worker.fetch(req);
35+
}
36+
37+
return new Response("Not Found", { status: 404 });
38+
});
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { getHelperMessage } from "helper-from-import-map";
2+
3+
Deno.serve((_req: Request) => {
4+
return new Response(
5+
JSON.stringify({
6+
message: getHelperMessage(),
7+
success: true,
8+
}),
9+
{
10+
headers: {
11+
"Content-Type": "application/json",
12+
Connection: "keep-alive",
13+
},
14+
},
15+
);
16+
});

crates/base/tests/integration_tests.rs

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ use base::utils::test_utils::ensure_npm_package_installed;
3030
use base::utils::test_utils::test_user_runtime_opts;
3131
use base::utils::test_utils::test_user_worker_pool_policy;
3232
use base::utils::test_utils::TestBedBuilder;
33-
use base::utils::test_utils::{self};
3433
use base::worker;
3534
use base::worker::TerminationToken;
3635
use base::WorkerKind;
@@ -4095,6 +4094,54 @@ async fn test_user_workers_cleanup_idle_workers() {
40954094
unreachable!("test failed");
40964095
}
40974096

4097+
#[tokio::test]
4098+
#[serial]
4099+
async fn test_user_worker_with_import_map() {
4100+
let assert_fn = |resp: Result<Response, reqwest::Error>| async {
4101+
let res = resp.unwrap();
4102+
let status = res.status().as_u16();
4103+
4104+
let body_bytes = res.bytes().await.unwrap();
4105+
let body_str = String::from_utf8_lossy(&body_bytes);
4106+
4107+
assert_eq!(
4108+
status, 200,
4109+
"Expected 200, got {} with body: {}",
4110+
status, body_str
4111+
);
4112+
4113+
assert!(
4114+
body_str.contains("import map works!"),
4115+
"Expected import map works!, got: {}",
4116+
body_str
4117+
);
4118+
};
4119+
{
4120+
integration_test!(
4121+
"./test_cases/user-worker-with-import-map",
4122+
NON_SECURE_PORT,
4123+
"import_map",
4124+
None,
4125+
None,
4126+
None,
4127+
(assert_fn),
4128+
TerminationToken::new()
4129+
);
4130+
}
4131+
{
4132+
integration_test!(
4133+
"./test_cases/user-worker-with-import-map",
4134+
NON_SECURE_PORT,
4135+
"inline_import_map",
4136+
None,
4137+
None,
4138+
None,
4139+
(assert_fn),
4140+
TerminationToken::new()
4141+
);
4142+
}
4143+
}
4144+
40984145
#[derive(Deserialize)]
40994146
struct ErrorResponsePayload {
41004147
msg: String,

crates/deno_facade/emitter.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ use ext_node::DenoFsNodeResolverEnv;
5858
use ext_node::NodeResolver;
5959
use ext_node::PackageJsonResolver;
6060

61+
use crate::import_map::load_import_map;
6162
use crate::permissions::RuntimePermissionDescriptorParser;
6263

6364
struct Deferred<T>(once_cell::unsync::OnceCell<T>);
@@ -120,6 +121,7 @@ pub struct EmitterFactory {
120121
sloppy_imports_resolver: Deferred<Option<Arc<CliSloppyImportsResolver>>>,
121122
workspace_resolver: Deferred<Arc<WorkspaceResolver>>,
122123

124+
import_map_path: Option<String>,
123125
cache_strategy: Option<CacheSetting>,
124126
deno_dir: DenoDir,
125127
deno_options: Option<Arc<DenoOptions>>,
@@ -160,6 +162,7 @@ impl EmitterFactory {
160162
sloppy_imports_resolver: Default::default(),
161163
workspace_resolver: Default::default(),
162164

165+
import_map_path: Default::default(),
163166
cache_strategy: None,
164167
deno_dir,
165168
deno_options: None,
@@ -180,6 +183,11 @@ impl EmitterFactory {
180183
self
181184
}
182185

186+
pub fn set_import_map_path(&mut self, value: Option<String>) -> &mut Self {
187+
self.import_map_path = value;
188+
self
189+
}
190+
183191
pub fn set_cache_strategy(
184192
&mut self,
185193
value: Option<CacheSetting>,
@@ -528,8 +536,12 @@ impl EmitterFactory {
528536
) -> Result<&Arc<WorkspaceResolver>, anyhow::Error> {
529537
self.workspace_resolver.get_or_try_init(|| {
530538
let options = self.deno_options()?;
539+
let specified_import_map =
540+
load_import_map(self.import_map_path.as_deref())?;
541+
531542
let resolver = options.create_workspace_resolver(
532543
self.file_fetcher()?,
544+
specified_import_map,
533545
if options.use_byonm() {
534546
PackageJsonDepResolution::Disabled
535547
} else {

crates/deno_facade/import_map.rs

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,15 @@
11
use anyhow::anyhow;
22
use anyhow::Error;
3+
use deno_config::workspace::SpecifiedImportMap;
4+
use deno_core::serde_json;
35
use deno_core::url::Url;
4-
use import_map::parse_from_json;
5-
use import_map::ImportMap;
6-
use urlencoding::decode;
7-
86
use std::fs;
97
use std::path::Path;
8+
use urlencoding::decode;
109

1110
pub fn load_import_map(
12-
maybe_path: Option<String>,
13-
) -> Result<Option<ImportMap>, Error> {
11+
maybe_path: Option<&str>,
12+
) -> Result<Option<SpecifiedImportMap>, Error> {
1413
if let Some(path_str) = maybe_path {
1514
let json_str;
1615
let base_url;
@@ -21,10 +20,13 @@ pub fn load_import_map(
2120
if path_str.starts_with("data:") {
2221
let data_uri = Url::parse(&path_str)?;
2322
json_str = decode(data_uri.path())?.into_owned();
24-
base_url = Url::from_directory_path(
25-
decode(data_uri.query().unwrap_or(""))?.into_owned(),
26-
)
27-
.map_err(|_| anyhow!("invalid import map base url"))?;
23+
if let Some(query) = data_uri.query() {
24+
base_url = Url::from_directory_path(decode(query)?.into_owned())
25+
.map_err(|_| anyhow!("invalid import map base url"))?;
26+
} else {
27+
base_url = Url::from_directory_path(std::env::current_dir().unwrap())
28+
.map_err(|_| anyhow!("invalid import map base url"))?;
29+
}
2830
} else {
2931
let path = Path::new(&path_str);
3032
let abs_path = std::env::current_dir().map(|p| p.join(path))?;
@@ -33,8 +35,9 @@ pub fn load_import_map(
3335
.map_err(|_| anyhow!("invalid import map base url"))?;
3436
}
3537

36-
let result = parse_from_json(base_url, json_str.as_str())?;
37-
Ok(Some(result.import_map))
38+
let value = serde_json::from_str(&json_str)?;
39+
40+
Ok(Some(SpecifiedImportMap { base_url, value }))
3841
} else {
3942
Ok(None)
4043
}

deno/lib.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ use deno_config::deno_json::TsConfigForEmit;
1616
use deno_config::deno_json::TsConfigType;
1717
use deno_config::workspace::CreateResolverOptions;
1818
use deno_config::workspace::PackageJsonDepResolution;
19+
use deno_config::workspace::SpecifiedImportMap;
1920
use deno_config::workspace::VendorEnablement;
2021
use deno_config::workspace::WorkspaceDirectory;
2122
use deno_config::workspace::WorkspaceDirectoryEmptyOptions;
@@ -199,12 +200,13 @@ impl DenoOptions {
199200
pub fn create_workspace_resolver(
200201
&self,
201202
_file_fetcher: &FileFetcher,
203+
specified_import_map: Option<SpecifiedImportMap>,
202204
pkg_json_dep_resolution: PackageJsonDepResolution,
203205
) -> Result<WorkspaceResolver, AnyError> {
204206
Ok(self.workspace().create_resolver(
205207
CreateResolverOptions {
206208
pkg_json_dep_resolution,
207-
specified_import_map: None,
209+
specified_import_map,
208210
},
209211
|path| Ok(std::fs::read_to_string(path)?),
210212
)?)

0 commit comments

Comments
 (0)