Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ updates:
- '/aliases'
- '/core'
- '/fetchindex'
- '/fixtures'
- '/newest'
- '/oldest'
- '/opt'
Expand Down
15 changes: 8 additions & 7 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,15 +54,16 @@ jobs:
- 'package-lock.json'
- '.github/workflows/ci.yml'
aliases: [*shared, 'aliases/**']
core: [*shared, 'core/**', 'parsefiles/**']
core: [*shared, 'core/**', 'parsefiles/**', 'fixtures/**']
fetchindex: [*shared, 'fetchindex/**']
newest: [*shared, 'newest/**', 'core/**', 'parsefiles/**']
oldest: [*shared, 'oldest/**', 'core/**', 'parsefiles/**']
fixtures: [*shared, 'fixtures/**']
newest: [*shared, 'newest/**', 'core/**', 'parsefiles/**', 'fixtures/**']
oldest: [*shared, 'oldest/**', 'core/**', 'parsefiles/**', 'fixtures/**']
opt: [*shared, 'opt/**']
parsefiles: [*shared, 'parsefiles/**']
ranges: [*shared, 'ranges/**', 'core/**', 'aliases/**', 'parsefiles/**']
static: [*shared, 'static/**', 'core/**', 'parsefiles/**']
translate: [*shared, 'translate/**', 'ranges/**', 'core/**', 'aliases/**', 'parsefiles/**']
parsefiles: [*shared, 'parsefiles/**', 'fixtures/**']
ranges: [*shared, 'ranges/**', 'core/**', 'aliases/**', 'parsefiles/**', 'fixtures/**']
static: [*shared, 'static/**', 'core/**', 'parsefiles/**', 'fixtures/**']
translate: [*shared, 'translate/**', 'ranges/**', 'core/**', 'aliases/**', 'parsefiles/**', 'fixtures/**']
# the site shows core's version, which every release bumps
docs: [*shared, 'docs/**', 'doc-kit.config.mjs', 'core/package.json', '.github/workflows/docs.yml']
workflows: ['.github/**']
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ jobs:
parsefiles
ranges
static
!fixtures
!opt
!translate

Expand Down Expand Up @@ -74,6 +75,7 @@ jobs:
parsefiles
ranges
static
!fixtures
!opt
!translate

Expand Down Expand Up @@ -111,6 +113,7 @@ jobs:
parsefiles
ranges
static
!fixtures
!opt
!translate

Expand Down
4 changes: 3 additions & 1 deletion .oxlintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,13 @@
"*/types/**"
],
"jsPlugins": [
{ "name": "anti-slop", "specifier": "./tools/oxlint/anti-slop/index.ts" }
{ "name": "anti-slop", "specifier": "./tools/oxlint/anti-slop/index.ts" },
{ "name": "nodevu", "specifier": "./tools/oxlint/nodevu/index.mjs" }
],
"rules": {
"no-unused-vars": ["warn", { "ignoreRestSiblings": true }],
"oxc/no-accumulating-spread": "error",
"nodevu/no-cross-package-paths": "error",
"anti-slop/no-array-filter-map": "error",
"anti-slop/no-reduce-accumulator-copy": "error",
"anti-slop/no-chained-type-assertions": "error",
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ nodevu is composed of a set of modules, each supporting Node.js 24 and later:

Installing needs npm 11.17 or newer, which ships with Node.js 24 and later. `.npmrc` skips packages published in the last 3 days and blocks git and URL dependencies. npm only runs the dependency install scripts that `allowScripts` in `package.json` approves, and `npm approve-scripts <pkg>` adds one pinned to its installed version.

- `npm test` and `npm run coverage` run every module's tests.
- `npm run lint` checks the whole repo with oxlint (warnings fail it too) and oxfmt, and checks with [docov](https://github.com/cutenode/docov) that everything the modules export has JSDoc. `docov.jsonc` sets what it scans. `npm run lint:harden` tests `tools/harden/` and runs [@lavamoat/harden](https://github.com/LavaMoat/LavaMoat/tree/main/packages/harden) to check that `.npmrc` and `package.json` keep every hardening setting, except the ones `tools/harden/decisions.json` turns off. `npm run lint:pack` packs every module and checks the tarball with [publint](https://publint.dev) and [Are the Types Wrong?](https://arethetypeswrong.github.io). `npm run lint:workspaces` checks that every config naming the modules (tsconfig, CI, release, Dependabot, the docs sidebar) matches `workspaces` in `package.json`. `npm run knip` runs [knip](https://knip.dev) to find unused files, exports and dependencies. `knip.jsonc` lists what it skips and why. `npm run lint` runs all of them.
- `npm test` and `npm run coverage` run every module's tests. The tests share the release data snapshot and its helpers through `fixtures/`, a private workspace that isn't published. `npm run test:update -w fixtures` refreshes the snapshot.
- `npm run lint` checks the whole repo with oxlint (warnings fail it too) and oxfmt, and checks with [docov](https://github.com/cutenode/docov) that everything the modules export has JSDoc. `docov.jsonc` sets what it scans. `npm run lint:harden` tests `tools/harden/` and runs [@lavamoat/harden](https://github.com/LavaMoat/LavaMoat/tree/main/packages/harden) to check that `.npmrc` and `package.json` keep every hardening setting, except the ones `tools/harden/decisions.json` turns off. `tools/oxlint/nodevu/` holds the repo's own oxlint rule, which stops code from reaching into another package's folder by relative path, and `npm run lint:rules` tests it. `npm run lint:pack` packs every module and checks the tarball with [publint](https://publint.dev) and [Are the Types Wrong?](https://arethetypeswrong.github.io). `npm run lint:workspaces` checks that every config naming the modules (tsconfig, CI, release, Dependabot, the docs sidebar) matches `workspaces` in `package.json`. `npm run knip` runs [knip](https://knip.dev) to find unused files, exports and dependencies. `knip.jsonc` lists what it skips and why. `npm run lint` runs all of them.
- CI also checks spelling with [typos](https://github.com/crate-ci/typos) (`_typos.toml` lists words it shouldn't flag), relative links and anchors with [lychee](https://lychee.cli.rs), the workflows with [actionlint](https://github.com/rhysd/actionlint) and [zizmor](https://docs.zizmor.sh), and that a PR adds no dependency with a known advisory. A weekly run tests every module on Linux, macOS and Windows, tries 50 times as many fast-check cases, and checks external links.
- `npm run types` type-checks the JSDoc, the tests and the scripts, and writes each module's TypeScript declarations to its `types/` folder. Commit them: CI fails when they're out of date.
- `npm run docs:dev` builds the docs from `docs/pages/` with [doc-kit](https://doc-kit.nodejs.org/) and serves them locally. `npm run docs` only builds them, into `out/`.
Expand Down
4 changes: 1 addition & 3 deletions core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,6 @@
"lint": "oxlint && oxfmt --check",
"lint:write": "oxlint --fix && oxfmt",
"test": "node --test",
"test:update": "npm run test:update:static && npm run test:update:now",
"test:update:static": "node ./util/dev/updateStaticData.js",
"test:update:now": "node ./util/dev/updateStaticNow.js",
"coverage": "c8 node --test"
},
"author": "Tierney Cyren <hello@bnb.im> (https://bnb.im/)",
Expand All @@ -27,6 +24,7 @@
"semver": "^7.6.3"
},
"devDependencies": {
"@nodevu/fixtures": "^0.0.0",
"c8": "^12.0.0",
"fast-check": "^4.10.2",
"undici": "^8.12.2"
Expand Down
4 changes: 2 additions & 2 deletions core/test/combos.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ const nodevu = require('../index');
const { describe, it, beforeEach } = require('node:test');
const undici = require('undici');

const beforeEachTemplate = require('../util/dev/beforeEachTemplate');
const beforeEachTemplate = require('@nodevu/fixtures/beforeEachTemplate');

/** @param {import('../index').Data} data */
function check(data) {
Expand All @@ -30,7 +30,7 @@ function check(data) {
assert.deepStrictEqual(data.v12.security.oldest, '12.8.1');
}

const now = require('./data/static/now.json');
const now = require('@nodevu/fixtures/data/now.json');

// undici's types only take its own Request class, so they don't match the
// global fetch's type even though it works as one
Expand Down
4 changes: 2 additions & 2 deletions core/test/dynamic-values.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ const assert = require('node:assert');
const nodevu = require('../index');
const { describe, it, beforeEach } = require('node:test');

const beforeEachTemplate = require('../util/dev/beforeEachTemplate');
const beforeEachTemplate = require('@nodevu/fixtures/beforeEachTemplate');

const now = require('./data/static/now.json');
const now = require('@nodevu/fixtures/data/now.json');

// this is a set of tests that only test against static data effectively frozen at a point in time.
// these tests should *probably* be updated sometimes. see the npm scripts for update scripts.
Expand Down
2 changes: 1 addition & 1 deletion core/test/properties.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ const { describe, it } = require('node:test');
const fc = require('fast-check');
const semver = require('semver');
const nodevu = require('../index');
const { at } = require('../util/dev/fixtures');
const { at } = require('@nodevu/fixtures');

const phases = ['alpha', 'start', 'lts', 'maintenance', 'end'];

Expand Down
2 changes: 1 addition & 1 deletion core/test/static-values.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ const assert = require('node:assert');
const nodevu = require('../index');
const { describe, it, beforeEach } = require('node:test');

const beforeEachTemplate = require('../util/dev/beforeEachTemplate');
const beforeEachTemplate = require('@nodevu/fixtures/beforeEachTemplate');

// these are tests that run with static data but do not need to be frozen in time.
describe('check that we get the values we expect from values that should not ever change', async () => {
Expand Down
2 changes: 1 addition & 1 deletion core/test/unique-source.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ const { deepStrictEqual, ok } = require('node:assert');
const nodevu = require('../index');
const { describe, it, beforeEach } = require('node:test');

const beforeEachTemplate = require('../util/dev/beforeEachTemplate');
const beforeEachTemplate = require('@nodevu/fixtures/beforeEachTemplate');

describe('check to make sure that changing sources works as expected', async () => {
beforeEach(beforeEachTemplate);
Expand Down
1 change: 0 additions & 1 deletion fetchindex/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@
"homepage": "https://github.com/cutenode/nodevu#readme",
"devDependencies": {
"c8": "^12.0.0",
"luxon": "^3.5.0",
"undici": "^8.12.2"
},
"types": "types/index.d.ts",
Expand Down
18 changes: 0 additions & 18 deletions fetchindex/test/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ const undiciFetch = /** @type {typeof fetch} */ (
);

const fetchindex = require('../index');
const optionsParser = require('../../core/util/prod/optionsParser');

// checks that verify the result of data returned
/** @param {import('../index').IndexRelease[]} data */
Expand Down Expand Up @@ -63,23 +62,6 @@ describe('under normal condiditons, versions should work', async () => {
});
});

describe('versions should work with optionsParser', async () => {
it('should work with the default output of optionsParser', async () => {
const parsedOptions = optionsParser({});
const data = await fetchindex(parsedOptions);
check(data);
});

it('should work with a different fetch passed to optionsParser', async () => {
const parsedOptions = optionsParser({
fetch: undiciFetch,
});

const data = await fetchindex(parsedOptions);
check(data);
});
});

describe('fetchindex', async () => {
it('should reject naming the URL and status when the response is not ok', async () => {
// Arrange
Expand Down
21 changes: 21 additions & 0 deletions fixtures/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License Copyright (c) 2022 Tierney Cyren

Permission is hereby granted, free
of charge, to any person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to the
following conditions:

The above copyright notice and this permission notice
(including the next paragraph) shall be included in all copies or substantial
portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@ const {
setGlobalDispatcher,
} = require('undici');

const staticIndex = require('../../test/data/static/index.json');
const staticSchedule = require('../../test/data/static/schedule.json');
const staticIndex = require('./data/index.json');
const staticSchedule = require('./data/schedule.json');

/**
* Mocks the network for a test: the default and `https://bnb.im` index.json
* and schedule.json URLs serve core's static snapshots, and any other request
* and schedule.json URLs serve the snapshots, and any other request
* fails.
*/
function beforeEachTemplate() {
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
15 changes: 10 additions & 5 deletions core/util/dev/fixtures.js → fixtures/index.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,22 @@
const index = require('../../test/data/static/index.json');
const schedule = require('../../test/data/static/schedule.json');
const index = require('./data/index.json');
const schedule = require('./data/schedule.json');

/**
* Returns options for @nodevu/core (and the packages built on it) that serve
* `data` (core's index.json and schedule.json snapshots by default) instead of
* `data` (the index.json and schedule.json snapshots by default) instead of
* fetching them, only including releases that were out by `now`, like the
* real index.json. Any other URL throws, so tests never touch the network.
*
* @param {string} now
* `now` defaults to 2025-09-01, picked so that current, active LTS,
* maintenance LTS and end of life each have a release line, and each of those
* lines already has a security release, so no range or security field is
* empty.
*
* @param {string} [now]
* @param {{ index: { date: string }[], schedule: unknown }} [data]
* @returns {{ now: string, fetch: typeof fetch }}
*/
function at(now, data = { index, schedule }) {
function at(now = '2025-09-01', data = { index, schedule }) {
return {
now,
fetch: async (url) => {
Expand Down
31 changes: 31 additions & 0 deletions fixtures/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{
"name": "@nodevu/fixtures",
"version": "0.0.0",
"description": "Snapshot data and helpers the nodevu packages' tests share. Not published.",
"main": "index.js",
"scripts": {
"test": "node --test",
"lint": "oxlint && oxfmt --check",
"lint:write": "oxlint --fix && oxfmt",
"coverage": "c8 node --test",
"test:update": "npm run test:update:static && npm run test:update:now",
"test:update:static": "node ./util/updateStaticData.js",
"test:update:now": "node ./util/updateStaticNow.js"
},
"author": "Tierney Cyren <hello@bnb.im> (https://bnb.im/)",
"license": "MIT",
"private": true,
"dependencies": {
"undici": "^8.12.4"
},
"devDependencies": {
"c8": "^12.0.0",
"fast-check": "^4.10.2",
"luxon": "^3.7.2"
},
"types": "types/index.d.ts",
"type": "commonjs",
"engines": {
"node": ">=24"
}
}
14 changes: 11 additions & 3 deletions core/test/dev-fixtures.js → fixtures/test/at.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
const assert = require('node:assert');
const { describe, it } = require('node:test');
const fc = require('fast-check');
const { at } = require('../util/dev/fixtures');
const schedule = require('./data/static/schedule.json');
const { at } = require('../index');
const schedule = require('../data/schedule.json');

describe('at', async () => {
it('should pass now through as an option', () => {
Expand All @@ -16,7 +16,15 @@ describe('at', async () => {
assert.strictEqual(options.now, now);
});

it("should serve core's schedule.json snapshot by default", async () => {
it('should default now to 2025-09-01', () => {
// Act
const options = at();

// Assert
assert.strictEqual(options.now, '2025-09-01');
});

it('should serve the schedule.json snapshot by default', async () => {
// Arrange
const { fetch } = at('2025-09-01');

Expand Down
19 changes: 9 additions & 10 deletions core/test/dev-scripts.js → fixtures/test/scripts.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ const { join } = require('node:path');
const { describe, it } = require('node:test');
const { promisify } = require('node:util');
const { DateTime } = require('luxon');
const index = require('./data/static/index.json');
const schedule = require('./data/static/schedule.json');
const index = require('../data/index.json');
const schedule = require('../data/schedule.json');

describe('test:update:static', async () => {
it('should save the index.json and schedule.json it downloads', async () => {
Expand All @@ -17,7 +17,7 @@ describe('test:update:static', async () => {
// Act
await runScript(
'updateStaticData.js',
`require(${JSON.stringify(require.resolve('../util/dev/beforeEachTemplate'))})();`,
`require(${JSON.stringify(require.resolve('../beforeEachTemplate'))})();`,
cwd,
);

Expand Down Expand Up @@ -53,10 +53,11 @@ describe('test:update:now', async () => {

// Helpers

// a directory laid out like core, for the scripts to write their snapshots into
// a directory laid out like this package, for the scripts to write their
// snapshots into
async function snapshotDirectory() {
const cwd = await mkdtemp(join(tmpdir(), 'nodevu-'));
await mkdir(join(cwd, 'test/data/static'), { recursive: true });
await mkdir(join(cwd, 'data'));

return cwd;
}
Expand All @@ -66,20 +67,18 @@ async function snapshotDirectory() {
* @param {string} file
*/
async function readSnapshot(cwd, file) {
return JSON.parse(
await readFile(join(cwd, 'test/data/static', file), 'utf8'),
);
return JSON.parse(await readFile(join(cwd, 'data', file), 'utf8'));
}

// runs a util/dev script the way its npm script does, after `setup` stubs
// runs a util script the way its npm script does, after `setup` stubs
// whatever it reaches outside the process for
/**
* @param {string} script
* @param {string} setup
* @param {string} cwd
*/
function runScript(script, setup, cwd) {
const path = require.resolve(`../util/dev/${script}`);
const path = require.resolve(`../util/${script}`);

return promisify(execFile)(
process.execPath,
Expand Down
2 changes: 1 addition & 1 deletion core/test/dev-write.js → fixtures/test/write.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ const { mkdtemp, readFile } = require('node:fs/promises');
const { tmpdir } = require('node:os');
const { join } = require('node:path');
const { describe, it } = require('node:test');
const write = require('../util/dev/write');
const write = require('../util/write');

describe('write', async () => {
it('should save data as tab-indented JSON', async () => {
Expand Down
5 changes: 5 additions & 0 deletions fixtures/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"extends": "../tsconfig.base.json",
"include": ["index.js", "beforeEachTemplate.js", "data/*.json"],
"references": []
}
Loading
Loading