1#![allow(dead_code)]
2
3use anyhow::{Context, Error};
4use is_macro::Is;
5#[cfg(feature = "rayon")]
6use rayon::iter::ParallelIterator;
7use swc_atoms::atom;
8use swc_common::{
9 sync::{Lock, Lrc},
10 FileName, SourceFile, SyntaxContext,
11};
12use swc_ecma_ast::{
13 CallExpr, Callee, Expr, Ident, ImportDecl, ImportSpecifier, MemberExpr, MemberProp, Module,
14 ModuleDecl, ModuleExportName, Str, SuperProp, SuperPropExpr,
15};
16use swc_ecma_transforms_base::resolver;
17use swc_ecma_visit::{
18 noop_visit_mut_type, noop_visit_type, Visit, VisitMut, VisitMutWith, VisitWith,
19};
20
21use super::{export::Exports, helpers::Helpers, Bundler};
22use crate::{
23 bundler::{export::RawExports, import::RawImports},
24 id::{Id, ModuleId},
25 load::ModuleData,
26 util,
27 util::IntoParallelIterator,
28 Load, Resolve,
29};
30#[derive(Debug, Clone)]
32pub(crate) struct TransformedModule {
33 pub id: ModuleId,
34 pub fm: Lrc<SourceFile>,
35 pub module: Lrc<Module>,
36 pub imports: Lrc<Imports>,
37 pub exports: Lrc<Exports>,
38
39 pub is_es6: bool,
41
42 pub helpers: Lrc<Helpers>,
44
45 pub swc_helpers: Lrc<Lock<swc_ecma_transforms_base::helpers::HelperData>>,
46
47 local_ctxt: SyntaxContext,
48 export_ctxt: SyntaxContext,
49}
50
51impl TransformedModule {
52 pub fn export_ctxt(&self) -> SyntaxContext {
54 self.export_ctxt
55 }
56
57 pub fn local_ctxt(&self) -> SyntaxContext {
59 self.local_ctxt
60 }
61}
62
63impl<L, R> Bundler<'_, L, R>
64where
65 L: Load,
66 R: Resolve,
67{
68 pub(super) fn load_transformed(
73 &self,
74 file_name: &FileName,
75 ) -> Result<Option<TransformedModule>, Error> {
76 self.run(|| {
77 tracing::trace!("load_transformed: ({})", file_name);
78
79 if let Some(cached) = self.scope.get_module_by_path(file_name) {
81 tracing::debug!("Cached: {}", file_name);
82 return Ok(Some(cached));
83 }
84
85 let (_, data) = self.load(file_name).context("Bundler.load() failed")?;
86 let (v, mut files) = self
87 .analyze(file_name, data)
88 .context("failed to analyze module")?;
89 files.dedup_by_key(|v| v.1.clone());
90
91 tracing::debug!(
92 "({:?}, {:?}, {:?}) Storing module: {}",
93 v.id,
94 v.local_ctxt(),
95 v.export_ctxt(),
96 file_name
97 );
98 self.scope.store_module(v.clone());
99
100 let results = files
102 .into_par_iter()
103 .map(|(_src, path)| {
104 tracing::trace!("loading dependency: {}", path);
105 self.load_transformed(&path)
106 })
107 .collect::<Vec<_>>();
108
109 for result in results {
111 result?;
112 }
113
114 Ok(Some(v))
115 })
116 }
117
118 fn load(&self, file_name: &FileName) -> Result<(ModuleId, ModuleData), Error> {
119 self.run(|| {
120 let (module_id, _, _) = self.scope.module_id_gen.gen(file_name);
121
122 let data = self
123 .loader
124 .load(file_name)
125 .with_context(|| format!("Bundler.loader.load({file_name}) failed"))?;
126 self.scope.mark_as_loaded(module_id);
127 Ok((module_id, data))
128 })
129 }
130
131 fn analyze(
133 &self,
134 file_name: &FileName,
135 mut data: ModuleData,
136 ) -> Result<(TransformedModule, Vec<(Source, Lrc<FileName>)>), Error> {
137 self.run(|| {
138 tracing::trace!("transform_module({})", data.fm.name);
139 let (id, local_mark, export_mark) = self.scope.module_id_gen.gen(file_name);
140
141 data.module.visit_mut_with(&mut ClearMark);
142
143 data.module
144 .visit_mut_with(&mut resolver(self.unresolved_mark, local_mark, false));
145
146 let imports = self.extract_import_info(file_name, &mut data.module, local_mark);
162
163 let exports = self.extract_export_info(
179 file_name,
180 &mut data.module,
181 SyntaxContext::empty().apply_mark(export_mark),
182 );
183
184 let is_es6 = if !self.config.require {
185 true
186 } else {
187 let mut v = Es6ModuleDetector {
188 forced_es6: false,
189 found_other: false,
190 };
191 data.module.visit_with(&mut v);
192 v.forced_es6 || !v.found_other
193 };
194
195 let (imports, exports) = util::join(
196 || self.resolve_imports(file_name, imports),
197 || self.resolve_exports(file_name, exports),
198 );
199 let (imports, mut import_files) = imports?;
200 let (exports, reexport_files) = exports?;
201 import_files.extend(reexport_files);
202
203 Ok((
204 TransformedModule {
205 id,
206 fm: data.fm,
207 module: Lrc::new(data.module),
208 imports: Lrc::new(imports),
209 exports: Lrc::new(exports),
210 is_es6,
211 helpers: Default::default(),
212 swc_helpers: Lrc::new(Lock::new(data.helpers.data())),
213 local_ctxt: SyntaxContext::empty().apply_mark(local_mark),
214 export_ctxt: SyntaxContext::empty().apply_mark(export_mark),
215 },
216 import_files,
217 ))
218 })
219 }
220
221 fn resolve_exports(
223 &self,
224 base: &FileName,
225 raw: RawExports,
226 ) -> Result<(Exports, Vec<(Source, Lrc<FileName>)>), Error> {
227 self.run(|| {
228 tracing::trace!("resolve_exports({})", base);
229 let mut files = Vec::new();
230
231 let mut exports = Exports::default();
232
233 let items = raw
234 .items
235 .into_par_iter()
236 .map(|(src, ss)| -> Result<_, Error> {
237 self.run(|| {
238 let info = match src {
239 Some(src) => {
240 let name = self.resolve(base, &src.value.to_string_lossy())?;
241 let (id, local_mark, export_mark) =
242 self.scope.module_id_gen.gen(&name);
243 Some((id, local_mark, export_mark, name, src))
244 }
245 None => None,
246 };
247
248 Ok((info, ss))
249 })
250 })
251 .collect::<Vec<_>>();
252
253 for res in items {
254 let (info, specifiers) = res?;
255
256 match info {
257 None => exports.items.extend(specifiers),
258 Some((id, local_mark, export_mark, name, src)) => {
259 let src = Source {
261 is_loaded_synchronously: true,
262 is_unconditional: false,
263 module_id: id,
264 local_ctxt: SyntaxContext::empty().apply_mark(local_mark),
265 export_ctxt: SyntaxContext::empty().apply_mark(export_mark),
266 src,
267 };
268 exports.reexports.push((src.clone(), specifiers));
269 files.push((src, name));
270 }
271 }
272 }
273
274 Ok((exports, files))
275 })
276 }
277
278 fn resolve_imports(
280 &self,
281 base: &FileName,
282 info: RawImports,
283 ) -> Result<(Imports, Vec<(Source, Lrc<FileName>)>), Error> {
284 self.run(|| {
285 tracing::trace!("resolve_imports({})", base);
286 let mut files = Vec::new();
287
288 let mut merged = Imports::default();
289 let RawImports {
290 imports,
291 lazy_imports,
292 dynamic_imports,
293 forced_ns,
294 } = info;
295
296 let loaded = imports
297 .into_par_iter()
298 .map(|v| (v, false, true))
299 .chain(lazy_imports.into_par_iter().map(|v| (v, false, false)))
300 .chain(dynamic_imports.into_par_iter().map(|src| {
301 (
302 ImportDecl {
303 span: src.span,
304 specifiers: Vec::new(),
305 src: Box::new(src),
306 type_only: false,
307 with: None,
308 phase: Default::default(),
309 },
310 true,
311 false,
312 )
313 }))
314 .map(|(decl, dynamic, unconditional)| -> Result<_, Error> {
315 self.run(|| {
316 let file_name = self.resolve(base, &decl.src.value.to_string_lossy())?;
318 let (id, local_mark, export_mark) =
319 self.scope.module_id_gen.gen(&file_name);
320
321 Ok((
322 id,
323 local_mark,
324 export_mark,
325 file_name,
326 decl,
327 dynamic,
328 unconditional,
329 ))
330 })
331 })
332 .collect::<Vec<_>>();
333
334 for res in loaded {
335 let (id, local_mark, export_mark, file_name, decl, is_dynamic, is_unconditional) =
337 res?;
338
339 let src = Source {
340 is_loaded_synchronously: !is_dynamic,
341 is_unconditional,
342 module_id: id,
343 local_ctxt: SyntaxContext::empty().apply_mark(local_mark),
344 export_ctxt: SyntaxContext::empty().apply_mark(export_mark),
345 src: *decl.src,
346 };
347 files.push((src.clone(), file_name));
348
349 let mut specifiers = Vec::new();
351 for s in decl.specifiers {
352 match s {
353 ImportSpecifier::Named(s) => {
354 let imported = match s.imported {
355 Some(ModuleExportName::Ident(ident)) => Some(ident),
356 Some(ModuleExportName::Str(..)) => {
357 unimplemented!("module string names unimplemented")
358 }
359 _ => None,
360 };
361 specifiers.push(Specifier::Specific {
362 local: s.local.into(),
363 alias: imported.map(From::from),
364 })
365 }
366 ImportSpecifier::Default(s) => specifiers.push(Specifier::Specific {
367 local: s.local.into(),
368 alias: Some(Id::new(atom!("default"), SyntaxContext::empty())),
369 }),
370 ImportSpecifier::Namespace(s) => {
371 specifiers.push(Specifier::Namespace {
372 local: s.local.into(),
373 all: forced_ns.contains(&src.src.value),
374 });
375 }
376 #[cfg(swc_ast_unknown)]
377 _ => panic!("unable to access unknown nodes"),
378 }
379 }
380
381 merged.specifiers.push((src, specifiers));
382 }
383
384 Ok((merged, files))
385 })
386 }
387}
388
389#[derive(Debug, Default)]
390pub(crate) struct Imports {
391 pub specifiers: Vec<(Source, Vec<Specifier>)>,
393}
394
395#[derive(Debug, Clone, Is)]
397pub(crate) enum Specifier {
398 Specific {
399 local: Id,
400 alias: Option<Id>,
401 },
402 Namespace {
403 local: Id,
404 all: bool,
406 },
407}
408
409#[derive(Debug, Clone, PartialEq, Eq, Hash)]
410pub(crate) struct Source {
411 pub is_loaded_synchronously: bool,
412 pub is_unconditional: bool,
413
414 pub module_id: ModuleId,
415 pub local_ctxt: SyntaxContext,
416 pub export_ctxt: SyntaxContext,
417
418 pub src: Str,
420}
421
422struct Es6ModuleDetector {
423 forced_es6: bool,
426 found_other: bool,
428}
429
430impl Visit for Es6ModuleDetector {
431 noop_visit_type!();
432
433 fn visit_call_expr(&mut self, e: &CallExpr) {
434 e.visit_children_with(self);
435
436 match &e.callee {
437 Callee::Expr(e) => {
438 if let Expr::Ident(Ident { sym: _require, .. }) = &**e {
439 self.found_other = true;
440 }
441 }
442 Callee::Super(_) | Callee::Import(_) => {}
443 #[cfg(swc_ast_unknown)]
444 _ => panic!("unable to access unknown nodes"),
445 }
446 }
447
448 fn visit_member_expr(&mut self, e: &MemberExpr) {
449 e.obj.visit_with(self);
450
451 if let MemberProp::Computed(c) = &e.prop {
452 c.visit_with(self);
453 }
454
455 if let Expr::Ident(i) = &*e.obj {
456 if i.sym == *"module" {
458 self.found_other = true;
459 }
460
461 if i.sym == *"exports" {
462 self.found_other = true;
463 }
464 }
465 }
466
467 fn visit_super_prop_expr(&mut self, e: &SuperPropExpr) {
468 if let SuperProp::Computed(c) = &e.prop {
469 c.visit_with(self);
470 }
471 }
472
473 fn visit_module_decl(&mut self, decl: &ModuleDecl) {
474 match decl {
475 ModuleDecl::Import(_)
476 | ModuleDecl::ExportDecl(_)
477 | ModuleDecl::ExportNamed(_)
478 | ModuleDecl::ExportDefaultDecl(_)
479 | ModuleDecl::ExportDefaultExpr(_)
480 | ModuleDecl::ExportAll(_) => {
481 self.forced_es6 = true;
482 }
483
484 ModuleDecl::TsImportEquals(_) => {}
485 ModuleDecl::TsExportAssignment(_) => {}
486 ModuleDecl::TsNamespaceExport(_) => {}
487 #[cfg(swc_ast_unknown)]
488 _ => panic!("unable to access unknown nodes"),
489 }
490 }
491}
492
493#[derive(Clone, Copy)]
494struct ClearMark;
495impl VisitMut for ClearMark {
496 noop_visit_mut_type!(fail);
497
498 fn visit_mut_ident(&mut self, ident: &mut Ident) {
499 ident.ctxt = SyntaxContext::empty();
500 }
501}