Closed ccgauche closed 1 year ago
When bundling TS, Using this code adapted from the GitHub examples:
use std::{ collections::HashMap, path::Path, time::{Duration, Instant}, }; use anyhow::Error; use swc_atoms::js_word; use swc_bundler::{Bundle, Bundler, Load, ModuleData, ModuleRecord}; use swc_common::{ sync::Lrc, FileName, Globals, Mark, SourceMap, Span, GLOBALS, FilePathMapping, }; use swc_ecma_ast::*; use swc_ecma_codegen::{ text_writer::{omit_trailing_semi, JsWriter, WriteJs}, Emitter, }; use swc_ecma_loader::{ resolvers::{lru::CachingResolver, node::NodeModulesResolver}, TargetEnv, }; use swc_ecma_minifier::option::{ CompressOptions, ExtraOptions, MangleOptions, MinifyOptions, TopLevelOptions, }; use swc_ecma_parser::{parse_file_as_module, Syntax, TsConfig}; use swc_ecma_transforms_base::fixer::fixer; use swc_ecma_visit::VisitMutWith; use crate::swc2::create_handler; fn print_bundles(cm: Lrc<SourceMap>, modules: Vec<Bundle>, minify: bool) -> String { let mut last= String::new(); for bundled in modules { let code = { let mut buf = vec![]; { let wr = JsWriter::new(cm.clone(), "\n", &mut buf, None); let mut emitter = Emitter { cfg: swc_ecma_codegen::Config { minify, target: EsVersion::Es2015, ..Default::default() }, cm: cm.clone(), comments: None, wr: if minify { Box::new(omit_trailing_semi(wr)) as Box<dyn WriteJs> } else { Box::new(wr) as Box<dyn WriteJs> }, }; emitter.emit_module(&bundled.module).unwrap(); } String::from_utf8_lossy(&buf).to_string() }; last = code; } last } fn do_test(_entry: &Path, entries: HashMap<String, FileName>, inline: bool, minify: bool) -> String { let cm = Lrc::new(SourceMap::new(FilePathMapping::empty())); let start = Instant::now(); let globals = Box::leak(Box::new(Globals::default())); let mut bundler = Bundler::new( globals, cm.clone(), Loader { cm: cm.clone() }, CachingResolver::new( 4096, NodeModulesResolver::new(TargetEnv::Browser, Default::default(), true), ), swc_bundler::Config { require: false, disable_inliner: !inline, external_modules: Default::default(), disable_fixer: minify, disable_hygiene: minify, disable_dce: false, module: Default::default(), }, Box::new(Hook), ); let mut modules = bundler .bundle(entries) .map_err(|err| println!("{}", err)).unwrap(); println!("Bundled as {} modules", modules.len()); { let dur = start.elapsed(); println!("Bundler.bundle() took {}", to_ms(dur)); } let error = false; if minify { let start = Instant::now(); modules = modules .into_iter() .map(|mut b| { GLOBALS.set(globals, || { b.module = swc_ecma_minifier::optimize( b.module.into(), cm.clone(), None, None, &MinifyOptions { compress: Some(CompressOptions { top_level: Some(TopLevelOptions { functions: true }), ..Default::default() }), mangle: Some(MangleOptions { top_level: Some(true), ..Default::default() }), ..Default::default() }, &ExtraOptions { unresolved_mark: Mark::new(), top_level_mark: Mark::new(), }, ) .expect_module(); b.module.visit_mut_with(&mut fixer(None)); b }) }) .collect(); let dur = start.elapsed(); println!("Minification took {}", to_ms(dur)); } let k = { let cm = cm; print_bundles(cm, modules, minify) }; if error { panic!("Error occured"); } k } fn to_ms(dur: Duration) -> String { format!("{}ms", dur.as_millis()) } pub fn main(main_file: &str) -> Result<String, Error> { let minify = false; let mut entries = HashMap::default(); entries.insert("main".to_string(), FileName::Real(main_file.into())); let start = Instant::now(); let k = do_test(Path::new(&main_file), entries, false, minify); let dur = start.elapsed(); println!("Took {}", to_ms(dur)); Ok(k) } struct Hook; impl swc_bundler::Hook for Hook { fn get_import_meta_props( &self, span: Span, module_record: &ModuleRecord, ) -> Result<Vec<KeyValueProp>, Error> { let file_name = module_record.file_name.to_string(); Ok(vec![ KeyValueProp { key: PropName::Ident(Ident::new(js_word!("url"), span)), value: Box::new(Expr::Lit(Lit::Str(Str { span, raw: None, value: file_name.into(), }))), }, KeyValueProp { key: PropName::Ident(Ident::new(js_word!("main"), span)), value: Box::new(if module_record.is_entry { Expr::Member(MemberExpr { span, obj: Box::new(Expr::MetaProp(MetaPropExpr { span, kind: MetaPropKind::ImportMeta, })), prop: MemberProp::Ident(Ident::new(js_word!("main"), span)), }) } else { Expr::Lit(Lit::Bool(Bool { span, value: false })) }), }, ]) } } pub struct Loader { pub cm: Lrc<SourceMap>, } impl Load for Loader { fn load(&self, f: &FileName) -> Result<ModuleData, Error> { let fm = match f { FileName::Real(path) => self.cm.load_file(path)?, e => Err(anyhow::anyhow!("Invalid filename {}",e))? }; let module = parse_file_as_module( &fm, Syntax::Typescript(TsConfig { decorators: true, ..Default::default() }), EsVersion::Es2015, None, &mut vec![], ) .unwrap_or_else(|err| { let handler = create_handler(self.cm.clone()); err.into_diagnostic(&handler).emit(); panic!("failed to parse") }); Ok(ModuleData { fm, module, helpers: Default::default(), }) } }
An unreachable is reached, the fix is quite simple (5 lines), I'll push it and link the PR here. In bundler/export.rs line 168 should be:
bundler/export.rs
Decl::TsEnum(ref e) => &e.id, Decl::TsInterface(ref i) => &i.id, Decl::TsTypeAlias(ref a) => &a.id, _ => unreachable!("Decl in ExportDecl: {:?}", decl.decl),
Those two TS files and be used to test the bug:
export interface A { }
import { A } from './to_import';
This closed issue has been automatically locked because it had no new activity for a month. If you are running into a similar issue, please create a new issue with the steps to reproduce. Thank you.
When bundling TS, Using this code adapted from the GitHub examples:
An unreachable is reached, the fix is quite simple (5 lines), I'll push it and link the PR here. In
bundler/export.rs
line 168 should be:Those two TS files and be used to test the bug:
to_import.ts
Entry point