mirror of
https://github.com/denoland/deno.git
synced 2024-11-21 15:04:11 -05:00
feat(publish): support sloppy imports and bare node built-ins (#22588)
This commit is contained in:
parent
47c2a63d87
commit
8d5c231349
17 changed files with 316 additions and 130 deletions
|
@ -44,10 +44,10 @@ use crate::resolver::SloppyImportsResolver;
|
|||
use crate::standalone::DenoCompileBinaryWriter;
|
||||
use crate::tools::check::TypeChecker;
|
||||
use crate::tools::coverage::CoverageCollector;
|
||||
use crate::tools::registry::deno_json_deps;
|
||||
use crate::tools::run::hmr::HmrRunner;
|
||||
use crate::util::file_watcher::WatcherCommunicator;
|
||||
use crate::util::fs::canonicalize_path_maybe_not_exists;
|
||||
use crate::util::import_map::deno_json_deps;
|
||||
use crate::util::progress_bar::ProgressBar;
|
||||
use crate::util::progress_bar::ProgressBarStyle;
|
||||
use crate::worker::CliMainWorkerFactory;
|
||||
|
|
|
@ -20,7 +20,7 @@ use deno_core::error::AnyError;
|
|||
use deno_graph::FastCheckDiagnostic;
|
||||
use lsp_types::Url;
|
||||
|
||||
use crate::util::import_map::ImportMapUnfurlDiagnostic;
|
||||
use super::unfurl::SpecifierUnfurlerDiagnostic;
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub struct PublishDiagnosticsCollector {
|
||||
|
@ -74,7 +74,7 @@ impl PublishDiagnosticsCollector {
|
|||
|
||||
pub enum PublishDiagnostic {
|
||||
FastCheck(FastCheckDiagnostic),
|
||||
ImportMapUnfurl(ImportMapUnfurlDiagnostic),
|
||||
SpecifierUnfurl(SpecifierUnfurlerDiagnostic),
|
||||
InvalidPath {
|
||||
path: PathBuf,
|
||||
message: String,
|
||||
|
@ -102,7 +102,7 @@ impl Diagnostic for PublishDiagnostic {
|
|||
..
|
||||
}) => DiagnosticLevel::Warning,
|
||||
FastCheck(_) => DiagnosticLevel::Error,
|
||||
ImportMapUnfurl(_) => DiagnosticLevel::Warning,
|
||||
SpecifierUnfurl(_) => DiagnosticLevel::Warning,
|
||||
InvalidPath { .. } => DiagnosticLevel::Error,
|
||||
DuplicatePath { .. } => DiagnosticLevel::Error,
|
||||
UnsupportedFileType { .. } => DiagnosticLevel::Warning,
|
||||
|
@ -114,7 +114,7 @@ impl Diagnostic for PublishDiagnostic {
|
|||
use PublishDiagnostic::*;
|
||||
match &self {
|
||||
FastCheck(diagnostic) => diagnostic.code(),
|
||||
ImportMapUnfurl(diagnostic) => Cow::Borrowed(diagnostic.code()),
|
||||
SpecifierUnfurl(diagnostic) => Cow::Borrowed(diagnostic.code()),
|
||||
InvalidPath { .. } => Cow::Borrowed("invalid-path"),
|
||||
DuplicatePath { .. } => Cow::Borrowed("case-insensitive-duplicate-path"),
|
||||
UnsupportedFileType { .. } => Cow::Borrowed("unsupported-file-type"),
|
||||
|
@ -126,7 +126,7 @@ impl Diagnostic for PublishDiagnostic {
|
|||
use PublishDiagnostic::*;
|
||||
match &self {
|
||||
FastCheck(diagnostic) => diagnostic.message(),
|
||||
ImportMapUnfurl(diagnostic) => Cow::Borrowed(diagnostic.message()),
|
||||
SpecifierUnfurl(diagnostic) => Cow::Borrowed(diagnostic.message()),
|
||||
InvalidPath { message, .. } => Cow::Borrowed(message.as_str()),
|
||||
DuplicatePath { .. } => {
|
||||
Cow::Borrowed("package path is a case insensitive duplicate of another path in the package")
|
||||
|
@ -142,8 +142,8 @@ impl Diagnostic for PublishDiagnostic {
|
|||
use PublishDiagnostic::*;
|
||||
match &self {
|
||||
FastCheck(diagnostic) => diagnostic.location(),
|
||||
ImportMapUnfurl(diagnostic) => match diagnostic {
|
||||
ImportMapUnfurlDiagnostic::UnanalyzableDynamicImport {
|
||||
SpecifierUnfurl(diagnostic) => match diagnostic {
|
||||
SpecifierUnfurlerDiagnostic::UnanalyzableDynamicImport {
|
||||
specifier,
|
||||
text_info,
|
||||
range,
|
||||
|
@ -180,8 +180,8 @@ impl Diagnostic for PublishDiagnostic {
|
|||
fn snippet(&self) -> Option<DiagnosticSnippet<'_>> {
|
||||
match &self {
|
||||
PublishDiagnostic::FastCheck(diagnostic) => diagnostic.snippet(),
|
||||
PublishDiagnostic::ImportMapUnfurl(diagnostic) => match diagnostic {
|
||||
ImportMapUnfurlDiagnostic::UnanalyzableDynamicImport {
|
||||
PublishDiagnostic::SpecifierUnfurl(diagnostic) => match diagnostic {
|
||||
SpecifierUnfurlerDiagnostic::UnanalyzableDynamicImport {
|
||||
text_info,
|
||||
range,
|
||||
..
|
||||
|
@ -227,7 +227,7 @@ impl Diagnostic for PublishDiagnostic {
|
|||
fn hint(&self) -> Option<Cow<'_, str>> {
|
||||
match &self {
|
||||
PublishDiagnostic::FastCheck(diagnostic) => diagnostic.hint(),
|
||||
PublishDiagnostic::ImportMapUnfurl(_) => None,
|
||||
PublishDiagnostic::SpecifierUnfurl(_) => None,
|
||||
PublishDiagnostic::InvalidPath { .. } => Some(
|
||||
Cow::Borrowed("rename or remove the file, or add it to 'publish.exclude' in the config file"),
|
||||
),
|
||||
|
@ -250,11 +250,11 @@ impl Diagnostic for PublishDiagnostic {
|
|||
PublishDiagnostic::FastCheck(diagnostic) => {
|
||||
diagnostic.info()
|
||||
}
|
||||
PublishDiagnostic::ImportMapUnfurl(diagnostic) => match diagnostic {
|
||||
ImportMapUnfurlDiagnostic::UnanalyzableDynamicImport { .. } => Cow::Borrowed(&[
|
||||
Cow::Borrowed("after publishing this package, imports from the local import map do not work"),
|
||||
PublishDiagnostic::SpecifierUnfurl(diagnostic) => match diagnostic {
|
||||
SpecifierUnfurlerDiagnostic::UnanalyzableDynamicImport { .. } => Cow::Borrowed(&[
|
||||
Cow::Borrowed("after publishing this package, imports from the local import map / package.json do not work"),
|
||||
Cow::Borrowed("dynamic imports that can not be analyzed at publish time will not be rewritten automatically"),
|
||||
Cow::Borrowed("make sure the dynamic import is resolvable at runtime without an import map")
|
||||
Cow::Borrowed("make sure the dynamic import is resolvable at runtime without an import map / package.json")
|
||||
]),
|
||||
},
|
||||
PublishDiagnostic::InvalidPath { .. } => Cow::Borrowed(&[
|
||||
|
@ -278,8 +278,8 @@ impl Diagnostic for PublishDiagnostic {
|
|||
fn docs_url(&self) -> Option<Cow<'_, str>> {
|
||||
match &self {
|
||||
PublishDiagnostic::FastCheck(diagnostic) => diagnostic.docs_url(),
|
||||
PublishDiagnostic::ImportMapUnfurl(diagnostic) => match diagnostic {
|
||||
ImportMapUnfurlDiagnostic::UnanalyzableDynamicImport { .. } => None,
|
||||
PublishDiagnostic::SpecifierUnfurl(diagnostic) => match diagnostic {
|
||||
SpecifierUnfurlerDiagnostic::UnanalyzableDynamicImport { .. } => None,
|
||||
},
|
||||
PublishDiagnostic::InvalidPath { .. } => {
|
||||
Some(Cow::Borrowed("https://jsr.io/go/invalid-path"))
|
||||
|
|
|
@ -35,13 +35,13 @@ use crate::factory::CliFactory;
|
|||
use crate::graph_util::ModuleGraphCreator;
|
||||
use crate::http_util::HttpClient;
|
||||
use crate::resolver::MappedSpecifierResolver;
|
||||
use crate::resolver::SloppyImportsResolver;
|
||||
use crate::tools::check::CheckOptions;
|
||||
use crate::tools::lint::no_slow_types;
|
||||
use crate::tools::registry::diagnostics::PublishDiagnostic;
|
||||
use crate::tools::registry::diagnostics::PublishDiagnosticsCollector;
|
||||
use crate::tools::registry::graph::collect_invalid_external_imports;
|
||||
use crate::util::display::human_size;
|
||||
use crate::util::import_map::ImportMapUnfurler;
|
||||
|
||||
mod api;
|
||||
mod auth;
|
||||
|
@ -50,10 +50,13 @@ mod graph;
|
|||
mod paths;
|
||||
mod publish_order;
|
||||
mod tar;
|
||||
mod unfurl;
|
||||
|
||||
use auth::get_auth_method;
|
||||
use auth::AuthMethod;
|
||||
use publish_order::PublishOrderGraph;
|
||||
pub use unfurl::deno_json_deps;
|
||||
use unfurl::SpecifierUnfurler;
|
||||
|
||||
use super::check::TypeChecker;
|
||||
|
||||
|
@ -81,12 +84,15 @@ impl PreparedPublishPackage {
|
|||
static SUGGESTED_ENTRYPOINTS: [&str; 4] =
|
||||
["mod.ts", "mod.js", "index.ts", "index.js"];
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
async fn prepare_publish(
|
||||
package_name: &str,
|
||||
deno_json: &ConfigFile,
|
||||
source_cache: Arc<ParsedSourceCache>,
|
||||
graph: Arc<deno_graph::ModuleGraph>,
|
||||
mapped_resolver: Arc<MappedSpecifierResolver>,
|
||||
sloppy_imports_resolver: Option<SloppyImportsResolver>,
|
||||
bare_node_builtins: bool,
|
||||
diagnostics_collector: &PublishDiagnosticsCollector,
|
||||
) -> Result<Rc<PreparedPublishPackage>, AnyError> {
|
||||
let config_path = deno_json.specifier.to_file_path().unwrap();
|
||||
|
@ -132,7 +138,11 @@ async fn prepare_publish(
|
|||
|
||||
let diagnostics_collector = diagnostics_collector.clone();
|
||||
let tarball = deno_core::unsync::spawn_blocking(move || {
|
||||
let unfurler = ImportMapUnfurler::new(&mapped_resolver);
|
||||
let unfurler = SpecifierUnfurler::new(
|
||||
&mapped_resolver,
|
||||
sloppy_imports_resolver.as_ref(),
|
||||
bare_node_builtins,
|
||||
);
|
||||
tar::create_gzipped_tarball(
|
||||
&dir_path,
|
||||
LazyGraphSourceParser::new(&source_cache, &graph),
|
||||
|
@ -661,7 +671,9 @@ async fn prepare_packages_for_publishing(
|
|||
let module_graph_creator = cli_factory.module_graph_creator().await?.as_ref();
|
||||
let source_cache = cli_factory.parsed_source_cache();
|
||||
let type_checker = cli_factory.type_checker().await?;
|
||||
let fs = cli_factory.fs();
|
||||
let cli_options = cli_factory.cli_options();
|
||||
let bare_node_builtins = cli_options.unstable_bare_node_builtins();
|
||||
|
||||
if members.len() > 1 {
|
||||
println!("Publishing a workspace...");
|
||||
|
@ -686,6 +698,11 @@ async fn prepare_packages_for_publishing(
|
|||
.into_iter()
|
||||
.map(|member| {
|
||||
let mapped_resolver = mapped_resolver.clone();
|
||||
let sloppy_imports_resolver = if cli_options.unstable_sloppy_imports() {
|
||||
Some(SloppyImportsResolver::new(fs.clone()))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let graph = graph.clone();
|
||||
async move {
|
||||
let package = prepare_publish(
|
||||
|
@ -694,6 +711,8 @@ async fn prepare_packages_for_publishing(
|
|||
source_cache.clone(),
|
||||
graph,
|
||||
mapped_resolver,
|
||||
sloppy_imports_resolver,
|
||||
bare_node_builtins,
|
||||
diagnostics_collector,
|
||||
)
|
||||
.await
|
||||
|
|
|
@ -18,10 +18,10 @@ use tar::Header;
|
|||
|
||||
use crate::cache::LazyGraphSourceParser;
|
||||
use crate::tools::registry::paths::PackagePath;
|
||||
use crate::util::import_map::ImportMapUnfurler;
|
||||
|
||||
use super::diagnostics::PublishDiagnostic;
|
||||
use super::diagnostics::PublishDiagnosticsCollector;
|
||||
use super::unfurl::SpecifierUnfurler;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct PublishableTarballFile {
|
||||
|
@ -40,7 +40,7 @@ pub fn create_gzipped_tarball(
|
|||
dir: &Path,
|
||||
source_parser: LazyGraphSourceParser,
|
||||
diagnostics_collector: &PublishDiagnosticsCollector,
|
||||
unfurler: &ImportMapUnfurler,
|
||||
unfurler: &SpecifierUnfurler,
|
||||
file_patterns: Option<FilePatterns>,
|
||||
) -> Result<PublishableTarball, AnyError> {
|
||||
let mut tar = TarGzArchive::new();
|
||||
|
@ -192,7 +192,7 @@ pub fn create_gzipped_tarball(
|
|||
fn resolve_content_maybe_unfurling(
|
||||
path: &Path,
|
||||
specifier: &Url,
|
||||
unfurler: &ImportMapUnfurler,
|
||||
unfurler: &SpecifierUnfurler,
|
||||
source_parser: LazyGraphSourceParser,
|
||||
diagnostics_collector: &PublishDiagnosticsCollector,
|
||||
) -> Result<Vec<u8>, AnyError> {
|
||||
|
@ -241,7 +241,7 @@ fn resolve_content_maybe_unfurling(
|
|||
|
||||
log::debug!("Unfurling {}", specifier);
|
||||
let mut reporter = |diagnostic| {
|
||||
diagnostics_collector.push(PublishDiagnostic::ImportMapUnfurl(diagnostic));
|
||||
diagnostics_collector.push(PublishDiagnostic::SpecifierUnfurl(diagnostic));
|
||||
};
|
||||
let content = unfurler.unfurl(specifier, &parsed_source, &mut reporter);
|
||||
Ok(content.into_bytes())
|
||||
|
|
|
@ -11,11 +11,13 @@ use deno_graph::DefaultModuleAnalyzer;
|
|||
use deno_graph::DependencyDescriptor;
|
||||
use deno_graph::DynamicTemplatePart;
|
||||
use deno_graph::TypeScriptReference;
|
||||
use deno_runtime::deno_node::is_builtin_node_module;
|
||||
use deno_semver::jsr::JsrDepPackageReq;
|
||||
use deno_semver::jsr::JsrPackageReqReference;
|
||||
use deno_semver::npm::NpmPackageReqReference;
|
||||
|
||||
use crate::resolver::MappedSpecifierResolver;
|
||||
use crate::resolver::SloppyImportsResolver;
|
||||
|
||||
pub fn deno_json_deps(
|
||||
config: &deno_config::ConfigFile,
|
||||
|
@ -61,7 +63,7 @@ fn values_to_set<'a>(
|
|||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum ImportMapUnfurlDiagnostic {
|
||||
pub enum SpecifierUnfurlerDiagnostic {
|
||||
UnanalyzableDynamicImport {
|
||||
specifier: ModuleSpecifier,
|
||||
text_info: SourceTextInfo,
|
||||
|
@ -69,7 +71,7 @@ pub enum ImportMapUnfurlDiagnostic {
|
|||
},
|
||||
}
|
||||
|
||||
impl ImportMapUnfurlDiagnostic {
|
||||
impl SpecifierUnfurlerDiagnostic {
|
||||
pub fn code(&self) -> &'static str {
|
||||
match self {
|
||||
Self::UnanalyzableDynamicImport { .. } => "unanalyzable-dynamic-import",
|
||||
|
@ -85,20 +87,153 @@ impl ImportMapUnfurlDiagnostic {
|
|||
}
|
||||
}
|
||||
|
||||
pub struct ImportMapUnfurler<'a> {
|
||||
import_map: &'a MappedSpecifierResolver,
|
||||
pub struct SpecifierUnfurler<'a> {
|
||||
mapped_resolver: &'a MappedSpecifierResolver,
|
||||
sloppy_imports_resolver: Option<&'a SloppyImportsResolver>,
|
||||
bare_node_builtins: bool,
|
||||
}
|
||||
|
||||
impl<'a> ImportMapUnfurler<'a> {
|
||||
pub fn new(import_map: &'a MappedSpecifierResolver) -> Self {
|
||||
Self { import_map }
|
||||
impl<'a> SpecifierUnfurler<'a> {
|
||||
pub fn new(
|
||||
mapped_resolver: &'a MappedSpecifierResolver,
|
||||
sloppy_imports_resolver: Option<&'a SloppyImportsResolver>,
|
||||
bare_node_builtins: bool,
|
||||
) -> Self {
|
||||
Self {
|
||||
mapped_resolver,
|
||||
sloppy_imports_resolver,
|
||||
bare_node_builtins,
|
||||
}
|
||||
}
|
||||
|
||||
fn unfurl_specifier(
|
||||
&self,
|
||||
referrer: &ModuleSpecifier,
|
||||
specifier: &str,
|
||||
) -> Option<String> {
|
||||
let resolved =
|
||||
if let Ok(resolved) = self.mapped_resolver.resolve(specifier, referrer) {
|
||||
resolved.into_specifier()
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let resolved = match resolved {
|
||||
Some(resolved) => resolved,
|
||||
None if self.bare_node_builtins && is_builtin_node_module(specifier) => {
|
||||
format!("node:{specifier}").parse().unwrap()
|
||||
}
|
||||
None => ModuleSpecifier::options()
|
||||
.base_url(Some(referrer))
|
||||
.parse(specifier)
|
||||
.ok()?,
|
||||
};
|
||||
// TODO(lucacasonato): this requires integration in deno_graph first
|
||||
// let resolved = if let Ok(specifier) =
|
||||
// NpmPackageReqReference::from_specifier(&resolved)
|
||||
// {
|
||||
// if let Some(scope_name) = specifier.req().name.strip_prefix("@jsr/") {
|
||||
// let (scope, name) = scope_name.split_once("__")?;
|
||||
// let new_specifier = JsrPackageReqReference::new(PackageReqReference {
|
||||
// req: PackageReq {
|
||||
// name: format!("@{scope}/{name}"),
|
||||
// version_req: specifier.req().version_req.clone(),
|
||||
// },
|
||||
// sub_path: specifier.sub_path().map(ToOwned::to_owned),
|
||||
// })
|
||||
// .to_string();
|
||||
// ModuleSpecifier::parse(&new_specifier).unwrap()
|
||||
// } else {
|
||||
// resolved
|
||||
// }
|
||||
// } else {
|
||||
// resolved
|
||||
// };
|
||||
let resolved =
|
||||
if let Some(sloppy_imports_resolver) = self.sloppy_imports_resolver {
|
||||
sloppy_imports_resolver
|
||||
.resolve(&resolved)
|
||||
.as_specifier()
|
||||
.clone()
|
||||
} else {
|
||||
resolved
|
||||
};
|
||||
relative_url(&resolved, referrer, specifier)
|
||||
}
|
||||
|
||||
/// Attempts to unfurl the dynamic dependency returning `true` on success
|
||||
/// or `false` when the import was not analyzable.
|
||||
fn try_unfurl_dynamic_dep(
|
||||
&self,
|
||||
module_url: &lsp_types::Url,
|
||||
parsed_source: &ParsedSource,
|
||||
dep: &deno_graph::DynamicDependencyDescriptor,
|
||||
text_changes: &mut Vec<deno_ast::TextChange>,
|
||||
) -> bool {
|
||||
match &dep.argument {
|
||||
deno_graph::DynamicArgument::String(specifier) => {
|
||||
let range = to_range(parsed_source, &dep.argument_range);
|
||||
let maybe_relative_index =
|
||||
parsed_source.text_info().text_str()[range.start..].find(specifier);
|
||||
let Some(relative_index) = maybe_relative_index else {
|
||||
return false;
|
||||
};
|
||||
let unfurled = self.unfurl_specifier(module_url, specifier);
|
||||
let Some(unfurled) = unfurled else {
|
||||
return false;
|
||||
};
|
||||
let start = range.start + relative_index;
|
||||
text_changes.push(deno_ast::TextChange {
|
||||
range: start..start + specifier.len(),
|
||||
new_text: unfurled,
|
||||
});
|
||||
true
|
||||
}
|
||||
deno_graph::DynamicArgument::Template(parts) => match parts.first() {
|
||||
Some(DynamicTemplatePart::String { value: specifier }) => {
|
||||
// relative doesn't need to be modified
|
||||
let is_relative =
|
||||
specifier.starts_with("./") || specifier.starts_with("../");
|
||||
if is_relative {
|
||||
return true;
|
||||
}
|
||||
if !specifier.ends_with('/') {
|
||||
return false;
|
||||
}
|
||||
let unfurled = self.unfurl_specifier(module_url, specifier);
|
||||
let Some(unfurled) = unfurled else {
|
||||
return false;
|
||||
};
|
||||
let range = to_range(parsed_source, &dep.argument_range);
|
||||
let maybe_relative_index =
|
||||
parsed_source.text_info().text_str()[range.start..].find(specifier);
|
||||
let Some(relative_index) = maybe_relative_index else {
|
||||
return false;
|
||||
};
|
||||
let start = range.start + relative_index;
|
||||
text_changes.push(deno_ast::TextChange {
|
||||
range: start..start + specifier.len(),
|
||||
new_text: unfurled,
|
||||
});
|
||||
true
|
||||
}
|
||||
Some(DynamicTemplatePart::Expr) => {
|
||||
false // failed analyzing
|
||||
}
|
||||
None => {
|
||||
true // ignore
|
||||
}
|
||||
},
|
||||
deno_graph::DynamicArgument::Expr => {
|
||||
false // failed analyzing
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unfurl(
|
||||
&self,
|
||||
url: &ModuleSpecifier,
|
||||
parsed_source: &ParsedSource,
|
||||
diagnostic_reporter: &mut dyn FnMut(ImportMapUnfurlDiagnostic),
|
||||
diagnostic_reporter: &mut dyn FnMut(SpecifierUnfurlerDiagnostic),
|
||||
) -> String {
|
||||
let mut text_changes = Vec::new();
|
||||
let module_info = DefaultModuleAnalyzer::module_info(parsed_source);
|
||||
|
@ -106,15 +241,12 @@ impl<'a> ImportMapUnfurler<'a> {
|
|||
|specifier: &str,
|
||||
range: &deno_graph::PositionRange,
|
||||
text_changes: &mut Vec<deno_ast::TextChange>| {
|
||||
let resolved = self.import_map.resolve(specifier, url);
|
||||
if let Ok(resolved) = resolved {
|
||||
if let Some(resolved) = resolved.into_specifier() {
|
||||
if let Some(unfurled) = self.unfurl_specifier(url, specifier) {
|
||||
text_changes.push(deno_ast::TextChange {
|
||||
range: to_range(parsed_source, range),
|
||||
new_text: make_relative_to(url, &resolved),
|
||||
new_text: unfurled,
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
for dep in &module_info.dependencies {
|
||||
match dep {
|
||||
|
@ -126,8 +258,7 @@ impl<'a> ImportMapUnfurler<'a> {
|
|||
);
|
||||
}
|
||||
DependencyDescriptor::Dynamic(dep) => {
|
||||
let success = try_unfurl_dynamic_dep(
|
||||
self.import_map,
|
||||
let success = self.try_unfurl_dynamic_dep(
|
||||
url,
|
||||
parsed_source,
|
||||
dep,
|
||||
|
@ -144,7 +275,7 @@ impl<'a> ImportMapUnfurler<'a> {
|
|||
.line_start(dep.argument_range.end.line)
|
||||
+ dep.argument_range.end.character;
|
||||
diagnostic_reporter(
|
||||
ImportMapUnfurlDiagnostic::UnanalyzableDynamicImport {
|
||||
SpecifierUnfurlerDiagnostic::UnanalyzableDynamicImport {
|
||||
specifier: url.to_owned(),
|
||||
range: SourceRange::new(start_pos, end_pos),
|
||||
text_info: parsed_source.text_info().clone(),
|
||||
|
@ -188,85 +319,20 @@ impl<'a> ImportMapUnfurler<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
fn make_relative_to(from: &ModuleSpecifier, to: &ModuleSpecifier) -> String {
|
||||
if to.scheme() == "file" {
|
||||
format!("./{}", from.make_relative(to).unwrap())
|
||||
fn relative_url(
|
||||
resolved: &ModuleSpecifier,
|
||||
referrer: &ModuleSpecifier,
|
||||
specifier: &str,
|
||||
) -> Option<String> {
|
||||
let new_specifier = if resolved.scheme() == "file" {
|
||||
format!("./{}", referrer.make_relative(resolved).unwrap())
|
||||
} else {
|
||||
to.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempts to unfurl the dynamic dependency returning `true` on success
|
||||
/// or `false` when the import was not analyzable.
|
||||
fn try_unfurl_dynamic_dep(
|
||||
mapped_resolver: &MappedSpecifierResolver,
|
||||
module_url: &lsp_types::Url,
|
||||
parsed_source: &ParsedSource,
|
||||
dep: &deno_graph::DynamicDependencyDescriptor,
|
||||
text_changes: &mut Vec<deno_ast::TextChange>,
|
||||
) -> bool {
|
||||
match &dep.argument {
|
||||
deno_graph::DynamicArgument::String(value) => {
|
||||
let range = to_range(parsed_source, &dep.argument_range);
|
||||
let maybe_relative_index =
|
||||
parsed_source.text_info().text_str()[range.start..].find(value);
|
||||
let Some(relative_index) = maybe_relative_index else {
|
||||
return false;
|
||||
resolved.to_string()
|
||||
};
|
||||
let resolved = mapped_resolver.resolve(value, module_url);
|
||||
let Ok(resolved) = resolved else {
|
||||
return false;
|
||||
};
|
||||
let Some(resolved) = resolved.into_specifier() else {
|
||||
return false;
|
||||
};
|
||||
let start = range.start + relative_index;
|
||||
text_changes.push(deno_ast::TextChange {
|
||||
range: start..start + value.len(),
|
||||
new_text: make_relative_to(module_url, &resolved),
|
||||
});
|
||||
true
|
||||
}
|
||||
deno_graph::DynamicArgument::Template(parts) => match parts.first() {
|
||||
Some(DynamicTemplatePart::String { value }) => {
|
||||
// relative doesn't need to be modified
|
||||
let is_relative = value.starts_with("./") || value.starts_with("../");
|
||||
if is_relative {
|
||||
return true;
|
||||
}
|
||||
if !value.ends_with('/') {
|
||||
return false;
|
||||
}
|
||||
let Ok(resolved) = mapped_resolver.resolve(value, module_url) else {
|
||||
return false;
|
||||
};
|
||||
let Some(resolved) = resolved.into_specifier() else {
|
||||
return false;
|
||||
};
|
||||
let range = to_range(parsed_source, &dep.argument_range);
|
||||
let maybe_relative_index =
|
||||
parsed_source.text_info().text_str()[range.start..].find(value);
|
||||
let Some(relative_index) = maybe_relative_index else {
|
||||
return false;
|
||||
};
|
||||
let start = range.start + relative_index;
|
||||
text_changes.push(deno_ast::TextChange {
|
||||
range: start..start + value.len(),
|
||||
new_text: make_relative_to(module_url, &resolved),
|
||||
});
|
||||
true
|
||||
}
|
||||
Some(DynamicTemplatePart::Expr) => {
|
||||
false // failed analyzing
|
||||
}
|
||||
None => {
|
||||
true // ignore
|
||||
}
|
||||
},
|
||||
deno_graph::DynamicArgument::Expr => {
|
||||
false // failed analyzing
|
||||
}
|
||||
if new_specifier == specifier {
|
||||
return None;
|
||||
}
|
||||
Some(new_specifier)
|
||||
}
|
||||
|
||||
fn to_range(
|
||||
|
@ -290,6 +356,7 @@ fn to_range(
|
|||
mod tests {
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::args::package_json::get_local_package_json_version_reqs;
|
||||
use crate::args::PackageJsonDepsProvider;
|
||||
|
||||
use super::*;
|
||||
|
@ -297,8 +364,12 @@ mod tests {
|
|||
use deno_ast::ModuleSpecifier;
|
||||
use deno_core::serde_json::json;
|
||||
use deno_core::url::Url;
|
||||
use deno_runtime::deno_fs::RealFs;
|
||||
use deno_runtime::deno_node::PackageJson;
|
||||
use import_map::ImportMapWithDiagnostics;
|
||||
use indexmap::IndexMap;
|
||||
use pretty_assertions::assert_eq;
|
||||
use test_util::testdata_path;
|
||||
|
||||
fn parse_ast(specifier: &Url, source_code: &str) -> ParsedSource {
|
||||
let media_type = MediaType::from_specifier(specifier);
|
||||
|
@ -315,22 +386,38 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_unfurling() {
|
||||
let cwd = testdata_path().join("unfurl").to_path_buf();
|
||||
|
||||
let deno_json_url =
|
||||
ModuleSpecifier::parse("file:///dev/deno.json").unwrap();
|
||||
ModuleSpecifier::from_file_path(cwd.join("deno.json")).unwrap();
|
||||
let value = json!({
|
||||
"imports": {
|
||||
"express": "npm:express@5",
|
||||
"lib/": "./lib/",
|
||||
"fizz": "./fizz/mod.ts"
|
||||
"fizz": "./fizz/mod.ts",
|
||||
"@std/fs": "npm:@jsr/std__fs@1",
|
||||
}
|
||||
});
|
||||
let ImportMapWithDiagnostics { import_map, .. } =
|
||||
import_map::parse_from_value(deno_json_url, value).unwrap();
|
||||
let mapped_resolved = MappedSpecifierResolver::new(
|
||||
let mut package_json = PackageJson::empty(cwd.join("package.json"));
|
||||
package_json.dependencies =
|
||||
Some(IndexMap::from([("chalk".to_string(), "5".to_string())]));
|
||||
let mapped_resolver = MappedSpecifierResolver::new(
|
||||
Some(Arc::new(import_map)),
|
||||
Arc::new(PackageJsonDepsProvider::new(None)),
|
||||
Arc::new(PackageJsonDepsProvider::new(Some(
|
||||
get_local_package_json_version_reqs(&package_json),
|
||||
))),
|
||||
);
|
||||
|
||||
let fs = Arc::new(RealFs);
|
||||
let sloppy_imports_resolver = SloppyImportsResolver::new(fs);
|
||||
|
||||
let unfurler = SpecifierUnfurler::new(
|
||||
&mapped_resolver,
|
||||
Some(&sloppy_imports_resolver),
|
||||
true,
|
||||
);
|
||||
let unfurler = ImportMapUnfurler::new(&mapped_resolved);
|
||||
|
||||
// Unfurling TS file should apply changes.
|
||||
{
|
||||
|
@ -338,6 +425,16 @@ mod tests {
|
|||
import foo from "lib/foo.ts";
|
||||
import bar from "lib/bar.ts";
|
||||
import fizz from "fizz";
|
||||
import chalk from "chalk";
|
||||
import baz from "./baz";
|
||||
import b from "./b.js";
|
||||
import b2 from "./b";
|
||||
import url from "url";
|
||||
// TODO: unfurl these to jsr
|
||||
// import "npm:@jsr/std__fs@1/file";
|
||||
// import "npm:@jsr/std__fs@1";
|
||||
// import "npm:@jsr/std__fs";
|
||||
// import "@std/fs";
|
||||
|
||||
const test1 = await import("lib/foo.ts");
|
||||
const test2 = await import(`lib/foo.ts`);
|
||||
|
@ -347,7 +444,8 @@ const test4 = await import(`./lib/${expr}`);
|
|||
const test5 = await import(`lib${expr}`);
|
||||
const test6 = await import(`${expr}`);
|
||||
"#;
|
||||
let specifier = ModuleSpecifier::parse("file:///dev/mod.ts").unwrap();
|
||||
let specifier =
|
||||
ModuleSpecifier::from_file_path(cwd.join("mod.ts")).unwrap();
|
||||
let source = parse_ast(&specifier, source_code);
|
||||
let mut d = Vec::new();
|
||||
let mut reporter = |diagnostic| d.push(diagnostic);
|
||||
|
@ -356,7 +454,7 @@ const test6 = await import(`${expr}`);
|
|||
assert!(
|
||||
matches!(
|
||||
d[0],
|
||||
ImportMapUnfurlDiagnostic::UnanalyzableDynamicImport { .. }
|
||||
SpecifierUnfurlerDiagnostic::UnanalyzableDynamicImport { .. }
|
||||
),
|
||||
"{:?}",
|
||||
d[0]
|
||||
|
@ -364,7 +462,7 @@ const test6 = await import(`${expr}`);
|
|||
assert!(
|
||||
matches!(
|
||||
d[1],
|
||||
ImportMapUnfurlDiagnostic::UnanalyzableDynamicImport { .. }
|
||||
SpecifierUnfurlerDiagnostic::UnanalyzableDynamicImport { .. }
|
||||
),
|
||||
"{:?}",
|
||||
d[1]
|
||||
|
@ -373,6 +471,16 @@ const test6 = await import(`${expr}`);
|
|||
import foo from "./lib/foo.ts";
|
||||
import bar from "./lib/bar.ts";
|
||||
import fizz from "./fizz/mod.ts";
|
||||
import chalk from "npm:chalk@5";
|
||||
import baz from "./baz/index.js";
|
||||
import b from "./b.ts";
|
||||
import b2 from "./b.ts";
|
||||
import url from "node:url";
|
||||
// TODO: unfurl these to jsr
|
||||
// import "npm:@jsr/std__fs@1/file";
|
||||
// import "npm:@jsr/std__fs@1";
|
||||
// import "npm:@jsr/std__fs";
|
||||
// import "@std/fs";
|
||||
|
||||
const test1 = await import("./lib/foo.ts");
|
||||
const test2 = await import(`./lib/foo.ts`);
|
|
@ -8,7 +8,6 @@ pub mod display;
|
|||
pub mod draw_thread;
|
||||
pub mod file_watcher;
|
||||
pub mod fs;
|
||||
pub mod import_map;
|
||||
pub mod logger;
|
||||
pub mod path;
|
||||
pub mod progress_bar;
|
||||
|
|
|
@ -224,6 +224,22 @@ itest!(config_flag {
|
|||
http_server: true,
|
||||
});
|
||||
|
||||
itest!(bare_node_builtins {
|
||||
args: "publish --token 'sadfasdf' --dry-run --unstable-bare-node-builtins",
|
||||
output: "publish/bare_node_builtins.out",
|
||||
cwd: Some("publish/bare_node_builtins"),
|
||||
envs: env_vars_for_jsr_npm_tests(),
|
||||
http_server: true,
|
||||
});
|
||||
|
||||
itest!(sloppy_imports {
|
||||
args: "publish --token 'sadfasdf' --dry-run --unstable-sloppy-imports",
|
||||
output: "publish/sloppy_imports.out",
|
||||
cwd: Some("publish/sloppy_imports"),
|
||||
envs: env_vars_for_jsr_tests(),
|
||||
http_server: true,
|
||||
});
|
||||
|
||||
itest!(jsr_jsonc {
|
||||
args: "publish --token 'sadfasdf'",
|
||||
cwd: Some("publish/jsr_jsonc"),
|
||||
|
|
11
tests/testdata/publish/bare_node_builtins.out
vendored
Normal file
11
tests/testdata/publish/bare_node_builtins.out
vendored
Normal file
|
@ -0,0 +1,11 @@
|
|||
Warning: Resolving "url" as "node:url" at file:///[WILDCARD]/publish/bare_node_builtins/mod.ts:1:22. If you want to use a built-in Node module, add a "node:" prefix.
|
||||
Warning: Resolving "url" as "node:url" at file:///[WILDCARD]/publish/bare_node_builtins/mod.ts:1:22. If you want to use a built-in Node module, add a "node:" prefix.
|
||||
Download http://localhost:4545/npm/registry/@types/node
|
||||
Download http://localhost:4545/npm/registry/@types/node/node-18.8.2.tgz
|
||||
Check file:///[WILDCARD]/publish/bare_node_builtins/mod.ts
|
||||
Checking for slow types in the public API...
|
||||
Check file:///[WILDCARD]/publish/bare_node_builtins/mod.ts
|
||||
Simulating publish of @foo/bar@1.0.0 with files:
|
||||
file:///[WILDCARD]/publish/bare_node_builtins/deno.json (87B)
|
||||
file:///[WILDCARD]/publish/bare_node_builtins/mod.ts (121B)
|
||||
Warning Aborting due to --dry-run
|
7
tests/testdata/publish/bare_node_builtins/deno.json
vendored
Normal file
7
tests/testdata/publish/bare_node_builtins/deno.json
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"name": "@foo/bar",
|
||||
"version": "1.0.0",
|
||||
"exports": {
|
||||
".": "./mod.ts"
|
||||
}
|
||||
}
|
5
tests/testdata/publish/bare_node_builtins/mod.ts
vendored
Normal file
5
tests/testdata/publish/bare_node_builtins/mod.ts
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
import * as url from "url";
|
||||
|
||||
export function foobar(): { href: string } {
|
||||
return url.pathToFileURL("/foo/bar");
|
||||
}
|
12
tests/testdata/publish/sloppy_imports.out
vendored
Normal file
12
tests/testdata/publish/sloppy_imports.out
vendored
Normal file
|
@ -0,0 +1,12 @@
|
|||
Warning Sloppy module resolution (hint: specify path to index.ts file in directory instead)
|
||||
at file:///[WILDCARD]/publish/sloppy_imports/mod.ts:1:20
|
||||
Warning Sloppy module resolution (hint: specify path to index.ts file in directory instead)
|
||||
at file:///[WILDCARD]/publish/sloppy_imports/mod.ts:1:20
|
||||
Check file:///[WILDCARD]/publish/sloppy_imports/mod.ts
|
||||
Checking for slow types in the public API...
|
||||
Check file:///[WILDCARD]/publish/sloppy_imports/mod.ts
|
||||
Simulating publish of @foo/bar@1.0.0 with files:
|
||||
file:///[WILDCARD]/publish/sloppy_imports/b/index.ts (27B)
|
||||
file:///[WILDCARD]/publish/sloppy_imports/deno.json (87B)
|
||||
file:///[WILDCARD]/publish/sloppy_imports/mod.ts (35B)
|
||||
Warning Aborting due to --dry-run
|
1
tests/testdata/publish/sloppy_imports/b/index.ts
vendored
Normal file
1
tests/testdata/publish/sloppy_imports/b/index.ts
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
export const PI = Math.PI;
|
7
tests/testdata/publish/sloppy_imports/deno.json
vendored
Normal file
7
tests/testdata/publish/sloppy_imports/deno.json
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"name": "@foo/bar",
|
||||
"version": "1.0.0",
|
||||
"exports": {
|
||||
".": "./mod.ts"
|
||||
}
|
||||
}
|
1
tests/testdata/publish/sloppy_imports/mod.ts
vendored
Normal file
1
tests/testdata/publish/sloppy_imports/mod.ts
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
export { PI } from "./b";
|
|
@ -7,9 +7,9 @@ warning[unanalyzable-dynamic-import]: unable to analyze dynamic import
|
|||
2 | await import("asd " + asd);
|
||||
| ^^^^^^^^^^^^ the unanalyzable dynamic import
|
||||
|
||||
info: after publishing this package, imports from the local import map do not work
|
||||
info: after publishing this package, imports from the local import map / package.json do not work
|
||||
info: dynamic imports that can not be analyzed at publish time will not be rewritten automatically
|
||||
info: make sure the dynamic import is resolvable at runtime without an import map
|
||||
info: make sure the dynamic import is resolvable at runtime without an import map / package.json
|
||||
|
||||
Publishing @foo/bar@1.0.0 ...
|
||||
Successfully published @foo/bar@1.0.0
|
||||
|
|
0
tests/testdata/unfurl/b.ts
vendored
Normal file
0
tests/testdata/unfurl/b.ts
vendored
Normal file
0
tests/testdata/unfurl/baz/index.js
vendored
Normal file
0
tests/testdata/unfurl/baz/index.js
vendored
Normal file
Loading…
Reference in a new issue