feat: Expand 'imports' section of deno.json (#22087)

This commit adds automatic expansion of "imports" field in "deno.json"
file.

If "npm:" or "jsr:" imports are encountered we automatically try to add
a "directory" remapping.

Previously users had to specify entries for both `foo` and `foo/` to be
able to import like
`import { symbol1 } from "foo";` and `import { symbol2 } from
"foo/some_file.js"`:
```
{
  "imports": {
    "foo": "npm:@foo/bar",
    "foo/": "npm:/@foo/bar/",
}
```

With this change users can only add entry for `foo`:
```
{
  "imports": {
    "foo": "npm:@foo/bar",
}
```
The entry for `foo/` will be provided automatically.

Similarly if user provides "directory" remapping explicitly, we will not
overwrite it.
This commit is contained in:
Bartek Iwańczuk 2024-01-24 23:44:06 +01:00 committed by GitHub
parent 801ed74118
commit 44f8b05f5b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 110 additions and 6 deletions

View file

@ -889,7 +889,19 @@ impl CliOptions {
.members
.iter()
.map(|member| {
let import_map_value = member.config_file.to_import_map_value();
let mut import_map_value = member.config_file.to_import_map_value();
let expanded_import_map_value = ::import_map::ext::expand_imports(
::import_map::ext::ImportMapConfig {
base_url: member.config_file.specifier.clone(),
import_map_value: import_map_value.clone(),
},
);
import_map_value
.as_object_mut()
.unwrap()
.insert("imports".to_string(), expanded_import_map_value);
::import_map::ext::ImportMapConfig {
base_url: member.config_file.specifier.clone(),
import_map_value,
@ -906,12 +918,24 @@ impl CliOptions {
"Workspace config generated this import map {}",
serde_json::to_string_pretty(&import_map).unwrap()
);
return import_map::import_map_from_value(
let maybe_import_map_result = import_map::import_map_from_value(
// TODO(bartlomieju): maybe should be stored on the workspace config?
&self.maybe_config_file.as_ref().unwrap().specifier,
import_map,
)
.map(Some);
return match maybe_import_map_result {
Ok(maybe_import_map) => {
if let Some(mut import_map) = maybe_import_map {
import_map.ext_expand_imports();
Ok(Some(import_map))
} else {
Ok(None)
}
}
Err(err) => Err(err),
};
}
}
@ -919,7 +943,8 @@ impl CliOptions {
Some(specifier) => specifier,
None => return Ok(None),
};
resolve_import_map_from_specifier(
let maybe_import_map_result = resolve_import_map_from_specifier(
&import_map_specifier,
self.maybe_config_file().as_ref(),
file_fetcher,
@ -928,7 +953,22 @@ impl CliOptions {
.with_context(|| {
format!("Unable to load '{import_map_specifier}' import map")
})
.map(Some)
.map(Some);
match maybe_import_map_result {
Ok(maybe_import_map) => {
if let Some(mut import_map) = maybe_import_map {
let url = import_map.base_url().as_str();
if url.ends_with("deno.json") || url.ends_with("deno.jsonc") {
import_map.ext_expand_imports();
}
Ok(Some(import_map))
} else {
Ok(None)
}
}
Err(err) => Err(err),
}
}
pub fn node_ipc_fd(&self) -> Option<i64> {

View file

@ -5113,3 +5113,64 @@ itest!(warn_on_deprecated_api_with_env_var {
http_server: true,
exit_code: 0,
});
#[test]
fn deno_json_imports_expand() {
let test_context = TestContextBuilder::for_npm().use_temp_cwd().build();
let dir = test_context.temp_dir();
dir.write(
"deno.json",
r#"{
"imports": {
"basic": "npm:@denotest/esm-basic"
}
}"#,
);
dir.write(
"main.ts",
r#"
// import map should resolve
import { setValue, getValue } from "basic";
// this entry should have been added automatically
import { hello } from "basic/other.mjs";
setValue(5);
console.log(getValue());
console.log(hello());
"#,
);
let output = test_context.new_command().args("run main.ts").run();
output.assert_matches_text("[WILDCARD]5\nhello, world!\n");
}
#[test]
fn deno_json_imports_expand_doesnt_overwrite_existing_entries() {
let test_context = TestContextBuilder::for_npm().use_temp_cwd().build();
let dir = test_context.temp_dir();
dir.write(
"deno.json",
r#"{
"imports": {
"basic": "npm:@denotest/esm-basic",
"basic/": "npm:/@denotest/sub-folders/folder_index_js/"
}
}"#,
);
dir.write(
"main.ts",
r#"
// import map should resolve
import { setValue, getValue } from "basic";
// this entry should map to explicitly specified "basic/" mapping
import { add } from "basic/index.js";
setValue(5);
console.log(getValue());
console.log(add(3, 4));
"#,
);
let output = test_context.new_command().args("run main.ts").run();
output.assert_matches_text("[WILDCARD]5\n7\n");
}

View file

@ -154,7 +154,7 @@ await assert.rejects(
}
// read dir
const readDirNames = ["main.d.mts", "main.mjs", "package.json"];
const readDirNames = ["main.d.mts", "main.mjs", "other.mjs", "package.json"];
{
const names = Array.from(Deno.readDirSync(dirPath))
.map((e) => e.name);

View file

@ -0,0 +1,3 @@
export function hello() {
return "hello, world!";
}

View file

@ -5,4 +5,4 @@ size: [WILDCARD]
file:///[WILDCARD]/main.ts (63B)
└─┬ file:///[WILDCARD]/lib.ts (166B)
└── npm:/@denotest/esm-basic@1.0.0 (416B)
└── npm:/@denotest/esm-basic@1.0.0 (471B)