Skip to content
NewThe SQL tag: a function called without parentheses4 min read

Codemod

uql-codemod rewrites your code across UQL’s mechanical breaking changes and reports what it cannot decide. Run it first when you upgrade, then tsc, then the upgrade guide for anything else.

Terminal window
npx uql-codemod --dry-run
needs a decision: src/entity/audit.ts:9: cannot infer 'type' for Json<Payload>
would change: src/entity/user.ts
...
23 file(s) would change
1 item(s) need a decision; see above

needs a decision: marks something it will not guess, and worth a look: a rewrite you may want to read. Drop --dry-run to write the files. Only the edited text changes; comments and formatting stay as you wrote them. --project=<path> picks another tsconfig.json, and --include=<a,b> limits the paths. It exits 0 when nothing is left, 1 when something is, and 2 when it could not start.

Everything it inserts is type-checked against the property it describes, so a wrong guess shows up as a tsc error.

Entities and relations:

  • A missing type on @Field/@Id, from the property’s declared type.
  • entity: () => X on a relation, and Relation<T> to T.
  • Members named by string to callbacks, in mappedBy, references, @Index, and the indexes and hooks of @Entity/defineEntity: @Index((post) => [post.title]).
  • references: (post) => post.authorId on each to-one, declaring the authorId column where it is missing.
  • | null on each nullable column’s property, except keys, relation aggregates and nullable: false fields.
  • The idKey brand on a key not called id, _id or uuid, and on every composite.
  • virtual to computed, and a check’s expression and a filter’s condition to where.

SQL and queries:

  • raw('SQL') to the tagged template, raw(fn, alias) to raw(fn).as(alias), and col('cost') to a ref: (product) => raw`${product.cost}` in a computed, refs(Item).cost in a statement.
  • A partial-index where string to raw, in entities and the migration builder, and the builder’s expr defaults: expr.now() to currentTimestamp, expr.uuid() to uuid, expr.raw('...') to raw`...` .
  • An aggregate’s $agg to $select, and fields named in it or in $text’s $fields to keys: { $sum: { amount: true } }.
  • An upsert’s { update: X } to X as its fourth argument, in upsertOne, upsertMany and a trigger’s upsertInto.
  • $lock: { wait: 'skip' } to { $wait: 'skip' }, or true for a lock that waits.
  • run and all to tags: all('SELECT * FROM t WHERE id = ?', [id]) to all`SELECT * FROM t WHERE id = ${id}` . A string built at run time goes in raw.text(...).

Imports and setup:

  • Renamed exports, with their imports and uses: QueryWhereMap, RelationKeyMap, FilterCondition, SqlMigrationModuleOptions, buildSqlQuerierMigrationModule, Sqlite3QuerierPool (to SqliteQuerierPool), the migration builder’s IMigrationBuilder, ITableBuilder and the like (to MigrationBuilder, TableBuilder), and each driver subclass (PgDialect, NeonQuerier, LibsqlQuerier, …) to its base class.
  • MongoQuerier, isMongoQuerier and MongoSchemaIntrospector, imported from uql-orm/mongodb.
  • Entries: uql-orm/maria, uql-orm/mongo and uql-orm/bunSql to uql-orm/mariadb, uql-orm/mongodb and uql-orm/bun-sql; uql-orm/type, uql-orm/dialect, uql-orm/entity, uql-orm/querier and uql-orm/namingStrategy to uql-orm.
  • Removes import 'reflect-metadata', and experimentalDecorators/emitDecoratorMetadata from tsconfig.json.
  • @Transactional(), @InjectQuerier(), @Log() and @Serialized(), which UQL does not have. Wrap a @Transactional() method’s body in pool.transaction() on the pool you choose.
  • Removed exports with no one-to-one replacement, such as setQuerierPool or AbstractPgQuerier, the internals the root no longer exports (AbstractSqlQuerier, fieldOf, …), the migration storages, and any import from uql-orm/util the root does not export: raw, refs and the rest it does move to uql-orm.
  • A col() whose entity it cannot tell: a raw built apart from its query, one under a relation filter, or a column no field maps. Read it off refs(Entity).
  • Values it cannot read statically: @Field(shared), a spread, a non-literal list.
  • A partial-index where template that interpolates, since raw would bind each interpolation as a value.
  • A run/all with values it cannot place safely, such as a non-literal list or SQL, or mixed ? and $n.
  • SQL in a definition that names a column by hand (worth a look:). Only a ref follows a rename: (user) => raw`lower(${user.email})` .
  • A to-one onto a composite key (declare a column per key and pair each in references), and a to-one whose <relation>Id is not a declared column.
  • A property whose type maps to no column, a key under a computed name, and a nullable column’s property with no written type.
  • A branded string id, written as type: String to keep your column. 'uuid' is a native column type and would need a migration.
  • target: esnext, which leaves decorators untransformed. Any other modern target works.