Skip to content

Commit

Permalink
fix: upgrade swc fixing many bundling and --no-check bugs (denoland…
Browse files Browse the repository at this point in the history
  • Loading branch information
dsherret committed Dec 9, 2021
1 parent 318f48f commit 1507b8c
Show file tree
Hide file tree
Showing 10 changed files with 352 additions and 193 deletions.
172 changes: 100 additions & 72 deletions Cargo.lock

Large diffs are not rendered by default.

14 changes: 7 additions & 7 deletions cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,26 +39,26 @@ winapi = "=0.3.9"
winres = "=0.1.11"

[dependencies]
deno_ast = { version = "0.5.0", features = ["bundler", "codegen", "dep_graph", "module_specifier", "proposal", "react", "sourcemap", "transforms", "typescript", "view", "visit"] }
deno_ast = { version = "0.7.0", features = ["bundler", "codegen", "dep_graph", "module_specifier", "proposal", "react", "sourcemap", "transforms", "typescript", "view", "visit"] }
deno_core = { version = "0.110.0", path = "../core" }
deno_doc = "0.21.0"
deno_graph = "0.12.0"
deno_lint = { version = "0.19.0", features = ["docs"] }
deno_doc = "0.22.0"
deno_graph = "0.13.0"
deno_lint = { version = "0.20.0", features = ["docs"] }
deno_runtime = { version = "0.36.0", path = "../runtime" }

atty = "=0.2.14"
base64 = "=0.13.0"
clap = "=2.33.3"
data-url = "=0.1.0"
data-url = "=0.1.1"
dissimilar = "=1.0.2"
dprint-plugin-json = "=0.13.2"
dprint-plugin-markdown = "=0.11.3"
dprint-plugin-typescript = "=0.59.2"
dprint-plugin-typescript = "=0.60.0"
encoding_rs = "=0.8.29"
env_logger = "=0.8.4"
fancy-regex = "=0.7.1"
http = "=0.2.4"
import_map = "=0.3.3"
import_map = "=0.4.0"
jsonc-parser = { version = "=0.17.0", features = ["serde"] }
lazy_static = "=1.4.0"
libc = "=0.2.106"
Expand Down
235 changes: 176 additions & 59 deletions cli/ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ use deno_ast::swc::common::Globals;
use deno_ast::swc::common::Mark;
use deno_ast::swc::common::SourceMap;
use deno_ast::swc::common::Spanned;
use deno_ast::swc::parser::error::Error as SwcError;
use deno_ast::swc::parser::error::SyntaxError;
use deno_ast::swc::parser::lexer::Lexer;
use deno_ast::swc::parser::StringInput;
use deno_ast::swc::transforms::fixer;
Expand All @@ -38,6 +40,7 @@ use deno_core::resolve_url_or_path;
use deno_core::serde_json;
use deno_core::ModuleSpecifier;
use std::cell::RefCell;
use std::fmt;
use std::rc::Rc;

mod bundle_hook;
Expand Down Expand Up @@ -103,6 +106,25 @@ impl std::fmt::Display for Location {
}
}

#[derive(Debug)]
pub struct Diagnostics(pub Vec<Diagnostic>);

impl std::error::Error for Diagnostics {}

impl fmt::Display for Diagnostics {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for (i, diagnostic) in self.0.iter().enumerate() {
if i > 0 {
write!(f, "\n\n")?;
}

write!(f, "{}", diagnostic)?
}

Ok(())
}
}

#[derive(Debug, Clone)]
pub enum ImportsNotUsedAsValues {
Remove,
Expand Down Expand Up @@ -259,6 +281,7 @@ pub fn transpile(
parsed_source: &ParsedSource,
options: &EmitOptions,
) -> Result<(String, Option<String>), AnyError> {
ensure_no_fatal_diagnostics(parsed_source.diagnostics().iter())?;
let program: Program = (*parsed_source.program()).clone();
let source_map = Rc::new(SourceMap::default());
let source_map_config = SourceMapConfig {
Expand Down Expand Up @@ -333,20 +356,18 @@ pub fn transpile_module(
let input = StringInput::from(&*source_file);
let comments = SingleThreadedComments::default();
let syntax = get_syntax(media_type);
let lexer = Lexer::new(syntax, deno_ast::TARGET, input, Some(&comments));
let lexer = Lexer::new(syntax, deno_ast::ES_VERSION, input, Some(&comments));
let mut parser = deno_ast::swc::parser::Parser::new_from(lexer);
let module = parser.parse_module().map_err(|err| {
let location = cm.lookup_char_pos(err.span().lo);
Diagnostic {
specifier: specifier.to_string(),
span: err.span(),
display_position: LineAndColumnDisplay {
line_number: location.line,
column_number: location.col_display + 1,
},
kind: err.into_kind(),
}
})?;
let module = parser
.parse_module()
.map_err(|e| swc_err_to_diagnostic(&cm, specifier, e))?;
let diagnostics = parser
.take_errors()
.into_iter()
.map(|e| swc_err_to_diagnostic(&cm, specifier, e))
.collect::<Vec<_>>();

ensure_no_fatal_diagnostics(diagnostics.iter())?;

let top_level_mark = Mark::fresh(Mark::root());
let program = fold_program(
Expand Down Expand Up @@ -396,28 +417,25 @@ fn fold_program(
comments: &SingleThreadedComments,
top_level_mark: Mark,
) -> Result<Program, AnyError> {
let jsx_pass = chain!(
resolver_with_mark(top_level_mark),
react::react(
source_map.clone(),
Some(comments),
react::Options {
pragma: options.jsx_factory.clone(),
pragma_frag: options.jsx_fragment_factory.clone(),
// this will use `Object.assign()` instead of the `_extends` helper
// when spreading props.
use_builtins: true,
runtime: if options.jsx_automatic {
Some(react::Runtime::Automatic)
} else {
None
},
development: options.jsx_development,
import_source: options.jsx_import_source.clone().unwrap_or_default(),
..Default::default()
let jsx_pass = react::react(
source_map.clone(),
Some(comments),
react::Options {
pragma: options.jsx_factory.clone(),
pragma_frag: options.jsx_fragment_factory.clone(),
// this will use `Object.assign()` instead of the `_extends` helper
// when spreading props.
use_builtins: true,
runtime: if options.jsx_automatic {
Some(react::Runtime::Automatic)
} else {
None
},
top_level_mark,
),
development: options.jsx_development,
import_source: options.jsx_import_source.clone().unwrap_or_default(),
..Default::default()
},
top_level_mark,
);
let mut passes = chain!(
Optional::new(transforms::DownlevelImportsFolder, options.repl_imports),
Expand All @@ -427,10 +445,12 @@ fn fold_program(
emit_metadata: options.emit_metadata
}),
helpers::inject_helpers(),
resolver_with_mark(top_level_mark),
Optional::new(
typescript::strip::strip_with_config(strip_config_from_emit_options(
options
)),
typescript::strip::strip_with_config(
strip_config_from_emit_options(options),
top_level_mark
),
!options.transform_jsx
),
Optional::new(
Expand All @@ -457,18 +477,32 @@ fn fold_program(
});

let diagnostics = diagnostics_cell.borrow();
if let Some(diagnostic) = diagnostics.iter().find(|d| is_fatal_diagnostic(d))
{
ensure_no_fatal_swc_diagnostics(&source_map, diagnostics.iter())?;
Ok(result)
}

fn ensure_no_fatal_swc_diagnostics<'a>(
source_map: &SourceMap,
diagnostics: impl Iterator<Item = &'a SwcDiagnostic>,
) -> Result<(), AnyError> {
let fatal_diagnostics = diagnostics
.filter(|d| is_fatal_swc_diagnostic(d))
.collect::<Vec<_>>();
if !fatal_diagnostics.is_empty() {
Err(anyhow!(
"{}",
format_swc_diagnostic(&source_map, diagnostic)
fatal_diagnostics
.iter()
.map(|d| format_swc_diagnostic(source_map, d))
.collect::<Vec<_>>()
.join("\n\n")
))
} else {
Ok(result)
Ok(())
}
}

fn is_fatal_diagnostic(diagnostic: &SwcDiagnostic) -> bool {
fn is_fatal_swc_diagnostic(diagnostic: &SwcDiagnostic) -> bool {
use deno_ast::swc::common::errors::Level;
match diagnostic.level {
Level::Bug
Expand Down Expand Up @@ -500,46 +534,129 @@ fn format_swc_diagnostic(
}
}

fn swc_err_to_diagnostic(
source_map: &SourceMap,
specifier: &ModuleSpecifier,
err: SwcError,
) -> Diagnostic {
let location = source_map.lookup_char_pos(err.span().lo);
Diagnostic {
specifier: specifier.to_string(),
span: err.span(),
display_position: LineAndColumnDisplay {
line_number: location.line,
column_number: location.col_display + 1,
},
kind: err.into_kind(),
}
}

fn ensure_no_fatal_diagnostics<'a>(
diagnostics: impl Iterator<Item = &'a Diagnostic>,
) -> Result<(), Diagnostics> {
let fatal_diagnostics = diagnostics
.filter(|d| is_fatal_syntax_error(&d.kind))
.map(ToOwned::to_owned)
.collect::<Vec<_>>();
if !fatal_diagnostics.is_empty() {
Err(Diagnostics(fatal_diagnostics))
} else {
Ok(())
}
}

fn is_fatal_syntax_error(error_kind: &SyntaxError) -> bool {
matches!(
error_kind,
// expected identifier
SyntaxError::TS1003 |
// expected semi-colon
SyntaxError::TS1005 |
// expected expression
SyntaxError::TS1109 |
// unterminated string literal
SyntaxError::UnterminatedStrLit
)
}

#[cfg(test)]
mod tests {
use super::*;
use deno_ast::parse_module;
use deno_ast::ParseParams;
use deno_ast::SourceTextInfo;

use pretty_assertions::assert_eq;

#[test]
fn test_transpile() {
let specifier = resolve_url_or_path("https://deno.land/x/mod.ts")
.expect("could not resolve specifier");
let source = r#"
enum D {
A,
B,
C,
}
enum D {
A,
B,
}
export class A {
private b: string;
protected c: number = 1;
e: "foo";
constructor (public d = D.A) {
const e = "foo" as const;
this.e = e;
}
}
namespace N {
export enum D {
A = "value"
}
export const Value = 5;
}
export class A {
private b: string;
protected c: number = 1;
e: "foo";
constructor (public d = D.A) {
const e = "foo" as const;
this.e = e;
console.log(N.Value);
}
}
"#;
let module = parse_module(ParseParams {
let module = deno_ast::parse_module(ParseParams {
specifier: specifier.as_str().to_string(),
source: SourceTextInfo::from_string(source.to_string()),
media_type: deno_ast::MediaType::TypeScript,
capture_tokens: false,
maybe_syntax: None,
scope_analysis: false,
})
.expect("could not parse module");
.unwrap();
let (code, maybe_map) = transpile(&module, &EmitOptions::default())
.expect("could not strip types");
assert!(code.starts_with("var D;\n(function(D) {\n"));
let expected_text = r#"var D;
(function(D) {
D[D["A"] = 0] = "A";
D[D["B"] = 1] = "B";
})(D || (D = {
}));
var N;
(function(N1) {
let D;
(function(D) {
D["A"] = "value";
})(D = N1.D || (N1.D = {
}));
N1.Value = 5;
})(N || (N = {
}));
export class A {
d;
b;
c = 1;
e;
constructor(d = D.A){
this.d = d;
const e = "foo";
this.e = e;
console.log(N.Value);
}
}
"#;
assert_eq!(&code[..expected_text.len()], expected_text);
assert!(
code.contains("\n//# sourceMappingURL=data:application/json;base64,")
);
Expand Down
3 changes: 2 additions & 1 deletion cli/http_util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,6 @@ mod tests {
use super::*;
use crate::version;
use deno_runtime::deno_fetch::create_http_client;
use deno_runtime::deno_tls::rustls::RootCertStore;
use std::fs::read;

fn create_test_client() -> Client {
Expand Down Expand Up @@ -409,6 +408,8 @@ mod tests {
#[cfg(not(windows))]
#[tokio::test]
async fn test_fetch_with_empty_certificate_store() {
use deno_runtime::deno_tls::rustls::RootCertStore;

let _http_server_guard = test_util::http_server();
// Relies on external http server with a valid mozilla root CA cert.
let url = Url::parse("https://deno.land").unwrap();
Expand Down
Loading

0 comments on commit 1507b8c

Please sign in to comment.