# Licia's zero dependency promise comes with a postinstall script

> Licia is a collection of more than 400 single function modules with no runtime dependencies, published as one npm package you import one module at a time. Its own test harness runs inside a Node 12 container, and installing the package executes a script from it.

**liriliri/licia** — Useful utility collection with zero dependencies

- Repository: https://github.com/liriliri/licia
- Website: https://licia.liriliri.io
- Stars: 2,382 · Forks: 164
- Language: JavaScript
- License: MIT
- Published: 2026-09-28 · Updated: 2026-09-28 · Language: en
- Canonical page: https://hysenlabs.com/projects/liriliri-licia

## A postinstall script runs on every npm install

The zero dependency claim is about runtime, and the install step is not silent. The package metadata defines a `postinstall` entry, `node ./lib/setup`, which npm runs after it unpacks the package. So an install of licia executes a script that ships inside licia before you import a single module from it. That is normal for a project whose build step is part of its install path, and the same design shows up elsewhere in the metadata: the `build` script runs `licia build`, formats the generated packages, and then copies `.licia/packages/licia/*` into node_modules with `copyfiles -u 2`, so the built output is written into an installed dependency directory rather than into a separate dist folder. The runtime list is empty, while the development list is long, with Babel, ESLint, Karma, Mocha and Prettier among the tools that never reach a consumer.

## The containerised test harness runs on Node 12.22.12

Per module tests run inside a container, and the image tag is pinned to an exact patch release:

```
services:
  test:
    image: node:12.22.12
    volumes:
      - ./:/licia
    working_dir: /licia
    environment:
      - MOD_NAME
    command: ./node_modules/.bin/mocha .licia/test/${MOD_NAME}
    deploy:
      resources:
        limits:
          cpus: '1.5'
          memory: 200M
```

Three things follow from that file. The test target is chosen by an environment variable, `MOD_NAME`, interpolated into the Mocha path, so one module is tested per container run rather than the whole suite at once. The runtime is Node 12.22.12 whatever Node your workstation has, so a behaviour difference between your machine and the test machine is not the explanation for a failure. And the resource block caps the container at 1.5 CPUs and 200M of memory, which is the kind of budget you set when the container has to behave the same way on every run.

## Two lint configs, and the release one runs with ignores disabled

The repository keeps two ESLint configurations and uses them for different code. The source lint script is `eslint -c eslint.src.js lib/**/*.js lib/*.js bin/*.js --fix`, which covers the library sources and the command line entry and rewrites files in place. The release lint script is `eslint -c eslint.release.js .licia/packages/licia-src/*.js --no-ignore`, pointed at the generated release sources rather than lib, and it passes `--no-ignore` so that the ignore files do not apply to code that is about to ship. The full CI script runs both, and it runs the project's own linter first: `licia lint`, then `npm run lint`, then the build, then the ES5 step, then `lint:release`, then the test suite. Two rulesets over the same repository means a style that is acceptable in a source file can still fail on the build output, and the ignore bypass means the release check is deliberately stricter about what it examines.

## The build badge names a main workflow on a master branch

The CI badge in the header points at `actions/workflow/status/liriliri/licia/main.yml` with the query string `branch=master`. The workflow file is named main.yml while the branch being asked about, and the repository's default branch, is master. The same split appears in the neighbouring badges: the coverage badge also queries `branch=master`, while the package size badge points at Bundlephobia and the version and license badges at npm. Small as it is, that mismatch is the kind of thing that makes a badge look stale for the wrong reason, since the status it reports comes from a workflow file whose name no longer matches the branch layout of the repository.

## Tests run four ways, one of them against the built package

The test script is a chain of four suites rather than one. `test:node` runs `licia test -s` against the sources, `test:browser` runs `licia test -bs` in a browser through Karma, `test:release` copies `.licia/packages/licia/*` into `.licia/node_modules` and then runs `licia test -r`, and `test:ts` runs `licia test -s --ts` followed by `tsc`. That third suite is the interesting one: it does not test the working tree, it tests the artifacts that consumers would install, after copying them into a node_modules directory. Type checking is part of the default run, so the TypeScript declarations shipped with the modules are compiled rather than trusted, and coverage runs through `nyc` with its own configuration file at the root. A fourth path, `test:sauce`, runs the browser suite with the `--sauce` flag for a Sauce Labs run, which is the only script in the set that needs a remote service.

## Over 400 modules, imported one at a time, with no category map

The positioning argument in the page is against categorised utility libraries. Underscore and mout are described as strictly separating functions into categories such as array, string and function, and licia is described instead as a collection of more than 400 micro modules dealing with problems in different aspects. Usage follows from that: you install one package and reach for the single module you need.

```bash
npm i licia --save
```

```javascript
const uuid = require('licia/uuid');

console.log(uuid()); // -> 0e3b84af-f911-4a55-b78a-cedf6f0bd815
```

The listed modules cover DOM work in a jQuery style, cookies, date formatting, a Promise polyfill, a small event emitter, Ajax and its Promise version fetch, underscore style helpers such as shuffle and unique, and an mkdir in the shape of mkdirp. For ES6 output or smaller bundles the page points at a separate package, licia-es, and for a subset of your own it points at an online builder.

## Two documentation files and 24 module demo pages

The documentation in the repository comes in two languages, with DOC.md and DOC_CN.md at the root and an i18n directory beside them, and a cspell.json for a spell checker. The browser demos are per module: the demo directory holds 24 HTML pages, one per module, among them Promise, Tween, ResizeSensor, MediaQuery, hotkey, fullscreen, orientation, highlight, notify, download, prefetch, copy, openFile, deprecate, uncaught, workerize, emulateTouch, compressImg, dpr, randomColor, scrollTo, stringifyAll, debug, Logger and Class. The demo script in the metadata is `licia demo`, so those pages are generated rather than hand maintained. Alongside them the root holds separate `src/` and `lib/` trees, an `index.json`, a `tsconfig.json`, a `benchmark/` directory, an `eslint.src.js` and `eslint.release.js` pair, and agent instruction files in AGENTS.md, CLAUDE.md and `.agents/`. Releases are frequent at the patch level: v1.49.0 on 2026-08-13, v1.48.1 on 2026-05-11 and v1.48.0 on 2025-03-27, and the last push to the repository carries the same date as the newest tag.

## Conclusion

Licia fits a JavaScript project that wants one small module for one job and would rather not inherit a dependency tree, especially where a tree shaking step or a bundle budget makes a single large library awkward. Before adding it, read the postinstall entry in the package metadata, because npm will run code from the package at install time, and check that the module you want exists before you commit to the shape of the import. Decide between the main package and licia-es for ES6 output and smaller bundles. It is a grab bag by design rather than a coherent library, and the repository ships its own tooling for building, linting and testing that collection.

## FAQ

### What is licia?

Licia is a JavaScript utility library made of more than 400 micro modules, each handling one problem, published under the MIT license with no runtime dependencies. You install the one package and import the individual module you need, for example require('licia/uuid').

### How do I install and use licia?

Run `npm i licia --save`, then require the module you want by path, such as `const uuid = require('licia/uuid')`. For ES6 output or smaller bundles the page points at a separate package, licia-es, and for a custom subset it points at an online builder tool.

### Does installing licia run any code?

Yes. The package metadata defines a postinstall script, `node ./lib/setup`, and npm runs it after unpacking the package. The published package itself declares no runtime dependencies, and the build script copies generated packages into node_modules with copyfiles.

### How does licia run its tests?

The test script chains four suites: node, browser through Karma, the built release packages after they are copied into a node_modules directory, and TypeScript compilation. A Sauce Labs variant exists, and the docker-compose service runs Mocha on one module at a time using the MOD_NAME variable inside a node:12.22.12 image capped at 1.5 CPUs and 200M of memory.

### Who publishes licia and how often does it change?

The package author is redhoodsu, the license is MIT and the homepage is licia.liriliri.io. The three most recent tags are v1.49.0 on 2026-08-13, v1.48.1 on 2026-05-11 and v1.48.0 on 2025-03-27, and the last push to the repository is dated 2026-08-13.

## Sources

- [License: MIT](https://github.com/liriliri/licia/blob/master/LICENSE)
- [liriliri/licia on GitHub](https://github.com/liriliri/licia)
- [Project website](https://licia.liriliri.io)
- [README](https://github.com/liriliri/licia/blob/master/README.md)
- [Releases](https://github.com/liriliri/licia/releases)

---

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