uql-codemod 0.36.0 → 0.38.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 +5 -3
- package/dist/edits.d.ts +1 -0
- package/dist/edits.js +3 -0
- package/dist/entitySql.d.ts +0 -6
- package/dist/entitySql.js +0 -13
- package/dist/keyMaps.d.ts +2 -0
- package/dist/keyMaps.js +7 -3
- package/dist/transform.js +74 -21
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -27,11 +27,12 @@ Only `--flag=value` is read, and anything unrecognised is an error. It exits `0`
|
|
|
27
27
|
- `type` on a `@Field`/`@Id` that has none, from the property's declared type; not beside `references`, whose column comes from the key it points at.
|
|
28
28
|
- `entity: () => X` on a bare relation decorator, and `Relation<T>` to `T`, its import going with the last use.
|
|
29
29
|
- `virtual` to `computed` (on `@Field` and in `defineEntity`'s `fields`), a check's `expression` and a filter's `condition` to `where` (`@Entity`/`defineEntity`, `@Filter`, `defineFilter`), `raw('sql')` to the tagged template, and a `raw` alias argument onto `.as()`.
|
|
30
|
-
- Members named by string to
|
|
31
|
-
-
|
|
30
|
+
- Members named by string to callbacks: `mappedBy: 'author'` to `(post) => post.author`, and `@Index`, `include`, `references` and `@Entity`/`defineEntity`'s `indexes` and `hooks` likewise: `@Index((post) => [post.title])`.
|
|
31
|
+
- `references: (post) => post.authorId` on a to-one (`@ManyToOne`, `@OneToOne`, or `defineEntity`/`defineRelation`) whose entity declares or inherits `authorId`, which it used to join by name.
|
|
32
|
+
- A partial-index `where` string to `raw`, importing it, on entities and in the migration builder.
|
|
32
33
|
- `col('cost')` to the ref it names, ``(product) => raw`${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.
|
|
33
34
|
- An aggregate's `$agg` to `$select`, and each field it or `$text`'s `$fields` names to a key: `{ $sum: { amount: true } }`.
|
|
34
|
-
- Renamed exports, at the import and every use: `QueryWhereMap` to `QueryWhere`, `RelationKeyMap` to `KeyMap`, `FilterCondition` to `FilterWhere`, and the removed driver classes to the one each extended: `PgDialect`/`NeonDialect`/`PgliteDialect` to `PostgresDialect`, `CrdbQuerier`/`NeonQuerier` to `PgQuerier`, `LibsqlQuerier`/`TursoQuerier` to `HranaQuerier`, `MySql2Dialect` to `MySqlDialect`, `MongodbNativeDialect` to `MongoDialect`, moving the import to the entry that exports it.
|
|
35
|
+
- Renamed exports, at the import and every use: `QueryWhereMap` to `QueryWhere`, `RelationKeyMap` to `KeyMap`, `FilterCondition` to `FilterWhere`, `SqlMigrationModuleOptions` to `MigrationModuleOptions`, `buildSqlQuerierMigrationModule` to `buildMigrationModule`, `QueryDialect` to `SqlQueryDialect`, `EngineFeatures` to `DialectFeatures`, `KnownMigratorDialect` to `DialectName` and `D1Preparer` to `D1Database`, 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`, moving the import to the entry that exports it.
|
|
35
36
|
- The `[idKey]?: '...'` brand, and its import, on a key not called `id`, `_id` or `uuid` and on every composite.
|
|
36
37
|
- `import 'reflect-metadata'`, and `experimentalDecorators`/`emitDecoratorMetadata` in `tsconfig.json`, comments and formatting kept.
|
|
37
38
|
|
|
@@ -43,6 +44,7 @@ Reported with what to do, never guessed:
|
|
|
43
44
|
- `@Log()`, `@Serialized()`, `@Transactional()` and `@InjectQuerier()`: a `@Transactional()` method becomes a `pool.transaction()` around its body, and only you know which pool.
|
|
44
45
|
- An export removed with no one-to-one replacement (`setQuerierPool`, `getQuerier`, `augmentWhere`, `AbstractPgQuerier`, ...).
|
|
45
46
|
- A value it cannot read: options passed as a variable or a spread, a list that is not a literal, a key under a computed name.
|
|
47
|
+
- A to-one onto a composite key whose columns the entity declares: pair each with its key in `references`, which the error at startup spells out.
|
|
46
48
|
- A partial-index `where` string that interpolates, since `raw` would bind what the template spliced in.
|
|
47
49
|
- 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.
|
|
48
50
|
- SQL in a definition that names a column by hand, as a note: a rename reaches a callback's refs, ``(user) => raw`lower(${user.email})` ``, and never the text.
|
package/dist/edits.d.ts
CHANGED
|
@@ -13,6 +13,7 @@ export type Edit = {
|
|
|
13
13
|
export declare function applyEdits(text: string, edits: readonly Edit[]): string;
|
|
14
14
|
export declare function replaced(node: ts.Node, text: string): Edit;
|
|
15
15
|
export declare function inserted(node: ts.Node, text: string): Edit;
|
|
16
|
+
export declare function appended(node: ts.Node, text: string): Edit;
|
|
16
17
|
/**
|
|
17
18
|
* Removes one element of a comma-separated list - a parameter, a named import, a JSON property - taking
|
|
18
19
|
* the separator that would otherwise dangle. The last element gives up the comma before it; any other
|
package/dist/edits.js
CHANGED
|
@@ -16,6 +16,9 @@ export function replaced(node, text) {
|
|
|
16
16
|
export function inserted(node, text) {
|
|
17
17
|
return { start: node.getStart(), end: node.getStart(), text };
|
|
18
18
|
}
|
|
19
|
+
export function appended(node, text) {
|
|
20
|
+
return { start: node.getEnd(), end: node.getEnd(), text };
|
|
21
|
+
}
|
|
19
22
|
/**
|
|
20
23
|
* Removes one element of a comma-separated list - a parameter, a named import, a JSON property - taking
|
|
21
24
|
* the separator that would otherwise dangle. The last element gives up the comma before it; any other
|
package/dist/entitySql.d.ts
CHANGED
|
@@ -3,12 +3,6 @@ import { type Edit } from './edits.js';
|
|
|
3
3
|
/** `raw\`...\`` or `raw(...)`. */
|
|
4
4
|
type RawSql = ts.TaggedTemplateExpression | ts.CallExpression;
|
|
5
5
|
export declare function isRaw(node: ts.Node): node is RawSql;
|
|
6
|
-
/**
|
|
7
|
-
* `() => ` before each expression a column list indexes, which an index takes as a callback now. One that
|
|
8
|
-
* interpolates takes the list callback's parameter as its refs: `(user) => [raw\`lower(${user.email})\`]`
|
|
9
|
-
* spliced in the key `'email'`, where the ref it reads now renders the column.
|
|
10
|
-
*/
|
|
11
|
-
export declare function expressionEntryEdits(list: ts.Expression | undefined): readonly Edit[];
|
|
12
6
|
/** The expressions of a column list, written as one or returned by its callback: each entry or `column` that is `raw`. */
|
|
13
7
|
export declare function columnExpressions(list: ts.Expression | undefined): readonly RawSql[];
|
|
14
8
|
/** SQL text as a `raw` tagged template, escaping what would end or interpolate it. */
|
package/dist/entitySql.js
CHANGED
|
@@ -9,16 +9,6 @@ export function isRaw(node) {
|
|
|
9
9
|
: undefined;
|
|
10
10
|
return callee !== undefined && ts.isIdentifier(callee) && callee.text === 'raw';
|
|
11
11
|
}
|
|
12
|
-
/**
|
|
13
|
-
* `() => ` before each expression a column list indexes, which an index takes as a callback now. One that
|
|
14
|
-
* interpolates takes the list callback's parameter as its refs: `(user) => [raw\`lower(${user.email})\`]`
|
|
15
|
-
* spliced in the key `'email'`, where the ref it reads now renders the column.
|
|
16
|
-
*/
|
|
17
|
-
export function expressionEntryEdits(list) {
|
|
18
|
-
const param = list && ts.isArrowFunction(list) ? list.parameters[0]?.name : undefined;
|
|
19
|
-
const refs = param && ts.isIdentifier(param) ? param.text : '';
|
|
20
|
-
return columnExpressions(list).map((sql) => inserted(sql, `(${interpolates(sql) ? refs : ''}) => `));
|
|
21
|
-
}
|
|
22
12
|
/** The expressions of a column list, written as one or returned by its callback: each entry or `column` that is `raw`. */
|
|
23
13
|
export function columnExpressions(list) {
|
|
24
14
|
const body = list && ts.isArrowFunction(list) ? list.body : list;
|
|
@@ -28,9 +18,6 @@ export function columnExpressions(list) {
|
|
|
28
18
|
return sql && isRaw(sql) ? [sql] : [];
|
|
29
19
|
});
|
|
30
20
|
}
|
|
31
|
-
function interpolates(sql) {
|
|
32
|
-
return ts.isTaggedTemplateExpression(sql) && ts.isTemplateExpression(sql.template);
|
|
33
|
-
}
|
|
34
21
|
/** SQL text as a `raw` tagged template, escaping what would end or interpolate it. */
|
|
35
22
|
export function rawTag(sql) {
|
|
36
23
|
return `raw\`${sql.replace(/\\/g, '\\\\').replace(/`/g, '\\`').replace(/\$\{/g, '\\${')}\``;
|
package/dist/keyMaps.d.ts
CHANGED
|
@@ -4,6 +4,8 @@ import { type Edit } from './edits.js';
|
|
|
4
4
|
export type Edits = readonly Edit[] | undefined;
|
|
5
5
|
/** A callback's parameter, named after the class it reads: `Post` -> `post`. */
|
|
6
6
|
export declare function paramFor(className: string | undefined): string;
|
|
7
|
+
/** `text` as a single-quoted string literal, escaping what would end it. */
|
|
8
|
+
export declare function quoted(text: string): string;
|
|
7
9
|
/** `post.title`, or `post['first-name']` for a key that is no identifier. */
|
|
8
10
|
export declare function memberAccess(param: string, key: string): string;
|
|
9
11
|
/** A key's name where it is spelled out, which is every form but a computed one (`{ [k]: v }`). */
|
package/dist/keyMaps.js
CHANGED
|
@@ -7,9 +7,13 @@ export function paramFor(className) {
|
|
|
7
7
|
function isIdentifierName(key) {
|
|
8
8
|
return /^[\p{ID_Start}$_][\p{ID_Continue}$\u200C\u200D]*$/u.test(key);
|
|
9
9
|
}
|
|
10
|
+
/** `text` as a single-quoted string literal, escaping what would end it. */
|
|
11
|
+
export function quoted(text) {
|
|
12
|
+
return `'${text.replace(/\\/g, '\\\\').replace(/'/g, "\\'")}'`;
|
|
13
|
+
}
|
|
10
14
|
/** `post.title`, or `post['first-name']` for a key that is no identifier. */
|
|
11
15
|
export function memberAccess(param, key) {
|
|
12
|
-
return isIdentifierName(key) ? `${param}.${key}` : `${param}[
|
|
16
|
+
return isIdentifierName(key) ? `${param}.${key}` : `${param}[${quoted(key)}]`;
|
|
13
17
|
}
|
|
14
18
|
/** A key's name where it is spelled out, which is every form but a computed one (`{ [k]: v }`). */
|
|
15
19
|
export function propertyKey(name) {
|
|
@@ -26,7 +30,7 @@ export function fieldKeysEdits(value) {
|
|
|
26
30
|
if (!names.every((name) => ts.isStringLiteralLike(name))) {
|
|
27
31
|
return undefined;
|
|
28
32
|
}
|
|
29
|
-
const keys = names.map(({ text }) => `${isIdentifierName(text) ? text :
|
|
33
|
+
const keys = names.map(({ text }) => `${isIdentifierName(text) ? text : quoted(text)}: true`);
|
|
30
34
|
return [replaced(value, `{ ${keys.join(', ')} }`)];
|
|
31
35
|
}
|
|
32
36
|
/** Whether `value` is already a function, the form every rewrite here produces. */
|
|
@@ -61,7 +65,7 @@ export function entityGetterTarget(relation) {
|
|
|
61
65
|
const getter = propertyValue(relation, 'entity');
|
|
62
66
|
return getter && ts.isArrowFunction(getter) && ts.isIdentifier(getter.body) ? getter.body.text : undefined;
|
|
63
67
|
}
|
|
64
|
-
/** A column-list entry, its name read off the
|
|
68
|
+
/** A column-list entry, its name read off the refs: an expression, or a `column` given otherwise, has none. */
|
|
65
69
|
function keyListEntryEdits(entry, param) {
|
|
66
70
|
if (ts.isStringLiteralLike(entry)) {
|
|
67
71
|
return [replaced(entry, memberAccess(param, entry.text))];
|
package/dist/transform.js
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import ts from 'typescript';
|
|
2
|
-
import { applyEdits, inserted, removeFromList, replaced } from './edits.js';
|
|
3
|
-
import { columnExpressions,
|
|
2
|
+
import { appended, applyEdits, inserted, removeFromList, replaced } from './edits.js';
|
|
3
|
+
import { columnExpressions, handNamedColumns, isRaw, rawTag, rawWhereEdit } from './entitySql.js';
|
|
4
4
|
import { fieldTypeFor, isBrandedString, relationTargetFor } from './fieldType.js';
|
|
5
|
-
import { entityGetterTarget, fieldKeysEdits, isCallback, keyListEdits, mappedByEdits, memberAccess, paramFor, propertyKey, propertyValue, referencesEdits, } from './keyMaps.js';
|
|
5
|
+
import { entityGetterTarget, fieldKeysEdits, isCallback, keyListEdits, mappedByEdits, memberAccess, paramFor, propertyKey, propertyValue, quoted, referencesEdits, } from './keyMaps.js';
|
|
6
6
|
const FIELD_DECORATORS = new Set(['Field', 'Id']);
|
|
7
7
|
const RELATION_DECORATORS = new Set(['OneToOne', 'ManyToOne', 'OneToMany', 'ManyToMany']);
|
|
8
|
+
const TO_ONE_DECORATORS = new Set(['OneToOne', 'ManyToOne']);
|
|
9
|
+
const TO_ONE_CARDINALITIES = new Set(['11', 'm1']);
|
|
8
10
|
/**
|
|
9
11
|
* Decorators that no longer exist, mapped to what to do instead. None is removed for you: what to put
|
|
10
12
|
* in its place is a judgement call, and removing only the import would swap a clear error for a
|
|
@@ -31,6 +33,29 @@ const REMOVED_EXPORTS = new Map([
|
|
|
31
33
|
['augmentWhere', 'spread the two maps: `{ ...where, ...extra }`'],
|
|
32
34
|
['buildQueryWhereAsMap', 'a `$where` is a map already; name the key for ids: `{ id: [1, 2] }`'],
|
|
33
35
|
['AbstractPgQuerier', 'every pg-compatible pool returns `PgQuerier` (`uql-orm/postgres`), which is concrete'],
|
|
36
|
+
['AbstractHranaQuerierPool', 'extend `AbstractSqlQuerierPool` and hand every querier a `HranaQuerier`'],
|
|
37
|
+
[
|
|
38
|
+
'PreparedSqliteQuerier',
|
|
39
|
+
'use `SqliteQuerier`, which takes any driver that prepares, or extend `AbstractSqliteQuerier`',
|
|
40
|
+
],
|
|
41
|
+
['toSqliteBindValues', 'pass the values as they are: every SQLite driver binds a `SqliteBindValue[]`'],
|
|
42
|
+
['libsqlUseRemoteForMigrations', '`LibsqlQuerierPool.getMigrationQuerier()` decides it'],
|
|
43
|
+
['SqlCallback', 'use `EntitySql<E>`, which holds the callback form'],
|
|
44
|
+
[
|
|
45
|
+
'IndexColumnOptions',
|
|
46
|
+
"an entity's index entry is `EntityIndexColumnInput<E>`, the migration builder's `IndexColumnInput`",
|
|
47
|
+
],
|
|
48
|
+
['isKnownMigratorDialect', 'every dialect has a migrator, so it always held: drop the check'],
|
|
49
|
+
['QuerierPoolDialect', "read the pool's own: `P['dialect']`"],
|
|
50
|
+
['QuerierPoolQuerier', "read the pool's own: `Awaited<ReturnType<P['getQuerier']>>`"],
|
|
51
|
+
[
|
|
52
|
+
'POSTGRES_WIRE_DRIVER_CAPABILITIES',
|
|
53
|
+
'pass `driverCapabilities: { nativeArrays: false, explicitJsonCast: true }` to the dialect',
|
|
54
|
+
],
|
|
55
|
+
['MysqlLikeSqlDialect', 'extend `MySqlDialect` (`uql-orm/mysql`) or `MariaDialect` (`uql-orm/maria`)'],
|
|
56
|
+
['D1Meta', "uql reads `D1Result['meta']`; the rest of a binding is typed by `@cloudflare/workers-types`"],
|
|
57
|
+
['D1ExecResult', 'the rest of a binding is typed by `@cloudflare/workers-types`'],
|
|
58
|
+
['createSchemaGenerator', 'use `new SqlSchemaGenerator(dialect)`, or `migrator.getSchemaGenerator()`'],
|
|
34
59
|
]);
|
|
35
60
|
/**
|
|
36
61
|
* Exports renamed and nothing else - the driver classes were empty subclasses - so the import and every
|
|
@@ -49,6 +74,14 @@ const RENAMED_EXPORTS = new Map([
|
|
|
49
74
|
['MongodbNativeDialect', { to: 'MongoDialect', from: 'uql-orm/mongo' }],
|
|
50
75
|
['LibsqlQuerier', { to: 'HranaQuerier', from: 'uql-orm/sqlite' }],
|
|
51
76
|
['TursoQuerier', { to: 'HranaQuerier', from: 'uql-orm/sqlite' }],
|
|
77
|
+
['TursoLocalQuerier', { to: 'SqliteQuerier', from: 'uql-orm/sqlite' }],
|
|
78
|
+
['TursoDatabase', { to: 'SqliteDatabase', from: 'uql-orm/sqlite' }],
|
|
79
|
+
['SqlMigrationModuleOptions', { to: 'MigrationModuleOptions' }],
|
|
80
|
+
['buildSqlQuerierMigrationModule', { to: 'buildMigrationModule' }],
|
|
81
|
+
['QueryDialect', { to: 'SqlQueryDialect' }],
|
|
82
|
+
['EngineFeatures', { to: 'DialectFeatures' }],
|
|
83
|
+
['KnownMigratorDialect', { to: 'DialectName', from: 'uql-orm' }],
|
|
84
|
+
['D1Preparer', { to: 'D1Database' }],
|
|
52
85
|
]);
|
|
53
86
|
/** A node's decorators, or none where it is a kind that cannot carry them. */
|
|
54
87
|
function decoratorsOf(node) {
|
|
@@ -194,6 +227,22 @@ function addRelationEntity(decorator, node, ctx) {
|
|
|
194
227
|
},
|
|
195
228
|
});
|
|
196
229
|
}
|
|
230
|
+
/**
|
|
231
|
+
* Names the foreign key a to-one found by name alone, `references: (post) => post.authorId` beside `author`,
|
|
232
|
+
* where its entity declares or inherits that key: a declared key is no longer joined by its name.
|
|
233
|
+
*/
|
|
234
|
+
function addForeignKeyReference(relation, key, owner, ctx) {
|
|
235
|
+
const options = { kind: 'literal', node: relation };
|
|
236
|
+
const cardinality = propertyValue(relation, 'cardinality');
|
|
237
|
+
const toOne = !cardinality || (ts.isStringLiteralLike(cardinality) && TO_ONE_CARDINALITIES.has(cardinality.text));
|
|
238
|
+
const joined = ['mappedBy', 'through', 'references'].some((option) => findProperty(options, option));
|
|
239
|
+
const last = relation.properties.at(-1);
|
|
240
|
+
const column = `${key}Id`;
|
|
241
|
+
if (!key || !last || !toOne || joined || !memberNames(owner.entity, ctx.checker).has(column)) {
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
ctx.edits.push(appended(last, `, references: (${owner.param}) => ${memberAccess(owner.param, column)}`));
|
|
245
|
+
}
|
|
197
246
|
/**
|
|
198
247
|
* Writes the edits `rewrite` reads off `value`, which still names members as strings, or reports it
|
|
199
248
|
* where it could not be read: what it holds is only known at runtime. A callback or an object literal
|
|
@@ -212,11 +261,11 @@ function rewriteValue(value, rewrite, node, advice, ctx) {
|
|
|
212
261
|
}
|
|
213
262
|
}
|
|
214
263
|
function rewriteKeyList(value, param, node, what, ctx) {
|
|
215
|
-
rewriteValue(value, (list) => keyListEdits(list, param), node, `write ${what} as a
|
|
264
|
+
rewriteValue(value, (list) => keyListEdits(list, param), node, `write ${what} as a callback`, ctx);
|
|
216
265
|
}
|
|
217
266
|
/**
|
|
218
267
|
* Rewrites a class's decorators - `@Index`, `@Filter` and `@Entity`'s options - for what they define now:
|
|
219
|
-
* members named by string read off
|
|
268
|
+
* members named by string read off a callback's parameter named after the class, and SQL written as it is taken.
|
|
220
269
|
*/
|
|
221
270
|
function rewriteClassDecorators(node, ctx) {
|
|
222
271
|
const owner = { param: paramFor(node.name?.text), entity: node };
|
|
@@ -254,6 +303,7 @@ function rewriteDefineCall(call, ctx) {
|
|
|
254
303
|
}
|
|
255
304
|
else if (name === 'defineRelation' && third && ts.isObjectLiteralExpression(third)) {
|
|
256
305
|
rewriteRelationOptions(third, owner.param, ctx);
|
|
306
|
+
addForeignKeyReference(third, second && ts.isStringLiteralLike(second) ? second.text : undefined, owner, ctx);
|
|
257
307
|
}
|
|
258
308
|
else if (name === 'defineFilter' && third) {
|
|
259
309
|
renameOption(optionsOf(third), 'condition', 'where', call, ctx);
|
|
@@ -280,6 +330,7 @@ function rewriteEntityOptions(options, owner, ctx) {
|
|
|
280
330
|
for (const relation of objectProperties(propertyValue(options, 'relations'))) {
|
|
281
331
|
if (ts.isObjectLiteralExpression(relation.initializer)) {
|
|
282
332
|
rewriteRelationOptions(relation.initializer, owner.param, ctx);
|
|
333
|
+
addForeignKeyReference(relation.initializer, propertyKey(relation.name), owner, ctx);
|
|
283
334
|
}
|
|
284
335
|
}
|
|
285
336
|
}
|
|
@@ -288,23 +339,22 @@ function rewriteIndexOptions(index, owner, ctx) {
|
|
|
288
339
|
}
|
|
289
340
|
/**
|
|
290
341
|
* An index, as `@Index(columns, options)` or a `defineIndex`/`indexes` entry holding both: its columns and
|
|
291
|
-
* `include` read off the
|
|
342
|
+
* `include` read off the entity's refs, its `where` written as {@link rewriteIndexWhere} writes it, its SQL noted.
|
|
292
343
|
*/
|
|
293
344
|
function rewriteIndex(columns, options, owner, node, what, ctx) {
|
|
294
345
|
const literal = options && ts.isObjectLiteralExpression(options) ? options : undefined;
|
|
295
346
|
rewriteKeyList(columns, owner.param, node, what, ctx);
|
|
296
347
|
rewriteKeyList(literal && propertyValue(literal, 'include'), owner.param, node, "'include'", ctx);
|
|
297
|
-
|
|
348
|
+
rewriteIndexWhere(literal, ctx);
|
|
298
349
|
for (const sql of [...columnExpressions(columns), literal && propertyValue(literal, 'where')]) {
|
|
299
350
|
noteHandNamedColumns(sql, owner, ctx);
|
|
300
351
|
}
|
|
301
352
|
}
|
|
302
353
|
/**
|
|
303
|
-
*
|
|
304
|
-
*
|
|
354
|
+
* A partial-index `where` string as `raw`, on an entity and in the migration builder alike. A string it
|
|
355
|
+
* cannot write as `raw` is reported.
|
|
305
356
|
*/
|
|
306
|
-
function
|
|
307
|
-
ctx.edits.push(...expressionEntryEdits(columns));
|
|
357
|
+
function rewriteIndexWhere(options, ctx) {
|
|
308
358
|
const where = options && propertyValue(options, 'where');
|
|
309
359
|
if (!where || !isStringTyped(ctx.checker.getTypeAtLocation(where))) {
|
|
310
360
|
return;
|
|
@@ -322,14 +372,14 @@ function rewriteIndexSql(columns, options, ctx) {
|
|
|
322
372
|
function isStringTyped(type) {
|
|
323
373
|
return type.isUnion() ? type.types.every(isStringTyped) : Boolean(type.flags & ts.TypeFlags.StringLike);
|
|
324
374
|
}
|
|
325
|
-
/** The migration builder's index methods, each with the position of the
|
|
375
|
+
/** The migration builder's index methods, each with the position of the options it takes. */
|
|
326
376
|
const BUILDER_INDEX_METHODS = new Map([
|
|
327
|
-
['index',
|
|
328
|
-
['unique',
|
|
329
|
-
['addIndex',
|
|
330
|
-
['createIndex',
|
|
377
|
+
['index', 1],
|
|
378
|
+
['unique', 1],
|
|
379
|
+
['addIndex', 1],
|
|
380
|
+
['createIndex', 2],
|
|
331
381
|
]);
|
|
332
|
-
/** Rewrites the
|
|
382
|
+
/** Rewrites the `where` of the migration builder's `t.index()`, `t.unique()`, `t.addIndex()` and `m.createIndex()`. */
|
|
333
383
|
function rewriteBuilderCall(call, ctx) {
|
|
334
384
|
const at = ts.isPropertyAccessExpression(call.expression)
|
|
335
385
|
? BUILDER_INDEX_METHODS.get(call.expression.name.text)
|
|
@@ -337,15 +387,15 @@ function rewriteBuilderCall(call, ctx) {
|
|
|
337
387
|
if (at === undefined) {
|
|
338
388
|
return;
|
|
339
389
|
}
|
|
340
|
-
const options = call.arguments[at
|
|
341
|
-
|
|
390
|
+
const options = call.arguments[at];
|
|
391
|
+
rewriteIndexWhere(options && ts.isObjectLiteralExpression(options) ? options : undefined, ctx);
|
|
342
392
|
}
|
|
343
393
|
/**
|
|
344
394
|
* `owner` is the declaring class's parameter and `target` the related one's, read off the `entity`
|
|
345
395
|
* getter unless the caller knows it from the property's type.
|
|
346
396
|
*/
|
|
347
397
|
function rewriteRelationOptions(relation, owner, ctx, target = paramFor(entityGetterTarget(relation))) {
|
|
348
|
-
const advice = (what) => `write '${what}' as a
|
|
398
|
+
const advice = (what) => `write '${what}' as a callback`;
|
|
349
399
|
const mappedBy = (value) => mappedByEdits(value, target);
|
|
350
400
|
const references = (value) => referencesEdits(value, owner, target);
|
|
351
401
|
rewriteValue(propertyValue(relation, 'mappedBy'), mappedBy, relation, advice('mappedBy'), ctx);
|
|
@@ -526,7 +576,7 @@ function brandIdKey(node, ctx) {
|
|
|
526
576
|
return;
|
|
527
577
|
}
|
|
528
578
|
const indent = ' '.repeat(node.getSourceFile().getLineAndCharacterOfPosition(ids[0].getStart()).character);
|
|
529
|
-
const brand = names.map(
|
|
579
|
+
const brand = names.map(quoted).join(' | ');
|
|
530
580
|
const start = node.members.pos;
|
|
531
581
|
ctx.edits.push({ start, end: start, text: `\n${indent}[idKey]?: ${brand};` });
|
|
532
582
|
ctx.imports.set('idKey', 'the brand(s)');
|
|
@@ -575,6 +625,9 @@ function rewriteProperty(node, ctx) {
|
|
|
575
625
|
const target = relationTargetFor(ctx.checker.getTypeAtLocation(node), ctx.checker);
|
|
576
626
|
rewriteRelationOptions(options.node, owner.param, ctx, paramFor(target));
|
|
577
627
|
}
|
|
628
|
+
if (TO_ONE_DECORATORS.has(name) && options.kind === 'literal') {
|
|
629
|
+
addForeignKeyReference(options.node, propertyKey(node.name), owner, ctx);
|
|
630
|
+
}
|
|
578
631
|
}
|
|
579
632
|
if (decorators.length) {
|
|
580
633
|
dropDeclare(node, ctx);
|