Using Javascript
Last updated
Was this helpful?
Last updated
Was this helpful?
Javascript is an all-around great scripting language well suited for code-generation tasks, especially if it is combined with a powerful templating engine such as Pulsar.
Pulsar allows you to extend the core language capabilities with javascript directly, allowing you to bring any functionality it doesn't provide out of the box.
To enable javascript extension of Pulsar blueprints in the exporter, add config
>js
key into the exporter.json
configuration pointing to the main javascript file you want to use, for example:
Adding "js": "support.js"
means that the exporter package will look for a javascript helper file in the root directory, under the name support.js
.
Your newly created support.js
file is your main entry-point to extend the language functionality of Pulsar. There are currently three main groups of functionality you can extend, each with distinct usage:
Adding new synchronous and asynchronous functions
Adding transformers, methods you can run on top of specific data types
Default data payload
Combined together, you have almost unlimited control over how the data inside blueprints is transformed and computed.
Pulsar allows you to call functions (with or without arguments) such as @ds.allTokens()
. This will fetch the data from the selected design system and give all of it as the result. Let's take the following example:
You can define a set of new non-native language functions as well, using the javascript. For example, what if we wanted to skip every N-th token @ds.allTokens()
function returns? To do that, we open our support.js file and pass the following code:
We are using Pulsar.
global-scope object to register new functionality into the language. Pulsar
is only available inside the file you have declared inside the exporter.json
configuration.
Once you've done that, the new function is immediately available in your blueprints! When used, the resulting array will be missing every n-th token:
Pulsar engine is in fact advanced enough to pick up this change right away even for your inline VSCode autocomplete, which you can validate by going to any blueprint and trying to write your function, starting with @
symbol - which signalizes that you are calling a function.
Note that all your custom functions are additionally automatically prefixed with @js.
and you must write them as such in your blueprint.
You can declare an unlimited number of helpers, with an unconstrained number of attributes. Over time, you can even create a library of custom functionality that you can be sharing between your different exporters.
In many cases, you might need promises instead of just plain functions. Pulsar lets you do that as well - in fact, the functions you see natively, such as @ds.allColorTokens()
are all using this advanced option. The great thing about promises in Pulsar is that they are automatically identified, analyzed and auto-awaited, so you can write serial code with a very complex "backend". Take the following example:
The dsm.allTokens()
is a function that selects an appropriate design system to target based on your selection, authenticates you, downloads data from Supernova servers, parses them, prepares them for easy use, yet all its thousands of lines of code powering it are completely hidden to you - allowing you to focus on what is important and that is the manipulation with that data.
In order to register asynchronous function, use the same approach as before, in fact, there is no difference between registering normal and asynchronous function because all is done automatically:
Also, the usage is the same as well:
Behind the scenes: This right here is the main reason why we have decided to build our own programming language from scratch.
The amount of stuff you have to code to properly translate design system data to code is staggering - and encapsulates everything from obtaining the data (such as Figma API), parsing, auth, writing the conversion scripts, adding automation servers, possibly containers, making it all robust.. It is so much and yet it will still break the second your tech stack, target platform, or the tooling changes.
Traditional templating languages are in no way capable of anything like this. With Pulsar, however, most of the data retrieval is a one-liner and you don't have to worry about anything mentioned above.
You can think of transformers as type-specific methods, for example, to make lowercase string:
Some transformers work on all data types (string, number, object..) while some only work on specific ones (such as lowercased that can only be used with strings). There is a whole native library of all base transformers you might ever need - but if you are missing some, you can create it yourself.
To do that, register a new transformer similarly to how you've done it with functions:
Then, you can use the transformer inside your blueprint, also available in code autocompletion:
Note that defining a transformer like this means it can be used on any data type. However, in this case, running the transformer on top of a string would result in strange results - so you can define transformers that are typed
and constrained only to one specific type:
This will properly force the transformer to do a type check and throw a proper error when an incorrect data type was provided. Allowed types are string
, number
, object
, array
and boolean
Transformers must always have at least one attribute (value), which is the value of the property they are transforming. Value is always sent the first. Transformers without values are not allowed (as it doesn't make sense), and you should use functions instead.
The last thing you can do is to provide the initial payload to blueprint execution. For example, say you want to have an exporter that can be easily reconfigured to either original or lowercased names of the tokens.
To achieve that, you can provide configuration object through the javascript, where will be available to all your blueprints and can serve as a single point of configuration that can be changed easily:
To access it, simply use it as you would use your own defined property inside the blueprint. The property is defined on a global scope and available everywhere.
Note: Payload is available as if you would declare it through a variable. You can think about it as global
variable in javascript, although it is immutable and can not be changed, so trying to override it will throw an error:
This is to highlight that purpose of providing the payload is to provide immutable, configuration data.
In order to prevent unintentional usage of the javascript engine and to make it as secure as possible to pass even the most strict enterprise data-protection requirements, we are running the javascript functionality in a sandboxed environment completely separately from the main execution system.
No javascript code has access to any functionality connected to the Supernova backend (your data) unless you specifically pass the data from the blueprint itself.
Additionally, for security reasons:
All the networking capabilities and access to the network have been removed
All the local file system capabilities have been removed
Date.now and new Date()
have been removed
RegExp.prototype.compile
have been removed
Math.random
and any other randomizer has been removed
All primordial
objects are non-extensible
All non-standard context properties have been removed
Additionally, no imports of npm modules are currently allowed. We are, however, working on adding this as soon as possible, as well as the ability to create the helpers with Typescript instead of Javascript.
Behind the scenes: Exporters have the ability to touch your sensitive data, such as design system tokens, components, documentation, and so on.
While we fully expect that before you use any community-built exporter, you will thoroughly inspect its code to be safe and happy (this is why no functionality of exporter is a black-box), we went to great extra lengths that potential data breach is not possible in the first place.
The execution of the exporter javascript code is done through a secure, fully contained sandbox, similarly to what Figma does with their plugin system. If you are interested in more details about the exporter security for enterprise purposes, we will be happy to answer any of your questions.