mirror of
https://github.com/swc-project/swc.git
synced 2024-12-18 03:01:48 +03:00
bundler: Fix issues (#1212)
swc_bundler: - Bundler rework. (denoland/deno#6802) - Reexports are not transitive. (denoland/deno#8246) - Dependencies of module with circular dependency. (denoland/deno#8302) - Order of injection between import vs export. (denoland/deno#8302) - `export *` in wrapped modules. (denoland/deno#8308, denoland/deno#8399) - `export { a as b }` in wrapped modules. - Fix denoland/deno#8314. - Fix denoland/deno#8325. - Fix denoland/deno#8344. - Make deno test verify exported names. - Handle `export * from './foo'`. swc_ecma_parser: - Don't panic on private name in interface (Closes #1211) swc_ecma_transforms: - dce: Prevent infinite loop - Faster constant propagation pass.
This commit is contained in:
parent
723970db1f
commit
4294b5e7ba
3
.github/workflows/integration.yml
vendored
3
.github/workflows/integration.yml
vendored
@ -28,7 +28,7 @@ jobs:
|
|||||||
- name: Set platform name
|
- name: Set platform name
|
||||||
run: |
|
run: |
|
||||||
export NODE_PLATFORM_NAME=$(node -e "console.log(require('os').platform())")
|
export NODE_PLATFORM_NAME=$(node -e "console.log(require('os').platform())")
|
||||||
echo "::set-env name=PLATFORM_NAME::$NODE_PLATFORM_NAME"
|
echo "name=PLATFORM_NAME::$NODE_PLATFORM_NAME" >> $GITHUB_ENV
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
- name: Prepare
|
- name: Prepare
|
||||||
@ -56,7 +56,6 @@ jobs:
|
|||||||
|
|
||||||
(cd integration-tests/three-js/build/test && qunit -r failonlyreporter unit/three.source.unit.js)
|
(cd integration-tests/three-js/build/test && qunit -r failonlyreporter unit/three.source.unit.js)
|
||||||
|
|
||||||
|
|
||||||
# terser: contains with statement in test
|
# terser: contains with statement in test
|
||||||
# Rome.js: I forgot the cause, but it didn't work.
|
# Rome.js: I forgot the cause, but it didn't work.
|
||||||
# jQuery: browser only (window.document is required)
|
# jQuery: browser only (window.document is required)
|
||||||
|
4
.github/workflows/publish-node.yml
vendored
4
.github/workflows/publish-node.yml
vendored
@ -37,7 +37,7 @@ jobs:
|
|||||||
- name: Set platform name
|
- name: Set platform name
|
||||||
run: |
|
run: |
|
||||||
export NODE_PLATFORM_NAME=$(node -e "console.log(require('os').platform())")
|
export NODE_PLATFORM_NAME=$(node -e "console.log(require('os').platform())")
|
||||||
echo "::set-env name=PLATFORM_NAME::$NODE_PLATFORM_NAME"
|
echo "name=PLATFORM_NAME::$NODE_PLATFORM_NAME" >> $GITHUB_ENV
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
- name: Install llvm
|
- name: Install llvm
|
||||||
@ -129,7 +129,7 @@ jobs:
|
|||||||
node-version: 12
|
node-version: 12
|
||||||
|
|
||||||
- name: Set release name
|
- name: Set release name
|
||||||
run: echo ::set-env name=RELEASE_VERSION::${GITHUB_REF#refs/*/}
|
run: echo name=RELEASE_VERSION::${GITHUB_REF#refs/*/} >> $GITHUB_ENV
|
||||||
|
|
||||||
# Do not cache node_modules, or yarn workspace links broken
|
# Do not cache node_modules, or yarn workspace links broken
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
|
@ -8,7 +8,7 @@ edition = "2018"
|
|||||||
license = "Apache-2.0/MIT"
|
license = "Apache-2.0/MIT"
|
||||||
name = "swc_bundler"
|
name = "swc_bundler"
|
||||||
repository = "https://github.com/swc-project/swc.git"
|
repository = "https://github.com/swc-project/swc.git"
|
||||||
version = "0.17.0"
|
version = "0.17.1"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
[features]
|
[features]
|
||||||
@ -33,7 +33,7 @@ swc_common = {version = "0.10.0", path = "../common"}
|
|||||||
swc_ecma_ast = {version = "0.35.0", path = "../ecmascript/ast"}
|
swc_ecma_ast = {version = "0.35.0", path = "../ecmascript/ast"}
|
||||||
swc_ecma_codegen = {version = "0.41.0", path = "../ecmascript/codegen"}
|
swc_ecma_codegen = {version = "0.41.0", path = "../ecmascript/codegen"}
|
||||||
swc_ecma_parser = {version = "0.43.0", path = "../ecmascript/parser"}
|
swc_ecma_parser = {version = "0.43.0", path = "../ecmascript/parser"}
|
||||||
swc_ecma_transforms = {version = "0.30.0", path = "../ecmascript/transforms"}
|
swc_ecma_transforms = {version = "0.30.1", path = "../ecmascript/transforms"}
|
||||||
swc_ecma_utils = {version = "0.25.0", path = "../ecmascript/utils"}
|
swc_ecma_utils = {version = "0.25.0", path = "../ecmascript/utils"}
|
||||||
swc_ecma_visit = {version = "0.21.0", path = "../ecmascript/visit"}
|
swc_ecma_visit = {version = "0.21.0", path = "../ecmascript/visit"}
|
||||||
|
|
||||||
@ -41,6 +41,7 @@ swc_ecma_visit = {version = "0.21.0", path = "../ecmascript/visit"}
|
|||||||
hex = "0.4"
|
hex = "0.4"
|
||||||
reqwest = {version = "0.10.8", features = ["blocking"]}
|
reqwest = {version = "0.10.8", features = ["blocking"]}
|
||||||
sha-1 = "0.9"
|
sha-1 = "0.9"
|
||||||
|
swc_ecma_transforms = {version = "0.30.1", path = "../ecmascript/transforms", features = ["react"]}
|
||||||
tempfile = "3.1.0"
|
tempfile = "3.1.0"
|
||||||
testing = {version = "0.10.0", path = "../testing"}
|
testing = {version = "0.10.0", path = "../testing"}
|
||||||
url = "2.1.1"
|
url = "2.1.1"
|
||||||
|
5
bundler/scripts/test.sh
Executable file
5
bundler/scripts/test.sh
Executable file
@ -0,0 +1,5 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -eux
|
||||||
|
|
||||||
|
cargo test --test fixture
|
||||||
|
cargo test --test deno $@
|
@ -1,13 +1,16 @@
|
|||||||
use super::{
|
use super::plan::CircularPlan;
|
||||||
merge::{ImportDropper, Unexporter},
|
use crate::{
|
||||||
plan::{CircularPlan, Plan},
|
bundler::{
|
||||||
|
chunk::{merge::Ctx, sort::sort},
|
||||||
|
load::TransformedModule,
|
||||||
|
},
|
||||||
|
id::Id,
|
||||||
|
Bundler, Load, ModuleId, Resolve,
|
||||||
};
|
};
|
||||||
use crate::{bundler::load::TransformedModule, util::CHashSet, Bundler, Load, ModuleId, Resolve};
|
|
||||||
use anyhow::{Context, Error};
|
use anyhow::{Context, Error};
|
||||||
use std::borrow::Borrow;
|
|
||||||
use swc_common::DUMMY_SP;
|
use swc_common::DUMMY_SP;
|
||||||
use swc_ecma_ast::*;
|
use swc_ecma_ast::*;
|
||||||
use swc_ecma_visit::{noop_visit_type, FoldWith, Node, Visit, VisitMutWith, VisitWith};
|
use swc_ecma_utils::find_ids;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests;
|
mod tests;
|
||||||
@ -20,309 +23,189 @@ where
|
|||||||
L: Load,
|
L: Load,
|
||||||
R: Resolve,
|
R: Resolve,
|
||||||
{
|
{
|
||||||
pub(super) fn merge_circular_modules(
|
pub(super) fn merge_circular(
|
||||||
&self,
|
&self,
|
||||||
plan: &Plan,
|
ctx: &Ctx,
|
||||||
circular_plan: &CircularPlan,
|
plan: &CircularPlan,
|
||||||
entry_id: ModuleId,
|
entry_id: ModuleId,
|
||||||
merged: &CHashSet<ModuleId>,
|
|
||||||
) -> Result<Module, Error> {
|
) -> Result<Module, Error> {
|
||||||
assert!(
|
assert!(
|
||||||
circular_plan.chunks.len() >= 1,
|
plan.chunks.len() >= 1,
|
||||||
"# of circular modules should be 2 or greater than 2 including entry. Got {:?}",
|
"# of circular modules should be 2 or greater than 2 including entry. Got {:?}",
|
||||||
circular_plan
|
plan
|
||||||
);
|
);
|
||||||
let entry_module = self.scope.get_module(entry_id).unwrap();
|
let entry_module = self.scope.get_module(entry_id).unwrap();
|
||||||
let direct_deps = entry_module
|
|
||||||
.imports
|
|
||||||
.specifiers
|
|
||||||
.iter()
|
|
||||||
.map(|v| v.0.module_id)
|
|
||||||
.chain(entry_module.exports.reexports.iter().map(|v| v.0.module_id))
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
let modules = circular_plan
|
let modules = plan
|
||||||
.chunks
|
.chunks
|
||||||
.iter()
|
.iter()
|
||||||
.map(|&id| self.scope.get_module(id).unwrap())
|
.map(|&id| self.scope.get_module(id).unwrap())
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
merged.insert(entry_id);
|
|
||||||
let mut entry = self
|
let mut entry = self
|
||||||
.merge_modules(plan, entry_id, false, true, merged)
|
.merge_modules(ctx, entry_id, false, false)
|
||||||
.context("failed to merge dependency of a cyclic module")?;
|
.context("failed to merge dependency of a cyclic module")?;
|
||||||
|
|
||||||
entry.visit_mut_with(&mut ImportDropper {
|
if !ctx.merged.insert(entry_id) {
|
||||||
imports: &entry_module.imports,
|
log::debug!("[circular] skip: {:?}", entry_id);
|
||||||
|
return Ok(Module {
|
||||||
|
span: DUMMY_SP,
|
||||||
|
body: Default::default(),
|
||||||
|
shebang: Default::default(),
|
||||||
});
|
});
|
||||||
// print_hygiene("entry:drop_imports", &self.cm, &entry);
|
}
|
||||||
let mut deps = circular_plan.chunks.clone();
|
|
||||||
deps.sort_by_key(|&dep| (!direct_deps.contains(&dep), dep));
|
|
||||||
|
|
||||||
for dep in deps {
|
let mut exports = vec![];
|
||||||
|
for item in entry.body.iter_mut() {
|
||||||
|
match item {
|
||||||
|
ModuleItem::ModuleDecl(decl) => match decl {
|
||||||
|
ModuleDecl::ExportDecl(export) => match &export.decl {
|
||||||
|
Decl::Class(ClassDecl { ident, .. }) | Decl::Fn(FnDecl { ident, .. }) => {
|
||||||
|
let mut exported = ident.clone();
|
||||||
|
exported.span.ctxt = entry_module.export_ctxt();
|
||||||
|
|
||||||
|
exports.push(ExportSpecifier::Named(ExportNamedSpecifier {
|
||||||
|
span: export.span,
|
||||||
|
orig: exported,
|
||||||
|
exported: None,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
Decl::Var(var) => {
|
||||||
|
let ids: Vec<Id> = find_ids(var);
|
||||||
|
|
||||||
|
for ident in ids {
|
||||||
|
let mut exported = ident.into_ident();
|
||||||
|
exported.span.ctxt = entry_module.export_ctxt();
|
||||||
|
|
||||||
|
exports.push(ExportSpecifier::Named(ExportNamedSpecifier {
|
||||||
|
span: export.span,
|
||||||
|
orig: exported,
|
||||||
|
exported: None,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
},
|
||||||
|
ModuleDecl::ExportNamed(export) => {
|
||||||
|
for specifier in &mut export.specifiers {
|
||||||
|
match specifier {
|
||||||
|
ExportSpecifier::Namespace(_) => {}
|
||||||
|
ExportSpecifier::Default(_) => {}
|
||||||
|
ExportSpecifier::Named(named) => {
|
||||||
|
let mut orig = named.orig.clone();
|
||||||
|
orig.span.ctxt = entry_module.export_ctxt();
|
||||||
|
|
||||||
|
exports.push(ExportSpecifier::Named(ExportNamedSpecifier {
|
||||||
|
span: export.span,
|
||||||
|
orig,
|
||||||
|
exported: named.exported.clone(),
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ModuleDecl::ExportDefaultDecl(_) => {}
|
||||||
|
ModuleDecl::ExportDefaultExpr(_) => {}
|
||||||
|
ModuleDecl::ExportAll(_) => {}
|
||||||
|
_ => {}
|
||||||
|
},
|
||||||
|
ModuleItem::Stmt(_) => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// print_hygiene("[circular] entry:init", &self.cm, &entry);
|
||||||
|
|
||||||
|
self.handle_import_deps(ctx, &entry_module, &mut entry, true);
|
||||||
|
|
||||||
|
// print_hygiene("[circular] entry:reexport", &self.cm, &entry);
|
||||||
|
|
||||||
|
// self.handle_import_deps(&entry_module, &mut entry, true);
|
||||||
|
|
||||||
|
// entry.visit_mut_with(&mut ImportDropper {
|
||||||
|
// imports: &entry_module.imports,
|
||||||
|
// });
|
||||||
|
// print_hygiene("entry:drop_imports", &self.cm, &entry);
|
||||||
|
let mut deps = plan.chunks.clone();
|
||||||
|
deps.retain(|&dep| {
|
||||||
if dep == entry_id {
|
if dep == entry_id {
|
||||||
continue;
|
return false;
|
||||||
}
|
}
|
||||||
if !merged.insert(dep) {
|
|
||||||
log::debug!("[circular merge] Already merged: {:?}", dep);
|
if !ctx.merged.insert(dep) {
|
||||||
continue;
|
log::debug!("[circular] skip: {:?}", dep);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
log::debug!("Circular merge: {:?}", dep);
|
log::debug!("Circular merge: {:?}", dep);
|
||||||
|
|
||||||
let new_module = self.merge_two_circular_modules(plan, &modules, entry, dep, merged)?;
|
true
|
||||||
|
});
|
||||||
|
deps.sort();
|
||||||
|
|
||||||
|
let new_module = self.merge_circular_modules(ctx, &modules, entry, deps)?;
|
||||||
|
|
||||||
entry = new_module;
|
entry = new_module;
|
||||||
|
|
||||||
// print_hygiene(
|
if !exports.is_empty() {
|
||||||
// "[circular] entry:merge_two_circular_modules",
|
entry
|
||||||
// &self.cm,
|
.body
|
||||||
// &entry,
|
.push(ModuleItem::ModuleDecl(ModuleDecl::ExportNamed(
|
||||||
// );
|
NamedExport {
|
||||||
|
span: DUMMY_SP,
|
||||||
|
specifiers: exports,
|
||||||
|
src: None,
|
||||||
|
type_only: false,
|
||||||
|
},
|
||||||
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// print_hygiene("[circular] done", &self.cm, &entry);
|
||||||
|
|
||||||
Ok(entry)
|
Ok(entry)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Merges `a` and `b` into one module.
|
/// Merges `a` and `b` into one module.
|
||||||
fn merge_two_circular_modules(
|
fn merge_circular_modules(
|
||||||
&self,
|
&self,
|
||||||
plan: &Plan,
|
ctx: &Ctx,
|
||||||
_circular_modules: &[TransformedModule],
|
_circular_modules: &[TransformedModule],
|
||||||
mut entry: Module,
|
mut entry: Module,
|
||||||
dep: ModuleId,
|
deps: Vec<ModuleId>,
|
||||||
merged: &CHashSet<ModuleId>,
|
|
||||||
) -> Result<Module, Error> {
|
) -> Result<Module, Error> {
|
||||||
self.run(|| {
|
self.run(|| {
|
||||||
|
let mut dep_body = vec![];
|
||||||
|
|
||||||
|
for dep in deps {
|
||||||
|
let dep_info = self.scope.get_module(dep).unwrap();
|
||||||
let mut dep = self
|
let mut dep = self
|
||||||
.merge_modules(plan, dep, false, true, merged)
|
.merge_modules(ctx, dep, false, false)
|
||||||
.context("failed to merge dependency of a cyclic module")?;
|
.context("failed to merge dependency of a cyclic module")?;
|
||||||
|
|
||||||
// print_hygiene("[circular] dep:init", &self.cm, &dep);
|
// print_hygiene("[circular] dep:init 1", &self.cm, &dep);
|
||||||
|
|
||||||
dep = dep.fold_with(&mut Unexporter);
|
self.handle_import_deps(ctx, &dep_info, &mut dep, true);
|
||||||
|
|
||||||
|
// print_hygiene("[circular] dep:init 2", &self.cm, &dep);
|
||||||
|
|
||||||
|
dep_body.extend(dep.body);
|
||||||
|
}
|
||||||
|
|
||||||
|
// dep = dep.fold_with(&mut Unexporter);
|
||||||
|
|
||||||
// Merge code
|
// Merge code
|
||||||
entry.body = merge_respecting_order(dep.body, entry.body);
|
entry.body = merge_respecting_order(dep_body, entry.body);
|
||||||
|
|
||||||
// print_hygiene(
|
|
||||||
// "[circular] END :merge_two_circular_modules",
|
|
||||||
// &self.cm,
|
|
||||||
// &entry,
|
|
||||||
// );
|
|
||||||
Ok(entry)
|
Ok(entry)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Originally, this method should create a dependency graph, but
|
fn merge_respecting_order(dep: Vec<ModuleItem>, entry: Vec<ModuleItem>) -> Vec<ModuleItem> {
|
||||||
fn merge_respecting_order(mut dep: Vec<ModuleItem>, mut entry: Vec<ModuleItem>) -> Vec<ModuleItem> {
|
|
||||||
let mut new = Vec::with_capacity(dep.len() + entry.len());
|
let mut new = Vec::with_capacity(dep.len() + entry.len());
|
||||||
|
|
||||||
// While looping over items from entry, we check for dependency.
|
new.extend(entry);
|
||||||
loop {
|
|
||||||
if dep.is_empty() {
|
|
||||||
log::trace!("dep is empty");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
let item = dep.drain(..=0).next().unwrap();
|
|
||||||
|
|
||||||
// Everything from dep is injected
|
|
||||||
if entry.is_empty() {
|
|
||||||
log::trace!("dep is empty");
|
|
||||||
new.push(item);
|
|
||||||
new.append(&mut dep);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the code of entry depends on dependency, we insert dependency source code
|
|
||||||
// at the position.
|
|
||||||
if let Some(pos) = dependency_index(&item, &entry) {
|
|
||||||
log::trace!("Found depndency: {}", pos);
|
|
||||||
|
|
||||||
new.extend(entry.drain(..=pos));
|
|
||||||
new.push(item);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// We checked the length of `dep`
|
|
||||||
if let Some(pos) = dependency_index(&entry[0], &[&item]) {
|
|
||||||
log::trace!("Found reverse depndency (index[0]): {}", pos);
|
|
||||||
|
|
||||||
new.push(item);
|
|
||||||
new.extend(entry.drain(..=0));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(pos) = dependency_index(&entry[0], &dep) {
|
|
||||||
log::trace!("Found reverse depndency: {}", pos);
|
|
||||||
|
|
||||||
new.push(item);
|
|
||||||
new.extend(dep.drain(..=pos));
|
|
||||||
new.extend(entry.drain(..=0));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
log::trace!("No dependency");
|
|
||||||
|
|
||||||
new.push(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
new.extend(dep);
|
new.extend(dep);
|
||||||
|
|
||||||
// Append remaining statements.
|
sort(&mut new);
|
||||||
new.extend(entry);
|
|
||||||
|
|
||||||
new
|
new
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Searches for top level declaration which provides requirements for `deps`.
|
|
||||||
fn dependency_index<T>(item: &ModuleItem, deps: &[T]) -> Option<usize>
|
|
||||||
where
|
|
||||||
T: Borrow<ModuleItem>,
|
|
||||||
{
|
|
||||||
let mut v = DepFinder {
|
|
||||||
deps,
|
|
||||||
idx: None,
|
|
||||||
last_usage_idx: None,
|
|
||||||
};
|
|
||||||
item.visit_with(&Invalid { span: DUMMY_SP }, &mut v);
|
|
||||||
v.idx.or(v.last_usage_idx)
|
|
||||||
}
|
|
||||||
|
|
||||||
struct DepFinder<'a, T>
|
|
||||||
where
|
|
||||||
T: Borrow<ModuleItem>,
|
|
||||||
{
|
|
||||||
deps: &'a [T],
|
|
||||||
last_usage_idx: Option<usize>,
|
|
||||||
idx: Option<usize>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> Visit for DepFinder<'_, T>
|
|
||||||
where
|
|
||||||
T: Borrow<ModuleItem>,
|
|
||||||
{
|
|
||||||
noop_visit_type!();
|
|
||||||
|
|
||||||
fn visit_ident(&mut self, i: &Ident, _: &dyn Node) {
|
|
||||||
if self.idx.is_some() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (idx, dep) in self.deps.iter().enumerate() {
|
|
||||||
match dep.borrow() {
|
|
||||||
ModuleItem::ModuleDecl(ModuleDecl::ExportDecl(ExportDecl {
|
|
||||||
decl: Decl::Class(decl),
|
|
||||||
..
|
|
||||||
}))
|
|
||||||
| ModuleItem::Stmt(Stmt::Decl(Decl::Class(decl))) => {
|
|
||||||
log::trace!(
|
|
||||||
"Decl (from dep) = {}{:?}, Ident = {}{:?}",
|
|
||||||
decl.ident.sym,
|
|
||||||
decl.ident.span.ctxt,
|
|
||||||
i.sym,
|
|
||||||
i.span.ctxt
|
|
||||||
);
|
|
||||||
if decl.ident.sym == i.sym && decl.ident.span.ctxt == i.span.ctxt {
|
|
||||||
self.idx = Some(idx);
|
|
||||||
log::debug!("Index is {}", idx);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dep => {
|
|
||||||
if DepUsageFinder::find(i, dep) {
|
|
||||||
self.last_usage_idx = Some(idx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn visit_import_decl(&mut self, _: &ImportDecl, _: &dyn Node) {}
|
|
||||||
#[inline]
|
|
||||||
fn visit_class_member(&mut self, _: &ClassMember, _: &dyn Node) {}
|
|
||||||
#[inline]
|
|
||||||
fn visit_function(&mut self, _: &Function, _: &dyn Node) {}
|
|
||||||
#[inline]
|
|
||||||
fn visit_arrow_expr(&mut self, _: &ArrowExpr, _: &dyn Node) {}
|
|
||||||
|
|
||||||
/// We only search for top-level binding
|
|
||||||
#[inline]
|
|
||||||
fn visit_stmts(&mut self, _: &[Stmt], _: &dyn Node) {}
|
|
||||||
/// We only search for top-level binding
|
|
||||||
#[inline]
|
|
||||||
fn visit_block_stmt(&mut self, _: &BlockStmt, _: &dyn Node) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Finds usage of `ident`
|
|
||||||
struct DepUsageFinder<'a> {
|
|
||||||
ident: &'a Ident,
|
|
||||||
found: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Visit for DepUsageFinder<'a> {
|
|
||||||
noop_visit_type!();
|
|
||||||
|
|
||||||
fn visit_call_expr(&mut self, e: &CallExpr, _: &dyn Node) {
|
|
||||||
if self.found {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
match &e.callee {
|
|
||||||
ExprOrSuper::Super(_) => {}
|
|
||||||
ExprOrSuper::Expr(callee) => match &**callee {
|
|
||||||
Expr::Ident(..) => {}
|
|
||||||
_ => {
|
|
||||||
callee.visit_with(e, self);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
e.args.visit_with(e, self);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn visit_ident(&mut self, i: &Ident, _: &dyn Node) {
|
|
||||||
if self.found {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if i.span.ctxt() == self.ident.span.ctxt() && i.sym == self.ident.sym {
|
|
||||||
self.found = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn visit_member_expr(&mut self, e: &MemberExpr, _: &dyn Node) {
|
|
||||||
if self.found {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
e.obj.visit_with(e as _, self);
|
|
||||||
|
|
||||||
if e.computed {
|
|
||||||
e.prop.visit_with(e as _, self);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> DepUsageFinder<'a> {
|
|
||||||
pub fn find<N>(ident: &'a Ident, node: &N) -> bool
|
|
||||||
where
|
|
||||||
N: VisitWith<Self>,
|
|
||||||
{
|
|
||||||
let mut v = DepUsageFinder {
|
|
||||||
ident,
|
|
||||||
found: false,
|
|
||||||
};
|
|
||||||
node.visit_with(&Invalid { span: DUMMY_SP } as _, &mut v);
|
|
||||||
v.found
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
use super::*;
|
use super::*;
|
||||||
|
use crate::debug::print_hygiene;
|
||||||
use swc_common::{sync::Lrc, FileName, SourceMap};
|
use swc_common::{sync::Lrc, FileName, SourceMap};
|
||||||
use swc_ecma_parser::{lexer::Lexer, JscTarget, Parser, StringInput, Syntax};
|
use swc_ecma_parser::{lexer::Lexer, JscTarget, Parser, StringInput, Syntax};
|
||||||
use swc_ecma_utils::drop_span;
|
use swc_ecma_utils::drop_span;
|
||||||
use testing::assert_eq;
|
|
||||||
|
|
||||||
fn parse(cm: Lrc<SourceMap>, name: &str, src: &str) -> Module {
|
fn parse(cm: Lrc<SourceMap>, name: &str, src: &str) -> Module {
|
||||||
let fm = cm.new_source_file(FileName::Custom(name.into()), src.into());
|
let fm = cm.new_source_file(FileName::Custom(name.into()), src.into());
|
||||||
@ -21,29 +21,24 @@ fn parse(cm: Lrc<SourceMap>, name: &str, src: &str) -> Module {
|
|||||||
|
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
fn assert_merge_respecting_order(modules: &[&str], output: &str) {
|
fn assert_merge_respecting_order(modules: &[&str], output: &str) {
|
||||||
for i in 0..modules.len() {
|
|
||||||
log::info!("[{}] Testing", i);
|
|
||||||
::testing::run_test2(false, |cm, _handler| {
|
::testing::run_test2(false, |cm, _handler| {
|
||||||
let mut entry = parse(cm.clone(), &format!("entry-{}", i), modules[i]).body;
|
let mut entry = parse(cm.clone(), &format!("entry"), modules[0]);
|
||||||
|
|
||||||
for j in 0..modules.len() {
|
for i in 1..modules.len() {
|
||||||
if i == j {
|
let dep = parse(cm.clone(), &format!("deps-{}", i), modules[i]);
|
||||||
continue;
|
entry.body = merge_respecting_order(entry.body, dep.body);
|
||||||
}
|
|
||||||
|
|
||||||
let dep = parse(cm.clone(), &format!("deps-{}-{}", i, j), modules[j]);
|
print_hygiene("merge", &cm, &entry);
|
||||||
entry = merge_respecting_order(entry, dep.body);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let output = parse(cm.clone(), "output", output);
|
let output = parse(cm.clone(), "output", output);
|
||||||
assert_eq!(entry, output.body, "[{}]", i);
|
if entry.body != output.body {
|
||||||
|
panic!()
|
||||||
log::info!("[{}] Success", i);
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
.unwrap()
|
.unwrap()
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -57,27 +52,59 @@ fn simple_two() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[track_caller]
|
#[test]
|
||||||
fn assert_dependency_index(entry: &str, dep: &str, expected: usize) {
|
fn many_vars_1() {
|
||||||
::testing::run_test2(false, |cm, _handler| {
|
assert_merge_respecting_order(
|
||||||
let entry = parse(cm.clone(), "entry", entry);
|
&[
|
||||||
let dep = parse(cm.clone(), "dep", dep);
|
"
|
||||||
|
const A6 = A5;
|
||||||
|
class B6 extends A6 {
|
||||||
|
}
|
||||||
|
const B7 = B6;
|
||||||
|
|
||||||
let calculated = dependency_index(&entry.body[0], &dep.body);
|
",
|
||||||
|
"
|
||||||
assert_eq!(calculated, Some(expected));
|
const B4 = B7;
|
||||||
|
class A4 {
|
||||||
Ok(())
|
method() {
|
||||||
})
|
return new B4();
|
||||||
.unwrap();
|
}
|
||||||
|
}
|
||||||
|
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]
|
#[test]
|
||||||
fn dep_index_class() {
|
fn no_dep_first_01() {
|
||||||
assert_dependency_index("class A extends B {}", "class B {}", 0);
|
assert_merge_respecting_order(
|
||||||
}
|
&[
|
||||||
|
"
|
||||||
#[test]
|
const b1 = b2;
|
||||||
fn dep_index_export_class() {
|
",
|
||||||
assert_dependency_index("class A extends B {}", "export class B {}", 0);
|
"
|
||||||
|
const b2 = 2;
|
||||||
|
const b3 = b1;
|
||||||
|
",
|
||||||
|
],
|
||||||
|
"
|
||||||
|
const b2 = 2;
|
||||||
|
const b1 = b2;
|
||||||
|
const b3 = b1;
|
||||||
|
",
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,17 @@
|
|||||||
use super::{merge::Unexporter, plan::Plan};
|
use super::merge::Unexporter;
|
||||||
use crate::{bundler::load::TransformedModule, Bundler, Load, ModuleId, Resolve};
|
use crate::{
|
||||||
|
bundler::{
|
||||||
|
chunk::{merge::Ctx, plan::Dependancy},
|
||||||
|
load::TransformedModule,
|
||||||
|
},
|
||||||
|
Bundler, Load, Resolve,
|
||||||
|
};
|
||||||
use anyhow::Error;
|
use anyhow::Error;
|
||||||
use std::{borrow::Cow, sync::atomic::Ordering};
|
use std::{borrow::Cow, sync::atomic::Ordering};
|
||||||
use swc_common::{Mark, SyntaxContext, DUMMY_SP};
|
use swc_atoms::js_word;
|
||||||
|
use swc_common::{SyntaxContext, DUMMY_SP};
|
||||||
use swc_ecma_ast::{ModuleItem, *};
|
use swc_ecma_ast::{ModuleItem, *};
|
||||||
use swc_ecma_utils::{prepend, undefined, ExprFactory};
|
use swc_ecma_utils::{prepend, quote_ident, undefined, ExprFactory};
|
||||||
use swc_ecma_visit::{noop_visit_mut_type, FoldWith, VisitMut, VisitMutWith};
|
use swc_ecma_visit::{noop_visit_mut_type, FoldWith, VisitMut, VisitMutWith};
|
||||||
|
|
||||||
impl<L, R> Bundler<'_, L, R>
|
impl<L, R> Bundler<'_, L, R>
|
||||||
@ -35,26 +42,30 @@ where
|
|||||||
/// modules.
|
/// modules.
|
||||||
pub(super) fn merge_cjs(
|
pub(super) fn merge_cjs(
|
||||||
&self,
|
&self,
|
||||||
plan: &Plan,
|
ctx: &Ctx,
|
||||||
is_entry: bool,
|
is_entry: bool,
|
||||||
entry: &mut Module,
|
entry: &mut Module,
|
||||||
info: &TransformedModule,
|
info: &TransformedModule,
|
||||||
dep: Cow<Module>,
|
dep: Cow<Module>,
|
||||||
dep_info: &TransformedModule,
|
dep_info: &TransformedModule,
|
||||||
targets: &mut Vec<ModuleId>,
|
targets: &mut Vec<Dependancy>,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
|
if info.is_es6 && dep_info.is_es6 {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
log::debug!("Merging as a common js module: {}", info.fm.name);
|
log::debug!("Merging as a common js module: {}", info.fm.name);
|
||||||
// If src is none, all requires are transpiled
|
// If src is none, all requires are transpiled
|
||||||
let mut v = RequireReplacer {
|
let mut v = RequireReplacer {
|
||||||
is_entry,
|
is_entry,
|
||||||
ctxt: dep_info.ctxt(),
|
ctxt: dep_info.export_ctxt(),
|
||||||
load_var: Ident::new("load".into(), DUMMY_SP.with_ctxt(dep_info.ctxt())),
|
load_var: Ident::new("load".into(), DUMMY_SP.with_ctxt(dep_info.export_ctxt())),
|
||||||
replaced: false,
|
replaced: false,
|
||||||
};
|
};
|
||||||
entry.body.visit_mut_with(&mut v);
|
entry.body.visit_mut_with(&mut v);
|
||||||
|
|
||||||
if v.replaced {
|
if v.replaced {
|
||||||
if let Some(idx) = targets.iter().position(|v| *v == dep_info.id) {
|
if let Some(idx) = targets.iter().position(|v| v.id == dep_info.id) {
|
||||||
targets.remove(idx);
|
targets.remove(idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -65,12 +76,15 @@ where
|
|||||||
|
|
||||||
let mut dep = dep.into_owned().fold_with(&mut Unexporter);
|
let mut dep = dep.into_owned().fold_with(&mut Unexporter);
|
||||||
dep.visit_mut_with(&mut ImportDropper);
|
dep.visit_mut_with(&mut ImportDropper);
|
||||||
|
dep.visit_mut_with(&mut DefaultHandler {
|
||||||
|
local_ctxt: dep_info.local_ctxt(),
|
||||||
|
});
|
||||||
|
|
||||||
prepend(
|
prepend(
|
||||||
&mut entry.body,
|
&mut entry.body,
|
||||||
ModuleItem::Stmt(wrap_module(
|
ModuleItem::Stmt(wrap_module(
|
||||||
SyntaxContext::empty(),
|
SyntaxContext::empty(),
|
||||||
dep_info.mark(),
|
dep_info.local_ctxt(),
|
||||||
load_var,
|
load_var,
|
||||||
dep,
|
dep,
|
||||||
)),
|
)),
|
||||||
@ -79,15 +93,15 @@ where
|
|||||||
log::warn!("Injecting load");
|
log::warn!("Injecting load");
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(normal_plan) = plan.normal.get(&dep_info.id) {
|
if let Some(normal_plan) = ctx.plan.normal.get(&dep_info.id) {
|
||||||
for &dep_id in &normal_plan.chunks {
|
for dep in normal_plan.chunks.iter() {
|
||||||
if !targets.contains(&dep_id) {
|
if !targets.contains(&dep) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let dep_info = self.scope.get_module(dep_id).unwrap();
|
let dep_info = self.scope.get_module(dep.id).unwrap();
|
||||||
self.merge_cjs(
|
self.merge_cjs(
|
||||||
plan,
|
ctx,
|
||||||
false,
|
false,
|
||||||
entry,
|
entry,
|
||||||
info,
|
info,
|
||||||
@ -105,7 +119,7 @@ where
|
|||||||
|
|
||||||
fn wrap_module(
|
fn wrap_module(
|
||||||
helper_ctxt: SyntaxContext,
|
helper_ctxt: SyntaxContext,
|
||||||
top_level_mark: Mark,
|
local_ctxt: SyntaxContext,
|
||||||
load_var: Ident,
|
load_var: Ident,
|
||||||
dep: Module,
|
dep: Module,
|
||||||
) -> Stmt {
|
) -> Stmt {
|
||||||
@ -118,19 +132,13 @@ fn wrap_module(
|
|||||||
Param {
|
Param {
|
||||||
span: DUMMY_SP,
|
span: DUMMY_SP,
|
||||||
decorators: Default::default(),
|
decorators: Default::default(),
|
||||||
pat: Pat::Ident(Ident::new(
|
pat: Pat::Ident(Ident::new("module".into(), DUMMY_SP.with_ctxt(local_ctxt))),
|
||||||
"module".into(),
|
|
||||||
DUMMY_SP.apply_mark(top_level_mark),
|
|
||||||
)),
|
|
||||||
},
|
},
|
||||||
// exports
|
// exports
|
||||||
Param {
|
Param {
|
||||||
span: DUMMY_SP,
|
span: DUMMY_SP,
|
||||||
decorators: Default::default(),
|
decorators: Default::default(),
|
||||||
pat: Pat::Ident(Ident::new(
|
pat: Pat::Ident(Ident::new("exports".into(), DUMMY_SP.with_ctxt(local_ctxt))),
|
||||||
"exports".into(),
|
|
||||||
DUMMY_SP.apply_mark(top_level_mark),
|
|
||||||
)),
|
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
decorators: vec![],
|
decorators: vec![],
|
||||||
@ -344,3 +352,41 @@ impl VisitMut for ImportDropper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct DefaultHandler {
|
||||||
|
local_ctxt: SyntaxContext,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VisitMut for DefaultHandler {
|
||||||
|
noop_visit_mut_type!();
|
||||||
|
|
||||||
|
fn visit_mut_expr(&mut self, e: &mut Expr) {
|
||||||
|
e.visit_mut_children_with(self);
|
||||||
|
|
||||||
|
match e {
|
||||||
|
Expr::Ident(i) => {
|
||||||
|
if i.sym == js_word!("default") {
|
||||||
|
*e = Expr::Member(MemberExpr {
|
||||||
|
span: i.span,
|
||||||
|
obj: ExprOrSuper::Expr(Box::new(Expr::Ident(Ident::new(
|
||||||
|
"module".into(),
|
||||||
|
DUMMY_SP.with_ctxt(self.local_ctxt),
|
||||||
|
)))),
|
||||||
|
prop: Box::new(Expr::Ident(quote_ident!("exports"))),
|
||||||
|
computed: false,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_mut_member_expr(&mut self, e: &mut MemberExpr) {
|
||||||
|
e.obj.visit_mut_with(self);
|
||||||
|
|
||||||
|
if e.computed {
|
||||||
|
e.prop.visit_mut_with(self);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
use super::plan::Plan;
|
use crate::{bundler::chunk::merge::Ctx, Bundler, Load, ModuleId, Resolve};
|
||||||
use crate::{bundler::load::TransformedModule, util::CHashSet, Bundler, Load, ModuleId, Resolve};
|
use anyhow::{bail, Error};
|
||||||
use anyhow::Error;
|
|
||||||
use std::mem::take;
|
use std::mem::take;
|
||||||
use swc_atoms::js_word;
|
use swc_atoms::js_word;
|
||||||
use swc_common::DUMMY_SP;
|
use swc_common::DUMMY_SP;
|
||||||
@ -29,15 +28,17 @@ where
|
|||||||
/// };
|
/// };
|
||||||
/// })();
|
/// })();
|
||||||
/// ```
|
/// ```
|
||||||
pub(super) fn wrap_esm_as_a_var(
|
pub(super) fn wrap_esm(
|
||||||
&self,
|
&self,
|
||||||
plan: &Plan,
|
ctx: &Ctx,
|
||||||
|
id: ModuleId,
|
||||||
module: Module,
|
module: Module,
|
||||||
info: &TransformedModule,
|
|
||||||
merged: &CHashSet<ModuleId>,
|
|
||||||
id: Ident,
|
|
||||||
) -> Result<Module, Error> {
|
) -> Result<Module, Error> {
|
||||||
let span = module.span;
|
let span = module.span;
|
||||||
|
let var_name = match self.scope.wrapped_esm_id(id) {
|
||||||
|
Some(v) => v,
|
||||||
|
None => bail!("{:?} should not be wrapped with a function", id),
|
||||||
|
};
|
||||||
|
|
||||||
let mut module_items = vec![];
|
let mut module_items = vec![];
|
||||||
|
|
||||||
@ -48,6 +49,13 @@ where
|
|||||||
.into_iter()
|
.into_iter()
|
||||||
.filter_map(|v| match v {
|
.filter_map(|v| match v {
|
||||||
ModuleItem::Stmt(s) => Some(s),
|
ModuleItem::Stmt(s) => Some(s),
|
||||||
|
ModuleItem::ModuleDecl(ModuleDecl::ExportAll(ref export)) => {
|
||||||
|
// We handle this later.
|
||||||
|
let mut map = ctx.export_stars_in_wrapped.lock();
|
||||||
|
map.entry(id).or_default().push(export.span.ctxt);
|
||||||
|
module_items.push(v);
|
||||||
|
None
|
||||||
|
}
|
||||||
_ => {
|
_ => {
|
||||||
module_items.push(v);
|
module_items.push(v);
|
||||||
None
|
None
|
||||||
@ -87,31 +95,18 @@ where
|
|||||||
decls: vec![VarDeclarator {
|
decls: vec![VarDeclarator {
|
||||||
span: DUMMY_SP,
|
span: DUMMY_SP,
|
||||||
definite: false,
|
definite: false,
|
||||||
name: Pat::Ident(id.clone()),
|
name: Pat::Ident(var_name.into_ident()),
|
||||||
init: Some(Box::new(module_expr)),
|
init: Some(Box::new(module_expr)),
|
||||||
}],
|
}],
|
||||||
};
|
};
|
||||||
|
|
||||||
module_items.push(ModuleItem::Stmt(Stmt::Decl(Decl::Var(var_decl))));
|
module_items.push(ModuleItem::Stmt(Stmt::Decl(Decl::Var(var_decl))));
|
||||||
|
|
||||||
let module = Module {
|
Ok(Module {
|
||||||
span: DUMMY_SP,
|
span: DUMMY_SP,
|
||||||
shebang: None,
|
shebang: None,
|
||||||
body: module_items,
|
body: module_items,
|
||||||
};
|
})
|
||||||
|
|
||||||
let module_plan;
|
|
||||||
let module_plan = match plan.normal.get(&info.id) {
|
|
||||||
Some(v) => v,
|
|
||||||
None => {
|
|
||||||
module_plan = Default::default();
|
|
||||||
&module_plan
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let module = self.merge_imports(&plan, module_plan, module, info, merged, false)?;
|
|
||||||
// print_hygiene("Imports", &self.cm, &module);
|
|
||||||
|
|
||||||
Ok(module)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -206,11 +201,30 @@ impl Fold for ExportToReturn {
|
|||||||
DefaultDecl::TsInterfaceDecl(_) => None,
|
DefaultDecl::TsInterfaceDecl(_) => None,
|
||||||
},
|
},
|
||||||
ModuleDecl::ExportDefaultExpr(_) => None,
|
ModuleDecl::ExportDefaultExpr(_) => None,
|
||||||
ModuleDecl::ExportAll(_) => {
|
ModuleDecl::ExportAll(export) => {
|
||||||
unimplemented!("export * from 'foo' inside a module loaded with computed key")
|
return ModuleItem::ModuleDecl(ModuleDecl::ExportAll(export))
|
||||||
}
|
}
|
||||||
ModuleDecl::ExportNamed(_) => {
|
ModuleDecl::ExportNamed(named) => {
|
||||||
unimplemented!("export {{ a }} from 'foo' inside a module loaded with computed key")
|
for specifier in &named.specifiers {
|
||||||
|
match specifier {
|
||||||
|
ExportSpecifier::Namespace(_) => {}
|
||||||
|
ExportSpecifier::Default(_) => {}
|
||||||
|
ExportSpecifier::Named(named) => match &named.exported {
|
||||||
|
Some(exported) => {
|
||||||
|
self.exports
|
||||||
|
.push(PropOrSpread::Prop(Box::new(Prop::KeyValue(
|
||||||
|
KeyValueProp {
|
||||||
|
key: PropName::Ident(exported.clone()),
|
||||||
|
value: Box::new(Expr::Ident(named.orig.clone())),
|
||||||
|
},
|
||||||
|
))))
|
||||||
|
}
|
||||||
|
None => {}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ModuleItem::ModuleDecl(ModuleDecl::ExportNamed(named));
|
||||||
}
|
}
|
||||||
ModuleDecl::TsImportEquals(_) => None,
|
ModuleDecl::TsImportEquals(_) => None,
|
||||||
ModuleDecl::TsExportAssignment(_) => None,
|
ModuleDecl::TsExportAssignment(_) => None,
|
||||||
@ -232,7 +246,6 @@ impl Fold for ExportToReturn {
|
|||||||
_ => true,
|
_ => true,
|
||||||
});
|
});
|
||||||
|
|
||||||
if !self.exports.is_empty() {
|
|
||||||
new.push(ModuleItem::Stmt(Stmt::Return(ReturnStmt {
|
new.push(ModuleItem::Stmt(Stmt::Return(ReturnStmt {
|
||||||
span: DUMMY_SP,
|
span: DUMMY_SP,
|
||||||
arg: Some(Box::new(Expr::Object(ObjectLit {
|
arg: Some(Box::new(Expr::Object(ObjectLit {
|
||||||
@ -240,7 +253,6 @@ impl Fold for ExportToReturn {
|
|||||||
props: take(&mut self.exports),
|
props: take(&mut self.exports),
|
||||||
}))),
|
}))),
|
||||||
})));
|
})));
|
||||||
}
|
|
||||||
|
|
||||||
new
|
new
|
||||||
}
|
}
|
||||||
|
@ -1,19 +1,18 @@
|
|||||||
use super::plan::{NormalPlan, Plan};
|
|
||||||
use crate::{
|
use crate::{
|
||||||
bundler::load::{Source, Specifier, TransformedModule},
|
bundler::{
|
||||||
util::{CHashSet, IntoParallelIterator},
|
chunk::merge::Ctx,
|
||||||
|
load::{Source, Specifier, TransformedModule},
|
||||||
|
},
|
||||||
|
util::{ExprExt, MapWithMut, VarDeclaratorExt},
|
||||||
Bundler, Load, ModuleId, Resolve,
|
Bundler, Load, ModuleId, Resolve,
|
||||||
};
|
};
|
||||||
use anyhow::{Context, Error};
|
use anyhow::{Context, Error};
|
||||||
#[cfg(feature = "concurrent")]
|
#[cfg(feature = "concurrent")]
|
||||||
use rayon::iter::ParallelIterator;
|
use rayon::iter::ParallelIterator;
|
||||||
use std::{
|
use std::mem::{replace, take};
|
||||||
collections::HashMap,
|
|
||||||
mem::{replace, take},
|
|
||||||
};
|
|
||||||
use swc_common::{Spanned, SyntaxContext, DUMMY_SP};
|
use swc_common::{Spanned, SyntaxContext, DUMMY_SP};
|
||||||
use swc_ecma_ast::*;
|
use swc_ecma_ast::*;
|
||||||
use swc_ecma_utils::{ident::IdentLike, Id};
|
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, noop_visit_mut_type, Fold, FoldWith, VisitMut, VisitMutWith};
|
||||||
|
|
||||||
impl<L, R> Bundler<'_, L, R>
|
impl<L, R> Bundler<'_, L, R>
|
||||||
@ -52,149 +51,99 @@ where
|
|||||||
///
|
///
|
||||||
/// console.log(bar, baz);
|
/// console.log(bar, baz);
|
||||||
/// ```
|
/// ```
|
||||||
pub(super) fn merge_reexports(
|
pub(super) fn merge_export(
|
||||||
&self,
|
&self,
|
||||||
plan: &Plan,
|
ctx: &Ctx,
|
||||||
nomral_plan: &NormalPlan,
|
dep_id: ModuleId,
|
||||||
entry: &mut Module,
|
specifiers: &[Specifier],
|
||||||
info: &TransformedModule,
|
) -> Result<Module, Error> {
|
||||||
merged: &CHashSet<ModuleId>,
|
self.run(|| {
|
||||||
) -> Result<(), Error> {
|
log::debug!("Reexporting {:?}", dep_id);
|
||||||
log::trace!("merge_reexports: {}", info.fm.name);
|
let dep_info = self.scope.get_module(dep_id).unwrap();
|
||||||
|
|
||||||
// Transitive dependencies
|
|
||||||
let mut additional_modules = vec![];
|
|
||||||
let mut reexports = vec![];
|
|
||||||
let mut decls_for_reexport: HashMap<_, Vec<VarDeclarator>> = HashMap::new();
|
|
||||||
|
|
||||||
// Remove transitive dependencies which is merged by parent moudle.
|
|
||||||
for v in info.exports.reexports.clone() {
|
|
||||||
if nomral_plan.chunks.contains(&v.0.module_id) {
|
|
||||||
if v.1.is_empty() {
|
|
||||||
additional_modules.push(v.clone());
|
|
||||||
}
|
|
||||||
|
|
||||||
reexports.push(v);
|
|
||||||
} else {
|
|
||||||
additional_modules.push(v);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (src, specifiers) in info.exports.reexports.iter() {
|
|
||||||
let imported = self.scope.get_module(src.module_id).unwrap();
|
|
||||||
|
|
||||||
// export * from './foo';
|
|
||||||
if specifiers.is_empty() {
|
|
||||||
let vars = decls_for_reexport.entry(src.module_id).or_default();
|
|
||||||
|
|
||||||
for specifier in imported.exports.items.iter() {
|
|
||||||
let var = match specifier {
|
|
||||||
Specifier::Specific { local, alias } => {
|
|
||||||
let init = Some(Box::new(Expr::Ident(
|
|
||||||
alias.clone().unwrap_or_else(|| local.clone()).into_ident(),
|
|
||||||
)));
|
|
||||||
|
|
||||||
VarDeclarator {
|
|
||||||
span: DUMMY_SP,
|
|
||||||
name: Pat::Ident(
|
|
||||||
local.clone().replace_mark(info.mark()).into_ident(),
|
|
||||||
),
|
|
||||||
init,
|
|
||||||
definite: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Specifier::Namespace { local, .. } => VarDeclarator {
|
|
||||||
span: DUMMY_SP,
|
|
||||||
name: Pat::Ident(local.clone().replace_mark(info.mark()).into_ident()),
|
|
||||||
init: Some(Box::new(Expr::Ident(local.clone().into_ident()))),
|
|
||||||
definite: false,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
vars.push(var)
|
|
||||||
}
|
|
||||||
|
|
||||||
for (_, specifiers) in imported.exports.reexports.iter() {
|
|
||||||
for specifier in specifiers {
|
|
||||||
let var = match specifier {
|
|
||||||
Specifier::Specific { local, alias } => {
|
|
||||||
let init = match alias {
|
|
||||||
Some(alias) => {
|
|
||||||
Some(Box::new(Expr::Ident(alias.clone().into_ident())))
|
|
||||||
}
|
|
||||||
None => continue,
|
|
||||||
};
|
|
||||||
|
|
||||||
VarDeclarator {
|
|
||||||
span: DUMMY_SP,
|
|
||||||
name: Pat::Ident(
|
|
||||||
local.clone().replace_mark(info.mark()).into_ident(),
|
|
||||||
),
|
|
||||||
init,
|
|
||||||
definite: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Specifier::Namespace { local, .. } => VarDeclarator {
|
|
||||||
span: DUMMY_SP,
|
|
||||||
name: Pat::Ident(
|
|
||||||
local.clone().replace_mark(info.mark()).into_ident(),
|
|
||||||
),
|
|
||||||
init: Some(Box::new(Expr::Ident(local.clone().into_ident()))),
|
|
||||||
definite: false,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
vars.push(var)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
let deps = reexports
|
|
||||||
.into_par_iter()
|
|
||||||
.map(|(src, specifiers)| -> Result<_, Error> {
|
|
||||||
let imported = self.scope.get_module(src.module_id).unwrap();
|
|
||||||
assert!(imported.is_es6, "Reexports are es6 only");
|
|
||||||
|
|
||||||
info.helpers.extend(&imported.helpers);
|
|
||||||
info.swc_helpers.extend_from(&imported.swc_helpers);
|
|
||||||
|
|
||||||
if !merged.insert(src.module_id) {
|
|
||||||
return Ok(None);
|
|
||||||
}
|
|
||||||
|
|
||||||
log::debug!("Merging exports: {} <- {}", info.fm.name, src.src.value);
|
|
||||||
|
|
||||||
let mut dep = self
|
let mut dep = self
|
||||||
.merge_modules(plan, src.module_id, false, false, merged)
|
.merge_modules(ctx, dep_id, false, true)
|
||||||
.with_context(|| {
|
.context("failed to get module for merging")?;
|
||||||
format!(
|
|
||||||
"failed to merge for reexport: ({}):{} <= ({}):{}",
|
|
||||||
info.id, info.fm.name, src.module_id, src.src.value
|
|
||||||
)
|
|
||||||
})?;
|
|
||||||
|
|
||||||
// print_hygiene(&format!("dep: start"), &self.cm, &dep);
|
// print_hygiene(
|
||||||
|
// &format!(
|
||||||
|
// "reexport: load dep: {} ({:?}, {:?})",
|
||||||
|
// dep_info.fm.name,
|
||||||
|
// dep_info.local_ctxt(),
|
||||||
|
// dep_info.export_ctxt()
|
||||||
|
// ),
|
||||||
|
// &self.cm,
|
||||||
|
// &dep,
|
||||||
|
// );
|
||||||
|
|
||||||
let id_of_export_namespace_from = specifiers.iter().find_map(|s| match s {
|
self.handle_reexport(&dep_info, &mut dep);
|
||||||
Specifier::Namespace { local, all: true } => Some(Ident::new(
|
|
||||||
local.sym().clone(),
|
|
||||||
DUMMY_SP.with_ctxt(info.ctxt()),
|
|
||||||
)),
|
|
||||||
_ => None,
|
|
||||||
});
|
|
||||||
|
|
||||||
if let Some(id) = id_of_export_namespace_from {
|
// print_hygiene(&format!("dep: handle reexport"), &self.cm, &dep);
|
||||||
dep = self.wrap_esm_as_a_var(plan, dep, &imported, merged, id)?;
|
|
||||||
} else {
|
// for stmt in &mut dep.body {
|
||||||
dep = self.remark_exports(dep, src.ctxt, None, false);
|
// let decl = match stmt {
|
||||||
|
// ModuleItem::ModuleDecl(decl) => decl,
|
||||||
|
// ModuleItem::Stmt(_) => continue,
|
||||||
|
// };
|
||||||
|
|
||||||
|
// match decl {
|
||||||
|
// ModuleDecl::ExportDecl(_) => {}
|
||||||
|
// ModuleDecl::ExportNamed(export) => {
|
||||||
|
// for specifier in &mut export.specifiers {
|
||||||
|
// match specifier {
|
||||||
|
// ExportSpecifier::Namespace(ns) => {}
|
||||||
|
// ExportSpecifier::Default(default) => {}
|
||||||
|
// ExportSpecifier::Named(named) => match &mut
|
||||||
|
// named.exported { Some(exported) => {
|
||||||
|
// if exported.span.ctxt != dep_info.local_ctxt() {
|
||||||
|
// continue;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// exported.span =
|
||||||
|
//
|
||||||
|
// exported.span.with_ctxt(dep_info.export_ctxt());
|
||||||
|
// } None => {
|
||||||
|
// if named.orig.span.ctxt != dep_info.local_ctxt()
|
||||||
|
// { continue;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// named.exported = Some(Ident::new(
|
||||||
|
// named.orig.sym.clone(),
|
||||||
|
//
|
||||||
|
// named.orig.span.with_ctxt(dep_info.export_ctxt()),
|
||||||
|
// )); }
|
||||||
|
// },
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// ModuleDecl::ExportDefaultDecl(_) => {}
|
||||||
|
// ModuleDecl::ExportDefaultExpr(_) => {}
|
||||||
|
// ModuleDecl::ExportAll(_) => {}
|
||||||
|
// _ => {}
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
if let Some(module_name) = self.scope.wrapped_esm_id(dep_info.id) {
|
||||||
|
dep = self.wrap_esm(ctx, dep_info.id, dep)?;
|
||||||
|
|
||||||
|
for specifier in specifiers {
|
||||||
|
match specifier {
|
||||||
|
Specifier::Namespace { local, .. } => {
|
||||||
|
dep.body.push(
|
||||||
|
module_name
|
||||||
|
.assign_to(local.clone())
|
||||||
|
.into_module_item("merge_export"),
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// print_hygiene(&format!("dep: remark exports"), &self.cm, &dep);
|
|
||||||
|
|
||||||
if !specifiers.is_empty() {
|
if !specifiers.is_empty() {
|
||||||
dep.visit_mut_with(&mut UnexportAsVar {
|
dep.visit_mut_with(&mut UnexportAsVar {
|
||||||
dep_ctxt: src.ctxt,
|
dep_export_ctxt: dep_info.export_ctxt(),
|
||||||
_entry_ctxt: info.ctxt(),
|
_specifiers: &specifiers,
|
||||||
_exports: &specifiers,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// print_hygiene(&format!("dep: unexport as var"), &self.cm, &dep);
|
// print_hygiene(&format!("dep: unexport as var"), &self.cm, &dep);
|
||||||
@ -206,164 +155,92 @@ where
|
|||||||
// print_hygiene(&format!("dep: unexport"), &self.cm, &dep);
|
// print_hygiene(&format!("dep: unexport"), &self.cm, &dep);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Some((src, dep)))
|
// TODO: Add varaible based on specifers
|
||||||
|
|
||||||
|
Ok(dep)
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
{
|
|
||||||
let mut normal_reexports = vec![];
|
|
||||||
let mut star_reexports = vec![];
|
|
||||||
for (src, specifiers) in additional_modules {
|
|
||||||
if specifiers.is_empty() {
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If a dependency is indirect, we need to export items from it manually.
|
/// # ExportDecl
|
||||||
let is_indirect = !nomral_plan.chunks.contains(&src.module_id);
|
///
|
||||||
|
/// For exported declarations, We should inject named exports.
|
||||||
|
///
|
||||||
|
/// ```ts
|
||||||
|
/// export const b__9 = 1;
|
||||||
|
/// console.log(b__9);
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// ```ts
|
||||||
|
/// const b__9 = 1;
|
||||||
|
/// 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);
|
||||||
|
|
||||||
let add_to = if specifiers.is_empty() && is_indirect {
|
for stmt in &mut module.body {
|
||||||
// User provided code like `export * from './foo';`, but planner decide to merge
|
let mut vars = vec![];
|
||||||
// it within dependency module. So we reexport them using a named export.
|
let mut stmt = stmt.take();
|
||||||
&mut star_reexports
|
|
||||||
} else {
|
|
||||||
&mut normal_reexports
|
|
||||||
};
|
|
||||||
|
|
||||||
for specifier in specifiers {
|
match &mut stmt {
|
||||||
let (imported, exported) = match specifier {
|
ModuleItem::ModuleDecl(ModuleDecl::Import(import)) => {
|
||||||
Specifier::Specific { local, alias } => {
|
for specifier in &import.specifiers {
|
||||||
let alias = alias.unwrap_or_else(|| local.clone());
|
match specifier {
|
||||||
let local = local.replace_mark(info.mark());
|
ImportSpecifier::Named(named) => match &named.imported {
|
||||||
(local.into_ident(), alias.into_ident())
|
Some(imported) => {
|
||||||
|
vars.push(imported.clone().assign_to(named.local.clone()));
|
||||||
}
|
}
|
||||||
Specifier::Namespace { .. } => continue,
|
None => {}
|
||||||
};
|
|
||||||
|
|
||||||
add_to.push((imported, exported));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !normal_reexports.is_empty() {
|
|
||||||
entry
|
|
||||||
.body
|
|
||||||
.push(ModuleItem::Stmt(Stmt::Decl(Decl::Var(VarDecl {
|
|
||||||
span: DUMMY_SP,
|
|
||||||
kind: VarDeclKind::Const,
|
|
||||||
declare: false,
|
|
||||||
decls: normal_reexports
|
|
||||||
.into_iter()
|
|
||||||
.map(|(imported, exported)| {
|
|
||||||
let var = VarDeclarator {
|
|
||||||
span: DUMMY_SP,
|
|
||||||
name: Pat::Ident(imported),
|
|
||||||
init: Some(Box::new(Expr::Ident(exported))),
|
|
||||||
definite: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
var
|
|
||||||
})
|
|
||||||
.collect(),
|
|
||||||
}))));
|
|
||||||
}
|
|
||||||
|
|
||||||
if !star_reexports.is_empty() {
|
|
||||||
entry
|
|
||||||
.body
|
|
||||||
.push(ModuleItem::ModuleDecl(ModuleDecl::ExportNamed(
|
|
||||||
NamedExport {
|
|
||||||
span: DUMMY_SP,
|
|
||||||
specifiers: star_reexports
|
|
||||||
.into_iter()
|
|
||||||
.map(|(imported, exported)| {
|
|
||||||
ExportNamedSpecifier {
|
|
||||||
span: DUMMY_SP,
|
|
||||||
orig: exported.clone(),
|
|
||||||
exported: Some(imported.clone()),
|
|
||||||
}
|
|
||||||
.into()
|
|
||||||
})
|
|
||||||
.collect(),
|
|
||||||
src: None,
|
|
||||||
type_only: false,
|
|
||||||
},
|
},
|
||||||
)));
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
import.specifiers.clear();
|
||||||
|
}
|
||||||
|
|
||||||
for dep in deps {
|
ModuleItem::ModuleDecl(ModuleDecl::ExportDecl(export)) => {
|
||||||
let dep = dep?;
|
match &export.decl {
|
||||||
let dep = match dep {
|
Decl::Class(ClassDecl { ident, .. }) | Decl::Fn(FnDecl { ident, .. }) => {
|
||||||
Some(v) => v,
|
let mut exported = ident.clone();
|
||||||
None => continue,
|
exported.span.ctxt = info.export_ctxt();
|
||||||
|
|
||||||
|
vars.push(ident.clone().assign_to(exported));
|
||||||
|
}
|
||||||
|
Decl::Var(var) => {
|
||||||
|
//
|
||||||
|
let ids: Vec<Ident> = 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),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
};
|
};
|
||||||
let (src, dep) = dep;
|
}
|
||||||
|
|
||||||
// print_hygiene(
|
_ => {}
|
||||||
// &format!(
|
}
|
||||||
// "entry: before reexport injection {:?} <- {:?}",
|
|
||||||
// info.ctxt(),
|
|
||||||
// src.ctxt,
|
|
||||||
// ),
|
|
||||||
// &self.cm,
|
|
||||||
// &entry,
|
|
||||||
// );
|
|
||||||
|
|
||||||
// Replace import statement / require with module body
|
new_body.push(stmt);
|
||||||
let mut injector = ExportInjector {
|
for var in vars {
|
||||||
imported: dep.body,
|
new_body.push(var.into_module_item("from_export_rs"))
|
||||||
source: src.clone(),
|
|
||||||
};
|
|
||||||
entry.body.visit_mut_with(&mut injector);
|
|
||||||
|
|
||||||
// Inject variables
|
|
||||||
if let Some(decls) = decls_for_reexport.remove(&src.module_id) {
|
|
||||||
if !decls.is_empty() {
|
|
||||||
entry
|
|
||||||
.body
|
|
||||||
.push(ModuleItem::Stmt(Stmt::Decl(Decl::Var(VarDecl {
|
|
||||||
span: DUMMY_SP,
|
|
||||||
kind: VarDeclKind::Const,
|
|
||||||
declare: false,
|
|
||||||
decls,
|
|
||||||
}))));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// print_hygiene(
|
module.body = new_body;
|
||||||
// &format!(
|
|
||||||
// "entry:reexport injection {:?} <- {:?}",
|
|
||||||
// info.ctxt(),
|
|
||||||
// src.ctxt,
|
|
||||||
// ),
|
|
||||||
// &self.cm,
|
|
||||||
// &entry,
|
|
||||||
// );
|
|
||||||
assert_eq!(injector.imported, vec![]);
|
|
||||||
}
|
|
||||||
|
|
||||||
let decls_for_reexport: Vec<_> = decls_for_reexport
|
|
||||||
.into_iter()
|
|
||||||
.map(|(_, decls)| decls)
|
|
||||||
.flatten()
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
if !decls_for_reexport.is_empty() {
|
|
||||||
entry
|
|
||||||
.body
|
|
||||||
.push(ModuleItem::Stmt(Stmt::Decl(Decl::Var(VarDecl {
|
|
||||||
span: DUMMY_SP,
|
|
||||||
kind: VarDeclKind::Const,
|
|
||||||
declare: false,
|
|
||||||
decls: decls_for_reexport,
|
|
||||||
}))));
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ExportInjector {
|
pub(super) struct ExportInjector {
|
||||||
imported: Vec<ModuleItem>,
|
pub imported: Vec<ModuleItem>,
|
||||||
source: Source,
|
pub source: Source,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VisitMut for ExportInjector {
|
impl VisitMut for ExportInjector {
|
||||||
@ -400,7 +277,7 @@ impl VisitMut for ExportInjector {
|
|||||||
..
|
..
|
||||||
}) => {
|
}) => {
|
||||||
let mut imported = imported.clone();
|
let mut imported = imported.clone();
|
||||||
imported.span = imported.span.with_ctxt(self.source.ctxt);
|
imported.span = imported.span.with_ctxt(self.source.export_ctxt);
|
||||||
|
|
||||||
Some(VarDeclarator {
|
Some(VarDeclarator {
|
||||||
span: DUMMY_SP,
|
span: DUMMY_SP,
|
||||||
@ -413,13 +290,8 @@ impl VisitMut for ExportInjector {
|
|||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
if !decls.is_empty() {
|
for var in decls {
|
||||||
buf.push(ModuleItem::Stmt(Stmt::Decl(Decl::Var(VarDecl {
|
buf.push(var.into_module_item("ExportInjector"));
|
||||||
span: DUMMY_SP,
|
|
||||||
kind: VarDeclKind::Const,
|
|
||||||
declare: false,
|
|
||||||
decls,
|
|
||||||
}))));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -492,12 +364,10 @@ impl VisitMut for ExportInjector {
|
|||||||
/// export { foo#7 as foo#5 } where #5 is mark of current entry.
|
/// export { foo#7 as foo#5 } where #5 is mark of current entry.
|
||||||
struct UnexportAsVar<'a> {
|
struct UnexportAsVar<'a> {
|
||||||
/// Syntax context for the generated variables.
|
/// Syntax context for the generated variables.
|
||||||
dep_ctxt: SyntaxContext,
|
dep_export_ctxt: SyntaxContext,
|
||||||
|
|
||||||
_entry_ctxt: SyntaxContext,
|
|
||||||
|
|
||||||
/// Exports to preserve
|
/// Exports to preserve
|
||||||
_exports: &'a [Specifier],
|
_specifiers: &'a [Specifier],
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VisitMut for UnexportAsVar<'_> {
|
impl VisitMut for UnexportAsVar<'_> {
|
||||||
@ -513,20 +383,16 @@ impl VisitMut for UnexportAsVar<'_> {
|
|||||||
Box::new(Expr::Invalid(Invalid { span: DUMMY_SP })),
|
Box::new(Expr::Invalid(Invalid { span: DUMMY_SP })),
|
||||||
);
|
);
|
||||||
|
|
||||||
*n = ModuleItem::Stmt(Stmt::Decl(Decl::Var(VarDecl {
|
*n = VarDeclarator {
|
||||||
span: export.span,
|
|
||||||
kind: VarDeclKind::Const,
|
|
||||||
declare: false,
|
|
||||||
decls: vec![VarDeclarator {
|
|
||||||
span: DUMMY_SP,
|
span: DUMMY_SP,
|
||||||
name: Pat::Ident(Ident::new(
|
name: Pat::Ident(Ident::new(
|
||||||
"__default".into(),
|
"__default".into(),
|
||||||
expr.span().with_ctxt(self.dep_ctxt),
|
expr.span().with_ctxt(self.dep_export_ctxt),
|
||||||
)),
|
)),
|
||||||
init: Some(expr),
|
init: Some(expr),
|
||||||
definite: false,
|
definite: false,
|
||||||
}],
|
}
|
||||||
})));
|
.into_module_item("UnexportAsVar");
|
||||||
}
|
}
|
||||||
ModuleItem::ModuleDecl(ModuleDecl::ExportNamed(
|
ModuleItem::ModuleDecl(ModuleDecl::ExportNamed(
|
||||||
ref export @ NamedExport { src: None, .. },
|
ref export @ NamedExport { src: None, .. },
|
||||||
@ -554,18 +420,24 @@ impl VisitMut for UnexportAsVar<'_> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
log::trace!("Alias: {:?} -> {:?}", n.orig, self.dep_ctxt);
|
if n.orig.span.ctxt != self.dep_export_ctxt {
|
||||||
|
log::trace!(
|
||||||
|
"Alias: {:?} -> {:?}",
|
||||||
|
n.orig,
|
||||||
|
self.dep_export_ctxt
|
||||||
|
);
|
||||||
|
|
||||||
decls.push(VarDeclarator {
|
decls.push(VarDeclarator {
|
||||||
span: n.span,
|
span: n.span,
|
||||||
name: Pat::Ident(Ident::new(
|
name: Pat::Ident(Ident::new(
|
||||||
n.orig.sym.clone(),
|
n.orig.sym.clone(),
|
||||||
n.orig.span.with_ctxt(self.dep_ctxt),
|
n.orig.span.with_ctxt(self.dep_export_ctxt),
|
||||||
)),
|
)),
|
||||||
init: Some(Box::new(Expr::Ident(n.orig.clone()))),
|
init: Some(Box::new(Expr::Ident(n.orig.clone()))),
|
||||||
definite: false,
|
definite: false,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -1,5 +1,8 @@
|
|||||||
use super::{load::TransformedModule, Bundler};
|
use super::{load::TransformedModule, Bundler};
|
||||||
use crate::{id::ModuleId, load::Load, resolve::Resolve, util::IntoParallelIterator, Bundle};
|
use crate::{
|
||||||
|
bundler::chunk::merge::Ctx, id::ModuleId, load::Load, resolve::Resolve,
|
||||||
|
util::IntoParallelIterator, Bundle,
|
||||||
|
};
|
||||||
use anyhow::{Context, Error};
|
use anyhow::{Context, Error};
|
||||||
#[cfg(feature = "rayon")]
|
#[cfg(feature = "rayon")]
|
||||||
use rayon::iter::ParallelIterator;
|
use rayon::iter::ParallelIterator;
|
||||||
@ -11,7 +14,7 @@ mod computed_key;
|
|||||||
mod export;
|
mod export;
|
||||||
mod merge;
|
mod merge;
|
||||||
mod plan;
|
mod plan;
|
||||||
mod remark;
|
mod sort;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct InternalEntry {
|
struct InternalEntry {
|
||||||
@ -43,13 +46,19 @@ where
|
|||||||
entries: HashMap<String, TransformedModule>,
|
entries: HashMap<String, TransformedModule>,
|
||||||
) -> Result<Vec<Bundle>, Error> {
|
) -> Result<Vec<Bundle>, Error> {
|
||||||
let plan = self.determine_entries(entries).context("failed to plan")?;
|
let plan = self.determine_entries(entries).context("failed to plan")?;
|
||||||
let merged = Default::default();
|
let ctx = Ctx {
|
||||||
|
plan,
|
||||||
|
merged: Default::default(),
|
||||||
|
transitive_remap: Default::default(),
|
||||||
|
export_stars_in_wrapped: Default::default(),
|
||||||
|
};
|
||||||
|
|
||||||
Ok((&*plan.entries)
|
Ok((&*ctx.plan.entries)
|
||||||
.into_par_iter()
|
.into_par_iter()
|
||||||
.map(|&entry| {
|
.map(|&entry| {
|
||||||
self.run(|| {
|
self.run(|| {
|
||||||
let kind = plan
|
let kind = ctx
|
||||||
|
.plan
|
||||||
.bundle_kinds
|
.bundle_kinds
|
||||||
.get(&entry)
|
.get(&entry)
|
||||||
.unwrap_or_else(|| {
|
.unwrap_or_else(|| {
|
||||||
@ -58,7 +67,7 @@ where
|
|||||||
.clone();
|
.clone();
|
||||||
|
|
||||||
let module = self
|
let module = self
|
||||||
.merge_modules(&plan, entry, true, false, &merged)
|
.merge_modules(&ctx, entry, true, true)
|
||||||
.context("failed to merge module")
|
.context("failed to merge module")
|
||||||
.unwrap(); // TODO
|
.unwrap(); // TODO
|
||||||
|
|
||||||
|
@ -107,29 +107,29 @@ pub(super) struct Plan {
|
|||||||
pub bundle_kinds: HashMap<ModuleId, BundleKind>,
|
pub bundle_kinds: HashMap<ModuleId, BundleKind>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Plan {
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
pub fn entry_as_circular(&self, entry: ModuleId) -> Option<&CircularPlan> {
|
pub(super) enum DepType {
|
||||||
let plan = self.circular.get(&entry)?;
|
|
||||||
if plan.chunks.is_empty() {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
Some(plan)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
|
||||||
pub(super) struct NormalPlan {
|
|
||||||
/// Direct dependencies
|
/// Direct dependencies
|
||||||
pub chunks: Vec<ModuleId>,
|
Direct,
|
||||||
|
|
||||||
/// Used to handle
|
/// Used to handle
|
||||||
///
|
///
|
||||||
/// - a -> b
|
/// - a -> b
|
||||||
/// - a -> c
|
/// - a -> c
|
||||||
/// - b -> d
|
/// - b -> d
|
||||||
/// - c -> d
|
/// - c -> d
|
||||||
pub transitive_chunks: Vec<ModuleId>,
|
Transitive,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub(super) struct Dependancy {
|
||||||
|
pub ty: DepType,
|
||||||
|
pub id: ModuleId,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Default)]
|
||||||
|
#[cfg_attr(test, derive(PartialEq, Eq))]
|
||||||
|
pub(super) struct NormalPlan {
|
||||||
|
pub chunks: Vec<Dependancy>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
@ -150,17 +150,10 @@ where
|
|||||||
entries: HashMap<String, TransformedModule>,
|
entries: HashMap<String, TransformedModule>,
|
||||||
) -> Result<Plan, Error> {
|
) -> Result<Plan, Error> {
|
||||||
let plan = self.calculate_plan(entries)?;
|
let plan = self.calculate_plan(entries)?;
|
||||||
let plan = self.handle_duplicates(plan);
|
|
||||||
|
|
||||||
Ok(plan)
|
Ok(plan)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 1. For entry -> a -> b -> a, entry -> a->c, entry -> b -> c,
|
|
||||||
/// we change c as transitive dependancy of entry.
|
|
||||||
fn handle_duplicates(&self, plan: Plan) -> Plan {
|
|
||||||
plan
|
|
||||||
}
|
|
||||||
|
|
||||||
fn calculate_plan(&self, entries: HashMap<String, TransformedModule>) -> Result<Plan, Error> {
|
fn calculate_plan(&self, entries: HashMap<String, TransformedModule>) -> Result<Plan, Error> {
|
||||||
let mut builder = PlanBuilder::default();
|
let mut builder = PlanBuilder::default();
|
||||||
|
|
||||||
@ -211,7 +204,7 @@ where
|
|||||||
Ok(self.build_plan(&metadata, builder))
|
Ok(self.build_plan(&metadata, builder))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_plan(&self, metadata: &HashMap<ModuleId, Metadata>, builder: PlanBuilder) -> Plan {
|
fn build_plan(&self, _metadata: &HashMap<ModuleId, Metadata>, builder: PlanBuilder) -> Plan {
|
||||||
let mut plans = Plan::default();
|
let mut plans = Plan::default();
|
||||||
|
|
||||||
for (id, kind) in builder.kinds.iter() {
|
for (id, kind) in builder.kinds.iter() {
|
||||||
@ -233,6 +226,14 @@ where
|
|||||||
.collect();
|
.collect();
|
||||||
deps.sort();
|
deps.sort();
|
||||||
|
|
||||||
|
let should_be_reexport = deps.iter().any(|&dep| {
|
||||||
|
builder
|
||||||
|
.all_deps
|
||||||
|
.get(&(entry, dep))
|
||||||
|
.copied()
|
||||||
|
.unwrap_or(false)
|
||||||
|
});
|
||||||
|
|
||||||
for &dep in &deps {
|
for &dep in &deps {
|
||||||
if let Some(circular_members) = builder.circular.get(entry) {
|
if let Some(circular_members) = builder.circular.get(entry) {
|
||||||
if circular_members.contains(&dep) {
|
if circular_members.contains(&dep) {
|
||||||
@ -243,7 +244,15 @@ where
|
|||||||
);
|
);
|
||||||
if entry != root_entry && dep != root_entry {
|
if entry != root_entry && dep != root_entry {
|
||||||
// done.insert(dep);
|
// done.insert(dep);
|
||||||
plans.normal.entry(entry).or_default().chunks.push(dep);
|
plans
|
||||||
|
.normal
|
||||||
|
.entry(entry)
|
||||||
|
.or_default()
|
||||||
|
.chunks
|
||||||
|
.push(Dependancy {
|
||||||
|
id: dep,
|
||||||
|
ty: DepType::Direct,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -260,7 +269,6 @@ where
|
|||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
dependants.sort();
|
dependants.sort();
|
||||||
|
|
||||||
if metadata.get(&dep).map(|md| md.bundle_cnt).unwrap_or(0) == 1 {
|
|
||||||
log::debug!("{:?} depends on {:?}", entry, dep);
|
log::debug!("{:?} depends on {:?}", entry, dep);
|
||||||
|
|
||||||
let is_reexport = builder
|
let is_reexport = builder
|
||||||
@ -282,7 +290,15 @@ where
|
|||||||
// b <- c
|
// b <- c
|
||||||
//
|
//
|
||||||
if dependants.len() <= 1 {
|
if dependants.len() <= 1 {
|
||||||
plans.normal.entry(entry).or_default().chunks.push(dep);
|
plans
|
||||||
|
.normal
|
||||||
|
.entry(entry)
|
||||||
|
.or_default()
|
||||||
|
.chunks
|
||||||
|
.push(Dependancy {
|
||||||
|
id: dep,
|
||||||
|
ty: DepType::Direct,
|
||||||
|
});
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -300,16 +316,22 @@ where
|
|||||||
let normal_plan = plans.normal.entry(module).or_default();
|
let normal_plan = plans.normal.entry(module).or_default();
|
||||||
|
|
||||||
for &dep in &deps {
|
for &dep in &deps {
|
||||||
if !normal_plan.chunks.contains(&dep)
|
let contains = normal_plan.chunks.iter().any(|d| d.id == dep);
|
||||||
&& !normal_plan.transitive_chunks.contains(&dep)
|
|
||||||
{
|
if !contains {
|
||||||
if dependants.contains(&module) {
|
if dependants.contains(&module) {
|
||||||
log::trace!("Normal: {:?} => {:?}", module, dep);
|
log::trace!("Normal (non-es6): {:?} => {:?}", module, dep);
|
||||||
// `entry` depends on `module` directly
|
// `entry` depends on `module` directly
|
||||||
normal_plan.chunks.push(dep);
|
normal_plan.chunks.push(Dependancy {
|
||||||
|
id: dep,
|
||||||
|
ty: DepType::Direct,
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
log::trace!("Transitive: {:?} => {:?}", module, dep);
|
log::trace!("Transitive (non-es6): {:?} => {:?}", module, dep);
|
||||||
normal_plan.transitive_chunks.push(dep);
|
normal_plan.chunks.push(Dependancy {
|
||||||
|
id: dep,
|
||||||
|
ty: DepType::Transitive,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -319,11 +341,18 @@ where
|
|||||||
|
|
||||||
if is_reexport {
|
if is_reexport {
|
||||||
let normal_plan = plans.normal.entry(entry).or_default();
|
let normal_plan = plans.normal.entry(entry).or_default();
|
||||||
if !normal_plan.chunks.contains(&dep) {
|
if normal_plan
|
||||||
|
.chunks
|
||||||
|
.iter()
|
||||||
|
.all(|dependancy| dependancy.id != dep)
|
||||||
|
{
|
||||||
done.insert(dep);
|
done.insert(dep);
|
||||||
|
|
||||||
log::trace!("Normal: {:?} => {:?}", entry, dep);
|
log::trace!("Normal: {:?} => {:?}", entry, dep);
|
||||||
normal_plan.chunks.push(dep);
|
normal_plan.chunks.push(Dependancy {
|
||||||
|
id: dep,
|
||||||
|
ty: DepType::Direct,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -332,16 +361,14 @@ where
|
|||||||
// Should be merged as a transitive dependency.
|
// Should be merged as a transitive dependency.
|
||||||
let higher_module = if plans.entries.contains(&dependants[0]) {
|
let higher_module = if plans.entries.contains(&dependants[0]) {
|
||||||
dependants[0]
|
dependants[0]
|
||||||
} else if dependants.len() == 2
|
} else if dependants.len() == 2 && plans.entries.contains(&dependants[1]) {
|
||||||
&& plans.entries.contains(&dependants[1])
|
|
||||||
{
|
|
||||||
dependants[1]
|
dependants[1]
|
||||||
} else {
|
} else {
|
||||||
least_common_ancestor(&builder, &dependants)
|
least_common_ancestor(&builder, &dependants)
|
||||||
};
|
};
|
||||||
|
|
||||||
if dependants.len() == 2 && dependants.contains(&higher_module) {
|
if dependants.len() == 2 && dependants.contains(&higher_module) {
|
||||||
let mut entry = if is_reexport {
|
let mut entry = if should_be_reexport {
|
||||||
higher_module
|
higher_module
|
||||||
} else {
|
} else {
|
||||||
*dependants.iter().find(|&&v| v != higher_module).unwrap()
|
*dependants.iter().find(|&&v| v != higher_module).unwrap()
|
||||||
@ -353,31 +380,63 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
let normal_plan = plans.normal.entry(entry).or_default();
|
let normal_plan = plans.normal.entry(entry).or_default();
|
||||||
if !normal_plan.chunks.contains(&dep) {
|
if normal_plan
|
||||||
|
.chunks
|
||||||
|
.iter()
|
||||||
|
.all(|dependancy| dependancy.id != dep)
|
||||||
|
{
|
||||||
log::trace!("Normal: {:?} => {:?}", entry, dep);
|
log::trace!("Normal: {:?} => {:?}", entry, dep);
|
||||||
done.insert(dep);
|
done.insert(dep);
|
||||||
normal_plan.chunks.push(dep);
|
normal_plan.chunks.push(Dependancy {
|
||||||
|
id: dep,
|
||||||
|
ty: DepType::Direct,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let t = &mut plans
|
if self.scope.should_be_wrapped_with_a_fn(dep) {
|
||||||
.normal
|
let normal_entry = &mut plans.normal.entry(entry).or_default();
|
||||||
.entry(higher_module)
|
|
||||||
.or_default()
|
let t = &mut normal_entry.chunks;
|
||||||
.transitive_chunks;
|
if t.iter().all(|dependancy| dependancy.id != dep) {
|
||||||
if !t.contains(&dep) {
|
log::info!("Normal, esm: {:?} => {:?}", entry, dep);
|
||||||
|
done.insert(dep);
|
||||||
|
t.push(Dependancy {
|
||||||
|
id: dep,
|
||||||
|
ty: DepType::Direct,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let normal_entry =
|
||||||
|
&mut plans.normal.entry(higher_module).or_default();
|
||||||
|
|
||||||
|
let t = &mut normal_entry.chunks;
|
||||||
|
if t.iter().all(|dependancy| dependancy.id != dep) {
|
||||||
log::trace!("Transitive: {:?} => {:?}", entry, dep);
|
log::trace!("Transitive: {:?} => {:?}", entry, dep);
|
||||||
done.insert(dep);
|
done.insert(dep);
|
||||||
t.push(dep)
|
t.push(Dependancy {
|
||||||
|
id: dep,
|
||||||
|
ty: if should_be_reexport {
|
||||||
|
DepType::Direct
|
||||||
|
} else {
|
||||||
|
DepType::Transitive
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Direct dependency.
|
// Direct dependency.
|
||||||
log::trace!("Normal: {:?} => {:?}", entry, dep);
|
log::trace!("Normal: {:?} => {:?}", entry, dep);
|
||||||
done.insert(dep);
|
done.insert(dep);
|
||||||
plans.normal.entry(entry).or_default().chunks.push(dep);
|
plans
|
||||||
}
|
.normal
|
||||||
|
.entry(entry)
|
||||||
continue;
|
.or_default()
|
||||||
|
.chunks
|
||||||
|
.push(Dependancy {
|
||||||
|
id: dep,
|
||||||
|
ty: DepType::Direct,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -385,7 +444,7 @@ where
|
|||||||
|
|
||||||
// Sort transitive chunks topologically.
|
// Sort transitive chunks topologically.
|
||||||
for (_, normal_plan) in &mut plans.normal {
|
for (_, normal_plan) in &mut plans.normal {
|
||||||
toposort(&builder, &mut normal_plan.transitive_chunks);
|
toposort(&builder, &mut normal_plan.chunks);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle circular imports
|
// Handle circular imports
|
||||||
@ -416,7 +475,8 @@ where
|
|||||||
|
|
||||||
{
|
{
|
||||||
let c = &mut plans.normal.entry(dep).or_default().chunks;
|
let c = &mut plans.normal.entry(dep).or_default().chunks;
|
||||||
if let Some(pos) = c.iter().position(|&v| v == circular_member)
|
if let Some(pos) =
|
||||||
|
c.iter().position(|&v| v.id == circular_member)
|
||||||
{
|
{
|
||||||
c.remove(pos);
|
c.remove(pos);
|
||||||
}
|
}
|
||||||
@ -428,7 +488,7 @@ where
|
|||||||
.entry(circular_member)
|
.entry(circular_member)
|
||||||
.or_default()
|
.or_default()
|
||||||
.chunks;
|
.chunks;
|
||||||
if let Some(pos) = c.iter().position(|&v| v == dep) {
|
if let Some(pos) = c.iter().position(|&v| v.id == dep) {
|
||||||
c.remove(pos);
|
c.remove(pos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -550,24 +610,51 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn toposort(b: &PlanBuilder, module_ids: &mut Vec<ModuleId>) {
|
fn toposort(b: &PlanBuilder, module_ids: &mut Vec<Dependancy>) {
|
||||||
let len = module_ids.len();
|
|
||||||
|
|
||||||
if module_ids.len() <= 1 {
|
if module_ids.len() <= 1 {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mut graph = b.direct_deps.clone();
|
||||||
|
let len = module_ids.len();
|
||||||
|
let mut orders: Vec<usize> = vec![];
|
||||||
|
|
||||||
|
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 {
|
for i in 0..len {
|
||||||
for j in i..len {
|
let m = module_ids[i].id;
|
||||||
let mi = module_ids[i];
|
if graph.neighbors_directed(m, Incoming).count() != 0 {
|
||||||
let mj = module_ids[j];
|
|
||||||
if mi == mj {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if b.direct_deps.contains_edge(mj, mi) {
|
did_work = true;
|
||||||
module_ids.swap(i, j);
|
orders.push(i);
|
||||||
|
// Remove dependency
|
||||||
|
graph.remove_node(m);
|
||||||
|
}
|
||||||
|
|
||||||
|
if !did_work {
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for i in 0..len {
|
||||||
|
if orders.contains(&i) {
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
orders.push(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut buf = Vec::with_capacity(module_ids.len());
|
||||||
|
for order in orders {
|
||||||
|
let item = module_ids[order];
|
||||||
|
buf.push(item)
|
||||||
|
}
|
||||||
|
|
||||||
|
*module_ids = buf;
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
use super::Plan;
|
use super::Plan;
|
||||||
use crate::bundler::tests::{suite, Tester};
|
use crate::bundler::{
|
||||||
|
chunk::plan::DepType,
|
||||||
|
tests::{suite, Tester},
|
||||||
|
};
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
use swc_common::FileName;
|
use swc_common::FileName;
|
||||||
|
|
||||||
@ -18,18 +21,33 @@ fn assert_normal_transitive(
|
|||||||
) {
|
) {
|
||||||
if deps.is_empty() {
|
if deps.is_empty() {
|
||||||
if let Some(v) = p.normal.get(&t.id(&format!("{}.js", entry))) {
|
if let Some(v) = p.normal.get(&t.id(&format!("{}.js", entry))) {
|
||||||
assert_eq!(v.chunks, vec![], "Should be empty");
|
let actual = v
|
||||||
|
.chunks
|
||||||
|
.iter()
|
||||||
|
.filter(|dep| match dep.ty {
|
||||||
|
DepType::Direct => true,
|
||||||
|
_ => false,
|
||||||
|
})
|
||||||
|
.copied()
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
assert_eq!(actual, vec![], "Should be empty");
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
assert_eq!(
|
let actual = p.normal[&t.id(&format!("{}.js", entry))]
|
||||||
p.normal[&t.id(&format!("{}.js", entry))]
|
|
||||||
.chunks
|
.chunks
|
||||||
.iter()
|
.iter()
|
||||||
.cloned()
|
.filter(|dep| match dep.ty {
|
||||||
.collect::<HashSet<_>>(),
|
DepType::Direct => true,
|
||||||
|
_ => false,
|
||||||
|
})
|
||||||
|
.map(|dep| dep.id)
|
||||||
|
.collect::<HashSet<_>>();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
actual,
|
||||||
deps.into_iter()
|
deps.into_iter()
|
||||||
.map(|s| format!("{}.js", s))
|
.map(|s| format!("{}.js", s))
|
||||||
.map(|s| t.id(&s))
|
.map(|s| t.id(&s))
|
||||||
@ -40,9 +58,13 @@ fn assert_normal_transitive(
|
|||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
p.normal[&t.id(&format!("{}.js", entry))]
|
p.normal[&t.id(&format!("{}.js", entry))]
|
||||||
.transitive_chunks
|
.chunks
|
||||||
.iter()
|
.iter()
|
||||||
.cloned()
|
.filter(|dep| match dep.ty {
|
||||||
|
DepType::Transitive => true,
|
||||||
|
_ => false,
|
||||||
|
})
|
||||||
|
.map(|dep| dep.id)
|
||||||
.collect::<HashSet<_>>(),
|
.collect::<HashSet<_>>(),
|
||||||
transitive_deps
|
transitive_deps
|
||||||
.into_iter()
|
.into_iter()
|
||||||
@ -958,3 +980,55 @@ fn deno_003() {
|
|||||||
Ok(())
|
Ok(())
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn deno_8302_3() {
|
||||||
|
suite()
|
||||||
|
.file(
|
||||||
|
"main.js",
|
||||||
|
"
|
||||||
|
import * as a from './a';
|
||||||
|
",
|
||||||
|
)
|
||||||
|
.file(
|
||||||
|
"a.js",
|
||||||
|
"
|
||||||
|
import * as b from './b';
|
||||||
|
import * as lib from './lib';
|
||||||
|
",
|
||||||
|
)
|
||||||
|
.file(
|
||||||
|
"b.js",
|
||||||
|
"
|
||||||
|
import * as c from './c';
|
||||||
|
import * as lib from './lib';
|
||||||
|
",
|
||||||
|
)
|
||||||
|
.file(
|
||||||
|
"c.js",
|
||||||
|
"
|
||||||
|
import * as a from './a';
|
||||||
|
import * as lib from './lib';
|
||||||
|
",
|
||||||
|
)
|
||||||
|
.file("lib.js", "")
|
||||||
|
.run(|t| {
|
||||||
|
let module = t
|
||||||
|
.bundler
|
||||||
|
.load_transformed(&FileName::Real("main.js".into()))?
|
||||||
|
.unwrap();
|
||||||
|
let mut entries = HashMap::default();
|
||||||
|
entries.insert("main.js".to_string(), module.clone());
|
||||||
|
|
||||||
|
let p = t.bundler.calculate_plan(entries)?;
|
||||||
|
|
||||||
|
dbg!(&p);
|
||||||
|
|
||||||
|
assert_normal(t, &p, "main", &["a"]);
|
||||||
|
|
||||||
|
assert_normal(t, &p, "a", &["lib"]);
|
||||||
|
assert_circular(t, &p, "a", &["b", "c"]);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
});
|
||||||
|
}
|
||||||
|
@ -1,571 +0,0 @@
|
|||||||
use crate::{bundler::load::Specifier, load::Load, resolve::Resolve, Bundler};
|
|
||||||
use std::collections::HashMap;
|
|
||||||
use swc_atoms::{js_word, JsWord};
|
|
||||||
use swc_common::{Mark, Spanned, SyntaxContext, DUMMY_SP};
|
|
||||||
use swc_ecma_ast::*;
|
|
||||||
use swc_ecma_utils::{ident::IdentLike, Id, StmtLike};
|
|
||||||
use swc_ecma_visit::{noop_fold_type, noop_visit_mut_type, Fold, FoldWith, VisitMut, VisitMutWith};
|
|
||||||
|
|
||||||
pub(super) type RemarkMap = HashMap<Id, SyntaxContext>;
|
|
||||||
|
|
||||||
impl<L, R> Bundler<'_, L, R>
|
|
||||||
where
|
|
||||||
L: Load,
|
|
||||||
R: Resolve,
|
|
||||||
{
|
|
||||||
/// Applied to dependencies
|
|
||||||
///
|
|
||||||
///
|
|
||||||
/// If `imports` is [None], it means all specifier can be used.
|
|
||||||
pub(super) fn remark_exports(
|
|
||||||
&self,
|
|
||||||
mut dep: Module,
|
|
||||||
dep_ctxt: SyntaxContext,
|
|
||||||
imports: Option<&[Specifier]>,
|
|
||||||
unexport: bool,
|
|
||||||
) -> Module {
|
|
||||||
let mut v = ExportRenamer {
|
|
||||||
dep_ctxt,
|
|
||||||
imports,
|
|
||||||
extras: vec![],
|
|
||||||
remark_map: Default::default(),
|
|
||||||
unexport,
|
|
||||||
};
|
|
||||||
dep = dep.fold_with(&mut v);
|
|
||||||
|
|
||||||
if !v.remark_map.is_empty() {
|
|
||||||
log::debug!("Remark map: {:?}", v.remark_map);
|
|
||||||
|
|
||||||
// Swap syntax context. Although name is remark, it's actually
|
|
||||||
// swapping because ExportRenamer inserts two-side conversion
|
|
||||||
// rule.
|
|
||||||
self.remark(&mut dep, &v.remark_map);
|
|
||||||
}
|
|
||||||
|
|
||||||
dep
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) fn remark(&self, module: &mut Module, remark_map: &RemarkMap) {
|
|
||||||
module.visit_mut_with(&mut RemarkIdents { map: remark_map });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// Applied to dependency modules.
|
|
||||||
struct ExportRenamer<'a> {
|
|
||||||
/// The mark applied to identifiers exported to dependant modules.
|
|
||||||
dep_ctxt: SyntaxContext,
|
|
||||||
remark_map: RemarkMap,
|
|
||||||
|
|
||||||
/// Dependant module's import
|
|
||||||
imports: Option<&'a [Specifier]>,
|
|
||||||
extras: Vec<Stmt>,
|
|
||||||
|
|
||||||
unexport: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ExportRenamer<'_> {
|
|
||||||
/// Returns [SyntaxContext] for the name of variable.
|
|
||||||
fn mark_as_remarking_required(&mut self, exported: Id, orig: Id) -> SyntaxContext {
|
|
||||||
log::debug!("Remarking required: {:?} -> {:?}", exported, orig);
|
|
||||||
|
|
||||||
let ctxt = SyntaxContext::empty().apply_mark(Mark::fresh(Mark::root()));
|
|
||||||
self.remark_map
|
|
||||||
.insert((exported.0.clone(), ctxt), exported.1);
|
|
||||||
self.remark_map.insert(orig, ctxt);
|
|
||||||
ctxt
|
|
||||||
}
|
|
||||||
|
|
||||||
fn aliased_import(&self, sym: &JsWord) -> Option<Id> {
|
|
||||||
if self.imports.is_none() {
|
|
||||||
return Some((sym.clone(), self.dep_ctxt));
|
|
||||||
}
|
|
||||||
|
|
||||||
self.imports
|
|
||||||
.as_ref()
|
|
||||||
.unwrap()
|
|
||||||
.iter()
|
|
||||||
.find_map(|s| match s {
|
|
||||||
Specifier::Specific {
|
|
||||||
ref local,
|
|
||||||
alias: Some(ref alias),
|
|
||||||
..
|
|
||||||
} if *alias == *sym => Some(local.clone()),
|
|
||||||
Specifier::Specific {
|
|
||||||
ref local,
|
|
||||||
alias: None,
|
|
||||||
..
|
|
||||||
} if *local == *sym => Some(local.clone()),
|
|
||||||
_ => None,
|
|
||||||
})
|
|
||||||
.map(|v| v.to_id())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ExportRenamer<'_> {
|
|
||||||
fn fold_stmt_like<T>(&mut self, items: Vec<T>) -> Vec<T>
|
|
||||||
where
|
|
||||||
T: FoldWith<Self> + StmtLike,
|
|
||||||
{
|
|
||||||
let mut buf = Vec::with_capacity(items.len() + 4);
|
|
||||||
|
|
||||||
for item in items {
|
|
||||||
let item = item.fold_with(self);
|
|
||||||
buf.push(item);
|
|
||||||
|
|
||||||
buf.extend(self.extras.drain(..).map(|v| T::from_stmt(v)))
|
|
||||||
}
|
|
||||||
|
|
||||||
buf
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Fold for ExportRenamer<'_> {
|
|
||||||
noop_fold_type!();
|
|
||||||
|
|
||||||
/// no-op, as imports or exports are only related to top level nodes.
|
|
||||||
fn fold_class(&mut self, node: Class) -> Class {
|
|
||||||
node
|
|
||||||
}
|
|
||||||
|
|
||||||
/// no-op, as imports or exports are only related to top level nodes.
|
|
||||||
fn fold_function(&mut self, node: Function) -> Function {
|
|
||||||
node
|
|
||||||
}
|
|
||||||
|
|
||||||
fn fold_module_item(&mut self, item: ModuleItem) -> ModuleItem {
|
|
||||||
let span = item.span();
|
|
||||||
let item: ModuleItem = item.fold_children_with(self);
|
|
||||||
|
|
||||||
match item {
|
|
||||||
ModuleItem::Stmt(..) => return item,
|
|
||||||
|
|
||||||
ModuleItem::ModuleDecl(ModuleDecl::ExportDefaultDecl(export)) => {
|
|
||||||
let ident = match export.decl {
|
|
||||||
DefaultDecl::Class(ClassExpr {
|
|
||||||
ident: Some(ident),
|
|
||||||
class,
|
|
||||||
}) => {
|
|
||||||
return ModuleItem::Stmt(Stmt::Decl(Decl::Class(ClassDecl {
|
|
||||||
ident,
|
|
||||||
declare: false,
|
|
||||||
class,
|
|
||||||
})))
|
|
||||||
}
|
|
||||||
DefaultDecl::Fn(FnExpr {
|
|
||||||
ident: Some(ident),
|
|
||||||
function,
|
|
||||||
}) => {
|
|
||||||
return ModuleItem::Stmt(Stmt::Decl(Decl::Fn(FnDecl {
|
|
||||||
ident,
|
|
||||||
declare: false,
|
|
||||||
function,
|
|
||||||
})))
|
|
||||||
}
|
|
||||||
_ => self.aliased_import(&js_word!("default")),
|
|
||||||
};
|
|
||||||
|
|
||||||
let ident = if let Some(id) = ident {
|
|
||||||
id
|
|
||||||
} else {
|
|
||||||
log::debug!("Dropping export default declaration because it's not used");
|
|
||||||
|
|
||||||
return Stmt::Empty(EmptyStmt { span: DUMMY_SP }).into();
|
|
||||||
};
|
|
||||||
|
|
||||||
match export.decl {
|
|
||||||
DefaultDecl::Class(c) => {
|
|
||||||
return ModuleItem::Stmt(Stmt::Decl(Decl::Var(VarDecl {
|
|
||||||
span: export.span,
|
|
||||||
kind: VarDeclKind::Const,
|
|
||||||
declare: false,
|
|
||||||
decls: vec![VarDeclarator {
|
|
||||||
span: DUMMY_SP,
|
|
||||||
name: Pat::Ident(Ident::new(
|
|
||||||
ident.0,
|
|
||||||
DUMMY_SP.with_ctxt(self.dep_ctxt),
|
|
||||||
)),
|
|
||||||
init: Some(Box::new(Expr::Class(c))),
|
|
||||||
definite: false,
|
|
||||||
}],
|
|
||||||
})))
|
|
||||||
}
|
|
||||||
DefaultDecl::Fn(f) => {
|
|
||||||
return ModuleItem::Stmt(Stmt::Decl(Decl::Var(VarDecl {
|
|
||||||
span: export.span,
|
|
||||||
kind: VarDeclKind::Const,
|
|
||||||
declare: false,
|
|
||||||
decls: vec![VarDeclarator {
|
|
||||||
span: DUMMY_SP,
|
|
||||||
name: Pat::Ident(Ident::new(
|
|
||||||
ident.0,
|
|
||||||
DUMMY_SP.with_ctxt(self.dep_ctxt),
|
|
||||||
)),
|
|
||||||
init: Some(Box::new(Expr::Fn(f))),
|
|
||||||
definite: false,
|
|
||||||
}],
|
|
||||||
})))
|
|
||||||
}
|
|
||||||
DefaultDecl::TsInterfaceDecl(_) => {
|
|
||||||
log::debug!(
|
|
||||||
"Dropping export default declaration because ts interface declaration \
|
|
||||||
is not supported yet"
|
|
||||||
);
|
|
||||||
|
|
||||||
return Stmt::Empty(EmptyStmt { span: DUMMY_SP }).into();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ModuleItem::ModuleDecl(ModuleDecl::ExportDefaultExpr(e)) => {
|
|
||||||
let ident = self.aliased_import(&js_word!("default"));
|
|
||||||
|
|
||||||
// TODO: Optimize if type of expression is identifier.
|
|
||||||
|
|
||||||
return if let Some(ident) = ident {
|
|
||||||
let ctxt = self.mark_as_remarking_required(
|
|
||||||
(ident.0.clone(), self.dep_ctxt),
|
|
||||||
ident.clone(),
|
|
||||||
);
|
|
||||||
|
|
||||||
ModuleItem::Stmt(Stmt::Decl(Decl::Var(VarDecl {
|
|
||||||
span: e.span,
|
|
||||||
kind: VarDeclKind::Const,
|
|
||||||
declare: false,
|
|
||||||
decls: vec![VarDeclarator {
|
|
||||||
span: DUMMY_SP,
|
|
||||||
name: Pat::Ident(Ident::new(ident.0, DUMMY_SP.with_ctxt(ctxt))),
|
|
||||||
init: Some(e.expr),
|
|
||||||
definite: false,
|
|
||||||
}],
|
|
||||||
})))
|
|
||||||
} else {
|
|
||||||
log::trace!("Removing default export expression as it's not imported");
|
|
||||||
|
|
||||||
// Expression statement cannot start with function
|
|
||||||
ModuleItem::Stmt(Stmt::Expr(ExprStmt {
|
|
||||||
span: e.span,
|
|
||||||
expr: Box::new(Expr::Paren(ParenExpr {
|
|
||||||
span: DUMMY_SP,
|
|
||||||
expr: e.expr,
|
|
||||||
})),
|
|
||||||
}))
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
ModuleItem::ModuleDecl(ModuleDecl::ExportNamed(e)) if e.src.is_none() => {
|
|
||||||
if self.unexport {
|
|
||||||
let mut var_decls = Vec::with_capacity(e.specifiers.len());
|
|
||||||
|
|
||||||
e.specifiers.into_iter().for_each(|specifier| {
|
|
||||||
let span = specifier.span();
|
|
||||||
let ident = match &specifier {
|
|
||||||
// TODO
|
|
||||||
ExportSpecifier::Namespace(s) => self.aliased_import(&s.name.sym),
|
|
||||||
ExportSpecifier::Default(..) => {
|
|
||||||
self.aliased_import(&js_word!("default"))
|
|
||||||
}
|
|
||||||
ExportSpecifier::Named(s) => {
|
|
||||||
if let Some(exported) = &s.exported {
|
|
||||||
// We need remarking
|
|
||||||
|
|
||||||
match self.aliased_import(&exported.sym) {
|
|
||||||
Some(v) => {
|
|
||||||
let ctxt = self.mark_as_remarking_required(
|
|
||||||
v.clone(),
|
|
||||||
s.orig.to_id(),
|
|
||||||
);
|
|
||||||
log::trace!(
|
|
||||||
"exported = {}{:?}",
|
|
||||||
exported.sym,
|
|
||||||
exported.span.ctxt
|
|
||||||
);
|
|
||||||
log::trace!("id = {:?}", v);
|
|
||||||
log::trace!(
|
|
||||||
"orig = {}{:?}",
|
|
||||||
s.orig.sym,
|
|
||||||
s.orig.span.ctxt()
|
|
||||||
);
|
|
||||||
|
|
||||||
Some((v.0, ctxt))
|
|
||||||
}
|
|
||||||
None => None,
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
match self.aliased_import(&s.orig.sym) {
|
|
||||||
Some(id) => {
|
|
||||||
let ctxt = self.mark_as_remarking_required(
|
|
||||||
(s.orig.sym.clone(), self.dep_ctxt),
|
|
||||||
s.orig.to_id(),
|
|
||||||
);
|
|
||||||
self.remark_map
|
|
||||||
.insert((id.0.clone(), ctxt), self.dep_ctxt);
|
|
||||||
|
|
||||||
Some((id.0, ctxt))
|
|
||||||
}
|
|
||||||
None => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Some(i) = ident {
|
|
||||||
let orig = match specifier {
|
|
||||||
// TODO
|
|
||||||
ExportSpecifier::Namespace(s) => s.name,
|
|
||||||
ExportSpecifier::Default(..) => {
|
|
||||||
Ident::new(js_word!("default"), span)
|
|
||||||
}
|
|
||||||
ExportSpecifier::Named(s) => s.orig,
|
|
||||||
};
|
|
||||||
|
|
||||||
var_decls.push(VarDeclarator {
|
|
||||||
span,
|
|
||||||
name: Pat::Ident(Ident::new(i.0, DUMMY_SP.with_ctxt(i.1))),
|
|
||||||
init: Some(Box::new(Expr::Ident(orig))),
|
|
||||||
definite: false,
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
log::trace!(
|
|
||||||
"Removing export specifier {:?} as it's not imported",
|
|
||||||
specifier
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if !var_decls.is_empty() {
|
|
||||||
self.extras.push(Stmt::Decl(Decl::Var(VarDecl {
|
|
||||||
span,
|
|
||||||
kind: VarDeclKind::Const,
|
|
||||||
declare: false,
|
|
||||||
decls: var_decls,
|
|
||||||
})))
|
|
||||||
}
|
|
||||||
|
|
||||||
return Stmt::Empty(EmptyStmt { span }).into();
|
|
||||||
} else {
|
|
||||||
let mut export_specifiers = Vec::with_capacity(e.specifiers.len());
|
|
||||||
|
|
||||||
e.specifiers.into_iter().for_each(|specifier| {
|
|
||||||
let span = specifier.span();
|
|
||||||
let ident = match &specifier {
|
|
||||||
// TODO
|
|
||||||
ExportSpecifier::Namespace(s) => self.aliased_import(&s.name.sym),
|
|
||||||
ExportSpecifier::Default(..) => {
|
|
||||||
self.aliased_import(&js_word!("default"))
|
|
||||||
}
|
|
||||||
ExportSpecifier::Named(s) => {
|
|
||||||
if let Some(exported) = &s.exported {
|
|
||||||
// We need remarking
|
|
||||||
|
|
||||||
match self.aliased_import(&exported.sym) {
|
|
||||||
Some(v) => {
|
|
||||||
let ctxt = self.mark_as_remarking_required(
|
|
||||||
v.clone(),
|
|
||||||
s.orig.to_id(),
|
|
||||||
);
|
|
||||||
log::trace!(
|
|
||||||
"exported = {}{:?}",
|
|
||||||
exported.sym,
|
|
||||||
exported.span.ctxt
|
|
||||||
);
|
|
||||||
log::trace!("id = {:?}", v);
|
|
||||||
log::trace!(
|
|
||||||
"orig = {}{:?}",
|
|
||||||
s.orig.sym,
|
|
||||||
s.orig.span.ctxt()
|
|
||||||
);
|
|
||||||
|
|
||||||
Some((v.0, ctxt))
|
|
||||||
}
|
|
||||||
None => None,
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
match self.aliased_import(&s.orig.sym) {
|
|
||||||
Some(id) => {
|
|
||||||
let ctxt = self.mark_as_remarking_required(
|
|
||||||
(s.orig.sym.clone(), self.dep_ctxt),
|
|
||||||
s.orig.to_id(),
|
|
||||||
);
|
|
||||||
|
|
||||||
Some((id.0, ctxt))
|
|
||||||
}
|
|
||||||
None => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Some(i) = ident {
|
|
||||||
let orig = match specifier {
|
|
||||||
// TODO
|
|
||||||
ExportSpecifier::Namespace(s) => s.name,
|
|
||||||
ExportSpecifier::Default(..) => {
|
|
||||||
Ident::new(js_word!("default"), span)
|
|
||||||
}
|
|
||||||
ExportSpecifier::Named(s) => s.orig,
|
|
||||||
};
|
|
||||||
|
|
||||||
export_specifiers.push(ExportSpecifier::Named(ExportNamedSpecifier {
|
|
||||||
span,
|
|
||||||
orig,
|
|
||||||
exported: Some(Ident::new(i.0, DUMMY_SP.with_ctxt(i.1))),
|
|
||||||
}));
|
|
||||||
|
|
||||||
// export_specifiers.push(VarDeclarator {
|
|
||||||
// span,
|
|
||||||
// name: Pat::Ident(Ident::new(i.0,
|
|
||||||
// DUMMY_SP.with_ctxt(i.1))),
|
|
||||||
// init: Some(Box::new(Expr::Ident(orig))),
|
|
||||||
// definite: false,
|
|
||||||
// })
|
|
||||||
} else {
|
|
||||||
log::trace!(
|
|
||||||
"Removing export specifier {:?} as it's not imported (`unexport` \
|
|
||||||
is false, but it's not used)",
|
|
||||||
specifier
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if !export_specifiers.is_empty() {
|
|
||||||
return ModuleItem::ModuleDecl(ModuleDecl::ExportNamed(NamedExport {
|
|
||||||
type_only: false,
|
|
||||||
span: e.span,
|
|
||||||
specifiers: export_specifiers,
|
|
||||||
src: None,
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
return Stmt::Empty(EmptyStmt { span }).into();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ModuleItem::ModuleDecl(ModuleDecl::ExportDecl(mut decl)) => {
|
|
||||||
//
|
|
||||||
return match decl.decl {
|
|
||||||
Decl::TsInterface(_)
|
|
||||||
| Decl::TsTypeAlias(_)
|
|
||||||
| Decl::TsEnum(_)
|
|
||||||
| Decl::TsModule(_) => ModuleItem::ModuleDecl(ModuleDecl::ExportDecl(decl)),
|
|
||||||
|
|
||||||
Decl::Class(mut c) => {
|
|
||||||
c.ident.visit_mut_with(&mut ActualMarker {
|
|
||||||
dep_ctxt: self.dep_ctxt,
|
|
||||||
imports: self.imports,
|
|
||||||
remarks: &mut self.remark_map,
|
|
||||||
});
|
|
||||||
|
|
||||||
if self.unexport {
|
|
||||||
Stmt::Decl(Decl::Class(c)).into()
|
|
||||||
} else {
|
|
||||||
ModuleItem::ModuleDecl(ModuleDecl::ExportDecl(ExportDecl {
|
|
||||||
decl: Decl::Class(c),
|
|
||||||
..decl
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Decl::Fn(mut f) => {
|
|
||||||
f.ident.visit_mut_with(&mut ActualMarker {
|
|
||||||
dep_ctxt: self.dep_ctxt,
|
|
||||||
imports: self.imports,
|
|
||||||
remarks: &mut self.remark_map,
|
|
||||||
});
|
|
||||||
if self.unexport {
|
|
||||||
Stmt::Decl(Decl::Fn(f)).into()
|
|
||||||
} else {
|
|
||||||
ModuleItem::ModuleDecl(ModuleDecl::ExportDecl(ExportDecl {
|
|
||||||
decl: Decl::Fn(f),
|
|
||||||
..decl
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Decl::Var(ref mut v) => {
|
|
||||||
v.decls.visit_mut_with(&mut ActualMarker {
|
|
||||||
dep_ctxt: self.dep_ctxt,
|
|
||||||
imports: self.imports,
|
|
||||||
remarks: &mut self.remark_map,
|
|
||||||
});
|
|
||||||
|
|
||||||
ModuleItem::ModuleDecl(ModuleDecl::ExportDecl(decl))
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
|
|
||||||
item
|
|
||||||
}
|
|
||||||
|
|
||||||
fn fold_module_items(&mut self, items: Vec<ModuleItem>) -> Vec<ModuleItem> {
|
|
||||||
self.fold_stmt_like(items)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn fold_stmts(&mut self, items: Vec<Stmt>) -> Vec<Stmt> {
|
|
||||||
self.fold_stmt_like(items)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct ActualMarker<'a, 'b> {
|
|
||||||
dep_ctxt: SyntaxContext,
|
|
||||||
|
|
||||||
/// Dependant module's import
|
|
||||||
imports: Option<&'a [Specifier]>,
|
|
||||||
|
|
||||||
remarks: &'b mut RemarkMap,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ActualMarker<'_, '_> {
|
|
||||||
fn rename(&mut self, ident: &mut Ident) {
|
|
||||||
if self.imports.is_none() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(id) = self.imports.as_ref().unwrap().iter().find_map(|s| match s {
|
|
||||||
Specifier::Specific {
|
|
||||||
alias: Some(alias),
|
|
||||||
local,
|
|
||||||
} if *alias == ident.sym => Some((local.sym().clone(), ident.span.ctxt)),
|
|
||||||
Specifier::Specific { alias: None, local } if *local == ident.sym => {
|
|
||||||
Some(local.to_id())
|
|
||||||
}
|
|
||||||
_ => None,
|
|
||||||
}) {
|
|
||||||
ident.sym = id.0.clone();
|
|
||||||
self.remarks.insert(id, self.dep_ctxt);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl VisitMut for ActualMarker<'_, '_> {
|
|
||||||
noop_visit_mut_type!();
|
|
||||||
|
|
||||||
fn visit_mut_expr(&mut self, _: &mut Expr) {}
|
|
||||||
|
|
||||||
fn visit_mut_ident(&mut self, ident: &mut Ident) {
|
|
||||||
self.rename(ident)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn visit_mut_private_name(&mut self, _: &mut PrivateName) {}
|
|
||||||
|
|
||||||
fn visit_mut_export_named_specifier(&mut self, s: &mut ExportNamedSpecifier) {
|
|
||||||
s.orig.visit_mut_with(self);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct RemarkIdents<'a> {
|
|
||||||
map: &'a RemarkMap,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl VisitMut for RemarkIdents<'_> {
|
|
||||||
noop_visit_mut_type!();
|
|
||||||
|
|
||||||
fn visit_mut_ident(&mut self, n: &mut Ident) {
|
|
||||||
let id = (*n).to_id();
|
|
||||||
if let Some(&ctxt) = self.map.get(&id) {
|
|
||||||
n.span = n.span.with_ctxt(ctxt);
|
|
||||||
log::debug!("Remark: {:?} -> {:?}", id, ctxt)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn visit_mut_private_name(&mut self, _: &mut PrivateName) {}
|
|
||||||
}
|
|
369
bundler/src/bundler/chunk/sort.rs
Normal file
369
bundler/src/bundler/chunk/sort.rs
Normal file
@ -0,0 +1,369 @@
|
|||||||
|
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<usize, Required>;
|
||||||
|
|
||||||
|
/// 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<ModuleItem>) {
|
||||||
|
new.retain(|item| match item {
|
||||||
|
ModuleItem::Stmt(Stmt::Empty(..)) => false,
|
||||||
|
_ => true,
|
||||||
|
});
|
||||||
|
|
||||||
|
let mut graph = StmtDepGraph::default();
|
||||||
|
let mut declared_by = HashMap::<Id, Vec<usize>>::default();
|
||||||
|
let mut uninitialized_ids = HashMap::<Id, usize>::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<Id> = 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<usize> = 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::<Vec<_>>()
|
||||||
|
{
|
||||||
|
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_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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -21,12 +21,14 @@ where
|
|||||||
&self,
|
&self,
|
||||||
file_name: &FileName,
|
file_name: &FileName,
|
||||||
module: &mut Module,
|
module: &mut Module,
|
||||||
|
export_ctxt: SyntaxContext,
|
||||||
) -> RawExports {
|
) -> RawExports {
|
||||||
self.run(|| {
|
self.run(|| {
|
||||||
let mut v = ExportFinder {
|
let mut v = ExportFinder {
|
||||||
info: Default::default(),
|
info: Default::default(),
|
||||||
file_name,
|
file_name,
|
||||||
bundler: self,
|
bundler: self,
|
||||||
|
export_ctxt,
|
||||||
};
|
};
|
||||||
|
|
||||||
module.visit_mut_with(&mut v);
|
module.visit_mut_with(&mut v);
|
||||||
@ -56,6 +58,7 @@ where
|
|||||||
info: RawExports,
|
info: RawExports,
|
||||||
file_name: &'a FileName,
|
file_name: &'a FileName,
|
||||||
bundler: &'a Bundler<'b, L, R>,
|
bundler: &'a Bundler<'b, L, R>,
|
||||||
|
export_ctxt: SyntaxContext,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<L, R> ExportFinder<'_, '_, L, R>
|
impl<L, R> ExportFinder<'_, '_, L, R>
|
||||||
@ -63,7 +66,8 @@ where
|
|||||||
L: Load,
|
L: Load,
|
||||||
R: Resolve,
|
R: Resolve,
|
||||||
{
|
{
|
||||||
fn ctxt_for(&self, src: &JsWord) -> Option<SyntaxContext> {
|
/// Returns `(local, export)`.
|
||||||
|
fn ctxt_for(&self, src: &JsWord) -> Option<(SyntaxContext, SyntaxContext)> {
|
||||||
// Don't apply mark if it's a core module.
|
// Don't apply mark if it's a core module.
|
||||||
if self
|
if self
|
||||||
.bundler
|
.bundler
|
||||||
@ -75,10 +79,12 @@ where
|
|||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let path = self.bundler.resolve(self.file_name, src).ok()?;
|
let path = self.bundler.resolve(self.file_name, src).ok()?;
|
||||||
let (_, mark) = self.bundler.scope.module_id_gen.gen(&path);
|
let (_, local_mark, export_mark) = self.bundler.scope.module_id_gen.gen(&path);
|
||||||
let ctxt = SyntaxContext::empty();
|
|
||||||
|
|
||||||
Some(ctxt.apply_mark(mark))
|
Some((
|
||||||
|
SyntaxContext::empty().apply_mark(local_mark),
|
||||||
|
SyntaxContext::empty().apply_mark(export_mark),
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mark_as_wrapping_required(&self, src: &JsWord) {
|
fn mark_as_wrapping_required(&self, src: &JsWord) {
|
||||||
@ -97,7 +103,7 @@ where
|
|||||||
Ok(v) => v,
|
Ok(v) => v,
|
||||||
_ => return,
|
_ => return,
|
||||||
};
|
};
|
||||||
let (id, _) = self.bundler.scope.module_id_gen.gen(&path);
|
let (id, _, _) = self.bundler.scope.module_id_gen.gen(&path);
|
||||||
|
|
||||||
self.bundler.scope.mark_as_wrapping_required(id);
|
self.bundler.scope.mark_as_wrapping_required(id);
|
||||||
}
|
}
|
||||||
@ -207,6 +213,10 @@ where
|
|||||||
for s in &mut named.specifiers {
|
for s in &mut named.specifiers {
|
||||||
match s {
|
match s {
|
||||||
ExportSpecifier::Namespace(n) => {
|
ExportSpecifier::Namespace(n) => {
|
||||||
|
if let Some((_, export_ctxt)) = ctxt {
|
||||||
|
n.name.span.ctxt = export_ctxt;
|
||||||
|
}
|
||||||
|
|
||||||
need_wrapping = true;
|
need_wrapping = true;
|
||||||
v.push(Specifier::Namespace {
|
v.push(Specifier::Namespace {
|
||||||
local: n.name.clone().into(),
|
local: n.name.clone().into(),
|
||||||
@ -220,8 +230,19 @@ where
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
ExportSpecifier::Named(n) => {
|
ExportSpecifier::Named(n) => {
|
||||||
if let Some(ctxt) = ctxt {
|
if let Some((_, export_ctxt)) = ctxt {
|
||||||
n.orig.span = n.orig.span.with_ctxt(ctxt);
|
n.orig.span.ctxt = export_ctxt;
|
||||||
|
}
|
||||||
|
|
||||||
|
match &mut n.exported {
|
||||||
|
Some(exported) => {
|
||||||
|
exported.span.ctxt = self.export_ctxt;
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
let mut exported: Ident = n.orig.clone();
|
||||||
|
exported.span.ctxt = self.export_ctxt;
|
||||||
|
n.exported = Some(exported);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(exported) = &n.exported {
|
if let Some(exported) = &n.exported {
|
||||||
@ -247,6 +268,11 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
ModuleItem::ModuleDecl(ModuleDecl::ExportAll(all)) => {
|
ModuleItem::ModuleDecl(ModuleDecl::ExportAll(all)) => {
|
||||||
|
let ctxt = self.ctxt_for(&all.src.value);
|
||||||
|
if let Some((_, export_ctxt)) = ctxt {
|
||||||
|
all.span.ctxt = export_ctxt;
|
||||||
|
}
|
||||||
|
|
||||||
self.info.items.entry(Some(all.src.clone())).or_default();
|
self.info.items.entry(Some(all.src.clone())).or_default();
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
|
@ -26,13 +26,13 @@ where
|
|||||||
&self,
|
&self,
|
||||||
path: &FileName,
|
path: &FileName,
|
||||||
module: &mut Module,
|
module: &mut Module,
|
||||||
module_mark: Mark,
|
module_local_mark: Mark,
|
||||||
) -> RawImports {
|
) -> RawImports {
|
||||||
self.run(|| {
|
self.run(|| {
|
||||||
let body = replace(&mut module.body, vec![]);
|
let body = replace(&mut module.body, vec![]);
|
||||||
|
|
||||||
let mut v = ImportHandler {
|
let mut v = ImportHandler {
|
||||||
module_ctxt: SyntaxContext::empty().apply_mark(module_mark),
|
module_ctxt: SyntaxContext::empty().apply_mark(module_local_mark),
|
||||||
path,
|
path,
|
||||||
bundler: self,
|
bundler: self,
|
||||||
top_level: false,
|
top_level: false,
|
||||||
@ -127,7 +127,8 @@ where
|
|||||||
L: Load,
|
L: Load,
|
||||||
R: Resolve,
|
R: Resolve,
|
||||||
{
|
{
|
||||||
fn ctxt_for(&self, src: &JsWord) -> Option<SyntaxContext> {
|
/// Retursn (local, export)
|
||||||
|
fn ctxt_for(&self, src: &JsWord) -> Option<(SyntaxContext, SyntaxContext)> {
|
||||||
// Don't apply mark if it's a core module.
|
// Don't apply mark if it's a core module.
|
||||||
if self
|
if self
|
||||||
.bundler
|
.bundler
|
||||||
@ -139,10 +140,12 @@ where
|
|||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let path = self.bundler.resolve(self.path, src).ok()?;
|
let path = self.bundler.resolve(self.path, src).ok()?;
|
||||||
let (_, mark) = self.bundler.scope.module_id_gen.gen(&path);
|
let (_, local_mark, export_mark) = self.bundler.scope.module_id_gen.gen(&path);
|
||||||
let ctxt = SyntaxContext::empty();
|
|
||||||
|
|
||||||
Some(ctxt.apply_mark(mark))
|
Some((
|
||||||
|
SyntaxContext::empty().apply_mark(local_mark),
|
||||||
|
SyntaxContext::empty().apply_mark(export_mark),
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mark_as_wrapping_required(&self, src: &JsWord) {
|
fn mark_as_wrapping_required(&self, src: &JsWord) {
|
||||||
@ -161,7 +164,7 @@ where
|
|||||||
Ok(v) => v,
|
Ok(v) => v,
|
||||||
Err(_) => return,
|
Err(_) => return,
|
||||||
};
|
};
|
||||||
let (id, _) = self.bundler.scope.module_id_gen.gen(&path);
|
let (id, _, _) = self.bundler.scope.module_id_gen.gen(&path);
|
||||||
|
|
||||||
self.bundler.scope.mark_as_wrapping_required(id);
|
self.bundler.scope.mark_as_wrapping_required(id);
|
||||||
}
|
}
|
||||||
@ -185,22 +188,30 @@ where
|
|||||||
{
|
{
|
||||||
return import;
|
return import;
|
||||||
}
|
}
|
||||||
if let Some(ctxt) = self.ctxt_for(&import.src.value) {
|
if let Some((_, export_ctxt)) = self.ctxt_for(&import.src.value) {
|
||||||
import.span = import.span.with_ctxt(ctxt);
|
import.span = import.span.with_ctxt(export_ctxt);
|
||||||
|
|
||||||
for specifier in &mut import.specifiers {
|
for specifier in &mut import.specifiers {
|
||||||
match specifier {
|
match specifier {
|
||||||
ImportSpecifier::Named(n) => {
|
ImportSpecifier::Named(n) => {
|
||||||
self.imported_idents.insert(n.local.to_id(), ctxt);
|
self.imported_idents.insert(n.local.to_id(), export_ctxt);
|
||||||
n.local.span = n.local.span.with_ctxt(ctxt);
|
match &mut n.imported {
|
||||||
|
Some(imported) => {
|
||||||
|
imported.span.ctxt = export_ctxt;
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
let mut imported: Ident = n.local.clone();
|
||||||
|
imported.span.ctxt = export_ctxt;
|
||||||
|
n.imported = Some(imported);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ImportSpecifier::Default(n) => {
|
ImportSpecifier::Default(n) => {
|
||||||
self.imported_idents.insert(n.local.to_id(), ctxt);
|
self.imported_idents
|
||||||
n.local.span = n.local.span.with_ctxt(ctxt);
|
.insert(n.local.to_id(), n.local.span.ctxt);
|
||||||
}
|
}
|
||||||
ImportSpecifier::Namespace(n) => {
|
ImportSpecifier::Namespace(n) => {
|
||||||
self.imported_idents.insert(n.local.to_id(), ctxt);
|
self.imported_idents.insert(n.local.to_id(), export_ctxt);
|
||||||
n.local.span = n.local.span.with_ctxt(ctxt);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -329,41 +340,26 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn fold_export_named_specifier(&mut self, mut s: ExportNamedSpecifier) -> ExportNamedSpecifier {
|
fn fold_export_named_specifier(&mut self, mut s: ExportNamedSpecifier) -> ExportNamedSpecifier {
|
||||||
if let Some(&ctxt) = self.imported_idents.get(&s.orig.to_id()) {
|
match &s.exported {
|
||||||
s.orig.span = s.orig.span.with_ctxt(ctxt);
|
Some(exported) => {
|
||||||
|
debug_assert_eq!(
|
||||||
|
exported.span.ctxt, self.module_ctxt,
|
||||||
|
"Exported names should have same (local) context as top-level module items"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
let exported =
|
||||||
|
Ident::new(s.orig.sym.clone(), s.orig.span.with_ctxt(self.module_ctxt));
|
||||||
|
s.exported = Some(exported);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
s
|
s
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fold_prop(&mut self, mut prop: Prop) -> Prop {
|
|
||||||
prop = prop.fold_children_with(self);
|
|
||||||
|
|
||||||
match prop {
|
|
||||||
Prop::Shorthand(mut i) => {
|
|
||||||
if let Some(&ctxt) = self.imported_idents.get(&i.to_id()) {
|
|
||||||
let local = i.clone();
|
|
||||||
|
|
||||||
i.span = i.span.with_ctxt(ctxt);
|
|
||||||
|
|
||||||
return Prop::KeyValue(KeyValueProp {
|
|
||||||
key: PropName::Ident(local),
|
|
||||||
value: Box::new(Expr::Ident(i)),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
Prop::Shorthand(i)
|
|
||||||
}
|
|
||||||
_ => prop,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn fold_expr(&mut self, e: Expr) -> Expr {
|
fn fold_expr(&mut self, e: Expr) -> Expr {
|
||||||
match e {
|
match e {
|
||||||
Expr::Ident(mut i) if self.deglob_phase => {
|
Expr::Ident(i) if self.deglob_phase => {
|
||||||
if let Some(&ctxt) = self.imported_idents.get(&i.to_id()) {
|
|
||||||
i.span = i.span.with_ctxt(ctxt);
|
|
||||||
}
|
|
||||||
return Expr::Ident(i);
|
return Expr::Ident(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -408,9 +404,9 @@ where
|
|||||||
false
|
false
|
||||||
}) {
|
}) {
|
||||||
let mark = self.ctxt_for(&import.src.value);
|
let mark = self.ctxt_for(&import.src.value);
|
||||||
let ctxt = match mark {
|
let exported_ctxt = match mark {
|
||||||
None => return e.into(),
|
None => return e.into(),
|
||||||
Some(mark) => mark,
|
Some(ctxts) => ctxts.1,
|
||||||
};
|
};
|
||||||
if self.deglob_phase {
|
if self.deglob_phase {
|
||||||
if self.info.forced_ns.contains(&import.src.value) {
|
if self.info.forced_ns.contains(&import.src.value) {
|
||||||
@ -421,7 +417,7 @@ where
|
|||||||
let i = match &*e.prop {
|
let i = match &*e.prop {
|
||||||
Expr::Ident(i) => {
|
Expr::Ident(i) => {
|
||||||
let mut i = i.clone();
|
let mut i = i.clone();
|
||||||
i.span = i.span.with_ctxt(ctxt);
|
i.span = i.span.with_ctxt(exported_ctxt);
|
||||||
i
|
i
|
||||||
}
|
}
|
||||||
_ => unreachable!(
|
_ => unreachable!(
|
||||||
@ -438,7 +434,7 @@ where
|
|||||||
let i = match &*e.prop {
|
let i = match &*e.prop {
|
||||||
Expr::Ident(i) => {
|
Expr::Ident(i) => {
|
||||||
let mut i = i.clone();
|
let mut i = i.clone();
|
||||||
i.span = i.span.with_ctxt(ctxt);
|
i.span = i.span.with_ctxt(exported_ctxt);
|
||||||
i
|
i
|
||||||
}
|
}
|
||||||
_ => unreachable!(
|
_ => unreachable!(
|
||||||
@ -496,8 +492,8 @@ where
|
|||||||
{
|
{
|
||||||
match &mut **callee {
|
match &mut **callee {
|
||||||
Expr::Ident(i) => {
|
Expr::Ident(i) => {
|
||||||
if let Some(ctxt) = self.ctxt_for(&src.value) {
|
if let Some((_, export_ctxt)) = self.ctxt_for(&src.value) {
|
||||||
i.span = i.span.with_ctxt(ctxt);
|
i.span = i.span.with_ctxt(export_ctxt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
@ -586,8 +582,8 @@ where
|
|||||||
|
|
||||||
match &mut **callee {
|
match &mut **callee {
|
||||||
Expr::Ident(i) => {
|
Expr::Ident(i) => {
|
||||||
if let Some(mark) = self.ctxt_for(&src.value) {
|
if let Some((_, export_ctxt)) = self.ctxt_for(&src.value) {
|
||||||
i.span = i.span.with_ctxt(mark);
|
i.span = i.span.with_ctxt(export_ctxt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
|
@ -12,7 +12,7 @@ use is_macro::Is;
|
|||||||
#[cfg(feature = "rayon")]
|
#[cfg(feature = "rayon")]
|
||||||
use rayon::iter::ParallelIterator;
|
use rayon::iter::ParallelIterator;
|
||||||
use swc_atoms::js_word;
|
use swc_atoms::js_word;
|
||||||
use swc_common::{sync::Lrc, FileName, Mark, SourceFile, SyntaxContext, DUMMY_SP};
|
use swc_common::{sync::Lrc, FileName, SourceFile, SyntaxContext, DUMMY_SP};
|
||||||
use swc_ecma_ast::{
|
use swc_ecma_ast::{
|
||||||
CallExpr, Expr, ExprOrSuper, Ident, ImportDecl, ImportSpecifier, Invalid, MemberExpr, Module,
|
CallExpr, Expr, ExprOrSuper, Ident, ImportDecl, ImportSpecifier, Invalid, MemberExpr, Module,
|
||||||
ModuleDecl, Str,
|
ModuleDecl, Str,
|
||||||
@ -36,17 +36,19 @@ pub(super) struct TransformedModule {
|
|||||||
|
|
||||||
pub swc_helpers: Lrc<swc_ecma_transforms::helpers::Helpers>,
|
pub swc_helpers: Lrc<swc_ecma_transforms::helpers::Helpers>,
|
||||||
|
|
||||||
mark: Mark,
|
local_ctxt: SyntaxContext,
|
||||||
|
export_ctxt: SyntaxContext,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TransformedModule {
|
impl TransformedModule {
|
||||||
/// THe marker for the module's top-level identifiers.
|
/// [SyntaxContext] for exported items.
|
||||||
pub fn mark(&self) -> Mark {
|
pub fn export_ctxt(&self) -> SyntaxContext {
|
||||||
self.mark
|
self.export_ctxt
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ctxt(&self) -> SyntaxContext {
|
/// Top level contexts.
|
||||||
SyntaxContext::empty().apply_mark(self.mark)
|
pub fn local_ctxt(&self) -> SyntaxContext {
|
||||||
|
self.local_ctxt
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -78,7 +80,13 @@ where
|
|||||||
.context("failed to analyze module")?;
|
.context("failed to analyze module")?;
|
||||||
files.dedup_by_key(|v| v.1.clone());
|
files.dedup_by_key(|v| v.1.clone());
|
||||||
|
|
||||||
log::debug!("({}, {:?}) Storing module: {}", v.id, v.ctxt(), file_name);
|
log::debug!(
|
||||||
|
"({:?}, {:?}, {:?}) Storing module: {}",
|
||||||
|
v.id,
|
||||||
|
v.local_ctxt(),
|
||||||
|
v.export_ctxt(),
|
||||||
|
file_name
|
||||||
|
);
|
||||||
self.scope.store_module(v.clone());
|
self.scope.store_module(v.clone());
|
||||||
|
|
||||||
// Load dependencies and store them in the `Scope`
|
// Load dependencies and store them in the `Scope`
|
||||||
@ -101,7 +109,7 @@ where
|
|||||||
|
|
||||||
fn load(&self, file_name: &FileName) -> Result<(ModuleId, ModuleData), Error> {
|
fn load(&self, file_name: &FileName) -> Result<(ModuleId, ModuleData), Error> {
|
||||||
self.run(|| {
|
self.run(|| {
|
||||||
let (module_id, _) = self.scope.module_id_gen.gen(file_name);
|
let (module_id, _, _) = self.scope.module_id_gen.gen(file_name);
|
||||||
|
|
||||||
let data = self
|
let data = self
|
||||||
.loader
|
.loader
|
||||||
@ -120,9 +128,9 @@ where
|
|||||||
) -> Result<(TransformedModule, Vec<(Source, Lrc<FileName>)>), Error> {
|
) -> Result<(TransformedModule, Vec<(Source, Lrc<FileName>)>), Error> {
|
||||||
self.run(|| {
|
self.run(|| {
|
||||||
log::trace!("transform_module({})", data.fm.name);
|
log::trace!("transform_module({})", data.fm.name);
|
||||||
let (id, mark) = self.scope.module_id_gen.gen(file_name);
|
let (id, local_mark, export_mark) = self.scope.module_id_gen.gen(file_name);
|
||||||
|
|
||||||
let mut module = data.module.fold_with(&mut resolver_with_mark(mark));
|
let mut module = data.module.fold_with(&mut resolver_with_mark(local_mark));
|
||||||
|
|
||||||
// {
|
// {
|
||||||
// let code = self
|
// let code = self
|
||||||
@ -139,7 +147,7 @@ where
|
|||||||
// println!("Resolved:\n{}\n\n", code);
|
// println!("Resolved:\n{}\n\n", code);
|
||||||
// }
|
// }
|
||||||
|
|
||||||
let imports = self.extract_import_info(file_name, &mut module, mark);
|
let imports = self.extract_import_info(file_name, &mut module, local_mark);
|
||||||
|
|
||||||
// {
|
// {
|
||||||
// let code = self
|
// let code = self
|
||||||
@ -156,7 +164,11 @@ where
|
|||||||
// println!("After imports:\n{}\n", code,);
|
// println!("After imports:\n{}\n", code,);
|
||||||
// }
|
// }
|
||||||
|
|
||||||
let exports = self.extract_export_info(file_name, &mut module);
|
let exports = self.extract_export_info(
|
||||||
|
file_name,
|
||||||
|
&mut module,
|
||||||
|
SyntaxContext::empty().apply_mark(export_mark),
|
||||||
|
);
|
||||||
|
|
||||||
let is_es6 = if !self.config.require {
|
let is_es6 = if !self.config.require {
|
||||||
true
|
true
|
||||||
@ -188,8 +200,9 @@ where
|
|||||||
exports: Lrc::new(exports),
|
exports: Lrc::new(exports),
|
||||||
is_es6,
|
is_es6,
|
||||||
helpers: Default::default(),
|
helpers: Default::default(),
|
||||||
mark,
|
|
||||||
swc_helpers: Lrc::new(data.helpers),
|
swc_helpers: Lrc::new(data.helpers),
|
||||||
|
local_ctxt: SyntaxContext::empty().apply_mark(local_mark),
|
||||||
|
export_ctxt: SyntaxContext::empty().apply_mark(export_mark),
|
||||||
},
|
},
|
||||||
import_files,
|
import_files,
|
||||||
))
|
))
|
||||||
@ -216,8 +229,9 @@ where
|
|||||||
let info = match src {
|
let info = match src {
|
||||||
Some(src) => {
|
Some(src) => {
|
||||||
let name = self.resolve(base, &src.value)?;
|
let name = self.resolve(base, &src.value)?;
|
||||||
let (id, mark) = self.scope.module_id_gen.gen(&name);
|
let (id, local_mark, export_mark) =
|
||||||
Some((id, mark, name, src))
|
self.scope.module_id_gen.gen(&name);
|
||||||
|
Some((id, local_mark, export_mark, name, src))
|
||||||
}
|
}
|
||||||
None => None,
|
None => None,
|
||||||
};
|
};
|
||||||
@ -232,13 +246,14 @@ where
|
|||||||
|
|
||||||
match info {
|
match info {
|
||||||
None => exports.items.extend(specifiers),
|
None => exports.items.extend(specifiers),
|
||||||
Some((id, mark, name, src)) => {
|
Some((id, local_mark, export_mark, name, src)) => {
|
||||||
//
|
//
|
||||||
let src = Source {
|
let src = Source {
|
||||||
is_loaded_synchronously: true,
|
is_loaded_synchronously: true,
|
||||||
is_unconditional: false,
|
is_unconditional: false,
|
||||||
module_id: id,
|
module_id: id,
|
||||||
ctxt: SyntaxContext::empty().apply_mark(mark),
|
local_ctxt: SyntaxContext::empty().apply_mark(local_mark),
|
||||||
|
export_ctxt: SyntaxContext::empty().apply_mark(export_mark),
|
||||||
src,
|
src,
|
||||||
};
|
};
|
||||||
exports.reexports.push((src.clone(), specifiers));
|
exports.reexports.push((src.clone(), specifiers));
|
||||||
@ -290,22 +305,33 @@ where
|
|||||||
self.run(|| {
|
self.run(|| {
|
||||||
//
|
//
|
||||||
let file_name = self.resolve(base, &decl.src.value)?;
|
let file_name = self.resolve(base, &decl.src.value)?;
|
||||||
let (id, mark) = self.scope.module_id_gen.gen(&file_name);
|
let (id, local_mark, export_mark) =
|
||||||
|
self.scope.module_id_gen.gen(&file_name);
|
||||||
|
|
||||||
Ok((id, mark, file_name, decl, dynamic, unconditional))
|
Ok((
|
||||||
|
id,
|
||||||
|
local_mark,
|
||||||
|
export_mark,
|
||||||
|
file_name,
|
||||||
|
decl,
|
||||||
|
dynamic,
|
||||||
|
unconditional,
|
||||||
|
))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
for res in loaded {
|
for res in loaded {
|
||||||
// TODO: Report error and proceed instead of returning an error
|
// TODO: Report error and proceed instead of returning an error
|
||||||
let (id, mark, file_name, decl, is_dynamic, is_unconditional) = res?;
|
let (id, local_mark, export_mark, file_name, decl, is_dynamic, is_unconditional) =
|
||||||
|
res?;
|
||||||
|
|
||||||
let src = Source {
|
let src = Source {
|
||||||
is_loaded_synchronously: !is_dynamic,
|
is_loaded_synchronously: !is_dynamic,
|
||||||
is_unconditional,
|
is_unconditional,
|
||||||
module_id: id,
|
module_id: id,
|
||||||
ctxt: SyntaxContext::empty().apply_mark(mark),
|
local_ctxt: SyntaxContext::empty().apply_mark(local_mark),
|
||||||
|
export_ctxt: SyntaxContext::empty().apply_mark(export_mark),
|
||||||
src: decl.src,
|
src: decl.src,
|
||||||
};
|
};
|
||||||
files.push((src.clone(), file_name));
|
files.push((src.clone(), file_name));
|
||||||
@ -365,7 +391,8 @@ pub(super) struct Source {
|
|||||||
pub is_unconditional: bool,
|
pub is_unconditional: bool,
|
||||||
|
|
||||||
pub module_id: ModuleId,
|
pub module_id: ModuleId,
|
||||||
pub ctxt: SyntaxContext,
|
pub local_ctxt: SyntaxContext,
|
||||||
|
pub export_ctxt: SyntaxContext,
|
||||||
|
|
||||||
// Clone is relatively cheap, thanks to string_cache.
|
// Clone is relatively cheap, thanks to string_cache.
|
||||||
pub src: Str,
|
pub src: Str,
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use crate::{Bundler, Load, Resolve};
|
use crate::{Bundler, Load, Resolve};
|
||||||
use swc_ecma_ast::*;
|
use swc_ecma_ast::*;
|
||||||
use swc_ecma_transforms::optimization::simplify::{dce, inlining};
|
use swc_ecma_transforms::optimization::simplify::{const_propgation::constant_propagation, dce};
|
||||||
use swc_ecma_visit::FoldWith;
|
use swc_ecma_visit::FoldWith;
|
||||||
|
|
||||||
impl<L, R> Bundler<'_, L, R>
|
impl<L, R> Bundler<'_, L, R>
|
||||||
@ -15,7 +15,7 @@ where
|
|||||||
pub(super) fn optimize(&self, mut node: Module) -> Module {
|
pub(super) fn optimize(&self, mut node: Module) -> Module {
|
||||||
self.run(|| {
|
self.run(|| {
|
||||||
if !self.config.disable_inliner {
|
if !self.config.disable_inliner {
|
||||||
node = node.fold_with(&mut inlining::inlining(inlining::Config {}))
|
node = node.fold_with(&mut constant_propagation())
|
||||||
}
|
}
|
||||||
|
|
||||||
node = node.fold_with(&mut dce::dce(dce::Config {
|
node = node.fold_with(&mut dce::dce(dce::Config {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use super::load::TransformedModule;
|
use super::load::TransformedModule;
|
||||||
use crate::{
|
use crate::{
|
||||||
id::{ModuleId, ModuleIdGenerator},
|
id::{Id, ModuleId, ModuleIdGenerator},
|
||||||
util::CloneMap,
|
util::CloneMap,
|
||||||
};
|
};
|
||||||
use std::sync::atomic::{AtomicBool, Ordering};
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
@ -36,7 +36,7 @@ impl Scope {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_module_by_path(&self, file_name: &FileName) -> Option<TransformedModule> {
|
pub fn get_module_by_path(&self, file_name: &FileName) -> Option<TransformedModule> {
|
||||||
let (id, _) = self.module_id_gen.gen(file_name);
|
let (id, _, _) = self.module_id_gen.gen(file_name);
|
||||||
self.get_module(id)
|
self.get_module(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -62,4 +62,15 @@ impl Scope {
|
|||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns `Some(module_ident)` if the module should be wrapped
|
||||||
|
/// with a function.
|
||||||
|
pub fn wrapped_esm_id(&self, id: ModuleId) -> Option<Id> {
|
||||||
|
if !self.should_be_wrapped_with_a_fn(id) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let info = self.get_module(id)?;
|
||||||
|
|
||||||
|
Some(Id::new("mod".into(), info.export_ctxt()))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@ use std::{
|
|||||||
};
|
};
|
||||||
use swc_atoms::JsWord;
|
use swc_atoms::JsWord;
|
||||||
use swc_common::{sync::Lock, FileName, Mark, SyntaxContext, DUMMY_SP};
|
use swc_common::{sync::Lock, FileName, Mark, SyntaxContext, DUMMY_SP};
|
||||||
use swc_ecma_ast::Ident;
|
use swc_ecma_ast::{Expr, Ident};
|
||||||
use swc_ecma_utils::ident::IdentLike;
|
use swc_ecma_utils::ident::IdentLike;
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
@ -27,20 +27,23 @@ impl fmt::Debug for ModuleId {
|
|||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub(crate) struct ModuleIdGenerator {
|
pub(crate) struct ModuleIdGenerator {
|
||||||
v: AtomicU32,
|
v: AtomicU32,
|
||||||
cache: Lock<HashMap<FileName, (ModuleId, Mark)>>,
|
/// `(module_id, local_mark, export_mark)`
|
||||||
|
cache: Lock<HashMap<FileName, (ModuleId, Mark, Mark)>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ModuleIdGenerator {
|
impl ModuleIdGenerator {
|
||||||
pub fn gen(&self, file_name: &FileName) -> (ModuleId, Mark) {
|
pub fn gen(&self, file_name: &FileName) -> (ModuleId, Mark, Mark) {
|
||||||
let mut w = self.cache.lock();
|
let mut w = self.cache.lock();
|
||||||
if let Some(v) = w.get(file_name) {
|
if let Some(v) = w.get(file_name) {
|
||||||
return v.clone();
|
return v.clone();
|
||||||
}
|
}
|
||||||
|
|
||||||
let id = ModuleId(self.v.fetch_add(1, SeqCst));
|
let id = ModuleId(self.v.fetch_add(1, SeqCst));
|
||||||
let mark = Mark::fresh(Mark::root());
|
let local_mark = Mark::fresh(Mark::root());
|
||||||
w.insert(file_name.clone(), (id, mark));
|
let export_mark = Mark::fresh(Mark::root());
|
||||||
(id, mark)
|
let v = (id, local_mark, export_mark);
|
||||||
|
w.insert(file_name.clone(), v);
|
||||||
|
(id, local_mark, export_mark)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -62,12 +65,16 @@ impl Id {
|
|||||||
&self.0
|
&self.0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn ctxt(&self) -> SyntaxContext {
|
||||||
|
self.1
|
||||||
|
}
|
||||||
|
|
||||||
pub fn into_ident(self) -> Ident {
|
pub fn into_ident(self) -> Ident {
|
||||||
Ident::new(self.0, DUMMY_SP.with_ctxt(self.1))
|
Ident::new(self.0, DUMMY_SP.with_ctxt(self.1))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn replace_mark(mut self, mark: Mark) -> Self {
|
pub fn with_ctxt(mut self, ctxt: SyntaxContext) -> Self {
|
||||||
self.1 = SyntaxContext::empty().apply_mark(mark);
|
self.1 = ctxt;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -109,3 +116,17 @@ impl PartialEq<JsWord> for Id {
|
|||||||
self.0 == *other
|
self.0 == *other
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<Id> for Ident {
|
||||||
|
#[inline]
|
||||||
|
fn from(id: Id) -> Self {
|
||||||
|
id.into_ident()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Id> for Expr {
|
||||||
|
#[inline]
|
||||||
|
fn from(id: Id) -> Self {
|
||||||
|
Expr::Ident(id.into_ident())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,9 +1,56 @@
|
|||||||
use std::{hash::Hash, mem::replace};
|
use std::{clone::Clone, cmp::Eq, hash::Hash, mem::replace};
|
||||||
use swc_atoms::js_word;
|
use swc_atoms::js_word;
|
||||||
use swc_common::{Span, SyntaxContext, DUMMY_SP};
|
use swc_common::{Span, SyntaxContext, DUMMY_SP};
|
||||||
use swc_ecma_ast::*;
|
use swc_ecma_ast::*;
|
||||||
|
use swc_ecma_utils::ident::IdentLike;
|
||||||
use swc_ecma_visit::{noop_visit_mut_type, VisitMut};
|
use swc_ecma_visit::{noop_visit_mut_type, VisitMut};
|
||||||
|
|
||||||
|
pub(crate) trait VarDeclaratorExt: Into<VarDeclarator> {
|
||||||
|
fn into_module_item(self, _name: &'static str) -> ModuleItem {
|
||||||
|
ModuleItem::Stmt(Stmt::Decl(Decl::Var(VarDecl {
|
||||||
|
span: DUMMY_SP,
|
||||||
|
kind: VarDeclKind::Const,
|
||||||
|
declare: false,
|
||||||
|
decls: vec![
|
||||||
|
self.into(),
|
||||||
|
/* Ident::new(name.into(), DUMMY_SP)
|
||||||
|
* .assign_to(Ident::new("INJECTED_FROM".into(), DUMMY_SP)), */
|
||||||
|
],
|
||||||
|
})))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> VarDeclaratorExt for T where T: Into<VarDeclarator> {}
|
||||||
|
|
||||||
|
pub(crate) trait ExprExt: Into<Expr> {
|
||||||
|
#[track_caller]
|
||||||
|
fn assign_to<T>(self, lhs: T) -> VarDeclarator
|
||||||
|
where
|
||||||
|
T: IdentLike,
|
||||||
|
{
|
||||||
|
let init = self.into();
|
||||||
|
let lhs = lhs.into_id();
|
||||||
|
|
||||||
|
if cfg!(debug_assertions) {
|
||||||
|
match &init {
|
||||||
|
Expr::Ident(rhs) => {
|
||||||
|
debug_assert_ne!(lhs, rhs.to_id());
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
VarDeclarator {
|
||||||
|
span: DUMMY_SP,
|
||||||
|
name: Pat::Ident(Ident::new(lhs.0, DUMMY_SP.with_ctxt(lhs.1))),
|
||||||
|
init: Some(Box::new(init)),
|
||||||
|
definite: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> ExprExt for T where T: Into<Expr> {}
|
||||||
|
|
||||||
/// Helper for migration from [Fold] to [VisitMut]
|
/// Helper for migration from [Fold] to [VisitMut]
|
||||||
pub(crate) trait MapWithMut: Sized {
|
pub(crate) trait MapWithMut: Sized {
|
||||||
fn dummy() -> Self;
|
fn dummy() -> Self;
|
||||||
@ -98,6 +145,54 @@ impl MapWithMut for PatOrExpr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl MapWithMut for ClassExpr {
|
||||||
|
fn dummy() -> Self {
|
||||||
|
ClassExpr {
|
||||||
|
ident: None,
|
||||||
|
class: MapWithMut::dummy(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MapWithMut for FnExpr {
|
||||||
|
fn dummy() -> Self {
|
||||||
|
FnExpr {
|
||||||
|
ident: None,
|
||||||
|
function: MapWithMut::dummy(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MapWithMut for Class {
|
||||||
|
fn dummy() -> Self {
|
||||||
|
Class {
|
||||||
|
span: Default::default(),
|
||||||
|
decorators: Default::default(),
|
||||||
|
body: Default::default(),
|
||||||
|
super_class: Default::default(),
|
||||||
|
is_abstract: Default::default(),
|
||||||
|
type_params: Default::default(),
|
||||||
|
super_type_params: Default::default(),
|
||||||
|
implements: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MapWithMut for Function {
|
||||||
|
fn dummy() -> Self {
|
||||||
|
Function {
|
||||||
|
params: Default::default(),
|
||||||
|
decorators: Default::default(),
|
||||||
|
span: Default::default(),
|
||||||
|
body: Default::default(),
|
||||||
|
is_generator: Default::default(),
|
||||||
|
is_async: Default::default(),
|
||||||
|
type_params: Default::default(),
|
||||||
|
return_type: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(crate) struct CHashSet<V>
|
pub(crate) struct CHashSet<V>
|
||||||
where
|
where
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
use anyhow::{Context, Error};
|
use anyhow::{Context, Error};
|
||||||
use sha1::{Digest, Sha1};
|
use sha1::{Digest, Sha1};
|
||||||
use std::{
|
use std::{
|
||||||
collections::HashMap,
|
collections::{HashMap, HashSet},
|
||||||
env,
|
env,
|
||||||
fs::{create_dir_all, read_to_string, write},
|
fs::{create_dir_all, read_to_string, write},
|
||||||
path::PathBuf,
|
path::PathBuf,
|
||||||
@ -13,81 +13,379 @@ use std::{
|
|||||||
};
|
};
|
||||||
use swc_atoms::js_word;
|
use swc_atoms::js_word;
|
||||||
use swc_bundler::{Bundler, Load, ModuleData, ModuleRecord, Resolve};
|
use swc_bundler::{Bundler, Load, ModuleData, ModuleRecord, Resolve};
|
||||||
use swc_common::{sync::Lrc, FileName, SourceMap, Span, GLOBALS};
|
use swc_common::{comments::SingleThreadedComments, sync::Lrc, FileName, SourceMap, Span, GLOBALS};
|
||||||
use swc_ecma_ast::{
|
use swc_ecma_ast::*;
|
||||||
Bool, Expr, ExprOrSuper, Ident, KeyValueProp, Lit, MemberExpr, MetaPropExpr, PropName, Str,
|
|
||||||
};
|
|
||||||
use swc_ecma_codegen::{text_writer::JsWriter, Emitter};
|
use swc_ecma_codegen::{text_writer::JsWriter, Emitter};
|
||||||
use swc_ecma_parser::{lexer::Lexer, JscTarget, Parser, StringInput, Syntax, TsConfig};
|
use swc_ecma_parser::{lexer::Lexer, JscTarget, Parser, StringInput, Syntax, TsConfig};
|
||||||
use swc_ecma_transforms::{proposals::decorators, typescript::strip};
|
use swc_ecma_transforms::{proposals::decorators, react, typescript::strip};
|
||||||
use swc_ecma_visit::FoldWith;
|
use swc_ecma_utils::{find_ids, Id};
|
||||||
|
use swc_ecma_visit::{FoldWith, Node, Visit, VisitWith};
|
||||||
|
use testing::assert_eq;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn oak_6_3_1_application() {
|
fn oak_6_3_1_application() {
|
||||||
run("https://deno.land/x/oak@v6.3.1/application.ts", None);
|
run(
|
||||||
|
"https://deno.land/x/oak@v6.3.1/application.ts",
|
||||||
|
&[
|
||||||
|
"ApplicationErrorEvent",
|
||||||
|
"ApplicationListenEvent",
|
||||||
|
"Application",
|
||||||
|
],
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn oak_6_3_1_mod() {
|
fn oak_6_3_1_mod() {
|
||||||
run("https://deno.land/x/oak@v6.3.1/mod.ts", None);
|
run(
|
||||||
|
"https://deno.land/x/oak@v6.3.1/mod.ts",
|
||||||
|
&[
|
||||||
|
"Application",
|
||||||
|
"Context",
|
||||||
|
"helpers",
|
||||||
|
"Cookies",
|
||||||
|
"HttpError",
|
||||||
|
"httpErrors",
|
||||||
|
"isHttpError",
|
||||||
|
"composeMiddleware",
|
||||||
|
"FormDataReader",
|
||||||
|
"Request",
|
||||||
|
"REDIRECT_BACK",
|
||||||
|
"Response",
|
||||||
|
"Router",
|
||||||
|
"send",
|
||||||
|
"ServerSentEvent",
|
||||||
|
"ServerSentEventTarget",
|
||||||
|
"isErrorStatus",
|
||||||
|
"isRedirectStatus",
|
||||||
|
"Status",
|
||||||
|
"STATUS_TEXT",
|
||||||
|
],
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn std_0_74_9_http_server() {
|
fn std_0_74_0_http_server() {
|
||||||
run("https://deno.land/std@0.74.0/http/server.ts", None);
|
run(
|
||||||
|
"https://deno.land/std@0.74.0/http/server.ts",
|
||||||
|
&[
|
||||||
|
"ServerRequest",
|
||||||
|
"Server",
|
||||||
|
"_parseAddrFromStr",
|
||||||
|
"serve",
|
||||||
|
"listenAndServe",
|
||||||
|
"serveTLS",
|
||||||
|
"listenAndServeTLS",
|
||||||
|
],
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[ignore = "Does not finish by default"]
|
#[ignore = "Does not finish by default"]
|
||||||
fn oak_6_3_1_example_server() {
|
fn oak_6_3_1_example_server() {
|
||||||
run("https://deno.land/x/oak@v6.3.1/examples/server.ts", None);
|
run("https://deno.land/x/oak@v6.3.1/examples/server.ts", &[]);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[ignore = "Does not finish by default"]
|
#[ignore = "Does not finish by default"]
|
||||||
fn oak_6_3_1_example_sse_server() {
|
fn oak_6_3_1_example_sse_server() {
|
||||||
run("https://deno.land/x/oak@v6.3.1/examples/sseServer.ts", None);
|
run("https://deno.land/x/oak@v6.3.1/examples/sseServer.ts", &[]);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn std_0_75_0_http_server() {
|
fn deno_8188_full() {
|
||||||
run("https://deno.land/std@0.75.0/http/server.ts", None);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn deno_8188() {
|
|
||||||
run(
|
run(
|
||||||
"https://raw.githubusercontent.com/nats-io/nats.ws/master/src/mod.ts",
|
"https://raw.githubusercontent.com/nats-io/nats.ws/master/src/mod.ts",
|
||||||
None,
|
&[
|
||||||
|
"connect",
|
||||||
|
"NatsConnectionImpl",
|
||||||
|
"Nuid",
|
||||||
|
"nuid",
|
||||||
|
"ErrorCode",
|
||||||
|
"NatsError",
|
||||||
|
"DebugEvents",
|
||||||
|
"Empty",
|
||||||
|
"Events",
|
||||||
|
"MsgImpl",
|
||||||
|
"SubscriptionImpl",
|
||||||
|
"Subscriptions",
|
||||||
|
"setTransportFactory",
|
||||||
|
"setUrlParseFn",
|
||||||
|
"Connect",
|
||||||
|
"createInbox",
|
||||||
|
"INFO",
|
||||||
|
"ProtocolHandler",
|
||||||
|
"deferred",
|
||||||
|
"delay",
|
||||||
|
"extractProtocolMessage",
|
||||||
|
"render",
|
||||||
|
"timeout",
|
||||||
|
"headers",
|
||||||
|
"MsgHdrsImpl",
|
||||||
|
"Heartbeat",
|
||||||
|
"MuxSubscription",
|
||||||
|
"DataBuffer",
|
||||||
|
"checkOptions",
|
||||||
|
"Request",
|
||||||
|
"credsAuthenticator",
|
||||||
|
"jwtAuthenticator",
|
||||||
|
"nkeyAuthenticator",
|
||||||
|
"JSONCodec",
|
||||||
|
"StringCodec",
|
||||||
|
"QueuedIterator",
|
||||||
|
"Kind",
|
||||||
|
"Parser",
|
||||||
|
"State",
|
||||||
|
"DenoBuffer",
|
||||||
|
"MAX_SIZE",
|
||||||
|
"readAll",
|
||||||
|
"writeAll",
|
||||||
|
"Bench",
|
||||||
|
"Metric",
|
||||||
|
"TD",
|
||||||
|
"TE",
|
||||||
|
"isIP",
|
||||||
|
"parseIP",
|
||||||
|
"nkeys",
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn deno_8188_01() {
|
||||||
|
run(
|
||||||
|
"https://raw.githubusercontent.com/nats-io/nats.deno/v1.0.0-12/nats-base-client/nkeys.ts",
|
||||||
|
&["nkeys"],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn deno_8188_02() {
|
||||||
|
run(
|
||||||
|
"https://raw.githubusercontent.com/nats-io/nkeys.js/v1.0.0-7/modules/esm/mod.ts",
|
||||||
|
&[
|
||||||
|
"NKeysError",
|
||||||
|
"NKeysErrorCode",
|
||||||
|
"createAccount",
|
||||||
|
"createOperator",
|
||||||
|
"createPair",
|
||||||
|
"createUser",
|
||||||
|
"decode",
|
||||||
|
"encode",
|
||||||
|
"fromPublic",
|
||||||
|
"fromSeed",
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn deno_8188_03() {
|
||||||
|
run(
|
||||||
|
"https://raw.githubusercontent.com/nats-io/nkeys.js/v1.0.0-7/modules/esm/deps.ts",
|
||||||
|
&["denoHelper"],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn deno_8189() {
|
fn deno_8189() {
|
||||||
run("https://deno.land/x/lz4/mod.ts", None);
|
run(
|
||||||
|
"https://deno.land/x/lz4/mod.ts",
|
||||||
|
&["compress", "decompress"],
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn deno_8211() {
|
fn deno_8211() {
|
||||||
run("https://unpkg.com/luxon@1.25.0/src/luxon.js", None);
|
run(
|
||||||
|
"https://unpkg.com/luxon@1.25.0/src/luxon.js",
|
||||||
|
&[
|
||||||
|
"DateTime",
|
||||||
|
"Duration",
|
||||||
|
"Interval",
|
||||||
|
"Info",
|
||||||
|
"Zone",
|
||||||
|
"FixedOffsetZone",
|
||||||
|
"IANAZone",
|
||||||
|
"InvalidZone",
|
||||||
|
"LocalZone",
|
||||||
|
"Settings",
|
||||||
|
],
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn deno_8246() {
|
fn deno_8246() {
|
||||||
run("https://raw.githubusercontent.com/nats-io/nats.deno/v1.0.0-11/nats-base-client/internal_mod.ts",None);
|
run("https://raw.githubusercontent.com/nats-io/nats.deno/v1.0.0-11/nats-base-client/internal_mod.ts",&[
|
||||||
|
"NatsConnectionImpl",
|
||||||
|
"Nuid",
|
||||||
|
"nuid",
|
||||||
|
"ErrorCode",
|
||||||
|
"NatsError",
|
||||||
|
"DebugEvents",
|
||||||
|
"Empty",
|
||||||
|
"Events",
|
||||||
|
"MsgImpl",
|
||||||
|
"SubscriptionImpl",
|
||||||
|
"Subscriptions",
|
||||||
|
"setTransportFactory",
|
||||||
|
"setUrlParseFn",
|
||||||
|
"Connect",
|
||||||
|
"createInbox",
|
||||||
|
"INFO",
|
||||||
|
"ProtocolHandler",
|
||||||
|
"deferred",
|
||||||
|
"delay",
|
||||||
|
"extractProtocolMessage",
|
||||||
|
"render",
|
||||||
|
"timeout",
|
||||||
|
"headers",
|
||||||
|
"MsgHdrsImpl",
|
||||||
|
"Heartbeat",
|
||||||
|
"MuxSubscription",
|
||||||
|
"DataBuffer",
|
||||||
|
"checkOptions",
|
||||||
|
"Request",
|
||||||
|
"credsAuthenticator",
|
||||||
|
"jwtAuthenticator",
|
||||||
|
"nkeyAuthenticator",
|
||||||
|
"JSONCodec",
|
||||||
|
"StringCodec",
|
||||||
|
"QueuedIterator",
|
||||||
|
"Kind",
|
||||||
|
"Parser",
|
||||||
|
"State",
|
||||||
|
"DenoBuffer",
|
||||||
|
"MAX_SIZE",
|
||||||
|
"readAll",
|
||||||
|
"writeAll",
|
||||||
|
"Bench",
|
||||||
|
"Metric",
|
||||||
|
"TD",
|
||||||
|
"TE",
|
||||||
|
"isIP",
|
||||||
|
"parseIP",
|
||||||
|
"nkeys",
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run(url: &str, expeceted_bytes: Option<usize>) {
|
#[test]
|
||||||
|
#[ignore = "document is not defined when I use deno run"]
|
||||||
|
fn deno_6802() {
|
||||||
|
run("tests/deno/issue-6802/input.tsx", &[]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn deno_8314_1() {
|
||||||
|
run("tests/deno/issue-8314/input.ts", &[]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn deno_8314_2() {
|
||||||
|
run("https://dev.jspm.io/ngraph.graph", &["default"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn deno_8302() {
|
||||||
|
run("tests/deno/issue-8302/input.ts", &["DB", "Empty", "Status"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn deno_8399_1() {
|
||||||
|
run("tests/deno/issue-8399-1/input.ts", &[]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn deno_8399_2() {
|
||||||
|
run("tests/deno/issue-8399-2/input.ts", &[]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn merging_order_01() {
|
||||||
|
run(
|
||||||
|
"https://deno.land/x/oak@v6.3.1/multipart.ts",
|
||||||
|
&["FormDataReader"],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn reexport_01() {
|
||||||
|
run(
|
||||||
|
"https://raw.githubusercontent.com/aricart/tweetnacl-deno/import-type-fixes/src/nacl.ts",
|
||||||
|
&[
|
||||||
|
"AuthLength",
|
||||||
|
"ByteArray",
|
||||||
|
"HalfArray",
|
||||||
|
"HashLength",
|
||||||
|
"IntArray",
|
||||||
|
"NumArray",
|
||||||
|
"SealedBoxLength",
|
||||||
|
"SignLength",
|
||||||
|
"WordArray",
|
||||||
|
"_hash",
|
||||||
|
"_verify_16",
|
||||||
|
"_verify_32",
|
||||||
|
"auth",
|
||||||
|
"auth_full",
|
||||||
|
"blake2b",
|
||||||
|
"blake2b_final",
|
||||||
|
"blake2b_init",
|
||||||
|
"blake2b_update",
|
||||||
|
"blake2s",
|
||||||
|
"blake2s_final",
|
||||||
|
"blake2s_init",
|
||||||
|
"blake2s_update",
|
||||||
|
"decodeBase64",
|
||||||
|
"decodeHex",
|
||||||
|
"decodeUTF8",
|
||||||
|
"encodeBase64",
|
||||||
|
"encodeHex",
|
||||||
|
"encodeUTF8",
|
||||||
|
"hash",
|
||||||
|
"randomBytes",
|
||||||
|
"scalarbase",
|
||||||
|
"scalarmult",
|
||||||
|
"sealedbox",
|
||||||
|
"sealedbox_open",
|
||||||
|
"sign",
|
||||||
|
"sign_detached",
|
||||||
|
"sign_detached_verify",
|
||||||
|
"sign_keyPair",
|
||||||
|
"sign_keyPair_fromSecretKey",
|
||||||
|
"sign_keyPair_fromSeed",
|
||||||
|
"sign_open",
|
||||||
|
"validateBase64",
|
||||||
|
"validateHex",
|
||||||
|
"verify",
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(url: &str, exports: &[&str]) {
|
||||||
let dir = tempfile::tempdir().expect("failed to crate temp file");
|
let dir = tempfile::tempdir().expect("failed to crate temp file");
|
||||||
let path = dir.path().join("main.js");
|
let path = dir.path().join("main.js");
|
||||||
println!("{}", path.display());
|
println!("{}", path.display());
|
||||||
|
|
||||||
let src = bundle(url);
|
let src = bundle(url);
|
||||||
write(&path, &src).unwrap();
|
write(&path, &src).unwrap();
|
||||||
if let Some(expected) = expeceted_bytes {
|
|
||||||
assert_eq!(src.len(), expected);
|
::testing::run_test2(false, |cm, _| {
|
||||||
}
|
let fm = cm.load_file(&path).unwrap();
|
||||||
|
let loader = Loader { cm: cm.clone() };
|
||||||
|
let module = loader.load(&fm.name).unwrap().module;
|
||||||
|
|
||||||
|
let mut actual_exports = collect_exports(&module).into_iter().collect::<Vec<_>>();
|
||||||
|
actual_exports.sort();
|
||||||
|
let mut expected_exports = exports
|
||||||
|
.into_iter()
|
||||||
|
.map(|s| s.to_string())
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
expected_exports.sort();
|
||||||
|
|
||||||
|
assert_eq!(expected_exports, actual_exports);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
if env::var("CI").is_ok() {
|
if env::var("CI").is_ok() {
|
||||||
return;
|
return;
|
||||||
@ -125,7 +423,14 @@ fn bundle(url: &str) -> String {
|
|||||||
Box::new(Hook),
|
Box::new(Hook),
|
||||||
);
|
);
|
||||||
let mut entries = HashMap::new();
|
let mut entries = HashMap::new();
|
||||||
entries.insert("main".to_string(), FileName::Custom(url.to_string()));
|
entries.insert(
|
||||||
|
"main".to_string(),
|
||||||
|
if url.starts_with("http") {
|
||||||
|
FileName::Custom(url.to_string())
|
||||||
|
} else {
|
||||||
|
FileName::Real(url.to_string().into())
|
||||||
|
},
|
||||||
|
);
|
||||||
let output = bundler.bundle(entries).unwrap();
|
let output = bundler.bundle(entries).unwrap();
|
||||||
let module = output.into_iter().next().unwrap().module;
|
let module = output.into_iter().next().unwrap().module;
|
||||||
|
|
||||||
@ -192,21 +497,30 @@ impl Load for Loader {
|
|||||||
fn load(&self, file: &FileName) -> Result<ModuleData, Error> {
|
fn load(&self, file: &FileName) -> Result<ModuleData, Error> {
|
||||||
eprintln!("{}", file);
|
eprintln!("{}", file);
|
||||||
|
|
||||||
let url = match file {
|
let tsx;
|
||||||
FileName::Custom(v) => v,
|
|
||||||
_ => unreachable!("this test only uses url"),
|
let fm = match file {
|
||||||
};
|
FileName::Real(path) => {
|
||||||
|
tsx = path.to_string_lossy().ends_with(".tsx");
|
||||||
|
self.cm.load_file(path)?
|
||||||
|
}
|
||||||
|
FileName::Custom(url) => {
|
||||||
|
tsx = url.ends_with(".tsx");
|
||||||
|
|
||||||
let url = Url::parse(&url).context("failed to parse url")?;
|
let url = Url::parse(&url).context("failed to parse url")?;
|
||||||
|
|
||||||
let src = load_url(url.clone())?;
|
let src = load_url(url.clone())?;
|
||||||
let fm = self
|
|
||||||
.cm
|
self.cm
|
||||||
.new_source_file(FileName::Custom(url.to_string()), src.to_string());
|
.new_source_file(FileName::Custom(url.to_string()), src.to_string())
|
||||||
|
}
|
||||||
|
_ => unreachable!("this test only uses url"),
|
||||||
|
};
|
||||||
|
|
||||||
let lexer = Lexer::new(
|
let lexer = Lexer::new(
|
||||||
Syntax::Typescript(TsConfig {
|
Syntax::Typescript(TsConfig {
|
||||||
decorators: true,
|
decorators: true,
|
||||||
|
tsx,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}),
|
}),
|
||||||
JscTarget::Es2020,
|
JscTarget::Es2020,
|
||||||
@ -215,12 +529,18 @@ impl Load for Loader {
|
|||||||
);
|
);
|
||||||
|
|
||||||
let mut parser = Parser::new_from(lexer);
|
let mut parser = Parser::new_from(lexer);
|
||||||
let module = parser.parse_typescript_module().unwrap();
|
let module = parser.parse_module().unwrap();
|
||||||
let module = module.fold_with(&mut decorators::decorators(decorators::Config {
|
let module = module
|
||||||
|
.fold_with(&mut decorators::decorators(decorators::Config {
|
||||||
legacy: true,
|
legacy: true,
|
||||||
emit_metadata: false,
|
emit_metadata: false,
|
||||||
}));
|
}))
|
||||||
let module = module.fold_with(&mut strip());
|
.fold_with(&mut react::react::<SingleThreadedComments>(
|
||||||
|
self.cm.clone(),
|
||||||
|
None,
|
||||||
|
Default::default(),
|
||||||
|
))
|
||||||
|
.fold_with(&mut strip());
|
||||||
|
|
||||||
Ok(ModuleData {
|
Ok(ModuleData {
|
||||||
fm,
|
fm,
|
||||||
@ -235,6 +555,11 @@ struct Resolver;
|
|||||||
|
|
||||||
impl Resolve for Resolver {
|
impl Resolve for Resolver {
|
||||||
fn resolve(&self, base: &FileName, module_specifier: &str) -> Result<FileName, Error> {
|
fn resolve(&self, base: &FileName, module_specifier: &str) -> Result<FileName, Error> {
|
||||||
|
match Url::parse(module_specifier) {
|
||||||
|
Ok(v) => return Ok(FileName::Custom(v.to_string())),
|
||||||
|
Err(_) => {}
|
||||||
|
}
|
||||||
|
|
||||||
let base_url = match base {
|
let base_url = match base {
|
||||||
FileName::Custom(v) => v,
|
FileName::Custom(v) => v,
|
||||||
_ => unreachable!("this test only uses url"),
|
_ => unreachable!("this test only uses url"),
|
||||||
@ -287,3 +612,61 @@ impl swc_bundler::Hook for Hook {
|
|||||||
])
|
])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn collect_exports(module: &Module) -> HashSet<String> {
|
||||||
|
let mut v = ExportCollector::default();
|
||||||
|
module.visit_with(module, &mut v);
|
||||||
|
|
||||||
|
v.exports
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
struct ExportCollector {
|
||||||
|
exports: HashSet<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Visit for ExportCollector {
|
||||||
|
fn visit_export_specifier(&mut self, s: &ExportSpecifier, _: &dyn Node) {
|
||||||
|
match s {
|
||||||
|
ExportSpecifier::Namespace(ns) => {
|
||||||
|
self.exports.insert(ns.name.sym.to_string());
|
||||||
|
}
|
||||||
|
ExportSpecifier::Default(_) => {
|
||||||
|
self.exports.insert("default".into());
|
||||||
|
}
|
||||||
|
ExportSpecifier::Named(named) => {
|
||||||
|
self.exports.insert(
|
||||||
|
named
|
||||||
|
.exported
|
||||||
|
.as_ref()
|
||||||
|
.unwrap_or(&named.orig)
|
||||||
|
.sym
|
||||||
|
.to_string(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_export_default_decl(&mut self, _: &ExportDefaultDecl, _: &dyn Node) {
|
||||||
|
self.exports.insert("default".into());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_export_default_expr(&mut self, _: &ExportDefaultExpr, _: &dyn Node) {
|
||||||
|
self.exports.insert("default".into());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_export_decl(&mut self, export: &ExportDecl, _: &dyn Node) {
|
||||||
|
match &export.decl {
|
||||||
|
swc_ecma_ast::Decl::Class(ClassDecl { ident, .. })
|
||||||
|
| swc_ecma_ast::Decl::Fn(FnDecl { ident, .. }) => {
|
||||||
|
self.exports.insert(ident.sym.to_string());
|
||||||
|
}
|
||||||
|
swc_ecma_ast::Decl::Var(var) => {
|
||||||
|
let ids: Vec<Id> = find_ids(var);
|
||||||
|
self.exports
|
||||||
|
.extend(ids.into_iter().map(|v| v.0.to_string()));
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
4
bundler/tests/deno/issue-6802/input.tsx
Normal file
4
bundler/tests/deno/issue-6802/input.tsx
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
import React from "https://cdn.pika.dev/@pika/react@^16.13.1";
|
||||||
|
import ReactDOM from "https://cdn.pika.dev/@pika/react-dom@^16.13.1";
|
||||||
|
|
||||||
|
ReactDOM.render(<div>Hi, React!</div>, document.getElementById("root"));
|
1
bundler/tests/deno/issue-8302/input.ts
Normal file
1
bundler/tests/deno/issue-8302/input.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export { DB, Empty, Status } from 'https://deno.land/x/sqlite@v2.3.1/mod.ts';
|
3
bundler/tests/deno/issue-8314/input.ts
Normal file
3
bundler/tests/deno/issue-8314/input.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import createGraph from "https://dev.jspm.io/ngraph.graph";
|
||||||
|
|
||||||
|
console.log(createGraph);
|
2
bundler/tests/deno/issue-8399-1/input.ts
Normal file
2
bundler/tests/deno/issue-8399-1/input.ts
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
import * as yaml from "https://deno.land/std@0.78.0/encoding/yaml.ts"
|
||||||
|
console.log(yaml && 1)
|
2
bundler/tests/deno/issue-8399-2/input.ts
Normal file
2
bundler/tests/deno/issue-8399-2/input.ts
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
import { stringify } from "https://deno.land/std@0.78.0/encoding/yaml.ts";
|
||||||
|
console.log(stringify({ a: 1 }));
|
@ -0,0 +1,4 @@
|
|||||||
|
import { b } from './b';
|
||||||
|
|
||||||
|
export const a = 'a';
|
||||||
|
export { b }
|
@ -0,0 +1,4 @@
|
|||||||
|
import { c } from './c';
|
||||||
|
|
||||||
|
export const b = 'b';
|
||||||
|
export { c }
|
@ -0,0 +1,4 @@
|
|||||||
|
import { a } from './a';
|
||||||
|
|
||||||
|
export const c = 'c';
|
||||||
|
export { a }
|
@ -0,0 +1,2 @@
|
|||||||
|
export { a } from './a';
|
||||||
|
export { c } from './c';
|
@ -0,0 +1,6 @@
|
|||||||
|
const a2 = 'a';
|
||||||
|
const a1 = a2;
|
||||||
|
const c2 = 'c';
|
||||||
|
const c1 = c2;
|
||||||
|
export { a1 as a };
|
||||||
|
export { c1 as c };
|
@ -0,0 +1,4 @@
|
|||||||
|
import { b } from './b';
|
||||||
|
|
||||||
|
export const a = 'a';
|
||||||
|
export { b }
|
@ -0,0 +1,4 @@
|
|||||||
|
import { c } from './c';
|
||||||
|
|
||||||
|
export const b = 'b';
|
||||||
|
export { c }
|
@ -0,0 +1,4 @@
|
|||||||
|
import { a } from './a';
|
||||||
|
|
||||||
|
export const c = 'c';
|
||||||
|
export { a }
|
@ -0,0 +1,4 @@
|
|||||||
|
import { a } from './a';
|
||||||
|
import { c } from './c';
|
||||||
|
|
||||||
|
console.log(a, c)
|
@ -0,0 +1,7 @@
|
|||||||
|
const a = 'a';
|
||||||
|
const a1 = a;
|
||||||
|
const c = 'c';
|
||||||
|
const c1 = c;
|
||||||
|
const a2 = a1;
|
||||||
|
const c2 = c1;
|
||||||
|
console.log(a2, c2);
|
5
bundler/tests/fixture/circular-imports/simple/input/a.js
Normal file
5
bundler/tests/fixture/circular-imports/simple/input/a.js
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import { b } from './b';
|
||||||
|
|
||||||
|
export { b };
|
||||||
|
|
||||||
|
export const a = 1;
|
5
bundler/tests/fixture/circular-imports/simple/input/b.js
Normal file
5
bundler/tests/fixture/circular-imports/simple/input/b.js
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import { a } from './a';
|
||||||
|
|
||||||
|
export { a };
|
||||||
|
|
||||||
|
export const b = 2;
|
@ -0,0 +1,4 @@
|
|||||||
|
import { a } from './a';
|
||||||
|
import { b } from './b';
|
||||||
|
|
||||||
|
console.log(a, b);
|
@ -0,0 +1,7 @@
|
|||||||
|
const a = 1;
|
||||||
|
const a1 = a;
|
||||||
|
const b = 2;
|
||||||
|
const b1 = b;
|
||||||
|
const a2 = a1;
|
||||||
|
const b2 = b1;
|
||||||
|
console.log(a2, b2);
|
@ -1,8 +1,10 @@
|
|||||||
const foo = function() {
|
const mod = function() {
|
||||||
class A {
|
class A {
|
||||||
}
|
}
|
||||||
|
const A1 = A;
|
||||||
return {
|
return {
|
||||||
A
|
A
|
||||||
};
|
};
|
||||||
}();
|
}();
|
||||||
|
const foo = mod;
|
||||||
export { foo };
|
export { foo };
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
const foo = function() {
|
const mod = function() {
|
||||||
async function foo1() {
|
async function foo() {
|
||||||
}
|
}
|
||||||
|
const foo1 = foo;
|
||||||
return {
|
return {
|
||||||
foo: foo1
|
foo
|
||||||
};
|
};
|
||||||
}();
|
}();
|
||||||
|
const foo = mod;
|
||||||
export { foo };
|
export { foo };
|
||||||
|
@ -1,13 +1,17 @@
|
|||||||
const foo = function() {
|
const mod = function() {
|
||||||
const [a, b, c] = [
|
const [a, b, c] = [
|
||||||
1,
|
1,
|
||||||
2,
|
2,
|
||||||
3
|
3
|
||||||
];
|
];
|
||||||
|
const a1 = a;
|
||||||
|
const b1 = b;
|
||||||
|
const c1 = c;
|
||||||
return {
|
return {
|
||||||
a,
|
a,
|
||||||
b,
|
b,
|
||||||
c
|
c
|
||||||
};
|
};
|
||||||
}();
|
}();
|
||||||
|
const foo = mod;
|
||||||
export { foo };
|
export { foo };
|
||||||
|
@ -1,12 +1,14 @@
|
|||||||
const foo = {
|
const __default = {
|
||||||
"a": "a",
|
"a": "a",
|
||||||
"b": "b",
|
"b": "b",
|
||||||
"c": "c"
|
"c": "c"
|
||||||
};
|
};
|
||||||
const foo1 = {
|
const foo = __default;
|
||||||
|
const __default1 = {
|
||||||
"a": "a1",
|
"a": "a1",
|
||||||
"b": "b1",
|
"b": "b1",
|
||||||
"c": "c1"
|
"c": "c1"
|
||||||
};
|
};
|
||||||
|
const foo1 = __default1;
|
||||||
console.log(foo);
|
console.log(foo);
|
||||||
console.log(foo1);
|
console.log(foo1);
|
||||||
|
1
bundler/tests/fixture/deno-8188-1/input/a.ts
Normal file
1
bundler/tests/fixture/deno-8188-1/input/a.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export const a = 1;
|
2
bundler/tests/fixture/deno-8188-1/input/b.ts
Normal file
2
bundler/tests/fixture/deno-8188-1/input/b.ts
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
import { a } from './a';
|
||||||
|
export const b = a + 1;
|
2
bundler/tests/fixture/deno-8188-1/input/c.ts
Normal file
2
bundler/tests/fixture/deno-8188-1/input/c.ts
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
import { a } from './a';
|
||||||
|
export const c = a + 2;
|
2
bundler/tests/fixture/deno-8188-1/input/entry.ts
Normal file
2
bundler/tests/fixture/deno-8188-1/input/entry.ts
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
import { a, b, c } from './lib';
|
||||||
|
console.log(a, b, c)
|
3
bundler/tests/fixture/deno-8188-1/input/lib.ts
Normal file
3
bundler/tests/fixture/deno-8188-1/input/lib.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export * from './a';
|
||||||
|
export * from './b';
|
||||||
|
export * from './c';
|
13
bundler/tests/fixture/deno-8188-1/output/entry.ts
Normal file
13
bundler/tests/fixture/deno-8188-1/output/entry.ts
Normal file
@ -0,0 +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 c1 = c;
|
||||||
|
const a5 = a4;
|
||||||
|
const b2 = b1;
|
||||||
|
const c2 = c1;
|
||||||
|
console.log(a5, b2, c2);
|
1
bundler/tests/fixture/deno-8188-2/input/a.ts
Normal file
1
bundler/tests/fixture/deno-8188-2/input/a.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export const a = 1;
|
2
bundler/tests/fixture/deno-8188-2/input/b.ts
Normal file
2
bundler/tests/fixture/deno-8188-2/input/b.ts
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
import { a } from './a';
|
||||||
|
export const b = a + 1;
|
2
bundler/tests/fixture/deno-8188-2/input/c.ts
Normal file
2
bundler/tests/fixture/deno-8188-2/input/c.ts
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
import { a } from './a';
|
||||||
|
export const c = a + 2;
|
5
bundler/tests/fixture/deno-8188-2/input/entry.ts
Normal file
5
bundler/tests/fixture/deno-8188-2/input/entry.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import { a, b, c } from './lib';
|
||||||
|
import { user1 } from './user1'
|
||||||
|
import { user2 } from './user2'
|
||||||
|
console.log(a, b, c)
|
||||||
|
console.log(user1, user2)
|
3
bundler/tests/fixture/deno-8188-2/input/lib.ts
Normal file
3
bundler/tests/fixture/deno-8188-2/input/lib.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export * from './a';
|
||||||
|
export * from './b';
|
||||||
|
export * from './c';
|
3
bundler/tests/fixture/deno-8188-2/input/user1.ts
Normal file
3
bundler/tests/fixture/deno-8188-2/input/user1.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import { a } from './a';
|
||||||
|
export const user1 = a + 1;
|
||||||
|
console.log('user 1', user1)
|
5
bundler/tests/fixture/deno-8188-2/input/user2.ts
Normal file
5
bundler/tests/fixture/deno-8188-2/input/user2.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import { a } from './a';
|
||||||
|
import { b } from './b';
|
||||||
|
import { user1 } from './user1';
|
||||||
|
export const user2 = user1 + a + b;
|
||||||
|
console.log('user 2', user2)
|
27
bundler/tests/fixture/deno-8188-2/output/entry.ts
Normal file
27
bundler/tests/fixture/deno-8188-2/output/entry.ts
Normal file
@ -0,0 +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 user11 = user1;
|
||||||
|
const user12 = user11;
|
||||||
|
const user2 = user12 + a6 + b4;
|
||||||
|
console.log('user 2', user2);
|
||||||
|
const user21 = user2;
|
||||||
|
const user22 = user21;
|
||||||
|
console.log(a5, b3, c2);
|
||||||
|
const user13 = user11;
|
||||||
|
console.log(user13, user22);
|
1
bundler/tests/fixture/deno-8188-3/input/a.ts
Normal file
1
bundler/tests/fixture/deno-8188-3/input/a.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export const a = 1;
|
1
bundler/tests/fixture/deno-8188-3/input/b.ts
Normal file
1
bundler/tests/fixture/deno-8188-3/input/b.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export { a as b } from './a';
|
2
bundler/tests/fixture/deno-8188-3/input/entry.ts
Normal file
2
bundler/tests/fixture/deno-8188-3/input/entry.ts
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
import { a, b } from './lib'
|
||||||
|
console.log(a, b)
|
2
bundler/tests/fixture/deno-8188-3/input/lib.ts
Normal file
2
bundler/tests/fixture/deno-8188-3/input/lib.ts
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
export * from './a';
|
||||||
|
export * from './b';
|
7
bundler/tests/fixture/deno-8188-3/output/entry.ts
Normal file
7
bundler/tests/fixture/deno-8188-3/output/entry.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
const a = 1;
|
||||||
|
const a1 = a;
|
||||||
|
const a2 = a;
|
||||||
|
const b = a1;
|
||||||
|
const a3 = a2;
|
||||||
|
const b1 = b;
|
||||||
|
console.log(a3, b1);
|
@ -1,5 +1,10 @@
|
|||||||
class Zone {
|
class Zone3 {
|
||||||
}
|
}
|
||||||
class FixedOffsetZone extends Zone {
|
const __default = Zone3;
|
||||||
|
const Zone1 = __default;
|
||||||
|
class FixedOffsetZone2 extends Zone1 {
|
||||||
}
|
}
|
||||||
export { Zone, FixedOffsetZone };
|
const __default1 = FixedOffsetZone2;
|
||||||
|
const FixedOffsetZone1 = __default1;
|
||||||
|
const Zone2 = __default;
|
||||||
|
export { Zone2 as Zone, FixedOffsetZone1 as FixedOffsetZone };
|
||||||
|
@ -1,10 +1,18 @@
|
|||||||
class Zone {
|
class Zone3 {
|
||||||
}
|
}
|
||||||
class FixedOffsetZone extends Zone {
|
const __default = Zone3;
|
||||||
|
const Zone1 = __default;
|
||||||
|
class FixedOffsetZone3 extends Zone1 {
|
||||||
}
|
}
|
||||||
class Info {
|
const __default1 = FixedOffsetZone3;
|
||||||
|
const FixedOffsetZone1 = __default1;
|
||||||
|
class Info2 {
|
||||||
use() {
|
use() {
|
||||||
console.log(FixedOffsetZone);
|
console.log(FixedOffsetZone1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export { Zone, Info, FixedOffsetZone };
|
const __default2 = Info2;
|
||||||
|
const Info1 = __default2;
|
||||||
|
const Zone2 = __default;
|
||||||
|
const FixedOffsetZone2 = __default1;
|
||||||
|
export { Zone2 as Zone, Info1 as Info, FixedOffsetZone2 as FixedOffsetZone };
|
||||||
|
6
bundler/tests/fixture/deno-8302-1/input/a.js
Normal file
6
bundler/tests/fixture/deno-8302-1/input/a.js
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import { b } from './b';
|
||||||
|
import { A } from './lib';
|
||||||
|
|
||||||
|
console.log(b, A);
|
||||||
|
|
||||||
|
export const a = 1;
|
6
bundler/tests/fixture/deno-8302-1/input/b.js
Normal file
6
bundler/tests/fixture/deno-8302-1/input/b.js
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import { c } from './c';
|
||||||
|
import { A } from './lib';
|
||||||
|
|
||||||
|
console.log(c, A);
|
||||||
|
|
||||||
|
export const b = 2
|
6
bundler/tests/fixture/deno-8302-1/input/c.js
Normal file
6
bundler/tests/fixture/deno-8302-1/input/c.js
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import { a } from './a';
|
||||||
|
import { A } from './lib';
|
||||||
|
|
||||||
|
console.log(a, A);
|
||||||
|
|
||||||
|
export const c = 3;
|
3
bundler/tests/fixture/deno-8302-1/input/entry.js
Normal file
3
bundler/tests/fixture/deno-8302-1/input/entry.js
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import { a } from './a';
|
||||||
|
|
||||||
|
console.log(a);
|
1
bundler/tests/fixture/deno-8302-1/input/lib.js
Normal file
1
bundler/tests/fixture/deno-8302-1/input/lib.js
Normal file
@ -0,0 +1 @@
|
|||||||
|
export class A { }
|
20
bundler/tests/fixture/deno-8302-1/output/entry.js
Normal file
20
bundler/tests/fixture/deno-8302-1/output/entry.js
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
const a = 1;
|
||||||
|
const a1 = a;
|
||||||
|
const b = 2;
|
||||||
|
const b1 = b;
|
||||||
|
const a2 = a1;
|
||||||
|
const c = 3;
|
||||||
|
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);
|
||||||
|
const A4 = A1;
|
||||||
|
console.log(a2, A4);
|
||||||
|
const a3 = a1;
|
||||||
|
console.log(a3);
|
3
bundler/tests/fixture/deno-8325-1/input/entry.ts
Normal file
3
bundler/tests/fixture/deno-8325-1/input/entry.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export default function square(a) {
|
||||||
|
return a * a;
|
||||||
|
}
|
3
bundler/tests/fixture/deno-8325-1/output/entry.ts
Normal file
3
bundler/tests/fixture/deno-8325-1/output/entry.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export default function square(a) {
|
||||||
|
return a * a;
|
||||||
|
};
|
3
bundler/tests/fixture/deno-8325-2/input/entry.ts
Normal file
3
bundler/tests/fixture/deno-8325-2/input/entry.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import entry from "./lib"
|
||||||
|
|
||||||
|
console.log(entry());
|
3
bundler/tests/fixture/deno-8325-2/input/lib.ts
Normal file
3
bundler/tests/fixture/deno-8325-2/input/lib.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export default function square(a) {
|
||||||
|
return a * a;
|
||||||
|
}
|
6
bundler/tests/fixture/deno-8325-2/output/entry.ts
Normal file
6
bundler/tests/fixture/deno-8325-2/output/entry.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
function square(a) {
|
||||||
|
return a * a;
|
||||||
|
}
|
||||||
|
const __default = square;
|
||||||
|
const entry = __default;
|
||||||
|
console.log(entry());
|
3
bundler/tests/fixture/deno-8344-1/input/entry.ts
Normal file
3
bundler/tests/fixture/deno-8344-1/input/entry.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import { u } from "./v";
|
||||||
|
|
||||||
|
console.log(u("a"));
|
7
bundler/tests/fixture/deno-8344-1/input/u.js
Normal file
7
bundler/tests/fixture/deno-8344-1/input/u.js
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
function u(str) {
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
export { u };
|
||||||
|
export default null;
|
||||||
|
export const __esModule = true;
|
2
bundler/tests/fixture/deno-8344-1/input/v.js
Normal file
2
bundler/tests/fixture/deno-8344-1/input/v.js
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
export * from "./u";
|
||||||
|
export { default } from "./u";
|
6
bundler/tests/fixture/deno-8344-1/output/entry.ts
Normal file
6
bundler/tests/fixture/deno-8344-1/output/entry.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
function u(str) {
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
const u1 = u;
|
||||||
|
const u2 = u1;
|
||||||
|
console.log(u2("a"));
|
@ -2,23 +2,22 @@ function a() {
|
|||||||
console.log("a");
|
console.log("a");
|
||||||
}
|
}
|
||||||
const a1 = a;
|
const a1 = a;
|
||||||
var O;
|
const a2 = a1;
|
||||||
|
var O3;
|
||||||
(function(O1) {
|
(function(O1) {
|
||||||
O1[O1["A"] = 0] = "A";
|
O1[O1["A"] = 0] = "A";
|
||||||
O1[O1["B"] = 1] = "B";
|
O1[O1["B"] = 1] = "B";
|
||||||
O1[O1["C"] = 2] = "C";
|
O1[O1["C"] = 2] = "C";
|
||||||
})(O || (O = {
|
})(O3 || (O3 = {
|
||||||
}));
|
}));
|
||||||
const O1 = O;
|
const defaultA = a2;
|
||||||
const defaultA = a1;
|
|
||||||
export { O1 as O };
|
|
||||||
class A {
|
class A {
|
||||||
#a;
|
#a;
|
||||||
#c;
|
#c;
|
||||||
constructor(o = {
|
constructor(o = {
|
||||||
}){
|
}){
|
||||||
const { a: a2 = defaultA , c , } = o;
|
const { a: a3 = defaultA , c , } = o;
|
||||||
this.#a = a2;
|
this.#a = a3;
|
||||||
this.#c = c;
|
this.#c = c;
|
||||||
}
|
}
|
||||||
a() {
|
a() {
|
||||||
@ -28,6 +27,9 @@ class A {
|
|||||||
console.log(this.#c);
|
console.log(this.#c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let a3 = new A();
|
let a4 = new A();
|
||||||
a3.a();
|
a4.a();
|
||||||
a3.c();
|
a4.c();
|
||||||
|
const O1 = O3;
|
||||||
|
const O2 = O1;
|
||||||
|
export { O2 as O };
|
||||||
|
@ -2,23 +2,22 @@ function a() {
|
|||||||
console.log("a");
|
console.log("a");
|
||||||
}
|
}
|
||||||
const a1 = a;
|
const a1 = a;
|
||||||
var O;
|
const a2 = a1;
|
||||||
|
var O3;
|
||||||
(function(O1) {
|
(function(O1) {
|
||||||
O1[O1["A"] = 0] = "A";
|
O1[O1["A"] = 0] = "A";
|
||||||
O1[O1["B"] = 1] = "B";
|
O1[O1["B"] = 1] = "B";
|
||||||
O1[O1["C"] = 2] = "C";
|
O1[O1["C"] = 2] = "C";
|
||||||
})(O || (O = {
|
})(O3 || (O3 = {
|
||||||
}));
|
}));
|
||||||
const O1 = O;
|
const defaultA = a2;
|
||||||
const defaultA = a1;
|
|
||||||
export { O1 as O };
|
|
||||||
class A {
|
class A {
|
||||||
#a;
|
#a;
|
||||||
#c;
|
#c;
|
||||||
constructor(o = {
|
constructor(o = {
|
||||||
}){
|
}){
|
||||||
const { a: a2 = defaultA , c , } = o;
|
const { a: a3 = defaultA , c , } = o;
|
||||||
this.#a = a2;
|
this.#a = a3;
|
||||||
this.#c = c;
|
this.#c = c;
|
||||||
}
|
}
|
||||||
a() {
|
a() {
|
||||||
@ -28,6 +27,9 @@ class A {
|
|||||||
console.log(this.#c);
|
console.log(this.#c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let a3 = new A();
|
let a4 = new A();
|
||||||
a3.a();
|
a4.a();
|
||||||
a3.c();
|
a4.c();
|
||||||
|
const O1 = O3;
|
||||||
|
const O2 = O1;
|
||||||
|
export { O2 as O };
|
||||||
|
@ -2,7 +2,8 @@ function a() {
|
|||||||
console.log("a");
|
console.log("a");
|
||||||
}
|
}
|
||||||
const a1 = a;
|
const a1 = a;
|
||||||
|
const a2 = a1;
|
||||||
function b() {
|
function b() {
|
||||||
a1();
|
a2();
|
||||||
}
|
}
|
||||||
b();
|
b();
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
const a = 'a';
|
const a = 'a';
|
||||||
const a1 = a;
|
const a1 = a;
|
||||||
|
const a2 = a1;
|
||||||
function b() {
|
function b() {
|
||||||
return a1;
|
return a2;
|
||||||
}
|
}
|
||||||
b();
|
b();
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
class a {
|
class a {
|
||||||
}
|
}
|
||||||
const a1 = a;
|
const a1 = a;
|
||||||
|
const a2 = a1;
|
||||||
function b() {
|
function b() {
|
||||||
return new a1();
|
return new a2();
|
||||||
}
|
}
|
||||||
b();
|
b();
|
||||||
|
@ -8,13 +8,15 @@ function d() {
|
|||||||
});
|
});
|
||||||
return Object.assign(promise, methods);
|
return Object.assign(promise, methods);
|
||||||
}
|
}
|
||||||
|
const d1 = d;
|
||||||
|
const d2 = d1;
|
||||||
class A {
|
class A {
|
||||||
s = d();
|
s = d2();
|
||||||
a() {
|
a() {
|
||||||
this.s.resolve();
|
this.s.resolve();
|
||||||
}
|
}
|
||||||
b() {
|
b() {
|
||||||
this.s = d();
|
this.s = d2();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
new A();
|
new A();
|
||||||
|
@ -8,8 +8,10 @@ function d() {
|
|||||||
});
|
});
|
||||||
return Object.assign(promise, methods);
|
return Object.assign(promise, methods);
|
||||||
}
|
}
|
||||||
|
const d1 = d;
|
||||||
|
const d2 = d1;
|
||||||
class A {
|
class A {
|
||||||
s = d();
|
s = d2();
|
||||||
a() {
|
a() {
|
||||||
this.s.resolve();
|
this.s.resolve();
|
||||||
}
|
}
|
||||||
|
3
bundler/tests/fixture/reexport/idnex-style/input/a.ts
Normal file
3
bundler/tests/fixture/reexport/idnex-style/input/a.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export class A { }
|
||||||
|
|
||||||
|
export function utilForA() { }
|
3
bundler/tests/fixture/reexport/idnex-style/input/b.ts
Normal file
3
bundler/tests/fixture/reexport/idnex-style/input/b.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export class B { }
|
||||||
|
|
||||||
|
export function utilForB() { }
|
3
bundler/tests/fixture/reexport/idnex-style/input/c.ts
Normal file
3
bundler/tests/fixture/reexport/idnex-style/input/c.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export class C { }
|
||||||
|
|
||||||
|
export function utilForC() { }
|
@ -0,0 +1,3 @@
|
|||||||
|
import { A } from './lib';
|
||||||
|
|
||||||
|
console.log(A)
|
3
bundler/tests/fixture/reexport/idnex-style/input/lib.ts
Normal file
3
bundler/tests/fixture/reexport/idnex-style/input/lib.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export * from './a';
|
||||||
|
export * from './b';
|
||||||
|
export * from './c';
|
@ -0,0 +1,5 @@
|
|||||||
|
class A {
|
||||||
|
}
|
||||||
|
const A1 = A;
|
||||||
|
const A2 = A1;
|
||||||
|
console.log(A2);
|
1
bundler/tests/fixture/reexport/nested/input/a.js
Normal file
1
bundler/tests/fixture/reexport/nested/input/a.js
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * from './b';
|
1
bundler/tests/fixture/reexport/nested/input/b.js
Normal file
1
bundler/tests/fixture/reexport/nested/input/b.js
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * from './c';
|
5
bundler/tests/fixture/reexport/nested/input/c.js
Normal file
5
bundler/tests/fixture/reexport/nested/input/c.js
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
export const a = 1;
|
||||||
|
|
||||||
|
export function foo() { }
|
||||||
|
|
||||||
|
export class Class { }
|
1
bundler/tests/fixture/reexport/nested/input/entry.js
Normal file
1
bundler/tests/fixture/reexport/nested/input/entry.js
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * from './a';
|
5
bundler/tests/fixture/reexport/nested/output/entry.js
Normal file
5
bundler/tests/fixture/reexport/nested/output/entry.js
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
export const a = 1;
|
||||||
|
export function foo() {
|
||||||
|
}
|
||||||
|
export class Class {
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user