mirror of
https://github.com/denoland/deno.git
synced 2024-12-25 16:49:18 -05:00
chore: improve unanalyzable dynamic import diagnostic (#22051)
This commit is contained in:
parent
930ce20870
commit
745333f073
11 changed files with 196 additions and 63 deletions
|
@ -50,6 +50,17 @@ itest!(javascript_missing_decl_file {
|
||||||
temp_cwd: true,
|
temp_cwd: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
itest!(unanalyzable_dynamic_import {
|
||||||
|
args: "publish --token 'sadfasdf'",
|
||||||
|
output: "publish/unanalyzable_dynamic_import.out",
|
||||||
|
cwd: Some("publish/unanalyzable_dynamic_import"),
|
||||||
|
copy_temp_dir: Some("publish/unanalyzable_dynamic_import"),
|
||||||
|
envs: env_vars_for_registry(),
|
||||||
|
exit_code: 0,
|
||||||
|
http_server: true,
|
||||||
|
temp_cwd: true,
|
||||||
|
});
|
||||||
|
|
||||||
itest!(javascript_decl_file {
|
itest!(javascript_decl_file {
|
||||||
args: "publish --token 'sadfasdf'",
|
args: "publish --token 'sadfasdf'",
|
||||||
output: "publish/javascript_decl_file.out",
|
output: "publish/javascript_decl_file.out",
|
||||||
|
|
|
@ -9,5 +9,4 @@ error[zap-missing-explicit-return-type]: missing explicit return type in the pub
|
||||||
info: all functions in the public API must have an explicit return type
|
info: all functions in the public API must have an explicit return type
|
||||||
docs: https://jsr.io/go/zap-missing-explicit-return-type
|
docs: https://jsr.io/go/zap-missing-explicit-return-type
|
||||||
|
|
||||||
|
|
||||||
error: Found 1 problem
|
error: Found 1 problem
|
||||||
|
|
|
@ -7,7 +7,6 @@ warning[zap-unsupported-javascript-entrypoint]: used a JavaScript module without
|
||||||
info: fast check avoids type inference, so JavaScript entrypoints should be avoided
|
info: fast check avoids type inference, so JavaScript entrypoints should be avoided
|
||||||
docs: https://jsr.io/go/zap-unsupported-javascript-entrypoint
|
docs: https://jsr.io/go/zap-unsupported-javascript-entrypoint
|
||||||
|
|
||||||
|
|
||||||
Publishing @foo/bar@1.0.0 ...
|
Publishing @foo/bar@1.0.0 ...
|
||||||
Successfully published @foo/bar@1.0.0
|
Successfully published @foo/bar@1.0.0
|
||||||
Visit http://127.0.0.1:4250/@foo/bar@1.0.0 for details
|
Visit http://127.0.0.1:4250/@foo/bar@1.0.0 for details
|
||||||
|
|
16
cli/tests/testdata/publish/unanalyzable_dynamic_import.out
vendored
Normal file
16
cli/tests/testdata/publish/unanalyzable_dynamic_import.out
vendored
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
Checking fast check type graph for errors...
|
||||||
|
Ensuring type checks...
|
||||||
|
Check file://[WILDCARD]/mod.ts
|
||||||
|
warning[unanalyzable-dynamic-import]: unable to analyze dynamic import
|
||||||
|
--> [WILDCARD]mod.ts:1:7
|
||||||
|
|
|
||||||
|
1 | await import("asd " + asd);
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^ the unanalyzable dynamic import
|
||||||
|
|
||||||
|
info: after publishing this package, imports from the local import map 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
|
||||||
|
|
||||||
|
Publishing @foo/bar@1.0.0 ...
|
||||||
|
Successfully published @foo/bar@1.0.0
|
||||||
|
Visit http://127.0.0.1:4250/@foo/bar@1.0.0 for details
|
7
cli/tests/testdata/publish/unanalyzable_dynamic_import/deno.json
vendored
Normal file
7
cli/tests/testdata/publish/unanalyzable_dynamic_import/deno.json
vendored
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"name": "@foo/bar",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"exports": {
|
||||||
|
".": "./mod.ts"
|
||||||
|
}
|
||||||
|
}
|
1
cli/tests/testdata/publish/unanalyzable_dynamic_import/mod.ts
vendored
Normal file
1
cli/tests/testdata/publish/unanalyzable_dynamic_import/mod.ts
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
await import("asd " + asd);
|
|
@ -21,6 +21,7 @@ use crate::diagnostics::DiagnosticSnippetSource;
|
||||||
use crate::diagnostics::DiagnosticSourcePos;
|
use crate::diagnostics::DiagnosticSourcePos;
|
||||||
use crate::diagnostics::DiagnosticSourceRange;
|
use crate::diagnostics::DiagnosticSourceRange;
|
||||||
use crate::diagnostics::SourceTextParsedSourceStore;
|
use crate::diagnostics::SourceTextParsedSourceStore;
|
||||||
|
use crate::util::import_map::ImportMapUnfurlDiagnostic;
|
||||||
|
|
||||||
#[derive(Clone, Default)]
|
#[derive(Clone, Default)]
|
||||||
pub struct PublishDiagnosticsCollector {
|
pub struct PublishDiagnosticsCollector {
|
||||||
|
@ -36,7 +37,7 @@ impl PublishDiagnosticsCollector {
|
||||||
let diagnostics = self.diagnostics.lock().unwrap().take();
|
let diagnostics = self.diagnostics.lock().unwrap().take();
|
||||||
let sources = SourceTextParsedSourceStore(sources);
|
let sources = SourceTextParsedSourceStore(sources);
|
||||||
for diagnostic in diagnostics {
|
for diagnostic in diagnostics {
|
||||||
eprintln!("{}", diagnostic.display(&sources));
|
eprint!("{}", diagnostic.display(&sources));
|
||||||
if matches!(diagnostic.level(), DiagnosticLevel::Error) {
|
if matches!(diagnostic.level(), DiagnosticLevel::Error) {
|
||||||
errors += 1;
|
errors += 1;
|
||||||
}
|
}
|
||||||
|
@ -58,34 +59,42 @@ impl PublishDiagnosticsCollector {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum PublishDiagnostic {
|
pub enum PublishDiagnostic {
|
||||||
FastCheck { diagnostic: FastCheckDiagnostic },
|
FastCheck(FastCheckDiagnostic),
|
||||||
|
ImportMapUnfurl(ImportMapUnfurlDiagnostic),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Diagnostic for PublishDiagnostic {
|
impl Diagnostic for PublishDiagnostic {
|
||||||
fn level(&self) -> DiagnosticLevel {
|
fn level(&self) -> DiagnosticLevel {
|
||||||
match self {
|
match self {
|
||||||
PublishDiagnostic::FastCheck {
|
PublishDiagnostic::FastCheck(
|
||||||
diagnostic: FastCheckDiagnostic::UnsupportedJavaScriptEntrypoint { .. },
|
FastCheckDiagnostic::UnsupportedJavaScriptEntrypoint { .. },
|
||||||
} => DiagnosticLevel::Warning,
|
) => DiagnosticLevel::Warning,
|
||||||
PublishDiagnostic::FastCheck { .. } => DiagnosticLevel::Error,
|
PublishDiagnostic::FastCheck(_) => DiagnosticLevel::Error,
|
||||||
|
PublishDiagnostic::ImportMapUnfurl(_) => DiagnosticLevel::Warning,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn code(&self) -> impl Display + '_ {
|
fn code(&self) -> impl Display + '_ {
|
||||||
match &self {
|
match &self {
|
||||||
PublishDiagnostic::FastCheck { diagnostic, .. } => diagnostic.code(),
|
PublishDiagnostic::FastCheck(diagnostic) => diagnostic.code(),
|
||||||
|
PublishDiagnostic::ImportMapUnfurl(diagnostic) => diagnostic.code(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn message(&self) -> impl Display + '_ {
|
fn message(&self) -> impl Display + '_ {
|
||||||
match &self {
|
match &self {
|
||||||
PublishDiagnostic::FastCheck { diagnostic, .. } => diagnostic.to_string(), // todo
|
PublishDiagnostic::FastCheck(diagnostic) => {
|
||||||
|
Cow::Owned(diagnostic.to_string())
|
||||||
|
}
|
||||||
|
PublishDiagnostic::ImportMapUnfurl(diagnostic) => {
|
||||||
|
Cow::Borrowed(diagnostic.message())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn location(&self) -> DiagnosticLocation {
|
fn location(&self) -> DiagnosticLocation {
|
||||||
match &self {
|
match &self {
|
||||||
PublishDiagnostic::FastCheck { diagnostic } => match diagnostic.range() {
|
PublishDiagnostic::FastCheck(diagnostic) => match diagnostic.range() {
|
||||||
Some(range) => DiagnosticLocation::PositionInFile {
|
Some(range) => DiagnosticLocation::PositionInFile {
|
||||||
specifier: Cow::Borrowed(diagnostic.specifier()),
|
specifier: Cow::Borrowed(diagnostic.specifier()),
|
||||||
source_pos: DiagnosticSourcePos::SourcePos(range.range.start),
|
source_pos: DiagnosticSourcePos::SourcePos(range.range.start),
|
||||||
|
@ -94,12 +103,21 @@ impl Diagnostic for PublishDiagnostic {
|
||||||
specifier: Cow::Borrowed(diagnostic.specifier()),
|
specifier: Cow::Borrowed(diagnostic.specifier()),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
PublishDiagnostic::ImportMapUnfurl(diagnostic) => match diagnostic {
|
||||||
|
ImportMapUnfurlDiagnostic::UnanalyzableDynamicImport {
|
||||||
|
specifier,
|
||||||
|
range,
|
||||||
|
} => DiagnosticLocation::PositionInFile {
|
||||||
|
specifier: Cow::Borrowed(specifier),
|
||||||
|
source_pos: DiagnosticSourcePos::SourcePos(range.start),
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn snippet(&self) -> Option<DiagnosticSnippet<'_>> {
|
fn snippet(&self) -> Option<DiagnosticSnippet<'_>> {
|
||||||
match &self {
|
match &self {
|
||||||
PublishDiagnostic::FastCheck { diagnostic } => {
|
PublishDiagnostic::FastCheck(diagnostic) => {
|
||||||
diagnostic.range().map(|range| DiagnosticSnippet {
|
diagnostic.range().map(|range| DiagnosticSnippet {
|
||||||
source: DiagnosticSnippetSource::Specifier(Cow::Borrowed(
|
source: DiagnosticSnippetSource::Specifier(Cow::Borrowed(
|
||||||
diagnostic.specifier(),
|
diagnostic.specifier(),
|
||||||
|
@ -114,14 +132,29 @@ impl Diagnostic for PublishDiagnostic {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
PublishDiagnostic::ImportMapUnfurl(diagnostic) => match diagnostic {
|
||||||
|
ImportMapUnfurlDiagnostic::UnanalyzableDynamicImport {
|
||||||
|
specifier,
|
||||||
|
range,
|
||||||
|
} => Some(DiagnosticSnippet {
|
||||||
|
source: DiagnosticSnippetSource::Specifier(Cow::Borrowed(specifier)),
|
||||||
|
highlight: DiagnosticSnippetHighlight {
|
||||||
|
style: DiagnosticSnippetHighlightStyle::Warning,
|
||||||
|
range: DiagnosticSourceRange {
|
||||||
|
start: DiagnosticSourcePos::SourcePos(range.start),
|
||||||
|
end: DiagnosticSourcePos::SourcePos(range.end),
|
||||||
|
},
|
||||||
|
description: Some("the unanalyzable dynamic import".into()),
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn hint(&self) -> Option<impl Display + '_> {
|
fn hint(&self) -> Option<impl Display + '_> {
|
||||||
match &self {
|
match &self {
|
||||||
PublishDiagnostic::FastCheck { diagnostic } => {
|
PublishDiagnostic::FastCheck(diagnostic) => Some(diagnostic.fix_hint()),
|
||||||
Some(diagnostic.fix_hint())
|
PublishDiagnostic::ImportMapUnfurl(_) => None,
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -131,7 +164,7 @@ impl Diagnostic for PublishDiagnostic {
|
||||||
|
|
||||||
fn info(&self) -> Cow<'_, [Cow<'_, str>]> {
|
fn info(&self) -> Cow<'_, [Cow<'_, str>]> {
|
||||||
match &self {
|
match &self {
|
||||||
PublishDiagnostic::FastCheck { diagnostic } => {
|
PublishDiagnostic::FastCheck(diagnostic) => {
|
||||||
let infos = diagnostic
|
let infos = diagnostic
|
||||||
.additional_info()
|
.additional_info()
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -139,14 +172,24 @@ impl Diagnostic for PublishDiagnostic {
|
||||||
.collect();
|
.collect();
|
||||||
Cow::Owned(infos)
|
Cow::Owned(infos)
|
||||||
}
|
}
|
||||||
|
PublishDiagnostic::ImportMapUnfurl(diagnostic) => match diagnostic {
|
||||||
|
ImportMapUnfurlDiagnostic::UnanalyzableDynamicImport { .. } => Cow::Borrowed(&[
|
||||||
|
Cow::Borrowed("after publishing this package, imports from the local import map 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")
|
||||||
|
]),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn docs_url(&self) -> Option<impl Display + '_> {
|
fn docs_url(&self) -> Option<impl Display + '_> {
|
||||||
match &self {
|
match &self {
|
||||||
PublishDiagnostic::FastCheck { diagnostic } => {
|
PublishDiagnostic::FastCheck(diagnostic) => {
|
||||||
Some(format!("https://jsr.io/go/{}", diagnostic.code()))
|
Some(format!("https://jsr.io/go/{}", diagnostic.code()))
|
||||||
}
|
}
|
||||||
|
PublishDiagnostic::ImportMapUnfurl(diagnostic) => match diagnostic {
|
||||||
|
ImportMapUnfurlDiagnostic::UnanalyzableDynamicImport { .. } => None,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -94,9 +94,8 @@ pub fn collect_fast_check_type_graph_diagnostics(
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
diagnostics_collector.push(PublishDiagnostic::FastCheck {
|
diagnostics_collector
|
||||||
diagnostic: diagnostic.clone(),
|
.push(PublishDiagnostic::FastCheck(diagnostic.clone()));
|
||||||
});
|
|
||||||
if matches!(
|
if matches!(
|
||||||
diagnostic,
|
diagnostic,
|
||||||
FastCheckDiagnostic::UnsupportedJavaScriptEntrypoint { .. }
|
FastCheckDiagnostic::UnsupportedJavaScriptEntrypoint { .. }
|
||||||
|
|
|
@ -11,9 +11,9 @@ use deno_config::ConfigFile;
|
||||||
use deno_core::anyhow::bail;
|
use deno_core::anyhow::bail;
|
||||||
use deno_core::anyhow::Context;
|
use deno_core::anyhow::Context;
|
||||||
use deno_core::error::AnyError;
|
use deno_core::error::AnyError;
|
||||||
|
use deno_core::futures::FutureExt;
|
||||||
use deno_core::serde_json;
|
use deno_core::serde_json;
|
||||||
use deno_core::serde_json::json;
|
use deno_core::serde_json::json;
|
||||||
use deno_core::unsync::JoinHandle;
|
|
||||||
use deno_core::unsync::JoinSet;
|
use deno_core::unsync::JoinSet;
|
||||||
use deno_runtime::colors;
|
use deno_runtime::colors;
|
||||||
use deno_runtime::deno_fetch::reqwest;
|
use deno_runtime::deno_fetch::reqwest;
|
||||||
|
@ -89,6 +89,7 @@ async fn prepare_publish(
|
||||||
deno_json: &ConfigFile,
|
deno_json: &ConfigFile,
|
||||||
source_cache: Arc<ParsedSourceCache>,
|
source_cache: Arc<ParsedSourceCache>,
|
||||||
import_map: Arc<ImportMap>,
|
import_map: Arc<ImportMap>,
|
||||||
|
diagnostics_collector: &PublishDiagnosticsCollector,
|
||||||
) -> Result<Rc<PreparedPublishPackage>, AnyError> {
|
) -> Result<Rc<PreparedPublishPackage>, AnyError> {
|
||||||
let config_path = deno_json.specifier.to_file_path().unwrap();
|
let config_path = deno_json.specifier.to_file_path().unwrap();
|
||||||
let dir_path = config_path.parent().unwrap().to_path_buf();
|
let dir_path = config_path.parent().unwrap().to_path_buf();
|
||||||
|
@ -134,11 +135,13 @@ async fn prepare_publish(
|
||||||
.to_files_config()
|
.to_files_config()
|
||||||
.map(|files| files.map(|f| f.exclude).unwrap_or_default())?;
|
.map(|files| files.map(|f| f.exclude).unwrap_or_default())?;
|
||||||
|
|
||||||
|
let diagnostics_collector = diagnostics_collector.clone();
|
||||||
let tarball = deno_core::unsync::spawn_blocking(move || {
|
let tarball = deno_core::unsync::spawn_blocking(move || {
|
||||||
let unfurler = ImportMapUnfurler::new(&import_map);
|
let unfurler = ImportMapUnfurler::new(&import_map);
|
||||||
tar::create_gzipped_tarball(
|
tar::create_gzipped_tarball(
|
||||||
&dir_path,
|
&dir_path,
|
||||||
&*source_cache,
|
&*source_cache,
|
||||||
|
&diagnostics_collector,
|
||||||
&unfurler,
|
&unfurler,
|
||||||
&exclude_patterns,
|
&exclude_patterns,
|
||||||
)
|
)
|
||||||
|
@ -666,8 +669,13 @@ async fn prepare_packages_for_publishing(
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
let mut prepared_package_by_name = HashMap::with_capacity(1);
|
let mut prepared_package_by_name = HashMap::with_capacity(1);
|
||||||
let package =
|
let package = prepare_publish(
|
||||||
prepare_publish(&deno_json, source_cache.clone(), import_map).await?;
|
&deno_json,
|
||||||
|
source_cache.clone(),
|
||||||
|
import_map,
|
||||||
|
diagnostics_collector,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
let package_name = format!("@{}/{}", package.scope, package.package);
|
let package_name = format!("@{}/{}", package.scope, package.package);
|
||||||
let publish_order_graph =
|
let publish_order_graph =
|
||||||
PublishOrderGraph::new_single(package_name.clone());
|
PublishOrderGraph::new_single(package_name.clone());
|
||||||
|
@ -692,29 +700,31 @@ async fn prepare_packages_for_publishing(
|
||||||
let publish_order_graph =
|
let publish_order_graph =
|
||||||
publish_order::build_publish_order_graph(&graph, &roots)?;
|
publish_order::build_publish_order_graph(&graph, &roots)?;
|
||||||
|
|
||||||
let results =
|
let results = workspace_config
|
||||||
workspace_config
|
|
||||||
.members
|
.members
|
||||||
.iter()
|
.iter()
|
||||||
.cloned()
|
.cloned()
|
||||||
.map(|member| {
|
.map(|member| {
|
||||||
let import_map = import_map.clone();
|
let import_map = import_map.clone();
|
||||||
let source_cache = source_cache.clone();
|
async move {
|
||||||
deno_core::unsync::spawn(async move {
|
let package = prepare_publish(
|
||||||
let package = prepare_publish(&member.config_file, source_cache, import_map)
|
&member.config_file,
|
||||||
|
source_cache.clone(),
|
||||||
|
import_map.clone(),
|
||||||
|
diagnostics_collector,
|
||||||
|
)
|
||||||
.await
|
.await
|
||||||
.with_context(|| {
|
.with_context(|| {
|
||||||
format!("Failed preparing '{}'.", member.package_name)
|
format!("Failed preparing '{}'.", member.package_name)
|
||||||
})?;
|
})?;
|
||||||
Ok((member.package_name, package))
|
Ok::<_, AnyError>((member.package_name, package))
|
||||||
|
}
|
||||||
|
.boxed()
|
||||||
})
|
})
|
||||||
})
|
.collect::<Vec<_>>();
|
||||||
.collect::<Vec<
|
|
||||||
JoinHandle<Result<(String, Rc<PreparedPublishPackage>), AnyError>>,
|
|
||||||
>>();
|
|
||||||
let results = deno_core::futures::future::join_all(results).await;
|
let results = deno_core::futures::future::join_all(results).await;
|
||||||
for result in results {
|
for result in results {
|
||||||
let (package_name, package) = result??;
|
let (package_name, package) = result?;
|
||||||
prepared_package_by_name.insert(package_name, package);
|
prepared_package_by_name.insert(package_name, package);
|
||||||
}
|
}
|
||||||
Ok((publish_order_graph, prepared_package_by_name))
|
Ok((publish_order_graph, prepared_package_by_name))
|
||||||
|
|
|
@ -15,6 +15,9 @@ use tar::Header;
|
||||||
use crate::util::import_map::ImportMapUnfurler;
|
use crate::util::import_map::ImportMapUnfurler;
|
||||||
use deno_config::glob::PathOrPatternSet;
|
use deno_config::glob::PathOrPatternSet;
|
||||||
|
|
||||||
|
use super::diagnostics::PublishDiagnostic;
|
||||||
|
use super::diagnostics::PublishDiagnosticsCollector;
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub struct PublishableTarballFile {
|
pub struct PublishableTarballFile {
|
||||||
pub path: PathBuf,
|
pub path: PathBuf,
|
||||||
|
@ -32,6 +35,7 @@ pub struct PublishableTarball {
|
||||||
pub fn create_gzipped_tarball(
|
pub fn create_gzipped_tarball(
|
||||||
dir: &Path,
|
dir: &Path,
|
||||||
source_cache: &dyn deno_graph::ParsedSourceStore,
|
source_cache: &dyn deno_graph::ParsedSourceStore,
|
||||||
|
diagnostics_collector: &PublishDiagnosticsCollector,
|
||||||
unfurler: &ImportMapUnfurler,
|
unfurler: &ImportMapUnfurler,
|
||||||
exclude_patterns: &PathOrPatternSet,
|
exclude_patterns: &PathOrPatternSet,
|
||||||
) -> Result<PublishableTarball, AnyError> {
|
) -> Result<PublishableTarball, AnyError> {
|
||||||
|
@ -72,9 +76,11 @@ pub fn create_gzipped_tarball(
|
||||||
});
|
});
|
||||||
let content = match source_cache.get_parsed_source(&url) {
|
let content = match source_cache.get_parsed_source(&url) {
|
||||||
Some(parsed_source) => {
|
Some(parsed_source) => {
|
||||||
let (content, unfurl_diagnostics) =
|
let mut reporter = |diagnostic| {
|
||||||
unfurler.unfurl(&url, &parsed_source);
|
diagnostics_collector
|
||||||
diagnostics.extend_from_slice(&unfurl_diagnostics);
|
.push(PublishDiagnostic::ImportMapUnfurl(diagnostic));
|
||||||
|
};
|
||||||
|
let content = unfurler.unfurl(&url, &parsed_source, &mut reporter);
|
||||||
content.into_bytes()
|
content.into_bytes()
|
||||||
}
|
}
|
||||||
None => data,
|
None => data,
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
|
|
||||||
use deno_ast::ParsedSource;
|
use deno_ast::ParsedSource;
|
||||||
|
use deno_ast::SourceRange;
|
||||||
use deno_core::serde_json;
|
use deno_core::serde_json;
|
||||||
use deno_core::ModuleSpecifier;
|
use deno_core::ModuleSpecifier;
|
||||||
use deno_graph::DefaultModuleAnalyzer;
|
use deno_graph::DefaultModuleAnalyzer;
|
||||||
|
@ -14,8 +15,6 @@ use deno_semver::jsr::JsrPackageReqReference;
|
||||||
use deno_semver::npm::NpmPackageReqReference;
|
use deno_semver::npm::NpmPackageReqReference;
|
||||||
use import_map::ImportMap;
|
use import_map::ImportMap;
|
||||||
|
|
||||||
use crate::graph_util::format_range_with_colors;
|
|
||||||
|
|
||||||
pub fn import_map_deps(value: &serde_json::Value) -> HashSet<JsrDepPackageReq> {
|
pub fn import_map_deps(value: &serde_json::Value) -> HashSet<JsrDepPackageReq> {
|
||||||
let Some(obj) = value.as_object() else {
|
let Some(obj) = value.as_object() else {
|
||||||
return Default::default();
|
return Default::default();
|
||||||
|
@ -69,6 +68,30 @@ fn values_to_set<'a>(
|
||||||
entries
|
entries
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum ImportMapUnfurlDiagnostic {
|
||||||
|
UnanalyzableDynamicImport {
|
||||||
|
specifier: ModuleSpecifier,
|
||||||
|
range: SourceRange,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ImportMapUnfurlDiagnostic {
|
||||||
|
pub fn code(&self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
Self::UnanalyzableDynamicImport { .. } => "unanalyzable-dynamic-import",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn message(&self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
Self::UnanalyzableDynamicImport { .. } => {
|
||||||
|
"unable to analyze dynamic import"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct ImportMapUnfurler<'a> {
|
pub struct ImportMapUnfurler<'a> {
|
||||||
import_map: &'a ImportMap,
|
import_map: &'a ImportMap,
|
||||||
}
|
}
|
||||||
|
@ -82,8 +105,8 @@ impl<'a> ImportMapUnfurler<'a> {
|
||||||
&self,
|
&self,
|
||||||
url: &ModuleSpecifier,
|
url: &ModuleSpecifier,
|
||||||
parsed_source: &ParsedSource,
|
parsed_source: &ParsedSource,
|
||||||
) -> (String, Vec<String>) {
|
diagnostic_reporter: &mut dyn FnMut(ImportMapUnfurlDiagnostic),
|
||||||
let mut diagnostics = Vec::new();
|
) -> String {
|
||||||
let mut text_changes = Vec::new();
|
let mut text_changes = Vec::new();
|
||||||
let module_info = DefaultModuleAnalyzer::module_info(parsed_source);
|
let module_info = DefaultModuleAnalyzer::module_info(parsed_source);
|
||||||
let analyze_specifier =
|
let analyze_specifier =
|
||||||
|
@ -117,14 +140,17 @@ impl<'a> ImportMapUnfurler<'a> {
|
||||||
);
|
);
|
||||||
|
|
||||||
if !success {
|
if !success {
|
||||||
diagnostics.push(
|
let start_pos =
|
||||||
format!("Dynamic import was not analyzable and won't use the import map once published.\n at {}",
|
parsed_source.text_info().line_start(dep.range.start.line)
|
||||||
format_range_with_colors(&deno_graph::Range {
|
+ dep.range.start.character;
|
||||||
specifier: url.clone(),
|
let end_pos =
|
||||||
start: dep.range.start.clone(),
|
parsed_source.text_info().line_start(dep.range.end.line)
|
||||||
end: dep.range.end.clone(),
|
+ dep.range.end.character;
|
||||||
})
|
diagnostic_reporter(
|
||||||
)
|
ImportMapUnfurlDiagnostic::UnanalyzableDynamicImport {
|
||||||
|
specifier: url.to_owned(),
|
||||||
|
range: SourceRange::new(start_pos, end_pos),
|
||||||
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -160,7 +186,7 @@ impl<'a> ImportMapUnfurler<'a> {
|
||||||
parsed_source.text_info().text_str(),
|
parsed_source.text_info().text_str(),
|
||||||
text_changes,
|
text_changes,
|
||||||
);
|
);
|
||||||
(rewritten_text, diagnostics)
|
rewritten_text
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -311,10 +337,26 @@ const test6 = await import(`${expr}`);
|
||||||
"#;
|
"#;
|
||||||
let specifier = ModuleSpecifier::parse("file:///dev/mod.ts").unwrap();
|
let specifier = ModuleSpecifier::parse("file:///dev/mod.ts").unwrap();
|
||||||
let source = parse_ast(&specifier, source_code);
|
let source = parse_ast(&specifier, source_code);
|
||||||
let (unfurled_source, d) = unfurler.unfurl(&specifier, &source);
|
let mut d = Vec::new();
|
||||||
|
let mut reporter = |diagnostic| d.push(diagnostic);
|
||||||
|
let unfurled_source = unfurler.unfurl(&specifier, &source, &mut reporter);
|
||||||
assert_eq!(d.len(), 2);
|
assert_eq!(d.len(), 2);
|
||||||
assert!(d[0].starts_with("Dynamic import was not analyzable and won't use the import map once published."));
|
assert!(
|
||||||
assert!(d[1].starts_with("Dynamic import was not analyzable and won't use the import map once published."));
|
matches!(
|
||||||
|
d[0],
|
||||||
|
ImportMapUnfurlDiagnostic::UnanalyzableDynamicImport { .. }
|
||||||
|
),
|
||||||
|
"{:?}",
|
||||||
|
d[0]
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
matches!(
|
||||||
|
d[1],
|
||||||
|
ImportMapUnfurlDiagnostic::UnanalyzableDynamicImport { .. }
|
||||||
|
),
|
||||||
|
"{:?}",
|
||||||
|
d[1]
|
||||||
|
);
|
||||||
let expected_source = r#"import express from "npm:express@5";"
|
let expected_source = r#"import express from "npm:express@5";"
|
||||||
import foo from "./lib/foo.ts";
|
import foo from "./lib/foo.ts";
|
||||||
import bar from "./lib/bar.ts";
|
import bar from "./lib/bar.ts";
|
||||||
|
|
Loading…
Reference in a new issue