From 2b5c35cdb17bdceaaf1b5a3ddf11bec59913e709 Mon Sep 17 00:00:00 2001 From: Tom Boucher Date: Wed, 22 Apr 2026 12:09:12 -0400 Subject: [PATCH] test(#2519): add regression test for sdk tarball dist inclusion (#2586) * test(#2519): add regression test verifying sdk/package.json has files + prepublishOnly Guards the sdk/package.json fix for #2519 (tarball shipped without dist/) so future edits can't silently drop either the `files` whitelist or the `prepublishOnly` build hook. Asserts: - `files` is a non-empty array - `files` includes "dist" (so compiled CLI ships in tarball) - `scripts.prepublishOnly` runs a build (npm run build / tsc) - `bin` target lives under dist/ (sanity tie-in) Closes #2519 * test(#2519): accept valid npm glob variants for dist in files matcher Addresses CodeRabbit nitpick: the previous equality check on 'dist' / 'dist/' / 'dist/**' would false-fail on other valid npm packaging forms like './dist', 'dist/**/*', or backslash-separated paths. Normalize each entry and use a regex that accepts all common dist path variants. --- tests/bug-2519-sdk-tarball-dist.test.cjs | 77 ++++++++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 tests/bug-2519-sdk-tarball-dist.test.cjs diff --git a/tests/bug-2519-sdk-tarball-dist.test.cjs b/tests/bug-2519-sdk-tarball-dist.test.cjs new file mode 100644 index 000000000..e4dbce7e1 --- /dev/null +++ b/tests/bug-2519-sdk-tarball-dist.test.cjs @@ -0,0 +1,77 @@ +/** + * Regression test for #2519: @gsd-build/sdk tarball shipped without dist/ + * + * The published 0.1.0 tarball lacked a `files` whitelist including `dist/` and + * a `prepublishOnly` hook to build `dist/` before publish. As a result the + * tarball contained only source and the declared `bin` target `./dist/cli.js` + * was absent at install time, breaking every `gsd-sdk query …` call. + * + * This test guards sdk/package.json so future edits cannot silently drop + * either safeguard. + */ + +'use strict'; + +const { describe, test } = require('node:test'); +const assert = require('node:assert/strict'); +const fs = require('fs'); +const path = require('path'); + +const SDK_PACKAGE_JSON = path.join(__dirname, '..', 'sdk', 'package.json'); + +describe('bug #2519: sdk/package.json ships dist/ in tarball', () => { + const pkg = JSON.parse(fs.readFileSync(SDK_PACKAGE_JSON, 'utf-8')); + + test('has a files whitelist (array) so publish is explicit', () => { + assert.ok( + Array.isArray(pkg.files), + 'sdk/package.json must declare a `files` array so the tarball contents are explicit', + ); + assert.ok( + pkg.files.length > 0, + '`files` array must not be empty', + ); + }); + + test('files whitelist includes dist/ so compiled output is published', () => { + assert.ok(Array.isArray(pkg.files), '`files` must be an array'); + const includesDist = pkg.files.some((entry) => { + if (typeof entry !== 'string') return false; + const normalized = entry.replace(/\\/g, '/').replace(/^\.\//, ''); + return /^dist(?:$|\/|\/\*\*|\/\*\*\/\*)/.test(normalized); + }); + assert.ok( + includesDist, + `sdk/package.json \`files\` must include "dist" so the published tarball contains the compiled CLI (bin target ./dist/cli.js). Found: ${JSON.stringify(pkg.files)}`, + ); + }); + + test('has prepublishOnly script that runs a build', () => { + assert.ok( + pkg.scripts && typeof pkg.scripts === 'object', + 'sdk/package.json must have a `scripts` object', + ); + const prepub = pkg.scripts.prepublishOnly; + assert.ok( + typeof prepub === 'string' && prepub.length > 0, + 'sdk/package.json must define `scripts.prepublishOnly` so dist/ is (re)built before every publish', + ); + // Must invoke a build — either `npm run build`, `tsc`, or similar. + const looksLikeBuild = /\b(build|tsc)\b/.test(prepub); + assert.ok( + looksLikeBuild, + `scripts.prepublishOnly must run a build command (e.g. "npm run build" or "tsc"). Got: ${JSON.stringify(prepub)}`, + ); + }); + + test('bin target lives under dist/ (sanity: the thing files+prepublish must ship)', () => { + assert.ok(pkg.bin, 'sdk/package.json must declare a `bin` field'); + const binValues = typeof pkg.bin === 'string' + ? [pkg.bin] + : Object.values(pkg.bin); + assert.ok( + binValues.some((p) => typeof p === 'string' && p.includes('dist/')), + `bin target must reference dist/ — otherwise the files+prepublishOnly guard is pointless. Got: ${JSON.stringify(pkg.bin)}`, + ); + }); +});