mirror of
https://github.com/denoland/deno.git
synced 2024-11-22 04:51:22 +00:00
feat: Import maps (#2360)
This commit is contained in:
parent
8ec5276d30
commit
a115340288
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)",
|
||||
"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)",
|
||||
"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)",
|
||||
"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)",
|
||||
@ -950,6 +951,7 @@ name = "serde_json"
|
||||
version = "1.0.39"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
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)",
|
||||
"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)",
|
||||
|
@ -1301,11 +1301,15 @@ rust_proc_macro("serde_derive") {
|
||||
rust_rlib("serde_json") {
|
||||
edition = "2015"
|
||||
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 = [
|
||||
"itoa",
|
||||
"ryu",
|
||||
"serde",
|
||||
"indexmap",
|
||||
]
|
||||
args = [
|
||||
"--cap-lints",
|
||||
|
@ -29,6 +29,7 @@ main_extern_rlib = [
|
||||
"http",
|
||||
"hyper",
|
||||
"hyper_rustls",
|
||||
"indexmap",
|
||||
"lazy_static",
|
||||
"libc",
|
||||
"log",
|
||||
|
@ -27,6 +27,7 @@ futures = "0.1.27"
|
||||
http = "0.1.17"
|
||||
hyper = "0.12.29"
|
||||
hyper-rustls = "0.16.1"
|
||||
indexmap = "1.0.2"
|
||||
integer-atomics = "1.0.2"
|
||||
lazy_static = "1.3.0"
|
||||
libc = "0.2.55"
|
||||
@ -38,7 +39,7 @@ ring = "0.14.6"
|
||||
rustyline = "4.1.0"
|
||||
serde = "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"
|
||||
tempfile = "3.0.8"
|
||||
tokio = "0.1.20"
|
||||
|
@ -1,4 +1,5 @@
|
||||
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
|
||||
use crate::import_map::ImportMapError;
|
||||
use crate::js_errors::JSErrorColor;
|
||||
pub use crate::msg::ErrorKind;
|
||||
use crate::resolve_addr::ResolveAddrError;
|
||||
@ -24,6 +25,7 @@ enum Repr {
|
||||
IoErr(io::Error),
|
||||
UrlErr(url::ParseError),
|
||||
HyperErr(hyper::Error),
|
||||
ImportMapErr(ImportMapError),
|
||||
}
|
||||
|
||||
pub fn new(kind: ErrorKind, msg: String) -> DenoError {
|
||||
@ -92,6 +94,7 @@ impl DenoError {
|
||||
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::UrlErr(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::UrlErr(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::UrlErr(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 {
|
||||
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
|
||||
/// the path passed on the command line, otherwise `None`.
|
||||
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 read_whitelist: Vec<String>,
|
||||
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")
|
||||
.long("no-prompt")
|
||||
.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 = parse_permission_args(flags, matches);
|
||||
flags = parse_run_args(flags, matches);
|
||||
// flags specific to "run" subcommand
|
||||
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
|
||||
@ -378,10 +391,7 @@ pub fn parse_flags(matches: &ArgMatches) -> DenoFlags {
|
||||
|
||||
/// Parse permission specific matches Args and assign to DenoFlags.
|
||||
/// This method is required because multiple subcommands use permission args.
|
||||
fn parse_permission_args(
|
||||
mut flags: DenoFlags,
|
||||
matches: &ArgMatches,
|
||||
) -> DenoFlags {
|
||||
fn parse_run_args(mut flags: DenoFlags, matches: &ArgMatches) -> DenoFlags {
|
||||
if matches.is_present("allow-read") {
|
||||
if matches.value_of("allow-read").is_some() {
|
||||
let read_wl = matches.values_of("allow-read").unwrap();
|
||||
@ -435,6 +445,7 @@ fn parse_permission_args(
|
||||
if matches.is_present("no-prompt") {
|
||||
flags.no_prompts = true;
|
||||
}
|
||||
flags.import_map_path = matches.value_of("importmap").map(ToOwned::to_owned);
|
||||
|
||||
flags
|
||||
}
|
||||
@ -912,6 +923,7 @@ mod tests {
|
||||
assert_eq!(subcommand, DenoSubcommand::Xeval);
|
||||
assert_eq!(argv, svec!["deno", "console.log(val)"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_flags_from_vec_19() {
|
||||
use tempfile::TempDir;
|
||||
@ -936,6 +948,7 @@ mod tests {
|
||||
assert_eq!(subcommand, DenoSubcommand::Run);
|
||||
assert_eq!(argv, svec!["deno", "script.ts"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_flags_from_vec_20() {
|
||||
use tempfile::TempDir;
|
||||
@ -960,6 +973,7 @@ mod tests {
|
||||
assert_eq!(subcommand, DenoSubcommand::Run);
|
||||
assert_eq!(argv, svec!["deno", "script.ts"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_flags_from_vec_21() {
|
||||
let (flags, subcommand, argv) = flags_from_vec(svec![
|
||||
@ -1067,4 +1081,35 @@ mod tests {
|
||||
assert_eq!(subcommand, DenoSubcommand::Bundle);
|
||||
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 clap;
|
||||
extern crate deno;
|
||||
extern crate indexmap;
|
||||
#[cfg(unix)]
|
||||
extern crate nix;
|
||||
extern crate rand;
|
||||
@ -24,6 +25,7 @@ mod fs;
|
||||
mod global_timer;
|
||||
mod http_body;
|
||||
mod http_util;
|
||||
mod import_map;
|
||||
pub mod js_errors;
|
||||
pub mod msg;
|
||||
pub mod msg_util;
|
||||
|
@ -136,6 +136,7 @@ enum ErrorKind: byte {
|
||||
OpNotAvaiable,
|
||||
WorkerInitFailed,
|
||||
UnixError,
|
||||
ImportMapError,
|
||||
}
|
||||
|
||||
table Cwd {}
|
||||
|
25
cli/ops.rs
25
cli/ops.rs
@ -29,6 +29,7 @@ use crate::worker::Worker;
|
||||
use deno::js_check;
|
||||
use deno::Buf;
|
||||
use deno::JSError;
|
||||
//use deno::Loader;
|
||||
use deno::Op;
|
||||
use deno::PinnedBuf;
|
||||
use flatbuffers::FlatBufferBuilder;
|
||||
@ -499,10 +500,30 @@ fn op_fetch_module_meta_data(
|
||||
let use_cache = !state.flags.reload;
|
||||
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
|
||||
.dir
|
||||
.fetch_module_meta_data_async(specifier, referrer, use_cache, no_fetch)
|
||||
.and_then(move |out| {
|
||||
.fetch_module_meta_data_async(
|
||||
&resolved_specifier,
|
||||
referrer,
|
||||
use_cache,
|
||||
no_fetch,
|
||||
).and_then(move |out| {
|
||||
let builder = &mut FlatBufferBuilder::new();
|
||||
let data_off = builder.create_vector(out.source_code.as_slice());
|
||||
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::flags;
|
||||
use crate::global_timer::GlobalTimer;
|
||||
use crate::import_map::ImportMap;
|
||||
use crate::msg;
|
||||
use crate::ops;
|
||||
use crate::permissions::DenoPermissions;
|
||||
@ -57,6 +58,7 @@ pub struct ThreadSafeState(Arc<State>);
|
||||
|
||||
#[cfg_attr(feature = "cargo-clippy", allow(stutter))]
|
||||
pub struct State {
|
||||
pub main_module: Option<String>,
|
||||
pub dir: deno_dir::DenoDir,
|
||||
pub argv: Vec<String>,
|
||||
pub permissions: DenoPermissions,
|
||||
@ -67,6 +69,9 @@ pub struct State {
|
||||
/// When flags contains a `.config_path` option, the fully qualified path
|
||||
/// name of the passed path will be resolved and set.
|
||||
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 worker_channels: Mutex<WorkerChannels>,
|
||||
pub global_timer: Mutex<GlobalTimer>,
|
||||
@ -111,9 +116,10 @@ pub fn fetch_module_meta_data_and_maybe_compile_async(
|
||||
let state_ = state.clone();
|
||||
let specifier = specifier.to_string();
|
||||
let referrer = referrer.to_string();
|
||||
let is_root = referrer == ".";
|
||||
|
||||
let f =
|
||||
futures::future::result(ThreadSafeState::resolve(&specifier, &referrer));
|
||||
futures::future::result(state.resolve(&specifier, &referrer, is_root));
|
||||
f.and_then(move |module_id| {
|
||||
let use_cache = !state_.flags.reload || state_.has_compiled(&module_id);
|
||||
let no_fetch = state_.flags.no_fetch;
|
||||
@ -157,7 +163,28 @@ pub fn fetch_module_meta_data_and_maybe_compile(
|
||||
impl Loader for ThreadSafeState {
|
||||
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)
|
||||
}
|
||||
|
||||
@ -233,14 +260,50 @@ impl ThreadSafeState {
|
||||
_ => 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 {
|
||||
dir: deno_dir::DenoDir::new(custom_root, &config, progress.clone())
|
||||
.unwrap(),
|
||||
main_module,
|
||||
dir,
|
||||
argv: argv_rest,
|
||||
permissions: DenoPermissions::from_flags(&flags),
|
||||
flags,
|
||||
config,
|
||||
config_path,
|
||||
import_map,
|
||||
metrics: Metrics::default(),
|
||||
worker_channels: Mutex::new(internal_channels),
|
||||
global_timer: Mutex::new(GlobalTimer::new()),
|
||||
@ -255,18 +318,9 @@ impl ThreadSafeState {
|
||||
|
||||
/// Read main module from argv
|
||||
pub fn main_module(&self) -> Option<String> {
|
||||
if self.argv.len() <= 1 {
|
||||
None
|
||||
} else {
|
||||
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
|
||||
}
|
||||
}
|
||||
match &self.main_module {
|
||||
Some(url) => Some(url.to_string()),
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -43,7 +43,12 @@ pub trait Loader: Send + Sync {
|
||||
/// When implementing an spec-complaint VM, this should be exactly the
|
||||
/// algorithm described here:
|
||||
/// 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.
|
||||
fn load(&self, url: &str) -> Box<SourceCodeInfoFuture<Self::Error>>;
|
||||
@ -98,17 +103,15 @@ impl<L: Loader> RecursiveLoad<L> {
|
||||
referrer: &str,
|
||||
parent_id: Option<deno_mod>,
|
||||
) -> 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();
|
||||
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.
|
||||
@ -251,7 +254,9 @@ impl<L: Loader> Future for RecursiveLoad<L> {
|
||||
|specifier: &str, referrer_id: deno_mod| -> deno_mod {
|
||||
let modules = self.modules.lock().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) {
|
||||
Some(id) => id,
|
||||
None => 0,
|
||||
@ -619,7 +624,12 @@ mod tests {
|
||||
impl Loader for MockLoader {
|
||||
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);
|
||||
let output_specifier =
|
||||
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-write=<allow-write> Allow file system write access
|
||||
-c, --config <FILE> Load compiler configuration file
|
||||
--importmap <FILE> Load import map file
|
||||
--v8-flags=<v8-flags> Set V8 command line options
|
||||
|
||||
SUBCOMMANDS:
|
||||
@ -676,6 +677,50 @@ Particularly useful ones:
|
||||
--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
|
||||
|
||||
### Deno and Linux analogy
|
||||
|
Loading…
Reference in New Issue
Block a user