diff --git a/bun.lock b/bun.lock index 0f05d9c..f147370 100644 --- a/bun.lock +++ b/bun.lock @@ -12,8 +12,6 @@ "devDependencies": { "@types/bun": "latest", "@types/diff": "^8.0.0", - }, - "peerDependencies": { "typescript": "^5.9.3", }, }, diff --git a/index.ts b/index.ts deleted file mode 100644 index f67b2c6..0000000 --- a/index.ts +++ /dev/null @@ -1 +0,0 @@ -console.log("Hello via Bun!"); \ No newline at end of file diff --git a/package.json b/package.json index 25b4c20..4d0a498 100644 --- a/package.json +++ b/package.json @@ -15,9 +15,6 @@ }, "scripts": { "check": "bunx tsc --noEmit", - "build": "./scripts/build.sh", - "cli:build": "bun run scripts/build.ts", - "cli:build:all": "bun run scripts/build.ts --all", "cli:install": "bun cli:build && sudo cp dist/shout /usr/local/bin", "cli:link": "ln -sf $(pwd)/src/cli/index.ts ~/.bun/bin/shout", "cli:uninstall": "sudo rm /usr/local/bin", @@ -25,9 +22,7 @@ }, "devDependencies": { "@types/bun": "latest", - "@types/diff": "^8.0.0" - }, - "peerDependencies": { + "@types/diff": "^8.0.0", "typescript": "^5.9.3" }, "dependencies": { diff --git a/src/cli/index.ts b/src/cli/index.ts index 0bbc63b..0cf3a9b 100755 --- a/src/cli/index.ts +++ b/src/cli/index.ts @@ -119,7 +119,6 @@ program pathDirs: opts.path, envVars, timeout: timeoutMs, - verbose: opts.verbose ?? false, onCommand: opts.verbose ? (cmd) => process.stderr.write(ansis.dim(` $ ${cmd.command}\n`)) : undefined, diff --git a/src/duration.ts b/src/duration.ts index f0b332d..95490d4 100644 --- a/src/duration.ts +++ b/src/duration.ts @@ -3,12 +3,11 @@ export function parseDuration(s: string): number { if (!match) throw new Error(`Invalid duration: ${s}`) const value = parseFloat(match[1]!) - const unit = match[2]! + const unit = match[2] as "ms" | "s" | "m" switch (unit) { case "ms": return value case "s": return value * 1000 case "m": return value * 60_000 - default: throw new Error(`Unknown unit: ${unit}`) } } diff --git a/src/format.ts b/src/format.ts index 352c7b2..87e33a2 100644 --- a/src/format.ts +++ b/src/format.ts @@ -72,20 +72,21 @@ export function formatFailure(test: TestResult): string { lines.push(` ${ansis.dim("$")} ${failure.result.command.command}`) if (failure.diffLines.length > 0) { - lines.push(ansis.red(" expected:")) + const expectedLines: string[] = [] + const actualLines: string[] = [] for (const dl of failure.diffLines) { + const text = dl.kind === "context" ? ansis.dim(dl.text) : dl.text if (dl.kind === "expected" || dl.kind === "equal" || dl.kind === "context") { const prefix = dl.kind === "expected" ? ansis.red(" > ") : " " - lines.push(`${prefix}${dl.kind === "context" ? ansis.dim(dl.text) : dl.text}`) + expectedLines.push(`${prefix}${text}`) } - } - lines.push(ansis.green(" actual:")) - for (const dl of failure.diffLines) { if (dl.kind === "actual" || dl.kind === "equal" || dl.kind === "context") { const prefix = dl.kind === "actual" ? ansis.green(" > ") : " " - lines.push(`${prefix}${dl.kind === "context" ? ansis.dim(dl.text) : dl.text}`) + actualLines.push(`${prefix}${text}`) } } + lines.push(ansis.red(" expected:"), ...expectedLines) + lines.push(ansis.green(" actual:"), ...actualLines) } if (failure.exitCodeMismatch) { diff --git a/src/index.ts b/src/index.ts index 29281a8..107ee68 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,4 +1,4 @@ -export type { Command, Directive, ShoutFile } from "./parse.ts" +export type { Command, ShoutFile } from "./parse.ts" export type { CommandResult, FileResult } from "./run.ts" export type { DiffLine } from "./match.ts" export type { TestResult } from "./format.ts" diff --git a/src/match.ts b/src/match.ts index c846630..deedf42 100644 --- a/src/match.ts +++ b/src/match.ts @@ -1,3 +1,5 @@ +import { escapeRegex } from "./utils.ts" + export function matchLine(pattern: string, actual: string): boolean { if (!pattern.includes("...")) return pattern === actual @@ -8,10 +10,6 @@ export function matchLine(pattern: string, actual: string): boolean { return regex.test(actual) } -function escapeRegex(s: string): string { - return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&") -} - export function matchOutput( expected: string[], actual: string[], diff --git a/src/parse.ts b/src/parse.ts index 94cb20c..8ba9aab 100644 --- a/src/parse.ts +++ b/src/parse.ts @@ -1,3 +1,5 @@ +import { trimTrailingEmpty } from "./utils.ts" + export type Command = { line: number raw: string @@ -48,12 +50,6 @@ function parseExitCode(lines: string[]): { return { lines, exitCode: null } } -function trimTrailingEmpty(lines: string[]): string[] { - let end = lines.length - while (end > 0 && lines[end - 1] === "") end-- - return lines.slice(0, end) -} - export function parse(path: string, content: string): ShoutFile { const rawLines = content.split("\n") diff --git a/src/run.test.ts b/src/run.test.ts index e2ab64f..a06f994 100644 --- a/src/run.test.ts +++ b/src/run.test.ts @@ -7,6 +7,7 @@ import type { ShoutFile } from "./parse.ts" function makeFile(commands: { command: string; expected?: string[] }[]): ShoutFile { return { path: "test.shout", + directives: [], commands: commands.map((c, i) => ({ line: i + 1, raw: `$ ${c.command}`, diff --git a/src/run.ts b/src/run.ts index 8df05a7..931b651 100644 --- a/src/run.ts +++ b/src/run.ts @@ -3,6 +3,7 @@ import { tmpdir } from "node:os" import { join } from "node:path" import type { Command, ShoutFile } from "./parse.ts" +import { trimTrailingEmpty, escapeRegex } from "./utils.ts" export type CommandResult = { command: Command @@ -22,13 +23,12 @@ type RunOptions = { pathDirs?: string[] envVars?: Record timeout: number - verbose: boolean onCommand?: (cmd: Command) => void } const SENTINEL_PREFIX = "__SHOUT_SENTINEL_" -function buildScript(commands: Command[], sentinel: string): string { +function buildScript(commands: Command[]): string { const lines: string[] = ["exec 2>&1"] for (let i = 0; i < commands.length; i++) { @@ -37,7 +37,7 @@ function buildScript(commands: Command[], sentinel: string): string { // Sentinel: printf to avoid echo interpretation issues // Format: __SHOUT_SENTINEL____ lines.push( - `printf '\\n${sentinel}%s_${i}__\\n' "$?"`, + `printf '\\n${SENTINEL_PREFIX}%s_${i}__\\n' "$?"`, ) } @@ -99,16 +99,6 @@ function parseSentinelOutput( return { outputs, exitCodes } } -function trimTrailingEmpty(lines: string[]): string[] { - let end = lines.length - while (end > 0 && lines[end - 1] === "") end-- - return lines.slice(0, end) -} - -function escapeRegex(s: string): string { - return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&") -} - export async function runFile( file: ShoutFile, options: RunOptions, @@ -119,8 +109,7 @@ export async function runFile( return { file, results: [], tmpDir } } - const sentinel = SENTINEL_PREFIX - const script = buildScript(file.commands, sentinel) + const script = buildScript(file.commands) const env: Record = options.cleanEnv ? {} @@ -157,12 +146,12 @@ export async function runFile( const { outputs, exitCodes } = parseSentinelOutput( stdout, - sentinel, + SENTINEL_PREFIX, file.commands.length, ) const results: CommandResult[] = file.commands.map((cmd, i) => { - if (options.verbose && options.onCommand) { + if (options.onCommand) { options.onCommand(cmd) } return { diff --git a/src/update.ts b/src/update.ts index e1ac545..d93e39a 100644 --- a/src/update.ts +++ b/src/update.ts @@ -1,6 +1,7 @@ import type { CommandResult } from "./run.ts" import type { ShoutFile } from "./parse.ts" -import { matchOutput, matchLine } from "./match.ts" +import { matchOutput } from "./match.ts" +import { trimTrailingEmpty } from "./utils.ts" export function rewriteFile( file: ShoutFile, @@ -75,9 +76,3 @@ export function rewriteFile( return output.join("\n") } - -function trimTrailingEmpty(lines: string[]): string[] { - let end = lines.length - while (end > 0 && lines[end - 1] === "") end-- - return lines.slice(0, end) -} diff --git a/src/utils.ts b/src/utils.ts new file mode 100644 index 0000000..ae4e3cb --- /dev/null +++ b/src/utils.ts @@ -0,0 +1,9 @@ +export function trimTrailingEmpty(lines: string[]): string[] { + let end = lines.length + while (end > 0 && lines[end - 1] === "") end-- + return lines.slice(0, end) +} + +export function escapeRegex(s: string): string { + return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&") +} diff --git a/tsconfig.json b/tsconfig.json index d5ad05c..23a8e6b 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -27,17 +27,6 @@ "noUnusedLocals": false, "noUnusedParameters": false, "noPropertyAccessFromIndexSignature": false, - "baseUrl": ".", - "paths": { - "$*": [ - "./src/server/*" - ], - "@*": [ - "./src/shared/*" - ], - "%*": [ - "./src/lib/*" - ] - } + "baseUrl": "." } } \ No newline at end of file