How To

Suggestions for common tasks

Define a shared header, footer, or side bar
Define a base page template
Transform a folder of markdown into HTML
Merge one folder into another
Add an index page to a folder created with a map
Hide parts of a tree
Define a default value
Extract specific resources from a site

You can create an Origami template that defines shared HTML elements or other content in a single file, which you can then include in other page templates for any page that needs that shared content.

In this example, you’ll create a simple navigation header.

Define a topNav.html file to hold the shared navigation elements.

<!-- topNav.html -->
<header>
  <a href="/">Home</a>
</header>

The above just defines a single link to the home page; add any other elements you want on your pages.

Define some templates for pages that will use the shared navigation. Inside each template, include a reference to ${ topNav.html }.

Examples:

// about.ori
Tree.indent`
  ${ topNav.html }
  <h2>About Us</h2>
  <p>We have fun making websites.</p>
`
// contact.ori
Tree.indent`
  ${ topNav.html }
  <h2>Contact Us</h2>
  <p>We'd love to hear from you.</p>
`

In a site.ori site definition, define HTML pages that use the above templates.

// site.ori
{
  about.html = about.ori/
  contact.html = contact.ori/
}

When Origami generates a page like about.ori, the page template incorporates the topNav.html file into the output.

$ ori site.ori/about.html
<header>
  <a href="/">Home</a>
</header>
<h2>About Us</h2>
<p>We have fun making websites.</p>

This technique can be combined with the following one.

Define a base page template for multiple pages #

Most sites define a consistent structure for all their pages that includes basic HTML elements for things like links to spreadsheet, <meta> tags, and other top-level page elements. You can define this structure in a base template that will be used by other templates.

Create a page.ori template that will serve as the base template.

// page.ori
(document) => Tree.indent`
  <!DOCTYPE html>
  <html lang="en">
    <head>
      <meta charset="utf-8">
      <meta name="viewport" content="width=device-width,initial-scale=1">
      <title>${ document.title }</title>
    </head>
    <body>
      <h1>${ document.title }</h1>
      ${ document._body }
    </body>
  </html>
`

This template expects to receive a document object that has a title property with the document title and a text property with the body text.

Create an about.ori template for an About page. This page will call the base page.ori template as a function, passing in the desired title and text for the About page.

// about.ori
page.ori({
  title: "About Us"
  _body: Tree.indent`
    <p>We have fun making websites.</p>
  `
})

Create a site.ori file to define your site:

// site.ori
{
  about.html = about.ori/
}

When a site visitor asks for about.html, this will invoke the about.ori template. That in turn will call the base page.ori template, which will incorporate the page title and body text into the page structure for the complete page.

$ ori site.ori/about.html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <title>About Us</title>
  </head>
  <body>
    <h1>About Us</h1>
    <p>We have fun making websites.</p>
  </body>
</html>

As a site grows, the base page template can become quite large. For clarity, it can be helpful to separate out pieces of the base template into separate files. For example, if the top navigation area gets complex, you can separate it into a separate top navigation template; see the preceding section.

Transform a folder of markdown into HTML #

You may want to use markdown format to write pages which are primarily text. You can then have Origami transform the markdown pages to HTML.

Create a folder called markdown to hold your .md markdown files:

src/
  markdown/
    about.md
    products.md
    support.md
  site.ori

Your markdown folder will have the following structure:

g markdown/ ->markdown/ markdown/ markdown/about.md # About markdown/->markdown/about.md about.md markdown/products.md # Products markdown/->markdown/products.md products.md markdown/support.md # Support markdown/->markdown/support.md support.md

In your site definition, add a line that calls the map to transform all the markdown files using the mdHtml builtin.

// site.ori
{
  pages/ = Tree.map(markdown/, Origami.mdHtml)
}

The map builtin will use mdHtml to transform both the keys (names) and values (contents) of the markdown files: the file extension on the keys will change from .md to .html, and the values will change from markdown text to HTML.

g pages/ ->pages/ pages/ pages/about.html <h1 id="about">About</h1> pages/->pages/about.html about.html pages/products.html <h1 id="products">Products</h1> pages/->pages/products.html products.html pages/support.html <h1 id="support">Support</h1> pages/->pages/support.html support.html

If you want the pages to appear at a higher level of the site, you can combine this technique with the spread operator; see below.

Merge one folder into another #

Sometimes you want to group a set of pages into a subfolder to keep your source content organized, but have all those pages appear as if they were direct children of some other folder.

For example, if you are an indie website author, you may want to create set of slash pages at the top level of your site for various aspects of you and your interests. You can create these pages in HTML directly (or use markdown; see the preceding section).

It may be useful to group such pages into a subfolder, but them merge them into the top level of your site so the pages have shorter URLs.

Group the pages into a subfolder called slash:

src/
  slash/
    about.html
    links.html
    now.html
  index.html
  site.ori

If you include the slash folder as a subfolder of your site:

// site.ori
{
  index.html
  slash/
}

you will have the following site hierarchy:

g index.html <h1>Home</h1> ->index.html index.html slash/ ->slash/ slash/ slash/about.html <h1>About</h1> slash/->slash/about.html about.html slash/links.html <h1>Links</h1> slash/->slash/links.html links.html slash/now.html <h1>Now</h1> slash/->slash/now.html now.html

This would give you URLs like /slash/now.html.

Since you want the slash pages to appear at the top level of your site, use the spread operator to merge the contents of the slash folder into the site’s top level:

// siteSpread.ori
{
  index.html
  ...slash/
}

which produces the following hierarchy:

g index.html <h1>Home</h1> ->index.html index.html about.html <h1>About</h1> ->about.html about.html links.html <h1>Links</h1> ->links.html links.html now.html <h1>Now</h1> ->now.html now.html

With this, all the pages are directly available at the root of the site and URLs like /now.html.

Add an index page to a folder created with a map #

Suppose you have a folder of posts you’re create with map, perhaps to transform a folder of markdown into HTML, and you want the resulting virtual folder of HTML to have its own index.html page. You can use the same spread operator shown above.

In this situation, you’re going to be using your transformed markdown twice: once to create the HTML pages in the pages area, and a second time for the index page for the pages area. To make your map reusable, define it in a separate file called data.ori

// data.ori
Tree.map(markdown/, Origami.mdHtml)

Now create a basic index page template in pagesIndex.ori:

// pagesIndex.ori
(pages) => Tree.indent`
  <ul>
    ${ Tree.map(pages, (page, name) => Tree.indent`
      <li><a href="/pages/${ name }">${ name }</a></li>
    `) }
  </ul>
`

Then define a site.ori formula to create the pages area. This expression will use the spread operator to incorporate all the individual pages. To that set of pages, the index.html formula will create the index page, passing the set of individual pages to the pagesIndex.ori template.

// site.ori
{
  pages = {
    ...data.ori
    index.html = pagesIndex.ori(data.ori)
  }
}

To visualize this operation: data.ori defines a tree that looks like this.

g markdown/ about.html <h1 id="about">About</h1> ->about.html about.html products.html <h1 id="products">Products</h1> ->products.html products.html support.html <h1 id="support">Support</h1> ->support.html support.html

The pages definition in site.ori includes all of that, plus the additional index.html page:

g about.html <h1 id="about">About</h1> ->about.html about.html products.html <h1 id="products">Products</h1> ->products.html products.html support.html <h1 id="support">Support</h1> ->support.html support.html index.html <ul> <li><a href="/pages/about.html">a… ->index.html index.html

Hide parts of a tree #

It can be helpful to break down the construction of complex tree values into simpler temporary values that are used but not made publicly available.

One way to accomplish this is to have a tree define a public value that is later extracted from the tree.

As a trivial example, this program defines a site whose index.html includes a name value:

// public.ori
{
  name = "Alice"
  index.html: `<h1>${ name }</h1>`
}

Along with index.html, this tree will expose the name value:

$ ori public.ori/
name: Alice
index.html: <h1>Alice</h1>

But if you don’t want the name value to be exposed, you can move the portion of the tree you do want to expose into a public subtree, and then arrange to return only that subtree:

// private.ori
{
  name = "Alice"
  public = {
    index.html = `<h1>${ name }</h1>`
  }
}.public

This program effectively defines a closure: inside the public subtree, the name property is available for use in calculations.

The .public at the end of the program returns only the public subtree, so the internal name value is not available externally:

$ ori private.ori/
index.html: <h1>Alice</h1>

Define a default value #

You can define a default value for a tree using the spread operator with an arrow function:

// default.ori
{
  ...() => 0 // Default value is zero
  a: 1
  b: 2
  c: 3
}

This tree returns the indicated value for any defined key, and zero for anything else:

$ ori default.ori/a
1
$ ori default.ori/b
2
$ ori default.ori/x
0

This works as follows:

  • The () => 0 syntax defines a function that returns zero for any input.
  • The ... spread operator merges that function into the tree.
  • Merging the function into the tree implicitly turns it into a tree: asking this tree for a key will call the indicated function, which will return zero.
  • The merged tree ends up combining two trees: 1) the tree defined by the =0 function, and 2) the tree of the explicitly defined keys and values.

When asked to get a key, the merged tree starts by consulting the second tree (of explicit keys/values). If the tree has a value, that will be returned. Otherwise, the merged tree consults the first tree (the function).

The idiom as written above is suitable for shallow trees: the ... spread operator does a shallow merge, and the () => 0 function defines a shallow tree.

You can adapt this idiom to provide a default value for deep trees using the Tree.deepMerge function to do the merge and the Tree.constant builtin to define the default value.

// deepDefault.ori
Tree.deepMerge(
  Tree.constant(0) // Default value is zero
  {
    a: 1
    b: {
      c: 2
    }
  }
)

This provides the default value of zero for any level of the tree:

$ ori deepDefault.ori/a
1
$ ori deepDefault.ori/x
0
$ ori deepDefault.ori/b/c
2
$ ori deepDefault.ori/b/y
0

One use for this is to provide a default “Not found” page for a dynamic site:

// Define a site with a custom "Not found" page
Tree.deepMerge(

  // Default value. A Response object cannot be served twice, so instead of
  // serving a Response object directly, we return a function that creates a
  // new Response object. The server will invoke that function to obtain and
  // serve a fresh Response.
  Tree.constant(() => new Response("I got nothing", { status: 404 }))

  // Actual resources
  {
    index.html: "Home"
    a: {
      b.html: "B"
    }
  }

)

Extract specific resources from a site #

The httpstree: protocol lets you treat a live website as a tree. Since sites don’t generally make their keys (routes) available, you can only use such a tree to obtain values at known routes.

That said, if you know the routes you want to extract from a site, you can combine httpstree: with Tree.deepMerge to extract those specific routes.

// extract.ori

Tree.deepMerge(
  // The site to get resources from
  httpstree://weborigami.org
  
  // The set of resources we want to get
  {
    index.html: undefined
    language: {
      index.html: undefined
    }
  }
)

This merges two trees together:

  1. The httpstree: defines an opaque tree with values but no keys.
  2. The object literal defines a skeleton tree with keys but no defined values.

When this merged tree is asked to enumerate its keys, it will return the keys from the second tree. When the merged tree is later asked for the values of those keys, it will look for those in the second tree – but since that tree has no defined values, the values from the first tree (the site) will be used.

The result is the tree of explicitly-requested resources from the site:

$ ori extract.ori/
index.html: [contents of index.html]
language:
  index.html: [contents of language/index.html]

This technique is useful when you know the set of resources you want to fetch. If you don’t know what resources the site provides, the dev:crawl builtin can return the complete publicly-reachable set of resources.