| Crates.io | swc_feature_flags |
| lib.rs | swc_feature_flags |
| version | 0.2.0 |
| created_at | 2026-01-23 02:45:54.706793+00 |
| updated_at | 2026-01-23 03:19:21.251215+00 |
| description | Feature flag transformation for SWC - build-time marking and runtime dead code elimination |
| homepage | https://github.com/swc-project/plugins |
| repository | https://github.com/swc-project/plugins.git |
| max_upload_size | |
| id | 2063238 |
| size | 94,102 |
A two-phase feature flag system for SWC that provides build-time marking and runtime dead code elimination.
This library enables powerful feature flag management with aggressive dead code elimination. It works in two phases:
__SWC_FLAGS__ markersconst { featureA, featureB } = useExperimentalFlags()Id system to handle variable shadowing correctly__SWC_FLAGS__ pattern that minifiers preserveThe build-time plugin (@swc/plugin-feature-flags) performs these transformations:
Input:
import { useExperimentalFlags } from '@their/library';
function App() {
const { featureA, featureB } = useExperimentalFlags();
if (featureA) {
console.log('Feature A enabled');
}
return featureB ? 'Beta' : 'Stable';
}
Output:
function App() {
if (__SWC_FLAGS__.featureA) {
console.log('Feature A enabled');
}
return __SWC_FLAGS__.featureB ? 'Beta' : 'Stable';
}
The plugin:
__SWC_FLAGS__.flagName markersThe runtime transformer substitutes flag values and eliminates dead code:
Input (from Phase 1):
function App() {
if (__SWC_FLAGS__.featureA) {
console.log('Feature A enabled');
}
return __SWC_FLAGS__.featureB ? 'Beta' : 'Stable';
}
Runtime Config:
{
"featureA": true,
"featureB": false
}
Output:
function App() {
console.log('Feature A enabled');
return 'Stable';
}
Add to your Cargo.toml:
[dependencies]
swc_feature_flags = "0.1"
npm install @swc/plugin-feature-flags
use swc_feature_flags::{build_time_pass, runtime_pass, BuildTimeConfig, RuntimeConfig, LibraryConfig};
use std::collections::HashMap;
use swc_ecma_transforms_base::resolver;
use swc_common::Mark;
// Build-time configuration
let mut libraries = HashMap::new();
libraries.insert(
"@their/library".to_string(),
LibraryConfig {
functions: vec!["useExperimentalFlags".to_string()],
},
);
let build_config = BuildTimeConfig {
libraries,
exclude_flags: vec![],
marker_object: "__SWC_FLAGS__".to_string(),
};
// Apply resolver first (required for scope safety)
let unresolved_mark = Mark::new();
let top_level_mark = Mark::new();
program = program.apply(resolver(unresolved_mark, top_level_mark, false));
// Apply build-time pass
program = program.apply(build_time_pass(build_config));
// Runtime configuration
let mut flag_values = HashMap::new();
flag_values.insert("featureA".to_string(), true);
flag_values.insert("featureB".to_string(), false);
let runtime_config = RuntimeConfig {
flag_values,
remove_markers: true,
collect_stats: true,
marker_object: "__SWC_FLAGS__".to_string(),
};
// Apply runtime pass
program = program.apply(runtime_pass(runtime_config));
{
"jsc": {
"experimental": {
"plugins": [
["@swc/plugin-feature-flags", {
"libraries": {
"@their/library": {
"functions": ["useExperimentalFlags", "getExperimentalFlags"]
},
"@another/flags": {
"functions": ["useFeatures"]
}
},
"excludeFlags": ["quickToggle"],
"markerObject": "__SWC_FLAGS__"
}]
]
}
}
}
interface BuildTimeConfig {
/** Library configurations: library name -> config */
libraries: Record<string, LibraryConfig>;
/** Flags to exclude from build-time marking */
excludeFlags?: string[];
/** Global object name for markers (default: "__SWC_FLAGS__") */
markerObject?: string;
}
interface LibraryConfig {
/** Function names to detect (e.g., ["useExperimentalFlags"]) */
functions: string[];
}
pub struct RuntimeConfig {
/// Flag values to apply (flag_name -> boolean)
pub flag_values: HashMap<String, bool>,
/// Whether to remove markers after processing
pub remove_markers: bool, // default: true
/// Whether to collect statistics
pub collect_stats: bool, // default: true
/// Marker object name (must match build-time)
pub marker_object: String, // default: "__SWC_FLAGS__"
}
The runtime transformer eliminates:
// Input
if (__SWC_FLAGS__.featureA) { // true
console.log('A');
} else {
console.log('B');
}
// Output
console.log('A');
// Input
const result = __SWC_FLAGS__.featureB ? 'On' : 'Off'; // false
// Output
const result = 'Off';
// Input
const a = __SWC_FLAGS__.featureA && expensive(); // true
const b = __SWC_FLAGS__.featureB && shouldNotRun(); // false
const c = __SWC_FLAGS__.featureA || fallback(); // true
// Output
const a = expensive();
const b = false;
const c = true;
// Input
const notA = !__SWC_FLAGS__.featureA; // true
// Output
const notA = false;
The library uses SWC's Id system (symbol + syntax context) to handle variable shadowing correctly:
import { useExperimentalFlags } from '@their/library';
function App() {
const { featureA } = useExperimentalFlags();
if (featureA) { // Replaced with __SWC_FLAGS__.featureA
console.log('Outer');
const featureA = false; // Shadowed variable
if (featureA) { // NOT replaced - uses local variable
console.log('Inner');
}
}
}
Output:
function App() {
if (__SWC_FLAGS__.featureA) {
console.log('Outer');
const featureA = false;
if (featureA) {
console.log('Inner');
}
}
}
When collect_stats is enabled, the runtime transformer tracks:
pub struct TransformStats {
pub original_bytes: usize, // Approximate original size
pub removed_bytes: usize, // Bytes removed by DCE
pub branches_eliminated: usize, // Number of branches eliminated
pub flags_processed: HashSet<String>, // Flags that were processed
}
# Run all tests
cargo test -p swc_feature_flags
# Run fixture tests only
cargo test -p swc_feature_flags --test fixture
Apache-2.0
Contributions are welcome! Please ensure: