1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-11-28 16:20:57 -05:00

fix: warn about import assertions when using typescript (#25135)

1. On emit, checks for the prescence of import assertions.
1. Warns and doesn't store the parsed source in the emit cache in this
case.
This commit is contained in:
David Sherret 2024-08-21 13:12:56 -04:00 committed by GitHub
parent e2c50f7e8a
commit 1d4169204c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 173 additions and 34 deletions

View file

@ -5,6 +5,9 @@ use crate::cache::FastInsecureHasher;
use crate::cache::ParsedSourceCache; use crate::cache::ParsedSourceCache;
use deno_ast::SourceMapOption; use deno_ast::SourceMapOption;
use deno_ast::SourceRange;
use deno_ast::SourceRanged;
use deno_ast::SourceRangedForSpanned;
use deno_ast::TranspileResult; use deno_ast::TranspileResult;
use deno_core::error::AnyError; use deno_core::error::AnyError;
use deno_core::futures::stream::FuturesUnordered; use deno_core::futures::stream::FuturesUnordered;
@ -109,26 +112,28 @@ impl Emitter {
let parsed_source_cache = self.parsed_source_cache.clone(); let parsed_source_cache = self.parsed_source_cache.clone();
let transpile_and_emit_options = let transpile_and_emit_options =
self.transpile_and_emit_options.clone(); self.transpile_and_emit_options.clone();
let transpile_result = deno_core::unsync::spawn_blocking({ let (should_cache, transpile_result) =
let specifier = specifier.clone(); deno_core::unsync::spawn_blocking({
let source = source.clone(); let specifier = specifier.clone();
move || -> Result<_, AnyError> { let source = source.clone();
EmitParsedSourceHelper::transpile( move || -> Result<_, AnyError> {
&parsed_source_cache, EmitParsedSourceHelper::transpile(
&specifier, &parsed_source_cache,
source.clone(), &specifier,
media_type, source.clone(),
&transpile_and_emit_options.0, media_type,
&transpile_and_emit_options.1, &transpile_and_emit_options.0,
) &transpile_and_emit_options.1,
} )
}) }
.await })
.unwrap()?; .await
.unwrap()?;
Ok(helper.post_emit_parsed_source( Ok(helper.post_emit_parsed_source(
specifier, specifier,
transpile_result, transpile_result,
source_hash, source_hash,
should_cache,
)) ))
} }
} }
@ -145,18 +150,20 @@ impl Emitter {
match helper.pre_emit_parsed_source(specifier, source) { match helper.pre_emit_parsed_source(specifier, source) {
PreEmitResult::Cached(emitted_text) => Ok(emitted_text), PreEmitResult::Cached(emitted_text) => Ok(emitted_text),
PreEmitResult::NotCached { source_hash } => { PreEmitResult::NotCached { source_hash } => {
let transpile_result = EmitParsedSourceHelper::transpile( let (should_cache, transpile_result) =
&self.parsed_source_cache, EmitParsedSourceHelper::transpile(
specifier, &self.parsed_source_cache,
source.clone(), specifier,
media_type, source.clone(),
&self.transpile_and_emit_options.0, media_type,
&self.transpile_and_emit_options.1, &self.transpile_and_emit_options.0,
)?; &self.transpile_and_emit_options.1,
)?;
Ok(helper.post_emit_parsed_source( Ok(helper.post_emit_parsed_source(
specifier, specifier,
transpile_result, transpile_result,
source_hash, source_hash,
should_cache,
)) ))
} }
} }
@ -254,12 +261,16 @@ impl<'a> EmitParsedSourceHelper<'a> {
media_type: MediaType, media_type: MediaType,
transpile_options: &deno_ast::TranspileOptions, transpile_options: &deno_ast::TranspileOptions,
emit_options: &deno_ast::EmitOptions, emit_options: &deno_ast::EmitOptions,
) -> Result<TranspileResult, AnyError> { ) -> Result<(bool, TranspileResult), AnyError> {
// nothing else needs the parsed source at this point, so remove from // nothing else needs the parsed source at this point, so remove from
// the cache in order to not transpile owned // the cache in order to not transpile owned
let parsed_source = parsed_source_cache let parsed_source = parsed_source_cache
.remove_or_parse_module(specifier, source, media_type)?; .remove_or_parse_module(specifier, source, media_type)?;
Ok(parsed_source.transpile(transpile_options, emit_options)?) let should_cache = !has_import_assertion(&parsed_source);
Ok((
should_cache,
parsed_source.transpile(transpile_options, emit_options)?,
))
} }
pub fn post_emit_parsed_source( pub fn post_emit_parsed_source(
@ -267,6 +278,8 @@ impl<'a> EmitParsedSourceHelper<'a> {
specifier: &ModuleSpecifier, specifier: &ModuleSpecifier,
transpile_result: TranspileResult, transpile_result: TranspileResult,
source_hash: u64, source_hash: u64,
// todo(dsherret): remove after Deno 2.0
should_cache: bool,
) -> ModuleCodeBytes { ) -> ModuleCodeBytes {
let transpiled_source = match transpile_result { let transpiled_source = match transpile_result {
TranspileResult::Owned(source) => source, TranspileResult::Owned(source) => source,
@ -276,11 +289,80 @@ impl<'a> EmitParsedSourceHelper<'a> {
} }
}; };
debug_assert!(transpiled_source.source_map.is_none()); debug_assert!(transpiled_source.source_map.is_none());
self.0.emit_cache.set_emit_code( if should_cache {
specifier, self.0.emit_cache.set_emit_code(
source_hash, specifier,
&transpiled_source.source, source_hash,
); &transpiled_source.source,
);
}
transpiled_source.source.into_boxed_slice().into() transpiled_source.source.into_boxed_slice().into()
} }
} }
fn has_import_assertion(parsed_source: &deno_ast::ParsedSource) -> bool {
fn has_import_assertion(text: &str) -> bool {
// good enough
text.contains(" assert ") && !text.contains(" with ")
}
fn warn_import_attribute(
parsed_source: &deno_ast::ParsedSource,
range: SourceRange,
) {
let text_info = parsed_source.text_info_lazy();
let loc = text_info.line_and_column_display(range.start);
deno_runtime::import_assertion_callback(
deno_core::ImportAssertionsSupportCustomCallbackArgs {
maybe_specifier: Some(parsed_source.specifier().to_string()),
maybe_line_number: Some(loc.line_number),
column_number: loc.column_number,
maybe_source_line: Some(range.text_fast(text_info).to_string()),
},
)
}
let Some(module) = parsed_source.program_ref().as_module() else {
return false;
};
let mut had_import_assertion = false;
for item in &module.body {
match item {
deno_ast::swc::ast::ModuleItem::ModuleDecl(decl) => match decl {
deno_ast::swc::ast::ModuleDecl::Import(n) => {
if n.with.is_some()
&& has_import_assertion(n.text_fast(parsed_source.text_info_lazy()))
{
had_import_assertion = true;
warn_import_attribute(parsed_source, n.range());
}
}
deno_ast::swc::ast::ModuleDecl::ExportAll(n) => {
if n.with.is_some()
&& has_import_assertion(n.text_fast(parsed_source.text_info_lazy()))
{
had_import_assertion = true;
warn_import_attribute(parsed_source, n.range());
}
}
deno_ast::swc::ast::ModuleDecl::ExportNamed(n) => {
if n.with.is_some()
&& has_import_assertion(n.text_fast(parsed_source.text_info_lazy()))
{
had_import_assertion = true;
warn_import_attribute(parsed_source, n.range());
}
}
deno_ast::swc::ast::ModuleDecl::ExportDecl(_)
| deno_ast::swc::ast::ModuleDecl::ExportDefaultDecl(_)
| deno_ast::swc::ast::ModuleDecl::ExportDefaultExpr(_)
| deno_ast::swc::ast::ModuleDecl::TsImportEquals(_)
| deno_ast::swc::ast::ModuleDecl::TsExportAssignment(_)
| deno_ast::swc::ast::ModuleDecl::TsNamespaceExport(_) => {}
},
deno_ast::swc::ast::ModuleItem::Stmt(_) => {}
}
}
had_import_assertion
}

View file

@ -44,6 +44,7 @@ pub use worker_bootstrap::WorkerExecutionMode;
pub use worker_bootstrap::WorkerLogLevel; pub use worker_bootstrap::WorkerLogLevel;
mod shared; mod shared;
pub use shared::import_assertion_callback;
pub use shared::runtime; pub use shared::runtime;
// NOTE(bartlomieju): keep IDs in sync with `runtime/90_deno_ns.js` (search for `unstableFeatures`) // NOTE(bartlomieju): keep IDs in sync with `runtime/90_deno_ns.js` (search for `unstableFeatures`)

View file

@ -0,0 +1,18 @@
{
"tests": {
"assertion": {
"steps": [{
"args": "run assertion.ts",
"output": "assertion.out"
}, {
// should output the same because the emit won't be cached
"args": "run assertion.ts",
"output": "assertion.out"
}]
},
"with": {
"args": "run with.ts",
"output": "with.out"
}
}
}

View file

@ -0,0 +1,14 @@
⚠️ Import assertions are deprecated. Use `with` keyword, instead of 'assert' keyword.
import test from "./data.json" assert { type: "json" };
at file:///[WILDLINE]/assertion.ts:1:1
{ prop: "data" }
⚠️ Import assertions are deprecated. Use `with` keyword, instead of 'assert' keyword.
console.log((await import("./data.json", {
at file:///[WILDLINE]/assertion.ts:5:13
{ prop: "data" }

View file

@ -0,0 +1,5 @@
import test from "./data.json" assert { type: "json" };
console.log(test);
console.log(
(await import("./data.json", { assert: { type: "json" } })).default,
);

View file

@ -0,0 +1,3 @@
{
"prop": "data"
}

View file

@ -0,0 +1,2 @@
{ prop: "data" }
{ prop: "data" }

View file

@ -0,0 +1,3 @@
import test from "./data.json" with { type: "json" };
console.log(test);
console.log((await import("./data.json", { with: { type: "json" } })).default);

View file

@ -1,4 +1,3 @@
// deno-lint-ignore no-import-assertions import json from "./json_with_shebang.json" with { type: "json" };
import json from "./json_with_shebang.json" assert { type: "json" };
console.log(json); console.log(json);

View file

@ -1 +1,7 @@
⚠️ Import assertions are deprecated. Use `with` keyword, instead of 'assert' keyword.
export { default } from "./data.json" assert { type: "json" };
at file:///[WILDLINE]
{ a: "b", c: { d: 10 } } { a: "b", c: { d: 10 } }

View file

@ -1,2 +1,8 @@
⚠️ Import assertions are deprecated. Use `with` keyword, instead of 'assert' keyword.
import data2 from "./data.json" assert { type: "json" };
at file:///[WILDLINE]
{ a: "b", c: { d: 10 } } { a: "b", c: { d: 10 } }
{ a: "b", c: { d: 10 } } { a: "b", c: { d: 10 } }

View file

@ -1,2 +1,2 @@
import config from "../jsx/deno-jsx.json" assert { type: "json" }; import config from "../jsx/deno-jsx.json" with { type: "json" };
console.log(config); console.log(config);