From 2039a5c6a7605b1f2c271e6cfb1a41793193e1f1 Mon Sep 17 00:00:00 2001 From: Kay Ward Date: Tue, 2 Dec 2025 09:13:21 +1300 Subject: [PATCH 1/2] feat(config): parse relative date in --before --- tap-snapshots/test/lib/docs.js.test.cjs | 4 +++- workspaces/config/lib/definitions/definitions.js | 5 ++++- workspaces/config/lib/type-defs.js | 15 +++++++++++++++ .../test/type-description.js.test.cjs | 1 + workspaces/config/test/type-defs.js | 5 +++++ 5 files changed, 28 insertions(+), 2 deletions(-) diff --git a/tap-snapshots/test/lib/docs.js.test.cjs b/tap-snapshots/test/lib/docs.js.test.cjs index f173a1cf67720..8943ab54023f1 100644 --- a/tap-snapshots/test/lib/docs.js.test.cjs +++ b/tap-snapshots/test/lib/docs.js.test.cjs @@ -260,13 +260,15 @@ config is given, this value will always be set to \`legacy\`. #### \`before\` * Default: null -* Type: null or Date +* Type: null, Date, or class RelativeDate {} If passed to \`npm install\`, will rebuild the npm tree such that only versions that were available **on or before** the given date are installed. If there are no versions available for the current set of dependencies, the command will error. +Accepts either a Date string or a relative date, e.g. "24h", "7d". + If the requested version is a \`dist-tag\` and the given tag does not pass the \`--before\` filter, the most recent version less than or equal to that tag will be used. For example, \`foo@latest\` might install \`foo@1.2\` even though diff --git a/workspaces/config/lib/definitions/definitions.js b/workspaces/config/lib/definitions/definitions.js index 570abecdb4484..b69f3f5cb2453 100644 --- a/workspaces/config/lib/definitions/definitions.js +++ b/workspaces/config/lib/definitions/definitions.js @@ -92,6 +92,7 @@ const { Umask: { type: Umask }, url: { type: url }, path: { type: path }, + relativeDate: { type: RelativeDate }, } = require('../type-defs.js') // basic flattening function, just copy it over camelCase @@ -231,13 +232,15 @@ const definitions = { before: new Definition('before', { default: null, hint: '', - type: [null, Date], + type: [null, Date, RelativeDate], description: ` If passed to \`npm install\`, will rebuild the npm tree such that only versions that were available **on or before** the given date are installed. If there are no versions available for the current set of dependencies, the command will error. + Accepts either a Date string or a relative date, e.g. "24h", "7d". + If the requested version is a \`dist-tag\` and the given tag does not pass the \`--before\` filter, the most recent version less than or equal to that tag will be used. For example, \`foo@latest\` might install diff --git a/workspaces/config/lib/type-defs.js b/workspaces/config/lib/type-defs.js index 3c9dfe19ded11..03f2e5a772019 100644 --- a/workspaces/config/lib/type-defs.js +++ b/workspaces/config/lib/type-defs.js @@ -1,9 +1,11 @@ const nopt = require('nopt') +const ms = require('ms') const { validate: validateUmask } = require('./umask.js') class Umask {} class Semver {} +class RelativeDate {} const semverValid = require('semver/functions/valid') const validateSemver = (data, k, val) => { const valid = semverValid(val) @@ -21,6 +23,14 @@ const validatePath = (data, k, val) => { return noptValidatePath(data, k, val) } +const validateRelativeDate = (data, k, val) => { + const valid = ms(val) + if (valid === undefined) { + return false + } + data[k] = new Date(Date.now() - valid) +} + // add descriptions so we can validate more usefully module.exports = { ...nopt.typeDefs, @@ -34,6 +44,11 @@ module.exports = { validate: validateUmask, description: 'octal number in range 0o000..0o777 (0..511)', }, + relativeDate: { + type: RelativeDate, + validate: validateRelativeDate, + description: 'valid relative date string e.g. "24h", "7d"', + }, url: { ...nopt.typeDefs.url, description: 'full url with "http://"', diff --git a/workspaces/config/tap-snapshots/test/type-description.js.test.cjs b/workspaces/config/tap-snapshots/test/type-description.js.test.cjs index 7325654569b3d..22257df8770cc 100644 --- a/workspaces/config/tap-snapshots/test/type-description.js.test.cjs +++ b/workspaces/config/tap-snapshots/test/type-description.js.test.cjs @@ -46,6 +46,7 @@ Object { "before": Array [ null, "valid Date string", + "valid relative date string e.g. \\"24h\\", \\"7d\\"", ], "bin-links": Array [ "boolean value (true or false)", diff --git a/workspaces/config/test/type-defs.js b/workspaces/config/test/type-defs.js index 89ca3e53cd03e..9c9d4ca5ba971 100644 --- a/workspaces/config/test/type-defs.js +++ b/workspaces/config/test/type-defs.js @@ -7,6 +7,9 @@ const { path: { validate: validatePath, }, + relativeDate: { + validate: validateRelativeDate, + }, } = typeDefs const { resolve } = require('node:path') @@ -20,3 +23,5 @@ t.equal(validatePath(d, 'somePath', null), false) t.equal(validatePath(d, 'somePath', 1234), false) t.equal(validatePath(d, 'somePath', 'false'), true) t.equal(d.somePath, resolve('false')) +t.equal(validateRelativeDate(d, 'someDate', 'foobar'), false) +t.equal(validateRelativeDate(d, 'someDate', '1d'), undefined) From 9b1a390478778d1e3a81e3d91eec8da326ec01e8 Mon Sep 17 00:00:00 2001 From: Kay Ward Date: Tue, 2 Dec 2025 09:15:04 +1300 Subject: [PATCH 2/2] deps(config): add ms@2.1.3 to config workspace --- DEPENDENCIES.md | 1 + package-lock.json | 3 +++ workspaces/config/package.json | 1 + 3 files changed, 5 insertions(+) diff --git a/DEPENDENCIES.md b/DEPENDENCIES.md index a758b768147ce..72326789eb7f3 100644 --- a/DEPENDENCIES.md +++ b/DEPENDENCIES.md @@ -579,6 +579,7 @@ graph LR; npmcli-arborist-->walk-up-path; npmcli-config-->ci-info; npmcli-config-->ini; + npmcli-config-->ms; npmcli-config-->nopt; npmcli-config-->npmcli-eslint-config["@npmcli/eslint-config"]; npmcli-config-->npmcli-map-workspaces["@npmcli/map-workspaces"]; diff --git a/package-lock.json b/package-lock.json index 945f903381ca1..c31eeef53995c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8365,6 +8365,8 @@ }, "node_modules/ms": { "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "inBundle": true, "license": "MIT" }, @@ -14736,6 +14738,7 @@ "@npmcli/package-json": "^7.0.0", "ci-info": "^4.0.0", "ini": "^6.0.0", + "ms": "^2.1.3", "nopt": "^9.0.0", "proc-log": "^6.0.0", "semver": "^7.3.5", diff --git a/workspaces/config/package.json b/workspaces/config/package.json index 1b4827125f983..be19c70bba0f8 100644 --- a/workspaces/config/package.json +++ b/workspaces/config/package.json @@ -41,6 +41,7 @@ "@npmcli/package-json": "^7.0.0", "ci-info": "^4.0.0", "ini": "^6.0.0", + "ms": "^2.1.3", "nopt": "^9.0.0", "proc-log": "^6.0.0", "semver": "^7.3.5",