Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions .changeset/fix-publish-dist-scope.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
---
'@agentscript/agentfabric-dialect': patch
'@agentscript/agentforce': patch
'@agentscript/agentforce-dialect': patch
'@agentscript/agentscript-dialect': patch
'@agentscript/compiler': patch
'@agentscript/language': patch
'@agentscript/lsp': patch
'@agentscript/lsp-browser': patch
'@agentscript/lsp-server': patch
'@agentscript/monaco': patch
'@agentscript/parser': patch
'@agentscript/parser-javascript': patch
'@agentscript/parser-tree-sitter': patch
---

Rewrite built dist imports to the published `@sf-agentscript/*` scope during release.
150 changes: 106 additions & 44 deletions scripts/publish.mjs
Original file line number Diff line number Diff line change
@@ -1,62 +1,124 @@
/**
* Publish script for CI: rewrites @agentscript/* → @sf-agentscript/* in all
* package.json files at publish time, then runs changeset publish.
* package.json files and built dist files at publish time, then runs
* changeset publish.
*
* This allows the codebase to use @agentscript/* internally while publishing
* under the @sf-agentscript npm scope.
*/

import { execFileSync } from 'node:child_process';
import { readFileSync, writeFileSync } from 'node:fs';
import { existsSync, readFileSync, readdirSync, writeFileSync } from 'node:fs';
import { join } from 'node:path';
import { fileURLToPath, pathToFileURL } from 'node:url';

const ROOT = new URL('..', import.meta.url).pathname;
const ROOT = fileURLToPath(new URL('..', import.meta.url));
const INTERNAL_SCOPE = '@agentscript/';
const PUBLISH_SCOPE = '@sf-agentscript/';
const DIST_FILE_SUFFIXES = [
'.js',
'.mjs',
'.cjs',
'.d.ts',
'.d.mts',
'.d.cts',
'.map',
];

// Step 1: Discover all workspace packages
const output = execFileSync('pnpm', ['-r', 'list', '--json', '--depth', '-1'], {
cwd: ROOT,
encoding: 'utf8',
});
const packages = JSON.parse(output);

// Step 2: Rewrite package.json files to publish scope
let count = 0;
for (const pkg of packages) {
const pkgJsonPath = join(pkg.path, 'package.json');
const raw = readFileSync(pkgJsonPath, 'utf8');
export function shouldRewriteDistFile(fileName) {
return DIST_FILE_SUFFIXES.some(suffix => fileName.endsWith(suffix));
}

function rewriteScopeInFile(filePath) {
const raw = readFileSync(filePath, 'utf8');
const rewritten = raw.replaceAll(INTERNAL_SCOPE, PUBLISH_SCOPE);

if (rewritten !== raw) {
writeFileSync(pkgJsonPath, rewritten);
count++;
console.log(
` ✓ ${pkg.name} → ${pkg.name.replace(INTERNAL_SCOPE, PUBLISH_SCOPE)}`
);
if (rewritten === raw) {
return false;
}

writeFileSync(filePath, rewritten);
return true;
}

export function rewriteDistFiles(directory) {
if (!existsSync(directory)) {
return 0;
}

let count = 0;
for (const entry of readdirSync(directory, { withFileTypes: true })) {
const entryPath = join(directory, entry.name);

if (entry.isDirectory()) {
count += rewriteDistFiles(entryPath);
} else if (entry.isFile() && shouldRewriteDistFile(entry.name)) {
count += rewriteScopeInFile(entryPath) ? 1 : 0;
}
}

return count;
}

function main() {
// Step 1: Discover all workspace packages
const output = execFileSync(
'pnpm',
['-r', 'list', '--json', '--depth', '-1'],
{
cwd: ROOT,
encoding: 'utf8',
}
);
const packages = JSON.parse(output);

// Step 2: Rewrite package.json files to publish scope
let packageJsonCount = 0;
for (const pkg of packages) {
const pkgJsonPath = join(pkg.path, 'package.json');

if (rewriteScopeInFile(pkgJsonPath)) {
packageJsonCount++;
console.log(
` ✓ ${pkg.name} → ${pkg.name.replace(INTERNAL_SCOPE, PUBLISH_SCOPE)}`
);
}
}

// Step 3: Rewrite changeset config so it recognizes the new package names
const changesetConfigPath = join(ROOT, '.changeset', 'config.json');
const changesetRaw = readFileSync(changesetConfigPath, 'utf8');
writeFileSync(
changesetConfigPath,
changesetRaw.replaceAll(INTERNAL_SCOPE, PUBLISH_SCOPE)
);

// Step 4: Rewrite built package outputs to publish scope
let distFileCount = 0;
for (const pkg of packages) {
distFileCount += rewriteDistFiles(join(pkg.path, 'dist'));
}

console.log(
`\nRewrote ${packageJsonCount} package.json files and ${distFileCount} dist files to ${PUBLISH_SCOPE}* scope\n`
);

// Step 5: Re-install so pnpm resolves workspace: references with new names
execFileSync('pnpm', ['install', '--no-frozen-lockfile'], {
cwd: ROOT,
stdio: 'inherit',
});

// Step 6: Publish via changeset
execFileSync('pnpm', ['changeset', 'publish'], {
cwd: ROOT,
stdio: 'inherit',
});
}

// Step 3: Rewrite changeset config so it recognizes the new package names
const changesetConfigPath = join(ROOT, '.changeset', 'config.json');
const changesetRaw = readFileSync(changesetConfigPath, 'utf8');
writeFileSync(
changesetConfigPath,
changesetRaw.replaceAll(INTERNAL_SCOPE, PUBLISH_SCOPE)
);

console.log(
`\nRewrote ${count} package.json files to ${PUBLISH_SCOPE}* scope\n`
);

// Step 4: Re-install so pnpm resolves workspace: references with new names
execFileSync('pnpm', ['install', '--no-frozen-lockfile'], {
cwd: ROOT,
stdio: 'inherit',
});

// Step 5: Publish via changeset
execFileSync('pnpm', ['changeset', 'publish'], {
cwd: ROOT,
stdio: 'inherit',
});
if (
process.argv[1] &&
import.meta.url === pathToFileURL(process.argv[1]).href
) {
main();
}