@tailor-platform/sdk-codemod 0.3.8 → 0.4.0-next.10

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 (27) hide show
  1. package/CHANGELOG.md +309 -0
  2. package/dist/codemods/ast-grep-helpers-CXtWn3RB.js +171 -0
  3. package/dist/codemods/v2/apply-to-deploy/scripts/transform.js +22 -4
  4. package/dist/codemods/v2/auth-attributes-rename/scripts/transform.js +213 -0
  5. package/dist/codemods/v2/auth-connection-token-helper/scripts/transform.js +243 -0
  6. package/dist/codemods/v2/auth-invoker-call-unwrap/scripts/transform.js +7 -0
  7. package/dist/codemods/v2/auth-invoker-unwrap/scripts/transform.js +108 -13
  8. package/dist/codemods/v2/cli-rename/scripts/transform.js +373 -14
  9. package/dist/codemods/v2/db-type-to-table/scripts/transform.js +383 -0
  10. package/dist/codemods/v2/env-var-rename/scripts/transform.js +88 -0
  11. package/dist/codemods/v2/erd-site-to-plugin/scripts/transform.js +195 -0
  12. package/dist/codemods/v2/exec-job-function-rename/scripts/transform.js +95 -0
  13. package/dist/codemods/v2/execute-script-arg/scripts/transform.js +60 -0
  14. package/dist/codemods/v2/forward-relation-name/scripts/transform.js +115 -0
  15. package/dist/codemods/v2/idp-publish-events-rename/scripts/transform.js +186 -0
  16. package/dist/codemods/v2/principal-unify/scripts/transform.js +1555 -44
  17. package/dist/codemods/v2/rename-bin/scripts/transform.js +1087 -0
  18. package/dist/codemods/v2/runtime-globals-opt-in/scripts/transform.js +103 -0
  19. package/dist/codemods/v2/runtime-subpath-namespace/scripts/transform.js +792 -0
  20. package/dist/codemods/v2/sdk-skills-shim/scripts/transform.js +3 -3
  21. package/dist/codemods/v2/seed-exec-to-cli-plugin/scripts/transform.js +115 -0
  22. package/dist/codemods/v2/tailor-output-ignore-dir/scripts/transform.js +14 -0
  23. package/dist/codemods/v2/tailordb-namespace/scripts/transform.js +5 -4
  24. package/dist/codemods/v2/wait-point-rename/scripts/transform.js +126 -0
  25. package/dist/codemods/v2/workflow-trigger-rename/scripts/transform.js +123 -0
  26. package/dist/index.js +1962 -51
  27. package/package.json +6 -5
@@ -0,0 +1,95 @@
1
+ import { a as importSource, c as localDeclarationNames, i as importBindings, o as importSpecNames, r as findImportStatements } from "../../../ast-grep-helpers-CXtWn3RB.js";
2
+ import { Lang, parse } from "@ast-grep/napi";
3
+ //#region codemods/v2/exec-job-function-rename/scripts/transform.ts
4
+ const DEPRECATED_METHOD = "startJobFunction";
5
+ const CANONICAL_METHOD = "execJobFunction";
6
+ const DEPRECATED_OPTIONS = "StartJobFunctionOptions";
7
+ const CANONICAL_OPTIONS = "ExecJobFunctionOptions";
8
+ const WORKFLOW_MODULE_SOURCES = /* @__PURE__ */ new Set(["@tailor-platform/sdk/runtime", "@tailor-platform/sdk/runtime/workflow"]);
9
+ function quickFilter(source) {
10
+ return source.includes(DEPRECATED_METHOD) || source.includes(DEPRECATED_OPTIONS);
11
+ }
12
+ function sourceLang(filePath, source) {
13
+ const lower = filePath.toLowerCase();
14
+ if (lower.endsWith(".tsx") || lower.endsWith(".jsx")) return Lang.Tsx;
15
+ return source.includes("</") || source.includes("/>") ? Lang.Tsx : Lang.TypeScript;
16
+ }
17
+ function collectWorkflowLocals(root, imports) {
18
+ const locals = /* @__PURE__ */ new Set();
19
+ for (const importStmt of imports) for (const binding of importBindings(importStmt)) if (binding.importedName === "workflow" && WORKFLOW_MODULE_SOURCES.has(binding.source)) locals.add(binding.localName);
20
+ const declaredNames = localDeclarationNames(root);
21
+ for (const name of locals) if (declaredNames.has(name)) locals.delete(name);
22
+ return locals;
23
+ }
24
+ function tailorIsAmbientGlobal(root, imports) {
25
+ if (localDeclarationNames(root).has("tailor")) return false;
26
+ return !imports.some((stmt) => importBindings(stmt).some((b) => b.localName === "tailor"));
27
+ }
28
+ function isAmbientTailorWorkflow(object) {
29
+ if (!object || object.kind() !== "member_expression") return false;
30
+ const base = object.field("object");
31
+ const property = object.field("property");
32
+ return base?.kind() === "identifier" && base.text() === "tailor" && property?.text() === "workflow";
33
+ }
34
+ /**
35
+ * Rename `StartJobFunctionOptions` import specifiers from the runtime module,
36
+ * plus the type references that resolve to them when the import is not aliased.
37
+ * @param root - Parsed file root
38
+ * @param imports - Top-level import statements
39
+ * @returns Edits renaming the deprecated options type
40
+ */
41
+ function optionsTypeEdits(root, imports) {
42
+ const edits = [];
43
+ for (const importStmt of imports) {
44
+ const source = importSource(importStmt);
45
+ if (source === null || !WORKFLOW_MODULE_SOURCES.has(source)) continue;
46
+ const specs = importStmt.findAll({ rule: { kind: "import_specifier" } });
47
+ if (specs.some((spec) => importSpecNames(spec)?.importedName === CANONICAL_OPTIONS)) continue;
48
+ for (const spec of specs) {
49
+ const names = importSpecNames(spec);
50
+ if (names?.importedName !== DEPRECATED_OPTIONS) continue;
51
+ const importedNode = spec.children().find((child) => child.kind() === "identifier");
52
+ if (!importedNode) continue;
53
+ edits.push(importedNode.replace(CANONICAL_OPTIONS));
54
+ if (names.localName !== DEPRECATED_OPTIONS) continue;
55
+ for (const reference of root.findAll({ rule: {
56
+ kind: "type_identifier",
57
+ regex: `^${DEPRECATED_OPTIONS}$`
58
+ } })) edits.push(reference.replace(CANONICAL_OPTIONS));
59
+ }
60
+ }
61
+ return edits;
62
+ }
63
+ /**
64
+ * Rewrite `.startJobFunction(` member accesses — on the ambient
65
+ * `tailor.workflow` global or on a `workflow` value imported from
66
+ * `@tailor-platform/sdk/runtime(/workflow)` — to the canonical
67
+ * `execJobFunction`, and rename the `StartJobFunctionOptions` type alias.
68
+ * @param source - File contents
69
+ * @param filePath - Absolute path to the file
70
+ * @returns Transformed source or null when nothing matched.
71
+ */
72
+ function transform(source, filePath) {
73
+ if (!quickFilter(source)) return null;
74
+ const root = parse(sourceLang(filePath ?? "", source), source).root();
75
+ const imports = findImportStatements(root);
76
+ const workflowLocals = collectWorkflowLocals(root, imports);
77
+ const tailorIsAmbient = tailorIsAmbientGlobal(root, imports);
78
+ const edits = optionsTypeEdits(root, imports);
79
+ for (const member of root.findAll({ rule: { kind: "member_expression" } })) {
80
+ const property = member.field("property");
81
+ if (!property || property.kind() !== "property_identifier") continue;
82
+ if (property.text() !== DEPRECATED_METHOD) continue;
83
+ const object = member.field("object");
84
+ if (!object) continue;
85
+ const isWorkflowImportReceiver = object.kind() === "identifier" && workflowLocals.has(object.text());
86
+ const isAmbientReceiver = tailorIsAmbient && isAmbientTailorWorkflow(object);
87
+ if (!isWorkflowImportReceiver && !isAmbientReceiver) continue;
88
+ edits.push(property.replace(CANONICAL_METHOD));
89
+ }
90
+ if (edits.length === 0) return null;
91
+ const result = root.commitEdits(edits);
92
+ return result === source ? null : result;
93
+ }
94
+ //#endregion
95
+ export { transform as default };
@@ -0,0 +1,60 @@
1
+ import { Lang, parse } from "@ast-grep/napi";
2
+ //#region codemods/v2/execute-script-arg/scripts/transform.ts
3
+ const NEEDLE = "executeScript";
4
+ function quickFilter(source) {
5
+ return source.includes(NEEDLE) && source.includes("JSON.stringify");
6
+ }
7
+ function pairKeyText(pair) {
8
+ const key = pair.children()[0];
9
+ if (!key) return null;
10
+ return key.text().replace(/^['"]|['"]$/g, "");
11
+ }
12
+ /**
13
+ * True when `stringifyCall` is the value of a top-level `arg:` property in the
14
+ * object literal passed directly to `executeScript(...)`. The chain checked is
15
+ * `JSON.stringify(...)` → pair (`arg:`) → object → arguments → `executeScript`
16
+ * call, so a nested `arg:` (e.g. `executeScript({ opts: { arg: ... } })`) or an
17
+ * unrelated `JSON.stringify` is left untouched.
18
+ */
19
+ function isExecuteScriptArg(stringifyCall) {
20
+ const pair = stringifyCall.parent();
21
+ if (!pair || pair.kind() !== "pair") return false;
22
+ if (pairKeyText(pair) !== "arg") return false;
23
+ const obj = pair.parent();
24
+ if (!obj || obj.kind() !== "object") return false;
25
+ const args = obj.parent();
26
+ if (!args || args.kind() !== "arguments") return false;
27
+ const call = args.parent();
28
+ if (!call || call.kind() !== "call_expression") return false;
29
+ const callee = call.children()[0];
30
+ return !!callee && callee.text() === NEEDLE;
31
+ }
32
+ /**
33
+ * Rewrite `executeScript({ ..., arg: JSON.stringify(X), ... })` to
34
+ * `executeScript({ ..., arg: X, ... })`.
35
+ *
36
+ * In v2 the `executeScript` `arg` option takes a JSON-serializable value and
37
+ * serializes it internally, so a pre-stringified argument double-encodes. Only
38
+ * the literal `arg: JSON.stringify(<single expr>)` form is rewritten; indirect
39
+ * forms (a stringified value held in a variable, `JSON.stringify(x, null, 2)`,
40
+ * etc.) are left for manual migration.
41
+ * @param source - File contents
42
+ * @param _filePath - Absolute path to the file (kept for the runner signature)
43
+ * @returns Transformed source or null when nothing matched.
44
+ */
45
+ function transform(source, _filePath) {
46
+ if (!quickFilter(source)) return null;
47
+ const root = parse(source.includes("</") || source.includes("/>") ? Lang.Tsx : Lang.TypeScript, source).root();
48
+ const edits = [];
49
+ for (const match of root.findAll({ rule: { pattern: "JSON.stringify($X)" } })) {
50
+ if (!isExecuteScriptArg(match)) continue;
51
+ const inner = match.getMatch("X");
52
+ if (!inner) continue;
53
+ edits.push(match.replace(inner.text()));
54
+ }
55
+ if (edits.length === 0) return null;
56
+ const result = root.commitEdits(edits);
57
+ return result === source ? null : result;
58
+ }
59
+ //#endregion
60
+ export { transform as default };
@@ -0,0 +1,115 @@
1
+ import { l as stringValue } from "../../../ast-grep-helpers-CXtWn3RB.js";
2
+ import { Lang, parse } from "@ast-grep/napi";
3
+ //#region codemods/v2/forward-relation-name/scripts/transform.ts
4
+ function sourceLang(filePath, source) {
5
+ const lowerPath = filePath.toLowerCase();
6
+ if (/\.(?:ts|mts|cts)$/u.test(lowerPath)) return Lang.TypeScript;
7
+ if (/\.(?:tsx|jsx|js)$/u.test(lowerPath)) return Lang.Tsx;
8
+ return source.includes("</") ? Lang.Tsx : Lang.TypeScript;
9
+ }
10
+ function isRelationCall(call, aliases) {
11
+ const callee = call.children()[0];
12
+ if (!callee) return false;
13
+ if (callee.kind() === "identifier") return aliases.has(callee.text());
14
+ if (callee.kind() === "subscript_expression") {
15
+ const property = literalStringValue(callee.field("index"));
16
+ return property === null || property === "relation";
17
+ }
18
+ if (callee.kind() !== "member_expression") return false;
19
+ return callee.children().findLast((child) => child.kind() === "property_identifier" || child.kind() === "identifier")?.text() === "relation";
20
+ }
21
+ function relationBindingName(pattern) {
22
+ if (pattern.kind() !== "object_pattern") return null;
23
+ for (const child of pattern.children()) {
24
+ if (child.kind() === "shorthand_property_identifier_pattern" && child.text() === "relation") return child.text();
25
+ if (child.kind() === "pair_pattern" && stringValue(child.field("key")) === "relation") {
26
+ const value = child.field("value");
27
+ return value?.kind() === "identifier" ? value.text() : null;
28
+ }
29
+ if (child.kind() === "object_assignment_pattern") {
30
+ const binding = child.children().find((node) => node.kind() === "shorthand_property_identifier_pattern");
31
+ if (binding?.text() === "relation") return binding.text();
32
+ }
33
+ }
34
+ return null;
35
+ }
36
+ function relationAliases(root) {
37
+ const aliases = /* @__PURE__ */ new Set();
38
+ for (const pattern of root.findAll({ rule: { kind: "object_pattern" } })) {
39
+ const name = relationBindingName(pattern);
40
+ if (name) aliases.add(name);
41
+ }
42
+ return aliases;
43
+ }
44
+ function callArgument(call) {
45
+ const args = call.children().find((child) => child.kind() === "arguments");
46
+ if (!args) return null;
47
+ const values = args.children().filter((child) => {
48
+ const kind = child.kind();
49
+ return kind !== "(" && kind !== ")" && kind !== "," && kind !== "comment";
50
+ });
51
+ return values.length === 1 ? values[0] : null;
52
+ }
53
+ function pairKey(pair) {
54
+ const key = pair.children()[0];
55
+ return stringValue(key ?? null);
56
+ }
57
+ function pairValue(pair) {
58
+ const children = pair.children();
59
+ const colonIndex = children.findIndex((child) => child.kind() === ":");
60
+ if (colonIndex === -1) return null;
61
+ return children.slice(colonIndex + 1).find((child) => child.kind() !== "comment") ?? null;
62
+ }
63
+ function objectPair(object, key) {
64
+ return object.children().find((child) => child.kind() === "pair" && pairKey(child) === key) ?? null;
65
+ }
66
+ function literalStringValue(node) {
67
+ if (node?.kind() !== "string") return null;
68
+ return stringValue(node);
69
+ }
70
+ function hasDynamicProperties(object) {
71
+ return object.children().some((child) => {
72
+ const kind = child.kind();
73
+ if (kind === "{" || kind === "}" || kind === "," || kind === "comment") return false;
74
+ if (kind !== "pair") return true;
75
+ const keyKind = child.children()[0]?.kind();
76
+ return keyKind !== "property_identifier" && keyKind !== "string";
77
+ });
78
+ }
79
+ function needsReview(call) {
80
+ const config = callArgument(call);
81
+ if (config?.kind() !== "object") return config != null;
82
+ if (hasDynamicProperties(config)) return true;
83
+ const relationType = objectPair(config, "type");
84
+ const toward = objectPair(config, "toward");
85
+ if (!relationType || !toward) return false;
86
+ if (literalStringValue(pairValue(relationType)) === "keyOnly") return false;
87
+ const towardConfig = pairValue(toward);
88
+ if (towardConfig?.kind() !== "object") return towardConfig != null;
89
+ if (hasDynamicProperties(towardConfig)) return true;
90
+ const targetType = objectPair(towardConfig, "type");
91
+ if (targetType && literalStringValue(pairValue(targetType)) === "self") return false;
92
+ const as = objectPair(towardConfig, "as");
93
+ if (as) {
94
+ const explicitName = literalStringValue(pairValue(as));
95
+ return explicitName === null || explicitName.length === 0;
96
+ }
97
+ if (!targetType) return false;
98
+ return true;
99
+ }
100
+ function transform(_source, _filePath) {
101
+ return null;
102
+ }
103
+ function reviewFindings(source, filePath, relativePath) {
104
+ if (!source.includes("relation")) return [];
105
+ const root = parse(sourceLang(filePath, source), source).root();
106
+ const aliases = relationAliases(root);
107
+ return root.findAll({ rule: { kind: "call_expression" } }).filter((call) => isRelationCall(call, aliases) && needsReview(call)).map((call) => ({
108
+ file: relativePath,
109
+ line: call.range().start.line + 1,
110
+ message: "Review the v2 forward GraphQL field name or add an explicit toward.as.",
111
+ excerpt: call.text().split("\n", 1)[0].trim()
112
+ }));
113
+ }
114
+ //#endregion
115
+ export { transform as default, reviewFindings };
@@ -0,0 +1,186 @@
1
+ import { a as importSource, c as localDeclarationNames, i as importBindings, l as stringValue, r as findImportStatements, s as isTypeOnlyImport } from "../../../ast-grep-helpers-CXtWn3RB.js";
2
+ import { Lang, parse } from "@ast-grep/napi";
3
+ //#region codemods/v2/idp-publish-events-rename/scripts/transform.ts
4
+ const SDK_MODULE = "@tailor-platform/sdk";
5
+ const DEFINE_IDP = "defineIdp";
6
+ const LEGACY_KEY = "publishUserEvents";
7
+ const NEW_KEY = "publishEvents";
8
+ function sourceLang(filePath, source) {
9
+ return filePath.endsWith(".tsx") || filePath.endsWith(".jsx") || source.includes("</") ? Lang.Tsx : Lang.TypeScript;
10
+ }
11
+ function collectDefineIdpNames(root) {
12
+ const direct = /* @__PURE__ */ new Set();
13
+ const namespaces = /* @__PURE__ */ new Set();
14
+ for (const importStmt of findImportStatements(root)) {
15
+ if (importSource(importStmt) !== SDK_MODULE || isTypeOnlyImport(importStmt)) continue;
16
+ for (const binding of importBindings(importStmt)) if (binding.importedName === DEFINE_IDP) direct.add(binding.localName);
17
+ for (const namespaceImport of importStmt.findAll({ rule: { kind: "namespace_import" } })) for (const identifier of namespaceImport.children()) if (identifier.kind() === "identifier") namespaces.add(identifier.text());
18
+ }
19
+ return {
20
+ direct,
21
+ namespaces
22
+ };
23
+ }
24
+ function memberProperty(member) {
25
+ return member.children().findLast((child) => child.kind() === "property_identifier" || child.kind() === "identifier") ?? null;
26
+ }
27
+ function isDefineIdpCall(call, names) {
28
+ const callee = call.children()[0];
29
+ if (!callee) return false;
30
+ if (callee.kind() === "identifier") return names.direct.has(callee.text());
31
+ if (callee.kind() !== "member_expression") return false;
32
+ const object = callee.field("object");
33
+ return object?.kind() === "identifier" && names.namespaces.has(object.text()) && memberProperty(callee)?.text() === DEFINE_IDP;
34
+ }
35
+ function objectArguments(call) {
36
+ const args = call.children().find((child) => child.kind() === "arguments");
37
+ if (!args) return [];
38
+ return args.children().filter((child) => child.kind() === "object");
39
+ }
40
+ function findLegacyEntries(object) {
41
+ const entries = [];
42
+ for (const child of object.children()) {
43
+ if (child.kind() === "shorthand_property_identifier" && child.text() === LEGACY_KEY) {
44
+ entries.push({
45
+ node: child,
46
+ shorthand: true
47
+ });
48
+ continue;
49
+ }
50
+ if (child.kind() !== "pair") continue;
51
+ const key = child.children()[0];
52
+ if (!key) continue;
53
+ if (key.kind() !== "property_identifier" && key.kind() !== "string") continue;
54
+ if (stringValue(key) !== LEGACY_KEY) continue;
55
+ entries.push({
56
+ node: key,
57
+ shorthand: false
58
+ });
59
+ }
60
+ return entries;
61
+ }
62
+ function renameEdit(entry) {
63
+ if (entry.shorthand) return entry.node.replace(`${NEW_KEY}: ${LEGACY_KEY}`);
64
+ const text = entry.node.text();
65
+ if (entry.node.kind() !== "string") return entry.node.replace(NEW_KEY);
66
+ const quote = text.startsWith("'") ? "'" : text.startsWith("`") ? "`" : "\"";
67
+ return entry.node.replace(`${quote}${NEW_KEY}${quote}`);
68
+ }
69
+ function parseRoot(source, filePath) {
70
+ try {
71
+ return parse(sourceLang(filePath, source), source).root();
72
+ } catch {
73
+ return null;
74
+ }
75
+ }
76
+ function shadowsDefineIdp(root, names) {
77
+ const declared = localDeclarationNames(root);
78
+ return [...names.direct, ...names.namespaces].some((name) => declared.has(name));
79
+ }
80
+ /**
81
+ * Rename the `publishUserEvents` option to `publishEvents` on `defineIdp` calls.
82
+ * @param source - File contents
83
+ * @param filePath - Path to the file being transformed
84
+ * @returns Transformed source, or null when nothing matched
85
+ */
86
+ function transform(source, filePath = "") {
87
+ if (!source.includes(LEGACY_KEY) || !source.includes(SDK_MODULE)) return null;
88
+ const root = parseRoot(source, filePath);
89
+ if (!root) return null;
90
+ const names = collectDefineIdpNames(root);
91
+ if (names.direct.size === 0 && names.namespaces.size === 0) return null;
92
+ if (shadowsDefineIdp(root, names)) return null;
93
+ const edits = [];
94
+ for (const call of root.findAll({ rule: { kind: "call_expression" } })) {
95
+ if (!isDefineIdpCall(call, names)) continue;
96
+ for (const object of objectArguments(call)) for (const entry of findLegacyEntries(object)) edits.push(renameEdit(entry));
97
+ }
98
+ return edits.length > 0 ? root.commitEdits(edits) : null;
99
+ }
100
+ function lineOf(node) {
101
+ return node.range().start.line + 1;
102
+ }
103
+ function excerptOf(node) {
104
+ return node.text().split("\n", 1)[0].trim();
105
+ }
106
+ function coversIndex(ranges, index) {
107
+ return ranges.some((range) => index >= range.start && index < range.end);
108
+ }
109
+ /** Quoted keys, e.g. `{ "publishUserEvents": true }`, whose node kind is a string. */
110
+ function quotedLegacyKeys(root) {
111
+ return root.findAll({ rule: { kind: "pair" } }).flatMap((pair) => {
112
+ const key = pair.children()[0];
113
+ return key?.kind() === "string" && stringValue(key) === LEGACY_KEY ? [key] : [];
114
+ });
115
+ }
116
+ /** Computed keys whose value cannot be read statically, e.g. `[key]: value`. */
117
+ function computedKeyEntries(object) {
118
+ return object.children().filter((child) => child.kind() === "pair").flatMap((pair) => {
119
+ const key = pair.children()[0];
120
+ return key?.kind() === "computed_property_name" ? [key] : [];
121
+ });
122
+ }
123
+ /**
124
+ * Report `publishUserEvents` occurrences the transform cannot rewrite.
125
+ *
126
+ * Occurrences nested deeper inside a `defineIdp` options object belong to a
127
+ * different option shape (e.g. `userAuthPolicy`) and are left alone, so only
128
+ * what the transform genuinely missed is reported.
129
+ * @param source - File contents
130
+ * @param filePath - Path to the file being reviewed
131
+ * @param relativePath - Repository-relative path reported to the user
132
+ * @returns Findings for occurrences needing a manual rename
133
+ */
134
+ function reviewFindings(source, filePath, relativePath) {
135
+ if (!source.includes(LEGACY_KEY) || !source.includes(SDK_MODULE)) return [];
136
+ const root = parseRoot(source, filePath);
137
+ if (!root) return [];
138
+ const names = collectDefineIdpNames(root);
139
+ const hasDefineIdp = names.direct.size > 0 || names.namespaces.size > 0;
140
+ if (hasDefineIdp && shadowsDefineIdp(root, names)) return [{
141
+ file: relativePath,
142
+ line: 1,
143
+ message: `A local declaration shadows the SDK ${DEFINE_IDP} import; rename ${LEGACY_KEY} to ${NEW_KEY} by hand.`,
144
+ excerpt: LEGACY_KEY
145
+ }];
146
+ const rewritten = /* @__PURE__ */ new Set();
147
+ const optionRanges = [];
148
+ const findings = [];
149
+ if (hasDefineIdp) for (const call of root.findAll({ rule: { kind: "call_expression" } })) {
150
+ if (!isDefineIdpCall(call, names)) continue;
151
+ for (const object of objectArguments(call)) {
152
+ const range = object.range();
153
+ optionRanges.push({
154
+ start: range.start.index,
155
+ end: range.end.index
156
+ });
157
+ for (const entry of findLegacyEntries(object)) rewritten.add(entry.node.range().start.index);
158
+ for (const key of computedKeyEntries(object)) findings.push({
159
+ file: relativePath,
160
+ line: lineOf(key),
161
+ message: `A computed ${DEFINE_IDP} option key may be ${LEGACY_KEY}; rename it to ${NEW_KEY} if so.`,
162
+ excerpt: excerptOf(key)
163
+ });
164
+ }
165
+ }
166
+ const residual = [...root.findAll({ rule: { any: [{
167
+ kind: "property_identifier",
168
+ regex: `^${LEGACY_KEY}$`
169
+ }, {
170
+ kind: "shorthand_property_identifier",
171
+ regex: `^${LEGACY_KEY}$`
172
+ }] } }), ...quotedLegacyKeys(root)];
173
+ for (const node of residual) {
174
+ const index = node.range().start.index;
175
+ if (rewritten.has(index) || coversIndex(optionRanges, index)) continue;
176
+ findings.push({
177
+ file: relativePath,
178
+ line: lineOf(node),
179
+ message: `Rename the IdP option ${LEGACY_KEY} to ${NEW_KEY}.`,
180
+ excerpt: excerptOf(node)
181
+ });
182
+ }
183
+ return findings;
184
+ }
185
+ //#endregion
186
+ export { transform as default, reviewFindings };