fix(x): honor --minimum-dependency-age and deno.json minimumDependencyAge (#36025)
## Summary
`deno x` ignored `--minimum-dependency-age` and an auto-discovered
`deno.json` `minimumDependencyAge`, keeping the pre-existing release-age
policy (`.npmrc` `min-release-age` or the 24h default). This affected
both `jsr:` targets and unprefixed npm targets (e.g. `deno x
create-vite`), even though `deno run --minimum-dependency-age=0`
resolves the same package correctly.
Fixes #35991.
## Root cause
`deno x` installs the target into an isolated temp dir and then re-runs
it as a `deno run <target>` subprocess in the user's cwd. The requested
age policy was applied at neither step:
1. **The re-run subprocess dropped explicit CLI selections.** The
subprocess argv (built in `cli/tools/x.rs`, also via `run_bin_value` for
npm `BinValue::JsFile` bins) was assembled from
`flags.to_permission_args()` + `unstable_args()` only, so an explicit
`--minimum-dependency-age`, `--config`, or `--permission-set` never
reached the child, which then re-resolved with the default policy / lost
config-backed run semantics. In the npm auto-install path it was worse:
the child was handed the internal temp `deno.json` path instead of the
user's config.
2. **The user's `deno.json` was discarded during install.**
`autoinstall_package` overrides `config_flag` to a generated temp
`deno.json`, dropping the user's cwd `deno.json`, so its
`minimumDependencyAge` (including any `exclude`) never reached the
install.
## Fix (`cli/tools/x.rs`)
- **Re-run subprocess:** forward the user's *explicit* selections —
`--minimum-dependency-age` (`Disabled` → `0`, else RFC3339 cutoff),
`--config`/`--no-config`, and `--permission-set` — resolved from the
**original** flags and cwd (not the internal temp config used for the
isolated install/bin lookup). For the npm installed-bin re-run, whose
config discovery anchors on the install dir rather than cwd, an original
`Discover` is resolved to the user's actually-discovered config path so
cwd config (incl. permission sets) still applies — matching `deno run
npm:<pkg>`. The jsr/url re-run keeps cwd auto-discovery (forwards
nothing on `Discover`). Forwarding the real config path (rather than a
collapsed age scalar) keeps structured `exclude` intact.
- **Install:** compute the user's effective full
`MinimumDependencyAgeConfig` (age + `exclude`) once from the original
factory (precedence: CLI flag > cwd `deno.json` > `.npmrc` > default,
which itself honors `--config`) and serialize it into the generated temp
`deno.json` under `minimumDependencyAge`, for both the jsr and npm temp
configs.
`=0` disables the restriction, a larger value tightens it,
`minimumDependencyAge.exclude` is honored, and an explicit out-of-cwd
`--config` (incl. its permission sets) is applied — matching `deno
run`/`deno install`.
## Tests
`tests/specs/x/` specs with mocked `@denotest/x-min-dep-age` fixtures
(jsr + npm; `v1.0.0` dated 2025-01-01, `v2.0.0` dated 2025-06-01; the
bin/module prints its own version so the selected version is
observable). Covers, with fresh `DENO_DIR`s: the CLI flag (cutoff →
`1.0.0`, `=0` → `2.0.0`), an auto-discovered `deno.json` cutoff, an
explicit out-of-cwd `--config` policy applied through the re-run
subprocess (incl. the URL path), `minimumDependencyAge.exclude` →
`2.0.0`, and npm auto-install `--config`/permission-set regressions
(explicit + auto-discovery) proving the child reads the user's original
config — each matching `deno run`/`deno install`. Confirmed the specs
fail on `main`/pre-fix commits and pass with the fix.
## Validation
- `cargo build --bin deno` — clean
- `cargo test -p specs_tests --test specs -- specs::x::` — all 41 x
specs pass
- `tools/format.js` and `tools/lint.js` (full Rust + JS) — clean
---------
Co-authored-by: Nathan Whitaker <nathan@deno.com>