diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 21b84d06..551f9ecf 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -7,6 +7,7 @@ updates: - '/aliases' - '/core' - '/fetchindex' + - '/fixtures' - '/newest' - '/oldest' - '/opt' diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0f216e51..25f463c3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -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/**'] diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a4de8a56..b8b12f79 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -41,6 +41,7 @@ jobs: parsefiles ranges static + !fixtures !opt !translate @@ -74,6 +75,7 @@ jobs: parsefiles ranges static + !fixtures !opt !translate @@ -111,6 +113,7 @@ jobs: parsefiles ranges static + !fixtures !opt !translate diff --git a/.oxlintrc.json b/.oxlintrc.json index 007569d2..3abc443f 100644 --- a/.oxlintrc.json +++ b/.oxlintrc.json @@ -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", diff --git a/README.md b/README.md index e6e185f3..10d063f1 100644 --- a/README.md +++ b/README.md @@ -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 ` 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/`. diff --git a/core/package.json b/core/package.json index 6ce7154e..95301b91 100644 --- a/core/package.json +++ b/core/package.json @@ -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 (https://bnb.im/)", @@ -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" diff --git a/core/test/combos.js b/core/test/combos.js index b11034d2..3cc1b440 100644 --- a/core/test/combos.js +++ b/core/test/combos.js @@ -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) { @@ -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 diff --git a/core/test/dynamic-values.js b/core/test/dynamic-values.js index e8a56169..5cae09a9 100644 --- a/core/test/dynamic-values.js +++ b/core/test/dynamic-values.js @@ -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. diff --git a/core/test/properties.js b/core/test/properties.js index 8f7b3b37..edbe0aff 100644 --- a/core/test/properties.js +++ b/core/test/properties.js @@ -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']; diff --git a/core/test/static-values.js b/core/test/static-values.js index ac3498df..2a402ef3 100644 --- a/core/test/static-values.js +++ b/core/test/static-values.js @@ -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 () => { diff --git a/core/test/unique-source.js b/core/test/unique-source.js index cb46deec..f6a26ec6 100644 --- a/core/test/unique-source.js +++ b/core/test/unique-source.js @@ -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); diff --git a/fetchindex/package.json b/fetchindex/package.json index a5ca2552..e3a50eeb 100644 --- a/fetchindex/package.json +++ b/fetchindex/package.json @@ -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", diff --git a/fetchindex/test/test.js b/fetchindex/test/test.js index 912e678b..767be668 100644 --- a/fetchindex/test/test.js +++ b/fetchindex/test/test.js @@ -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 */ @@ -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 diff --git a/fixtures/LICENSE b/fixtures/LICENSE new file mode 100644 index 00000000..b8e356ab --- /dev/null +++ b/fixtures/LICENSE @@ -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. \ No newline at end of file diff --git a/core/util/dev/beforeEachTemplate.js b/fixtures/beforeEachTemplate.js similarity index 88% rename from core/util/dev/beforeEachTemplate.js rename to fixtures/beforeEachTemplate.js index b3525485..65f030ab 100644 --- a/core/util/dev/beforeEachTemplate.js +++ b/fixtures/beforeEachTemplate.js @@ -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() { diff --git a/core/test/data/static/index.json b/fixtures/data/index.json similarity index 100% rename from core/test/data/static/index.json rename to fixtures/data/index.json diff --git a/core/test/data/static/now.json b/fixtures/data/now.json similarity index 100% rename from core/test/data/static/now.json rename to fixtures/data/now.json diff --git a/core/test/data/static/schedule.json b/fixtures/data/schedule.json similarity index 100% rename from core/test/data/static/schedule.json rename to fixtures/data/schedule.json diff --git a/core/util/dev/fixtures.js b/fixtures/index.js similarity index 59% rename from core/util/dev/fixtures.js rename to fixtures/index.js index 717502da..cc2865d0 100644 --- a/core/util/dev/fixtures.js +++ b/fixtures/index.js @@ -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) => { diff --git a/fixtures/package.json b/fixtures/package.json new file mode 100644 index 00000000..08a8d9a9 --- /dev/null +++ b/fixtures/package.json @@ -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 (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" + } +} diff --git a/core/test/dev-fixtures.js b/fixtures/test/at.js similarity index 86% rename from core/test/dev-fixtures.js rename to fixtures/test/at.js index 2bf8a1a1..89549ab9 100644 --- a/core/test/dev-fixtures.js +++ b/fixtures/test/at.js @@ -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', () => { @@ -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'); diff --git a/core/test/dev-scripts.js b/fixtures/test/scripts.js similarity index 76% rename from core/test/dev-scripts.js rename to fixtures/test/scripts.js index cbd3b6b4..e663461e 100644 --- a/core/test/dev-scripts.js +++ b/fixtures/test/scripts.js @@ -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 () => { @@ -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, ); @@ -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; } @@ -66,12 +67,10 @@ 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 @@ -79,7 +78,7 @@ async function readSnapshot(cwd, file) { * @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, diff --git a/core/test/dev-write.js b/fixtures/test/write.js similarity index 93% rename from core/test/dev-write.js rename to fixtures/test/write.js index 7d393296..fc65c0f0 100644 --- a/core/test/dev-write.js +++ b/fixtures/test/write.js @@ -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 () => { diff --git a/fixtures/tsconfig.json b/fixtures/tsconfig.json new file mode 100644 index 00000000..b4bbbc2d --- /dev/null +++ b/fixtures/tsconfig.json @@ -0,0 +1,5 @@ +{ + "extends": "../tsconfig.base.json", + "include": ["index.js", "beforeEachTemplate.js", "data/*.json"], + "references": [] +} diff --git a/fixtures/types/beforeEachTemplate.d.ts b/fixtures/types/beforeEachTemplate.d.ts new file mode 100644 index 00000000..f0a88f04 --- /dev/null +++ b/fixtures/types/beforeEachTemplate.d.ts @@ -0,0 +1,7 @@ +export = beforeEachTemplate; +/** + * Mocks the network for a test: the default and `https://bnb.im` index.json + * and schedule.json URLs serve the snapshots, and any other request + * fails. + */ +declare function beforeEachTemplate(): void; diff --git a/fixtures/types/index.d.ts b/fixtures/types/index.d.ts new file mode 100644 index 00000000..3ad523c2 --- /dev/null +++ b/fixtures/types/index.d.ts @@ -0,0 +1,28 @@ +declare const _exports: { + at: typeof at; +}; +export = _exports; +/** + * Returns options for @nodevu/core (and the packages built on it) that serve + * `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. + * + * `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 }} + */ +declare function at(now?: string, data?: { + index: { + date: string; + }[]; + schedule: unknown; +}): { + now: string; + fetch: typeof fetch; +}; diff --git a/core/util/dev/updateStaticData.js b/fixtures/util/updateStaticData.js similarity index 76% rename from core/util/dev/updateStaticData.js rename to fixtures/util/updateStaticData.js index d6aa1311..b6a54080 100644 --- a/core/util/dev/updateStaticData.js +++ b/fixtures/util/updateStaticData.js @@ -11,8 +11,8 @@ async function update() { const index = await rawIndex.json(); const schedule = await rawSchedule.json(); - await write('./test/data/static/index.json', index); - await write('./test/data/static/schedule.json', schedule); + await write('./data/index.json', index); + await write('./data/schedule.json', schedule); } update(); diff --git a/core/util/dev/updateStaticNow.js b/fixtures/util/updateStaticNow.js similarity index 75% rename from core/util/dev/updateStaticNow.js rename to fixtures/util/updateStaticNow.js index c8cb6b9b..a1704b3f 100644 --- a/core/util/dev/updateStaticNow.js +++ b/fixtures/util/updateStaticNow.js @@ -3,7 +3,7 @@ const write = require('./write'); async function updateStaticDateTime() { // write serializes the DateTime to its ISO string - await write('./test/data/static/now.json', DateTime.now()); + await write('./data/now.json', DateTime.now()); } updateStaticDateTime(); diff --git a/core/util/dev/write.js b/fixtures/util/write.js similarity index 100% rename from core/util/dev/write.js rename to fixtures/util/write.js diff --git a/knip.jsonc b/knip.jsonc index 8e639a84..44a863a6 100644 --- a/knip.jsonc +++ b/knip.jsonc @@ -24,14 +24,5 @@ // aliases doc-kit's React generator defines while it builds the site "ignoreUnresolved": ["^#theme/"], }, - "fetchindex": { - // the tests require core's optionsParser, which requires luxon, and CI - // installs only this workspace's dependencies - "ignoreDependencies": ["luxon"], - }, - "static": { - // the build test resolves core's own luxon to stop core's clock - "ignoreDependencies": ["luxon"], - }, }, } diff --git a/newest/package.json b/newest/package.json index 05524203..e274582a 100644 --- a/newest/package.json +++ b/newest/package.json @@ -32,6 +32,7 @@ "@nodevu/core": "^1.0.0" }, "devDependencies": { + "@nodevu/fixtures": "^0.0.0", "c8": "^12.0.0" }, "types": "types/index.d.ts", diff --git a/newest/test/test.js b/newest/test/test.js index c5a33e6f..0d15bc5e 100644 --- a/newest/test/test.js +++ b/newest/test/test.js @@ -1,9 +1,9 @@ const assert = require('node:assert'); const { newest, lts, security } = require('../index'); const { describe, it } = require('node:test'); -const fixtures = require('../../core/util/dev/fixtures'); +const fixtures = require('@nodevu/fixtures'); -// the day core's snapshot was taken (see now.json), so every release in it is +// the day the snapshot was taken (see now.json), so every release in it is // out, and releases a later snapshot refresh adds get filtered out, so the // expected values don't move const at = (now = '2025-10-30') => fixtures.at(now); diff --git a/oldest/package.json b/oldest/package.json index b8468655..2c8983f1 100644 --- a/oldest/package.json +++ b/oldest/package.json @@ -32,6 +32,7 @@ "@nodevu/core": "^1.0.0" }, "devDependencies": { + "@nodevu/fixtures": "^0.0.0", "c8": "^12.0.0" }, "types": "types/index.d.ts", diff --git a/oldest/test/test.js b/oldest/test/test.js index 323d8f07..8f032da2 100644 --- a/oldest/test/test.js +++ b/oldest/test/test.js @@ -1,9 +1,9 @@ const assert = require('node:assert'); const { oldest, lts, security } = require('../index'); const { describe, it } = require('node:test'); -const fixtures = require('../../core/util/dev/fixtures'); +const fixtures = require('@nodevu/fixtures'); -// the day core's snapshot was taken (see now.json), so every release in it is +// the day the snapshot was taken (see now.json), so every release in it is // out, and releases a later snapshot refresh adds get filtered out, so the // expected values don't move const at = (now = '2025-10-30') => fixtures.at(now); diff --git a/package-lock.json b/package-lock.json index 0d748d46..392344eb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,7 +18,8 @@ "aliases", "translate", "fetchindex", - "opt" + "opt", + "fixtures" ], "devDependencies": { "@arethetypeswrong/cli": "0.18.5", @@ -28,7 +29,6 @@ "@lavamoat/harden": "0.8.1", "@node-core/ui-components": "1.8.1", "@oxlint/plugins": "1.89.0", - "@types/luxon": "3.7.6", "@types/node": "26.6.5", "@types/semver": "7.8.0", "knip": "6.44.1", @@ -62,6 +62,7 @@ "semver": "^7.6.3" }, "devDependencies": { + "@nodevu/fixtures": "^0.0.0", "c8": "^12.0.0", "fast-check": "^4.10.2", "undici": "^8.12.2" @@ -76,13 +77,28 @@ "license": "MIT", "devDependencies": { "c8": "^12.0.0", - "luxon": "^3.5.0", "undici": "^8.12.2" }, "engines": { "node": ">=24" } }, + "fixtures": { + "name": "@nodevu/fixtures", + "version": "0.0.0", + "license": "MIT", + "dependencies": { + "undici": "^8.12.4" + }, + "devDependencies": { + "c8": "^12.0.0", + "fast-check": "^4.10.2", + "luxon": "^3.7.2" + }, + "engines": { + "node": ">=24" + } + }, "newest": { "name": "@nodevu/newest", "version": "1.1.0", @@ -91,6 +107,7 @@ "@nodevu/core": "^1.0.0" }, "devDependencies": { + "@nodevu/fixtures": "^0.0.0", "c8": "^12.0.0" }, "engines": { @@ -1249,6 +1266,10 @@ "resolved": "fetchindex", "link": true }, + "node_modules/@nodevu/fixtures": { + "resolved": "fixtures", + "link": true + }, "node_modules/@nodevu/newest": { "resolved": "newest", "link": true @@ -10011,7 +10032,6 @@ "version": "8.12.4", "resolved": "https://registry.npmjs.org/undici/-/undici-8.12.4.tgz", "integrity": "sha512-8Mx+uNKKER2bA4AdtedkqRbwUCIJFr2nJckXRe5yrDqzePqwLP7VNcctN8/XdmDSbF7mS+jJKIszlL+vbam1VA==", - "dev": true, "license": "MIT", "engines": { "node": ">=22.19.0" @@ -10566,6 +10586,7 @@ "@nodevu/core": "^1.0.0" }, "devDependencies": { + "@nodevu/fixtures": "^0.0.0", "c8": "^12.0.0" }, "engines": { @@ -10594,6 +10615,7 @@ "version": "1.1.0", "license": "MIT", "devDependencies": { + "@nodevu/fixtures": "^0.0.0", "c8": "^12.0.0", "fast-check": "^4.10.2" }, @@ -10610,6 +10632,7 @@ "@nodevu/core": "^1.0.0" }, "devDependencies": { + "@nodevu/fixtures": "^0.0.0", "c8": "^12.0.0", "fast-check": "^4.10.2" }, @@ -10623,6 +10646,7 @@ "license": "MIT", "devDependencies": { "@nodevu/core": "^1.1.0", + "@nodevu/fixtures": "^0.0.0", "c8": "^12.0.0" }, "engines": { @@ -10643,6 +10667,7 @@ "license": "MIT", "devDependencies": { "@nodevu/aliases": "^1.1.0", + "@nodevu/fixtures": "^0.0.0", "@nodevu/ranges": "^1.0.0", "c8": "^12.0.0", "fast-check": "^4.10.2" diff --git a/package.json b/package.json index e70febba..db5fae8e 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "version": "0.0.1", "description": "comprehensive node.js version tooling", "scripts": { - "lint": "oxlint --deny-warnings && oxfmt --check && docov && npm run lint:harden && npm run lint:pack && npm run lint:workspaces && npm run knip", + "lint": "oxlint --deny-warnings && oxfmt --check && docov && npm run lint:harden && npm run lint:rules && npm run lint:pack && npm run lint:workspaces && npm run knip", "lint:write": "oxlint --fix && oxfmt", "lint:harden": "node --test tools/harden/test/*.test.mjs && harden check -p npm -l strict --json | node tools/harden/check.mjs", "lint:pack": "npm exec --ws -- publint --strict && npm exec --ws -- attw --pack .", @@ -13,7 +13,8 @@ "docs": "doc-kit generate", "docs:dev": "doc-kit generate && sirv out --dev", "types": "tsc -b", - "knip": "knip --treat-config-hints-as-errors" + "knip": "knip --treat-config-hints-as-errors", + "lint:rules": "node --test tools/oxlint/nodevu/test/*.test.mjs" }, "repository": { "type": "git", @@ -39,7 +40,8 @@ "aliases", "translate", "fetchindex", - "opt" + "opt", + "fixtures" ], "devDependencies": { "@arethetypeswrong/cli": "0.18.5", @@ -49,7 +51,6 @@ "@lavamoat/harden": "0.8.1", "@node-core/ui-components": "1.8.1", "@oxlint/plugins": "1.89.0", - "@types/luxon": "3.7.6", "@types/node": "26.6.5", "@types/semver": "7.8.0", "knip": "6.44.1", diff --git a/parsefiles/package.json b/parsefiles/package.json index 0c80c836..b85eba56 100644 --- a/parsefiles/package.json +++ b/parsefiles/package.json @@ -23,6 +23,7 @@ "author": "Tierney Cyren ", "license": "MIT", "devDependencies": { + "@nodevu/fixtures": "^0.0.0", "c8": "^12.0.0", "fast-check": "^4.10.2" }, diff --git a/parsefiles/test/parseFilename.test.js b/parsefiles/test/parseFilename.test.js index cb2838c3..fdd25670 100644 --- a/parsefiles/test/parseFilename.test.js +++ b/parsefiles/test/parseFilename.test.js @@ -3,7 +3,7 @@ const { describe, it } = require('node:test'); const fc = require('fast-check'); const parseFilename = require('../index'); -const releases = require('../../core/test/data/static/index.json'); +const releases = require('@nodevu/fixtures/data/index.json'); describe('parseFilename', () => { it('should only list the win-x86 directory for win-x86-exe', () => { diff --git a/ranges/package.json b/ranges/package.json index e69222b6..d92de5ed 100644 --- a/ranges/package.json +++ b/ranges/package.json @@ -29,6 +29,7 @@ "@nodevu/core": "^1.0.0" }, "devDependencies": { + "@nodevu/fixtures": "^0.0.0", "c8": "^12.0.0", "fast-check": "^4.10.2" }, diff --git a/ranges/test/alpha.test.js b/ranges/test/alpha.test.js index 804f358f..d4403d2d 100644 --- a/ranges/test/alpha.test.js +++ b/ranges/test/alpha.test.js @@ -48,7 +48,7 @@ const schedule = { }, }; -// serves the made-up data above instead of core's snapshots +// serves the made-up data above instead of the snapshots /** @param {string} now */ const at = (now) => fixtures.at(now, { index, schedule }); diff --git a/ranges/test/bounds.test.js b/ranges/test/bounds.test.js index b6713771..a816963e 100644 --- a/ranges/test/bounds.test.js +++ b/ranges/test/bounds.test.js @@ -5,7 +5,7 @@ const { describe, it } = require('node:test'); // the LTS and security bounds of lts/maintenance and lts/active come from the // newest and oldest lines in them, even when those lines have none. the -// expected values come from core's index.json and schedule.json snapshots. +// expected values come from the index.json and schedule.json snapshots. describe('generateRanges', () => { it('should take the newest bounds of "lts/active" from "lts/maintenance" when "lts/latest" is empty', async () => { // Arrange diff --git a/ranges/test/emptyFields.test.js b/ranges/test/emptyFields.test.js index 5bb3b8a4..fc5e76cf 100644 --- a/ranges/test/emptyFields.test.js +++ b/ranges/test/emptyFields.test.js @@ -3,8 +3,8 @@ const ranges = require('../index'); const { at } = require('./fixtures'); const { describe, it } = require('node:test'); -// core's snapshot time, when the Current line hasn't had a security release yet -const snapshotNow = require('../../core/test/data/static/now.json'); +// the snapshot time, when the Current line hasn't had a security release yet +const snapshotNow = require('@nodevu/fixtures/data/now.json'); describe('fields without a value should be undefined, not "undefined"', async () => { it('should leave out the security fields of a Current line without security releases', async () => { diff --git a/ranges/test/fixtures.js b/ranges/test/fixtures.js index cc655390..5ba2be48 100644 --- a/ranges/test/fixtures.js +++ b/ranges/test/fixtures.js @@ -1,46 +1,6 @@ -const index = require('../../core/test/data/static/index.json'); -const schedule = require('../../core/test/data/static/schedule.json'); - -// the default date for tests. it's 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. -const now = '2025-09-01'; +const { at } = require('@nodevu/fixtures'); // every version in index.json, like v22.19.0 const versionPattern = /^v\d+\.\d+\.\d+$/; -// returns options for `ranges` that serve `data` (core's index.json and -// schedule.json snapshots by default) instead of fetching them, only including -// releases that would be out by `now`, like the real index.json. any other URL -// throws, so tests never touch the network. -/** - * @param {string} [date] - * @param {{ - * index: { date: string }[], - * schedule: import('@nodevu/core/util/prod/schedule').Schedule, - * }} [data] - */ -function at(date = now, data = { index, schedule }) { - return { - now: date, - // core only calls this with a URL string - fetch: /** @type {typeof fetch} */ ( - async (/** @type {string} */ url) => { - if (url.endsWith('/schedule.json')) { - return Response.json(data.schedule); - } - - if (url.endsWith('/index.json')) { - return Response.json( - data.index.filter((entry) => entry.date <= date), - ); - } - - throw new Error(`No fixture data for ${url}`); - } - ), - }; -} - module.exports = { at, versionPattern }; diff --git a/ranges/test/properties.test.js b/ranges/test/properties.test.js index 9db5f7ab..30708fa0 100644 --- a/ranges/test/properties.test.js +++ b/ranges/test/properties.test.js @@ -3,8 +3,8 @@ const aliases = require('@nodevu/aliases'); const fc = require('fast-check'); const ranges = require('../index'); const { at } = require('./fixtures'); -const index = require('../../core/test/data/static/index.json'); -const schedule = require('../../core/test/data/static/schedule.json'); +const index = require('@nodevu/fixtures/data/index.json'); +const schedule = require('@nodevu/fixtures/data/schedule.json'); const { describe, it } = require('node:test'); // an LTS bound from @nodevu/core, a version without a `v`, like 20.9.0 diff --git a/static/package.json b/static/package.json index 29feb589..0867d417 100644 --- a/static/package.json +++ b/static/package.json @@ -37,6 +37,7 @@ "homepage": "https://github.com/cutenode/nodevu#readme", "devDependencies": { "@nodevu/core": "^1.1.0", + "@nodevu/fixtures": "^0.0.0", "c8": "^12.0.0" }, "types": "types/index.d.ts", diff --git a/static/test/test.build.js b/static/test/test.build.js index 90142d1c..34a3155d 100644 --- a/static/test/test.build.js +++ b/static/test/test.build.js @@ -2,12 +2,12 @@ const assert = require('node:assert'); const { execFile } = require('node:child_process'); const { mkdir, mkdtemp, readFile } = require('node:fs/promises'); const { tmpdir } = require('node:os'); -const { dirname, join } = require('node:path'); +const { join } = require('node:path'); const { describe, it } = require('node:test'); const { promisify } = require('node:util'); const nodevu = require('@nodevu/core'); -const fixtures = require('../../core/util/dev/fixtures'); -const now = require('../../core/test/data/static/now.json'); +const fixtures = require('@nodevu/fixtures'); +const now = require('@nodevu/fixtures/data/now.json'); // a full diff of every release takes node minutes to build, so failures say // this instead @@ -89,20 +89,17 @@ function toJSON(data) { } // runs a build script the way `npm run build -w static` does, from a copy of -// static/, with core's snapshot data served instead of nodejs.org and the +// static/, with the snapshot data served instead of nodejs.org and the // clock stopped at the time the snapshot was taken. returns what it wrote. /** @param {'default' | 'releases' | 'security' | 'support'} name */ async function runBuild(name) { const cwd = join(await mkdtemp(join(tmpdir(), 'nodevu-')), 'static'); await mkdir(join(cwd, 'data'), { recursive: true }); - const luxon = require.resolve('luxon', { - paths: [dirname(require.resolve('@nodevu/core'))], - }); - const setup = [ - `require(${JSON.stringify(luxon)}).Settings.now = () => ${Date.parse(now)};`, - `globalThis.fetch = require(${JSON.stringify(require.resolve('../../core/util/dev/fixtures'))}).at(${JSON.stringify(now)}).fetch;`, + // core reads the time through luxon, which calls Date.now() + `Date.now = () => ${Date.parse(now)};`, + `globalThis.fetch = require(${JSON.stringify(require.resolve('@nodevu/fixtures'))}).at(${JSON.stringify(now)}).fetch;`, ].join(' '); await promisify(execFile)( diff --git a/test/workspaces.test.mjs b/test/workspaces.test.mjs index f8cdecfe..447035fa 100644 --- a/test/workspaces.test.mjs +++ b/test/workspaces.test.mjs @@ -21,6 +21,7 @@ function read(path) { * @typedef {object} Manifest * @property {string[]} [workspaces] * @property {{ url: string }} [repository] + * @property {boolean} [private] * @property {Record} [scripts] * @property {Record} [dependencies] * @property {Record} [devDependencies] @@ -121,16 +122,19 @@ describe('the workspace list', () => { assert.equal(new Set(releaseLists().map(String)).size, 1); }); - it('has a docs page and a sidebar link for each module', () => { + // private modules, like the tests' fixtures, aren't for users + it('has a docs page and a sidebar link for each public module', () => { + const documented = workspaces.filter((ws) => !manifest(ws).private); + const links = docKit.html.navigation.sidebar .flatMap(({ items }) => items) .map(({ link }) => link.slice(1)) .filter((link) => link !== 'index') .sort(); - assert.deepStrictEqual(links, workspaces); + assert.deepStrictEqual(links, documented); - for (const ws of workspaces) { + for (const ws of documented) { assert.ok( existsSync(new URL(`../docs/pages/${ws}.md`, import.meta.url)), ws, diff --git a/tools/oxlint/nodevu/index.mjs b/tools/oxlint/nodevu/index.mjs new file mode 100644 index 00000000..b0544167 --- /dev/null +++ b/tools/oxlint/nodevu/index.mjs @@ -0,0 +1,118 @@ +import { existsSync } from 'node:fs'; +import { dirname, join, resolve } from 'node:path'; + +import { definePlugin, defineRule } from '@oxlint/plugins'; + +/** @type {Map} */ +const packageDirs = new Map(); + +/** + * The folder of the package `path` is in: the nearest one holding a + * package.json, which is a workspace or the repo root. + * @param {string} path + * @returns {string} + */ +function packageDir(path) { + const cached = packageDirs.get(path); + + if (cached !== undefined) { + return cached; + } + + const parent = dirname(path); + const isPackage = existsSync(join(path, 'package.json')) || parent === path; + const dir = isPackage ? path : packageDir(parent); + packageDirs.set(path, dir); + + return dir; +} + +/** + * The module path an argument holds, when it's written out in full. Literals + * that aren't strings come back as strings that are never relative paths. + * @param {import('@oxlint/plugins').ESTree.Node | null | undefined} node + * @returns {string | null} + */ +function staticPath(node) { + if (node?.type === 'Literal') { + return String(node.value); + } + + if (node?.type === 'TemplateLiteral' && node.expressions.length === 0) { + return node.quasis[0].value.cooked; + } + + return null; +} + +/** + * Whether `callee` is `require` or `require.resolve`. + * @param {import('@oxlint/plugins').ESTree.Node} callee + * @returns {boolean} + */ +function isRequire(callee) { + if (callee.type === 'Identifier') { + return callee.name === 'require'; + } + + return ( + callee.type === 'MemberExpression' && + !callee.computed && + callee.object.type === 'Identifier' && + callee.object.name === 'require' && + callee.property.type === 'Identifier' && + callee.property.name === 'resolve' + ); +} + +const noCrossPackagePaths = defineRule({ + meta: { + type: 'problem', + docs: { + description: + "Disallow relative paths into another package's folder, so each package only depends on what it lists.", + }, + messages: { + crossPackage: + "'{{path}}' is in another package's folder. Depend on that package and use its name.", + }, + }, + create(context) { + const fromPackage = packageDir(dirname(context.filename)); + + /** @param {import('@oxlint/plugins').ESTree.Node | null | undefined} node */ + function check(node) { + const path = staticPath(node); + + const isRelative = path !== null && /^\.\.?(\/|$)/.test(path); + + if (!isRelative) { + return; + } + + const target = resolve(dirname(context.filename), path); + + if (packageDir(target) !== fromPackage) { + context.report({ node, messageId: 'crossPackage', data: { path } }); + } + } + + return { + ImportDeclaration: (node) => check(node.source), + ExportAllDeclaration: (node) => check(node.source), + ExportNamedDeclaration: (node) => check(node.source), + ImportExpression: (node) => check(node.source), + CallExpression(node) { + if (isRequire(node.callee)) { + check(node.arguments[0]); + } + }, + }; + }, +}); + +/** nodevu's own oxlint rules. */ +export default definePlugin({ + meta: { name: 'nodevu' }, + rules: { 'no-cross-package-paths': noCrossPackagePaths }, +}); diff --git a/tools/oxlint/nodevu/test/no-cross-package-paths.test.mjs b/tools/oxlint/nodevu/test/no-cross-package-paths.test.mjs new file mode 100644 index 00000000..80e8c3f0 --- /dev/null +++ b/tools/oxlint/nodevu/test/no-cross-package-paths.test.mjs @@ -0,0 +1,103 @@ +import { describe, it } from 'node:test'; +import { fileURLToPath } from 'node:url'; + +import { RuleTester } from 'oxlint/plugins-dev'; + +import plugin from '../index.mjs'; + +RuleTester.describe = describe; + +RuleTester.it = it; + +// the rule looks for package.json files on disk, so the test files sit in the +// repo's real folders +const root = fileURLToPath(new URL('../../../../', import.meta.url)); + +const rangesTest = `${root}ranges/test/example.test.js`; + +const translateTest = `${root}translate/test/example.test.mjs`; + +const rootTool = `${root}tools/example.mjs`; + +const tester = new RuleTester({ languageOptions: { sourceType: 'commonjs' } }); + +tester.run('no-cross-package-paths', plugin.rules['no-cross-package-paths'], { + valid: [ + { code: "require('../index');", filename: rangesTest }, + { code: "require('./fixtures');", filename: rangesTest }, + { code: "require.resolve('../index');", filename: rangesTest }, + { code: "require('@nodevu/fixtures');", filename: rangesTest }, + { code: "require('node:path');", filename: rangesTest }, + // only paths written out in full can be checked + { code: 'require(`../../core/${name}`);', filename: rangesTest }, + { code: 'require(path);', filename: rangesTest }, + { code: 'require(5);', filename: rangesTest }, + { code: "resolve('../../core');", filename: rangesTest }, + { code: "require.cache('../../core');", filename: rangesTest }, + { + code: "import ranges from '../index.js';", + filename: translateTest, + languageOptions: { sourceType: 'module' }, + }, + { + code: "import { check } from './harden/check.mjs';", + filename: rootTool, + languageOptions: { sourceType: 'module' }, + }, + // outside any package, the search stops at the filesystem root + { code: "require('./example');", filename: '/example.js' }, + ], + invalid: [ + { + code: "require('../../core/util/prod/optionsParser');", + filename: `${root}fetchindex/test/test.js`, + errors: [{ messageId: 'crossPackage' }], + }, + { + code: "require('../../core');", + filename: rangesTest, + errors: [{ messageId: 'crossPackage' }], + }, + { + code: "require.resolve('../../ranges/test/fixtures');", + filename: `${root}translate/test/examples.test.js`, + errors: [{ messageId: 'crossPackage' }], + }, + { + code: 'require(`../../core/index.js`);', + filename: rangesTest, + errors: [{ messageId: 'crossPackage' }], + }, + { + code: "import ranges from '../../ranges/index.js';", + filename: translateTest, + languageOptions: { sourceType: 'module' }, + errors: [{ messageId: 'crossPackage' }], + }, + { + code: "export * from '../../ranges/index.js';", + filename: translateTest, + languageOptions: { sourceType: 'module' }, + errors: [{ messageId: 'crossPackage' }], + }, + { + code: "export { at } from '../../ranges/test/fixtures.js';", + filename: translateTest, + languageOptions: { sourceType: 'module' }, + errors: [{ messageId: 'crossPackage' }], + }, + { + code: "await import('../../ranges/index.js');", + filename: translateTest, + languageOptions: { sourceType: 'module' }, + errors: [{ messageId: 'crossPackage' }], + }, + // the repo root is a package of its own + { + code: "import core from '../core/index.js';", + filename: rootTool, + languageOptions: { sourceType: 'module' }, + errors: [{ messageId: 'crossPackage' }], + }, + ], +}); diff --git a/translate/examples/default.js b/translate/examples/default.js index 87bec249..e7e2f4a2 100644 --- a/translate/examples/default.js +++ b/translate/examples/default.js @@ -1,4 +1,4 @@ -const ranges = require('../../ranges'); // would normally be "const ranges = require('@nodevu/ranges')" in your code +const ranges = require('@nodevu/ranges'); const translate = require('../index'); // would normally be "const translate = require('@nodevu/translate')" in your code async function getTranslatedData() { diff --git a/translate/package.json b/translate/package.json index 4a5b3884..f27023e3 100644 --- a/translate/package.json +++ b/translate/package.json @@ -28,6 +28,7 @@ }, "devDependencies": { "@nodevu/aliases": "^1.1.0", + "@nodevu/fixtures": "^0.0.0", "@nodevu/ranges": "^1.0.0", "c8": "^12.0.0", "fast-check": "^4.10.2" diff --git a/translate/test/examples.test.js b/translate/test/examples.test.js index 0938e273..1df1cd72 100644 --- a/translate/test/examples.test.js +++ b/translate/test/examples.test.js @@ -6,8 +6,8 @@ const { promisify } = require('node:util'); describe('examples/default.js', async () => { it('should print the ranges for each Package Support term', async () => { // Arrange - // serve core's snapshot data instead of fetching from nodejs.org - const setup = `globalThis.fetch = require(${JSON.stringify(require.resolve('../../ranges/test/fixtures'))}).at().fetch;`; + // serve the snapshot data instead of fetching from nodejs.org + const setup = `globalThis.fetch = require(${JSON.stringify(require.resolve('@nodevu/fixtures'))}).at().fetch;`; const example = JSON.stringify(require.resolve('../examples/default')); // Act diff --git a/translate/test/ranges.test.js b/translate/test/ranges.test.js index 1e902f26..7e30a5b0 100644 --- a/translate/test/ranges.test.js +++ b/translate/test/ranges.test.js @@ -2,9 +2,9 @@ const assert = require('node:assert'); const translate = require('../index'); const ranges = require('@nodevu/ranges'); const { describe, it } = require('node:test'); -const { at } = require('../../ranges/test/fixtures'); +const { at } = require('@nodevu/fixtures'); -// serve core's snapshot data at a fixed date so results don't depend on +// serve the snapshot data at a fixed date so results don't depend on // nodejs.org or today's date const options = at(); diff --git a/tsconfig.json b/tsconfig.json index c25853d5..46e8b7c9 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -16,7 +16,7 @@ "test/**/*", "*/test/**/*.js", "*/test/**/*.mjs", - "core/util/dev/*.js", + "fixtures/util/*.js", "static/build/*.js", "static/util/dev/*.js", "translate/examples/*.js", @@ -27,6 +27,7 @@ { "path": "aliases" }, { "path": "core" }, { "path": "fetchindex" }, + { "path": "fixtures" }, { "path": "newest" }, { "path": "oldest" }, { "path": "opt" },