From b66ee58ee3a5c6a61d9e5bfa566eb66f9c0725fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B0=95=EB=8F=99=EC=9C=A4?= Date: Sun, 27 Dec 2020 19:02:41 +0900 Subject: [PATCH] fix(bundler): Fix statement ordering issue (#1264) swc_bundler: - Reduce binary size by reducing usage of visitor / folders. - Handle `export *` and `export { default }` from same source. (denoland/deno#8530, denoland/deno#8679) - Fix ordering of statements. (denoland/deno#8545) - Sort statements in wrapped modules. (https://github.com/denoland/deno/issues/8211#issuecomment-741070299) - Exclude default export while handling `export *`. - Exclude `export { default }` and `export { foo as default }` while handling `export *`. - Make statements from same module to be injected together. (denoland/deno#8620) swc_ecma_transforms: - fixer: Handle assignments in the callee of `new` correctly. - fixer: Handle seqence expression in the callee of `new` correctly. --- bundler/Cargo.toml | 1 + bundler/scripts/test.sh | 3 + bundler/src/bundler/chunk/circular.rs | 64 +- bundler/src/bundler/chunk/circular/tests.rs | 110 --- bundler/src/bundler/chunk/cjs.rs | 52 +- bundler/src/bundler/chunk/computed_key.rs | 29 +- bundler/src/bundler/chunk/export.rs | 485 +++++----- bundler/src/bundler/chunk/merge.rs | 435 +++++---- bundler/src/bundler/chunk/mod.rs | 3 +- bundler/src/bundler/chunk/sort.rs | 381 -------- bundler/src/bundler/mod.rs | 7 + bundler/src/bundler/modules/mod.rs | 242 +++++ bundler/src/bundler/modules/sort.rs | 724 +++++++++++++++ bundler/src/bundler/modules/sort/graph.rs | 73 ++ bundler/src/bundler/modules/tests.rs | 111 +++ bundler/src/util.rs | 4 +- .../deno-8211/case2}/entry.ts | 3 + .../tests/deno-exec/deno-8211/case3/deps.ts | 1 + .../tests/deno-exec/deno-8211/case3/input.ts | 5 + .../input => deno-exec/deno-8545}/entry.ts | 5 +- .../tests/deno-exec/deno-8597/.case1/entry.ts | 1 + .../tests/deno-exec/deno-8597/.case4/entry.ts | 1 + .../tests/deno-exec/deno-8597/.case4/tfjs.ts | 6 + .../tests/deno-exec/deno-8597/.case5/entry.ts | 1 + .../tests/deno-exec/deno-8597/.case5/tfjs.ts | 4 + .../tests/deno-exec/deno-8597/case2/core.ts | 8 + .../tests/deno-exec/deno-8597/case2/entry.ts | 1 + .../tests/deno-exec/deno-8597/case2/layers.ts | 7 + .../tests/deno-exec/deno-8597/case2/tfjs.ts | 2 + .../tests/deno-exec/deno-8597/case3/entry.ts | 1 + .../tests/deno-exec/deno-8597/case3/tfjs.ts | 5 + bundler/tests/deno.rs | 75 +- .../tests/fixture/.deno-8211-3/input/deps.ts | 1 + .../tests/fixture/.deno-8211-3/input/entry.ts | 5 + .../complex-export/output/entry.js | 4 +- .../complex-import/output/entry.js | 4 +- .../circular-imports/simple/output/entry.js | 4 +- .../fixture/deno-8148/test-4/output/entry.ts | 2 +- .../tests/fixture/deno-8188-1/output/entry.ts | 10 +- .../tests/fixture/deno-8188-2/output/entry.ts | 34 +- .../tests/fixture/deno-8188-3/output/entry.ts | 8 +- .../tests/fixture/deno-8211-1/output/entry.ts | 2 +- .../tests/fixture/deno-8211-2/output/entry.ts | 4 +- .../tests/fixture/deno-8302-1/output/entry.js | 22 +- .../deno-8545/simplified-1/input/deps.ts | 2 + .../deno-8545/simplified-1/input/entry.ts | 18 + .../deno-8545/simplified-1/output/entry.ts | 20 + .../tests/fixture/deno-8574/output/entry.ts | 654 +++++++------- .../tests/fixture/deno-8584/output/entry.ts | 2 +- .../tests/fixture/deno-8625/output/entry.ts | 2 +- .../tests/fixture/deno-8679/input/entry.ts | 3 + bundler/tests/fixture/deno-8679/input/one.ts | 2 + bundler/tests/fixture/deno-8679/input/two.ts | 2 + .../tests/fixture/deno-8679/output/entry.ts | 3 + .../fixture/issue-1150-2/output/entry.ts | 6 +- .../tests/fixture/issue-1150/output/entry.ts | 6 +- .../wrapped/export-named/output/entry.ts | 2 +- ecmascript/transforms/src/fixer.rs | 11 +- .../export-named-same-name/output/entry.js | 4 +- .../pass/alias/import/multi/output/entry.js | 14 +- .../pass/alias/import/simple/output/entry.js | 8 +- .../tests/pass/basic/extends/output/entry.js | 2 +- .../complex-class-function/output/entry.js | 10 +- .../hygiene/class-inheritance/output/entry.js | 10 +- .../tests/pass/circular/mixed/output/entry.js | 8 +- .../pass/circular/simple/output/entry.js | 8 +- .../circular/top-level-idents/output/entry.js | 4 +- .../output/entry.js | 8 +- .../output/entry.js | 30 +- .../tests/pass/deno-001/full/output/entry.js | 852 +++++++++--------- .../pass/deno-001/simple-1/output/entry.js | 364 ++++---- .../pass/deno-001/simple-2/output/entry.js | 32 +- .../pass/deno-001/simple-3/output/entry.js | 2 +- .../pass/deno-001/simple-4/output/entry.js | 8 +- .../pass/deno-001/simple-5/output/entry.js | 6 +- .../side-effect/import-multi/output/entry.js | 4 +- spack/tests/pass/export/all-2/output/entry.js | 6 +- .../pass/export/complex-1/output/entry.js | 4 +- .../simple-1/output/entry.js | 2 +- .../import/commons-default/output/entry.js | 14 +- .../pass/import/commons-named/output/entry.js | 14 +- .../name-conflict/simple/output/entry.js | 2 +- .../name-conflict/in-entry/output/entry.js | 8 +- .../pass/issue-1138/example-1/output/entry.js | 4 +- .../pass/issue-1138/example-2/output/entry.js | 4 +- .../pass/issue-1138/example-3/output/entry.js | 4 +- .../pass/issue-1139/example-1/output/entry.js | 8 +- .../pass/issue-1139/example-2/output/entry.js | 6 +- spack/tests/pass/merge/basic/output/entry.js | 2 +- .../name-conflict/simple/output/entry.js | 8 +- .../library/simple/output/entry.js | 6 +- .../pass/pr-1105/example-2/output/entry.js | 8 +- .../pass/pr-1105/example-7/output/entry.js | 2 +- .../reexport/default-1-simple/output/entry.js | 2 +- .../reexport/named-1-alias/output/entry.js | 2 +- .../reexport/named-1-orig/output/entry.js | 2 +- .../reexport/named-2-nested/output/entry.js | 2 +- .../pass/reexport/named-3-var/output/entry.js | 2 +- .../pass/reexport/named-4-fn/output/entry.js | 2 +- .../reexport/named-5-class/output/entry.js | 2 +- .../reexport/recursive-step1/output/entry.js | 2 +- .../reexport/recursive-step2/output/entry.js | 2 +- .../pass/reexport/recursive/output/entry.js | 2 +- .../import/simple-1/output/entry.js | 4 +- .../import/simple-2/output/entry.js | 30 +- 105 files changed, 3078 insertions(+), 2135 deletions(-) delete mode 100644 bundler/src/bundler/chunk/circular/tests.rs delete mode 100644 bundler/src/bundler/chunk/sort.rs create mode 100644 bundler/src/bundler/modules/mod.rs create mode 100644 bundler/src/bundler/modules/sort.rs create mode 100644 bundler/src/bundler/modules/sort/graph.rs create mode 100644 bundler/src/bundler/modules/tests.rs rename bundler/tests/{deno/deno-8211-1/input => deno-exec/deno-8211/case2}/entry.ts (63%) create mode 100644 bundler/tests/deno-exec/deno-8211/case3/deps.ts create mode 100644 bundler/tests/deno-exec/deno-8211/case3/input.ts rename bundler/tests/{deno/deno-8545/input => deno-exec/deno-8545}/entry.ts (84%) create mode 100644 bundler/tests/deno-exec/deno-8597/.case1/entry.ts create mode 100644 bundler/tests/deno-exec/deno-8597/.case4/entry.ts create mode 100644 bundler/tests/deno-exec/deno-8597/.case4/tfjs.ts create mode 100644 bundler/tests/deno-exec/deno-8597/.case5/entry.ts create mode 100644 bundler/tests/deno-exec/deno-8597/.case5/tfjs.ts create mode 100644 bundler/tests/deno-exec/deno-8597/case2/core.ts create mode 100644 bundler/tests/deno-exec/deno-8597/case2/entry.ts create mode 100644 bundler/tests/deno-exec/deno-8597/case2/layers.ts create mode 100644 bundler/tests/deno-exec/deno-8597/case2/tfjs.ts create mode 100644 bundler/tests/deno-exec/deno-8597/case3/entry.ts create mode 100644 bundler/tests/deno-exec/deno-8597/case3/tfjs.ts create mode 100644 bundler/tests/fixture/.deno-8211-3/input/deps.ts create mode 100644 bundler/tests/fixture/.deno-8211-3/input/entry.ts create mode 100644 bundler/tests/fixture/deno-8545/simplified-1/input/deps.ts create mode 100644 bundler/tests/fixture/deno-8545/simplified-1/input/entry.ts create mode 100644 bundler/tests/fixture/deno-8545/simplified-1/output/entry.ts create mode 100644 bundler/tests/fixture/deno-8679/input/entry.ts create mode 100644 bundler/tests/fixture/deno-8679/input/one.ts create mode 100644 bundler/tests/fixture/deno-8679/input/two.ts create mode 100644 bundler/tests/fixture/deno-8679/output/entry.ts diff --git a/bundler/Cargo.toml b/bundler/Cargo.toml index 96a6c2d9362a..3a6924002f77 100644 --- a/bundler/Cargo.toml +++ b/bundler/Cargo.toml @@ -39,6 +39,7 @@ swc_ecma_visit = {version = "0.22.0", path = "../ecmascript/visit"} [dev-dependencies] hex = "0.4" +ntest = "0.7.2" reqwest = {version = "0.10.8", features = ["blocking"]} sha-1 = "0.9" swc_ecma_transforms = {version = "0.31.0", path = "../ecmascript/transforms", features = ["react"]} diff --git a/bundler/scripts/test.sh b/bundler/scripts/test.sh index 6188bbce0984..e3213dfeb2a3 100755 --- a/bundler/scripts/test.sh +++ b/bundler/scripts/test.sh @@ -1,6 +1,9 @@ #!/usr/bin/env bash set -eux +cargo test --no-run + +cargo test --lib cargo test --test fixture (cd ../spack && cargo test --test fixture) cargo test --test deno $@ -- --nocapture \ No newline at end of file diff --git a/bundler/src/bundler/chunk/circular.rs b/bundler/src/bundler/chunk/circular.rs index ad9720d2955c..a42424c46976 100644 --- a/bundler/src/bundler/chunk/circular.rs +++ b/bundler/src/bundler/chunk/circular.rs @@ -1,17 +1,11 @@ use super::plan::CircularPlan; -use crate::{ - bundler::chunk::{merge::Ctx, sort::sort}, - id::Id, - Bundler, Load, ModuleId, Resolve, -}; +use crate::bundler::modules::Modules; +use crate::{bundler::chunk::merge::Ctx, id::Id, Bundler, Load, ModuleId, Resolve}; use anyhow::{Context, Error}; use swc_common::DUMMY_SP; use swc_ecma_ast::*; use swc_ecma_utils::find_ids; -#[cfg(test)] -mod tests; - /// Circular imports are hard to handle. /// /// We use some dedicated method to handle circular dependencies. @@ -25,7 +19,7 @@ where ctx: &Ctx, plan: &CircularPlan, entry_id: ModuleId, - ) -> Result { + ) -> Result { assert!( plan.chunks.len() >= 1, "# of circular modules should be 2 or greater than 2 including entry. Got {:?}", @@ -34,11 +28,7 @@ where if !ctx.merged.insert(entry_id) { log::debug!("[circular] skip: {:?}", entry_id); - return Ok(Module { - span: DUMMY_SP, - body: Default::default(), - shebang: Default::default(), - }); + return Ok(Modules::empty(self.injected_ctxt)); } log::debug!("[circular] Stsrting with: {:?}", entry_id); @@ -50,7 +40,7 @@ where .context("failed to merge dependency of a cyclic module")?; let mut exports = vec![]; - for item in entry.body.iter_mut() { + for item in entry.iter_mut() { match item { ModuleItem::ModuleDecl(decl) => match decl { ModuleDecl::ExportDecl(export) => match &export.decl { @@ -125,16 +115,14 @@ where entry = new_module; if !exports.is_empty() { - entry - .body - .push(ModuleItem::ModuleDecl(ModuleDecl::ExportNamed( - NamedExport { - span: DUMMY_SP.with_ctxt(self.synthesized_ctxt), - specifiers: exports, - src: None, - type_only: false, - }, - ))); + entry.inject(ModuleItem::ModuleDecl(ModuleDecl::ExportNamed( + NamedExport { + span: DUMMY_SP.with_ctxt(self.synthesized_ctxt), + specifiers: exports, + src: None, + type_only: false, + }, + ))); } // print_hygiene("[circular] done", &self.cm, &entry); @@ -146,10 +134,10 @@ where pub(super) fn merge_circular_modules( &self, ctx: &Ctx, - mut entry: Module, + mut entry: Modules, entry_id: ModuleId, mut deps: Vec, - ) -> Result { + ) -> Result { deps.retain(|&dep| { if dep == entry_id { return false; @@ -167,40 +155,26 @@ where deps.sort(); self.run(|| { - let mut dep_body = vec![]; - for dep in deps { let dep_info = self.scope.get_module(dep).unwrap(); let mut dep = self .merge_modules(ctx, dep, false, false) .context("failed to merge dependency of a cyclic module")?; - // print_hygiene("[circular] dep:init 1", &self.cm, &dep); + // print_hygiene("[circular] dep:init 1", &self.cm, &dep.clone().into()); self.handle_import_deps(ctx, &dep_info, &mut dep, true); // print_hygiene("[circular] dep:init 2", &self.cm, &dep); - dep_body.extend(dep.body); + entry.prepend_all(dep); } - // dep = dep.fold_with(&mut Unexporter); + // print_hygiene("before circular sort", &self.cm, &entry.clone().into()); - // Merge code - entry.body = merge_respecting_order(dep_body, entry.body); + // entry.sort(); Ok(entry) }) } } - -fn merge_respecting_order(dep: Vec, entry: Vec) -> Vec { - let mut new = Vec::with_capacity(dep.len() + entry.len()); - - new.extend(entry); - new.extend(dep); - - sort(&mut new); - - new -} diff --git a/bundler/src/bundler/chunk/circular/tests.rs b/bundler/src/bundler/chunk/circular/tests.rs deleted file mode 100644 index 79fdd5d80bf6..000000000000 --- a/bundler/src/bundler/chunk/circular/tests.rs +++ /dev/null @@ -1,110 +0,0 @@ -use super::*; -use crate::debug::print_hygiene; -use swc_common::{sync::Lrc, FileName, SourceMap}; -use swc_ecma_parser::{lexer::Lexer, JscTarget, Parser, StringInput, Syntax}; -use swc_ecma_utils::drop_span; - -fn parse(cm: Lrc, name: &str, src: &str) -> Module { - let fm = cm.new_source_file(FileName::Custom(name.into()), src.into()); - let lexer = Lexer::new( - Syntax::default(), - JscTarget::Es2020, - StringInput::from(&*fm), - None, - ); - let mut parser = Parser::new_from(lexer); - - let module = parser.parse_module().unwrap(); - - drop_span(module) -} - -#[track_caller] -fn assert_merge_respecting_order(modules: &[&str], output: &str) { - ::testing::run_test2(false, |cm, _handler| { - let mut entry = parse(cm.clone(), &format!("entry"), modules[0]); - - for i in 1..modules.len() { - let dep = parse(cm.clone(), &format!("deps-{}", i), modules[i]); - entry.body = merge_respecting_order(entry.body, dep.body); - - print_hygiene("merge", &cm, &entry); - } - - let output = parse(cm.clone(), "output", output); - if entry.body != output.body { - panic!() - } - - Ok(()) - }) - .unwrap() -} - -#[test] -fn simple_two() { - assert_merge_respecting_order( - &["export class A {}", "export class B extends A {}"], - " - export class A {} - export class B extends A {} - ", - ); -} - -#[test] -fn many_vars_1() { - assert_merge_respecting_order( - &[ - " - const A6 = A5; - class B6 extends A6 { - } - const B7 = B6; - - ", - " - const B4 = B7; - class A4 { - method() { - return new B4(); - } - } - const A5 = A4; - ", - ], - " - class A4 { - method() { - return new B4(); - } - } - const A5 = A4; - const A6 = A5; - class B6 extends A6 { - } - const B7 = B6; - const B4 = B7; - ", - ); -} - -#[test] -fn no_dep_first_01() { - assert_merge_respecting_order( - &[ - " - const b1 = b2; - ", - " - const b2 = 2; - const b3 = b1; - ", - ], - " - const b2 = 2; - const b1 = b2; - const b3 = b1; -", - ); -} diff --git a/bundler/src/bundler/chunk/cjs.rs b/bundler/src/bundler/chunk/cjs.rs index 83aed69e0e23..9c33e5114321 100644 --- a/bundler/src/bundler/chunk/cjs.rs +++ b/bundler/src/bundler/chunk/cjs.rs @@ -1,4 +1,5 @@ use super::merge::Unexporter; +use crate::bundler::modules::Modules; use crate::{ bundler::{ chunk::{merge::Ctx, plan::Dependancy}, @@ -7,12 +8,12 @@ use crate::{ Bundler, Load, Resolve, }; use anyhow::Error; -use std::{borrow::Cow, sync::atomic::Ordering}; +use std::sync::atomic::Ordering; use swc_atoms::js_word; use swc_common::{SyntaxContext, DUMMY_SP}; use swc_ecma_ast::{ModuleItem, *}; -use swc_ecma_utils::{prepend, quote_ident, undefined, ExprFactory}; -use swc_ecma_visit::{noop_visit_mut_type, FoldWith, VisitMut, VisitMutWith}; +use swc_ecma_utils::{quote_ident, undefined, ExprFactory}; +use swc_ecma_visit::{noop_visit_mut_type, VisitMut, VisitMutWith}; impl Bundler<'_, L, R> where @@ -44,9 +45,9 @@ where &self, ctx: &Ctx, is_entry: bool, - entry: &mut Module, + entry: &mut Modules, info: &TransformedModule, - dep: Cow, + dep: Modules, dep_info: &TransformedModule, targets: &mut Vec, ) -> Result<(), Error> { @@ -62,7 +63,7 @@ where load_var: Ident::new("load".into(), DUMMY_SP.with_ctxt(dep_info.export_ctxt())), replaced: false, }; - entry.body.visit_mut_with(&mut v); + entry.visit_mut_with(&mut v); if v.replaced { if let Some(idx) = targets.iter().position(|v| v.id == dep_info.id) { @@ -74,21 +75,18 @@ where { info.helpers.require.store(true, Ordering::SeqCst); - let mut dep = dep.into_owned().fold_with(&mut Unexporter); - dep.visit_mut_with(&mut ImportDropper); + let mut dep = dep.fold_with(&mut Unexporter); + drop_module_decls(&mut dep); dep.visit_mut_with(&mut DefaultHandler { local_ctxt: dep_info.local_ctxt(), }); - prepend( - &mut entry.body, - ModuleItem::Stmt(wrap_module( - SyntaxContext::empty(), - dep_info.local_ctxt(), - load_var, - dep, - )), - ); + entry.prepend(ModuleItem::Stmt(wrap_module( + SyntaxContext::empty(), + dep_info.local_ctxt(), + load_var, + dep.into(), + ))); log::warn!("Injecting load"); } @@ -105,7 +103,7 @@ where false, entry, info, - Cow::Borrowed(&dep_info.module), + Modules::from((*dep_info.module).clone(), self.injected_ctxt), &dep_info, targets, )?; @@ -338,19 +336,11 @@ impl VisitMut for RequireReplacer { } } -struct ImportDropper; - -impl VisitMut for ImportDropper { - noop_visit_mut_type!(); - - fn visit_mut_module_item(&mut self, i: &mut ModuleItem) { - match i { - ModuleItem::ModuleDecl(..) => { - *i = ModuleItem::Stmt(Stmt::Empty(EmptyStmt { span: DUMMY_SP })) - } - ModuleItem::Stmt(_) => {} - } - } +fn drop_module_decls(modules: &mut Modules) { + modules.retain_mut(|i| match i { + ModuleItem::ModuleDecl(..) => false, + ModuleItem::Stmt(_) => true, + }) } struct DefaultHandler { diff --git a/bundler/src/bundler/chunk/computed_key.rs b/bundler/src/bundler/chunk/computed_key.rs index 25bde77771ac..36a1d1833b21 100644 --- a/bundler/src/bundler/chunk/computed_key.rs +++ b/bundler/src/bundler/chunk/computed_key.rs @@ -1,3 +1,4 @@ +use crate::bundler::modules::Modules; use crate::{bundler::chunk::merge::Ctx, Bundler, Load, ModuleId, Resolve}; use anyhow::{bail, Error}; use std::mem::take; @@ -5,7 +6,7 @@ use swc_atoms::js_word; use swc_common::{SyntaxContext, DUMMY_SP}; use swc_ecma_ast::*; use swc_ecma_utils::{find_ids, private_ident, ExprFactory}; -use swc_ecma_visit::{noop_fold_type, noop_visit_type, Fold, FoldWith, Node, Visit, VisitWith}; +use swc_ecma_visit::{noop_fold_type, noop_visit_type, Fold, FoldWith, Node, Visit}; impl Bundler<'_, L, R> where @@ -32,24 +33,25 @@ where &self, ctx: &Ctx, id: ModuleId, - module: Module, - ) -> Result { - let span = module.span; + mut module: Modules, + ) -> Result { + let span = DUMMY_SP; let var_name = match self.scope.wrapped_esm_id(id) { Some(v) => v, None => bail!("{:?} should not be wrapped with a function", id), }; + module.sort(); let is_async = { let mut v = TopLevelAwaitFinder { found: false }; - module.visit_with(&Invalid { span: DUMMY_SP }, &mut v); + module.visit_with(&mut v); v.found }; let mut module_items = vec![]; let stmts = { - let mut module = module.fold_with(&mut ExportToReturn { + let mut module = Module::from(module).fold_with(&mut ExportToReturn { synthesized_ctxt: self.synthesized_ctxt, exports: Default::default(), }); @@ -105,7 +107,7 @@ where } let var_decl = VarDecl { - span, + span: span.with_ctxt(self.injected_ctxt), declare: false, kind: VarDeclKind::Const, decls: vec![VarDeclarator { @@ -118,11 +120,14 @@ where module_items.push(ModuleItem::Stmt(Stmt::Decl(Decl::Var(var_decl)))); - Ok(Module { - span: DUMMY_SP, - shebang: None, - body: module_items, - }) + Ok(Modules::from( + Module { + span: DUMMY_SP, + shebang: None, + body: module_items, + }, + self.injected_ctxt, + )) } } diff --git a/bundler/src/bundler/chunk/export.rs b/bundler/src/bundler/chunk/export.rs index 68ccebcd1591..434b153d60b3 100644 --- a/bundler/src/bundler/chunk/export.rs +++ b/bundler/src/bundler/chunk/export.rs @@ -1,3 +1,4 @@ +use crate::bundler::modules::Modules; use crate::{ bundler::{ chunk::merge::Ctx, @@ -9,12 +10,13 @@ use crate::{ use anyhow::{Context, Error}; #[cfg(feature = "concurrent")] use rayon::iter::ParallelIterator; -use std::mem::{replace, take}; +use retain_mut::RetainMut; +use std::mem::replace; use swc_atoms::js_word; use swc_common::{Spanned, SyntaxContext, DUMMY_SP}; use swc_ecma_ast::*; use swc_ecma_utils::{find_ids, ident::IdentLike, Id}; -use swc_ecma_visit::{noop_fold_type, noop_visit_mut_type, Fold, FoldWith, VisitMut, VisitMutWith}; +use swc_ecma_visit::{noop_fold_type, Fold}; impl Bundler<'_, L, R> where @@ -57,7 +59,7 @@ where ctx: &Ctx, dep_id: ModuleId, specifiers: &[Specifier], - ) -> Result { + ) -> Result { self.run(|| { log::debug!("Reexporting {:?}", dep_id); let dep_info = self.scope.get_module(dep_id).unwrap(); @@ -78,7 +80,11 @@ where self.handle_reexport(&dep_info, &mut dep); - // print_hygiene(&format!("dep: handle reexport"), &self.cm, &dep); + // print_hygiene( + // &format!("dep: handle reexport"), + // &self.cm, + // &dep.clone().into(), + // ); // for stmt in &mut dep.body { // let decl = match stmt { @@ -124,15 +130,15 @@ where // } if let Some(module_name) = self.scope.wrapped_esm_id(dep_info.id) { - dep = self.wrap_esm(ctx, dep_info.id, dep)?; + dep = self.wrap_esm(ctx, dep_info.id, dep.into())?; for specifier in specifiers { match specifier { Specifier::Namespace { local, .. } => { - dep.body.push( + dep.inject( module_name .assign_to(local.clone()) - .into_module_item("merge_export"), + .into_module_item(self.injected_ctxt, "merge_export"), ); break; } @@ -142,18 +148,14 @@ where } if !specifiers.is_empty() { - dep.visit_mut_with(&mut UnexportAsVar { - dep_export_ctxt: dep_info.export_ctxt(), - _specifiers: &specifiers, - }); - - // print_hygiene(&format!("dep: unexport as var"), &self.cm, &dep); + unexprt_as_var(&mut dep, dep_info.export_ctxt()); dep = dep.fold_with(&mut DepUnexporter { exports: &specifiers, }); - // print_hygiene(&format!("dep: unexport"), &self.cm, &dep); + // print_hygiene(&format!("dep: unexport"), &self.cm, + // &dep.clone().into()); } // TODO: Add varaible based on specifers @@ -176,184 +178,192 @@ where /// export { b__9 as b__10 }; /// console.log(b__9); /// ``` - fn handle_reexport(&self, info: &TransformedModule, module: &mut Module) { - let mut new_body = Vec::with_capacity(module.body.len() + 20); - - for stmt in &mut module.body { - let mut vars = vec![]; - let mut stmt = stmt.take(); - - match &mut stmt { - ModuleItem::ModuleDecl(ModuleDecl::Import(import)) => { - for specifier in &import.specifiers { - match specifier { - ImportSpecifier::Named(named) => match &named.imported { - Some(imported) => { - vars.push(imported.clone().assign_to(named.local.clone())); + fn handle_reexport(&self, info: &TransformedModule, module: &mut Modules) { + let mut vars = vec![]; + + module.map_any_items(|stmts| { + let mut new_body = Vec::with_capacity(stmts.len() + 32); + + for mut stmt in stmts { + match &mut stmt { + ModuleItem::ModuleDecl(ModuleDecl::Import(import)) => { + for specifier in &import.specifiers { + match specifier { + ImportSpecifier::Named(named) => match &named.imported { + Some(imported) => { + vars.push(imported.clone().assign_to(named.local.clone())); + } + None => {} + }, + ImportSpecifier::Default(default) => { + let imported = Ident::new( + js_word!("default"), + DUMMY_SP.with_ctxt(import.span.ctxt), + ); + vars.push(imported.clone().assign_to(default.local.clone())); } - None => {} - }, - ImportSpecifier::Default(default) => { - let imported = Ident::new( - js_word!("default"), - DUMMY_SP.with_ctxt(import.span.ctxt), - ); - vars.push(imported.clone().assign_to(default.local.clone())); + _ => {} } - _ => {} } + import.specifiers.clear(); } - import.specifiers.clear(); - } - ModuleItem::ModuleDecl(ModuleDecl::ExportDefaultDecl(export)) => { - match &mut export.decl { - DefaultDecl::Class(expr) => { - let expr = expr.take(); - let export_name = Ident::new( - js_word!("default"), - export.span.with_ctxt(info.export_ctxt()), - ); + ModuleItem::ModuleDecl(ModuleDecl::ExportDefaultDecl(export)) => { + match &mut export.decl { + DefaultDecl::Class(expr) => { + let expr = expr.take(); + let export_name = Ident::new( + js_word!("default"), + export.span.with_ctxt(info.export_ctxt()), + ); - let (init, s) = match expr.ident { - Some(name) => { - ( + let (init, s) = match expr.ident { + Some(name) => { + ( + Expr::Ident(name.clone()), + ModuleItem::Stmt(Stmt::Decl(Decl::Class(ClassDecl { + // Context of the span is local. + ident: name, + declare: false, + class: expr.class, + }))), + ) + } + None => ( + Expr::Class(expr), + ModuleItem::Stmt(Stmt::Empty(EmptyStmt { span: DUMMY_SP })), + ), + }; + + vars.push(VarDeclarator { + span: DUMMY_SP, + name: Pat::Ident(export_name.clone()), + init: Some(Box::new(init)), + definite: false, + }); + + let export_specifier = + ExportSpecifier::Named(ExportNamedSpecifier { + span: DUMMY_SP, + orig: export_name.clone(), + exported: Some(export_name.clone()), + }); + new_body.push(ModuleItem::ModuleDecl(ModuleDecl::ExportNamed( + NamedExport { + span: DUMMY_SP.with_ctxt(self.synthesized_ctxt), + specifiers: vec![export_specifier], + src: None, + type_only: false, + }, + ))); + + stmt = s; + } + DefaultDecl::Fn(expr) => { + let expr = expr.take(); + let export_name = Ident::new( + js_word!("default"), + export.span.with_ctxt(info.export_ctxt()), + ); + + let (init, s) = match expr.ident { + Some(name) => ( Expr::Ident(name.clone()), - ModuleItem::Stmt(Stmt::Decl(Decl::Class(ClassDecl { - // Context of the span is local. + ModuleItem::Stmt(Stmt::Decl(Decl::Fn(FnDecl { ident: name, declare: false, - class: expr.class, + function: expr.function, }))), - ) - } - None => ( - Expr::Class(expr), - ModuleItem::Stmt(Stmt::Empty(EmptyStmt { span: DUMMY_SP })), - ), - }; - - vars.push(VarDeclarator { - span: DUMMY_SP, - name: Pat::Ident(export_name.clone()), - init: Some(Box::new(init)), - definite: false, - }); - - let export_specifier = ExportSpecifier::Named(ExportNamedSpecifier { - span: DUMMY_SP, - orig: export_name.clone(), - exported: Some(export_name.clone()), - }); - new_body.push(ModuleItem::ModuleDecl(ModuleDecl::ExportNamed( - NamedExport { - span: DUMMY_SP.with_ctxt(self.synthesized_ctxt), - specifiers: vec![export_specifier], - src: None, - type_only: false, - }, - ))); - - stmt = s; - } - DefaultDecl::Fn(expr) => { - let expr = expr.take(); - let export_name = Ident::new( - js_word!("default"), - export.span.with_ctxt(info.export_ctxt()), - ); - - let (init, s) = match expr.ident { - Some(name) => ( - Expr::Ident(name.clone()), - ModuleItem::Stmt(Stmt::Decl(Decl::Fn(FnDecl { - ident: name, - declare: false, - function: expr.function, - }))), - ), - None => ( - Expr::Fn(expr), - ModuleItem::Stmt(Stmt::Empty(EmptyStmt { span: DUMMY_SP })), - ), - }; - - vars.push(init.assign_to(export_name.clone())); - - let export_specifier = ExportSpecifier::Named(ExportNamedSpecifier { - span: DUMMY_SP, - orig: export_name.clone(), - exported: Some(export_name.clone()), - }); - new_body.push(ModuleItem::ModuleDecl(ModuleDecl::ExportNamed( - NamedExport { - span: DUMMY_SP.with_ctxt(self.synthesized_ctxt), - specifiers: vec![export_specifier], - src: None, - type_only: false, - }, - ))); - - stmt = s; + ), + None => ( + Expr::Fn(expr), + ModuleItem::Stmt(Stmt::Empty(EmptyStmt { span: DUMMY_SP })), + ), + }; + + vars.push(init.assign_to(export_name.clone())); + + let export_specifier = + ExportSpecifier::Named(ExportNamedSpecifier { + span: DUMMY_SP, + orig: export_name.clone(), + exported: Some(export_name.clone()), + }); + new_body.push(ModuleItem::ModuleDecl(ModuleDecl::ExportNamed( + NamedExport { + span: DUMMY_SP.with_ctxt(self.synthesized_ctxt), + specifiers: vec![export_specifier], + src: None, + type_only: false, + }, + ))); + + stmt = s; + } + DefaultDecl::TsInterfaceDecl(_) => {} } - DefaultDecl::TsInterfaceDecl(_) => {} } - } - ModuleItem::ModuleDecl(ModuleDecl::ExportDecl(export)) => { - match &export.decl { - Decl::Class(ClassDecl { ident, .. }) | Decl::Fn(FnDecl { ident, .. }) => { - let mut exported = ident.clone(); - exported.span.ctxt = info.export_ctxt(); + ModuleItem::ModuleDecl(ModuleDecl::ExportDecl(export)) => { + match &export.decl { + Decl::Class(ClassDecl { ident, .. }) + | Decl::Fn(FnDecl { ident, .. }) => { + let mut exported = ident.clone(); + exported.span.ctxt = info.export_ctxt(); - vars.push(ident.clone().assign_to(exported)); - } - Decl::Var(var) => { - // - let ids: Vec = find_ids(&var.decls); - - vars.extend( - ids.into_iter() - .map(|i| { - let mut exported = i.clone(); - exported.span.ctxt = info.export_ctxt(); + vars.push(ident.clone().assign_to(exported)); + } + Decl::Var(var) => { + // + let ids: Vec = find_ids(&var.decls); + + vars.extend( + ids.into_iter() + .map(|i| { + let mut exported = i.clone(); + exported.span.ctxt = info.export_ctxt(); + + i.assign_to(exported) + }) + .map(From::from), + ); + } + _ => {} + }; + } - i.assign_to(exported) - }) - .map(From::from), - ); - } - _ => {} - }; + _ => {} } - _ => {} + new_body.push(stmt); } - new_body.push(stmt); - for var in vars { - new_body.push(var.into_module_item("from_export_rs")) - } + new_body + }); + for var in vars { + module.inject(var.into_module_item(self.injected_ctxt, "from_export_rs")) } - - module.body = new_body; } } -pub(super) struct ExportInjector<'a> { - pub ctx: &'a Ctx, - pub export_ctxt: SyntaxContext, - pub wrapped: bool, - pub imported: Vec, - pub source: Source, -} - -impl VisitMut for ExportInjector<'_> { - noop_visit_mut_type!(); - - fn visit_mut_module_items(&mut self, orig: &mut Vec) { - let items = take(orig); - let mut buf = Vec::with_capacity(self.imported.len() + items.len()); +pub(super) fn inject_export( + entry: &mut Modules, + ctx: &Ctx, + entry_export_ctxt: SyntaxContext, + wrapped: bool, + dep: Modules, + source: Source, +) -> Result<(), Modules> { + let injected_ctxt = entry.injected_ctxt; + let mut dep = Some(dep); + // This is required because `export *` does not exports `default` from the + // source. + // But as user may specify both of `export *` and `export { default }` from same + // module, we have to store it somewhere. + let mut export_default_stmt = None; + let mut vars = vec![]; + entry.map_any_items(|items| { + let mut buf = vec![]; for item in items { // @@ -368,9 +378,11 @@ impl VisitMut for ExportInjector<'_> { // // See: https://github.com/swc-project/swc/issues/1150 ModuleItem::ModuleDecl(ModuleDecl::Import(ref import)) - if import.src.value == self.source.src.value => + if import.src.value == source.src.value => { - buf.extend(take(&mut self.imported)); + if let Some(dep) = dep.take() { + buf.extend(dep.into_items()); + } let decls = import .specifiers @@ -382,7 +394,7 @@ impl VisitMut for ExportInjector<'_> { .. }) => { let mut imported = imported.clone(); - imported.span = imported.span.with_ctxt(self.source.export_ctxt); + imported.span = imported.span.with_ctxt(source.export_ctxt); Some(VarDeclarator { span: DUMMY_SP, @@ -396,26 +408,29 @@ impl VisitMut for ExportInjector<'_> { .collect::>(); for var in decls { - buf.push(var.into_module_item("ExportInjector")); + vars.push(var.into_module_item(injected_ctxt, "ExportInjector")); } } ModuleItem::ModuleDecl(ModuleDecl::ExportAll(ref export)) - if export.src.value == self.source.src.value => + if export.src.value == source.src.value => { - if !self.wrapped { + if !wrapped { let export_ctxt = export.span.ctxt; - self.ctx - .transitive_remap - .insert(self.export_ctxt, export_ctxt); + ctx.transitive_remap.insert(entry_export_ctxt, export_ctxt); } - buf.extend(take(&mut self.imported)); + if let Some(mut dep) = dep.take() { + if let Some(item) = remove_default_export(&mut dep) { + export_default_stmt = Some(item) + } + buf.extend(dep.into_items()); + } } ModuleItem::ModuleDecl(ModuleDecl::ExportNamed( export @ NamedExport { src: Some(..), .. }, - )) if export.src.as_ref().unwrap().value == self.source.src.value => { + )) if export.src.as_ref().unwrap().value == source.src.value => { let namespace_name = export .specifiers .iter() @@ -426,7 +441,12 @@ impl VisitMut for ExportInjector<'_> { }) .next(); - buf.extend(take(&mut self.imported)); + if let Some(dep) = dep.take() { + buf.extend(dep.into_items()); + } + if let Some(stmt) = export_default_stmt.take() { + buf.push(stmt); + } if let Some(ns_name) = namespace_name { buf.push(ModuleItem::ModuleDecl(ModuleDecl::ExportNamed( @@ -455,10 +475,75 @@ impl VisitMut for ExportInjector<'_> { } } - *orig = buf; + buf + }); + + entry.inject_all(vars); + + match dep { + Some(dep) => Err(dep), + None => Ok(()), } } +fn remove_default_export(dep: &mut Modules) -> Option { + let mut export_default_stmt = None; + + dep.retain_mut(|item| match item { + ModuleItem::ModuleDecl(ModuleDecl::ExportDefaultExpr(..)) => { + export_default_stmt = Some(item.take()); + false + } + ModuleItem::ModuleDecl(ModuleDecl::ExportNamed(NamedExport { + span, specifiers, .. + })) => { + // + let mut default_specifer = None; + specifiers.retain_mut(|specifier| { + match specifier { + ExportSpecifier::Namespace(_) => {} + ExportSpecifier::Default(_) => {} + ExportSpecifier::Named(named) => match &named.exported { + Some(exported) => { + // + if exported.sym == js_word!("default") { + default_specifer = Some(named.orig.clone()); + return false; + } + } + None => { + // + if named.orig.sym == js_word!("default") { + default_specifer = Some(named.orig.take()); + + return false; + } + } + }, + } + + true + }); + + match default_specifer { + Some(expr) => { + export_default_stmt = Some(ModuleItem::ModuleDecl( + ModuleDecl::ExportDefaultExpr(ExportDefaultExpr { + span: *span, + expr: Box::new(Expr::Ident(expr)), + }), + )); + !specifiers.is_empty() + } + None => true, + } + } + _ => true, + }); + + export_default_stmt +} + /// Converts /// /// ```js @@ -474,20 +559,10 @@ impl VisitMut for ExportInjector<'_> { /// export { foo#7 } from './b' where #7 is mark of './b' /// => /// export { foo#7 as foo#5 } where #5 is mark of current entry. -struct UnexportAsVar<'a> { - /// Syntax context for the generated variables. - dep_export_ctxt: SyntaxContext, - - /// Exports to preserve - _specifiers: &'a [Specifier], -} - -impl VisitMut for UnexportAsVar<'_> { - noop_visit_mut_type!(); - - fn visit_mut_module_item(&mut self, n: &mut ModuleItem) { - n.visit_mut_children_with(self); +fn unexprt_as_var(modules: &mut Modules, dep_export_ctxt: SyntaxContext) { + let injected_ctxt = modules.injected_ctxt; + modules.map_items_mut(|n| { match n { ModuleItem::ModuleDecl(ModuleDecl::ExportDefaultExpr(export)) => { let expr = replace( @@ -498,13 +573,13 @@ impl VisitMut for UnexportAsVar<'_> { *n = VarDeclarator { span: DUMMY_SP, name: Pat::Ident(Ident::new( - "__default".into(), - expr.span().with_ctxt(self.dep_export_ctxt), + "default".into(), + expr.span().with_ctxt(dep_export_ctxt), )), init: Some(expr), definite: false, } - .into_module_item("UnexportAsVar"); + .into_module_item(injected_ctxt, "UnexportAsVar"); } ModuleItem::ModuleDecl(ModuleDecl::ExportNamed( ref export @ NamedExport { src: None, .. }, @@ -532,18 +607,14 @@ impl VisitMut for UnexportAsVar<'_> { } } None => { - if n.orig.span.ctxt != self.dep_export_ctxt { - log::trace!( - "Alias: {:?} -> {:?}", - n.orig, - self.dep_export_ctxt - ); + if n.orig.span.ctxt != dep_export_ctxt { + log::trace!("Alias: {:?} -> {:?}", n.orig, dep_export_ctxt); decls.push(VarDeclarator { span: n.span, name: Pat::Ident(Ident::new( n.orig.sym.clone(), - n.orig.span.with_ctxt(self.dep_export_ctxt), + n.orig.span.with_ctxt(dep_export_ctxt), )), init: Some(Box::new(Expr::Ident(n.orig.clone()))), definite: false, @@ -567,9 +638,7 @@ impl VisitMut for UnexportAsVar<'_> { } _ => {} } - } - - fn visit_mut_stmt(&mut self, _: &mut Stmt) {} + }) } struct DepUnexporter<'a> { diff --git a/bundler/src/bundler/chunk/merge.rs b/bundler/src/bundler/chunk/merge.rs index 7f99503a37be..3c6f75f45309 100644 --- a/bundler/src/bundler/chunk/merge.rs +++ b/bundler/src/bundler/chunk/merge.rs @@ -1,8 +1,10 @@ use super::plan::{DepType, Plan}; +use crate::bundler::chunk::export::inject_export; use crate::{ bundler::{ - chunk::{export::ExportInjector, plan::NormalPlan, sort::sort}, + chunk::plan::NormalPlan, load::{Imports, Source, Specifier, TransformedModule}, + modules::Modules, }, id::{Id, ModuleId}, load::Load, @@ -13,13 +15,12 @@ use crate::{ use anyhow::{Context, Error}; #[cfg(feature = "concurrent")] use rayon::iter::ParallelIterator; -use retain_mut::RetainMut; -use std::{borrow::Cow, collections::HashMap, mem::take}; +use std::collections::HashMap; use swc_atoms::js_word; use swc_common::{sync::Lock, FileName, SyntaxContext, DUMMY_SP}; use swc_ecma_ast::*; use swc_ecma_utils::{find_ids, prepend, private_ident, ExprFactory}; -use swc_ecma_visit::{noop_fold_type, noop_visit_mut_type, Fold, FoldWith, VisitMut, VisitMutWith}; +use swc_ecma_visit::{noop_fold_type, noop_visit_mut_type, Fold, VisitMut, VisitMutWith}; use util::CHashSet; pub(super) struct Ctx { @@ -41,7 +42,7 @@ where module_id: ModuleId, is_entry: bool, allow_circular: bool, - ) -> Result { + ) -> Result { self.run(|| { let info = self.scope.get_module(module_id).unwrap(); @@ -61,6 +62,7 @@ where let mut module = self .get_module_for_merging(ctx, module_id, is_entry) + .map(|module| Modules::from(module, self.injected_ctxt)) .with_context(|| format!("Failed to clone {:?} for merging", module_id))?; { @@ -104,7 +106,9 @@ where dep_id: ModuleId, src: &Source, specifiers: &[Specifier], - ) -> Result { + ) -> Result { + let injected_ctxt = self.injected_ctxt; + log::debug!("Merging {:?} directly", dep_id); let dep_info = self.scope.get_module(dep_id).unwrap(); @@ -112,8 +116,10 @@ where // Now we handle imports let mut module = if wrapped { - let mut module = self.get_module_for_merging(ctx, dep_id, false)?; - module = self.wrap_esm(ctx, dep_id, module)?; + let mut module: Modules = self + .get_module_for_merging(ctx, dep_id, false) + .map(|module| Modules::from(module, self.injected_ctxt))?; + module = self.wrap_esm(ctx, dep_id, module.into())?.into(); // Inject local_name = wrapped_esm_module_name let module_ident = specifiers.iter().find_map(|specifier| match specifier { @@ -126,11 +132,11 @@ where let esm_id = self.scope.wrapped_esm_id(dep_id).unwrap(); if let Some(module_ident) = &module_ident { - module.body.push( + module.inject( esm_id .clone() .assign_to(module_ident.clone()) - .into_module_item("merge_direct_import"), + .into_module_item(injected_ctxt, "merge_direct_import"), ); } @@ -164,9 +170,9 @@ where let from = esm_id.clone().into_ident().make_member(i.clone()); - module.body.push( + module.inject( from.assign_to(i.with_ctxt(src.export_ctxt)) - .into_module_item("namespace_with_normal"), + .into_module_item(injected_ctxt, "namespace_with_normal"), ); } Specifier::Namespace { .. } => continue, @@ -211,15 +217,15 @@ where })); for var in var_decls { - module - .body - .push(var.into_module_item("from_merge_direct_import")); + module.inject(var.into_module_item(injected_ctxt, "from_merge_direct_import")); } Ok(module) } - fn merge_transitive_import(&self, ctx: &Ctx, dep_id: ModuleId) -> Result { + fn merge_transitive_import(&self, ctx: &Ctx, dep_id: ModuleId) -> Result { + let injected_ctxt = self.injected_ctxt; + log::debug!("Merging {:?} transitively", dep_id); let dep_info = self.scope.get_module(dep_id).unwrap(); @@ -231,9 +237,7 @@ where module = module.fold_with(&mut Unexporter); for var in var_decls { - module - .body - .push(var.into_module_item("from_merge_transitive_import")); + module.inject(var.into_module_item(injected_ctxt, "from_merge_transitive_import")); } Ok(module) @@ -252,12 +256,12 @@ where fn transform_indirect_reexports( &self, _ctx: &Ctx, - module: &mut Module, + module: &mut Modules, deps: Vec, ) -> Result<(), Error> { self.run(|| { // - for stmt in &mut module.body { + for stmt in module.iter_mut() { let decl = match stmt { ModuleItem::ModuleDecl(decl) => decl, ModuleItem::Stmt(_) => continue, @@ -288,11 +292,11 @@ where &self, ctx: &Ctx, is_entry: bool, - mut module: Module, + mut module: Modules, plan: &NormalPlan, info: &TransformedModule, from_circular: bool, - ) -> Result { + ) -> Result { self.run(|| -> Result<_, Error> { log::debug!( "Normal merging: ({:?}) {} <= {:?}", @@ -387,15 +391,22 @@ where if is_export { assert!(dep_info.is_es6, "export statements are es6-only"); - let mut injector = ExportInjector { - imported: take(&mut dep_module.body), - source: source.unwrap().clone(), + + let res = inject_export( + &mut module, ctx, - export_ctxt: info.export_ctxt(), - // We use id of the entry - wrapped: self.scope.should_be_wrapped_with_a_fn(info.id), - }; - module.body.visit_mut_with(&mut injector); + info.export_ctxt(), + self.scope.should_be_wrapped_with_a_fn(info.id), + dep_module, + source.unwrap().clone(), + ); + + match res { + Ok(()) => {} + Err(..) => { + unreachable!("Merging as export when export statement does not exist?") + } + } log::debug!( "Merged {} into {} as a reexport", @@ -411,7 +422,7 @@ where match dep.ty { DepType::Transitive => { - module.body.extend(dep_module.body); + module.push_all(dep_module); log::debug!( "Merged {} into {} as a transitive es module", @@ -431,38 +442,39 @@ where // &module, // ); - // Replace import statement / require with module body - let mut injector = Es6ModuleInjector { - imported: take(&mut dep_module.body), - dep_export_ctxt: dep_info.export_ctxt(), - is_direct: match dep.ty { + // Replace import statement with module body + let res = inject_es_module( + &mut module, + dep_module, + dep_info.export_ctxt(), + match dep.ty { DepType::Direct => true, _ => false, }, - }; - module.body.visit_mut_with(&mut injector); + ); - if injector.imported.is_empty() { - log::debug!( - "Merged {} into {} as an es module", - dep_info.fm.name, - info.fm.name, - ); - // print_hygiene( - // &format!("ES6: {:?} <- {:?}", info.ctxt(), dep_info.ctxt()), - // &self.cm, - // &entry, - // ); - continue; - } + dep_module = match res { + Ok(()) => { + log::debug!( + "Merged {} into {} as an es module", + dep_info.fm.name, + info.fm.name, + ); + // print_hygiene( + // &format!("ES6: {:?} <- {:?}", info.ctxt(), dep_info.ctxt()), + // &self.cm, + // &entry, + // ); + continue; + } + Err(dep) => dep, + }; if info.is_es6 && dep_info.is_es6 { - module.body.extend(injector.imported); + module.push_all(dep_module); continue; } - dep_module.body = take(&mut injector.imported); - // print_hygiene( // &format!("entry: failed to inject: {}; {:?}", // dep_info.fm.name, dep), &self.cm, @@ -476,7 +488,7 @@ where is_entry, &mut module, &info, - Cow::Owned(dep_module), + dep_module, &dep_info, &mut targets, )?; @@ -525,23 +537,29 @@ where /// /// This method does not care about orders of statement, and it's expected /// to be called before `sort`. - fn handle_export_stars(&self, ctx: &Ctx, entry: &mut Module) { + fn handle_export_stars(&self, ctx: &Ctx, entry: &mut Modules) { + let injected_ctxt = self.injected_ctxt; + { // Handle `export *` for non-wrapped modules. - let mut extra_stmts = vec![]; + let mut vars = vec![]; - for stmt in entry.body.iter() { + for stmt in entry.iter() { match stmt { ModuleItem::Stmt(Stmt::Decl(Decl::Var(decl))) => { let ids: Vec = find_ids(decl); for id in ids { + if *id.sym() == js_word!("default") { + continue; + } + if let Some(remapped) = ctx.transitive_remap.get(&id.ctxt()) { let reexported = id.clone().with_ctxt(remapped); - extra_stmts.push( + vars.push( id.assign_to(reexported) - .into_module_item("export_star_replacer"), + .into_module_item(injected_ctxt, "export_star_replacer"), ); } } @@ -551,7 +569,7 @@ where } } - entry.body.extend(extra_stmts); + entry.inject_all(vars); } { @@ -559,12 +577,16 @@ where let mut additional_props = HashMap::<_, Vec<_>>::new(); // Handle `export *` for wrapped modules. for (module_id, ctxts) in map.drain() { - for stmt in entry.body.iter() { + for stmt in entry.iter() { match stmt { ModuleItem::Stmt(Stmt::Decl(Decl::Var(decl))) => { let ids: Vec = find_ids(decl); for id in ids { + if *id.sym() == js_word!("default") { + continue; + } + if ctxts.contains(&id.ctxt()) { additional_props.entry(module_id).or_default().push( PropOrSpread::Prop(Box::new(Prop::Shorthand( @@ -585,7 +607,7 @@ where None => continue, }; - for stmt in &mut entry.body { + for stmt in entry.iter_mut() { let var = match stmt { ModuleItem::Stmt(Stmt::Decl(Decl::Var( var @@ -653,14 +675,16 @@ where } } - fn finalize_merging_of_entry(&self, ctx: &Ctx, entry: &mut Module) { + fn finalize_merging_of_entry(&self, ctx: &Ctx, entry: &mut Modules) { self.handle_export_stars(ctx, entry); - sort(&mut entry.body); + // print_hygiene("before sort", &self.cm, &entry.clone().into()); + + entry.sort(); - // print_hygiene("done", &self.cm, &entry); + // print_hygiene("done", &self.cm, &entry.clone().into()); - entry.body.retain_mut(|item| { + entry.retain_mut(|item| { match item { ModuleItem::ModuleDecl(ModuleDecl::ExportAll(..)) => return false, @@ -720,78 +744,96 @@ where // ); } - pub(super) fn replace_import_specifiers(&self, info: &TransformedModule, module: &mut Module) { - let mut new = Vec::with_capacity(module.body.len() + 32); + pub(super) fn replace_import_specifiers(&self, info: &TransformedModule, module: &mut Modules) { + let injected_ctxt = self.injected_ctxt; - for stmt in take(&mut module.body) { - match &stmt { - ModuleItem::ModuleDecl(ModuleDecl::Import(import)) => { - for specifier in &import.specifiers { - match specifier { - ImportSpecifier::Named(named) => match &named.imported { - Some(imported) => { - new.push( - imported - .clone() - .assign_to(named.local.clone()) - .into_module_item("from_replace_import_specifiers"), - ); - continue; - } - None => {} - }, - ImportSpecifier::Default(default) => { - if let Some((src, _)) = info - .imports - .specifiers - .iter() - .find(|s| s.0.src.value == import.src.value) - { - let imported = Ident::new( - js_word!("default"), - DUMMY_SP.with_ctxt(src.export_ctxt), - ); - new.push( - imported - .assign_to(default.local.clone()) - .into_module_item("from_replace_import_specifiers"), - ); - continue; + let mut vars = vec![]; + module.map_any_items(|stmts| { + let mut new = Vec::with_capacity(stmts.len() + 32); + + for stmt in stmts { + match &stmt { + ModuleItem::ModuleDecl(ModuleDecl::Import(import)) => { + for specifier in &import.specifiers { + match specifier { + ImportSpecifier::Named(named) => match &named.imported { + Some(imported) => { + vars.push( + imported + .clone() + .assign_to(named.local.clone()) + .into_module_item( + injected_ctxt, + "from_replace_import_specifiers", + ), + ); + continue; + } + None => {} + }, + ImportSpecifier::Default(default) => { + if let Some((src, _)) = info + .imports + .specifiers + .iter() + .find(|s| s.0.src.value == import.src.value) + { + let imported = Ident::new( + js_word!("default"), + DUMMY_SP.with_ctxt(src.export_ctxt), + ); + vars.push( + imported + .assign_to(default.local.clone()) + .into_module_item( + injected_ctxt, + "from_replace_import_specifiers", + ), + ); + continue; + } } - } - ImportSpecifier::Namespace(s) => { - if let Some((src, _)) = info - .imports - .specifiers - .iter() - .find(|s| s.0.src.value == import.src.value) - { - let esm_id = self.scope.wrapped_esm_id(src.module_id).expect( - "If a namespace impoet specifier is preserved, it means \ - failutre of deblobbing and as a result module should be \ - marked as wrpaped esm", - ); - new.push( - esm_id.clone().assign_to(s.local.clone()).into_module_item( - "from_replace_import_specifiers: namespaced", - ), - ); - continue; + ImportSpecifier::Namespace(s) => { + if let Some((src, _)) = info + .imports + .specifiers + .iter() + .find(|s| s.0.src.value == import.src.value) + { + let esm_id = + self.scope.wrapped_esm_id(src.module_id).expect( + "If a namespace impoet specifier is preserved, it \ + means failutre of deblobbing and as a result \ + module should be marked as wrpaped esm", + ); + vars.push( + esm_id + .clone() + .assign_to(s.local.clone()) + .into_module_item( + injected_ctxt, + "from_replace_import_specifiers: namespaced", + ), + ); + continue; + } } } } - } - // We should remove imports - continue; + // We should remove imports + continue; + } + _ => {} } - _ => {} + + new.push(stmt); } - new.push(stmt); - } + new + }); - module.body = new; + module.inject_all(vars) } /// If a dependency aliased exports, we handle them at here. @@ -799,14 +841,16 @@ where &self, ctx: &Ctx, info: &TransformedModule, - module: &mut Module, + module: &mut Modules, _for_circular: bool, ) { + let injected_ctxt = self.injected_ctxt; + self.replace_import_specifiers(info, module); let mut vars = vec![]; - for orig_stmt in module.body.iter_mut() { + for orig_stmt in module.iter_mut() { let mut stmt = orig_stmt.take(); match stmt { @@ -818,12 +862,10 @@ where ExportSpecifier::Namespace(ns) => { let mut lhs = ns.name.clone(); lhs.span = lhs.span.with_ctxt(info.export_ctxt()); - vars.push( - ns.name - .clone() - .assign_to(lhs) - .into_module_item("import_deps_namespace"), - ); + vars.push(ns.name.clone().assign_to(lhs).into_module_item( + injected_ctxt, + "import_deps_namespace", + )); } ExportSpecifier::Default(default) => { let mut lhs = default.exported.clone(); @@ -833,12 +875,17 @@ where .exported .clone() .assign_to(lhs) - .into_module_item("import_deps_default"), + .into_module_item( + injected_ctxt, + "import_deps_default", + ), ); } ExportSpecifier::Named(named) => match &named.exported { Some(exported) => { - if named.orig.span.ctxt != info.export_ctxt() { + if named.orig.span.ctxt != info.export_ctxt() + && exported.sym != js_word!("default") + { let mut lhs = exported.clone(); lhs.span = lhs.span.with_ctxt(info.export_ctxt()); vars.push( @@ -846,10 +893,13 @@ where .orig .clone() .assign_to(lhs) - .into_module_item(&format!( - "import_deps_named_alias of {}", - info.fm.name - )), + .into_module_item( + injected_ctxt, + &format!( + "import_deps_named_alias of {}", + info.fm.name + ), + ), ); } } @@ -862,7 +912,10 @@ where .orig .clone() .assign_to(lhs) - .into_module_item("import_deps_named"), + .into_module_item( + injected_ctxt, + "import_deps_named", + ), ); } } @@ -905,7 +958,10 @@ where init: Some(Box::new(init)), definite: false, } - .into_module_item("import_deps_export_default_decl"), + .into_module_item( + injected_ctxt, + "import_deps_export_default_decl", + ), ); s @@ -932,10 +988,10 @@ where ), }; - vars.push( - init.assign_to(export_name) - .into_module_item("import_deps_export_default_decl_fn"), - ); + vars.push(init.assign_to(export_name).into_module_item( + injected_ctxt, + "import_deps_export_default_decl_fn", + )); s } DefaultDecl::TsInterfaceDecl(_) => { @@ -948,12 +1004,10 @@ where | Decl::Fn(FnDecl { ident, .. }) => { let mut exported = ident.clone(); exported.span.ctxt = info.export_ctxt(); - vars.push( - ident - .clone() - .assign_to(exported) - .into_module_item("import_deps_export_decl"), - ); + vars.push(ident.clone().assign_to(exported).into_module_item( + injected_ctxt, + "import_deps_export_decl", + )); } Decl::Var(var) => { let ids: Vec = find_ids(var); @@ -961,10 +1015,10 @@ where for id in ids { let mut exported = id.clone(); exported.span.ctxt = info.export_ctxt(); - vars.push( - id.assign_to(exported) - .into_module_item("import_deps_export_var_decl"), - ); + vars.push(id.assign_to(exported).into_module_item( + injected_ctxt, + "import_deps_export_var_decl", + )); } } _ => {} @@ -975,7 +1029,7 @@ where ModuleDecl::ExportDefaultExpr(mut export) => { vars.push( VarDeclarator { - span: DUMMY_SP, + span: DUMMY_SP.with_ctxt(injected_ctxt), name: Pat::Ident(Ident::new( js_word!("default"), DUMMY_SP.with_ctxt(info.export_ctxt()), @@ -983,7 +1037,7 @@ where init: Some(export.expr.take()), definite: false, } - .into_module_item("import_deps_export_default_expr"), + .into_module_item(injected_ctxt, "import_deps_export_default_expr"), ); ModuleItem::Stmt(Stmt::Empty(EmptyStmt { span: DUMMY_SP })) } @@ -1005,19 +1059,19 @@ where } for var in vars { - module.body.push(var) + module.inject(var); } } } -fn vars_from_exports(dep_info: &TransformedModule, module: &Module) -> Vec { +fn vars_from_exports(dep_info: &TransformedModule, module: &Modules) -> Vec { // Convert all exports into variables in form of // // A__export = A__local let mut vars = vec![]; - for item in &module.body { + for item in module.iter() { let item = match item { ModuleItem::ModuleDecl(item) => item, ModuleItem::Stmt(_) => continue, @@ -1164,29 +1218,36 @@ impl Fold for Unexporter { } } -struct Es6ModuleInjector { - imported: Vec, - /// Export context of the dependency module. +/// Returns `Err(dep)` on error. +fn inject_es_module( + entry: &mut Modules, + dep: Modules, dep_export_ctxt: SyntaxContext, is_direct: bool, -} +) -> Result<(), Modules> { + let injected_ctxt = entry.injected_ctxt; -impl VisitMut for Es6ModuleInjector { - noop_visit_mut_type!(); + let mut vars = vec![]; + let mut dep = Some(dep); + + entry.map_any_items(|items| { + if dep.is_none() { + return items; + } - fn visit_mut_module_items(&mut self, orig: &mut Vec) { - let items = take(orig); - let mut buf = Vec::with_capacity(self.imported.len() + items.len()); + let mut buf = vec![]; for item in items { // match item { ModuleItem::ModuleDecl(ModuleDecl::Import(ImportDecl { span, specifiers, .. - })) if span.ctxt == self.dep_export_ctxt => { - buf.extend(take(&mut self.imported)); + })) if span.ctxt == dep_export_ctxt => { + if let Some(dep) = dep.take() { + buf.extend(dep.into_items()); + } - if !self.is_direct { + if !is_direct { let decls = specifiers .iter() .filter_map(|specifier| match specifier { @@ -1196,7 +1257,7 @@ impl VisitMut for Es6ModuleInjector { .. }) => { let mut imported = imported.clone(); - imported.span = imported.span.with_ctxt(self.dep_export_ctxt); + imported.span = imported.span.with_ctxt(dep_export_ctxt); Some(VarDeclarator { span: DUMMY_SP, @@ -1210,7 +1271,7 @@ impl VisitMut for Es6ModuleInjector { .collect::>(); for var in decls { - buf.push(var.into_module_item("Es6ModuleInjector")); + vars.push(var.into_module_item(injected_ctxt, "Es6ModuleInjector")); } } } @@ -1219,7 +1280,14 @@ impl VisitMut for Es6ModuleInjector { } } - *orig = buf; + buf + }); + + entry.inject_all(vars); + + match dep { + Some(dep) => Err(dep), + None => Ok(()), } } @@ -1262,6 +1330,13 @@ impl VisitMut for DefaultRenamer { n.prop.visit_mut_with(self) } } + + fn visit_mut_export_named_specifier(&mut self, n: &mut ExportNamedSpecifier) { + if n.orig.sym == js_word!("default") { + n.orig.sym = "__default".into(); + return; + } + } } struct ImportMetaHandler<'a, 'b> { diff --git a/bundler/src/bundler/chunk/mod.rs b/bundler/src/bundler/chunk/mod.rs index 8348cf5cbd71..a54042d00723 100644 --- a/bundler/src/bundler/chunk/mod.rs +++ b/bundler/src/bundler/chunk/mod.rs @@ -14,7 +14,6 @@ mod computed_key; mod export; mod merge; mod plan; -mod sort; #[derive(Debug)] struct InternalEntry { @@ -74,7 +73,7 @@ where Bundle { kind, id: entry, - module, + module: module.into(), } }) }) diff --git a/bundler/src/bundler/chunk/sort.rs b/bundler/src/bundler/chunk/sort.rs deleted file mode 100644 index bd9500350bd8..000000000000 --- a/bundler/src/bundler/chunk/sort.rs +++ /dev/null @@ -1,381 +0,0 @@ -use crate::{id::Id, util::MapWithMut}; -use indexmap::IndexSet; -use petgraph::{ - graphmap::DiGraphMap, - EdgeDirection::{Incoming, Outgoing}, -}; -use std::collections::HashMap; -use swc_common::DUMMY_SP; -use swc_ecma_ast::*; -use swc_ecma_utils::find_ids; -use swc_ecma_visit::{noop_visit_type, Node, Visit, VisitWith}; - -type StmtDepGraph = DiGraphMap; - -/// Is dependancy between nodes hard? -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -enum Required { - /// Required to evaluate - Always, - - /// Maybe required to evaluate - Maybe, -} - -pub(super) fn sort(new: &mut Vec) { - new.retain(|item| match item { - ModuleItem::Stmt(Stmt::Empty(..)) => false, - _ => true, - }); - - let mut graph = StmtDepGraph::default(); - let mut declared_by = HashMap::>::default(); - let mut uninitialized_ids = HashMap::::new(); - - for (idx, item) in new.iter().enumerate() { - graph.add_node(idx); - - // We start by calculating ids created by statements. Note that we don't need to - // analyze bodies of functions nor members of classes, because it's not - // evaludated until they are called. - - match item { - // We only check declarations because ids are created by declarations. - ModuleItem::ModuleDecl(ModuleDecl::ExportDecl(ExportDecl { decl, .. })) - | ModuleItem::Stmt(Stmt::Decl(decl)) => { - // - match decl { - Decl::Class(ClassDecl { ident, .. }) | Decl::Fn(FnDecl { ident, .. }) => { - declared_by.entry(Id::from(ident)).or_default().push(idx); - } - Decl::Var(vars) => { - for var in &vars.decls { - // - let ids: Vec = find_ids(&var.name); - for id in ids { - if var.init.is_none() { - uninitialized_ids.insert(id.clone(), idx); - } - declared_by.entry(id).or_default().push(idx); - } - } - } - _ => {} - } - } - _ => {} - } - } - - // Handle uninitialized variables - // - // Compiled typescript enum is not initialized by declaration - // - // var Status; - // (function(Status){})(Status) - for (uninit_id, start_idx) in uninitialized_ids { - for (idx, item) in new.iter().enumerate().filter(|(idx, _)| *idx > start_idx) { - let mut finder = InitializerFinder { - ident: uninit_id.clone(), - found: false, - in_complex: false, - }; - item.visit_with(&Invalid { span: DUMMY_SP }, &mut finder); - if finder.found { - declared_by.entry(uninit_id).or_default().push(idx); - break; - } - } - } - - for (idx, item) in new.iter().enumerate() { - // We then calculate which ids a statement require to be executed. - // Again, we don't need to analyze non-top-level idents because they - // are not evaluated while lpoading module. - let mut visitor = RequirementCalculartor::default(); - item.visit_with(&Invalid { span: DUMMY_SP }, &mut visitor); - - for (id, kind) in visitor.required_ids { - if let Some(declarator_indexes) = declared_by.get(&id) { - for &declarator_index in declarator_indexes { - if declarator_index != idx { - graph.add_edge(declarator_index, idx, kind); - } - } - } - } - } - - // Now graph contains enough information to sort statements. - let len = new.len(); - let mut orders: Vec = vec![]; - - // No dependencies - loop { - if graph.all_edges().count() == 0 { - break; - } - - let mut did_work = false; - // Add nodes which does not have any dependencies. - for i in 0..len { - if orders.contains(&i) { - continue; - } - - let dependants = graph.neighbors_directed(i, Incoming); - - if dependants.count() != 0 { - continue; - } - - did_work = true; - orders.push(i); - - // Remove dependencies to other node. - graph.remove_node(i); - } - - if !did_work { - break; - } - } - - // Strong dependencies - loop { - if graph.all_edges().count() == 0 { - break; - } - - let mut did_work = false; - // Add nodes which does not have any dependencies. - for i in 0..len { - if orders.contains(&i) { - continue; - } - - let dependants = graph - .neighbors_directed(i, Incoming) - .filter(|&entry| graph.edge_weight(entry, i) == Some(&Required::Always)); - - if dependants.count() != 0 { - continue; - } - - did_work = true; - orders.push(i); - - // Remove strong dependency - for dependancy in graph - .neighbors_directed(i, Outgoing) - .filter(|&dep| graph.edge_weight(i, dep) == Some(&Required::Always)) - .collect::>() - { - graph.remove_edge(i, dependancy).unwrap(); - } - } - - if !did_work { - break; - } - } - - // Weak dependencies - loop { - if graph.all_edges().count() == 0 { - break; - } - - let mut did_work = false; - // Add nodes which does not have any dependencies. - for i in 0..len { - let dependants = graph.neighbors_directed(i, Incoming); - - if orders.contains(&i) || dependants.count() != 0 { - continue; - } - - did_work = true; - orders.push(i); - - // Remove dependency - graph.remove_node(i); - } - - if !did_work { - break; - } - } - - // Now all dependencies are merged. - for i in 0..len { - if orders.contains(&i) { - continue; - } - orders.push(i); - } - - assert_eq!(orders.len(), new.len()); - - let mut buf = Vec::with_capacity(new.len()); - for order in orders { - let stmt = new[order].take(); - buf.push(stmt) - } - - *new = buf; -} - -/// Finds usage of `ident` -struct InitializerFinder { - ident: Id, - found: bool, - in_complex: bool, -} - -impl Visit for InitializerFinder { - noop_visit_type!(); - - fn visit_pat(&mut self, pat: &Pat, _: &dyn Node) { - match pat { - Pat::Ident(i) if self.ident == *i => { - self.found = true; - } - - _ => { - pat.visit_children_with(self); - } - } - } - - fn visit_ident(&mut self, i: &Ident, _: &dyn Node) { - if self.in_complex && self.ident == *i { - self.found = true; - } - } - - fn visit_expr_or_spread(&mut self, node: &ExprOrSpread, _: &dyn Node) { - let in_complex = self.in_complex; - self.in_complex = true; - node.visit_children_with(self); - self.in_complex = in_complex; - } - - fn visit_member_expr(&mut self, e: &MemberExpr, _: &dyn Node) { - { - let in_complex = self.in_complex; - self.in_complex = true; - e.obj.visit_children_with(self); - self.in_complex = in_complex; - } - - if e.computed { - e.prop.visit_with(e as _, self); - } - } -} - -/// We do not care about variables created by current statement. -/// But we care about modifications. -#[derive(Default)] -struct RequirementCalculartor { - required_ids: IndexSet<(Id, Required)>, - - in_weak: bool, - in_var_decl: bool, -} - -macro_rules! weak { - ($name:ident, $T:ty) => { - fn $name(&mut self, f: &$T, _: &dyn Node) { - let in_weak = self.in_weak; - self.in_weak = true; - - f.visit_children_with(self); - - self.in_weak = in_weak; - } - }; -} - -impl RequirementCalculartor { - fn insert(&mut self, i: Id) { - self.required_ids.insert(( - i, - if self.in_weak { - Required::Maybe - } else { - Required::Always - }, - )); - } -} - -impl Visit for RequirementCalculartor { - noop_visit_type!(); - - weak!(visit_arrow_expr, ArrowExpr); - weak!(visit_function, Function); - weak!(visit_class_method, ClassMethod); - weak!(visit_private_method, PrivateMethod); - weak!(visit_method_prop, MethodProp); - - fn visit_export_named_specifier(&mut self, n: &ExportNamedSpecifier, _: &dyn Node) { - self.insert(n.orig.clone().into()); - } - - fn visit_pat(&mut self, pat: &Pat, _: &dyn Node) { - match pat { - Pat::Ident(i) => { - // We do not care about variables created by current statement. - if self.in_var_decl { - return; - } - self.insert(i.into()); - } - _ => {} - } - } - - fn visit_var_declarator(&mut self, var: &VarDeclarator, _: &dyn Node) { - let in_var_decl = self.in_var_decl; - self.in_var_decl = true; - - var.visit_children_with(self); - - self.in_var_decl = in_var_decl; - } - - fn visit_expr(&mut self, expr: &Expr, _: &dyn Node) { - let in_var_decl = self.in_var_decl; - self.in_var_decl = false; - - match expr { - Expr::Ident(i) => { - self.insert(i.into()); - } - _ => { - expr.visit_children_with(self); - } - } - - self.in_var_decl = in_var_decl; - } - - fn visit_prop(&mut self, prop: &Prop, _: &dyn Node) { - match prop { - Prop::Shorthand(i) => { - self.insert(i.into()); - } - _ => prop.visit_children_with(self), - } - } - - fn visit_member_expr(&mut self, e: &MemberExpr, _: &dyn Node) { - e.obj.visit_with(e as _, self); - - if e.computed { - e.prop.visit_with(e as _, self); - } - } -} diff --git a/bundler/src/bundler/mod.rs b/bundler/src/bundler/mod.rs index 9f8e7f7f9483..3c6c1b7168fb 100644 --- a/bundler/src/bundler/mod.rs +++ b/bundler/src/bundler/mod.rs @@ -12,6 +12,7 @@ mod finalize; mod helpers; mod import; mod load; +mod modules; mod optimize; mod scope; #[cfg(test)] @@ -87,6 +88,9 @@ where /// spans. synthesized_ctxt: SyntaxContext, + /// Used to mark a variable declaration as injected. + injected_ctxt: SyntaxContext, + scope: Scope, hook: Box, @@ -112,6 +116,8 @@ where log::debug!("Helper ctxt: {:?}", helper_ctxt); let synthesized_ctxt = SyntaxContext::empty().apply_mark(Mark::fresh(Mark::root())); log::debug!("Synthesized ctxt: {:?}", synthesized_ctxt); + let injected_ctxt = SyntaxContext::empty().apply_mark(Mark::fresh(Mark::root())); + log::debug!("Injected ctxt: {:?}", injected_ctxt); Bundler { config, @@ -122,6 +128,7 @@ where used_mark, _helper_ctxt: helper_ctxt, synthesized_ctxt, + injected_ctxt, scope: Default::default(), hook, } diff --git a/bundler/src/bundler/modules/mod.rs b/bundler/src/bundler/modules/mod.rs new file mode 100644 index 000000000000..8696ab8dda52 --- /dev/null +++ b/bundler/src/bundler/modules/mod.rs @@ -0,0 +1,242 @@ +use retain_mut::RetainMut; +use std::mem::take; +use swc_common::SyntaxContext; +use swc_common::DUMMY_SP; +use swc_ecma_ast::*; +use swc_ecma_visit::Fold; +use swc_ecma_visit::FoldWith; +use swc_ecma_visit::Visit; +use swc_ecma_visit::VisitMut; +use swc_ecma_visit::VisitMutWith; +use swc_ecma_visit::VisitWith; + +mod sort; +#[cfg(test)] +mod tests; +#[derive(Debug, Clone)] +pub struct Modules { + /// Indicates that a statement is injected. + /// + /// Note: This context shoulod be shared for a bundle. + pub(crate) injected_ctxt: SyntaxContext, + + // We will change this into `Vec`. + modules: Vec, + prepended: Vec, + injected: Vec, +} + +impl Modules { + pub fn empty(injected_ctxt: SyntaxContext) -> Self { + Self { + injected_ctxt, + modules: Default::default(), + prepended: Default::default(), + injected: Default::default(), + } + } + + pub fn from(module: Module, injected_ctxt: SyntaxContext) -> Self { + let mut ret = Self::empty(injected_ctxt); + ret.modules.push(module); + ret + } + + pub fn into_items(self) -> Vec { + // self.sort(); + // self.modules.pop().unwrap().body + self.prepended + .into_iter() + .chain(self.modules.into_iter().flat_map(|v| v.body)) + .chain(self.injected) + .collect() + } + + pub fn prepend_all(&mut self, mut other: Modules) { + other.prepended.append(&mut self.prepended); + other.modules.append(&mut self.modules); + other.injected.append(&mut self.injected); + *self = other; + } + + pub fn push_all(&mut self, item: Modules) { + self.prepended.extend(item.prepended); + self.modules.extend(item.modules); + self.injected.extend(item.injected); + } + + pub fn iter(&self) -> impl Iterator { + self.prepended + .iter() + .chain(self.modules.iter().flat_map(|m| m.body.iter())) + .chain(self.injected.iter()) + } + + pub fn iter_mut(&mut self) -> impl Iterator { + self.prepended + .iter_mut() + .chain(self.modules.iter_mut().flat_map(|m| m.body.iter_mut())) + .chain(self.injected.iter_mut()) + } + + pub fn map_any_items(&mut self, mut op: F) + where + F: FnMut(Vec) -> Vec, + { + self.prepended = op(take(&mut self.prepended)); + self.modules = take(&mut self.modules) + .into_iter() + .map(|mut m| { + let body = op(take(&mut m.body)); + + Module { body, ..m } + }) + .collect(); + self.injected = op(take(&mut self.injected)); + } + + pub fn map_items_mut(&mut self, mut op: F) + where + F: FnMut(&mut ModuleItem), + { + self.iter_mut().for_each(|item| { + op(item); + }) + } + + /// Insert a statement which dependency of can be analyzed statically. + pub fn inject_all(&mut self, items: impl IntoIterator) { + let injected_ctxt = self.injected_ctxt; + self.injected.extend(items.into_iter().map(|mut item| { + mark(&mut item, injected_ctxt); + item + })); + } + + /// Insert a statement which dependency of can be analyzed statically. + pub fn inject(&mut self, mut var: ModuleItem) { + mark(&mut var, self.injected_ctxt); + + self.injected.push(var) + } + + pub fn prepend(&mut self, item: ModuleItem) { + self.prepended.push(item) + } + + pub fn visit_mut_with(&mut self, v: &mut V) + where + V: VisitMut, + { + self.prepended.visit_mut_with(&mut *v); + for module in &mut self.modules { + module.visit_mut_with(&mut *v); + } + self.injected.visit_mut_with(&mut *v); + } + + pub fn fold_with(mut self, v: &mut V) -> Self + where + V: Fold, + { + self.prepended = self.prepended.fold_with(&mut *v); + self.modules = self + .modules + .into_iter() + .map(|m| m.fold_with(&mut *v)) + .collect(); + self.injected = self.injected.fold_with(&mut *v); + + self + } + + pub fn visit_with(&self, v: &mut V) + where + V: Visit, + { + self.prepended.visit_with(&Invalid { span: DUMMY_SP }, v); + self.modules + .iter() + .for_each(|m| m.visit_with(&Invalid { span: DUMMY_SP }, v)); + self.injected.visit_with(&Invalid { span: DUMMY_SP }, v); + } + + pub fn retain_mut(&mut self, mut op: F) + where + F: FnMut(&mut ModuleItem) -> bool, + { + self.prepended.retain_mut(&mut op); + for module in &mut self.modules { + module.body.retain_mut(&mut op); + } + self.injected.retain_mut(&mut op); + } +} + +impl From for Module { + fn from(modules: Modules) -> Self { + // TODO + Self { + span: DUMMY_SP, + body: modules.into_items(), + shebang: None, + } + } +} + +fn mark(item: &mut ModuleItem, ctxt: SyntaxContext) { + match item { + ModuleItem::ModuleDecl(item) => match item { + ModuleDecl::Import(ImportDecl { span, .. }) + | ModuleDecl::ExportDecl(ExportDecl { span, .. }) + | ModuleDecl::ExportNamed(NamedExport { span, .. }) + | ModuleDecl::ExportDefaultDecl(ExportDefaultDecl { span, .. }) + | ModuleDecl::ExportDefaultExpr(ExportDefaultExpr { span, .. }) + | ModuleDecl::ExportAll(ExportAll { span, .. }) + | ModuleDecl::TsImportEquals(TsImportEqualsDecl { span, .. }) + | ModuleDecl::TsExportAssignment(TsExportAssignment { span, .. }) + | ModuleDecl::TsNamespaceExport(TsNamespaceExportDecl { span, .. }) => { + span.ctxt = ctxt; + } + }, + ModuleItem::Stmt(stmt) => match stmt { + Stmt::Empty(_) => return, + Stmt::Block(BlockStmt { span, .. }) + | Stmt::Debugger(DebuggerStmt { span, .. }) + | Stmt::With(WithStmt { span, .. }) + | Stmt::Return(ReturnStmt { span, .. }) + | Stmt::Labeled(LabeledStmt { span, .. }) + | Stmt::Break(BreakStmt { span, .. }) + | Stmt::Continue(ContinueStmt { span, .. }) + | Stmt::If(IfStmt { span, .. }) + | Stmt::Switch(SwitchStmt { span, .. }) + | Stmt::Throw(ThrowStmt { span, .. }) + | Stmt::Try(TryStmt { span, .. }) + | Stmt::While(WhileStmt { span, .. }) + | Stmt::DoWhile(DoWhileStmt { span, .. }) + | Stmt::For(ForStmt { span, .. }) + | Stmt::ForIn(ForInStmt { span, .. }) + | Stmt::ForOf(ForOfStmt { span, .. }) + | Stmt::Expr(ExprStmt { span, .. }) => { + span.ctxt = ctxt; + } + Stmt::Decl(decl) => match decl { + Decl::Class(ClassDecl { + class: Class { span, .. }, + .. + }) + | Decl::Fn(FnDecl { + function: Function { span, .. }, + .. + }) + | Decl::Var(VarDecl { span, .. }) + | Decl::TsInterface(TsInterfaceDecl { span, .. }) + | Decl::TsTypeAlias(TsTypeAliasDecl { span, .. }) + | Decl::TsEnum(TsEnumDecl { span, .. }) + | Decl::TsModule(TsModuleDecl { span, .. }) => { + span.ctxt = ctxt; + } + }, + }, + } +} diff --git a/bundler/src/bundler/modules/sort.rs b/bundler/src/bundler/modules/sort.rs new file mode 100644 index 000000000000..b219a0f0c854 --- /dev/null +++ b/bundler/src/bundler/modules/sort.rs @@ -0,0 +1,724 @@ +use super::Modules; +use crate::{id::Id, util::MapWithMut}; +use indexmap::IndexSet; +use petgraph::EdgeDirection::Incoming as Dependants; +use petgraph::EdgeDirection::Outgoing as Dependancies; +use retain_mut::RetainMut; +use std::collections::HashMap; +use std::collections::HashSet; +use std::collections::VecDeque; +use std::iter::from_fn; +use std::mem::take; +use std::ops::Range; +use swc_atoms::js_word; +use swc_common::Spanned; +use swc_common::DUMMY_SP; +use swc_ecma_ast::*; +use swc_ecma_utils::find_ids; +use swc_ecma_visit::{noop_visit_type, Node, Visit, VisitWith}; + +type StmtDepGraph = self::graph::StmtDepGraph; + +mod graph; +/// Is dependancy between nodes hard? +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +enum Required { + /// Required to evaluate + Always, + + /// Maybe required to evaluate + Maybe, +} + +impl Modules { + pub fn sort(&mut self) { + let injected_ctxt = self.injected_ctxt; + { + let mut modules = take(&mut self.modules); + for module in &mut modules { + module.body.retain_mut(|item| { + let is_free = item.span().ctxt == injected_ctxt; + if is_free { + self.injected.push(item.take()); + false + } else { + true + } + }); + } + self.modules = modules; + } + + self.retain_mut(|item| match item { + ModuleItem::Stmt(Stmt::Empty(..)) => false, + _ => true, + }); + + let mut new = vec![]; + let mut graph = StmtDepGraph::default(); + + new.extend(self.prepended.drain(..)); + let mut module_starts = vec![]; + let mut same_module_ranges = vec![]; + for module in self.modules.drain(..) { + let start = new.len(); + let inner_len = module.body.len(); + if inner_len != 0 { + let end = start + inner_len; + + module_starts.push(start); + same_module_ranges.push(start..end); + } + + // for (inner_idx, item) in module.body.into_iter().enumerate() { + // let idx = new.len(); + // if inner_idx != 0 && inner_idx + 1 != inner_len && !new.is_empty() { + // graph.add_edge(idx - 1, idx, Required::Always); + // } + + // new.push(item); + // } + + new.extend(module.body); + } + let free = new.len()..(new.len() + self.injected.len()); + if cfg!(debug_assertions) { + for rng in &same_module_ranges { + for idx in rng.clone() { + assert!(!free.contains(&idx)); + } + } + } + new.extend(self.injected.drain(..)); + + let mut declared_by = HashMap::>::default(); + let mut uninitialized_ids = HashMap::::new(); + + for (idx, item) in new.iter().enumerate() { + graph.add_node(idx); + + // We start by calculating ids created by statements. Note that we don't need to + // analyze bodies of functions nor members of classes, because it's not + // evaludated until they are called. + + match item { + // We only check declarations because ids are created by declarations. + ModuleItem::ModuleDecl(ModuleDecl::ExportDecl(ExportDecl { decl, .. })) + | ModuleItem::Stmt(Stmt::Decl(decl)) => { + // + match decl { + Decl::Class(ClassDecl { ident, .. }) | Decl::Fn(FnDecl { ident, .. }) => { + // eprintln!("`{}` declares {:?}`", idx, Id::from(ident)); + declared_by.entry(Id::from(ident)).or_default().push(idx); + } + Decl::Var(vars) => { + for var in &vars.decls { + // + let ids: Vec = find_ids(&var.name); + for id in ids { + if var.init.is_none() { + uninitialized_ids.insert(id.clone(), idx); + } + + // eprintln!("`{}` declares {:?}`", idx, id); + declared_by.entry(id).or_default().push(idx); + } + } + } + _ => {} + } + } + _ => {} + } + + { + // Find extra initializations. + let mut v = PrototypeUsageFinter::default(); + item.visit_with(&Invalid { span: DUMMY_SP }, &mut v); + + for id in v.accessed { + if let Some(declarator_indexes) = declared_by.get(&id) { + for &declarator_index in declarator_indexes { + if declarator_index != idx { + graph.add_edge(idx, declarator_index, Required::Always); + // eprintln!("`{}` depends on `{}: {:?}`", idx, + // declarator_index, &id); + } + } + + // eprintln!("`{}` declares {:?}`", idx, id); + declared_by.entry(id).or_default().push(idx); + } + } + } + } + + // Handle uninitialized variables + // + // Compiled typescript enum is not initialized by declaration + // + // var Status; + // (function(Status){})(Status) + for (uninit_id, start_idx) in uninitialized_ids { + for (idx, item) in new.iter().enumerate().filter(|(idx, _)| *idx > start_idx) { + let mut finder = InitializerFinder { + ident: uninit_id.clone(), + found: false, + in_complex: false, + }; + item.visit_with(&Invalid { span: DUMMY_SP }, &mut finder); + if finder.found { + declared_by.entry(uninit_id).or_default().push(idx); + break; + } + } + } + + for (idx, item) in new.iter().enumerate() { + // We then calculate which ids a statement require to be executed. + // Again, we don't need to analyze non-top-level idents because they + // are not evaluated while lpoading module. + + let mut visitor = RequirementCalculartor::default(); + + item.visit_with(&Invalid { span: DUMMY_SP }, &mut visitor); + + for (id, kind) in visitor.required_ids { + if let Some(declarator_indexes) = declared_by.get(&id) { + for &declarator_index in declarator_indexes { + if declarator_index != idx { + graph.add_edge(idx, declarator_index, kind); + // eprintln!("`{}` depends on `{}: {:?}`", idx, declarator_index, id); + if cfg!(debug_assertions) { + let deps: Vec<_> = + graph.neighbors_directed(idx, Dependancies).collect(); + assert!(deps.contains(&declarator_index)); + } + } + } + } + } + } + + // Now graph contains enough information to sort statements. + let mut orders = vec![]; + orders.extend(iter( + &mut graph, + &same_module_ranges, + free.clone(), + &module_starts, + &new, + )); + // let mut delayed = HashSet::new(); + + assert_eq!(orders.len(), new.len()); + + let mut buf = Vec::with_capacity(new.len()); + for order in orders { + let stmt = new[order].take(); + buf.push(stmt) + } + + *self = Modules::from( + Module { + span: DUMMY_SP, + body: buf, + shebang: None, + }, + injected_ctxt, + ); + } +} + +fn iter<'a>( + graph: &'a mut StmtDepGraph, + same_module_ranges: &'a [Range], + free: Range, + module_starts: &[usize], + stmts: &'a [ModuleItem], +) -> impl 'a + Iterator { + let len = graph.node_count(); + + // dbg!(&same_module_ranges); + // dbg!(&free); + // dbg!(&module_starts); + + let mut moves = HashSet::new(); + let mut done = HashSet::new(); + let mut stack = VecDeque::new(); + stack.extend(module_starts.iter().copied()); + + for &start in module_starts { + let range = same_module_ranges + .iter() + .find(|range| range.contains(&start)) + .cloned(); + if let Some(range) = range { + for v in range { + stack.push_back(v); + } + } + } + for v in free.clone() { + stack.push_back(v); + } + + from_fn(move || { + if done.len() == len { + return None; + } + + // Check for first item. + 'main: while let Some(idx) = stack.pop_front() { + if done.contains(&idx) { + // eprintln!("Done: {}", idx); + continue; + } + // dbg!(idx); + // match &stmts[idx] { + // ModuleItem::Stmt(Stmt::Decl(Decl::Var(var))) => { + // let ids: Vec = find_ids(&var.decls); + // eprintln!("({}) Declare: `{:?}`", idx, ids); + // } + // ModuleItem::Stmt(Stmt::Decl(Decl::Class(cls))) => { + // eprintln!("({}) Declare: `{:?}`", idx, Id::from(&cls.ident)); + // } + // ModuleItem::Stmt(Stmt::Decl(Decl::Fn(f))) => { + // eprintln!("({}) Declare: `{:?}`", idx, Id::from(&f.ident)); + // } + // item => eprintln!("({}) Stmt: {:?}", idx, item), + // } + + let current_range = same_module_ranges + .iter() + .find(|range| range.contains(&idx)) + .cloned(); + + // dbg!(¤t_range); + + let can_ignore_deps = match &stmts[idx] { + ModuleItem::ModuleDecl(ModuleDecl::ExportDecl(ExportDecl { + decl: Decl::Fn(..), + .. + })) + | ModuleItem::Stmt(Stmt::Decl(Decl::Fn(..))) => true, + + ModuleItem::ModuleDecl(ModuleDecl::ExportDecl(ExportDecl { + decl: Decl::Class(cls), + .. + })) + | ModuleItem::Stmt(Stmt::Decl(Decl::Class(cls))) + if cls.class.super_class.is_none() => + { + true + } + + _ => false, + }; + let can_ignore_weak_deps = can_ignore_deps + || match &stmts[idx] { + ModuleItem::ModuleDecl(ModuleDecl::ExportDecl(ExportDecl { + decl: Decl::Class(..), + .. + })) + | ModuleItem::Stmt(Stmt::Decl(Decl::Class(..))) => true, + _ => false, + }; + + // We + { + let deps = graph + .neighbors_directed(idx, Dependancies) + .filter(|dep| { + // Exlcude emitted items + !done.contains(dep) + }) + .collect::>(); + + // dbg!(&deps); + + if !deps.is_empty() { + let mut deps_to_push = vec![]; + for dep in deps.iter().rev().copied() { + if deps_to_push.contains(&dep) { + continue; + } + + let can_ignore_dep = can_ignore_deps + || (can_ignore_weak_deps + && graph.edge_weight(idx, dep) == Some(Required::Maybe)); + + if can_ignore_dep { + if graph.has_a_path(dep, idx) { + // Just emit idx. + continue; + } + } + + if let Some(range) = ¤t_range { + if dep > idx && range.contains(&dep) { + continue; + } + } + + deps_to_push.push(dep); + } + + // dbg!(&deps_to_push); + + if !deps_to_push.is_empty() { + // We should check idx again after emitting dependencies. + stack.push_front(idx); + + for dep in deps_to_push { + // eprintln!("[Move]{} => {}; kind = dep", idx, dep); + stack.push_front(dep) + } + + continue; + } + } + } + let is_free = free.contains(&idx); + + // dbg!(is_free); + + if is_free { + let dependants = graph + .neighbors_directed(idx, Dependants) + .collect::>(); + + for dependant in dependants { + if !done.contains(&dependant) { + stack.push_front(dependant); + } + } + + graph.remove_node(idx); + done.insert(idx); + return Some(idx); + } + + let current_range = match current_range { + Some(v) => v, + None => { + let dependants = graph + .neighbors_directed(idx, Dependants) + .collect::>(); + + // dbg!(&dependants); + + for dependant in dependants { + if !done.contains(&dependant) { + stack.push_front(dependant); + } + } + + // It's not within a module, so explicit ordering is not required. + graph.remove_node(idx); + done.insert(idx); + return Some(idx); + } + }; + + // We should respect original order of statements within a module. + for preceding in current_range { + // We should select first statement in module which is not emitted yet. + if done.contains(&preceding) { + continue; + } + // dbg!(preceding); + if preceding == idx { + continue; + } + if preceding > idx { + break; + } + + if !moves.insert((idx, preceding)) { + // idx = preceding; + break; + } + + let dependants = graph + .neighbors_directed(idx, Dependants) + .collect::>(); + + // dbg!(&dependants); + + for dependant in dependants { + if !done.contains(&dependant) { + stack.push_front(dependant); + } + } + + // We found a preceding statement which is not emitted yet. + stack.push_front(idx); + stack.push_front(preceding); + continue 'main; + } + + graph.remove_node(idx); + done.insert(idx); + return Some(idx); + } + + None + }) +} + +/// Using prototype should be treated as an initialization. +#[derive(Default)] +struct PrototypeUsageFinter { + in_object_assign: bool, + in_rhs: bool, + accessed: HashSet, +} + +impl Visit for PrototypeUsageFinter { + fn visit_assign_expr(&mut self, e: &AssignExpr, _: &dyn Node) { + let old = self.in_rhs; + e.left.visit_with(e, self); + + self.in_rhs = true; + e.right.visit_with(e, self); + self.in_rhs = old; + } + + fn visit_pat(&mut self, e: &Pat, _: &dyn Node) { + let old = self.in_rhs; + self.in_rhs = false; + e.visit_children_with(self); + self.in_rhs = old; + } + + fn visit_call_expr(&mut self, e: &CallExpr, _: &dyn Node) { + match &e.callee { + ExprOrSuper::Super(_) => {} + ExprOrSuper::Expr(callee) => match &**callee { + Expr::Member(callee) => match &callee.obj { + ExprOrSuper::Expr(callee_obj) => match &**callee_obj { + Expr::Ident(Ident { + sym: js_word!("Object"), + .. + }) => match &*callee.prop { + Expr::Ident(Ident { sym: prop_sym, .. }) + if !callee.computed && *prop_sym == *"assign" => + { + let old = self.in_object_assign; + self.in_object_assign = true; + + e.args.visit_with(e, self); + self.in_object_assign = old; + + return; + } + _ => {} + }, + _ => {} + }, + ExprOrSuper::Super(_) => {} + }, + + _ => {} + }, + } + + e.visit_children_with(self); + } + + fn visit_member_expr(&mut self, e: &MemberExpr, _: &dyn Node) { + e.obj.visit_with(e, self); + + if e.computed { + e.prop.visit_with(e, self); + } + + if !self.in_rhs || self.in_object_assign { + match &e.obj { + ExprOrSuper::Expr(obj) => match &**obj { + Expr::Ident(obj) => match &*e.prop { + Expr::Ident(Ident { sym: prop_sym, .. }) + if !e.computed && *prop_sym == *"prototype" => + { + self.accessed.insert(obj.into()); + } + _ => {} + }, + _ => {} + }, + + _ => {} + } + } + } +} + +/// Finds usage of `ident` +struct InitializerFinder { + ident: Id, + found: bool, + in_complex: bool, +} + +impl Visit for InitializerFinder { + noop_visit_type!(); + + fn visit_pat(&mut self, pat: &Pat, _: &dyn Node) { + match pat { + Pat::Ident(i) if self.ident == *i => { + self.found = true; + } + + _ => { + pat.visit_children_with(self); + } + } + } + + fn visit_ident(&mut self, i: &Ident, _: &dyn Node) { + if self.in_complex && self.ident == *i { + self.found = true; + } + } + + fn visit_expr_or_spread(&mut self, node: &ExprOrSpread, _: &dyn Node) { + let in_complex = self.in_complex; + self.in_complex = true; + node.visit_children_with(self); + self.in_complex = in_complex; + } + + fn visit_member_expr(&mut self, e: &MemberExpr, _: &dyn Node) { + { + let in_complex = self.in_complex; + self.in_complex = true; + e.obj.visit_children_with(self); + self.in_complex = in_complex; + } + + if e.computed { + e.prop.visit_with(e as _, self); + } + } +} + +/// We do not care about variables created by current statement. +/// But we care about modifications. +#[derive(Default)] +struct RequirementCalculartor { + required_ids: IndexSet<(Id, Required)>, + + in_weak: bool, + in_var_decl: bool, + in_assign_lhs: bool, +} + +macro_rules! weak { + ($name:ident, $T:ty) => { + fn $name(&mut self, f: &$T, _: &dyn Node) { + let in_weak = self.in_weak; + self.in_weak = true; + + f.visit_children_with(self); + + self.in_weak = in_weak; + } + }; +} + +impl RequirementCalculartor { + fn insert(&mut self, i: Id) { + self.required_ids.insert(( + i, + if self.in_weak { + Required::Maybe + } else { + Required::Always + }, + )); + } +} + +impl Visit for RequirementCalculartor { + noop_visit_type!(); + + weak!(visit_arrow_expr, ArrowExpr); + weak!(visit_function, Function); + weak!(visit_class_method, ClassMethod); + weak!(visit_private_method, PrivateMethod); + weak!(visit_method_prop, MethodProp); + + fn visit_export_named_specifier(&mut self, n: &ExportNamedSpecifier, _: &dyn Node) { + self.insert(n.orig.clone().into()); + } + + fn visit_assign_expr(&mut self, e: &AssignExpr, _: &dyn Node) { + let old = self.in_assign_lhs; + + self.in_assign_lhs = true; + e.left.visit_with(e, self); + + self.in_assign_lhs = false; + e.right.visit_with(e, self); + + self.in_assign_lhs = old; + } + + fn visit_pat(&mut self, pat: &Pat, _: &dyn Node) { + match pat { + Pat::Ident(i) => { + // We do not care about variables created by current statement. + if self.in_var_decl && !self.in_assign_lhs { + return; + } + self.insert(i.into()); + } + _ => { + pat.visit_children_with(self); + } + } + } + + fn visit_var_declarator(&mut self, var: &VarDeclarator, _: &dyn Node) { + let in_var_decl = self.in_var_decl; + self.in_var_decl = true; + + var.visit_children_with(self); + + self.in_var_decl = in_var_decl; + } + + fn visit_expr(&mut self, expr: &Expr, _: &dyn Node) { + let in_var_decl = self.in_var_decl; + self.in_var_decl = false; + + match expr { + Expr::Ident(i) => { + self.insert(i.into()); + } + _ => { + expr.visit_children_with(self); + } + } + + self.in_var_decl = in_var_decl; + } + + fn visit_prop(&mut self, prop: &Prop, _: &dyn Node) { + match prop { + Prop::Shorthand(i) => { + self.insert(i.into()); + } + _ => prop.visit_children_with(self), + } + } + + fn visit_member_expr(&mut self, e: &MemberExpr, _: &dyn Node) { + e.obj.visit_with(e as _, self); + + if e.computed { + e.prop.visit_with(e as _, self); + } + } +} diff --git a/bundler/src/bundler/modules/sort/graph.rs b/bundler/src/bundler/modules/sort/graph.rs new file mode 100644 index 000000000000..4d338f9b5b8b --- /dev/null +++ b/bundler/src/bundler/modules/sort/graph.rs @@ -0,0 +1,73 @@ +use crate::bundler::modules::sort::Required; +use petgraph::graphmap::DiGraphMap; +use petgraph::EdgeDirection; +use petgraph::EdgeDirection::Incoming; +use petgraph::EdgeDirection::Outgoing; +use std::collections::HashSet; + +/// Used to debug petgraph. +#[derive(Debug, Default)] +pub(super) struct StmtDepGraph { + inner: DiGraphMap, + /// Read-optimized hashset which contains all direct dependencies and + /// transitive dependencies. + paths: HashSet<(usize, usize)>, +} + +impl StmtDepGraph { + pub fn node_count(&self) -> usize { + self.inner.node_count() + } + + pub fn add_node(&mut self, node: usize) { + self.inner.add_node(node); + } + + pub fn remove_node(&mut self, node: usize) { + self.inner.add_node(node); + } + + pub fn add_edge(&mut self, a: usize, b: usize, required: Required) { + self.inner.add_edge(a, b, required); + + self.insert_transitives(a, b); + } + + fn insert_transitives(&mut self, from: usize, to: usize) { + if !self.paths.insert((from, to)) { + return; + } + + for transitive in self + .inner + .neighbors_directed(to, Outgoing) + .collect::>() + { + self.insert_transitives(from, transitive) + } + + for transitive in self + .inner + .neighbors_directed(from, Incoming) + .collect::>() + { + self.insert_transitives(transitive, to) + } + } + + pub fn edge_weight(&self, a: usize, b: usize) -> Option { + self.inner.edge_weight(a, b).cloned() + } + + pub fn neighbors_directed<'a>( + &'a self, + start: usize, + dir: EdgeDirection, + ) -> impl 'a + Iterator { + self.inner.neighbors_directed(start, dir) + } + + pub fn has_a_path(&self, from: usize, to: usize) -> bool { + self.paths.contains(&(from, to)) + } +} diff --git a/bundler/src/bundler/modules/tests.rs b/bundler/src/bundler/modules/tests.rs new file mode 100644 index 000000000000..4fc937a4cbc9 --- /dev/null +++ b/bundler/src/bundler/modules/tests.rs @@ -0,0 +1,111 @@ +use crate::bundler::modules::Modules; +use crate::bundler::tests::suite; +use crate::debug::print_hygiene; +use swc_ecma_ast::Module; +use swc_ecma_utils::drop_span; + +fn assert_sorted(src: &[&str], res: &str) { + suite().run(|t| { + let mut modules = Modules::empty(t.bundler.injected_ctxt); + + for src in src { + let actual: Module = drop_span(t.parse(src)); + modules.push_all(Modules::from(actual, t.bundler.injected_ctxt)); + } + + modules.sort(); + let actual: Module = modules.into(); + + let expected = drop_span(t.parse(res)); + + if actual == expected { + return Ok(()); + } + + print_hygiene("actual", &t.cm, &actual); + print_hygiene("expected", &t.cm, &expected); + panic!() + }); +} + +#[test] +fn sort_001() { + assert_sorted( + &["_9[0] = 133;", "const _9 = new ByteArray(32);"], + " + const _9 = new ByteArray(32); + _9[0] = 133; + ", + ) +} + +#[test] +fn sort_002() { + assert_sorted( + &[ + " + const mod = (function(){ + const A = v; + }()); + ", + "const v = 5;", + ], + " + const v = 5; + const mod = (function(){ + const A = v; + }()); + ", + ) +} + +#[test] +fn sort_003() { + assert_sorted( + &[ + "class Constraint extends serialization.Serializable {}", + "const serialization = {};", + ], + " + const serialization = {}; + class Constraint extends serialization.Serializable { + } + ", + ); +} + +#[test] +fn sort_004() { + assert_sorted( + &["use(global);", "const global = getGlobal();"], + " + const global = getGlobal(); + use(global); + ", + ); +} + +#[test] +fn sort_005() { + assert_sorted( + &[ + "use(a);", + " + const a = new A(); + export const b = 1; + ", + " + use(b); + export class A {} + ", + ], + " + export class A { + } + const a = new A(); + export const b = 1; + use(b); + use(a); + ", + ); +} diff --git a/bundler/src/util.rs b/bundler/src/util.rs index 14e12da5ecb4..caed075e3ce5 100644 --- a/bundler/src/util.rs +++ b/bundler/src/util.rs @@ -6,9 +6,9 @@ use swc_ecma_utils::ident::IdentLike; use swc_ecma_visit::{noop_visit_mut_type, VisitMut}; pub(crate) trait VarDeclaratorExt: Into { - fn into_module_item(self, _name: &str) -> ModuleItem { + fn into_module_item(self, injected_ctxt: SyntaxContext, _name: &str) -> ModuleItem { ModuleItem::Stmt(Stmt::Decl(Decl::Var(VarDecl { - span: DUMMY_SP, + span: DUMMY_SP.with_ctxt(injected_ctxt), kind: VarDeclKind::Const, declare: false, decls: vec![ diff --git a/bundler/tests/deno/deno-8211-1/input/entry.ts b/bundler/tests/deno-exec/deno-8211/case2/entry.ts similarity index 63% rename from bundler/tests/deno/deno-8211-1/input/entry.ts rename to bundler/tests/deno-exec/deno-8211/case2/entry.ts index 2fa41c695a85..9be05e792ed8 100644 --- a/bundler/tests/deno/deno-8211-1/input/entry.ts +++ b/bundler/tests/deno-exec/deno-8211/case2/entry.ts @@ -1,3 +1,6 @@ +// https://github.com/denoland/deno/issues/8211#issuecomment-736498065 + import * as c from "https://deno.land/x/case@v2.1.0/mod.ts"; + const s = "one FINE day"; console.log("camel:", c.camelCase(s)); \ No newline at end of file diff --git a/bundler/tests/deno-exec/deno-8211/case3/deps.ts b/bundler/tests/deno-exec/deno-8211/case3/deps.ts new file mode 100644 index 000000000000..17b128f0167d --- /dev/null +++ b/bundler/tests/deno-exec/deno-8211/case3/deps.ts @@ -0,0 +1 @@ +export * as luxon from 'https://unpkg.com/luxon@1.25.0/src/luxon.js'; diff --git a/bundler/tests/deno-exec/deno-8211/case3/input.ts b/bundler/tests/deno-exec/deno-8211/case3/input.ts new file mode 100644 index 000000000000..a2275ab78d8a --- /dev/null +++ b/bundler/tests/deno-exec/deno-8211/case3/input.ts @@ -0,0 +1,5 @@ +import { luxon } from './deps.ts'; + +const date = new Date(); +const dt = luxon.DateTime.fromJSDate(date); +console.log(dt.toISO()); \ No newline at end of file diff --git a/bundler/tests/deno/deno-8545/input/entry.ts b/bundler/tests/deno-exec/deno-8545/entry.ts similarity index 84% rename from bundler/tests/deno/deno-8545/input/entry.ts rename to bundler/tests/deno-exec/deno-8545/entry.ts index 665d93d971f8..e6e2b331d1e0 100644 --- a/bundler/tests/deno/deno-8545/input/entry.ts +++ b/bundler/tests/deno-exec/deno-8545/entry.ts @@ -15,4 +15,7 @@ app.use(router.routes()); app.use(router.allowedMethods()); console.log(`Now listening on http://0.0.0.0:3000`); -await app.listen("0.0.0.0:3000"); \ No newline at end of file +setTimeout(() => { + Deno.exit(0); +}, 1000) +await app.listen("0.0.0.0:58545"); \ No newline at end of file diff --git a/bundler/tests/deno-exec/deno-8597/.case1/entry.ts b/bundler/tests/deno-exec/deno-8597/.case1/entry.ts new file mode 100644 index 000000000000..c03bb29a58a7 --- /dev/null +++ b/bundler/tests/deno-exec/deno-8597/.case1/entry.ts @@ -0,0 +1 @@ +import 'https://cdn.skypack.dev/-/@tensorflow/tfjs@v2.6.0-Nuh6TCs4sBWcdq0s5qn4/dist=es2020/@tensorflow/tfjs.js' \ No newline at end of file diff --git a/bundler/tests/deno-exec/deno-8597/.case4/entry.ts b/bundler/tests/deno-exec/deno-8597/.case4/entry.ts new file mode 100644 index 000000000000..7c0f76d4f497 --- /dev/null +++ b/bundler/tests/deno-exec/deno-8597/.case4/entry.ts @@ -0,0 +1 @@ +import './tfjs' \ No newline at end of file diff --git a/bundler/tests/deno-exec/deno-8597/.case4/tfjs.ts b/bundler/tests/deno-exec/deno-8597/.case4/tfjs.ts new file mode 100644 index 000000000000..12076a84b7bf --- /dev/null +++ b/bundler/tests/deno-exec/deno-8597/.case4/tfjs.ts @@ -0,0 +1,6 @@ +import { version_core } from "https://cdn.skypack.dev/-/@tensorflow/tfjs-core@v2.6.0-JX8lR1C58ILDYMLTVvxc/dist=es2020/@tensorflow/tfjs-core.js"; +export * from "https://cdn.skypack.dev/-/@tensorflow/tfjs-core@v2.6.0-JX8lR1C58ILDYMLTVvxc/dist=es2020/@tensorflow/tfjs-core.js"; +export * from "https://cdn.skypack.dev/-/@tensorflow/tfjs-layers@v2.6.0-CoxcCXNxQxzq0ZkceiB7/dist=es2020/@tensorflow/tfjs-layers.js"; +export * from "https://cdn.skypack.dev/-/@tensorflow/tfjs-converter@v2.6.0-PVxqoUi9WvmXRY4ebp1A/dist=es2020/@tensorflow/tfjs-converter.js"; +import * as tfjsData from "https://cdn.skypack.dev/-/@tensorflow/tfjs-data@v2.6.0-bGWyCMdgRZzRuv3XEODN/dist=es2020/@tensorflow/tfjs-data.js"; +export { tfjsData as data }; diff --git a/bundler/tests/deno-exec/deno-8597/.case5/entry.ts b/bundler/tests/deno-exec/deno-8597/.case5/entry.ts new file mode 100644 index 000000000000..7c0f76d4f497 --- /dev/null +++ b/bundler/tests/deno-exec/deno-8597/.case5/entry.ts @@ -0,0 +1 @@ +import './tfjs' \ No newline at end of file diff --git a/bundler/tests/deno-exec/deno-8597/.case5/tfjs.ts b/bundler/tests/deno-exec/deno-8597/.case5/tfjs.ts new file mode 100644 index 000000000000..82926c4a2ded --- /dev/null +++ b/bundler/tests/deno-exec/deno-8597/.case5/tfjs.ts @@ -0,0 +1,4 @@ +export * from "https://cdn.skypack.dev/-/@tensorflow/tfjs-core@v2.6.0-JX8lR1C58ILDYMLTVvxc/dist=es2020/@tensorflow/tfjs-core.js"; +export * from "https://cdn.skypack.dev/-/@tensorflow/tfjs-layers@v2.6.0-CoxcCXNxQxzq0ZkceiB7/dist=es2020/@tensorflow/tfjs-layers.js"; +import * as tfjsData from "https://cdn.skypack.dev/-/@tensorflow/tfjs-data@v2.6.0-bGWyCMdgRZzRuv3XEODN/dist=es2020/@tensorflow/tfjs-data.js"; +export { tfjsData as data }; diff --git a/bundler/tests/deno-exec/deno-8597/case2/core.ts b/bundler/tests/deno-exec/deno-8597/case2/core.ts new file mode 100644 index 000000000000..b47ae3230ff1 --- /dev/null +++ b/bundler/tests/deno-exec/deno-8597/case2/core.ts @@ -0,0 +1,8 @@ +class Class { } + +var serialization = Object.freeze({ + __proto__: null, + Class, +}); + +export { serialization }; \ No newline at end of file diff --git a/bundler/tests/deno-exec/deno-8597/case2/entry.ts b/bundler/tests/deno-exec/deno-8597/case2/entry.ts new file mode 100644 index 000000000000..acb1b4153e21 --- /dev/null +++ b/bundler/tests/deno-exec/deno-8597/case2/entry.ts @@ -0,0 +1 @@ +import './tfjs' diff --git a/bundler/tests/deno-exec/deno-8597/case2/layers.ts b/bundler/tests/deno-exec/deno-8597/case2/layers.ts new file mode 100644 index 000000000000..4dd3c393f832 --- /dev/null +++ b/bundler/tests/deno-exec/deno-8597/case2/layers.ts @@ -0,0 +1,7 @@ +import { serialization } from './core'; + +export class Constraint extends serialization.Class { + getConfig() { + return {}; + } +} \ No newline at end of file diff --git a/bundler/tests/deno-exec/deno-8597/case2/tfjs.ts b/bundler/tests/deno-exec/deno-8597/case2/tfjs.ts new file mode 100644 index 000000000000..ea6cddd8a3d1 --- /dev/null +++ b/bundler/tests/deno-exec/deno-8597/case2/tfjs.ts @@ -0,0 +1,2 @@ +export * from "./core"; +export * from "./layers"; \ No newline at end of file diff --git a/bundler/tests/deno-exec/deno-8597/case3/entry.ts b/bundler/tests/deno-exec/deno-8597/case3/entry.ts new file mode 100644 index 000000000000..7c0f76d4f497 --- /dev/null +++ b/bundler/tests/deno-exec/deno-8597/case3/entry.ts @@ -0,0 +1 @@ +import './tfjs' \ No newline at end of file diff --git a/bundler/tests/deno-exec/deno-8597/case3/tfjs.ts b/bundler/tests/deno-exec/deno-8597/case3/tfjs.ts new file mode 100644 index 000000000000..c52d18c11b9b --- /dev/null +++ b/bundler/tests/deno-exec/deno-8597/case3/tfjs.ts @@ -0,0 +1,5 @@ +import { version_core } from "https://cdn.skypack.dev//-/@tensorflow/tfjs-core@v2.6.0-JX8lR1C58ILDYMLTVvxc/dist=es2020/@tensorflow/tfjs-core.js"; +export * from "https://cdn.skypack.dev//-/@tensorflow/tfjs-core@v2.6.0-JX8lR1C58ILDYMLTVvxc/dist=es2020/@tensorflow/tfjs-core.js"; +export * from "https://cdn.skypack.dev//-/@tensorflow/tfjs-layers@v2.6.0-CoxcCXNxQxzq0ZkceiB7/dist=es2020/@tensorflow/tfjs-layers.js"; +export * from "https://cdn.skypack.dev//-/@tensorflow/tfjs-converter@v2.6.0-PVxqoUi9WvmXRY4ebp1A/dist=es2020/@tensorflow/tfjs-converter.js"; + diff --git a/bundler/tests/deno.rs b/bundler/tests/deno.rs index 04a8bdae7479..fcda3f97a166 100644 --- a/bundler/tests/deno.rs +++ b/bundler/tests/deno.rs @@ -5,6 +5,8 @@ use self::common::*; use anyhow::Error; +use ntest::timeout; +use std::path::PathBuf; use std::{ collections::{HashMap, HashSet}, env, @@ -24,6 +26,7 @@ use testing::assert_eq; mod common; #[test] +#[timeout(60000)] fn oak_6_3_1_application() { run( "https://deno.land/x/oak@v6.3.1/application.ts", @@ -36,6 +39,7 @@ fn oak_6_3_1_application() { } #[test] +#[timeout(60000)] fn oak_6_3_1_mod() { run( "https://deno.land/x/oak@v6.3.1/mod.ts", @@ -65,6 +69,7 @@ fn oak_6_3_1_mod() { } #[test] +#[timeout(60000)] fn std_0_74_0_http_server() { run( "https://deno.land/std@0.74.0/http/server.ts", @@ -81,12 +86,14 @@ fn std_0_74_0_http_server() { } #[test] +#[timeout(60000)] #[ignore = "Does not finish by default"] fn oak_6_3_1_example_server() { run("https://deno.land/x/oak@v6.3.1/examples/server.ts", &[]); } #[test] +#[timeout(60000)] #[ignore = "Does not finish by default"] fn oak_6_3_1_example_sse_server() { run("https://deno.land/x/oak@v6.3.1/examples/sseServer.ts", &[]); @@ -94,6 +101,7 @@ fn oak_6_3_1_example_sse_server() { #[test] #[ignore = "Will be fixed by #1264"] +#[timeout(60000)] fn deno_8188_full() { run( "https://raw.githubusercontent.com/nats-io/nats.ws/master/src/mod.ts", @@ -153,6 +161,7 @@ fn deno_8188_full() { } #[test] +#[timeout(60000)] fn deno_8188_01() { run( "https://raw.githubusercontent.com/nats-io/nats.deno/v1.0.0-12/nats-base-client/nkeys.ts", @@ -161,6 +170,7 @@ fn deno_8188_01() { } #[test] +#[timeout(60000)] fn deno_8188_02() { run( "https://raw.githubusercontent.com/nats-io/nkeys.js/v1.0.0-7/modules/esm/mod.ts", @@ -180,6 +190,7 @@ fn deno_8188_02() { } #[test] +#[timeout(60000)] fn deno_8188_03() { run( "https://raw.githubusercontent.com/nats-io/nkeys.js/v1.0.0-7/modules/esm/deps.ts", @@ -188,6 +199,7 @@ fn deno_8188_03() { } #[test] +#[timeout(60000)] fn deno_8189() { run( "https://deno.land/x/lz4/mod.ts", @@ -196,7 +208,8 @@ fn deno_8189() { } #[test] -fn deno_8211() { +#[timeout(60000)] +fn deno_8211_1() { run( "https://unpkg.com/luxon@1.25.0/src/luxon.js", &[ @@ -214,13 +227,8 @@ fn deno_8211() { ); } -/// https://github.com/denoland/deno/issues/8211#issuecomment-736498065 -#[test] -fn deno_8211_1() { - run("tests/deno/deno-8211-1/input/entry.ts", &[]); -} - #[test] +#[timeout(60000)] fn deno_8246() { run("https://raw.githubusercontent.com/nats-io/nats.deno/v1.0.0-11/nats-base-client/internal_mod.ts",&[ "NatsConnectionImpl", @@ -276,48 +284,57 @@ fn deno_8246() { } #[test] +#[timeout(60000)] #[ignore = "document is not defined when I use deno run"] fn deno_6802() { run("tests/deno/issue-6802/input.tsx", &[]); } #[test] +#[timeout(60000)] fn deno_8314_1() { run("tests/deno/issue-8314/input.ts", &[]); } #[test] +#[timeout(60000)] fn deno_8314_2() { run("https://dev.jspm.io/ngraph.graph", &["default"]); } #[test] +#[timeout(60000)] fn deno_8302() { run("tests/deno/issue-8302/input.ts", &["DB", "Empty", "Status"]); } #[test] +#[timeout(60000)] fn deno_8399_1() { run("tests/deno/issue-8399-1/input.ts", &[]); } #[test] +#[timeout(60000)] fn deno_8399_2() { run("tests/deno/issue-8399-2/input.ts", &[]); } #[test] +#[timeout(60000)] fn deno_8486_1() { run("tests/deno/issue-8486-1/input.ts", &["myCLI"]); } #[test] +#[timeout(60000)] fn deno_7288_1() { run("tests/deno/deno-7288-1/input.ts", &[]); } #[test] #[ignore = "Will be fixed by #1264"] +#[timeout(60000)] fn deno_8481_1() { run( "https://raw.githubusercontent.com/nats-io/nats.ws/master/src/mod.ts", @@ -378,22 +395,20 @@ fn deno_8481_1() { #[test] #[ignore = "Will be fixed by #1264"] +#[timeout(60000)] fn deno_8530() { run("tests/deno/deno-8530/input/entry.ts", &[]) } #[test] -#[ignore] -fn deno_8545() { - run("tests/deno/deno-8545/input/entry.ts", &[]) -} - -#[test] +#[timeout(60000)] fn deno_8573() { run("tests/deno/deno-8573/entry.ts", &[]) } + +/// Timeout is long because tfjs is so large. #[test] -#[ignore = "Will be fixed by #1268"] +#[ignore = "Not implemented yet"] fn deno_8597() { run( "https://cdn.skypack.dev/@tensorflow/tfjs@2.6.0", @@ -867,17 +882,20 @@ fn deno_8597() { #[test] #[ignore = "Will be fixed by #1264"] +#[timeout(60000)] fn deno_8620() { run("tests/deno/deno-8620/entry.ts", &[]) } #[test] +#[timeout(60000)] #[ignore = "Requires newer version of deno"] fn deno_8627() { run("tests/deno/deno-8627/input.ts", &[]) } #[test] +#[timeout(60000)] fn merging_order_01() { run( "https://deno.land/x/oak@v6.3.1/multipart.ts", @@ -886,6 +904,7 @@ fn merging_order_01() { } #[test] +#[timeout(60000)] fn reexport_01() { run( "https://raw.githubusercontent.com/aricart/tweetnacl-deno/import-type-fixes/src/nacl.ts", @@ -1127,3 +1146,31 @@ impl Visit for ExportCollector { } } } + +#[testing::fixture("deno-exec/**/entry.ts")] +fn exec(input: PathBuf) { + let dir = tempfile::tempdir().expect("failed to crate temp file"); + let path = dir.path().join("main.js"); + println!("{}", path.display()); + + let src = bundle(&input.to_string_lossy()); + write(&path, &src).unwrap(); + + if env::var("CI").is_ok() { + return; + } + + let output = Command::new("deno") + .arg("run") + .arg("--allow-all") + .arg("--no-check") + .arg(&path) + .stdout(Stdio::inherit()) + .stderr(Stdio::inherit()) + .status() + .unwrap(); + + std::mem::forget(dir); + + assert!(output.success()); +} diff --git a/bundler/tests/fixture/.deno-8211-3/input/deps.ts b/bundler/tests/fixture/.deno-8211-3/input/deps.ts new file mode 100644 index 000000000000..17b128f0167d --- /dev/null +++ b/bundler/tests/fixture/.deno-8211-3/input/deps.ts @@ -0,0 +1 @@ +export * as luxon from 'https://unpkg.com/luxon@1.25.0/src/luxon.js'; diff --git a/bundler/tests/fixture/.deno-8211-3/input/entry.ts b/bundler/tests/fixture/.deno-8211-3/input/entry.ts new file mode 100644 index 000000000000..a2275ab78d8a --- /dev/null +++ b/bundler/tests/fixture/.deno-8211-3/input/entry.ts @@ -0,0 +1,5 @@ +import { luxon } from './deps.ts'; + +const date = new Date(); +const dt = luxon.DateTime.fromJSDate(date); +console.log(dt.toISO()); \ No newline at end of file diff --git a/bundler/tests/fixture/circular-imports/complex-export/output/entry.js b/bundler/tests/fixture/circular-imports/complex-export/output/entry.js index 624fbd6620ce..b7cbaba5d661 100644 --- a/bundler/tests/fixture/circular-imports/complex-export/output/entry.js +++ b/bundler/tests/fixture/circular-imports/complex-export/output/entry.js @@ -1,6 +1,6 @@ +const c2 = 'c'; const a2 = 'a'; const a1 = a2; -const c2 = 'c'; -const c1 = c2; export { a1 as a }; +const c1 = c2; export { c1 as c }; diff --git a/bundler/tests/fixture/circular-imports/complex-import/output/entry.js b/bundler/tests/fixture/circular-imports/complex-import/output/entry.js index 9a8452eaea02..a8c9739643f5 100644 --- a/bundler/tests/fixture/circular-imports/complex-import/output/entry.js +++ b/bundler/tests/fixture/circular-imports/complex-import/output/entry.js @@ -1,7 +1,7 @@ +const c = 'c'; const a = 'a'; const a1 = a; -const c = 'c'; -const c1 = c; const a2 = a1; +const c1 = c; const c2 = c1; console.log(a2, c2); diff --git a/bundler/tests/fixture/circular-imports/simple/output/entry.js b/bundler/tests/fixture/circular-imports/simple/output/entry.js index a021f4da9dd6..f8a454b8d56b 100644 --- a/bundler/tests/fixture/circular-imports/simple/output/entry.js +++ b/bundler/tests/fixture/circular-imports/simple/output/entry.js @@ -1,7 +1,7 @@ +const b = 2; const a = 1; const a1 = a; -const b = 2; -const b1 = b; const a2 = a1; +const b1 = b; const b2 = b1; console.log(a2, b2); diff --git a/bundler/tests/fixture/deno-8148/test-4/output/entry.ts b/bundler/tests/fixture/deno-8148/test-4/output/entry.ts index 63dba7f3a0ad..78f95d293cf8 100644 --- a/bundler/tests/fixture/deno-8148/test-4/output/entry.ts +++ b/bundler/tests/fixture/deno-8148/test-4/output/entry.ts @@ -4,11 +4,11 @@ const __default = { "c": "c" }; const foo = __default; +console.log(foo); const __default1 = { "a": "a1", "b": "b1", "c": "c1" }; const foo1 = __default1; -console.log(foo); console.log(foo1); diff --git a/bundler/tests/fixture/deno-8188-1/output/entry.ts b/bundler/tests/fixture/deno-8188-1/output/entry.ts index d8a8ab1e4fdf..eb1d595f32f6 100644 --- a/bundler/tests/fixture/deno-8188-1/output/entry.ts +++ b/bundler/tests/fixture/deno-8188-1/output/entry.ts @@ -1,13 +1,13 @@ const a = 1; const a1 = a; const a2 = a1; -const b = a2 + 1; const a3 = a1; -const c = a3 + 2; -const a4 = a; -const b1 = b; +const b = a3 + 1; +const c = a2 + 2; const c1 = c; +const c2 = c1; +const a4 = a; const a5 = a4; +const b1 = b; const b2 = b1; -const c2 = c1; console.log(a5, b2, c2); diff --git a/bundler/tests/fixture/deno-8188-2/output/entry.ts b/bundler/tests/fixture/deno-8188-2/output/entry.ts index 218e3bb5e50b..41e109bd8219 100644 --- a/bundler/tests/fixture/deno-8188-2/output/entry.ts +++ b/bundler/tests/fixture/deno-8188-2/output/entry.ts @@ -1,27 +1,27 @@ const a = 1; const a1 = a; const a2 = a1; -const b = a2 + 1; -const b1 = b; const a3 = a1; -const c = a3 + 2; -const a4 = a; -const b2 = b; -const c1 = c; -const a5 = a4; -const b3 = b2; -const c2 = c1; -const a6 = a1; -const b4 = b1; -const a7 = a1; -const user1 = a7 + 1; -console.log('user 1', user1); +const a4 = a1; +const b = a4 + 1; +const a5 = a1; +const c = a5 + 2; +const user1 = a3 + 1; const user11 = user1; const user12 = user11; -const user2 = user12 + a6 + b4; -console.log('user 2', user2); +const b1 = b; +const b2 = b1; +console.log('user 1', user1); +const user2 = user12 + a2 + b2; const user21 = user2; const user22 = user21; -console.log(a5, b3, c2); const user13 = user11; +console.log('user 2', user2); +const a6 = a; +const a7 = a6; +const b3 = b; +const b4 = b3; +const c1 = c; +const c2 = c1; +console.log(a7, b4, c2); console.log(user13, user22); diff --git a/bundler/tests/fixture/deno-8188-3/output/entry.ts b/bundler/tests/fixture/deno-8188-3/output/entry.ts index 89504d4273e0..6094016d8de5 100644 --- a/bundler/tests/fixture/deno-8188-3/output/entry.ts +++ b/bundler/tests/fixture/deno-8188-3/output/entry.ts @@ -1,7 +1,7 @@ const a = 1; const a1 = a; -const a2 = a; -const b = a1; -const a3 = a2; +const a2 = a1; +const a3 = a; +const b = a3; const b1 = b; -console.log(a3, b1); +console.log(a2, b1); diff --git a/bundler/tests/fixture/deno-8211-1/output/entry.ts b/bundler/tests/fixture/deno-8211-1/output/entry.ts index 011f34eb7fa9..530dea5f4706 100644 --- a/bundler/tests/fixture/deno-8211-1/output/entry.ts +++ b/bundler/tests/fixture/deno-8211-1/output/entry.ts @@ -4,7 +4,7 @@ const __default = Zone3; const Zone1 = __default; class FixedOffsetZone2 extends Zone1 { } +const Zone2 = __default; const __default1 = FixedOffsetZone2; const FixedOffsetZone1 = __default1; -const Zone2 = __default; export { Zone2 as Zone, FixedOffsetZone1 as FixedOffsetZone }; diff --git a/bundler/tests/fixture/deno-8211-2/output/entry.ts b/bundler/tests/fixture/deno-8211-2/output/entry.ts index fe9fe00c3875..c55de61d1d2f 100644 --- a/bundler/tests/fixture/deno-8211-2/output/entry.ts +++ b/bundler/tests/fixture/deno-8211-2/output/entry.ts @@ -4,6 +4,7 @@ const __default = Zone3; const Zone1 = __default; class FixedOffsetZone3 extends Zone1 { } +const Zone2 = __default; const __default1 = FixedOffsetZone3; const FixedOffsetZone1 = __default1; class Info2 { @@ -11,8 +12,7 @@ class Info2 { console.log(FixedOffsetZone1); } } +const FixedOffsetZone2 = __default1; const __default2 = Info2; const Info1 = __default2; -const Zone2 = __default; -const FixedOffsetZone2 = __default1; export { Zone2 as Zone, Info1 as Info, FixedOffsetZone2 as FixedOffsetZone }; diff --git a/bundler/tests/fixture/deno-8302-1/output/entry.js b/bundler/tests/fixture/deno-8302-1/output/entry.js index 563ee71639b8..e52bac702b54 100644 --- a/bundler/tests/fixture/deno-8302-1/output/entry.js +++ b/bundler/tests/fixture/deno-8302-1/output/entry.js @@ -1,20 +1,20 @@ const a = 1; const a1 = a; -const b = 2; -const b1 = b; const a2 = a1; -const c = 3; +const a3 = a1; class A { } const A1 = A; -const c1 = c; -const b2 = b1; const A2 = A1; -console.log(b2, A2); -const c2 = c1; const A3 = A1; -console.log(c2, A3); +console.log(a3, A3); +const c = 3; +const c1 = c; +const c2 = c1; const A4 = A1; -console.log(a2, A4); -const a3 = a1; -console.log(a3); +console.log(c2, A4); +const b = 2; +const b1 = b; +const b2 = b1; +console.log(b2, A2); +console.log(a2); diff --git a/bundler/tests/fixture/deno-8545/simplified-1/input/deps.ts b/bundler/tests/fixture/deno-8545/simplified-1/input/deps.ts new file mode 100644 index 000000000000..44d9ea8f0e9e --- /dev/null +++ b/bundler/tests/fixture/deno-8545/simplified-1/input/deps.ts @@ -0,0 +1,2 @@ +export class Application { } +export class Router { } \ No newline at end of file diff --git a/bundler/tests/fixture/deno-8545/simplified-1/input/entry.ts b/bundler/tests/fixture/deno-8545/simplified-1/input/entry.ts new file mode 100644 index 000000000000..4ebc3e186b68 --- /dev/null +++ b/bundler/tests/fixture/deno-8545/simplified-1/input/entry.ts @@ -0,0 +1,18 @@ +import { Application, Router } from "./deps.ts"; + +const app = new Application(); +const router = new Router(); + +router.get("/", (ctx) => { + ctx.response.body = "Index Page"; +}); + +router.get("/users", (ctx) => { + ctx.response.body = "Users Page"; +}); + +app.use(router.routes()); +app.use(router.allowedMethods()); + +console.log(`Now listening on http://0.0.0.0:3000`); +await app.listen("0.0.0.0:3000"); \ No newline at end of file diff --git a/bundler/tests/fixture/deno-8545/simplified-1/output/entry.ts b/bundler/tests/fixture/deno-8545/simplified-1/output/entry.ts new file mode 100644 index 000000000000..c940b4ad9f25 --- /dev/null +++ b/bundler/tests/fixture/deno-8545/simplified-1/output/entry.ts @@ -0,0 +1,20 @@ +class Application { +} +class Router { +} +const Application1 = Application; +const Application2 = Application1; +const app = new Application2(); +const Router1 = Router; +const Router2 = Router1; +const router = new Router2(); +router.get("/", (ctx)=>{ + ctx.response.body = "Index Page"; +}); +router.get("/users", (ctx)=>{ + ctx.response.body = "Users Page"; +}); +app.use(router.routes()); +app.use(router.allowedMethods()); +console.log(`Now listening on http://0.0.0.0:3000`); +await app.listen("0.0.0.0:3000"); diff --git a/bundler/tests/fixture/deno-8574/output/entry.ts b/bundler/tests/fixture/deno-8574/output/entry.ts index baf6caccb78f..75ab51564ddc 100644 --- a/bundler/tests/fixture/deno-8574/output/entry.ts +++ b/bundler/tests/fixture/deno-8574/output/entry.ts @@ -9,6 +9,41 @@ function lowercaseKeys(object) { }, { }); } +function isObject(o) { + return Object.prototype.toString.call(o) === "[object Object]"; +} +function isPlainObject(o) { + var ctor, prot; + if (isObject(o) === false) return false; + ctor = o.constructor; + if (ctor === void 0) return true; + prot = ctor.prototype; + if (isObject(prot) === false) return false; + if (prot.hasOwnProperty("isPrototypeOf") === false) { + return false; + } + return true; +} +const isPlainObject1 = isPlainObject; +const isPlainObject2 = isPlainObject1; +const isPlainObject3 = isPlainObject1; +function mergeDeep(defaults, options) { + const result = Object.assign({ + }, defaults); + Object.keys(options).forEach((key)=>{ + if (isPlainObject3(options[key])) { + if (!(key in defaults)) Object.assign(result, { + [key]: options[key] + }); + else result[key] = mergeDeep(defaults[key], options[key]); + } else { + Object.assign(result, { + [key]: options[key] + }); + } + }); + return result; +} function removeUndefinedProperties(obj) { for(const key in obj){ if (obj[key] === void 0) { @@ -17,6 +52,32 @@ function removeUndefinedProperties(obj) { } return obj; } +function merge(defaults, route, options) { + if (typeof route === "string") { + let [method, url] = route.split(" "); + options = Object.assign(url ? { + method, + url + } : { + url: method + }, options); + } else { + options = Object.assign({ + }, route); + } + options.headers = lowercaseKeys(options.headers); + removeUndefinedProperties(options); + removeUndefinedProperties(options.headers); + const mergedOptions = mergeDeep(defaults || { + }, options); + if (defaults && defaults.mediaType.previews.length) { + mergedOptions.mediaType.previews = defaults.mediaType.previews.filter((preview)=>!mergedOptions.mediaType.previews.includes(preview) + ).concat(mergedOptions.mediaType.previews); + } + mergedOptions.mediaType.previews = mergedOptions.mediaType.previews.map((preview)=>preview.replace(/-preview/, "") + ); + return mergedOptions; +} function addQueryParameters(url, parameters) { const separator = /\?/.test(url) ? "&" : "?"; const names = Object.keys(parameters); @@ -133,6 +194,11 @@ function getValues(context, operator, key, modifier) { } return result; } +function parseUrl(template) { + return { + expand: expand.bind(null, template) + }; +} function expand(template, context) { var operators = [ "+", @@ -171,30 +237,97 @@ function expand(template, context) { } }); } +function parse(options) { + let method = options.method.toUpperCase(); + let url = (options.url || "/").replace(/:([a-z]\w+)/g, "{$1}"); + let headers = Object.assign({ + }, options.headers); + let body; + let parameters = omit(options, [ + "method", + "baseUrl", + "url", + "headers", + "request", + "mediaType" + ]); + const urlVariableNames = extractUrlVariableNames(url); + url = parseUrl(url).expand(parameters); + if (!/^http/.test(url)) { + url = options.baseUrl + url; + } + const omittedParameters = Object.keys(options).filter((option)=>urlVariableNames.includes(option) + ).concat("baseUrl"); + const remainingParameters = omit(parameters, omittedParameters); + const isBinaryRequest = /application\/octet-stream/i.test(headers.accept); + if (!isBinaryRequest) { + if (options.mediaType.format) { + headers.accept = headers.accept.split(/,/).map((preview)=>preview.replace(/application\/vnd(\.\w+)(\.v3)?(\.\w+)?(\+json)?$/, `application/vnd$1$2.${options.mediaType.format}`) + ).join(","); + } + if (options.mediaType.previews.length) { + const previewsFromAcceptHeader = headers.accept.match(/[\w-]+(?=-preview)/g) || []; + headers.accept = previewsFromAcceptHeader.concat(options.mediaType.previews).map((preview)=>{ + const format = options.mediaType.format ? `.${options.mediaType.format}` : "+json"; + return `application/vnd.github.${preview}-preview${format}`; + }).join(","); + } + } + if ([ + "GET", + "HEAD" + ].includes(method)) { + url = addQueryParameters(url, remainingParameters); + } else { + if ("data" in remainingParameters) { + body = remainingParameters.data; + } else { + if (Object.keys(remainingParameters).length) { + body = remainingParameters; + } else { + headers["content-length"] = 0; + } + } + } + if (!headers["content-type"] && typeof body !== "undefined") { + headers["content-type"] = "application/json; charset=utf-8"; + } + if ([ + "PATCH", + "PUT" + ].includes(method) && typeof body === "undefined") { + body = ""; + } + return Object.assign({ + method, + url, + headers + }, typeof body !== "undefined" ? { + body + } : null, options.request ? { + request: options.request + } : null); +} +function endpointWithDefaults(defaults, route, options) { + return parse(merge(defaults, route, options)); +} +function withDefaults(oldDefaults, newDefaults) { + const DEFAULTS2 = merge(oldDefaults, newDefaults); + const endpoint2 = endpointWithDefaults.bind(null, DEFAULTS2); + return Object.assign(endpoint2, { + DEFAULTS: DEFAULTS2, + defaults: withDefaults.bind(null, DEFAULTS2), + merge: merge.bind(null, DEFAULTS2), + parse + }); +} const VERSION = "6.0.10"; +var queue = []; +var draining = false; function defaultSetTimout() { throw new Error("setTimeout has not been defined"); } -function defaultClearTimeout() { - throw new Error("clearTimeout has not been defined"); -} var cachedSetTimeout = defaultSetTimout; -var cachedClearTimeout = defaultClearTimeout; -var globalContext; -if (typeof window !== "undefined") { - globalContext = window; -} else if (typeof self !== "undefined") { - globalContext = self; -} else { - globalContext = { - }; -} -if (typeof globalContext.setTimeout === "function") { - cachedSetTimeout = setTimeout; -} -if (typeof globalContext.clearTimeout === "function") { - cachedClearTimeout = clearTimeout; -} function runTimeout(fun) { if (cachedSetTimeout === setTimeout) { return setTimeout(fun, 0); @@ -213,6 +346,12 @@ function runTimeout(fun) { } } } +var currentQueue; +var queueIndex = -1; +function defaultClearTimeout() { + throw new Error("clearTimeout has not been defined"); +} +var cachedClearTimeout = defaultClearTimeout; function runClearTimeout(marker) { if (cachedClearTimeout === clearTimeout) { return clearTimeout(marker); @@ -231,33 +370,51 @@ function runClearTimeout(marker) { } } } -var queue = []; -var draining = false; -var currentQueue; -var queueIndex = -1; -function Item(fun, array) { - this.fun = fun; - this.array = array; +function drainQueue() { + if (draining) { + return; + } + var timeout = runTimeout(cleanUpNextTick); + draining = true; + var len = queue.length; + while(len){ + currentQueue = queue; + queue = []; + while((++queueIndex) < len){ + if (currentQueue) { + currentQueue[queueIndex].run(); + } + } + queueIndex = -1; + len = queue.length; + } + currentQueue = null; + draining = false; + runClearTimeout(timeout); +} +function nextTick(fun) { + var args = new Array(arguments.length - 1); + if (arguments.length > 1) { + for(var i = 1; i < arguments.length; i++){ + args[i - 1] = arguments[i]; + } + } + queue.push(new Item(fun, args)); + if (queue.length === 1 && !draining) { + runTimeout(drainQueue); + } } -Item.prototype.run = function() { - this.fun.apply(null, this.array); -}; var title = "browser"; -var platform = "browser"; var browser = true; var argv = []; var version = ""; var versions = { }; -var release = { -}; -var config = { -}; function noop() { } var on = noop; var addListener = noop; -var once = noop; +var once1 = noop; var off = noop; var removeListener = noop; var removeAllListeners = noop; @@ -274,6 +431,15 @@ function chdir(dir) { function umask() { return 0; } +var globalContext; +if (typeof window !== "undefined") { + globalContext = window; +} else if (typeof self !== "undefined") { + globalContext = self; +} else { + globalContext = { + }; +} var performance = globalContext.performance || { }; var performanceNow = performance.now || performance.mozNow || performance.msNow || performance.oNow || performance.webkitNow || function() { @@ -296,29 +462,109 @@ function hrtime(previousTimestamp) { nanoseconds ]; } +var platform = "browser"; +var release = { +}; +var config = { +}; var startTime = new Date(); function uptime() { var currentTime = new Date(); var dif = currentTime - startTime; return dif / 1000; } -function isObject(o) { - return Object.prototype.toString.call(o) === "[object Object]"; -} -function isPlainObject(o) { - var ctor, prot; - if (isObject(o) === false) return false; - ctor = o.constructor; - if (ctor === void 0) return true; - prot = ctor.prototype; - if (isObject(prot) === false) return false; - if (prot.hasOwnProperty("isPrototypeOf") === false) { - return false; - } - return true; -} -const isPlainObject1 = isPlainObject; -const isPlainObject2 = isPlainObject1; +var process = { + nextTick, + title, + browser, + env: { + NODE_ENV: "production" + }, + argv, + version, + versions, + on, + addListener, + once: once1, + off, + removeListener, + removeAllListeners, + emit, + binding, + cwd, + chdir, + umask, + hrtime, + platform, + release, + config, + uptime +}; +function getUserAgent() { + if (typeof navigator === "object" && "userAgent" in navigator) { + return navigator.userAgent; + } + if (typeof process === "object" && "version" in process) { + return `Node.js/${process.version.substr(1)} (${process.platform}; ${process.arch})`; + } + return ""; +} +const getUserAgent1 = getUserAgent; +const getUserAgent2 = getUserAgent1; +var getGlobal = function() { + if (typeof self !== "undefined") { + return self; + } + if (typeof window !== "undefined") { + return window; + } + if (typeof global !== "undefined") { + return global; + } + throw new Error("unable to locate global object"); +}; +const getUserAgent3 = getUserAgent1; +const userAgent = `octokit-endpoint.js/${VERSION} ${getUserAgent3()}`; +const DEFAULTS = { + method: "GET", + baseUrl: "https://api.github.com", + headers: { + accept: "application/vnd.github.v3+json", + "user-agent": userAgent + }, + mediaType: { + format: "", + previews: [] + } +}; +const endpoint = withDefaults(null, DEFAULTS); +if (typeof globalContext.setTimeout === "function") { + cachedSetTimeout = setTimeout; +} +if (typeof globalContext.clearTimeout === "function") { + cachedClearTimeout = clearTimeout; +} +function cleanUpNextTick() { + if (!draining || !currentQueue) { + return; + } + draining = false; + if (currentQueue.length) { + queue = currentQueue.concat(queue); + } else { + queueIndex = -1; + } + if (queue.length) { + drainQueue(); + } +} +function Item(fun, array) { + this.fun = fun; + this.array = array; +} +Item.prototype.run = function() { + this.fun.apply(null, this.array); +}; class Deprecation extends Error { constructor(message){ super(message); @@ -328,8 +574,7 @@ class Deprecation extends Error { this.name = "Deprecation"; } } -const Deprecation1 = Deprecation; -const Deprecation2 = Deprecation1; +var wrappy_1 = wrappy; function wrappy(fn, cb) { if (fn && cb) return wrappy(fn)(cb); if (typeof fn !== "function") throw new TypeError("need wrapper function"); @@ -352,7 +597,25 @@ function wrappy(fn, cb) { return ret; } } -function once1(fn) { +const __default = wrappy_1; +const wrappy2 = __default; +var once_1 = wrappy2(once2); +var strict = wrappy2(onceStrict); +once2.proto = once2(function() { + Object.defineProperty(Function.prototype, "once", { + value: function() { + return once2(this); + }, + configurable: true + }); + Object.defineProperty(Function.prototype, "onceStrict", { + value: function() { + return onceStrict(this); + }, + configurable: true + }); +}); +function once2(fn) { var f = function() { if (f.called) return f.value; f.called = true; @@ -372,167 +635,13 @@ function onceStrict(fn) { f.called = false; return f; } -const VERSION1 = "5.4.12"; -function getBufferResponse(response) { - return response.arrayBuffer(); -} -const isPlainObject3 = isPlainObject1; -function mergeDeep(defaults, options) { - const result = Object.assign({ - }, defaults); - Object.keys(options).forEach((key)=>{ - if (isPlainObject3(options[key])) { - if (!(key in defaults)) Object.assign(result, { - [key]: options[key] - }); - else result[key] = mergeDeep(defaults[key], options[key]); - } else { - Object.assign(result, { - [key]: options[key] - }); - } - }); - return result; -} -function merge(defaults, route, options) { - if (typeof route === "string") { - let [method, url] = route.split(" "); - options = Object.assign(url ? { - method, - url - } : { - url: method - }, options); - } else { - options = Object.assign({ - }, route); - } - options.headers = lowercaseKeys(options.headers); - removeUndefinedProperties(options); - removeUndefinedProperties(options.headers); - const mergedOptions = mergeDeep(defaults || { - }, options); - if (defaults && defaults.mediaType.previews.length) { - mergedOptions.mediaType.previews = defaults.mediaType.previews.filter((preview)=>!mergedOptions.mediaType.previews.includes(preview) - ).concat(mergedOptions.mediaType.previews); - } - mergedOptions.mediaType.previews = mergedOptions.mediaType.previews.map((preview)=>preview.replace(/-preview/, "") - ); - return mergedOptions; -} -function parseUrl(template) { - return { - expand: expand.bind(null, template) - }; -} -function parse(options) { - let method = options.method.toUpperCase(); - let url = (options.url || "/").replace(/:([a-z]\w+)/g, "{$1}"); - let headers = Object.assign({ - }, options.headers); - let body; - let parameters = omit(options, [ - "method", - "baseUrl", - "url", - "headers", - "request", - "mediaType" - ]); - const urlVariableNames = extractUrlVariableNames(url); - url = parseUrl(url).expand(parameters); - if (!/^http/.test(url)) { - url = options.baseUrl + url; - } - const omittedParameters = Object.keys(options).filter((option)=>urlVariableNames.includes(option) - ).concat("baseUrl"); - const remainingParameters = omit(parameters, omittedParameters); - const isBinaryRequest = /application\/octet-stream/i.test(headers.accept); - if (!isBinaryRequest) { - if (options.mediaType.format) { - headers.accept = headers.accept.split(/,/).map((preview)=>preview.replace(/application\/vnd(\.\w+)(\.v3)?(\.\w+)?(\+json)?$/, `application/vnd$1$2.${options.mediaType.format}`) - ).join(","); - } - if (options.mediaType.previews.length) { - const previewsFromAcceptHeader = headers.accept.match(/[\w-]+(?=-preview)/g) || []; - headers.accept = previewsFromAcceptHeader.concat(options.mediaType.previews).map((preview)=>{ - const format = options.mediaType.format ? `.${options.mediaType.format}` : "+json"; - return `application/vnd.github.${preview}-preview${format}`; - }).join(","); - } - } - if ([ - "GET", - "HEAD" - ].includes(method)) { - url = addQueryParameters(url, remainingParameters); - } else { - if ("data" in remainingParameters) { - body = remainingParameters.data; - } else { - if (Object.keys(remainingParameters).length) { - body = remainingParameters; - } else { - headers["content-length"] = 0; - } - } - } - if (!headers["content-type"] && typeof body !== "undefined") { - headers["content-type"] = "application/json; charset=utf-8"; - } - if ([ - "PATCH", - "PUT" - ].includes(method) && typeof body === "undefined") { - body = ""; - } - return Object.assign({ - method, - url, - headers - }, typeof body !== "undefined" ? { - body - } : null, options.request ? { - request: options.request - } : null); -} -function endpointWithDefaults(defaults, route, options) { - return parse(merge(defaults, route, options)); -} -function withDefaults(oldDefaults, newDefaults) { - const DEFAULTS2 = merge(oldDefaults, newDefaults); - const endpoint2 = endpointWithDefaults.bind(null, DEFAULTS2); - return Object.assign(endpoint2, { - DEFAULTS: DEFAULTS2, - defaults: withDefaults.bind(null, DEFAULTS2), - merge: merge.bind(null, DEFAULTS2), - parse - }); -} -var wrappy_1 = wrappy; -const __default = wrappy_1; -const wrappy2 = __default; -var once_1 = wrappy2(once1); -var strict = wrappy2(onceStrict); -once1.proto = once1(function() { - Object.defineProperty(Function.prototype, "once", { - value: function() { - return once1(this); - }, - configurable: true - }); - Object.defineProperty(Function.prototype, "onceStrict", { - value: function() { - return onceStrict(this); - }, - configurable: true - }); -}); once_1.strict = strict; const __default1 = once_1; -const once2 = __default1; -const logOnce = once2((deprecation2)=>console.warn(deprecation2) +const once21 = __default1; +const logOnce = once21((deprecation2)=>console.warn(deprecation2) ); +const Deprecation1 = Deprecation; +const Deprecation2 = Deprecation1; class RequestError extends Error { constructor(message1, statusCode, options){ super(message1); @@ -561,108 +670,14 @@ class RequestError extends Error { this.request = requestCopy; } } +var global = getGlobal(); +var nodeFetch = global.fetch.bind(global); const RequestError1 = RequestError; const RequestError2 = RequestError1; -function cleanUpNextTick() { - if (!draining || !currentQueue) { - return; - } - draining = false; - if (currentQueue.length) { - queue = currentQueue.concat(queue); - } else { - queueIndex = -1; - } - if (queue.length) { - drainQueue(); - } -} -function drainQueue() { - if (draining) { - return; - } - var timeout = runTimeout(cleanUpNextTick); - draining = true; - var len = queue.length; - while(len){ - currentQueue = queue; - queue = []; - while((++queueIndex) < len){ - if (currentQueue) { - currentQueue[queueIndex].run(); - } - } - queueIndex = -1; - len = queue.length; - } - currentQueue = null; - draining = false; - runClearTimeout(timeout); -} -function nextTick(fun) { - var args = new Array(arguments.length - 1); - if (arguments.length > 1) { - for(var i = 1; i < arguments.length; i++){ - args[i - 1] = arguments[i]; - } - } - queue.push(new Item(fun, args)); - if (queue.length === 1 && !draining) { - runTimeout(drainQueue); - } -} -var process = { - nextTick, - title, - browser, - env: { - NODE_ENV: "production" - }, - argv, - version, - versions, - on, - addListener, - once, - off, - removeListener, - removeAllListeners, - emit, - binding, - cwd, - chdir, - umask, - hrtime, - platform, - release, - config, - uptime -}; -function getUserAgent() { - if (typeof navigator === "object" && "userAgent" in navigator) { - return navigator.userAgent; - } - if (typeof process === "object" && "version" in process) { - return `Node.js/${process.version.substr(1)} (${process.platform}; ${process.arch})`; - } - return ""; +const VERSION1 = "5.4.12"; +function getBufferResponse(response) { + return response.arrayBuffer(); } -const getUserAgent1 = getUserAgent; -const getUserAgent2 = getUserAgent1; -var getGlobal = function() { - if (typeof self !== "undefined") { - return self; - } - if (typeof window !== "undefined") { - return window; - } - if (typeof global !== "undefined") { - return global; - } - throw new Error("unable to locate global object"); -}; -var global = getGlobal(); -var nodeFetch = global.fetch.bind(global); function fetchWrapper(requestOptions) { if (isPlainObject2(requestOptions.body) || Array.isArray(requestOptions.body)) { requestOptions.body = JSON.stringify(requestOptions.body); @@ -763,21 +778,6 @@ function withDefaults1(oldEndpoint, newDefaults) { defaults: withDefaults1.bind(null, endpoint3) }); } -const getUserAgent3 = getUserAgent1; -const userAgent = `octokit-endpoint.js/${VERSION} ${getUserAgent3()}`; -const DEFAULTS = { - method: "GET", - baseUrl: "https://api.github.com", - headers: { - accept: "application/vnd.github.v3+json", - "user-agent": userAgent - }, - mediaType: { - format: "", - previews: [] - } -}; -const endpoint = withDefaults(null, DEFAULTS); const endpoint1 = endpoint; const endpoint2 = endpoint1; const request = withDefaults1(endpoint2, { diff --git a/bundler/tests/fixture/deno-8584/output/entry.ts b/bundler/tests/fixture/deno-8584/output/entry.ts index e871a319ed78..3403d1538d6f 100644 --- a/bundler/tests/fixture/deno-8584/output/entry.ts +++ b/bundler/tests/fixture/deno-8584/output/entry.ts @@ -1,8 +1,8 @@ const mod = await async function() { async function setup() { } - const setup1 = setup; await setup(); + const setup1 = setup; return { setup }; diff --git a/bundler/tests/fixture/deno-8625/output/entry.ts b/bundler/tests/fixture/deno-8625/output/entry.ts index 3f926bcd35b7..ef280737ccfb 100644 --- a/bundler/tests/fixture/deno-8625/output/entry.ts +++ b/bundler/tests/fixture/deno-8625/output/entry.ts @@ -9,7 +9,7 @@ const mod = function() { const Foo = mod.Foo; const Foo1 = Foo; const bar = Foo1('bar'); +const foo = mod; const __default = bar; const bar1 = __default; -const foo = mod; console.log(foo, bar1); diff --git a/bundler/tests/fixture/deno-8679/input/entry.ts b/bundler/tests/fixture/deno-8679/input/entry.ts new file mode 100644 index 000000000000..b87721f53c4f --- /dev/null +++ b/bundler/tests/fixture/deno-8679/input/entry.ts @@ -0,0 +1,3 @@ +import two from "./two.ts"; + +console.log(two); \ No newline at end of file diff --git a/bundler/tests/fixture/deno-8679/input/one.ts b/bundler/tests/fixture/deno-8679/input/one.ts new file mode 100644 index 000000000000..262513343522 --- /dev/null +++ b/bundler/tests/fixture/deno-8679/input/one.ts @@ -0,0 +1,2 @@ +export default "one"; +export const one = "one"; \ No newline at end of file diff --git a/bundler/tests/fixture/deno-8679/input/two.ts b/bundler/tests/fixture/deno-8679/input/two.ts new file mode 100644 index 000000000000..9bc8c2ec78d8 --- /dev/null +++ b/bundler/tests/fixture/deno-8679/input/two.ts @@ -0,0 +1,2 @@ +export * from "./one.ts"; +export { default } from "./one.ts"; \ No newline at end of file diff --git a/bundler/tests/fixture/deno-8679/output/entry.ts b/bundler/tests/fixture/deno-8679/output/entry.ts new file mode 100644 index 000000000000..d305ba167f5d --- /dev/null +++ b/bundler/tests/fixture/deno-8679/output/entry.ts @@ -0,0 +1,3 @@ +const __default = "one"; +const two = __default; +console.log(two); diff --git a/bundler/tests/fixture/issue-1150-2/output/entry.ts b/bundler/tests/fixture/issue-1150-2/output/entry.ts index 1a85d30f06b5..da64d866a70a 100644 --- a/bundler/tests/fixture/issue-1150-2/output/entry.ts +++ b/bundler/tests/fixture/issue-1150-2/output/entry.ts @@ -10,6 +10,9 @@ var O3; O1[O1["C"] = 2] = "C"; })(O3 || (O3 = { })); +const O1 = O3; +const O2 = O1; +export { O2 as O }; const defaultA = a2; class A { #a; @@ -30,6 +33,3 @@ class A { let a4 = new A(); a4.a(); a4.c(); -const O1 = O3; -const O2 = O1; -export { O2 as O }; diff --git a/bundler/tests/fixture/issue-1150/output/entry.ts b/bundler/tests/fixture/issue-1150/output/entry.ts index 1a85d30f06b5..da64d866a70a 100644 --- a/bundler/tests/fixture/issue-1150/output/entry.ts +++ b/bundler/tests/fixture/issue-1150/output/entry.ts @@ -10,6 +10,9 @@ var O3; O1[O1["C"] = 2] = "C"; })(O3 || (O3 = { })); +const O1 = O3; +const O2 = O1; +export { O2 as O }; const defaultA = a2; class A { #a; @@ -30,6 +33,3 @@ class A { let a4 = new A(); a4.a(); a4.c(); -const O1 = O3; -const O2 = O1; -export { O2 as O }; diff --git a/bundler/tests/fixture/wrapped/export-named/output/entry.ts b/bundler/tests/fixture/wrapped/export-named/output/entry.ts index d7a4e9a26f49..f854a3151fed 100644 --- a/bundler/tests/fixture/wrapped/export-named/output/entry.ts +++ b/bundler/tests/fixture/wrapped/export-named/output/entry.ts @@ -4,11 +4,11 @@ const [a, b, c] = [ 3 ]; const b1 = b; +const b2 = b1; const mod = function() { return { b: b1 }; }(); const foo = mod; -const b2 = b1; console.log(foo); diff --git a/ecmascript/transforms/src/fixer.rs b/ecmascript/transforms/src/fixer.rs index 95ef406e99f2..733ba9fca0f5 100644 --- a/ecmascript/transforms/src/fixer.rs +++ b/ecmascript/transforms/src/fixer.rs @@ -74,7 +74,9 @@ impl VisitMut for Fixer<'_> { self.ctx = Context::Callee { is_new: true }; node.callee.visit_mut_with(self); match *node.callee { - Expr::Call(..) | Expr::Bin(..) => self.wrap(&mut node.callee), + Expr::Call(..) | Expr::Bin(..) | Expr::Assign(..) | Expr::Seq(..) => { + self.wrap(&mut node.callee) + } _ => {} } self.ctx = old; @@ -1013,4 +1015,11 @@ var store = global[SHARED] || (global[SHARED] = {}); ); identical!(deno_8722, "console.log((true || false) ?? true);"); + + identical!( + deno_8597, + " + biasInitializer = new (_a = class CustomInit extends Initializer {})(); + " + ); } diff --git a/spack/tests/pass/alias/export-named-same-name/output/entry.js b/spack/tests/pass/alias/export-named-same-name/output/entry.js index 383e7acac275..dd854f272d03 100644 --- a/spack/tests/pass/alias/export-named-same-name/output/entry.js +++ b/spack/tests/pass/alias/export-named-same-name/output/entry.js @@ -1,8 +1,8 @@ const foo = 1; const bar = 2; const bar1 = foo; -const baz = bar; const bar2 = bar1; -const baz1 = baz; console.log(bar2); +const baz = bar; +const baz1 = baz; console.log(baz1); diff --git a/spack/tests/pass/alias/import/multi/output/entry.js b/spack/tests/pass/alias/import/multi/output/entry.js index 30228f9f3233..16e0b60f38c1 100644 --- a/spack/tests/pass/alias/import/multi/output/entry.js +++ b/spack/tests/pass/alias/import/multi/output/entry.js @@ -1,15 +1,15 @@ function a() { console.log("a()"); } -const a1 = a; -const foo = a1; function b() { console.log("a()"); } -const b1 = b; -const bar = b1; -function a2() { +function a1() { } -function b2() { +function b1() { } -console.log(a2(), foo(), b2(), bar()); +const a2 = a; +const foo = a2; +const b2 = b; +const bar = b2; +console.log(a1(), foo(), b1(), bar()); diff --git a/spack/tests/pass/alias/import/simple/output/entry.js b/spack/tests/pass/alias/import/simple/output/entry.js index 0fce605a58dc..92544293ee11 100644 --- a/spack/tests/pass/alias/import/simple/output/entry.js +++ b/spack/tests/pass/alias/import/simple/output/entry.js @@ -1,8 +1,8 @@ function a() { console.log("a()"); } -const a1 = a; -const foo = a1; -function a2() { +function a1() { } -console.log(a2(), foo()); +const a2 = a; +const foo = a2; +console.log(a1(), foo()); diff --git a/spack/tests/pass/basic/extends/output/entry.js b/spack/tests/pass/basic/extends/output/entry.js index d5e8bb7d4648..63f3111243d0 100644 --- a/spack/tests/pass/basic/extends/output/entry.js +++ b/spack/tests/pass/basic/extends/output/entry.js @@ -4,7 +4,7 @@ const B1 = B; const B2 = B1; class A extends B2 { } +const B3 = B1; const A1 = A; const A2 = A1; -const B3 = B1; console.log(A2, B3); diff --git a/spack/tests/pass/circular/complex-class-function/output/entry.js b/spack/tests/pass/circular/complex-class-function/output/entry.js index 9ce68b745095..b14c8db0df9e 100644 --- a/spack/tests/pass/circular/complex-class-function/output/entry.js +++ b/spack/tests/pass/circular/complex-class-function/output/entry.js @@ -1,15 +1,15 @@ -class C { -} function getC() { return C; } -const getC1 = getC; -const getC2 = getC1; -class A extends getC2() { +class C { } function a() { return new A(); } +const getC1 = getC; +const getC2 = getC1; +class A extends getC2() { +} const a1 = a; const a2 = a1; console.log(a2, a2()); diff --git a/spack/tests/pass/circular/hygiene/class-inheritance/output/entry.js b/spack/tests/pass/circular/hygiene/class-inheritance/output/entry.js index 548f977eb8b3..33c2ff7f905f 100644 --- a/spack/tests/pass/circular/hygiene/class-inheritance/output/entry.js +++ b/spack/tests/pass/circular/hygiene/class-inheritance/output/entry.js @@ -3,22 +3,22 @@ class C { throw new Error('Unimplemented'); } b() { - return new B2(); + return new B3(); } } const C1 = C; const C2 = C1; class B extends C2 { a() { - return new A2(); + return new A3(); } } const B1 = B; const B2 = B1; -const B3 = B1; -class A extends B3 { +class A extends B2 { } +const B3 = B1; const A1 = A; const A2 = A1; +console.log(A2, 'Loaded!'); const A3 = A1; -console.log(A3, 'Loaded!'); diff --git a/spack/tests/pass/circular/mixed/output/entry.js b/spack/tests/pass/circular/mixed/output/entry.js index 7b1a244cc72a..87cad993ac27 100644 --- a/spack/tests/pass/circular/mixed/output/entry.js +++ b/spack/tests/pass/circular/mixed/output/entry.js @@ -1,15 +1,15 @@ console.log('c'); class A { method() { - return new B2(); + return new B3(); } } const A1 = A; const A2 = A1; -class B extends A2 { +const A3 = A1; +class B extends A3 { } const B1 = B; const B2 = B1; -const A3 = A1; +console.log(A2, B2); const B3 = B1; -console.log(A3, B3); diff --git a/spack/tests/pass/circular/simple/output/entry.js b/spack/tests/pass/circular/simple/output/entry.js index 8b4088dfec84..a0794c2769b2 100644 --- a/spack/tests/pass/circular/simple/output/entry.js +++ b/spack/tests/pass/circular/simple/output/entry.js @@ -1,14 +1,14 @@ class A { method() { - return new B2(); + return new B3(); } } const A1 = A; const A2 = A1; -class B extends A2 { +const A3 = A1; +class B extends A3 { } const B1 = B; const B2 = B1; -const A3 = A1; +console.log(A2, B2); const B3 = B1; -console.log(A3, B3); diff --git a/spack/tests/pass/circular/top-level-idents/output/entry.js b/spack/tests/pass/circular/top-level-idents/output/entry.js index 3330e73d33de..291bdc4563aa 100644 --- a/spack/tests/pass/circular/top-level-idents/output/entry.js +++ b/spack/tests/pass/circular/top-level-idents/output/entry.js @@ -1,4 +1,4 @@ -console.log('a'); -console.log('b'); console.log('c'); +console.log('b'); +console.log('a'); console.log('entry'); diff --git a/spack/tests/pass/cjs/issue-967-no-recursive-require/output/entry.js b/spack/tests/pass/cjs/issue-967-no-recursive-require/output/entry.js index 428b075abe2c..f73c8c921af0 100644 --- a/spack/tests/pass/cjs/issue-967-no-recursive-require/output/entry.js +++ b/spack/tests/pass/cjs/issue-967-no-recursive-require/output/entry.js @@ -14,14 +14,14 @@ function __spack_require__(mod) { }; } var load = __spack_require__.bind(void 0, function(module, exports) { - console.log('c'); + console.log('a'); }); +load(); var load1 = __spack_require__.bind(void 0, function(module, exports) { console.log('b'); }); +load1(); var load2 = __spack_require__.bind(void 0, function(module, exports) { - console.log('a'); + console.log('c'); }); load2(); -load1(); -load(); diff --git a/spack/tests/pass/cjs/issue-967-recursive-require/output/entry.js b/spack/tests/pass/cjs/issue-967-recursive-require/output/entry.js index 72769fc8a3f4..f02eae056955 100644 --- a/spack/tests/pass/cjs/issue-967-recursive-require/output/entry.js +++ b/spack/tests/pass/cjs/issue-967-recursive-require/output/entry.js @@ -14,34 +14,34 @@ function __spack_require__(mod) { }; } var load = __spack_require__.bind(void 0, function(module, exports) { - console.log('c'); -}); -var load1 = __spack_require__.bind(void 0, function(module, exports) { console.log('b'); module.exports = 'b'; }); -var load2 = __spack_require__.bind(void 0, function(module, exports) { - var load3 = __spack_require__.bind(void 0, function(module1, exports1) { - console.log('a-b'); - exports1.default = 'ab'; - }); - var load4 = __spack_require__.bind(void 0, function(module1, exports1) { - var load5 = __spack_require__.bind(void 0, function(module2, exports2) { +var load1 = __spack_require__.bind(void 0, function(module, exports) { + var load2 = __spack_require__.bind(void 0, function(module1, exports1) { + var load3 = __spack_require__.bind(void 0, function(module2, exports2) { module2.exports = { default: 'a-a-a' }; }); - module1.exports = load5(); + module1.exports = load3(); + }); + var load3 = __spack_require__.bind(void 0, function(module1, exports1) { + console.log('a-b'); + exports1.default = 'ab'; }); - var aa = load4(); + var aa = load2(); var bb = load3(); - load1(); + load(); module.exports = { aa: aa, bb: bb }; }); +load1(); +var b = load(); +var load2 = __spack_require__.bind(void 0, function(module, exports) { + console.log('c'); +}); load2(); -var b = load1(); -load(); console.log(b); diff --git a/spack/tests/pass/deno-001/full/output/entry.js b/spack/tests/pass/deno-001/full/output/entry.js index 558095bce0d3..0e384ea463b0 100644 --- a/spack/tests/pass/deno-001/full/output/entry.js +++ b/spack/tests/pass/deno-001/full/output/entry.js @@ -1,339 +1,30 @@ -function deferred() { - let methods; - const promise = new Promise((resolve, reject)=>{ - methods = { - resolve, - reject - }; - }); - return Object.assign(promise, methods); -} -const deferred1 = deferred; -const deferred2 = deferred1; -var tmp = Symbol.asyncIterator; -class MuxAsyncIterator { - add(iterator) { - ++this.iteratorCount; - this.callIteratorNext(iterator); - } - async callIteratorNext(iterator) { - try { - const { value , done } = await iterator.next(); - if (done) --this.iteratorCount; - else this.yields.push({ - iterator, - value - }); - } catch (e) { - this.throws.push(e); - } - this.signal.resolve(); - } - async *iterate() { - while(this.iteratorCount > 0){ - // Sleep until any of the wrapped iterators yields. - await this.signal; - // Note that while we're looping over `yields`, new items may be added. - for(let i = 0; i < this.yields.length; i++){ - const { iterator , value } = this.yields[i]; - yield value; - this.callIteratorNext(iterator); - } - if (this.throws.length) { - for (const e of this.throws)throw e; - this.throws.length = 0; - } - // Clear the `yields` list and reset the `signal` promise. - this.yields.length = 0; - this.signal = deferred2(); - } - } - [tmp]() { - return this.iterate(); - } - constructor(){ - this.iteratorCount = 0; - this.yields = []; - this.throws = []; - this.signal = deferred2(); - } -} -const deferred3 = deferred; -const MuxAsyncIterator1 = MuxAsyncIterator; -const deferred4 = deferred3; -const MuxAsyncIterator2 = MuxAsyncIterator1; -var tmp1 = Symbol.asyncIterator; -function _parseAddrFromStr(addr) { - let url; - try { - const host = addr.startsWith(":") ? `0.0.0.0${addr}` : addr; - url = new URL(`http://${host}`); - } catch { - throw new TypeError("Invalid address."); - } - if (url.username || url.password || url.pathname != "/" || url.search || url.hash) throw new TypeError("Invalid address."); - return { - hostname: url.hostname, - port: url.port === "" ? 80 : Number(url.port) - }; -} -const DEFAULT_BUF_SIZE = 4096; -const MIN_BUF_SIZE = 16; -const MAX_CONSECUTIVE_EMPTY_READS = 100; -const CR = "\r".charCodeAt(0); -const LF = "\n".charCodeAt(0); -class BufferFullError extends Error { - constructor(partial){ - super("Buffer full"); - this.partial = partial; - this.name = "BufferFullError"; - } -} -class AbstractBufBase { - /** Size returns the size of the underlying buffer in bytes. */ size() { - return this.buf.byteLength; - } - /** Returns how many bytes are unused in the buffer. */ available() { - return this.buf.byteLength - this.usedBufferBytes; - } - /** buffered returns the number of bytes that have been written into the - * current buffer. - */ buffered() { - return this.usedBufferBytes; - } - constructor(){ - this.usedBufferBytes = 0; - this.err = null; - } -} -/** Generate longest proper prefix which is also suffix array. */ function createLPS(pat) { - const lps = new Uint8Array(pat.length); - lps[0] = 0; - let prefixEnd = 0; - let i = 1; - while(i < lps.length)if (pat[i] == pat[prefixEnd]) { - prefixEnd++; - lps[i] = prefixEnd; - i++; - } else if (prefixEnd === 0) { - lps[i] = 0; - i++; - } else prefixEnd = pat[prefixEnd - 1]; - return lps; -} -async function* readDelim(reader, delim) { - // Avoid unicode problems - const delimLen = delim.length; - const delimLPS = createLPS(delim); - let inputBuffer = new Deno.Buffer(); - const inspectArr = new Uint8Array(Math.max(1024, delimLen + 1)); - // Modified KMP - let inspectIndex = 0; - let matchIndex = 0; - while(true){ - const result = await reader.read(inspectArr); - if (result === null) { - // Yield last chunk. - yield inputBuffer.bytes(); - return; - } - if (result < 0) // Discard all remaining and silently fail. - return; - const sliceRead = inspectArr.subarray(0, result); - await Deno.writeAll(inputBuffer, sliceRead); - let sliceToProcess = inputBuffer.bytes(); - while(inspectIndex < sliceToProcess.length)if (sliceToProcess[inspectIndex] === delim[matchIndex]) { - inspectIndex++; - matchIndex++; - if (matchIndex === delimLen) { - // Full match - const matchEnd = inspectIndex - delimLen; - const readyBytes = sliceToProcess.subarray(0, matchEnd); - // Copy - const pendingBytes = sliceToProcess.slice(inspectIndex); - yield readyBytes; - // Reset match, different from KMP. - sliceToProcess = pendingBytes; - inspectIndex = 0; - matchIndex = 0; - } - } else if (matchIndex === 0) inspectIndex++; - else matchIndex = delimLPS[matchIndex - 1]; - // Keep inspectIndex and matchIndex. - inputBuffer = new Deno.Buffer(sliceToProcess); - } -} -async function* readStringDelim(reader, delim) { - const encoder = new TextEncoder(); - const decoder = new TextDecoder(); - for await (const chunk of readDelim(reader, encoder.encode(delim)))yield decoder.decode(chunk); -} -function assert(expr, msg = "") { - if (!expr) throw new DenoStdInternalError(msg); -} -const assert1 = assert; -function findIndex(source, pat) { - const s = pat[0]; - for(let i = 0; i < source.length; i++){ - if (source[i] !== s) continue; - const pin = i; - let matched = 1; - let j = i; - while(matched < pat.length){ - j++; - if (source[j] !== pat[j - pin]) break; - matched++; - } - if (matched === pat.length) return pin; - } - return -1; -} -function concat(origin, b) { - const output = new Uint8Array(origin.length + b.length); - output.set(origin, 0); - output.set(b, origin.length); - return output; -} -function copyBytes(src, dst, off = 0) { - off = Math.max(0, Math.min(off, dst.byteLength)); - const dstBytesAvailable = dst.byteLength - off; - if (src.byteLength > dstBytesAvailable) src = src.subarray(0, dstBytesAvailable); - dst.set(src, off); - return src.byteLength; -} -const concat1 = concat; -const copyBytes1 = copyBytes; -const concat2 = concat1; -// FROM https://github.com/denoland/deno/blob/b34628a26ab0187a827aa4ebe256e23178e25d39/cli/js/web/headers.ts#L9 -const invalidHeaderCharRegex = /[^\t\x20-\x7e\x80-\xff]/g; -function charCode(s) { - return s.charCodeAt(0); -} -const encoder = new TextEncoder(); -function encode(input) { - return encoder.encode(input); -} -const decoder = new TextDecoder(); -function decode(input) { - return decoder.decode(input); -} -const encoder1 = encoder; -const encode1 = encode; -const decode1 = decode; -const assert2 = assert1; -const encoder2 = encoder1; -const STATUS_TEXT = new Map([]); -const STATUS_TEXT1 = STATUS_TEXT; -const STATUS_TEXT2 = STATUS_TEXT1; -function emptyReader() { - return { - read (_) { - return Promise.resolve(null); - } - }; -} -function bodyReader(contentLength, r) { - let totalRead = 0; - let finished = false; - async function read(buf) { - if (finished) return null; - let result; - const remaining = contentLength - totalRead; - if (remaining >= buf.byteLength) result = await r.read(buf); - else { - const readBuf = buf.subarray(0, remaining); - result = await r.read(readBuf); - } - if (result !== null) totalRead += result; - finished = totalRead === contentLength; - return result; - } - return { - read - }; -} -function isProhibidedForTrailer(key) { - const s = new Set([ - "transfer-encoding", - "content-length", - "trailer" - ]); - return s.has(key.toLowerCase()); -} -function parseTrailer(field) { - if (field == null) return undefined; - const trailerNames = field.split(",").map((v)=>v.trim().toLowerCase() - ); - if (trailerNames.length === 0) throw new Deno.errors.InvalidData("Empty trailer header."); - const prohibited = trailerNames.filter((k)=>isProhibidedForTrailer(k) - ); - if (prohibited.length > 0) throw new Deno.errors.InvalidData(`Prohibited trailer names: ${Deno.inspect(prohibited)}.`); - return new Headers(trailerNames.map((key)=>[ - key, - "" - ] - )); -} -function parseHTTPVersion(vers) { - switch(vers){ - case "HTTP/1.1": - return [ - 1, - 1 - ]; - case "HTTP/1.0": - return [ - 1, - 0 - ]; - default: - { - const Big = 1000000; // arbitrary upper bound - if (!vers.startsWith("HTTP/")) break; - const dot = vers.indexOf("."); - if (dot < 0) break; - const majorStr = vers.substring(vers.indexOf("/") + 1, dot); - const major = Number(majorStr); - if (!Number.isInteger(major) || major < 0 || major > Big) break; - const minorStr = vers.substring(dot + 1); - const minor = Number(minorStr); - if (!Number.isInteger(minor) || minor < 0 || minor > Big) break; - return [ - major, - minor - ]; - } +const DEFAULT_BUF_SIZE = 4096; +const MIN_BUF_SIZE = 16; +const MAX_CONSECUTIVE_EMPTY_READS = 100; +const CR = "\r".charCodeAt(0); +const LF = "\n".charCodeAt(0); +class BufferFullError extends Error { + constructor(partial){ + super("Buffer full"); + this.partial = partial; + this.name = "BufferFullError"; } - throw new Error(`malformed HTTP version ${vers}`); } -function fixLength(req) { - const contentLength = req.headers.get("Content-Length"); - if (contentLength) { - const arrClen = contentLength.split(","); - if (arrClen.length > 1) { - const distinct = [ - ...new Set(arrClen.map((e)=>e.trim() - )) - ]; - if (distinct.length > 1) throw Error("cannot contain multiple Content-Length headers"); - else req.headers.set("Content-Length", distinct[0]); - } - const c = req.headers.get("Content-Length"); - if (req.method === "HEAD" && c && c !== "0") throw Error("http: method cannot contain a Content-Length"); - if (c && req.headers.has("transfer-encoding")) // A sender MUST NOT send a Content-Length header field in any message - // that contains a Transfer-Encoding header field. - // rfc: https://tools.ietf.org/html/rfc7230#section-3.3.2 - throw new Error("http: Transfer-Encoding and Content-Length cannot be send together"); - } +function assert(expr, msg = "") { + if (!expr) throw new DenoStdInternalError(msg); } -const emptyReader1 = emptyReader; -const bodyReader1 = bodyReader; -const encode2 = encode1; +const assert1 = assert; +const assert2 = assert1; const assert3 = assert1; -const bodyReader2 = bodyReader1; -const emptyReader2 = emptyReader1; +function copyBytes(src, dst, off = 0) { + off = Math.max(0, Math.min(off, dst.byteLength)); + const dstBytesAvailable = dst.byteLength - off; + if (src.byteLength > dstBytesAvailable) src = src.subarray(0, dstBytesAvailable); + dst.set(src, off); + return src.byteLength; +} +const copyBytes1 = copyBytes; const copyBytes2 = copyBytes1; -const assert4 = assert1; class BufReader { // private lastByte: number; // private lastCharSize: number; @@ -362,7 +53,7 @@ class BufReader { this.eof = true; return; } - assert4(rr >= 0, "negative read"); + assert3(rr >= 0, "negative read"); this.w += rr; if (rr > 0) return; } @@ -392,7 +83,7 @@ class BufReader { // Read directly into p to avoid copy. const rr1 = await this.rd.read(p); const nread = rr1 ?? 0; - assert4(nread >= 0, "negative read"); + assert3(nread >= 0, "negative read"); // if (rr.nread > 0) { // this.lastByte = p[rr.nread - 1]; // this.lastCharSize = -1; @@ -405,7 +96,7 @@ class BufReader { this.w = 0; rr = await this.rd.read(this.buf); if (rr === 0 || rr === null) return rr; - assert4(rr >= 0, "negative read"); + assert3(rr >= 0, "negative read"); this.w += rr; } // copy as much as we can @@ -494,7 +185,7 @@ class BufReader { line = await this.readSlice(LF); } catch (err) { let { partial: partial1 } = err; - assert4(partial1 instanceof Uint8Array, "bufio: caught error from `readSlice()` without `partial` property"); + assert3(partial1 instanceof Uint8Array, "bufio: caught error from `readSlice()` without `partial` property"); // Don't throw if `readSlice()` failed with `BufferFullError`, instead we // just return whatever is available and set the `more` flag. if (!(err instanceof BufferFullError)) throw err; @@ -502,7 +193,7 @@ class BufReader { if (!this.eof && partial1.byteLength > 0 && partial1[partial1.byteLength - 1] === CR) { // Put the '\r' back on buf and drop it from line. // Let the next call to ReadLine check for "\r\n". - assert4(this.r > 0, "bufio: tried to rewind past start of buffer"); + assert3(this.r > 0, "bufio: tried to rewind past start of buffer"); this.r--; partial1 = partial1.subarray(0, partial1.byteLength - 1); } @@ -621,6 +312,23 @@ class BufReader { this._reset(new Uint8Array(size1), rd); } } +class AbstractBufBase { + /** Size returns the size of the underlying buffer in bytes. */ size() { + return this.buf.byteLength; + } + /** Returns how many bytes are unused in the buffer. */ available() { + return this.buf.byteLength - this.usedBufferBytes; + } + /** buffered returns the number of bytes that have been written into the + * current buffer. + */ buffered() { + return this.usedBufferBytes; + } + constructor(){ + this.usedBufferBytes = 0; + this.err = null; + } +} class BufWriter extends AbstractBufBase { /** return new BufWriter unless writer is BufWriter */ static create(writer, size = DEFAULT_BUF_SIZE) { return writer instanceof BufWriter ? writer : new BufWriter(writer, size); @@ -747,14 +455,107 @@ class BufWriterSync extends AbstractBufBase { this.buf = new Uint8Array(size3); } } -const BufReader1 = BufReader; -const BufWriter1 = BufWriter; -const BufWriter2 = BufWriter1; +/** Generate longest proper prefix which is also suffix array. */ function createLPS(pat) { + const lps = new Uint8Array(pat.length); + lps[0] = 0; + let prefixEnd = 0; + let i = 1; + while(i < lps.length)if (pat[i] == pat[prefixEnd]) { + prefixEnd++; + lps[i] = prefixEnd; + i++; + } else if (prefixEnd === 0) { + lps[i] = 0; + i++; + } else prefixEnd = pat[prefixEnd - 1]; + return lps; +} +async function* readDelim(reader, delim) { + // Avoid unicode problems + const delimLen = delim.length; + const delimLPS = createLPS(delim); + let inputBuffer = new Deno.Buffer(); + const inspectArr = new Uint8Array(Math.max(1024, delimLen + 1)); + // Modified KMP + let inspectIndex = 0; + let matchIndex = 0; + while(true){ + const result = await reader.read(inspectArr); + if (result === null) { + // Yield last chunk. + yield inputBuffer.bytes(); + return; + } + if (result < 0) // Discard all remaining and silently fail. + return; + const sliceRead = inspectArr.subarray(0, result); + await Deno.writeAll(inputBuffer, sliceRead); + let sliceToProcess = inputBuffer.bytes(); + while(inspectIndex < sliceToProcess.length)if (sliceToProcess[inspectIndex] === delim[matchIndex]) { + inspectIndex++; + matchIndex++; + if (matchIndex === delimLen) { + // Full match + const matchEnd = inspectIndex - delimLen; + const readyBytes = sliceToProcess.subarray(0, matchEnd); + // Copy + const pendingBytes = sliceToProcess.slice(inspectIndex); + yield readyBytes; + // Reset match, different from KMP. + sliceToProcess = pendingBytes; + inspectIndex = 0; + matchIndex = 0; + } + } else if (matchIndex === 0) inspectIndex++; + else matchIndex = delimLPS[matchIndex - 1]; + // Keep inspectIndex and matchIndex. + inputBuffer = new Deno.Buffer(sliceToProcess); + } +} +async function* readStringDelim(reader, delim) { + const encoder = new TextEncoder(); + const decoder = new TextDecoder(); + for await (const chunk of readDelim(reader, encoder.encode(delim)))yield decoder.decode(chunk); +} +function findIndex(source, pat) { + const s = pat[0]; + for(let i = 0; i < source.length; i++){ + if (source[i] !== s) continue; + const pin = i; + let matched = 1; + let j = i; + while(matched < pat.length){ + j++; + if (source[j] !== pat[j - pin]) break; + matched++; + } + if (matched === pat.length) return pin; + } + return -1; +} +function concat(origin, b) { + const output = new Uint8Array(origin.length + b.length); + output.set(origin, 0); + output.set(b, origin.length); + return output; +} +// FROM https://github.com/denoland/deno/blob/b34628a26ab0187a827aa4ebe256e23178e25d39/cli/js/web/headers.ts#L9 +const invalidHeaderCharRegex = /[^\t\x20-\x7e\x80-\xff]/g; +const decoder = new TextDecoder(); +function decode(input) { + return decoder.decode(input); +} +const decode1 = decode; const decode2 = decode1; function str(buf) { if (buf == null) return ""; else return decode2(buf); } +function charCode(s) { + return s.charCodeAt(0); +} +const concat1 = concat; +const concat2 = concat1; class TextProtoReader { /** readLine() reads a single line from the TextProtoReader, * eliding the final \n or \r\n from the returned string. @@ -834,26 +635,141 @@ class TextProtoReader { if (this.skipSpace(l) === 0) return new Uint8Array(0); return l; } - line = line ? concat2(line, l) : l; - if (!more) break; - } - return line; - } - skipSpace(l) { - let n = 0; - for(let i = 0; i < l.length; i++){ - if (l[i] === charCode(" ") || l[i] === charCode("\t")) continue; - n++; + line = line ? concat2(line, l) : l; + if (!more) break; + } + return line; + } + skipSpace(l) { + let n = 0; + for(let i = 0; i < l.length; i++){ + if (l[i] === charCode(" ") || l[i] === charCode("\t")) continue; + n++; + } + return n; + } + constructor(r1){ + this.r = r1; + this.r = r1; + } +} +const encoder = new TextEncoder(); +function encode(input) { + return encoder.encode(input); +} +const STATUS_TEXT = new Map([]); +function emptyReader() { + return { + read (_) { + return Promise.resolve(null); + } + }; +} +function bodyReader(contentLength, r1) { + let totalRead = 0; + let finished = false; + async function read(buf) { + if (finished) return null; + let result; + const remaining = contentLength - totalRead; + if (remaining >= buf.byteLength) result = await r1.read(buf); + else { + const readBuf = buf.subarray(0, remaining); + result = await r1.read(readBuf); + } + if (result !== null) totalRead += result; + finished = totalRead === contentLength; + return result; + } + return { + read + }; +} +const bodyReader1 = bodyReader; +const bodyReader2 = bodyReader1; +const TextProtoReader1 = TextProtoReader; +const TextProtoReader2 = TextProtoReader1; +const assert4 = assert1; +const STATUS_TEXT1 = STATUS_TEXT; +const STATUS_TEXT2 = STATUS_TEXT1; +const BufWriter1 = BufWriter; +const BufWriter2 = BufWriter1; +const BufReader1 = BufReader; +const BufReader2 = BufReader1; +function chunkedBodyReader(h, r1) { + // Based on https://tools.ietf.org/html/rfc2616#section-19.4.6 + const tp = new TextProtoReader2(r1); + let finished = false; + const chunks = []; + async function read(buf) { + if (finished) return null; + const [chunk] = chunks; + if (chunk) { + const chunkRemaining = chunk.data.byteLength - chunk.offset; + const readLength = Math.min(chunkRemaining, buf.byteLength); + for(let i = 0; i < readLength; i++)buf[i] = chunk.data[chunk.offset + i]; + chunk.offset += readLength; + if (chunk.offset === chunk.data.byteLength) { + chunks.shift(); + // Consume \r\n; + if (await tp.readLine() === null) throw new Deno.errors.UnexpectedEof(); + } + return readLength; + } + const line = await tp.readLine(); + if (line === null) throw new Deno.errors.UnexpectedEof(); + // TODO: handle chunk extension + const [chunkSizeString] = line.split(";"); + const chunkSize = parseInt(chunkSizeString, 16); + if (Number.isNaN(chunkSize) || chunkSize < 0) throw new Error("Invalid chunk size"); + if (chunkSize > 0) { + if (chunkSize > buf.byteLength) { + let eof = await r1.readFull(buf); + if (eof === null) throw new Deno.errors.UnexpectedEof(); + const restChunk = new Uint8Array(chunkSize - buf.byteLength); + eof = await r1.readFull(restChunk); + if (eof === null) throw new Deno.errors.UnexpectedEof(); + else chunks.push({ + offset: 0, + data: restChunk + }); + return buf.byteLength; + } else { + const bufToFill = buf.subarray(0, chunkSize); + const eof = await r1.readFull(bufToFill); + if (eof === null) throw new Deno.errors.UnexpectedEof(); + // Consume \r\n + if (await tp.readLine() === null) throw new Deno.errors.UnexpectedEof(); + return chunkSize; + } + } else { + assert4(chunkSize === 0); + // Consume \r\n + if (await r1.readLine() === null) throw new Deno.errors.UnexpectedEof(); + await readTrailers(h, r1); + finished = true; + return null; } - return n; - } - constructor(r1){ - this.r = r1; - this.r = r1; } + return { + read + }; +} +const chunkedBodyReader1 = chunkedBodyReader; +const chunkedBodyReader2 = chunkedBodyReader1; +const emptyReader1 = emptyReader; +const emptyReader2 = emptyReader1; +const BufWriter3 = BufWriter1; +const encoder1 = encoder; +const encoder2 = encoder1; +function isProhibidedForTrailer(key) { + const s = new Set([ + "transfer-encoding", + "content-length", + "trailer" + ]); + return s.has(key.toLowerCase()); } -const TextProtoReader1 = TextProtoReader; -const TextProtoReader2 = TextProtoReader1; async function readTrailers(headers, r1) { const trailers = parseTrailer(headers.get("trailer")); if (trailers == null) return; @@ -874,8 +790,22 @@ async function readTrailers(headers, r1) { if (missingTrailers.length > 0) throw new Deno.errors.InvalidData(`Missing trailers: ${Deno.inspect(missingTrailers)}.`); headers.delete("trailer"); } +function parseTrailer(field) { + if (field == null) return undefined; + const trailerNames = field.split(",").map((v)=>v.trim().toLowerCase() + ); + if (trailerNames.length === 0) throw new Deno.errors.InvalidData("Empty trailer header."); + const prohibited = trailerNames.filter((k)=>isProhibidedForTrailer(k) + ); + if (prohibited.length > 0) throw new Deno.errors.InvalidData(`Prohibited trailer names: ${Deno.inspect(prohibited)}.`); + return new Headers(trailerNames.map((key)=>[ + key, + "" + ] + )); +} async function writeChunkedBody(w, r1) { - const writer = BufWriter2.create(w); + const writer = BufWriter3.create(w); for await (const chunk of Deno.iter(r1)){ if (chunk.byteLength <= 0) continue; const start = encoder2.encode(`${chunk.byteLength.toString(16)}\r\n`); @@ -892,7 +822,7 @@ async function writeTrailers(w, headers, trailers) { if (trailer === null) throw new TypeError("Missing trailer header."); const transferEncoding = headers.get("transfer-encoding"); if (transferEncoding === null || !transferEncoding.match(/^chunked/)) throw new TypeError(`Trailers are only allowed for "transfer-encoding: chunked", got "transfer-encoding: ${transferEncoding}".`); - const writer = BufWriter2.create(w); + const writer = BufWriter3.create(w); const trailerNames = trailer.split(",").map((s)=>s.trim().toLowerCase() ); const prohibitedTrailers = trailerNames.filter((k)=>isProhibidedForTrailer(k) @@ -912,7 +842,7 @@ async function writeResponse(w, r1) { const protoMinor = 1; const statusCode = r1.status || 200; const statusText = STATUS_TEXT2.get(statusCode); - const writer = BufWriter2.create(w); + const writer = BufWriter3.create(w); if (!statusText) throw new Deno.errors.InvalidData("Bad status code"); if (!r1.body) r1.body = new Uint8Array(); if (typeof r1.body === "string") r1.body = encoder2.encode(r1.body); @@ -926,16 +856,16 @@ async function writeResponse(w, r1) { out += `\r\n`; const header = encoder2.encode(out); const n = await writer.write(header); - assert2(n === header.byteLength); + assert4(n === header.byteLength); if (r1.body instanceof Uint8Array) { const n1 = await writer.write(r1.body); - assert2(n1 === r1.body.byteLength); + assert4(n1 === r1.body.byteLength); } else if (headers.has("content-length")) { const contentLength = headers.get("content-length"); - assert2(contentLength != null); + assert4(contentLength != null); const bodyLength = parseInt(contentLength); const n1 = await Deno.copy(r1.body, writer); - assert2(n1 === bodyLength); + assert4(n1 === bodyLength); } else await writeChunkedBody(writer, r1.body); if (r1.trailers) { const t = await r1.trailers(); @@ -944,70 +874,51 @@ async function writeResponse(w, r1) { await writer.flush(); } const writeResponse1 = writeResponse; -const BufReader2 = BufReader1; -const BufWriter3 = BufWriter1; const writeResponse2 = writeResponse1; -function chunkedBodyReader(h, r1) { - // Based on https://tools.ietf.org/html/rfc2616#section-19.4.6 - const tp = new TextProtoReader2(r1); - let finished = false; - const chunks = []; - async function read(buf) { - if (finished) return null; - const [chunk] = chunks; - if (chunk) { - const chunkRemaining = chunk.data.byteLength - chunk.offset; - const readLength = Math.min(chunkRemaining, buf.byteLength); - for(let i = 0; i < readLength; i++)buf[i] = chunk.data[chunk.offset + i]; - chunk.offset += readLength; - if (chunk.offset === chunk.data.byteLength) { - chunks.shift(); - // Consume \r\n; - if (await tp.readLine() === null) throw new Deno.errors.UnexpectedEof(); - } - return readLength; - } - const line = await tp.readLine(); - if (line === null) throw new Deno.errors.UnexpectedEof(); - // TODO: handle chunk extension - const [chunkSizeString] = line.split(";"); - const chunkSize = parseInt(chunkSizeString, 16); - if (Number.isNaN(chunkSize) || chunkSize < 0) throw new Error("Invalid chunk size"); - if (chunkSize > 0) { - if (chunkSize > buf.byteLength) { - let eof = await r1.readFull(buf); - if (eof === null) throw new Deno.errors.UnexpectedEof(); - const restChunk = new Uint8Array(chunkSize - buf.byteLength); - eof = await r1.readFull(restChunk); - if (eof === null) throw new Deno.errors.UnexpectedEof(); - else chunks.push({ - offset: 0, - data: restChunk - }); - return buf.byteLength; - } else { - const bufToFill = buf.subarray(0, chunkSize); - const eof = await r1.readFull(bufToFill); - if (eof === null) throw new Deno.errors.UnexpectedEof(); - // Consume \r\n - if (await tp.readLine() === null) throw new Deno.errors.UnexpectedEof(); - return chunkSize; +function parseHTTPVersion(vers) { + switch(vers){ + case "HTTP/1.1": + return [ + 1, + 1 + ]; + case "HTTP/1.0": + return [ + 1, + 0 + ]; + default: + { + const Big = 1000000; // arbitrary upper bound + if (!vers.startsWith("HTTP/")) break; + const dot = vers.indexOf("."); + if (dot < 0) break; + const majorStr = vers.substring(vers.indexOf("/") + 1, dot); + const major = Number(majorStr); + if (!Number.isInteger(major) || major < 0 || major > Big) break; + const minorStr = vers.substring(dot + 1); + const minor = Number(minorStr); + if (!Number.isInteger(minor) || minor < 0 || minor > Big) break; + return [ + major, + minor + ]; } - } else { - assert2(chunkSize === 0); - // Consume \r\n - if (await r1.readLine() === null) throw new Deno.errors.UnexpectedEof(); - await readTrailers(h, r1); - finished = true; - return null; - } } - return { - read - }; + throw new Error(`malformed HTTP version ${vers}`); } -const chunkedBodyReader1 = chunkedBodyReader; -const chunkedBodyReader2 = chunkedBodyReader1; +function deferred() { + let methods; + const promise = new Promise((resolve, reject)=>{ + methods = { + resolve, + reject + }; + }); + return Object.assign(promise, methods); +} +const deferred1 = deferred; +const deferred2 = deferred1; class ServerRequest { /** * Value of Content-Length header. @@ -1037,7 +948,7 @@ class ServerRequest { if (transferEncoding != null) { const parts = transferEncoding.split(",").map((e)=>e.trim().toLowerCase() ); - assert3(parts.includes("chunked"), 'transfer-encoding must include "chunked" if content-length is not set'); + assert2(parts.includes("chunked"), 'transfer-encoding must include "chunked" if content-length is not set'); this._body = chunkedBodyReader2(this.headers, this.r); } else // Neither content-length nor transfer-encoding: chunked this._body = emptyReader2(); @@ -1073,7 +984,7 @@ class ServerRequest { this.finalized = true; } constructor(){ - this.done = deferred4(); + this.done = deferred2(); this._contentLength = undefined; this._body = null; this.finalized = false; @@ -1096,8 +1007,83 @@ async function readRequest(conn, bufr) { fixLength(req); return req; } +const deferred3 = deferred; +const deferred4 = deferred3; +function fixLength(req) { + const contentLength = req.headers.get("Content-Length"); + if (contentLength) { + const arrClen = contentLength.split(","); + if (arrClen.length > 1) { + const distinct = [ + ...new Set(arrClen.map((e)=>e.trim() + )) + ]; + if (distinct.length > 1) throw Error("cannot contain multiple Content-Length headers"); + else req.headers.set("Content-Length", distinct[0]); + } + const c = req.headers.get("Content-Length"); + if (req.method === "HEAD" && c && c !== "0") throw Error("http: method cannot contain a Content-Length"); + if (c && req.headers.has("transfer-encoding")) // A sender MUST NOT send a Content-Length header field in any message + // that contains a Transfer-Encoding header field. + // rfc: https://tools.ietf.org/html/rfc7230#section-3.3.2 + throw new Error("http: Transfer-Encoding and Content-Length cannot be send together"); + } +} +var tmp = Symbol.asyncIterator; +class MuxAsyncIterator { + add(iterator) { + ++this.iteratorCount; + this.callIteratorNext(iterator); + } + async callIteratorNext(iterator) { + try { + const { value , done } = await iterator.next(); + if (done) --this.iteratorCount; + else this.yields.push({ + iterator, + value + }); + } catch (e) { + this.throws.push(e); + } + this.signal.resolve(); + } + async *iterate() { + while(this.iteratorCount > 0){ + // Sleep until any of the wrapped iterators yields. + await this.signal; + // Note that while we're looping over `yields`, new items may be added. + for(let i = 0; i < this.yields.length; i++){ + const { iterator , value } = this.yields[i]; + yield value; + this.callIteratorNext(iterator); + } + if (this.throws.length) { + for (const e of this.throws)throw e; + this.throws.length = 0; + } + // Clear the `yields` list and reset the `signal` promise. + this.yields.length = 0; + this.signal = deferred4(); + } + } + [tmp]() { + return this.iterate(); + } + constructor(){ + this.iteratorCount = 0; + this.yields = []; + this.throws = []; + this.signal = deferred4(); + } +} const readRequest1 = readRequest; const readRequest2 = readRequest1; +const encode1 = encode; +const encode2 = encode1; +var tmp1 = Symbol.asyncIterator; +const MuxAsyncIterator1 = MuxAsyncIterator; +const MuxAsyncIterator2 = MuxAsyncIterator1; class Server { close() { this.closing = true; @@ -1112,7 +1098,7 @@ class Server { // Yields all HTTP requests on a single TCP connection. async *iterateHttpRequests(conn) { const reader = new BufReader2(conn); - const writer = new BufWriter3(conn); + const writer = new BufWriter2(conn); while(!this.closing){ let request; try { @@ -1185,6 +1171,20 @@ class Server { this.connections = []; } } +function _parseAddrFromStr(addr) { + let url; + try { + const host = addr.startsWith(":") ? `0.0.0.0${addr}` : addr; + url = new URL(`http://${host}`); + } catch { + throw new TypeError("Invalid address."); + } + if (url.username || url.password || url.pathname != "/" || url.search || url.hash) throw new TypeError("Invalid address."); + return { + hostname: url.hostname, + port: url.port === "" ? 80 : Number(url.port) + }; +} function serve(addr) { if (typeof addr === "string") addr = _parseAddrFromStr(addr); const listener1 = Deno.listen(addr); diff --git a/spack/tests/pass/deno-001/simple-1/output/entry.js b/spack/tests/pass/deno-001/simple-1/output/entry.js index edd8686fa22e..00342421c7b0 100644 --- a/spack/tests/pass/deno-001/simple-1/output/entry.js +++ b/spack/tests/pass/deno-001/simple-1/output/entry.js @@ -1,82 +1,3 @@ -function deferred() { - let methods; - const promise = new Promise((resolve, reject)=>{ - methods = { - resolve, - reject - }; - }); - return Object.assign(promise, methods); -} -const deferred1 = deferred; -const deferred2 = deferred1; -var tmp = Symbol.asyncIterator; -class MuxAsyncIterator { - add(iterator) { - ++this.iteratorCount; - this.callIteratorNext(iterator); - } - async callIteratorNext(iterator) { - try { - const { value , done } = await iterator.next(); - if (done) --this.iteratorCount; - else this.yields.push({ - iterator, - value - }); - } catch (e) { - this.throws.push(e); - } - this.signal.resolve(); - } - async *iterate() { - while(this.iteratorCount > 0){ - // Sleep until any of the wrapped iterators yields. - await this.signal; - // Note that while we're looping over `yields`, new items may be added. - for(let i = 0; i < this.yields.length; i++){ - const { iterator , value } = this.yields[i]; - yield value; - this.callIteratorNext(iterator); - } - if (this.throws.length) { - for (const e of this.throws)throw e; - this.throws.length = 0; - } - // Clear the `yields` list and reset the `signal` promise. - this.yields.length = 0; - this.signal = deferred2(); - } - } - [tmp]() { - return this.iterate(); - } - constructor(){ - this.iteratorCount = 0; - this.yields = []; - this.throws = []; - this.signal = deferred2(); - } -} -const deferred3 = deferred; -const MuxAsyncIterator1 = MuxAsyncIterator; -const deferred4 = deferred3; -const MuxAsyncIterator2 = MuxAsyncIterator1; -var tmp1 = Symbol.asyncIterator; -function _parseAddrFromStr(addr) { - let url; - try { - const host = addr.startsWith(":") ? `0.0.0.0${addr}` : addr; - url = new URL(`http://${host}`); - } catch { - throw new TypeError("Invalid address."); - } - if (url.username || url.password || url.pathname != "/" || url.search || url.hash) throw new TypeError("Invalid address."); - return { - hostname: url.hostname, - port: url.port === "" ? 80 : Number(url.port) - }; -} function emptyReader() { return { read (_) { @@ -104,6 +25,65 @@ function bodyReader(contentLength, r) { read }; } +function chunkedBodyReader(h, r) { + // Based on https://tools.ietf.org/html/rfc2616#section-19.4.6 + const tp = new TextProtoReader(r); + let finished = false; + const chunks = []; + async function read(buf) { + if (finished) return null; + const [chunk] = chunks; + if (chunk) { + const chunkRemaining = chunk.data.byteLength - chunk.offset; + const readLength = Math.min(chunkRemaining, buf.byteLength); + for(let i = 0; i < readLength; i++)buf[i] = chunk.data[chunk.offset + i]; + chunk.offset += readLength; + if (chunk.offset === chunk.data.byteLength) { + chunks.shift(); + // Consume \r\n; + if (await tp.readLine() === null) throw new Deno.errors.UnexpectedEof(); + } + return readLength; + } + const line = await tp.readLine(); + if (line === null) throw new Deno.errors.UnexpectedEof(); + // TODO: handle chunk extension + const [chunkSizeString] = line.split(";"); + const chunkSize = parseInt(chunkSizeString, 16); + if (Number.isNaN(chunkSize) || chunkSize < 0) throw new Error("Invalid chunk size"); + if (chunkSize > 0) { + if (chunkSize > buf.byteLength) { + let eof = await r.readFull(buf); + if (eof === null) throw new Deno.errors.UnexpectedEof(); + const restChunk = new Uint8Array(chunkSize - buf.byteLength); + eof = await r.readFull(restChunk); + if (eof === null) throw new Deno.errors.UnexpectedEof(); + else chunks.push({ + offset: 0, + data: restChunk + }); + return buf.byteLength; + } else { + const bufToFill = buf.subarray(0, chunkSize); + const eof = await r.readFull(bufToFill); + if (eof === null) throw new Deno.errors.UnexpectedEof(); + // Consume \r\n + if (await tp.readLine() === null) throw new Deno.errors.UnexpectedEof(); + return chunkSize; + } + } else { + assert(chunkSize === 0); + // Consume \r\n + if (await r.readLine() === null) throw new Deno.errors.UnexpectedEof(); + await readTrailers(h, r); + finished = true; + return null; + } + } + return { + read + }; +} function isProhibidedForTrailer(key) { const s = new Set([ "transfer-encoding", @@ -112,6 +92,26 @@ function isProhibidedForTrailer(key) { ]); return s.has(key.toLowerCase()); } +async function readTrailers(headers, r) { + const trailers = parseTrailer(headers.get("trailer")); + if (trailers == null) return; + const trailerNames = [ + ...trailers.keys() + ]; + const tp = new TextProtoReader(r); + const result = await tp.readMIMEHeader(); + if (result == null) throw new Deno.errors.InvalidData("Missing trailer header."); + const undeclared = [ + ...result.keys() + ].filter((k)=>!trailerNames.includes(k) + ); + if (undeclared.length > 0) throw new Deno.errors.InvalidData(`Undeclared trailers: ${Deno.inspect(undeclared)}.`); + for (const [k, v] of result)headers.append(k, v); + const missingTrailers = trailerNames.filter((k1)=>!result.has(k1) + ); + if (missingTrailers.length > 0) throw new Deno.errors.InvalidData(`Missing trailers: ${Deno.inspect(missingTrailers)}.`); + headers.delete("trailer"); +} function parseTrailer(field) { if (field == null) return undefined; const trailerNames = field.split(",").map((v)=>v.trim().toLowerCase() @@ -227,113 +227,26 @@ function parseHTTPVersion(vers) { } throw new Error(`malformed HTTP version ${vers}`); } -function fixLength(req) { - const contentLength = req.headers.get("Content-Length"); - if (contentLength) { - const arrClen = contentLength.split(","); - if (arrClen.length > 1) { - const distinct = [ - ...new Set(arrClen.map((e)=>e.trim() - )) - ]; - if (distinct.length > 1) throw Error("cannot contain multiple Content-Length headers"); - else req.headers.set("Content-Length", distinct[0]); - } - const c = req.headers.get("Content-Length"); - if (req.method === "HEAD" && c && c !== "0") throw Error("http: method cannot contain a Content-Length"); - if (c && req.headers.has("transfer-encoding")) // A sender MUST NOT send a Content-Length header field in any message - // that contains a Transfer-Encoding header field. - // rfc: https://tools.ietf.org/html/rfc7230#section-3.3.2 - throw new Error("http: Transfer-Encoding and Content-Length cannot be send together"); - } -} -const emptyReader1 = emptyReader; const bodyReader1 = bodyReader; -const writeResponse1 = writeResponse; const bodyReader2 = bodyReader1; +const chunkedBodyReader1 = chunkedBodyReader; +const chunkedBodyReader2 = chunkedBodyReader1; +const emptyReader1 = emptyReader; const emptyReader2 = emptyReader1; +const writeResponse1 = writeResponse; const writeResponse2 = writeResponse1; -async function readTrailers(headers, r) { - const trailers = parseTrailer(headers.get("trailer")); - if (trailers == null) return; - const trailerNames = [ - ...trailers.keys() - ]; - const tp = new TextProtoReader(r); - const result = await tp.readMIMEHeader(); - if (result == null) throw new Deno.errors.InvalidData("Missing trailer header."); - const undeclared = [ - ...result.keys() - ].filter((k)=>!trailerNames.includes(k) - ); - if (undeclared.length > 0) throw new Deno.errors.InvalidData(`Undeclared trailers: ${Deno.inspect(undeclared)}.`); - for (const [k, v] of result)headers.append(k, v); - const missingTrailers = trailerNames.filter((k1)=>!result.has(k1) - ); - if (missingTrailers.length > 0) throw new Deno.errors.InvalidData(`Missing trailers: ${Deno.inspect(missingTrailers)}.`); - headers.delete("trailer"); -} -function chunkedBodyReader(h, r) { - // Based on https://tools.ietf.org/html/rfc2616#section-19.4.6 - const tp = new TextProtoReader(r); - let finished = false; - const chunks = []; - async function read(buf) { - if (finished) return null; - const [chunk] = chunks; - if (chunk) { - const chunkRemaining = chunk.data.byteLength - chunk.offset; - const readLength = Math.min(chunkRemaining, buf.byteLength); - for(let i = 0; i < readLength; i++)buf[i] = chunk.data[chunk.offset + i]; - chunk.offset += readLength; - if (chunk.offset === chunk.data.byteLength) { - chunks.shift(); - // Consume \r\n; - if (await tp.readLine() === null) throw new Deno.errors.UnexpectedEof(); - } - return readLength; - } - const line = await tp.readLine(); - if (line === null) throw new Deno.errors.UnexpectedEof(); - // TODO: handle chunk extension - const [chunkSizeString] = line.split(";"); - const chunkSize = parseInt(chunkSizeString, 16); - if (Number.isNaN(chunkSize) || chunkSize < 0) throw new Error("Invalid chunk size"); - if (chunkSize > 0) { - if (chunkSize > buf.byteLength) { - let eof = await r.readFull(buf); - if (eof === null) throw new Deno.errors.UnexpectedEof(); - const restChunk = new Uint8Array(chunkSize - buf.byteLength); - eof = await r.readFull(restChunk); - if (eof === null) throw new Deno.errors.UnexpectedEof(); - else chunks.push({ - offset: 0, - data: restChunk - }); - return buf.byteLength; - } else { - const bufToFill = buf.subarray(0, chunkSize); - const eof = await r.readFull(bufToFill); - if (eof === null) throw new Deno.errors.UnexpectedEof(); - // Consume \r\n - if (await tp.readLine() === null) throw new Deno.errors.UnexpectedEof(); - return chunkSize; - } - } else { - assert(chunkSize === 0); - // Consume \r\n - if (await r.readLine() === null) throw new Deno.errors.UnexpectedEof(); - await readTrailers(h, r); - finished = true; - return null; - } - } - return { - read - }; +function deferred() { + let methods; + const promise = new Promise((resolve, reject)=>{ + methods = { + resolve, + reject + }; + }); + return Object.assign(promise, methods); } -const chunkedBodyReader1 = chunkedBodyReader; -const chunkedBodyReader2 = chunkedBodyReader1; +const deferred1 = deferred; +const deferred2 = deferred1; class ServerRequest { /** * Value of Content-Length header. @@ -399,7 +312,7 @@ class ServerRequest { this.finalized = true; } constructor(){ - this.done = deferred4(); + this.done = deferred2(); this._contentLength = undefined; this._body = null; this.finalized = false; @@ -422,8 +335,81 @@ async function readRequest(conn, bufr) { fixLength(req); return req; } +const deferred3 = deferred; +const deferred4 = deferred3; +function fixLength(req) { + const contentLength = req.headers.get("Content-Length"); + if (contentLength) { + const arrClen = contentLength.split(","); + if (arrClen.length > 1) { + const distinct = [ + ...new Set(arrClen.map((e)=>e.trim() + )) + ]; + if (distinct.length > 1) throw Error("cannot contain multiple Content-Length headers"); + else req.headers.set("Content-Length", distinct[0]); + } + const c = req.headers.get("Content-Length"); + if (req.method === "HEAD" && c && c !== "0") throw Error("http: method cannot contain a Content-Length"); + if (c && req.headers.has("transfer-encoding")) // A sender MUST NOT send a Content-Length header field in any message + // that contains a Transfer-Encoding header field. + // rfc: https://tools.ietf.org/html/rfc7230#section-3.3.2 + throw new Error("http: Transfer-Encoding and Content-Length cannot be send together"); + } +} +var tmp = Symbol.asyncIterator; +class MuxAsyncIterator { + add(iterator) { + ++this.iteratorCount; + this.callIteratorNext(iterator); + } + async callIteratorNext(iterator) { + try { + const { value , done } = await iterator.next(); + if (done) --this.iteratorCount; + else this.yields.push({ + iterator, + value + }); + } catch (e) { + this.throws.push(e); + } + this.signal.resolve(); + } + async *iterate() { + while(this.iteratorCount > 0){ + // Sleep until any of the wrapped iterators yields. + await this.signal; + // Note that while we're looping over `yields`, new items may be added. + for(let i = 0; i < this.yields.length; i++){ + const { iterator , value } = this.yields[i]; + yield value; + this.callIteratorNext(iterator); + } + if (this.throws.length) { + for (const e of this.throws)throw e; + this.throws.length = 0; + } + // Clear the `yields` list and reset the `signal` promise. + this.yields.length = 0; + this.signal = deferred4(); + } + } + [tmp]() { + return this.iterate(); + } + constructor(){ + this.iteratorCount = 0; + this.yields = []; + this.throws = []; + this.signal = deferred4(); + } +} const readRequest1 = readRequest; const readRequest2 = readRequest1; +var tmp1 = Symbol.asyncIterator; +const MuxAsyncIterator1 = MuxAsyncIterator; +const MuxAsyncIterator2 = MuxAsyncIterator1; class Server { close() { this.closing = true; @@ -511,6 +497,20 @@ class Server { this.connections = []; } } +function _parseAddrFromStr(addr) { + let url; + try { + const host = addr.startsWith(":") ? `0.0.0.0${addr}` : addr; + url = new URL(`http://${host}`); + } catch { + throw new TypeError("Invalid address."); + } + if (url.username || url.password || url.pathname != "/" || url.search || url.hash) throw new TypeError("Invalid address."); + return { + hostname: url.hostname, + port: url.port === "" ? 80 : Number(url.port) + }; +} function serve(addr) { if (typeof addr === "string") addr = _parseAddrFromStr(addr); const listener1 = Deno.listen(addr); diff --git a/spack/tests/pass/deno-001/simple-2/output/entry.js b/spack/tests/pass/deno-001/simple-2/output/entry.js index d9ee6ff0e2d7..96f8d5bc7936 100644 --- a/spack/tests/pass/deno-001/simple-2/output/entry.js +++ b/spack/tests/pass/deno-001/simple-2/output/entry.js @@ -2,35 +2,35 @@ function deferred() { } const deferred1 = deferred; const deferred2 = deferred1; -class MuxAsyncIterator { - constructor(){ - this.signal = deferred2(); - } -} -const deferred3 = deferred; -const MuxAsyncIterator1 = MuxAsyncIterator; -const deferred4 = deferred3; -const MuxAsyncIterator2 = MuxAsyncIterator1; class ServerRequest { constructor(){ - this.done = deferred4(); + this.done = deferred2(); } } -async function listenAndServe(addr, handler) { -} const ServerRequest1 = ServerRequest; -const listenAndServe1 = listenAndServe; const ServerRequest2 = ServerRequest1; console.log(ServerRequest2); async function writeResponse(w, r) { } +const writeResponse1 = writeResponse; +const writeResponse2 = writeResponse1; async function readRequest(conn, bufr) { } -const writeResponse1 = writeResponse; const readRequest1 = readRequest; -const writeResponse2 = writeResponse1; const readRequest2 = readRequest1; -console.log(deferred4, writeResponse2, readRequest2, MuxAsyncIterator2); +const deferred3 = deferred; +const deferred4 = deferred3; +class MuxAsyncIterator { + constructor(){ + this.signal = deferred4(); + } +} +const MuxAsyncIterator1 = MuxAsyncIterator; +const MuxAsyncIterator2 = MuxAsyncIterator1; +console.log(deferred2, writeResponse2, readRequest2, MuxAsyncIterator2); +async function listenAndServe(addr, handler) { +} +const listenAndServe1 = listenAndServe; const listenAndServe2 = listenAndServe1; listenAndServe2({ port: 8080 diff --git a/spack/tests/pass/deno-001/simple-3/output/entry.js b/spack/tests/pass/deno-001/simple-3/output/entry.js index 29ffe6e6b593..e097d25ea73d 100644 --- a/spack/tests/pass/deno-001/simple-3/output/entry.js +++ b/spack/tests/pass/deno-001/simple-3/output/entry.js @@ -8,7 +8,7 @@ class MuxAsyncIterator { } } const deferred3 = deferred; -const MuxAsyncIterator1 = MuxAsyncIterator; const deferred4 = deferred3; +const MuxAsyncIterator1 = MuxAsyncIterator; const MuxAsyncIterator2 = MuxAsyncIterator1; console.log(deferred4, MuxAsyncIterator2); diff --git a/spack/tests/pass/deno-001/simple-4/output/entry.js b/spack/tests/pass/deno-001/simple-4/output/entry.js index f658922e262b..22b1a2334753 100644 --- a/spack/tests/pass/deno-001/simple-4/output/entry.js +++ b/spack/tests/pass/deno-001/simple-4/output/entry.js @@ -2,14 +2,14 @@ function deferred() { } const deferred1 = deferred; const deferred2 = deferred1; +const deferred3 = deferred2; +const deferred4 = deferred1; class MuxAsyncIterator { constructor(){ - this.signal = deferred2(); + this.signal = deferred4(); } } const MuxAsyncIterator1 = MuxAsyncIterator; -const deferred3 = deferred1; const MuxAsyncIterator2 = MuxAsyncIterator1; -const deferred4 = deferred3; const MuxAsyncIterator3 = MuxAsyncIterator2; -console.log(deferred4, MuxAsyncIterator3); +console.log(deferred3, MuxAsyncIterator3); diff --git a/spack/tests/pass/deno-001/simple-5/output/entry.js b/spack/tests/pass/deno-001/simple-5/output/entry.js index a50f4d1da1c0..5719ccb64deb 100644 --- a/spack/tests/pass/deno-001/simple-5/output/entry.js +++ b/spack/tests/pass/deno-001/simple-5/output/entry.js @@ -1,11 +1,11 @@ function deferred() { } -const deferred1 = deferred; function MuxAsyncIterator() { } -const MuxAsyncIterator1 = MuxAsyncIterator; +const deferred1 = deferred; const deferred2 = deferred1; -const MuxAsyncIterator2 = MuxAsyncIterator1; const deferred3 = deferred2; +const MuxAsyncIterator1 = MuxAsyncIterator; +const MuxAsyncIterator2 = MuxAsyncIterator1; const MuxAsyncIterator3 = MuxAsyncIterator2; console.log(deferred3, MuxAsyncIterator3); diff --git a/spack/tests/pass/drop-unused/side-effect/import-multi/output/entry.js b/spack/tests/pass/drop-unused/side-effect/import-multi/output/entry.js index 756f28690d2a..b16692f3e1d1 100644 --- a/spack/tests/pass/drop-unused/side-effect/import-multi/output/entry.js +++ b/spack/tests/pass/drop-unused/side-effect/import-multi/output/entry.js @@ -1,9 +1,9 @@ function a() { } -const a1 = a; -const a2 = a1; function b() { } +const a1 = a; +const a2 = a1; const b1 = b; const b2 = b1; console.log(a2, b2); diff --git a/spack/tests/pass/export/all-2/output/entry.js b/spack/tests/pass/export/all-2/output/entry.js index 13512d599af7..215da027d62c 100644 --- a/spack/tests/pass/export/all-2/output/entry.js +++ b/spack/tests/pass/export/all-2/output/entry.js @@ -2,8 +2,8 @@ class Root { } const Root1 = Root; const Root2 = Root1; -export class A extends Root2 { -} const Root3 = Root1; -export class B extends Root3 { +export class A extends Root3 { +} +export class B extends Root2 { } diff --git a/spack/tests/pass/export/complex-1/output/entry.js b/spack/tests/pass/export/complex-1/output/entry.js index 71700f00bd97..fb1694004fb7 100644 --- a/spack/tests/pass/export/complex-1/output/entry.js +++ b/spack/tests/pass/export/complex-1/output/entry.js @@ -1,8 +1,8 @@ const b3 = '1'; +const a2 = '1'; const b1 = b3; const b2 = b1; -const a2 = '1'; -const a1 = a2; console.log(b2); +const a1 = a2; export { a1 as a }; export { b1 as b }; diff --git a/spack/tests/pass/import-with-export/simple-1/output/entry.js b/spack/tests/pass/import-with-export/simple-1/output/entry.js index c59e663769eb..99c7d736fd84 100644 --- a/spack/tests/pass/import-with-export/simple-1/output/entry.js +++ b/spack/tests/pass/import-with-export/simple-1/output/entry.js @@ -1,6 +1,6 @@ export const a = 1; -const a1 = a; export const b = 2; export const c = 3; +const a1 = a; const a2 = a1; console.log(a2); diff --git a/spack/tests/pass/import/commons-default/output/entry.js b/spack/tests/pass/import/commons-default/output/entry.js index ebe54fec4da9..5ffe6603a73e 100644 --- a/spack/tests/pass/import/commons-default/output/entry.js +++ b/spack/tests/pass/import/commons-default/output/entry.js @@ -2,13 +2,13 @@ class Common { } const __default = Common; const Common1 = __default; -class A extends Common1 { -} -const __default1 = A; -const A1 = __default1; const Common2 = __default; -class B extends Common2 { +class A extends Common2 { +} +class B extends Common1 { } -const __default2 = B; -const B1 = __default2; +const __default1 = B; +const B1 = __default1; +const __default2 = A; +const A1 = __default2; console.log(A1, B1); diff --git a/spack/tests/pass/import/commons-named/output/entry.js b/spack/tests/pass/import/commons-named/output/entry.js index 195b1a0bdebe..4e657eb1b676 100644 --- a/spack/tests/pass/import/commons-named/output/entry.js +++ b/spack/tests/pass/import/commons-named/output/entry.js @@ -2,13 +2,13 @@ class Common { } const Common1 = Common; const Common2 = Common1; -class A extends Common2 { -} -const __default = A; -const A1 = __default; const Common3 = Common1; -class B extends Common3 { +class A extends Common3 { +} +class B extends Common2 { } -const __default1 = B; -const B1 = __default1; +const __default = B; +const B1 = __default; +const __default1 = A; +const A1 = __default1; console.log(A1, B1); diff --git a/spack/tests/pass/import/name-conflict/simple/output/entry.js b/spack/tests/pass/import/name-conflict/simple/output/entry.js index 76f8940fffa2..ea64caa7c6d9 100644 --- a/spack/tests/pass/import/name-conflict/simple/output/entry.js +++ b/spack/tests/pass/import/name-conflict/simple/output/entry.js @@ -1,6 +1,6 @@ const foo = 10; const bar = foo; +const foo1 = 5; const bar1 = bar; const bar2 = bar1; -const foo1 = 5; console.log(foo1, bar2); diff --git a/spack/tests/pass/import/namespace/name-conflict/in-entry/output/entry.js b/spack/tests/pass/import/namespace/name-conflict/in-entry/output/entry.js index 24ecccd58b8c..78d114bb8c59 100644 --- a/spack/tests/pass/import/namespace/name-conflict/in-entry/output/entry.js +++ b/spack/tests/pass/import/namespace/name-conflict/in-entry/output/entry.js @@ -2,8 +2,8 @@ function a() { } function foo() { } -const a1 = a; -const foo1 = foo; -function foo2() { +function foo1() { } -console.log(foo2(), a1(), foo1()); +const a1 = a; +const foo2 = foo; +console.log(foo1(), a1(), foo2()); diff --git a/spack/tests/pass/issue-1138/example-1/output/entry.js b/spack/tests/pass/issue-1138/example-1/output/entry.js index 77f4eeb9ff00..81cf4a5dc446 100644 --- a/spack/tests/pass/issue-1138/example-1/output/entry.js +++ b/spack/tests/pass/issue-1138/example-1/output/entry.js @@ -1,8 +1,8 @@ var a = "a"; +var o = { +}; const a1 = a; const a2 = a1; const defaultA = a2; -var o = { -}; var _a = o.a, a3 = _a === void 0 ? defaultA : _a; console.log(a3); diff --git a/spack/tests/pass/issue-1138/example-2/output/entry.js b/spack/tests/pass/issue-1138/example-2/output/entry.js index 880631f77b44..d60eadd60164 100644 --- a/spack/tests/pass/issue-1138/example-2/output/entry.js +++ b/spack/tests/pass/issue-1138/example-2/output/entry.js @@ -1,8 +1,8 @@ var defaultA = "a"; +var o = { +}; const defaultA1 = defaultA; const defaultA2 = defaultA1; const defaultA3 = defaultA2; -var o = { -}; var _a = o.a, a = _a === void 0 ? defaultA3 : _a; console.log(a); diff --git a/spack/tests/pass/issue-1138/example-3/output/entry.js b/spack/tests/pass/issue-1138/example-3/output/entry.js index 9925683e3686..2aa0ef5478aa 100644 --- a/spack/tests/pass/issue-1138/example-3/output/entry.js +++ b/spack/tests/pass/issue-1138/example-3/output/entry.js @@ -1,8 +1,8 @@ const a = "a"; +const o = { +}; const a1 = a; const a2 = a1; const defaultA = a2; -const o = { -}; const { a: a3 = defaultA } = o; console.log(a3); diff --git a/spack/tests/pass/issue-1139/example-1/output/entry.js b/spack/tests/pass/issue-1139/example-1/output/entry.js index 58c7e46e926c..98a7931201d4 100644 --- a/spack/tests/pass/issue-1139/example-1/output/entry.js +++ b/spack/tests/pass/issue-1139/example-1/output/entry.js @@ -1,11 +1,11 @@ +function f2() { + console.log("f2"); +} function f1() { console.log("f1"); } const f11 = f1; -function f2() { - console.log("f2"); -} +export { f11 as f1 }; f11(); const f21 = f2; f21(); -export { f11 as f1 }; diff --git a/spack/tests/pass/issue-1139/example-2/output/entry.js b/spack/tests/pass/issue-1139/example-2/output/entry.js index 3700578b7830..0f312344d172 100644 --- a/spack/tests/pass/issue-1139/example-2/output/entry.js +++ b/spack/tests/pass/issue-1139/example-2/output/entry.js @@ -1,10 +1,10 @@ +function f2() { + console.log("f2"); +} function f1() { console.log("f1"); } const f11 = f1; -function f2() { - console.log("f2"); -} f11(); const f21 = f2; f21(); diff --git a/spack/tests/pass/merge/basic/output/entry.js b/spack/tests/pass/merge/basic/output/entry.js index 1bc2196b1c40..a913b293c68e 100644 --- a/spack/tests/pass/merge/basic/output/entry.js +++ b/spack/tests/pass/merge/basic/output/entry.js @@ -1,7 +1,7 @@ const a3 = 'a.js'; +const b3 = 'b.js'; const a1 = a3; const a2 = a1; -const b3 = 'b.js'; const b1 = b3; const b2 = b1; export { a2 as a, b2 as b }; diff --git a/spack/tests/pass/merge/name-conflict/simple/output/entry.js b/spack/tests/pass/merge/name-conflict/simple/output/entry.js index 89eb62db7e42..d729265e7b46 100644 --- a/spack/tests/pass/merge/name-conflict/simple/output/entry.js +++ b/spack/tests/pass/merge/name-conflict/simple/output/entry.js @@ -1,13 +1,13 @@ -function foo() { +const a3 = foo1(); +function foo1() { return 1; } -function foo1() { +const b3 = foo2(); +function foo2() { return 2; } -const a3 = foo(); const a1 = a3; const a2 = a1; -const b3 = foo1(); const b1 = b3; const b2 = b1; export { a2 as a, b2 as b }; diff --git a/spack/tests/pass/node-modules/library/simple/output/entry.js b/spack/tests/pass/node-modules/library/simple/output/entry.js index bddc927b1059..7f58571374b9 100644 --- a/spack/tests/pass/node-modules/library/simple/output/entry.js +++ b/spack/tests/pass/node-modules/library/simple/output/entry.js @@ -14,14 +14,14 @@ function __spack_require__(mod) { }; } var load = __spack_require__.bind(void 0, function(module, exports) { - var load1 = __spack_require__.bind(void 0, function(module1, exports1) /*! + var load1 = __spack_require__.bind(void 0, function(module1, exports1) { + /*! * node-progress * Copyright(c) 2011 TJ Holowaychuk * MIT Licensed */ /** * Expose `ProgressBar`. - */ { - exports1 = module1.exports = ProgressBar; + */ exports1 = module1.exports = ProgressBar; /** * Initialize a `ProgressBar` with the given `fmt` string and `options` or * `total`. diff --git a/spack/tests/pass/pr-1105/example-2/output/entry.js b/spack/tests/pass/pr-1105/example-2/output/entry.js index e900d8092309..bcad0c96a9ea 100644 --- a/spack/tests/pass/pr-1105/example-2/output/entry.js +++ b/spack/tests/pass/pr-1105/example-2/output/entry.js @@ -1,17 +1,17 @@ +const b = "b"; +const b1 = b; +console.log(b1); // "b" const mod = function() { const c = "c"; - const c1 = c; class C { } const __default = C; + const c1 = c; return { c, default: __default }; }(); const c = mod; -const b = "b"; const c1 = c; -const b1 = b; -console.log(b1); // "b" console.log(c1); // { c: "c", default: class C } diff --git a/spack/tests/pass/pr-1105/example-7/output/entry.js b/spack/tests/pass/pr-1105/example-7/output/entry.js index b455a906336f..27f3508121f3 100644 --- a/spack/tests/pass/pr-1105/example-7/output/entry.js +++ b/spack/tests/pass/pr-1105/example-7/output/entry.js @@ -9,8 +9,8 @@ const importMeta1 = { const isMain = importMeta1.main; const modUrl = importMeta1.url; const isMain1 = isMain; -const modUrl1 = modUrl; const isMain2 = isMain1; +const modUrl1 = modUrl; const modUrl2 = modUrl1; console.log(isMain2, modUrl2); console.log(importMeta); diff --git a/spack/tests/pass/reexport/default-1-simple/output/entry.js b/spack/tests/pass/reexport/default-1-simple/output/entry.js index 16fc66ed254b..8b69425f1f63 100644 --- a/spack/tests/pass/reexport/default-1-simple/output/entry.js +++ b/spack/tests/pass/reexport/default-1-simple/output/entry.js @@ -1,6 +1,6 @@ const b = 1; -const __default = b; console.log('b'); +const __default = b; const a1 = __default; console.log('a.js'); export { a1 as a }; diff --git a/spack/tests/pass/reexport/named-1-alias/output/entry.js b/spack/tests/pass/reexport/named-1-alias/output/entry.js index 3f2e7dfb1f54..46ecab0e3fdf 100644 --- a/spack/tests/pass/reexport/named-1-alias/output/entry.js +++ b/spack/tests/pass/reexport/named-1-alias/output/entry.js @@ -1,6 +1,6 @@ const b = 1; -const b1 = b; console.log('b', b); +const b1 = b; const a1 = b1; console.log('a'); export { a1 as a }; diff --git a/spack/tests/pass/reexport/named-1-orig/output/entry.js b/spack/tests/pass/reexport/named-1-orig/output/entry.js index af5f6801442d..ed9adb154e1f 100644 --- a/spack/tests/pass/reexport/named-1-orig/output/entry.js +++ b/spack/tests/pass/reexport/named-1-orig/output/entry.js @@ -1,6 +1,6 @@ const b = 1; -const b1 = b; console.log('b', b); +const b1 = b; const b2 = b1; console.log('a'); export { b2 as a }; diff --git a/spack/tests/pass/reexport/named-2-nested/output/entry.js b/spack/tests/pass/reexport/named-2-nested/output/entry.js index 1b0dc0e2f985..3005d5812048 100644 --- a/spack/tests/pass/reexport/named-2-nested/output/entry.js +++ b/spack/tests/pass/reexport/named-2-nested/output/entry.js @@ -1,6 +1,6 @@ const c = 1; -const c1 = c; console.log('c', c); +const c1 = c; const b = c1; console.log('b'); const b1 = b; diff --git a/spack/tests/pass/reexport/named-3-var/output/entry.js b/spack/tests/pass/reexport/named-3-var/output/entry.js index 0e07a74d97ba..5ee16126379b 100644 --- a/spack/tests/pass/reexport/named-3-var/output/entry.js +++ b/spack/tests/pass/reexport/named-3-var/output/entry.js @@ -1,6 +1,6 @@ const c = 1; -const c1 = c; console.log('c', c); +const c1 = c; const b = c1; console.log('b'); const a1 = b; diff --git a/spack/tests/pass/reexport/named-4-fn/output/entry.js b/spack/tests/pass/reexport/named-4-fn/output/entry.js index c5694ba2f6b4..e69756206a4e 100644 --- a/spack/tests/pass/reexport/named-4-fn/output/entry.js +++ b/spack/tests/pass/reexport/named-4-fn/output/entry.js @@ -1,8 +1,8 @@ function b() { console.log(b); } -const b1 = b; console.log('b', b); +const b1 = b; const b2 = b1; console.log('a'); export { b2 as a }; diff --git a/spack/tests/pass/reexport/named-5-class/output/entry.js b/spack/tests/pass/reexport/named-5-class/output/entry.js index 7fa86a3caa8b..c099e5cd2f11 100644 --- a/spack/tests/pass/reexport/named-5-class/output/entry.js +++ b/spack/tests/pass/reexport/named-5-class/output/entry.js @@ -1,7 +1,7 @@ class b { } -const b1 = b; console.log('b', b); +const b1 = b; const b2 = b1; console.log('a'); export { b2 as a }; diff --git a/spack/tests/pass/reexport/recursive-step1/output/entry.js b/spack/tests/pass/reexport/recursive-step1/output/entry.js index 362b6a0a3d17..5ce34391b83a 100644 --- a/spack/tests/pass/reexport/recursive-step1/output/entry.js +++ b/spack/tests/pass/reexport/recursive-step1/output/entry.js @@ -1,9 +1,9 @@ const c = 'c'; console.log('c'); +console.log('b'); const c1 = c; const b2 = c1; const __default = b2; -console.log('b'); const b1 = __default; console.log('a.js'); export { b1 as b }; diff --git a/spack/tests/pass/reexport/recursive-step2/output/entry.js b/spack/tests/pass/reexport/recursive-step2/output/entry.js index b1aafec7807a..d8157275c16c 100644 --- a/spack/tests/pass/reexport/recursive-step2/output/entry.js +++ b/spack/tests/pass/reexport/recursive-step2/output/entry.js @@ -2,10 +2,10 @@ const d = 1; const d1 = d; console.log('d'); console.log('c'); +console.log('b'); const c = d1; const b2 = c; const __default = b2; -console.log('b'); const b1 = __default; console.log('a.js'); export { b1 as b }; diff --git a/spack/tests/pass/reexport/recursive/output/entry.js b/spack/tests/pass/reexport/recursive/output/entry.js index d1ed6670e226..41f99e93dc42 100644 --- a/spack/tests/pass/reexport/recursive/output/entry.js +++ b/spack/tests/pass/reexport/recursive/output/entry.js @@ -5,10 +5,10 @@ const d = e1; const d1 = d; console.log('d'); console.log('c'); +console.log('b'); const c = d1; const b2 = c; const __default = b2; -console.log('b'); const b1 = __default; console.log('a.js'); export { b1 as b }; diff --git a/spack/tests/pass/transitive/import/simple-1/output/entry.js b/spack/tests/pass/transitive/import/simple-1/output/entry.js index d0aa17169d18..574e5903b4bc 100644 --- a/spack/tests/pass/transitive/import/simple-1/output/entry.js +++ b/spack/tests/pass/transitive/import/simple-1/output/entry.js @@ -1,6 +1,6 @@ const common = 1; const common1 = common; const common2 = common1; -console.log(common2, 'a.js'); const common3 = common1; -console.log(common3, 'b.js'); +console.log(common3, 'a.js'); +console.log(common2, 'b.js'); diff --git a/spack/tests/pass/transitive/import/simple-2/output/entry.js b/spack/tests/pass/transitive/import/simple-2/output/entry.js index 29cc78f4b63e..e9b6d708a218 100644 --- a/spack/tests/pass/transitive/import/simple-2/output/entry.js +++ b/spack/tests/pass/transitive/import/simple-2/output/entry.js @@ -1,22 +1,22 @@ -var common4; -try { - common4 = 4; -} catch (e) { -} -const common41 = common4; -const common42 = common41; -const common3 = 3; -const common31 = common3; const common1 = 1; const common11 = common1; -const common = 2; -const common2 = common; const common12 = common11; -const common21 = common2; -console.log('a', common12, common21); +const common3 = 3; +const common31 = common3; const common32 = common31; const common13 = common11; -console.log('b', common32, common13); +const common = 2; +const common2 = common; +const common21 = common2; +var common4; const common22 = common2; +console.log('a', common13, common22); const common33 = common31; -console.log('c', common42, common22, common33); +console.log('b', common33, common12); +try { + common4 = 4; +} catch (e) { +} +const common41 = common4; +const common42 = common41; +console.log('c', common42, common21, common32);