@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.
Files changed (199) hide show
  1. package/LICENSE.md +1 -3
  2. package/README.md +1 -1
  3. package/dist/checks/catalog.d.ts +4 -2
  4. package/dist/checks/catalog.d.ts.map +1 -1
  5. package/dist/checks/catalog.js +72 -68
  6. package/dist/checks/catalog.js.map +1 -1
  7. package/dist/checks/module-cycles.d.ts +11 -9
  8. package/dist/checks/module-cycles.d.ts.map +1 -1
  9. package/dist/checks/module-cycles.js +32 -30
  10. package/dist/checks/module-cycles.js.map +1 -1
  11. package/dist/checks/purity/manifest.d.ts +44 -38
  12. package/dist/checks/purity/manifest.d.ts.map +1 -1
  13. package/dist/checks/purity/manifest.js +121 -115
  14. package/dist/checks/purity/manifest.js.map +1 -1
  15. package/dist/checks/purity/probe.d.ts +22 -18
  16. package/dist/checks/purity/probe.d.ts.map +1 -1
  17. package/dist/checks/purity/probe.js +101 -99
  18. package/dist/checks/purity/probe.js.map +1 -1
  19. package/dist/checks/purity/run.d.ts +18 -16
  20. package/dist/checks/purity/run.d.ts.map +1 -1
  21. package/dist/checks/purity/run.js +90 -86
  22. package/dist/checks/purity/run.js.map +1 -1
  23. package/dist/checks/test-controls.d.ts +17 -15
  24. package/dist/checks/test-controls.d.ts.map +1 -1
  25. package/dist/checks/test-controls.js +71 -69
  26. package/dist/checks/test-controls.js.map +1 -1
  27. package/dist/checks/test-dead-code.d.ts +5 -3
  28. package/dist/checks/test-dead-code.d.ts.map +1 -1
  29. package/dist/checks/test-dead-code.js +23 -21
  30. package/dist/checks/test-dead-code.js.map +1 -1
  31. package/dist/checks/test-mutate.d.ts +12 -7
  32. package/dist/checks/test-mutate.d.ts.map +1 -1
  33. package/dist/checks/test-mutate.js +59 -56
  34. package/dist/checks/test-mutate.js.map +1 -1
  35. package/dist/checks/test-weight.d.ts +11 -8
  36. package/dist/checks/test-weight.d.ts.map +1 -1
  37. package/dist/checks/test-weight.js +98 -94
  38. package/dist/checks/test-weight.js.map +1 -1
  39. package/dist/cli/check.d.ts.map +1 -1
  40. package/dist/cli/check.js +11 -8
  41. package/dist/cli/check.js.map +1 -1
  42. package/dist/cli/codemod/async-style.d.ts +62 -0
  43. package/dist/cli/codemod/async-style.d.ts.map +1 -0
  44. package/dist/cli/codemod/async-style.js +248 -0
  45. package/dist/cli/codemod/async-style.js.map +1 -0
  46. package/dist/cli/codemod/const-classes.d.ts.map +1 -1
  47. package/dist/cli/codemod/const-classes.js +2 -1
  48. package/dist/cli/codemod/const-classes.js.map +1 -1
  49. package/dist/cli/codemod/drop-redundant-casts.d.ts.map +1 -1
  50. package/dist/cli/codemod/drop-redundant-casts.js +2 -1
  51. package/dist/cli/codemod/drop-redundant-casts.js.map +1 -1
  52. package/dist/cli/codemod/files.d.ts +7 -0
  53. package/dist/cli/codemod/files.d.ts.map +1 -1
  54. package/dist/cli/codemod/files.js +33 -0
  55. package/dist/cli/codemod/files.js.map +1 -1
  56. package/dist/cli/codemod/index.d.ts.map +1 -1
  57. package/dist/cli/codemod/index.js +12 -3
  58. package/dist/cli/codemod/index.js.map +1 -1
  59. package/dist/cli/codemod/model-settings.d.ts +7 -5
  60. package/dist/cli/codemod/model-settings.d.ts.map +1 -1
  61. package/dist/cli/codemod/model-settings.js +293 -291
  62. package/dist/cli/codemod/model-settings.js.map +1 -1
  63. package/dist/cli/codemod/namespace-functions/index.d.ts +20 -0
  64. package/dist/cli/codemod/namespace-functions/index.d.ts.map +1 -0
  65. package/dist/cli/codemod/namespace-functions/index.js +182 -0
  66. package/dist/cli/codemod/namespace-functions/index.js.map +1 -0
  67. package/dist/cli/codemod/namespace-functions/logger-scope.d.ts +32 -0
  68. package/dist/cli/codemod/namespace-functions/logger-scope.d.ts.map +1 -0
  69. package/dist/cli/codemod/namespace-functions/logger-scope.js +128 -0
  70. package/dist/cli/codemod/namespace-functions/logger-scope.js.map +1 -0
  71. package/dist/cli/codemod/namespace-functions/mapping.d.ts +66 -0
  72. package/dist/cli/codemod/namespace-functions/mapping.d.ts.map +1 -0
  73. package/dist/cli/codemod/namespace-functions/mapping.js +99 -0
  74. package/dist/cli/codemod/namespace-functions/mapping.js.map +1 -0
  75. package/dist/cli/codemod/namespace-functions/mappings/boot.d.ts +7 -0
  76. package/dist/cli/codemod/namespace-functions/mappings/boot.d.ts.map +1 -0
  77. package/dist/cli/codemod/namespace-functions/mappings/boot.js +28 -0
  78. package/dist/cli/codemod/namespace-functions/mappings/boot.js.map +1 -0
  79. package/dist/cli/codemod/namespace-functions/mappings/connectors.d.ts +4 -0
  80. package/dist/cli/codemod/namespace-functions/mappings/connectors.d.ts.map +1 -0
  81. package/dist/cli/codemod/namespace-functions/mappings/connectors.js +218 -0
  82. package/dist/cli/codemod/namespace-functions/mappings/connectors.js.map +1 -0
  83. package/dist/cli/codemod/namespace-functions/mappings/core-server.d.ts +7 -0
  84. package/dist/cli/codemod/namespace-functions/mappings/core-server.d.ts.map +1 -0
  85. package/dist/cli/codemod/namespace-functions/mappings/core-server.js +162 -0
  86. package/dist/cli/codemod/namespace-functions/mappings/core-server.js.map +1 -0
  87. package/dist/cli/codemod/namespace-functions/mappings/core-worker.d.ts +7 -0
  88. package/dist/cli/codemod/namespace-functions/mappings/core-worker.d.ts.map +1 -0
  89. package/dist/cli/codemod/namespace-functions/mappings/core-worker.js +18 -0
  90. package/dist/cli/codemod/namespace-functions/mappings/core-worker.js.map +1 -0
  91. package/dist/cli/codemod/namespace-functions/mappings/dev-configs.d.ts +7 -0
  92. package/dist/cli/codemod/namespace-functions/mappings/dev-configs.d.ts.map +1 -0
  93. package/dist/cli/codemod/namespace-functions/mappings/dev-configs.js +192 -0
  94. package/dist/cli/codemod/namespace-functions/mappings/dev-configs.js.map +1 -0
  95. package/dist/cli/codemod/namespace-functions/mappings/filter.d.ts +7 -0
  96. package/dist/cli/codemod/namespace-functions/mappings/filter.d.ts.map +1 -0
  97. package/dist/cli/codemod/namespace-functions/mappings/filter.js +30 -0
  98. package/dist/cli/codemod/namespace-functions/mappings/filter.js.map +1 -0
  99. package/dist/cli/codemod/namespace-functions/mappings/helpers.d.ts +16 -0
  100. package/dist/cli/codemod/namespace-functions/mappings/helpers.d.ts.map +1 -0
  101. package/dist/cli/codemod/namespace-functions/mappings/helpers.js +535 -0
  102. package/dist/cli/codemod/namespace-functions/mappings/helpers.js.map +1 -0
  103. package/dist/cli/codemod/namespace-functions/mappings/index.d.ts +4 -0
  104. package/dist/cli/codemod/namespace-functions/mappings/index.d.ts.map +1 -0
  105. package/dist/cli/codemod/namespace-functions/mappings/index.js +25 -0
  106. package/dist/cli/codemod/namespace-functions/mappings/index.js.map +1 -0
  107. package/dist/cli/codemod/namespace-functions/mappings/inversion.d.ts +10 -0
  108. package/dist/cli/codemod/namespace-functions/mappings/inversion.d.ts.map +1 -0
  109. package/dist/cli/codemod/namespace-functions/mappings/inversion.js +81 -0
  110. package/dist/cli/codemod/namespace-functions/mappings/inversion.js.map +1 -0
  111. package/dist/cli/codemod/namespace-functions/mappings/kernel.d.ts +17 -0
  112. package/dist/cli/codemod/namespace-functions/mappings/kernel.d.ts.map +1 -0
  113. package/dist/cli/codemod/namespace-functions/mappings/kernel.js +229 -0
  114. package/dist/cli/codemod/namespace-functions/mappings/kernel.js.map +1 -0
  115. package/dist/cli/codemod/namespace-functions/rewriter.d.ts +61 -0
  116. package/dist/cli/codemod/namespace-functions/rewriter.d.ts.map +1 -0
  117. package/dist/cli/codemod/namespace-functions/rewriter.js +564 -0
  118. package/dist/cli/codemod/namespace-functions/rewriter.js.map +1 -0
  119. package/dist/cli/index.js +2 -0
  120. package/dist/cli/index.js.map +1 -1
  121. package/dist/compilers/bun.d.ts +1 -1
  122. package/dist/compilers/bun.js +1 -1
  123. package/dist/compilers/bun.js.map +1 -1
  124. package/dist/linters/house/await-support.d.ts +74 -0
  125. package/dist/linters/house/await-support.d.ts.map +1 -0
  126. package/dist/linters/house/await-support.js +238 -0
  127. package/dist/linters/house/await-support.js.map +1 -0
  128. package/dist/linters/house/common.d.ts +15 -9
  129. package/dist/linters/house/common.d.ts.map +1 -1
  130. package/dist/linters/house/common.js +20 -15
  131. package/dist/linters/house/common.js.map +1 -1
  132. package/dist/linters/house/configs.d.ts +26 -0
  133. package/dist/linters/house/configs.d.ts.map +1 -1
  134. package/dist/linters/house/configs.js +55 -1
  135. package/dist/linters/house/configs.js.map +1 -1
  136. package/dist/linters/house/const-class-values.rule.d.ts.map +1 -1
  137. package/dist/linters/house/const-class-values.rule.js +27 -19
  138. package/dist/linters/house/const-class-values.rule.js.map +1 -1
  139. package/dist/linters/house/effect-cleanup-consistency.rule.d.ts +17 -0
  140. package/dist/linters/house/effect-cleanup-consistency.rule.d.ts.map +1 -0
  141. package/dist/linters/house/effect-cleanup-consistency.rule.js +87 -0
  142. package/dist/linters/house/effect-cleanup-consistency.rule.js.map +1 -0
  143. package/dist/linters/house/hook-support.d.ts +29 -0
  144. package/dist/linters/house/hook-support.d.ts.map +1 -0
  145. package/dist/linters/house/hook-support.js +72 -0
  146. package/dist/linters/house/hook-support.js.map +1 -0
  147. package/dist/linters/house/index.d.ts +9 -0
  148. package/dist/linters/house/index.d.ts.map +1 -1
  149. package/dist/linters/house/index.js +9 -0
  150. package/dist/linters/house/index.js.map +1 -1
  151. package/dist/linters/house/max-params-own.rule.d.ts.map +1 -1
  152. package/dist/linters/house/max-params-own.rule.js +33 -24
  153. package/dist/linters/house/max-params-own.rule.js.map +1 -1
  154. package/dist/linters/house/no-async-hook-callback.rule.d.ts +18 -0
  155. package/dist/linters/house/no-async-hook-callback.rule.d.ts.map +1 -0
  156. package/dist/linters/house/no-async-hook-callback.rule.js +50 -0
  157. package/dist/linters/house/no-async-hook-callback.rule.js.map +1 -0
  158. package/dist/linters/house/no-await-promise-chain.rule.d.ts +24 -0
  159. package/dist/linters/house/no-await-promise-chain.rule.d.ts.map +1 -0
  160. package/dist/linters/house/no-await-promise-chain.rule.js +298 -0
  161. package/dist/linters/house/no-await-promise-chain.rule.js.map +1 -0
  162. package/dist/linters/house/no-inline-await.rule.d.ts +20 -0
  163. package/dist/linters/house/no-inline-await.rule.d.ts.map +1 -0
  164. package/dist/linters/house/no-inline-await.rule.js +70 -0
  165. package/dist/linters/house/no-inline-await.rule.js.map +1 -0
  166. package/dist/linters/house/no-new-error.rule.js +4 -4
  167. package/dist/linters/house/no-new-error.rule.js.map +1 -1
  168. package/dist/linters/house/no-return-await.rule.d.ts +15 -0
  169. package/dist/linters/house/no-return-await.rule.d.ts.map +1 -0
  170. package/dist/linters/house/no-return-await.rule.js +92 -0
  171. package/dist/linters/house/no-return-await.rule.js.map +1 -0
  172. package/dist/linters/house/no-silent-catch.rule.d.ts +22 -0
  173. package/dist/linters/house/no-silent-catch.rule.d.ts.map +1 -0
  174. package/dist/linters/house/no-silent-catch.rule.js +88 -0
  175. package/dist/linters/house/no-silent-catch.rule.js.map +1 -0
  176. package/dist/linters/house/plugin.d.ts.map +1 -1
  177. package/dist/linters/house/plugin.js +12 -0
  178. package/dist/linters/house/plugin.js.map +1 -1
  179. package/dist/linters/house/promise-chain-support.d.ts +49 -0
  180. package/dist/linters/house/promise-chain-support.d.ts.map +1 -0
  181. package/dist/linters/house/promise-chain-support.js +188 -0
  182. package/dist/linters/house/promise-chain-support.js.map +1 -0
  183. package/dist/linters/rules/no-restricted-properties.rule.js +1 -1
  184. package/dist/linters/rules/no-restricted-properties.rule.js.map +1 -1
  185. package/dist/testing/rejection.helper.d.ts +25 -20
  186. package/dist/testing/rejection.helper.d.ts.map +1 -1
  187. package/dist/testing/rejection.helper.js +33 -31
  188. package/dist/testing/rejection.helper.js.map +1 -1
  189. package/dist/testing/repo-tmp.d.ts +19 -9
  190. package/dist/testing/repo-tmp.d.ts.map +1 -1
  191. package/dist/testing/repo-tmp.js +50 -48
  192. package/dist/testing/repo-tmp.js.map +1 -1
  193. package/dist/testing/spawn.helper.d.ts +12 -10
  194. package/dist/testing/spawn.helper.d.ts.map +1 -1
  195. package/dist/testing/spawn.helper.js +22 -16
  196. package/dist/testing/spawn.helper.js.map +1 -1
  197. package/dist/testing/tmpdir-preload.js +1 -1
  198. package/dist/testing/tmpdir-preload.js.map +1 -1
  199. package/package.json +2 -3
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.migrateModelSettings = void 0;
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
- const migrateModelSettings = (opts) => {
20
- const { typescript, fileName, source } = opts;
21
- const sourceFile = typescript.createSourceFile(fileName, source, typescript.ScriptTarget.Latest, true);
22
- const edits = [];
23
- const reports = [];
24
- let rewritten = 0;
25
- const report = (node, reason) => {
26
- const { line, character } = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile));
27
- reports.push({ line: line + 1, column: character + 1, reason });
28
- };
29
- const nameOf = (property) => {
30
- if (!typescript.isPropertyAssignment(property) &&
31
- !typescript.isShorthandPropertyAssignment(property)) {
32
- return undefined;
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
- const isModelCallee = (callee) => {
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
- if (typescript.isMethodDeclaration(property)) {
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 name = nameOf(property);
123
- if (name === undefined) {
124
- report(property, 'settings contains a computed key; left untouched');
125
- return;
52
+ const bindings = statement.importClause?.namedBindings;
53
+ if (!bindings) {
54
+ continue;
126
55
  }
127
- if (OLD_KEYS.has(name)) {
128
- if (!typescript.isPropertyAssignment(property)) {
129
- report(property, `settings.${name} is a shorthand property; left untouched`);
130
- return;
131
- }
132
- old.set(name, property);
56
+ if (typescript.isNamespaceImport(bindings)) {
57
+ namespaces.add(bindings.name.text);
58
+ continue;
133
59
  }
134
- if (name === 'query' && typescript.isPropertyAssignment(property)) {
135
- existingQuery = property;
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
- if (old.size === 0) {
139
- return;
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
- const inner = scopeFilter.initializer;
151
- const guard = inner.properties.find((property) => nameOf(property) === 'writeGuard' && typescript.isPropertyAssignment(property));
152
- const shared = inner.properties.find((property) => nameOf(property) === 'sharedRows' && typescript.isPropertyAssignment(property));
153
- const hasSpread = inner.properties.some((property) => typescript.isSpreadAssignment(property));
154
- if (hasSpread) {
155
- report(scopeFilter, 'settings.scopeFilter contains a spread; left untouched');
156
- return;
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
- if (inner.properties.some((property) => nameOf(property) === 'write')) {
159
- report(scopeFilter, 'settings.scopeFilter already has a write key; left untouched');
160
- return;
161
- }
162
- localEdits.push({
163
- start: scopeFilter.name.getStart(sourceFile),
164
- end: scopeFilter.name.end,
165
- text: 'access',
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
- // ---- defaultFilter / defaultLimit / maxLimit -> query
187
- if (queryOld.length > 0) {
188
- const filter = old.get('defaultFilter');
189
- const defaultLimit = old.get('defaultLimit');
190
- const maxLimit = old.get('maxLimit');
191
- if (existingQuery) {
192
- if (!typescript.isObjectLiteralExpression(existingQuery.initializer)) {
193
- report(existingQuery, 'settings.query is not an object literal; left untouched');
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
- const queryObject = existingQuery.initializer;
197
- const queryKeys = new Set(queryObject.properties.map((property) => nameOf(property)));
198
- const existingLimit = queryObject.properties.find((property) => nameOf(property) === 'limit');
199
- if (filter && queryKeys.has('filter')) {
200
- report(filter, 'settings.defaultFilter and settings.query.filter are both set; left untouched');
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 ((defaultLimit || maxLimit) && existingLimit) {
204
- if (!typescript.isObjectLiteralExpression(existingLimit.initializer)) {
205
- report(existingLimit, 'settings.query.limit is not an object literal; left untouched');
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
- const additions = [];
215
- if (filter) {
216
- additions.push(`filter: ${text(filter.initializer)}`);
135
+ if (name === 'query' && typescript.isPropertyAssignment(property)) {
136
+ existingQuery = property;
217
137
  }
218
- const limitMembers = [
219
- defaultLimit ? `default: ${text(defaultLimit.initializer)}` : undefined,
220
- maxLimit ? `max: ${text(maxLimit.initializer)}` : undefined,
221
- ].filter((member) => member !== undefined);
222
- if (limitMembers.length > 0 && existingLimit) {
223
- localEdits.push(append(existingLimit.initializer, limitMembers));
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
- else if (limitMembers.length > 0) {
226
- additions.push(`limit: { ${limitMembers.join(', ')} }`);
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 (additions.length > 0) {
229
- localEdits.push(append(queryObject, additions));
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
- for (const key of queryOld) {
232
- const property = old.get(key);
233
- localEdits.push(removal(property));
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
- else {
237
- const members = [];
238
- if (filter) {
239
- members.push(`filter: ${text(filter.initializer)}`);
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
- const limitMembers = [
242
- defaultLimit ? `default: ${text(defaultLimit.initializer)}` : undefined,
243
- maxLimit ? `max: ${text(maxLimit.initializer)}` : undefined,
244
- ].filter((member) => member !== undefined);
245
- if (limitMembers.length > 0) {
246
- members.push(`limit: { ${limitMembers.join(', ')} }`);
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
- const ordered = queryOld
249
- .map((key) => old.get(key))
250
- .sort((a, b) => a.getStart(sourceFile) - b.getStart(sourceFile));
251
- const [first, ...others] = ordered;
252
- localEdits.push({
253
- start: first.getFullStart(),
254
- end: first.end,
255
- text: `${composeLeading(first, others)}query: { ${members.join(', ')} }`,
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
- edits.push(...localEdits);
263
- rewritten += 1;
264
- };
265
- /**
266
- * `skipMigrate: true` becomes `migration: { use: false }`; `skipMigrate: false` is the default and
267
- * is removed. Anything that is not a boolean literal, or a model that already has `migration`, is
268
- * reported and left alone.
269
- */
270
- const migrateSkipMigrate = (argument) => {
271
- const property = argument.properties.find((candidate) => nameOf(candidate) === 'skipMigrate');
272
- if (!property) {
273
- return;
274
- }
275
- if (!typescript.isPropertyAssignment(property)) {
276
- report(property, 'skipMigrate is a shorthand property; left untouched');
277
- return;
278
- }
279
- if (argument.properties.some((candidate) => nameOf(candidate) === 'migration')) {
280
- report(property, 'skipMigrate and migration are both set; left untouched');
281
- return;
282
- }
283
- const { initializer } = property;
284
- if (initializer.kind === typescript.SyntaxKind.TrueKeyword) {
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
- else if (argument) {
319
- report(argument, '@model argument is not an object literal (a variable or a call); left untouched');
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
- typescript.forEachChild(node, visit);
323
- };
324
- visit(sourceFile);
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
- merged.push({ ...edit });
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
- return { output, changed: output !== source, rewritten, reports };
342
- };
343
- exports.migrateModelSettings = migrateModelSettings;
344
+ }
345
+ exports.ModelSettingsCodemod = ModelSettingsCodemod;
344
346
  //# sourceMappingURL=model-settings.js.map