Sandcastle (Alpha)
Sandcastle is a node.js server for local development that uses dynamic Webpack configs to allow you to test your React components in a browser in isolation.
How it works
The short version
// sandcastle.js
const startSandcastle = require('path/to/sandcastle');
startSandcastle({ webpackConfig: require('./webpack.config.js) });
// in your shell
node sandcastle.js
To test components/MyComponent.jsx
, simply load /components/MyComponent.jsx
on the Sandcastle server. For example, for a Sandcastle server running on localhost with port 3141 (the default port), you would load http://localhost:3141/components/MyComponent.jsx
in your browser.
The longer version
When you load /components/MyComponent.jsx
, the Sandbox server creates an instance of the Webpack compiler that serves compiled JS using webpack-dev-middlware
. Any requests for webpack assets from this page will connect to this same middleware so that content like hot module reloading data, source maps, dynamically-loaded chunks, and anything else served by webpack will re-use the same webpack compiler and middleware.
The file browser
Sandcastle mounts a file browser page at /
so that you can quickly search and open your files. This page will automatically be opened in your browser at Sandcastle startup if you set the openBrowser
config to true
.
Sandcastle components and module resolution
Sandcastle will look in three places for the component you're testing. A __sandcastle__
folder in the same directory as your component can be used to set up props and any other dependencies for testing your component.
- First, it looks for
__sandcastle__/${componentFilename}
in the component's directory. - If that file doesn't exist, it will look for
__sandcastle__/${componentFilenameWithJsxExtension}
in the component's directory. - If neither of those files exist, it will load the component from the path provided in the Sandcastle URL.
For example, if you're loading the TypeScript component components/MyTypescriptComponent.tsx
, it will look for component files in this order.
components/__sandcastle__/MyTypescriptComponent.tsx
components/__sandcastle__/MyTypescriptComponent.jsx
components/MyTypescriptComponent.tsx
This allows you to create a Sandcastle component that sets your component up for testing in the same language as the component or as a jsx file (or use the original component if your component does not require setup).
Environment
Sandcastle can use an Environment
component to set up depenencies common to all components you'll be testing. This is useful for setting up React context providers so that you can use context consumers in the components you're testing. The props for your Environment
component are set up using an environment setup function (except for children
, which depends on the component and layout being rendered). The path to the Environment
component module is set with the environment
config option.
Environment setup
You can define a module that's used to set up the props for your Environment
component. This module exports a function that will be run once when initially loading the sandbox page to create the environment props. It can be an asynchronous function if you need to do any asynchronous work to initialize the environment such as HTTP requests. The path to the environment setup module is set with the setupEnvironment
config option.
Layouts
When initializing Sandcastle, you can provide a layouts
object that maps layout names to file paths containing layout modules.
A layout module should export a React component with ony a children
prop.
To use a layout in Sandcastle, use the layout
query param to select a layout name. If no layout
query param is present, the layout under the default
key will be used. If no layouts are defined, the component will be renderd as-is on the page.
It may be useful to create a layout that centers a component for more pleasant development, and another with no margins or padding for components that take up the full page.
You may find it useful to use query params inside of layouts for more options (e.g. a background color).
API
startSandcastle(sandcastleOptions)
Starts the sandcastle server.
Example usage (with default options shown):
const startSandcastle = require('@change/sandcastle');
startSandcastle({
webpackConfig: undefined,
openBrowser: false,
port: 3141,
webpackPath: '__sandcastle_webpack',
hotMiddlewarePath: '__webpack_hmr',
sandcastleComponentFolder: '__sandcastle__',
environment: undefined,
setupEnvironment: undefined,
bootstrap: undefined,
webpackDefine: {},
layouts: {},
});
sandcastleOptions
webpackConfig
(required): The webpack config object to extend when creating Webpack middleware.openBrowser
(optional): Whether to open the Sandcastle homepage when running the server. Default: false
.port
(optional): the port to run the sandcastle server on. Default: 3141
.rootDirectory
(optional): The base path under which to look for files. Default: the current working directory of the sandcastle process.fileBrowserGlobPatterns
(optional): The glob patterns to use when listing and searching files in the file browser. You can use this to restrict file extensions or ignore certain directories (perhaps test files). Default: ['!(node_modules)/**']
(all files except those in node_modules).before
(optional): A function that takes Sandcastle's express app as an argument and is run before all sandcastle
routes. Can be used to add custom middleware or routes. Default: undefined
.apiPath
(optional): The path under which to serve the client REST api. Default: '__api'
.webpackPath
(optional): The path under which to serve webpack content on the Sandcastle server. Default: '__sandcastle_webpack'
.hotMiddlewarePath
(optional): The path under the webpackPath
under which to receive hot reload events. Default: '__webpack_hmr'
.sandcastleComponentFolder
(optional): If you want to use a directory name other than __sandcastle__
for storing Sandcastle components, you can specify an alternative name here. Default: '__sandcastle__'
.environment
(optional): Path to a module exporting an environment component.setupEnvironment
(optional): Path to a module exporting a function that creates the props for the environment component.bootstrap
(optional): Path to a module run before all other code on the Sandcastle client. Can be used to set up polyfills and other global dependencies.webpackDefine
(optional): Config object for Webpack's DefinePlugin
for inserting data in to the client bundle. Can be useful to make server data (such as configuration) available on the client for use in layouts or environment files. Default: {}
.layouts
(optional): Map of layout names to paths to their files. Default: {}
.
The following options can be passed as a function to allow a change in configuration depending on the path of the file loaded. The function will be called with the relative path to the component file requested.
webpackConfig
environment
setupEnvironment
bootstrap
webpackDefine
Developing
See DEVELOPMING.md