uql-codemod 0.50.0 → 0.50.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.
package/README.md CHANGED
@@ -25,13 +25,13 @@ Only `--flag=value` is read, and anything unrecognised is an error. It exits `0`
25
25
  ## What it rewrites
26
26
 
27
27
  - `virtual` to `computed` (on `@Field` and in `defineEntity`'s `fields`), a check's `expression` and a filter's `condition` to `where` (`@Entity`, `defineEntity`, `@Filter`), `raw('...')` to the `sql` tagged template, and a second argument onto `.as()`.
28
- - A column's options to its builder, last, after the rewrites above: `@Id()` and `@Field({ type: String, nullable: true, length: 80 })` to `@Field(f.number().id())` and `@Field(f.string().nullable().length(80))`, a NOT NULL column losing its `nullable: false`, `computed` to `@Computed((row) => f.string().as(...))` or `@Computed((row) => row.books.count())`, and a schema's `fields` likewise; a schema's other options to the sections chained after it, and its `aggregates` to `.relations()`' second callback. A schema column's foreign key becomes a relation: its `references` is dropped where a relation joins on the column (by `(order) => order.customerId`, by `[{ local, foreign }]` pairs, or as the ORM infers the one column naming its target, written into it), its `onDelete` moved into that relation, and where none joins, a `manyToOne` named as the column less its `Id` is written for it: `customer: () => rel.manyToOne(Customer, { references: (order) => order.customerId })`. Then each index, check and trigger entry, in `@Entity` and a schema, goes to the helper its callback is handed: `(post, index) => [index([post.title], 'by_title')]`.
28
+ - A column's options to its builder, last, after the rewrites above: `@Id()` and `@Field({ type: String, nullable: true, length: 80 })` to `@Field(f.number().id())` and `@Field(f.string().nullable().length(80))`, a NOT NULL column losing its `nullable: false`, `computed` to `@Computed((row) => f.string().as(...))` or `@Computed((row) => row.books.count())`, and a defined entity's `fields` likewise; its other options to the sections chained after it, and its `aggregates` to `.relations()`' second callback. A defined column's foreign key becomes a relation: its `references` is dropped where a relation joins on the column (by `(order) => order.customerId`, by `[{ local, foreign }]` pairs, or as the ORM infers the one column naming its target, written into it), its `onDelete` moved into that relation, and where none joins, a `manyToOne` named as the column less its `Id` is written for it: `customer: () => rel.manyToOne(Customer, { references: (order) => order.customerId })`. Then each index, check and trigger entry, in `@Entity` and `defineEntity`, goes to the helper its callback is handed: `(post, index) => [index([post.title], 'by_title')]`.
29
29
  - A migration's columns to the same methods: `table.string('email', { length: 80, nullable: true })` to `table.string('email').length(80).nullable()`, `.defaultValue()` to `.default()`, `.primaryKey()` to `.id()`, `.computed()` to `.as()`, and `.notNullable()` dropped.
30
30
  - `@Index`, `@Trigger` and `@Filter` into `@Entity`'s `indexes`, `triggers` and `filters`, each decorator's comments kept above its entry; a class with no `@Entity` becomes `@Entity({ abstract: true, ... })`, a base that is never a table.
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 schema it declares, whole or not at all: `export const Post = defineEntity({ name: 'Post', fields })` with its sections chained, as a schema'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 a derived 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.
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 a derived 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`.
@@ -51,10 +51,10 @@ Reported with what to do, never guessed:
51
51
  - An `$exclude` it cannot read, or one beside a `$select`: merge it in, each field as `field: false`.
52
52
  - A to-one onto a composite key: declare a column per key and pair each with it in `references`.
53
53
  - A to-one whose `<relation>Id` is not a declared column: an undecorated property, or a `defineEntity` whose `fields` leave it out.
54
- - A class `defineEntity` registers that a schema cannot declare as it is: one that extends another class, has a method no hook runs or is decorated as well; options it cannot read, or a column or section its chain cannot write; a class registered in another file, or an entity that is no class declared in the file. It is left as written, each reason reported.
55
- - A schema column's foreign key no relation can take: one whose relation name (the column less its `Id`) is taken or cannot be read off it, a key with no type of its own (`f.references(() => Order)`), or an `onDelete` its relation contradicts.
54
+ - A class `defineEntity` registers that `defineEntity({ ... })` cannot declare as it is: one that extends another class, has a method no hook runs or is decorated as well; options it cannot read, or a column or section its chain cannot write; a class registered in another file, or an entity that is no class declared in the file. It is left as written, each reason reported.
55
+ - A defined column's foreign key no relation can take: one whose relation name (the column less its `Id`) is taken or cannot be read off it, a key with no type of its own (`f.references(() => Order)`), or an `onDelete` its relation contradicts.
56
56
  - A definer on a decorated class (`defineIndex(Post, ...)`), and `@Entity`'s `fields`, `relations`, `hooks` and `extends`: each is a decorator, or the class's own `extends`, now.
57
- - A property typed more narrowly than its schema derives, as a note: a `Json<T>` column wants a Standard Schema, a string-literal union `enum`.
57
+ - A property typed more narrowly than `defineEntity` derives, as a note: a `Json<T>` column wants a Standard Schema, a string-literal union `enum`.
58
58
  - A partial-index `where` string that interpolates, since `sql` would bind what the template spliced in.
59
59
  - A JSON index entry whose `path` is not a string literal, or that takes an option besides `path`, `type` and `length`.
60
60
  - 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.
@@ -8,7 +8,7 @@ export declare function isBuilder(node: ts.Expression): boolean;
8
8
  /** The names a builder writes, which the file must not bind otherwise. */
9
9
  export declare const BUILDER_NAMES: ReadonlySet<string>;
10
10
  /**
11
- * Writes entity definitions as column builders, `@Field(f.string().nullable())`, and a schema as its columns and a
11
+ * Writes entity definitions as column builders, `@Field(f.string().nullable())`, and a defined entity as its columns and a
12
12
  * chain of sections. Read off the text the other rewrites produced, so it only moves what is written; whatever it
13
13
  * cannot write faithfully is left as it is and reported.
14
14
  */
@@ -17,10 +17,10 @@ export declare function toBuilders(fileName: string, text: string, { locate, tak
17
17
  readonly taken: ReadonlySet<string>;
18
18
  }): BuildersResult;
19
19
  /**
20
- * A schema's statement, `const X = defineEntity({...})`, its call written as a chain and the builders it writes, or why
20
+ * A defined entity's statement, `const X = defineEntity({...})`, its call written as a chain and the builders it writes, or why
21
21
  * it cannot be.
22
22
  */
23
- export declare function chainedSchema(text: string): {
23
+ export declare function chainedEntity(text: string): {
24
24
  readonly text: string;
25
25
  readonly builders: ReadonlySet<string>;
26
26
  } | {
package/dist/builders.js CHANGED
@@ -1,11 +1,11 @@
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 { FOREIGN_KEY_IN_DEFINED, NO_KEY_TYPE, nameClash, ON_DELETE_ON_RELATION } from './definedMessages.js';
5
+ import { CHAINED_OPTIONS, FIRST_CALL_OPTIONS } from './definedOptions.js';
4
6
  import { applyEdits, editedText, replaced, replacingUses, reparsedAgain, unedited, } from './edits.js';
5
7
  import { importEdits } from './imports.js';
6
8
  import { isCallback, paramFor } from './keyMaps.js';
7
- import { FOREIGN_KEY_IN_SCHEMA, NO_KEY_TYPE, nameClash, ON_DELETE_ON_RELATION } from './schemaMessages.js';
8
- import { CHAINED_OPTIONS, FIRST_CALL_OPTIONS } from './schemaOptions.js';
9
9
  import { chainOf, decoratorName, decoratorsOf, FIELD_DECORATORS, identifierText, identifierUses, indentOf, indentUnit, keyOf, objectLiteral, objectProperties, optionsOf, propertiesByKey, propertyAssignment, propertyKey, propertyValue, } from './syntax.js';
10
10
  /** The column type each constructor implies, and the builder it is now. */
11
11
  const CONSTRUCTORS = {
@@ -232,9 +232,9 @@ function computedDecorator(call, options, computed, findings, used) {
232
232
  export function isBuilder(node) {
233
233
  return identifierText(chainOf(node).root) === 'f';
234
234
  }
235
- /** The options a schema's first call keeps. */
235
+ /** The options a defined entity's first call keeps. */
236
236
  const FIRST_CALL = new Set(FIRST_CALL_OPTIONS);
237
- /** Every key a schema's `defineEntity` options may state, as its first call or as a section. */
237
+ /** Every key `defineEntity`'s options may state, as its first call or as a section. */
238
238
  const SCHEMA_KEYS = new Set([
239
239
  ...FIRST_CALL,
240
240
  ...CHAINED_OPTIONS,
@@ -242,14 +242,14 @@ const SCHEMA_KEYS = new Set([
242
242
  'aggregates',
243
243
  ]);
244
244
  /** `defineEntity({ fields: {...}, relations, ... })` as its first call and a chain of sections. */
245
- function schemaEdits(call, findings, used) {
245
+ function definedEdits(call, findings, used) {
246
246
  const options = objectLiteral(call.arguments[0]);
247
247
  const fields = options && objectLiteral(propertyValue(options, 'fields'));
248
248
  const chained = options?.properties.some((prop) => !FIRST_CALL.has(keyOf(prop)));
249
- if (!options || !fields || (!chained && fields.properties.every((prop) => isSchemaColumn(prop)))) {
249
+ if (!options || !fields || (!chained && fields.properties.every((prop) => isDefinedColumn(prop)))) {
250
250
  return [];
251
251
  }
252
- const param = schemaParam(call);
252
+ const param = definedParam(call);
253
253
  const unknown = options.properties.filter((prop) => !SCHEMA_KEYS.has(keyOf(prop)));
254
254
  if (unknown.length) {
255
255
  findings.push({
@@ -263,7 +263,7 @@ function schemaEdits(call, findings, used) {
263
263
  const aggregates = propertyValue(options, 'aggregates');
264
264
  const read = fields.properties.map((prop) => ({
265
265
  prop,
266
- column: ts.isPropertyAssignment(prop) ? schemaColumn(prop.initializer) : undefined,
266
+ column: ts.isPropertyAssignment(prop) ? definedColumn(prop.initializer) : undefined,
267
267
  }));
268
268
  const { joins, inferred } = joinsOf(relations, targetsOf(read), param);
269
269
  const targets = {
@@ -324,14 +324,14 @@ function schemaEdits(call, findings, used) {
324
324
  replaced(call, `${call.expression.getText()}(${literal(options, kept)})${sections.map((it) => `\n${indent}${unit}${it}`).join('')}`),
325
325
  ];
326
326
  }
327
- /** The parameter a schema's callbacks are named after: its `name` where that is a literal, else its variable's. */
328
- function schemaParam(call) {
327
+ /** The parameter a defined entity's callbacks are named after: its `name` where that is a literal, else its variable's. */
328
+ function definedParam(call) {
329
329
  const options = objectLiteral(call.arguments[0]);
330
330
  const name = options && /^'(.+)'$/.exec(propertyValue(options, 'name')?.getText() ?? '')?.[1];
331
331
  return paramFor(name ?? identifierText(variableOf(call)));
332
332
  }
333
- /** A schema column's builder off its options or chain, its `references` and `onDelete` taken out to move to a relation. */
334
- function schemaColumn(node) {
333
+ /** A defined column's builder off its options or chain, its `references` and `onDelete` taken out to move to a relation. */
334
+ function definedColumn(node) {
335
335
  if (isBuilder(node)) {
336
336
  const methods = chainMethods(node);
337
337
  const references = methods.get('references');
@@ -340,7 +340,7 @@ function schemaColumn(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 : FOREIGN_KEY_IN_SCHEMA };
343
+ return { message: target ? NO_KEY_TYPE : FOREIGN_KEY_IN_DEFINED };
344
344
  }
345
345
  const onDelete = methods.get('onDelete');
346
346
  const cut = [references, ...(onDelete ? [onDelete] : [])].map((method) => ({
@@ -360,7 +360,7 @@ function schemaColumn(node) {
360
360
  const references = options.get('references');
361
361
  const target = references && targetOf(references);
362
362
  if (references && !target) {
363
- return { message: FOREIGN_KEY_IN_SCHEMA };
363
+ return { message: FOREIGN_KEY_IN_DEFINED };
364
364
  }
365
365
  if (target && !options.has('type')) {
366
366
  return { message: NO_KEY_TYPE };
@@ -381,7 +381,7 @@ function targetOf(getter) {
381
381
  function targetsOf(read) {
382
382
  return new Map(read.flatMap(({ prop, column }) => column && 'key' in column && column.key ? [[prop.name?.getText(), column.key.target]] : []));
383
383
  }
384
- /** The schema's columns' foreign keys claimed in turn, the problem of each column by its name node. */
384
+ /** The defined entity's columns' foreign keys claimed in turn, the problem of each column by its name node. */
385
385
  function claimColumns(read, start, targets) {
386
386
  const problems = new Map();
387
387
  const claims = read.reduce((state, { prop, column }) => {
@@ -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: FOREIGN_KEY_IN_SCHEMA };
411
+ return { claims, problem: FOREIGN_KEY_IN_DEFINED };
412
412
  }
413
413
  const written = [
414
414
  `references: (${targets.param}) => ${targets.param}.${key}`,
@@ -437,7 +437,7 @@ function movedOnDelete(relation, onDelete, claims) {
437
437
  ? { claims }
438
438
  : { claims: { ...claims, options: new Map(claims.options).set(relation, [...current, written]) } };
439
439
  }
440
- /** The relation calls a schema's `relations` returns, one per relation. */
440
+ /** The relation calls a defined entity's `relations` returns, one per relation. */
441
441
  function relationCalls(relations) {
442
442
  return objectProperties(returnedLiteral(relations)).flatMap((prop) => {
443
443
  const relation = ts.isArrowFunction(prop.initializer) ? prop.initializer.body : undefined;
@@ -543,8 +543,8 @@ function appendedEntries(literal, entries, base) {
543
543
  const at = last.getEnd() - base;
544
544
  return { start: at, end: at, text: entries.map((entry) => `${separator}${entry}`).join('') };
545
545
  }
546
- /** Whether a schema column is a builder naming no foreign key, which a schema's `fields` keep as they are. */
547
- function isSchemaColumn(prop) {
546
+ /** Whether a defined column is a builder naming no foreign key, which a defined entity's `fields` keep as they are. */
547
+ function isDefinedColumn(prop) {
548
548
  return (ts.isPropertyAssignment(prop) && isBuilder(prop.initializer) && !chainMethods(prop.initializer).has('references'));
549
549
  }
550
550
  /** Each method a builder chain calls, by name. */
@@ -680,7 +680,7 @@ function entityEdits(cls, findings) {
680
680
  });
681
681
  });
682
682
  }
683
- /** A schema's computed map as what its section returns: each a column `as()` its SQL. */
683
+ /** A defined entity's computed map as what its section returns: each a column `as()` its SQL. */
684
684
  function computedSection(value, param) {
685
685
  const map = objectLiteral(value);
686
686
  const entries = map?.properties.map((prop) => {
@@ -714,7 +714,7 @@ export const BUILDER_NAMES = new Set(['f', 'Computed']);
714
714
  /** Whether the file declares anything this pass rewrites. */
715
715
  const REWRITES = /@(?:Field|Id)\(|\bdefineEntity\(\s*\{|@Entity\(\s*\{/;
716
716
  /**
717
- * Writes entity definitions as column builders, `@Field(f.string().nullable())`, and a schema as its columns and a
717
+ * Writes entity definitions as column builders, `@Field(f.string().nullable())`, and a defined entity as its columns and a
718
718
  * chain of sections. Read off the text the other rewrites produced, so it only moves what is written; whatever it
719
719
  * cannot write faithfully is left as it is and reported.
720
720
  */
@@ -734,9 +734,9 @@ export function toBuilders(fileName, text, { locate, taken }) {
734
734
  edits.push(...entityEdits(node, findings));
735
735
  }
736
736
  if (ts.isCallExpression(node) && identifierText(node.expression) === 'defineEntity') {
737
- const schema = schemaEdits(node, findings, used);
738
- if (schema.length) {
739
- edits.push(...schema);
737
+ const defined = definedEdits(node, findings, used);
738
+ if (defined.length) {
739
+ edits.push(...defined);
740
740
  return;
741
741
  }
742
742
  }
@@ -761,17 +761,17 @@ export function toBuilders(fileName, text, { locate, taken }) {
761
761
  };
762
762
  }
763
763
  /**
764
- * A schema's statement, `const X = defineEntity({...})`, its call written as a chain and the builders it writes, or why
764
+ * A defined entity's statement, `const X = defineEntity({...})`, its call written as a chain and the builders it writes, or why
765
765
  * it cannot be.
766
766
  */
767
- export function chainedSchema(text) {
768
- const source = ts.createSourceFile('/schema.ts', text, ts.ScriptTarget.Latest, true);
767
+ export function chainedEntity(text) {
768
+ const source = ts.createSourceFile('/entity.ts', text, ts.ScriptTarget.Latest, true);
769
769
  const findings = [];
770
770
  const used = new Set();
771
771
  const edits = [];
772
772
  const visit = (node) => {
773
773
  if (ts.isCallExpression(node) && identifierText(node.expression) === 'defineEntity') {
774
- edits.push(...schemaEdits(node, findings, used));
774
+ edits.push(...definedEdits(node, findings, used));
775
775
  return;
776
776
  }
777
777
  ts.forEachChild(node, visit);
@@ -1,15 +1,15 @@
1
1
  import { type Pass } from './edits.js';
2
- export type SchemaResult = Pass & {
2
+ export type DefinedResult = Pass & {
3
3
  readonly unresolved: string[];
4
4
  readonly notes: string[];
5
5
  };
6
6
  /**
7
- * Declares each class `defineEntity(Class, options)` registered as the schema `defineEntity({ name, fields })` and its
7
+ * Declares each class `defineEntity(Class, options)` registered as `defineEntity({ name, fields })` and its
8
8
  * chain of sections, folding the string-keyed definers of the same class in. Read off the text the other rewrites
9
9
  * produced, whose options already name members by callback, so it only moves what is written. An entity it cannot
10
10
  * declare faithfully, `taken` naming a builder it writes among them, is left as written and reported.
11
11
  */
12
- export declare function toSchemaEntities(fileName: string, text: string, { locate, taken }: {
12
+ export declare function toDefinedEntities(fileName: string, text: string, { locate, taken }: {
13
13
  readonly locate?: (at: number) => string;
14
14
  readonly taken: ReadonlySet<string>;
15
- }): SchemaResult;
15
+ }): DefinedResult;
@@ -1,14 +1,14 @@
1
1
  import ts from 'typescript';
2
- import { chainedSchema } from './builders.js';
2
+ import { chainedEntity } from './builders.js';
3
3
  import { formatFindings, locationAt } from './context.js';
4
+ import { nameClash } from './definedMessages.js';
5
+ import { KEPT_OPTIONS } from './definedOptions.js';
4
6
  import { CLASS_DEFINERS, definerCall } from './definers.js';
5
7
  import { editedText, removeStatement, replaced, replacingUses, reparsedAgain, unedited, } from './edits.js';
6
8
  import { importEdits } from './imports.js';
7
9
  import { memberKey, paramFor, quoted } from './keyMaps.js';
8
- import { nameClash } from './schemaMessages.js';
9
- import { KEPT_OPTIONS } from './schemaOptions.js';
10
10
  import { arrayElements, decoratorsOf, freeName, hasModifier, identifierText, identifierUses, indentOf, indentUnit, isNamedProperty, objectLiteral, objectProperties, propertyKey, propertyValue, } from './syntax.js';
11
- /** Imports a converted file may no longer read: the definers folded in, and the brands a schema derives. */
11
+ /** Imports a converted file may no longer read: the definers folded in, and the brands a defined entity derives. */
12
12
  const FOLDED_IMPORTS = new Set([
13
13
  ...Object.keys(CLASS_DEFINERS).filter((name) => name !== 'defineEntity'),
14
14
  'idKey',
@@ -49,12 +49,12 @@ function readsAs(value, shape) {
49
49
  /** What a class-form relation said that a `rel` builder says by its name and first argument. */
50
50
  const RELATION_SHAPE = new Set(['cardinality', 'entity']);
51
51
  /**
52
- * Declares each class `defineEntity(Class, options)` registered as the schema `defineEntity({ name, fields })` and its
52
+ * Declares each class `defineEntity(Class, options)` registered as `defineEntity({ name, fields })` and its
53
53
  * chain of sections, folding the string-keyed definers of the same class in. Read off the text the other rewrites
54
54
  * produced, whose options already name members by callback, so it only moves what is written. An entity it cannot
55
55
  * declare faithfully, `taken` naming a builder it writes among them, is left as written and reported.
56
56
  */
57
- export function toSchemaEntities(fileName, text, { locate, taken }) {
57
+ export function toDefinedEntities(fileName, text, { locate, taken }) {
58
58
  if (!CLASS_FORM_CALL.test(text)) {
59
59
  return { ...unedited(text), unresolved: [], notes: [] };
60
60
  }
@@ -82,12 +82,12 @@ export function toSchemaEntities(fileName, text, { locate, taken }) {
82
82
  });
83
83
  continue;
84
84
  }
85
- const schema = schemaOf(name, cls, calls, entityCall, unit);
86
- if ('message' in schema) {
87
- unresolved.push(schema);
85
+ const defined = definedOf(name, cls, calls, entityCall, unit);
86
+ if ('message' in defined) {
87
+ unresolved.push(defined);
88
88
  continue;
89
89
  }
90
- const chained = chainedSchema(schema.text);
90
+ const chained = chainedEntity(defined.text);
91
91
  if (!('text' in chained)) {
92
92
  unresolved.push(...chained.reasons.map((reason) => ({ node: entityCall, message: `'${name}' is left as written: ${reason}` })));
93
93
  continue;
@@ -99,7 +99,7 @@ export function toSchemaEntities(fileName, text, { locate, taken }) {
99
99
  }
100
100
  for (const builder of chained.builders)
101
101
  written.add(builder);
102
- notes.push(...schema.notes);
102
+ notes.push(...defined.notes);
103
103
  edits.push(removeStatement(cls));
104
104
  for (const { call } of calls) {
105
105
  edits.push(call === entityCall ? replaced(call.parent, indented(chained.text, call.parent)) : removeStatement(call.parent));
@@ -113,7 +113,7 @@ export function toSchemaEntities(fileName, text, { locate, taken }) {
113
113
  notes: formatFindings(notes, located),
114
114
  };
115
115
  }
116
- /** The class a `defineEntity(Class, { extends })` names as its base, which its schema's const reads where it is declared. */
116
+ /** The class a `defineEntity(Class, { extends })` names as its base, which its defined const reads where it is declared. */
117
117
  function baseName(entityCall) {
118
118
  const options = objectLiteral(entityCall.arguments[1]);
119
119
  return identifierText(options && propertyValue(options, 'extends'));
@@ -140,7 +140,7 @@ function collectRegistrations(source, unresolved) {
140
140
  registrations.set(name, registration);
141
141
  }
142
142
  else if (read.definer === 'defineEntity') {
143
- unresolved.push({ node, message: 'defineEntity takes a schema now: defineEntity({ name, fields, ... })' });
143
+ unresolved.push({ node, message: 'defineEntity takes options alone now: defineEntity({ name, fields, ... })' });
144
144
  }
145
145
  else {
146
146
  unresolved.push({ node, message: removedDefiner(read.definer, textOf(read.entity)) });
@@ -155,7 +155,7 @@ function collectRegistrations(source, unresolved) {
155
155
  function removedDefiner(definer, entity) {
156
156
  return `${definer} was removed: register '${entity}' with defineEntity in this file, or decorate it (${CLASS_DEFINERS[definer]})`;
157
157
  }
158
- function schemaOf(name, cls, calls, entityCall, unit) {
158
+ function definedOf(name, cls, calls, entityCall, unit) {
159
159
  const refused = (message) => ({ node: entityCall, message });
160
160
  const options = objectLiteral(entityCall.arguments[1]) ?? (entityCall.arguments[1] ? undefined : emptyOptions());
161
161
  if (!options || options.properties.some((prop) => !ts.isPropertyAssignment(prop))) {
@@ -165,15 +165,15 @@ function schemaOf(name, cls, calls, entityCall, unit) {
165
165
  return refused(`'${name}' extends a class: declare the base with defineEntity too and pass it as 'extends'`);
166
166
  }
167
167
  if (decoratorsOf(cls).length) {
168
- return refused(`'${name}' is decorated as well: declare it one way, decorators or a schema`);
168
+ return refused(`'${name}' is decorated as well: declare it one way, decorators or defineEntity`);
169
169
  }
170
170
  const exported = hasModifier(cls, ts.SyntaxKind.ExportKeyword);
171
171
  if (hasModifier(cls, ts.SyntaxKind.DefaultKeyword)) {
172
- return refused(`'${name}' is a default export: export the schema's const by name instead`);
172
+ return refused(`'${name}' is a default export: export its defineEntity const by name instead`);
173
173
  }
174
174
  const unread = objectProperties(options).find(({ name: key }) => !READ_OPTIONS.has(propertyKey(key)));
175
175
  if (unread) {
176
- return refused(`'${textOf(unread.name)}' has no place in a schema: move it by hand`);
176
+ return refused(`'${textOf(unread.name)}' has no place in defineEntity: move it by hand`);
177
177
  }
178
178
  for (const [option, shape] of Object.entries(ENTRY_OPTIONS)) {
179
179
  const value = propertyValue(options, option);
@@ -421,7 +421,7 @@ function gather({ call, definer, key, options: third, rest }, options, gathered,
421
421
  return undefined;
422
422
  }
423
423
  }
424
- /** Whether a field's options compute it, which a schema declares under `computed`. */
424
+ /** Whether a field's options compute it, which a defined entity declares under `computed`. */
425
425
  function isComputed(options) {
426
426
  const literal = objectLiteral(options);
427
427
  return !!literal && propertyValue(literal, 'computed') !== undefined;
@@ -520,7 +520,7 @@ function hookFunction(method, preferred) {
520
520
  }
521
521
  /**
522
522
  * `total: () => rel.items.count()` from a field computed over a relation, `{ computed: (counted) => counted.items.count() }`,
523
- * which a schema declares among its aggregates; nothing for a field reading no relation, and why where it cannot move.
523
+ * which a defined entity declares among its aggregates; nothing for a field reading no relation, and why where it cannot move.
524
524
  */
525
525
  function aggregateEntry(label, key, options, relations, rel) {
526
526
  const literal = objectLiteral(options);
@@ -546,7 +546,7 @@ function aggregateEntry(label, key, options, relations, rel) {
546
546
  function substituted(body, uses, replacement) {
547
547
  return rebased(replacingUses(body, uses, replacement), body);
548
548
  }
549
- /** Notes each property the class typed more narrowly than its schema derives, with the option that keeps it. */
549
+ /** Notes each property the class typed more narrowly than defineEntity derives, with the option that keeps it. */
550
550
  function looserTypes(cls, options) {
551
551
  const fields = objectLiteral(propertyValue(options, 'fields'));
552
552
  return cls.members.flatMap((member) => {
@@ -1,8 +1,8 @@
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
- /** Why a schema column's foreign key is refused: no relation joins on it, and none can be written for it. */
4
- export declare const FOREIGN_KEY_IN_SCHEMA = "references another entity, and no relation can be named for it: declare one in '.relations()', as 'rel.manyToOne(Target, { references: (row) => row.key })'";
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 FOREIGN_KEY_IN_DEFINED = "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
- /** Why a foreign key with no type is refused: a schema column states its own, which only its target's key implied. */
7
+ /** Why a foreign key with no type is refused: a defined column states its own, which only its target's key implied. */
8
8
  export declare const NO_KEY_TYPE = "takes its type from its target: write its builder, f.number() and so on, as its key's type";
@@ -1,8 +1,8 @@
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
- /** Why a schema column's foreign key is refused: no relation joins on it, and none can be written for it. */
4
- export const FOREIGN_KEY_IN_SCHEMA = "references another entity, and no relation can be named for it: declare one in '.relations()', as 'rel.manyToOne(Target, { references: (row) => row.key })'";
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 FOREIGN_KEY_IN_DEFINED = "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
- /** Why a foreign key with no type is refused: a schema column states its own, which only its target's key implied. */
7
+ /** Why a foreign key with no type is refused: a defined column states its own, which only its target's key implied. */
8
8
  export const NO_KEY_TYPE = "takes its type from its target: write its builder, f.number() and so on, as its key's type";
@@ -0,0 +1,6 @@
1
+ /** The options a defined entity's first call keeps, each as it is written there. */
2
+ export declare const FIRST_CALL_OPTIONS: readonly ["name", "table", "schema", "abstract", "extends", "fields"];
3
+ /** The sections a defined entity chains after its first call, in the order it chains them, `relations` last. */
4
+ export declare const CHAINED_OPTIONS: readonly ["computed", "indexes", "checks", "triggers", "filters", "hooks"];
5
+ /** The options a class-form `defineEntity` took that a defined entity keeps as they are, and that incremental definers add to. */
6
+ export declare const KEPT_OPTIONS: readonly ["indexes", "checks", "filters", "triggers"];
@@ -0,0 +1,6 @@
1
+ /** The options a defined entity's first call keeps, each as it is written there. */
2
+ export const FIRST_CALL_OPTIONS = ['name', 'table', 'schema', 'abstract', 'extends', 'fields'];
3
+ /** The sections a defined entity chains after its first call, in the order it chains them, `relations` last. */
4
+ export const CHAINED_OPTIONS = ['computed', 'indexes', 'checks', 'triggers', 'filters', 'hooks'];
5
+ /** The options a class-form `defineEntity` took that a defined entity keeps as they are, and that incremental definers add to. */
6
+ export const KEPT_OPTIONS = ['indexes', 'checks', 'filters', 'triggers'];
@@ -20,5 +20,5 @@ export type DefinerCall = {
20
20
  readonly options: ts.Expression | undefined;
21
21
  readonly rest: readonly ts.Expression[];
22
22
  };
23
- /** `call` read as a class-form definer, or nothing where it is another call, the schema form included. */
23
+ /** `call` read as a class-form definer, or nothing where it is another call, the defined form included. */
24
24
  export declare function definerCall(call: ts.CallExpression): DefinerCall | undefined;
package/dist/definers.js CHANGED
@@ -20,7 +20,7 @@ const KEYED_DEFINERS = new Set([
20
20
  'defineHook',
21
21
  'defineFilter',
22
22
  ]);
23
- /** `call` read as a class-form definer, or nothing where it is another call, the schema form included. */
23
+ /** `call` read as a class-form definer, or nothing where it is another call, the defined form included. */
24
24
  export function definerCall(call) {
25
25
  const definer = identifierText(call.expression);
26
26
  const [entity, ...rest] = call.arguments;
@@ -10,14 +10,14 @@ export type FileResult = {
10
10
  readonly notes: readonly string[];
11
11
  };
12
12
  /**
13
- * Rewrites one file across every breaking change: the earlier ones off the checker, then column builders and schema
13
+ * Rewrites one file across every breaking change: the earlier ones off the checker, then column builders and defineEntity
14
14
  * chains off the text those produced, so neither pass edits what the other writes.
15
15
  */
16
16
  export declare function transformFile(source: ts.SourceFile, checker: ts.TypeChecker, options?: {
17
17
  readonly strictNullChecks: boolean;
18
18
  }): FileResult;
19
19
  /**
20
- * The rewrites the checker reads types for, up to the schema entities a class-form registration declares. The
20
+ * The rewrites the checker reads types for, up to the defined entities a class-form registration declares. The
21
21
  * builder's and querier's methods are known by name, so they are read only in a file importing uql-orm, and `expr`
22
22
  * only where it is uql's.
23
23
  */
package/dist/transform.js CHANGED
@@ -3,6 +3,8 @@ import { BUILDER_NAMES, isBuilder, toBuilders } from './builders.js';
3
3
  import { foldClassDecorators } from './classOptions.js';
4
4
  import { rewriteColumnRefs } from './columnRefs.js';
5
5
  import { createContext, instanceTypeOf, locationAt, memberNames, ownerOf, } from './context.js';
6
+ import { toDefinedEntities } from './definedEntity.js';
7
+ import { nameClash } from './definedMessages.js';
6
8
  import { definerCall } from './definers.js';
7
9
  import { appended, editedText, followed, inserted, original, replaced, unedited, } from './edits.js';
8
10
  import { columnExpressions, handNamedColumns, sqlTag, sqlWhereEdit, templateOf } from './entitySql.js';
@@ -11,8 +13,6 @@ import { rewriteBuilderIndexLengths, rewriteJsonIndexEntries } from './jsonIndex
11
13
  import { entityGetterTarget, fieldKeysEdits, isCallback, keyListEdits, mappedByEdits, memberAccess, paramFor, quoted, referencesEdits, } from './keyMaps.js';
12
14
  import { toMigrationBuilders } from './migrationBuilders.js';
13
15
  import { relationTargetFor } from './relationTarget.js';
14
- import { toSchemaEntities } from './schemaEntity.js';
15
- import { nameClash } from './schemaMessages.js';
16
16
  import { statementTemplate } from './statementSql.js';
17
17
  import { arrayElements, decoratorName, decoratorsOf, FIELD_DECORATORS, identifierText, indentOf, namedProperty, objectLiteral, objectProperties, optionsOf, propertyKey, propertyValue, } from './syntax.js';
18
18
  const RELATION_DECORATORS = new Set(['OneToOne', 'ManyToOne', 'OneToMany', 'ManyToMany']);
@@ -682,7 +682,7 @@ function rewriteRawCall(node, ctx) {
682
682
  }
683
683
  }
684
684
  /**
685
- * Rewrites one file across every breaking change: the earlier ones off the checker, then column builders and schema
685
+ * Rewrites one file across every breaking change: the earlier ones off the checker, then column builders and defineEntity
686
686
  * chains off the text those produced, so neither pass edits what the other writes.
687
687
  */
688
688
  export function transformFile(source, checker, options = { strictNullChecks: true }) {
@@ -700,7 +700,7 @@ export function transformFile(source, checker, options = { strictNullChecks: tru
700
700
  };
701
701
  }
702
702
  /**
703
- * The rewrites the checker reads types for, up to the schema entities a class-form registration declares. The
703
+ * The rewrites the checker reads types for, up to the defined entities a class-form registration declares. The
704
704
  * builder's and querier's methods are known by name, so they are read only in a file importing uql-orm, and `expr`
705
705
  * only where it is uql's.
706
706
  */
@@ -751,13 +751,13 @@ export function rewriteLegacy(source, checker, options, taken = foreignNames(sou
751
751
  rewriteImports(dead, ctx);
752
752
  reportRemovedExports(ctx);
753
753
  const edited = editedText(source.getFullText(), ctx.edits);
754
- const schema = toSchemaEntities(source.fileName, edited.text, {
754
+ const defined = toDefinedEntities(source.fileName, edited.text, {
755
755
  locate: (at) => locationAt(source, edited.sourceOffset(at)),
756
756
  taken,
757
757
  });
758
- ctx.unresolved.push(...schema.unresolved);
759
- ctx.notes.push(...schema.notes);
760
- return { ...fileResult(ctx, schema.text), sourceOffset: followed(edited, schema).sourceOffset };
758
+ ctx.unresolved.push(...defined.unresolved);
759
+ ctx.notes.push(...defined.notes);
760
+ return { ...fileResult(ctx, defined.text), sourceOffset: followed(edited, defined).sourceOffset };
761
761
  }
762
762
  function fileResult({ source, unresolved, notes }, text) {
763
763
  return { fileName: source.fileName, text, changed: text !== source.getFullText(), unresolved, notes };
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "uql-codemod",
3
3
  "description": "Upgrade uql-orm across versions that introduce breaking changes",
4
4
  "license": "MIT",
5
- "version": "0.50.0",
5
+ "version": "0.50.1",
6
6
  "type": "module",
7
7
  "engines": {
8
8
  "node": ">=22.18"
@@ -1,6 +0,0 @@
1
- /** The options a schema's first call keeps, each as it is written there. */
2
- export declare const FIRST_CALL_OPTIONS: readonly ["name", "table", "schema", "abstract", "extends", "fields"];
3
- /** The sections a schema chains after its first call, in the order it chains them, `relations` last. */
4
- export declare const CHAINED_OPTIONS: readonly ["computed", "indexes", "checks", "triggers", "filters", "hooks"];
5
- /** The options a class-form `defineEntity` took that a schema keeps as they are, and that incremental definers add to. */
6
- export declare const KEPT_OPTIONS: readonly ["indexes", "checks", "filters", "triggers"];
@@ -1,6 +0,0 @@
1
- /** The options a schema's first call keeps, each as it is written there. */
2
- export const FIRST_CALL_OPTIONS = ['name', 'table', 'schema', 'abstract', 'extends', 'fields'];
3
- /** The sections a schema chains after its first call, in the order it chains them, `relations` last. */
4
- export const CHAINED_OPTIONS = ['computed', 'indexes', 'checks', 'triggers', 'filters', 'hooks'];
5
- /** The options a class-form `defineEntity` took that a schema keeps as they are, and that incremental definers add to. */
6
- export const KEPT_OPTIONS = ['indexes', 'checks', 'filters', 'triggers'];