Accessing Supernova Data

Exporters consist mostly of blueprints - "templates" that define exactly how code artifacts are generated from design data. An example blueprint might look like this:

{* Get screen data *}
{[ var screenData @project.screenById(screenId) /]}
{* Output base class *}
class {{ screenData.name.camelcased(true) }} {
init() {}
}

Blueprints combine a scripting language and a templating language into one, allowing you to access the data in the Supernova universal data model and immediately use them. The data can be accessed by the @ symbol, which signifies a function call:

@project.screenById(screenId)

There are dozens of functions, allowing access to every part of Supernova - from the definition of components and screens to navigation, animations and localizations... you name it. If you can set it inside the editor, you can access it inside your blueprint as well (but you can also access additional data usually hidden to users that Supernova computes over time, such as layout and behaviors).

In the example above, calling the function will retrieve all basic data about the screen specified by the screenId variable, returning a result something like this:

{
"id" : "Bhy-qP-ns7",
"name" : "Welcome",
"navigationBar" : {
"id" : null,
"isPresent" : false
},
"statusBar" : {
"isPresent" : true,
"type" : "light"
},
"tabBar" : {
"id" : null,
"isPresent" : false
},
"type" : "screen"
}

Glancing through the output, you'll see that we are currently generating code for a screen with the name "Welcome" - it shows a status bar and it has no tab bar or navigation bar. It has a unique id that can be used to retrieve more information about it, such as its component structure, any animation or navigation connection associated with it, and more. The id is the same as the one already defined in screenId property.

However, if you look closely at the blueprint, you'll likely notice that the screenId is not defined anywhere within the blueprint itself. This is because it comes from the blueprint Invocation configuration and is available as a global variable everywhere in the blueprint.

Blueprint Invocation

Some blueprints produce just one file. Good examples might be a README file or a CI configuration file -each project has exactly one. So when executed, the following artifacts are created:

ReadmeBlueprint.blazar
produces: root/README.md
CIBlueprint.blazar
produces: root/CI.yaml

However, in certain situations, you want to use the same blueprint to produce several files. A good example would be a class definition or screen file, different for every screen in the project. For such a scenario, you would want to achieve the following (depending on your platform):

ScreenBlueprint.blazar
produces: root/screens/Welcome.swift
root/screens/Login.swift
root/screens/Profile.swift

or maybe even a different structure, like this:

ScreenBlueprint.blazar
produces: root/Welcome/index.js
root/Login/index.js
root/Profile/index.js

Note that because you are building a generic system that will generate those files based on a Supernova project, you can't hardcode the names or the content as you would in case of the README file. This is achievable through blueprint invocation.

Each blueprint defines its invocation, which signifies what initial variables are defined in the blueprint and how many times the blueprint will run when executed.

Blueprint invocation can be set right inside the editor

Blueprint Invocation Types

Each type of invocation behaves differently, triggering file generation based on different aspects. Note that in all cases, you can always access all Supernova data using the @ notation.

Currently, the following invocations are available:

  • Once runs the blueprint one time

    • It gives no additional context information when executed

    • Very useful for generating files that are static, such as style definitions, configuration files, documentation, and similar tasks

  • Once Per Screen runs the blueprint once per every project screen

    • If there are X screens, the same blueprint will be executed X times

    • If there are no screens, the blueprint is not executed

    • screenId global variable is available for the entire blueprint and signifies the current exported screen

  • Once Per Component runs the blueprint once per every shared component

    • componentId global variable is available for the entire blueprint and signifies the current exported component

    • Note: only table views, collection views are currently considered shared

  • Once Per Tab Group runs the blueprint once per every project tab group

    • tabGroupId global variable is available for the entire blueprint and signifies the current exported tab group

  • Once Per Language runs the blueprint once per every project localisation language

    • languageId global variable is available for the entire blueprint and signifies the current exported screen, in ISO format

    • Every project has at least one "base" language, which currently defaults to "en". This means the blueprint will always execute at least once

    • If localizations are disabled, any additional languages will be ignored even if defined, generating only the "en" base language, running just once

  • Once Per Style runs the blueprint once per every style

    • styleId global variable is available for the entire blueprint and signifies the current exported style

  • Once Per Request runs the blueprint once, fetching remote data first

    • All data retrieved from the network call will be provided as a context for the blueprint, with variables named by the keys retrieved

    • The fetched data must be in JSON format

    • Note: This is currently an experimental feature

Now that you know how blueprints and exporters behave, it is time to build your first exporter! Let's get to it.