Skip to content
This repository was archived by the owner on Jul 6, 2025. It is now read-only.

Commit ad6c5f8

Browse files
author
ije
committed
refactor(compiler): star_exports -> bundle_star_exports
1 parent e230d6c commit ad6c5f8

File tree

5 files changed

+95
-51
lines changed

5 files changed

+95
-51
lines changed

compiler/mod.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ export type TransformOptions = {
4343
export type TransformResult = {
4444
code: string
4545
deps: DependencyDescriptor[]
46+
bundleStarExports?: string[]
4647
map?: string
4748
}
4849

@@ -120,7 +121,7 @@ export async function transform(url: string, code: string, options: TransformOpt
120121
}
121122

122123
const { loaders, ...transformOptions } = options
123-
let { code: jsContent, inlineStyles, deps, map } = transformSync(url, code, transformOptions)
124+
let { code: jsContent, deps, map, inlineStyles, bundleStarExports } = transformSync(url, code, transformOptions)
124125

125126
// resolve inline-style
126127
await Promise.all(Object.entries(inlineStyles as InlineStyles).map(async ([key, style]) => {
@@ -165,7 +166,7 @@ export async function transform(url: string, code: string, options: TransformOpt
165166
jsContent = jsContent.replace(`"%%${key}-placeholder%%"`, '`' + tpl + '`')
166167
}))
167168

168-
return { code: jsContent, deps, map }
169+
return { code: jsContent, deps, map, bundleStarExports }
169170
}
170171

171172
/* parse export names of the module */

compiler/src/lib.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ pub struct TransformOutput {
9999
pub map: Option<String>,
100100
pub deps: Vec<DependencyDescriptor>,
101101
pub inline_styles: HashMap<String, InlineStyle>,
102-
pub star_exports: Vec<String>,
102+
pub bundle_star_exports: Option<Vec<String>>,
103103
}
104104

105105
#[wasm_bindgen(js_name = "parseExportNamesSync")]
@@ -163,7 +163,11 @@ pub fn transform_sync(url: &str, code: &str, options: JsValue) -> Result<JsValue
163163
map,
164164
deps: r.dep_graph.clone(),
165165
inline_styles: r.inline_styles.clone(),
166-
star_exports: r.star_exports.clone(),
166+
bundle_star_exports: if r.bundle_star_exports.len() > 0 {
167+
Some(r.bundle_star_exports.clone())
168+
} else {
169+
None
170+
},
167171
})
168172
.unwrap(),
169173
)

compiler/src/resolve.rs

Lines changed: 15 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ lazy_static! {
1919
)
2020
.unwrap();
2121
pub static ref RE_REACT_URL: Regex = Regex::new(
22-
r"^https?://(esm.sh/|cdn.esm.sh/v\d+/|esm.x-static.io/v\d+/|jspm.dev/|cdn.skypack.dev/|jspm.dev/npm:|esm.run/)react(\-dom)?(@[\^|~]{0,1}[0-9a-z\.\-]+)?([/|\?].*)?$"
22+
r"^https?://(esm.sh/|cdn.esm.sh/v\d+/|cdn.esm.sh.cn/v\d+/|esm.x-static.io/v\d+/)react(\-dom)?(@[\^|~]{0,1}[0-9a-z\.\-]+)?([/|\?].*)?$"
2323
)
2424
.unwrap();
2525
}
@@ -45,20 +45,20 @@ pub struct Resolver {
4545
pub specifier: String,
4646
/// a flag indicating if the specifier is remote url or not.
4747
pub specifier_is_remote: bool,
48-
/// builtin jsx tags like `a`, `link`, `head`, etc
49-
pub used_builtin_jsx_tags: IndexSet<String>,
5048
/// dependency graph
5149
pub dep_graph: Vec<DependencyDescriptor>,
52-
/// star exports
53-
pub star_exports: Vec<String>,
5450
/// inline styles
5551
pub inline_styles: HashMap<String, InlineStyle>,
5652
/// bundle mode
5753
pub bundle_mode: bool,
5854
/// bundled modules
5955
pub bundle_external: IndexSet<String>,
56+
/// bundle star exports
57+
pub bundle_star_exports: Vec<String>,
6058
/// extra imports
6159
pub extra_imports: IndexSet<String>,
60+
/// builtin jsx tags like `a`, `link`, `head`, etc
61+
pub used_builtin_jsx_tags: IndexSet<String>,
6262

6363
// private
6464
import_map: ImportMap,
@@ -84,7 +84,7 @@ impl Resolver {
8484
specifier_is_remote: is_remote_url(specifier),
8585
used_builtin_jsx_tags: IndexSet::new(),
8686
dep_graph: Vec::new(),
87-
star_exports: Vec::new(),
87+
bundle_star_exports: Vec::new(),
8888
inline_styles: HashMap::new(),
8989
import_map: ImportMap::from_hashmap(import_map),
9090
aleph_pkg_uri,
@@ -108,13 +108,10 @@ impl Resolver {
108108

109109
/// fix import/export url.
110110
// - `https://esm.sh/react` -> `/-/esm.sh/react.js`
111-
// - `https://esm.sh/[email protected]?target=es2015&dev` -> `/-/esm.sh/[email protected].1_target=es2015&dev.js`
111+
// - `https://esm.sh/[email protected]?target=es2015&dev` -> `/-/esm.sh/[base64('target=es2015&dev')][email protected].1.js`
112112
// - `http://localhost:8080/mod` -> `/-/http_localhost_8080/mod.js`
113-
// - `/components/logo.tsx` -> `/components/logo.tsx`
114-
// - `../components/logo.tsx` -> `../components/logo.tsx`
115-
// - `./button.tsx` -> `./button.tsx`
116-
// - `/components/foo/./logo.tsx` -> `/components/foo/logo.tsx`
117-
// - `/components/foo/../logo.tsx` -> `/components/logo.tsx`
113+
// - `/components/x/./logo.tsx` -> `/components/x/logo.tsx`
114+
// - `/components/x/../logo.tsx` -> `/components/logo.tsx`
118115
pub fn fix_import_url(&self, url: &str) -> String {
119116
let is_remote = is_remote_url(url);
120117
if !is_remote {
@@ -194,8 +191,8 @@ impl Resolver {
194191
// [/pages/index.tsx]
195192
// - `https://esm.sh/swr` -> `../-/esm.sh/swr.js`
196193
// - `https://esm.sh/react` -> `../-/esm.sh/react@${REACT_VERSION}.js`
197-
// - `https://deno.land/x/aleph/mod.ts` -> `../-/deno.land/x/aleph@v${CURRENT_ALEPH_VERSION}/mod.ts`
198-
// - `../components/logo.tsx` -> `../components/logo.js#/styles/app.css@000000`
194+
// - `https://deno.land/x/aleph/mod.ts` -> `../-/deno.land/x/aleph@v${ALEPH_VERSION}/mod.ts`
195+
// - `../components/logo.tsx` -> `../components/logo.js#/components/logo.tsx@000000`
199196
// - `../styles/app.css` -> `../styles/app.css.js#/styles/app.css@000000`
200197
pub fn resolve(&mut self, url: &str, is_dynamic: bool) -> (String, String) {
201198
// apply import map
@@ -328,12 +325,8 @@ impl Resolver {
328325
.unwrap()
329326
.trim_end_matches(s)
330327
.to_owned();
331-
if self.bundle_mode {
332-
if is_dynamic {
333-
filename.push_str("bundle.");
334-
} else {
335-
filename.push_str("bundling.");
336-
}
328+
if self.bundle_mode && !is_dynamic {
329+
filename.push_str("bundling.");
337330
}
338331
filename.push_str("js");
339332
if !is_remote && !self.specifier_is_remote {
@@ -351,12 +344,8 @@ impl Resolver {
351344
.to_str()
352345
.unwrap()
353346
.to_owned();
354-
if self.bundle_mode {
355-
if is_dynamic {
356-
filename.push_str(".bundle");
357-
} else {
358-
filename.push_str(".bundling");
359-
}
347+
if self.bundle_mode && !is_dynamic {
348+
filename.push_str(".bundling");
360349
}
361350
filename.push_str(".js#");
362351
filename.push_str(fixed_url.as_str());

compiler/src/resolve_fold.rs

Lines changed: 71 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@ impl Fold for ResolveFold {
208208
let mut resolver = self.resolver.borrow_mut();
209209
let (resolved_path, fixed_url) = resolver.resolve(src.value.as_ref(), false);
210210
if resolver.bundle_mode && resolver.bundle_external.contains(fixed_url.as_str()) {
211-
resolver.star_exports.push(fixed_url.clone());
211+
resolver.bundle_star_exports.push(fixed_url.clone());
212212
ModuleItem::ModuleDecl(ModuleDecl::ExportDecl(ExportDecl {
213213
span: DUMMY_SP,
214214
decl: Decl::Var(VarDecl {
@@ -217,7 +217,7 @@ impl Fold for ResolveFold {
217217
declare: false,
218218
decls: vec![create_aleph_pack_var_decl(
219219
fixed_url.as_ref(),
220-
quote_ident!(format!("$$star_{}", resolver.star_exports.len() - 1)),
220+
quote_ident!(format!("$$star_{}", resolver.bundle_star_exports.len() - 1)),
221221
)],
222222
}),
223223
}))
@@ -304,15 +304,17 @@ impl Fold for ResolveFold {
304304
prop: quote_ident!("import"),
305305
})))
306306
}
307-
call.args = vec![ExprOrSpread {
308-
spread: None,
309-
expr: Box::new(Expr::Lit(Lit::Str(new_str(resolver.resolve(url, true).0)))),
310-
}];
307+
let (resolved_path, fixed_url) = resolver.resolve(url, true);
311308
if resolver.bundle_mode {
312-
call.args.push(ExprOrSpread {
309+
call.args = vec![ExprOrSpread {
313310
spread: None,
314-
expr: Box::new(Expr::Lit(Lit::Str(new_str(resolver.specifier.clone())))),
315-
})
311+
expr: Box::new(Expr::Lit(Lit::Str(new_str(fixed_url)))),
312+
}];
313+
} else {
314+
call.args = vec![ExprOrSpread {
315+
spread: None,
316+
expr: Box::new(Expr::Lit(Lit::Str(new_str(resolved_path)))),
317+
}];
316318
}
317319
} else if is_call_expr_by_name(&call, "useDeno") {
318320
let callback_span = match call.args.first() {
@@ -330,6 +332,7 @@ impl Fold for ResolveFold {
330332
let bundle_mode = self.resolver.borrow().bundle_mode;
331333
let id = self.new_use_deno_hook_ident(span);
332334
if bundle_mode {
335+
// tree-shake useDeno callback in bundle mode
333336
call.args[0] = ExprOrSpread {
334337
spread: None,
335338
expr: Box::new(Expr::Lit(Lit::Null(Null { span: DUMMY_SP }))),
@@ -552,23 +555,70 @@ mod tests {
552555
use crate::resolve::Resolver;
553556
use crate::swc::{st, EmitOptions, SWC};
554557
use sha1::{Digest, Sha1};
558+
use std::collections::HashMap;
559+
560+
#[test]
561+
fn resolve_import_export() {
562+
let source = r#"
563+
import React from 'react'
564+
import { redirect } from 'aleph'
565+
import { useDeno } from 'aleph/hooks.ts'
566+
import { render } from 'react-dom/server'
567+
import { render as _render } from 'https://cdn.esm.sh/v1/[email protected]/es2020/react-dom.js'
568+
import Logo from '../component/logo.tsx'
569+
import Logo2 from '~/component/logo.tsx'
570+
import Logo3 from '@/component/logo.tsx'
571+
const AsyncLogo = React.lazy(() => import('../components/async-logo.tsx'))
572+
export { useState } from 'https://esm.sh/react'
573+
export * from 'https://esm.sh/swr'
574+
export { React, redirect, useDeno, render, _render, Logo, Logo2, Logo3, AsyncLogo }
575+
"#;
576+
let module = SWC::parse("/pages/index.tsx", source, None).expect("could not parse module");
577+
let mut imports: HashMap<String, String> = HashMap::new();
578+
imports.insert("@/".into(), "./".into());
579+
imports.insert("~/".into(), "./".into());
580+
imports.insert("aleph".into(), "https://deno.land/x/aleph/mod.ts".into());
581+
imports.insert("aleph/".into(), "https://deno.land/x/aleph/".into());
582+
imports.insert("react".into(), "https://esm.sh/react".into());
583+
imports.insert("react-dom/".into(), "https://esm.sh/react-dom/".into());
584+
let resolver = Rc::new(RefCell::new(Resolver::new(
585+
"/pages/index.tsx",
586+
ImportHashMap {
587+
imports,
588+
scopes: HashMap::new(),
589+
},
590+
Some("https://deno.land/x/[email protected]".into()),
591+
Some("17.0.1".into()),
592+
false,
593+
vec![],
594+
)));
595+
let (code, _) = module
596+
.transform(resolver.clone(), &EmitOptions::default())
597+
.expect("could not transform module");
598+
println!("{}", code);
599+
assert!(code.contains("import React from \"../-/esm.sh/[email protected]\""));
600+
assert!(code.contains("import { redirect } from \"../-/deno.land/x/[email protected]/mod.js\""));
601+
assert!(code.contains("import { useDeno } from \"../-/deno.land/x/[email protected]/hooks.js\""));
602+
assert!(code.contains("import { render } from \"../-/esm.sh/[email protected]/server.js\""));
603+
assert!(code.contains("import { render as _render } from \"../-/cdn.esm.sh/v1/[email protected]/es2020/react-dom.js\""));
604+
assert!(code.contains("import Logo from \"../component/logo.js#/component/logo.tsx@000000\""));
605+
assert!(code.contains("import Logo2 from \"../component/logo.js#/component/logo.tsx@000000\""));
606+
assert!(code.contains("import Logo3 from \"../component/logo.js#/component/logo.tsx@000000\""));
607+
assert!(code.contains("const AsyncLogo = React.lazy(()=>import(\"../components/async-logo.js#/components/async-logo.tsx@000000\")"));
608+
assert!(code.contains("export { useState } from \"../-/esm.sh/[email protected]\""));
609+
assert!(code.contains("export * from \"../-/esm.sh/swr.js\""));
610+
}
555611

556612
#[test]
557613
fn sign_use_deno_hook() {
558614
let specifer = "/pages/index.tsx";
559615
let source = r#"
560616
export default function Index() {
561617
const verison = useDeno(() => Deno.version)
562-
const verison = useDeno(async function() {
618+
const data = useDeno(async function() {
563619
return await readJson("./data.json")
564620
}, 1000)
565-
return (
566-
<>
567-
<p>Deno v{version.deno}</p>
568-
<V8 />
569-
<TS />
570-
</>
571-
)
621+
return null
572622
}
573623
"#;
574624

@@ -623,8 +673,8 @@ mod tests {
623673
import '../shared/iife.ts'
624674
import '../shared/iife2.ts'
625675
export * from "https://esm.sh/react"
626-
export { render } from "https://esm.sh/react-dom"
627676
export * as ReactDom from "https://esm.sh/react-dom"
677+
export { render } from "https://esm.sh/react-dom"
628678
629679
const AsyncLogo = React.lazy(() => import('../components/async-logo.tsx'))
630680
@@ -667,8 +717,9 @@ mod tests {
667717
);
668718
assert!(!code.contains("__ALEPH.pack[\"/shared/iife.ts\"]"));
669719
assert!(code.contains("import \"../shared/iife2.bundling.js#/shared/iife2.ts@000000\""));
670-
assert!(code
671-
.contains("AsyncLogo = React.lazy(()=>__ALEPH.import(\"../components/async-logo.bundle.js#/components/async-logo.tsx@000000\", \"/pages/index.tsx\""));
720+
assert!(
721+
code.contains("AsyncLogo = React.lazy(()=>__ALEPH.import(\"/components/async-logo.tsx\"")
722+
);
672723
assert!(code.contains(
673724
"const { default: __ALEPH_Head } = __ALEPH.pack[\"https://deno.land/x/aleph/framework/react/head.ts\"]"
674725
));

compiler/src/swc.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -372,6 +372,5 @@ mod tests {
372372
assert!(code.contains("React.createElement(React.Fragment, null"));
373373
assert!(code.contains("React.createElement(\"h1\", {"));
374374
assert!(code.contains("className: \"title\""));
375-
assert!(code.contains("import React from \"../-/esm.sh/react.js\""));
376375
}
377376
}

0 commit comments

Comments
 (0)