Skip to content

Commit e66c3db

Browse files
authored
fix(rslib): align isolated dts diagnostics (#14143)
1 parent 0ccfa7b commit e66c3db

7 files changed

Lines changed: 114 additions & 43 deletions

File tree

crates/rspack_plugin_rslib/src/isolated_dts.rs

Lines changed: 53 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@ use std::{collections::VecDeque, sync::Arc};
22

33
use rspack_core::{
44
Compilation, DependencyCategory, IsolatedDts, Resolve, ResolveOptionsWithDependencyType,
5-
ResolveResult, TsconfigOptions, TsconfigReferences,
5+
ResolveResult, TsconfigOptions, TsconfigReferences, diagnostics::ModuleBuildError,
66
};
7-
use rspack_error::{Result, error};
7+
use rspack_error::{Diagnostic, Error, Result};
88
use rspack_javascript_compiler::JavaScriptCompiler;
99
use rspack_paths::{Utf8Path, Utf8PathBuf};
10-
use rspack_util::node_path::NodePath;
10+
use rspack_util::{identifier::absolute_to_request, node_path::NodePath};
1111
use rustc_hash::FxHashSet as HashSet;
1212
use swc_core::{
1313
common::FileName,
@@ -24,6 +24,11 @@ pub(crate) struct IsolatedDtsAsset {
2424
pub code: String,
2525
}
2626

27+
pub(crate) struct CompletedIsolatedDtsOutputs {
28+
pub assets: Vec<IsolatedDtsAsset>,
29+
pub diagnostics: Vec<Diagnostic>,
30+
}
31+
2732
struct IsolatedDtsReferences {
2833
issuer_resource_path: String,
2934
references: Vec<String>,
@@ -33,12 +38,17 @@ pub(crate) async fn complete_isolated_dts_outputs(
3338
compilation: &mut Compilation,
3439
options: &SwcEmitDtsOptions,
3540
roots: Vec<IsolatedDts>,
36-
) -> Result<Vec<IsolatedDtsAsset>> {
41+
module_resources: Vec<Utf8PathBuf>,
42+
) -> Result<CompletedIsolatedDtsOutputs> {
3743
if roots.is_empty() {
38-
return Ok(Vec::new());
44+
return Ok(CompletedIsolatedDtsOutputs {
45+
assets: Vec::new(),
46+
diagnostics: Vec::new(),
47+
});
3948
}
4049

4150
let mut outputs = Vec::with_capacity(roots.len());
51+
let mut diagnostics = Vec::new();
4252
let mut queue = VecDeque::with_capacity(roots.len());
4353

4454
for IsolatedDts {
@@ -61,7 +71,10 @@ pub(crate) async fn complete_isolated_dts_outputs(
6171
}
6272

6373
if queue.is_empty() {
64-
return Ok(outputs);
74+
return Ok(CompletedIsolatedDtsOutputs {
75+
assets: outputs,
76+
diagnostics,
77+
});
6578
}
6679

6780
let compiler_root = compilation.options.context.as_path().to_path_buf();
@@ -76,10 +89,14 @@ pub(crate) async fn complete_isolated_dts_outputs(
7689
dependency_category: DependencyCategory::Esm,
7790
});
7891
let mut javascript_compiler = None;
79-
let mut seen: HashSet<Utf8PathBuf> = outputs
80-
.iter()
81-
.map(|output| resolve_path(&compiler_root, &output.resource_path))
82-
.collect();
92+
// Reference completion should only generate files that are absent from the
93+
// normal module graph; normal module builds already own their dts diagnostics.
94+
let mut completed_resources: HashSet<Utf8PathBuf> = module_resources.into_iter().collect();
95+
completed_resources.extend(
96+
outputs
97+
.iter()
98+
.map(|output| resolve_path(&compiler_root, &output.resource_path)),
99+
);
83100

84101
while let Some(IsolatedDtsReferences {
85102
issuer_resource_path,
@@ -115,10 +132,13 @@ pub(crate) async fn complete_isolated_dts_outputs(
115132
continue;
116133
}
117134

118-
if !seen.insert(resource_path.clone()) {
135+
if !completed_resources.insert(resource_path.clone()) {
119136
continue;
120137
}
121138

139+
let diagnostic_file = Utf8PathBuf::from(
140+
absolute_to_request(compiler_root.as_str(), resource_path.as_str()).into_owned(),
141+
);
122142
let std_resource_path = resource_path.clone().into_std_path_buf();
123143
compilation
124144
.file_dependencies
@@ -141,7 +161,24 @@ pub(crate) async fn complete_isolated_dts_outputs(
141161
syntax,
142162
EsVersion::EsNext,
143163
)?;
144-
handle_isolated_dts_diagnostics(&resource_path, dts_output.diagnostics)?;
164+
let mut dts_diagnostics = dts_output.diagnostics.into_iter();
165+
if let Some(first) = dts_diagnostics.next() {
166+
let mut source_error = Error::error(first);
167+
source_error.code = Some("RslibPlugin".into());
168+
169+
let mut dts_error = Error::error("Failed to generate declaration files.".to_string());
170+
dts_error.code = Some("RslibPlugin".into());
171+
dts_error.source_error = Some(Box::new(source_error));
172+
let remaining = dts_diagnostics.collect::<Vec<_>>();
173+
if !remaining.is_empty() {
174+
dts_error.help = Some(remaining.join("\n"));
175+
}
176+
177+
let mut diagnostic: Diagnostic = Error::from(ModuleBuildError::new(dts_error, None)).into();
178+
diagnostic.file = Some(diagnostic_file);
179+
diagnostics.push(diagnostic);
180+
continue;
181+
}
145182

146183
let has_references = !dts_output.references.is_empty();
147184
let resource_path = resource_path.as_str().to_string();
@@ -158,7 +195,10 @@ pub(crate) async fn complete_isolated_dts_outputs(
158195
}
159196
}
160197

161-
Ok(outputs)
198+
Ok(CompletedIsolatedDtsOutputs {
199+
assets: outputs,
200+
diagnostics,
201+
})
162202
}
163203

164204
fn resolve_path(base: &Utf8Path, value: &str) -> Utf8PathBuf {
@@ -231,24 +271,3 @@ fn type_resolve_options(tsconfig: Option<TsconfigOptions>) -> Resolve {
231271
..Default::default()
232272
}
233273
}
234-
235-
fn handle_isolated_dts_diagnostics(
236-
resource_path: &Utf8Path,
237-
diagnostics: Vec<String>,
238-
) -> Result<()> {
239-
let mut diagnostics = diagnostics.into_iter();
240-
let Some(first) = diagnostics.next() else {
241-
return Ok(());
242-
};
243-
let remaining = diagnostics.collect::<Vec<_>>();
244-
let help = if remaining.is_empty() {
245-
String::new()
246-
} else {
247-
format!("\n{}", remaining.join("\n"))
248-
};
249-
250-
Err(error!(
251-
"Failed to generate declaration files for {}.\n{}{}",
252-
resource_path, first, help
253-
))
254-
}

crates/rspack_plugin_rslib/src/plugin.rs

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -349,19 +349,30 @@ async fn process_assets(&self, compilation: &mut Compilation) -> Result<()> {
349349
let Some(options) = &self.options.emit_dts else {
350350
return Ok(());
351351
};
352-
let dts_outputs = compilation
353-
.get_module_graph()
354-
.modules()
355-
.filter_map(|(_, module)| module.build_info().isolated_dts.as_deref().cloned())
356-
.collect::<Vec<_>>();
352+
let mut dts_outputs = Vec::new();
353+
let mut module_resources = Vec::new();
354+
let module_graph = compilation.get_module_graph();
355+
for (_, module) in module_graph.modules() {
356+
let module = module.as_ref();
357+
if let Some(isolated_dts) = module.build_info().isolated_dts.as_deref() {
358+
dts_outputs.push(isolated_dts.clone());
359+
}
360+
if let Some(normal_module) = module.as_normal_module()
361+
&& let Some(resource_path) = normal_module.resource_resolved_data().path()
362+
{
363+
module_resources.push(resource_path.node_normalize());
364+
}
365+
}
357366
if dts_outputs.is_empty() {
358367
return Ok(());
359368
}
360369

361-
let dts_outputs = complete_isolated_dts_outputs(compilation, options, dts_outputs).await?;
370+
let dts_outputs =
371+
complete_isolated_dts_outputs(compilation, options, dts_outputs, module_resources).await?;
372+
compilation.extend_diagnostics(dts_outputs.diagnostics);
362373
let emit_context = EmitIsolatedDtsAssetContext::new(compilation, options);
363374

364-
for dts in dts_outputs {
375+
for dts in dts_outputs.assets {
365376
emit_isolated_dts_asset(compilation, &emit_context, dts)?;
366377
}
367378

tests/rspack-test/statsOutputCases/rslib-emit-isolated-dts-error/__snapshots__/stats.txt

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,16 @@
1+
ERROR in ./foo.ts
2+
× Module build failed:
3+
├─▶ × Failed to generate declaration files.
4+
5+
╰─▶ × x TS9007: Function must have an explicit return type annotation with --isolatedDeclarations.
6+
│ ,-[<TEST_ROOT>/statsOutputCases/rslib-emit-isolated-dts-error/foo.ts<LINE_COL>]
7+
│ 1 | export function foo(value: number) {
8+
│ : ^^^
9+
│ 2 | return value;
10+
│ 3 | }
11+
│ `----
12+
13+
114
ERROR in ./index.ts
215
× Module build failed (from builtin:swc-loader):
316
├─▶ × Failed to generate declaration files.
@@ -10,4 +23,17 @@ ERROR in ./index.ts
1023
│ `----
1124
1225

13-
Rspack compiled with 1 error
26+
ERROR in ./sum.ts
27+
× Module build failed (from builtin:swc-loader):
28+
├─▶ × Failed to generate declaration files.
29+
30+
╰─▶ × x TS9007: Function must have an explicit return type annotation with --isolatedDeclarations.
31+
│ ,-[<TEST_ROOT>/statsOutputCases/rslib-emit-isolated-dts-error/sum.ts<LINE_COL>]
32+
│ 1 | export function sum(left: number, right: number) {
33+
│ : ^^^
34+
│ 2 | return left + right;
35+
│ 3 | }
36+
│ `----
37+
38+
39+
Rspack compiled with 3 errors
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
export function foo(value: number) {
2+
return value;
3+
}
4+
5+
export type Foo = typeof foo;
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import type { Foo } from './foo';
2+
export { sum } from './sum';
3+
4+
export type Result = Foo;

tests/rspack-test/statsOutputCases/rslib-emit-isolated-dts-error/rspack.config.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,10 @@ const {
44

55
/** @type {import('@rspack/core').Configuration} */
66
module.exports = {
7-
entry: './index',
7+
entry: {
8+
index: './index',
9+
reference: './reference',
10+
},
811
stats: 'errors-warnings',
912
resolve: {
1013
extensions: ['...', '.ts', '.tsx', '.jsx'],
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export function sum(left: number, right: number) {
2+
return left + right;
3+
}

0 commit comments

Comments
 (0)