@mittwald/flow-codemods 1.1.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.
- package/README.md +106 -0
- package/dist/catalog/entries.d.ts +17 -0
- package/dist/catalog/entries.d.ts.map +1 -0
- package/dist/catalog/entries.js +9 -0
- package/dist/catalog/select.d.ts +28 -0
- package/dist/catalog/select.d.ts.map +1 -0
- package/dist/catalog/select.js +27 -0
- package/dist/catalog/types.d.ts +48 -0
- package/dist/catalog/types.d.ts.map +1 -0
- package/dist/catalog/types.js +0 -0
- package/dist/checks/context.d.ts +16 -0
- package/dist/checks/context.d.ts.map +1 -0
- package/dist/checks/context.js +72 -0
- package/dist/checks/load.d.ts +13 -0
- package/dist/checks/load.d.ts.map +1 -0
- package/dist/checks/load.js +53 -0
- package/dist/checks/types.d.ts +49 -0
- package/dist/checks/types.d.ts.map +1 -0
- package/dist/checks/types.js +0 -0
- package/dist/cli/args.d.ts +22 -0
- package/dist/cli/args.d.ts.map +1 -0
- package/dist/cli/args.js +56 -0
- package/dist/cli/choose.d.ts +31 -0
- package/dist/cli/choose.d.ts.map +1 -0
- package/dist/cli/choose.js +31 -0
- package/dist/cli/codemod.d.ts +24 -0
- package/dist/cli/codemod.d.ts.map +1 -0
- package/dist/cli/codemod.js +76 -0
- package/dist/cli/detect.d.ts +35 -0
- package/dist/cli/detect.d.ts.map +1 -0
- package/dist/cli/detect.js +62 -0
- package/dist/cli/list.d.ts +71 -0
- package/dist/cli/list.d.ts.map +1 -0
- package/dist/cli/list.js +293 -0
- package/dist/cli/upgrade.d.ts +34 -0
- package/dist/cli/upgrade.d.ts.map +1 -0
- package/dist/cli/upgrade.js +217 -0
- package/dist/cli/verify.d.ts +77 -0
- package/dist/cli/verify.d.ts.map +1 -0
- package/dist/cli/verify.js +148 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +117 -0
- package/dist/detect/align-to-combine.d.ts +8 -0
- package/dist/detect/align-to-combine.d.ts.map +1 -0
- package/dist/detect/align-to-combine.js +9 -0
- package/dist/detect/overlay-controller-add-on-close-return-type.d.ts +9 -0
- package/dist/detect/overlay-controller-add-on-close-return-type.d.ts.map +1 -0
- package/dist/detect/overlay-controller-add-on-close-return-type.js +10 -0
- package/dist/detect/tooltip-trigger-delay-type.d.ts +11 -0
- package/dist/detect/tooltip-trigger-delay-type.d.ts.map +1 -0
- package/dist/detect/tooltip-trigger-delay-type.js +12 -0
- package/dist/flowPackages.generated.d.ts +3 -0
- package/dist/flowPackages.generated.d.ts.map +1 -0
- package/dist/flowPackages.generated.js +18 -0
- package/dist/git.d.ts +13 -0
- package/dist/git.d.ts.map +1 -0
- package/dist/git.js +41 -0
- package/dist/install.d.ts +37 -0
- package/dist/install.d.ts.map +1 -0
- package/dist/install.js +71 -0
- package/dist/manifest.d.ts +45 -0
- package/dist/manifest.d.ts.map +1 -0
- package/dist/manifest.js +88 -0
- package/dist/migrations.generated.d.ts +4 -0
- package/dist/migrations.generated.d.ts.map +1 -0
- package/dist/migrations.generated.js +213 -0
- package/dist/resolve/range.d.ts +50 -0
- package/dist/resolve/range.d.ts.map +1 -0
- package/dist/resolve/range.js +173 -0
- package/dist/resolve/registry.d.ts +33 -0
- package/dist/resolve/registry.d.ts.map +1 -0
- package/dist/resolve/registry.js +69 -0
- package/dist/resolve/target.d.ts +63 -0
- package/dist/resolve/target.d.ts.map +1 -0
- package/dist/resolve/target.js +115 -0
- package/dist/run/jscodeshift.d.ts +42 -0
- package/dist/run/jscodeshift.d.ts.map +1 -0
- package/dist/run/jscodeshift.js +88 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/dist/verify/align-to-combine.d.ts +11 -0
- package/dist/verify/align-to-combine.d.ts.map +1 -0
- package/dist/verify/align-to-combine.js +22 -0
- package/dist/verify/overlay-controller-add-on-close-return-type.d.ts +14 -0
- package/dist/verify/overlay-controller-add-on-close-return-type.d.ts.map +1 -0
- package/dist/verify/overlay-controller-add-on-close-return-type.js +21 -0
- package/dist/verify/tooltip-trigger-delay-type.d.ts +13 -0
- package/dist/verify/tooltip-trigger-delay-type.d.ts.map +1 -0
- package/dist/verify/tooltip-trigger-delay-type.js +18 -0
- package/package.json +41 -0
- package/src/migrations/accent-box-color-to-background-color/transform.ts +218 -0
- package/src/migrations/action-prop-to-on-action/transform.ts +110 -0
- package/src/migrations/align-to-combine/transform.ts +212 -0
- package/src/migrations/button-color-accent-to-success/transform.ts +164 -0
- package/src/migrations/button-props-interfaces/transform.ts +229 -0
- package/src/migrations/color-primary-to-default/transform.ts +173 -0
- package/src/migrations/imports-to-package-root/transform.ts +107 -0
- package/src/migrations/muted-action-error-to-abort-action-error/transform.ts +260 -0
- package/src/migrations/password-tools-rule/transform.ts +219 -0
- package/src/migrations/password-tools-subpath-renamed/transform.ts +61 -0
- package/src/tools/to-remote-package.ts +37 -0
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import { writeFileSync } from "node:fs";
|
|
2
|
+
import { isAbsolute, relative } from "node:path";
|
|
3
|
+
import { gt } from "semver";
|
|
4
|
+
import { allEntries } from "../catalog/entries.js";
|
|
5
|
+
import { selectEntries } from "../catalog/select.js";
|
|
6
|
+
import { flowPackages } from "../flowPackages.generated.js";
|
|
7
|
+
import { hasUncommittedChanges } from "../git.js";
|
|
8
|
+
import { detectPackageManagerIn, runInstall, } from "../install.js";
|
|
9
|
+
import { applyTarget, isWritable } from "../manifest.js";
|
|
10
|
+
import { fetchVersions } from "../resolve/registry.js";
|
|
11
|
+
import { readInstalledVersion, resolveRange } from "../resolve/range.js";
|
|
12
|
+
import { runCodemod } from "../run/jscodeshift.js";
|
|
13
|
+
import { resolveSourcePath } from "./codemod.js";
|
|
14
|
+
import { renderList } from "./list.js";
|
|
15
|
+
export const defaultUpgradeDeps = (cwd) => ({
|
|
16
|
+
cwd,
|
|
17
|
+
fetchVersions,
|
|
18
|
+
install: runInstall,
|
|
19
|
+
runCodemod,
|
|
20
|
+
choose: async (entries) => entries,
|
|
21
|
+
isDirty: hasUncommittedChanges,
|
|
22
|
+
readInstalledVersion,
|
|
23
|
+
log: (message) => process.stdout.write(`${message}\n`),
|
|
24
|
+
});
|
|
25
|
+
/**
|
|
26
|
+
* Whether `path` lies outside `cwd`.
|
|
27
|
+
*
|
|
28
|
+
* `--path` can point anywhere, including outside the repository the dirty-tree
|
|
29
|
+
* guard checks. `path` is always resolved (see `resolveSourcePath`), so this is
|
|
30
|
+
* a plain prefix test via `relative` rather than a string comparison that a
|
|
31
|
+
* trailing slash or `..` segment could fool.
|
|
32
|
+
*/
|
|
33
|
+
const isOutside = (path, cwd) => {
|
|
34
|
+
const rel = relative(cwd, path);
|
|
35
|
+
return rel !== "" && (rel.startsWith("..") || isAbsolute(rel));
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* The indentation the manifest already uses, read from its first indented line.
|
|
39
|
+
*
|
|
40
|
+
* Reusing it (instead of always emitting 2 spaces) keeps the rewrite to the
|
|
41
|
+
* dependency lines a consumer actually cares about — a tabs- or 4-space project
|
|
42
|
+
* otherwise gets its whole `package.json` reformatted inside the upgrade diff.
|
|
43
|
+
*/
|
|
44
|
+
const detectIndent = (raw) => {
|
|
45
|
+
const match = /\n([ \t]+)\S/.exec(raw);
|
|
46
|
+
return match?.[1] ?? " ";
|
|
47
|
+
};
|
|
48
|
+
/**
|
|
49
|
+
* Bump every Flow dependency, install, then run the codemods the resolved range
|
|
50
|
+
* calls for — and end by naming what no codemod covers.
|
|
51
|
+
*
|
|
52
|
+
* The order is not cosmetic: the codemods run against the installed target, so
|
|
53
|
+
* `tsc` can be green when the command returns. `--dry` skips both the manifest
|
|
54
|
+
* write and the install, so the codemods it still runs act against whatever is
|
|
55
|
+
* currently installed rather than the target — their output is indicative, not
|
|
56
|
+
* exact.
|
|
57
|
+
*
|
|
58
|
+
* Selection has no lower bound at all (see `selectEntries`) — every entry with
|
|
59
|
+
* `since <= target` is in, codemod or manual — so a project already on `target`
|
|
60
|
+
* still gets a full pass: it skips only the write and the install, which
|
|
61
|
+
* genuinely have nothing to do.
|
|
62
|
+
*/
|
|
63
|
+
export const runUpgrade = async (parsed, deps) => {
|
|
64
|
+
const { cwd, log } = deps;
|
|
65
|
+
const dry = parsed.dry === true;
|
|
66
|
+
// Resolved before the dirty-tree guard below: an explicit --path can point
|
|
67
|
+
// outside the repository the guard would otherwise check, and codemods
|
|
68
|
+
// rewrite files in place there just the same.
|
|
69
|
+
const path = resolveSourcePath(parsed.path, cwd);
|
|
70
|
+
const outsideRepo = isOutside(path, cwd);
|
|
71
|
+
if (!parsed.allowDirty &&
|
|
72
|
+
(deps.isDirty(cwd) || (outsideRepo && deps.isDirty(path)))) {
|
|
73
|
+
log("The working tree has uncommitted changes. Codemods rewrite files in place, so commit or stash first — or pass --allow-dirty.");
|
|
74
|
+
return 1;
|
|
75
|
+
}
|
|
76
|
+
const { revision } = parsed;
|
|
77
|
+
if (revision === undefined) {
|
|
78
|
+
// args.ts always defaults this for the "upgrade" command — this only
|
|
79
|
+
// guards a ParsedCommand built by hand without it, so it is not a second
|
|
80
|
+
// source of truth for the default.
|
|
81
|
+
log("No revision given.");
|
|
82
|
+
return 1;
|
|
83
|
+
}
|
|
84
|
+
const range = await resolveRange(revision, deps);
|
|
85
|
+
if (!range.ok) {
|
|
86
|
+
log(range.reason);
|
|
87
|
+
return 1;
|
|
88
|
+
}
|
|
89
|
+
const { manifestPath, manifestRaw, manifest, dependencies, current, target } = range;
|
|
90
|
+
// A stale dist-tag or an exact version at or below `current` resolves
|
|
91
|
+
// without complaint — `resolveRange` deliberately does not judge that
|
|
92
|
+
// (`list` treats the same fact as a legitimate answer). There is nothing to
|
|
93
|
+
// bump or install in that case, but that does not mean there is nothing to
|
|
94
|
+
// run: selection has no lower bound (see `selectEntries`). A consumer can
|
|
95
|
+
// already be sitting on `target` having never run this tool once, which is
|
|
96
|
+
// the exact gap this command exists to close, so the migration pass below
|
|
97
|
+
// still runs; only the bump and the install are skipped.
|
|
98
|
+
// One reporter for both branches: a peer range is reported, never rewritten
|
|
99
|
+
// (see `writableFields` in manifest.ts), and `--dry` must say the same thing
|
|
100
|
+
// the real run does.
|
|
101
|
+
const reportDependencies = () => {
|
|
102
|
+
for (const dependency of dependencies) {
|
|
103
|
+
log(isWritable(dependency)
|
|
104
|
+
? ` ${dependency.name} → ${target}`
|
|
105
|
+
: ` ${dependency.name} ${dependency.range} — left as it is, ${dependency.field} states what your package supports, not what it installs`);
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
const bump = gt(target, current);
|
|
109
|
+
if (!bump) {
|
|
110
|
+
// Nothing to write or install — fall through to the codemod pass below.
|
|
111
|
+
log(`Already on ${current}; "${revision}" resolves to ${target}. No dependency bump needed — checking for codemods to catch up on, since nothing records whether this project already ran them.`);
|
|
112
|
+
}
|
|
113
|
+
else if (dry) {
|
|
114
|
+
log(`Upgrading Flow from ${current} to ${target} (--dry)`);
|
|
115
|
+
log(`--dry: would write the following to ${manifestPath}:`);
|
|
116
|
+
reportDependencies();
|
|
117
|
+
log("--dry: skipping the install.");
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
log(`Upgrading Flow from ${current} to ${target}`);
|
|
121
|
+
const indent = detectIndent(manifestRaw);
|
|
122
|
+
writeFileSync(manifestPath, `${JSON.stringify(applyTarget(manifest, target, flowPackages), null, indent)}\n`, "utf8");
|
|
123
|
+
reportDependencies();
|
|
124
|
+
const manager = detectPackageManagerIn(cwd);
|
|
125
|
+
log(`Installing with ${manager}`);
|
|
126
|
+
try {
|
|
127
|
+
deps.install(manager, cwd);
|
|
128
|
+
}
|
|
129
|
+
catch (error) {
|
|
130
|
+
log(`The dependency bump was written but the install failed, so package.json is on\n${target} while node_modules still holds ${current}.\n\nEither re-run this command once the install works, or undo the bump with\n git checkout package.json\n\n${error instanceof Error ? error.message : error}`);
|
|
131
|
+
return 1;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
const selected = selectEntries(allEntries, target);
|
|
135
|
+
const automatic = selected.filter((entry) => entry.action === "codemod");
|
|
136
|
+
const byHand = selected.filter((entry) => entry.action !== "codemod");
|
|
137
|
+
const chosen = await deps.choose(automatic);
|
|
138
|
+
// Only meaningful once a bump actually moves the installed version away
|
|
139
|
+
// from `target` — when there is no bump, the codemods below run against
|
|
140
|
+
// exactly `target` already, so there is no "not exact" caveat to raise.
|
|
141
|
+
if (dry && bump && chosen.length > 0) {
|
|
142
|
+
log(`\n--dry: running the codemods against the currently installed version, not ${target} — their output is indicative, not exact.`);
|
|
143
|
+
}
|
|
144
|
+
let hadFailure = false;
|
|
145
|
+
const incomplete = [];
|
|
146
|
+
let ranCount = 0;
|
|
147
|
+
let changedCount = 0;
|
|
148
|
+
for (const entry of chosen) {
|
|
149
|
+
ranCount += 1;
|
|
150
|
+
let result;
|
|
151
|
+
try {
|
|
152
|
+
result = await deps.runCodemod({
|
|
153
|
+
id: entry.id,
|
|
154
|
+
path,
|
|
155
|
+
dry: parsed.dry,
|
|
156
|
+
print: parsed.print,
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
catch (error) {
|
|
160
|
+
hadFailure = true;
|
|
161
|
+
incomplete.push(entry.id);
|
|
162
|
+
log(` ${entry.id}: failed to run — ${error instanceof Error ? error.message : error}`);
|
|
163
|
+
continue;
|
|
164
|
+
}
|
|
165
|
+
if (result.changed > 0) {
|
|
166
|
+
changedCount += 1;
|
|
167
|
+
}
|
|
168
|
+
// Same three-way distinction as the single-codemod command: "0 changed" on
|
|
169
|
+
// its own would read as success where nothing was looked at, or where the
|
|
170
|
+
// transform declined everything it saw.
|
|
171
|
+
if (result.errors > 0) {
|
|
172
|
+
hadFailure = true;
|
|
173
|
+
log(` ${entry.id}: ${result.errors} file(s) failed to transform`);
|
|
174
|
+
}
|
|
175
|
+
else if (result.processedNothing) {
|
|
176
|
+
hadFailure = true;
|
|
177
|
+
log(` ${entry.id}: no files under ${path} were processed`);
|
|
178
|
+
}
|
|
179
|
+
else if (result.changed === 0 && result.skipped > 0) {
|
|
180
|
+
hadFailure = true;
|
|
181
|
+
log(` ${entry.id}: declined all ${result.skipped} file(s) it looked at`);
|
|
182
|
+
}
|
|
183
|
+
else {
|
|
184
|
+
log(` ${entry.id}: ${result.changed} file(s) changed`);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
// Selection has no lower bound (see `selectEntries`), so this loop can run
|
|
188
|
+
// every codemod in the catalogue on a project that never crossed a version
|
|
189
|
+
// at all — "N run, 0 changed" is the confirmation that there was nothing to
|
|
190
|
+
// catch up on, not a list of new work.
|
|
191
|
+
if (ranCount > 0) {
|
|
192
|
+
log(`\n${ranCount} codemod${ranCount === 1 ? "" : "s"} run, ${changedCount} changed something.`);
|
|
193
|
+
}
|
|
194
|
+
if (byHand.length > 0) {
|
|
195
|
+
log(`\n${byHand.length} migration(s) in this range have no codemod — apply them by hand:\n`);
|
|
196
|
+
// `frame: false` — this already printed the heading above and, further
|
|
197
|
+
// down, its own aggregate ("N codemods run, N changed something");
|
|
198
|
+
// renderList's own frame (the range/legend on top, the counts at the
|
|
199
|
+
// bottom) would just repeat both for the same list. `range` is still
|
|
200
|
+
// passed so each entry gets its catch-up mark — a manual entry that
|
|
201
|
+
// shipped at or before `current` may already be done, and the mark is
|
|
202
|
+
// the only thing left that says so now that hiding it is gone.
|
|
203
|
+
log(renderList({
|
|
204
|
+
entries: byHand,
|
|
205
|
+
range: { from: current, to: target },
|
|
206
|
+
json: false,
|
|
207
|
+
frame: false,
|
|
208
|
+
}));
|
|
209
|
+
}
|
|
210
|
+
else {
|
|
211
|
+
log("\nNo migration in this range required a change by hand.");
|
|
212
|
+
}
|
|
213
|
+
if (incomplete.length > 0) {
|
|
214
|
+
log(`\n${incomplete.length} codemod(s) did not complete: ${incomplete.join(", ")}`);
|
|
215
|
+
}
|
|
216
|
+
return hadFailure ? 1 : 0;
|
|
217
|
+
};
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { type CatalogEntry } from "../catalog/entries.js";
|
|
2
|
+
import { loadVerifier } from "../checks/load.js";
|
|
3
|
+
import type { VerifyResult } from "../checks/types.js";
|
|
4
|
+
export interface VerifyEntryResult {
|
|
5
|
+
entry: CatalogEntry;
|
|
6
|
+
result: VerifyResult;
|
|
7
|
+
}
|
|
8
|
+
export interface RunVerifyDeps {
|
|
9
|
+
/** Which entries to try. Defaults to the whole catalogue. */
|
|
10
|
+
entries?: CatalogEntry[];
|
|
11
|
+
/** Injectable for tests — defaults to the real module loader. */
|
|
12
|
+
load?: typeof loadVerifier;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Runs every available verifier over `path`.
|
|
16
|
+
*
|
|
17
|
+
* Only a fraction of the catalogue has a verifier module so far (see
|
|
18
|
+
* `checks/load.ts`); an id with none is skipped, not reported as a failure —
|
|
19
|
+
* the remaining verifiers are follow-up work, not something this run got
|
|
20
|
+
* wrong.
|
|
21
|
+
*/
|
|
22
|
+
export declare const runVerify: (path: string, { entries, load }?: RunVerifyDeps) => Promise<VerifyEntryResult[]>;
|
|
23
|
+
/**
|
|
24
|
+
* Three counts, not one pass/fail verdict — conflating them is exactly the "23
|
|
25
|
+
* entries still need a person" problem this replaces: with every verifier in
|
|
26
|
+
* place, almost all of them would carry at least the typecheck reminder, so
|
|
27
|
+
* counting "has a hint" swamps the entries that actually need a person's
|
|
28
|
+
* judgement with the ones that only need the same routine `tsc --noEmit` run.
|
|
29
|
+
*
|
|
30
|
+
* - `failed` — `ok: false`: this run found something concretely wrong.
|
|
31
|
+
* - `needsReview` — `ok: true` but `hints` is non-empty: nothing this module
|
|
32
|
+
* could decide is wrong, but a person still has to judge something no
|
|
33
|
+
* compiler check can catch.
|
|
34
|
+
* - `verified` — `ok: true` and `hints` is empty: nothing left for a person to
|
|
35
|
+
* judge here (the typecheck below may still apply). Deliberately not
|
|
36
|
+
* "done"/"complete": see `checks/types.ts`'s doc comment on `VerifyResult`
|
|
37
|
+
* for why `ok: true` never means the migration is finished.
|
|
38
|
+
*
|
|
39
|
+
* `typecheckCount` is tallied separately again: it is not a judgement call like
|
|
40
|
+
* `needsReview`, and `tsc --noEmit` is one command a person runs once for the
|
|
41
|
+
* whole project, not once per entry — so it is reported as a single closing
|
|
42
|
+
* line, not folded into any of the three per-entry buckets.
|
|
43
|
+
*/
|
|
44
|
+
export declare const summarize: (results: VerifyEntryResult[]) => {
|
|
45
|
+
failed: number;
|
|
46
|
+
needsReview: number;
|
|
47
|
+
verified: number;
|
|
48
|
+
typecheckCount: number;
|
|
49
|
+
};
|
|
50
|
+
/**
|
|
51
|
+
* Renders `runVerify`'s result as text — the presentation half kept separate
|
|
52
|
+
* from the async file-reading half, the same split `renderList` uses.
|
|
53
|
+
*
|
|
54
|
+
* Prints only what needs attention. An entry that is `ok: true` with no hints
|
|
55
|
+
* has nothing left for a person to judge (the closing typecheck line still
|
|
56
|
+
* covers it if it relies on one) — it swells the count in the header and
|
|
57
|
+
* nothing else, the same way `verified` is counted but never itself printed.
|
|
58
|
+
* That is the fix for the four problems the maintainer called out: a passing
|
|
59
|
+
* entry whose only hint was the typecheck reminder no longer earns a block at
|
|
60
|
+
* all, the reminder itself moves out of the loop to one line at the end,
|
|
61
|
+
* hints stop repeating their own label, and findings show a path relative to
|
|
62
|
+
* `path` instead of the absolute one the header already established.
|
|
63
|
+
*/
|
|
64
|
+
export declare const renderVerify: (results: VerifyEntryResult[], path: string, { color, width }?: {
|
|
65
|
+
color?: boolean;
|
|
66
|
+
width?: number;
|
|
67
|
+
}) => string;
|
|
68
|
+
/**
|
|
69
|
+
* Whether a `verify` run should exit non-zero: exactly when at least one
|
|
70
|
+
* verifier decided `ok: false`. A hint alone — even every entry carrying one —
|
|
71
|
+
* is the normal "a person still has to look" state, not a failure, so it must
|
|
72
|
+
* never flip this. Kept as its own pure function (over `results`, not
|
|
73
|
+
* `process.exitCode` directly) so a test can drive it with injected fakes
|
|
74
|
+
* instead of spawning the CLI.
|
|
75
|
+
*/
|
|
76
|
+
export declare const verifyExitCode: (results: VerifyEntryResult[]) => number;
|
|
77
|
+
//# sourceMappingURL=verify.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verify.d.ts","sourceRoot":"","sources":["../../src/cli/verify.ts"],"names":[],"mappings":"AAEA,OAAO,EAAc,KAAK,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAEtE,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAGvD,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,YAAY,CAAC;IACpB,MAAM,EAAE,YAAY,CAAC;CACtB;AAED,MAAM,WAAW,aAAa;IAC5B,6DAA6D;IAC7D,OAAO,CAAC,EAAE,YAAY,EAAE,CAAC;IACzB,iEAAiE;IACjE,IAAI,CAAC,EAAE,OAAO,YAAY,CAAC;CAC5B;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,SAAS,SACd,MAAM,sBACmC,aAAa,KAC3D,OAAO,CAAC,iBAAiB,EAAE,CAa7B,CAAC;AA2DF;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,SAAS,YACX,iBAAiB,EAAE,KAC3B;IACD,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,EAAE,MAAM,CAAC;CAqBxB,CAAC;AAEF;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,YAAY,YACd,iBAAiB,EAAE,QACtB,MAAM,qBACmB;IAAE,KAAK,CAAC,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,KACjE,MA8BF,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,cAAc,YAAa,iBAAiB,EAAE,KAAG,MACZ,CAAC"}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { relative } from "node:path";
|
|
2
|
+
import colors from "picocolors";
|
|
3
|
+
import { allEntries } from "../catalog/entries.js";
|
|
4
|
+
import { createCheckContext } from "../checks/context.js";
|
|
5
|
+
import { loadVerifier } from "../checks/load.js";
|
|
6
|
+
import { indent, painter, wrap } from "./list.js";
|
|
7
|
+
/**
|
|
8
|
+
* Runs every available verifier over `path`.
|
|
9
|
+
*
|
|
10
|
+
* Only a fraction of the catalogue has a verifier module so far (see
|
|
11
|
+
* `checks/load.ts`); an id with none is skipped, not reported as a failure —
|
|
12
|
+
* the remaining verifiers are follow-up work, not something this run got
|
|
13
|
+
* wrong.
|
|
14
|
+
*/
|
|
15
|
+
export const runVerify = async (path, { entries = allEntries, load = loadVerifier } = {}) => {
|
|
16
|
+
const context = createCheckContext(path);
|
|
17
|
+
const results = [];
|
|
18
|
+
for (const entry of entries) {
|
|
19
|
+
const verifier = await load(entry.id);
|
|
20
|
+
if (verifier === undefined) {
|
|
21
|
+
continue;
|
|
22
|
+
}
|
|
23
|
+
results.push({ entry, result: await verifier.verify(context) });
|
|
24
|
+
}
|
|
25
|
+
return results;
|
|
26
|
+
};
|
|
27
|
+
/** One `path:line text` row, path shown relative to the search root. */
|
|
28
|
+
const findingLine = (finding, width, paint, root) => {
|
|
29
|
+
const location = paint.dim(`${relative(root, finding.file)}:${finding.line}`);
|
|
30
|
+
const body = wrap(finding.text, Math.max(width - indent.length, 20));
|
|
31
|
+
return body.map((line, index) => index === 0 ? `${indent}${location} ${line}` : `${indent} ${line}`);
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* One hint, wrapped and indented under its migration. No repeated label — a
|
|
35
|
+
* hint that wraps to several lines said "hint" once per line before this,
|
|
36
|
+
* which is the label, not the content, taking up the repetition.
|
|
37
|
+
*/
|
|
38
|
+
const hintLine = (hint, width) => wrap(hint, Math.max(width - indent.length, 20)).map((line) => `${indent}${line}`);
|
|
39
|
+
/**
|
|
40
|
+
* Renders one migration's block: `!` for `ok: false`, `?` for `ok: true` with
|
|
41
|
+
* a hint — the only two states this function is ever called for, see
|
|
42
|
+
* `renderVerify`'s filter. Findings first, then hints; the `typecheckHint`
|
|
43
|
+
* never appears here — it is one global action, folded into the closing line
|
|
44
|
+
* instead of repeated under every entry that relies on it.
|
|
45
|
+
*/
|
|
46
|
+
const renderEntry = ({ entry, result }, width, color, root) => {
|
|
47
|
+
const paint = painter(color);
|
|
48
|
+
const mark = result.ok
|
|
49
|
+
? color
|
|
50
|
+
? colors.yellow("?")
|
|
51
|
+
: "?"
|
|
52
|
+
: color
|
|
53
|
+
? colors.red("!")
|
|
54
|
+
: "!";
|
|
55
|
+
const lines = [
|
|
56
|
+
`${mark} ${paint.bold(entry.id)}`,
|
|
57
|
+
...result.findings.flatMap((finding) => findingLine(finding, width, paint, root)),
|
|
58
|
+
...result.hints.flatMap((hint) => hintLine(hint, width)),
|
|
59
|
+
];
|
|
60
|
+
return lines.join("\n");
|
|
61
|
+
};
|
|
62
|
+
/**
|
|
63
|
+
* Three counts, not one pass/fail verdict — conflating them is exactly the "23
|
|
64
|
+
* entries still need a person" problem this replaces: with every verifier in
|
|
65
|
+
* place, almost all of them would carry at least the typecheck reminder, so
|
|
66
|
+
* counting "has a hint" swamps the entries that actually need a person's
|
|
67
|
+
* judgement with the ones that only need the same routine `tsc --noEmit` run.
|
|
68
|
+
*
|
|
69
|
+
* - `failed` — `ok: false`: this run found something concretely wrong.
|
|
70
|
+
* - `needsReview` — `ok: true` but `hints` is non-empty: nothing this module
|
|
71
|
+
* could decide is wrong, but a person still has to judge something no
|
|
72
|
+
* compiler check can catch.
|
|
73
|
+
* - `verified` — `ok: true` and `hints` is empty: nothing left for a person to
|
|
74
|
+
* judge here (the typecheck below may still apply). Deliberately not
|
|
75
|
+
* "done"/"complete": see `checks/types.ts`'s doc comment on `VerifyResult`
|
|
76
|
+
* for why `ok: true` never means the migration is finished.
|
|
77
|
+
*
|
|
78
|
+
* `typecheckCount` is tallied separately again: it is not a judgement call like
|
|
79
|
+
* `needsReview`, and `tsc --noEmit` is one command a person runs once for the
|
|
80
|
+
* whole project, not once per entry — so it is reported as a single closing
|
|
81
|
+
* line, not folded into any of the three per-entry buckets.
|
|
82
|
+
*/
|
|
83
|
+
export const summarize = (results) => {
|
|
84
|
+
let failed = 0;
|
|
85
|
+
let needsReview = 0;
|
|
86
|
+
let verified = 0;
|
|
87
|
+
let typecheckCount = 0;
|
|
88
|
+
for (const { result } of results) {
|
|
89
|
+
if (!result.ok) {
|
|
90
|
+
failed++;
|
|
91
|
+
}
|
|
92
|
+
else if (result.hints.length > 0) {
|
|
93
|
+
needsReview++;
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
verified++;
|
|
97
|
+
}
|
|
98
|
+
if (result.typecheckHint !== undefined) {
|
|
99
|
+
typecheckCount++;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return { failed, needsReview, verified, typecheckCount };
|
|
103
|
+
};
|
|
104
|
+
/**
|
|
105
|
+
* Renders `runVerify`'s result as text — the presentation half kept separate
|
|
106
|
+
* from the async file-reading half, the same split `renderList` uses.
|
|
107
|
+
*
|
|
108
|
+
* Prints only what needs attention. An entry that is `ok: true` with no hints
|
|
109
|
+
* has nothing left for a person to judge (the closing typecheck line still
|
|
110
|
+
* covers it if it relies on one) — it swells the count in the header and
|
|
111
|
+
* nothing else, the same way `verified` is counted but never itself printed.
|
|
112
|
+
* That is the fix for the four problems the maintainer called out: a passing
|
|
113
|
+
* entry whose only hint was the typecheck reminder no longer earns a block at
|
|
114
|
+
* all, the reminder itself moves out of the loop to one line at the end,
|
|
115
|
+
* hints stop repeating their own label, and findings show a path relative to
|
|
116
|
+
* `path` instead of the absolute one the header already established.
|
|
117
|
+
*/
|
|
118
|
+
export const renderVerify = (results, path, { color = false, width = 80 } = {}) => {
|
|
119
|
+
const paint = painter(color);
|
|
120
|
+
if (results.length === 0) {
|
|
121
|
+
return `No verifiers are available yet for anything under ${path}.\n`;
|
|
122
|
+
}
|
|
123
|
+
const { failed, needsReview, typecheckCount } = summarize(results);
|
|
124
|
+
const header = [
|
|
125
|
+
`${paint.bold(String(results.length))} checked`,
|
|
126
|
+
`${paint.bold(String(failed))} failed`,
|
|
127
|
+
`${paint.bold(String(needsReview))} needs review`,
|
|
128
|
+
].join(" · ");
|
|
129
|
+
const needsAttention = results.filter(({ result }) => !result.ok || result.hints.length > 0);
|
|
130
|
+
const body = needsAttention.length === 0
|
|
131
|
+
? "Nothing needs attention."
|
|
132
|
+
: needsAttention
|
|
133
|
+
.map((entryResult) => renderEntry(entryResult, width, color, path))
|
|
134
|
+
.join("\n\n");
|
|
135
|
+
const typecheckLine = typecheckCount === 0
|
|
136
|
+
? ""
|
|
137
|
+
: `\n\nThen run \`tsc --noEmit\` once — ${typecheckCount} of these rely on it.`;
|
|
138
|
+
return `${header}\n\n${body}${typecheckLine}\n`;
|
|
139
|
+
};
|
|
140
|
+
/**
|
|
141
|
+
* Whether a `verify` run should exit non-zero: exactly when at least one
|
|
142
|
+
* verifier decided `ok: false`. A hint alone — even every entry carrying one —
|
|
143
|
+
* is the normal "a person still has to look" state, not a failure, so it must
|
|
144
|
+
* never flip this. Kept as its own pure function (over `results`, not
|
|
145
|
+
* `process.exitCode` directly) so a test can drive it with injected fakes
|
|
146
|
+
* instead of spawning the CLI.
|
|
147
|
+
*/
|
|
148
|
+
export const verifyExitCode = (results) => results.some(({ result }) => !result.ok) ? 1 : 0;
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { readFileSync } from "node:fs";
|
|
3
|
+
import { parseArguments } from "./cli/args.js";
|
|
4
|
+
import { createChoose } from "./cli/choose.js";
|
|
5
|
+
import { runSingleCodemod } from "./cli/codemod.js";
|
|
6
|
+
import { defaultListDeps, runList } from "./cli/list.js";
|
|
7
|
+
import { defaultUpgradeDeps, runUpgrade } from "./cli/upgrade.js";
|
|
8
|
+
const usage = `flow-codemods — migrate a codebase across Flow versions
|
|
9
|
+
|
|
10
|
+
Usage:
|
|
11
|
+
flow-codemods upgrade [revision] Bump every Flow dependency, install, run codemods
|
|
12
|
+
flow-codemods <id> [path] Run a single codemod
|
|
13
|
+
flow-codemods list [revision] Show the migrations for the whole catalogue, or
|
|
14
|
+
for the range a revision would touch
|
|
15
|
+
|
|
16
|
+
Revision: patch | minor | major | a dist-tag (latest, next) | an exact version.
|
|
17
|
+
"upgrade" defaults to minor, which stays inside the current major. "list" has
|
|
18
|
+
no default — without one it lists the whole catalogue, offline.
|
|
19
|
+
|
|
20
|
+
Options:
|
|
21
|
+
-y, --yes Accept every default. Implied when stdin is not a TTY.
|
|
22
|
+
--allow-dirty Run even though the working tree has uncommitted changes
|
|
23
|
+
--dry Do not write files
|
|
24
|
+
--print Print the transformed output
|
|
25
|
+
--path Sources to transform
|
|
26
|
+
--json Machine-readable output for "list"
|
|
27
|
+
-h, --help Show this text
|
|
28
|
+
-V, --version Show the version
|
|
29
|
+
`;
|
|
30
|
+
const main = async () => {
|
|
31
|
+
const parsed = parseArguments(process.argv.slice(2));
|
|
32
|
+
// Deliberate single ordered report stream: every command-level failure below
|
|
33
|
+
// that a real invocation can hit — `list`'s and `upgrade`'s range resolution,
|
|
34
|
+
// `codemod`'s and `upgrade`'s injected `log` — writes to stdout, interleaved
|
|
35
|
+
// with the rest of that command's normal output, in the order it happened.
|
|
36
|
+
// (The `default` case is dead code — `Command` is exhaustively covered above
|
|
37
|
+
// it — so its stderr write never actually runs.) Only the top-level
|
|
38
|
+
// rejection handler below (an unexpected throw, not a command refusing)
|
|
39
|
+
// writes to stderr. So `2>/dev/null` on this CLI does not suppress a
|
|
40
|
+
// refusal reason today — do not "fix" a command's own failure message onto
|
|
41
|
+
// stderr without weighing that.
|
|
42
|
+
switch (parsed.command) {
|
|
43
|
+
case "help":
|
|
44
|
+
process.stdout.write(usage);
|
|
45
|
+
return 0;
|
|
46
|
+
case "version": {
|
|
47
|
+
// `readFileSync` rather than an import attribute: `with { type: "json" }`
|
|
48
|
+
// was the newest syntax in this package and the only thing forcing the
|
|
49
|
+
// Node floor above what the rest of the code needs. Consumers reach this
|
|
50
|
+
// CLI through `npx`, so that floor is theirs, not the repo's.
|
|
51
|
+
const manifest = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8"));
|
|
52
|
+
process.stdout.write(`${manifest.version}\n`);
|
|
53
|
+
return 0;
|
|
54
|
+
}
|
|
55
|
+
case "list":
|
|
56
|
+
return await runList(parsed, {
|
|
57
|
+
...defaultListDeps(process.cwd()),
|
|
58
|
+
// Colour only when a person is looking at a terminal. `NO_COLOR` is
|
|
59
|
+
// the cross-tool convention for turning it off; a pipe or a file gets
|
|
60
|
+
// plain text so the output stays greppable.
|
|
61
|
+
color: process.stdout.isTTY === true &&
|
|
62
|
+
process.env.NO_COLOR === undefined &&
|
|
63
|
+
!parsed.json,
|
|
64
|
+
// Clamped at both ends: a terminal can report an unusably small width
|
|
65
|
+
// (or none at all), and beyond ~100 columns long prose gets harder to
|
|
66
|
+
// read rather than easier.
|
|
67
|
+
width: Math.min(Math.max(process.stdout.columns ?? 80, 60), 100),
|
|
68
|
+
});
|
|
69
|
+
case "codemod":
|
|
70
|
+
return await runSingleCodemod(parsed, {
|
|
71
|
+
cwd: process.cwd(),
|
|
72
|
+
log: (message) => process.stdout.write(`${message}\n`),
|
|
73
|
+
});
|
|
74
|
+
case "upgrade": {
|
|
75
|
+
// Loaded here, not at the top: `list` and a single-codemod run never
|
|
76
|
+
// prompt, and paying for the prompt library's module graph on every
|
|
77
|
+
// invocation is the kind of cost a CLI is judged by.
|
|
78
|
+
const { checkbox } = await import("@inquirer/prompts");
|
|
79
|
+
// `-y` accepts every default, and no TTY implies it: CI and agent runs
|
|
80
|
+
// have nobody to answer the prompt. That is also why the dirty-tree guard
|
|
81
|
+
// exists — see git.ts.
|
|
82
|
+
const choose = createChoose({
|
|
83
|
+
yes: parsed.yes,
|
|
84
|
+
isTTY: process.stdin.isTTY === true,
|
|
85
|
+
isCI: process.env.CI !== undefined,
|
|
86
|
+
onCancel: (message) => process.stdout.write(`${message}\n`),
|
|
87
|
+
prompt: (entries) => checkbox({
|
|
88
|
+
message: "Which codemods should run?",
|
|
89
|
+
choices: entries.map((entry) => ({
|
|
90
|
+
name: `${entry.id} — ${entry.title}`,
|
|
91
|
+
value: entry.id,
|
|
92
|
+
checked: true,
|
|
93
|
+
})),
|
|
94
|
+
}),
|
|
95
|
+
});
|
|
96
|
+
return runUpgrade(parsed, {
|
|
97
|
+
...defaultUpgradeDeps(process.cwd()),
|
|
98
|
+
choose,
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
default:
|
|
102
|
+
process.stderr.write(`"${parsed.command}" is not implemented yet\n`);
|
|
103
|
+
return 1;
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
// `process.exitCode` rather than `process.exit()`: exiting explicitly can
|
|
107
|
+
// terminate the process before a pending write to stdout has flushed, which on
|
|
108
|
+
// POSIX is the common case when stdout is a pipe. Every command exits through
|
|
109
|
+
// here, including `list --json` and the upgrade report, so the output that would
|
|
110
|
+
// be truncated is the largest output this CLI produces. Nothing keeps a handle
|
|
111
|
+
// open, so Node exits on its own once the event loop drains.
|
|
112
|
+
main().then((code) => {
|
|
113
|
+
process.exitCode = code;
|
|
114
|
+
}, (error) => {
|
|
115
|
+
process.stderr.write(`${error instanceof Error ? error.message : error}\n`);
|
|
116
|
+
process.exitCode = 1;
|
|
117
|
+
});
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { Detector } from "../checks/types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Translated from the catalogue's `detect: rg -t ts '\bAlign(Props)?\b'` — see
|
|
4
|
+
* the translation rule in the plan: `rg -t ts 'PATTERN'` becomes
|
|
5
|
+
* `search(/PATTERN/, tsExtensions)`.
|
|
6
|
+
*/
|
|
7
|
+
export declare const detector: Detector;
|
|
8
|
+
//# sourceMappingURL=align-to-combine.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"align-to-combine.d.ts","sourceRoot":"","sources":["../../src/detect/align-to-combine.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAEnD;;;;GAIG;AACH,eAAO,MAAM,QAAQ,EAAE,QAEtB,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { tsExtensions } from "../checks/context.js";
|
|
2
|
+
/**
|
|
3
|
+
* Translated from the catalogue's `detect: rg -t ts '\bAlign(Props)?\b'` — see
|
|
4
|
+
* the translation rule in the plan: `rg -t ts 'PATTERN'` becomes
|
|
5
|
+
* `search(/PATTERN/, tsExtensions)`.
|
|
6
|
+
*/
|
|
7
|
+
export const detector = {
|
|
8
|
+
detect: (context) => context.search(/\bAlign(Props)?\b/, tsExtensions),
|
|
9
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Detector } from "../checks/types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Translated from the catalogue's `detect: rg -t ts 'addOnClose|addOnOpen'`.
|
|
4
|
+
* Every call site needs a by-hand look at its handler — see
|
|
5
|
+
* `src/verify/overlay-controller-add-on-close-return-type.ts` for why no
|
|
6
|
+
* compiler check can narrow this list further.
|
|
7
|
+
*/
|
|
8
|
+
export declare const detector: Detector;
|
|
9
|
+
//# sourceMappingURL=overlay-controller-add-on-close-return-type.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"overlay-controller-add-on-close-return-type.d.ts","sourceRoot":"","sources":["../../src/detect/overlay-controller-add-on-close-return-type.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAEnD;;;;;GAKG;AACH,eAAO,MAAM,QAAQ,EAAE,QAEtB,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { tsExtensions } from "../checks/context.js";
|
|
2
|
+
/**
|
|
3
|
+
* Translated from the catalogue's `detect: rg -t ts 'addOnClose|addOnOpen'`.
|
|
4
|
+
* Every call site needs a by-hand look at its handler — see
|
|
5
|
+
* `src/verify/overlay-controller-add-on-close-return-type.ts` for why no
|
|
6
|
+
* compiler check can narrow this list further.
|
|
7
|
+
*/
|
|
8
|
+
export const detector = {
|
|
9
|
+
detect: (context) => context.search(/addOnClose|addOnOpen/, tsExtensions),
|
|
10
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Detector } from "../checks/types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Translated from the catalogue's `detect: rg -t ts 'TooltipTrigger'`. This
|
|
4
|
+
* over-matches on purpose — every `TooltipTrigger` usage, not just the ones
|
|
5
|
+
* with a numeric `delay` — because a regex can't tell a numeric literal from a
|
|
6
|
+
* string one without a full parse; the reader looks at each hit. See
|
|
7
|
+
* `src/verify/tooltip-trigger-delay-type.ts` for why the real check is left to
|
|
8
|
+
* `tsc --noEmit` instead.
|
|
9
|
+
*/
|
|
10
|
+
export declare const detector: Detector;
|
|
11
|
+
//# sourceMappingURL=tooltip-trigger-delay-type.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tooltip-trigger-delay-type.d.ts","sourceRoot":"","sources":["../../src/detect/tooltip-trigger-delay-type.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAEnD;;;;;;;GAOG;AACH,eAAO,MAAM,QAAQ,EAAE,QAEtB,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { tsExtensions } from "../checks/context.js";
|
|
2
|
+
/**
|
|
3
|
+
* Translated from the catalogue's `detect: rg -t ts 'TooltipTrigger'`. This
|
|
4
|
+
* over-matches on purpose — every `TooltipTrigger` usage, not just the ones
|
|
5
|
+
* with a numeric `delay` — because a regex can't tell a numeric literal from a
|
|
6
|
+
* string one without a full parse; the reader looks at each hit. See
|
|
7
|
+
* `src/verify/tooltip-trigger-delay-type.ts` for why the real check is left to
|
|
8
|
+
* `tsc --noEmit` instead.
|
|
9
|
+
*/
|
|
10
|
+
export const detector = {
|
|
11
|
+
detect: (context) => context.search(/TooltipTrigger/, tsExtensions),
|
|
12
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"flowPackages.generated.d.ts","sourceRoot":"","sources":["../src/flowPackages.generated.ts"],"names":[],"mappings":"AAIA,mFAAmF;AACnF,eAAO,MAAM,YAAY,EAAE,MAAM,EAahC,CAAC"}
|