Working with file types

Representing files #

The specific form a “file” takes can depend on how you reference it.

  • When you reference a file in the file system in Origami, Origami uses a FileMap to resolve the reference and return the value as a Node Buffer object.
  • When you reference a file on a site via a URL, Origami uses a SiteMap to resolve the reference and return the value as a JavaScript ArrayBuffer.

Standard file types #

Origami has built-in support for handling the following types of files.

File extension Type
.css Text
.csv Comma-separated values
.htm Text
.html Text
.jpeg JPEG image
.jpg JPEG image
.js JavaScript
.json JSON
.md Text
.mjs JavaScript
.ori Origami
.ori.<something> Origami
.sh Shell script
.ts TypeScript
.tsv Tab-separated values
.txt Text
.wasm WebAssembly
.xhtml Text
.yaml YAML
.yml YAML

See below for details on these types.

Unpacking files #

Regardless of a file’s representation, you can use Origami path syntax to traverse the file and work with a more meaningful representation that reflects the file’s type. Origami determines how to “unpack” the file contents based on the file extension.

For example, suppose you have a JSON file pet.json containing

{
  "name": "Fluffy",
  "owner": "Alice"
}

If you use the ori CLI to display this file, you will see the plain file contents:

$ ori pet.json
{
  "name": "Fluffy",
  "owner": "Alice"
}

Since JSON files are a supported file type, you can unpack this file and traverse into its data with a slash path:

$ ori pet.json/name
Fluffy

Ending a path with a slash unpacks the file and returns all the data. By default, ori will display it in YAML format:

$ ori pet.json/
name: Fluffy
owner: Alice

Most builtin functions that work on data will implicitly unpack a file in order to work on it. For example, you can hand the above JSON file directly to the keys function to view the keys of the data in that file, without having to append a / trailing slash to the pet.json file name:

$ ori keys pet.json
- name
- owner

If you’re writing Origami that works with a file and needs to explicitly unpack it, you can call the unpack builtin function.

Comma-separated values files #

Origami can unpack a .csv file as containing comma-separated values. The first row is assumed to be a header row with property names. Values containing commas can be quoted. Quotes can be escaped as double-quotes.

If catBreeds.csv contains:

breed,origin,size,coat,temperament
Siamese,Thailand,medium,short,Vocal and Social
Persian,Iran,medium,long,Calm and Affectionate
Maine Coon,United States,large,long,Gentle and Friendly
Sphynx,Canada,medium,hairless,Curious and Social

then this can be unpacked to an array of objects:

$ ori catBreeds.csv/
- breed: Siamese
  origin: Thailand
  size: medium
  coat: short
  temperament: Vocal and Social
- breed: Persian
  origin: Iran
  size: medium
  coat: long
  temperament: Calm and Affectionate
- breed: Maine Coon
  origin: United States
  size: large
  coat: long
  temperament: Gentle and Friendly
- breed: Sphynx
  origin: Canada
  size: medium
  coat: hairless
  temperament: Curious and Social

For formatting an array of objects as CSV, see Origami.csv.

JavaScript files #

Unpacking a .js file returns the file’s default export or, if there are multiple exports, all the exports. If that export is map-like, you can traverse into that export using slash syntax.

Example: If data.js contains

export default {
  a: 1,
};

then you can retrieve traverse the data with a slash path:

$ ori data.js/a
1

JPEG image files #

JPEG images can contain metadata in Exif format. You can retrieve this data via slash syntax:

$ ori sample.jpg/caption
A nice photo at the beach

Origami also has a small set of built-in functions in the Origami.image collection for resizing or reformatting images.

JSON files #

You can traverse into a JSON file using slash syntax; see Unpacking files above.

Origami files #

A file with a .ori extension indicates a file containing an Origami expression. If the result of that expression is map-like you can traverse into it using slash syntax.

If greet.ori contains

{
  name: "Bob"
  greeting: `Hello, ${name}!`
}

then you can retrieve a specific value (including any required evaluation) with:

$ ori greet.ori/greeting
Hello, Bob!

You can add another extension after .ori to create a file like bold.ori.html. This allows you to define a file type that your editor will recognize as, say, an HTML file, and at the same time be able to treat the file as an Origami template document. When you evaluate the file, any Origami expressions in ${ } placeholders will be replaced with their results.

You can also invoke the document as a function with one argument that will be available as the variable _:

<!-- bold.ori.html -->
<b>${ _ }</b>
$ ori "bold.ori.html('Hooray')"
<b>Hooray</b>

Shell script files #

A file with a .sh extension unpacks to a function that invokes the text as a shell script. This function can accept an argument that will be treated by the script as standard input.

For example, this script invokes the wc shell command to count words in the standard input:

# wc.sh

# Count words in stdin
wc -w

Suppose the markdown file hokusai.md contains:

I write, erase, rewrite
Erase again, and then
A poppy blooms.

Invoking the script as a function and passing the markdown file as an argument counts the words in the file:

$ ori wc.sh hokusai.md
11

Here the leading spaces in the output come from the wc command. If you want to convert that text to a simpler number, you could process the output further in the script, or process the result of the script with a JavaScript function:

$ ori parseInt wc.sh hokusai.md
11

Tab-separated values files #

Files with a .tsv extension are treated as tab-separated values with a header row.

If bunnies.tsv contains:

breed	fur	color	origin
American Fuzzy Lop	Long	Various	United States
Belgian Hare	Short	Black, Chestnut	Belgium
Meissner Lop	Short	Various	Germany
Thrianta	Short	Chestnut	Netherlands

This can be unpacked and traversed with expressions like:

$ ori bunnies.tsv[2]
breed: Meissner Lop
fur: Short
color: Various
origin: Germany
$ ori bunnies.tsv[3].breed
Thrianta

To output data in TSV format, see Origami.tsv.

Text files #

Text files can contain data as front matter in YAML or JSON. You can traverse into the front matter data with slash syntax.

$ cat post1.md
---
title: The First Post
---

Here's the text of my first post.
$ ori post1.md/title
The First Post

Origami assumes that text files are encoded in UTF-8.

See more about working with text documents in Origami.

TypeScript files #

If you are running Node v23.6.0 or later, Origami can load TypeScript .ts files the same as .js files (above). This only strips out type information; it does not support using any TypeScript-specific language transformations.

WebAssembly files #

You can traverse into a WebAssembly module, for example, to invoke a function defined in WebAssembly.

If add.wasm exports a function called add that adds two integer arguments, then you can invoke it with:

$ ori "add.wasm/add(1, 2)"
3

This can give you access to functions defined in the many other languages that can be compiled to WebAssembly.

WebAssembly modules run in a “sandbox” that is isolated from your computer so you can download and execute modules directly:

$ ori "(https://webassembly.js.org/example-add.wasm)/add(2, 3)"
5

The parentheses around the URL for a WebAssembly module cause it to be evaluated first, which downloads the module. The / slash after the .wasm causes the downloaded module to be loaded, and the add obtains a function with that name from the module. The final (2, 3) invokes the add function and passes those two values to it.

YAML files #

Like JSON files, you can traverse into a YAML file using slash syntax.

Custom file types #

You can tell Origami how to handle other types of files based on their file extension.

Define a handler for a file extension #

Suppose your project has .user files that define data about users. For simplicity, let’s assume the contents of a .user file is in plain JSON text.

You can define a JavaScript file user.handler.js:

import { toString } from "@weborigami/origami";

export default {
  mediaType: "application/json",

  unpack: (packed) => {
    const text = toString(packed);
    return JSON.parse(text);
  },
};

The mediaType declaration tells the Origami server to transmit any .user files as JSON. The unpack method defines how to turn the file contents into data. A file may be a Buffer, ArrayBuffer, or other data type depending on where it is stored. The toString() utility function in Origami converts any of those types to plain text. The JSON.parse() call then parses the text into data.

Add your extension handler to your Origami configuration #

The second step is to tell Origami to use your user.handler.js file to handle any .user files.

Define a config.ori file at the root of your project. Inside that, define a key user.handler that points to the location of the .js file:

{
  user.handler = src/user.handler.js
}

With that, Origami will call your custom file handler whenever you reference a .user file:

$ cat alice.user
{
  "name": "Alice",
  "location": "Jakarta"
}
$ ori alice.user/
name: Alice
location: Jakarta
$ ori alice.user/name
Alice

Defining a handler for a template language #

As a reference example, the Origami Handlebars extension defines a handler for .hbs files that contain Handlebars templates.