Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 66e8088

Browse files
feat: Add option moduleSideEffectsRule for treeshake.moduleSideEffects (#2695)
<!-- Thank you for contributing! --> ### Description 1. part of #2606 <!-- Please insert your description here and provide especially info about the "what" this PR is solving -->
1 parent 3a1912b commit 66e8088

24 files changed

Lines changed: 345 additions & 178 deletions

File tree

crates/rolldown/src/ecmascript/ecma_module_view_factory.rs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,6 @@ pub async fn create_ecma_view<'any>(
9393
ctx.resolved_id.module_def_format,
9494
ctx.options,
9595
)?;
96-
9796
let ScanResult {
9897
named_imports,
9998
named_exports,
@@ -157,10 +156,9 @@ pub async fn create_ecma_view<'any>(
157156
TreeshakeOptions::Boolean(false) => DeterminedSideEffects::NoTreeshake,
158157
TreeshakeOptions::Boolean(true) => unreachable!(),
159158
TreeshakeOptions::Option(ref opt) => {
160-
if opt.module_side_effects.resolve(&stable_id) {
161-
lazy_check_side_effects()
162-
} else {
163-
DeterminedSideEffects::UserDefined(false)
159+
match opt.module_side_effects.resolve(&stable_id, ctx.resolved_id.is_external) {
160+
Some(value) => DeterminedSideEffects::UserDefined(value),
161+
None => lazy_check_side_effects(),
164162
}
165163
}
166164
},

crates/rolldown_binding/src/options/binding_input_options/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ pub struct BindingInputOptions {
7373
// extra
7474
pub cwd: String,
7575
// pub builtins: BuiltinsOptions,
76+
#[serde(skip_deserializing)]
7677
pub treeshake: Option<treeshake::BindingTreeshake>,
7778

7879
pub module_types: Option<HashMap<String, String>>,

crates/rolldown_binding/src/options/binding_input_options/treeshake.rs

Lines changed: 45 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,58 @@
1-
use rolldown::{InnerOptions, ModuleSideEffects};
1+
use napi::Either;
2+
use rolldown::{InnerOptions, ModuleSideEffects, ModuleSideEffectsRule};
23
use rolldown_utils::js_regex::HybridRegex;
34
use serde::Deserialize;
45

5-
#[napi_derive::napi(object)]
6-
#[derive(Deserialize, Debug, Default)]
6+
use crate::options::plugin::types::binding_js_or_regex::JsRegExp;
7+
8+
pub(crate) type BindingModuleSideEffects = Either<bool, Vec<BindingModuleSideEffectsRule>>;
9+
#[napi_derive::napi(object, object_to_js = false)]
10+
#[derive(Deserialize, Debug)]
711
#[serde(rename_all = "camelCase")]
812
pub struct BindingTreeshake {
9-
pub module_side_effects: String,
13+
#[napi(ts_type = "boolean | BindingModuleSideEffectsRule[]")]
14+
#[serde(skip_deserializing, default = "default_module_side_effects")]
15+
pub module_side_effects: BindingModuleSideEffects,
16+
}
17+
18+
fn default_module_side_effects() -> BindingModuleSideEffects {
19+
Either::A(true)
20+
}
21+
22+
#[napi_derive::napi(object, object_to_js = false)]
23+
#[derive(Deserialize, Debug, Default)]
24+
#[serde(rename_all = "camelCase")]
25+
pub struct BindingModuleSideEffectsRule {
26+
#[napi(ts_type = "RegExp | undefined")]
27+
pub test: Option<JsRegExp>,
28+
pub side_effects: bool,
29+
#[napi(ts_type = "boolean | undefined")]
30+
pub external: Option<bool>,
1031
}
1132

1233
impl TryFrom<BindingTreeshake> for rolldown::TreeshakeOptions {
1334
fn try_from(value: BindingTreeshake) -> anyhow::Result<Self> {
14-
match value.module_side_effects.as_str() {
15-
"true" => {
16-
Ok(Self::Option(InnerOptions { module_side_effects: ModuleSideEffects::Boolean(true) }))
35+
match value.module_side_effects {
36+
Either::A(value) => {
37+
Ok(Self::Option(InnerOptions { module_side_effects: ModuleSideEffects::Boolean(value) }))
1738
}
18-
"false" => {
19-
Ok(Self::Option(InnerOptions { module_side_effects: ModuleSideEffects::Boolean(false) }))
20-
}
21-
_ => {
22-
let regex = HybridRegex::new(&value.module_side_effects)?;
23-
Ok(Self::Option(InnerOptions { module_side_effects: ModuleSideEffects::Regex(regex) }))
39+
Either::B(rules) => {
40+
let mut ret = Vec::with_capacity(rules.len());
41+
for rule in rules {
42+
let test = match rule.test {
43+
Some(test) => Some(HybridRegex::try_from(test)?),
44+
None => None,
45+
};
46+
ret.push(ModuleSideEffectsRule {
47+
test,
48+
side_effects: rule.side_effects,
49+
external: rule.external,
50+
});
51+
}
52+
53+
Ok(Self::Option(InnerOptions {
54+
module_side_effects: ModuleSideEffects::ModuleSideEffectsRules(ret),
55+
}))
2456
}
2557
}
2658
}

crates/rolldown_binding/src/options/plugin/types/binding_js_or_regex.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,11 +70,19 @@ impl TryFrom<BindingStringOrRegex> for HybridRegex {
7070
fn try_from(value: BindingStringOrRegex) -> Result<Self, Self::Error> {
7171
match value.0 {
7272
Either::A(value) => HybridRegex::new(&value),
73-
Either::B(value) => HybridRegex::with_flags(&value.source, &value.flags),
73+
Either::B(value) => HybridRegex::try_from(value),
7474
}
7575
}
7676
}
7777

78+
impl TryFrom<JsRegExp> for HybridRegex {
79+
type Error = anyhow::Error;
80+
81+
fn try_from(value: JsRegExp) -> Result<Self, Self::Error> {
82+
HybridRegex::with_flags(&value.source, &value.flags)
83+
}
84+
}
85+
7886
impl TryFrom<BindingStringOrRegex> for StringOrRegex {
7987
type Error = anyhow::Error;
8088

crates/rolldown_common/src/inner_bundler_options/types/treeshake.rs

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,15 +24,47 @@ impl Default for TreeshakeOptions {
2424

2525
#[derive(Debug)]
2626
pub enum ModuleSideEffects {
27-
Regex(HybridRegex),
27+
ModuleSideEffectsRules(Vec<ModuleSideEffectsRule>),
2828
Boolean(bool),
2929
}
3030

31+
#[derive(Debug)]
32+
pub struct ModuleSideEffectsRule {
33+
pub test: Option<HybridRegex>,
34+
pub external: Option<bool>,
35+
pub side_effects: bool,
36+
}
37+
3138
impl ModuleSideEffects {
32-
pub fn resolve(&self, path: &str) -> bool {
39+
pub fn resolve(&self, path: &str, is_external: bool) -> Option<bool> {
3340
match self {
34-
ModuleSideEffects::Regex(reg) => reg.matches(path),
35-
ModuleSideEffects::Boolean(b) => *b,
41+
ModuleSideEffects::ModuleSideEffectsRules(rules) => {
42+
for ModuleSideEffectsRule { test, external, side_effects } in rules {
43+
match (test, external) {
44+
(Some(test), Some(external)) => {
45+
if test.matches(path) && *external == is_external {
46+
return Some(*side_effects);
47+
}
48+
}
49+
(None, Some(external)) => {
50+
if *external == is_external {
51+
return Some(*side_effects);
52+
}
53+
}
54+
(Some(test), None) => {
55+
if test.matches(path) {
56+
return Some(*side_effects);
57+
}
58+
}
59+
// At least one of `test` or `external` should be defined
60+
(None, None) => unreachable!(),
61+
};
62+
}
63+
// analyze side effects from source code
64+
None
65+
}
66+
ModuleSideEffects::Boolean(false) => Some(false),
67+
ModuleSideEffects::Boolean(true) => None,
3668
}
3769
}
3870
}

crates/rolldown_common/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ pub mod bundler_options {
3434
sourcemap_ignore_list::SourceMapIgnoreList,
3535
sourcemap_path_transform::SourceMapPathTransform,
3636
target::ESTarget,
37-
treeshake::{InnerOptions, ModuleSideEffects, TreeshakeOptions},
37+
treeshake::{InnerOptions, ModuleSideEffects, ModuleSideEffectsRule, TreeshakeOptions},
3838
watch_option::{NotifyOption, WatchOption},
3939
},
4040
BundlerOptions,

packages/rolldown/src/binding.d.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,12 @@ export interface BindingModulePreloadPolyfillPluginConfig {
280280
skip?: boolean
281281
}
282282

283+
export interface BindingModuleSideEffectsRule {
284+
test?: RegExp | undefined
285+
sideEffects: boolean
286+
external?: boolean | undefined
287+
}
288+
283289
export interface BindingNotifyOption {
284290
pollInterval?: number
285291
compareContents?: boolean
@@ -436,7 +442,7 @@ export interface BindingTransformPluginConfig {
436442
}
437443

438444
export interface BindingTreeshake {
439-
moduleSideEffects: string
445+
moduleSideEffects: boolean | BindingModuleSideEffectsRule[]
440446
}
441447

442448
export declare enum BindingWatcherEvent {

packages/rolldown/src/rolldown-binding.wasi-browser.js

Lines changed: 63 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -80,68 +80,69 @@ function __napi_rs_initialize_modules(__napiInstance) {
8080
__napiInstance.exports['__napi_register__BindingNotifyOption_struct_13']?.()
8181
__napiInstance.exports['__napi_register__BindingResolveOptions_struct_14']?.()
8282
__napiInstance.exports['__napi_register__BindingTreeshake_struct_15']?.()
83-
__napiInstance.exports['__napi_register__BindingInputOptions_struct_16']?.()
84-
__napiInstance.exports['__napi_register__BindingAdvancedChunksOptions_struct_17']?.()
85-
__napiInstance.exports['__napi_register__BindingMatchGroup_struct_18']?.()
86-
__napiInstance.exports['__napi_register__BindingOutputOptions_struct_19']?.()
87-
__napiInstance.exports['__napi_register__BindingPluginContext_struct_20']?.()
88-
__napiInstance.exports['__napi_register__BindingPluginContext_impl_27']?.()
89-
__napiInstance.exports['__napi_register__BindingPluginContextResolvedId_struct_28']?.()
90-
__napiInstance.exports['__napi_register__BindingPluginOptions_struct_29']?.()
91-
__napiInstance.exports['__napi_register__BindingPluginWithIndex_struct_30']?.()
92-
__napiInstance.exports['__napi_register__BindingTransformPluginContext_struct_31']?.()
93-
__napiInstance.exports['__napi_register__BindingTransformPluginContext_impl_33']?.()
94-
__napiInstance.exports['__napi_register__BindingAssetSource_struct_34']?.()
95-
__napiInstance.exports['__napi_register__BindingEmittedAsset_struct_35']?.()
96-
__napiInstance.exports['__napi_register__BindingGeneralHookFilter_struct_36']?.()
97-
__napiInstance.exports['__napi_register__BindingTransformHookFilter_struct_37']?.()
98-
__napiInstance.exports['__napi_register__BindingHookLoadOutput_struct_38']?.()
99-
__napiInstance.exports['__napi_register__BindingHookRenderChunkOutput_struct_39']?.()
100-
__napiInstance.exports['__napi_register__BindingHookResolveIdExtraArgs_struct_40']?.()
101-
__napiInstance.exports['__napi_register__BindingHookResolveIdOutput_struct_41']?.()
102-
__napiInstance.exports['__napi_register__BindingHookSideEffects_42']?.()
103-
__napiInstance.exports['__napi_register__BindingHookTransformOutput_struct_43']?.()
104-
__napiInstance.exports['__napi_register__BindingPluginContextResolveOptions_struct_44']?.()
105-
__napiInstance.exports['__napi_register__BindingTransformHookExtraArgs_struct_45']?.()
106-
__napiInstance.exports['__napi_register__BindingBuiltinPlugin_struct_46']?.()
107-
__napiInstance.exports['__napi_register__BindingBuiltinPluginName_47']?.()
108-
__napiInstance.exports['__napi_register__BindingGlobImportPluginConfig_struct_48']?.()
109-
__napiInstance.exports['__napi_register__BindingManifestPluginConfig_struct_49']?.()
110-
__napiInstance.exports['__napi_register__BindingModulePreloadPolyfillPluginConfig_struct_50']?.()
111-
__napiInstance.exports['__napi_register__BindingJsonPluginConfig_struct_51']?.()
112-
__napiInstance.exports['__napi_register__BindingTransformPluginConfig_struct_52']?.()
113-
__napiInstance.exports['__napi_register__BindingAliasPluginConfig_struct_53']?.()
114-
__napiInstance.exports['__napi_register__BindingAliasPluginAlias_struct_54']?.()
115-
__napiInstance.exports['__napi_register__BindingBuildImportAnalysisPluginConfig_struct_55']?.()
116-
__napiInstance.exports['__napi_register__BindingReplacePluginConfig_struct_56']?.()
117-
__napiInstance.exports['__napi_register__BindingPluginOrder_57']?.()
118-
__napiInstance.exports['__napi_register__BindingPluginHookMeta_struct_58']?.()
119-
__napiInstance.exports['__napi_register__ParallelJsPluginRegistry_struct_59']?.()
120-
__napiInstance.exports['__napi_register__ParallelJsPluginRegistry_impl_61']?.()
121-
__napiInstance.exports['__napi_register__register_plugins_62']?.()
122-
__napiInstance.exports['__napi_register__BindingLog_struct_63']?.()
123-
__napiInstance.exports['__napi_register__BindingLogLevel_64']?.()
124-
__napiInstance.exports['__napi_register__BindingModuleInfo_struct_65']?.()
125-
__napiInstance.exports['__napi_register__BindingModuleInfo_impl_67']?.()
126-
__napiInstance.exports['__napi_register__BindingOutputAsset_struct_68']?.()
127-
__napiInstance.exports['__napi_register__BindingOutputAsset_impl_73']?.()
128-
__napiInstance.exports['__napi_register__JsOutputAsset_struct_74']?.()
129-
__napiInstance.exports['__napi_register__BindingOutputChunk_struct_75']?.()
130-
__napiInstance.exports['__napi_register__BindingOutputChunk_impl_90']?.()
131-
__napiInstance.exports['__napi_register__JsOutputChunk_struct_91']?.()
132-
__napiInstance.exports['__napi_register__BindingOutputs_struct_92']?.()
133-
__napiInstance.exports['__napi_register__BindingOutputs_impl_95']?.()
134-
__napiInstance.exports['__napi_register__JsChangedOutputs_struct_96']?.()
135-
__napiInstance.exports['__napi_register__PreRenderedChunk_struct_97']?.()
136-
__napiInstance.exports['__napi_register__RenderedChunk_struct_98']?.()
137-
__napiInstance.exports['__napi_register__BindingRenderedModule_struct_99']?.()
138-
__napiInstance.exports['__napi_register__AliasItem_struct_100']?.()
139-
__napiInstance.exports['__napi_register__ExtensionAliasItem_struct_101']?.()
140-
__napiInstance.exports['__napi_register__BindingSourcemap_struct_102']?.()
141-
__napiInstance.exports['__napi_register__BindingJsonSourcemap_struct_103']?.()
142-
__napiInstance.exports['__napi_register__BindingWatcher_struct_104']?.()
143-
__napiInstance.exports['__napi_register__BindingWatcher_impl_107']?.()
144-
__napiInstance.exports['__napi_register__BindingWatcherEvent_108']?.()
83+
__napiInstance.exports['__napi_register__BindingModuleSideEffectsRule_struct_16']?.()
84+
__napiInstance.exports['__napi_register__BindingInputOptions_struct_17']?.()
85+
__napiInstance.exports['__napi_register__BindingAdvancedChunksOptions_struct_18']?.()
86+
__napiInstance.exports['__napi_register__BindingMatchGroup_struct_19']?.()
87+
__napiInstance.exports['__napi_register__BindingOutputOptions_struct_20']?.()
88+
__napiInstance.exports['__napi_register__BindingPluginContext_struct_21']?.()
89+
__napiInstance.exports['__napi_register__BindingPluginContext_impl_28']?.()
90+
__napiInstance.exports['__napi_register__BindingPluginContextResolvedId_struct_29']?.()
91+
__napiInstance.exports['__napi_register__BindingPluginOptions_struct_30']?.()
92+
__napiInstance.exports['__napi_register__BindingPluginWithIndex_struct_31']?.()
93+
__napiInstance.exports['__napi_register__BindingTransformPluginContext_struct_32']?.()
94+
__napiInstance.exports['__napi_register__BindingTransformPluginContext_impl_34']?.()
95+
__napiInstance.exports['__napi_register__BindingAssetSource_struct_35']?.()
96+
__napiInstance.exports['__napi_register__BindingEmittedAsset_struct_36']?.()
97+
__napiInstance.exports['__napi_register__BindingGeneralHookFilter_struct_37']?.()
98+
__napiInstance.exports['__napi_register__BindingTransformHookFilter_struct_38']?.()
99+
__napiInstance.exports['__napi_register__BindingHookLoadOutput_struct_39']?.()
100+
__napiInstance.exports['__napi_register__BindingHookRenderChunkOutput_struct_40']?.()
101+
__napiInstance.exports['__napi_register__BindingHookResolveIdExtraArgs_struct_41']?.()
102+
__napiInstance.exports['__napi_register__BindingHookResolveIdOutput_struct_42']?.()
103+
__napiInstance.exports['__napi_register__BindingHookSideEffects_43']?.()
104+
__napiInstance.exports['__napi_register__BindingHookTransformOutput_struct_44']?.()
105+
__napiInstance.exports['__napi_register__BindingPluginContextResolveOptions_struct_45']?.()
106+
__napiInstance.exports['__napi_register__BindingTransformHookExtraArgs_struct_46']?.()
107+
__napiInstance.exports['__napi_register__BindingBuiltinPlugin_struct_47']?.()
108+
__napiInstance.exports['__napi_register__BindingBuiltinPluginName_48']?.()
109+
__napiInstance.exports['__napi_register__BindingGlobImportPluginConfig_struct_49']?.()
110+
__napiInstance.exports['__napi_register__BindingManifestPluginConfig_struct_50']?.()
111+
__napiInstance.exports['__napi_register__BindingModulePreloadPolyfillPluginConfig_struct_51']?.()
112+
__napiInstance.exports['__napi_register__BindingJsonPluginConfig_struct_52']?.()
113+
__napiInstance.exports['__napi_register__BindingTransformPluginConfig_struct_53']?.()
114+
__napiInstance.exports['__napi_register__BindingAliasPluginConfig_struct_54']?.()
115+
__napiInstance.exports['__napi_register__BindingAliasPluginAlias_struct_55']?.()
116+
__napiInstance.exports['__napi_register__BindingBuildImportAnalysisPluginConfig_struct_56']?.()
117+
__napiInstance.exports['__napi_register__BindingReplacePluginConfig_struct_57']?.()
118+
__napiInstance.exports['__napi_register__BindingPluginOrder_58']?.()
119+
__napiInstance.exports['__napi_register__BindingPluginHookMeta_struct_59']?.()
120+
__napiInstance.exports['__napi_register__ParallelJsPluginRegistry_struct_60']?.()
121+
__napiInstance.exports['__napi_register__ParallelJsPluginRegistry_impl_62']?.()
122+
__napiInstance.exports['__napi_register__register_plugins_63']?.()
123+
__napiInstance.exports['__napi_register__BindingLog_struct_64']?.()
124+
__napiInstance.exports['__napi_register__BindingLogLevel_65']?.()
125+
__napiInstance.exports['__napi_register__BindingModuleInfo_struct_66']?.()
126+
__napiInstance.exports['__napi_register__BindingModuleInfo_impl_68']?.()
127+
__napiInstance.exports['__napi_register__BindingOutputAsset_struct_69']?.()
128+
__napiInstance.exports['__napi_register__BindingOutputAsset_impl_74']?.()
129+
__napiInstance.exports['__napi_register__JsOutputAsset_struct_75']?.()
130+
__napiInstance.exports['__napi_register__BindingOutputChunk_struct_76']?.()
131+
__napiInstance.exports['__napi_register__BindingOutputChunk_impl_91']?.()
132+
__napiInstance.exports['__napi_register__JsOutputChunk_struct_92']?.()
133+
__napiInstance.exports['__napi_register__BindingOutputs_struct_93']?.()
134+
__napiInstance.exports['__napi_register__BindingOutputs_impl_96']?.()
135+
__napiInstance.exports['__napi_register__JsChangedOutputs_struct_97']?.()
136+
__napiInstance.exports['__napi_register__PreRenderedChunk_struct_98']?.()
137+
__napiInstance.exports['__napi_register__RenderedChunk_struct_99']?.()
138+
__napiInstance.exports['__napi_register__BindingRenderedModule_struct_100']?.()
139+
__napiInstance.exports['__napi_register__AliasItem_struct_101']?.()
140+
__napiInstance.exports['__napi_register__ExtensionAliasItem_struct_102']?.()
141+
__napiInstance.exports['__napi_register__BindingSourcemap_struct_103']?.()
142+
__napiInstance.exports['__napi_register__BindingJsonSourcemap_struct_104']?.()
143+
__napiInstance.exports['__napi_register__BindingWatcher_struct_105']?.()
144+
__napiInstance.exports['__napi_register__BindingWatcher_impl_108']?.()
145+
__napiInstance.exports['__napi_register__BindingWatcherEvent_109']?.()
145146
}
146147
export const BindingLog = __napiModule.exports.BindingLog
147148
export const BindingModuleInfo = __napiModule.exports.BindingModuleInfo

0 commit comments

Comments
 (0)