# mori's entry point is a build artifact, and its comparison version is not installed

> mori exposes ClojureScript's persistent data structures to plain JavaScript as one optimized file with no runtime dependencies. Its benchmark examples call shell globals, and the Immutable.js release it compares against is not the one in its own dependency list.

**swannodette/mori** — GitHub describes it as ClojureScript's persistent data structures and supporting API from the comfort of vanilla JavaScript. The repository metadata lists Clojure as its primary language. This article stays within the project description and details documented in the GitHub repository README.

- Repository: https://github.com/swannodette/mori
- Website: http://swannodette.github.io/mori
- Stars: 3,371 · Forks: 142
- Language: Clojure
- License: not declared
- Published: 2026-08-13 · Updated: 2026-08-18 · Language: en
- Canonical page: https://hysenlabs.com/projects/swannodette-mori

## The entry point is a generated file

The package manifest sets its main field to a single file at the root of the package, and the README says the installed package contains one optimized JavaScript file and nothing else. That file is produced by the build, and the manifest wires the build into publishing: the prepublish script runs the clean build variant before anything is pushed. So the artifact a user installs is output, not source, and the source tree stays where it is. The dependency list is empty, which fits a package that carries its own compiled output and needs nothing at runtime. Three loading paths are documented for that one file: a CommonJS require in Node, a script tag in a browser, and an AMD module definition for a loader such as RequireJS. The install itself is one command:

```shell
npm install mori
```

## The compared version is not the installed version

The manifest's two development dependencies are the competing immutable library at one exact version and a test runner at one exact version. The README's performance comparison names a different version of that competing library. The claim about small maps is that a Mori map of eight keys or fewer can be built nearly an order of magnitude faster than the other library's maps at version 3.6.2, while the pinned development dependency is one minor version behind that. So the headline number was measured against something the repository does not currently have installed, which means re-running the comparison today requires changing the pin first. That is a small thing, and it is the kind of small thing that decides whether a benchmark in a README can still be reproduced.

## One benchmark example calls shell globals

The freeze and thaw example compares building an array by repeated pushes against building a persistent vector through the library's internal conversion functions, and it reports roughly 220 milliseconds for the first against roughly 70 for the second, over ten iterations of ten million items on a specific V8 version and a specific laptop. The code that produces those numbers calls a print function and a garbage collection function directly, which are globals of a JavaScript shell rather than of Node or a browser, so the example as written does not run unmodified in the environments the rest of the README targets. It also reaches into internals: the thaw and freeze functions carry leading underscores, the conj call is written as a fixed-arity variant, and the count function is used to read the result.

## Small maps start as array-backed maps

One change in the current major line is about map representation rather than speed in the abstract. Mori maps now default to the array-backed map type from ClojureScript and are promoted to the hash map type as they grow, which the file credits for the small-map result above. The reasoning given is that array-backed maps perform better at small sizes and when the keys are simple, and eight keys is the threshold named. It is also a change with a consequence worth stating: the internal type of a map is no longer fixed, so anything that depended on the hash representation, including serialisation assumptions or code that counted on stable iteration characteristics, has to be checked against the promotion path.

## Map and Set interfaces are described as a proposal

All Mori maps and sets support the non-mutating half of the Map and Set interfaces as they were specified, with one deliberate difference: key lookup is by value rather than by reference. The iteration methods return the mutable iterators the specification proposed, and the README closes that section with a warning that the feature is subject to changes in the proposal. That warning is the honest reading. The library is exposing an interface that was still moving, and the mutable iterator return type in particular was not what shipped. Iteration also works through the symbol key or the string key the ClojureScript runtime uses, so the collections are reachable from ES6 iteration and from the older protocol.

## Transducers carry the performance argument

The transducers section makes the largest claim in the file. It builds a persistent vector from a million element array, then times two ways of applying an increment three times and summing. The first composes three mapping transducers and applies the composed function once, reported at roughly 190 milliseconds for ten iterations. The second chains three array maps and a reduce, reported at roughly 440. Both numbers come with the same V8 and machine note as the earlier example, which is how you should read them. The section's own summary is that transducers give zero allocation collection operations, and the gap between the two columns is the argument for them.

## The build needs Java, and Windows needs more

Building from source starts with a prerequisite the install instructions do not mention, which is a Java SDK. From there the paths diverge. On Unix-like systems the build tool is handled for you: it is installed inside the project if the executable is not on your path or if the version you have is older than 2.0.0. On Windows you are asked to install that tool manually before you start. Then there are three ways to run the build, a shell script for Unix-like systems, an npm script that calls the same script, and a PowerShell script for Windows. The output is the one optimized file the package ships, suitable for Node, for a browser or for any other JavaScript environment, including an AMD loader.

## An EPL licence, a retired host, and a 2015 copyright

The licence story is consistent once you find it, and it starts in the manifest rather than the README. The manifest declares an Eclipse Public License with a URL pointing at a GitHub raw host that the rest of the project does not use anywhere else, and the same licence text is committed at the repository root. The README states it plainly and adds the reason, that the library is distributed under the same licence as Clojure. The repository's own licence metadata records no assertion rather than a name, which leaves the file and the manifest as the only two sources. The copyright line in the README ends at 2015 while the last commit to the default branch is dated 2026-03-06, and the manifest still claims support for Node from 0.8.22 upwards in an era of code that iterates with symbols.

## Conclusion

mori fits a project that wants immutable collections and lazy sequences with a functional calling convention and does not want to build anything to use it. Check four things first: that the functional API suits you, since the structures deliberately have no public methods and that is the main difference from the library it benchmarks against, that your Node version is one the package still claims to support, that the ES6-style interfaces you rely on are the ones described, which the README warns are subject to a proposal changing, and that you are comfortable with a licence declared as the same one Clojure uses. The build path exists if you need it, and it needs Java.

## FAQ

### What is mori?

A bridge that exposes ClojureScript's persistent data structures to plain JavaScript, with a functional API in which the structures carry no public methods. The installed package is a single optimized JavaScript file with no runtime dependencies.

### How do you install mori?

Through npm, by installing the mori package. It has an empty dependency list, loads in Node with require, loads in a browser with a script tag, and can also be loaded as an AMD module with a loader such as RequireJS.

### How does mori compare with Immutable.js?

Its README lists three differences: a functional API with no public methods on the structures, faster performance attributed to nearly four years of real world use and continuous benchmarking on ClojureScript's own structures, and a gzipped base module about 6K larger.

### What changed in mori 0.3.0?

The API moved to idiomatic JavaScript naming conventions, which the README lists as a breaking change. The same release added direct arity invokes to remove dispatch overhead, made small maps array-backed with automatic promotion, and added ES6 iteration support.

### Do I need ClojureScript to use mori?

To build it, yes: the source build needs a Java SDK and the Leiningen build tool, installed automatically on Unix-like systems when missing or older than 2.0.0, and manually on Windows. To use the published package, you need neither.

### What licence is mori released under?

The Eclipse Public License, declared in the package manifest with the licence text also committed at the repository root, which the README says is the same licence Clojure uses. The repository's licence metadata records no assertion instead of a name.

## Sources

- [Official documentation](http://swannodette.github.io/mori)
- [Official README](https://github.com/swannodette/mori#readme)
- [Project repository](https://github.com/swannodette/mori)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/swannodette-mori
