mirror of
https://github.com/denoland/deno
synced 2024-08-27 12:00:17 +00:00
parent
45425c1146
commit
f5eb177f50
8
Cargo.lock
generated
8
Cargo.lock
generated
|
@ -782,9 +782,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "deno_doc"
|
name = "deno_doc"
|
||||||
version = "0.19.0"
|
version = "0.20.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "08abadd9f3ede74c5ba6e3d9a688ecfe160cf7fb2988ae133ef4e3d591d091e7"
|
checksum = "6d2d76b6b75a6fbfda0f529e310fc3cab960f4219403280b430ce93dcf8cf9a2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if 1.0.0",
|
"cfg-if 1.0.0",
|
||||||
"deno_ast",
|
"deno_ast",
|
||||||
|
@ -827,9 +827,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "deno_graph"
|
name = "deno_graph"
|
||||||
version = "0.10.0"
|
version = "0.11.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6df7e1b135780d9424ce4fb9a8927983d27d2c094922cb84b5fd5d72a4c85b82"
|
checksum = "f6d84ddee0cf83bf295721be792b6769b92214983bda29d52c2b05a89d1e968f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"cfg-if 1.0.0",
|
"cfg-if 1.0.0",
|
||||||
|
|
|
@ -41,8 +41,8 @@ winres = "0.1.11"
|
||||||
[dependencies]
|
[dependencies]
|
||||||
deno_ast = { version = "0.5.0", features = ["bundler", "codegen", "dep_graph", "module_specifier", "proposal", "react", "sourcemap", "transforms", "typescript", "view", "visit"] }
|
deno_ast = { version = "0.5.0", features = ["bundler", "codegen", "dep_graph", "module_specifier", "proposal", "react", "sourcemap", "transforms", "typescript", "view", "visit"] }
|
||||||
deno_core = { version = "0.105.0", path = "../core" }
|
deno_core = { version = "0.105.0", path = "../core" }
|
||||||
deno_doc = "0.19.0"
|
deno_doc = "0.20.0"
|
||||||
deno_graph = "0.10.0"
|
deno_graph = "0.11.1"
|
||||||
deno_lint = { version = "0.19.0", features = ["docs"] }
|
deno_lint = { version = "0.19.0", features = ["docs"] }
|
||||||
deno_runtime = { version = "0.31.0", path = "../runtime" }
|
deno_runtime = { version = "0.31.0", path = "../runtime" }
|
||||||
deno_tls = { version = "0.10.0", path = "../ext/tls" }
|
deno_tls = { version = "0.10.0", path = "../ext/tls" }
|
||||||
|
|
143
cli/ast/mod.rs
143
cli/ast/mod.rs
|
@ -122,15 +122,26 @@ pub struct EmitOptions {
|
||||||
pub inline_source_map: bool,
|
pub inline_source_map: bool,
|
||||||
/// Should the sources be inlined in the source map. Defaults to `true`.
|
/// Should the sources be inlined in the source map. Defaults to `true`.
|
||||||
pub inline_sources: bool,
|
pub inline_sources: bool,
|
||||||
// Should a corresponding .map file be created for the output. This should be
|
/// Should a corresponding .map file be created for the output. This should be
|
||||||
// false if inline_source_map is true. Defaults to `false`.
|
/// false if inline_source_map is true. Defaults to `false`.
|
||||||
pub source_map: bool,
|
pub source_map: bool,
|
||||||
|
/// `true` if the program should use an implicit JSX import source/the "new"
|
||||||
|
/// JSX transforms.
|
||||||
|
pub jsx_automatic: bool,
|
||||||
|
/// If JSX is automatic, if it is in development mode, meaning that it should
|
||||||
|
/// import `jsx-dev-runtime` and transform JSX using `jsxDEV` import from the
|
||||||
|
/// JSX import source as well as provide additional debug information to the
|
||||||
|
/// JSX factory.
|
||||||
|
pub jsx_development: bool,
|
||||||
/// When transforming JSX, what value should be used for the JSX factory.
|
/// When transforming JSX, what value should be used for the JSX factory.
|
||||||
/// Defaults to `React.createElement`.
|
/// Defaults to `React.createElement`.
|
||||||
pub jsx_factory: String,
|
pub jsx_factory: String,
|
||||||
/// When transforming JSX, what value should be used for the JSX fragment
|
/// When transforming JSX, what value should be used for the JSX fragment
|
||||||
/// factory. Defaults to `React.Fragment`.
|
/// factory. Defaults to `React.Fragment`.
|
||||||
pub jsx_fragment_factory: String,
|
pub jsx_fragment_factory: String,
|
||||||
|
/// The string module specifier to implicitly import JSX factories from when
|
||||||
|
/// transpiling JSX.
|
||||||
|
pub jsx_import_source: Option<String>,
|
||||||
/// Should JSX be transformed or preserved. Defaults to `true`.
|
/// Should JSX be transformed or preserved. Defaults to `true`.
|
||||||
pub transform_jsx: bool,
|
pub transform_jsx: bool,
|
||||||
/// Should import declarations be transformed to variable declarations.
|
/// Should import declarations be transformed to variable declarations.
|
||||||
|
@ -146,8 +157,11 @@ impl Default for EmitOptions {
|
||||||
inline_source_map: true,
|
inline_source_map: true,
|
||||||
inline_sources: true,
|
inline_sources: true,
|
||||||
source_map: false,
|
source_map: false,
|
||||||
|
jsx_automatic: false,
|
||||||
|
jsx_development: false,
|
||||||
jsx_factory: "React.createElement".into(),
|
jsx_factory: "React.createElement".into(),
|
||||||
jsx_fragment_factory: "React.Fragment".into(),
|
jsx_fragment_factory: "React.Fragment".into(),
|
||||||
|
jsx_import_source: None,
|
||||||
transform_jsx: true,
|
transform_jsx: true,
|
||||||
repl_imports: false,
|
repl_imports: false,
|
||||||
}
|
}
|
||||||
|
@ -164,15 +178,25 @@ impl From<config_file::TsConfig> for EmitOptions {
|
||||||
"error" => ImportsNotUsedAsValues::Error,
|
"error" => ImportsNotUsedAsValues::Error,
|
||||||
_ => ImportsNotUsedAsValues::Remove,
|
_ => ImportsNotUsedAsValues::Remove,
|
||||||
};
|
};
|
||||||
|
let (transform_jsx, jsx_automatic, jsx_development) =
|
||||||
|
match options.jsx.as_str() {
|
||||||
|
"react" => (true, false, false),
|
||||||
|
"react-jsx" => (true, true, false),
|
||||||
|
"react-jsxdev" => (true, true, true),
|
||||||
|
_ => (false, false, false),
|
||||||
|
};
|
||||||
EmitOptions {
|
EmitOptions {
|
||||||
emit_metadata: options.emit_decorator_metadata,
|
emit_metadata: options.emit_decorator_metadata,
|
||||||
imports_not_used_as_values,
|
imports_not_used_as_values,
|
||||||
inline_source_map: options.inline_source_map,
|
inline_source_map: options.inline_source_map,
|
||||||
inline_sources: options.inline_sources,
|
inline_sources: options.inline_sources,
|
||||||
source_map: options.source_map,
|
source_map: options.source_map,
|
||||||
|
jsx_automatic,
|
||||||
|
jsx_development,
|
||||||
jsx_factory: options.jsx_factory,
|
jsx_factory: options.jsx_factory,
|
||||||
jsx_fragment_factory: options.jsx_fragment_factory,
|
jsx_fragment_factory: options.jsx_fragment_factory,
|
||||||
transform_jsx: options.jsx == "react",
|
jsx_import_source: options.jsx_import_source,
|
||||||
|
transform_jsx,
|
||||||
repl_imports: false,
|
repl_imports: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -355,6 +379,13 @@ fn fold_program(
|
||||||
// this will use `Object.assign()` instead of the `_extends` helper
|
// this will use `Object.assign()` instead of the `_extends` helper
|
||||||
// when spreading props.
|
// when spreading props.
|
||||||
use_builtins: true,
|
use_builtins: true,
|
||||||
|
runtime: if options.jsx_automatic {
|
||||||
|
Some(react::Runtime::Automatic)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
},
|
||||||
|
development: options.jsx_development,
|
||||||
|
import_source: options.jsx_import_source.clone().unwrap_or_default(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
top_level_mark,
|
top_level_mark,
|
||||||
|
@ -495,6 +526,112 @@ function App() {
|
||||||
assert_eq!(&code[..expected.len()], expected);
|
assert_eq!(&code[..expected.len()], expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_transpile_jsx_import_source_pragma() {
|
||||||
|
let specifier = resolve_url_or_path("https://deno.land/x/mod.tsx")
|
||||||
|
.expect("could not resolve specifier");
|
||||||
|
let source = r#"
|
||||||
|
/** @jsxImportSource jsx_lib */
|
||||||
|
|
||||||
|
function App() {
|
||||||
|
return (
|
||||||
|
<div><></></div>
|
||||||
|
);
|
||||||
|
}"#;
|
||||||
|
let module = parse_module(ParseParams {
|
||||||
|
specifier: specifier.as_str().to_string(),
|
||||||
|
source: SourceTextInfo::from_string(source.to_string()),
|
||||||
|
media_type: deno_ast::MediaType::Jsx,
|
||||||
|
capture_tokens: false,
|
||||||
|
maybe_syntax: None,
|
||||||
|
scope_analysis: true,
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
let (code, _) = transpile(&module, &EmitOptions::default()).unwrap();
|
||||||
|
let expected = r#"import { jsx as _jsx, Fragment as _Fragment } from "jsx_lib/jsx-runtime";
|
||||||
|
/** @jsxImportSource jsx_lib */ function App() {
|
||||||
|
return(/*#__PURE__*/ _jsx("div", {
|
||||||
|
children: /*#__PURE__*/ _jsx(_Fragment, {
|
||||||
|
})
|
||||||
|
}));
|
||||||
|
"#;
|
||||||
|
assert_eq!(&code[..expected.len()], expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_transpile_jsx_import_source_no_pragma() {
|
||||||
|
let specifier = resolve_url_or_path("https://deno.land/x/mod.tsx")
|
||||||
|
.expect("could not resolve specifier");
|
||||||
|
let source = r#"
|
||||||
|
function App() {
|
||||||
|
return (
|
||||||
|
<div><></></div>
|
||||||
|
);
|
||||||
|
}"#;
|
||||||
|
let module = parse_module(ParseParams {
|
||||||
|
specifier: specifier.as_str().to_string(),
|
||||||
|
source: SourceTextInfo::from_string(source.to_string()),
|
||||||
|
media_type: deno_ast::MediaType::Jsx,
|
||||||
|
capture_tokens: false,
|
||||||
|
maybe_syntax: None,
|
||||||
|
scope_analysis: true,
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
let emit_options = EmitOptions {
|
||||||
|
jsx_automatic: true,
|
||||||
|
jsx_import_source: Some("jsx_lib".to_string()),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
let (code, _) = transpile(&module, &emit_options).unwrap();
|
||||||
|
let expected = r#"import { jsx as _jsx, Fragment as _Fragment } from "jsx_lib/jsx-runtime";
|
||||||
|
function App() {
|
||||||
|
return(/*#__PURE__*/ _jsx("div", {
|
||||||
|
children: /*#__PURE__*/ _jsx(_Fragment, {
|
||||||
|
})
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
"#;
|
||||||
|
assert_eq!(&code[..expected.len()], expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(@kitsonk) https://github.com/swc-project/swc/issues/2656
|
||||||
|
// #[test]
|
||||||
|
// fn test_transpile_jsx_import_source_no_pragma_dev() {
|
||||||
|
// let specifier = resolve_url_or_path("https://deno.land/x/mod.tsx")
|
||||||
|
// .expect("could not resolve specifier");
|
||||||
|
// let source = r#"
|
||||||
|
// function App() {
|
||||||
|
// return (
|
||||||
|
// <div><></></div>
|
||||||
|
// );
|
||||||
|
// }"#;
|
||||||
|
// let module = parse_module(ParseParams {
|
||||||
|
// specifier: specifier.as_str().to_string(),
|
||||||
|
// source: SourceTextInfo::from_string(source.to_string()),
|
||||||
|
// media_type: deno_ast::MediaType::Jsx,
|
||||||
|
// capture_tokens: false,
|
||||||
|
// maybe_syntax: None,
|
||||||
|
// scope_analysis: true,
|
||||||
|
// })
|
||||||
|
// .unwrap();
|
||||||
|
// let emit_options = EmitOptions {
|
||||||
|
// jsx_automatic: true,
|
||||||
|
// jsx_import_source: Some("jsx_lib".to_string()),
|
||||||
|
// jsx_development: true,
|
||||||
|
// ..Default::default()
|
||||||
|
// };
|
||||||
|
// let (code, _) = transpile(&module, &emit_options).unwrap();
|
||||||
|
// let expected = r#"import { jsx as _jsx, Fragment as _Fragment } from "jsx_lib/jsx-dev-runtime";
|
||||||
|
// function App() {
|
||||||
|
// return(/*#__PURE__*/ _jsx("div", {
|
||||||
|
// children: /*#__PURE__*/ _jsx(_Fragment, {
|
||||||
|
// })
|
||||||
|
// }));
|
||||||
|
// }
|
||||||
|
// "#;
|
||||||
|
// assert_eq!(&code[..expected.len()], expected);
|
||||||
|
// }
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_transpile_decorators() {
|
fn test_transpile_decorators() {
|
||||||
let specifier = resolve_url_or_path("https://deno.land/x/mod.ts")
|
let specifier = resolve_url_or_path("https://deno.land/x/mod.ts")
|
||||||
|
|
|
@ -14,12 +14,12 @@ use regex::Regex;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub(crate) struct NodeEsmResolver<'a> {
|
pub(crate) struct NodeEsmResolver {
|
||||||
maybe_import_map_resolver: Option<ImportMapResolver<'a>>,
|
maybe_import_map_resolver: Option<ImportMapResolver>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> NodeEsmResolver<'a> {
|
impl NodeEsmResolver {
|
||||||
pub fn new(maybe_import_map_resolver: Option<ImportMapResolver<'a>>) -> Self {
|
pub fn new(maybe_import_map_resolver: Option<ImportMapResolver>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
maybe_import_map_resolver,
|
maybe_import_map_resolver,
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,7 @@ impl<'a> NodeEsmResolver<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Resolver for NodeEsmResolver<'_> {
|
impl Resolver for NodeEsmResolver {
|
||||||
fn resolve(
|
fn resolve(
|
||||||
&self,
|
&self,
|
||||||
specifier: &str,
|
specifier: &str,
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
|
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
|
||||||
|
|
||||||
use crate::fs_util::canonicalize_path;
|
use crate::fs_util::canonicalize_path;
|
||||||
|
|
||||||
use deno_core::error::anyhow;
|
use deno_core::error::anyhow;
|
||||||
|
use deno_core::error::custom_error;
|
||||||
use deno_core::error::AnyError;
|
use deno_core::error::AnyError;
|
||||||
use deno_core::error::Context;
|
use deno_core::error::Context;
|
||||||
use deno_core::serde::Deserialize;
|
use deno_core::serde::Deserialize;
|
||||||
|
@ -17,6 +19,9 @@ use std::fmt;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
pub(crate) type MaybeImportsResult =
|
||||||
|
Result<Option<Vec<(ModuleSpecifier, Vec<String>)>>, AnyError>;
|
||||||
|
|
||||||
/// The transpile options that are significant out of a user provided tsconfig
|
/// The transpile options that are significant out of a user provided tsconfig
|
||||||
/// file, that we want to deserialize out of the final config for a transpile.
|
/// file, that we want to deserialize out of the final config for a transpile.
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
|
@ -31,6 +36,7 @@ pub struct EmitConfigOptions {
|
||||||
pub jsx: String,
|
pub jsx: String,
|
||||||
pub jsx_factory: String,
|
pub jsx_factory: String,
|
||||||
pub jsx_fragment_factory: String,
|
pub jsx_fragment_factory: String,
|
||||||
|
pub jsx_import_source: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// There are certain compiler options that can impact what modules are part of
|
/// There are certain compiler options that can impact what modules are part of
|
||||||
|
@ -38,6 +44,8 @@ pub struct EmitConfigOptions {
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct CompilerOptions {
|
pub struct CompilerOptions {
|
||||||
|
pub jsx: Option<String>,
|
||||||
|
pub jsx_import_source: Option<String>,
|
||||||
pub types: Option<Vec<String>>,
|
pub types: Option<Vec<String>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -404,15 +412,50 @@ impl ConfigFile {
|
||||||
|
|
||||||
/// If the configuration file contains "extra" modules (like TypeScript
|
/// If the configuration file contains "extra" modules (like TypeScript
|
||||||
/// `"types"`) options, return them as imports to be added to a module graph.
|
/// `"types"`) options, return them as imports to be added to a module graph.
|
||||||
pub fn to_maybe_imports(
|
pub fn to_maybe_imports(&self) -> MaybeImportsResult {
|
||||||
&self,
|
let mut imports = Vec::new();
|
||||||
) -> Option<Vec<(ModuleSpecifier, Vec<String>)>> {
|
let compiler_options_value =
|
||||||
|
if let Some(value) = self.json.compiler_options.as_ref() {
|
||||||
|
value
|
||||||
|
} else {
|
||||||
|
return Ok(None);
|
||||||
|
};
|
||||||
|
let compiler_options: CompilerOptions =
|
||||||
|
serde_json::from_value(compiler_options_value.clone())?;
|
||||||
|
let referrer = ModuleSpecifier::from_file_path(&self.path)
|
||||||
|
.map_err(|_| custom_error("TypeError", "bad config file specifier"))?;
|
||||||
|
if let Some(types) = compiler_options.types {
|
||||||
|
imports.extend(types);
|
||||||
|
}
|
||||||
|
if compiler_options.jsx == Some("react-jsx".to_string()) {
|
||||||
|
imports.push(format!(
|
||||||
|
"{}/jsx-runtime",
|
||||||
|
compiler_options.jsx_import_source.ok_or_else(|| custom_error("TypeError", "Compiler option 'jsx' set to 'react-jsx', but no 'jsxImportSource' defined."))?
|
||||||
|
));
|
||||||
|
} else if compiler_options.jsx == Some("react-jsxdev".to_string()) {
|
||||||
|
imports.push(format!(
|
||||||
|
"{}/jsx-dev-runtime",
|
||||||
|
compiler_options.jsx_import_source.ok_or_else(|| custom_error("TypeError", "Compiler option 'jsx' set to 'react-jsxdev', but no 'jsxImportSource' defined."))?
|
||||||
|
));
|
||||||
|
}
|
||||||
|
if !imports.is_empty() {
|
||||||
|
Ok(Some(vec![(referrer, imports)]))
|
||||||
|
} else {
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Based on the compiler options in the configuration file, return the
|
||||||
|
/// implied JSX import source module.
|
||||||
|
pub fn to_maybe_jsx_import_source_module(&self) -> Option<String> {
|
||||||
let compiler_options_value = self.json.compiler_options.as_ref()?;
|
let compiler_options_value = self.json.compiler_options.as_ref()?;
|
||||||
let compiler_options: CompilerOptions =
|
let compiler_options: CompilerOptions =
|
||||||
serde_json::from_value(compiler_options_value.clone()).ok()?;
|
serde_json::from_value(compiler_options_value.clone()).ok()?;
|
||||||
let referrer = ModuleSpecifier::from_file_path(&self.path).ok()?;
|
match compiler_options.jsx.as_deref() {
|
||||||
let types = compiler_options.types?;
|
Some("react-jsx") => Some("jsx-runtime".to_string()),
|
||||||
Some(vec![(referrer, types)])
|
Some("react-jsxdev") => Some("jsx-dev-runtime".to_string()),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_fmt_config(&self) -> Result<Option<FmtConfig>, AnyError> {
|
pub fn to_fmt_config(&self) -> Result<Option<FmtConfig>, AnyError> {
|
||||||
|
|
9
cli/dts/lib.deno.unstable.d.ts
vendored
9
cli/dts/lib.deno.unstable.d.ts
vendored
|
@ -277,15 +277,20 @@ declare namespace Deno {
|
||||||
/** Emit the source alongside the source maps within a single file; requires
|
/** Emit the source alongside the source maps within a single file; requires
|
||||||
* `inlineSourceMap` or `sourceMap` to be set. Defaults to `false`. */
|
* `inlineSourceMap` or `sourceMap` to be set. Defaults to `false`. */
|
||||||
inlineSources?: boolean;
|
inlineSources?: boolean;
|
||||||
/** Support JSX in `.tsx` files: `"react"`, `"preserve"`, `"react-native"`.
|
/** Support JSX in `.tsx` files: `"react"`, `"preserve"`, `"react-native"`,
|
||||||
|
* `"react-jsx", `"react-jsxdev"`.
|
||||||
* Defaults to `"react"`. */
|
* Defaults to `"react"`. */
|
||||||
jsx?: "react" | "preserve" | "react-native";
|
jsx?: "react" | "preserve" | "react-native" | "react-jsx" | "react-jsx-dev";
|
||||||
/** Specify the JSX factory function to use when targeting react JSX emit,
|
/** Specify the JSX factory function to use when targeting react JSX emit,
|
||||||
* e.g. `React.createElement` or `h`. Defaults to `React.createElement`. */
|
* e.g. `React.createElement` or `h`. Defaults to `React.createElement`. */
|
||||||
jsxFactory?: string;
|
jsxFactory?: string;
|
||||||
/** Specify the JSX fragment factory function to use when targeting react
|
/** Specify the JSX fragment factory function to use when targeting react
|
||||||
* JSX emit, e.g. `Fragment`. Defaults to `React.Fragment`. */
|
* JSX emit, e.g. `Fragment`. Defaults to `React.Fragment`. */
|
||||||
jsxFragmentFactory?: string;
|
jsxFragmentFactory?: string;
|
||||||
|
/** Declares the module specifier to be used for importing the `jsx` and
|
||||||
|
* `jsxs` factory functions when using jsx as `"react-jsx"` or
|
||||||
|
* `"react-jsxdev"`. Defaults to `"react"`. */
|
||||||
|
jsxImportSource?: string;
|
||||||
/** Resolve keyof to string valued property names only (no numbers or
|
/** Resolve keyof to string valued property names only (no numbers or
|
||||||
* symbols). Defaults to `false`. */
|
* symbols). Defaults to `false`. */
|
||||||
keyofStringsOnly?: string;
|
keyofStringsOnly?: string;
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
|
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
|
||||||
use crate::auth_tokens::AuthToken;
|
use crate::auth_tokens::AuthToken;
|
||||||
|
|
||||||
|
use deno_core::error::custom_error;
|
||||||
use deno_core::error::generic_error;
|
use deno_core::error::generic_error;
|
||||||
use deno_core::error::AnyError;
|
use deno_core::error::AnyError;
|
||||||
use deno_core::url::Url;
|
use deno_core::url::Url;
|
||||||
|
@ -123,11 +124,18 @@ pub async fn fetch_once(
|
||||||
|
|
||||||
if response.status().is_client_error() || response.status().is_server_error()
|
if response.status().is_client_error() || response.status().is_server_error()
|
||||||
{
|
{
|
||||||
let err = generic_error(format!(
|
let err = if response.status() == StatusCode::NOT_FOUND {
|
||||||
"Import '{}' failed: {}",
|
custom_error(
|
||||||
args.url,
|
"NotFound",
|
||||||
response.status()
|
format!("Import '{}' failed, not found.", args.url),
|
||||||
));
|
)
|
||||||
|
} else {
|
||||||
|
generic_error(format!(
|
||||||
|
"Import '{}' failed: {}",
|
||||||
|
args.url,
|
||||||
|
response.status()
|
||||||
|
))
|
||||||
|
};
|
||||||
return Err(err);
|
return Err(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,9 +2,11 @@
|
||||||
|
|
||||||
use crate::cache::CacherLoader;
|
use crate::cache::CacherLoader;
|
||||||
use crate::cache::FetchCacher;
|
use crate::cache::FetchCacher;
|
||||||
|
use crate::config_file::ConfigFile;
|
||||||
use crate::flags::Flags;
|
use crate::flags::Flags;
|
||||||
use crate::proc_state::ProcState;
|
use crate::proc_state::ProcState;
|
||||||
use crate::resolver::ImportMapResolver;
|
use crate::resolver::ImportMapResolver;
|
||||||
|
use crate::resolver::JsxResolver;
|
||||||
|
|
||||||
use deno_core::error::anyhow;
|
use deno_core::error::anyhow;
|
||||||
use deno_core::error::AnyError;
|
use deno_core::error::AnyError;
|
||||||
|
@ -13,6 +15,7 @@ use deno_runtime::permissions::Permissions;
|
||||||
use deno_runtime::tokio_util::create_basic_runtime;
|
use deno_runtime::tokio_util::create_basic_runtime;
|
||||||
use import_map::ImportMap;
|
use import_map::ImportMap;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
use std::sync::Arc;
|
||||||
use std::thread;
|
use std::thread;
|
||||||
use tokio::sync::mpsc;
|
use tokio::sync::mpsc;
|
||||||
use tokio::sync::oneshot;
|
use tokio::sync::oneshot;
|
||||||
|
@ -27,7 +30,8 @@ pub(crate) struct CacheServer(mpsc::UnboundedSender<Request>);
|
||||||
impl CacheServer {
|
impl CacheServer {
|
||||||
pub async fn new(
|
pub async fn new(
|
||||||
maybe_cache_path: Option<PathBuf>,
|
maybe_cache_path: Option<PathBuf>,
|
||||||
maybe_import_map: Option<ImportMap>,
|
maybe_import_map: Option<Arc<ImportMap>>,
|
||||||
|
maybe_config_file: Option<ConfigFile>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let (tx, mut rx) = mpsc::unbounded_channel::<Request>();
|
let (tx, mut rx) = mpsc::unbounded_channel::<Request>();
|
||||||
let _join_handle = thread::spawn(move || {
|
let _join_handle = thread::spawn(move || {
|
||||||
|
@ -39,8 +43,26 @@ impl CacheServer {
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let maybe_resolver =
|
let maybe_import_map_resolver =
|
||||||
maybe_import_map.as_ref().map(ImportMapResolver::new);
|
maybe_import_map.map(ImportMapResolver::new);
|
||||||
|
let maybe_jsx_resolver = maybe_config_file
|
||||||
|
.as_ref()
|
||||||
|
.map(|cf| {
|
||||||
|
cf.to_maybe_jsx_import_source_module()
|
||||||
|
.map(|im| JsxResolver::new(im, maybe_import_map_resolver.clone()))
|
||||||
|
})
|
||||||
|
.flatten();
|
||||||
|
let maybe_resolver = if maybe_jsx_resolver.is_some() {
|
||||||
|
maybe_jsx_resolver.as_ref().map(|jr| jr.as_resolver())
|
||||||
|
} else {
|
||||||
|
maybe_import_map_resolver
|
||||||
|
.as_ref()
|
||||||
|
.map(|im| im.as_resolver())
|
||||||
|
};
|
||||||
|
let maybe_imports = maybe_config_file
|
||||||
|
.map(|cf| cf.to_maybe_imports().ok())
|
||||||
|
.flatten()
|
||||||
|
.flatten();
|
||||||
let mut cache = FetchCacher::new(
|
let mut cache = FetchCacher::new(
|
||||||
ps.dir.gen_cache.clone(),
|
ps.dir.gen_cache.clone(),
|
||||||
ps.file_fetcher.clone(),
|
ps.file_fetcher.clone(),
|
||||||
|
@ -52,9 +74,9 @@ impl CacheServer {
|
||||||
let graph = deno_graph::create_graph(
|
let graph = deno_graph::create_graph(
|
||||||
roots,
|
roots,
|
||||||
false,
|
false,
|
||||||
None,
|
maybe_imports.clone(),
|
||||||
cache.as_mut_loader(),
|
cache.as_mut_loader(),
|
||||||
maybe_resolver.as_ref().map(|r| r.as_resolver()),
|
maybe_resolver,
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,14 +1,16 @@
|
||||||
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
|
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
|
||||||
|
|
||||||
use super::resolver::ImportMapResolver;
|
|
||||||
use super::text::LineIndex;
|
use super::text::LineIndex;
|
||||||
use super::tsc;
|
use super::tsc;
|
||||||
|
|
||||||
|
use crate::config_file::ConfigFile;
|
||||||
use crate::file_fetcher::get_source_from_bytes;
|
use crate::file_fetcher::get_source_from_bytes;
|
||||||
use crate::file_fetcher::map_content_type;
|
use crate::file_fetcher::map_content_type;
|
||||||
use crate::file_fetcher::SUPPORTED_SCHEMES;
|
use crate::file_fetcher::SUPPORTED_SCHEMES;
|
||||||
use crate::http_cache;
|
use crate::http_cache;
|
||||||
use crate::http_cache::HttpCache;
|
use crate::http_cache::HttpCache;
|
||||||
|
use crate::resolver::ImportMapResolver;
|
||||||
|
use crate::resolver::JsxResolver;
|
||||||
use crate::text_encoding;
|
use crate::text_encoding;
|
||||||
|
|
||||||
use deno_ast::MediaType;
|
use deno_ast::MediaType;
|
||||||
|
@ -19,6 +21,7 @@ use deno_core::parking_lot::Mutex;
|
||||||
use deno_core::url;
|
use deno_core::url;
|
||||||
use deno_core::ModuleSpecifier;
|
use deno_core::ModuleSpecifier;
|
||||||
use lspower::lsp;
|
use lspower::lsp;
|
||||||
|
use std::collections::BTreeMap;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
|
@ -131,6 +134,59 @@ impl IndexValid {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO(@kitsonk) expose the synthetic module from deno_graph
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct SyntheticModule {
|
||||||
|
dependencies: BTreeMap<String, deno_graph::Resolved>,
|
||||||
|
specifier: ModuleSpecifier,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SyntheticModule {
|
||||||
|
pub fn new(
|
||||||
|
specifier: ModuleSpecifier,
|
||||||
|
dependencies: Vec<(String, Option<lsp::Range>)>,
|
||||||
|
maybe_resolver: Option<&dyn deno_graph::source::Resolver>,
|
||||||
|
) -> Self {
|
||||||
|
let dependencies = dependencies
|
||||||
|
.iter()
|
||||||
|
.map(|(dep, maybe_range)| {
|
||||||
|
let range = to_deno_graph_range(&specifier, maybe_range.as_ref());
|
||||||
|
let result = if let Some(resolver) = maybe_resolver {
|
||||||
|
resolver.resolve(dep, &specifier).map_err(|err| {
|
||||||
|
if let Some(specifier_error) =
|
||||||
|
err.downcast_ref::<deno_graph::SpecifierError>()
|
||||||
|
{
|
||||||
|
deno_graph::ResolutionError::InvalidSpecifier(
|
||||||
|
specifier_error.clone(),
|
||||||
|
range.clone(),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
deno_graph::ResolutionError::ResolverError(
|
||||||
|
Arc::new(err),
|
||||||
|
dep.to_string(),
|
||||||
|
range.clone(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
deno_core::resolve_import(dep, specifier.as_str()).map_err(|err| {
|
||||||
|
deno_graph::ResolutionError::ResolverError(
|
||||||
|
Arc::new(err.into()),
|
||||||
|
dep.to_string(),
|
||||||
|
range.clone(),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
};
|
||||||
|
(dep.to_string(), Some(result.map(|s| (s, range))))
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
Self {
|
||||||
|
dependencies,
|
||||||
|
specifier,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(crate) struct Document {
|
pub(crate) struct Document {
|
||||||
line_index: Arc<LineIndex>,
|
line_index: Arc<LineIndex>,
|
||||||
|
@ -347,6 +403,32 @@ pub(crate) fn to_lsp_range(range: &deno_graph::Range) -> lsp::Range {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn to_deno_graph_range(
|
||||||
|
specifier: &ModuleSpecifier,
|
||||||
|
maybe_range: Option<&lsp::Range>,
|
||||||
|
) -> deno_graph::Range {
|
||||||
|
let specifier = specifier.clone();
|
||||||
|
if let Some(range) = maybe_range {
|
||||||
|
deno_graph::Range {
|
||||||
|
specifier,
|
||||||
|
start: deno_graph::Position {
|
||||||
|
line: range.start.line as usize,
|
||||||
|
character: range.start.character as usize,
|
||||||
|
},
|
||||||
|
end: deno_graph::Position {
|
||||||
|
line: range.end.line as usize,
|
||||||
|
character: range.end.character as usize,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
deno_graph::Range {
|
||||||
|
specifier,
|
||||||
|
start: deno_graph::Position::zeroed(),
|
||||||
|
end: deno_graph::Position::zeroed(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Recurse and collect specifiers that appear in the dependent map.
|
/// Recurse and collect specifiers that appear in the dependent map.
|
||||||
fn recurse_dependents(
|
fn recurse_dependents(
|
||||||
specifier: &ModuleSpecifier,
|
specifier: &ModuleSpecifier,
|
||||||
|
@ -376,8 +458,13 @@ struct Inner {
|
||||||
/// A map of documents that can either be "open" in the language server, or
|
/// A map of documents that can either be "open" in the language server, or
|
||||||
/// just present on disk.
|
/// just present on disk.
|
||||||
docs: HashMap<ModuleSpecifier, Document>,
|
docs: HashMap<ModuleSpecifier, Document>,
|
||||||
|
/// Any imports to the context supplied by configuration files. This is like
|
||||||
|
/// the imports into the a module graph in CLI.
|
||||||
|
imports: HashMap<ModuleSpecifier, SyntheticModule>,
|
||||||
/// The optional import map that should be used when resolving dependencies.
|
/// The optional import map that should be used when resolving dependencies.
|
||||||
maybe_import_map: Option<ImportMapResolver>,
|
maybe_import_map: Option<ImportMapResolver>,
|
||||||
|
/// The optional JSX resolver, which is used when JSX imports are configured.
|
||||||
|
maybe_jsx_resolver: Option<JsxResolver>,
|
||||||
redirects: HashMap<ModuleSpecifier, ModuleSpecifier>,
|
redirects: HashMap<ModuleSpecifier, ModuleSpecifier>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -388,7 +475,9 @@ impl Inner {
|
||||||
dirty: true,
|
dirty: true,
|
||||||
dependents_map: HashMap::default(),
|
dependents_map: HashMap::default(),
|
||||||
docs: HashMap::default(),
|
docs: HashMap::default(),
|
||||||
|
imports: HashMap::default(),
|
||||||
maybe_import_map: None,
|
maybe_import_map: None,
|
||||||
|
maybe_jsx_resolver: None,
|
||||||
redirects: HashMap::default(),
|
redirects: HashMap::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -407,7 +496,7 @@ impl Inner {
|
||||||
version,
|
version,
|
||||||
None,
|
None,
|
||||||
content,
|
content,
|
||||||
self.maybe_import_map.as_ref().map(|r| r.as_resolver()),
|
self.get_maybe_resolver(),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
let cache_filename = self.cache.get_cache_filename(&specifier)?;
|
let cache_filename = self.cache.get_cache_filename(&specifier)?;
|
||||||
|
@ -421,7 +510,7 @@ impl Inner {
|
||||||
version,
|
version,
|
||||||
maybe_headers,
|
maybe_headers,
|
||||||
content,
|
content,
|
||||||
self.maybe_import_map.as_ref().map(|r| r.as_resolver()),
|
self.get_maybe_resolver(),
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
self.dirty = true;
|
self.dirty = true;
|
||||||
|
@ -481,6 +570,14 @@ impl Inner {
|
||||||
version: i32,
|
version: i32,
|
||||||
changes: Vec<lsp::TextDocumentContentChangeEvent>,
|
changes: Vec<lsp::TextDocumentContentChangeEvent>,
|
||||||
) -> Result<(), AnyError> {
|
) -> Result<(), AnyError> {
|
||||||
|
// this duplicates the .get_resolver() method, because there is no easy
|
||||||
|
// way to avoid the double borrow of self that occurs here with getting the
|
||||||
|
// mut doc out.
|
||||||
|
let maybe_resolver = if self.maybe_jsx_resolver.is_some() {
|
||||||
|
self.maybe_jsx_resolver.as_ref().map(|jr| jr.as_resolver())
|
||||||
|
} else {
|
||||||
|
self.maybe_import_map.as_ref().map(|im| im.as_resolver())
|
||||||
|
};
|
||||||
let doc = self.docs.get_mut(specifier).map_or_else(
|
let doc = self.docs.get_mut(specifier).map_or_else(
|
||||||
|| {
|
|| {
|
||||||
Err(custom_error(
|
Err(custom_error(
|
||||||
|
@ -491,11 +588,7 @@ impl Inner {
|
||||||
Ok,
|
Ok,
|
||||||
)?;
|
)?;
|
||||||
self.dirty = true;
|
self.dirty = true;
|
||||||
doc.change(
|
doc.change(version, changes, maybe_resolver)
|
||||||
version,
|
|
||||||
changes,
|
|
||||||
self.maybe_import_map.as_ref().map(|r| r.as_resolver()),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn close(&mut self, specifier: &ModuleSpecifier) -> Result<(), AnyError> {
|
fn close(&mut self, specifier: &ModuleSpecifier) -> Result<(), AnyError> {
|
||||||
|
@ -518,8 +611,7 @@ impl Inner {
|
||||||
specifier: &str,
|
specifier: &str,
|
||||||
referrer: &ModuleSpecifier,
|
referrer: &ModuleSpecifier,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let maybe_resolver =
|
let maybe_resolver = self.get_maybe_resolver();
|
||||||
self.maybe_import_map.as_ref().map(|im| im.as_resolver());
|
|
||||||
let maybe_specifier = if let Some(resolver) = maybe_resolver {
|
let maybe_specifier = if let Some(resolver) = maybe_resolver {
|
||||||
resolver.resolve(specifier, referrer).ok()
|
resolver.resolve(specifier, referrer).ok()
|
||||||
} else {
|
} else {
|
||||||
|
@ -604,6 +696,14 @@ impl Inner {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_maybe_resolver(&self) -> Option<&dyn deno_graph::source::Resolver> {
|
||||||
|
if self.maybe_jsx_resolver.is_some() {
|
||||||
|
self.maybe_jsx_resolver.as_ref().map(|jr| jr.as_resolver())
|
||||||
|
} else {
|
||||||
|
self.maybe_import_map.as_ref().map(|im| im.as_resolver())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn get_maybe_types_for_dependency(
|
fn get_maybe_types_for_dependency(
|
||||||
&mut self,
|
&mut self,
|
||||||
dependency: &deno_graph::Dependency,
|
dependency: &deno_graph::Dependency,
|
||||||
|
@ -706,12 +806,13 @@ impl Inner {
|
||||||
language_id: LanguageId,
|
language_id: LanguageId,
|
||||||
content: Arc<String>,
|
content: Arc<String>,
|
||||||
) {
|
) {
|
||||||
|
let maybe_resolver = self.get_maybe_resolver();
|
||||||
let document_data = Document::open(
|
let document_data = Document::open(
|
||||||
specifier.clone(),
|
specifier.clone(),
|
||||||
version,
|
version,
|
||||||
language_id,
|
language_id,
|
||||||
content,
|
content,
|
||||||
self.maybe_import_map.as_ref().map(|r| r.as_resolver()),
|
maybe_resolver,
|
||||||
);
|
);
|
||||||
self.docs.insert(specifier, document_data);
|
self.docs.insert(specifier, document_data);
|
||||||
self.dirty = true;
|
self.dirty = true;
|
||||||
|
@ -758,6 +859,12 @@ impl Inner {
|
||||||
} else {
|
} else {
|
||||||
results.push(None);
|
results.push(None);
|
||||||
}
|
}
|
||||||
|
} else if let Some(Some(Ok((specifier, _)))) =
|
||||||
|
self.resolve_imports_dependency(&specifier)
|
||||||
|
{
|
||||||
|
// clone here to avoid double borrow of self
|
||||||
|
let specifier = specifier.clone();
|
||||||
|
results.push(self.resolve_dependency(&specifier));
|
||||||
} else {
|
} else {
|
||||||
results.push(None);
|
results.push(None);
|
||||||
}
|
}
|
||||||
|
@ -790,6 +897,22 @@ impl Inner {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Iterate through any "imported" modules, checking to see if a dependency
|
||||||
|
/// is available. This is used to provide "global" imports like the JSX import
|
||||||
|
/// source.
|
||||||
|
fn resolve_imports_dependency(
|
||||||
|
&self,
|
||||||
|
specifier: &str,
|
||||||
|
) -> Option<&deno_graph::Resolved> {
|
||||||
|
for module in self.imports.values() {
|
||||||
|
let maybe_dep = module.dependencies.get(specifier);
|
||||||
|
if maybe_dep.is_some() {
|
||||||
|
return maybe_dep;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
fn resolve_remote_specifier(
|
fn resolve_remote_specifier(
|
||||||
&self,
|
&self,
|
||||||
specifier: &ModuleSpecifier,
|
specifier: &ModuleSpecifier,
|
||||||
|
@ -832,15 +955,6 @@ impl Inner {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_import_map(
|
|
||||||
&mut self,
|
|
||||||
maybe_import_map: Option<Arc<import_map::ImportMap>>,
|
|
||||||
) {
|
|
||||||
// TODO update resolved dependencies?
|
|
||||||
self.maybe_import_map = maybe_import_map.map(ImportMapResolver::new);
|
|
||||||
self.dirty = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_location(&mut self, location: PathBuf) {
|
fn set_location(&mut self, location: PathBuf) {
|
||||||
// TODO update resolved dependencies?
|
// TODO update resolved dependencies?
|
||||||
self.cache = HttpCache::new(&location);
|
self.cache = HttpCache::new(&location);
|
||||||
|
@ -886,6 +1000,36 @@ impl Inner {
|
||||||
self.get(specifier).map(|d| d.source.clone())
|
self.get(specifier).map(|d| d.source.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn update_config(
|
||||||
|
&mut self,
|
||||||
|
maybe_import_map: Option<Arc<import_map::ImportMap>>,
|
||||||
|
maybe_config_file: Option<&ConfigFile>,
|
||||||
|
) {
|
||||||
|
// TODO(@kitsonk) update resolved dependencies?
|
||||||
|
self.maybe_import_map = maybe_import_map.map(ImportMapResolver::new);
|
||||||
|
self.maybe_jsx_resolver = maybe_config_file
|
||||||
|
.map(|cf| {
|
||||||
|
cf.to_maybe_jsx_import_source_module()
|
||||||
|
.map(|im| JsxResolver::new(im, self.maybe_import_map.clone()))
|
||||||
|
})
|
||||||
|
.flatten();
|
||||||
|
if let Some(Ok(Some(imports))) =
|
||||||
|
maybe_config_file.map(|cf| cf.to_maybe_imports())
|
||||||
|
{
|
||||||
|
for (referrer, dependencies) in imports {
|
||||||
|
let dependencies =
|
||||||
|
dependencies.into_iter().map(|s| (s, None)).collect();
|
||||||
|
let module = SyntheticModule::new(
|
||||||
|
referrer.clone(),
|
||||||
|
dependencies,
|
||||||
|
self.get_maybe_resolver(),
|
||||||
|
);
|
||||||
|
self.imports.insert(referrer, module);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.dirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
fn version(&mut self, specifier: &ModuleSpecifier) -> Option<String> {
|
fn version(&mut self, specifier: &ModuleSpecifier) -> Option<String> {
|
||||||
self.get(specifier).map(|d| {
|
self.get(specifier).map(|d| {
|
||||||
d.maybe_lsp_version
|
d.maybe_lsp_version
|
||||||
|
@ -1050,14 +1194,6 @@ impl Documents {
|
||||||
self.0.lock().resolve(specifiers, referrer)
|
self.0.lock().resolve(specifiers, referrer)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the optional import map for the document cache.
|
|
||||||
pub fn set_import_map(
|
|
||||||
&self,
|
|
||||||
maybe_import_map: Option<Arc<import_map::ImportMap>>,
|
|
||||||
) {
|
|
||||||
self.0.lock().set_import_map(maybe_import_map);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Update the location of the on disk cache for the document store.
|
/// Update the location of the on disk cache for the document store.
|
||||||
pub fn set_location(&self, location: PathBuf) {
|
pub fn set_location(&self, location: PathBuf) {
|
||||||
self.0.lock().set_location(location)
|
self.0.lock().set_location(location)
|
||||||
|
@ -1095,6 +1231,17 @@ impl Documents {
|
||||||
self.0.lock().text_info(specifier)
|
self.0.lock().text_info(specifier)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn update_config(
|
||||||
|
&self,
|
||||||
|
maybe_import_map: Option<Arc<import_map::ImportMap>>,
|
||||||
|
maybe_config_file: Option<&ConfigFile>,
|
||||||
|
) {
|
||||||
|
self
|
||||||
|
.0
|
||||||
|
.lock()
|
||||||
|
.update_config(maybe_import_map, maybe_config_file)
|
||||||
|
}
|
||||||
|
|
||||||
/// Return the version of a document in the document cache.
|
/// Return the version of a document in the document cache.
|
||||||
pub fn version(&self, specifier: &ModuleSpecifier) -> Option<String> {
|
pub fn version(&self, specifier: &ModuleSpecifier) -> Option<String> {
|
||||||
self.0.lock().version(specifier)
|
self.0.lock().version(specifier)
|
||||||
|
|
|
@ -116,7 +116,7 @@ pub(crate) struct Inner {
|
||||||
/// file which will be used by the Deno LSP.
|
/// file which will be used by the Deno LSP.
|
||||||
maybe_config_uri: Option<Url>,
|
maybe_config_uri: Option<Url>,
|
||||||
/// An optional import map which is used to resolve modules.
|
/// An optional import map which is used to resolve modules.
|
||||||
pub(crate) maybe_import_map: Option<ImportMap>,
|
pub(crate) maybe_import_map: Option<Arc<ImportMap>>,
|
||||||
/// The URL for the import map which is used to determine relative imports.
|
/// The URL for the import map which is used to determine relative imports.
|
||||||
maybe_import_map_uri: Option<Url>,
|
maybe_import_map_uri: Option<Url>,
|
||||||
/// A collection of measurements which instrument that performance of the LSP.
|
/// A collection of measurements which instrument that performance of the LSP.
|
||||||
|
@ -481,13 +481,13 @@ impl Inner {
|
||||||
)
|
)
|
||||||
})?
|
})?
|
||||||
};
|
};
|
||||||
let import_map =
|
let import_map = Arc::new(ImportMap::from_json(
|
||||||
ImportMap::from_json(&import_map_url.to_string(), &import_map_json)?;
|
&import_map_url.to_string(),
|
||||||
|
&import_map_json,
|
||||||
|
)?);
|
||||||
self.maybe_import_map_uri = Some(import_map_url);
|
self.maybe_import_map_uri = Some(import_map_url);
|
||||||
self.maybe_import_map = Some(import_map.clone());
|
self.maybe_import_map = Some(import_map);
|
||||||
self.documents.set_import_map(Some(Arc::new(import_map)));
|
|
||||||
} else {
|
} else {
|
||||||
self.documents.set_import_map(None);
|
|
||||||
self.maybe_import_map = None;
|
self.maybe_import_map = None;
|
||||||
}
|
}
|
||||||
self.performance.measure(mark);
|
self.performance.measure(mark);
|
||||||
|
@ -700,6 +700,10 @@ impl Inner {
|
||||||
if let Err(err) = self.update_registries().await {
|
if let Err(err) = self.update_registries().await {
|
||||||
self.client.show_message(MessageType::Warning, err).await;
|
self.client.show_message(MessageType::Warning, err).await;
|
||||||
}
|
}
|
||||||
|
self.documents.update_config(
|
||||||
|
self.maybe_import_map.clone(),
|
||||||
|
self.maybe_config_file.as_ref(),
|
||||||
|
);
|
||||||
|
|
||||||
self.performance.measure(mark);
|
self.performance.measure(mark);
|
||||||
Ok(InitializeResult {
|
Ok(InitializeResult {
|
||||||
|
@ -908,6 +912,10 @@ impl Inner {
|
||||||
if let Err(err) = self.diagnostics_server.update() {
|
if let Err(err) = self.diagnostics_server.update() {
|
||||||
error!("{}", err);
|
error!("{}", err);
|
||||||
}
|
}
|
||||||
|
self.documents.update_config(
|
||||||
|
self.maybe_import_map.clone(),
|
||||||
|
self.maybe_config_file.as_ref(),
|
||||||
|
);
|
||||||
|
|
||||||
self.performance.measure(mark);
|
self.performance.measure(mark);
|
||||||
}
|
}
|
||||||
|
@ -942,6 +950,10 @@ impl Inner {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if touched {
|
if touched {
|
||||||
|
self.documents.update_config(
|
||||||
|
self.maybe_import_map.clone(),
|
||||||
|
self.maybe_config_file.as_ref(),
|
||||||
|
);
|
||||||
self.diagnostics_server.invalidate_all().await;
|
self.diagnostics_server.invalidate_all().await;
|
||||||
if let Err(err) = self.diagnostics_server.update() {
|
if let Err(err) = self.diagnostics_server.update() {
|
||||||
error!("Cannot update diagnostics: {}", err);
|
error!("Cannot update diagnostics: {}", err);
|
||||||
|
@ -2624,6 +2636,7 @@ impl Inner {
|
||||||
CacheServer::new(
|
CacheServer::new(
|
||||||
self.maybe_cache_path.clone(),
|
self.maybe_cache_path.clone(),
|
||||||
self.maybe_import_map.clone(),
|
self.maybe_import_map.clone(),
|
||||||
|
self.maybe_config_file.clone(),
|
||||||
)
|
)
|
||||||
.await,
|
.await,
|
||||||
);
|
);
|
||||||
|
|
|
@ -19,7 +19,6 @@ mod path_to_regex;
|
||||||
mod performance;
|
mod performance;
|
||||||
mod refactor;
|
mod refactor;
|
||||||
mod registries;
|
mod registries;
|
||||||
mod resolver;
|
|
||||||
mod semantic_tokens;
|
mod semantic_tokens;
|
||||||
mod text;
|
mod text;
|
||||||
mod tsc;
|
mod tsc;
|
||||||
|
|
|
@ -1,33 +0,0 @@
|
||||||
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
|
|
||||||
|
|
||||||
use deno_core::error::AnyError;
|
|
||||||
use deno_core::ModuleSpecifier;
|
|
||||||
use deno_graph::source::Resolver;
|
|
||||||
use import_map::ImportMap;
|
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub(crate) struct ImportMapResolver(Arc<ImportMap>);
|
|
||||||
|
|
||||||
impl ImportMapResolver {
|
|
||||||
pub fn new(import_map: Arc<ImportMap>) -> Self {
|
|
||||||
Self(import_map)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn as_resolver(&self) -> &dyn Resolver {
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Resolver for ImportMapResolver {
|
|
||||||
fn resolve(
|
|
||||||
&self,
|
|
||||||
specifier: &str,
|
|
||||||
referrer: &ModuleSpecifier,
|
|
||||||
) -> Result<ModuleSpecifier, AnyError> {
|
|
||||||
self
|
|
||||||
.0
|
|
||||||
.resolve(specifier, referrer.as_str())
|
|
||||||
.map_err(|err| err.into())
|
|
||||||
}
|
|
||||||
}
|
|
71
cli/main.rs
71
cli/main.rs
|
@ -60,6 +60,7 @@ use crate::fmt_errors::PrettyJsError;
|
||||||
use crate::module_loader::CliModuleLoader;
|
use crate::module_loader::CliModuleLoader;
|
||||||
use crate::proc_state::ProcState;
|
use crate::proc_state::ProcState;
|
||||||
use crate::resolver::ImportMapResolver;
|
use crate::resolver::ImportMapResolver;
|
||||||
|
use crate::resolver::JsxResolver;
|
||||||
use crate::source_maps::apply_source_map;
|
use crate::source_maps::apply_source_map;
|
||||||
use crate::tools::installer::infer_name_from_url;
|
use crate::tools::installer::infer_name_from_url;
|
||||||
use deno_ast::MediaType;
|
use deno_ast::MediaType;
|
||||||
|
@ -468,14 +469,29 @@ async fn info_command(
|
||||||
Permissions::allow_all(),
|
Permissions::allow_all(),
|
||||||
);
|
);
|
||||||
let maybe_locker = lockfile::as_maybe_locker(ps.lockfile.clone());
|
let maybe_locker = lockfile::as_maybe_locker(ps.lockfile.clone());
|
||||||
let maybe_resolver =
|
let maybe_import_map_resolver =
|
||||||
ps.maybe_import_map.as_ref().map(ImportMapResolver::new);
|
ps.maybe_import_map.clone().map(ImportMapResolver::new);
|
||||||
|
let maybe_jsx_resolver = ps
|
||||||
|
.maybe_config_file
|
||||||
|
.as_ref()
|
||||||
|
.map(|cf| {
|
||||||
|
cf.to_maybe_jsx_import_source_module()
|
||||||
|
.map(|im| JsxResolver::new(im, maybe_import_map_resolver.clone()))
|
||||||
|
})
|
||||||
|
.flatten();
|
||||||
|
let maybe_resolver = if maybe_jsx_resolver.is_some() {
|
||||||
|
maybe_jsx_resolver.as_ref().map(|jr| jr.as_resolver())
|
||||||
|
} else {
|
||||||
|
maybe_import_map_resolver
|
||||||
|
.as_ref()
|
||||||
|
.map(|im| im.as_resolver())
|
||||||
|
};
|
||||||
let graph = deno_graph::create_graph(
|
let graph = deno_graph::create_graph(
|
||||||
vec![specifier],
|
vec![specifier],
|
||||||
false,
|
false,
|
||||||
None,
|
None,
|
||||||
&mut cache,
|
&mut cache,
|
||||||
maybe_resolver.as_ref().map(|r| r.as_resolver()),
|
maybe_resolver,
|
||||||
maybe_locker,
|
maybe_locker,
|
||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
|
@ -637,19 +653,35 @@ async fn create_graph_and_maybe_check(
|
||||||
Permissions::allow_all(),
|
Permissions::allow_all(),
|
||||||
);
|
);
|
||||||
let maybe_locker = lockfile::as_maybe_locker(ps.lockfile.clone());
|
let maybe_locker = lockfile::as_maybe_locker(ps.lockfile.clone());
|
||||||
let maybe_imports = ps
|
let maybe_imports = if let Some(config_file) = &ps.maybe_config_file {
|
||||||
|
config_file.to_maybe_imports()?
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
let maybe_import_map_resolver =
|
||||||
|
ps.maybe_import_map.clone().map(ImportMapResolver::new);
|
||||||
|
let maybe_jsx_resolver = ps
|
||||||
.maybe_config_file
|
.maybe_config_file
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|cf| cf.to_maybe_imports())
|
.map(|cf| {
|
||||||
|
cf.to_maybe_jsx_import_source_module()
|
||||||
|
.map(|im| JsxResolver::new(im, maybe_import_map_resolver.clone()))
|
||||||
|
})
|
||||||
.flatten();
|
.flatten();
|
||||||
let maybe_resolver = ps.maybe_import_map.as_ref().map(ImportMapResolver::new);
|
let maybe_resolver = if maybe_jsx_resolver.is_some() {
|
||||||
|
maybe_jsx_resolver.as_ref().map(|jr| jr.as_resolver())
|
||||||
|
} else {
|
||||||
|
maybe_import_map_resolver
|
||||||
|
.as_ref()
|
||||||
|
.map(|im| im.as_resolver())
|
||||||
|
};
|
||||||
let graph = Arc::new(
|
let graph = Arc::new(
|
||||||
deno_graph::create_graph(
|
deno_graph::create_graph(
|
||||||
vec![root],
|
vec![root],
|
||||||
false,
|
false,
|
||||||
maybe_imports,
|
maybe_imports,
|
||||||
&mut cache,
|
&mut cache,
|
||||||
maybe_resolver.as_ref().map(|r| r.as_resolver()),
|
maybe_resolver,
|
||||||
maybe_locker,
|
maybe_locker,
|
||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
|
@ -965,19 +997,34 @@ async fn run_with_watch(flags: Flags, script: String) -> Result<(), AnyError> {
|
||||||
Permissions::allow_all(),
|
Permissions::allow_all(),
|
||||||
);
|
);
|
||||||
let maybe_locker = lockfile::as_maybe_locker(ps.lockfile.clone());
|
let maybe_locker = lockfile::as_maybe_locker(ps.lockfile.clone());
|
||||||
let maybe_imports = ps
|
let maybe_imports = if let Some(config_file) = &ps.maybe_config_file {
|
||||||
|
config_file.to_maybe_imports()?
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
let maybe_import_map_resolver =
|
||||||
|
ps.maybe_import_map.clone().map(ImportMapResolver::new);
|
||||||
|
let maybe_jsx_resolver = ps
|
||||||
.maybe_config_file
|
.maybe_config_file
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|cf| cf.to_maybe_imports())
|
.map(|cf| {
|
||||||
|
cf.to_maybe_jsx_import_source_module()
|
||||||
|
.map(|im| JsxResolver::new(im, maybe_import_map_resolver.clone()))
|
||||||
|
})
|
||||||
.flatten();
|
.flatten();
|
||||||
let maybe_resolver =
|
let maybe_resolver = if maybe_jsx_resolver.is_some() {
|
||||||
ps.maybe_import_map.as_ref().map(ImportMapResolver::new);
|
maybe_jsx_resolver.as_ref().map(|jr| jr.as_resolver())
|
||||||
|
} else {
|
||||||
|
maybe_import_map_resolver
|
||||||
|
.as_ref()
|
||||||
|
.map(|im| im.as_resolver())
|
||||||
|
};
|
||||||
let graph = deno_graph::create_graph(
|
let graph = deno_graph::create_graph(
|
||||||
vec![main_module.clone()],
|
vec![main_module.clone()],
|
||||||
false,
|
false,
|
||||||
maybe_imports,
|
maybe_imports,
|
||||||
&mut cache,
|
&mut cache,
|
||||||
maybe_resolver.as_ref().map(|r| r.as_resolver()),
|
maybe_resolver,
|
||||||
maybe_locker,
|
maybe_locker,
|
||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
|
|
|
@ -7,6 +7,7 @@ use crate::emit;
|
||||||
use crate::errors::get_error_class_name;
|
use crate::errors::get_error_class_name;
|
||||||
use crate::proc_state::ProcState;
|
use crate::proc_state::ProcState;
|
||||||
use crate::resolver::ImportMapResolver;
|
use crate::resolver::ImportMapResolver;
|
||||||
|
use crate::resolver::JsxResolver;
|
||||||
|
|
||||||
use deno_core::error::custom_error;
|
use deno_core::error::custom_error;
|
||||||
use deno_core::error::generic_error;
|
use deno_core::error::generic_error;
|
||||||
|
@ -71,14 +72,66 @@ struct EmitResult {
|
||||||
stats: emit::Stats,
|
stats: emit::Stats,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Provides inferred imported modules from configuration options, like the
|
||||||
|
/// `"types"` and `"jsxImportSource"` imports.
|
||||||
fn to_maybe_imports(
|
fn to_maybe_imports(
|
||||||
referrer: &ModuleSpecifier,
|
referrer: &ModuleSpecifier,
|
||||||
maybe_options: Option<&HashMap<String, Value>>,
|
maybe_options: Option<&HashMap<String, Value>>,
|
||||||
) -> Option<Vec<(ModuleSpecifier, Vec<String>)>> {
|
) -> Option<Vec<(ModuleSpecifier, Vec<String>)>> {
|
||||||
let options = maybe_options.as_ref()?;
|
let options = maybe_options?;
|
||||||
let types_value = options.get("types")?;
|
let mut imports = Vec::new();
|
||||||
let types: Vec<String> = serde_json::from_value(types_value.clone()).ok()?;
|
if let Some(types_value) = options.get("types") {
|
||||||
Some(vec![(referrer.clone(), types)])
|
if let Ok(types) =
|
||||||
|
serde_json::from_value::<Vec<String>>(types_value.clone())
|
||||||
|
{
|
||||||
|
imports.extend(types);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(jsx_value) = options.get("jsx") {
|
||||||
|
if let Ok(jsx) = serde_json::from_value::<String>(jsx_value.clone()) {
|
||||||
|
let jsx_import_source =
|
||||||
|
if let Some(jsx_import_source_value) = options.get("jsxImportSource") {
|
||||||
|
if let Ok(jsx_import_source) =
|
||||||
|
serde_json::from_value::<String>(jsx_import_source_value.clone())
|
||||||
|
{
|
||||||
|
jsx_import_source
|
||||||
|
} else {
|
||||||
|
"react".to_string()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
"react".to_string()
|
||||||
|
};
|
||||||
|
match jsx.as_str() {
|
||||||
|
"react-jsx" => {
|
||||||
|
imports.push(format!("{}/jsx-runtime", jsx_import_source));
|
||||||
|
}
|
||||||
|
"react-jsxdev" => {
|
||||||
|
imports.push(format!("{}/jsx-dev-runtime", jsx_import_source));
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !imports.is_empty() {
|
||||||
|
Some(vec![(referrer.clone(), imports)])
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Converts the compiler options to the JSX import source module that will be
|
||||||
|
/// loaded when transpiling JSX.
|
||||||
|
fn to_maybe_jsx_import_source_module(
|
||||||
|
maybe_options: Option<&HashMap<String, Value>>,
|
||||||
|
) -> Option<String> {
|
||||||
|
let options = maybe_options?;
|
||||||
|
let jsx_value = options.get("jsx")?;
|
||||||
|
let jsx: String = serde_json::from_value(jsx_value.clone()).ok()?;
|
||||||
|
match jsx.as_str() {
|
||||||
|
"react-jsx" => Some("jsx-runtime".to_string()),
|
||||||
|
"react-jsxdev" => Some("jsx-dev-runtime".to_string()),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn op_emit(
|
async fn op_emit(
|
||||||
|
@ -108,7 +161,9 @@ async fn op_emit(
|
||||||
runtime_permissions.clone(),
|
runtime_permissions.clone(),
|
||||||
))
|
))
|
||||||
};
|
};
|
||||||
let maybe_import_map = if let Some(import_map_str) = args.import_map_path {
|
let maybe_import_map_resolver = if let Some(import_map_str) =
|
||||||
|
args.import_map_path
|
||||||
|
{
|
||||||
let import_map_specifier = resolve_url_or_path(&import_map_str)
|
let import_map_specifier = resolve_url_or_path(&import_map_str)
|
||||||
.context(format!("Bad URL (\"{}\") for import map.", import_map_str))?;
|
.context(format!("Bad URL (\"{}\") for import map.", import_map_str))?;
|
||||||
let import_map = if let Some(value) = args.import_map {
|
let import_map = if let Some(value) = args.import_map {
|
||||||
|
@ -126,23 +181,32 @@ async fn op_emit(
|
||||||
})?;
|
})?;
|
||||||
ImportMap::from_json(import_map_specifier.as_str(), &file.source)?
|
ImportMap::from_json(import_map_specifier.as_str(), &file.source)?
|
||||||
};
|
};
|
||||||
Some(import_map)
|
Some(ImportMapResolver::new(Arc::new(import_map)))
|
||||||
} else if args.import_map.is_some() {
|
} else if args.import_map.is_some() {
|
||||||
return Err(generic_error("An importMap was specified, but no importMapPath was provided, which is required."));
|
return Err(generic_error("An importMap was specified, but no importMapPath was provided, which is required."));
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
let maybe_jsx_resolver =
|
||||||
|
to_maybe_jsx_import_source_module(args.compiler_options.as_ref())
|
||||||
|
.map(|im| JsxResolver::new(im, maybe_import_map_resolver.clone()));
|
||||||
|
let maybe_resolver = if maybe_jsx_resolver.is_some() {
|
||||||
|
maybe_jsx_resolver.as_ref().map(|jr| jr.as_resolver())
|
||||||
|
} else {
|
||||||
|
maybe_import_map_resolver
|
||||||
|
.as_ref()
|
||||||
|
.map(|imr| imr.as_resolver())
|
||||||
|
};
|
||||||
let roots = vec![resolve_url_or_path(&root_specifier)?];
|
let roots = vec![resolve_url_or_path(&root_specifier)?];
|
||||||
let maybe_imports =
|
let maybe_imports =
|
||||||
to_maybe_imports(&roots[0], args.compiler_options.as_ref());
|
to_maybe_imports(&roots[0], args.compiler_options.as_ref());
|
||||||
let maybe_resolver = maybe_import_map.as_ref().map(ImportMapResolver::new);
|
|
||||||
let graph = Arc::new(
|
let graph = Arc::new(
|
||||||
deno_graph::create_graph(
|
deno_graph::create_graph(
|
||||||
roots,
|
roots,
|
||||||
true,
|
true,
|
||||||
maybe_imports,
|
maybe_imports,
|
||||||
cache.as_mut_loader(),
|
cache.as_mut_loader(),
|
||||||
maybe_resolver.as_ref().map(|r| r.as_resolver()),
|
maybe_resolver,
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
|
|
|
@ -5,6 +5,7 @@ use crate::colors;
|
||||||
use crate::compat;
|
use crate::compat;
|
||||||
use crate::compat::NodeEsmResolver;
|
use crate::compat::NodeEsmResolver;
|
||||||
use crate::config_file::ConfigFile;
|
use crate::config_file::ConfigFile;
|
||||||
|
use crate::config_file::MaybeImportsResult;
|
||||||
use crate::deno_dir;
|
use crate::deno_dir;
|
||||||
use crate::emit;
|
use crate::emit;
|
||||||
use crate::errors::get_module_graph_error_class;
|
use crate::errors::get_module_graph_error_class;
|
||||||
|
@ -15,6 +16,7 @@ use crate::http_cache;
|
||||||
use crate::lockfile::as_maybe_locker;
|
use crate::lockfile::as_maybe_locker;
|
||||||
use crate::lockfile::Lockfile;
|
use crate::lockfile::Lockfile;
|
||||||
use crate::resolver::ImportMapResolver;
|
use crate::resolver::ImportMapResolver;
|
||||||
|
use crate::resolver::JsxResolver;
|
||||||
use crate::source_maps::SourceMapGetter;
|
use crate::source_maps::SourceMapGetter;
|
||||||
use crate::version;
|
use crate::version;
|
||||||
|
|
||||||
|
@ -79,7 +81,7 @@ pub struct Inner {
|
||||||
graph_data: Arc<Mutex<GraphData>>,
|
graph_data: Arc<Mutex<GraphData>>,
|
||||||
pub lockfile: Option<Arc<Mutex<Lockfile>>>,
|
pub lockfile: Option<Arc<Mutex<Lockfile>>>,
|
||||||
pub maybe_config_file: Option<ConfigFile>,
|
pub maybe_config_file: Option<ConfigFile>,
|
||||||
pub maybe_import_map: Option<ImportMap>,
|
pub maybe_import_map: Option<Arc<ImportMap>>,
|
||||||
pub maybe_inspector_server: Option<Arc<InspectorServer>>,
|
pub maybe_inspector_server: Option<Arc<InspectorServer>>,
|
||||||
pub root_cert_store: Option<RootCertStore>,
|
pub root_cert_store: Option<RootCertStore>,
|
||||||
pub blob_store: BlobStore,
|
pub blob_store: BlobStore,
|
||||||
|
@ -200,7 +202,7 @@ impl ProcState {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
let maybe_import_map: Option<ImportMap> =
|
let maybe_import_map: Option<Arc<ImportMap>> =
|
||||||
match flags.import_map_path.as_ref() {
|
match flags.import_map_path.as_ref() {
|
||||||
None => None,
|
None => None,
|
||||||
Some(import_map_url) => {
|
Some(import_map_url) => {
|
||||||
|
@ -218,7 +220,7 @@ impl ProcState {
|
||||||
))?;
|
))?;
|
||||||
let import_map =
|
let import_map =
|
||||||
ImportMap::from_json(import_map_specifier.as_str(), &file.source)?;
|
ImportMap::from_json(import_map_specifier.as_str(), &file.source)?;
|
||||||
Some(import_map)
|
Some(Arc::new(import_map))
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -258,10 +260,10 @@ impl ProcState {
|
||||||
|
|
||||||
/// Return any imports that should be brought into the scope of the module
|
/// Return any imports that should be brought into the scope of the module
|
||||||
/// graph.
|
/// graph.
|
||||||
fn get_maybe_imports(&self) -> Option<Vec<(ModuleSpecifier, Vec<String>)>> {
|
fn get_maybe_imports(&self) -> MaybeImportsResult {
|
||||||
let mut imports = Vec::new();
|
let mut imports = Vec::new();
|
||||||
if let Some(config_file) = &self.maybe_config_file {
|
if let Some(config_file) = &self.maybe_config_file {
|
||||||
if let Some(config_imports) = config_file.to_maybe_imports() {
|
if let Some(config_imports) = config_file.to_maybe_imports()? {
|
||||||
imports.extend(config_imports);
|
imports.extend(config_imports);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -269,9 +271,9 @@ impl ProcState {
|
||||||
imports.extend(compat::get_node_imports());
|
imports.extend(compat::get_node_imports());
|
||||||
}
|
}
|
||||||
if imports.is_empty() {
|
if imports.is_empty() {
|
||||||
None
|
Ok(None)
|
||||||
} else {
|
} else {
|
||||||
Some(imports)
|
Ok(Some(imports))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -297,16 +299,30 @@ impl ProcState {
|
||||||
dynamic_permissions.clone(),
|
dynamic_permissions.clone(),
|
||||||
);
|
);
|
||||||
let maybe_locker = as_maybe_locker(self.lockfile.clone());
|
let maybe_locker = as_maybe_locker(self.lockfile.clone());
|
||||||
let maybe_imports = self.get_maybe_imports();
|
let maybe_imports = self.get_maybe_imports()?;
|
||||||
let node_resolver = NodeEsmResolver::new(
|
let node_resolver = NodeEsmResolver::new(
|
||||||
self.maybe_import_map.as_ref().map(ImportMapResolver::new),
|
self.maybe_import_map.clone().map(ImportMapResolver::new),
|
||||||
);
|
);
|
||||||
let import_map_resolver =
|
let maybe_import_map_resolver =
|
||||||
self.maybe_import_map.as_ref().map(ImportMapResolver::new);
|
self.maybe_import_map.clone().map(ImportMapResolver::new);
|
||||||
|
let maybe_jsx_resolver = self
|
||||||
|
.maybe_config_file
|
||||||
|
.as_ref()
|
||||||
|
.map(|cf| {
|
||||||
|
cf.to_maybe_jsx_import_source_module()
|
||||||
|
.map(|im| JsxResolver::new(im, maybe_import_map_resolver.clone()))
|
||||||
|
})
|
||||||
|
.flatten();
|
||||||
let maybe_resolver = if self.flags.compat {
|
let maybe_resolver = if self.flags.compat {
|
||||||
Some(node_resolver.as_resolver())
|
Some(node_resolver.as_resolver())
|
||||||
|
} else if maybe_jsx_resolver.is_some() {
|
||||||
|
// the JSX resolver offloads to the import map if present, otherwise uses
|
||||||
|
// the default Deno explicit import resolution.
|
||||||
|
maybe_jsx_resolver.as_ref().map(|jr| jr.as_resolver())
|
||||||
} else {
|
} else {
|
||||||
import_map_resolver.as_ref().map(|im| im.as_resolver())
|
maybe_import_map_resolver
|
||||||
|
.as_ref()
|
||||||
|
.map(|im| im.as_resolver())
|
||||||
};
|
};
|
||||||
// TODO(bartlomieju): this is very make-shift, is there an existing API
|
// TODO(bartlomieju): this is very make-shift, is there an existing API
|
||||||
// that we could include it like with "maybe_imports"?
|
// that we could include it like with "maybe_imports"?
|
||||||
|
|
|
@ -1,27 +1,29 @@
|
||||||
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
|
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
|
||||||
|
|
||||||
use deno_core::error::AnyError;
|
use deno_core::error::AnyError;
|
||||||
|
use deno_core::resolve_import;
|
||||||
use deno_core::ModuleSpecifier;
|
use deno_core::ModuleSpecifier;
|
||||||
use deno_graph::source::Resolver;
|
use deno_graph::source::Resolver;
|
||||||
use import_map::ImportMap;
|
use import_map::ImportMap;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
/// Wraps an import map to be used when building a deno_graph module graph.
|
/// Wraps an import map to be used when building a deno_graph module graph.
|
||||||
/// This is done to avoid having `import_map` be a direct dependency of
|
/// This is done to avoid having `import_map` be a direct dependency of
|
||||||
/// `deno_graph`.
|
/// `deno_graph`.
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Clone)]
|
||||||
pub(crate) struct ImportMapResolver<'a>(&'a ImportMap);
|
pub(crate) struct ImportMapResolver(Arc<ImportMap>);
|
||||||
|
|
||||||
impl<'a> ImportMapResolver<'a> {
|
impl ImportMapResolver {
|
||||||
pub fn new(import_map: &'a ImportMap) -> Self {
|
pub fn new(import_map: Arc<ImportMap>) -> Self {
|
||||||
Self(import_map)
|
Self(import_map)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn as_resolver(&'a self) -> &'a dyn Resolver {
|
pub fn as_resolver(&self) -> &dyn Resolver {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Resolver for ImportMapResolver<'_> {
|
impl Resolver for ImportMapResolver {
|
||||||
fn resolve(
|
fn resolve(
|
||||||
&self,
|
&self,
|
||||||
specifier: &str,
|
specifier: &str,
|
||||||
|
@ -33,3 +35,42 @@ impl Resolver for ImportMapResolver<'_> {
|
||||||
.map_err(|err| err.into())
|
.map_err(|err| err.into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default, Clone)]
|
||||||
|
pub(crate) struct JsxResolver {
|
||||||
|
jsx_import_source_module: String,
|
||||||
|
maybe_import_map_resolver: Option<ImportMapResolver>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl JsxResolver {
|
||||||
|
pub fn new(
|
||||||
|
jsx_import_source_module: String,
|
||||||
|
maybe_import_map_resolver: Option<ImportMapResolver>,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
jsx_import_source_module,
|
||||||
|
maybe_import_map_resolver,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_resolver(&self) -> &dyn Resolver {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Resolver for JsxResolver {
|
||||||
|
fn jsx_import_source_module(&self) -> &str {
|
||||||
|
self.jsx_import_source_module.as_str()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resolve(
|
||||||
|
&self,
|
||||||
|
specifier: &str,
|
||||||
|
referrer: &ModuleSpecifier,
|
||||||
|
) -> Result<ModuleSpecifier, AnyError> {
|
||||||
|
self.maybe_import_map_resolver.as_ref().map_or_else(
|
||||||
|
|| resolve_import(specifier, referrer.as_str()).map_err(|err| err.into()),
|
||||||
|
|r| r.resolve(specifier, referrer),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -63,6 +63,12 @@
|
||||||
"default": "React.Fragment",
|
"default": "React.Fragment",
|
||||||
"markdownDescription": "Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'.\n\nSee more: https://www.typescriptlang.org/tsconfig#jsxFragmentFactory"
|
"markdownDescription": "Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'.\n\nSee more: https://www.typescriptlang.org/tsconfig#jsxFragmentFactory"
|
||||||
},
|
},
|
||||||
|
"jsxImportSource": {
|
||||||
|
"description": "Specify module specifier used to import the JSX factory functions when using jsx: 'react-jsx*'.",
|
||||||
|
"type": "string",
|
||||||
|
"default": "react",
|
||||||
|
"markdownDescription": "Specify module specifier used to import the JSX factory functions when using jsx: `react-jsx*`.\n\nSee more: https://www.typescriptlang.org/tsconfig/#jsxImportSource"
|
||||||
|
},
|
||||||
"keyofStringsOnly": {
|
"keyofStringsOnly": {
|
||||||
"description": "Make keyof only return strings instead of string, numbers or symbols. Legacy option.",
|
"description": "Make keyof only return strings instead of string, numbers or symbols. Legacy option.",
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
|
@ -73,7 +79,9 @@
|
||||||
"description": "Specify a set of bundled library declaration files that describe the target runtime environment.",
|
"description": "Specify a set of bundled library declaration files that describe the target runtime environment.",
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"uniqueItems": true,
|
"uniqueItems": true,
|
||||||
"default": ["deno.window"],
|
"default": [
|
||||||
|
"deno.window"
|
||||||
|
],
|
||||||
"items": {
|
"items": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
|
|
@ -3684,3 +3684,82 @@ fn lsp_lint_with_config() {
|
||||||
}
|
}
|
||||||
shutdown(&mut client);
|
shutdown(&mut client);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn lsp_jsx_import_source_pragma() {
|
||||||
|
let _g = http_server();
|
||||||
|
let mut client = init("initialize_params.json");
|
||||||
|
did_open(
|
||||||
|
&mut client,
|
||||||
|
json!({
|
||||||
|
"textDocument": {
|
||||||
|
"uri": "file:///a/file.tsx",
|
||||||
|
"languageId": "typescriptreact",
|
||||||
|
"version": 1,
|
||||||
|
"text":
|
||||||
|
"/** @jsxImportSource http://localhost:4545/jsx */
|
||||||
|
|
||||||
|
function A() {
|
||||||
|
return \"hello\";
|
||||||
|
}
|
||||||
|
|
||||||
|
export function B() {
|
||||||
|
return <A></A>;
|
||||||
|
}
|
||||||
|
",
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
let (maybe_res, maybe_err) = client
|
||||||
|
.write_request::<_, _, Value>(
|
||||||
|
"deno/cache",
|
||||||
|
json!({
|
||||||
|
"referrer": {
|
||||||
|
"uri": "file:///a/file.tsx",
|
||||||
|
},
|
||||||
|
"uris": [
|
||||||
|
{
|
||||||
|
"uri": "http://127.0.0.1:4545/jsx/jsx-runtime",
|
||||||
|
}
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
assert!(maybe_err.is_none());
|
||||||
|
assert!(maybe_res.is_some());
|
||||||
|
let (maybe_res, maybe_err) = client
|
||||||
|
.write_request::<_, _, Value>(
|
||||||
|
"textDocument/hover",
|
||||||
|
json!({
|
||||||
|
"textDocument": {
|
||||||
|
"uri": "file:///a/file.tsx"
|
||||||
|
},
|
||||||
|
"position": {
|
||||||
|
"line": 0,
|
||||||
|
"character": 25
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
assert!(maybe_err.is_none());
|
||||||
|
assert_eq!(
|
||||||
|
maybe_res,
|
||||||
|
Some(json!({
|
||||||
|
"contents": {
|
||||||
|
"kind": "markdown",
|
||||||
|
"value": "**Resolved Dependency**\n\n**Code**: http​://localhost:4545/jsx/jsx-runtime\n",
|
||||||
|
},
|
||||||
|
"range": {
|
||||||
|
"start": {
|
||||||
|
"line": 0,
|
||||||
|
"character": 21
|
||||||
|
},
|
||||||
|
"end": {
|
||||||
|
"line": 0,
|
||||||
|
"character": 46
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
);
|
||||||
|
shutdown(&mut client);
|
||||||
|
}
|
||||||
|
|
|
@ -1100,7 +1100,7 @@ fn basic_auth_tokens() {
|
||||||
eprintln!("{}", stderr_str);
|
eprintln!("{}", stderr_str);
|
||||||
|
|
||||||
assert!(stderr_str.contains(
|
assert!(stderr_str.contains(
|
||||||
"Import 'http://127.0.0.1:4554/001_hello.js' failed: 404 Not Found"
|
"Import 'http://127.0.0.1:4554/001_hello.js' failed, not found."
|
||||||
));
|
));
|
||||||
|
|
||||||
let output = util::deno_cmd()
|
let output = util::deno_cmd()
|
||||||
|
|
|
@ -1215,6 +1215,118 @@ itest!(jsx_import_from_ts {
|
||||||
output: "jsx_import_from_ts.ts.out",
|
output: "jsx_import_from_ts.ts.out",
|
||||||
});
|
});
|
||||||
|
|
||||||
|
itest!(jsx_import_source_pragma {
|
||||||
|
args: "run --reload jsx_import_source_pragma.tsx",
|
||||||
|
output: "jsx_import_source.out",
|
||||||
|
http_server: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
itest!(jsx_import_source_pragma_with_config {
|
||||||
|
args: "run --reload --config jsx/deno-jsx.jsonc jsx_import_source_pragma.tsx",
|
||||||
|
output: "jsx_import_source.out",
|
||||||
|
http_server: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
itest!(jsx_import_source_pragma_with_dev_config {
|
||||||
|
args:
|
||||||
|
"run --reload --config jsx/deno-jsxdev.jsonc jsx_import_source_pragma.tsx",
|
||||||
|
output: "jsx_import_source_dev.out",
|
||||||
|
http_server: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
itest!(jsx_import_source_no_pragma {
|
||||||
|
args:
|
||||||
|
"run --reload --config jsx/deno-jsx.jsonc jsx_import_source_no_pragma.tsx",
|
||||||
|
output: "jsx_import_source.out",
|
||||||
|
http_server: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
itest!(jsx_import_source_no_pragma_dev {
|
||||||
|
args: "run --reload --config jsx/deno-jsxdev.jsonc jsx_import_source_no_pragma.tsx",
|
||||||
|
output: "jsx_import_source_dev.out",
|
||||||
|
http_server: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
itest!(jsx_import_source_pragma_import_map {
|
||||||
|
args: "run --reload --import-map jsx/import-map.json jsx_import_source_pragma_import_map.tsx",
|
||||||
|
output: "jsx_import_source_import_map.out",
|
||||||
|
http_server: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
itest!(jsx_import_source_pragma_import_map_dev {
|
||||||
|
args: "run --reload --import-map jsx/import-map.json --config jsx/deno-jsxdev-import-map.jsonc jsx_import_source_pragma_import_map.tsx",
|
||||||
|
output: "jsx_import_source_import_map_dev.out",
|
||||||
|
http_server: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
itest!(jsx_import_source_import_map {
|
||||||
|
args: "run --reload --import-map jsx/import-map.json --config jsx/deno-jsx-import-map.jsonc jsx_import_source_no_pragma.tsx",
|
||||||
|
output: "jsx_import_source_import_map.out",
|
||||||
|
http_server: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
itest!(jsx_import_source_import_map_dev {
|
||||||
|
args: "run --reload --import-map jsx/import-map.json --config jsx/deno-jsxdev-import-map.jsonc jsx_import_source_no_pragma.tsx",
|
||||||
|
output: "jsx_import_source_import_map_dev.out",
|
||||||
|
http_server: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
itest!(jsx_import_source_pragma_no_check {
|
||||||
|
args: "run --reload --no-check jsx_import_source_pragma.tsx",
|
||||||
|
output: "jsx_import_source.out",
|
||||||
|
http_server: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
itest!(jsx_import_source_pragma_with_config_no_check {
|
||||||
|
args: "run --reload --config jsx/deno-jsx.jsonc --no-check jsx_import_source_pragma.tsx",
|
||||||
|
output: "jsx_import_source.out",
|
||||||
|
http_server: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
// itest!(jsx_import_source_pragma_with_dev_config_no_check {
|
||||||
|
// args:
|
||||||
|
// "run --reload --config jsx/deno-jsxdev.jsonc --no-check jsx_import_source_pragma.tsx",
|
||||||
|
// output: "jsx_import_source_dev.out",
|
||||||
|
// http_server: true,
|
||||||
|
// });
|
||||||
|
|
||||||
|
itest!(jsx_import_source_no_pragma_no_check {
|
||||||
|
args:
|
||||||
|
"run --reload --config jsx/deno-jsx.jsonc --no-check jsx_import_source_no_pragma.tsx",
|
||||||
|
output: "jsx_import_source.out",
|
||||||
|
http_server: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
// itest!(jsx_import_source_no_pragma_dev_no_check {
|
||||||
|
// args: "run --reload --config jsx/deno-jsxdev.jsonc --no-check jsx_import_source_no_pragma.tsx",
|
||||||
|
// output: "jsx_import_source_dev.out",
|
||||||
|
// http_server: true,
|
||||||
|
// });
|
||||||
|
|
||||||
|
itest!(jsx_import_source_pragma_import_map_no_check {
|
||||||
|
args: "run --reload --import-map jsx/import-map.json --no-check jsx_import_source_pragma_import_map.tsx",
|
||||||
|
output: "jsx_import_source_import_map.out",
|
||||||
|
http_server: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
// itest!(jsx_import_source_pragma_import_map_dev_no_check {
|
||||||
|
// args: "run --reload --import-map jsx/import-map.json --config jsx/deno-jsxdev-import-map.jsonc --no-check jsx_import_source_pragma_import_map.tsx",
|
||||||
|
// output: "jsx_import_source_import_map_dev.out",
|
||||||
|
// http_server: true,
|
||||||
|
// });
|
||||||
|
|
||||||
|
itest!(jsx_import_source_import_map_no_check {
|
||||||
|
args: "run --reload --import-map jsx/import-map.json --config jsx/deno-jsx-import-map.jsonc --no-check jsx_import_source_no_pragma.tsx",
|
||||||
|
output: "jsx_import_source_import_map.out",
|
||||||
|
http_server: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
// itest!(jsx_import_source_import_map_dev_no_check {
|
||||||
|
// args: "run --reload --import-map jsx/import-map.json --config jsx/deno-jsxdev-import-map.jsonc --no-check jsx_import_source_no_pragma.tsx",
|
||||||
|
// output: "jsx_import_source_import_map_dev.out",
|
||||||
|
// http_server: true,
|
||||||
|
// });
|
||||||
|
|
||||||
// TODO(#11128): Flaky. Re-enable later.
|
// TODO(#11128): Flaky. Re-enable later.
|
||||||
// itest!(single_compile_with_reload {
|
// itest!(single_compile_with_reload {
|
||||||
// args: "run --reload --allow-read single_compile_with_reload.ts",
|
// args: "run --reload --allow-read single_compile_with_reload.ts",
|
||||||
|
|
78
cli/tests/testdata/compiler_api_test.ts
vendored
78
cli/tests/testdata/compiler_api_test.ts
vendored
|
@ -557,3 +557,81 @@ Deno.test({
|
||||||
assertEquals(sourceMap.sourcesContent.length, 1);
|
assertEquals(sourceMap.sourcesContent.length, 1);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Deno.test({
|
||||||
|
name: "Deno.emit() - JSX import source pragma",
|
||||||
|
async fn() {
|
||||||
|
const { files } = await Deno.emit(
|
||||||
|
"file:///a.tsx",
|
||||||
|
{
|
||||||
|
sources: {
|
||||||
|
"file:///a.tsx": `/** @jsxImportSource https://example.com/jsx */
|
||||||
|
|
||||||
|
export function App() {
|
||||||
|
return (
|
||||||
|
<div><></></div>
|
||||||
|
);
|
||||||
|
}`,
|
||||||
|
"https://example.com/jsx/jsx-runtime": `export function jsx(
|
||||||
|
_type,
|
||||||
|
_props,
|
||||||
|
_key,
|
||||||
|
_source,
|
||||||
|
_self,
|
||||||
|
) {}
|
||||||
|
export const jsxs = jsx;
|
||||||
|
export const jsxDEV = jsx;
|
||||||
|
export const Fragment = Symbol("Fragment");
|
||||||
|
console.log("imported", import.meta.url);
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
assert(files["file:///a.tsx.js"]);
|
||||||
|
assert(
|
||||||
|
files["file:///a.tsx.js"].startsWith(
|
||||||
|
`import { Fragment as _Fragment, jsx as _jsx } from "https://example.com/jsx/jsx-runtime";\n`,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
Deno.test({
|
||||||
|
name: "Deno.emit() - JSX import source no pragma",
|
||||||
|
async fn() {
|
||||||
|
const { files } = await Deno.emit(
|
||||||
|
"file:///a.tsx",
|
||||||
|
{
|
||||||
|
compilerOptions: {
|
||||||
|
jsx: "react-jsx",
|
||||||
|
jsxImportSource: "https://example.com/jsx",
|
||||||
|
},
|
||||||
|
sources: {
|
||||||
|
"file:///a.tsx": `export function App() {
|
||||||
|
return (
|
||||||
|
<div><></></div>
|
||||||
|
);
|
||||||
|
}`,
|
||||||
|
"https://example.com/jsx/jsx-runtime": `export function jsx(
|
||||||
|
_type,
|
||||||
|
_props,
|
||||||
|
_key,
|
||||||
|
_source,
|
||||||
|
_self,
|
||||||
|
) {}
|
||||||
|
export const jsxs = jsx;
|
||||||
|
export const jsxDEV = jsx;
|
||||||
|
export const Fragment = Symbol("Fragment");
|
||||||
|
console.log("imported", import.meta.url);
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
assert(files["file:///a.tsx.js"]);
|
||||||
|
assert(
|
||||||
|
files["file:///a.tsx.js"].startsWith(
|
||||||
|
`import { Fragment as _Fragment, jsx as _jsx } from "https://example.com/jsx/jsx-runtime";\n`,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
6
cli/tests/testdata/jsx/deno-jsx-import-map.jsonc
vendored
Normal file
6
cli/tests/testdata/jsx/deno-jsx-import-map.jsonc
vendored
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"jsx": "react-jsx",
|
||||||
|
"jsxImportSource": "jsx"
|
||||||
|
}
|
||||||
|
}
|
6
cli/tests/testdata/jsx/deno-jsx.jsonc
vendored
Normal file
6
cli/tests/testdata/jsx/deno-jsx.jsonc
vendored
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"jsx": "react-jsx",
|
||||||
|
"jsxImportSource": "http://localhost:4545/jsx"
|
||||||
|
}
|
||||||
|
}
|
6
cli/tests/testdata/jsx/deno-jsxdev-import-map.jsonc
vendored
Normal file
6
cli/tests/testdata/jsx/deno-jsxdev-import-map.jsonc
vendored
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"jsx": "react-jsxdev",
|
||||||
|
"jsxImportSource": "jsx"
|
||||||
|
}
|
||||||
|
}
|
6
cli/tests/testdata/jsx/deno-jsxdev.jsonc
vendored
Normal file
6
cli/tests/testdata/jsx/deno-jsxdev.jsonc
vendored
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"jsx": "react-jsxdev",
|
||||||
|
"jsxImportSource": "http://localhost:4545/jsx"
|
||||||
|
}
|
||||||
|
}
|
6
cli/tests/testdata/jsx/import-map.json
vendored
Normal file
6
cli/tests/testdata/jsx/import-map.json
vendored
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"imports": {
|
||||||
|
"jsx/jsx-runtime": "http://localhost:4545/jsx/jsx-runtime/index.ts",
|
||||||
|
"jsx/jsx-dev-runtime": "http://localhost:4545/jsx/jsx-dev-runtime/index.ts"
|
||||||
|
}
|
||||||
|
}
|
12
cli/tests/testdata/jsx/jsx-dev-runtime/index.ts
vendored
Normal file
12
cli/tests/testdata/jsx/jsx-dev-runtime/index.ts
vendored
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
// deno-lint-ignore-file no-explicit-any
|
||||||
|
export function jsx(
|
||||||
|
_type: any,
|
||||||
|
_props: any,
|
||||||
|
_key: any,
|
||||||
|
_source: any,
|
||||||
|
_self: any,
|
||||||
|
) {}
|
||||||
|
export const jsxs = jsx;
|
||||||
|
export const jsxDEV = jsx;
|
||||||
|
export const Fragment = Symbol("Fragment");
|
||||||
|
console.log("imported", import.meta.url);
|
12
cli/tests/testdata/jsx/jsx-runtime/index.ts
vendored
Normal file
12
cli/tests/testdata/jsx/jsx-runtime/index.ts
vendored
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
// deno-lint-ignore-file no-explicit-any
|
||||||
|
export function jsx(
|
||||||
|
_type: any,
|
||||||
|
_props: any,
|
||||||
|
_key: any,
|
||||||
|
_source: any,
|
||||||
|
_self: any,
|
||||||
|
) {}
|
||||||
|
export const jsxs = jsx;
|
||||||
|
export const jsxDEV = jsx;
|
||||||
|
export const Fragment = Symbol("Fragment");
|
||||||
|
console.log("imported", import.meta.url);
|
2
cli/tests/testdata/jsx_import_source.out
vendored
Normal file
2
cli/tests/testdata/jsx_import_source.out
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
[WILDCARD]
|
||||||
|
imported http://localhost:4545/jsx/jsx-runtime
|
2
cli/tests/testdata/jsx_import_source_dev.out
vendored
Normal file
2
cli/tests/testdata/jsx_import_source_dev.out
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
[WILDCARD]
|
||||||
|
imported http://localhost:4545/jsx/jsx-dev-runtime
|
2
cli/tests/testdata/jsx_import_source_import_map.out
vendored
Normal file
2
cli/tests/testdata/jsx_import_source_import_map.out
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
[WILDCARD]
|
||||||
|
imported http://localhost:4545/jsx/jsx-runtime/index.ts
|
2
cli/tests/testdata/jsx_import_source_import_map_dev.out
vendored
Normal file
2
cli/tests/testdata/jsx_import_source_import_map_dev.out
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
[WILDCARD]
|
||||||
|
imported http://localhost:4545/jsx/jsx-dev-runtime/index.ts
|
7
cli/tests/testdata/jsx_import_source_no_pragma.tsx
vendored
Normal file
7
cli/tests/testdata/jsx_import_source_no_pragma.tsx
vendored
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
function A() {
|
||||||
|
return "hello";
|
||||||
|
}
|
||||||
|
|
||||||
|
export function B() {
|
||||||
|
return <A></A>;
|
||||||
|
}
|
9
cli/tests/testdata/jsx_import_source_pragma.tsx
vendored
Normal file
9
cli/tests/testdata/jsx_import_source_pragma.tsx
vendored
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
/** @jsxImportSource http://localhost:4545/jsx */
|
||||||
|
|
||||||
|
function A() {
|
||||||
|
return "hello";
|
||||||
|
}
|
||||||
|
|
||||||
|
export function B() {
|
||||||
|
return <A></A>;
|
||||||
|
}
|
9
cli/tests/testdata/jsx_import_source_pragma_import_map.tsx
vendored
Normal file
9
cli/tests/testdata/jsx_import_source_pragma_import_map.tsx
vendored
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
/** @jsxImportSource jsx */
|
||||||
|
|
||||||
|
function A() {
|
||||||
|
return "hello";
|
||||||
|
}
|
||||||
|
|
||||||
|
export function B() {
|
||||||
|
return <A></A>;
|
||||||
|
}
|
|
@ -38,7 +38,7 @@ impl Loader for StubDocLoader {
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct DocResolver {
|
struct DocResolver {
|
||||||
import_map: Option<ImportMap>,
|
import_map: Option<Arc<ImportMap>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Resolver for DocResolver {
|
impl Resolver for DocResolver {
|
||||||
|
|
|
@ -668,8 +668,11 @@ impl ReplSession {
|
||||||
imports_not_used_as_values: ImportsNotUsedAsValues::Preserve,
|
imports_not_used_as_values: ImportsNotUsedAsValues::Preserve,
|
||||||
// JSX is not supported in the REPL
|
// JSX is not supported in the REPL
|
||||||
transform_jsx: false,
|
transform_jsx: false,
|
||||||
|
jsx_automatic: false,
|
||||||
|
jsx_development: false,
|
||||||
jsx_factory: "React.createElement".into(),
|
jsx_factory: "React.createElement".into(),
|
||||||
jsx_fragment_factory: "React.Fragment".into(),
|
jsx_fragment_factory: "React.Fragment".into(),
|
||||||
|
jsx_import_source: None,
|
||||||
repl_imports: true,
|
repl_imports: true,
|
||||||
},
|
},
|
||||||
)?
|
)?
|
||||||
|
|
|
@ -18,6 +18,7 @@ use crate::lockfile;
|
||||||
use crate::ops;
|
use crate::ops;
|
||||||
use crate::proc_state::ProcState;
|
use crate::proc_state::ProcState;
|
||||||
use crate::resolver::ImportMapResolver;
|
use crate::resolver::ImportMapResolver;
|
||||||
|
use crate::resolver::JsxResolver;
|
||||||
use crate::tools::coverage::CoverageCollector;
|
use crate::tools::coverage::CoverageCollector;
|
||||||
|
|
||||||
use deno_ast::swc::common::comments::CommentKind;
|
use deno_ast::swc::common::comments::CommentKind;
|
||||||
|
@ -1053,14 +1054,21 @@ pub async fn run_tests_with_watch(
|
||||||
let paths_to_watch = paths_to_watch.clone();
|
let paths_to_watch = paths_to_watch.clone();
|
||||||
let paths_to_watch_clone = paths_to_watch.clone();
|
let paths_to_watch_clone = paths_to_watch.clone();
|
||||||
|
|
||||||
let maybe_resolver =
|
let maybe_import_map_resolver =
|
||||||
ps.maybe_import_map.as_ref().map(ImportMapResolver::new);
|
ps.maybe_import_map.clone().map(ImportMapResolver::new);
|
||||||
|
let maybe_jsx_resolver = ps
|
||||||
|
.maybe_config_file
|
||||||
|
.as_ref()
|
||||||
|
.map(|cf| {
|
||||||
|
cf.to_maybe_jsx_import_source_module()
|
||||||
|
.map(|im| JsxResolver::new(im, maybe_import_map_resolver.clone()))
|
||||||
|
})
|
||||||
|
.flatten();
|
||||||
let maybe_locker = lockfile::as_maybe_locker(ps.lockfile.clone());
|
let maybe_locker = lockfile::as_maybe_locker(ps.lockfile.clone());
|
||||||
let maybe_imports = ps
|
let maybe_imports = ps
|
||||||
.maybe_config_file
|
.maybe_config_file
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|cf| cf.to_maybe_imports())
|
.map(|cf| cf.to_maybe_imports());
|
||||||
.flatten();
|
|
||||||
let files_changed = changed.is_some();
|
let files_changed = changed.is_some();
|
||||||
let include = include.clone();
|
let include = include.clone();
|
||||||
let ignore = ignore.clone();
|
let ignore = ignore.clone();
|
||||||
|
@ -1081,13 +1089,24 @@ pub async fn run_tests_with_watch(
|
||||||
.filter_map(|url| deno_core::resolve_url(url.as_str()).ok())
|
.filter_map(|url| deno_core::resolve_url(url.as_str()).ok())
|
||||||
.collect()
|
.collect()
|
||||||
};
|
};
|
||||||
|
let maybe_imports = if let Some(result) = maybe_imports {
|
||||||
|
result?
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
let maybe_resolver = if maybe_jsx_resolver.is_some() {
|
||||||
|
maybe_jsx_resolver.as_ref().map(|jr| jr.as_resolver())
|
||||||
|
} else {
|
||||||
|
maybe_import_map_resolver
|
||||||
|
.as_ref()
|
||||||
|
.map(|im| im.as_resolver())
|
||||||
|
};
|
||||||
let graph = deno_graph::create_graph(
|
let graph = deno_graph::create_graph(
|
||||||
test_modules.clone(),
|
test_modules.clone(),
|
||||||
false,
|
false,
|
||||||
maybe_imports,
|
maybe_imports,
|
||||||
cache.as_mut_loader(),
|
cache.as_mut_loader(),
|
||||||
maybe_resolver.as_ref().map(|r| r.as_resolver()),
|
maybe_resolver,
|
||||||
maybe_locker,
|
maybe_locker,
|
||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
|
|
70
cli/tsc.rs
70
cli/tsc.rs
|
@ -414,6 +414,27 @@ pub struct ResolveArgs {
|
||||||
pub specifiers: Vec<String>,
|
pub specifiers: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn resolve_specifier(
|
||||||
|
state: &mut State,
|
||||||
|
specifier: &ModuleSpecifier,
|
||||||
|
) -> (String, String) {
|
||||||
|
let media_type = state
|
||||||
|
.graph
|
||||||
|
.get(specifier)
|
||||||
|
.map_or(&MediaType::Unknown, |m| &m.media_type);
|
||||||
|
let specifier_str = match specifier.scheme() {
|
||||||
|
"data" | "blob" => {
|
||||||
|
let specifier_str = hash_url(specifier, media_type);
|
||||||
|
state
|
||||||
|
.data_url_map
|
||||||
|
.insert(specifier_str.clone(), specifier.clone());
|
||||||
|
specifier_str
|
||||||
|
}
|
||||||
|
_ => specifier.to_string(),
|
||||||
|
};
|
||||||
|
(specifier_str, media_type.as_ts_extension().into())
|
||||||
|
}
|
||||||
|
|
||||||
fn op_resolve(state: &mut State, args: Value) -> Result<Value, AnyError> {
|
fn op_resolve(state: &mut State, args: Value) -> Result<Value, AnyError> {
|
||||||
let v: ResolveArgs = serde_json::from_value(args)
|
let v: ResolveArgs = serde_json::from_value(args)
|
||||||
.context("Invalid request from JavaScript for \"op_resolve\".")?;
|
.context("Invalid request from JavaScript for \"op_resolve\".")?;
|
||||||
|
@ -434,30 +455,31 @@ fn op_resolve(state: &mut State, args: Value) -> Result<Value, AnyError> {
|
||||||
MediaType::from(specifier).as_ts_extension().to_string(),
|
MediaType::from(specifier).as_ts_extension().to_string(),
|
||||||
));
|
));
|
||||||
} else {
|
} else {
|
||||||
let resolved_dependency =
|
// here, we try to resolve the specifier via the referrer, but if we can't
|
||||||
match state.graph.resolve_dependency(specifier, &referrer, true) {
|
// we will try to resolve the specifier via the configuration file, if
|
||||||
Some(resolved_specifier) => {
|
// present, finally defaulting to a "placeholder" specifier. This handles
|
||||||
let media_type = state
|
// situations like the jsxImportSource, which tsc tries to resolve the
|
||||||
.graph
|
// import source from a JSX module, but the module graph only contains the
|
||||||
.get(resolved_specifier)
|
// import as a dependency of the configuration file.
|
||||||
.map_or(&MediaType::Unknown, |m| &m.media_type);
|
let resolved_dependency = if let Some(resolved_specifier) = state
|
||||||
let resolved_specifier_str = match resolved_specifier.scheme() {
|
.graph
|
||||||
"data" | "blob" => {
|
.resolve_dependency(specifier, &referrer, true)
|
||||||
let specifier_str = hash_url(resolved_specifier, media_type);
|
.cloned()
|
||||||
state
|
{
|
||||||
.data_url_map
|
resolve_specifier(state, &resolved_specifier)
|
||||||
.insert(specifier_str.clone(), resolved_specifier.clone());
|
} else if let Some(resolved_specifier) = state
|
||||||
specifier_str
|
.maybe_config_specifier
|
||||||
}
|
.as_ref()
|
||||||
_ => resolved_specifier.to_string(),
|
.map(|cf| state.graph.resolve_dependency(specifier, cf, true).cloned())
|
||||||
};
|
.flatten()
|
||||||
(resolved_specifier_str, media_type.as_ts_extension().into())
|
{
|
||||||
}
|
resolve_specifier(state, &resolved_specifier)
|
||||||
None => (
|
} else {
|
||||||
"deno:///missing_dependency.d.ts".to_string(),
|
(
|
||||||
".d.ts".to_string(),
|
"deno:///missing_dependency.d.ts".to_string(),
|
||||||
),
|
".d.ts".to_string(),
|
||||||
};
|
)
|
||||||
|
};
|
||||||
resolved.push(resolved_dependency);
|
resolved.push(resolved_dependency);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -566,7 +566,9 @@ async fn absolute_redirect(
|
||||||
Ok(file_resp)
|
Ok(file_resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn main_server(req: Request<Body>) -> hyper::Result<Response<Body>> {
|
async fn main_server(
|
||||||
|
req: Request<Body>,
|
||||||
|
) -> Result<Response<Body>, hyper::http::Error> {
|
||||||
return match (req.method(), req.uri().path()) {
|
return match (req.method(), req.uri().path()) {
|
||||||
(&hyper::Method::POST, "/echo_server") => {
|
(&hyper::Method::POST, "/echo_server") => {
|
||||||
let (parts, body) = req.into_parts();
|
let (parts, body) = req.into_parts();
|
||||||
|
@ -849,6 +851,27 @@ async fn main_server(req: Request<Body>) -> hyper::Result<Response<Body>> {
|
||||||
let version = format!("{:?}", req.version());
|
let version = format!("{:?}", req.version());
|
||||||
Ok(Response::new(version.into()))
|
Ok(Response::new(version.into()))
|
||||||
}
|
}
|
||||||
|
(_, "/jsx/jsx-runtime") | (_, "/jsx/jsx-dev-runtime") => {
|
||||||
|
let mut res = Response::new(Body::from(
|
||||||
|
r#"export function jsx(
|
||||||
|
_type,
|
||||||
|
_props,
|
||||||
|
_key,
|
||||||
|
_source,
|
||||||
|
_self,
|
||||||
|
) {}
|
||||||
|
export const jsxs = jsx;
|
||||||
|
export const jsxDEV = jsx;
|
||||||
|
export const Fragment = Symbol("Fragment");
|
||||||
|
console.log("imported", import.meta.url);
|
||||||
|
"#,
|
||||||
|
));
|
||||||
|
res.headers_mut().insert(
|
||||||
|
"Content-type",
|
||||||
|
HeaderValue::from_static("application/javascript"),
|
||||||
|
);
|
||||||
|
Ok(res)
|
||||||
|
}
|
||||||
_ => {
|
_ => {
|
||||||
let mut file_path = testdata_path();
|
let mut file_path = testdata_path();
|
||||||
file_path.push(&req.uri().path()[1..]);
|
file_path.push(&req.uri().path()[1..]);
|
||||||
|
@ -857,7 +880,9 @@ async fn main_server(req: Request<Body>) -> hyper::Result<Response<Body>> {
|
||||||
return Ok(file_resp);
|
return Ok(file_resp);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Ok(Response::new(Body::empty()));
|
Response::builder()
|
||||||
|
.status(StatusCode::NOT_FOUND)
|
||||||
|
.body(Body::empty())
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue