Contribution Point

In many cases, you would like to allow users to have fine-grained control over the exporter behavior, and maybe deeply integrate it with the Supernova interface as well. For this reason, exporters have access to contribution points, which allow them to directly integrate with Supernova.

Using contribution point

Contributions are done by expanding exporter.json with contributes key (object). For example, this is how you would expand exporter.json to contribute user configuration into Supernova interface:

{
    "id": "io.supernova.html-preview",
    "name": "HTML Preview",
    "description": "Exporter for visual preview of design system elements",
    ....
    "contributes": {
        "configuration": [{
            "default": true,
            "key": "generateColors",
            "label": "Generate color list page",
            "type": "boolean"
        }]
    }
}

To use specific functionality within the contribution point, simply add one of the following keys with their respective sub-definitions. Each key defines one specific area of contributions:

  • configuration - Allows to define configuration values that can be changed by the user, and then accessed inside blueprints or to modify which files should be produced by the exporter.

Contribution Point: Configuration

Configuration allows you to expose certain properties of the exporter to the user without them needing to clone or fork your exporter to make the change:

    ....
    "contributes": {
        "configuration": [{
            "default": true,
            "key": "booleanKey",
            "label": "Boolean value you can set",
            "type": "boolean"
        },{
            "default": "SN",
            "key": "stringKey",
            "label": "String value you can set",
            "type": "string"
        },{
            "default": 42,
            "key": "numericKey",
            "label": "Numeric value you can set",
            "type": "number"
        }]
    }

There are three types of configuration options:

  • number forcing numeric input from the user of your exporter

  • string forcing string input from the user of your exporter

  • boolean forcing true/false input from the user of your exporter

Each of the entries in the configuration must define the following keys:

  • key that you will use to reference this configuration in your blueprints

  • label which is human-readable description that will show inside the interface

  • type which defines the type of the configuration - number, string or boolean

  • default value which is provided when the user doesn't override given values

You can declare an unlimited number of configuration keys, to give users as much flexibility as you want. User can override default values inside the settings for each exporter.

Using configuration inside blueprints

You can use configuration keys to decide the behavior from your code blueprints. To do that, simply call the configuration function @configuration() and access any of the declared configuration keys. For example, to generate class name with prefix provided by the user, you can declare the following:

   ....
    "contributes": {
        "configuration": [{
            "default": "",            // Empty prefix by default
            "key": "classPrefix",
            "label": "Prefix for all generated classes",
            "type": "string"
        }]
    }

Then, in your blueprint, simply retrieve that information:

{[ const className = "Colors".prefixed(@configuration.classPrefix) /]}

The resulting variable will contain a prefixed value if user-provided one, or just Colors if they did not.

Using configuration inside javascript

The same configuration can also be accessed inside your javascript code. To do that, simply access the configuration through Pulsar.configuration property:

/**
* Convert group into properly formatted header
*/
Pulsar.registerFunction("formattedGroupHeader", function (tokenGroup, showSubpath) {

    // Retrieve token group either including or not including the path to the group
    if (tokenGroup.path.length > 0) {
        let light = tokenGroup.path.join(" / ")
        let dark = Pulsar.classPrefix + tokenGroup.name // Class prefix used for group name
        return `<span class="light">${light} / </span>${dark}`
    } else {
        return tokenGroup.name
    }
})

Using configuration inside output.json

Lastly, you can use configuration to define the behavior of the file output. In this case, you can use your boolean keys to decide whether a file should be generated, or not. Simply add when to your output.json entry that defines one file:

{
    "blueprints": [
        {
            "invoke": "colors.pr",
            "write_to": "colors.html",
            "when": "generateColors"
        },
    ]
}

In this case, when generateColors is true, colors.html will be generated, but otherwise, it will be ignored and won't show at the output.

You can also use negation to take the opposite value:

{
    "blueprints": [
        {
            "invoke": "colors.pr",
            "write_to": "colors.html",
            "when": "!ignoreColors"
        },
    ]
}

This will generate colors.html only when ignoreColors is false. Note that in all cases, this key must be defined inside exporter.json in contributes.configuration, otherwise, an error will be thrown.

Behind the scenes: Right now, it is only possible to contribute dynamic configuration, but this feature will grow as we expand the capabilities of the system. Credits where due: Thanks to the VSCode developers for coming up with their contribution system in VSCode plugins, which inspired the creation of this system as well.

Last updated