diff --git a/api/src/analysis.rs b/api/src/analysis.rs index eb70b5dc..443eb84f 100644 --- a/api/src/analysis.rs +++ b/api/src/analysis.rs @@ -5,6 +5,7 @@ use std::collections::HashSet; use std::sync::Arc; use deno_ast::swc::common::comments::CommentKind; +use deno_ast::swc::common::Span; use deno_ast::LineAndColumnDisplay; use deno_ast::MediaType; use deno_ast::ModuleSpecifier; @@ -727,6 +728,54 @@ fn check_for_banned_syntax( continue; } }, + ast::ModuleDecl::Import(n) => { + if let Some(with) = &n.with { + let range = + Span::new(n.src.span.hi(), with.span.lo(), n.src.span.ctxt) + .range(); + let keyword = parsed_source.text_info().range_text(&range); + if keyword.contains("assert") { + let (line, column) = line_col(&with.span.range()); + return Err(PublishError::BannedImportAssertion { + specifier: parsed_source.specifier().to_string(), + line, + column, + }); + } + } + } + ast::ModuleDecl::ExportNamed(n) => { + if let Some(with) = &n.with { + let src = n.src.as_ref().unwrap(); + let range = + Span::new(src.span.hi(), with.span.lo(), src.span.ctxt).range(); + let keyword = parsed_source.text_info().range_text(&range); + if keyword.contains("assert") { + let (line, column) = line_col(&with.span.range()); + return Err(PublishError::BannedImportAssertion { + specifier: parsed_source.specifier().to_string(), + line, + column, + }); + } + } + } + ast::ModuleDecl::ExportAll(n) => { + if let Some(with) = &n.with { + let range = + Span::new(n.src.span.hi(), with.span.lo(), n.src.span.ctxt) + .range(); + let keyword = parsed_source.text_info().range_text(&range); + if keyword.contains("assert") { + let (line, column) = line_col(&with.span.range()); + return Err(PublishError::BannedImportAssertion { + specifier: parsed_source.specifier().to_string(), + line, + column, + }); + } + } + } _ => continue, }, ast::ModuleItem::Stmt(n) => match n { @@ -920,5 +969,29 @@ mod tests { let x = parse("import express = React.foo;"); assert!(super::check_for_banned_syntax(&x).is_ok()); + + let x = parse("import './data.json' assert { type: 'json' }"); + let err = super::check_for_banned_syntax(&x).unwrap_err(); + assert!( + matches!(err, super::PublishError::BannedImportAssertion { .. }), + "{err:?}", + ); + + let x = parse("export { a } from './data.json' assert { type: 'json' }"); + let err = super::check_for_banned_syntax(&x).unwrap_err(); + assert!( + matches!(err, super::PublishError::BannedImportAssertion { .. }), + "{err:?}", + ); + + let x = parse("export * from './data.json' assert { type: 'json' }"); + let err = super::check_for_banned_syntax(&x).unwrap_err(); + assert!( + matches!(err, super::PublishError::BannedImportAssertion { .. }), + "{err:?}", + ); + + let x = parse("export * from './data.json' with { type: 'json' }"); + assert!(super::check_for_banned_syntax(&x).is_ok(), "{err:?}",); } } diff --git a/api/src/publish.rs b/api/src/publish.rs index e88475b1..e3a03ad1 100644 --- a/api/src/publish.rs +++ b/api/src/publish.rs @@ -972,6 +972,41 @@ pub mod tests { assert_eq!(error.code, "invalidPath"); } + #[tokio::test] + async fn import_assertions() { + let t = TestSetup::new().await; + + let bytes = create_mock_tarball("import_assertions"); + let task = process_tarball_setup2( + &t, + bytes, + &PackageName::try_from("foo").unwrap(), + &Version::try_from("1.2.3").unwrap(), + false, + ) + .await; + assert_eq!(task.status, PublishingTaskStatus::Failure, "{task:#?}"); + let error = task.error.unwrap(); + assert_eq!(error.code, "bannedImportAssertion"); + assert_eq!(error.message, "import assertions are not allowed, use import attributes instead (replace 'assert' with 'with') file:///mod.ts:1:29"); + } + + #[tokio::test] + async fn import_attributes() { + let t = TestSetup::new().await; + + let bytes = create_mock_tarball("import_attributes"); + let task = process_tarball_setup2( + &t, + bytes, + &PackageName::try_from("foo").unwrap(), + &Version::try_from("1.2.3").unwrap(), + false, + ) + .await; + assert_eq!(task.status, PublishingTaskStatus::Success, "{task:#?}"); + } + #[tokio::test] async fn jsr_jsonc() { let t = TestSetup::new().await; diff --git a/api/src/tarball.rs b/api/src/tarball.rs index 49e233fd..b3f8f4ff 100644 --- a/api/src/tarball.rs +++ b/api/src/tarball.rs @@ -485,7 +485,7 @@ pub enum PublishError { #[error("invalid external import to '{specifier}', only 'jsr:', 'npm:', 'data:' and 'node:' imports are allowed ({info})")] InvalidExternalImport { specifier: String, info: String }, - #[error("Modifying global types is not allowed {specifier}:{line}:{column}")] + #[error("modifying global types is not allowed {specifier}:{line}:{column}")] GlobalTypeAugmentation { specifier: String, line: usize, @@ -499,13 +499,20 @@ pub enum PublishError { column: usize, }, - #[error("Triple slash directives that modify globals (for example, '/// ' or '/// ') are not allowed. Instead instruct the user of your package to specify these directives. {specifier}:{line}:{column}")] + #[error("triple slash directives that modify globals (for example, '/// ' or '/// ') are not allowed. Instead instruct the user of your package to specify these directives. {specifier}:{line}:{column}")] BannedTripleSlashDirectives { specifier: String, line: usize, column: usize, }, + #[error("import assertions are not allowed, use import attributes instead (replace 'assert' with 'with') {specifier}:{line}:{column}")] + BannedImportAssertion { + specifier: String, + line: usize, + column: usize, + }, + #[error( "file at path '{path}' too large, max size is {max_size}, got {size}" )] @@ -614,6 +621,9 @@ impl PublishError { PublishError::BannedTripleSlashDirectives { .. } => { Some("bannedTripleSlashDirectives") } + PublishError::BannedImportAssertion { .. } => { + Some("bannedImportAssertion") + } PublishError::InvalidExternalImport { .. } => { Some("invalidExternalImport") } diff --git a/api/testdata/tarballs/import_assertions/data.json b/api/testdata/tarballs/import_assertions/data.json new file mode 100644 index 00000000..d1cc1b4e --- /dev/null +++ b/api/testdata/tarballs/import_assertions/data.json @@ -0,0 +1 @@ +"abc" diff --git a/api/testdata/tarballs/import_assertions/jsr.json b/api/testdata/tarballs/import_assertions/jsr.json new file mode 100644 index 00000000..94c22c0e --- /dev/null +++ b/api/testdata/tarballs/import_assertions/jsr.json @@ -0,0 +1,5 @@ +{ + "name": "@scope/foo", + "version": "1.2.3", + "exports": "./mod.ts" +} diff --git a/api/testdata/tarballs/import_assertions/mod.ts b/api/testdata/tarballs/import_assertions/mod.ts new file mode 100644 index 00000000..2568964b --- /dev/null +++ b/api/testdata/tarballs/import_assertions/mod.ts @@ -0,0 +1 @@ +import "./data.json" assert { type: "json" }; diff --git a/api/testdata/tarballs/import_attributes/data.json b/api/testdata/tarballs/import_attributes/data.json new file mode 100644 index 00000000..d1cc1b4e --- /dev/null +++ b/api/testdata/tarballs/import_attributes/data.json @@ -0,0 +1 @@ +"abc" diff --git a/api/testdata/tarballs/import_attributes/jsr.json b/api/testdata/tarballs/import_attributes/jsr.json new file mode 100644 index 00000000..94c22c0e --- /dev/null +++ b/api/testdata/tarballs/import_attributes/jsr.json @@ -0,0 +1,5 @@ +{ + "name": "@scope/foo", + "version": "1.2.3", + "exports": "./mod.ts" +} diff --git a/api/testdata/tarballs/import_attributes/mod.ts b/api/testdata/tarballs/import_attributes/mod.ts new file mode 100644 index 00000000..16f4d15b --- /dev/null +++ b/api/testdata/tarballs/import_attributes/mod.ts @@ -0,0 +1 @@ +import "./data.json" with { type: "json" }; diff --git a/frontend/docs/troubleshooting.md b/frontend/docs/troubleshooting.md index 7d8651b3..77e12d4c 100644 --- a/frontend/docs/troubleshooting.md +++ b/frontend/docs/troubleshooting.md @@ -105,6 +105,17 @@ allowed by JSR. JSR only allows triple slash directives that are You can fix this error by removing the triple slash directive from your source. +### `bannedImportAssertion` + +The package being published contains the legacy "import assertions" syntax, +which is not allowed by JSR. JSR only allows the new "import attributes" syntax. + +`import "./data.json" assert { type: "json" };` is not allowed. +`import "./data.json" with { type: "json" };` is allowed. + +You can fix this error by updating the import assertion to an import attribute, +by replacing `assert` with `with`. + ### `fileTooLarge` The package being published contains a file that is too large. JSR only allows