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
18 changes: 5 additions & 13 deletions packages/bun-test-extended/src/augment-bun-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,10 @@
import 'jest-extended';
import type { JestExtendedMatcherName } from './types.ts';

/**
* Augment bun:test with only the matchers bun's builtin interfaces don't
* already declare — bun implements many jest-extended-style matchers natively
* with slightly different signatures, and extending over them is a TS2320.
*/
// only the matchers bun's builtin interfaces don't declare: bun implements
// many jest-extended matchers natively with different signatures, and
// extending over one is a TS2320
declare module 'bun:test' {
/**
* toResolve/toReject come from the promise-typed pick: the two async
* matchers return a promise the caller must await.
*/
interface Matchers<T>
extends
Pick<
Expand All @@ -21,10 +15,8 @@ declare module 'bun:test' {
>,
Pick<CustomMatchers<Promise<void>>, 'toResolve' | 'toReject'> {}

/**
* `never` (not `any`) so asymmetric matchers fit any value position in
* toEqual/toMatchObject without tripping typescript/no-unsafe-assignment
*/
// `never`, not `any`: an asymmetric matcher then fits any value position in
// toEqual/toMatchObject without tripping typescript/no-unsafe-assignment
// oxlint-disable-next-line typescript/no-empty-interface -- module augmentation, emptiness is the point
interface AsymmetricMatchers extends Pick<
CustomMatchers<never>,
Expand Down
9 changes: 3 additions & 6 deletions packages/bun-test-extended/src/with-jest-context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,9 @@ import type { JestExtendedMatcher, MatcherContext } from './types.ts';
// The exact util functions jest-extended matchers destructure off `this.utils`.
const jestMatcherUtils = { matcherHint, printExpected, printReceived, printWithType };

// jest-extended matchers destructure `this.utils` (e.g. `const { printReceived } =
// this.utils`) and expect jest's util signatures. Bun's native utils are brand-checked
// — calling them unbound throws "Expected this to be instanceof ExpectMatcherUtils"
// — so every failing assertion would crash instead of printing its message. Hand
// each matcher a context whose `utils` come from the real jest-matcher-utils package
// and whose remaining methods (`equals`, …) stay bound to the native context.
// jest-extended matchers destructure `this.utils`, and Bun's native utils are
// brand-checked: called unbound they throw "Expected this to be instanceof
// ExpectMatcherUtils", so a failing assertion would crash instead of printing
export function withJestContext(matcher: JestExtendedMatcher): JestExtendedMatcher {
return function jestContextAdapter(
this: Readonly<MatcherContext>,
Expand Down
4 changes: 0 additions & 4 deletions packages/format-codemod/src/cli.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,6 @@ import { fileURLToPath } from 'node:url';

const cliPath = fileURLToPath(new URL('cli.ts', import.meta.url));

/**
* Runs the CLI from source under bun; the exit-code contract asserted here is
* what the root format pipeline and the pre-commit hook consume.
*/
function runCLI(
args: readonly string[],
cwd?: string,
Expand Down
5 changes: 0 additions & 5 deletions packages/format-codemod/src/cli/apply-diff-mode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,6 @@ export interface DiffOutput {
readonly stdout: string | null;
}

/**
* Reached only when the file needs edits. Write mode saves the file here — the
* one file-system effect below the entry; what to print is returned, never
* printed, because the entry owns the output streams.
*/
export function applyDiffMode(edit: FileEdit, mode: CLIMode): DiffOutput {
if (mode === 'check') {
return { message: `DIFF ${edit.file} ${edit.result.edits} edit(s)`, stdout: null };
Expand Down
28 changes: 0 additions & 28 deletions packages/format-codemod/src/cli/build-unified-diff.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,3 @@
/**
* A real unified diff — @@ hunk headers with context — so --dry output can be
* applied with patch(1). Inputs are expected to end with a newline, which
* TypeScript sources do and the transform preserves.
*/
export function buildUnifiedDiff(before: string, after: string, label: string): string {
const ops = new MyersDiff(splitLines(before), splitLines(after)).buildOps();

Expand Down Expand Up @@ -35,11 +30,6 @@ interface Position {
readonly y: number;
}

/**
* Myers O((N+M)·D) line diff. D is the edit distance — for padding diffs just
* the handful of inserted blanks — so this stays near-linear, and being a
* minimal diff it never mis-pairs repeated lines.
*/
class MyersDiff {
private readonly a: readonly string[];

Expand All @@ -60,10 +50,6 @@ class MyersDiff {
return this.backtrack();
}

/**
* One snapshot of v per edit-distance round; the round that reaches the end
* stops the search and the snapshots drive the backtrack.
*/
private updateTrace(): void {
for (let d = 0; d <= this.a.length + this.b.length; d++) {
this.trace.push(new Map(this.v));
Expand Down Expand Up @@ -149,9 +135,6 @@ class MyersDiff {
return { prev: { x: prevX, y: prevX - prevK }, moveDown };
}

/**
* The diagonal walk back from pos to prev — the lines both sides share.
*/
private buildEqualOps(pos: Position, prev: Position): DiffOp[] {
const ops: DiffOp[] = [];
let x = pos.x;
Expand Down Expand Up @@ -182,15 +165,8 @@ function buildHunks(ops: readonly DiffOp[]): Hunk[] {
return buildWindows(ops).map((w) => buildHunk(ops, w));
}

/**
* Context lines per hunk, matching diff -u / git defaults.
*/
const CONTEXT_LINES = 3;

/**
* Each changed op pulls a context radius of surrounding ops into its window;
* overlapping or adjacent windows merge into one hunk.
*/
function buildWindows(ops: readonly DiffOp[]): Window[] {
const windows: Window[] = [];

Expand Down Expand Up @@ -243,10 +219,6 @@ function countPrecedingLines(
return { aLine, bLine };
}

/**
* An empty range anchors to the line before it (already correct 0-based);
* a populated one is 1-based.
*/
function formatRange(line: number, count: number): string {
return count === 0 ? `${line},0` : `${line + 1},${count}`;
}
Expand Down
20 changes: 2 additions & 18 deletions packages/format-codemod/src/cli/expand-inputs.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,6 @@
import fs from 'node:fs';
import path from 'node:path';

/**
* Deduped: overlapping patterns (a `src/**` glob plus a file inside src/) must
* not process — or report — the same file twice. Ignore globs filter the final
* list uniformly, so a file is skipped whether it arrived via a directory,
* a glob, or an explicit path argument.
*/
export async function expandInputs(
patterns: readonly string[],
ignore: readonly string[] = [],
Expand Down Expand Up @@ -37,13 +31,8 @@ function expandPattern(p: string): Promise<string[]> {
return Promise.resolve([p]);
}

/**
* Keeps directory expansion out of dependency trees and nested git dirs — a bare
* `format-codemod .` at a repo root must not descend into installed packages.
* Node passes directories to `exclude` (pruning descent); Bun filters final
* matches — segment matching handles both, and Dirents in case withFileTypes
* semantics ever leak through.
*/
// Node passes directories to `exclude` (pruning descent) while Bun filters
// final matches, so the check matches a segment and accepts a Dirent
function isExcluded(
entry: string | { readonly name: string; readonly parentPath?: string },
): boolean {
Expand All @@ -52,11 +41,6 @@ function isExcluded(
return p.split(/[\\/]/u).some((seg) => seg === 'node_modules' || seg === '.git');
}

/**
* A pattern is tried against the path both as expanded and relative to the
* working directory, so `dist/**` ignores dist/ files whether the caller
* passed a relative or an absolute input.
*/
function isIgnored(file: string, patterns: readonly string[]): boolean {
if (patterns.length === 0) {
return false;
Expand Down
7 changes: 0 additions & 7 deletions packages/format-codemod/src/cli/load-format-ignore.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,6 @@
import fs from 'node:fs';
import path from 'node:path';

/**
* Reads `.formatignore` from the given directory: one glob per line, blank
* lines and `#`-prefixed comment lines skipped. Lines share --ignore's glob
* semantics — this is not a gitignore dialect, so `!` negation and directory
* anchoring don't apply. A missing file yields an empty list, so callers
* merge unconditionally.
*/
export function loadFormatIgnore(dir: string): readonly string[] {
const file = path.join(dir, '.formatignore');

Expand Down
10 changes: 0 additions & 10 deletions packages/format-codemod/src/cli/parse-cli-args.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,6 @@ export interface CLIArgs {
readonly inputs: readonly string[];
}

/**
* Strict parsing: an unknown flag is a usage error, not a silent no-op. That
* matters because the default mode writes files — a typo'd --check must never
* fall through to an in-place rewrite. CLIArgs is always an object, so the
* string (error message) return is unambiguous.
*/
export function parseCLIArgs(argv: readonly string[]): CLIArgs | string {
try {
const parsed = parseArgs({
Expand Down Expand Up @@ -48,10 +42,6 @@ export function parseCLIArgs(argv: readonly string[]): CLIArgs | string {
}
}

/**
* --check wins over --dry when both are passed — the stricter mode's
* no-writes guarantee must hold.
*/
function pickMode(check: boolean, dry: boolean): CLIMode {
if (check) {
return 'check';
Expand Down
5 changes: 0 additions & 5 deletions packages/format-codemod/src/cli/print-report.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
import type { FileReport } from './types.ts';

/**
* Prints one file's outcome: the stdout payload (--dry diffs) to stdout,
* messages and per-file progress to stderr. Quiet drops the OK/SKIP noise but
* never the messages.
*/
export function printReport(file: string, report: FileReport, quiet: boolean): void {
if (report.stdout !== null) {
process.stdout.write(report.stdout);
Expand Down
5 changes: 0 additions & 5 deletions packages/format-codemod/src/cli/read-package-version.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
import fs from 'node:fs';

/**
* Reads the version field of the package manifest at the given path, throwing
* when the file isn't a manifest. Callers own the path so it resolves against
* their own location.
*/
export function readPackageVersion(pkgPath: string): string {
const parsed: unknown = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));

Expand Down
5 changes: 0 additions & 5 deletions packages/format-codemod/src/cli/try-check-file.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,6 @@
import { checkFile } from './check-file.ts';
import type { CLIMode, FileReport } from './types.ts';

/**
* A failed file must fail the batch: --check would otherwise exit 0 having
* validated nothing, and a throw would kill the process mid-batch with the
* remaining files silently skipped.
*/
export function tryCheckFile(file: string, mode: CLIMode): FileReport {
if (file.endsWith('.d.ts')) {
return { outcome: 'skipped', bytes: 0, parsed: false, message: null, stdout: null };
Expand Down
5 changes: 0 additions & 5 deletions packages/format-codemod/src/cli/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,6 @@ export type CLIMode = 'write' | 'check' | 'dry';

export type FileOutcome = 'ok' | 'changed' | 'failed' | 'skipped';

/**
* Everything the entry needs to report one file: the outcome for exit-code
* logic, sizes for --bench, and the text to print — modules below the entry
* never write to stdout/stderr themselves.
*/
export interface FileReport {
readonly outcome: FileOutcome;
readonly bytes: number;
Expand Down
8 changes: 0 additions & 8 deletions packages/format-codemod/src/transform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,9 @@ import { parseSource } from './transform/parse-source.ts';
import type { TransformResult } from './types.ts';

export interface TransformOptions {
/**
* Picks the parse dialect: `.tsx` enables JSX, anything else is plain
* TypeScript. Defaults to 'source.ts' — JSX callers must say so.
*/
readonly filename?: string;
}

/**
* Pure transform: source string in, edited source string out, no I/O. On a
* parse error the input is returned untouched alongside the error message.
*/
export function transform(src: string, options?: TransformOptions): TransformResult {
const parsed = parseSource(src, options?.filename ?? 'source.ts');

Expand Down
5 changes: 0 additions & 5 deletions packages/format-codemod/src/transform/apply-edits.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
import type { Edit } from '../types.ts';

/**
* Callers must pass edits sorted last-to-first, so each splice's offsets stay
* valid without adjustment. Segments are collected and joined once instead of
* re-copying the whole string per edit.
*/
export function applyEdits(src: string, edits: readonly Edit[]): string {
const segments: string[] = [];
let tail = src.length;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,6 @@ import type { ParsedSource } from '../types.ts';
import { buildEditsFromAST } from './build-edits-from-ast.ts';
import { parseSource } from './parse-source.ts';

/**
* Narrows the parser's ParsedSource-or-error-message union for fixtures that
* are known-valid TypeScript, sparing every test the same narrowing dance.
* The throw is unreachable today but makes a fixture that stops parsing fail
* loudly with the parser's own message.
*/
function parse(src: string): ParsedSource {
const parsed = parseSource(src, 'file.ts');

Expand Down
20 changes: 2 additions & 18 deletions packages/format-codemod/src/transform/build-edits-from-ast.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,6 @@ import { collectChildNodes } from './collect-child-nodes.ts';
import { needsBlankLine } from './needs-blank-line.ts';
import { planGapEdit } from './plan-gap-edit.ts';

/**
* Sorted last-to-first so applying splices in order never shifts later offsets.
*/
export function buildEditsFromAST(src: string, parsed: ParsedSource): Edit[] {
const file: SourceFile = { src, comments: parsed.comments };

Expand All @@ -27,10 +24,6 @@ function walk(file: SourceFile, node: ASTNode): Edit[] {
return edits;
}

/**
* The statement lists this node directly contains — the sequences whose
* adjacent pairs the padding rules apply to.
*/
function getStatementLists(node: ASTNode): (readonly ASTNode[])[] {
const bodies: (readonly ASTNode[])[] = [];

Expand Down Expand Up @@ -72,21 +65,12 @@ function buildPairEdits(file: SourceFile, container: ASTNode, body: readonly AST
return edits;
}

/**
* Adjacent imports are never padded or collapsed, whatever their shape: the
* interior of an import block belongs to the import sorter, and edits there
* would be reordered or stripped out from under us. The boundary between the
* last import and the first real statement is still padded as usual.
*/
// the interior of an import block belongs to the import sorter, which would
// reorder or strip any edit made there
function isImportPair(prev: ASTNode, next: ASTNode): boolean {
return prev.type === 'ImportDeclaration' && next.type === 'ImportDeclaration';
}

/**
* A statement that spans multiple lines is separated from both neighbours —
* its shape already reads as a paragraph, so it gets paragraph spacing.
* Single-line statements may sit tight.
*/
function isMultiline(src: string, node: ASTNode): boolean {
return (
typeof node.start === 'number' &&
Expand Down
4 changes: 0 additions & 4 deletions packages/format-codemod/src/transform/collect-ast-nodes.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
import type { ASTNode } from '../types.ts';
import { isASTNode } from './is-ast-node.ts';

/**
* The AST nodes in a property value: the node itself, an array's node
* elements, or nothing for non-node values.
*/
export function collectASTNodes(value: unknown): ASTNode[] {
if (!Array.isArray(value)) {
return isASTNode(value) ? [value] : [];
Expand Down
4 changes: 0 additions & 4 deletions packages/format-codemod/src/transform/collect-child-nodes.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
import type { ASTNode } from '../types.ts';
import { collectASTNodes } from './collect-ast-nodes.ts';

/**
* A node's direct child nodes across all properties, skipping the location
* and parent back-references a traversal must not follow.
*/
export function collectChildNodes(node: ASTNode): ASTNode[] {
const children: ASTNode[] = [];

Expand Down
Loading