Skip to content

Conversation

gaoachao
Copy link
Contributor

@gaoachao gaoachao commented Jul 31, 2025

This PR #10449 changed GlobalPassOption.build to pub(crate), but ReactLynx's WASM plugin depends on it (see https://github.com/lynx-family/lynx-stack/blob/main/packages/react/transform/src/lib.rs#L412).

When upgrading swc_core, we encountered a crash due to this change. This PR aims to restore GlobalPassOption.build to pub.

@gaoachao gaoachao requested a review from a team as a code owner July 31, 2025 12:47
Copy link

changeset-bot bot commented Jul 31, 2025

⚠️ No Changeset found

Latest commit: 278b1a8

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@CLAassistant
Copy link

CLAassistant commented Jul 31, 2025

CLA assistant check
All committers have signed the CLA.

@gaoachao gaoachao force-pushed the fix/expose-GlobalPassOption-build-method branch from 7d44c39 to 278b1a8 Compare July 31, 2025 12:49
Copy link

codspeed-hq bot commented Jul 31, 2025

CodSpeed Performance Report

Merging #10968 will not alter performance

Comparing gaoachao:fix/expose-GlobalPassOption-build-method (5f521d7) with main (862e70d)

Summary

✅ 140 untouched benchmarks

Copy link
Member

@kdy1 kdy1 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I intentionally hide this to reduce maintenance burden.
Is copying the source code of the function acceptable? It's a wrapper for a visitor creator function so I think it's trivial enough, and Wasm plugin should not depend on the swc crate

@gaoachao
Copy link
Contributor Author

gaoachao commented Aug 1, 2025

use dashmap::DashMap;
use once_cell::sync::Lazy;
use rustc_hash::{FxBuildHasher, FxHashMap};
use std::{collections::HashMap, env, sync::Arc};
use swc_core::{
  atoms::Atom,
  base::config::{GlobalInliningPassEnvs, GlobalPassOption},
  common::{errors::Handler, FileName, Mark, SourceMap},
  ecma::{
    ast::*,
    parser::{parse_file_as_expr, Syntax},
    transforms::optimization::{inline_globals, GlobalExprMap},
    utils::NodeIgnoringSpan,
  },
};

pub fn opts_build(
  global_pass_option: GlobalPassOption,
  cm: &SourceMap,
  handler: &Handler,
  unresolved_mark: Mark,
) -> impl 'static + Pass {
  type ValuesMap = Arc<FxHashMap<Atom, Expr>>;

  fn expr(cm: &SourceMap, handler: &Handler, src: String) -> Box<Expr> {
    let fm = cm.new_source_file(FileName::Anon.into(), src);

    let mut errors = Vec::new();
    let expr = parse_file_as_expr(
      &fm,
      Syntax::Es(Default::default()),
      Default::default(),
      None,
      &mut errors,
    );

    for e in errors {
      e.into_diagnostic(handler).emit()
    }

    match expr {
      Ok(v) => v,
      _ => panic!("{} is not a valid expression", fm.src),
    }
  }

  fn mk_map(
    cm: &SourceMap,
    handler: &Handler,
    values: impl Iterator<Item = (Atom, Atom)>,
    is_env: bool,
  ) -> ValuesMap {
    let mut m = HashMap::default();

    for (k, v) in values {
      let v = if is_env {
        format!("'{v}'")
      } else {
        (*v).into()
      };
      let v_str = v.clone();

      let e = expr(cm, handler, v_str);

      m.insert((*k).into(), *e);
    }

    Arc::new(m)
  }

  let env_map = if cfg!(target_arch = "wasm32") {
    Arc::new(Default::default())
  } else {
    match &global_pass_option.envs {
      GlobalInliningPassEnvs::List(env_list) => {
        static CACHE: Lazy<DashMap<Vec<String>, ValuesMap, FxBuildHasher>> =
          Lazy::new(Default::default);

        let cache_key = env_list.iter().cloned().collect::<Vec<_>>();
        if let Some(v) = CACHE.get(&cache_key).as_deref().cloned() {
          v
        } else {
          let map = mk_map(
            cm,
            handler,
            env::vars()
              .filter(|(k, _)| env_list.contains(k))
              .map(|(k, v)| (k.into(), v.into())),
            true,
          );
          CACHE.insert(cache_key, map.clone());
          map
        }
      }

      GlobalInliningPassEnvs::Map(map) => {
        static CACHE: Lazy<DashMap<Vec<(Atom, Atom)>, ValuesMap, FxBuildHasher>> =
          Lazy::new(Default::default);

        let cache_key = global_pass_option
          .vars
          .iter()
          .map(|(k, v)| (k.clone(), v.clone()))
          .collect::<Vec<_>>();
        if let Some(v) = CACHE.get(&cache_key) {
          (*v).clone()
        } else {
          let map = mk_map(
            cm,
            handler,
            map.iter().map(|(k, v)| (k.clone(), v.clone())),
            false,
          );
          CACHE.insert(cache_key, map.clone());
          map
        }
      }
    }
  };

  let global_exprs = {
    static CACHE: Lazy<DashMap<Vec<(Atom, Atom)>, GlobalExprMap, FxBuildHasher>> =
      Lazy::new(Default::default);

    let cache_key = global_pass_option
      .vars
      .iter()
      .filter(|(k, _)| k.contains('.'))
      .map(|(k, v)| (k.clone(), v.clone()))
      .collect::<Vec<_>>();

    if let Some(v) = CACHE.get(&cache_key) {
      (*v).clone()
    } else {
      let map = global_pass_option
        .vars
        .iter()
        .filter(|(k, _)| k.contains('.'))
        .map(|(k, v)| {
          (
            NodeIgnoringSpan::owned(*expr(cm, handler, k.to_string())),
            *expr(cm, handler, v.to_string()),
          )
        })
        .collect::<FxHashMap<_, _>>();
      let map = Arc::new(map);
      CACHE.insert(cache_key, map.clone());
      map
    }
  };

  let global_map = {
    static CACHE: Lazy<DashMap<Vec<(Atom, Atom)>, ValuesMap, FxBuildHasher>> =
      Lazy::new(Default::default);

    let cache_key = global_pass_option
      .vars
      .iter()
      .filter(|(k, _)| !k.contains('.'))
      .map(|(k, v)| (k.clone(), v.clone()))
      .collect::<Vec<_>>();
    if let Some(v) = CACHE.get(&cache_key) {
      (*v).clone()
    } else {
      let map = mk_map(
        cm,
        handler,
        global_pass_option
          .vars
          .into_iter()
          .filter(|(k, _)| !k.contains('.')),
        false,
      );
      CACHE.insert(cache_key, map.clone());
      map
    }
  };

  inline_globals(
    unresolved_mark,
    env_map,
    global_map,
    global_exprs,
    Arc::new(global_pass_option.typeofs),
  )
}

I attempted to copy the source code into our codebase and implement it as shown below, but it's not working as expected. Could you help me find a better implementation approach, or simply expose GlobalPassOption.build as public?

  let define_dce_plugin = {
    let opts = GlobalPassOption {
      vars: match &options.define_dce {
        Either::A(_) => Default::default(),
        Either::B(config) => {
          let mut map = indexmap::IndexMap::<_, _, FxBuildHasher>::default();
          for (key, value) in &config.define {
            map.insert(key.as_str().into(), value.as_str().into());
          }
          map
        }
      },
      envs: Default::default(),
      typeofs: Default::default(),
    };

    HANDLER.with(|handler| {
      Optional::new(
        opts_build(opts, &cm, handler, unresolved_mark),
        matches!(options.define_dce, Either::B(_)),
      )
    })
  };

@gaoachao gaoachao requested a review from kdy1 August 1, 2025 07:35
@kdy1 kdy1 requested a review from a team as a code owner August 2, 2025 15:32
@kdy1 kdy1 merged commit c8a2668 into swc-project:main Aug 2, 2025
35 checks passed
@gaoachao gaoachao deleted the fix/expose-GlobalPassOption-build-method branch August 2, 2025 15:58
@kdy1 kdy1 modified the milestones: Planned, 1.13.4 Aug 20, 2025
@swc-project swc-project locked as resolved and limited conversation to collaborators Sep 20, 2025
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Development

Successfully merging this pull request may close these issues.

3 participants