Remove development from package.exports before publishing (#3917)

This commit is contained in:
Greg Bergé
2026-01-20 17:22:24 +01:00
committed by GitHub
parent 1e53376151
commit df07fbe242
3 changed files with 152 additions and 7 deletions
+34 -7
View File
@@ -8,6 +8,14 @@
set -euo pipefail
DRY_RUN=false
for arg in "$@"; do
if [[ "${arg}" == "--dry-run" ]]; then
DRY_RUN=true
break
fi
done
NAME=$(node -p "require('./package.json').name")
VERSION=$(node -p "require('./package.json').version")
@@ -16,6 +24,27 @@ if npm view "${NAME}@${VERSION}" >/dev/null 2>&1; then
exit 0
fi
# We compute the workspace root relative to this script
WORKSPACE_ROOT=$(dirname $(dirname $(realpath $0)))
# Strip development exports before packing and restore afterward.
PACKAGE_JSON_PATH="${PWD}/package.json"
PACKAGE_JSON_BACKUP=$(mktemp)
cp "${PACKAGE_JSON_PATH}" "${PACKAGE_JSON_BACKUP}"
cleanup() {
if [[ -n "${TARBALL_PATH:-}" && -f "${TARBALL_PATH}" ]]; then
rm -f "${TARBALL_PATH}"
fi
if [[ -f "${PACKAGE_JSON_BACKUP}" ]]; then
mv "${PACKAGE_JSON_BACKUP}" "${PACKAGE_JSON_PATH}"
fi
}
trap cleanup EXIT
node "${WORKSPACE_ROOT}/scripts/strip-development-exports.mjs" "${PACKAGE_JSON_PATH}"
# Sanitize the name to make it a valid filename as bun doesn't support @ in filenames
SANITIZED_NAME=${NAME//@/}
SANITIZED_NAME=${SANITIZED_NAME//\//-}
@@ -23,9 +52,6 @@ TARBALL_FILENAME="${SANITIZED_NAME}-${VERSION}.tgz"
bun pm pack --filename "${TARBALL_FILENAME}"
# We compute the workspace root relative to this script
WORKSPACE_ROOT=$(dirname $(dirname $(realpath $0)))
# Bun pack puts the tarball in the workspace root
TARBALL_PATH="${WORKSPACE_ROOT}/${TARBALL_FILENAME}"
@@ -34,8 +60,9 @@ if [[ ! -f "${TARBALL_PATH}" ]]; then
exit 1
fi
# Clean up the tarball
trap 'rm -f "${TARBALL_PATH}"' EXIT
# Publish with verbose logging to aid debugging
npm publish "${TARBALL_PATH}" --no-workspaces --provenance
if [[ "${DRY_RUN}" == "true" ]]; then
echo "npm publish \"${TARBALL_PATH}\" --no-workspaces --provenance"
else
npm publish "${TARBALL_PATH}" --no-workspaces --provenance
fi
+57
View File
@@ -0,0 +1,57 @@
import fs from 'node:fs/promises';
import path from 'node:path';
import { pathToFileURL } from 'node:url';
export function stripDevelopmentExports(exportsField) {
if (!exportsField || typeof exportsField !== 'object' || Array.isArray(exportsField)) {
return false;
}
let changed = false;
if (Object.prototype.hasOwnProperty.call(exportsField, 'development')) {
delete exportsField.development;
changed = true;
}
for (const value of Object.values(exportsField)) {
if (value && typeof value === 'object' && !Array.isArray(value)) {
if (stripDevelopmentExports(value)) {
changed = true;
}
}
}
return changed;
}
export async function stripDevelopmentExportsInFile(filePath) {
const raw = await fs.readFile(filePath, 'utf8');
const parsed = JSON.parse(raw);
if (!parsed.exports) {
return false;
}
const changed = stripDevelopmentExports(parsed.exports);
if (!changed) {
return false;
}
const next = `${JSON.stringify(parsed, null, 4)}\n`;
await fs.writeFile(filePath, next);
return true;
}
async function main() {
const filePath = process.argv[2] || path.join(process.cwd(), 'package.json');
await stripDevelopmentExportsInFile(filePath);
}
if (import.meta.url === pathToFileURL(process.argv[1]).href) {
main().catch((error) => {
console.error('Failed to strip development exports:', error);
process.exit(1);
});
}
@@ -0,0 +1,61 @@
import { expect, test } from 'bun:test';
import fs from 'node:fs/promises';
import os from 'node:os';
import path from 'node:path';
import { stripDevelopmentExportsInFile } from './strip-development-exports.mjs';
test('stripDevelopmentExportsInFile removes development conditions', async () => {
const dir = await fs.mkdtemp(path.join(os.tmpdir(), 'strip-dev-exports-'));
const filePath = path.join(dir, 'package.json');
const original = {
name: '@gitbook/example',
exports: {
'.': {
types: './dist/index.d.ts',
development: './src/index.ts',
default: './dist/index.js',
},
'./icons': {
types: './dist/icons.d.ts',
development: './src/icons.ts',
default: './dist/icons.js',
},
},
};
await fs.writeFile(filePath, `${JSON.stringify(original, null, 4)}\n`);
const changed = await stripDevelopmentExportsInFile(filePath);
expect(changed).toBe(true);
const updated = JSON.parse(await fs.readFile(filePath, 'utf8'));
expect(updated.exports['.'].development).toBeUndefined();
expect(updated.exports['./icons'].development).toBeUndefined();
expect(updated.exports['.'].default).toBe('./dist/index.js');
});
test('stripDevelopmentExportsInFile returns false when nothing to remove', async () => {
const dir = await fs.mkdtemp(path.join(os.tmpdir(), 'strip-dev-exports-'));
const filePath = path.join(dir, 'package.json');
const original = {
name: '@gitbook/example',
exports: {
'.': {
types: './dist/index.d.ts',
default: './dist/index.js',
},
},
};
await fs.writeFile(filePath, `${JSON.stringify(original, null, 4)}\n`);
const changed = await stripDevelopmentExportsInFile(filePath);
expect(changed).toBe(false);
const updated = JSON.parse(await fs.readFile(filePath, 'utf8'));
expect(updated).toEqual(original);
});