In this update
Welcome to the September 2023 edition of “What’s New in RokuCommunity.” Please consider subscribing to stay up to date with what’s happening in RokuCommunity.
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:
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 bslint#46: declutter multi-scope diagnostics. If you’ve been using bslint, you may have noticed many duplicate diagnostics whenever a lint issue is discovered across multiple components.
We’d like to improve this by emitting a single diagnostic that has relatedInformation
, which can be collapsed in vscode and generally is much easier for developers to interact with.
If you’re interested in working on this feature, please comment on the github issue or reach out to us on Slack
We adopted the brs project! brs was a BrightScript simulator that allowed for running brightscript (and some simple scenegraph) logic on Windows, Mac, and Linux. It ran on NodeJS, and was primarily developed to support the roca project for off-device testing.
Development on the original project stalled in September of 2021. We at RokuCommunity believe in the vision of brs, so after some discussions with the original author, we decided to fork the project in order to ensure its continued development.
Many thanks to @sjbarag (Sean Barag) for the incredible work building and guiding brs to what it is today, and to @lvcabral (Marcelo Cabral) for agreeing to pick up and maintain @rokucommunity/brs under the umbrella of RokuCommunity!
We are proud to announce the release of the @rokucommunity/promises library!
Much of this design is based on JavaScript Promises. However, there are some differences:
If you’ve been around the community for a while, you may have seen roku-promise which is a popular promise-like library that was created by @briandunnington back in 2018. @rokucommunity/promises is fundamentally different than roku-promise. roku-promise creates tasks for you, executes the work, then returns some type of response to your code in the form of a callback.
The big difference is, @rokucommunity/promises does not manage tasks at all. The puropose of a promise is to create an object that represents the future completion of an asynchronous operation. It’s not supposed to initiate or execute that operation, just represent its status.
So by using @rokucommunity/promises, you’ll need to create Task
nodes yourself (or timer, or observer), create the promises yourself (using our helper library), then mark the promise as “completed” when the task has finished its work.
Here’s a quick example of some of the awesome things you can do with promises. Assume you want to run the following logical flow:
Here’s an example of how you can do that using promises:
function logIn()
context = {
username: invalid,
authToken: invalid,
profileImageUrl: invalid
}
' assume this function returns a promise
usernamePromise = getUsernameFromRegistryAsync()
promises.chain(usernamePromise, context).then(function(response, context)
context.username = response.username
'return a promise that forces the next callback to wait for it
return getAuthToken(context.username)
end function).then(function(response, context)
context.authToken = response.authToken
return getProfileImageUrl(context.authToken)
end function).then(function(response, context)
context.profileImageUrl = response.profileImageUrl
'yay, we signed in. Set the user data on our scene so we can start watching stuff!
m.top.userData = context
'this catch function is called if any runtime exception or promise rejection happened during the async flows above
end function).catch(function(error, context)
print "Something went wrong logging the user in", error, context
end function)
end function
This month we improved the look of the node details page in SceneGraph inspector. It should feel a lot cleaner now with a proper table layout. Here’s a quick demo of the new layout:
Which is a nice upgrade from the old layout:
We’ve added a new panel called the Roku Test Automation panel. This enables you to create a series of remote or text input commands that can be run against a Roku device. It supports running automatically on launch, or can also be run manually.
Here are some highlights:
In the screenshot below, you can see an automation sequence that will run wait for 8 seconds, then press down
, down
, right
, ok
on the remote in order:
If you’re using the brighterscript import feature, the VSCode extension and brighterscript-formatter are now able to sort imports alphabetically. Just be sure to set "sortImports": true
in your bsfmt.json
or set "brightscript.format.sortImports": true
in your vscode settings. (#75)
Here’s the feature in action:
If your development team prefers to use tabs in your source code (instead of spaces), you may have noticed that sometimes the diagnostics printed in the console by bsc are not properly formatted. #f616265 fixes that now so everything should now line up properly!
Before:
After (fixed!):
Many BrighterScript diagnostics include related information (such as the path to a scope or file). These were showing in the vscode problems panel, but were surprisingly absent from bsc
command-line interface. As of brighterscript v0.65.5, the bsc
cli will now print related information as well.
This may not apply to most BrighterScript users, but for brighterScript plugin authors, we now more gracefully handle when brighterscript plugins inject AST that is missing .range
properties. (yay, no more crashes…we hope 🤞) (#869)
To reduce code duplication, brighterscript injects the bslib.brs
script into most SceneGraph complent xml, which includes several common library functions used to support features like the ternary operator or template strings. We discovered some issues where the script would randomly be injected into certain unexpected components. We’ve fixed the randomness, so the scripts are now inserted in a much more predictable manner. (#870)
There’s a new option in brighterscript called bslibDestinationDir
which you can use to specify what directory you want bslib.brs
to be written to. This can be useful when dealing with complex brighterscript library projects. (#871)
bsconfig.json
{
"rootDir": "./src",
"bslibDestinationDir": "./dist/roku_modules/bslib"
}
noproject
cli flagThere are situations where you might want to run the brighterscript CLI without loading a bsconfig.json
even though it’s present. Historically, you had to do some hacky workarounds like specifying a path to a non-existent file:
npx bsc --project "not-there.json"
Thanks to brighterscript#868, you can now specify --noproject
to explicitly disable bsconfig.json
loading. This will also force any --project
flag to be completely ignored. Here’s how you use it:
npx bsc --noproject
You can now add a bs_const
property to your bsconfig.json
. NOTE: these do not update the output manifest file, but they will impact the conditional compile items, allowing for a more consistent editing experience. (#887)
We hope to eventually have these values be included in the generated ouptput manifest, but that will need to wait until the file api lands in the mainline branch.
bsconfig.json
{
"rootDir": "./src",
"bs_const": {
"DEBUG": true,
"ENABLE_DEBUG_LOGGING": true
}
}
emitDefinitions
to docsBrighterScript has supported emitting type definitions (similar to TypeScript’s Type Declarations) for years now, but it was completely missing from the docs. We’ve fixed that! (#893)
Type definitions are super useful if you’re writing a library in brighterscript, but then distributing your library as brightscript code. The type definitions will support describing the way your code was originally written so that other brighterscript projects will be able to get better type validation.
someFile.d.bs
namespace player
enum PlayState
playing
paused
end enum
sub play(video as VideoMetadata)
end sub
sub pause(video as VideoMetadata)
end sub
interface VideoMetadata
url as string
subtitleUrl as string
end interface
end namespace
We fixed a bug in the brighterscript template string transpiler that was incorrectly calculating line and column numbers, which caused poor debugging experiences because the source maps were then incorrect as well. This has been fixed, so template strings should no longer cause frustrating line offets while debugging! (#921)
console.log(`
yay, I don't mess up line numbers
anymore
`);
We’ve added new information to the BrighterScript plugins documentation related to modifying the bsconfig values after the project has loaded (#913)’
Here’s an example brighterscript plugin example showing how to do exactly that:
import { CompilerPlugin, ProgramBuilder } from 'brighterscript';
export default function plugin() {
return {
name: 'addFilesDynamically',
beforeProgramCreate: (builder: ProgramBuilder) => {
if (!builder.options.files) {
builder.options.files = [];
}
builder.options.files.push({
src: 'path/to/plugin/file.bs',
dest: 'some/destination/path/goes/here'
});
}
} as CompilerPlugin;
}
This month we’re giving the brighterscript v0.66 branch its own entire section, because there has been a LOT of significant progress made. Almost all of this work has been implemented by @markwpearce (Mark Pearce), who is doing an incredible job moving us closer to the finish line.
You can try most of these out by installing the latest v0.66 alphas:
npm install brighterscript@next
We’ve started migrating bslint to the new BrighterScript v0.66 alphas. It’s a work in progress, but hopefully here soon we’ll have a version that is functional. In the mean time, you’ll need to disable bslint in the plugins
section of your bsconfig.json
because it will cause runtime errors.
Here are few of the PRs that went into prepping the project:
We now validate class method calls!
In some of the initial v0.66 alphas, we accidentally allowed users to write type cast expressions (thing as SomeType
) in plain brightscript projects. We now properly warn that the feature is only supported in BrighterScript.
We fixed a few bugs when hovering over certain types that was showing the wrong thing. Now we do a better job.
We now validate the actual return type vs. the declared return type. Subs and void functions will have validation errors when an actual type is included, and this works in .brs
and .bs
files!
Arrays can now have a type by defining them like <type>[]
(e.g. string[]
or SomeClass[]
). We also support multidimensional arrays (e.g. integer[][]
), as well as support overrides for built in methods for arrays (eg. push()
, pop()
) so it is consistent with the type. We will infer types from array literals as well, which can be very helpful for reducing needless as <someType>
code.
This feature is only available in BrighterScript.
We can now assign a specific type to classes, which then allow us to validate type mismatches for class members.
Line 3 should be an error because we’re assigning an integer to a string.
In order to significantly improve the type validation experience, we have now integrated all of the native BrightScript component types, SGNode types, and custom components into the type system. This will allow you to declare variables as those types, and then get a much richer editor experience and better type safety when you interact with those items. Here are some highlights:
Additions:
roDeviceInfo
, roBitmap
, roDateTime
, etc.) as types usable in Brighterscript, including completions on methods and documentationifDraw2d
, ifAppManager
, etc)roInputEvent
, roSGNodeEvent
)AssociativeArrayType
that can be built from an AA literal (eg. myAA = {name: “Mark”, coolFactor: 100}), and will correctly be compatible with interface types that have declared the same members, eg:interface Developer
name as string
coolFactor as integer
end interface
sub takesDeveloper(dev as Developer)
print dev
end sub
sub foo()
' no validation error, because AA meets required interface
takesDeveloper({name: "Mark", coolFactor: 100})
end sub
We have added SceneGraph nodes to type system. The type’s name is the component’s name prefixed with roSGNode … so roSGNodePoster
, roSGNodeRowList
, roSGNodeStdDlgTextItem
are all available as types (with completions, documentation, etc)
Custom components are also added (again, prefixed with roSgNode
). Eg:
<?xml version="1.0" encoding="utf-8" ?>
<component name="Widget" extends="Group">
<interface>
<field id="alpha" type="assocArray" />
<field id="beta" type="float" />
<field id="charlie" type="nodeArray" />
</interface>
</component>
This will create a type in the type system with name roSGNodeWidget
, with completions and type inferences for properties alpha, beta, charlie, as well as validation on methods like getChildren()
, subType()
, etc.
Adding string and number now gets flagged as a compile error. (#896)
Somehow when we were implementing brighterscript interfaces
, we forgot to support parameters in function declarations. That has now been fixed, so you can property define your functions parameters in interfaces.
We’ve fixed several issues around validation interface usage, as well as adding extra details about missing or mismatched members:
This section is still related to the 0.66 alphas, but focuses on BrighterScript plugin authors and how they interact with the BrighterScript lifecycle and AST.
As plugin authors, you may sometimes want to know about leading/trailing comments for a given expression or token. brighterscript#885 introduces a concept of “leading trivia” which will currently just contain leading comments. Function, class, method, field, interface, namespace statements return valid data from getLeadingTrivia() function. This should work with annotations as well.
Here’s an example showing how to get all comment statements for the first function in the file
import { Parser, TokenKind } from 'brighterscript';
const parser = Parser.parse(`
'writes a message to the console
sub consoleLog(message)
print message
end sub
`);
const trivia = this.ast.findChild(isFunctionStatement).getLeadingTrivia();
const comments = trivia.filter((x) => x.kind === TokenKind.Comment);
isLiteralInvalid
reflection method and added other isLiteral*
methodsBrighterScript plugin authors can leverage the isLiteral*
methods to make certain decisions about the AST they are interacting with. As a result of adding the type system, several of these methods got broken. We’ve fixed them, and also added any missing methods for types. (#902)
Historically the completions logic was fairly involved to write and maintain, because we were doing very specific lookups against specific AST items. With the new symbol table and type system, that can be significantly simplified, while also being far more accurate.
There’s not really a screenshot or demo to show with this, but just know that the completions are now much more maintainable and should more easily adapt to the changing type system.
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:
Contributions to brighterscript-formatter:
Contributions to bslint:
Contributions to brs:
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 @arturocuya, powered by Astro
by