Skip to content
Merged
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
6 changes: 4 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co

`@alminabrulic/deploykit` — a local-only TypeScript CLI (ESM, Node ≥ 20) that reads a Turbo or Nx monorepo and emits the CI/CD files to deploy it to Fly.io: Dockerfiles, `fly.toml`, a GitHub Actions workflow, `.dockerignore`, `DEPLOYMENTS.md`, and `deploykit.config.ts`. There is no backend, no runtime, and no telemetry — the output is files the user owns.

The package ships **two** tsup ESM builds ([tsup.config.ts](tsup.config.ts) exports an array):
The `build` script is `tsup && tsc -p tsconfig.dts.json`. tsup ([tsup.config.ts](tsup.config.ts) exports an array) emits the two ESM bundles; `tsc` emits the one `.d.ts`:

- `src/index.ts` → `dist/index.js` — the CLI (`bin`, shebang banner, owns `clean`).
- `src/config.ts` → `dist/config.js` + `.d.ts` — the library entry behind `package.json` `exports`, because every generated `deploykit.config.ts` opens with `import { defineConfig } from "@alminabrulic/deploykit"`. Without it that import resolves to nothing and the user's config fails to typecheck — which is the entire reason the config is TypeScript instead of JSON.
- `src/config.ts` → `dist/config.js` (tsup) + `dist/config.d.ts` (tsc, via [tsconfig.dts.json](tsconfig.dts.json)) — the library entry behind `package.json` `exports`, because every generated `deploykit.config.ts` opens with `import { defineConfig } from "@alminabrulic/deploykit"`. Without it that import resolves to nothing and the user's config fails to typecheck — which is the entire reason the config is TypeScript instead of JSON.

The declaration is emitted by `tsc`, not tsup's `dts`, because the TS7 native compiler doesn't expose the classic programmatic API that tsup's `dts` (rollup-plugin-dts) drives — enabling it crashes the build. `config.ts` is self-contained (no imports), so a plain single-file `tsc --emitDeclarationOnly` produces the same bundled declaration. Keeping `config.ts` import-free is therefore load-bearing: an import would split the emit across files and pull that module into the published types.

So [src/config.ts](src/config.ts) is **published public API**: anything exported there ships with types, and any import added to it lands in consumers' dependency graph. Keep it types + `defineConfig` + pure constants. After touching either build or the `exports` map, verify like a consumer, not just with `pnpm build`: `npm pack`, install the tarball in a temp project, then `node -e "import('@alminabrulic/deploykit')"` and `tsc` over a generated config.

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,7 @@ To report a vulnerability, please use [GitHub's private vulnerability reporting]
```bash
pnpm install
pnpm dev init --dry-run # run the CLI from source (tsx)
pnpm build # bundle to dist/ (tsup)
pnpm build # bundle to dist/ (tsup) + emit config.d.ts (tsc)
pnpm test # vitest
pnpm typecheck # tsc --noEmit
pnpm lint # biome check
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
]
},
"scripts": {
"build": "tsup",
"build": "tsup && tsc -p tsconfig.dts.json",
"dev": "tsx src/index.ts",
"start": "node dist/index.js",
"typecheck": "tsc --noEmit",
Expand Down
16 changes: 16 additions & 0 deletions tsconfig.dts.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
// Emits only `dist/config.d.ts` — the published types behind the package
// `exports`. Kept separate from the base config (which is typecheck-only,
// `declaration: false`) and run after `tsup` in the `build` script. `tsup`'s
// own `dts` can't be used: the TS7 native compiler doesn't expose the
// programmatic API rollup-plugin-dts needs. Only `src/config.ts` is emitted;
// it has no imports, so a single-file declaration emit is self-contained.
"extends": "./tsconfig.json",
"compilerOptions": {
"declaration": true,
"emitDeclarationOnly": true,
"rootDir": "src",
"outDir": "dist"
},
"include": ["src/config.ts"]
}
11 changes: 8 additions & 3 deletions tsup.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,17 @@ export default defineConfig([
// `import { defineConfig } from "@alminabrulic/deploykit"` — without this
// build (and the matching "exports" in package.json) that import resolves
// to nothing, so the user's config fails to typecheck and can't be loaded
// by their own tooling. `dts` is what makes the config typed in an editor,
// which is the whole reason the config is TypeScript rather than JSON.
// by their own tooling.
//
// The matching `dist/config.d.ts` — what makes the config typed in an
// editor, the whole reason the config is TypeScript rather than JSON — is
// emitted by `tsc -p tsconfig.dts.json` in the `build` script, not here:
// the TS7 native compiler doesn't expose the classic programmatic API that
// tsup's `dts` (rollup-plugin-dts) drives. `config.ts` is self-contained
// (no imports), so a plain single-file declaration emit is equivalent.
entry: ["src/config.ts"],
format: ["esm"],
target: "node20",
dts: true,
minify: false,
},
]);
Loading