# Web Origami Web Origami is a system for creating sites that complements your understanding of HTML and CSS with the ability to concisely define the structure and content of the site you want. Web Origami includes these tools and ideas: - The Origami dialect of JavaScript (see below) lets you concisely define a site or other structured using formulas similar to those in a spreadsheet. - The [ori command-line interface](/cli/) (CLI) lets you use Origami expressions in a command terminal to manipulate files or get resources out of an Origami site in the command line. The CLI is also useful as a general tool in its own right for working with JavaScript in the shell. - The [built-in functions](/builtins/) let you perform a number of common site development tasks in the Origami language or JavaScript. - The [async-tree library](/async-tree/) lets JavaScript programmers use core Origami features in JavaScript applications. - The [conceptual pattern](/pattern/) at the foundation of it all lets you represent a wide variety of data types as tree structures that can be easily traversed and transformed. There's no code at this level; you can use the ideas in any project. ## Origami language documentation Origami is **JavaScript expressions with paths** — a dialect of JavaScript expressions that includes minor adaptations that make it easier to define sites. If you can already write even a little JavaScript, then you know 95% of what you need to write Origami. Origami's language features are generally optional, but it does enforce one stylistic rule: you have to put spaces around operators like math operators. Origami only deals with JavaScript expressions, which is sufficient to handle most tasks related to creating a site. This avoids many of the complexities and pitfalls of using JavaScript _statements_ (including control structures like `for`, `while`, etc.), which are not supported. If you need full JavaScript, you can write an Origami expression that [calls a JavaScript function](/language/fileTypes.html#javascript-files). ## Quick reference
| Origami | Equivalent JavaScript and notes |
|---|---|
src/data.json |
await fs.readFile("src/data.json")
A file name or path in source code reads that file. |
3 / 2 |
3 / 2 or 3/2
Spaces are required around math operators. |
<My File.txt> |
await fs.readFile("My File.txt")
Use angle brackets if a path contains spaces, parentheses, quotes, etc. |
(data.json).name |
JSON.parse(await fs.readFile("data.json")).name
Reading data out of a file implicitly parses it. |
https://example.com |
await fetch("https://example.com)
A URL can fetch network resources. |
"hello" |
export default "hello";
An Origami file implicitly exports the value of its top-level expression. |
fn(a, b) |
await fn(await a, await b)
All functions and values are potentially async. |
|
An object's properties can reference its other properties. |
|
Define a getter with an equals sign. |
|
A property name in parentheses makes it non-enumerable. |
|
Newlines can be used as separators in arrays, objects, and parameters. |
() => { a: 1 } |
() => ({ a: 1 })
A function can directly return an object without surrounding parentheses. |
|
Object keys can be file names without having to be quoted. |
a → b → c |
c(b(a))
The pipe operator lets left-to-right order indicate the order of evaluation. |
x || y | left-to-right |
| Nullish coalescing | `x ?? y` | left-to-right |
| Conditional (ternary) | `x ? y : z` | right-to-left |
| [Arrow](#arrow-functions) | `(x) => y` | right-to-left |
| [Implicit parentheses](/cli/shorthand.html#implicit-parentheses-for-function-arguments) | `x y` | right-to-left |
| [Shorthand function](/cli/shorthand.html#shorthand-functions) | `=x` | right-to-left |
| [Pipe](#pipe-operator) | `x -> y` | left-to-right |
| [Spread](#spread-operator) | `...x` | n/a |
| Comma | `x, y` | left-to-right |
Note that Origami does not support the JavaScript operators that have side effects: the postfix and prefix operators `++` and `--`; the assignment operators like `=`, `+=`, `-=`, etc.; or the `delete` operator.
An operator’s precedence determines how the Origami parser handles expressions that have more than one possible interpretation.
Example: `a -> b => c` could be interpreted as `a -> (b => c)` or `(a -> b) => c`. Origami uses the former interpretation, because the standard `=>` arrow operator has a higher precedence than Origami's `->` pipe operator.
Origami requires spaces around binary operators:
- `x + y`
- `x - y`
- `x * y`
- `x / y`
Without the spaces, Origami interprets `x/y` as a [path](#paths). Similarly, operators without surrounding spaces are treated as part of an [identifier](#identifiers): `package-lock.json` is an identifier, not a subtraction.
Spaces are not required around unary operators: `-x` negates the value of `x`. If you need to reference a local file that starts with a hyphen, put the name in [angle brackets](#angle-brackets): `<-x>`.
Some addition usage notes:
- Instead of calling [`import`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/import), you can generally use a [path](#paths) instead.
- The `await` operator is implied in Origami so can always be omitted.
### Spread operator
You can use `...` three periods to merge arrays and objects, or to incorporate an array of items into a set of function arguments.
```console
$ ori data1.yaml
${ samples/help/merge/data1.yaml }$ ori data2.yaml
${ samples/help/merge/data2.yaml }$ ori { ...data1.yaml, ...data2.yaml }
${ Origami.yaml({
...samples/help/merge/data1.yaml
...samples/help/merge/data2.yaml
}) }
```
As in JavaScript, the _last_ definition of a given key will be used. While both files above define a value for `c`, it's the second file whose value of `c` is used.
In an `.ori` file, you can use this to merge a folder into an object that also defines individual files.
```ori
{
index.html: "Hello!"
// Merge in everything in the `styles` folder
...styles
}
```
If you'd prefer to use a function to merge objects instead of the spread operator, you can perform a shallow merge with [`Tree.merge`](/builtins/tree/merge.html) or a deep merge with [`Tree.deepMerge`](/builtins/tree/deepMerge.html). For concatenating arrays with a function, use [`Tree.concat`](/builtins/tree/concat.html).
### Pipe operator
Origami has a pipe operator `→` (which can also be written `->`) for representing a sequence of function calls. This can be used to avoid deep call nesting.
These deeply-nested function calls:
```ori
three(two(one(value)))
```
can be rewritten using the pipe operator:
```ori
value → one → two → three
```
This can be useful when applying multiple transformations of data. Suppose an index page is generated from markdown and then placed inside a template:
```ori
{
index.html = template.ori(Origami.mdHtml(index.md))
}
```
You can rewrite the above using the pipe operator so that the flow of data reads proceeds from left to right:
```ori
{
index.html = index.md → Origami.mdHtml → template.ori
}
```
This may make the flow of data easier to see.
The pipe operator passes the value from the left side of the operator to the function on the right side. If you need to provide additional arguments to that function, wrap the call in a [arrow function](#arrow-functions) that accepts a single argument and then calls your desired function with the additional arguments.
Example: these nested calls to [`Origami.mdHtml`](/builtins/origami/mdHtml.html) and [`Tree.sort`](/builtins/tree/sort.html) both take multiple arguments:
```ori
Tree.sort(Tree.map(markdown/, Origami.mdHtml), { compare: Origami.naturalOrder })
```
This can be rewritten with the pipe operator and arrow functions:
```ori
markdown/
→ (mdFiles) => Tree.map(mdFiles, Origami.mdHtml)
→ (htmlFiles) => Tree.sort(htmlFiles, { compare: Origami.naturalOrder })
```
## Grouping
You can group expressions with `(` `)` parentheses.
## Comments
Just like JavaScript, line comments start with `//` double slashes and extend to the end of the line:
```
// This is a line comment
```
One difference is that, in a [URL](#urls) or [path](#paths), Origami interprets consecutive double slashes as part of the path, _not_ a comment.
```
https://example.com/
path/with/consecutive//slashes
```
Those lines contain double slashes, but neither contains a comment.
Block comments are enclosed by `/* */`
```
/*
Block comment
*/
```
## Implied exports
Evaluating any Origami file with the `.ori` extension returns an evaluation of the file's top-level expression — unlike JavaScript, there is no need to explicitly `export` a value.
This `message.ori` file evaluates to a string:
```ori
// message.ori
"This file exports this message."
```
It is equivalent to this JavaScript:
```js
// message.js
export default "This file exports this message.";
```
## Shell shorthand syntax
To accommodate the use of Origami on the command line, the Origami [CLI](/cli) supports some additional [shorthand syntax](/cli/shorthand.html). The shorthands are not supported inside Origami `.ori` files.
## Unsupported JavaScript features
As an expression language, Origami does not include any of JavaScript's control structures like `for` or `while` loops.
Origami does not support these JavaScript keywords because only make sense if in a language with statements:
- `class`
- `extends`
- `function`
- `this`
Origami does not support JavaScript operators that have side effects:
- Postfix and prefix operators `++`, `--`
- Assignment operators `=`, `+=`, `-=`, etc.
- `delete` operator
## Additional language documentation
[Configuration](/language/configuration.html)
[Development tools](/language/devTools.html)
[Working with text documents](/language/documents.html)
[Example sites](/language/examples.html)
[Working with file types](/language/fileTypes.html)
[How To](/language/howTo.html)
[Origami expression language](/language/index.html)
[Language design principles](/language/principles.html)
[Scope](/language/scope.html)
[Defining a site](/language/sites.html)
[Origami template documents](/language/templateDocuments.html)
[Origami templates](/language/templates.html)
[Create a basic blog in Origami](/language/tutorial.html)
[Origami in YAML](/language/yaml.html)
## Origami built-in functions
Built-ins are grouped into namespaces that are registered as Origami/JavaScript globals. Here each built-in is listed by its name.
* [Tree.addNextPrevious](/builtins/tree/addNextPrevious.html) Add next/previous fields to the map's values
* [Tree.assign](/builtins/tree/assign.html) Apply key/values from source to target
* [Dev.audit](/builtins/dev/audit.html) Identify broken internal links and references
* [Origami.basename](/builtins/origami/basename.html) Removes an extension from the key if present
* [Dev.breakpoint](/builtins/dev/breakpoint.html) Break into the JavaScript debugger, then return arg
* [Tree.cache](/builtins/tree/cache.html) Caches values from the tree
* [Tree.calendar](/builtins/tree/calendar.html) Return a tree structure for years/months/days
* [Dev.changes](/builtins/dev/changes.html) Return a tree of changes
* [Tree.child](/builtins/tree/child.html) Returns the indicated child node, creating it if necessary
* [Tree.clear](/builtins/tree/clear.html) Remove all values from the map
* [Tree.concat](/builtins/tree/concat.html) Merge trees, renumbering numeric keys
* [Origami.config](/builtins/origami/config.html) The current project's configuration
* [Tree.constant](/builtins/tree/constant.html) Return a deep tree with a single constant value
* [Dev.copy](/builtins/dev/copy.html) Copy the source tree to the target
* [Dev.crawl](/builtins/dev/crawl.html) A tree of a site's discoverable resources
* [Origami.csv](/builtins/origami/csv.html) Render the tree as a CSV file
* [Dev.debug](/builtins/dev/debug.html) Add debug features to the tree
* [Tree.deepMap](/builtins/tree/deepMap.html) Map the keys and values of a deep tree
* [Tree.deepMerge](/builtins/tree/deepMerge.html) Return a deeply-merged tree
* [Tree.deepReverse](/builtins/tree/deepReverse.html) Reverse order of keys at all levels of the tree
* [Tree.deepTake](/builtins/tree/deepTake.html) The first n values from the deep tree
* [Tree.deepText](/builtins/tree/deepText.html) The text values of the deep tree
* [Tree.deepValues](/builtins/tree/deepValues.html) The in-order leaf values of the tree
* [Tree.deflatePaths](/builtins/tree/deflatePaths.html) Convert a tree to a mapping of paths to values
* [Tree.delete](/builtins/tree/delete.html) Delete the value for the key from map
* [Origami.document](/builtins/origami/document.html) Create a document object with the text and data
* [Tree.entries](/builtins/tree/entries.html) The map's [key, value] pairs
* [Protocol.explore](/builtins/protocol/explore.html) Treat a website with JSON keys as a tree
* [Origami.extension](/builtins/origami/extension.html) Helpers for working with file extensions
* [Origami.fetch](/builtins/origami/fetch.html) Fetch a resource from a URL with support for extensions
* [Protocol.files](/builtins/protocol/files.html) File system folders and files
* [Tree.filter](/builtins/tree/filter.html) Filter a tree by a condition
* [Tree.first](/builtins/tree/first.html) The first value in the map
* [Tree.flat](/builtins/tree/flat.html) Flatten the tree to the indicated depth
* [Tree.forEach](/builtins/tree/forEach.html) Apply fn to each (value, key)
* [Tree.from](/builtins/tree/from.html) Create a map from an object
* [Tree.globKeys](/builtins/tree/globKeys.html) A tree whose keys can include glob wildcard patterns
* [Tree.groupBy](/builtins/tree/groupBy.html) A new map with values grouped by the function
* [Tree.has](/builtins/tree/has.html) True if key exists in map
* [Dev.help](/builtins/dev/help.html) Get help on builtin namespaces and commands
* [Origami.htmlEscape](/builtins/origami/htmlEscape.html) Escape HTML entities in the text
* [Protocol.http](/builtins/protocol/http.html) Web resources via HTTP
* [Protocol.https](/builtins/protocol/https.html) Web resources via HTTPS
* [Protocol.httpstree](/builtins/protocol/httpstree.html) Website tree via HTTPS
* [Protocol.httptree](/builtins/protocol/httptree.html) Website tree via HTTP
* [Origami.image](/builtins/origami/image.html) Collection of functions for working with images
* [Tree.indent](/builtins/tree/indent.html) Tagged template literal for normalizing indentation
* [Origami.indexPage](/builtins/origami/indexPage.html) A default index.html page for the tree
* [Tree.inflatePaths](/builtins/tree/inflatePaths.html) Convert mapping of paths to values into a tree
* [Origami.inline](/builtins/origami/inline.html) Inline Origami expressions found in the text
* [Tree.inners](/builtins/tree/inners.html) The tree's interior nodes
* [Tree.invokeFunctions](/builtins/tree/invokeFunctions.html) Getting a map value invokes it if it's a function
* [Tree.isMap](/builtins/tree/isMap.html) True if object is a map
* [Tree.isMaplike](/builtins/tree/isMaplike.html) True if object can be coerced to a tree
* [Tree.isReadOnlyMap](/builtins/tree/isReadOnlyMap.html) True if object is a read-only map
* [Tree.isTraversable](/builtins/tree/isTraversable.html) True if object is traversable
* [Tree.json](/builtins/tree/json.html) Render the tree in JSON format
* [Origami.jsonKeys](/builtins/origami/jsonKeys.html) Add .keys.json files to a tree
* [Origami.jsonParse](/builtins/origami/jsonParse.html) Parse text as JSON
* [Tree.keys](/builtins/tree/keys.html) The keys of the map
* [Dev.log](/builtins/dev/log.html) Log message to the console and return a
* [Tree.map](/builtins/tree/map.html) Create a new map by transforming keys and/or values
* [Tree.mapExtension](/builtins/tree/mapExtension.html) Create a new map by transforming extensions
* [Tree.mapReduce](/builtins/tree/mapReduce.html) Map the keys and/or values in a tree and reduce them
* [Tree.mask](/builtins/tree/mask.html) Return the source tree with only the keys in the mask
* [Tree.match](/builtins/tree/match.html) Matches simple patterns or regular expressions
* [Origami.mdHtml](/builtins/origami/mdHtml.html) Render the markdown as HTML
* [Origami.mdOutline](/builtins/origami/mdOutline.html) The outline structure of the markdown document
* [Tree.merge](/builtins/tree/merge.html) Return a new tree merging the given maps
* [Origami.naturalOrder](/builtins/origami/naturalOrder.html) A comparison function for natural sort order
* [Protocol.node](/builtins/protocol/node.html) Installed Node.js modules
* [Origami.once](/builtins/origami/once.html) Run the function only once, return the same result
* [Origami.ori](/builtins/origami/ori.html) Evaluate the text as an Origami expression
* [Protocol.package](/builtins/protocol/package.html) Packages installed in node_modules
* [Tree.paginate](/builtins/tree/paginate.html) Group the map's values into fixed-size sets
* [Tree.parent](/builtins/tree/parent.html) The parent of the given tree node
* [Tree.paths](/builtins/tree/paths.html) Slash-separated paths for the tree's values
* [Tree.plain](/builtins/tree/plain.html) Render the tree as a plain JavaScript object
* [Origami.post](/builtins/origami/post.html) POST the given data to the URL
* [Origami.projectRoot](/builtins/origami/projectRoot.html) The root folder for the current Origami project
* [Origami.redirect](/builtins/origami/redirect.html) Redirect to the given URL
* [Tree.reduce](/builtins/tree/reduce.html) Reduce the tree to a single value
* [Tree.regExpKeys](/builtins/tree/regExpKeys.html) A tree whose keys are regular expression strings
* [Origami.repeat](/builtins/origami/repeat.html) An array of n copies of the object
* [Tree.reverse](/builtins/tree/reverse.html) Reverse the order of the map's keys
* [Tree.root](/builtins/tree/root.html) The root node of the given tree
* [Origami.rss](/builtins/origami/rss.html) Transforms a JSON Feed tree to RSS XML
* [Tree.scope](/builtins/tree/scope.html) A merged view of the tree and its ancestors
* [Dev.serve](/builtins/dev/serve.html) Start a web server for the tree
* [Tree.set](/builtins/tree/set.html) Set the value for the key in the map
* [Origami.shell](/builtins/origami/shell.html) Run the text as a shell command, return the output
* [Tree.shuffle](/builtins/tree/shuffle.html) Shuffle the keys of the map
* [Origami.sitemap](/builtins/origami/sitemap.html) Generate a sitemap for the tree
* [Tree.size](/builtins/tree/size.html) The map's size (number of keys)
* [Origami.slash](/builtins/origami/slash.html) Helpers for working with trailing slashes
* [Origami.slug](/builtins/origami/slug.html) A version of the text suitable for use in URLs
* [Tree.sort](/builtins/tree/sort.html) A new map with its keys sorted
* [Origami.static](/builtins/origami/static.html) Define common static files for the tree
* [Dev.stdin](/builtins/dev/stdin.html) Returns the content of the standard input stream
* [Origami.string](/builtins/origami/string.html) Coerce a buffer or document to a string
* [Dev.svg](/builtins/dev/svg.html) Render a tree visually in SVG format
* [Tree.sync](/builtins/tree/sync.html) Awaits all asynchronous values in the tree
* [Tree.take](/builtins/tree/take.html) The first n values in the map
* [Tree.text](/builtins/tree/text.html) Tagged template literal for rendering trees
* [Origami.toFunction](/builtins/origami/toFunction.html) Coerce a tree or packed function definition to a function
* [Tree.traverse](/builtins/tree/traverse.html) Return the value at the path of keys
* [Tree.traverseOrThrow](/builtins/tree/traverseOrThrow.html) Return the value at the path of keys or throw
* [Tree.traversePath](/builtins/tree/traversePath.html) Traverse a slash-separated path
* [Origami.unpack](/builtins/origami/unpack.html) Unpack the buffer into a usable form
* [Tree.values](/builtins/tree/values.html) The map's values
* [Dev.version](/builtins/dev/version.html) Return the version number of the Origami language
* [Tree.visit](/builtins/tree/visit.html) Force reading of all values from the tree
* [Dev.watch](/builtins/dev/watch.html) Reevaluate fn when tree changes
* [Tree.withKeys](/builtins/tree/withKeys.html) Use the given keys for the map
* [Origami.yaml](/builtins/origami/yaml.html) Render the object in YAML format
* [Origami.yamlParse](/builtins/origami/yamlParse.html) Parse text as YAML
## Sample Origami site repositories
Look at the `src` folders, particularly the top-level site definitions, which are usually found in `src/site.ori`.
* [About Us](https://github.com/WebOrigami/about-us) - A simple "About Us" area for a hypothetical organization that generates a home page and a set of team member pages from a data file.
* [All City Someday](https://github.com/WebOrigami/all-city-someday) - A photo blog backed by Google Drive: photos saved to a shared Google Drive folder will appear on the site.
* [Aventour Expeditions](https://github.com/WebOrigami/aventour-expeditions) - A travel site for an adventure trekking company. Showcases the use of a different template language (Handlebars), the transformation of markdown with front matter into HTML, and the creation of an image gallery that shows all the photos in a given folder.
* [Cat Prints Store](https://github.com/WebOrigami/cat-prints-store) - A small web storefront with a shopping cart provided by a third-party service designed for use with static sites. This site also features the use of CSS view transitions.
* [Cherokee Myths](https://github.com/WebOrigami/cherokee-myths) - A text-focused site with a generated table of contents and full-text search.
* [Japan Traverse Hike ebook](https://github.com/WebOrigami/japan-hike-ebook) - In addition to creating websites, Origami can generate other software artifacts like ebooks. This example project generates an ebook using markdown text with images.
* [Nick Simson's personal site](https://github.com/nsmsn/dotcom) - Blog, notes, and numerous slash pages for topics like books, radio, and records.
* [Jim Nielsen's Notes](https://github.com/jimniels/notes/) - Short responses to other people's writings. Reads notes from Dropbox via the Origami Dropbox extension and formats them as a single page with JSON and RSS feeds.
* [pondlife](https://github.com/WebOrigami/pondlife) - A sample blog built from markdown posts, with generated index pages, post pages, and feeds.
* [vale.rocks](https://github.com/DeclanChidlow/vale.rocks) - The personal site of Declan Chidlow, including a blog and a portfolio.
* [Web Origami documentation](https://github.com/WebOrigami/docs) - The Web Origami documentation site itself has many features. The primary content is defined in markdown with inlined Origami expressions, allowing individual documentation pages to generate SVG diagrams and run code samples in order to incorporate their output. Various navigation elements like a sidebar and top navigation bar are generated from the structure of the content. The site also generates API documentation from JavaScript source files and includes a search feature that indexes all the content.
* [WESL documentation site](https://github.com/wgsl-tooling-wg/website) - The documentation site for the WebGPU Shading Language (WESL). The site's Origami definition pulls markdown content from GitHub repositories for the WESL specification and an accompanying wiki, adding navigation and a search feature.