Skip to content

Commit 7960924

Browse files
committed
fix: publish dist while developing from source
Keep workspace packages source-first so local apps run directly from TypeScript, while generating dist manifests for release artifacts. This avoids worktree-specific dist drift and keeps npm publishes pointed at built files.
1 parent 9b6fb21 commit 7960924

9 files changed

Lines changed: 219 additions & 80 deletions

File tree

.github/workflows/release.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ jobs:
5959
shell: bash
6060
run: |
6161
for package_dir in packages/paykit packages/polar packages/stripe; do
62-
(cd "$package_dir" && bun publish --access public --tag "${{ steps.npm_tag.outputs.tag }}")
62+
(cd "$package_dir/dist" && bun publish --access public --tag "${{ steps.npm_tag.outputs.tag }}")
6363
done
6464
env:
6565
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

packages/paykit/package.json

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -19,34 +19,34 @@
1919
"url": "git+https://github.com/getpaykit/paykit.git"
2020
},
2121
"bin": {
22-
"paykitjs": "./dist/cli/index.js"
22+
"paykitjs": "./src/cli/index.ts"
2323
},
2424
"files": [
2525
"dist"
2626
],
2727
"type": "module",
28-
"main": "./dist/index.js",
29-
"types": "./dist/index.d.ts",
28+
"main": "./src/index.ts",
29+
"types": "./src/index.ts",
3030
"exports": {
3131
".": {
32-
"types": "./dist/index.d.ts",
3332
"paykit-source": "./src/index.ts",
34-
"default": "./dist/index.js"
33+
"types": "./src/index.ts",
34+
"default": "./src/index.ts"
3535
},
3636
"./handlers/next": {
37-
"types": "./dist/handlers/next.d.ts",
3837
"paykit-source": "./src/handlers/next.ts",
39-
"default": "./dist/handlers/next.js"
38+
"types": "./src/handlers/next.ts",
39+
"default": "./src/handlers/next.ts"
4040
},
4141
"./client": {
42-
"types": "./dist/client/index.d.ts",
4342
"paykit-source": "./src/client/index.ts",
44-
"default": "./dist/client/index.js"
43+
"types": "./src/client/index.ts",
44+
"default": "./src/client/index.ts"
4545
},
4646
"./database": {
47-
"types": "./dist/database/schema.d.ts",
4847
"paykit-source": "./src/database/schema.ts",
49-
"default": "./dist/database/schema.js"
48+
"types": "./src/database/schema.ts",
49+
"default": "./src/database/schema.ts"
5050
}
5151
},
5252
"scripts": {
@@ -56,6 +56,7 @@
5656
},
5757
"dependencies": {
5858
"@better-fetch/fetch": "^1.1.21",
59+
"@types/pg": "^8.18.0",
5960
"@clack/prompts": "^1.1.0",
6061
"better-call": "^2.0.2",
6162
"commander": "^12.1.0",
@@ -71,7 +72,6 @@
7172
"zod": "^4.0.0"
7273
},
7374
"devDependencies": {
74-
"@types/pg": "^8.18.0",
7575
"drizzle-kit": "^0.31.5",
7676
"tsdown": "^0.21.1",
7777
"vitest": "^4.0.18"

packages/paykit/tsdown.config.ts

Lines changed: 20 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,23 @@
1+
import { fileURLToPath } from "node:url";
2+
13
import { defineConfig } from "tsdown";
24

3-
export default defineConfig({
4-
clean: true,
5-
copy: [
6-
{
7-
flatten: false,
8-
from: "src/database/migrations/**/*",
5+
import { createPackageTsdownConfig } from "../../tsdown.base.ts";
6+
7+
export default defineConfig(
8+
createPackageTsdownConfig({
9+
packageRoot: fileURLToPath(new URL(".", import.meta.url)),
10+
copy: [
11+
{
12+
flatten: false,
13+
from: "src/database/migrations/**/*",
14+
},
15+
],
16+
entry: {
17+
index: "src/index.ts",
18+
"cli/index": "src/cli/index.ts",
19+
"handlers/next": "src/handlers/next.ts",
20+
"client/index": "src/client/index.ts",
921
},
10-
],
11-
deps: {
12-
onlyAllowBundle: false,
13-
skipNodeModulesBundle: true,
14-
},
15-
dts: true,
16-
entry: {
17-
index: "src/index.ts",
18-
"cli/index": "src/cli/index.ts",
19-
"handlers/next": "src/handlers/next.ts",
20-
"client/index": "src/client/index.ts",
21-
},
22-
fixedExtension: false,
23-
format: "esm",
24-
outDir: "dist",
25-
platform: "node",
26-
target: "node22",
27-
unbundle: true,
28-
});
22+
}),
23+
);

packages/polar/package.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,13 @@
1111
"dist"
1212
],
1313
"type": "module",
14-
"main": "./dist/index.js",
15-
"types": "./dist/index.d.ts",
14+
"main": "./src/index.ts",
15+
"types": "./src/index.ts",
1616
"exports": {
1717
".": {
18-
"types": "./dist/index.d.ts",
1918
"paykit-source": "./src/index.ts",
20-
"default": "./dist/index.js"
19+
"types": "./src/index.ts",
20+
"default": "./src/index.ts"
2121
}
2222
},
2323
"scripts": {

packages/polar/tsdown.config.ts

Lines changed: 12 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,14 @@
1+
import { fileURLToPath } from "node:url";
2+
13
import { defineConfig } from "tsdown";
24

3-
export default defineConfig({
4-
clean: true,
5-
deps: {
6-
onlyAllowBundle: false,
7-
skipNodeModulesBundle: true,
8-
},
9-
dts: true,
10-
entry: {
11-
index: "src/index.ts",
12-
},
13-
fixedExtension: false,
14-
format: "esm",
15-
outDir: "dist",
16-
platform: "node",
17-
target: "node22",
18-
unbundle: true,
19-
});
5+
import { createPackageTsdownConfig } from "../../tsdown.base.ts";
6+
7+
export default defineConfig(
8+
createPackageTsdownConfig({
9+
packageRoot: fileURLToPath(new URL(".", import.meta.url)),
10+
entry: {
11+
index: "src/index.ts",
12+
},
13+
}),
14+
);

packages/stripe/package.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,13 @@
1111
"dist"
1212
],
1313
"type": "module",
14-
"main": "./dist/index.js",
15-
"types": "./dist/index.d.ts",
14+
"main": "./src/index.ts",
15+
"types": "./src/index.ts",
1616
"exports": {
1717
".": {
18-
"types": "./dist/index.d.ts",
1918
"paykit-source": "./src/index.ts",
20-
"default": "./dist/index.js"
19+
"types": "./src/index.ts",
20+
"default": "./src/index.ts"
2121
}
2222
},
2323
"scripts": {

packages/stripe/tsdown.config.ts

Lines changed: 12 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,14 @@
1+
import { fileURLToPath } from "node:url";
2+
13
import { defineConfig } from "tsdown";
24

3-
export default defineConfig({
4-
clean: true,
5-
deps: {
6-
onlyAllowBundle: false,
7-
skipNodeModulesBundle: true,
8-
},
9-
dts: true,
10-
entry: {
11-
index: "src/index.ts",
12-
},
13-
fixedExtension: false,
14-
format: "esm",
15-
outDir: "dist",
16-
platform: "node",
17-
target: "node22",
18-
unbundle: true,
19-
});
5+
import { createPackageTsdownConfig } from "../../tsdown.base.ts";
6+
7+
export default defineConfig(
8+
createPackageTsdownConfig({
9+
packageRoot: fileURLToPath(new URL(".", import.meta.url)),
10+
entry: {
11+
index: "src/index.ts",
12+
},
13+
}),
14+
);

scripts/prepare-package-dist.ts

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
import { cp, mkdir, readFile, writeFile } from "node:fs/promises";
2+
import path from "node:path";
3+
import { fileURLToPath } from "node:url";
4+
5+
type ExportTarget = {
6+
types?: string;
7+
default?: string;
8+
};
9+
10+
type PackageJson = {
11+
name: string;
12+
version: string;
13+
description?: string;
14+
keywords?: string[];
15+
license?: string;
16+
repository?: unknown;
17+
type?: string;
18+
main?: string;
19+
types?: string;
20+
exports: Record<string, ExportTarget>;
21+
bin?: Record<string, string>;
22+
dependencies?: Record<string, string>;
23+
};
24+
25+
const repoRoot = path.resolve(fileURLToPath(new URL("..", import.meta.url)));
26+
27+
export async function preparePackageDist(packageRoot: string) {
28+
const distDir = path.join(packageRoot, "dist");
29+
const pkg = JSON.parse(
30+
await readFile(path.join(packageRoot, "package.json"), "utf8"),
31+
) as PackageJson;
32+
33+
await mkdir(distDir, { recursive: true });
34+
35+
const publishManifest = omitUndefined({
36+
name: pkg.name,
37+
version: pkg.version,
38+
description: pkg.description,
39+
keywords: pkg.keywords,
40+
license: pkg.license,
41+
repository: pkg.repository,
42+
type: pkg.type,
43+
main: toDistRuntimePath(pkg.main),
44+
types: toDistTypesPath(pkg.types),
45+
exports: Object.fromEntries(
46+
Object.entries(pkg.exports).map(([subpath, target]) => [
47+
subpath,
48+
omitUndefined({
49+
types: toDistTypesPath(target.types),
50+
default: toDistRuntimePath(target.default),
51+
}),
52+
]),
53+
),
54+
bin: pkg.bin
55+
? Object.fromEntries(
56+
Object.entries(pkg.bin).map(([name, target]) => [name, toDistRuntimePath(target)]),
57+
)
58+
: undefined,
59+
dependencies: rewriteWorkspaceDependencies(pkg.dependencies, pkg.version),
60+
});
61+
62+
await writeFile(
63+
path.join(distDir, "package.json"),
64+
`${JSON.stringify(publishManifest, null, 2)}\n`,
65+
"utf8",
66+
);
67+
68+
await copyFirstExisting(distDir, [
69+
path.join(packageRoot, "README.md"),
70+
path.join(repoRoot, "README.md"),
71+
]);
72+
await copyFirstExisting(distDir, [
73+
path.join(packageRoot, "LICENSE"),
74+
path.join(packageRoot, "LICENSE.md"),
75+
path.join(repoRoot, "LICENSE"),
76+
path.join(repoRoot, "LICENSE.md"),
77+
]);
78+
}
79+
80+
function toDistRuntimePath(filePath: string | undefined) {
81+
if (!filePath) return undefined;
82+
return toDistPath(filePath).replace(/\.(?:[cm]?ts|tsx)$/, ".js");
83+
}
84+
85+
function toDistTypesPath(filePath: string | undefined) {
86+
if (!filePath) return undefined;
87+
return toDistPath(filePath).replace(/\.(?:[cm]?ts|tsx)$/, ".d.ts");
88+
}
89+
90+
function toDistPath(filePath: string) {
91+
const normalized = filePath.startsWith("./") ? filePath.slice(2) : filePath;
92+
if (normalized.startsWith("src/")) return `./${normalized.slice(4)}`;
93+
if (normalized.startsWith("dist/")) return `./${normalized.slice(5)}`;
94+
return `./${normalized}`;
95+
}
96+
97+
function rewriteWorkspaceDependencies(
98+
dependencies: Record<string, string> | undefined,
99+
packageVersion: string,
100+
) {
101+
if (!dependencies) return undefined;
102+
103+
return Object.fromEntries(
104+
Object.entries(dependencies).map(([name, version]) => [
105+
name,
106+
version === "workspace:*" ? packageVersion : version,
107+
]),
108+
);
109+
}
110+
111+
function omitUndefined<T extends Record<string, unknown>>(value: T) {
112+
return Object.fromEntries(Object.entries(value).filter(([, entry]) => entry !== undefined));
113+
}
114+
115+
async function copyFirstExisting(distDir: string, candidates: string[]) {
116+
for (const candidate of candidates) {
117+
try {
118+
await cp(candidate, path.join(distDir, path.basename(candidate)));
119+
return;
120+
} catch {}
121+
}
122+
}
123+
124+
if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
125+
await preparePackageDist(process.cwd());
126+
}

tsdown.base.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { preparePackageDist } from "./scripts/prepare-package-dist.ts";
2+
3+
type PackageTsdownOptions = {
4+
packageRoot: string;
5+
entry: Record<string, string>;
6+
copy?: Array<{ from: string; flatten?: boolean }>;
7+
};
8+
9+
export function createPackageTsdownConfig(options: PackageTsdownOptions) {
10+
return {
11+
clean: true,
12+
copy: options.copy,
13+
deps: {
14+
skipNodeModulesBundle: true,
15+
},
16+
dts: true,
17+
entry: options.entry,
18+
fixedExtension: false,
19+
format: "esm",
20+
onSuccess: async () => {
21+
await preparePackageDist(options.packageRoot);
22+
},
23+
outDir: "dist",
24+
platform: "node",
25+
target: "node22",
26+
unbundle: true,
27+
};
28+
}

0 commit comments

Comments
 (0)