Skip to content

Commit 69144b0

Browse files
authored
fix: emit .d.ts and .js files into _dist folder and other fixes (#463)
Fixes #462 To make this work, also bans publishing packages containing a top level folder called `_dist`. Also we now do not reference `.d.ts` files from other `.d.ts` files using the `.d.ts` extension, because otherwise tsc gets upset. So we instead use `.js` (even if the file does not exist!) Also we emit source maps into separate files, because TSC doesn't understand embedded source maps.
1 parent cac9856 commit 69144b0

18 files changed

+447
-111
lines changed

Cargo.lock

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

api/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ flate2 = "1"
7979
thiserror = "1"
8080
async-tar = "0.4.2"
8181
deno_graph = "0.74.1"
82-
deno_ast = "0.38.1"
82+
deno_ast = { version = "0.38.1", features = ["view"] }
8383
deno_doc = { version = "0.128.0", features = ["tree-sitter"] }
8484
comrak = { version = "0.20.0", default-features = false }
8585
async-trait = "0.1.73"

api/src/ids.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -459,6 +459,9 @@ pub enum VersionValidateError {
459459
/// The path must only contain ascii alphanumeric characters, and the characters
460460
/// '$', '(', ')', '+', '-', '.', '@', '[', ']', '_', '{', '}', '~'.
461461
///
462+
/// The path must not start with `/_dist/`, as this is the directory JSR will
463+
/// emit `.d.ts` and `.js` files to when building the npm tarball.
464+
///
462465
/// Path's are case sensitive, and comparisons and hashing are also case
463466
/// sensitive. However, to ensure no collisions based only on case-sensitivity,
464467
/// one may use the `CaseInsensitivePackagePath` type to compare paths in a
@@ -487,6 +490,7 @@ impl PackagePath {
487490
};
488491

489492
let mut last = None;
493+
let mut first = true;
490494

491495
while let Some(component) = components.next() {
492496
last = Some(component);
@@ -524,6 +528,11 @@ impl PackagePath {
524528
component.to_owned(),
525529
));
526530
}
531+
532+
if first && component.eq_ignore_ascii_case("_dist") {
533+
return Err(PackagePathValidationError::ReservedUnderscoreDist);
534+
}
535+
first = false;
527536
}
528537

529538
// Due to restrictions in how tarballs are built, we need the ensure that
@@ -703,6 +712,11 @@ pub enum PackagePathValidationError {
703712
)]
704713
ReservedName(String),
705714

715+
#[error(
716+
"package path must not start with /_dist/, as this is the directory JSR will emit .d.ts and .js files to when building the npm tarball"
717+
)]
718+
ReservedUnderscoreDist,
719+
706720
#[error("path segment must not end in a dot (found '{0}')")]
707721
TrailingDot(String),
708722

@@ -960,6 +974,7 @@ mod tests {
960974
assert!(PackagePath::try_from("/~/foo").is_ok());
961975
assert!(PackagePath::try_from("/~foo/bar").is_ok());
962976
assert!(PackagePath::try_from("/(section)/32").is_ok());
977+
assert!(PackagePath::try_from("/foo/_dist/23").is_ok());
963978

964979
// Test invalid package paths
965980
assert!(PackagePath::try_from("").is_err());
@@ -996,6 +1011,8 @@ mod tests {
9961011
assert!(PackagePath::try_from("/CON.txt").is_err());
9971012
assert!(PackagePath::try_from("/foo.").is_err());
9981013
assert!(PackagePath::try_from("/f".repeat(81)).is_err());
1014+
assert!(PackagePath::try_from("/_dist").is_err());
1015+
assert!(PackagePath::try_from("/_dist/foo.txt").is_err());
9991016
}
10001017

10011018
#[test]

api/src/npm/emit.rs

Lines changed: 34 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,24 +9,29 @@ use deno_ast::SourceMap;
99
use deno_ast::SourceMapOption;
1010
use deno_ast::TranspileOptions;
1111
use deno_graph::FastCheckTypeModule;
12+
use url::Url;
1213

1314
use crate::npm::import_transform::ImportRewriteTransformer;
15+
use crate::npm::specifiers::relative_import_specifier;
1416

1517
use super::specifiers::RewriteKind;
1618
use super::specifiers::SpecifierRewriter;
1719

1820
pub fn transpile_to_js(
1921
source: &ParsedSource,
2022
specifier_rewriter: SpecifierRewriter,
21-
) -> Result<String, anyhow::Error> {
23+
target_specifier: &Url,
24+
) -> Result<(String, String), anyhow::Error> {
25+
let basename = target_specifier.path().rsplit_once('/').unwrap().1;
2226
let emit_options = deno_ast::EmitOptions {
23-
source_map: SourceMapOption::Inline,
24-
source_map_file: None,
27+
source_map: SourceMapOption::Separate,
28+
source_map_file: Some(basename.to_owned()),
2529
inline_sources: false,
2630
keep_comments: true,
2731
};
2832

29-
let file_name = source.specifier().path().split('/').last().unwrap();
33+
let file_name =
34+
relative_import_specifier(target_specifier, source.specifier());
3035
let source_map =
3136
SourceMap::single(file_name, source.text_info().text_str().to_owned());
3237

@@ -60,27 +65,37 @@ pub fn transpile_to_js(
6065
source.diagnostics(),
6166
)?;
6267

63-
let emitted = emit(&program, &comments, &source_map, &emit_options)?;
68+
let EmittedSource {
69+
mut text,
70+
source_map,
71+
} = emit(&program, &comments, &source_map, &emit_options)?;
72+
if !text.ends_with('\n') {
73+
text.push('\n');
74+
}
75+
text.push_str(format!("//# sourceMappingURL={}.map", basename).as_str());
6476

65-
Ok(emitted.text)
77+
Ok((text, source_map.unwrap()))
6678
})
6779
}
6880

6981
pub fn transpile_to_dts(
7082
source: &ParsedSource,
7183
fast_check_module: &FastCheckTypeModule,
7284
specifier_rewriter: SpecifierRewriter,
73-
) -> Result<String, anyhow::Error> {
85+
target_specifier: &Url,
86+
) -> Result<(String, String), anyhow::Error> {
7487
let dts = fast_check_module.dts.as_ref().unwrap();
7588

89+
let basename = target_specifier.path().rsplit_once('/').unwrap().1;
7690
let emit_options = deno_ast::EmitOptions {
77-
source_map: SourceMapOption::Inline,
78-
source_map_file: None,
91+
source_map: SourceMapOption::Separate,
92+
source_map_file: Some(basename.to_owned()),
7993
inline_sources: false,
8094
keep_comments: true,
8195
};
8296

83-
let file_name = source.specifier().path().split('/').last().unwrap();
97+
let file_name =
98+
relative_import_specifier(target_specifier, source.specifier());
8499
let source_map =
85100
SourceMap::single(file_name, source.text_info().text_str().to_owned());
86101

@@ -94,8 +109,14 @@ pub fn transpile_to_dts(
94109
};
95110
program.visit_mut_with(&mut import_rewrite_transformer);
96111

97-
let EmittedSource { text, .. } =
98-
emit(&program, &comments, &source_map, &emit_options)?;
112+
let EmittedSource {
113+
mut text,
114+
source_map,
115+
} = emit(&program, &comments, &source_map, &emit_options)?;
116+
if !text.ends_with('\n') {
117+
text.push('\n');
118+
}
119+
text.push_str(format!("//# sourceMappingURL={}.map", basename).as_str());
99120

100-
Ok(text)
121+
Ok((text, source_map.unwrap()))
101122
}

api/src/npm/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ pub use self::tarball::NpmTarballOptions;
2929
pub use self::types::NpmMappedJsrPackageName;
3030
use self::types::NpmVersionInfo;
3131

32-
pub const NPM_TARBALL_REVISION: u32 = 9;
32+
pub const NPM_TARBALL_REVISION: u32 = 10;
3333

3434
pub async fn generate_npm_version_manifest<'a>(
3535
db: &Database,

api/src/npm/specifiers.rs

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// Copyright 2024 the JSR authors. All rights reserved. MIT license.
22

3+
use std::borrow::Cow;
34
use std::collections::HashMap;
45

56
use deno_ast::ModuleSpecifier;
@@ -40,22 +41,41 @@ impl<'a> SpecifierRewriter<'a> {
4041
RewriteKind::Declaration => self.declaration_rewrites,
4142
};
4243

43-
let resolved_specifier = follow_specifier(specifier, rewrites)?;
44+
let mut resolved_specifier =
45+
Cow::Borrowed(follow_specifier(specifier, rewrites)?);
4446

4547
if let Some(specifier) =
4648
rewrite_npm_and_jsr_specifier(resolved_specifier.as_str())
4749
{
4850
return Some(specifier);
4951
};
5052

51-
if resolved_specifier == specifier {
53+
if matches!(kind, RewriteKind::Declaration)
54+
&& resolved_specifier.scheme() == "file"
55+
{
56+
let path = resolved_specifier.path();
57+
if path.ends_with(".d.ts") || path.ends_with(".d.mts") {
58+
// If the base specifier is a declaration file, and a dependency is also a
59+
// declaration file, TypeScript will not allow the import (TS2846). In
60+
// this case, replace the `.d.ts` extension in the resolved specifier
61+
// with `.js` so that TypeScript thinks we're importing a source file,
62+
// which is allowed. It will then probe for the `.d.ts` file, which it
63+
// will find.
64+
// We do not use extensionless imports, because TypeScript does not
65+
// allow them under `moduleResolution: "nodenext"` (TS2835).
66+
let path = rewrite_path_extension(path, Extension::Js).unwrap();
67+
resolved_specifier.to_mut().set_path(&path);
68+
}
69+
}
70+
71+
if *resolved_specifier == *specifier {
5272
// No need to rewrite if the specifier is the same as the resolved
5373
// specifier.
5474
return None;
5575
}
5676

5777
let new_specifier = if resolved_specifier.scheme() == "file" {
58-
relative_import_specifier(self.base_specifier, resolved_specifier)
78+
relative_import_specifier(self.base_specifier, &resolved_specifier)
5979
} else {
6080
resolved_specifier.to_string()
6181
};
@@ -142,14 +162,18 @@ pub enum Extension {
142162
Dts,
143163
}
144164

145-
pub fn rewrite_file_specifier_extension(
165+
pub fn rewrite_file_specifier(
146166
specifier: &ModuleSpecifier,
167+
prefix: &str,
147168
new_extension: Extension,
148169
) -> Option<ModuleSpecifier> {
149170
assert_eq!(specifier.scheme(), "file");
150171
let path = specifier.path();
151172
let rewritten_path = rewrite_path_extension(path, new_extension)?;
152-
Some(ModuleSpecifier::parse(&format!("file://{}", rewritten_path)).unwrap())
173+
Some(
174+
ModuleSpecifier::parse(&format!("file://{prefix}{rewritten_path}"))
175+
.unwrap(),
176+
)
153177
}
154178

155179
pub fn rewrite_path_extension(

0 commit comments

Comments
 (0)