# Build and test

> Bun scripts for typecheck, bun test, bun build of src/index.ts to dist, and the local bun run src/index.ts dev path.

- Repository: jaredpalmer/vwaffle
- GitHub: https://github.com/jaredpalmer/vwaffle
- Human docs: https://grok-wiki.com/public/docs/jaredpalmer-vwaffle-7983cb893581
- Complete Markdown: https://grok-wiki.com/public/docs/jaredpalmer-vwaffle-7983cb893581/llms-full.txt

## Source Files

- `package.json`
- `src/config.test.ts`
- `src/index.ts`
- `src/config.ts`

---

---
title: "Build and test"
description: "Bun scripts for typecheck, bun test, bun build of src/index.ts to dist, and the local bun run src/index.ts dev path."
---

`package.json` defines five scripts for this package: `typecheck` runs `tsc --noEmit`, `test` runs `bun test`, `build` runs `bun build src/index.ts --outdir dist --target node`, `dev` runs `bun run src/index.ts`, and `prepublishOnly` chains typecheck, test, and build. The published `vwaffle` bin is `./dist/index.js`. The package is ESM (`"type": "module"`), requires Node `>=18`, and lists `typescript` `^5.6.0` and `@types/bun` `^1.2.0` as the only `devDependencies`.

## Scripts

| Script | Command | Role |
| --- | --- | --- |
| `typecheck` | `tsc --noEmit` | Type-check without emitting files |
| `test` | `bun test` | Run `bun:test` suites |
| `build` | `bun build src/index.ts --outdir dist --target node` | Bundle the CLI entry to `dist/` for Node |
| `dev` | `bun run src/index.ts` | Execute `src/index.ts` in place |
| `prepublishOnly` | `bun run typecheck && bun run test && bun run build` | Gate publish on all three succeeding |

<ParamField body="typecheck" type="npm script">
`tsc --noEmit`. Uses the `typescript` `^5.6.0` devDependency. No emit path and no `tsconfig` contents are defined in this package surface.
</ParamField>

<ParamField body="test" type="npm script">
`bun test`. The checked-in suite is `src/config.test.ts`, which imports `describe`, `expect`, and `test` from `bun:test`.
</ParamField>

<ParamField body="build" type="npm script">
`bun build src/index.ts --outdir dist --target node`. Entry is `src/index.ts`. Output directory is `dist`. Target runtime is Node, matching `engines.node` `>=18` and the `#!/usr/bin/env node` shebang on the entry.
</ParamField>

<ParamField body="dev" type="npm script">
`bun run src/index.ts`. Same TypeScript entry as the build, without writing `dist/`.
</ParamField>

<ParamField body="prepublishOnly" type="npm script">
`bun run typecheck && bun run test && bun run build`. Stops on the first failing command.
</ParamField>

```bash
bun run typecheck
bun run test
bun run build
bun run dev
```

`bun test` is the script body for `test`, so it is equivalent to `bun run test`.

## Maintainer layout

```text
src/index.ts         CLI entry (shebang #!/usr/bin/env node)
src/config.ts        interpolate, loadDesiredConfig, redacted, …
src/config.test.ts   bun:test suites for config + diff
dist/index.js        bun build output; package bin "vwaffle"
```

`src/index.ts` imports `./api.ts`, `./config.ts`, `./diff.ts`, and `./types.ts`. `src/config.test.ts` imports `interpolate`, `redacted`, and `removeRulesWithMissingEnv` from `./config.ts`, plus `diff` and `NO_DRIFT` from `./diff.ts`. `package.json` `files` publishes `dist`, `README.md`, and `LICENSE` — not `src/`.

## Typecheck

`bun run typecheck` invokes `tsc --noEmit`. There is no emit directory and no compiler-option file in this surface. A clean run is a zero-exit typecheck of the TypeScript sources the compiler is configured to see.

## Test

`bun run test` (or `bun test`) loads `bun:test`. The suite in `src/config.test.ts` covers interpolation, rule dropping, redaction, and drift text.

### interpolate

Replaces `${VAR}` from a supplied env map and records secrets.

- Input object: `{ rules: [{ name: 'x', value: 'token-${API_KEY}' }] }`
- Env: `{ API_KEY: 's3cret' }`
- Expected object: `{ rules: [{ name: 'x', value: 'token-s3cret' }] }`
- `missing.size` is `0`
- `secrets.get('API_KEY')` is `'s3cret'`

Unset placeholders stay in the value and are recorded with a JSON path:

- Input: `{ a: ['${NOPE}'] }` with empty env
- Result still contains `'${NOPE}'`
- `missing` is `['NOPE ($.a[0])']`

### removeRulesWithMissingEnv

Drops only rules whose serialized text still contains a `${NAME}` for a missing variable. A config with `keep` (no placeholder) and `drop` (`value: '${GONE}'`) plus missing set `['GONE ($.rules[1])']` yields `result.rules` names `['keep']`.

### redacted

Serializes the value and replaces recorded secret strings with `[REDACTED]`. For `{ value: 'token-s3cret' }` and secrets `[['API_KEY', 's3cret']]`, the output does not contain `s3cret` and does contain `[REDACTED]`.

### diff

`diff({ a: 1 }, { a: 1 })` equals `NO_DRIFT`. Differing objects produce line-oriented text that contains `- \t"a": 1` and `+ \t"a": 2`.

```ts title="src/config.test.ts"
import { describe, expect, test } from 'bun:test';
import { interpolate, redacted, removeRulesWithMissingEnv } from './config.ts';
import { diff, NO_DRIFT } from './diff.ts';
```

## Build

`bun run build` is:

```bash
bun build src/index.ts --outdir dist --target node
```

| Flag / input | Value |
| --- | --- |
| Entry | `src/index.ts` |
| `--outdir` | `dist` |
| `--target` | `node` |
| Package bin | `vwaffle` → `./dist/index.js` |

The entry starts with `#!/usr/bin/env node` and sets `VERSION` to `'0.1.0'` (same as `package.json` `version`). After a successful build, the installable CLI is the file at `dist/index.js`, not the TypeScript sources.

<Note>
`package.json` lists no production `dependencies`. The bundle input is the `src/index.ts` graph (`./api.ts`, `./config.ts`, `./diff.ts`, `./types.ts` plus Node built-ins such as `node:fs/promises` and `node:path`).
</Note>

## Local `src/index.ts` path

`bun run dev` is `bun run src/index.ts`. That runs `main()` in the TypeScript entry without requiring `dist/`.

`main()` parses `process.argv.slice(2)` and dispatches `help`, `version`, `init`, `pull`, `plan`, and `apply`. With no command, `parseArgs` defaults `command` to `'help'`. Unknown commands throw `unknown command ${options.command}. Run \`vwaffle help\`.` Failures print `vwaffle: ${message}` and set `process.exitCode = 1`.

<Steps>
<Step title="Run the TypeScript entry">
```bash
bun run src/index.ts
bun run dev
```
Both execute `src/index.ts`. Pass CLI arguments after the file path when invoking the file directly:

```bash
bun run src/index.ts help
bun run src/index.ts --version
```
</Step>
<Step title="Typecheck">
```bash
bun run typecheck
```
Expect `tsc --noEmit` to exit 0.
</Step>
<Step title="Run tests">
```bash
bun test
```
Expect the `interpolate`, `removeRulesWithMissingEnv`, `redacted`, and `diff` describes in `src/config.test.ts` to pass.
</Step>
<Step title="Produce the Node binary">
```bash
bun run build
```
Expect `dist/` to contain the `--target node` bundle. The published name for that file is `./dist/index.js` (`bin.vwaffle`).
</Step>
</Steps>

<Warning>
`dev` is not a substitute for `build`. npm/npx consumers resolve `vwaffle` to `./dist/index.js`. `prepublishOnly` rebuilds that file only after typecheck and tests succeed.
</Warning>

## Publish gate

`prepublishOnly` runs, in order:

1. `bun run typecheck` → `tsc --noEmit`
2. `bun run test` → `bun test`
3. `bun run build` → `bun build src/index.ts --outdir dist --target node`

A failure in any step aborts the chain (`&&`). The published tarball includes `dist`, `README.md`, and `LICENSE` only.

## Constraints

| Constraint | Value |
| --- | --- |
| Package name / version | `vwaffle` `0.1.0` |
| Module type | `module` |
| Engine | `node` `>=18` |
| Type checker | `typescript` `^5.6.0` via `tsc --noEmit` |
| Test runner | `bun test` / `bun:test` |
| Bun types | `@types/bun` `^1.2.0` |
| Build target | `node` |
| Published bin | `./dist/index.js` |

## Related pages

<CardGroup>
<Card title="Contributing" href="/contributing">
`prepublishOnly` gates, Node engine, Apache-2.0 metadata, and the files published as the vwaffle CLI.
</Card>
<Card title="Installation" href="/installation">
Node `>=18`, npm / npx / bunx invocation, and the published `dist/index.js` binary.
</Card>
<Card title="CLI reference" href="/cli-reference">
Commands and flags the local `bun run src/index.ts` path dispatches.
</Card>
</CardGroup>
