mirror of
https://github.com/swc-project/swc.git
synced 2024-12-24 14:16:12 +03:00
bundler: Handle export * properly (#1083)
This commit is contained in:
parent
b0049c0fa6
commit
fa756a1b48
@ -6,7 +6,7 @@ edition = "2018"
|
||||
license = "Apache-2.0/MIT"
|
||||
name = "swc_bundler"
|
||||
repository = "https://github.com/swc-project/swc.git"
|
||||
version = "0.7.3"
|
||||
version = "0.7.4"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
[features]
|
||||
|
@ -126,8 +126,19 @@ where
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
{
|
||||
let mut decls = vec![];
|
||||
let mut normal_reexports = vec![];
|
||||
let mut star_reexports = vec![];
|
||||
for (src, mut specifiers) in additional_modules {
|
||||
// If a dependency is indirect, we need to export items from it manually.
|
||||
let is_indirect = !nomral_plan.chunks.contains(&src.module_id);
|
||||
|
||||
let add_to = if specifiers.is_empty() && is_indirect {
|
||||
// User provided code like `export * from './foo';`, but planner decide to merge
|
||||
// it within dependency module. So we reexport them using a named export.
|
||||
&mut star_reexports
|
||||
} else {
|
||||
&mut normal_reexports
|
||||
};
|
||||
if specifiers.is_empty() {
|
||||
//
|
||||
let dep = self.scope.get_module(src.module_id).unwrap();
|
||||
@ -146,26 +157,56 @@ where
|
||||
unimplemented!("namespaced re-export: local={:?}, all={}", local, all)
|
||||
}
|
||||
};
|
||||
let var = VarDeclarator {
|
||||
span: DUMMY_SP,
|
||||
name: Pat::Ident(imported),
|
||||
init: Some(Box::new(Expr::Ident(exported))),
|
||||
definite: false,
|
||||
};
|
||||
decls.push(var);
|
||||
|
||||
add_to.push((imported, exported));
|
||||
}
|
||||
}
|
||||
|
||||
if !decls.is_empty() {
|
||||
if !normal_reexports.is_empty() {
|
||||
entry
|
||||
.body
|
||||
.push(ModuleItem::Stmt(Stmt::Decl(Decl::Var(VarDecl {
|
||||
span: DUMMY_SP,
|
||||
kind: VarDeclKind::Const,
|
||||
declare: false,
|
||||
decls,
|
||||
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,
|
||||
},
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
for dep in deps {
|
||||
|
@ -326,6 +326,7 @@ where
|
||||
// }
|
||||
|
||||
if is_entry {
|
||||
// print_hygiene("done", &self.cm, &entry);
|
||||
self.finalize_merging_of_entry(plan, &mut entry);
|
||||
}
|
||||
|
||||
@ -336,6 +337,8 @@ where
|
||||
fn finalize_merging_of_entry(&self, plan: &Plan, entry: &mut Module) {
|
||||
entry.body.retain_mut(|item| {
|
||||
match item {
|
||||
ModuleItem::ModuleDecl(ModuleDecl::ExportAll(..)) => return false,
|
||||
|
||||
ModuleItem::ModuleDecl(ModuleDecl::ExportNamed(export)) => {
|
||||
export.src = None;
|
||||
}
|
||||
|
9
spack/tests/pass/deno-002/full/input/.swcrc
Normal file
9
spack/tests/pass/deno-002/full/input/.swcrc
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"jsc": {
|
||||
"target": "es2020",
|
||||
"parser": {
|
||||
"syntax": "typescript",
|
||||
"decorators": true
|
||||
}
|
||||
}
|
||||
}
|
26
spack/tests/pass/deno-002/full/input/async/deferred.ts
Normal file
26
spack/tests/pass/deno-002/full/input/async/deferred.ts
Normal file
@ -0,0 +1,26 @@
|
||||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
// TODO(ry) It'd be better to make Deferred a class that inherits from
|
||||
// Promise, rather than an interface. This is possible in ES2016, however
|
||||
// typescript produces broken code when targeting ES5 code.
|
||||
// See https://github.com/Microsoft/TypeScript/issues/15202
|
||||
// At the time of writing, the github issue is closed but the problem remains.
|
||||
export interface Deferred<T> extends Promise<T> {
|
||||
resolve: (value?: T | PromiseLike<T>) => void;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
reject: (reason?: any) => void;
|
||||
}
|
||||
|
||||
/** Creates a Promise with the `reject` and `resolve` functions
|
||||
* placed as methods on the promise object itself. It allows you to do:
|
||||
*
|
||||
* const p = deferred<number>();
|
||||
* // ...
|
||||
* p.resolve(42);
|
||||
*/
|
||||
export function deferred<T>(): Deferred<T> {
|
||||
let methods;
|
||||
const promise = new Promise<T>((resolve, reject): void => {
|
||||
methods = { resolve, reject };
|
||||
});
|
||||
return Object.assign(promise, methods) as Deferred<T>;
|
||||
}
|
9
spack/tests/pass/deno-002/full/input/async/delay.ts
Normal file
9
spack/tests/pass/deno-002/full/input/async/delay.ts
Normal file
@ -0,0 +1,9 @@
|
||||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
/* Resolves after the given number of milliseconds. */
|
||||
export function delay(ms: number): Promise<void> {
|
||||
return new Promise((res): number =>
|
||||
setTimeout((): void => {
|
||||
res();
|
||||
}, ms)
|
||||
);
|
||||
}
|
5
spack/tests/pass/deno-002/full/input/async/mod.ts
Normal file
5
spack/tests/pass/deno-002/full/input/async/mod.ts
Normal file
@ -0,0 +1,5 @@
|
||||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
export * from "./deferred";
|
||||
export * from "./delay";
|
||||
export * from "./mux_async_iterator";
|
||||
export * from "./pool";
|
@ -0,0 +1,69 @@
|
||||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
import { Deferred, deferred } from "./deferred.ts";
|
||||
|
||||
interface TaggedYieldedValue<T> {
|
||||
iterator: AsyncIterableIterator<T>;
|
||||
value: T;
|
||||
}
|
||||
|
||||
/** The MuxAsyncIterator class multiplexes multiple async iterators into a
|
||||
* single stream. It currently makes an assumption:
|
||||
* - The final result (the value returned and not yielded from the iterator)
|
||||
* does not matter; if there is any, it is discarded.
|
||||
*/
|
||||
export class MuxAsyncIterator<T> implements AsyncIterable<T> {
|
||||
private iteratorCount = 0;
|
||||
private yields: Array<TaggedYieldedValue<T>> = [];
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
private throws: any[] = [];
|
||||
private signal: Deferred<void> = deferred();
|
||||
|
||||
add(iterator: AsyncIterableIterator<T>): void {
|
||||
++this.iteratorCount;
|
||||
this.callIteratorNext(iterator);
|
||||
}
|
||||
|
||||
private async callIteratorNext(
|
||||
iterator: AsyncIterableIterator<T>,
|
||||
): Promise<void> {
|
||||
try {
|
||||
const { value, done } = await iterator.next();
|
||||
if (done) {
|
||||
--this.iteratorCount;
|
||||
} else {
|
||||
this.yields.push({ iterator, value });
|
||||
}
|
||||
} catch (e) {
|
||||
this.throws.push(e);
|
||||
}
|
||||
this.signal.resolve();
|
||||
}
|
||||
|
||||
async *iterate(): AsyncIterableIterator<T> {
|
||||
while (this.iteratorCount > 0) {
|
||||
// Sleep until any of the wrapped iterators yields.
|
||||
await this.signal;
|
||||
|
||||
// Note that while we're looping over `yields`, new items may be added.
|
||||
for (let i = 0; i < this.yields.length; i++) {
|
||||
const { iterator, value } = this.yields[i];
|
||||
yield value;
|
||||
this.callIteratorNext(iterator);
|
||||
}
|
||||
|
||||
if (this.throws.length) {
|
||||
for (const e of this.throws) {
|
||||
throw e;
|
||||
}
|
||||
this.throws.length = 0;
|
||||
}
|
||||
// Clear the `yields` list and reset the `signal` promise.
|
||||
this.yields.length = 0;
|
||||
this.signal = deferred();
|
||||
}
|
||||
}
|
||||
|
||||
[Symbol.asyncIterator](): AsyncIterableIterator<T> {
|
||||
return this.iterate();
|
||||
}
|
||||
}
|
46
spack/tests/pass/deno-002/full/input/async/pool.ts
Normal file
46
spack/tests/pass/deno-002/full/input/async/pool.ts
Normal file
@ -0,0 +1,46 @@
|
||||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
/**
|
||||
* pooledMap transforms values from an (async) iterable into another async
|
||||
* iterable. The transforms are done concurrently, with a max concurrency
|
||||
* defined by the poolLimit.
|
||||
*
|
||||
* @param poolLimit The maximum count of items being processed concurrently.
|
||||
* @param array The input array for mapping.
|
||||
* @param iteratorFn The function to call for every item of the array.
|
||||
*/
|
||||
export function pooledMap<T, R>(
|
||||
poolLimit: number,
|
||||
array: Iterable<T> | AsyncIterable<T>,
|
||||
iteratorFn: (data: T) => Promise<R>,
|
||||
): AsyncIterableIterator<R> {
|
||||
// Create the async iterable that is returned from this function.
|
||||
const res = new TransformStream<Promise<R>, R>({
|
||||
async transform(
|
||||
p: Promise<R>,
|
||||
controller: TransformStreamDefaultController<R>,
|
||||
): Promise<void> {
|
||||
controller.enqueue(await p);
|
||||
},
|
||||
});
|
||||
// Start processing items from the iterator
|
||||
(async (): Promise<void> => {
|
||||
const writer = res.writable.getWriter();
|
||||
const executing: Array<Promise<unknown>> = [];
|
||||
for await (const item of array) {
|
||||
const p = Promise.resolve().then(() => iteratorFn(item));
|
||||
writer.write(p);
|
||||
const e: Promise<unknown> = p.then(() =>
|
||||
executing.splice(executing.indexOf(e), 1)
|
||||
);
|
||||
executing.push(e);
|
||||
if (executing.length >= poolLimit) {
|
||||
await Promise.race(executing);
|
||||
}
|
||||
}
|
||||
// Wait until all ongoing events have processed, then close the writer.
|
||||
await Promise.all(executing);
|
||||
writer.close();
|
||||
})();
|
||||
return res.readable.getIterator();
|
||||
}
|
1
spack/tests/pass/deno-002/full/input/entry.js
Normal file
1
spack/tests/pass/deno-002/full/input/entry.js
Normal file
@ -0,0 +1 @@
|
||||
export * from './async/mod'
|
101
spack/tests/pass/deno-002/full/output/entry.js
Normal file
101
spack/tests/pass/deno-002/full/output/entry.js
Normal file
@ -0,0 +1,101 @@
|
||||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
/* Resolves after the given number of milliseconds. */ export function delay(ms) {
|
||||
return new Promise((res)=>setTimeout(()=>{
|
||||
res();
|
||||
}, ms)
|
||||
);
|
||||
}
|
||||
function deferred1() {
|
||||
let methods;
|
||||
const promise = new Promise((resolve, reject)=>{
|
||||
});
|
||||
return Object.assign(promise, methods);
|
||||
}
|
||||
var tmp = Symbol.asyncIterator;
|
||||
/** The MuxAsyncIterator class multiplexes multiple async iterators into a
|
||||
* single stream. It currently makes an assumption:
|
||||
* - The final result (the value returned and not yielded from the iterator)
|
||||
* does not matter; if there is any, it is discarded.
|
||||
*/ export class MuxAsyncIterator {
|
||||
add(iterator) {
|
||||
++this.iteratorCount;
|
||||
this.callIteratorNext(iterator);
|
||||
}
|
||||
async callIteratorNext(iterator) {
|
||||
try {
|
||||
const { value , done } = await iterator.next();
|
||||
if (done) --this.iteratorCount;
|
||||
else this.yields.push({
|
||||
iterator,
|
||||
value
|
||||
});
|
||||
} catch (e) {
|
||||
this.throws.push(e);
|
||||
}
|
||||
this.signal.resolve();
|
||||
}
|
||||
async *iterate() {
|
||||
while(this.iteratorCount > 0){
|
||||
// Sleep until any of the wrapped iterators yields.
|
||||
await this.signal;
|
||||
// Note that while we're looping over `yields`, new items may be added.
|
||||
for(let i = 0; i < this.yields.length; i++){
|
||||
const { iterator , value } = this.yields[i];
|
||||
yield value;
|
||||
this.callIteratorNext(iterator);
|
||||
}
|
||||
if (this.throws.length) {
|
||||
for (const e of this.throws)throw e;
|
||||
this.throws.length = 0;
|
||||
}
|
||||
// Clear the `yields` list and reset the `signal` promise.
|
||||
this.yields.length = 0;
|
||||
this.signal = deferred1();
|
||||
}
|
||||
}
|
||||
[tmp]() {
|
||||
return this.iterate();
|
||||
}
|
||||
constructor(){
|
||||
this.iteratorCount = 0;
|
||||
this.yields = [];
|
||||
this.throws = [];
|
||||
this.signal = deferred1();
|
||||
}
|
||||
}
|
||||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
/**
|
||||
* pooledMap transforms values from an (async) iterable into another async
|
||||
* iterable. The transforms are done concurrently, with a max concurrency
|
||||
* defined by the poolLimit.
|
||||
*
|
||||
* @param poolLimit The maximum count of items being processed concurrently.
|
||||
* @param array The input array for mapping.
|
||||
* @param iteratorFn The function to call for every item of the array.
|
||||
*/ export function pooledMap(poolLimit, array, iteratorFn) {
|
||||
// Create the async iterable that is returned from this function.
|
||||
const res = new TransformStream({
|
||||
async transform (p, controller) {
|
||||
controller.enqueue(await p);
|
||||
}
|
||||
});
|
||||
// Start processing items from the iterator
|
||||
(async ()=>{
|
||||
const writer = res.writable.getWriter();
|
||||
const executing = [];
|
||||
for await (const item of array){
|
||||
const p = Promise.resolve().then(()=>iteratorFn(item)
|
||||
);
|
||||
writer.write(p);
|
||||
const e = p.then(()=>executing.splice(executing.indexOf(e), 1)
|
||||
);
|
||||
executing.push(e);
|
||||
if (executing.length >= poolLimit) await Promise.race(executing);
|
||||
}
|
||||
// Wait until all ongoing events have processed, then close the writer.
|
||||
await Promise.all(executing);
|
||||
writer.close();
|
||||
})();
|
||||
return res.readable.getIterator();
|
||||
}
|
||||
export { deferred1 as deferred };
|
Loading…
Reference in New Issue
Block a user