uql-codemod 0.48.2 → 0.50.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 +23 -12
- package/dist/builders.d.ts +28 -0
- package/dist/builders.js +783 -0
- package/dist/classOptions.d.ts +9 -0
- package/dist/classOptions.js +240 -0
- package/dist/columnMethods.d.ts +11 -0
- package/dist/columnMethods.js +6 -0
- package/dist/columnRefs.js +1 -8
- package/dist/context.d.ts +18 -5
- package/dist/context.js +22 -4
- package/dist/definers.d.ts +24 -0
- package/dist/definers.js +34 -0
- package/dist/edits.d.ts +26 -5
- package/dist/edits.js +54 -12
- package/dist/entitySql.js +2 -4
- package/dist/imports.d.ts +21 -5
- package/dist/imports.js +103 -31
- package/dist/index.d.ts +3 -3
- package/dist/index.js +9 -12
- package/dist/jsonIndex.d.ts +11 -0
- package/dist/jsonIndex.js +78 -0
- package/dist/keyMaps.d.ts +3 -1
- package/dist/keyMaps.js +5 -1
- package/dist/migrationBuilders.d.ts +13 -0
- package/dist/migrationBuilders.js +182 -0
- package/dist/relationTarget.d.ts +6 -0
- package/dist/relationTarget.js +21 -0
- package/dist/schemaEntity.d.ts +15 -0
- package/dist/schemaEntity.js +578 -0
- package/dist/schemaMessages.d.ts +8 -0
- package/dist/schemaMessages.js +8 -0
- package/dist/schemaOptions.d.ts +6 -0
- package/dist/schemaOptions.js +6 -0
- package/dist/syntax.d.ts +54 -0
- package/dist/syntax.js +131 -1
- package/dist/transform.d.ts +11 -2
- package/dist/transform.js +142 -131
- package/dist/tsconfig.d.ts +3 -9
- package/dist/tsconfig.js +18 -21
- package/package.json +1 -1
- package/dist/fieldType.d.ts +0 -13
- package/dist/fieldType.js +0 -56
package/dist/syntax.d.ts
CHANGED
|
@@ -5,18 +5,72 @@ export declare function identifierText(node: ts.Node | undefined): string | unde
|
|
|
5
5
|
export declare function propertyKey(name: ts.PropertyName): string | undefined;
|
|
6
6
|
/** A property of an object literal written with its own name: a plain or quoted key, or a shorthand. */
|
|
7
7
|
export type NamedProperty = ts.PropertyAssignment | ts.ShorthandPropertyAssignment;
|
|
8
|
+
export declare function isNamedProperty(prop: ts.ObjectLiteralElementLike): prop is NamedProperty;
|
|
8
9
|
/** The property `name` of an object literal, a shorthand included. */
|
|
9
10
|
export declare function namedProperty(node: ts.ObjectLiteralExpression, name: string): NamedProperty | undefined;
|
|
11
|
+
/** The key a property is written under, a shorthand's included; nothing for a spread, a method or a computed key. */
|
|
12
|
+
export declare function keyOf(prop: ts.ObjectLiteralElementLike): string | undefined;
|
|
13
|
+
/**
|
|
14
|
+
* What the codemod can see of an options object. `opaque` is one it cannot write into without dropping what
|
|
15
|
+
* the author put there: `@Field(shared)`, or `{ ...base }`, whose spread may carry the option and would win.
|
|
16
|
+
*/
|
|
17
|
+
export type Options = {
|
|
18
|
+
readonly kind: 'empty';
|
|
19
|
+
readonly call: ts.CallExpression;
|
|
20
|
+
} | {
|
|
21
|
+
readonly kind: 'literal';
|
|
22
|
+
readonly node: ts.ObjectLiteralExpression;
|
|
23
|
+
} | {
|
|
24
|
+
readonly kind: 'opaque';
|
|
25
|
+
readonly reason: string;
|
|
26
|
+
};
|
|
27
|
+
/** The options `value` writes: a literal to read and write into, or why it is not one. */
|
|
28
|
+
export declare function optionsOf(value: ts.Expression): Exclude<Options, {
|
|
29
|
+
kind: 'empty';
|
|
30
|
+
}>;
|
|
31
|
+
/**
|
|
32
|
+
* An object literal's named properties by key, each as its value (a shorthand's value is its name), or `undefined`
|
|
33
|
+
* where `node` is no literal or one of its properties is spread, computed or a method.
|
|
34
|
+
*/
|
|
35
|
+
export declare function propertiesByKey(node: ts.Expression): Map<string, ts.Expression> | undefined;
|
|
36
|
+
/** A method called on a chain: the call, the method's name, and the receiver it is called on. */
|
|
37
|
+
export type ChainMethod = {
|
|
38
|
+
readonly call: ts.CallExpression;
|
|
39
|
+
readonly name: string;
|
|
40
|
+
readonly receiver: ts.Expression;
|
|
41
|
+
};
|
|
42
|
+
/** The root a method chain starts from, and its methods in source order: `f.string().nullable()` is `f` and two. */
|
|
43
|
+
export declare function chainOf(node: ts.Expression): {
|
|
44
|
+
readonly root: ts.Expression;
|
|
45
|
+
readonly methods: readonly ChainMethod[];
|
|
46
|
+
};
|
|
47
|
+
export declare function hasModifier(node: ts.Node, kind: ts.ModifierSyntaxKind): boolean;
|
|
48
|
+
/** `preferred`, or it numbered past every name `nodes` mention, so a parameter named so shadows nothing they read. */
|
|
49
|
+
export declare function freeName(preferred: string, nodes: readonly ts.Node[]): string;
|
|
50
|
+
/** Each identifier in `node` naming `name`, in source order, nested functions included, but none a member named so. */
|
|
51
|
+
export declare function identifierUses(node: ts.Node, name: string): ts.Identifier[];
|
|
52
|
+
/** Whether `node` reads a binding named `name`, which a callback parameter of that name would shadow: stops at the first. */
|
|
53
|
+
export declare function mentions(node: ts.Node, name: string): boolean;
|
|
10
54
|
/** The property `name` of an object literal that is written with a value. */
|
|
11
55
|
export declare function propertyAssignment(node: ts.ObjectLiteralExpression, name: string): ts.PropertyAssignment | undefined;
|
|
12
56
|
export declare function propertyValue(node: ts.ObjectLiteralExpression, name: string): ts.Expression | undefined;
|
|
13
57
|
/** The property assignments of an object literal, or none where `node` is not one. */
|
|
14
58
|
export declare function objectProperties(node: ts.Expression | undefined): readonly ts.PropertyAssignment[];
|
|
59
|
+
/** The entries of a column list, written as an array or returned by its callback. */
|
|
60
|
+
export declare function columnListEntries(list: ts.Expression | undefined): readonly ts.Expression[];
|
|
15
61
|
/** The elements of an array literal, or none where `node` is not one. */
|
|
16
62
|
export declare function arrayElements(node: ts.Expression | undefined): readonly ts.Expression[];
|
|
17
63
|
/** `node` where it is an object literal. */
|
|
18
64
|
export declare function objectLiteral(node: ts.Expression | undefined): ts.ObjectLiteralExpression | undefined;
|
|
65
|
+
/** One level of indentation as the file writes it: the shallowest indent of its code, two spaces where it has none. */
|
|
66
|
+
export declare function indentUnit(source: ts.SourceFile): string;
|
|
19
67
|
/** A node's decorators, or none where it is a kind that cannot carry them. */
|
|
20
68
|
export declare function decoratorsOf(node: ts.Node): readonly ts.Decorator[];
|
|
21
69
|
/** `Field` for `@Field` and `@Field(...)`. */
|
|
22
70
|
export declare function decoratorName(node: ts.Decorator): string | undefined;
|
|
71
|
+
/** The decorators declaring a field: `@Field`, and `@Id` for the key the class names. */
|
|
72
|
+
export declare const FIELD_DECORATORS: ReadonlySet<string | undefined>;
|
|
73
|
+
/** Where the line holding offset `at` of `text` starts. */
|
|
74
|
+
export declare function lineStartOf(text: string, at: number): number;
|
|
75
|
+
/** The whitespace the line holding `node` starts with. */
|
|
76
|
+
export declare function indentOf(node: ts.Node): string;
|
package/dist/syntax.js
CHANGED
|
@@ -7,9 +7,91 @@ export function identifierText(node) {
|
|
|
7
7
|
export function propertyKey(name) {
|
|
8
8
|
return ts.isIdentifier(name) || ts.isStringLiteral(name) ? name.text : undefined;
|
|
9
9
|
}
|
|
10
|
+
export function isNamedProperty(prop) {
|
|
11
|
+
return ts.isPropertyAssignment(prop) || ts.isShorthandPropertyAssignment(prop);
|
|
12
|
+
}
|
|
10
13
|
/** The property `name` of an object literal, a shorthand included. */
|
|
11
14
|
export function namedProperty(node, name) {
|
|
12
|
-
return node.properties.find((prop) => (
|
|
15
|
+
return node.properties.find((prop) => isNamedProperty(prop) && propertyKey(prop.name) === name);
|
|
16
|
+
}
|
|
17
|
+
/** The key a property is written under, a shorthand's included; nothing for a spread, a method or a computed key. */
|
|
18
|
+
export function keyOf(prop) {
|
|
19
|
+
return isNamedProperty(prop) ? propertyKey(prop.name) : undefined;
|
|
20
|
+
}
|
|
21
|
+
/** The options `value` writes: a literal to read and write into, or why it is not one. */
|
|
22
|
+
export function optionsOf(value) {
|
|
23
|
+
if (!ts.isObjectLiteralExpression(value)) {
|
|
24
|
+
return { kind: 'opaque', reason: `its options are passed as '${value.getText()}'` };
|
|
25
|
+
}
|
|
26
|
+
return value.properties.some(ts.isSpreadAssignment)
|
|
27
|
+
? { kind: 'opaque', reason: 'its options object spreads another' }
|
|
28
|
+
: { kind: 'literal', node: value };
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* An object literal's named properties by key, each as its value (a shorthand's value is its name), or `undefined`
|
|
32
|
+
* where `node` is no literal or one of its properties is spread, computed or a method.
|
|
33
|
+
*/
|
|
34
|
+
export function propertiesByKey(node) {
|
|
35
|
+
const literal = objectLiteral(node);
|
|
36
|
+
if (!literal) {
|
|
37
|
+
return undefined;
|
|
38
|
+
}
|
|
39
|
+
const props = new Map();
|
|
40
|
+
for (const prop of literal.properties) {
|
|
41
|
+
if (!isNamedProperty(prop)) {
|
|
42
|
+
return undefined;
|
|
43
|
+
}
|
|
44
|
+
const key = propertyKey(prop.name);
|
|
45
|
+
if (key === undefined) {
|
|
46
|
+
return undefined;
|
|
47
|
+
}
|
|
48
|
+
props.set(key, ts.isShorthandPropertyAssignment(prop) ? prop.name : prop.initializer);
|
|
49
|
+
}
|
|
50
|
+
return props;
|
|
51
|
+
}
|
|
52
|
+
/** The root a method chain starts from, and its methods in source order: `f.string().nullable()` is `f` and two. */
|
|
53
|
+
export function chainOf(node) {
|
|
54
|
+
const methods = [];
|
|
55
|
+
let root = node;
|
|
56
|
+
while (ts.isCallExpression(root) && ts.isPropertyAccessExpression(root.expression)) {
|
|
57
|
+
methods.unshift({ call: root, name: root.expression.name.text, receiver: root.expression.expression });
|
|
58
|
+
root = root.expression.expression;
|
|
59
|
+
}
|
|
60
|
+
return { root, methods };
|
|
61
|
+
}
|
|
62
|
+
export function hasModifier(node, kind) {
|
|
63
|
+
return ts.canHaveModifiers(node) && !!ts.getModifiers(node)?.some((modifier) => modifier.kind === kind);
|
|
64
|
+
}
|
|
65
|
+
/** `preferred`, or it numbered past every name `nodes` mention, so a parameter named so shadows nothing they read. */
|
|
66
|
+
export function freeName(preferred, nodes) {
|
|
67
|
+
const taken = (name) => nodes.some((node) => mentions(node, name));
|
|
68
|
+
let name = preferred;
|
|
69
|
+
for (let n = 2; taken(name); n++) {
|
|
70
|
+
name = `${preferred}${n}`;
|
|
71
|
+
}
|
|
72
|
+
return name;
|
|
73
|
+
}
|
|
74
|
+
/** Whether `node` is an identifier reading `name`, not a member named so. */
|
|
75
|
+
function readsName(node, name) {
|
|
76
|
+
return (ts.isIdentifier(node) &&
|
|
77
|
+
node.text === name &&
|
|
78
|
+
!(ts.isPropertyAccessExpression(node.parent) && node.parent.name === node));
|
|
79
|
+
}
|
|
80
|
+
/** Each identifier in `node` naming `name`, in source order, nested functions included, but none a member named so. */
|
|
81
|
+
export function identifierUses(node, name) {
|
|
82
|
+
const uses = [];
|
|
83
|
+
const visit = (child) => {
|
|
84
|
+
if (readsName(child, name)) {
|
|
85
|
+
uses.push(child);
|
|
86
|
+
}
|
|
87
|
+
ts.forEachChild(child, visit);
|
|
88
|
+
};
|
|
89
|
+
visit(node);
|
|
90
|
+
return uses;
|
|
91
|
+
}
|
|
92
|
+
/** Whether `node` reads a binding named `name`, which a callback parameter of that name would shadow: stops at the first. */
|
|
93
|
+
export function mentions(node, name) {
|
|
94
|
+
return readsName(node, name) || !!ts.forEachChild(node, (child) => mentions(child, name) || undefined);
|
|
13
95
|
}
|
|
14
96
|
/** The property `name` of an object literal that is written with a value. */
|
|
15
97
|
export function propertyAssignment(node, name) {
|
|
@@ -22,6 +104,13 @@ export function propertyValue(node, name) {
|
|
|
22
104
|
export function objectProperties(node) {
|
|
23
105
|
return node && ts.isObjectLiteralExpression(node) ? node.properties.filter(ts.isPropertyAssignment) : [];
|
|
24
106
|
}
|
|
107
|
+
/** The entries of a column list, written as an array or returned by its callback. */
|
|
108
|
+
export function columnListEntries(list) {
|
|
109
|
+
if (list && ts.isArrowFunction(list) && !ts.isBlock(list.body)) {
|
|
110
|
+
return arrayElements(list.body);
|
|
111
|
+
}
|
|
112
|
+
return arrayElements(list);
|
|
113
|
+
}
|
|
25
114
|
/** The elements of an array literal, or none where `node` is not one. */
|
|
26
115
|
export function arrayElements(node) {
|
|
27
116
|
return node && ts.isArrayLiteralExpression(node) ? node.elements : [];
|
|
@@ -30,6 +119,35 @@ export function arrayElements(node) {
|
|
|
30
119
|
export function objectLiteral(node) {
|
|
31
120
|
return node && ts.isObjectLiteralExpression(node) ? node : undefined;
|
|
32
121
|
}
|
|
122
|
+
const indentUnits = new WeakMap();
|
|
123
|
+
/** Whether `node` starts a line of code whose indent a file's unit is read from: a member, a property, a statement. */
|
|
124
|
+
const startsCodeLine = (node) => ts.isClassElement(node) ||
|
|
125
|
+
ts.isPropertyAssignment(node) ||
|
|
126
|
+
ts.isShorthandPropertyAssignment(node) ||
|
|
127
|
+
ts.isVariableStatement(node) ||
|
|
128
|
+
ts.isExpressionStatement(node) ||
|
|
129
|
+
ts.isClassDeclaration(node) ||
|
|
130
|
+
ts.isFunctionDeclaration(node) ||
|
|
131
|
+
ts.isImportDeclaration(node);
|
|
132
|
+
/** One level of indentation as the file writes it: the shallowest indent of its code, two spaces where it has none. */
|
|
133
|
+
export function indentUnit(source) {
|
|
134
|
+
const known = indentUnits.get(source);
|
|
135
|
+
if (known !== undefined) {
|
|
136
|
+
return known;
|
|
137
|
+
}
|
|
138
|
+
const indents = [];
|
|
139
|
+
const visit = (node) => {
|
|
140
|
+
const indent = startsCodeLine(node) ? indentOf(node) : '';
|
|
141
|
+
if (indent) {
|
|
142
|
+
indents.push(indent);
|
|
143
|
+
}
|
|
144
|
+
ts.forEachChild(node, visit);
|
|
145
|
+
};
|
|
146
|
+
visit(source);
|
|
147
|
+
const unit = indents.reduce((shallow, indent) => (indent.length < shallow.length ? indent : shallow), indents[0] ?? ' ');
|
|
148
|
+
indentUnits.set(source, unit);
|
|
149
|
+
return unit;
|
|
150
|
+
}
|
|
33
151
|
/** A node's decorators, or none where it is a kind that cannot carry them. */
|
|
34
152
|
export function decoratorsOf(node) {
|
|
35
153
|
return ts.canHaveDecorators(node) ? (ts.getDecorators(node) ?? []) : [];
|
|
@@ -39,3 +157,15 @@ export function decoratorName(node) {
|
|
|
39
157
|
const { expression } = node;
|
|
40
158
|
return identifierText(ts.isCallExpression(expression) ? expression.expression : expression);
|
|
41
159
|
}
|
|
160
|
+
/** The decorators declaring a field: `@Field`, and `@Id` for the key the class names. */
|
|
161
|
+
export const FIELD_DECORATORS = new Set(['Field', 'Id']);
|
|
162
|
+
/** Where the line holding offset `at` of `text` starts. */
|
|
163
|
+
export function lineStartOf(text, at) {
|
|
164
|
+
return text.lastIndexOf('\n', at - 1) + 1;
|
|
165
|
+
}
|
|
166
|
+
/** The whitespace the line holding `node` starts with. */
|
|
167
|
+
export function indentOf(node) {
|
|
168
|
+
const text = node.getSourceFile().text;
|
|
169
|
+
const line = text.slice(lineStartOf(text, node.getStart()), node.getStart());
|
|
170
|
+
return line.slice(0, line.length - line.trimStart().length);
|
|
171
|
+
}
|
package/dist/transform.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import ts from 'typescript';
|
|
2
|
+
import { type Pass } from './edits.js';
|
|
2
3
|
export type FileResult = {
|
|
3
4
|
readonly fileName: string;
|
|
4
5
|
readonly text: string;
|
|
@@ -9,9 +10,17 @@ export type FileResult = {
|
|
|
9
10
|
readonly notes: readonly string[];
|
|
10
11
|
};
|
|
11
12
|
/**
|
|
12
|
-
* Rewrites one
|
|
13
|
-
*
|
|
13
|
+
* Rewrites one file across every breaking change: the earlier ones off the checker, then column builders and schema
|
|
14
|
+
* chains off the text those produced, so neither pass edits what the other writes.
|
|
14
15
|
*/
|
|
15
16
|
export declare function transformFile(source: ts.SourceFile, checker: ts.TypeChecker, options?: {
|
|
16
17
|
readonly strictNullChecks: boolean;
|
|
17
18
|
}): FileResult;
|
|
19
|
+
/**
|
|
20
|
+
* The rewrites the checker reads types for, up to the schema entities a class-form registration declares. The
|
|
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
|
+
* only where it is uql's.
|
|
23
|
+
*/
|
|
24
|
+
export declare function rewriteLegacy(source: ts.SourceFile, checker: ts.TypeChecker, options: {
|
|
25
|
+
readonly strictNullChecks: boolean;
|
|
26
|
+
}, taken?: ReadonlySet<string>): FileResult & Pass;
|