Configuring CLI Settings

Importing Components

ūüĎČ

If you haven't already installed the CLI and initialised your settings files, please see

Component Settings

The import settings file .interplay/interplay.config.js controls the CLI import and allows you to run the import repeatedly - either against your local repo, or automatically as part of a CI process.

In this section we will work through the import in stages, adding configuration settings used by each stage.

1. Build your code

Update the interplay.config.js to specify the packages you want to import.

For each package, provide an index file to use as the entry point. For example, to import the @auth0/cosmos package we can add configuration as follows:

//interplay.config.js

framework: 'react',
packages: [
  {
    name: "@auth0/cosmos",                          //Name of package to import
    packagePath: "./core/components/package.json",  //Path to package.json
    src: "./core/components/index.ts",              //Entry file for component parsing
    build: "./core/components/index.ts",            //Entry file for component bundling
    ignoreExports: [],                              //Exports to ignore when parsing
  },
],

//Specify any npm packages to build as externals (react and react-dom are set automatically)
peerDependencies: {},


//aliases to use for resolving import paths for parsing and bundling
alias: {
  //"<alias string>": "./aliased/relative/path/"
},

When you have entered your package settings, use the CLI to run webpack:

interplay build

The CLI will output the build(s) to the .interplay/stage/files/interplay/build folder, ready for deployment to Interplay.

2. Generate component config

We use the same package settings to parse your components.

interplay parse

The CLI uses the index file to find the source code of your components and parse the components and properties to generate JSON config describing your components.

This generated config is output to .interplay/stage/files/interplay/config folder, ready for deployment to Interplay.

3. Generate preset config

Once we have parsed the components we are ready to generate presets.

Presets are useful configurations of your components that your designers can use as a starting point for using the component in your design tools.

You can create presets through the Interplay UI, but it usually easier to generate them from JSX in your repo.

First we need to update the config file to tell the CLI which files to look in for instances of your components. We can do this with the presetPaths setting:

//PRESETS SETTINGS in interplay.config.js

//Specify files to parse looking for component instances to use as presets.
//Can be list of local paths or glob pattern.
presetPaths: ["core/components/**/*.story.tsx"],

//Specify rules for which component instances parsed should be used as presets
presetRules: [
  {
    //Specify which preset file(s) to match with this rule
    presetPath: ".*/{componentName}.", //e.g. matches component instances whose name matches first part of filename
    limit: 5, //per-component, per-file limit
  },
  {
    //match component instances whose name matches folder in preset file path
    presetPath: "{componentName}/",
    limit: 5,
  },
  //add more preset rules here
],

Now we can run the parsing again. This time it will parse both the components and the usage examples:

interplay parse

The output config should now include components and presets.

4. Deploy to Interplay

Now we are ready to deploy the build and generated config JSON to Interplay. To do this, run the deploy command:

interplay deploy

The deploy command will load and validate the config found in the stage folder and send the build and JSON to Interplay.

At the end of the deploy, a URL will be provided to click through to your project in Interplay.

5. Save the settings in your repo

Once your import is working correctly, you can save your settings to your repo.

Please check in the following files in your .interplay folder:

  • interplay.config.js
  • targets.json
  • config.d.ts

The files generated during the run of the CLI can be excluded from the repo. For git you can do this with an entry in your .gitignore file

# .gitignore - exclude subfolders of .interplay 
# unless you have saved required files in those subfolders

.interplay/*/

Generating component presets

Presets are useful configurations of your component that act as starting points for your designers to use - they are the configurations used when they drag and drop a component onto the canvas in Figma and Interplay.

You can generate preset configuration for your components from your repo. You do this by configuring the CLI to parse files in your repo containing instances of your components.

You specify which files to parse in the presetPaths setting of interplay.config.js

During its run, the CLI will first find all the components in the import, then parse the presetPaths files. It will identify known component instances from the components it has found.

For example, when importing the reactstrap components, we may also configure this file as one of the presetPaths for the repo:

//buttongroup.example.js
import React from 'react';
import { Button, ButtonGroup, Stack } from 'reactstrap';

export const Example = () => {
  return (
		<Stack>
	    <ButtonGroup>
	      <Button>Button</Button>
	      <Button>Button</Button>
	      <Button>Button</Button>
	    </ButtonGroup>
		</Stack>
  );
}

The CLI would find the instances of Stack, ButtonGroup and Button and the content they contain.

These component instances are then compared with presetRules to determine which should be created as presets in Interplay. By default we set these rules to pattern-match the ButtonGroup instance in the buttongroup.story.js file, Card in card.story.js file etc. This is to allow you to parse your existing storybook or documentation files but control which instances are used as presets.

For even greater control of your presets, we recommend either:

  • Create dedicated preset files for Interplay to parse.
  • Adding data attributes to your specific examples to mark them for import.

For more information on these options, please see

Preset File Format Support

You may already have suitable configurations of your components that you use as documentation files (e.g. storybook files), in which case you can configure those files as your presetPaths in `interplay.config.js`.

Javascript

The CLI extracts examples from .js and .jsx files by first attempting to require the files using babel-register, so that data imported from other files and dynamically generated component instances can be found.

If the file cannot be loaded (required), or no component instances are exported from the file then the CLI falls back to using static analysis. In this mode the file is parsed to AST and the syntax scanned - this allows basic component usages to be found but does not support dynamically generated content.

The parser will recognise exported instances of components as in the example above and Storybook format files using both Storybook's Component Story Format (CSF) and the older storiesOf format, in both typescript and javascript. We recommend using the newer Component Story Format. For more details of this format, please see

Typescript

The CLI extracts examples from .ts and .tsx files in the same way as for javascript files - it first tries to run the example. If it can't load the file it falls back to static AST analysis.

Markdown

When extracting examples from Markdown files the CLI first examines the Markdown file to extract javascript or JSX code blocks. It then processes those sections in turn, treating them as independent fragments of code to process.

Here is an example file written in Markdown:

image

In this case Interplay will find the jsx fragment and then parse the 5 different Button configurations from within it.

As outlined above, Interplay can parse your existing repo files to find examples to get you up and running quickly. Depending on how your existing files are structured, this approach may be sufficient. Running the CLI again will import any example changes in those files into Interplay.

Preset Rules

You specify presetRules in interplay.config.js to specify which instances of components found in your presetPaths should be matched and used as presets.

Preset rules use simple regex patterns to match component instances

A component instance will be converted into a preset if it matches any of your preset rules.

If you use the {componentName} string in a rule, it will be substituted before attempting to match the rule.

Here are some examples of preset rules:

presetRules: [
    {
        //match component instances whose name matches first part of filename
        "presetPath": "/{componentName}.",                                  
        limit: 5      //per-component, per-file limit
    },
    {
        //match component instances whose name matches folder in preset file path
        "presetPath": "/{componentName}/",                                  
        limit: 5     
    },

    {
        //match all component instances in Icons.stories.js
			  //whose source path 
        "presetPath": "lib/components/icons/Icons.stories.js",           //rule only applies to this file
        "componentPath": "lib/components/icons/*.*"                      //match components whose source resolves here
    },
    {
				//always true - matches every component instance
        "presetPath": ".*",
    },
],

Custom component builds

ūüĎČ

If needed, you can customize the CLI's webpack settings, precompile your source files or provide a completely custom build of your components. You do not normally need to modify your component source code for Interplay.

The Interplay CLI uses webpack to bundle your code source code packages with a default set of loaders and plugins.

The default behaviour is to use the src value in your interplay.config.js settings as the entry point for parsing and bundling each package:

//interplay.config.js
{
   "framework": "react",
   "packages": [
      {
         "name": "your-package",,
         "packagePath": "package.json",
         "src": "src/index.js"//used for parsing and bundling by default
      }
   ]
}

In many cases these defaults will work, but you can customize the build process using one of the following 3 approaches.

Customizing the CLI's webpack config

In some cases you may want to modify the webpack config that the CLI uses to bundle your index file defined in your interplay.json file. You can do this by defining a webpackConfig modifier function in an interplay.js file along side your interplay.json as shown below. This function is called immediately before calling webpack.

In this example we are modifying an individual loader configuration in the webpack config:

//interplay.js
module.exports = {
    modifiers: {
 /**
         *@paramconfig - the webpack config used by the CLI
         *@paramwebpack - the instance of webpack
         */
webpackConfig: (config, webpack) => {
//see existing loader rules
            config.module.rules.map((rule) => {
                console.log(rule);
            });
//Find the sass loader config
            const sassRule = config.module.rules.find((rule) => {
                return rule.test.source == "\\.sass$");
            });
//set custom sass loader config here
            sassRule.use = [  ]
            return config;
        },
    },
};


In this example we replace all of the CLI's default loader rules with our own:

const myCustomWebpackConfig =require("./custom/config");
//interplay.js
module.exports = {
    modifiers: {
webpackConfig: (config, webpack) => {
            config.module.rules = myCustomWebpackConfig.module.rules;
            return config;
        },
    },
};


When modifying the default webpack config, please leave the "entry" and "output" values unchanged. This ensures the CLI will find the files it expects.

Precompiling your source files

Instead of allowing the CLI to compile your source code, you can use your own process to compile your individual files. Typically you may have a custom build script that handles your CSS pre-processing, or uses babel to transpile javascript files.

As a simple example, you might use the @babel/cli to transpile your directory of code, including your index file.

npx babel src --out-dir dist
Successfully compiled 106 files with Babel.


In this example babel picks up its config from the local babel configuration file such as .babelrc or babel.config.js and transpiles each file in the src directory to an equivalent file in the dist directory. This includes the src/index.js file, which is transpiled to dist/index.js

You can then tell Interplay to bundle your transpiled files by adding a build.entry to interplay.json for each package that points to your compiled index instead of your source index:

//interplay.config.js
{
   "framework": "react",
   "packages": [
      {
         "name": "your-package",
         "packagePath": "package.json",
         "src": "src/index.js",       //parsing entry point
         "build": "dist/index.js".    //bundling entry point
      }
   ]
}

Here the CLI will still use src/index.js as the entry point for parsing, but now it will use dist/index.js as the entrypoint for webpack bundling.

Providing a custom build

Alternatively you can create your own build of your components to use in Interplay.

Your bundle should:

  • be a¬†UMD,¬†commonjs¬†or¬†commonjs2¬†library
  • be a single file
  • have any shared peer dependencies defined as external to the build e.g. react, react-dom, emotion, styled-components

Here is an example webpack config that does this:

//custom.webpack.config.js
const path =require('path');
module.exports = () => ({
  mode: 'production',
  entry: './src/index.js',//your component index path
  output: {
    path: path.join(process.cwd(), 'dist'),//output folder
    filename: 'bundle.js',//output file name
    libraryTarget: 'umd',//or commonjs or commonjs2
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        use: {
          loader: 'babel-loader',
          options: {
            cacheDirectory: true
          },
        },
      },
//add more loaders here if required for your files
    ],
  },
  resolve: {
     alias: {
//add any required aliases here
     }
  }
  externals: {
    react: {
      root: 'React',
      commonjs2: 'react',
      commonjs: 'react',
      amd: 'react',
    },
    'react-dom': {
      root: 'ReactDOM',
      commonjs2: 'react-dom',
      commonjs: 'react-dom',
      amd: 'react-dom',
    },
  },
});


If we save this file as custom.webpack.config.js, we can run the webpack cli:

npx webpack-cli --config custom.webpack.config.js
//output
asset bundle.js 145 KiB [emitted] [minimized] (name: main) 1 related asset
webpack 5.43.0 compiled successfully in 3477 ms


This creates a build at dist/bundle.js, as specified in the output section of the webpack config above.

We can now tell the CLI to use this bundle by setting it as the entrypoint of the build:

//interplay.config.js
{
   "framework": "react",
   "packages": [
      {
         "name": "your-package",
         "relativePath": "package.json",
         "src": "src/index.js",            //parsing entry point
         "build": "dist/bundle.js".        //bundling entry point
      }
   ]
}

Now when we run the CLI again...

interplay

...it will use src/index.js as the entrypoint for parsing the component source, and use the dist/bundle.js as the build. If dist/bundle.js is a UMD file it will be deployed unchanged. If it is a commonjs or commonjs2 file the CLI will wrap it as a UMD bundle for deployment to the browser.

Wrapper components

ūüĎČ

You can provide a wrapper component to supply required context and styles to your components.

Using an existing wrapper component

If the packages the CLI is importing already export a wrapper component you can configure this in the advanced settings page of the import wizard, or add it directly to the interplay.json settings file.

The setting takes the form packagename/ExportName:

//e.g. package
"wrapperComponent": "mypackagename/ThemeProvider"


//e.g scoped package
"wrapperComponent": "@myscope/mypackagename/ThemeProvider"

Interplay will wrap your components in this wrapper component wherever it displays your components.

Your wrapper component must be exported from one of the packages you are importing to Interplay (otherwise Interplay doesn't know about it).

Note that if your components require a theme to be present, your wrapper component should set a default theme. Interplay will soon support supplying configuration to the wrapper component such as multiple themes.

Create a custom wrapper component

If you don't have an existing wrapper component you can implement a custom wrapper as follows.

1. Create the wrapper component code

Create a wrapper component that supplies any required Providers.

  • It should accept and render the children prop
  • It should set default values for any required props such as theme. (Interplay will support passing configuration to the wrapper)

Here is an example wrapper. As you can see, you can use multiple layers of providers if required:

import React from 'react';
import {Provider as StyletronProvider} from 'styletron-react';
import {Client as Styletron} from 'styletron-engine-atomic';
import {BaseProvider} from '../../src/index.js';
import {LightTheme} from '../../src/themes/index.js';
const engine = new Styletron();
export constThemeWrapper = ({children}) => (
    <>
      <StyletronProvidervalue={engine}>
      <BaseProvider
theme={LightTheme}
      >
        {children}
      </BaseProvider>
    </StyletronProvider>
    </>
  );

Once you've created your wrapper code, you can save this to your existing components folder, or store in the interplay folder in your repo.

2. Add the wrapper component to your index file

For Interplay to be able to run your wrapper component, it needs to be included in the components build created by the CLI.

To do this, add your wrapper to the index file exporting your components:

//index.js
export { ThemeWrapper } from "./components/ThemeWrapper.js"


3. Configure the wrapper component in interplay.json

Next we tell Interplay which of your components to use as the wrapper. To do this you add a setting to interplay.json as outlined above for existing wrapper components.

"wrapperComponent": "your-package-name/ThemeWrapper"


4. Re-run the CLI

Once the wrapper is added to the index, the CLI will then pick it up on the next import run, bundling it with the other components as usual.

If you are using a custom build process you will need to re-build your index after this wrapper component has been added, and then run the CLI.

Deploying to multiple projects

Sometimes it is useful to deploy the same code library to different projects in Interplay.

You can do this using the --targets option.

interplay init --target mytarget

This will popup the browser to initialise the new target and populate the team and project for mytargetin your targets.json file. Your existing interplay.config.js is preserved.

You can then run the other CLI commands as normal, and append --target mytarget to use your new target.

CLI settings types file (config.d.ts)

Here is the current content of the config.d.ts file referenced to validate your interplay.config.js file in your IDE. This d.ts file is created when you first install the CLI

config.d.ts (CLI v2.0.0-beta.6)

Example CLI settings

Perhaps the easiest way to understand the CLI settings is to see how they can be used to import other code libraries.

This section contains settings we've used to import some open source code libraries, along with some quick points highlighting why the approach was taken in each case.

Where possible we have re-used existing preset files from the live repos to show you how to do that in different situations. In practice you may want to create a curated set of preset files containing the most useful configurations for your designers to use in their design tools.

Javascript libraries

Reactstrap

Javascript library generating presets from javascript files. Uses external css.

Reactstrap is a set of React components that act as a wrapper for the Bootstrap CSS library. The react components themselves don't contain the bootstrap CSS, but instead require it to be included on any web page using the reactstrap components.

The import steps we used for Reactstrap are:

  1. git clone https://github.com/reactstrap/reactstrap to clone a local copy of the repo
  2. yarn to install the package dependencies
  3. yarn build to run the existing reactstrap build - building src/index.js into dist/reactstrap.js
  4. Inspect the repo to find where the files to use for preset are stored - in this case in docs/lib/examples/*.js
  5. interplay to run the CLI, entering enter the settings below:


module.exports = () => {

   return {
      framework: "react",
      presetPaths: [
         "docs/lib/examples/*.js"
      ],
      includePaths: [
         "https://maxcdn.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css"
      ],
      deployPaths: [],
      packages: [
         {
            name: "reactstrap",
            packagePath: "./package.json", 
            src: "src/index.js",
            build: "dist/reactstrap.js"
         }
      ],
      devBuild: true,
      presetRules: [
			{
				presetPath: ".*/{componentName}.",
				limit: 3
			},
			{
				presetPath: "{componentName}/",
				limit: 3
			},
		],
   }
}

In this case we enter the full URL to the css we want included on every page using the components, because it is not a local file. For other libraries we can enter a relative path here to deploy and include the local CSS that is produced in their build.

The CLI will:

  • Use the¬†src\index.js¬†file to find all the component source files and parse them for properties
  • Use webpack to bundle the¬†dist\reactstrap.js¬†file and deploy it to Interplay
  • Add config to ensure the CSS is included on every page with the components

Typescript libraries

Ant Design

Typescript library with markdown example files

For our sample import we will use the CSS file that is output from the build.

  1. git clone https://github.com/ant-design/ant-design.git to clone a local copy of the repo
  2. yarn to install the package dependencies
  3. yarn build to run the repo build. This outputs the build as dist/antd.js and dist/antd.css
  4. Inspect the repo to find where the example files are - in this case they are in markdown files in demo folders alongside each component. So we use a wildcard for the component folder name and another wildcard for the filename: components/*/demo/*.md
  5. interplay to run the CLI, entering enter the settings below:
module.exports = () => {
    return {
      framework: "react",
      packages: [
        {
          name: "antd",
          packagePath: "./package.json",
          src: "components/index.tsx",
          build: "dist/antd.js"
        }
     ],
      includePaths: [
        "dist/antd.css"
      ],
      deployPaths: [],
      presetPaths: [
        "components/*/demo/*.md"
      ],
      peerDependencies: {},
      presetRules: [
        {
	        //match component instances whose name matches first part of filename
	        presetPath: ".*/{componentName}.",
	        limit: 5
        },
        {
	        //match component instances whose name matches folder in file path
	        presetPath: "{componentName}/",
	        limit: 5
	       }
        //add more preset rules here
      ]
    };
  };