@intentius/chant-lexicon-sql 0.118.0 → 0.118.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -8,7 +8,11 @@
8
8
  * git-ignored paths, child projects and the project's `exclude` globs are
9
9
  * left out. A file whose first lines carry `-- chant-discovery-skip` (or the
10
10
  * `/* chant-discovery-skip *\/` form) is not read, for SQL that is not schema:
11
- * seed data, a migration, a query.
11
+ * seed data, a migration, a query. Nor is a file under a `migrations`
12
+ * directory (#3713): versioned migrations and an ORM's generated migrations
13
+ * are `ALTER`s and repeated `CREATE`s of objects declared elsewhere, and a
14
+ * build of the project root, as `chant lifecycle diff` and an ApplyOp's plan
15
+ * run with no `sourceDir`, walks into them.
12
16
  *
13
17
  * The dialect is the declarations': a project whose templates are Postgres
14
18
  * reads its files as Postgres. With no templates it is `sql.dialect`, and
@@ -19,6 +23,10 @@ import type { BuildRootContext, BuildRootContribution } from "@intentius/chant/l
19
23
  import { type SqlDialect } from "../dialects.js";
20
24
  /** Whether a file is a `.sql` file to read: the name, and no skip or generated marker. */
21
25
  export declare function isSqlSourceFile(name: string, full: string): boolean;
26
+ /** A directory whose `.sql` files are migrations, never declarations. */
27
+ export declare const MIGRATIONS_DIR = "migrations";
28
+ /** Whether `file` sits under a {@link MIGRATIONS_DIR} directory below `sourceDir`. */
29
+ export declare function inMigrationsDir(sourceDir: string, file: string): boolean;
22
30
  /** The dialect the project's `.sql` files are read as. */
23
31
  export declare function filesDialect(entities: ReadonlyMap<string, Declarable> | undefined, config: Record<string, unknown>): SqlDialect;
24
32
  /** The `buildRoots` contribution: every `.sql` file's declarations, or nothing when the source directory holds none. */
@@ -1 +1 @@
1
- {"version":3,"file":"build-root.d.ts","sourceRoot":"","sources":["../../src/files/build-root.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAIH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAC9D,OAAO,KAAK,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,MAAM,0BAA0B,CAAC;AAGxF,OAAO,EAAgB,KAAK,UAAU,EAAE,MAAM,aAAa,CAAC;AAQ5D,0FAA0F;AAC1F,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAInE;AAED,0DAA0D;AAC1D,wBAAgB,YAAY,CAAC,QAAQ,EAAE,WAAW,CAAC,MAAM,EAAE,UAAU,CAAC,GAAG,SAAS,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,UAAU,CAY/H;AAED,wHAAwH;AACxH,wBAAsB,iBAAiB,CAAC,GAAG,EAAE,gBAAgB,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAO7F"}
1
+ {"version":3,"file":"build-root.d.ts","sourceRoot":"","sources":["../../src/files/build-root.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAIH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAC9D,OAAO,KAAK,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,MAAM,0BAA0B,CAAC;AAGxF,OAAO,EAAgB,KAAK,UAAU,EAAE,MAAM,aAAa,CAAC;AAQ5D,0FAA0F;AAC1F,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAInE;AAED,yEAAyE;AACzE,eAAO,MAAM,cAAc,eAAe,CAAC;AAE3C,sFAAsF;AACtF,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAExE;AAED,0DAA0D;AAC1D,wBAAgB,YAAY,CAAC,QAAQ,EAAE,WAAW,CAAC,MAAM,EAAE,UAAU,CAAC,GAAG,SAAS,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,UAAU,CAY/H;AAED,wHAAwH;AACxH,wBAAsB,iBAAiB,CAAC,GAAG,EAAE,gBAAgB,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAO7F"}
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "algorithm": "sha256",
3
3
  "artifacts": {
4
- "manifest.json": "e2bf7028a41757883dc8f527812bce692e5d90dc5f84c1ab2a593f76735f3a91",
4
+ "manifest.json": "dee03b1b2c3883aa14ff3fb1868f4d6f28e6771cd19654ab48cc4b2e92530520",
5
5
  "meta.json": "44b1dcfa5b5e5be2239fdc9614ab6c11d3f1461d64e61d572e3658af6c96aa68",
6
6
  "types/index.d.ts": "0286ebb367773e84c76818b0f69cb14cff8328fd2f8c65ed87563c8d5815458c",
7
7
  "rules/postgres-templates.ts": "da6e305bc2ff3d0d64a20925fe588e051c2a4b556b37f2581c61c314ef7c580d",
@@ -57,5 +57,5 @@
57
57
  "rules/sqlpg117.ts": "3d5c2d57ccdee3cf91c3ba3cb6363750787a850df8ebd75e9cc3f33113303827",
58
58
  "rules/sqlpg118.ts": "e2a40a88a9374c4d99d02118ca4c60877ba0c6eb1ce603a93b24f5fd23d81dde"
59
59
  },
60
- "composite": "bd178b112dd161bc7445a81be2f7471b080eff8ad34e48ce2b7601d32f11823e"
60
+ "composite": "870e87be62c02d9d259b98eb5c9f9928db21950ffd6a569e0f1df10268cea4e3"
61
61
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sql",
3
- "version": "0.118.0",
3
+ "version": "0.118.1",
4
4
  "chantVersion": ">=0.1.0",
5
5
  "namespace": "SQL",
6
6
  "intrinsics": [],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@intentius/chant-lexicon-sql",
3
- "version": "0.118.0",
3
+ "version": "0.118.1",
4
4
  "description": "SQL lexicon for chant — database schema as typed declarations, one dialect at a time; ClickHouse first, with types read from a pinned server's catalog",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://intentius.io/chant",
@@ -70,7 +70,7 @@
70
70
  "typescript": "^5.9.3"
71
71
  },
72
72
  "peerDependencies": {
73
- "@intentius/chant": "^0.118.0",
73
+ "@intentius/chant": "^0.118.1",
74
74
  "typescript": "^5.9.3"
75
75
  }
76
76
  }
@@ -8,7 +8,11 @@
8
8
  * git-ignored paths, child projects and the project's `exclude` globs are
9
9
  * left out. A file whose first lines carry `-- chant-discovery-skip` (or the
10
10
  * `/* chant-discovery-skip *\/` form) is not read, for SQL that is not schema:
11
- * seed data, a migration, a query.
11
+ * seed data, a migration, a query. Nor is a file under a `migrations`
12
+ * directory (#3713): versioned migrations and an ORM's generated migrations
13
+ * are `ALTER`s and repeated `CREATE`s of objects declared elsewhere, and a
14
+ * build of the project root, as `chant lifecycle diff` and an ApplyOp's plan
15
+ * run with no `sourceDir`, walks into them.
12
16
  *
13
17
  * The dialect is the declarations': a project whose templates are Postgres
14
18
  * reads its files as Postgres. With no templates it is `sql.dialect`, and
@@ -16,7 +20,7 @@
16
20
  */
17
21
 
18
22
  import { readFileSync } from "node:fs";
19
- import { relative, resolve } from "node:path";
23
+ import { relative, resolve, sep } from "node:path";
20
24
  import type { Declarable } from "@intentius/chant/declarable";
21
25
  import type { BuildRootContext, BuildRootContribution } from "@intentius/chant/lexicon";
22
26
  import { findSourceFiles, hasDiscoveryMarkerSync } from "@intentius/chant/discovery/files";
@@ -36,6 +40,14 @@ export function isSqlSourceFile(name: string, full: string): boolean {
36
40
  return !SQL_SKIP_MARKER.test(readHead(full, 1024));
37
41
  }
38
42
 
43
+ /** A directory whose `.sql` files are migrations, never declarations. */
44
+ export const MIGRATIONS_DIR = "migrations";
45
+
46
+ /** Whether `file` sits under a {@link MIGRATIONS_DIR} directory below `sourceDir`. */
47
+ export function inMigrationsDir(sourceDir: string, file: string): boolean {
48
+ return relative(sourceDir, file).split(sep).slice(0, -1).includes(MIGRATIONS_DIR);
49
+ }
50
+
39
51
  /** The dialect the project's `.sql` files are read as. */
40
52
  export function filesDialect(entities: ReadonlyMap<string, Declarable> | undefined, config: Record<string, unknown>): SqlDialect {
41
53
  for (const e of entities?.values() ?? []) {
@@ -54,7 +66,7 @@ export function filesDialect(entities: ReadonlyMap<string, Declarable> | undefin
54
66
  /** The `buildRoots` contribution: every `.sql` file's declarations, or nothing when the source directory holds none. */
55
67
  export async function sqlFilesBuildRoot(ctx: BuildRootContext): Promise<BuildRootContribution> {
56
68
  const sourceDir = ctx.sourceDir ?? resolve(ctx.projectRoot, typeof ctx.config.sourceDir === "string" ? ctx.config.sourceDir : ".");
57
- const files = (await findSourceFiles(sourceDir, isSqlSourceFile)).sort();
69
+ const files = (await findSourceFiles(sourceDir, isSqlSourceFile)).filter((f) => !inMigrationsDir(sourceDir, f)).sort();
58
70
  if (files.length === 0) return { entities: new Map() };
59
71
  const dialect = filesDialect(ctx.entities, ctx.config);
60
72
  const sources = files.map((f) => ({ origin: relative(ctx.projectRoot, f) || f, ddl: readFileSync(f, "utf8") }));
@@ -174,6 +174,38 @@ export const activeUsers = view\`CREATE VIEW app.active_users AS SELECT id FROM
174
174
  });
175
175
  });
176
176
 
177
+ describe("SQL that is not schema, in a build of the project root (#3713)", () => {
178
+ const dirs: string[] = [];
179
+ afterAll(async () => {
180
+ for (const d of dirs) await rm(d, { recursive: true, force: true });
181
+ });
182
+
183
+ test("a .sql file under a migrations directory, at any depth, is not read", async () => {
184
+ const dir = await project("migrations", {
185
+ "app.ts": `import { schema, table } from "@intentius/chant-lexicon-sql/postgres";\nexport const app = schema\`CREATE SCHEMA app\`;\nexport const users = table\`CREATE TABLE app.users (id bigint)\`;\n`,
186
+ });
187
+ dirs.push(dir);
188
+ await mkdir(join(dir, "src", "migrations", "20261010T0000-add-email"), { recursive: true });
189
+ await writeFile(join(dir, "src", "migrations", "20261010T0000-add-email", "migration.sql"), "ALTER TABLE app.users ADD COLUMN email text;\n");
190
+ await mkdir(join(dir, "src", "prisma", "migrations", "0001_init"), { recursive: true });
191
+ await writeFile(join(dir, "src", "prisma", "migrations", "0001_init", "migration.sql"), "CREATE TABLE app.users (id bigint);\n");
192
+ const { errors, doc } = await buildProject(dir);
193
+ expect(errors).toEqual([]);
194
+ expect(doc!.applyOrder).toEqual(["app", "users"]);
195
+ });
196
+
197
+ test("a file named migrations.sql, or in a directory that only starts with the word, is still read", async () => {
198
+ const dir = await project("not-migrations", { "app.ts": `import { schema } from "@intentius/chant-lexicon-sql/postgres";\nexport const app = schema\`CREATE SCHEMA app\`;\n` });
199
+ dirs.push(dir);
200
+ await writeFile(join(dir, "src", "migrations.sql"), "CREATE TABLE app.a (id bigint);\n");
201
+ await mkdir(join(dir, "src", "migrations-old"), { recursive: true });
202
+ await writeFile(join(dir, "src", "migrations-old", "b.sql"), "CREATE TABLE app.b (id bigint);\n");
203
+ const { errors, doc } = await buildProject(dir);
204
+ expect(errors).toEqual([]);
205
+ expect([...doc!.applyOrder].sort()).toEqual(["a", "app", "b"]);
206
+ });
207
+ });
208
+
177
209
  describe("references between a file's objects", () => {
178
210
  test("a bare name where SQL names an object is a reference, and a cycle is an error", async () => {
179
211
  const { sqlFileEntities } = await import("./sql-files");