Skip to content

Commit

Permalink
Added magic comment visitor and wired it up to the transformer
Browse files Browse the repository at this point in the history
  • Loading branch information
alshdavid committed Nov 18, 2024
1 parent 66c8cd0 commit cf586d0
Show file tree
Hide file tree
Showing 7 changed files with 350 additions and 1 deletion.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use std::sync::Arc;

use atlaspack_core::diagnostic;
use indexmap::IndexMap;
use std::collections::HashMap;
use swc_core::atoms::{Atom, JsWord};

use atlaspack_core::plugin::{PluginOptions, TransformResult};
Expand Down Expand Up @@ -44,6 +45,7 @@ pub(crate) fn convert_result(
&options.project_root,
transformer_config,
result.dependencies,
result.magic_comments,
&asset,
)?;

Expand Down Expand Up @@ -381,6 +383,7 @@ pub(crate) fn convert_dependencies(
project_root: &Path,
transformer_config: &atlaspack_js_swc_core::Config,
dependencies: Vec<atlaspack_js_swc_core::DependencyDescriptor>,
magic_comments: HashMap<String, String>,
asset: &Asset,
) -> Result<(IndexMap<Atom, Dependency>, Vec<PathBuf>), Vec<Diagnostic>> {
let mut dependency_by_specifier = IndexMap::new();
Expand All @@ -400,7 +403,13 @@ pub(crate) fn convert_dependencies(
)?;

match result {
DependencyConversionResult::Dependency(dependency) => {
DependencyConversionResult::Dependency(mut dependency) => {
if let Some(chunk_name) = magic_comments.get(&dependency.specifier) {
dependency.meta.insert(
"chunkNameMagicComment".to_string(),
chunk_name.clone().into(),
);
}
dependency_by_specifier.insert(placeholder, dependency);
}
DependencyConversionResult::InvalidateOnFileChange(file_path) => {
Expand Down
1 change: 1 addition & 0 deletions packages/transformers/js/core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -47,5 +47,6 @@ atlaspack_contextual_imports = { path = "../../../../crates/atlaspack_contextual
atlaspack_swc_runner = { path = "../../../../crates/atlaspack_swc_runner" }
parking_lot = { workspace = true }
regex = { workspace = true }
anyhow = { workspace = true }
tracing = { workspace = true }
tracing-test = { workspace = true }
8 changes: 8 additions & 0 deletions packages/transformers/js/core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ mod env_replacer;
mod fs;
mod global_replacer;
mod hoist;
mod magic_comments;
mod modules;
mod node_replacer;
pub mod test_utils;
Expand Down Expand Up @@ -39,6 +40,7 @@ pub use hoist::ExportedSymbol;
use hoist::HoistResult;
pub use hoist::ImportedSymbol;
use indexmap::IndexMap;
use magic_comments::MagicCommentsVisitor;
use modules::esm2cjs;
use node_replacer::NodeReplacer;
use path_slash::PathExt;
Expand Down Expand Up @@ -152,6 +154,7 @@ pub struct TransformResult {
pub has_node_replacements: bool,
pub is_constant_module: bool,
pub conditions: HashSet<Condition>,
pub magic_comments: HashMap<String, String>,
}

fn targets_to_versions(targets: &Option<HashMap<String, String>>) -> Option<Versions> {
Expand Down Expand Up @@ -266,6 +269,11 @@ pub fn transform(

let global_mark = Mark::fresh(Mark::root());
let unresolved_mark = Mark::fresh(Mark::root());

let mut magic_comment_visitor = MagicCommentsVisitor::new(code);
module.visit_with(&mut magic_comment_visitor);
result.magic_comments = magic_comment_visitor.magic_comments;

let module = module.fold_with(&mut chain!(
resolver(unresolved_mark, global_mark, config.is_type_script),
// Decorators can use type information, so must run before the TypeScript pass.
Expand Down
5 changes: 5 additions & 0 deletions packages/transformers/js/core/src/magic_comments/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
mod visitor;
#[cfg(test)]
mod visitor_test;

pub use self::visitor::*;
72 changes: 72 additions & 0 deletions packages/transformers/js/core/src/magic_comments/visitor.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
use std::collections::HashMap;
use std::rc::Rc;

use regex::Regex;
use swc_core::common::Span;
use swc_core::ecma::ast::*;
use swc_core::ecma::utils::ExprExt;
use swc_core::ecma::visit::{Visit, VisitWith};

thread_local! {
static RE_CHUNK_NAME: Rc<Regex> = Rc::new(Regex::new(r#"webpackChunkName:\s*['"](?<name>[^'"]+)['"]"#).unwrap());
}

#[derive(Debug)]
pub struct MagicCommentsVisitor<'a> {
pub magic_comments: HashMap<String, String>,
pub offset: u32,
pub code: &'a str,
}

impl<'a> MagicCommentsVisitor<'a> {
pub fn new(code: &'a str) -> Self {
Self {
magic_comments: Default::default(),
offset: 0,
code,
}
}

fn fix_callee(&self, span: &Span) -> (u32, u32) {
let start = span.lo().0 - self.offset;
let end = span.hi().0 - self.offset;
(start, end)
}
}

impl<'a> Visit for MagicCommentsVisitor<'a> {
fn visit_module(&mut self, node: &Module) {
self.offset = node.span.lo().0;
node.visit_children_with(self);
}

fn visit_call_expr(&mut self, node: &CallExpr) {
if !node.callee.is_import() || node.args.len() == 0 || !node.args[0].expr.is_str() {
node.visit_children_with(self);
return;
}

let (code_start, code_end) = self.fix_callee(&node.span);
let code_start_index = (code_start - 1) as usize;
let code_end_index = (code_end - 1) as usize;
let inner = &self.code[code_start_index..code_end_index];

let re = RE_CHUNK_NAME.with(|re| re.clone());

let Some(caps) = re.captures(inner) else {
return;
};

let Some(found) = caps.name("name") else {
return;
};

let Expr::Lit(Lit::Str(specifier)) = &*node.args[0].expr else {
return;
};

self
.magic_comments
.insert(specifier.value.to_string(), found.as_str().to_string());
}
}
Loading

0 comments on commit cf586d0

Please sign in to comment.