Files
phishing/scripts/checkDenoMap.mjs
T
SatoshiQaziMuhammedandClaude Opus 5 d2c8bdefb0 Let the Deno build reach its dependencies, and let its check fail (#8)
The build rewrites every @pezkuwi import into a deno.land URL, so @pezkuwi/util
becomes https://deno.land/x/pezkuwi/util/mod.ts. Upstream can do that because
deno.land/x/polkadot is published. Our fork renamed the prefix to x/pezkuwi and
never published there: deno.land/x/pezkuwi answers 404, and `deno check` has
failed on three modules ever since. Reproduced locally with the same Deno the
workflow installs, 2.9.7:

    TS2307 Cannot find module 'https://deno.land/x/pezkuwi/util/mod.ts'
    TS2307 Cannot find module 'https://deno.land/x/pezkuwi/util-crypto/mod.ts'
    TS2307 Cannot find module 'https://deno.land/x/pezkuwi/x-fetch/mod.ts'

import_map.in.json is the tooling's own hook for exactly this —
pezkuwi-dev-deno-map merges its entries into the generated import_map.json. All
three are mapped to npm: specifiers, which Deno resolves natively, at the same
version ranges the node build uses. The three packages are already on npm, so
nothing new is published and the Deno build now checks against the very code we
ship. `yarn deno` exits 0.

This deliberately does not decide whether we ever claim deno.land/x/pezkuwi. If
we do, the mappings come out again; until then the build works with what we have.

The reason none of this was noticed is the second half of the change. The PR job
carried continue-on-error from upstream, so its four steps could never turn a
pull request red: build had been failing since 2026-09-13 and deno for far
longer, and the job reported success throughout. All four pass now — verified
locally, lint included, on a clean tree — so the flag is gone and they are gates
again.

deno.lock joins mod.ts and import_map.json in .gitignore: same check, same kind
of generated artifact. package.json carries the key order its own build:before
step produces.

Gate: scripts/checkDenoMap.mjs, in the Lists workflow so it runs even when the
toolchain cannot be installed. Nails: removing the map, bumping a dependency
without the map, adding a @pezkuwi dependency with no mapping, and leaving a
mapping behind after its dependency goes — each turns it red.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-17 17:16:47 +04:00

75 lines
3.2 KiB
JavaScript

// Copyright 2020-2026 @pezkuwi/phishing authors & contributors
// SPDX-License-Identifier: Apache-2.0
// Whether the Deno build can still reach its dependencies.
//
// The build rewrites every @pezkuwi/* import into a deno.land URL:
// @pezkuwi/util becomes https://deno.land/x/pezkuwi/util/mod.ts. Upstream can do
// that because deno.land/x/polkadot is published; our fork renamed the prefix to
// x/pezkuwi and never published there, so deno.land/x/pezkuwi answers 404 and
// `deno check` failed on three modules — for months, unnoticed, because the PR
// job carries continue-on-error and can never turn a pull request red.
//
// import_map.in.json is the tooling's own hook for this: pezkuwi-dev-deno-map
// merges its entries into the generated import_map.json. The three externals are
// mapped to npm: specifiers, which Deno resolves natively, so the Deno build uses
// the very packages we already publish. Nothing new has to be published, and
// claiming the deno.land/x/pezkuwi namespace stays a decision we can take later
// rather than one this build forces.
//
// What can rot: a dependency is bumped in packages/phishing/package.json and the
// map keeps pointing at the old range, so `deno check` type-checks a different
// version of @pezkuwi/util than the node build compiles against. That divergence
// would not fail anything — it would just quietly check the wrong code.
//
// node scripts/checkDenoMap.mjs
import { existsSync, readFileSync } from 'node:fs';
const errors = [];
function check (name, ok, detail) {
console.log((ok ? 'ok ' : 'FAIL ') + name + (ok ? '' : ` — got: ${JSON.stringify(detail)}`));
if (!ok) {
errors.push(name);
}
}
check('the deno import map exists', existsSync('import_map.in.json'));
if (errors.length) {
console.log(`FAILED: ${errors.length}`);
process.exit(1);
}
const map = JSON.parse(readFileSync('import_map.in.json', 'utf8')).imports || {};
const pkg = JSON.parse(readFileSync('packages/phishing/package.json', 'utf8'));
const deps = pkg.dependencies || {};
// Every @pezkuwi dependency the package pulls in has to be reachable from Deno,
// because the build turns all of them into deno.land URLs.
const external = Object.keys(deps).filter((d) => d.startsWith('@pezkuwi/'));
check('the package has @pezkuwi dependencies to map', external.length > 0, external);
for (const dep of external) {
const short = dep.replace('@pezkuwi/', '');
const key = `https://deno.land/x/pezkuwi/${short}/mod.ts`;
const want = `npm:${dep}@${deps[dep]}`;
check(`${dep}: the deno build has somewhere to resolve it`, !!map[key], { have: Object.keys(map), key });
check(`${dep}: the deno build and the node build agree on the version`, map[key] === want, { map: map[key], want });
}
// And nothing in the map points at a package we no longer depend on, which would
// hide a removed dependency behind a mapping that still resolves.
for (const [key, value] of Object.entries(map)) {
const named = String(value).replace(/^npm:/, '').replace(/@[^@/]*$/, '');
check(`${key}: maps to a dependency we still declare`, external.includes(named), { named, value });
}
console.log(errors.length ? `FAILED: ${errors.length}` : 'all deno map checks passed');
process.exit(errors.length ? 1 : 0);