Edit on GitHub

How does Landr work?

The main goal of Landr is to generate useful and decent websites to describe a software project with the least possible amount of configuration. Landr is supposed to be opinionated by design and rely on common open source conventions rather than on user input.

meta.json

Landr reads all the information it needs to know about a repository from a file called meta.json, which is supposed to be the only input for Landr. This is a pretty big JSON file generated by a tool such as scrutinizer, which scans the content of the repo, the GitHub API, the package managers where the repository is published (i.e. npmjs.org), and much more. Check Landr's meta.json as an example.

Right now Landr works on the assumption that the meta.json file is there by the time Landr runs. Within Balena, we do this by setting up Balena CI to run scrutinizer on every PR merge and making the balena-ci user commit the file to master. This is a step that makes it hard for people outside Balena to use Landr, and we hope to sort this out in the short term.

The amount of information we can obtain from a repository differs on the project and on how standard the conventions that the project is adopting are, so most of the things that meta.json might contain is optional.

Routes

The routes that Landr will statically generate are defined in lib/routes.js. This file contains a function that takes meta.json and returns an array of routes that Landr will render. Each route contains the following properties:

  • title: The page title, most often used inside <title></title>
  • path: The page path, as an array of fragments. For example [ 'foo', 'bar' ] corresponds to /foo/bar
  • context: A free-form object that represents the page's focus information. For example, a page that renders the project's code of conduct will have the code of conduct plus any related information as its context

The order in which routes are returned doesn't make a difference.

Components

The Landr project ships with a collection of React component that represent the library of UI section that Landr will use when rendering a website. These components live in lib/components/. Each component has the following properties:

  • name: The unique name of the component
  • render: A function that takes a props object and returns a React component
  • variants: The different ways this component can be rendered given a meta.json. We will explain this in more detail later

Variants

Each component has a variants function that takes information such as meta.json and a route context and returns an array defining all the ways that the component is happy to render itself given the information it has, sorted by order of preference.

For example, a jumbotron may have a title, a subtitle, and a primary action button. Our jumbotron component may define that its happy to render itself in the following ways:

  • Using the GitHub description as a title, the README first paragraph as a subtitle, and a link to GitHub Releases as the primary action button
  • Using the GitHub description as a title, and the README first paragraph as a subtitle, without a primary action button
  • Using the repository name as the title, and the README first paragraph as a subtitle, without a primary action button
  • And more

If a "choice" is returned before another choice, then the component expresses that it prefers to be rendered in the first way, but is happy to take any choice. We internally call these preferences the rank.

Rules

The Landr engine will go through each of the routes defined for a website, and it will generate all possible combinations of components and the choices they are willing to consider when rendering themselves. A lot of these options will be invalid, such as saying that we want a footer before the navigation bar, so Landr also has a set of declarative rules to filter out invalid component combinations. These rules live in lib/rules.js and express things such as:

  • If there is a jumbotron, then it must be positioned right after a navigation bar
  • There should not be a jumbotron in pages other than the main page
  • The footer should be the last thing in a page

And many more.

Landr will check every combination it generated against these rules, keep the valid ones, and choose the one where the components are rendered with the highest possible ranks.

Rendering

Once we figure out what to display and how, we generate React components and pass them to React Static to get you some lovely static files!