mirror of
https://github.com/swc-project/swc.git
synced 2024-12-22 21:21:31 +03:00
feat(es/transforms/optimization): Improve inline_globals
(#2479)
swc_ecma_transforms_optimization: - `inline_globals`: Support inlining into shorthand properties. - `inline_globals`: Support replacing member expressions. swc: - Add an option to disable simplifier when using `inline_globals`. node_swc: - Improve error message on panic.
This commit is contained in:
parent
9b96885171
commit
b0361caa58
6
Cargo.lock
generated
6
Cargo.lock
generated
@ -883,6 +883,7 @@ dependencies = [
|
||||
"autocfg 1.0.1",
|
||||
"hashbrown",
|
||||
"rayon",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2324,13 +2325,14 @@ checksum = "6446ced80d6c486436db5c078dde11a9f73d42b57fb273121e160b84f63d894c"
|
||||
|
||||
[[package]]
|
||||
name = "swc"
|
||||
version = "0.75.0"
|
||||
version = "0.76.0"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"anyhow",
|
||||
"base64 0.13.0",
|
||||
"dashmap",
|
||||
"either",
|
||||
"indexmap",
|
||||
"lru",
|
||||
"once_cell",
|
||||
"pathdiff",
|
||||
@ -2878,7 +2880,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "swc_ecma_transforms_optimization"
|
||||
version = "0.58.0"
|
||||
version = "0.58.1"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"dashmap",
|
||||
|
@ -21,7 +21,7 @@ include = ["Cargo.toml", "src/**/*.rs"]
|
||||
license = "Apache-2.0/MIT"
|
||||
name = "swc"
|
||||
repository = "https://github.com/swc-project/swc.git"
|
||||
version = "0.75.0"
|
||||
version = "0.76.0"
|
||||
|
||||
[lib]
|
||||
name = "swc"
|
||||
@ -51,6 +51,7 @@ anyhow = "1"
|
||||
base64 = "0.13.0"
|
||||
dashmap = "4.0.2"
|
||||
either = "1"
|
||||
indexmap = {version = "1", features = ["serde"]}
|
||||
lru = "0.6.1"
|
||||
once_cell = "1"
|
||||
pathdiff = "0.2.0"
|
||||
|
@ -47,6 +47,7 @@
|
||||
"elems",
|
||||
"esbuild",
|
||||
"esms",
|
||||
"eval",
|
||||
"Eval",
|
||||
"exponentation",
|
||||
"fargs",
|
||||
|
@ -6,7 +6,7 @@ edition = "2018"
|
||||
license = "Apache-2.0/MIT"
|
||||
name = "swc_ecma_transforms_optimization"
|
||||
repository = "https://github.com/swc-project/swc.git"
|
||||
version = "0.58.0"
|
||||
version = "0.58.1"
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[features]
|
||||
|
@ -2,21 +2,44 @@ use swc_atoms::{js_word, JsWord};
|
||||
use swc_common::{
|
||||
collections::{AHashMap, AHashSet},
|
||||
sync::Lrc,
|
||||
EqIgnoreSpan,
|
||||
};
|
||||
use swc_ecma_ast::*;
|
||||
use swc_ecma_transforms_base::perf::Parallel;
|
||||
use swc_ecma_transforms_macros::parallel;
|
||||
use swc_ecma_utils::{collect_decls, Id};
|
||||
use swc_ecma_utils::{collect_decls, ident::IdentLike, Id};
|
||||
use swc_ecma_visit::{as_folder, noop_visit_mut_type, Fold, VisitMut, VisitMutWith};
|
||||
|
||||
/// The key will be compared using [EqIgnoreSpan::eq_ignore_span], and matched
|
||||
/// expressions will be replaced with the value.
|
||||
pub type GlobalExprMap = Lrc<Vec<(Expr, Expr)>>;
|
||||
|
||||
/// Create a global inlining pass, which replaces expressions with the specified
|
||||
/// value.
|
||||
pub fn inline_globals(
|
||||
envs: Lrc<AHashMap<JsWord, Expr>>,
|
||||
globals: Lrc<AHashMap<JsWord, Expr>>,
|
||||
typeofs: Lrc<AHashMap<JsWord, JsWord>>,
|
||||
) -> impl Fold + VisitMut {
|
||||
inline_globals2(envs, globals, Default::default(), typeofs)
|
||||
}
|
||||
|
||||
/// Create a global inlining pass, which replaces expressions with the specified
|
||||
/// value.
|
||||
///
|
||||
/// See [GlobalExprMap] for description.
|
||||
///
|
||||
/// Note: Values specified in `global_exprs` have higher precedence than
|
||||
pub fn inline_globals2(
|
||||
envs: Lrc<AHashMap<JsWord, Expr>>,
|
||||
globals: Lrc<AHashMap<JsWord, Expr>>,
|
||||
global_exprs: GlobalExprMap,
|
||||
typeofs: Lrc<AHashMap<JsWord, JsWord>>,
|
||||
) -> impl Fold + VisitMut {
|
||||
as_folder(InlineGlobals {
|
||||
envs,
|
||||
globals,
|
||||
global_exprs,
|
||||
typeofs,
|
||||
bindings: Default::default(),
|
||||
})
|
||||
@ -26,6 +49,8 @@ pub fn inline_globals(
|
||||
struct InlineGlobals {
|
||||
envs: Lrc<AHashMap<JsWord, Expr>>,
|
||||
globals: Lrc<AHashMap<JsWord, Expr>>,
|
||||
global_exprs: GlobalExprMap,
|
||||
|
||||
typeofs: Lrc<AHashMap<JsWord, JsWord>>,
|
||||
|
||||
bindings: Lrc<AHashSet<Id>>,
|
||||
@ -44,14 +69,28 @@ impl VisitMut for InlineGlobals {
|
||||
noop_visit_mut_type!();
|
||||
|
||||
fn visit_mut_expr(&mut self, expr: &mut Expr) {
|
||||
expr.visit_mut_children_with(self);
|
||||
|
||||
match expr {
|
||||
Expr::Ident(Ident { ref sym, span, .. }) => {
|
||||
if self.bindings.contains(&(sym.clone(), span.ctxt)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
_ => {}
|
||||
}
|
||||
|
||||
for (key, value) in self.global_exprs.iter() {
|
||||
if key.eq_ignore_span(&*expr) {
|
||||
*expr = value.clone();
|
||||
expr.visit_mut_with(self);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
expr.visit_mut_children_with(self);
|
||||
|
||||
match expr {
|
||||
Expr::Ident(Ident { ref sym, .. }) => {
|
||||
// It's ok because we don't recurse into member expressions.
|
||||
if let Some(value) = self.globals.get(sym) {
|
||||
let mut value = value.clone();
|
||||
@ -152,6 +191,30 @@ impl VisitMut for InlineGlobals {
|
||||
module.visit_mut_children_with(self);
|
||||
}
|
||||
|
||||
fn visit_mut_prop(&mut self, p: &mut Prop) {
|
||||
p.visit_mut_children_with(self);
|
||||
|
||||
match p {
|
||||
Prop::Shorthand(i) => {
|
||||
if self.bindings.contains(&i.to_id()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// It's ok because we don't recurse into member expressions.
|
||||
if let Some(mut value) = self.globals.get(&i.sym).cloned().map(Box::new) {
|
||||
value.visit_mut_with(self);
|
||||
*p = Prop::KeyValue(KeyValueProp {
|
||||
key: PropName::Ident(i.clone()),
|
||||
value,
|
||||
});
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_mut_script(&mut self, script: &mut Script) {
|
||||
self.bindings = Lrc::new(collect_decls(&*script));
|
||||
|
||||
@ -212,12 +275,7 @@ mod tests {
|
||||
|
||||
test!(
|
||||
::swc_ecma_parser::Syntax::default(),
|
||||
|tester| as_folder(InlineGlobals {
|
||||
envs: envs(tester, &[]),
|
||||
globals: globals(tester, &[]),
|
||||
typeofs: Default::default(),
|
||||
bindings: Default::default()
|
||||
}),
|
||||
|tester| inline_globals(envs(tester, &[]), globals(tester, &[]), Default::default(),),
|
||||
issue_215,
|
||||
r#"if (process.env.x === 'development') {}"#,
|
||||
r#"if (process.env.x === 'development') {}"#
|
||||
@ -225,12 +283,11 @@ mod tests {
|
||||
|
||||
test!(
|
||||
::swc_ecma_parser::Syntax::default(),
|
||||
|tester| as_folder(InlineGlobals {
|
||||
envs: envs(tester, &[("NODE_ENV", "development")]),
|
||||
globals: globals(tester, &[]),
|
||||
typeofs: Default::default(),
|
||||
bindings: Default::default()
|
||||
}),
|
||||
|tester| inline_globals(
|
||||
envs(tester, &[("NODE_ENV", "development")]),
|
||||
globals(tester, &[]),
|
||||
Default::default(),
|
||||
),
|
||||
node_env,
|
||||
r#"if (process.env.NODE_ENV === 'development') {}"#,
|
||||
r#"if ('development' === 'development') {}"#
|
||||
@ -238,25 +295,23 @@ mod tests {
|
||||
|
||||
test!(
|
||||
::swc_ecma_parser::Syntax::default(),
|
||||
|tester| as_folder(InlineGlobals {
|
||||
envs: envs(tester, &[]),
|
||||
globals: globals(tester, &[("__DEBUG__", "true")]),
|
||||
typeofs: Default::default(),
|
||||
bindings: Default::default()
|
||||
}),
|
||||
inline_globals,
|
||||
|tester| inline_globals(
|
||||
envs(tester, &[]),
|
||||
globals(tester, &[("__DEBUG__", "true")]),
|
||||
Default::default(),
|
||||
),
|
||||
globals_simple,
|
||||
r#"if (__DEBUG__) {}"#,
|
||||
r#"if (true) {}"#
|
||||
);
|
||||
|
||||
test!(
|
||||
::swc_ecma_parser::Syntax::default(),
|
||||
|tester| as_folder(InlineGlobals {
|
||||
envs: envs(tester, &[]),
|
||||
globals: globals(tester, &[("debug", "true")]),
|
||||
typeofs: Default::default(),
|
||||
bindings: Default::default()
|
||||
}),
|
||||
|tester| inline_globals(
|
||||
envs(tester, &[]),
|
||||
globals(tester, &[("debug", "true")]),
|
||||
Default::default(),
|
||||
),
|
||||
non_global,
|
||||
r#"if (foo.debug) {}"#,
|
||||
r#"if (foo.debug) {}"#
|
||||
@ -264,12 +319,7 @@ mod tests {
|
||||
|
||||
test!(
|
||||
Default::default(),
|
||||
|tester| as_folder(InlineGlobals {
|
||||
envs: envs(tester, &[]),
|
||||
globals: globals(tester, &[]),
|
||||
typeofs: Default::default(),
|
||||
bindings: Default::default()
|
||||
}),
|
||||
|tester| inline_globals(envs(tester, &[]), globals(tester, &[]), Default::default(),),
|
||||
issue_417_1,
|
||||
"const test = process.env['x']",
|
||||
"const test = process.env['x']"
|
||||
@ -277,12 +327,11 @@ mod tests {
|
||||
|
||||
test!(
|
||||
Default::default(),
|
||||
|tester| as_folder(InlineGlobals {
|
||||
envs: envs(tester, &[("x", "FOO")]),
|
||||
globals: globals(tester, &[]),
|
||||
typeofs: Default::default(),
|
||||
bindings: Default::default()
|
||||
}),
|
||||
|tester| inline_globals(
|
||||
envs(tester, &[("x", "FOO")]),
|
||||
globals(tester, &[]),
|
||||
Default::default(),
|
||||
),
|
||||
issue_417_2,
|
||||
"const test = process.env['x']",
|
||||
"const test = 'FOO'"
|
||||
|
@ -1,5 +1,7 @@
|
||||
pub use self::{
|
||||
const_modules::const_modules, inline_globals::inline_globals, json_parse::json_parse,
|
||||
const_modules::const_modules,
|
||||
inline_globals::{inline_globals, inline_globals2, GlobalExprMap},
|
||||
json_parse::json_parse,
|
||||
simplify::simplifier,
|
||||
};
|
||||
|
||||
|
@ -1,11 +1,11 @@
|
||||
use crate::{
|
||||
complete_output, get_compiler,
|
||||
util::{CtxtExt, MapErr},
|
||||
util::{try_with, CtxtExt, MapErr},
|
||||
};
|
||||
use napi::{CallContext, JsObject, Task};
|
||||
use serde::Deserialize;
|
||||
use std::sync::Arc;
|
||||
use swc::{try_with_handler, TransformOutput};
|
||||
use swc::TransformOutput;
|
||||
use swc_common::{collections::AHashMap, sync::Lrc, FileName, SourceFile, SourceMap};
|
||||
|
||||
struct MinifyTask {
|
||||
@ -48,7 +48,7 @@ impl Task for MinifyTask {
|
||||
type JsValue = JsObject;
|
||||
|
||||
fn compute(&mut self) -> napi::Result<Self::Output> {
|
||||
try_with_handler(self.c.cm.clone(), |handler| {
|
||||
try_with(self.c.cm.clone(), |handler| {
|
||||
let fm = self.code.to_file(self.c.cm.clone());
|
||||
|
||||
self.c.minify(fm, &handler, &self.opts)
|
||||
@ -82,8 +82,7 @@ pub fn minify_sync(cx: CallContext) -> napi::Result<JsObject> {
|
||||
|
||||
let fm = code.to_file(c.cm.clone());
|
||||
|
||||
let output =
|
||||
try_with_handler(c.cm.clone(), |handler| c.minify(fm, &handler, &opts)).convert_err()?;
|
||||
let output = try_with(c.cm.clone(), |handler| c.minify(fm, &handler, &opts)).convert_err()?;
|
||||
|
||||
complete_output(&cx.env, output)
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::{
|
||||
get_compiler,
|
||||
util::{CtxtExt, MapErr},
|
||||
util::{try_with, CtxtExt, MapErr},
|
||||
};
|
||||
use anyhow::Context as _;
|
||||
use napi::{CallContext, Either, Env, JsObject, JsString, JsUndefined, Task};
|
||||
@ -8,7 +8,7 @@ use std::{
|
||||
path::{Path, PathBuf},
|
||||
sync::Arc,
|
||||
};
|
||||
use swc::{config::ParseOptions, try_with_handler, Compiler};
|
||||
use swc::{config::ParseOptions, Compiler};
|
||||
use swc_common::{FileName, SourceFile};
|
||||
use swc_ecma_ast::Program;
|
||||
|
||||
@ -38,7 +38,7 @@ impl Task for ParseTask {
|
||||
type JsValue = JsString;
|
||||
|
||||
fn compute(&mut self) -> napi::Result<Self::Output> {
|
||||
let program = try_with_handler(self.c.cm.clone(), |handler| {
|
||||
let program = try_with(self.c.cm.clone(), |handler| {
|
||||
self.c.parse_js(
|
||||
self.fm.clone(),
|
||||
&handler,
|
||||
@ -63,7 +63,7 @@ impl Task for ParseFileTask {
|
||||
type JsValue = JsString;
|
||||
|
||||
fn compute(&mut self) -> napi::Result<Self::Output> {
|
||||
try_with_handler(self.c.cm.clone(), |handler| {
|
||||
try_with(self.c.cm.clone(), |handler| {
|
||||
self.c.run(|| {
|
||||
let fm = self
|
||||
.c
|
||||
@ -125,7 +125,7 @@ pub fn parse_sync(cx: CallContext) -> napi::Result<JsString> {
|
||||
FileName::Anon
|
||||
};
|
||||
|
||||
let program = try_with_handler(c.cm.clone(), |handler| {
|
||||
let program = try_with(c.cm.clone(), |handler| {
|
||||
c.run(|| {
|
||||
let fm = c.cm.new_source_file(filename, src);
|
||||
c.parse_js(
|
||||
@ -150,7 +150,7 @@ pub fn parse_file_sync(cx: CallContext) -> napi::Result<JsString> {
|
||||
let options: ParseOptions = cx.get_deserialized(1)?;
|
||||
|
||||
let program = {
|
||||
try_with_handler(c.cm.clone(), |handler| {
|
||||
try_with(c.cm.clone(), |handler| {
|
||||
let fm =
|
||||
c.cm.load_file(Path::new(path.as_str()?))
|
||||
.expect("failed to read program file");
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::{
|
||||
complete_output, get_compiler,
|
||||
util::{deserialize_json, CtxtExt, MapErr},
|
||||
util::{deserialize_json, try_with, CtxtExt, MapErr},
|
||||
};
|
||||
use anyhow::{Context as _, Error};
|
||||
use napi::{CallContext, Env, JsBoolean, JsObject, JsString, Task};
|
||||
@ -9,7 +9,7 @@ use std::{
|
||||
path::{Path, PathBuf},
|
||||
sync::Arc,
|
||||
};
|
||||
use swc::{config::Options, try_with_handler, Compiler, TransformOutput};
|
||||
use swc::{config::Options, Compiler, TransformOutput};
|
||||
use swc_common::{FileName, SourceFile};
|
||||
use swc_ecma_ast::Program;
|
||||
|
||||
@ -35,7 +35,7 @@ impl Task for TransformTask {
|
||||
type JsValue = JsObject;
|
||||
|
||||
fn compute(&mut self) -> napi::Result<Self::Output> {
|
||||
try_with_handler(self.c.cm.clone(), |handler| {
|
||||
try_with(self.c.cm.clone(), |handler| {
|
||||
self.c.run(|| match self.input {
|
||||
Input::Program(ref s) => {
|
||||
let program: Program =
|
||||
@ -93,7 +93,7 @@ where
|
||||
options.config.adjust(Path::new(&options.filename));
|
||||
}
|
||||
|
||||
let output = try_with_handler(c.cm.clone(), |handler| {
|
||||
let output = try_with(c.cm.clone(), |handler| {
|
||||
c.run(|| {
|
||||
if is_module.get_value()? {
|
||||
let program: Program =
|
||||
|
@ -1,7 +1,35 @@
|
||||
use anyhow::{Context, Error};
|
||||
use anyhow::{anyhow, Context, Error};
|
||||
use napi::{CallContext, JsBuffer, Status};
|
||||
use serde::de::DeserializeOwned;
|
||||
use std::any::type_name;
|
||||
use std::{
|
||||
any::type_name,
|
||||
panic::{catch_unwind, AssertUnwindSafe},
|
||||
};
|
||||
use swc::try_with_handler;
|
||||
use swc_common::{errors::Handler, sync::Lrc, SourceMap};
|
||||
|
||||
pub fn try_with<F, Ret>(cm: Lrc<SourceMap>, op: F) -> Result<Ret, Error>
|
||||
where
|
||||
F: FnOnce(&Handler) -> Result<Ret, Error>,
|
||||
{
|
||||
try_with_handler(cm, |handler| {
|
||||
//
|
||||
let result = catch_unwind(AssertUnwindSafe(|| op(handler)));
|
||||
|
||||
let p = match result {
|
||||
Ok(v) => return v,
|
||||
Err(v) => v,
|
||||
};
|
||||
|
||||
if let Some(s) = p.downcast_ref::<String>() {
|
||||
Err(anyhow!("failed to handle: {}", s))
|
||||
} else if let Some(s) = p.downcast_ref::<&str>() {
|
||||
Err(anyhow!("failed to handle: {}", s))
|
||||
} else {
|
||||
Err(anyhow!("failed to handle with unknown panic message"))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub trait MapErr<T>: Into<Result<T, anyhow::Error>> {
|
||||
fn convert_err(self) -> napi::Result<T> {
|
||||
|
@ -3,6 +3,7 @@ use crate::{builder::PassBuilder, SwcComments, SwcImportResolver};
|
||||
use anyhow::{bail, Context, Error};
|
||||
use dashmap::DashMap;
|
||||
use either::Either;
|
||||
use indexmap::IndexMap;
|
||||
use once_cell::sync::Lazy;
|
||||
use regex::Regex;
|
||||
use serde::{Deserialize, Serialize};
|
||||
@ -23,7 +24,7 @@ use swc_common::{
|
||||
errors::Handler,
|
||||
FileName, Mark, SourceMap,
|
||||
};
|
||||
use swc_ecma_ast::{Expr, ExprStmt, ModuleItem, Stmt};
|
||||
use swc_ecma_ast::Expr;
|
||||
use swc_ecma_ext_transforms::jest;
|
||||
use swc_ecma_loader::resolvers::{
|
||||
lru::CachingResolver, node::NodeModulesResolver, tsc::TsConfigResolver,
|
||||
@ -37,11 +38,12 @@ use swc_ecma_parser::{lexer::Lexer, Parser, StringInput, Syntax, TsConfig};
|
||||
use swc_ecma_transforms::{
|
||||
hygiene, modules,
|
||||
modules::{hoist::import_hoister, path::NodeImportResolver, util::Scope},
|
||||
optimization::{const_modules, inline_globals, json_parse, simplifier},
|
||||
optimization::{const_modules, json_parse, simplifier},
|
||||
pass::{noop, Optional},
|
||||
proposals::{decorators, export_default_from, import_assertions},
|
||||
react, resolver_with_mark, typescript,
|
||||
};
|
||||
use swc_ecma_transforms_optimization::{inline_globals2, GlobalExprMap};
|
||||
use swc_ecma_visit::Fold;
|
||||
|
||||
#[cfg(test)]
|
||||
@ -218,7 +220,6 @@ impl Options {
|
||||
transform.legacy_decorator = true;
|
||||
}
|
||||
let optimizer = transform.optimizer;
|
||||
let enable_optimizer = optimizer.is_some();
|
||||
|
||||
let const_modules = {
|
||||
let enabled = transform.const_modules.is_some();
|
||||
@ -236,6 +237,8 @@ impl Options {
|
||||
}
|
||||
};
|
||||
|
||||
let enable_simplifier = optimizer.as_ref().map(|v| v.simplify).unwrap_or_default();
|
||||
|
||||
let optimization = {
|
||||
let pass =
|
||||
if let Some(opts) = optimizer.map(|o| o.globals.unwrap_or_else(Default::default)) {
|
||||
@ -255,7 +258,7 @@ impl Options {
|
||||
const_modules,
|
||||
optimization,
|
||||
Optional::new(export_default_from(), syntax.export_default_from()),
|
||||
Optional::new(simplifier(Default::default()), enable_optimizer),
|
||||
Optional::new(simplifier(Default::default()), enable_simplifier),
|
||||
json_parse_pass
|
||||
);
|
||||
|
||||
@ -968,6 +971,9 @@ pub struct OptimizerConfig {
|
||||
#[serde(default)]
|
||||
pub globals: Option<GlobalPassOption>,
|
||||
|
||||
#[serde(default = "true_by_default")]
|
||||
pub simplify: bool,
|
||||
|
||||
#[serde(default)]
|
||||
pub jsonify: Option<JsonifyOption>,
|
||||
}
|
||||
@ -987,7 +993,7 @@ fn default_jsonify_min_cost() -> usize {
|
||||
#[serde(deny_unknown_fields, rename_all = "camelCase")]
|
||||
pub struct GlobalPassOption {
|
||||
#[serde(default)]
|
||||
pub vars: AHashMap<JsWord, JsWord>,
|
||||
pub vars: IndexMap<JsWord, JsWord, ahash::RandomState>,
|
||||
#[serde(default)]
|
||||
pub envs: GlobalInliningPassEnvs,
|
||||
|
||||
@ -1016,6 +1022,28 @@ impl GlobalPassOption {
|
||||
pub fn build(self, cm: &SourceMap, handler: &Handler) -> impl 'static + Fold {
|
||||
type ValuesMap = Arc<AHashMap<JsWord, Expr>>;
|
||||
|
||||
fn expr(cm: &SourceMap, handler: &Handler, src: String) -> Box<Expr> {
|
||||
let fm = cm.new_source_file(FileName::Anon, src);
|
||||
let lexer = Lexer::new(
|
||||
Syntax::Es(Default::default()),
|
||||
Default::default(),
|
||||
StringInput::from(&*fm),
|
||||
None,
|
||||
);
|
||||
|
||||
let mut p = Parser::new_from(lexer);
|
||||
let expr = p.parse_expr();
|
||||
|
||||
for e in p.take_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,
|
||||
@ -1031,36 +1059,10 @@ impl GlobalPassOption {
|
||||
(*v).into()
|
||||
};
|
||||
let v_str = v.clone();
|
||||
let fm = cm.new_source_file(FileName::Custom(format!("GLOBAL.{}", k)), v);
|
||||
let lexer = Lexer::new(
|
||||
Syntax::Es(Default::default()),
|
||||
Default::default(),
|
||||
StringInput::from(&*fm),
|
||||
None,
|
||||
);
|
||||
|
||||
let mut p = Parser::new_from(lexer);
|
||||
let module = p.parse_module();
|
||||
let e = expr(cm, handler, v_str);
|
||||
|
||||
for e in p.take_errors() {
|
||||
e.into_diagnostic(handler).emit()
|
||||
}
|
||||
|
||||
let mut module = module
|
||||
.map_err(|e| e.into_diagnostic(handler).emit())
|
||||
.unwrap_or_else(|()| {
|
||||
panic!(
|
||||
"failed to parse global variable {}=`{}` as module",
|
||||
k, v_str
|
||||
)
|
||||
});
|
||||
|
||||
let expr = match module.body.pop() {
|
||||
Some(ModuleItem::Stmt(Stmt::Expr(ExprStmt { expr, .. }))) => *expr,
|
||||
_ => panic!("{} is not a valid expression", v_str),
|
||||
};
|
||||
|
||||
m.insert((*k).into(), expr);
|
||||
m.insert((*k).into(), *e);
|
||||
}
|
||||
|
||||
Arc::new(m)
|
||||
@ -1117,6 +1119,37 @@ impl GlobalPassOption {
|
||||
}
|
||||
};
|
||||
|
||||
let global_exprs = {
|
||||
static CACHE: Lazy<DashMap<Vec<(JsWord, JsWord)>, GlobalExprMap, ahash::RandomState>> =
|
||||
Lazy::new(|| Default::default());
|
||||
|
||||
let cache_key = self
|
||||
.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 = self
|
||||
.vars
|
||||
.iter()
|
||||
.filter(|(k, _)| k.contains('.'))
|
||||
.map(|(k, v)| {
|
||||
(
|
||||
*expr(cm, handler, k.to_string()),
|
||||
*expr(cm, handler, v.to_string()),
|
||||
)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let map = Arc::new(map);
|
||||
CACHE.insert(cache_key, map.clone());
|
||||
map
|
||||
}
|
||||
};
|
||||
|
||||
let global_map = {
|
||||
static CACHE: Lazy<DashMap<Vec<(JsWord, JsWord)>, ValuesMap, ahash::RandomState>> =
|
||||
Lazy::new(|| Default::default());
|
||||
@ -1124,18 +1157,24 @@ impl GlobalPassOption {
|
||||
let cache_key = self
|
||||
.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, self.vars.into_iter(), false);
|
||||
let map = mk_map(
|
||||
cm,
|
||||
handler,
|
||||
self.vars.into_iter().filter(|(k, _)| !k.contains('.')),
|
||||
false,
|
||||
);
|
||||
CACHE.insert(cache_key, map.clone());
|
||||
map
|
||||
}
|
||||
};
|
||||
|
||||
inline_globals(env_map, global_map, Arc::new(self.typeofs))
|
||||
inline_globals2(env_map, global_map, global_exprs, Arc::new(self.typeofs))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2,6 +2,7 @@
|
||||
"jsc": {
|
||||
"transform": {
|
||||
"optimizer": {
|
||||
"simplify": false,
|
||||
"globals": {
|
||||
"envs": {
|
||||
"NODE_ENV_ALT": "true"
|
@ -2,6 +2,7 @@
|
||||
"jsc": {
|
||||
"transform": {
|
||||
"optimizer": {
|
||||
"simplify": false,
|
||||
"globals": {
|
||||
"envs": {
|
||||
"NODE_ENV_ALT": "true"
|
14
tests/fixture/globals/member-expr/1/input/.swcrc
Normal file
14
tests/fixture/globals/member-expr/1/input/.swcrc
Normal file
@ -0,0 +1,14 @@
|
||||
{
|
||||
"jsc": {
|
||||
"transform": {
|
||||
"optimizer": {
|
||||
"simplify": false,
|
||||
"globals": {
|
||||
"vars": {
|
||||
"process.browser": "true"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
4
tests/fixture/globals/member-expr/1/input/index.js
Normal file
4
tests/fixture/globals/member-expr/1/input/index.js
Normal file
@ -0,0 +1,4 @@
|
||||
|
||||
if (process.browser) {
|
||||
console.log('Pass')
|
||||
}
|
3
tests/fixture/globals/member-expr/1/output/index.js
Normal file
3
tests/fixture/globals/member-expr/1/output/index.js
Normal file
@ -0,0 +1,3 @@
|
||||
if (true) {
|
||||
console.log('Pass');
|
||||
}
|
15
tests/fixture/globals/member-expr/precendence/input/.swcrc
Normal file
15
tests/fixture/globals/member-expr/precendence/input/.swcrc
Normal file
@ -0,0 +1,15 @@
|
||||
{
|
||||
"jsc": {
|
||||
"transform": {
|
||||
"optimizer": {
|
||||
"simplify": false,
|
||||
"globals": {
|
||||
"vars": {
|
||||
"value.debug": "true",
|
||||
"value": "'foo'"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
|
||||
console.log(value.debug)
|
||||
|
||||
if (value.debug) {
|
||||
console.log('Pass')
|
||||
}
|
||||
|
||||
console.log(value)
|
@ -0,0 +1,5 @@
|
||||
console.log(true);
|
||||
if (true) {
|
||||
console.log('Pass');
|
||||
}
|
||||
console.log('foo');
|
@ -2,6 +2,7 @@
|
||||
"jsc": {
|
||||
"transform": {
|
||||
"optimizer": {
|
||||
"simplify": false,
|
||||
"globals": {
|
||||
"typeofs": {
|
||||
"window": "object"
|
Loading…
Reference in New Issue
Block a user