This month we ran a trial of auto-enabling the debug protocol by default. We added several much-needed performance improvements in the brighterscript v0.66 alphas. We fixed a few bugs in the brs emulator, improved our telemetry tracking, added a few improvements to the dev experience for our RokuCommunity contributors, and a lot more! As always, many thanks to all who have worked so hard and contributed this month!

There are many updates in this version that we hope you’ll like, some of the key highlights include:

We need your help

The RokuCommunity projects are maintained by a relatively small group of developers (mostly volunteers), and we have a growing list of of unresolved issues. We need your help! There are many different ways you can contribute. Whether it’s addressing bugs, improving documentation, introducing new features, or simply helping us manage our expanding list of GitHub issues, your involvement would be greatly appreciated. We are more than happy to guide you in finding the most suitable contribution method that aligns with your interests. To learn more about how you can contribute, feel free to reach out to us on Slack, or explore the existing GitHub issues:

Issue of the month

In this section, we highlight a specific issue where we could benefit from the community’s assistance in finding a solution. These problems are generally straightforward to address, and serve as an excellent opportunity to become acquainted with the RokuCommunity codebases.

This month, we’d like to draw attention to vscode-brightscript-language#409. A rather strange issue that Roku developers encounter is when the Roku device stops allowing developers to sideload channels. Sometimes this is due to the Roku device requiring you to manually “check for updates”. It’s curious why the Roku won’t just do this itself, but alas, it’s not something in our power to fix. However, what we can do is properly detect that this is the reason why the sideload failed. That’s what vscode-brightscript-language#409 is asking to do.


We’d like to detect when the sideload fails as a result of the “Failed to check for sofware update” issue, and then show a modal to the developer explaining why the sideload failed.

If you’re interested in working on this feature, please comment on the github issue or reach out to us on Slack


Telemetry tracking for Roku OS version

We added a new data point to our telemetry tracking. We now track Roku OS version during the startDebugSession telemetry event. This allows us to better understand what Roku OS versions our developers are actively using so we can better plan our roadmap for future features. We are very intentional about what data we collect. You can review all of our telemetry tracking data points in this file.

Here’s a chart showing the OS version across all debug sessions for a month:


Notification when developer mode is disabled

There’s now a warning that will appear when starting a debug session against a Roku device that doesn’t have developer mode enabled. Previously the debug session would silently fail, requiring you to dig through the output logs to figure out what went wrong. image

Testing auto-enabled debug protocol on 12.5 devices

We ran a test this month to try out the debug protocol to a large portion of our user base. The test ran for about 4 weeks to see what issues were uncovered. We were hoping that if the test went smoothly then we’d leave this feature enabled indefinitely. However, we discovered several issues as a result of this test, so the default debugger has been restored to telnet.

Here’s how the test worked: Set enableDebugProtocol to true for any debug session started on a device running RokuOS 12.5 or greater that didn’t explicitly define enableDebugProtocol. We notified the user that we enabled the protocol, but we still gave them an option for using telnet instead. We hoped that most users would still choose to test out the debug protocol because it will become the default debug mode some time in the future.

Here’s what the popups looked like:


Then after 2 clicks of the “Use telnet” button, we switch to this so the user can hide the popup for 12 hours. image

here’s the “report issue” dialog: image


  • logging issues: Most of the complaints were focused on the logging side of things. The telnet debugger allows a developer to start a debug session, then press the home button to exit the app, and then use the remote to reconnect to the app again. Since the vscode extension monitors the telnet output, launching the app with the remote would cause vscode to “reattach” to the new debug session. With the debug protocol this is not possible, because a debug protocol session is terminated when the app shuts down. We are investigating some creative under-the-hood ways of replicate this type of behavior with the debug protocol.
  • long launch timeouts: We introduced (and fixed) a long timeout bug when sideloading. Now that we’re doing a device-info request on every launch, if the device can’t be found, vscode would appear to do nothing for up to 2.5 minutes. Now it only waits about 5 seconds. (Read more about this issue here)
  • popups are annoying: we had to modify the popups to show up less often because they were becoming annoying. We figured most people would choose the debug protocol to test it out, but that was not the case, so we made some changes in #522 to reduce the frequency of the popup for telnet users too. image

After a month of testing, we decided to turn this feature back off. The debug protocol is still available by setting enableDebugProtocol: true in your launch.json. We hope to fix these issues and run another test at some point in the new year, once we are confident we’ve addressed most of the pain points. We are still confident that the debug protocol be the most efficient and effective way to debug Roku apps due to its stability and dependability, but we need to make sure our implementation maintains feature parity with the telnet debugger.

Shorten the timeout for device-info query on launch

As part of the telemetry tracking update, we now do a device-info request at the start of every launch request, in order to discover certain features of your device (is dev mode enabled, what OS is the device running, etc). However, we forgot to set a reasonable timeout on that request. It was previously set to 2.5 minutes. We’ve now adjusted that to a much more sensible 5 seconds.

We now show a statusbar loading animation while this query is running. In most cases, this request should only take 20-100ms. If it takes longer, that’s typically indicative of a much bigger issue like the device being offline or inaccessible on the network.


In the devices view in vscode, you can now click on the new View Registry button under any device. After choosing your app from the list, it will then open the registry ECP response in your web browser. While we show all channels on the device in the list, you can only access the dev app and channels matching the keyed device.

Here’s an example of this in action:


Backtick support in surrounding & autoClosing pairs

For projects that leverage the template string feature of BrighterScript, we’ve added language/editor support for backticks (templatestrings) for autoClosingPairs and autoSurroundingPairs. So you can finally get editor help like this:



Fix sideload crash when failed to delete sideloaded channel

When starting a debug session, we delete any dev channel right before sideloading your app to the Roku device. Sometimes that delete call would fail and then crash that debug session. We’re not entirely sure why the delete fails, but in most cases this is recoverable, so now we try/catch the delete and continue with the rest of the launch flow. This should hopefully reduce the number of times the vscode extension stops a debug session for seemingly no reason.

Fix small typo in debug protocol message

We fixed a small typo in the “you’re using the debug protocol” message. Before:

Protocol Version 3.0.0 has not been tested and my not work as intended.
Please open any issues you have with this version to https://github.com/rokucommunity/roku-debug/issues


Protocol Version 3.0.0 has not been tested and may not work as intended.
Please open any issues you have with this version to https://github.com/rokucommunity/roku-debug/issues

Thanks @jeanbenitezu (Jean Benitez)!

Fix bug with compile error reporting

During a debug protocol debug session, often times the debug session just shuts down with no explanation. We found an issue related to compile errors where the debug protocol sometimes shuts down the control port before we’ve had a chance to receive the compile error events. We’ve fixed this issue in two ways:

  • always scrape telnet output for compile errors (even when debug protocol is enabled)
  • when showing the compile error “exception” hack, don’t wait for the adapter to resolve (because it might never resolve)

With this fix in place, you can once again see all your wonderful syntax errors in bright red highlights!



Fix issue with unary expression parsing

We fixed a small precedence issue when parsing unary expressions related to negative expressions like -x or -someValue. You can check out brighterscript#938 for more information.

Enums as class initial values

We fixed a transpilation bug when using enums directly (eg. with no namespace prefix) inside a namespace. It was especially prevalent in classes.

namespace MyNS
    class HasEnumKlass
        enumValue = MyEnum.A
    end class

    enum MyEnum
        A = "A"
        B = "B"
    end enum
end namespace

was being transpiled to (notice the MyEnum.A):

function __MyNS_HasEnumKlass_builder()
    instance = {}
    instance.new = sub()
        m.enumValue = MyEnum.A
    end sub
    return instance
end function
function MyNS_HasEnumKlass()
    instance = __MyNS_HasEnumKlass_builder()
    return instance
end function

We’ve now fixed it, so it will properly transpile to:

function __MyNS_HasEnumKlass_builder()
    instance = {}
    instance.new = sub()
        m.enumValue = "A"
    end sub
    return instance
end function
function MyNS_HasEnumKlass()
    instance = __MyNS_HasEnumKlass_builder()
    return instance
end function

Community Tools


Add logic for optional chaining

We’ve added logic to the brs emulator for optional chaining. If you’re leveraging this awesome project for off-device brs runtime, you can now safely use optional chaining in your brs code.

sub test()
    print m?.optional?.chaining?.works ' yay!
end sub

Prevent multiple calls for dot-chained methods

We fixed a bug with how chained method calls were interpreted. They were incorrectly re-evaluating each item in the chain multiple times, when they should have been evaluated exactly once.

? CreateObject("roTimeSpan").TotalSeconds().ToStr().Trim()

Would result in .TotalSeconds() being evaluated/called 4 times.

It’s now been fixed to only evaluate once, which aligns us with Roku’s brs runtime.

Fixed math errors caused by using the wrong negative sign precedence

We fixed a parsing bug that would apply the wrong negative sign precedence, which would result in math errors.

print -1000 +1000

This will now apply the - and + in the correct precedence and the result will be 0.


Enhance getDeviceInfo() method

Roku’s device-info ECP endpoint is extremely useful for determining what functionality a given Roku device can support. We use it across many RokuCommunity projects, such as the VSCode extension, roku-debug, and roku-deploy. We recently discovered that each of those projects had their own implementation of fetching device-info. So to reduce code duplication and centralize our logic, we’ve enhanced the implementation in roku-deploy by adding an enhance option that returns the properties in camelCase instead of kebab-case and converts boolean strings to booleans and number strings to numbers. This opt-in flag retains backwards compatibility by adding an overloaded signature that is off by default.

Here’s how you can leverage this functionality:

import { rokuDeploy } from 'roku-deploy';
const deviceInfo = rokuDeploy.getDeviceInfo({
    host: '',
console.log(deviceInfo.serialNumber); // yay no more kebab-case keys!
console.log(deviceInfo.eveloperEnabled); //prints `true` (as boolean, not string)

Added some more message grabbing logic

We found (and fixed) a few bug in the roku-deploy zip upload response parsing. There were several additional errors that could be scraped from the response which would provide much better information about why certain issues were occurring. We now properly parse those errors and include them as part of any emitted error. Check out roku-deploy@127 for more information.

Wait for file stream to close before resolving promise

We fixed a bug in roku-deploy where we weren’t waiting for the downloaded file stream to close before resolving the file path promise. When used in CI environments, this could actually cause the CI job to fail because sometimes the file wasn’t finished downloading, which could trigger “file not found” errors if the CI moved ahead too quickly.

Preview features

We made a lot of great progress again this month on the latest brighterscript v0.66 alpha releases. Here are some of the highlights:

Added ifDraw2d to roRegion interface

We added the ifDraw2d items to the roRegion interface, which should result in much better completions and validations when interacting with those items.

Completion performance

We fixed some performance problems when computing completions. Since completions are one of the most time-sensitive operations, this was a huge win! Here’s how the flow was changed:


for each scope file is in
    on file text completion,
        for each scope the file is in
             get all symbols from entire symbol table


on file text completion,
    get symbols local to the completion position
    for each scope the file was in
         get symbols available at scope level
         get symbols based on namespace inclusion
   get the symbols from global scope

remove duplicates of same completion across different projects

Interface optional properties

We’ve finally added the ability to mark interface (and class) properties as optional. These properties can be omitted from interfaces without fear of validation errors when they’re missing (i.e. the entire point of optional properties…).


Better go to definition support

We added fixes for the “go to definition” logic so that it finds classes and interfaces.

Here’s it working in action! goto-definition

Performance fixes

The v0.66 alphas are known to be slower than the mainline release. Much of this is attributed to the significant amount of additional validation being performed. This month we focused heavily on finding performance improvements to try and gain back some of that lost speed. We found several ways to carve out some more performance:

  • per-scope symbol caching that only invalidates a single scope when that scope changes~
  • Smarter validation, so that only statements in a file that require re-validation for each scope get re-validated
  • A few small ReferenceType bug fixes
  • Fixes ifDraw2d,DrawScaledObject overload
  • Rewrote ScopeValidator.iterateFileExpressions to do work on an AST Walk
  • Uses better caching of Namespace data when linking scopes

npm run benchmark -- --targets validate :

Current release-0.66:

validate@local  ----------- 8.252 ops/sec
validate@0.65.8 --------- 104.338 ops/sec

Implementing algorithm above:

validate@local  ---------- 21.253 ops/sec
validate@0.65.8 --------- 105.035 ops/sec

We also found some significant performance improvements during the program validation cycle. Let us know if you are still seeing performance issues while validation your projects. Check out the benchmark results (higher is better):

    validate@local   --------- 271.514 ops/sec
    validate@0.65.10 --------- 101.219 ops/sec

⚡ Huge improvement to validation times! ⚡

How it works:

  • When a file changes:
    • the file is broken down into individually-validatable segments (eg. each function statement, etc.)
    • each segment is indexed for symbols it requires to compile
    • all the symbols that are available externally to the file are indexed.
    • all the symbols that file requires are indexed (this is a Set of all the required symbols for all its segments)
    • all the symbols that have incompatible types to the previously-known types are marked especially.
  • When there’s a program validation
    • for the files that changed, check to see:
      • if there are any “duplicate symbols” in the same scope
      • if the required symbols are are compatible for each scope the file is in
      • if there’s a required symbol that is not in any scope
    • do a scope validation, but also pass along a reference to files that have changed, and what the changes are
  • When validating a scope:
    • Mark each diagnostic if it was generated for a full scope validation vs. a validation based on an AST segment
    • clear any previously generated diagnostics if they were on a full scope validation
    • for each file in the scope
      • validate the entire file if it’s changed - but only once!
      • if it hasn’t changed, and it does not require any symbols that have changed since last validation, do not validate!
      • if it does require something that has changed, than only for the segments in the file that require that symbol, validate the segment.
      • do appropriate clearing of diagnostics attached to the scope based on file and segment re-validations.

Cache range and position

We found another area for performance improvement by caching the range and position objects since they’re immutable. This helps reduce memory footprint and garbage collection churn. Also after a range has been created at least once, it should theoretically cost less CPU cycles since we just look it up from the cache instead of building a new one.

For a large private project:

location cache: DISABLED
build: (8s191.958ms) (7s967.413ms) (8s222.454ms) (8s035.043ms) | (avg: 8.104217seconds)
watch: (8s103.313ms) (8s104.476ms) (8s200.875ms) (8s100.466ms) | (avg: 8.127283seconds)

location cache: ENABLED
build:  (7s965.510ms) (7s977.796ms) (8s167.780ms) (7s960.608ms) (avg: 7.7679235seconds)
watch:  (7s873.529ms) (7s967.520ms) (8s018.557ms) (8s091.832ms) (avg: 7.9878595seconds)

For the same project, we end up caching a ton of ranges and positions. As the app lives in watch mode for a while, I would expect more and more of those locations to get recycled:

cold boot:
Ranges:    254,551 cached 193,085 not cached
Positions: 418,227 cached   10,588 not cached

after 4 watch validations:
Ranges:    392,111 cached 163,335 not cached
Positions: 418,227 cached  10,588 not cached

Fix class fields using constructors not transpiling correctly

If there was a class field that used a constructor, the constructor was not namespaced correctly in the transpilation. This fixes that issue.

namespace MyNS
    class KlassOne
        other = new KlassTwo()
    end class
    class KlassTwo
    end class
end namespace

Now properly transpiles to:

unction __MyNS_KlassOne_builder()
    instance = {}
    instance.new = sub()
        m.other = MyNS_KlassTwo() 'This was the issue before, it used to transpile to `KlassTwo()`
    end sub
    return instance
end function
function MyNS_KlassOne()
    instance = __MyNS_KlassOne_builder()
    return instance
end function
function __MyNS_KlassTwo_builder()
    instance = {}
    instance.new = sub()
    end sub
    return instance
end function
function MyNS_KlassTwo()
    instance = __MyNS_KlassTwo_builder()
    return instance
end function

Fixes operator order for not keyword

We fixed a bug in the brighterscript parser related to how the not keyword is interpreted.

function stringHasStuff(myStr as string) as boolean
    return not myStr = ""
end function

The parser treats this as (not myStr) = "" instead of how Roku treats it: not (myStr = "")

As a result, here’s the fix: Before:

not myString = ""` -> `BinaryExpression<=>(UnaryExpression<not>(myString), "")


not myString = ""` -> `UnaryExpression<not>(BinaryExpression<=>(myString, ""))

This might not mean much to you, but just know that your code is now being interpreted (and transpiled) more accurately now.

For Contributors

Add create-package label build script

We’ve added a new github label called create-package. This will leverage GitHub actions to build one-off npm package releases for any git branch. This makes it very easy to test a pull request without needing to clone the repo or do npm link shenanigans. Simply set a create-package tag on the PR, then the bot will comment when the build is ready to test.

This is available in:

  • brighterscript
  • brighterscript-formatter
  • roku-deploy
  • roku-debug
  • brs
  • @rokucommunity/bslint

Here’s what the comment on PRs looks like:


Enable noUnusedLocals tsconfig flag in vscode-brightscript-language

We’ve enabled the noUnusedLocals flag in the tsconfig in vscode-brightscript-language and cleaned up all the warnings that introduced. This means that you’ll now see typescript warnings and errors for unused variables. This also applies to unused imports.


Fix watch-all for alternate cloned dir name

Contributors often follow this guide for setting up their RokuCommunity development environment. We fixed a few bugs in d816cce related to how the watch-all command works when you cloned vscode-brightscript-language to a folder not named vscode-brightscript-language. So you probably won’t notice, but it works better now.

Fix watch-all problem matcher and output

In the spirit of fixing problems with watch-all, we also fixed the problem matcher in the watch-all task when running the RokuCommunity workspace. This is fixed by emitting absolute paths instead of relative paths, which then work for both the workspace and individual folders. Just like the last item, this is just a small quality of life thing.

Move common user input into a dedicated manager

For contributors of the vscode-brightscript-language, we’ve created a set of helper functions that reduce duplication when prompting for common user input (such as host, password, etc). This first iteration introduces a promptForHost method into a UserInputManager class so we can start centralizing our user input flows and sharing them to unify the experience across the extension.


roku-debug upgrades to new deviceInfo api from roku-deploy

We discovered that several different places across the RokuCommunity projects that all ran similar device-info requests. We have eliminated this duplication by leveraging the getDeviceInfo() call from roku-deploy. In the future, all device-info calls should be handled by using that singular interface.

Thank you

Last but certainly not least, a big Thank You to the following people who contributed this month:

Contributions to vscode-brightscript-language:

Contributions to brighterscript:

  • @markwpearce (Mark Pearce)
    • Fixes operator order for not keyword (PR #932)
    • Fix class fields using constructors not transpiling correctly (PR #933)
    • More performance fixes (PR #936)
    • Validation Performance: File level providedSymbols and requiredSymbols (PR #944)
    • Fixes Transpilation bug - enums as class initial values (PR #949)
    • Enums as class initial values (PR #950)
    • Interface optional properties (PR #946)
    • Fix for the bad fix (PR #952)
    • Fix for the fix (master version) (PR #953)
    • Completion performance (PR #958)
    • Reverse compatibility fixes (PR #959)
    • Added ifDraw2d to reRegion interface (PR #960)
  • @TwitchBronBron (Bronley Plumb)
    • ci: Don’t run test-related-projects on release since it already ran on build (157fc2ee)
    • Fix issue with unary expression parsing (PR #938)
    • Cache range and position (PR #940)
    • Add create-package label build script (PR #945)
    • Better go to definition support (PR #948)
    • Fix param order for AST class constructors for interface/class members (PR #954)
    • Remove v8-profiler from dependencies (1287a5d7)

Contributions to roku-deploy:

Contributions to roku-debug:

Contributions to brs:

Home What's new Slack channel Newsletter Edit this website on Github

Legal notice

The views and opinions expressed in this website and all of its pages are those from within our open source community and do not represent the views or positions of Roku, Inc. or any entities they represent.

Designed with favorite by @arturocuya, powered by Astro