Range check: build each package's own tagged version too; cultnet-ts 0.4.0 (#144)
This commit is contained in:
parent
4ebef4f023
commit
60e50b1cb7
7 changed files with 77 additions and 11 deletions
|
|
@ -22,6 +22,7 @@ import { fileURLToPath } from "node:url";
|
|||
const repoRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..");
|
||||
const rootRequire = createRequire(join(repoRoot, "package.json"));
|
||||
const ts = rootRequire("typescript");
|
||||
const npmCli = join(npmRoot(), "bin", "npm-cli.js");
|
||||
const semver = createRequire(join(npmRoot(), "package.json"))("semver");
|
||||
|
||||
// npm ships semver, so the check needs no dependency of its own. npm's root is beside the node binary on Windows and under lib/ on Unix.
|
||||
|
|
@ -84,16 +85,17 @@ function sourceFiles(directory) {
|
|||
});
|
||||
}
|
||||
|
||||
// Builds the package as the tag released it, in a scratch directory that borrows the workspace's node_modules.
|
||||
// Builds the package as the tag released it (with the repo-level contracts its generator reads), in a scratch directory that borrows the workspace's node_modules.
|
||||
function buildRelease(directory, tag, scratch) {
|
||||
const archive = join(scratch, `${tag}.tar`);
|
||||
git("archive", "--format=tar", "-o", archive, tag, `packages/${directory}`);
|
||||
git("archive", "--format=tar", "-o", archive, tag, `packages/${directory}`, "contracts");
|
||||
const extracted = join(scratch, tag);
|
||||
mkdirSync(extracted);
|
||||
execFileSync("tar", ["-xf", archive, "-C", extracted]);
|
||||
symlinkSync(join(repoRoot, "node_modules"), join(extracted, "node_modules"), "junction");
|
||||
const packageRoot = join(extracted, "packages", directory);
|
||||
execFileSync(process.execPath, [rootRequire.resolve("typescript/bin/tsc"), "-p", "tsconfig.json"], { cwd: packageRoot, stdio: "inherit" });
|
||||
// The package's own build, which is how a release was made: cultnet-ts generates its contracts module first.
|
||||
execFileSync(process.execPath, [npmCli, "run", "build", "--silent"], { cwd: packageRoot, stdio: "inherit" });
|
||||
return rootRequire(join(packageRoot, "dist", "index.js"));
|
||||
}
|
||||
|
||||
|
|
@ -127,7 +129,7 @@ try {
|
|||
const prefix = `${dependency.directory}-v`;
|
||||
const admitted = tags.filter((tag) => {
|
||||
const version = tag.startsWith(prefix) ? tag.slice(prefix.length) : undefined;
|
||||
return version !== undefined && semver.valid(version) !== null && semver.satisfies(version, range) && version !== dependency.manifest.version;
|
||||
return version !== undefined && semver.valid(version) !== null && semver.satisfies(version, range);
|
||||
});
|
||||
for (const tag of admitted) {
|
||||
if (!built.has(tag)) {
|
||||
|
|
|
|||
64
scripts/test-declared-dependency-ranges.test.mjs
Normal file
64
scripts/test-declared-dependency-ranges.test.mjs
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
// test-declared-dependency-ranges.mjs against a fixture repo: a dependency that gained an export a dependent imports,
|
||||
// while its manifest still carries the version of its latest release tag, must fail the check, because a consumer's
|
||||
// range admits that tagged release and it lacks the export. Bumping the version and the range makes it pass.
|
||||
import assert from "node:assert/strict";
|
||||
import { execFileSync, spawnSync } from "node:child_process";
|
||||
import { copyFileSync, mkdirSync, mkdtempSync, rmSync, symlinkSync, writeFileSync } from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
import { dirname, join, resolve } from "node:path";
|
||||
import test from "node:test";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
const repoRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..");
|
||||
const script = join(repoRoot, "scripts", "test-declared-dependency-ranges.mjs");
|
||||
|
||||
const write = (root, path, text) => {
|
||||
mkdirSync(dirname(join(root, path)), { recursive: true });
|
||||
writeFileSync(join(root, path), text);
|
||||
};
|
||||
const git = (root, ...args) => execFileSync("git", ["-c", "user.name=t", "-c", "user.email=t@t", ...args], { cwd: root, stdio: "pipe" });
|
||||
const manifest = (name, version, extra = {}) =>
|
||||
JSON.stringify({ name: `@t/${name}`, version, scripts: { build: "node ../../node_modules/typescript/bin/tsc -p tsconfig.json" }, ...extra });
|
||||
const tsconfig = JSON.stringify({ compilerOptions: { outDir: "dist", module: "commonjs", target: "es2020", declaration: false }, include: ["src"] });
|
||||
|
||||
function fixture() {
|
||||
const root = mkdtempSync(join(tmpdir(), "declared-ranges-fixture-"));
|
||||
write(root, "package.json", JSON.stringify({ name: "fixture", private: true, workspaces: ["packages/a", "packages/b"] }));
|
||||
write(root, "contracts/.keep", "");
|
||||
write(root, "packages/a/package.json", manifest("a", "1.0.0"));
|
||||
write(root, "packages/a/tsconfig.json", tsconfig);
|
||||
write(root, "packages/a/src/index.ts", "export const foo = 1;\n");
|
||||
write(root, "packages/b/package.json", manifest("b", "1.0.0", { dependencies: { "@t/a": "^1.0.0" } }));
|
||||
write(root, "packages/b/tsconfig.json", tsconfig);
|
||||
write(root, "packages/b/src/index.ts", 'import { foo } from "@t/a";\nexport const b = foo;\n');
|
||||
mkdirSync(join(root, "scripts"));
|
||||
copyFileSync(script, join(root, "scripts", "test-declared-dependency-ranges.mjs"));
|
||||
symlinkSync(join(repoRoot, "node_modules"), join(root, "node_modules"), "junction");
|
||||
git(root, "init", "-q", "-b", "main");
|
||||
git(root, "add", "package.json", "contracts", "packages", "scripts");
|
||||
git(root, "commit", "-q", "-m", "release a 1.0.0");
|
||||
git(root, "tag", "a-v1.0.0");
|
||||
return root;
|
||||
}
|
||||
|
||||
const check = (root) => spawnSync(process.execPath, [join(root, "scripts", "test-declared-dependency-ranges.mjs")], { cwd: root, encoding: "utf8" });
|
||||
|
||||
test("a dependency that gained an export a dependent imports, still at its latest tag's version, fails the check", () => {
|
||||
const root = fixture();
|
||||
try {
|
||||
assert.equal(check(root).status, 0, "the fixture passes before the export is added");
|
||||
|
||||
write(root, "packages/a/src/index.ts", "export const foo = 1;\nexport const bar = 2;\n");
|
||||
write(root, "packages/b/src/index.ts", 'import { foo, bar } from "@t/a";\nexport const b = foo + bar;\n');
|
||||
const stale = check(root);
|
||||
assert.notEqual(stale.status, 0, stale.stdout);
|
||||
assert.match(stale.stderr, /a-v1\.0\.0 does not export bar/u);
|
||||
|
||||
write(root, "packages/a/package.json", manifest("a", "1.1.0"));
|
||||
write(root, "packages/b/package.json", manifest("b", "1.0.0", { dependencies: { "@t/a": "^1.1.0" } }));
|
||||
const bumped = check(root);
|
||||
assert.equal(bumped.status, 0, bumped.stderr);
|
||||
} finally {
|
||||
rmSync(root, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
Loading…
Reference in a new issue