# aip.dev static site generator

Welcome to our NIH static site generator.

## Why?

We are not fans of rolling our own tools when off-the-shelf alternatives exist.

GitHub Pages normally automatically builds documentation with [Jekyll][], but
as the AIP system has grown, we are beginning to reach the limits of what
Jekyll can handle, and other off-the-shelf generators had similar issues:

- AIP adoption is handled through fork-and-merge and top-down configuration
  files will lead to repetitive merge conflicts.
- Our grouping and listing logic has grown complicated, and had to be
  maintained using complex and error-prone Liquid templates.
- Jekyll is extensible but GitHub requires specific Jekyll plugins, meaning we
  can not use off-the-shelf solutions for planned features (e.g. tabbed code
  examples).
- Lack of meaningful build CI caused failures.
- Working with the development environment was (really) slow.

There are some additional advantages that we unlock with a custom generator:

- We can override segments of AIPs using template extensions in new files
  rather than modifying existing files.
- We can provide useful abstractions for common deviations between companies
  (e.g. case systems) that minimize the need to fork AIPs.

## Grumble. Fine. How does it work?

This is essentially split into three parts:

- Python code (`aip_site/`):
  - The majority of the code is models (`aip_site/models/`) that represent the
    fundamental concept of an AIP site. These are rolled up into a singleton
    object called `Site` that is used everywhere. All models are
    [dataclasses][] that get sent to templates.
  - There is also a publisher class (`aip_site/publisher.py`) that is able to
    slurp up a repo of AIPs and build a static site.
  - There is some server code (`aip_site/server.py`) that can run a development
    server.
  - All remaining files are thin support code to avoid repeating things in or
    between the above.
- Templates (`support/templates/`) are [Jinja2][] templates containing (mostly)
  HTML that makes up the layout of the site.
- Assets (`support/assets/` and `support/scss/`) are other static files. SCSS
  is automatically compiled into CSS at publication.

Of the models, there are three models in particular that matter:

- **Site:** A singleton that provides access to all scopes, AIPs, and static
  pages. This is sent to every template as the `site` variable.
- **AIP:** A representation of a single AIP, including both content and
  metadata. This is sent to the AIP rendering template as the `aip` variable.
- **Scope:** A group of AIPs that apply to a particular scope. The "general"
  scope is special, and is the "root" group. This is sent to the AIP _listing_
  template as the `scope` variable.

Templates are [jinja2][] files in the `templates/` directory.

**Note:** We run Jinja in with "strict undefined", so referencing an undefined
variable in a template is a hard error rather than an empty string.

### Entry points

There are two entry points for the app. The _publisher_
(`aip_site/publisher.py`) is the program that iterates over the relevant
directories, renders HTML files, and writes them out to disk. The _app_
(`aip_site/server.py`) is a lightweight Flask app that provides a development
server.

These entry points are routed through the CLI file (`aip_site/cli.py`); when
this application is installed using pip, it makes the `aip-site-gen`
(publisher) and `aip-site-serve` (server) commands available.

### Extensions

This site generator includes a basic extension system for AIPs. When processing
AIPs as plain Markdown files, it will make any Markdown (level 2 or 3) header
into a block. Therefore...

```md
## Foo bar baz

Lorem ipsum dolor set amet
```

Becomes...

```j2
{% block foo_bar_baz %}
## Foo bar baz

Lorem ipsum dolor set amet
{% endblock %}
```

That allows an overriding template to extend the original one and override
sections:

```j2
{% extends aip.templates.generic %}

{% block foo_bar_baz %}

## My mo-betta foo bar baz

Lorem ipsum dolor set something-not-amet
{% endblock %}
```

[dataclasses]: https://docs.python.org/3/library/dataclasses.html
[jekyll]: https://jekyllrb.com/
[jinja2]: https://jinja.palletsprojects.com/en/2.11.x/
