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:
parent
e2c50f7e8a
commit
1d4169204c
12 changed files with 173 additions and 34 deletions
144
cli/emit.rs
144
cli/emit.rs
|
@ -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
|
||||||
|
}
|
||||||
|
|
|
@ -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`)
|
||||||
|
|
18
tests/specs/run/ts_import_assertions/__test__.jsonc
Normal file
18
tests/specs/run/ts_import_assertions/__test__.jsonc
Normal 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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
14
tests/specs/run/ts_import_assertions/assertion.out
Normal file
14
tests/specs/run/ts_import_assertions/assertion.out
Normal 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" }
|
5
tests/specs/run/ts_import_assertions/assertion.ts
Normal file
5
tests/specs/run/ts_import_assertions/assertion.ts
Normal 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,
|
||||||
|
);
|
3
tests/specs/run/ts_import_assertions/data.json
Normal file
3
tests/specs/run/ts_import_assertions/data.json
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"prop": "data"
|
||||||
|
}
|
2
tests/specs/run/ts_import_assertions/with.out
Normal file
2
tests/specs/run/ts_import_assertions/with.out
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
{ prop: "data" }
|
||||||
|
{ prop: "data" }
|
3
tests/specs/run/ts_import_assertions/with.ts
Normal file
3
tests/specs/run/ts_import_assertions/with.ts
Normal 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);
|
|
@ -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);
|
||||||
|
|
|
@ -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 } }
|
||||||
|
|
|
@ -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 } }
|
||||||
|
|
2
tests/testdata/run/config_json_import.ts
vendored
2
tests/testdata/run/config_json_import.ts
vendored
|
@ -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);
|
||||||
|
|
Loading…
Reference in a new issue