Skip to content

Repository files navigation

@cldmv/wisp

@cldmv/wisp loads JSON files in Node.js without caring which JSON import syntax the running Node.js version understands. It tries the modern import ... with { type: "json" } form first, falls back to the legacy assert form, and finally reads and parses the file itself, so the same call works from Node.js 16 through the current release.

Relative paths resolve from the file that calls wisp, not from wisp's own location, so wispSync("./config.json") means what it looks like it means from anywhere in your project — including from packages that depend on wisp.

Load JSON the same way on every Node.js version — quietly, like a wisp.

npm version npm downloads GitHub downloads Last commit npm last update

Contributors Sponsor shinrai


✨ What's New

Latest: v1.0.7 (October 2026)

  • require() works in bundles and fails clearly on older Node.js — index.cjs now loads the ESM entry with a plain require("./index.mjs") instead of createRequire(__filename), so require("@cldmv/wisp") survives esbuild and webpack bundling. On Node.js versions without synchronous require(esm), where require() never worked, it now throws an ERR_REQUIRE_ESM error that names the supported versions (^20.19.0 or >=22.12.0) and points to import() (#30).
  • A failed validation throws instead of loading the fallback — fallback is now used only when the primary file cannot be read or parsed; a validate rejection is reported as an error, and a fallback that also fails no longer loops forever (#35). On the import() paths, a validate rejection now throws the validation error once instead of Unsupported type, and reviver / validate on a module without a default export receive a plain-object copy of its exports instead of failing (#41). The package is also relicensed under Apache-2.0 (#34).
  • Built package in dist/ — the published package is now bundled with tsup into dist/index.mjs, with dist/index.cjs as a thin require() wrapper, and ships only dist/, types/, README.md and LICENSE. import and require() of @cldmv/wisp work exactly as before; code that loaded the old root index.mjs / index.cjs or src/ files by path must use the package specifier (#40). The test suite now runs on @cldmv/vitest-runner with 100% coverage (#37).
  • View full v1.0.7 Changelog

Recent Releases

  • v1.0.6 (October 2026) — Uniform file headers via @cldmv/fix-headers, a CI fix and a development-dependency security update; no runtime change (Changelog)
  • v1.0.5 (October 2026) — First npm release since v1.0.1; TypeScript 6, chai 6 and @types/node 26 for development, v4 workflow syncs; no runtime change (Changelog)
  • v1.0.4 (September 2026) — Thrown errors now carry the original error as cause; ESLint wired up; mocha 12 (Changelog)
  • v1.0.3 (August 2026) — Development-dependency security update (picomatch); no runtime change (Changelog)

📚 For complete version history and detailed release notes, see the docs/changelog/ folder.


🚀 Key Features

  • Version-agnostic JSON imports — with, then assert, then a file-system read; whichever the running Node.js supports.
  • Caller-aware paths — relative paths resolve from the calling file; base overrides it.
  • Async and sync — wisp() returns a promise, wispSync() returns the value directly.
  • Validation and revivers — validate rejects bad data, reviver is passed to JSON.parse.
  • Fallback files — fallback names a second file to try when the first cannot be loaded.
  • ESM and CommonJS — import and require() entry points, with TypeScript declarations included.
  • Zero runtime dependencies.

Node.js Version Support

Node Version import ... with { type: 'json' } import ... assert { type: 'json' } Fallback
≥ 22.10 ✅ ✅ ✅
≥ 20.10 ✅ ✅ ✅
≥ 18.20 ✅ ✅ ✅
≥ 16.14 ❌ ✅ ✅
< 16.14 ❌ ❌ ✅

📦 Installation

Requirements

  • Node.js 16 or higher for import (ESM).
  • require() needs Node.js ^20.19.0 or >=22.12.0 (synchronous require(esm)). On older Node.js versions, load the package with import() instead.

Install

npm install @cldmv/wisp

🚀 Quick Start

ESM

import { wisp, wispSync } from "@cldmv/wisp";

// Asynchronous loading
const config = await wisp("./config.json");

// Synchronous loading
const data = wispSync("./data.json");

CommonJS

const { wisp, wispSync } = require("@cldmv/wisp");

// Asynchronous loading
wisp("./config.json").then((config) => {
	console.log(config);
});

// Synchronous loading
const data = wispSync("./data.json");

📖 API Reference

wisp(input, options?)

Asynchronously loads JSON from a file.

Parameters

  • input (string | URL): Path or URL to the JSON file
  • options (object, optional):
    • base (string | URL, optional): Base URL for resolving relative paths. Defaults to the caller's file URL.
    • validate (function, optional): Validation function called with the parsed JSON. Throws if validation fails.
    • reviver (function, optional): Reviver function passed to JSON.parse. For a module loaded through import() that has no default export, reviver and validate receive a plain-object copy of its exports.
    • type (string, optional): Import attribute type used for the import() attempts. Defaults to "json"; the file-system fallback only runs for "json".
    • fallback (string | URL, optional): A second file to load when input cannot be read or parsed. A validate failure on input throws rather than falling back.

Returns

Promise<*>: The parsed JSON value.

Example

import { wisp } from "@cldmv/wisp";

const data = await wisp("./config.json", {
	validate: (json) => {
		if (!json.requiredField) throw new Error("Missing required field");
	},
	reviver: (key, value) => (key === "date" ? new Date(value) : value)
});

wispSync(input, options?)

Synchronously loads JSON from a file.

Parameters

  • input (string | URL): Path or URL to the JSON file
  • options (object, optional): Same as wisp options, except type (the file is always read and parsed as JSON).

Returns

*: The parsed JSON value.

Example

import { wispSync } from "@cldmv/wisp";

const data = wispSync("./config.json", {
	validate: (json) => {
		if (!json.version) throw new Error("Version required");
	}
});

⚙️ Options

Option Type Description
base string/URL Base URL for relative path resolution. Defaults to caller's file URL.
validate function Validation function. Receives parsed JSON, should throw on invalid data.
reviver function JSON.parse reviver function for custom parsing.
type string Import attribute type for wisp()'s import() attempts. Defaults to "json".
fallback string/URL File to load instead when input cannot be read or parsed (missing, unreadable, or not valid JSON). A validate failure throws instead.

🧭 Path Resolution

@cldmv/wisp uses caller-aware path resolution:

  • Relative paths are resolved relative to the file that calls wisp or wispSync
  • Absolute paths and URLs are used as-is
  • The base option overrides the default caller-based resolution

🔁 Fallback Order

The module attempts to load JSON in this order:

  1. import(url, { with: { type: 'json' } }) (Node ≥ 18.20/20.10/22)
  2. import(url, { assert: { type: 'json' } }) (Node ≥ 16.14)
  3. fs.readFile / fs.readFileSync (all supported Node versions)

This ensures maximum compatibility across Node.js versions. wispSync always uses fs.readFileSync.


🛡 Error Handling

Errors thrown by wisp are prefixed with @cldmv/wisp: for easy identification, and carry the underlying error as error.cause. A validation failure is reported as part of the load error:

try {
	await wisp("./invalid.json", {
		validate: () => {
			throw new Error("Custom validation failed");
		}
	});
} catch (error) {
	console.log(error.message); // "@cldmv/wisp: Failed to load JSON file at file:///…/invalid.json: @cldmv/wisp: Custom validation failed"
}

📚 Documentation

CodeFactor OpenSSF Scorecard npms.io score npm unpacked size Repo size


🤝 Contributing

Contributions are welcome — open an issue or a pull request.

Contributors Sponsor shinrai


🔗 Links


📄 License

GitHub license npm license

Apache-2.0 © CLDMV Inc. See LICENSE for the full text.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Sponsor this project

Packages

Used by

Contributors

Languages