@venizia/dev-configs 0.4.6 → 0.4.7-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/LICENSE.md +1 -3
- package/README.md +1 -1
- package/dist/checks/catalog.d.ts +4 -2
- package/dist/checks/catalog.d.ts.map +1 -1
- package/dist/checks/catalog.js +72 -68
- package/dist/checks/catalog.js.map +1 -1
- package/dist/checks/module-cycles.d.ts +11 -9
- package/dist/checks/module-cycles.d.ts.map +1 -1
- package/dist/checks/module-cycles.js +32 -30
- package/dist/checks/module-cycles.js.map +1 -1
- package/dist/checks/purity/manifest.d.ts +44 -38
- package/dist/checks/purity/manifest.d.ts.map +1 -1
- package/dist/checks/purity/manifest.js +121 -115
- package/dist/checks/purity/manifest.js.map +1 -1
- package/dist/checks/purity/probe.d.ts +22 -18
- package/dist/checks/purity/probe.d.ts.map +1 -1
- package/dist/checks/purity/probe.js +101 -99
- package/dist/checks/purity/probe.js.map +1 -1
- package/dist/checks/purity/run.d.ts +18 -16
- package/dist/checks/purity/run.d.ts.map +1 -1
- package/dist/checks/purity/run.js +90 -86
- package/dist/checks/purity/run.js.map +1 -1
- package/dist/checks/test-controls.d.ts +17 -15
- package/dist/checks/test-controls.d.ts.map +1 -1
- package/dist/checks/test-controls.js +71 -69
- package/dist/checks/test-controls.js.map +1 -1
- package/dist/checks/test-dead-code.d.ts +5 -3
- package/dist/checks/test-dead-code.d.ts.map +1 -1
- package/dist/checks/test-dead-code.js +23 -21
- package/dist/checks/test-dead-code.js.map +1 -1
- package/dist/checks/test-mutate.d.ts +12 -7
- package/dist/checks/test-mutate.d.ts.map +1 -1
- package/dist/checks/test-mutate.js +59 -56
- package/dist/checks/test-mutate.js.map +1 -1
- package/dist/checks/test-weight.d.ts +11 -8
- package/dist/checks/test-weight.d.ts.map +1 -1
- package/dist/checks/test-weight.js +98 -94
- package/dist/checks/test-weight.js.map +1 -1
- package/dist/cli/check.d.ts.map +1 -1
- package/dist/cli/check.js +11 -8
- package/dist/cli/check.js.map +1 -1
- package/dist/cli/codemod/async-style.d.ts +62 -0
- package/dist/cli/codemod/async-style.d.ts.map +1 -0
- package/dist/cli/codemod/async-style.js +248 -0
- package/dist/cli/codemod/async-style.js.map +1 -0
- package/dist/cli/codemod/const-classes.d.ts.map +1 -1
- package/dist/cli/codemod/const-classes.js +2 -1
- package/dist/cli/codemod/const-classes.js.map +1 -1
- package/dist/cli/codemod/drop-redundant-casts.d.ts.map +1 -1
- package/dist/cli/codemod/drop-redundant-casts.js +2 -1
- package/dist/cli/codemod/drop-redundant-casts.js.map +1 -1
- package/dist/cli/codemod/files.d.ts +7 -0
- package/dist/cli/codemod/files.d.ts.map +1 -1
- package/dist/cli/codemod/files.js +33 -0
- package/dist/cli/codemod/files.js.map +1 -1
- package/dist/cli/codemod/index.d.ts.map +1 -1
- package/dist/cli/codemod/index.js +12 -3
- package/dist/cli/codemod/index.js.map +1 -1
- package/dist/cli/codemod/model-settings.d.ts +7 -5
- package/dist/cli/codemod/model-settings.d.ts.map +1 -1
- package/dist/cli/codemod/model-settings.js +293 -291
- package/dist/cli/codemod/model-settings.js.map +1 -1
- package/dist/cli/codemod/namespace-functions/index.d.ts +20 -0
- package/dist/cli/codemod/namespace-functions/index.d.ts.map +1 -0
- package/dist/cli/codemod/namespace-functions/index.js +182 -0
- package/dist/cli/codemod/namespace-functions/index.js.map +1 -0
- package/dist/cli/codemod/namespace-functions/logger-scope.d.ts +32 -0
- package/dist/cli/codemod/namespace-functions/logger-scope.d.ts.map +1 -0
- package/dist/cli/codemod/namespace-functions/logger-scope.js +128 -0
- package/dist/cli/codemod/namespace-functions/logger-scope.js.map +1 -0
- package/dist/cli/codemod/namespace-functions/mapping.d.ts +66 -0
- package/dist/cli/codemod/namespace-functions/mapping.d.ts.map +1 -0
- package/dist/cli/codemod/namespace-functions/mapping.js +99 -0
- package/dist/cli/codemod/namespace-functions/mapping.js.map +1 -0
- package/dist/cli/codemod/namespace-functions/mappings/boot.d.ts +7 -0
- package/dist/cli/codemod/namespace-functions/mappings/boot.d.ts.map +1 -0
- package/dist/cli/codemod/namespace-functions/mappings/boot.js +28 -0
- package/dist/cli/codemod/namespace-functions/mappings/boot.js.map +1 -0
- package/dist/cli/codemod/namespace-functions/mappings/connectors.d.ts +4 -0
- package/dist/cli/codemod/namespace-functions/mappings/connectors.d.ts.map +1 -0
- package/dist/cli/codemod/namespace-functions/mappings/connectors.js +218 -0
- package/dist/cli/codemod/namespace-functions/mappings/connectors.js.map +1 -0
- package/dist/cli/codemod/namespace-functions/mappings/core-server.d.ts +7 -0
- package/dist/cli/codemod/namespace-functions/mappings/core-server.d.ts.map +1 -0
- package/dist/cli/codemod/namespace-functions/mappings/core-server.js +162 -0
- package/dist/cli/codemod/namespace-functions/mappings/core-server.js.map +1 -0
- package/dist/cli/codemod/namespace-functions/mappings/core-worker.d.ts +7 -0
- package/dist/cli/codemod/namespace-functions/mappings/core-worker.d.ts.map +1 -0
- package/dist/cli/codemod/namespace-functions/mappings/core-worker.js +18 -0
- package/dist/cli/codemod/namespace-functions/mappings/core-worker.js.map +1 -0
- package/dist/cli/codemod/namespace-functions/mappings/dev-configs.d.ts +7 -0
- package/dist/cli/codemod/namespace-functions/mappings/dev-configs.d.ts.map +1 -0
- package/dist/cli/codemod/namespace-functions/mappings/dev-configs.js +192 -0
- package/dist/cli/codemod/namespace-functions/mappings/dev-configs.js.map +1 -0
- package/dist/cli/codemod/namespace-functions/mappings/filter.d.ts +7 -0
- package/dist/cli/codemod/namespace-functions/mappings/filter.d.ts.map +1 -0
- package/dist/cli/codemod/namespace-functions/mappings/filter.js +30 -0
- package/dist/cli/codemod/namespace-functions/mappings/filter.js.map +1 -0
- package/dist/cli/codemod/namespace-functions/mappings/helpers.d.ts +16 -0
- package/dist/cli/codemod/namespace-functions/mappings/helpers.d.ts.map +1 -0
- package/dist/cli/codemod/namespace-functions/mappings/helpers.js +535 -0
- package/dist/cli/codemod/namespace-functions/mappings/helpers.js.map +1 -0
- package/dist/cli/codemod/namespace-functions/mappings/index.d.ts +4 -0
- package/dist/cli/codemod/namespace-functions/mappings/index.d.ts.map +1 -0
- package/dist/cli/codemod/namespace-functions/mappings/index.js +25 -0
- package/dist/cli/codemod/namespace-functions/mappings/index.js.map +1 -0
- package/dist/cli/codemod/namespace-functions/mappings/inversion.d.ts +10 -0
- package/dist/cli/codemod/namespace-functions/mappings/inversion.d.ts.map +1 -0
- package/dist/cli/codemod/namespace-functions/mappings/inversion.js +81 -0
- package/dist/cli/codemod/namespace-functions/mappings/inversion.js.map +1 -0
- package/dist/cli/codemod/namespace-functions/mappings/kernel.d.ts +17 -0
- package/dist/cli/codemod/namespace-functions/mappings/kernel.d.ts.map +1 -0
- package/dist/cli/codemod/namespace-functions/mappings/kernel.js +229 -0
- package/dist/cli/codemod/namespace-functions/mappings/kernel.js.map +1 -0
- package/dist/cli/codemod/namespace-functions/rewriter.d.ts +61 -0
- package/dist/cli/codemod/namespace-functions/rewriter.d.ts.map +1 -0
- package/dist/cli/codemod/namespace-functions/rewriter.js +564 -0
- package/dist/cli/codemod/namespace-functions/rewriter.js.map +1 -0
- package/dist/cli/index.js +2 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/compilers/bun.d.ts +1 -1
- package/dist/compilers/bun.js +1 -1
- package/dist/compilers/bun.js.map +1 -1
- package/dist/linters/house/await-support.d.ts +74 -0
- package/dist/linters/house/await-support.d.ts.map +1 -0
- package/dist/linters/house/await-support.js +238 -0
- package/dist/linters/house/await-support.js.map +1 -0
- package/dist/linters/house/common.d.ts +15 -9
- package/dist/linters/house/common.d.ts.map +1 -1
- package/dist/linters/house/common.js +20 -15
- package/dist/linters/house/common.js.map +1 -1
- package/dist/linters/house/configs.d.ts +26 -0
- package/dist/linters/house/configs.d.ts.map +1 -1
- package/dist/linters/house/configs.js +55 -1
- package/dist/linters/house/configs.js.map +1 -1
- package/dist/linters/house/const-class-values.rule.d.ts.map +1 -1
- package/dist/linters/house/const-class-values.rule.js +27 -19
- package/dist/linters/house/const-class-values.rule.js.map +1 -1
- package/dist/linters/house/effect-cleanup-consistency.rule.d.ts +17 -0
- package/dist/linters/house/effect-cleanup-consistency.rule.d.ts.map +1 -0
- package/dist/linters/house/effect-cleanup-consistency.rule.js +87 -0
- package/dist/linters/house/effect-cleanup-consistency.rule.js.map +1 -0
- package/dist/linters/house/hook-support.d.ts +29 -0
- package/dist/linters/house/hook-support.d.ts.map +1 -0
- package/dist/linters/house/hook-support.js +72 -0
- package/dist/linters/house/hook-support.js.map +1 -0
- package/dist/linters/house/index.d.ts +9 -0
- package/dist/linters/house/index.d.ts.map +1 -1
- package/dist/linters/house/index.js +9 -0
- package/dist/linters/house/index.js.map +1 -1
- package/dist/linters/house/max-params-own.rule.d.ts.map +1 -1
- package/dist/linters/house/max-params-own.rule.js +33 -24
- package/dist/linters/house/max-params-own.rule.js.map +1 -1
- package/dist/linters/house/no-async-hook-callback.rule.d.ts +18 -0
- package/dist/linters/house/no-async-hook-callback.rule.d.ts.map +1 -0
- package/dist/linters/house/no-async-hook-callback.rule.js +50 -0
- package/dist/linters/house/no-async-hook-callback.rule.js.map +1 -0
- package/dist/linters/house/no-await-promise-chain.rule.d.ts +24 -0
- package/dist/linters/house/no-await-promise-chain.rule.d.ts.map +1 -0
- package/dist/linters/house/no-await-promise-chain.rule.js +298 -0
- package/dist/linters/house/no-await-promise-chain.rule.js.map +1 -0
- package/dist/linters/house/no-inline-await.rule.d.ts +20 -0
- package/dist/linters/house/no-inline-await.rule.d.ts.map +1 -0
- package/dist/linters/house/no-inline-await.rule.js +70 -0
- package/dist/linters/house/no-inline-await.rule.js.map +1 -0
- package/dist/linters/house/no-new-error.rule.js +4 -4
- package/dist/linters/house/no-new-error.rule.js.map +1 -1
- package/dist/linters/house/no-return-await.rule.d.ts +15 -0
- package/dist/linters/house/no-return-await.rule.d.ts.map +1 -0
- package/dist/linters/house/no-return-await.rule.js +92 -0
- package/dist/linters/house/no-return-await.rule.js.map +1 -0
- package/dist/linters/house/no-silent-catch.rule.d.ts +22 -0
- package/dist/linters/house/no-silent-catch.rule.d.ts.map +1 -0
- package/dist/linters/house/no-silent-catch.rule.js +88 -0
- package/dist/linters/house/no-silent-catch.rule.js.map +1 -0
- package/dist/linters/house/plugin.d.ts.map +1 -1
- package/dist/linters/house/plugin.js +12 -0
- package/dist/linters/house/plugin.js.map +1 -1
- package/dist/linters/house/promise-chain-support.d.ts +49 -0
- package/dist/linters/house/promise-chain-support.d.ts.map +1 -0
- package/dist/linters/house/promise-chain-support.js +188 -0
- package/dist/linters/house/promise-chain-support.js.map +1 -0
- package/dist/linters/rules/no-restricted-properties.rule.js +1 -1
- package/dist/linters/rules/no-restricted-properties.rule.js.map +1 -1
- package/dist/testing/rejection.helper.d.ts +25 -20
- package/dist/testing/rejection.helper.d.ts.map +1 -1
- package/dist/testing/rejection.helper.js +33 -31
- package/dist/testing/rejection.helper.js.map +1 -1
- package/dist/testing/repo-tmp.d.ts +19 -9
- package/dist/testing/repo-tmp.d.ts.map +1 -1
- package/dist/testing/repo-tmp.js +50 -48
- package/dist/testing/repo-tmp.js.map +1 -1
- package/dist/testing/spawn.helper.d.ts +12 -10
- package/dist/testing/spawn.helper.d.ts.map +1 -1
- package/dist/testing/spawn.helper.js +22 -16
- package/dist/testing/spawn.helper.js.map +1 -1
- package/dist/testing/tmpdir-preload.js +1 -1
- package/dist/testing/tmpdir-preload.js.map +1 -1
- package/package.json +2 -3
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.ModelSettingsCodemod = void 0;
|
|
4
4
|
/** The packages that export the `@model` decorator, so an aliased import of it is recognised. */
|
|
5
5
|
const MODEL_MODULES = new Set(['@venizia/ignis', '@venizia/ignis-kernel']);
|
|
6
6
|
const QUERY_KEYS = { defaultFilter: 'filter', defaultLimit: 'default', maxLimit: 'max' };
|
|
@@ -16,329 +16,331 @@ const OLD_KEYS = new Set([...Object.keys(QUERY_KEYS), 'scopeFilter']);
|
|
|
16
16
|
* never guesses: a `settings` value that is not an object literal, a spread, a computed key, or a
|
|
17
17
|
* `scopeFilter` that is not an object literal is reported and the model is left untouched.
|
|
18
18
|
*/
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
!typescript.
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
const { name } = property;
|
|
35
|
-
if (typescript.isIdentifier(name) || typescript.isStringLiteral(name)) {
|
|
36
|
-
return name.text;
|
|
37
|
-
}
|
|
38
|
-
return undefined;
|
|
39
|
-
};
|
|
40
|
-
const text = (node) => source.slice(node.getStart(sourceFile), node.end);
|
|
41
|
-
// Names that mean `@model`: the identifier itself, an alias of a named import from IGNIS, and the
|
|
42
|
-
// namespace of `import * as ignis` (`@ignis.model(...)`).
|
|
43
|
-
const modelNames = new Set(['model']);
|
|
44
|
-
const namespaces = new Set();
|
|
45
|
-
for (const statement of sourceFile.statements) {
|
|
46
|
-
if (!typescript.isImportDeclaration(statement) ||
|
|
47
|
-
!typescript.isStringLiteral(statement.moduleSpecifier) ||
|
|
48
|
-
!MODEL_MODULES.has(statement.moduleSpecifier.text)) {
|
|
49
|
-
continue;
|
|
50
|
-
}
|
|
51
|
-
const bindings = statement.importClause?.namedBindings;
|
|
52
|
-
if (!bindings) {
|
|
53
|
-
continue;
|
|
54
|
-
}
|
|
55
|
-
if (typescript.isNamespaceImport(bindings)) {
|
|
56
|
-
namespaces.add(bindings.name.text);
|
|
57
|
-
continue;
|
|
58
|
-
}
|
|
59
|
-
for (const element of bindings.elements) {
|
|
60
|
-
if ((element.propertyName ?? element.name).text === 'model') {
|
|
61
|
-
modelNames.add(element.name.text);
|
|
19
|
+
class ModelSettingsCodemod {
|
|
20
|
+
static migrate(opts) {
|
|
21
|
+
const { typescript, fileName, source } = opts;
|
|
22
|
+
const sourceFile = typescript.createSourceFile(fileName, source, typescript.ScriptTarget.Latest, true);
|
|
23
|
+
const edits = [];
|
|
24
|
+
const reports = [];
|
|
25
|
+
let rewritten = 0;
|
|
26
|
+
const report = (node, reason) => {
|
|
27
|
+
const { line, character } = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile));
|
|
28
|
+
reports.push({ line: line + 1, column: character + 1, reason });
|
|
29
|
+
};
|
|
30
|
+
const nameOf = (property) => {
|
|
31
|
+
if (!typescript.isPropertyAssignment(property) &&
|
|
32
|
+
!typescript.isShorthandPropertyAssignment(property)) {
|
|
33
|
+
return undefined;
|
|
62
34
|
}
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
if (typescript.isIdentifier(callee)) {
|
|
67
|
-
return modelNames.has(callee.text);
|
|
68
|
-
}
|
|
69
|
-
return (typescript.isPropertyAccessExpression(callee) &&
|
|
70
|
-
typescript.isIdentifier(callee.expression) &&
|
|
71
|
-
namespaces.has(callee.expression.text) &&
|
|
72
|
-
callee.name.text === 'model');
|
|
73
|
-
};
|
|
74
|
-
/**
|
|
75
|
-
* The leading trivia of `first` (its own comments and indentation) followed by the comments that
|
|
76
|
-
* were above the properties being merged into it, so no comment is dropped.
|
|
77
|
-
*/
|
|
78
|
-
const composeLeading = (first, merged) => {
|
|
79
|
-
const trivia = source.slice(first.getFullStart(), first.getStart(sourceFile));
|
|
80
|
-
const indent = /[ \t]*$/.exec(trivia)?.[0] ?? '';
|
|
81
|
-
const extra = merged
|
|
82
|
-
.flatMap((node) => typescript.getLeadingCommentRanges(source, node.getFullStart()) ?? [])
|
|
83
|
-
.map((range) => `${source.slice(range.pos, range.end)}\n${indent}`)
|
|
84
|
-
.join('');
|
|
85
|
-
return trivia + extra;
|
|
86
|
-
};
|
|
87
|
-
/**
|
|
88
|
-
* Removes a property with the comma after it; the last property has none, so the comma before it
|
|
89
|
-
* goes instead, leaving no dangling `, }`.
|
|
90
|
-
*/
|
|
91
|
-
const removal = (node) => {
|
|
92
|
-
const after = /^\s*,/.exec(source.slice(node.end));
|
|
93
|
-
if (after) {
|
|
94
|
-
return { start: node.getFullStart(), end: node.end + after[0].length, text: '' };
|
|
95
|
-
}
|
|
96
|
-
const before = /,\s*$/.exec(source.slice(0, node.getFullStart()));
|
|
97
|
-
const start = before ? node.getFullStart() - before[0].length : node.getFullStart();
|
|
98
|
-
return { start, end: node.end, text: '' };
|
|
99
|
-
};
|
|
100
|
-
/** Appends members to an object literal: after its last member, or inside empty braces. */
|
|
101
|
-
const append = (object, members) => {
|
|
102
|
-
const last = object.properties[object.properties.length - 1];
|
|
103
|
-
if (last) {
|
|
104
|
-
return { start: last.end, end: last.end, text: `, ${members.join(', ')}` };
|
|
105
|
-
}
|
|
106
|
-
const at = object.getStart(sourceFile) + 1;
|
|
107
|
-
return { start: at, end: at, text: ` ${members.join(', ')} ` };
|
|
108
|
-
};
|
|
109
|
-
const migrateSettings = (settings) => {
|
|
110
|
-
const properties = [...settings.properties];
|
|
111
|
-
const old = new Map();
|
|
112
|
-
let existingQuery;
|
|
113
|
-
for (const property of properties) {
|
|
114
|
-
if (typescript.isSpreadAssignment(property)) {
|
|
115
|
-
// A spread can carry old keys we cannot see.
|
|
116
|
-
report(property, 'settings contains a spread, so its keys are unknown; left untouched');
|
|
117
|
-
return;
|
|
35
|
+
const { name } = property;
|
|
36
|
+
if (typescript.isIdentifier(name) || typescript.isStringLiteral(name)) {
|
|
37
|
+
return name.text;
|
|
118
38
|
}
|
|
119
|
-
|
|
39
|
+
return undefined;
|
|
40
|
+
};
|
|
41
|
+
const text = (node) => source.slice(node.getStart(sourceFile), node.end);
|
|
42
|
+
// Names that mean `@model`: the identifier itself, an alias of a named import from IGNIS, and the
|
|
43
|
+
// namespace of `import * as ignis` (`@ignis.model(...)`).
|
|
44
|
+
const modelNames = new Set(['model']);
|
|
45
|
+
const namespaces = new Set();
|
|
46
|
+
for (const statement of sourceFile.statements) {
|
|
47
|
+
if (!typescript.isImportDeclaration(statement) ||
|
|
48
|
+
!typescript.isStringLiteral(statement.moduleSpecifier) ||
|
|
49
|
+
!MODEL_MODULES.has(statement.moduleSpecifier.text)) {
|
|
120
50
|
continue;
|
|
121
51
|
}
|
|
122
|
-
const
|
|
123
|
-
if (
|
|
124
|
-
|
|
125
|
-
return;
|
|
52
|
+
const bindings = statement.importClause?.namedBindings;
|
|
53
|
+
if (!bindings) {
|
|
54
|
+
continue;
|
|
126
55
|
}
|
|
127
|
-
if (
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
return;
|
|
131
|
-
}
|
|
132
|
-
old.set(name, property);
|
|
56
|
+
if (typescript.isNamespaceImport(bindings)) {
|
|
57
|
+
namespaces.add(bindings.name.text);
|
|
58
|
+
continue;
|
|
133
59
|
}
|
|
134
|
-
|
|
135
|
-
|
|
60
|
+
for (const element of bindings.elements) {
|
|
61
|
+
if ((element.propertyName ?? element.name).text === 'model') {
|
|
62
|
+
modelNames.add(element.name.text);
|
|
63
|
+
}
|
|
136
64
|
}
|
|
137
65
|
}
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
const queryOld = [...old.keys()].filter((key) => key in QUERY_KEYS);
|
|
142
|
-
const localEdits = [];
|
|
143
|
-
// ---- scopeFilter -> access (rename the key, rewrite writeGuard / sharedRows inside)
|
|
144
|
-
const scopeFilter = old.get('scopeFilter');
|
|
145
|
-
if (scopeFilter) {
|
|
146
|
-
if (!typescript.isObjectLiteralExpression(scopeFilter.initializer)) {
|
|
147
|
-
report(scopeFilter, 'settings.scopeFilter is not an object literal; left untouched');
|
|
148
|
-
return;
|
|
66
|
+
const isModelCallee = (callee) => {
|
|
67
|
+
if (typescript.isIdentifier(callee)) {
|
|
68
|
+
return modelNames.has(callee.text);
|
|
149
69
|
}
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
70
|
+
return (typescript.isPropertyAccessExpression(callee) &&
|
|
71
|
+
typescript.isIdentifier(callee.expression) &&
|
|
72
|
+
namespaces.has(callee.expression.text) &&
|
|
73
|
+
callee.name.text === 'model');
|
|
74
|
+
};
|
|
75
|
+
/**
|
|
76
|
+
* The leading trivia of `first` (its own comments and indentation) followed by the comments that
|
|
77
|
+
* were above the properties being merged into it, so no comment is dropped.
|
|
78
|
+
*/
|
|
79
|
+
const composeLeading = (first, merged) => {
|
|
80
|
+
const trivia = source.slice(first.getFullStart(), first.getStart(sourceFile));
|
|
81
|
+
const indent = /[ \t]*$/.exec(trivia)?.[0] ?? '';
|
|
82
|
+
const extra = merged
|
|
83
|
+
.flatMap((node) => typescript.getLeadingCommentRanges(source, node.getFullStart()) ?? [])
|
|
84
|
+
.map((range) => `${source.slice(range.pos, range.end)}\n${indent}`)
|
|
85
|
+
.join('');
|
|
86
|
+
return trivia + extra;
|
|
87
|
+
};
|
|
88
|
+
/**
|
|
89
|
+
* Removes a property with the comma after it; the last property has none, so the comma before it
|
|
90
|
+
* goes instead, leaving no dangling `, }`.
|
|
91
|
+
*/
|
|
92
|
+
const removal = (node) => {
|
|
93
|
+
const after = /^\s*,/.exec(source.slice(node.end));
|
|
94
|
+
if (after) {
|
|
95
|
+
return { start: node.getFullStart(), end: node.end + after[0].length, text: '' };
|
|
157
96
|
}
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
if (guard || shared) {
|
|
168
|
-
const members = [
|
|
169
|
-
guard ? `mode: ${text(guard.initializer)}` : undefined,
|
|
170
|
-
shared ? `sharedRows: ${text(shared.initializer)}` : undefined,
|
|
171
|
-
].filter((member) => member !== undefined);
|
|
172
|
-
const ordered = [guard, shared]
|
|
173
|
-
.filter((property) => property !== undefined)
|
|
174
|
-
.sort((a, b) => a.getStart(sourceFile) - b.getStart(sourceFile));
|
|
175
|
-
const [first, ...merged] = ordered;
|
|
176
|
-
localEdits.push({
|
|
177
|
-
start: first.getFullStart(),
|
|
178
|
-
end: first.end,
|
|
179
|
-
text: `${composeLeading(first, merged)}write: { ${members.join(', ')} }`,
|
|
180
|
-
});
|
|
181
|
-
for (const second of merged) {
|
|
182
|
-
localEdits.push(removal(second));
|
|
183
|
-
}
|
|
97
|
+
const before = /,\s*$/.exec(source.slice(0, node.getFullStart()));
|
|
98
|
+
const start = before ? node.getFullStart() - before[0].length : node.getFullStart();
|
|
99
|
+
return { start, end: node.end, text: '' };
|
|
100
|
+
};
|
|
101
|
+
/** Appends members to an object literal: after its last member, or inside empty braces. */
|
|
102
|
+
const append = (object, members) => {
|
|
103
|
+
const last = object.properties[object.properties.length - 1];
|
|
104
|
+
if (last) {
|
|
105
|
+
return { start: last.end, end: last.end, text: `, ${members.join(', ')}` };
|
|
184
106
|
}
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
const
|
|
190
|
-
const
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
107
|
+
const at = object.getStart(sourceFile) + 1;
|
|
108
|
+
return { start: at, end: at, text: ` ${members.join(', ')} ` };
|
|
109
|
+
};
|
|
110
|
+
const migrateSettings = (settings) => {
|
|
111
|
+
const properties = [...settings.properties];
|
|
112
|
+
const old = new Map();
|
|
113
|
+
let existingQuery;
|
|
114
|
+
for (const property of properties) {
|
|
115
|
+
if (typescript.isSpreadAssignment(property)) {
|
|
116
|
+
// A spread can carry old keys we cannot see.
|
|
117
|
+
report(property, 'settings contains a spread, so its keys are unknown; left untouched');
|
|
194
118
|
return;
|
|
195
119
|
}
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
120
|
+
if (typescript.isMethodDeclaration(property)) {
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
const name = nameOf(property);
|
|
124
|
+
if (name === undefined) {
|
|
125
|
+
report(property, 'settings contains a computed key; left untouched');
|
|
201
126
|
return;
|
|
202
127
|
}
|
|
203
|
-
if ((
|
|
204
|
-
if (!typescript.
|
|
205
|
-
report(
|
|
206
|
-
return;
|
|
207
|
-
}
|
|
208
|
-
const limitKeys = new Set(existingLimit.initializer.properties.map((property) => nameOf(property)));
|
|
209
|
-
if ((defaultLimit && limitKeys.has('default')) || (maxLimit && limitKeys.has('max'))) {
|
|
210
|
-
report(existingLimit, 'an old limit key and its new key are both set; left untouched');
|
|
128
|
+
if (OLD_KEYS.has(name)) {
|
|
129
|
+
if (!typescript.isPropertyAssignment(property)) {
|
|
130
|
+
report(property, `settings.${name} is a shorthand property; left untouched`);
|
|
211
131
|
return;
|
|
212
132
|
}
|
|
133
|
+
old.set(name, property);
|
|
213
134
|
}
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
additions.push(`filter: ${text(filter.initializer)}`);
|
|
135
|
+
if (name === 'query' && typescript.isPropertyAssignment(property)) {
|
|
136
|
+
existingQuery = property;
|
|
217
137
|
}
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
138
|
+
}
|
|
139
|
+
if (old.size === 0) {
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
const queryOld = [...old.keys()].filter((key) => key in QUERY_KEYS);
|
|
143
|
+
const localEdits = [];
|
|
144
|
+
// ---- scopeFilter -> access (rename the key, rewrite writeGuard / sharedRows inside)
|
|
145
|
+
const scopeFilter = old.get('scopeFilter');
|
|
146
|
+
if (scopeFilter) {
|
|
147
|
+
if (!typescript.isObjectLiteralExpression(scopeFilter.initializer)) {
|
|
148
|
+
report(scopeFilter, 'settings.scopeFilter is not an object literal; left untouched');
|
|
149
|
+
return;
|
|
224
150
|
}
|
|
225
|
-
|
|
226
|
-
|
|
151
|
+
const inner = scopeFilter.initializer;
|
|
152
|
+
const guard = inner.properties.find((property) => nameOf(property) === 'writeGuard' && typescript.isPropertyAssignment(property));
|
|
153
|
+
const shared = inner.properties.find((property) => nameOf(property) === 'sharedRows' && typescript.isPropertyAssignment(property));
|
|
154
|
+
const hasSpread = inner.properties.some((property) => typescript.isSpreadAssignment(property));
|
|
155
|
+
if (hasSpread) {
|
|
156
|
+
report(scopeFilter, 'settings.scopeFilter contains a spread; left untouched');
|
|
157
|
+
return;
|
|
227
158
|
}
|
|
228
|
-
if (
|
|
229
|
-
|
|
159
|
+
if (inner.properties.some((property) => nameOf(property) === 'write')) {
|
|
160
|
+
report(scopeFilter, 'settings.scopeFilter already has a write key; left untouched');
|
|
161
|
+
return;
|
|
230
162
|
}
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
163
|
+
localEdits.push({
|
|
164
|
+
start: scopeFilter.name.getStart(sourceFile),
|
|
165
|
+
end: scopeFilter.name.end,
|
|
166
|
+
text: 'access',
|
|
167
|
+
});
|
|
168
|
+
if (guard || shared) {
|
|
169
|
+
const members = [
|
|
170
|
+
guard ? `mode: ${text(guard.initializer)}` : undefined,
|
|
171
|
+
shared ? `sharedRows: ${text(shared.initializer)}` : undefined,
|
|
172
|
+
].filter((member) => member !== undefined);
|
|
173
|
+
const ordered = [guard, shared]
|
|
174
|
+
.filter((property) => property !== undefined)
|
|
175
|
+
.sort((a, b) => a.getStart(sourceFile) - b.getStart(sourceFile));
|
|
176
|
+
const [first, ...merged] = ordered;
|
|
177
|
+
localEdits.push({
|
|
178
|
+
start: first.getFullStart(),
|
|
179
|
+
end: first.end,
|
|
180
|
+
text: `${composeLeading(first, merged)}write: { ${members.join(', ')} }`,
|
|
181
|
+
});
|
|
182
|
+
for (const second of merged) {
|
|
183
|
+
localEdits.push(removal(second));
|
|
184
|
+
}
|
|
234
185
|
}
|
|
235
186
|
}
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
187
|
+
// ---- defaultFilter / defaultLimit / maxLimit -> query
|
|
188
|
+
if (queryOld.length > 0) {
|
|
189
|
+
const filter = old.get('defaultFilter');
|
|
190
|
+
const defaultLimit = old.get('defaultLimit');
|
|
191
|
+
const maxLimit = old.get('maxLimit');
|
|
192
|
+
if (existingQuery) {
|
|
193
|
+
if (!typescript.isObjectLiteralExpression(existingQuery.initializer)) {
|
|
194
|
+
report(existingQuery, 'settings.query is not an object literal; left untouched');
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
const queryObject = existingQuery.initializer;
|
|
198
|
+
const queryKeys = new Set(queryObject.properties.map((property) => nameOf(property)));
|
|
199
|
+
const existingLimit = queryObject.properties.find((property) => nameOf(property) === 'limit');
|
|
200
|
+
if (filter && queryKeys.has('filter')) {
|
|
201
|
+
report(filter, 'settings.defaultFilter and settings.query.filter are both set; left untouched');
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
if ((defaultLimit || maxLimit) && existingLimit) {
|
|
205
|
+
if (!typescript.isObjectLiteralExpression(existingLimit.initializer)) {
|
|
206
|
+
report(existingLimit, 'settings.query.limit is not an object literal; left untouched');
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
const limitKeys = new Set(existingLimit.initializer.properties.map((property) => nameOf(property)));
|
|
210
|
+
if ((defaultLimit && limitKeys.has('default')) || (maxLimit && limitKeys.has('max'))) {
|
|
211
|
+
report(existingLimit, 'an old limit key and its new key are both set; left untouched');
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
const additions = [];
|
|
216
|
+
if (filter) {
|
|
217
|
+
additions.push(`filter: ${text(filter.initializer)}`);
|
|
218
|
+
}
|
|
219
|
+
const limitMembers = [
|
|
220
|
+
defaultLimit ? `default: ${text(defaultLimit.initializer)}` : undefined,
|
|
221
|
+
maxLimit ? `max: ${text(maxLimit.initializer)}` : undefined,
|
|
222
|
+
].filter((member) => member !== undefined);
|
|
223
|
+
if (limitMembers.length > 0 && existingLimit) {
|
|
224
|
+
localEdits.push(append(existingLimit.initializer, limitMembers));
|
|
225
|
+
}
|
|
226
|
+
else if (limitMembers.length > 0) {
|
|
227
|
+
additions.push(`limit: { ${limitMembers.join(', ')} }`);
|
|
228
|
+
}
|
|
229
|
+
if (additions.length > 0) {
|
|
230
|
+
localEdits.push(append(queryObject, additions));
|
|
231
|
+
}
|
|
232
|
+
for (const key of queryOld) {
|
|
233
|
+
const property = old.get(key);
|
|
234
|
+
localEdits.push(removal(property));
|
|
235
|
+
}
|
|
240
236
|
}
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
237
|
+
else {
|
|
238
|
+
const members = [];
|
|
239
|
+
if (filter) {
|
|
240
|
+
members.push(`filter: ${text(filter.initializer)}`);
|
|
241
|
+
}
|
|
242
|
+
const limitMembers = [
|
|
243
|
+
defaultLimit ? `default: ${text(defaultLimit.initializer)}` : undefined,
|
|
244
|
+
maxLimit ? `max: ${text(maxLimit.initializer)}` : undefined,
|
|
245
|
+
].filter((member) => member !== undefined);
|
|
246
|
+
if (limitMembers.length > 0) {
|
|
247
|
+
members.push(`limit: { ${limitMembers.join(', ')} }`);
|
|
248
|
+
}
|
|
249
|
+
const ordered = queryOld
|
|
250
|
+
.map((key) => old.get(key))
|
|
251
|
+
.sort((a, b) => a.getStart(sourceFile) - b.getStart(sourceFile));
|
|
252
|
+
const [first, ...others] = ordered;
|
|
253
|
+
localEdits.push({
|
|
254
|
+
start: first.getFullStart(),
|
|
255
|
+
end: first.end,
|
|
256
|
+
text: `${composeLeading(first, others)}query: { ${members.join(', ')} }`,
|
|
257
|
+
});
|
|
258
|
+
for (const other of others) {
|
|
259
|
+
localEdits.push(removal(other));
|
|
260
|
+
}
|
|
247
261
|
}
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
262
|
+
}
|
|
263
|
+
edits.push(...localEdits);
|
|
264
|
+
rewritten += 1;
|
|
265
|
+
};
|
|
266
|
+
/**
|
|
267
|
+
* `skipMigrate: true` becomes `migration: { use: false }`; `skipMigrate: false` is the default and
|
|
268
|
+
* is removed. Anything that is not a boolean literal, or a model that already has `migration`, is
|
|
269
|
+
* reported and left alone.
|
|
270
|
+
*/
|
|
271
|
+
const migrateSkipMigrate = (argument) => {
|
|
272
|
+
const property = argument.properties.find((candidate) => nameOf(candidate) === 'skipMigrate');
|
|
273
|
+
if (!property) {
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
276
|
+
if (!typescript.isPropertyAssignment(property)) {
|
|
277
|
+
report(property, 'skipMigrate is a shorthand property; left untouched');
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
if (argument.properties.some((candidate) => nameOf(candidate) === 'migration')) {
|
|
281
|
+
report(property, 'skipMigrate and migration are both set; left untouched');
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
const { initializer } = property;
|
|
285
|
+
if (initializer.kind === typescript.SyntaxKind.TrueKeyword) {
|
|
286
|
+
edits.push({
|
|
287
|
+
start: property.getStart(sourceFile),
|
|
288
|
+
end: property.end,
|
|
289
|
+
text: 'migration: { use: false }',
|
|
256
290
|
});
|
|
257
|
-
for (const other of others) {
|
|
258
|
-
localEdits.push(removal(other));
|
|
259
|
-
}
|
|
260
291
|
}
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
edits.push({
|
|
286
|
-
start: property.getStart(sourceFile),
|
|
287
|
-
end: property.end,
|
|
288
|
-
text: 'migration: { use: false }',
|
|
289
|
-
});
|
|
290
|
-
}
|
|
291
|
-
else if (initializer.kind === typescript.SyntaxKind.FalseKeyword) {
|
|
292
|
-
edits.push(removal(property));
|
|
293
|
-
}
|
|
294
|
-
else {
|
|
295
|
-
report(property, 'skipMigrate is not a boolean literal; left untouched');
|
|
296
|
-
return;
|
|
297
|
-
}
|
|
298
|
-
rewritten += 1;
|
|
299
|
-
};
|
|
300
|
-
const visit = (node) => {
|
|
301
|
-
if (typescript.isDecorator(node) &&
|
|
302
|
-
typescript.isCallExpression(node.expression) &&
|
|
303
|
-
isModelCallee(node.expression.expression)) {
|
|
304
|
-
const argument = node.expression.arguments[0];
|
|
305
|
-
if (argument && typescript.isObjectLiteralExpression(argument)) {
|
|
306
|
-
const settingsProperty = argument.properties.find((property) => nameOf(property) === 'settings');
|
|
307
|
-
if (settingsProperty) {
|
|
308
|
-
if (typescript.isPropertyAssignment(settingsProperty) &&
|
|
309
|
-
typescript.isObjectLiteralExpression(settingsProperty.initializer)) {
|
|
310
|
-
migrateSettings(settingsProperty.initializer);
|
|
311
|
-
}
|
|
312
|
-
else {
|
|
313
|
-
report(settingsProperty, 'settings is not an object literal (a variable, a call or a shorthand); left untouched');
|
|
292
|
+
else if (initializer.kind === typescript.SyntaxKind.FalseKeyword) {
|
|
293
|
+
edits.push(removal(property));
|
|
294
|
+
}
|
|
295
|
+
else {
|
|
296
|
+
report(property, 'skipMigrate is not a boolean literal; left untouched');
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
rewritten += 1;
|
|
300
|
+
};
|
|
301
|
+
const visit = (node) => {
|
|
302
|
+
if (typescript.isDecorator(node) &&
|
|
303
|
+
typescript.isCallExpression(node.expression) &&
|
|
304
|
+
isModelCallee(node.expression.expression)) {
|
|
305
|
+
const argument = node.expression.arguments[0];
|
|
306
|
+
if (argument && typescript.isObjectLiteralExpression(argument)) {
|
|
307
|
+
const settingsProperty = argument.properties.find((property) => nameOf(property) === 'settings');
|
|
308
|
+
if (settingsProperty) {
|
|
309
|
+
if (typescript.isPropertyAssignment(settingsProperty) &&
|
|
310
|
+
typescript.isObjectLiteralExpression(settingsProperty.initializer)) {
|
|
311
|
+
migrateSettings(settingsProperty.initializer);
|
|
312
|
+
}
|
|
313
|
+
else {
|
|
314
|
+
report(settingsProperty, 'settings is not an object literal (a variable, a call or a shorthand); left untouched');
|
|
315
|
+
}
|
|
314
316
|
}
|
|
317
|
+
migrateSkipMigrate(argument);
|
|
318
|
+
}
|
|
319
|
+
else if (argument) {
|
|
320
|
+
report(argument, '@model argument is not an object literal (a variable or a call); left untouched');
|
|
315
321
|
}
|
|
316
|
-
migrateSkipMigrate(argument);
|
|
317
322
|
}
|
|
318
|
-
|
|
319
|
-
|
|
323
|
+
typescript.forEachChild(node, visit);
|
|
324
|
+
};
|
|
325
|
+
visit(sourceFile);
|
|
326
|
+
// Neighbouring removals can touch the same comma: merge those, then apply from the end so
|
|
327
|
+
// earlier offsets stay valid.
|
|
328
|
+
const ascending = [...edits].sort((a, b) => a.start - b.start || a.end - b.end);
|
|
329
|
+
const merged = [];
|
|
330
|
+
for (const edit of ascending) {
|
|
331
|
+
const previous = merged[merged.length - 1];
|
|
332
|
+
if (previous && edit.start < previous.end && previous.text === '' && edit.text === '') {
|
|
333
|
+
previous.end = Math.max(previous.end, edit.end);
|
|
334
|
+
continue;
|
|
320
335
|
}
|
|
336
|
+
merged.push({ ...edit });
|
|
321
337
|
}
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
// Neighbouring removals can touch the same comma: merge those, then apply from the end so
|
|
326
|
-
// earlier offsets stay valid.
|
|
327
|
-
const ascending = [...edits].sort((a, b) => a.start - b.start || a.end - b.end);
|
|
328
|
-
const merged = [];
|
|
329
|
-
for (const edit of ascending) {
|
|
330
|
-
const previous = merged[merged.length - 1];
|
|
331
|
-
if (previous && edit.start < previous.end && previous.text === '' && edit.text === '') {
|
|
332
|
-
previous.end = Math.max(previous.end, edit.end);
|
|
333
|
-
continue;
|
|
338
|
+
let output = source;
|
|
339
|
+
for (const edit of merged.reverse()) {
|
|
340
|
+
output = output.slice(0, edit.start) + edit.text + output.slice(edit.end);
|
|
334
341
|
}
|
|
335
|
-
|
|
336
|
-
}
|
|
337
|
-
let output = source;
|
|
338
|
-
for (const edit of merged.reverse()) {
|
|
339
|
-
output = output.slice(0, edit.start) + edit.text + output.slice(edit.end);
|
|
342
|
+
return { output, changed: output !== source, rewritten, reports };
|
|
340
343
|
}
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
exports.migrateModelSettings = migrateModelSettings;
|
|
344
|
+
}
|
|
345
|
+
exports.ModelSettingsCodemod = ModelSettingsCodemod;
|
|
344
346
|
//# sourceMappingURL=model-settings.js.map
|