fix(db): normalise line endings when parsing queries
The generator embeds each query's SQL as a string literal, taking whatever line endings the checkout happened to have. On a CRLF checkout every regenerated query differed from the committed one by `\n` -> `\r\n`, so `wrnexus build` dirtied the working tree and that churn buried real changes in the same file -- which is how a hand-applied edit ends up preferable to running the generator. Line endings carry no meaning in SQL, so normalise on parse and let generated output be stable across platforms. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -29,8 +29,17 @@ export interface ModelRef {
|
|||||||
model: Model;
|
model: Model;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Parse annotated queries from one `.sql` file's contents. */
|
/**
|
||||||
|
* Parse annotated queries from one `.sql` file's contents.
|
||||||
|
*
|
||||||
|
* Line endings are normalised to LF first. The generator embeds each query's
|
||||||
|
* SQL as a string literal, so without this a CRLF checkout regenerated every
|
||||||
|
* query with CRLF where the committed file had LF -- a build dirtied the
|
||||||
|
* working tree, and that churn buried real changes in the same file. Line
|
||||||
|
* endings carry no meaning in SQL.
|
||||||
|
*/
|
||||||
export function parseQueries(content: string): QueryDef[] {
|
export function parseQueries(content: string): QueryDef[] {
|
||||||
|
content = content.replace(/\r\n?/g, "\n");
|
||||||
const out: QueryDef[] = [];
|
const out: QueryDef[] = [];
|
||||||
const re = /--\s*name:\s*(\w+)\s*:(one|many|exec)\b[^\n]*\n([\s\S]*?)(?=--\s*name:|$)/gi;
|
const re = /--\s*name:\s*(\w+)\s*:(one|many|exec)\b[^\n]*\n([\s\S]*?)(?=--\s*name:|$)/gi;
|
||||||
let m: RegExpExecArray | null;
|
let m: RegExpExecArray | null;
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
import { expect, test } from "bun:test";
|
||||||
|
import { parseQueries } from "../src/generate.ts";
|
||||||
|
|
||||||
|
// The generator embeds each query's SQL as a string literal. It used to embed
|
||||||
|
// whatever line endings the checkout happened to have, so on a CRLF checkout
|
||||||
|
// every regenerated query differed from the committed one by `\n` -> `\r\n`.
|
||||||
|
// A build therefore dirtied the working tree, and the churn buried real
|
||||||
|
// changes in the same file. Line endings carry no meaning in SQL, so the
|
||||||
|
// parser normalises them and generated output stays stable across platforms.
|
||||||
|
test("query SQL is normalised to LF regardless of the checkout's line endings", () => {
|
||||||
|
const lf = "-- name: GetOne :one\nSELECT a,\n b\nFROM t\nWHERE id = :id;\n";
|
||||||
|
const crlf = lf.replace(/\n/g, "\r\n");
|
||||||
|
|
||||||
|
const fromLf = parseQueries(lf);
|
||||||
|
const fromCrlf = parseQueries(crlf);
|
||||||
|
|
||||||
|
expect(fromCrlf).toEqual(fromLf);
|
||||||
|
expect(fromCrlf[0]!.sql).not.toContain("\r");
|
||||||
|
// The SQL itself must still be intact, not merely stripped of carriage returns.
|
||||||
|
expect(fromCrlf[0]!.sql).toContain("SELECT a,");
|
||||||
|
expect(fromCrlf[0]!.sql).toContain("WHERE id = :id");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("a lone CR does not survive into the embedded SQL either", () => {
|
||||||
|
const cr = "-- name: GetOne :one\rSELECT 1\r";
|
||||||
|
for (const q of parseQueries(cr)) expect(q.sql).not.toContain("\r");
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user