UNPKG

@mapbox/batfish

Version:

The React-powered static-site generator you didn't know you wanted

190 lines (142 loc) 7 kB
# Advanced Usage ![](../batfish-ultra.png) ## Table of contents - [Draft pages](#draft-pages) - [Injecting data](#injecting-data) - [Routing within a page](#routing-within-a-page) - [Markdown within JS](#markdown-within-js) - [Route change listeners](#route-change-listeners) - [Analyzing bundles](#analyzing-bundles) - [Page-specific CSS](#page-specific-css) ## Draft pages Any page with the front matter `published: false` will be considered a draft page. In development, draft pages are built and visible. However, in [`production`] builds these pages are **not** included and should be handled with a 404 by the server. ## Injecting data Most of the time, you should store data as JSON or JS and `import` or `require` it as needed. Nothing special. If, however, you are dealing with *lots* of data; that data is used across a number of pages; and each of those pages does not need *all* of the data — then you may not want to write *all* that data into your JS bundles. You may want to control which parts of it get written to which bundles. You can do this with the [`dataSelectors`] configuration option. Store data in JSON or JS, anywhere in your project, then specify which data to inject into any given page with [`dataSelectors`] in your configuration. [`dataSelectors`] also have access to build-time data, like the front matter of all the pages being compiled. Each data selector creates a module that can be `import`ed to inject the return value into a component or page. **The return value of each data selector is the default export of the module available at `@mapbox/batfish/data/[selector-name-kebab-cased]`.** Example: ```js // batfish.config.js const myBigData = require('path/to/my/big-data.json'); module.exports = () => { return { /* ... */ dataSelectors: { posts: data => { return data.pages.filter(pagesData => /\/posts\//.test(pagesData.path)); }, fancyDesserts: () => { return myBigData.recipes.desserts; } } }; }; // Page import React from 'react'; import { DessertDisplay } from 'path/to/dessert-display'; import posts from '@mapbox/batfish/data/posts'; import fancyDesserts from '@mapbox/batfish/data/fancy-desserts'; export default class MyPage extends React.PureComponent { render() { return ( <div> <h1>Page!</h1> <h2>Posts</h2> {posts.map(post => { return ( <div key={post.path}> <a href={post.path}>{post.frontMatter.title}</a> </div> ); })} <h2>Desserts</h2> {fancyDesserts.map(dessert => { return ( <DessertDisplay key={dessert.id} {...dessert} /> ); })} </div> ); } } ``` ## Routing within a page If you'd like to use a client-side routing library *within a Batfish page*, like [React Router](https://reacttraining.com/react-router/) or [nanorouter](https://github.com/yoshuawuyts/nanorouter), add `internalRoutes: true` to the page's front matter. By specifying that the page has internal routes, any URLs that *start with* the page's path will be considered matches. If the page is `pages/animals.js`, for example, then `/animals/` will match as usual, but `/animals/tiger/` and `/animals/zebra/` will *also* match. The client-side router you use within the page can determine what to do with the rest of the URL. Look at [`examples/internal-routing`](../examples/internal-routing) to see how this works. ## Markdown within JS You can use [jsxtreme-markdown](https://github.com/mapbox/jsxtreme-markdown) within JS, as well as in `.md` page files. It is compiled by Babel, so your browser bundle will not need to include a Markdown parser! Batfish exposes [babel-plugin-transform-jsxtreme-markdown] as `@mapbox/batfish/modules/md`. The value of this (fake) module is a [template literal tag](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals#Tagged_template_literals). Any template literal with this tag will be compiled as Markdown (jsxtreme-markdown, with interpolated JS expression and JSX elements) at compile time. ```js const React = require('react'); const md = require('@mapbox/batfish/modules/md'); class MyPage extends React.Component { render() { const text = md` # A title This is a paragraph. Receives interpolated props, like this one: {{this.props.location}}. You can use interpolated {{<span className="foo">JSX elements</span>}}, also. `; return ( <div> {/* some fancy stuff */} {text} {/* some more fancy stuff */} </div> ); } } ``` ## Route change listeners To attach listeners to route change events (e.g. add a page-loading animation), use the [`route-change-listeners`] module. ## Analyzing bundles Batfish's `start` and `end` commands output [Webpack's `stats.json`](https://webpack.js.org/api/stats/) so you can use it to analyze the composition of your bundles. [webpack-bundle-analyzer](https://github.com/th0r/webpack-bundle-analyzer) and [webpack.github.io/analyse](https://webpack.github.io/analyse/) are two great tools that you can feed your `stats.json` to. There are also others out there in the Webpack ecosystem. ## Page-specific CSS Most of the time, you should add CSS to your site with the [`stylesheets`] configuration option. However, if you are adding a *lot* of CSS that is *not widely used*, you might choose to add it to one page at a time, instead of adding it to the full site's stylesheet. Batfish includes a way to to this. If you `import` a `.css` file within your [`pagesDirectory`], you will get a React component (with no props) that you can render within the page. When the component mounts, the stylesheet's content (processed through PostCSS, using your [`postcssPlugins`]) will be inserted into a `<style>` tag in the `<head>` of the document. When the component unmounts, that `<style>` tag will be removed. Like other React components, this one will only be added to the JS bundle of the page that uses it (unless you use it in a number of pages); and it will be rendered into the page's HTML during static rendering. So that's how you can page-specific CSS, when the fancy strikes. Example: ```js import React from 'react'; import SpecialStyles from './special-styles.css'; export default class SomePage extends React.Component { render() { return ( <div> <SpecialStyles /> <h1>Some page</h1> {/* ... */} </div> ); } } ``` [`route-change-listeners`]: ./batfish-modules.md#route-change-listeners [babel-plugin-transform-jsxtreme-markdown]: https://github.com/mapbox/babel-plugin-transform-jsxtreme-markdown [`stylesheets`]: ./configuration.md#stylesheets [`pagesdirectory`]: ./configuration.md#pagesdirectory [`postcssplugins`]: ./configuration.md#postcssplugins [`production`]: ./configuration.md#production [`dataselectors`]: ./configuration.md#dataselectors