A flag rule identifies an exact source shape and assigns its final primitive value.
selector[=value]
The value defaults to true when omitted.
| Source shape | CLI rule | Library definition |
|---|---|---|
| Identifier | FLAG=false |
{ identifier: "FLAG", value: false } |
| Member access | features.newUi=false |
{ identifier: "features", path: ["newUi"], value: false } |
| Imported value | ./flags#NEW_UI=false |
{ module: "./flags", export: "NEW_UI", value: false } |
| Call | useFlag("new-ui")=false |
{ call: "useFlag", arguments: ["new-ui"], value: false } |
| Dotted call | client.isEnabled("new-ui")=false |
{ call: "client.isEnabled", arguments: ["new-ui"], value: false } |
| Imported call | flag-client#useFlag("new-ui")=false |
{ module: "flag-client", call: "useFlag", arguments: ["new-ui"], value: false } |
Quote call rules in your shell so parentheses and spaces are not interpreted.
Match a program-level or unresolved identifier:
npx flag-prune --set 'NEW_CHECKOUT=false' src
Match a static member path:
npx flag-prune --set 'features.checkout.newUi=false' src
This matches static dot access and equivalent optional access:
features.checkout.newUi
features?.checkout?.newUi
Computed dynamic keys do not match:
features.checkout[key]
A computed string literal is still static and can match:
features["checkout"].newUi
Use the exact module specifier, #, and the imported export name:
npx flag-prune --set './flags#NEW_CHECKOUT=false' src
Given:
import { NEW_CHECKOUT as checkoutEnabled } from "./flags"
The rule matches checkoutEnabled because import aliases are resolved. A local parameter or variable that shadows the alias is not changed.
Namespace imports are supported:
import * as flags from "./flags"
if (flags.NEW_CHECKOUT) {
// ...
}
The same ./flags#NEW_CHECKOUT=false rule matches the namespace member.
Default imports use default as the export name in the library definition:
{
module: "flag-client",
export: "default",
path: ["newCheckout"],
value: false,
}
For SDK examples, see the Unleash, LaunchDarkly, PostHog, Statsig, and OpenFeature guides.
Calls match an exact static callee and an exact required argument prefix:
npx flag-prune --set 'client.isEnabled("new-ui")=false' src
This matches:
client.isEnabled("new-ui")
client.isEnabled("new-ui", context)
client.isEnabled("new-ui", loadContext())
It does not match:
client.isEnabled()
client.isEnabled("other")
client.isEnabled(flagName)
Configured arguments can be strings, numbers, booleans, negative numbers, or null:
npx flag-prune --set 'resolveExperiment("checkout", 2, true, null)=treatment' src
Dynamic configured arguments are rejected because they would make matching ambiguous:
# Invalid
npx flag-prune --set 'useFlag(flagName)=false' src
Arguments after the configured prefix do not participate in matching. Their required evaluation is preserved when the flag call is removed.
if (client.isEnabled("new-ui", loadContext())) {
renderNewUi()
}
With the value true, the result retains the context call:
loadContext()
renderNewUi()
Pure trailing values can disappear. Calls, getters, computed keys, spreads, and other observable evaluation are retained.
Limit a call rule to one imported binding by adding the module specifier:
npx flag-prune --set 'flag-client#useFlag("new-ui")=false' src
Aliases are resolved, and shadowed functions are not matched.
Without module, a call rule binds to a program-level import or declaration when one exists. Otherwise, an unresolved function name may match. Dotted calls such as client.isEnabled can also match a local parameter or object binding with that exact static path.
Supported values are:
| Kind | Examples |
|---|---|
| Boolean | true, false |
| Number | 0, 25, -1, 3.5 |
| Null | null |
| String | treatment, 'pro tier', "pro tier" |
| Array | [10, 20], ["a", "b"] |
| Object | { enabled: true, name: "treatment" } |
Unquoted tokens that are not booleans, numbers, or null are strings:
npx flag-prune --set 'getVariant("checkout")=treatment' src
Quote a string value when it contains spaces or shell-sensitive characters:
npx flag-prune --set 'getVariant("checkout")="new treatment"' src
A value that begins with { or [ is parsed as a static object or array
literal. Use this for SDKs that return a variant object such as Unleash
getVariant or OpenFeature useFlag:
npx flag-prune --set 'getVariant("checkout")={ enabled: true, name: "treatment" }' src
Object and array values may nest, and their leaves must be static booleans,
numbers, strings, or null. Keys may be bare identifiers or quoted strings:
npx flag-prune --set 'useFlag("theme")={ payload: { mode: "dark" }, "content-type": "json" }' src
Quote the whole rule in your shell so the braces and spaces are passed through intact.
When a flag resolves to an object or array, flag-prune folds static member and
index reads to their configured values:
const variant = getVariant("checkout")
if (variant.enabled && variant.name === "treatment") {
showTreatment()
}
With getVariant("checkout")={ enabled: true, name: "treatment" }, this becomes:
showTreatment()
Object identity is preserved. The declaration is kept when the whole value is still used elsewhere (for example passed to a function), and only the member reads are folded. A declaration whose reads are all folded is removed as unused.
flag-prune does not use fuzzy text replacement.
These constraints are what make the transform repeatable and conservative.
A member selector normally ignores function-local variables and parameters.
Use --allow-local-bindings only when matching stable application-owned
bindings by name is intentional:
npx flag-prune \
--set 'hasFeature.newAccessControl=true' \
--allow-local-bindings \
--write src
const hasFeature = useFeatures()
if (hasFeature.newAccessControl) useNewAccess()
The member read folds, while the potentially effectful useFeatures() call is
retained. Reassigned bindings are still rejected. Because replacing a member
also removes any getter or proxy-trap evaluation on that member, this option is
appropriate only when that property read is known to be safe to replace.
Repeat --set:
npx flag-prune \
--set 'A=true' \
--set 'B=false' \
--set 'getVariant("checkout")=control' \
src
Use -- before a target whose name begins with -:
npx flag-prune --set 'FLAG=false' -- --generated.ts