Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
f5b045a
Base tree shaking implementation
iObject Mar 18, 2026
022dd84
bsconfig control
iObject Mar 18, 2026
f04302e
Change from annotation to comments
iObject Mar 18, 2026
0ab9294
spec update
iObject Mar 18, 2026
adf11e7
Potential fix for pull request finding
iObject Mar 18, 2026
b6aed2e
Potential fix for pull request finding
iObject Mar 18, 2026
5d18b91
correct tests
iObject Mar 18, 2026
208d9d6
remove only
iObject Mar 18, 2026
e690a39
bsconfig.schema.json update
iObject Mar 18, 2026
0d70977
readme updates
iObject Mar 18, 2026
b001258
spec rename
iObject Mar 18, 2026
e226ecd
documentation fix
iObject Mar 18, 2026
c58c812
runscreensaver
iObject Mar 18, 2026
7ee5624
optimizations
iObject Mar 18, 2026
19f13f8
Potential fix for pull request finding
iObject Mar 18, 2026
6b6a080
Merge branch 'master' into 183-tree-shaking
iObject Mar 18, 2026
def4082
Potential fix for pull request finding
iObject Mar 18, 2026
e5d83aa
resolve issues
iObject Mar 18, 2026
421d77b
fix shorthand conflict
iObject Mar 18, 2026
1f32d43
Potential fix for pull request finding
iObject Mar 18, 2026
4879eb7
Potential fix for pull request finding
iObject Mar 18, 2026
18f7fd5
Potential fix for pull request finding
iObject Mar 18, 2026
cf64d4c
fix bug
iObject Mar 19, 2026
de7dece
Windows src path normalization
iObject Mar 19, 2026
0569d01
Tree shaking now lives in Program.beforeProgramTranspile
iObject Mar 19, 2026
5ca9b74
remove space
iObject Mar 19, 2026
4296778
Potential fix for pull request finding
iObject Mar 19, 2026
ff2d8df
Move TreeShaker
iObject Mar 19, 2026
949f67b
Merge branch '183-tree-shaking' of github.com:rokucommunity/brighters…
iObject Mar 19, 2026
edd9cea
Updated comment
iObject Mar 19, 2026
301a919
Potential fix for pull request finding
iObject Mar 19, 2026
12643fd
logging
iObject Mar 19, 2026
b6d9d57
fix(tree-shaker): retain .brs library functions called via namespace …
iObject Mar 19, 2026
bbc8752
move logging to single output
iObject Mar 19, 2026
83e0f6b
tests for excludes brs
iObject Mar 19, 2026
b6f036a
Move treeShaker back into BscPlugin.ts
iObject Mar 19, 2026
7672891
remove whitespace
iObject Mar 19, 2026
5986f59
readme update
iObject Mar 19, 2026
c4d231d
omit fully protected files from transpiler
iObject Mar 19, 2026
a9b0de5
Fix issue
iObject Mar 19, 2026
e3717f1
pattern fix
iObject Mar 19, 2026
94f9c9d
treeShaking normalization tests
iObject Mar 19, 2026
8d6d302
changes map for any compiled rule that ends up with no criteria
iObject Mar 19, 2026
9267d14
Merge branch 'master' into 183-tree-shaking
iObject Mar 19, 2026
84a4da2
readme update
iObject Mar 19, 2026
ac3d832
customizations support
iObject Mar 20, 2026
45a03cf
Merge branch '183-tree-shaking' of github.com:rokucommunity/brighters…
iObject Mar 20, 2026
41bcae3
test for function passed by reference as an argument to another function
iObject Mar 20, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
documentation fix
  • Loading branch information
iObject committed Mar 18, 2026
commit e226ecde12d1663ed2bfed90be1a523767892bcc
11 changes: 5 additions & 6 deletions docs/shaking.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,10 @@ end sub
### What `bs:keep` Does NOT Do

- A `bs:keep` comment placed **inside** a function body does not protect that function.
- A `bs:keep` comment does not automatically keep the functions that the annotated function calls — only the annotated function itself. If those callees are also unused by the rest of the program, they will still be removed. Use [`treeShaking.keep`](#treeshakingkeep-rules) rules with dependency closure, or annotate each callee individually, if you need to retain an entire call graph.

### Dependency Closure

A `bs:keep` annotation preserves the full call chain of the annotated function. BrighterScript's reference pass walks every function body — including those of kept functions — so anything called directly or transitively from a `bs:keep` function is automatically retained.

## `treeShaking.keep` Rules

Expand Down Expand Up @@ -192,11 +195,7 @@ Keep only functions whose name starts with `api_` **and** that live in a specifi

### Dependency Closure

Keep rules do not automatically pull in the transitive dependencies of a matched function. If `api_login` calls `crypto_hash` and only `api_login` is matched by a keep rule, `crypto_hash` will still be removed if nothing else calls it.

To retain the entire reachable graph of a kept function, either:
- Add a `bs:keep` comment to each function you want to preserve, or
- Add additional keep rules (e.g. a `src` rule that covers the whole file containing the helpers)
Keep rules preserve the full call chain of every matched function. BrighterScript's reference pass walks every function body, so anything called directly or transitively from a kept function is automatically retained.

## Configuration Reference

Expand Down
87 changes: 87 additions & 0 deletions src/bscPlugin/treeShaker/TreeShaker.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,36 @@ describe('TreeShaker', () => {
expect(code).not.to.include('sub canGo()');
});

it('preserves the full call chain of a bs:keep function', async () => {
program.setFile('source/main.bs', `
sub main()
end sub

' bs:keep
sub topLevel()
middle()
end sub

sub middle()
leaf()
end sub

sub leaf()
print "end of chain"
end sub

sub unrelated()
print "no connection to topLevel"
end sub
`);

const code = await getTranspiled('source/main.bs');
expect(code).to.include('sub topLevel()');
expect(code).to.include('sub middle()');
expect(code).to.include('sub leaf()');
expect(code).not.to.include('sub unrelated()');
});

it('does not apply a bs:keep comment to the function before it', async () => {
program.setFile('source/main.bs', `
sub main()
Expand Down Expand Up @@ -304,6 +334,63 @@ describe('TreeShaker', () => {
expect(code).not.to.include('sub unused()');
});

it('preserves a namespaced function called relatively (without namespace prefix) from within the same namespace', async () => {
program.setFile('source/main.bs', `
namespace utils
sub caller()
helper() ' relative call — no "utils." prefix
end sub

sub helper()
print "called relatively from within the namespace"
end sub
end namespace

sub main()
utils.caller()
end sub
`);

const code = await getTranspiled('source/main.bs');
expect(code).to.include('utils_caller');
expect(code).to.include('utils_helper');
});

it('conservatively preserves all same-named functions across namespaces when one is called relatively', async () => {
// When `helper()` is called relatively inside `ns1`, the AST contains only
// the simple name "helper". The shaker adds "helper" to calledNames, which
// causes ns2_helper to survive even though it was never actually called.
// This is safe (no false removals) but not maximally precise.
program.setFile('source/main.bs', `
namespace ns1
sub caller()
helper() ' relative call — resolves to ns1_helper at runtime
end sub

sub helper()
print "ns1 helper"
end sub
end namespace

namespace ns2
sub helper()
print "ns2 helper — conservatively kept due to simple name match"
end sub
end namespace

sub main()
ns1.caller()
end sub
`);

const code = await getTranspiled('source/main.bs');
expect(code).to.include('ns1_caller');
expect(code).to.include('ns1_helper');
// ns2_helper is kept as a conservative side-effect of the simple name "helper"
// being in calledNames — not a bug, just imprecision in the static analysis.
expect(code).to.include('ns2_helper');
});

it('preserves namespaced functions that are called', async () => {
program.setFile('source/main.bs', `
namespace utils
Expand Down