
Security News
Deno 2.2 Improves Dependency Management and Expands Node.js Compatibility
Deno 2.2 enhances Node.js compatibility, improves dependency management, adds OpenTelemetry support, and expands linting and task automation for developers.
@mapbox/batfish
Advanced tools
A minimalistic static-site generator powered by React and Webpack.
🚧🚧 WORK IN PROGRESS! 🚧🚧
Batfish provides the essentials for building excellent static websites with React and Webpack.
The CLI has three commands:
start
: Start a development server.build
: Build the static site.serve-static
: Serve the static site.All will look for your configuration module in the current working directory or where you specify with the --config
option.
For more details, run batfish --help
.
The Node API exposes three functions:
start(batfishConfig?: Object, projectDirectory?: string): void
: Start a development server.build(batfishConfig?: Object, projectDirectory?: string): Promise<void>
: Build the static site.
Returns a Promise that resolves when the build is complete.serveStatic(batfishConfig?: Object, projectDirectory?: string): void
: Serve the static site.In all of the above, the projectDirectory
argument is used to determine configuration defaults if you have not provided certain options (e.g. pagesDirectory
, outputDirectory
).
It defaults to the current working directory.
The structure of your pagesDirectory
determines the URLs of your site.
JS and Markdown files map directly to distinct URLs.
So src/pages/industries/real-estate.js
corresponds to the URL /industries/real-estate/
.
When a page is rendered, it is passed the following props:
location
: The browser's current Location.frontMatter
: The page's parsed front matter (minus any siteData
array)siteData
: Any site-wide data that the page has selected for injection.JS pages must export a single React component (either module.exports
(Node.js modules) or export default
(ES2015 modules)).
JS pages can include front matter within block comments, delimited by /*---
and ---*/
(see example above).
Markdown pages can include front matter, delimited by ---
and ---
, as is the norm.
These files are interpreted as jsxtreme-markdown, so the Markdown text can include interpolated JS expressions and JSX elements! They are transformed into React components.
All the props for the page (frontMatter
, siteData
, etc.) are available on props
, e.g. props.frontMatter.title
.
In jsxtreme-markdown components, you can specify modules to import and use within the interpolated code. By default, the following modules are specified (they are documented below):
const prefixUrl = require('batfish/prefix-url');
const routeTo = require('batfish/route-to');
This means that these modules can be used with no additional configuration.
Learn more about [security]({{prefixUrl('/about/security')}}).
Sometimes you need to put an asset at a specific URL.
A favicon.ico
in the root directory, for example; or a special image for social media <meta>
tags.
For this reason, any non-page files within the pages directory are copied directly into the same location during the static build.
When you access these files from pages, though, you need to use root-relative or absolute URLs.
That is, within src/pages/foo/bar.js
you cannot access src/pages/foo/bar.jpg
as bar.jpg
: you need to use /foo/bar.jpg
.
(You may want to prefix the URLs).
You can store data in JSON, anywhere in your project, then specify which specific data to inject into any given page.
To specify data, use the data
and dataSelectors
options in your configuration.
To select data for a page, then, provide siteData
front matter that is a sequence of strings, each representing one of the following:
data
object. In this case, the entire value will be injected.dataSelectors
object. In this case, the return value from that selector will be injected.Example:
// batfish.config.js
module.exports = () => {
return {
/* ... */
data: {
cta: 'Buy now!',
siteTitle: 'Place to buy things'
},
dataSelectors: {
posts: data => {
return data.pages.filter(pagesData => /\/posts\//.test(pagesData.path));
},
things: data => { /* ... */ }
}
};
};
// Page
/*---
siteData:
- cta
- posts
---*/
const React = require('react');
class MyPage extends React.PureComponent {
render() {
return (
<div>
<h1>Page!</h1>
<p>Here is our call to action: {this.props.siteData.cta}</p>
<h2>Posts</h2>
{this.props.siteData.posts.map(post => {
return (
<div key={post.path}>
<a href={post.path}>{post.data.title}</a>
</div>
);
})}
</div>
);
}
}
Note that adding the notFoundPath
property is optional in your batfish.config.js
. By default, it looks for a 404.js
in your directory. If you provide notFoundPath
a valid string path, the 404s will point to this absolute path.
In development, you can expect to test and see your 404 page by entering an invalid path. Locally if you run serve-static
, expect to see Cannot GET /yourInvalidPathHere
. In production
, your 404 page will need to be handled and rendered by the server.
Any page with published: false
in its front matter will be considered a draft page.
In development, draft pages are built and visible. However, in production
, these pages are not included in builds and should be handled with a 404 by the server.
By default, all CSS you include with Webpack (via require
or import
) will be bundled together.
During the static build, each page has the CSS relevant to it injected inline, and the complete stylesheet is loaded lazily, after the rest of the page is rendered.
Sometimes, however, you want to include CSS that will never be used on other pages, so you don't want it to be included in the complete stylesheet.
To do that, create CSS files within the pagesDirectory
— preferably adjacent to the page that uses them.
Import a page-specific CSS from the page that will use it.
It exports a React component that you should render in your page. For example:
const AboutCss = require('./about.css');
class AboutPage extends React.PureComponent {
render() {
return (
<PageShell>
<AboutCss />
{/* The rest of the page content */}
</PageShell>
);
}
}
If you'd like to use a different client-side routing library inside a page, like React Router or 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 within the page can determine what to do with the rest of the URL.
You can use jsxtreme-markdown within JS, as well as in .md
page files.
It is compiled by Babel, so will not affect your browser bundle.
To do so, Batfish exposes babel-plugin-transform-jsxtreme-markdown as batfish/md
.
The value of this (fake) module is a template literal tag.
Any template literal will this tag will be compiled as Markdown (with interpolated JS expression and JSX elements) at compile time.
const React = require('react');
const md = require('batfish/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>
);
}
}
During Webpack compilation, Batfish exposes the module batfish/prefix-url
.
Use this to prefix your URLs according to the siteBasePath
and siteOrigin
you specified in your configuration, ensuring that they point to the right place both during development and in production.
// Let's imagine:
// - siteBasePath === '/about/jobs/'
// - siteOrigin === 'https://mydomain.com'
const prefixUrl = require('batfish/prefix-url');
// The function prefixes a URL with siteBasePath
prefixUrl('engineer') // -> '/about/jobs/engineer'
// You can also prefix an absolute path, if you've provided siteOrigin
prefixUrl.absolute('engineer') // -> 'https://mydomain.com/about/jobs/engineer'
You can use regular <a>
elements throughout your site.
When the user clicks a link, Batfish checks to see if the link's href
refers to a page it knows about.
If so, client-side routing is used.
If not, the link behaves normally.
If you would like to use an <a>
that doesn't get hijacked (e.g. for your own internal routing within a page), you can give it the attribute data-no-hijack
.
During Webpack compilation, Batfish exposes the module batfish/route-to
.
Use this to dynamically change pages.
If the URL argument matches a page Batfish knows about, client-side routing is used.
If not, Location.assign
is used, and the page transitions normally.
// Let's imagine:
// - siteBasePath === '/about/jobs/'
// - /about/jobs/writer/ is a page you made
const routeTo = require('batfish/route-to');
// Client-side routing is used
routeTo('/about/jobs/writer/');
// Automatically prefix the URL with siteBasePath
routeTo.prefixed('writer');
// Regular link behavior is used, since this is not a page Batfish made
routeTo('/about/money');
<head>
Batfish has a peer dependency on react-helmet.
Use react-helmet to add things your document <head>
.
The development server (for start
and serve-static
commands) is a Browsersync server, for easy cross-device testing.
Usually when you change a file, Webpack will recompile and the browser will automatically refresh. However, the browser will not automatically refresh for the following changes:
When you do one of these things, restart the server to see your change.
Each subdirectory in examples/
is an example site, illustrating some subset of Batfish's features.
cd
into the example's directory.yarn install
(or npm install
) to get any dependencies of that example.npm run batfish -- start
(or build
or serve-static
).npm run batfish
is just a shortcut script that examples should include.
You can also use the Batfish CLI directly to run the examples: it lives in bin/batfish.js
.
You'll need to make sure you either run the command from the example's directory or else use the --config
argument, so Batfish can find the example's configuration.
Examples:
# From project root directory
bin/batfish.js --config examples/initial-experiments/batfish.config.js start
# From examples/initial-experiments/
../../bin/batfish.js build && ../../bin/batfish.js serve-static
Create a new directory in examples/
.
Add the following package.json
:
{
"private": true,
"scripts": {
"batfish": "../../bin/batfish.js"
},
"dependencies": {}
}
Install dependencies as needed.
Create a configuration file and some pages ... and go from there!
FAQs
The React-powered static-site generator you didn't know you wanted
The npm package @mapbox/batfish receives a total of 75 weekly downloads. As such, @mapbox/batfish popularity was classified as not popular.
We found that @mapbox/batfish demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 28 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
Deno 2.2 enhances Node.js compatibility, improves dependency management, adds OpenTelemetry support, and expands linting and task automation for developers.
Security News
React's CRA deprecation announcement sparked community criticism over framework recommendations, leading to quick updates acknowledging build tools like Vite as valid alternatives.
Security News
Ransomware payment rates hit an all-time low in 2024 as law enforcement crackdowns, stronger defenses, and shifting policies make attacks riskier and less profitable.