@tailor-platform/sdk-codemod 0.3.0-next.3 → 0.3.0-next.4
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/CHANGELOG.md +23 -0
- package/dist/codemods/{ast-grep-helpers-Bfn39biW.js → ast-grep-helpers-D3FXAKNz.js} +1 -1
- package/dist/codemods/v2/auth-connection-token-helper/scripts/transform.js +1 -1
- package/dist/codemods/v2/db-type-to-table/scripts/transform.js +383 -0
- package/dist/codemods/v2/runtime-globals-opt-in/scripts/transform.js +1 -1
- package/dist/codemods/v2/runtime-subpath-namespace/scripts/transform.js +792 -0
- package/dist/index.js +75 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,28 @@
|
|
|
1
1
|
# @tailor-platform/sdk-codemod
|
|
2
2
|
|
|
3
|
+
## 0.3.0-next.4
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#1693](https://github.com/tailor-platform/sdk/pull/1693) [`4751214`](https://github.com/tailor-platform/sdk/commit/4751214c0923e094a844f9ce322279a47e871075) Thanks [@dqn](https://github.com/dqn)! - Rename the TailorDB schema builder from `db.type()` to `db.table()`.
|
|
8
|
+
|
|
9
|
+
Update TailorDB definitions:
|
|
10
|
+
|
|
11
|
+
```diff
|
|
12
|
+
import { db } from "@tailor-platform/sdk";
|
|
13
|
+
|
|
14
|
+
-export const user = db.type("User", {
|
|
15
|
+
+export const user = db.table("User", {
|
|
16
|
+
name: db.string(),
|
|
17
|
+
});
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
- [#1704](https://github.com/tailor-platform/sdk/pull/1704) [`9c81d9c`](https://github.com/tailor-platform/sdk/commit/9c81d9c18b1d29b3e9307ea17fe54c8ce55f4dda) Thanks [@dqn](https://github.com/dqn)! - Remove flat value and default exports from `@tailor-platform/sdk/runtime/*` subpath modules. Import each subpath through its self-named namespace export instead, for example `import { iconv } from "@tailor-platform/sdk/runtime/iconv"`.
|
|
21
|
+
|
|
22
|
+
The aggregate `@tailor-platform/sdk/runtime` entry remains named-only, and its deprecated `file.deleteFile` alias is removed in favor of `file.delete`. The v2 codemod rewrites straightforward namespace-star subpath imports, flat named value imports, and aggregate `file.deleteFile` calls to the new namespace-object style.
|
|
23
|
+
|
|
24
|
+
`TailorContextAPI` and `TailorWorkflowAPI` now describe the SDK wrapper objects. Code that types the platform-provided `globalThis.tailor.context` or `globalThis.tailor.workflow` objects directly must use `PlatformContextAPI` or `PlatformWorkflowAPI` instead.
|
|
25
|
+
|
|
3
26
|
## 0.3.0-next.3
|
|
4
27
|
|
|
5
28
|
### Patch Changes
|
|
@@ -168,4 +168,4 @@ function localDeclarationNames(root) {
|
|
|
168
168
|
return names;
|
|
169
169
|
}
|
|
170
170
|
//#endregion
|
|
171
|
-
export {
|
|
171
|
+
export { importSource as a, localDeclarationNames as c, importBindings as i, collectBindingNames as n, importSpecNames as o, findImportStatements as r, isTypeOnlyImport as s, buildAddNamedImportEdit as t };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as
|
|
1
|
+
import { a as importSource, c as localDeclarationNames, i as importBindings, o as importSpecNames, r as findImportStatements, s as isTypeOnlyImport, t as buildAddNamedImportEdit } from "../../../ast-grep-helpers-D3FXAKNz.js";
|
|
2
2
|
import { Lang, parse } from "@ast-grep/napi";
|
|
3
3
|
//#region codemods/v2/auth-connection-token-helper/scripts/transform.ts
|
|
4
4
|
const RUNTIME_MODULE = "@tailor-platform/sdk/runtime";
|
|
@@ -0,0 +1,383 @@
|
|
|
1
|
+
import { a as importSource, i as importBindings, r as findImportStatements } from "../../../ast-grep-helpers-D3FXAKNz.js";
|
|
2
|
+
import { Lang, parse } from "@ast-grep/napi";
|
|
3
|
+
//#region codemods/v2/db-type-to-table/scripts/transform.ts
|
|
4
|
+
const SDK_MODULE = "@tailor-platform/sdk";
|
|
5
|
+
function sourceLang(filePath, source) {
|
|
6
|
+
return filePath.endsWith(".tsx") || filePath.endsWith(".jsx") || source.includes("</") ? Lang.Tsx : Lang.TypeScript;
|
|
7
|
+
}
|
|
8
|
+
function namespaceImportNames(importStmt) {
|
|
9
|
+
return importStmt.findAll({ rule: { kind: "namespace_import" } }).flatMap((node) => node.children().filter((child) => child.kind() === "identifier")).map((node) => node.text());
|
|
10
|
+
}
|
|
11
|
+
function isInsideImportStatement(node) {
|
|
12
|
+
let current = node.parent();
|
|
13
|
+
while (current) {
|
|
14
|
+
if (current.kind() === "import_statement") return true;
|
|
15
|
+
current = current.parent();
|
|
16
|
+
}
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
function isBindingLeafKind(kind) {
|
|
20
|
+
return kind === "identifier" || kind === "shorthand_property_identifier_pattern";
|
|
21
|
+
}
|
|
22
|
+
function isBindingPatternKind(kind) {
|
|
23
|
+
return isBindingLeafKind(kind) || kind === "object_pattern" || kind === "array_pattern" || kind === "rest_pattern";
|
|
24
|
+
}
|
|
25
|
+
function collectBindingNodes(node, names, result) {
|
|
26
|
+
if (isBindingLeafKind(node.kind())) {
|
|
27
|
+
if (names.has(node.text())) result.push(node);
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
for (const child of node.children()) {
|
|
31
|
+
if (child.kind() === "property_identifier") continue;
|
|
32
|
+
if (child.kind() === "=") break;
|
|
33
|
+
collectBindingNodes(child, names, result);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
function bindingNodes(node, names) {
|
|
37
|
+
const result = [];
|
|
38
|
+
collectBindingNodes(node, names, result);
|
|
39
|
+
return result;
|
|
40
|
+
}
|
|
41
|
+
function directBindingNodes(node, names) {
|
|
42
|
+
const result = [];
|
|
43
|
+
for (const child of node.children()) {
|
|
44
|
+
if (child.kind() === "=") break;
|
|
45
|
+
if (isBindingPatternKind(child.kind())) collectBindingNodes(child, names, result);
|
|
46
|
+
}
|
|
47
|
+
return result;
|
|
48
|
+
}
|
|
49
|
+
function firstDeclaratorChild(node) {
|
|
50
|
+
return node.children().find((child) => child.kind() !== "=") ?? null;
|
|
51
|
+
}
|
|
52
|
+
function declaratorValue(node) {
|
|
53
|
+
const children = node.children();
|
|
54
|
+
const equalsIndex = children.findIndex((child) => child.kind() === "=");
|
|
55
|
+
if (equalsIndex === -1) return null;
|
|
56
|
+
return children.slice(equalsIndex + 1).find((child) => child.kind() !== "comment") ?? null;
|
|
57
|
+
}
|
|
58
|
+
function assignmentTarget(node) {
|
|
59
|
+
const children = node.children();
|
|
60
|
+
const equalsIndex = children.findIndex((child) => child.kind() === "=");
|
|
61
|
+
if (equalsIndex === -1) return null;
|
|
62
|
+
return children.slice(0, equalsIndex).find((child) => child.kind() !== "comment") ?? null;
|
|
63
|
+
}
|
|
64
|
+
function assignmentValue(node) {
|
|
65
|
+
const children = node.children();
|
|
66
|
+
const equalsIndex = children.findIndex((child) => child.kind() === "=");
|
|
67
|
+
if (equalsIndex === -1) return null;
|
|
68
|
+
return children.slice(equalsIndex + 1).find((child) => child.kind() !== "comment") ?? null;
|
|
69
|
+
}
|
|
70
|
+
function parameterDefaultTarget(node) {
|
|
71
|
+
const children = node.children();
|
|
72
|
+
const equalsIndex = children.findIndex((child) => child.kind() === "=");
|
|
73
|
+
if (equalsIndex === -1) return null;
|
|
74
|
+
return children.slice(0, equalsIndex).find((child) => isBindingPatternKind(child.kind())) ?? null;
|
|
75
|
+
}
|
|
76
|
+
function parameterDefaultValue(node) {
|
|
77
|
+
const children = node.children();
|
|
78
|
+
const equalsIndex = children.findIndex((child) => child.kind() === "=");
|
|
79
|
+
if (equalsIndex === -1) return null;
|
|
80
|
+
return children.slice(equalsIndex + 1).find((child) => child.kind() !== "comment") ?? null;
|
|
81
|
+
}
|
|
82
|
+
function addShadowedRange(shadowedRanges, name, scopeNode) {
|
|
83
|
+
const range = scopeNode.range();
|
|
84
|
+
if (!shadowedRanges.has(name)) shadowedRanges.set(name, []);
|
|
85
|
+
shadowedRanges.get(name).push({
|
|
86
|
+
start: range.start.index,
|
|
87
|
+
end: range.end.index
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
function nearestScope(node) {
|
|
91
|
+
let current = node.parent();
|
|
92
|
+
while (current) {
|
|
93
|
+
const kind = current.kind();
|
|
94
|
+
if (kind === "statement_block" || kind === "program" || kind === "switch_body" || kind === "for_statement" || kind === "for_in_statement") return current;
|
|
95
|
+
current = current.parent();
|
|
96
|
+
}
|
|
97
|
+
return node;
|
|
98
|
+
}
|
|
99
|
+
function functionScope(node) {
|
|
100
|
+
let current = node.parent();
|
|
101
|
+
while (current) {
|
|
102
|
+
const kind = current.kind();
|
|
103
|
+
if (kind === "function_declaration" || kind === "function_expression" || kind === "arrow_function" || kind === "method_definition" || kind === "program") return current;
|
|
104
|
+
current = current.parent();
|
|
105
|
+
}
|
|
106
|
+
return node;
|
|
107
|
+
}
|
|
108
|
+
function variableDeclarationScope(node) {
|
|
109
|
+
const declaration = node.parent();
|
|
110
|
+
if (/^var\b/.test(declaration?.text().trimStart() ?? "")) return functionScope(node);
|
|
111
|
+
return nearestScope(node);
|
|
112
|
+
}
|
|
113
|
+
function parameterScope(node) {
|
|
114
|
+
let current = node.parent();
|
|
115
|
+
while (current) {
|
|
116
|
+
const kind = current.kind();
|
|
117
|
+
if (kind === "formal_parameters") {
|
|
118
|
+
current = current.parent();
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
121
|
+
if (kind === "function_declaration" || kind === "function_expression" || kind === "arrow_function" || kind === "method_definition") return current;
|
|
122
|
+
break;
|
|
123
|
+
}
|
|
124
|
+
return nearestScope(node);
|
|
125
|
+
}
|
|
126
|
+
function buildShadowedRanges(root, names) {
|
|
127
|
+
const shadowedRanges = /* @__PURE__ */ new Map();
|
|
128
|
+
for (const decl of root.findAll({ rule: { kind: "variable_declarator" } })) {
|
|
129
|
+
if (isInsideImportStatement(decl)) continue;
|
|
130
|
+
const binding = firstDeclaratorChild(decl);
|
|
131
|
+
if (!binding) continue;
|
|
132
|
+
for (const name of bindingNodes(binding, names)) addShadowedRange(shadowedRanges, name.text(), variableDeclarationScope(decl));
|
|
133
|
+
}
|
|
134
|
+
for (const decl of root.findAll({ rule: { any: [
|
|
135
|
+
{ kind: "function_declaration" },
|
|
136
|
+
{ kind: "class_declaration" },
|
|
137
|
+
{ kind: "enum_declaration" }
|
|
138
|
+
] } })) {
|
|
139
|
+
const name = decl.children().find((child) => child.kind() === "identifier" && names.has(child.text()));
|
|
140
|
+
if (name) addShadowedRange(shadowedRanges, name.text(), nearestScope(decl));
|
|
141
|
+
}
|
|
142
|
+
for (const param of root.findAll({ rule: { any: [{ kind: "required_parameter" }, { kind: "optional_parameter" }] } })) for (const name of directBindingNodes(param, names)) addShadowedRange(shadowedRanges, name.text(), parameterScope(param));
|
|
143
|
+
for (const arrow of root.findAll({ rule: { kind: "arrow_function" } })) {
|
|
144
|
+
const children = arrow.children();
|
|
145
|
+
const arrowIndex = children.findIndex((child) => child.kind() === "=>");
|
|
146
|
+
if (arrowIndex === -1) continue;
|
|
147
|
+
for (const child of children.slice(0, arrowIndex)) {
|
|
148
|
+
if (child.kind() === "=") break;
|
|
149
|
+
if (!isBindingPatternKind(child.kind())) continue;
|
|
150
|
+
for (const name of bindingNodes(child, names)) addShadowedRange(shadowedRanges, name.text(), arrow);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
for (const catchClause of root.findAll({ rule: { kind: "catch_clause" } })) for (const name of directBindingNodes(catchClause, names)) addShadowedRange(shadowedRanges, name.text(), catchClause);
|
|
154
|
+
for (const loop of root.findAll({ rule: { kind: "for_in_statement" } })) {
|
|
155
|
+
const children = loop.children();
|
|
156
|
+
const keywordIndex = children.findIndex((child) => child.kind() === "in" || child.kind() === "of");
|
|
157
|
+
if (keywordIndex === -1) continue;
|
|
158
|
+
for (const child of children.slice(0, keywordIndex)) for (const name of bindingNodes(child, names)) addShadowedRange(shadowedRanges, name.text(), loop);
|
|
159
|
+
}
|
|
160
|
+
return shadowedRanges;
|
|
161
|
+
}
|
|
162
|
+
function isShadowed(node, shadowedRanges) {
|
|
163
|
+
const ranges = shadowedRanges.get(node.text());
|
|
164
|
+
if (!ranges) return false;
|
|
165
|
+
const position = node.range().start.index;
|
|
166
|
+
return ranges.some((range) => position >= range.start && position < range.end);
|
|
167
|
+
}
|
|
168
|
+
function unwrapExpression(node) {
|
|
169
|
+
let current = node;
|
|
170
|
+
while (current) {
|
|
171
|
+
const kind = current.kind();
|
|
172
|
+
if (kind === "parenthesized_expression") {
|
|
173
|
+
current = current.children().find((child) => child.kind() !== "(" && child.kind() !== ")") ?? null;
|
|
174
|
+
continue;
|
|
175
|
+
}
|
|
176
|
+
if (kind === "as_expression" || kind === "satisfies_expression" || kind === "non_null_expression") {
|
|
177
|
+
current = current.children()[0] ?? null;
|
|
178
|
+
continue;
|
|
179
|
+
}
|
|
180
|
+
if (kind === "type_assertion") {
|
|
181
|
+
current = current.children().find((child) => child.kind() !== "type_arguments") ?? null;
|
|
182
|
+
continue;
|
|
183
|
+
}
|
|
184
|
+
return current;
|
|
185
|
+
}
|
|
186
|
+
return null;
|
|
187
|
+
}
|
|
188
|
+
function isSdkDbMember(object, dbNames, namespaceNames, shadowedRanges) {
|
|
189
|
+
const unwrapped = unwrapExpression(object);
|
|
190
|
+
if (!unwrapped) return false;
|
|
191
|
+
if (unwrapped.kind() === "identifier") return dbNames.has(unwrapped.text()) && !isShadowed(unwrapped, shadowedRanges);
|
|
192
|
+
if (unwrapped.kind() !== "member_expression") return false;
|
|
193
|
+
const base = unwrapExpression(unwrapped.field("object"));
|
|
194
|
+
const property = memberProperty(unwrapped);
|
|
195
|
+
return base?.kind() === "identifier" && namespaceNames.has(base.text()) && !isShadowed(base, shadowedRanges) && property?.text() === "db";
|
|
196
|
+
}
|
|
197
|
+
function memberProperty(member) {
|
|
198
|
+
const children = member.children();
|
|
199
|
+
for (let index = children.length - 1; index >= 0; index -= 1) {
|
|
200
|
+
const child = children[index];
|
|
201
|
+
if (child.kind() === "property_identifier") return child;
|
|
202
|
+
}
|
|
203
|
+
return member.field("property");
|
|
204
|
+
}
|
|
205
|
+
function typeStringLiteral(node) {
|
|
206
|
+
if (!node) return null;
|
|
207
|
+
const kind = node.kind();
|
|
208
|
+
if (kind !== "string" && kind !== "template_string") return null;
|
|
209
|
+
const fragments = node.children().filter((child) => child.kind() === "string_fragment");
|
|
210
|
+
return fragments.length === 1 && fragments[0].text() === "type" ? node : null;
|
|
211
|
+
}
|
|
212
|
+
function replaceStringLiteralValue(node, value) {
|
|
213
|
+
const text = node.text();
|
|
214
|
+
const quote = text.startsWith("'") ? "'" : text.startsWith("`") ? "`" : "\"";
|
|
215
|
+
return node.replace(`${quote}${value}${quote}`);
|
|
216
|
+
}
|
|
217
|
+
function hasTypeBuilderUse(root, name, afterIndex) {
|
|
218
|
+
for (const member of root.findAll({ rule: { kind: "member_expression" } })) {
|
|
219
|
+
if (member.range().start.index <= afterIndex) continue;
|
|
220
|
+
if (memberProperty(member)?.text() !== "type") continue;
|
|
221
|
+
const object = unwrapExpression(member.field("object"));
|
|
222
|
+
if (object?.kind() === "identifier" && object.text() === name) return true;
|
|
223
|
+
}
|
|
224
|
+
for (const subscript of root.findAll({ rule: { kind: "subscript_expression" } })) {
|
|
225
|
+
if (subscript.range().start.index <= afterIndex) continue;
|
|
226
|
+
if (!typeStringLiteral(subscript.field("index"))) continue;
|
|
227
|
+
const object = unwrapExpression(subscript.field("object"));
|
|
228
|
+
if (object?.kind() === "identifier" && object.text() === name) return true;
|
|
229
|
+
}
|
|
230
|
+
return false;
|
|
231
|
+
}
|
|
232
|
+
function transform(source, filePath) {
|
|
233
|
+
if (!source.includes("type") || !source.includes(SDK_MODULE)) return null;
|
|
234
|
+
let root;
|
|
235
|
+
try {
|
|
236
|
+
root = parse(sourceLang(filePath, source), source).root();
|
|
237
|
+
} catch {
|
|
238
|
+
return null;
|
|
239
|
+
}
|
|
240
|
+
const imports = findImportStatements(root).filter((importStmt) => importSource(importStmt) === SDK_MODULE);
|
|
241
|
+
if (imports.length === 0) return null;
|
|
242
|
+
const dbNames = /* @__PURE__ */ new Set();
|
|
243
|
+
const namespaceNames = /* @__PURE__ */ new Set();
|
|
244
|
+
for (const importStmt of imports) {
|
|
245
|
+
for (const binding of importBindings(importStmt)) if (binding.importedName === "db") dbNames.add(binding.localName);
|
|
246
|
+
for (const name of namespaceImportNames(importStmt)) namespaceNames.add(name);
|
|
247
|
+
}
|
|
248
|
+
if (dbNames.size === 0 && namespaceNames.size === 0) return null;
|
|
249
|
+
const shadowedRanges = buildShadowedRanges(root, /* @__PURE__ */ new Set([...dbNames, ...namespaceNames]));
|
|
250
|
+
const edits = [];
|
|
251
|
+
for (const member of root.findAll({ rule: { kind: "member_expression" } })) {
|
|
252
|
+
const property = memberProperty(member);
|
|
253
|
+
if (property?.text() !== "type") continue;
|
|
254
|
+
if (!isSdkDbMember(member.field("object"), dbNames, namespaceNames, shadowedRanges)) continue;
|
|
255
|
+
edits.push(property.replace("table"));
|
|
256
|
+
}
|
|
257
|
+
for (const subscript of root.findAll({ rule: { kind: "subscript_expression" } })) {
|
|
258
|
+
const index = typeStringLiteral(subscript.field("index"));
|
|
259
|
+
if (!index) continue;
|
|
260
|
+
if (!isSdkDbMember(subscript.field("object"), dbNames, namespaceNames, shadowedRanges)) continue;
|
|
261
|
+
edits.push(replaceStringLiteralValue(index, "table"));
|
|
262
|
+
}
|
|
263
|
+
return edits.length > 0 ? root.commitEdits(edits) : null;
|
|
264
|
+
}
|
|
265
|
+
function lineForIndex(source, index) {
|
|
266
|
+
return source.slice(0, index).split(/\r\n|\r|\n/).length;
|
|
267
|
+
}
|
|
268
|
+
function excerptAtIndex(source, index) {
|
|
269
|
+
const lineStart = Math.max(source.lastIndexOf("\n", index - 1) + 1, 0);
|
|
270
|
+
const lineEnd = source.indexOf("\n", index);
|
|
271
|
+
return source.slice(lineStart, lineEnd === -1 ? source.length : lineEnd).trim();
|
|
272
|
+
}
|
|
273
|
+
function objectPatternHasTypeProperty(pattern) {
|
|
274
|
+
return pattern.findAll({ rule: { any: [{
|
|
275
|
+
kind: "property_identifier",
|
|
276
|
+
regex: "^type$"
|
|
277
|
+
}, {
|
|
278
|
+
kind: "shorthand_property_identifier_pattern",
|
|
279
|
+
regex: "^type$"
|
|
280
|
+
}] } }).some((node) => node.text() === "type");
|
|
281
|
+
}
|
|
282
|
+
function namespaceDbAliasBindings(pattern) {
|
|
283
|
+
const aliases = [];
|
|
284
|
+
for (const child of pattern.children()) {
|
|
285
|
+
if (child.kind() === "shorthand_property_identifier_pattern" && child.text() === "db") {
|
|
286
|
+
aliases.push(child);
|
|
287
|
+
continue;
|
|
288
|
+
}
|
|
289
|
+
if (child.kind() !== "pair_pattern") continue;
|
|
290
|
+
if (child.field("key")?.text() !== "db") continue;
|
|
291
|
+
const value = child.field("value");
|
|
292
|
+
if (value?.kind() === "identifier") aliases.push(value);
|
|
293
|
+
}
|
|
294
|
+
return aliases;
|
|
295
|
+
}
|
|
296
|
+
function isSdkNamespaceMember(node, namespaceNames, shadowedRanges) {
|
|
297
|
+
const unwrapped = unwrapExpression(node);
|
|
298
|
+
return unwrapped?.kind() === "identifier" && namespaceNames.has(unwrapped.text()) && !isShadowed(unwrapped, shadowedRanges);
|
|
299
|
+
}
|
|
300
|
+
function reviewFindings(source, filePath, relativePath) {
|
|
301
|
+
if (!source.includes("type") || !source.includes(SDK_MODULE)) return [];
|
|
302
|
+
let root;
|
|
303
|
+
try {
|
|
304
|
+
root = parse(sourceLang(filePath, source), source).root();
|
|
305
|
+
} catch {
|
|
306
|
+
return [];
|
|
307
|
+
}
|
|
308
|
+
const imports = findImportStatements(root).filter((importStmt) => importSource(importStmt) === SDK_MODULE);
|
|
309
|
+
if (imports.length === 0) return [];
|
|
310
|
+
const dbNames = /* @__PURE__ */ new Set();
|
|
311
|
+
const namespaceNames = /* @__PURE__ */ new Set();
|
|
312
|
+
for (const importStmt of imports) {
|
|
313
|
+
for (const binding of importBindings(importStmt)) if (binding.importedName === "db") dbNames.add(binding.localName);
|
|
314
|
+
for (const name of namespaceImportNames(importStmt)) namespaceNames.add(name);
|
|
315
|
+
}
|
|
316
|
+
if (dbNames.size === 0 && namespaceNames.size === 0) return [];
|
|
317
|
+
const shadowedRanges = buildShadowedRanges(root, /* @__PURE__ */ new Set([...dbNames, ...namespaceNames]));
|
|
318
|
+
const findings = [];
|
|
319
|
+
for (const decl of root.findAll({ rule: { kind: "variable_declarator" } })) {
|
|
320
|
+
const binding = firstDeclaratorChild(decl);
|
|
321
|
+
if (binding?.kind() !== "object_pattern" || !objectPatternHasTypeProperty(binding)) continue;
|
|
322
|
+
if (!isSdkDbMember(declaratorValue(decl), dbNames, namespaceNames, shadowedRanges)) continue;
|
|
323
|
+
findings.push({
|
|
324
|
+
file: relativePath,
|
|
325
|
+
line: lineForIndex(source, binding.range().start.index),
|
|
326
|
+
message: "Review destructured db.type builder usage and migrate it to db.table.",
|
|
327
|
+
excerpt: excerptAtIndex(source, binding.range().start.index)
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
for (const decl of root.findAll({ rule: { kind: "variable_declarator" } })) {
|
|
331
|
+
const binding = firstDeclaratorChild(decl);
|
|
332
|
+
if (binding?.kind() !== "object_pattern") continue;
|
|
333
|
+
if (!isSdkNamespaceMember(declaratorValue(decl), namespaceNames, shadowedRanges)) continue;
|
|
334
|
+
for (const alias of namespaceDbAliasBindings(binding)) {
|
|
335
|
+
if (!hasTypeBuilderUse(root, alias.text(), decl.range().end.index)) continue;
|
|
336
|
+
findings.push({
|
|
337
|
+
file: relativePath,
|
|
338
|
+
line: lineForIndex(source, binding.range().start.index),
|
|
339
|
+
message: "Review SDK db alias usage and migrate db.type builder calls to db.table.",
|
|
340
|
+
excerpt: excerptAtIndex(source, binding.range().start.index)
|
|
341
|
+
});
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
for (const decl of root.findAll({ rule: { kind: "variable_declarator" } })) {
|
|
345
|
+
const binding = firstDeclaratorChild(decl);
|
|
346
|
+
if (binding?.kind() !== "identifier") continue;
|
|
347
|
+
if (!isSdkDbMember(declaratorValue(decl), dbNames, namespaceNames, shadowedRanges)) continue;
|
|
348
|
+
if (!hasTypeBuilderUse(root, binding.text(), decl.range().end.index)) continue;
|
|
349
|
+
findings.push({
|
|
350
|
+
file: relativePath,
|
|
351
|
+
line: lineForIndex(source, binding.range().start.index),
|
|
352
|
+
message: "Review SDK db alias usage and migrate db.type builder calls to db.table.",
|
|
353
|
+
excerpt: excerptAtIndex(source, binding.range().start.index)
|
|
354
|
+
});
|
|
355
|
+
}
|
|
356
|
+
for (const assignment of root.findAll({ rule: { kind: "assignment_expression" } })) {
|
|
357
|
+
const target = assignmentTarget(assignment);
|
|
358
|
+
if (target?.kind() !== "identifier") continue;
|
|
359
|
+
if (!isSdkDbMember(assignmentValue(assignment), dbNames, namespaceNames, shadowedRanges)) continue;
|
|
360
|
+
if (!hasTypeBuilderUse(root, target.text(), assignment.range().end.index)) continue;
|
|
361
|
+
findings.push({
|
|
362
|
+
file: relativePath,
|
|
363
|
+
line: lineForIndex(source, assignment.range().start.index),
|
|
364
|
+
message: "Review SDK db alias usage and migrate db.type builder calls to db.table.",
|
|
365
|
+
excerpt: excerptAtIndex(source, assignment.range().start.index)
|
|
366
|
+
});
|
|
367
|
+
}
|
|
368
|
+
for (const param of root.findAll({ rule: { any: [{ kind: "required_parameter" }, { kind: "optional_parameter" }] } })) {
|
|
369
|
+
const target = parameterDefaultTarget(param);
|
|
370
|
+
if (target?.kind() !== "identifier") continue;
|
|
371
|
+
if (!isSdkDbMember(parameterDefaultValue(param), dbNames, namespaceNames, shadowedRanges)) continue;
|
|
372
|
+
if (!hasTypeBuilderUse(root, target.text(), param.range().end.index)) continue;
|
|
373
|
+
findings.push({
|
|
374
|
+
file: relativePath,
|
|
375
|
+
line: lineForIndex(source, param.range().start.index),
|
|
376
|
+
message: "Review SDK db alias usage and migrate db.type builder calls to db.table.",
|
|
377
|
+
excerpt: excerptAtIndex(source, param.range().start.index)
|
|
378
|
+
});
|
|
379
|
+
}
|
|
380
|
+
return findings;
|
|
381
|
+
}
|
|
382
|
+
//#endregion
|
|
383
|
+
export { transform as default, reviewFindings };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { c as localDeclarationNames, i as importBindings, r as findImportStatements, t as buildAddNamedImportEdit } from "../../../ast-grep-helpers-D3FXAKNz.js";
|
|
2
2
|
import { Lang, parse } from "@ast-grep/napi";
|
|
3
3
|
//#region codemods/v2/runtime-globals-opt-in/scripts/transform.ts
|
|
4
4
|
const RUNTIME_MODULE = "@tailor-platform/sdk/runtime";
|
|
@@ -0,0 +1,792 @@
|
|
|
1
|
+
import { a as importSource, c as localDeclarationNames, i as importBindings, n as collectBindingNames, o as importSpecNames, r as findImportStatements, s as isTypeOnlyImport } from "../../../ast-grep-helpers-D3FXAKNz.js";
|
|
2
|
+
import { Lang, parse } from "@ast-grep/napi";
|
|
3
|
+
//#region codemods/v2/runtime-subpath-namespace/scripts/transform.ts
|
|
4
|
+
const MODULES_BY_SOURCE = new Map([
|
|
5
|
+
{
|
|
6
|
+
namespace: "iconv",
|
|
7
|
+
source: "@tailor-platform/sdk/runtime/iconv",
|
|
8
|
+
members: {
|
|
9
|
+
convert: "convert",
|
|
10
|
+
convertBuffer: "convertBuffer",
|
|
11
|
+
decode: "decode",
|
|
12
|
+
encode: "encode",
|
|
13
|
+
encodings: "encodings",
|
|
14
|
+
Iconv: "Iconv"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
namespace: "secretmanager",
|
|
19
|
+
source: "@tailor-platform/sdk/runtime/secretmanager",
|
|
20
|
+
members: {
|
|
21
|
+
getSecrets: "getSecrets",
|
|
22
|
+
getSecret: "getSecret"
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
namespace: "authconnection",
|
|
27
|
+
source: "@tailor-platform/sdk/runtime/authconnection",
|
|
28
|
+
members: { getConnectionToken: "getConnectionToken" }
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
namespace: "idp",
|
|
32
|
+
source: "@tailor-platform/sdk/runtime/idp",
|
|
33
|
+
members: { Client: "Client" }
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
namespace: "workflow",
|
|
37
|
+
source: "@tailor-platform/sdk/runtime/workflow",
|
|
38
|
+
members: {
|
|
39
|
+
triggerWorkflow: "triggerWorkflow",
|
|
40
|
+
resumeWorkflow: "resumeWorkflow",
|
|
41
|
+
triggerJobFunction: "triggerJobFunction",
|
|
42
|
+
wait: "wait",
|
|
43
|
+
resolve: "resolve"
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
namespace: "context",
|
|
48
|
+
source: "@tailor-platform/sdk/runtime/context",
|
|
49
|
+
members: { getInvoker: "getInvoker" }
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
namespace: "file",
|
|
53
|
+
source: "@tailor-platform/sdk/runtime/file",
|
|
54
|
+
members: {
|
|
55
|
+
upload: "upload",
|
|
56
|
+
download: "download",
|
|
57
|
+
downloadAsBase64: "downloadAsBase64",
|
|
58
|
+
delete: "delete",
|
|
59
|
+
deleteFile: "delete",
|
|
60
|
+
getMetadata: "getMetadata",
|
|
61
|
+
downloadStream: "downloadStream",
|
|
62
|
+
uploadStream: "uploadStream"
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
namespace: "aigateway",
|
|
67
|
+
source: "@tailor-platform/sdk/runtime/aigateway",
|
|
68
|
+
members: { get: "get" }
|
|
69
|
+
}
|
|
70
|
+
].map((mod) => [mod.source, mod]));
|
|
71
|
+
const AGGREGATE_RUNTIME_SOURCE = "@tailor-platform/sdk/runtime";
|
|
72
|
+
const JSX_FILE_EXTENSIONS = /* @__PURE__ */ new Set([".tsx", ".jsx"]);
|
|
73
|
+
const JS_FILE_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
74
|
+
".js",
|
|
75
|
+
".mjs",
|
|
76
|
+
".cjs"
|
|
77
|
+
]);
|
|
78
|
+
function quickFilter(source) {
|
|
79
|
+
return source.includes(AGGREGATE_RUNTIME_SOURCE);
|
|
80
|
+
}
|
|
81
|
+
function sourceLang(filePath, source) {
|
|
82
|
+
const lower = filePath.toLowerCase();
|
|
83
|
+
const extension = lower.slice(lower.lastIndexOf("."));
|
|
84
|
+
if (JSX_FILE_EXTENSIONS.has(extension)) return Lang.Tsx;
|
|
85
|
+
if (JS_FILE_EXTENSIONS.has(extension) && /<>|<\/>|<[A-Za-z][\w.$:-]/.test(source)) return Lang.Tsx;
|
|
86
|
+
return Lang.TypeScript;
|
|
87
|
+
}
|
|
88
|
+
function importClause(importStmt) {
|
|
89
|
+
return importStmt.children().find((child) => child.kind() === "import_clause") ?? null;
|
|
90
|
+
}
|
|
91
|
+
function hasDefaultImport(importStmt) {
|
|
92
|
+
return importClause(importStmt)?.children().some((child) => child.kind() === "identifier") ?? false;
|
|
93
|
+
}
|
|
94
|
+
function namespaceImportName(importStmt) {
|
|
95
|
+
return importClause(importStmt)?.children().find((child) => child.kind() === "namespace_import")?.children().find((child) => child.kind() === "identifier")?.text() ?? null;
|
|
96
|
+
}
|
|
97
|
+
function formatImport(source, namedSpecs, typeOnly = false, attributeText = "") {
|
|
98
|
+
const importKeyword = typeOnly ? "import type" : "import";
|
|
99
|
+
const named = namedSpecs.length > 0 ? `{ ${namedSpecs.join(", ")} }` : null;
|
|
100
|
+
const attributes = attributeText === "" ? "" : ` ${attributeText}`;
|
|
101
|
+
if (named) return `${importKeyword} ${named} from "${source}"${attributes};`;
|
|
102
|
+
return "";
|
|
103
|
+
}
|
|
104
|
+
function importAttributeText(importStmt) {
|
|
105
|
+
return importStmt.children().find((child) => child.kind() === "import_attribute")?.text() ?? "";
|
|
106
|
+
}
|
|
107
|
+
function replaceImportStatement(importStmt, nextText, sourceText) {
|
|
108
|
+
if (nextText !== "") return importStmt.replace(nextText);
|
|
109
|
+
const range = importStmt.range();
|
|
110
|
+
let endPos = range.end.index;
|
|
111
|
+
if (sourceText[endPos] === "\r" && sourceText[endPos + 1] === "\n") endPos += 2;
|
|
112
|
+
else if (sourceText[endPos] === "\n") endPos += 1;
|
|
113
|
+
return {
|
|
114
|
+
startPos: range.start.index,
|
|
115
|
+
endPos,
|
|
116
|
+
insertedText: ""
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
function isInsideImportStatement(node) {
|
|
120
|
+
let current = node.parent();
|
|
121
|
+
while (current) {
|
|
122
|
+
if (current.kind() === "import_statement") return true;
|
|
123
|
+
current = current.parent();
|
|
124
|
+
}
|
|
125
|
+
return false;
|
|
126
|
+
}
|
|
127
|
+
function isInsideExportSpecifier(node) {
|
|
128
|
+
let current = node.parent();
|
|
129
|
+
while (current) {
|
|
130
|
+
if (current.kind() === "export_specifier") return true;
|
|
131
|
+
if (current.kind() === "export_statement") return false;
|
|
132
|
+
current = current.parent();
|
|
133
|
+
}
|
|
134
|
+
return false;
|
|
135
|
+
}
|
|
136
|
+
function isInsideTypeQuery(node) {
|
|
137
|
+
let current = node.parent();
|
|
138
|
+
while (current) {
|
|
139
|
+
if (current.kind() === "type_query") return true;
|
|
140
|
+
if (current.kind() === "statement_block" || current.kind() === "program") return false;
|
|
141
|
+
current = current.parent();
|
|
142
|
+
}
|
|
143
|
+
return false;
|
|
144
|
+
}
|
|
145
|
+
function isJsxTagName(node) {
|
|
146
|
+
const parentKind = node.parent()?.kind();
|
|
147
|
+
return parentKind === "jsx_opening_element" || parentKind === "jsx_self_closing_element" || parentKind === "jsx_closing_element";
|
|
148
|
+
}
|
|
149
|
+
function sameNode(left, right) {
|
|
150
|
+
if (!left) return false;
|
|
151
|
+
const leftRange = left.range();
|
|
152
|
+
const rightRange = right.range();
|
|
153
|
+
return leftRange.start.index === rightRange.start.index && leftRange.end.index === rightRange.end.index;
|
|
154
|
+
}
|
|
155
|
+
function typeParameterName(typeParameter) {
|
|
156
|
+
return typeParameter.children().find((child) => child.kind() === "type_identifier") ?? null;
|
|
157
|
+
}
|
|
158
|
+
function typeParametersDeclare(typeParameters, name) {
|
|
159
|
+
return typeParameters.children().some((child) => child.kind() === "type_parameter" && typeParameterName(child)?.text() === name);
|
|
160
|
+
}
|
|
161
|
+
function isTypeParameterScoped(node) {
|
|
162
|
+
let current = node.parent();
|
|
163
|
+
while (current) {
|
|
164
|
+
if (current.kind() === "type_parameter" && sameNode(typeParameterName(current), node)) return true;
|
|
165
|
+
const typeParameters = current.children().find((child) => child.kind() === "type_parameters");
|
|
166
|
+
if (typeParameters && typeParametersDeclare(typeParameters, node.text())) return true;
|
|
167
|
+
current = current.parent();
|
|
168
|
+
}
|
|
169
|
+
return false;
|
|
170
|
+
}
|
|
171
|
+
function isNestedTypeMember(node) {
|
|
172
|
+
const parent = node.parent();
|
|
173
|
+
if (parent?.kind() !== "nested_type_identifier") return false;
|
|
174
|
+
return !sameNode(parent.children().find((child) => child.kind() === "identifier" || child.kind() === "type_identifier"), node);
|
|
175
|
+
}
|
|
176
|
+
function localTypeScopeDeclarationNames(root) {
|
|
177
|
+
const names = /* @__PURE__ */ new Set();
|
|
178
|
+
for (const node of root.findAll({ rule: { any: [
|
|
179
|
+
{ kind: "type_parameter" },
|
|
180
|
+
{ kind: "infer_type" },
|
|
181
|
+
{ kind: "mapped_type_clause" }
|
|
182
|
+
] } })) {
|
|
183
|
+
const name = node.children().find((child) => child.kind() === "type_identifier");
|
|
184
|
+
if (name) names.add(name.text());
|
|
185
|
+
}
|
|
186
|
+
return names;
|
|
187
|
+
}
|
|
188
|
+
function hasExportSpecifierReference(root, names) {
|
|
189
|
+
return root.findAll({ rule: { kind: "export_specifier" } }).some((specifier) => specifier.children().some((child) => child.kind() === "identifier" && names.has(child.text())));
|
|
190
|
+
}
|
|
191
|
+
function hasNonTypeQueryTypeReference(root, names) {
|
|
192
|
+
return root.findAll({ rule: { kind: "type_identifier" } }).some((node) => {
|
|
193
|
+
if (!names.has(node.text())) return false;
|
|
194
|
+
if (isInsideImportStatement(node)) return false;
|
|
195
|
+
if (isInsideExportSpecifier(node)) return false;
|
|
196
|
+
if (isTypeParameterScoped(node)) return false;
|
|
197
|
+
if (isNestedTypeMember(node)) return false;
|
|
198
|
+
return !isInsideTypeQuery(node);
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
function hasNamespaceTypeMemberReference(root, namespaceLocal) {
|
|
202
|
+
return root.findAll({ rule: { kind: "nested_type_identifier" } }).some((node) => {
|
|
203
|
+
return node.children().find((child) => child.kind() === "identifier" || child.kind() === "type_identifier")?.text() === namespaceLocal;
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
function findExportStatements(root) {
|
|
207
|
+
return root.findAll({ rule: { kind: "export_statement" } }).filter((stmt) => stmt.parent()?.kind() === "program").toSorted((a, b) => a.range().start.index - b.range().start.index);
|
|
208
|
+
}
|
|
209
|
+
function aggregateFileImportLocals(imports) {
|
|
210
|
+
const locals = /* @__PURE__ */ new Set();
|
|
211
|
+
for (const importStmt of imports) {
|
|
212
|
+
if (importSource(importStmt) !== AGGREGATE_RUNTIME_SOURCE) continue;
|
|
213
|
+
for (const binding of importBindings(importStmt)) if (!binding.typeOnly && binding.importedName === "file") locals.add(binding.localName);
|
|
214
|
+
}
|
|
215
|
+
return locals;
|
|
216
|
+
}
|
|
217
|
+
function isValueBindingLeafKind(kind) {
|
|
218
|
+
return kind === "identifier" || kind === "shorthand_property_identifier_pattern";
|
|
219
|
+
}
|
|
220
|
+
function isValueBindingPatternKind(kind) {
|
|
221
|
+
return isValueBindingLeafKind(kind) || kind === "object_pattern" || kind === "array_pattern" || kind === "rest_pattern";
|
|
222
|
+
}
|
|
223
|
+
function collectValueBindingNames(node, names, result) {
|
|
224
|
+
if (isValueBindingLeafKind(node.kind())) {
|
|
225
|
+
if (names.has(node.text())) result.add(node.text());
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
for (const child of node.children()) {
|
|
229
|
+
if (child.kind() === "property_identifier") continue;
|
|
230
|
+
if (child.kind() === "=") break;
|
|
231
|
+
collectValueBindingNames(child, names, result);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
function valueBindingNames(node, names) {
|
|
235
|
+
const result = /* @__PURE__ */ new Set();
|
|
236
|
+
collectValueBindingNames(node, names, result);
|
|
237
|
+
return result;
|
|
238
|
+
}
|
|
239
|
+
function directValueBindingNames(node, names) {
|
|
240
|
+
const result = /* @__PURE__ */ new Set();
|
|
241
|
+
for (const child of node.children()) {
|
|
242
|
+
if (child.kind() === "=") break;
|
|
243
|
+
if (isValueBindingPatternKind(child.kind())) collectValueBindingNames(child, names, result);
|
|
244
|
+
}
|
|
245
|
+
return result;
|
|
246
|
+
}
|
|
247
|
+
function firstDeclaratorChild(node) {
|
|
248
|
+
return node.children().find((child) => child.kind() !== "=") ?? null;
|
|
249
|
+
}
|
|
250
|
+
function addShadowedRange(ranges, name, scope) {
|
|
251
|
+
const range = scope.range();
|
|
252
|
+
const existing = ranges.get(name) ?? [];
|
|
253
|
+
existing.push({
|
|
254
|
+
start: range.start.index,
|
|
255
|
+
end: range.end.index
|
|
256
|
+
});
|
|
257
|
+
ranges.set(name, existing);
|
|
258
|
+
}
|
|
259
|
+
function nearestValueScope(node) {
|
|
260
|
+
let current = node.parent();
|
|
261
|
+
while (current) {
|
|
262
|
+
const kind = current.kind();
|
|
263
|
+
if (kind === "statement_block" || kind === "program" || kind === "switch_body" || kind === "for_statement" || kind === "for_in_statement") return current;
|
|
264
|
+
current = current.parent();
|
|
265
|
+
}
|
|
266
|
+
return node;
|
|
267
|
+
}
|
|
268
|
+
function functionValueScope(node) {
|
|
269
|
+
let current = node.parent();
|
|
270
|
+
while (current) {
|
|
271
|
+
const kind = current.kind();
|
|
272
|
+
if (kind === "function_declaration" || kind === "function_expression" || kind === "arrow_function" || kind === "method_definition" || kind === "program") return current;
|
|
273
|
+
current = current.parent();
|
|
274
|
+
}
|
|
275
|
+
return node;
|
|
276
|
+
}
|
|
277
|
+
function variableValueScope(node) {
|
|
278
|
+
const declaration = node.parent();
|
|
279
|
+
return /^var\b/.test(declaration?.text().trimStart() ?? "") ? functionValueScope(node) : nearestValueScope(node);
|
|
280
|
+
}
|
|
281
|
+
function parameterValueScope(node) {
|
|
282
|
+
let current = node.parent();
|
|
283
|
+
while (current) {
|
|
284
|
+
const kind = current.kind();
|
|
285
|
+
if (kind === "formal_parameters") {
|
|
286
|
+
current = current.parent();
|
|
287
|
+
continue;
|
|
288
|
+
}
|
|
289
|
+
if (kind === "function_declaration" || kind === "function_expression" || kind === "arrow_function" || kind === "method_definition") return current;
|
|
290
|
+
break;
|
|
291
|
+
}
|
|
292
|
+
return nearestValueScope(node);
|
|
293
|
+
}
|
|
294
|
+
function buildShadowedRanges(root, names) {
|
|
295
|
+
const ranges = /* @__PURE__ */ new Map();
|
|
296
|
+
for (const decl of root.findAll({ rule: { kind: "variable_declarator" } })) {
|
|
297
|
+
if (isInsideImportStatement(decl)) continue;
|
|
298
|
+
const binding = firstDeclaratorChild(decl);
|
|
299
|
+
if (!binding) continue;
|
|
300
|
+
for (const name of valueBindingNames(binding, names)) addShadowedRange(ranges, name, variableValueScope(decl));
|
|
301
|
+
}
|
|
302
|
+
for (const decl of root.findAll({ rule: { any: [
|
|
303
|
+
{ kind: "function_declaration" },
|
|
304
|
+
{ kind: "class_declaration" },
|
|
305
|
+
{ kind: "enum_declaration" }
|
|
306
|
+
] } })) {
|
|
307
|
+
const name = decl.children().find((child) => child.kind() === "identifier" && names.has(child.text()));
|
|
308
|
+
if (name) addShadowedRange(ranges, name.text(), nearestValueScope(decl));
|
|
309
|
+
}
|
|
310
|
+
for (const expression of root.findAll({ rule: { any: [{ kind: "function_expression" }, { kind: "class" }] } })) {
|
|
311
|
+
const name = expression.children().find((child) => (child.kind() === "identifier" || child.kind() === "type_identifier") && names.has(child.text()));
|
|
312
|
+
if (name) addShadowedRange(ranges, name.text(), expression);
|
|
313
|
+
}
|
|
314
|
+
for (const param of root.findAll({ rule: { any: [{ kind: "required_parameter" }, { kind: "optional_parameter" }] } })) for (const name of directValueBindingNames(param, names)) addShadowedRange(ranges, name, parameterValueScope(param));
|
|
315
|
+
for (const arrow of root.findAll({ rule: { kind: "arrow_function" } })) {
|
|
316
|
+
const children = arrow.children();
|
|
317
|
+
const arrowIndex = children.findIndex((child) => child.kind() === "=>");
|
|
318
|
+
if (arrowIndex === -1) continue;
|
|
319
|
+
for (const child of children.slice(0, arrowIndex)) {
|
|
320
|
+
if (child.kind() === "=") break;
|
|
321
|
+
if (!isValueBindingPatternKind(child.kind())) continue;
|
|
322
|
+
for (const name of valueBindingNames(child, names)) addShadowedRange(ranges, name, arrow);
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
for (const catchClause of root.findAll({ rule: { kind: "catch_clause" } })) for (const name of directValueBindingNames(catchClause, names)) addShadowedRange(ranges, name, catchClause);
|
|
326
|
+
for (const loop of root.findAll({ rule: { kind: "for_in_statement" } })) {
|
|
327
|
+
const children = loop.children();
|
|
328
|
+
const keywordIndex = children.findIndex((child) => child.kind() === "in" || child.kind() === "of");
|
|
329
|
+
if (keywordIndex === -1) continue;
|
|
330
|
+
for (const child of children.slice(0, keywordIndex)) for (const name of valueBindingNames(child, names)) addShadowedRange(ranges, name, loop);
|
|
331
|
+
}
|
|
332
|
+
return ranges;
|
|
333
|
+
}
|
|
334
|
+
function isShadowed(node, ranges) {
|
|
335
|
+
const candidates = ranges.get(node.text());
|
|
336
|
+
if (!candidates) return false;
|
|
337
|
+
const position = node.range().start.index;
|
|
338
|
+
return candidates.some((range) => position >= range.start && position < range.end);
|
|
339
|
+
}
|
|
340
|
+
function isAggregateFileReceiver(node, fileLocals, shadowedRanges) {
|
|
341
|
+
return node?.kind() === "identifier" && fileLocals.has(node.text()) && !isShadowed(node, shadowedRanges);
|
|
342
|
+
}
|
|
343
|
+
function aggregateFileDeleteAccesses(root, imports) {
|
|
344
|
+
const fileLocals = aggregateFileImportLocals(imports);
|
|
345
|
+
if (fileLocals.size === 0) return [];
|
|
346
|
+
const shadowedRanges = buildShadowedRanges(root, fileLocals);
|
|
347
|
+
const accesses = [];
|
|
348
|
+
for (const member of root.findAll({ rule: { kind: "member_expression" } })) {
|
|
349
|
+
const receiver = member.field("object");
|
|
350
|
+
const property = member.children().find((child) => child.kind() === "property_identifier");
|
|
351
|
+
if (isAggregateFileReceiver(receiver, fileLocals, shadowedRanges) && property?.text() === "deleteFile") accesses.push({
|
|
352
|
+
node: member,
|
|
353
|
+
property,
|
|
354
|
+
computed: false
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
for (const subscript of root.findAll({ rule: { kind: "subscript_expression" } })) {
|
|
358
|
+
const receiver = subscript.field("object");
|
|
359
|
+
const property = subscript.field("index");
|
|
360
|
+
if (isAggregateFileReceiver(receiver, fileLocals, shadowedRanges) && property?.kind() === "string" && property.text().slice(1, -1) === "deleteFile") accesses.push({
|
|
361
|
+
node: subscript,
|
|
362
|
+
property,
|
|
363
|
+
computed: true
|
|
364
|
+
});
|
|
365
|
+
}
|
|
366
|
+
return accesses;
|
|
367
|
+
}
|
|
368
|
+
function aggregateFileDeleteEdits(root, imports) {
|
|
369
|
+
return aggregateFileDeleteAccesses(root, imports).map(({ property, computed }) => {
|
|
370
|
+
if (!computed) return property.replace("delete");
|
|
371
|
+
const quote = property.text()[0] ?? "\"";
|
|
372
|
+
return property.replace(`${quote}delete${quote}`);
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
function declaratorSides(node) {
|
|
376
|
+
const children = node.children();
|
|
377
|
+
const equalsIndex = children.findIndex((child) => child.kind() === "=");
|
|
378
|
+
if (equalsIndex === -1) return null;
|
|
379
|
+
const binding = children.slice(0, equalsIndex).find((child) => child.kind() !== "comment");
|
|
380
|
+
const value = children.slice(equalsIndex + 1).find((child) => child.kind() !== "comment");
|
|
381
|
+
return binding && value ? {
|
|
382
|
+
binding,
|
|
383
|
+
value
|
|
384
|
+
} : null;
|
|
385
|
+
}
|
|
386
|
+
function aggregateFileDeleteDestructures(root, imports) {
|
|
387
|
+
const fileLocals = aggregateFileImportLocals(imports);
|
|
388
|
+
if (fileLocals.size === 0) return [];
|
|
389
|
+
const shadowedRanges = buildShadowedRanges(root, fileLocals);
|
|
390
|
+
return root.findAll({ rule: { kind: "variable_declarator" } }).filter((declarator) => {
|
|
391
|
+
const sides = declaratorSides(declarator);
|
|
392
|
+
if (!sides || sides.binding.kind() !== "object_pattern" || !isAggregateFileReceiver(sides.value, fileLocals, shadowedRanges)) return false;
|
|
393
|
+
return sides.binding.findAll({ rule: { any: [{ kind: "property_identifier" }, { kind: "shorthand_property_identifier_pattern" }] } }).some((property) => property.text() === "deleteFile");
|
|
394
|
+
});
|
|
395
|
+
}
|
|
396
|
+
function aggregateFileDeleteFindingNodes(root, imports) {
|
|
397
|
+
return [...aggregateFileDeleteAccesses(root, imports).map((access) => access.node), ...aggregateFileDeleteDestructures(root, imports)];
|
|
398
|
+
}
|
|
399
|
+
function usedNames(root, imports, removedNames) {
|
|
400
|
+
const names = localDeclarationNames(root);
|
|
401
|
+
for (const importStmt of imports) for (const binding of importBindings(importStmt)) if (!removedNames.has(binding.localName)) names.add(binding.localName);
|
|
402
|
+
return names;
|
|
403
|
+
}
|
|
404
|
+
function uniqueNamespaceLocal(mod, root, imports, removedNames) {
|
|
405
|
+
const names = usedNames(root, imports, removedNames);
|
|
406
|
+
if (!names.has(mod.namespace)) return mod.namespace;
|
|
407
|
+
const base = `${mod.namespace}Runtime`;
|
|
408
|
+
if (!names.has(base)) return base;
|
|
409
|
+
for (let i = 2;; i++) {
|
|
410
|
+
const candidate = `${base}${i}`;
|
|
411
|
+
if (!names.has(candidate)) return candidate;
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
function selfNamespaceSpec(mod, localName) {
|
|
415
|
+
return localName === mod.namespace ? mod.namespace : `${mod.namespace} as ${localName}`;
|
|
416
|
+
}
|
|
417
|
+
function typeOnlySelfNamespaceSpec(mod, localName) {
|
|
418
|
+
return `type ${selfNamespaceSpec(mod, localName)}`;
|
|
419
|
+
}
|
|
420
|
+
function existingSelfNamespaceImport(importStmt, mod, statementTypeOnly) {
|
|
421
|
+
for (const spec of importStmt.findAll({ rule: { kind: "import_specifier" } })) {
|
|
422
|
+
const names = importSpecNames(spec);
|
|
423
|
+
if (!names || names.importedName !== mod.namespace) continue;
|
|
424
|
+
return {
|
|
425
|
+
localName: names.localName,
|
|
426
|
+
typeOnly: statementTypeOnly || names.typeOnly
|
|
427
|
+
};
|
|
428
|
+
}
|
|
429
|
+
return null;
|
|
430
|
+
}
|
|
431
|
+
function flatImportsFor(importStmt, mod) {
|
|
432
|
+
const statementTypeOnly = isTypeOnlyImport(importStmt);
|
|
433
|
+
const flatImports = [];
|
|
434
|
+
for (const spec of importStmt.findAll({ rule: { kind: "import_specifier" } })) {
|
|
435
|
+
const names = importSpecNames(spec);
|
|
436
|
+
if (!names) continue;
|
|
437
|
+
const memberName = mod.members[names.importedName];
|
|
438
|
+
if (!memberName) continue;
|
|
439
|
+
flatImports.push({
|
|
440
|
+
localName: names.localName,
|
|
441
|
+
memberName,
|
|
442
|
+
typeOnly: statementTypeOnly || names.typeOnly
|
|
443
|
+
});
|
|
444
|
+
}
|
|
445
|
+
return flatImports;
|
|
446
|
+
}
|
|
447
|
+
function plannedValueNamespaceLocal(importStmt, mod, root, imports) {
|
|
448
|
+
const source = importSource(importStmt);
|
|
449
|
+
if (!source) return null;
|
|
450
|
+
for (const candidate of imports) {
|
|
451
|
+
if (sameNode(candidate, importStmt)) continue;
|
|
452
|
+
if (importSource(candidate) !== source || isTypeOnlyImport(candidate)) continue;
|
|
453
|
+
const namespaceName = namespaceImportName(candidate);
|
|
454
|
+
if (namespaceName) return namespaceName;
|
|
455
|
+
const existingSelf = existingSelfNamespaceImport(candidate, mod, false);
|
|
456
|
+
if (existingSelf && !existingSelf.typeOnly) return existingSelf.localName;
|
|
457
|
+
const valueFlatImports = flatImportsFor(candidate, mod).filter((binding) => !binding.typeOnly);
|
|
458
|
+
if (valueFlatImports.length === 0) continue;
|
|
459
|
+
const removedNames = new Set(valueFlatImports.map((binding) => binding.localName));
|
|
460
|
+
const declaredNames = /* @__PURE__ */ new Set([...localDeclarationNames(root), ...localTypeScopeDeclarationNames(root)]);
|
|
461
|
+
if (valueFlatImports.some((binding) => declaredNames.has(binding.localName))) continue;
|
|
462
|
+
if (hasExportSpecifierReference(root, removedNames)) continue;
|
|
463
|
+
return uniqueNamespaceLocal(mod, root, imports, removedNames);
|
|
464
|
+
}
|
|
465
|
+
return null;
|
|
466
|
+
}
|
|
467
|
+
function buildImportReplacement(importStmt, mod, root, imports, sourceText, emittedNamespaceSpecifiers) {
|
|
468
|
+
const source = importSource(importStmt);
|
|
469
|
+
if (!source) return null;
|
|
470
|
+
const statementTypeOnly = isTypeOnlyImport(importStmt);
|
|
471
|
+
const attributes = importAttributeText(importStmt);
|
|
472
|
+
const namespaceName = namespaceImportName(importStmt);
|
|
473
|
+
if (namespaceName) {
|
|
474
|
+
if (hasNamespaceTypeMemberReference(root, namespaceName)) return null;
|
|
475
|
+
return {
|
|
476
|
+
edit: importStmt.replace(formatImport(source, [selfNamespaceSpec(mod, namespaceName)], statementTypeOnly, attributes)),
|
|
477
|
+
flatImports: [],
|
|
478
|
+
namespaceLocal: namespaceName
|
|
479
|
+
};
|
|
480
|
+
}
|
|
481
|
+
if (hasDefaultImport(importStmt)) return null;
|
|
482
|
+
const existingSelf = existingSelfNamespaceImport(importStmt, mod, statementTypeOnly);
|
|
483
|
+
const flatImports = [];
|
|
484
|
+
const keptSpecs = [];
|
|
485
|
+
for (const spec of importStmt.findAll({ rule: { kind: "import_specifier" } })) {
|
|
486
|
+
const names = importSpecNames(spec);
|
|
487
|
+
if (!names) continue;
|
|
488
|
+
const memberName = mod.members[names.importedName];
|
|
489
|
+
if (memberName) {
|
|
490
|
+
flatImports.push({
|
|
491
|
+
localName: names.localName,
|
|
492
|
+
memberName,
|
|
493
|
+
typeOnly: statementTypeOnly || names.typeOnly
|
|
494
|
+
});
|
|
495
|
+
continue;
|
|
496
|
+
}
|
|
497
|
+
keptSpecs.push(spec.text());
|
|
498
|
+
}
|
|
499
|
+
if (flatImports.length === 0) return null;
|
|
500
|
+
const removedNames = new Set(flatImports.map((binding) => binding.localName));
|
|
501
|
+
const declaredNames = /* @__PURE__ */ new Set([...localDeclarationNames(root), ...localTypeScopeDeclarationNames(root)]);
|
|
502
|
+
if (flatImports.some((binding) => declaredNames.has(binding.localName))) return null;
|
|
503
|
+
if (hasExportSpecifierReference(root, removedNames)) return null;
|
|
504
|
+
if (hasNonTypeQueryTypeReference(root, removedNames)) return null;
|
|
505
|
+
const flatImportsAreTypeOnly = flatImports.every((binding) => binding.typeOnly);
|
|
506
|
+
const canUseExistingSelf = existingSelf != null && (!existingSelf.typeOnly || flatImportsAreTypeOnly);
|
|
507
|
+
const plannedValueLocal = flatImportsAreTypeOnly ? plannedValueNamespaceLocal(importStmt, mod, root, imports) : null;
|
|
508
|
+
const namespaceLocal = plannedValueLocal ?? (canUseExistingSelf ? existingSelf.localName : uniqueNamespaceLocal(mod, root, imports, removedNames));
|
|
509
|
+
const namespaceSpecifierKey = [
|
|
510
|
+
source,
|
|
511
|
+
namespaceLocal,
|
|
512
|
+
flatImportsAreTypeOnly ? "type" : "value"
|
|
513
|
+
].join("\0");
|
|
514
|
+
const namespaceAlreadyEmitted = emittedNamespaceSpecifiers.has(namespaceSpecifierKey);
|
|
515
|
+
const needsNamespaceSpecifier = plannedValueLocal == null && !canUseExistingSelf && !namespaceAlreadyEmitted;
|
|
516
|
+
if (needsNamespaceSpecifier) emittedNamespaceSpecifiers.add(namespaceSpecifierKey);
|
|
517
|
+
const namespaceSpecifier = flatImportsAreTypeOnly && !statementTypeOnly ? typeOnlySelfNamespaceSpec(mod, namespaceLocal) : selfNamespaceSpec(mod, namespaceLocal);
|
|
518
|
+
return {
|
|
519
|
+
edit: replaceImportStatement(importStmt, formatImport(source, needsNamespaceSpecifier ? [namespaceSpecifier, ...keptSpecs] : keptSpecs, statementTypeOnly, attributes), sourceText),
|
|
520
|
+
flatImports,
|
|
521
|
+
namespaceLocal
|
|
522
|
+
};
|
|
523
|
+
}
|
|
524
|
+
function referenceEdits(root, replacements) {
|
|
525
|
+
const byLocalName = /* @__PURE__ */ new Map();
|
|
526
|
+
for (const replacement of replacements) for (const binding of replacement.flatImports) byLocalName.set(binding.localName, {
|
|
527
|
+
namespaceLocal: replacement.namespaceLocal,
|
|
528
|
+
memberName: binding.memberName,
|
|
529
|
+
typeOnly: binding.typeOnly
|
|
530
|
+
});
|
|
531
|
+
const edits = [];
|
|
532
|
+
const replacementFor = (name) => {
|
|
533
|
+
const binding = byLocalName.get(name);
|
|
534
|
+
return binding ? `${binding.namespaceLocal}.${binding.memberName}` : null;
|
|
535
|
+
};
|
|
536
|
+
for (const node of root.findAll({ rule: { kind: "identifier" } })) {
|
|
537
|
+
if (isInsideImportStatement(node)) continue;
|
|
538
|
+
if (isInsideExportSpecifier(node)) continue;
|
|
539
|
+
if (isJsxTagName(node)) continue;
|
|
540
|
+
if (byLocalName.get(node.text())?.typeOnly && !isInsideTypeQuery(node)) continue;
|
|
541
|
+
const replacement = replacementFor(node.text());
|
|
542
|
+
if (!replacement) continue;
|
|
543
|
+
edits.push(node.replace(replacement));
|
|
544
|
+
}
|
|
545
|
+
for (const node of root.findAll({ rule: { kind: "type_identifier" } })) {
|
|
546
|
+
if (isInsideImportStatement(node)) continue;
|
|
547
|
+
if (isInsideExportSpecifier(node)) continue;
|
|
548
|
+
if (!isInsideTypeQuery(node)) continue;
|
|
549
|
+
if (isTypeParameterScoped(node)) continue;
|
|
550
|
+
if (isNestedTypeMember(node)) continue;
|
|
551
|
+
const replacement = replacementFor(node.text());
|
|
552
|
+
if (!replacement) continue;
|
|
553
|
+
edits.push(node.replace(replacement));
|
|
554
|
+
}
|
|
555
|
+
for (const node of root.findAll({ rule: { kind: "shorthand_property_identifier" } })) {
|
|
556
|
+
if (byLocalName.get(node.text())?.typeOnly) continue;
|
|
557
|
+
const replacement = replacementFor(node.text());
|
|
558
|
+
if (!replacement) continue;
|
|
559
|
+
edits.push(node.replace(`${node.text()}: ${replacement}`));
|
|
560
|
+
}
|
|
561
|
+
return edits;
|
|
562
|
+
}
|
|
563
|
+
/**
|
|
564
|
+
* Rewrite v1 runtime subpath imports to the v2 namespace object exports.
|
|
565
|
+
* @param source - File contents
|
|
566
|
+
* @param filePath - Absolute path to the file
|
|
567
|
+
* @returns Transformed source or null when nothing matched.
|
|
568
|
+
*/
|
|
569
|
+
function transform(source, filePath) {
|
|
570
|
+
if (!quickFilter(source)) return null;
|
|
571
|
+
const root = parse(sourceLang(filePath, source), source).root();
|
|
572
|
+
const imports = findImportStatements(root);
|
|
573
|
+
const replacements = [];
|
|
574
|
+
const emittedNamespaceSpecifiers = /* @__PURE__ */ new Set();
|
|
575
|
+
for (const importStmt of imports) {
|
|
576
|
+
const sourceName = importSource(importStmt);
|
|
577
|
+
if (!sourceName) continue;
|
|
578
|
+
const mod = MODULES_BY_SOURCE.get(sourceName);
|
|
579
|
+
if (!mod) continue;
|
|
580
|
+
const replacement = buildImportReplacement(importStmt, mod, root, imports, source, emittedNamespaceSpecifiers);
|
|
581
|
+
if (replacement) replacements.push(replacement);
|
|
582
|
+
}
|
|
583
|
+
const aggregateEdits = aggregateFileDeleteEdits(root, imports);
|
|
584
|
+
if (replacements.length === 0 && aggregateEdits.length === 0) return null;
|
|
585
|
+
const edits = [
|
|
586
|
+
...replacements.map((replacement) => replacement.edit),
|
|
587
|
+
...referenceEdits(root, replacements),
|
|
588
|
+
...aggregateEdits
|
|
589
|
+
];
|
|
590
|
+
const result = root.commitEdits(edits);
|
|
591
|
+
return result === source ? null : result;
|
|
592
|
+
}
|
|
593
|
+
function hasRemovedFlatSpecifier(node, mod) {
|
|
594
|
+
return node.findAll({ rule: { any: [{ kind: "import_specifier" }, { kind: "export_specifier" }] } }).some((spec) => {
|
|
595
|
+
const names = importSpecNames(spec);
|
|
596
|
+
return names != null && mod.members[names.importedName] != null;
|
|
597
|
+
});
|
|
598
|
+
}
|
|
599
|
+
function isExportStar(node) {
|
|
600
|
+
if (node.children().some((child) => child.kind() === "*")) return true;
|
|
601
|
+
return node.children().find((child) => child.kind() === "namespace_export")?.children().some((child) => child.kind() === "*") ?? false;
|
|
602
|
+
}
|
|
603
|
+
function literalModuleSource(node) {
|
|
604
|
+
if (node.kind() === "string") return importSource(node);
|
|
605
|
+
if (node.kind() !== "template_string") return null;
|
|
606
|
+
if (node.children().some((child) => child.kind() === "template_substitution")) return null;
|
|
607
|
+
const text = node.text();
|
|
608
|
+
return text.startsWith("`") && text.endsWith("`") ? text.slice(1, -1) : null;
|
|
609
|
+
}
|
|
610
|
+
function isSourceScopeNode(node) {
|
|
611
|
+
const kind = node.kind();
|
|
612
|
+
return kind === "program" || kind === "statement_block" || kind === "function_declaration" || kind === "arrow_function" || kind === "method_definition";
|
|
613
|
+
}
|
|
614
|
+
function nearestSourceScope(node) {
|
|
615
|
+
let current = node.parent();
|
|
616
|
+
while (current) {
|
|
617
|
+
if (isSourceScopeNode(current)) return current;
|
|
618
|
+
current = current.parent();
|
|
619
|
+
}
|
|
620
|
+
return null;
|
|
621
|
+
}
|
|
622
|
+
function sameRange(left, right) {
|
|
623
|
+
if (!left) return false;
|
|
624
|
+
const leftRange = left.range();
|
|
625
|
+
const rightRange = right.range();
|
|
626
|
+
return leftRange.start.index === rightRange.start.index && leftRange.end.index === rightRange.end.index;
|
|
627
|
+
}
|
|
628
|
+
function isConstVariableDeclarator(node) {
|
|
629
|
+
return node.parent()?.children().some((child) => child.kind() === "const") ?? false;
|
|
630
|
+
}
|
|
631
|
+
function sourceConstInitializerContent(node) {
|
|
632
|
+
const directValue = literalModuleSource(node);
|
|
633
|
+
if (directValue != null) return directValue;
|
|
634
|
+
if (node.kind() !== "as_expression" && node.kind() !== "satisfies_expression" && node.kind() !== "parenthesized_expression") return null;
|
|
635
|
+
for (const child of node.children()) {
|
|
636
|
+
const childValue = sourceConstInitializerContent(child);
|
|
637
|
+
if (childValue != null) return childValue;
|
|
638
|
+
}
|
|
639
|
+
return null;
|
|
640
|
+
}
|
|
641
|
+
function sourceConstVariableDeclaratorContent(node, name) {
|
|
642
|
+
if (!isConstVariableDeclarator(node)) return null;
|
|
643
|
+
const names = /* @__PURE__ */ new Set();
|
|
644
|
+
collectBindingNames(node, names);
|
|
645
|
+
if (!names.has(name)) return null;
|
|
646
|
+
const initializer = node.children().findLast((child) => sourceConstInitializerContent(child) != null);
|
|
647
|
+
return initializer == null ? null : sourceConstInitializerContent(initializer);
|
|
648
|
+
}
|
|
649
|
+
function bindingNames(node) {
|
|
650
|
+
const names = /* @__PURE__ */ new Set();
|
|
651
|
+
collectBindingNames(node, names);
|
|
652
|
+
return names;
|
|
653
|
+
}
|
|
654
|
+
function sourceStringVariableInScope(scope, name, before) {
|
|
655
|
+
const bindings = scope.findAll({ rule: { any: [
|
|
656
|
+
{ kind: "variable_declarator" },
|
|
657
|
+
{ kind: "required_parameter" },
|
|
658
|
+
{ kind: "optional_parameter" },
|
|
659
|
+
{ kind: "catch_clause" }
|
|
660
|
+
] } }).filter((node) => node.range().start.index < before && sameRange(nearestSourceScope(node), scope)).toSorted((a, b) => b.range().start.index - a.range().start.index);
|
|
661
|
+
for (const binding of bindings) {
|
|
662
|
+
if (!bindingNames(binding).has(name)) continue;
|
|
663
|
+
return binding.kind() === "variable_declarator" ? sourceConstVariableDeclaratorContent(binding, name) : null;
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
function sourceScopedStringVariableContent(identifier) {
|
|
667
|
+
const name = identifier.text();
|
|
668
|
+
const before = identifier.range().start.index;
|
|
669
|
+
let current = identifier.parent();
|
|
670
|
+
while (current) {
|
|
671
|
+
if (isSourceScopeNode(current)) {
|
|
672
|
+
const value = sourceStringVariableInScope(current, name, before);
|
|
673
|
+
if (value !== void 0) return value;
|
|
674
|
+
}
|
|
675
|
+
current = current.parent();
|
|
676
|
+
}
|
|
677
|
+
return null;
|
|
678
|
+
}
|
|
679
|
+
function isDynamicImportCall(node) {
|
|
680
|
+
return node.kind() === "call_expression" && node.children().some((child) => child.kind() === "import");
|
|
681
|
+
}
|
|
682
|
+
function isRequireCall(node) {
|
|
683
|
+
return node.kind() === "call_expression" && node.children().some((child) => child.kind() === "identifier" && child.text() === "require");
|
|
684
|
+
}
|
|
685
|
+
function isArgumentSyntaxNode(node) {
|
|
686
|
+
const kind = node.kind();
|
|
687
|
+
return kind === "(" || kind === ")" || kind === "," || kind === "comment";
|
|
688
|
+
}
|
|
689
|
+
function firstCallArgument(callExpression) {
|
|
690
|
+
return callExpression.children().find((child) => child.kind() === "arguments")?.children().find((child) => !isArgumentSyntaxNode(child)) ?? null;
|
|
691
|
+
}
|
|
692
|
+
function moduleCallSourceName(callExpression) {
|
|
693
|
+
const sourceArg = firstCallArgument(callExpression);
|
|
694
|
+
if (!sourceArg) return null;
|
|
695
|
+
const sourceName = literalModuleSource(sourceArg);
|
|
696
|
+
if (sourceName != null) return sourceName;
|
|
697
|
+
return sourceArg.kind() === "identifier" ? sourceScopedStringVariableContent(sourceArg) : null;
|
|
698
|
+
}
|
|
699
|
+
function dynamicImportSourceName(callExpression) {
|
|
700
|
+
return moduleCallSourceName(callExpression);
|
|
701
|
+
}
|
|
702
|
+
function dynamicImportExcerptNode(callExpression) {
|
|
703
|
+
let current = callExpression;
|
|
704
|
+
while (current.parent()) {
|
|
705
|
+
const parent = current.parent();
|
|
706
|
+
if (!parent) break;
|
|
707
|
+
if (parent.kind() !== "await_expression" && parent.kind() !== "parenthesized_expression" && parent.kind() !== "member_expression") break;
|
|
708
|
+
current = parent;
|
|
709
|
+
}
|
|
710
|
+
return current;
|
|
711
|
+
}
|
|
712
|
+
function dynamicRuntimeImportFindings(root, relativePath) {
|
|
713
|
+
const findings = [];
|
|
714
|
+
for (const callExpression of root.findAll({ rule: { kind: "call_expression" } })) {
|
|
715
|
+
if (!isDynamicImportCall(callExpression)) continue;
|
|
716
|
+
const sourceName = dynamicImportSourceName(callExpression);
|
|
717
|
+
if (!sourceName || !MODULES_BY_SOURCE.has(sourceName)) continue;
|
|
718
|
+
const excerptNode = dynamicImportExcerptNode(callExpression);
|
|
719
|
+
findings.push({
|
|
720
|
+
file: relativePath,
|
|
721
|
+
line: excerptNode.range().start.line + 1,
|
|
722
|
+
message: "Dynamic runtime subpath import may still access a removed flat value export.",
|
|
723
|
+
excerpt: excerptNode.text().trim()
|
|
724
|
+
});
|
|
725
|
+
}
|
|
726
|
+
return findings;
|
|
727
|
+
}
|
|
728
|
+
function runtimeRequireFindings(root, relativePath) {
|
|
729
|
+
const findings = [];
|
|
730
|
+
for (const callExpression of root.findAll({ rule: { kind: "call_expression" } })) {
|
|
731
|
+
if (isInsideImportStatement(callExpression)) continue;
|
|
732
|
+
if (!isRequireCall(callExpression)) continue;
|
|
733
|
+
const sourceName = moduleCallSourceName(callExpression);
|
|
734
|
+
if (!sourceName || !MODULES_BY_SOURCE.has(sourceName)) continue;
|
|
735
|
+
findings.push({
|
|
736
|
+
file: relativePath,
|
|
737
|
+
line: callExpression.range().start.line + 1,
|
|
738
|
+
message: "CommonJS runtime subpath require may still access a removed flat value export.",
|
|
739
|
+
excerpt: callExpression.text().trim()
|
|
740
|
+
});
|
|
741
|
+
}
|
|
742
|
+
return findings;
|
|
743
|
+
}
|
|
744
|
+
function isImportRequireStatement(importStmt) {
|
|
745
|
+
return importStmt.children().some((child) => child.kind() === "import_require_clause");
|
|
746
|
+
}
|
|
747
|
+
function reviewFindings(source, filePath, relativePath) {
|
|
748
|
+
if (!quickFilter(source)) return [];
|
|
749
|
+
const root = parse(sourceLang(filePath, source), source).root();
|
|
750
|
+
const imports = findImportStatements(root);
|
|
751
|
+
const findings = [];
|
|
752
|
+
findings.push(...dynamicRuntimeImportFindings(root, relativePath));
|
|
753
|
+
findings.push(...runtimeRequireFindings(root, relativePath));
|
|
754
|
+
for (const access of aggregateFileDeleteFindingNodes(root, imports)) findings.push({
|
|
755
|
+
file: relativePath,
|
|
756
|
+
line: access.range().start.line + 1,
|
|
757
|
+
message: "Aggregate runtime file namespace still uses the removed deleteFile alias.",
|
|
758
|
+
excerpt: access.text().trim()
|
|
759
|
+
});
|
|
760
|
+
for (const importStmt of imports) {
|
|
761
|
+
const sourceName = importSource(importStmt);
|
|
762
|
+
if (!sourceName) continue;
|
|
763
|
+
const mod = MODULES_BY_SOURCE.get(sourceName);
|
|
764
|
+
if (!mod) continue;
|
|
765
|
+
const hasImportRequire = isImportRequireStatement(importStmt);
|
|
766
|
+
const hasRemovedDefaultImport = hasDefaultImport(importStmt);
|
|
767
|
+
const hasNamespaceImport = namespaceImportName(importStmt) != null;
|
|
768
|
+
const hasRemovedFlatImport = hasRemovedFlatSpecifier(importStmt, mod);
|
|
769
|
+
if (!hasImportRequire && !hasRemovedDefaultImport && !hasNamespaceImport && !hasRemovedFlatImport) continue;
|
|
770
|
+
findings.push({
|
|
771
|
+
file: relativePath,
|
|
772
|
+
line: importStmt.range().start.line + 1,
|
|
773
|
+
message: hasImportRequire ? "TypeScript runtime subpath import-equals may still access a removed flat value export." : "Runtime subpath import still uses a removed default, namespace-star, or flat value import.",
|
|
774
|
+
excerpt: importStmt.text().trim()
|
|
775
|
+
});
|
|
776
|
+
}
|
|
777
|
+
for (const exportStmt of findExportStatements(root)) {
|
|
778
|
+
const sourceName = importSource(exportStmt);
|
|
779
|
+
if (!sourceName) continue;
|
|
780
|
+
const mod = MODULES_BY_SOURCE.get(sourceName);
|
|
781
|
+
if (!mod || !hasRemovedFlatSpecifier(exportStmt, mod) && !isExportStar(exportStmt)) continue;
|
|
782
|
+
findings.push({
|
|
783
|
+
file: relativePath,
|
|
784
|
+
line: exportStmt.range().start.line + 1,
|
|
785
|
+
message: "Runtime subpath re-export still uses a removed flat value export.",
|
|
786
|
+
excerpt: exportStmt.text().trim()
|
|
787
|
+
});
|
|
788
|
+
}
|
|
789
|
+
return findings;
|
|
790
|
+
}
|
|
791
|
+
//#endregion
|
|
792
|
+
export { transform as default, reviewFindings };
|
package/dist/index.js
CHANGED
|
@@ -116,6 +116,7 @@ const RENAME_BIN_QUOTED_LEGACY_COMMAND_PATTERN = new RegExp([
|
|
|
116
116
|
].join(""));
|
|
117
117
|
const V2_NEXT_1 = "2.0.0-next.1";
|
|
118
118
|
const V2_NEXT_2 = "2.0.0-next.2";
|
|
119
|
+
const V2_NEXT_3 = "2.0.0-next.3";
|
|
119
120
|
/** All registered codemods, in registration order. */
|
|
120
121
|
const allCodemods = [
|
|
121
122
|
{
|
|
@@ -494,6 +495,54 @@ const allCodemods = [
|
|
|
494
495
|
"wrapper or global tailor.authconnection."
|
|
495
496
|
].join("\n")
|
|
496
497
|
},
|
|
498
|
+
{
|
|
499
|
+
id: "v2/runtime-subpath-namespace",
|
|
500
|
+
name: "Runtime subpath imports use namespace objects",
|
|
501
|
+
description: "Rewrite `@tailor-platform/sdk/runtime/*` namespace-star and flat value imports to self-named namespace imports, and aggregate `file.deleteFile` calls to `file.delete`. `TailorContextAPI` and `TailorWorkflowAPI` now describe SDK wrappers; direct platform globals use `PlatformContextAPI` and `PlatformWorkflowAPI`.",
|
|
502
|
+
since: "1.0.0",
|
|
503
|
+
until: "2.0.0",
|
|
504
|
+
prereleaseUntil: V2_NEXT_3,
|
|
505
|
+
scriptPath: "v2/runtime-subpath-namespace/scripts/transform.js",
|
|
506
|
+
filePatterns: ["**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs}"],
|
|
507
|
+
legacyPatterns: [
|
|
508
|
+
"@tailor-platform/sdk/runtime/iconv",
|
|
509
|
+
"@tailor-platform/sdk/runtime/secretmanager",
|
|
510
|
+
"@tailor-platform/sdk/runtime/authconnection",
|
|
511
|
+
"@tailor-platform/sdk/runtime/idp",
|
|
512
|
+
"@tailor-platform/sdk/runtime/workflow",
|
|
513
|
+
"@tailor-platform/sdk/runtime/context",
|
|
514
|
+
"@tailor-platform/sdk/runtime/file",
|
|
515
|
+
"@tailor-platform/sdk/runtime/aigateway"
|
|
516
|
+
],
|
|
517
|
+
examples: [
|
|
518
|
+
{
|
|
519
|
+
before: "import * as iconv from \"@tailor-platform/sdk/runtime/iconv\";\niconv.convert(value, \"UTF-8\", \"Shift_JIS\");",
|
|
520
|
+
after: "import { iconv } from \"@tailor-platform/sdk/runtime/iconv\";\niconv.convert(value, \"UTF-8\", \"Shift_JIS\");"
|
|
521
|
+
},
|
|
522
|
+
{
|
|
523
|
+
before: "import { get } from \"@tailor-platform/sdk/runtime/aigateway\";\nconst gateway = await get(\"main\");",
|
|
524
|
+
after: "import { aigateway } from \"@tailor-platform/sdk/runtime/aigateway\";\nconst gateway = await aigateway.get(\"main\");"
|
|
525
|
+
},
|
|
526
|
+
{
|
|
527
|
+
before: "import { file } from \"@tailor-platform/sdk/runtime\";\nawait file.deleteFile(\"ns\", \"Doc\", \"blob\", \"record-id\");",
|
|
528
|
+
after: "import { file } from \"@tailor-platform/sdk/runtime\";\nawait file.delete(\"ns\", \"Doc\", \"blob\", \"record-id\");"
|
|
529
|
+
}
|
|
530
|
+
],
|
|
531
|
+
prompt: [
|
|
532
|
+
"In Tailor SDK v2, runtime subpath modules export only a self-named namespace",
|
|
533
|
+
"object (for example, `iconv` from `@tailor-platform/sdk/runtime/iconv`).",
|
|
534
|
+
"Default and flat value imports such as",
|
|
535
|
+
"`import { get } from \"@tailor-platform/sdk/runtime/aigateway\"` are removed.",
|
|
536
|
+
"The codemod rewrites straightforward namespace-star imports and flat named value",
|
|
537
|
+
"imports. It also rewrites direct `file.deleteFile` calls on the aggregate runtime",
|
|
538
|
+
"namespace to `file.delete`. Destructured aggregate `deleteFile` references require",
|
|
539
|
+
"manual migration. Review any remaining runtime imports manually, especially when",
|
|
540
|
+
"a local binding or nested scope shadows an imported value, or when",
|
|
541
|
+
"type-position namespace member references need explicit top-level type imports.",
|
|
542
|
+
"For direct platform globals, replace `TailorContextAPI` and `TailorWorkflowAPI`",
|
|
543
|
+
"type references with `PlatformContextAPI` and `PlatformWorkflowAPI` respectively."
|
|
544
|
+
].join("\n")
|
|
545
|
+
},
|
|
497
546
|
{
|
|
498
547
|
id: "v2/tailordb-namespace",
|
|
499
548
|
name: "Tailordb → tailordb (lowercase ambient namespace)",
|
|
@@ -518,6 +567,32 @@ const allCodemods = [
|
|
|
518
567
|
"declarations automatically on SDK import."
|
|
519
568
|
].join("\n")
|
|
520
569
|
},
|
|
570
|
+
{
|
|
571
|
+
id: "v2/db-type-to-table",
|
|
572
|
+
name: "db.type() → db.table()",
|
|
573
|
+
description: "Rename TailorDB schema builder calls from `db.type()` to `db.table()`. TailorDB schema definitions now use table terminology in SDK projects.",
|
|
574
|
+
since: "1.0.0",
|
|
575
|
+
until: "2.0.0",
|
|
576
|
+
prereleaseUntil: V2_NEXT_3,
|
|
577
|
+
scriptPath: "v2/db-type-to-table/scripts/transform.js",
|
|
578
|
+
filePatterns: ["**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs}"],
|
|
579
|
+
legacyPatterns: ["db.type"],
|
|
580
|
+
examples: [{
|
|
581
|
+
before: "import { db } from \"@tailor-platform/sdk\";\n\nexport const user = db.type(\"User\", {\n name: db.string(),\n});",
|
|
582
|
+
after: "import { db } from \"@tailor-platform/sdk\";\n\nexport const user = db.table(\"User\", {\n name: db.string(),\n});"
|
|
583
|
+
}],
|
|
584
|
+
prompt: [
|
|
585
|
+
"In Tailor SDK v2, TailorDB schema definitions use db.table(...) instead of",
|
|
586
|
+
"db.type(...). The codemod rewrites member accesses on db imported from",
|
|
587
|
+
"@tailor-platform/sdk, including aliases such as `import { db as schema }`.",
|
|
588
|
+
"It flags destructured builder aliases such as `const { type } = db` and",
|
|
589
|
+
"local builder aliases such as `const schema = db`, `schema = db`, or",
|
|
590
|
+
"`function make(schema = db) { ... }` for manual review because the local",
|
|
591
|
+
"alias may require call-site renaming.",
|
|
592
|
+
"Review any remaining db.type references and rename SDK TailorDB schema builder",
|
|
593
|
+
"calls to db.table. Leave unrelated local objects with a .type() method unchanged."
|
|
594
|
+
].join("\n")
|
|
595
|
+
},
|
|
521
596
|
{
|
|
522
597
|
id: "v2/execute-script-arg",
|
|
523
598
|
name: "executeScript arg JSON.stringify → value",
|