uql-codemod 0.50.1 → 0.51.0
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.
- package/README.md +3 -2
- package/dist/builders.js +4 -4
- package/dist/definedMessages.d.ts +1 -1
- package/dist/definedMessages.js +1 -1
- package/dist/imports.js +1 -0
- package/dist/transform.js +16 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -31,12 +31,13 @@ Only `--flag=value` is read, and anything unrecognised is an error. It exits `0`
|
|
|
31
31
|
- Members named by string to callbacks: `mappedBy: 'author'` to `(post) => post.author`, and `include`, `references` and `@Entity`/`defineEntity`'s `indexes` and `hooks` likewise: `{ columns: (post) => [post.title] }`.
|
|
32
32
|
- `references: (post) => post.authorId` on a to-one (`@ManyToOne`, `@OneToOne`, or in `defineEntity`) that names no foreign key, declaring `@Field({ references: () => User }) authorId` first on a decorated class that has no such column, typed as the key it points at.
|
|
33
33
|
- A partial-index `where` string to `sql`, importing it, on entities and in the migration builder.
|
|
34
|
-
- A class registered with `defineEntity(Post, { ... })` to the `defineEntity({ ... })` call it stands for, whole or not at all: `export const Post = defineEntity({ name: 'Post', fields })` with its sections chained, as a defined entity's options are above, and `export type Post = InstanceType<typeof Post>`. The class is removed and a table it named kept as `table`; a field stating no `type` takes the one its property declares (`authorId?: number | null` is `f.number().nullable()`), each relation becomes `author: () => rel.manyToOne(User, { ... })`, a relation aggregate
|
|
34
|
+
- A class registered with `defineEntity(Post, { ... })` to the `defineEntity({ ... })` call it stands for, whole or not at all: `export const Post = defineEntity({ name: 'Post', fields })` with its sections chained, as a defined entity's options are above, and `export type Post = InstanceType<typeof Post>`. The class is removed and a table it named kept as `table`; a field stating no `type` takes the one its property declares (`authorId?: number | null` is `f.number().nullable()`), each relation becomes `author: () => rel.manyToOne(User, { ... })`, a relation aggregate an aggregate field (`(post) => ({ total: () => post.comments.count() })`), a hook's method a function of the row (`this` read as the row), and the same class's `defineField`, `defineId`, `defineRelation`, `defineHook`, `defineIndex`, `defineTrigger` and `defineFilter` are folded in.
|
|
35
35
|
- A JSON index entry to the path read off the column's ref: `{ column: post.kind, jsonPath: { path: 'theme', type: Number } }` to `{ column: post.kind.theme, type: Number }`, and a `jsonArray`'s element type to `type: [String]`.
|
|
36
36
|
- `col('cost')` to the ref it names, ``(product) => sql`${product.cost}` `` in a `computed` and `refs(Item).cost` in a statement, and `$lock: { wait: 'skip' }` to `{ $wait: 'skip' }`, or to `true` for `{}` and a `'block'` wait.
|
|
37
37
|
- `$exclude` to a `$select` leaving its fields out: `$exclude: { password: true }` to `$select: { password: false }`, at every depth of `$populate`.
|
|
38
38
|
- An aggregate's `$agg` to `$select`, and each field it or `$text`'s `$fields` names to a key: `{ $sum: { amount: true } }`.
|
|
39
|
-
-
|
|
39
|
+
- An upsert's conflict keys to a query, `upsertOne(User, { email: true }, row)` to `upsertOne(User, { $conflict: { email: true } }, row)`, and its `{ update: X }` options to `X` as its fourth argument, in `upsertOne`, `upsertMany` and a trigger's `upsertInto`; `const { id } = await upsertOne(...)` to `const id`.
|
|
40
|
+
- Renamed exports, at the import and every use: `raw` to `sql`, `QueryWhereMap` to `QueryWhere`, `IndexColumnSchema` to `IndexEntry`, `IndexColumnModifiers` to `IndexEntryModifiers`, `RelationKeyMap` to `KeyMap`, `FilterCondition` to `FilterWhere`, `SqlMigrationModuleOptions` to `MigrationModuleOptions`, `buildSqlQuerierMigrationModule` to `buildMigrationModule`, `QueryDialect` to `SqlQueryDialect`, `EngineFeatures` to `DialectFeatures`, `KnownMigratorDialect` to `DialectName`, `QueryConflictPaths` to `QueryConflictKeys`, `D1Preparer`/`D1Database` to `D1Queryable`, and the removed driver classes to the one each extended: `PgDialect`/`NeonDialect`/`PgliteDialect` to `PostgresDialect`, `CrdbQuerier`/`NeonQuerier` to `PgQuerier`, `LibsqlQuerier`/`TursoQuerier` to `HranaQuerier`, `TursoLocalQuerier` to `SqliteQuerier` and `TursoDatabase` to `SqliteDatabase`, `MySql2Dialect` to `MySqlDialect`, `MongodbNativeDialect` to `MongoDialect`, `Sqlite3QuerierPool` to `SqliteQuerierPool`, the migration builder's `I`-prefixed types to the unprefixed name (`IColumnBuilder`/`IColumnFactory`, and `uql-orm/migrate`'s `ColumnBuilder`/`ColumnFactory`, to `TableColumnBuilder`/`TableColumnFactory`), moving the import to the entry that exports it; and `MongoQuerier`, `isMongoQuerier` and `MongoSchemaIntrospector` to `uql-orm/mongodb`.
|
|
40
41
|
- Imports off `uql-orm/maria`, `uql-orm/mongo` and `uql-orm/bunSql` to `uql-orm/mariadb`, `uql-orm/mongodb` and `uql-orm/bun-sql`, and off `uql-orm/type`, `uql-orm/dialect`, `uql-orm/entity`, `uql-orm/querier` and `uql-orm/namingStrategy` to `uql-orm`.
|
|
41
42
|
- The `[idKey]?: '...'` brand, and its import, on a key not called `id`, `_id` or `uuid` and on every composite.
|
|
42
43
|
|
package/dist/builders.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import ts from 'typescript';
|
|
2
2
|
import { call, flag, flagOrCall } from './columnMethods.js';
|
|
3
3
|
import { formatFindings, locationAt } from './context.js';
|
|
4
|
-
import {
|
|
4
|
+
import { UNJOINED_FOREIGN_KEY, NO_KEY_TYPE, nameClash, ON_DELETE_ON_RELATION } from './definedMessages.js';
|
|
5
5
|
import { CHAINED_OPTIONS, FIRST_CALL_OPTIONS } from './definedOptions.js';
|
|
6
6
|
import { applyEdits, editedText, replaced, replacingUses, reparsedAgain, unedited, } from './edits.js';
|
|
7
7
|
import { importEdits } from './imports.js';
|
|
@@ -340,7 +340,7 @@ function definedColumn(node) {
|
|
|
340
340
|
}
|
|
341
341
|
const target = targetOf(references.call.arguments[0]);
|
|
342
342
|
if (identifierText(references.receiver) === 'f' || !target) {
|
|
343
|
-
return { message: target ? NO_KEY_TYPE :
|
|
343
|
+
return { message: target ? NO_KEY_TYPE : UNJOINED_FOREIGN_KEY };
|
|
344
344
|
}
|
|
345
345
|
const onDelete = methods.get('onDelete');
|
|
346
346
|
const cut = [references, ...(onDelete ? [onDelete] : [])].map((method) => ({
|
|
@@ -360,7 +360,7 @@ function definedColumn(node) {
|
|
|
360
360
|
const references = options.get('references');
|
|
361
361
|
const target = references && targetOf(references);
|
|
362
362
|
if (references && !target) {
|
|
363
|
-
return { message:
|
|
363
|
+
return { message: UNJOINED_FOREIGN_KEY };
|
|
364
364
|
}
|
|
365
365
|
if (target && !options.has('type')) {
|
|
366
366
|
return { message: NO_KEY_TYPE };
|
|
@@ -408,7 +408,7 @@ function claimKey(name, { target, onDelete }, claims, targets) {
|
|
|
408
408
|
}
|
|
409
409
|
const relation = ts.isIdentifier(name) ? /^(.+?)(?:Id|_id)$/.exec(key)?.[1] : undefined;
|
|
410
410
|
if (!relation || claims.named.has(relation) || !targets.insertable) {
|
|
411
|
-
return { claims, problem:
|
|
411
|
+
return { claims, problem: UNJOINED_FOREIGN_KEY };
|
|
412
412
|
}
|
|
413
413
|
const written = [
|
|
414
414
|
`references: (${targets.param}) => ${targets.param}.${key}`,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/** Why a name the codemod writes is refused: a file already binds it. */
|
|
2
2
|
export declare const nameClash: (name: string) => string;
|
|
3
3
|
/** Why a defined column's foreign key is refused: no relation joins on it, and none can be written for it. */
|
|
4
|
-
export declare const
|
|
4
|
+
export declare const UNJOINED_FOREIGN_KEY = "references another entity, and no relation can be named for it: declare one in '.relations()', as 'rel.manyToOne(Target, { references: (row) => row.key })'";
|
|
5
5
|
/** Why a joined column's `onDelete` is refused: its relation states another, or takes options the codemod cannot write into. */
|
|
6
6
|
export declare const ON_DELETE_ON_RELATION = "joins through a relation that cannot take its 'onDelete': settle the action in that relation's options";
|
|
7
7
|
/** Why a foreign key with no type is refused: a defined column states its own, which only its target's key implied. */
|
package/dist/definedMessages.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/** Why a name the codemod writes is refused: a file already binds it. */
|
|
2
2
|
export const nameClash = (name) => `'${name}' is already named here, and the codemod writes it`;
|
|
3
3
|
/** Why a defined column's foreign key is refused: no relation joins on it, and none can be written for it. */
|
|
4
|
-
export const
|
|
4
|
+
export const UNJOINED_FOREIGN_KEY = "references another entity, and no relation can be named for it: declare one in '.relations()', as 'rel.manyToOne(Target, { references: (row) => row.key })'";
|
|
5
5
|
/** Why a joined column's `onDelete` is refused: its relation states another, or takes options the codemod cannot write into. */
|
|
6
6
|
export const ON_DELETE_ON_RELATION = "joins through a relation that cannot take its 'onDelete': settle the action in that relation's options";
|
|
7
7
|
/** Why a foreign key with no type is refused: a defined column states its own, which only its target's key implied. */
|
package/dist/imports.js
CHANGED
|
@@ -76,6 +76,7 @@ export const SQL_TAG = 'sql';
|
|
|
76
76
|
*/
|
|
77
77
|
const RENAMED_EXPORTS = new Map([
|
|
78
78
|
['QueryWhereMap', { to: 'QueryWhere' }],
|
|
79
|
+
['QueryConflictPaths', { to: 'QueryConflictKeys' }],
|
|
79
80
|
['IndexColumnSchema', { to: 'IndexEntry' }],
|
|
80
81
|
['IndexColumnModifiers', { to: 'IndexEntryModifiers' }],
|
|
81
82
|
['RelationKeyMap', { to: 'KeyMap' }],
|
package/dist/transform.js
CHANGED
|
@@ -428,19 +428,31 @@ function rewriteBuilderCall(call, ctx) {
|
|
|
428
428
|
}
|
|
429
429
|
const UPSERT_CALLS = new Set(['upsertOne', 'upsertMany', 'upsertInto']);
|
|
430
430
|
/**
|
|
431
|
-
* Rewrites an upsert's
|
|
432
|
-
*
|
|
433
|
-
*
|
|
431
|
+
* Rewrites an upsert's conflict keys into its query's `$conflict`, `{ $conflict: { sku: true } }`, and its options
|
|
432
|
+
* holding `update` alone into that update, its fourth argument now. Read wherever it is called, since a querier is
|
|
433
|
+
* seldom imported from uql where it is used. A query already naming `$conflict`, or typed with one, is left as it is,
|
|
434
|
+
* and any other fourth argument is already an update, `{}` included, so a second run changes nothing.
|
|
434
435
|
*/
|
|
435
436
|
function rewriteUpsertCall(call, ctx) {
|
|
436
437
|
const callee = call.expression;
|
|
437
438
|
const name = identifierText(ts.isPropertyAccessExpression(callee) ? callee.name : callee);
|
|
439
|
+
if (!name || !UPSERT_CALLS.has(name)) {
|
|
440
|
+
return;
|
|
441
|
+
}
|
|
442
|
+
const keys = call.arguments[1];
|
|
443
|
+
if (call.arguments.length >= 3 && keys && isConflictKeys(keys, ctx)) {
|
|
444
|
+
ctx.edits.push(replaced(keys, ['{ $conflict: ', original(keys), ' }']));
|
|
445
|
+
}
|
|
438
446
|
const options = call.arguments[3];
|
|
439
447
|
const update = options && upsertUpdate(options);
|
|
440
|
-
if (
|
|
448
|
+
if (update) {
|
|
441
449
|
ctx.edits.push(replaced(options, [original(update)]));
|
|
442
450
|
}
|
|
443
451
|
}
|
|
452
|
+
/** Whether `keys` is the old conflict map, `{ sku: true }`: an argument of its own, not yet a query naming `$conflict`. */
|
|
453
|
+
function isConflictKeys(keys, ctx) {
|
|
454
|
+
return !ts.isSpreadElement(keys) && !ctx.checker.getTypeAtLocation(keys).getProperty('$conflict');
|
|
455
|
+
}
|
|
444
456
|
/** The value of options holding `update` alone, `{ update: value }` or `{ update }`. */
|
|
445
457
|
function upsertUpdate(options) {
|
|
446
458
|
const literal = objectLiteral(options);
|