mirror of
https://github.com/denoland/deno.git
synced 2025-01-12 17:09:00 -05:00
feat: Import maps (#2360)
This commit is contained in:
parent
8ec5276d30
commit
a115340288
24 changed files with 2406 additions and 36 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
@ -221,6 +221,7 @@ dependencies = [
|
||||||
"http 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)",
|
"http 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"hyper 0.12.29 (registry+https://github.com/rust-lang/crates.io-index)",
|
"hyper 0.12.29 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"hyper-rustls 0.16.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"hyper-rustls 0.16.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"indexmap 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"integer-atomics 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"integer-atomics 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)",
|
"libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
@ -950,6 +951,7 @@ name = "serde_json"
|
||||||
version = "1.0.39"
|
version = "1.0.39"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"indexmap 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
"itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"ryu 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
"ryu 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"serde 1.0.91 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde 1.0.91 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
|
|
@ -1301,11 +1301,15 @@ rust_proc_macro("serde_derive") {
|
||||||
rust_rlib("serde_json") {
|
rust_rlib("serde_json") {
|
||||||
edition = "2015"
|
edition = "2015"
|
||||||
source_root = "$cargo_home/registry/src/github.com-1ecc6299db9ec823/serde_json-1.0.39/src/lib.rs"
|
source_root = "$cargo_home/registry/src/github.com-1ecc6299db9ec823/serde_json-1.0.39/src/lib.rs"
|
||||||
features = [ "default" ]
|
features = [
|
||||||
|
"default",
|
||||||
|
"preserve_order",
|
||||||
|
]
|
||||||
extern_rlib = [
|
extern_rlib = [
|
||||||
"itoa",
|
"itoa",
|
||||||
"ryu",
|
"ryu",
|
||||||
"serde",
|
"serde",
|
||||||
|
"indexmap",
|
||||||
]
|
]
|
||||||
args = [
|
args = [
|
||||||
"--cap-lints",
|
"--cap-lints",
|
||||||
|
|
|
@ -29,6 +29,7 @@ main_extern_rlib = [
|
||||||
"http",
|
"http",
|
||||||
"hyper",
|
"hyper",
|
||||||
"hyper_rustls",
|
"hyper_rustls",
|
||||||
|
"indexmap",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"libc",
|
"libc",
|
||||||
"log",
|
"log",
|
||||||
|
|
|
@ -27,6 +27,7 @@ futures = "0.1.27"
|
||||||
http = "0.1.17"
|
http = "0.1.17"
|
||||||
hyper = "0.12.29"
|
hyper = "0.12.29"
|
||||||
hyper-rustls = "0.16.1"
|
hyper-rustls = "0.16.1"
|
||||||
|
indexmap = "1.0.2"
|
||||||
integer-atomics = "1.0.2"
|
integer-atomics = "1.0.2"
|
||||||
lazy_static = "1.3.0"
|
lazy_static = "1.3.0"
|
||||||
libc = "0.2.55"
|
libc = "0.2.55"
|
||||||
|
@ -38,7 +39,7 @@ ring = "0.14.6"
|
||||||
rustyline = "4.1.0"
|
rustyline = "4.1.0"
|
||||||
serde = "1.0.91"
|
serde = "1.0.91"
|
||||||
serde_derive = "1.0.91"
|
serde_derive = "1.0.91"
|
||||||
serde_json = "1.0.39"
|
serde_json = { version = "1.0.39", features = [ "preserve_order" ] }
|
||||||
source-map-mappings = "0.5.0"
|
source-map-mappings = "0.5.0"
|
||||||
tempfile = "3.0.8"
|
tempfile = "3.0.8"
|
||||||
tokio = "0.1.20"
|
tokio = "0.1.20"
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
|
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
|
||||||
|
use crate::import_map::ImportMapError;
|
||||||
use crate::js_errors::JSErrorColor;
|
use crate::js_errors::JSErrorColor;
|
||||||
pub use crate::msg::ErrorKind;
|
pub use crate::msg::ErrorKind;
|
||||||
use crate::resolve_addr::ResolveAddrError;
|
use crate::resolve_addr::ResolveAddrError;
|
||||||
|
@ -24,6 +25,7 @@ enum Repr {
|
||||||
IoErr(io::Error),
|
IoErr(io::Error),
|
||||||
UrlErr(url::ParseError),
|
UrlErr(url::ParseError),
|
||||||
HyperErr(hyper::Error),
|
HyperErr(hyper::Error),
|
||||||
|
ImportMapErr(ImportMapError),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new(kind: ErrorKind, msg: String) -> DenoError {
|
pub fn new(kind: ErrorKind, msg: String) -> DenoError {
|
||||||
|
@ -92,6 +94,7 @@ impl DenoError {
|
||||||
ErrorKind::HttpOther
|
ErrorKind::HttpOther
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Repr::ImportMapErr(ref _err) => ErrorKind::ImportMapError,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -103,6 +106,7 @@ impl fmt::Display for DenoError {
|
||||||
Repr::IoErr(ref err) => err.fmt(f),
|
Repr::IoErr(ref err) => err.fmt(f),
|
||||||
Repr::UrlErr(ref err) => err.fmt(f),
|
Repr::UrlErr(ref err) => err.fmt(f),
|
||||||
Repr::HyperErr(ref err) => err.fmt(f),
|
Repr::HyperErr(ref err) => err.fmt(f),
|
||||||
|
Repr::ImportMapErr(ref err) => f.pad(&err.msg),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -114,6 +118,7 @@ impl std::error::Error for DenoError {
|
||||||
Repr::IoErr(ref err) => err.description(),
|
Repr::IoErr(ref err) => err.description(),
|
||||||
Repr::UrlErr(ref err) => err.description(),
|
Repr::UrlErr(ref err) => err.description(),
|
||||||
Repr::HyperErr(ref err) => err.description(),
|
Repr::HyperErr(ref err) => err.description(),
|
||||||
|
Repr::ImportMapErr(ref err) => &err.msg,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -123,6 +128,7 @@ impl std::error::Error for DenoError {
|
||||||
Repr::IoErr(ref err) => Some(err),
|
Repr::IoErr(ref err) => Some(err),
|
||||||
Repr::UrlErr(ref err) => Some(err),
|
Repr::UrlErr(ref err) => Some(err),
|
||||||
Repr::HyperErr(ref err) => Some(err),
|
Repr::HyperErr(ref err) => Some(err),
|
||||||
|
Repr::ImportMapErr(ref _err) => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -202,6 +208,14 @@ impl From<UnixError> for DenoError {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<ImportMapError> for DenoError {
|
||||||
|
fn from(err: ImportMapError) -> Self {
|
||||||
|
Self {
|
||||||
|
repr: Repr::ImportMapErr(err),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn bad_resource() -> DenoError {
|
pub fn bad_resource() -> DenoError {
|
||||||
new(ErrorKind::BadResource, String::from("bad resource id"))
|
new(ErrorKind::BadResource, String::from("bad resource id"))
|
||||||
}
|
}
|
||||||
|
|
57
cli/flags.rs
57
cli/flags.rs
|
@ -15,6 +15,9 @@ pub struct DenoFlags {
|
||||||
/// When the `--config`/`-c` flag is used to pass the name, this will be set
|
/// When the `--config`/`-c` flag is used to pass the name, this will be set
|
||||||
/// the path passed on the command line, otherwise `None`.
|
/// the path passed on the command line, otherwise `None`.
|
||||||
pub config_path: Option<String>,
|
pub config_path: Option<String>,
|
||||||
|
/// When the `--importmap` flag is used to pass the name, this will be set
|
||||||
|
/// the path passed on the command line, otherwise `None`.
|
||||||
|
pub import_map_path: Option<String>,
|
||||||
pub allow_read: bool,
|
pub allow_read: bool,
|
||||||
pub read_whitelist: Vec<String>,
|
pub read_whitelist: Vec<String>,
|
||||||
pub allow_write: bool,
|
pub allow_write: bool,
|
||||||
|
@ -82,6 +85,16 @@ fn add_run_args<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> {
|
||||||
Arg::with_name("no-prompt")
|
Arg::with_name("no-prompt")
|
||||||
.long("no-prompt")
|
.long("no-prompt")
|
||||||
.help("Do not use prompts"),
|
.help("Do not use prompts"),
|
||||||
|
).arg(
|
||||||
|
Arg::with_name("importmap")
|
||||||
|
.long("importmap")
|
||||||
|
.value_name("FILE")
|
||||||
|
.help("Load import map file")
|
||||||
|
.long_help(
|
||||||
|
"Load import map file
|
||||||
|
Specification: https://wicg.github.io/import-maps/
|
||||||
|
Examples: https://github.com/WICG/import-maps#the-import-map",
|
||||||
|
).takes_value(true),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -367,10 +380,10 @@ pub fn parse_flags(matches: &ArgMatches) -> DenoFlags {
|
||||||
flags.v8_flags = Some(v8_flags);
|
flags.v8_flags = Some(v8_flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
flags = parse_permission_args(flags, matches);
|
flags = parse_run_args(flags, matches);
|
||||||
// flags specific to "run" subcommand
|
// flags specific to "run" subcommand
|
||||||
if let Some(run_matches) = matches.subcommand_matches("run") {
|
if let Some(run_matches) = matches.subcommand_matches("run") {
|
||||||
flags = parse_permission_args(flags.clone(), run_matches);
|
flags = parse_run_args(flags.clone(), run_matches);
|
||||||
}
|
}
|
||||||
|
|
||||||
flags
|
flags
|
||||||
|
@ -378,10 +391,7 @@ pub fn parse_flags(matches: &ArgMatches) -> DenoFlags {
|
||||||
|
|
||||||
/// Parse permission specific matches Args and assign to DenoFlags.
|
/// Parse permission specific matches Args and assign to DenoFlags.
|
||||||
/// This method is required because multiple subcommands use permission args.
|
/// This method is required because multiple subcommands use permission args.
|
||||||
fn parse_permission_args(
|
fn parse_run_args(mut flags: DenoFlags, matches: &ArgMatches) -> DenoFlags {
|
||||||
mut flags: DenoFlags,
|
|
||||||
matches: &ArgMatches,
|
|
||||||
) -> DenoFlags {
|
|
||||||
if matches.is_present("allow-read") {
|
if matches.is_present("allow-read") {
|
||||||
if matches.value_of("allow-read").is_some() {
|
if matches.value_of("allow-read").is_some() {
|
||||||
let read_wl = matches.values_of("allow-read").unwrap();
|
let read_wl = matches.values_of("allow-read").unwrap();
|
||||||
|
@ -435,6 +445,7 @@ fn parse_permission_args(
|
||||||
if matches.is_present("no-prompt") {
|
if matches.is_present("no-prompt") {
|
||||||
flags.no_prompts = true;
|
flags.no_prompts = true;
|
||||||
}
|
}
|
||||||
|
flags.import_map_path = matches.value_of("importmap").map(ToOwned::to_owned);
|
||||||
|
|
||||||
flags
|
flags
|
||||||
}
|
}
|
||||||
|
@ -912,6 +923,7 @@ mod tests {
|
||||||
assert_eq!(subcommand, DenoSubcommand::Xeval);
|
assert_eq!(subcommand, DenoSubcommand::Xeval);
|
||||||
assert_eq!(argv, svec!["deno", "console.log(val)"]);
|
assert_eq!(argv, svec!["deno", "console.log(val)"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_flags_from_vec_19() {
|
fn test_flags_from_vec_19() {
|
||||||
use tempfile::TempDir;
|
use tempfile::TempDir;
|
||||||
|
@ -936,6 +948,7 @@ mod tests {
|
||||||
assert_eq!(subcommand, DenoSubcommand::Run);
|
assert_eq!(subcommand, DenoSubcommand::Run);
|
||||||
assert_eq!(argv, svec!["deno", "script.ts"]);
|
assert_eq!(argv, svec!["deno", "script.ts"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_flags_from_vec_20() {
|
fn test_flags_from_vec_20() {
|
||||||
use tempfile::TempDir;
|
use tempfile::TempDir;
|
||||||
|
@ -960,6 +973,7 @@ mod tests {
|
||||||
assert_eq!(subcommand, DenoSubcommand::Run);
|
assert_eq!(subcommand, DenoSubcommand::Run);
|
||||||
assert_eq!(argv, svec!["deno", "script.ts"]);
|
assert_eq!(argv, svec!["deno", "script.ts"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_flags_from_vec_21() {
|
fn test_flags_from_vec_21() {
|
||||||
let (flags, subcommand, argv) = flags_from_vec(svec![
|
let (flags, subcommand, argv) = flags_from_vec(svec![
|
||||||
|
@ -1067,4 +1081,35 @@ mod tests {
|
||||||
assert_eq!(subcommand, DenoSubcommand::Bundle);
|
assert_eq!(subcommand, DenoSubcommand::Bundle);
|
||||||
assert_eq!(argv, svec!["deno", "source.ts", "bundle.js"])
|
assert_eq!(argv, svec!["deno", "source.ts", "bundle.js"])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_flags_from_vec_27() {
|
||||||
|
let (flags, subcommand, argv) = flags_from_vec(svec![
|
||||||
|
"deno",
|
||||||
|
"run",
|
||||||
|
"--importmap=importmap.json",
|
||||||
|
"script.ts"
|
||||||
|
]);
|
||||||
|
assert_eq!(
|
||||||
|
flags,
|
||||||
|
DenoFlags {
|
||||||
|
import_map_path: Some("importmap.json".to_owned()),
|
||||||
|
..DenoFlags::default()
|
||||||
|
}
|
||||||
|
);
|
||||||
|
assert_eq!(subcommand, DenoSubcommand::Run);
|
||||||
|
assert_eq!(argv, svec!["deno", "script.ts"]);
|
||||||
|
|
||||||
|
let (flags, subcommand, argv) =
|
||||||
|
flags_from_vec(svec!["deno", "--importmap=importmap.json", "script.ts"]);
|
||||||
|
assert_eq!(
|
||||||
|
flags,
|
||||||
|
DenoFlags {
|
||||||
|
import_map_path: Some("importmap.json".to_owned()),
|
||||||
|
..DenoFlags::default()
|
||||||
|
}
|
||||||
|
);
|
||||||
|
assert_eq!(subcommand, DenoSubcommand::Run);
|
||||||
|
assert_eq!(argv, svec!["deno", "script.ts"]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
2133
cli/import_map.rs
Normal file
2133
cli/import_map.rs
Normal file
File diff suppressed because it is too large
Load diff
|
@ -9,6 +9,7 @@ extern crate futures;
|
||||||
extern crate serde_json;
|
extern crate serde_json;
|
||||||
extern crate clap;
|
extern crate clap;
|
||||||
extern crate deno;
|
extern crate deno;
|
||||||
|
extern crate indexmap;
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
extern crate nix;
|
extern crate nix;
|
||||||
extern crate rand;
|
extern crate rand;
|
||||||
|
@ -24,6 +25,7 @@ mod fs;
|
||||||
mod global_timer;
|
mod global_timer;
|
||||||
mod http_body;
|
mod http_body;
|
||||||
mod http_util;
|
mod http_util;
|
||||||
|
mod import_map;
|
||||||
pub mod js_errors;
|
pub mod js_errors;
|
||||||
pub mod msg;
|
pub mod msg;
|
||||||
pub mod msg_util;
|
pub mod msg_util;
|
||||||
|
|
|
@ -136,6 +136,7 @@ enum ErrorKind: byte {
|
||||||
OpNotAvaiable,
|
OpNotAvaiable,
|
||||||
WorkerInitFailed,
|
WorkerInitFailed,
|
||||||
UnixError,
|
UnixError,
|
||||||
|
ImportMapError,
|
||||||
}
|
}
|
||||||
|
|
||||||
table Cwd {}
|
table Cwd {}
|
||||||
|
|
25
cli/ops.rs
25
cli/ops.rs
|
@ -29,6 +29,7 @@ use crate::worker::Worker;
|
||||||
use deno::js_check;
|
use deno::js_check;
|
||||||
use deno::Buf;
|
use deno::Buf;
|
||||||
use deno::JSError;
|
use deno::JSError;
|
||||||
|
//use deno::Loader;
|
||||||
use deno::Op;
|
use deno::Op;
|
||||||
use deno::PinnedBuf;
|
use deno::PinnedBuf;
|
||||||
use flatbuffers::FlatBufferBuilder;
|
use flatbuffers::FlatBufferBuilder;
|
||||||
|
@ -499,10 +500,30 @@ fn op_fetch_module_meta_data(
|
||||||
let use_cache = !state.flags.reload;
|
let use_cache = !state.flags.reload;
|
||||||
let no_fetch = state.flags.no_fetch;
|
let no_fetch = state.flags.no_fetch;
|
||||||
|
|
||||||
|
// TODO(bartlomieju): I feel this is wrong - specifier is only resolved if there's an
|
||||||
|
// import map - why it is not always resolved? Eg. "bad-module.ts" will return NotFound
|
||||||
|
// error whilst it should return RelativeUrlWithCannotBeABaseBase error
|
||||||
|
let resolved_specifier = match &state.import_map {
|
||||||
|
Some(import_map) => {
|
||||||
|
match import_map.resolve(specifier, referrer) {
|
||||||
|
Ok(result) => match result {
|
||||||
|
Some(url) => url.clone(),
|
||||||
|
None => specifier.to_string(),
|
||||||
|
},
|
||||||
|
Err(err) => panic!("error resolving using import map: {:?}", err), // TODO: this should be coerced to DenoError
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => specifier.to_string(),
|
||||||
|
};
|
||||||
|
|
||||||
let fut = state
|
let fut = state
|
||||||
.dir
|
.dir
|
||||||
.fetch_module_meta_data_async(specifier, referrer, use_cache, no_fetch)
|
.fetch_module_meta_data_async(
|
||||||
.and_then(move |out| {
|
&resolved_specifier,
|
||||||
|
referrer,
|
||||||
|
use_cache,
|
||||||
|
no_fetch,
|
||||||
|
).and_then(move |out| {
|
||||||
let builder = &mut FlatBufferBuilder::new();
|
let builder = &mut FlatBufferBuilder::new();
|
||||||
let data_off = builder.create_vector(out.source_code.as_slice());
|
let data_off = builder.create_vector(out.source_code.as_slice());
|
||||||
let msg_args = msg::FetchModuleMetaDataResArgs {
|
let msg_args = msg::FetchModuleMetaDataResArgs {
|
||||||
|
|
86
cli/state.rs
86
cli/state.rs
|
@ -6,6 +6,7 @@ use crate::errors::DenoError;
|
||||||
use crate::errors::DenoResult;
|
use crate::errors::DenoResult;
|
||||||
use crate::flags;
|
use crate::flags;
|
||||||
use crate::global_timer::GlobalTimer;
|
use crate::global_timer::GlobalTimer;
|
||||||
|
use crate::import_map::ImportMap;
|
||||||
use crate::msg;
|
use crate::msg;
|
||||||
use crate::ops;
|
use crate::ops;
|
||||||
use crate::permissions::DenoPermissions;
|
use crate::permissions::DenoPermissions;
|
||||||
|
@ -57,6 +58,7 @@ pub struct ThreadSafeState(Arc<State>);
|
||||||
|
|
||||||
#[cfg_attr(feature = "cargo-clippy", allow(stutter))]
|
#[cfg_attr(feature = "cargo-clippy", allow(stutter))]
|
||||||
pub struct State {
|
pub struct State {
|
||||||
|
pub main_module: Option<String>,
|
||||||
pub dir: deno_dir::DenoDir,
|
pub dir: deno_dir::DenoDir,
|
||||||
pub argv: Vec<String>,
|
pub argv: Vec<String>,
|
||||||
pub permissions: DenoPermissions,
|
pub permissions: DenoPermissions,
|
||||||
|
@ -67,6 +69,9 @@ pub struct State {
|
||||||
/// When flags contains a `.config_path` option, the fully qualified path
|
/// When flags contains a `.config_path` option, the fully qualified path
|
||||||
/// name of the passed path will be resolved and set.
|
/// name of the passed path will be resolved and set.
|
||||||
pub config_path: Option<String>,
|
pub config_path: Option<String>,
|
||||||
|
/// When flags contains a `.import_map_path` option, the content of the
|
||||||
|
/// import map file will be resolved and set.
|
||||||
|
pub import_map: Option<ImportMap>,
|
||||||
pub metrics: Metrics,
|
pub metrics: Metrics,
|
||||||
pub worker_channels: Mutex<WorkerChannels>,
|
pub worker_channels: Mutex<WorkerChannels>,
|
||||||
pub global_timer: Mutex<GlobalTimer>,
|
pub global_timer: Mutex<GlobalTimer>,
|
||||||
|
@ -111,9 +116,10 @@ pub fn fetch_module_meta_data_and_maybe_compile_async(
|
||||||
let state_ = state.clone();
|
let state_ = state.clone();
|
||||||
let specifier = specifier.to_string();
|
let specifier = specifier.to_string();
|
||||||
let referrer = referrer.to_string();
|
let referrer = referrer.to_string();
|
||||||
|
let is_root = referrer == ".";
|
||||||
|
|
||||||
let f =
|
let f =
|
||||||
futures::future::result(ThreadSafeState::resolve(&specifier, &referrer));
|
futures::future::result(state.resolve(&specifier, &referrer, is_root));
|
||||||
f.and_then(move |module_id| {
|
f.and_then(move |module_id| {
|
||||||
let use_cache = !state_.flags.reload || state_.has_compiled(&module_id);
|
let use_cache = !state_.flags.reload || state_.has_compiled(&module_id);
|
||||||
let no_fetch = state_.flags.no_fetch;
|
let no_fetch = state_.flags.no_fetch;
|
||||||
|
@ -157,7 +163,28 @@ pub fn fetch_module_meta_data_and_maybe_compile(
|
||||||
impl Loader for ThreadSafeState {
|
impl Loader for ThreadSafeState {
|
||||||
type Error = DenoError;
|
type Error = DenoError;
|
||||||
|
|
||||||
fn resolve(specifier: &str, referrer: &str) -> Result<String, Self::Error> {
|
fn resolve(
|
||||||
|
&self,
|
||||||
|
specifier: &str,
|
||||||
|
referrer: &str,
|
||||||
|
is_root: bool,
|
||||||
|
) -> Result<String, Self::Error> {
|
||||||
|
if !is_root {
|
||||||
|
if let Some(import_map) = &self.import_map {
|
||||||
|
match import_map.resolve(specifier, referrer) {
|
||||||
|
Ok(result) => {
|
||||||
|
if result.is_some() {
|
||||||
|
return Ok(result.unwrap());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
// TODO(bartlomieju): this should be coerced to DenoError
|
||||||
|
panic!("error resolving using import map: {:?}", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
resolve_module_spec(specifier, referrer).map_err(DenoError::from)
|
resolve_module_spec(specifier, referrer).map_err(DenoError::from)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -233,14 +260,50 @@ impl ThreadSafeState {
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let dir =
|
||||||
|
deno_dir::DenoDir::new(custom_root, &config, progress.clone()).unwrap();
|
||||||
|
|
||||||
|
let main_module: Option<String> = if argv_rest.len() <= 1 {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
let specifier = argv_rest[1].clone();
|
||||||
|
let referrer = ".";
|
||||||
|
// TODO: does this really have to be resolved by DenoDir?
|
||||||
|
// Maybe we can call `resolve_module_spec`
|
||||||
|
match dir.resolve_module_url(&specifier, referrer) {
|
||||||
|
Ok(url) => Some(url.to_string()),
|
||||||
|
Err(e) => {
|
||||||
|
debug!("Potentially swallowed error {}", e);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut import_map = None;
|
||||||
|
if let Some(file_name) = &flags.import_map_path {
|
||||||
|
let base_url = match &main_module {
|
||||||
|
Some(url) => url,
|
||||||
|
None => unreachable!(),
|
||||||
|
};
|
||||||
|
|
||||||
|
match ImportMap::load(base_url, file_name) {
|
||||||
|
Ok(map) => import_map = Some(map),
|
||||||
|
Err(err) => {
|
||||||
|
println!("{:?}", err);
|
||||||
|
panic!("Error parsing import map");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ThreadSafeState(Arc::new(State {
|
ThreadSafeState(Arc::new(State {
|
||||||
dir: deno_dir::DenoDir::new(custom_root, &config, progress.clone())
|
main_module,
|
||||||
.unwrap(),
|
dir,
|
||||||
argv: argv_rest,
|
argv: argv_rest,
|
||||||
permissions: DenoPermissions::from_flags(&flags),
|
permissions: DenoPermissions::from_flags(&flags),
|
||||||
flags,
|
flags,
|
||||||
config,
|
config,
|
||||||
config_path,
|
config_path,
|
||||||
|
import_map,
|
||||||
metrics: Metrics::default(),
|
metrics: Metrics::default(),
|
||||||
worker_channels: Mutex::new(internal_channels),
|
worker_channels: Mutex::new(internal_channels),
|
||||||
global_timer: Mutex::new(GlobalTimer::new()),
|
global_timer: Mutex::new(GlobalTimer::new()),
|
||||||
|
@ -255,18 +318,9 @@ impl ThreadSafeState {
|
||||||
|
|
||||||
/// Read main module from argv
|
/// Read main module from argv
|
||||||
pub fn main_module(&self) -> Option<String> {
|
pub fn main_module(&self) -> Option<String> {
|
||||||
if self.argv.len() <= 1 {
|
match &self.main_module {
|
||||||
None
|
Some(url) => Some(url.to_string()),
|
||||||
} else {
|
None => None,
|
||||||
let specifier = self.argv[1].clone();
|
|
||||||
let referrer = ".";
|
|
||||||
match self.dir.resolve_module_url(&specifier, referrer) {
|
|
||||||
Ok(url) => Some(url.to_string()),
|
|
||||||
Err(e) => {
|
|
||||||
debug!("Potentially swallowed error {}", e);
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -43,7 +43,12 @@ pub trait Loader: Send + Sync {
|
||||||
/// When implementing an spec-complaint VM, this should be exactly the
|
/// When implementing an spec-complaint VM, this should be exactly the
|
||||||
/// algorithm described here:
|
/// algorithm described here:
|
||||||
/// https://html.spec.whatwg.org/multipage/webappapis.html#resolve-a-module-specifier
|
/// https://html.spec.whatwg.org/multipage/webappapis.html#resolve-a-module-specifier
|
||||||
fn resolve(specifier: &str, referrer: &str) -> Result<String, Self::Error>;
|
fn resolve(
|
||||||
|
&self,
|
||||||
|
specifier: &str,
|
||||||
|
referrer: &str,
|
||||||
|
is_root: bool,
|
||||||
|
) -> Result<String, Self::Error>;
|
||||||
|
|
||||||
/// Given an absolute url, load its source code.
|
/// Given an absolute url, load its source code.
|
||||||
fn load(&self, url: &str) -> Box<SourceCodeInfoFuture<Self::Error>>;
|
fn load(&self, url: &str) -> Box<SourceCodeInfoFuture<Self::Error>>;
|
||||||
|
@ -98,17 +103,15 @@ impl<L: Loader> RecursiveLoad<L> {
|
||||||
referrer: &str,
|
referrer: &str,
|
||||||
parent_id: Option<deno_mod>,
|
parent_id: Option<deno_mod>,
|
||||||
) -> Result<String, L::Error> {
|
) -> Result<String, L::Error> {
|
||||||
let url = L::resolve(specifier, referrer)?;
|
let is_root = parent_id.is_none();
|
||||||
|
let url = self.loader.resolve(specifier, referrer, is_root)?;
|
||||||
|
|
||||||
let is_root = if let Some(parent_id) = parent_id {
|
if !is_root {
|
||||||
{
|
{
|
||||||
let mut m = self.modules.lock().unwrap();
|
let mut m = self.modules.lock().unwrap();
|
||||||
m.add_child(parent_id, &url);
|
m.add_child(parent_id.unwrap(), &url);
|
||||||
}
|
}
|
||||||
false
|
}
|
||||||
} else {
|
|
||||||
true
|
|
||||||
};
|
|
||||||
|
|
||||||
{
|
{
|
||||||
// #B We only add modules that have not yet been resolved for RecursiveLoad.
|
// #B We only add modules that have not yet been resolved for RecursiveLoad.
|
||||||
|
@ -251,7 +254,9 @@ impl<L: Loader> Future for RecursiveLoad<L> {
|
||||||
|specifier: &str, referrer_id: deno_mod| -> deno_mod {
|
|specifier: &str, referrer_id: deno_mod| -> deno_mod {
|
||||||
let modules = self.modules.lock().unwrap();
|
let modules = self.modules.lock().unwrap();
|
||||||
let referrer = modules.get_name(referrer_id).unwrap();
|
let referrer = modules.get_name(referrer_id).unwrap();
|
||||||
match L::resolve(specifier, &referrer) {
|
// TODO(bartlomieju): there must be a better way
|
||||||
|
let is_root = referrer == ".";
|
||||||
|
match self.loader.resolve(specifier, &referrer, is_root) {
|
||||||
Ok(url) => match modules.get_id(&url) {
|
Ok(url) => match modules.get_id(&url) {
|
||||||
Some(id) => id,
|
Some(id) => id,
|
||||||
None => 0,
|
None => 0,
|
||||||
|
@ -619,7 +624,12 @@ mod tests {
|
||||||
impl Loader for MockLoader {
|
impl Loader for MockLoader {
|
||||||
type Error = MockError;
|
type Error = MockError;
|
||||||
|
|
||||||
fn resolve(specifier: &str, referrer: &str) -> Result<String, Self::Error> {
|
fn resolve(
|
||||||
|
&self,
|
||||||
|
specifier: &str,
|
||||||
|
referrer: &str,
|
||||||
|
_is_root: bool,
|
||||||
|
) -> Result<String, Self::Error> {
|
||||||
eprintln!(">> RESOLVING, S: {}, R: {}", specifier, referrer);
|
eprintln!(">> RESOLVING, S: {}, R: {}", specifier, referrer);
|
||||||
let output_specifier =
|
let output_specifier =
|
||||||
if specifier.starts_with("./") && referrer.starts_with("./") {
|
if specifier.starts_with("./") && referrer.starts_with("./") {
|
||||||
|
|
7
tests/033_import_map.out
Normal file
7
tests/033_import_map.out
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
Hello from remapped moment!
|
||||||
|
Hello from remapped moment dir!
|
||||||
|
Hello from remapped lodash!
|
||||||
|
Hello from remapped lodash dir!
|
||||||
|
Hello from remapped Vue!
|
||||||
|
Hello from scoped moment!
|
||||||
|
Hello from scoped!
|
2
tests/033_import_map.test
Normal file
2
tests/033_import_map.test
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
args: run --reload --importmap=tests/importmaps/import_map.json tests/importmaps/test.ts
|
||||||
|
output: tests/033_import_map.out
|
14
tests/importmaps/import_map.json
Normal file
14
tests/importmaps/import_map.json
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
{
|
||||||
|
"imports": {
|
||||||
|
"moment": "./moment/moment.ts",
|
||||||
|
"moment/": "./moment/",
|
||||||
|
"lodash": "./lodash/lodash.ts",
|
||||||
|
"lodash/": "./lodash/",
|
||||||
|
"https://www.unpkg.com/vue/dist/vue.runtime.esm.js": "./vue.ts"
|
||||||
|
},
|
||||||
|
"scopes": {
|
||||||
|
"scope/": {
|
||||||
|
"moment": "./scoped_moment.ts"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
1
tests/importmaps/lodash/lodash.ts
Normal file
1
tests/importmaps/lodash/lodash.ts
Normal file
|
@ -0,0 +1 @@
|
||||||
|
console.log("Hello from remapped lodash!");
|
1
tests/importmaps/lodash/other_file.ts
Normal file
1
tests/importmaps/lodash/other_file.ts
Normal file
|
@ -0,0 +1 @@
|
||||||
|
console.log("Hello from remapped lodash dir!");
|
1
tests/importmaps/moment/moment.ts
Normal file
1
tests/importmaps/moment/moment.ts
Normal file
|
@ -0,0 +1 @@
|
||||||
|
console.log("Hello from remapped moment!");
|
1
tests/importmaps/moment/other_file.ts
Normal file
1
tests/importmaps/moment/other_file.ts
Normal file
|
@ -0,0 +1 @@
|
||||||
|
console.log("Hello from remapped moment dir!");
|
2
tests/importmaps/scope/scoped.ts
Normal file
2
tests/importmaps/scope/scoped.ts
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
import "moment";
|
||||||
|
console.log("Hello from scoped!");
|
1
tests/importmaps/scoped_moment.ts
Normal file
1
tests/importmaps/scoped_moment.ts
Normal file
|
@ -0,0 +1 @@
|
||||||
|
console.log("Hello from scoped moment!");
|
6
tests/importmaps/test.ts
Normal file
6
tests/importmaps/test.ts
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
import "moment";
|
||||||
|
import "moment/other_file.ts";
|
||||||
|
import "lodash";
|
||||||
|
import "lodash/other_file.ts";
|
||||||
|
import "https://www.unpkg.com/vue/dist/vue.runtime.esm.js";
|
||||||
|
import "./scope/scoped.ts";
|
1
tests/importmaps/vue.ts
Normal file
1
tests/importmaps/vue.ts
Normal file
|
@ -0,0 +1 @@
|
||||||
|
console.log("Hello from remapped Vue!");
|
|
@ -634,6 +634,7 @@ OPTIONS:
|
||||||
--allow-read=<allow-read> Allow file system read access
|
--allow-read=<allow-read> Allow file system read access
|
||||||
--allow-write=<allow-write> Allow file system write access
|
--allow-write=<allow-write> Allow file system write access
|
||||||
-c, --config <FILE> Load compiler configuration file
|
-c, --config <FILE> Load compiler configuration file
|
||||||
|
--importmap <FILE> Load import map file
|
||||||
--v8-flags=<v8-flags> Set V8 command line options
|
--v8-flags=<v8-flags> Set V8 command line options
|
||||||
|
|
||||||
SUBCOMMANDS:
|
SUBCOMMANDS:
|
||||||
|
@ -676,6 +677,50 @@ Particularly useful ones:
|
||||||
--async-stack-trace
|
--async-stack-trace
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Import maps
|
||||||
|
|
||||||
|
Deno supports [import maps](https://github.com/WICG/import-maps).
|
||||||
|
|
||||||
|
One can use import map with `--importmap=<FILE>` CLI flag.
|
||||||
|
|
||||||
|
Current limitations:
|
||||||
|
|
||||||
|
- single import map
|
||||||
|
- no fallback URLs
|
||||||
|
- Deno does not support `std:` namespace
|
||||||
|
- Does supports only `file:`, `http:` and `https:` schemes
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```js
|
||||||
|
// import_map.json
|
||||||
|
|
||||||
|
{
|
||||||
|
"imports": {
|
||||||
|
"http/": "https://deno.land/std/http/"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```ts
|
||||||
|
// hello_server.ts
|
||||||
|
|
||||||
|
import { serve } from "http/server.ts";
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
const body = new TextEncoder().encode("Hello World\n");
|
||||||
|
for await (const req of serve(":8000")) {
|
||||||
|
req.respond({ body });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
main();
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ deno run --importmap=import_map.json hello_server.ts
|
||||||
|
```
|
||||||
|
|
||||||
## Internal details
|
## Internal details
|
||||||
|
|
||||||
### Deno and Linux analogy
|
### Deno and Linux analogy
|
||||||
|
|
Loading…
Reference in a new issue