react-doctor 0.0.27 → 0.0.28
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/dist/cli.js +220 -100
- package/dist/cli.js.map +1 -1
- package/dist/index.js +4 -4
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createRequire } from "node:module";
|
|
3
3
|
import { execSync, spawn, spawnSync } from "node:child_process";
|
|
4
|
-
import fs, { appendFileSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
4
|
+
import fs, { appendFileSync, existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from "node:fs";
|
|
5
5
|
import os, { homedir, tmpdir } from "node:os";
|
|
6
6
|
import path, { join } from "node:path";
|
|
7
7
|
import { Command } from "commander";
|
|
8
8
|
import { randomUUID } from "node:crypto";
|
|
9
9
|
import { performance } from "node:perf_hooks";
|
|
10
10
|
import pc from "picocolors";
|
|
11
|
+
import basePrompts from "prompts";
|
|
11
12
|
import { main } from "knip";
|
|
12
13
|
import { createOptions } from "knip/session";
|
|
13
14
|
import { fileURLToPath } from "node:url";
|
|
14
15
|
import ora from "ora";
|
|
15
|
-
import basePrompts from "prompts";
|
|
16
16
|
|
|
17
17
|
//#region src/constants.ts
|
|
18
18
|
const SOURCE_FILE_PATTERN = /\.(tsx?|jsx?)$/;
|
|
@@ -40,6 +40,8 @@ const WARNING_RULE_PENALTY = .75;
|
|
|
40
40
|
const ERROR_ESTIMATED_FIX_RATE = .85;
|
|
41
41
|
const WARNING_ESTIMATED_FIX_RATE = .8;
|
|
42
42
|
const MAX_KNIP_RETRIES = 5;
|
|
43
|
+
const OXLINT_NODE_REQUIREMENT = "^20.19.0 || >=22.12.0";
|
|
44
|
+
const OXLINT_RECOMMENDED_NODE_MAJOR = 24;
|
|
43
45
|
const AMI_WEBSITE_URL = "https://ami.dev";
|
|
44
46
|
const AMI_INSTALL_URL = `${AMI_WEBSITE_URL}/install.sh`;
|
|
45
47
|
const AMI_RELEASES_URL = "https://github.com/millionco/ami-releases/releases";
|
|
@@ -641,6 +643,167 @@ const loadConfig = (rootDirectory) => {
|
|
|
641
643
|
return null;
|
|
642
644
|
};
|
|
643
645
|
|
|
646
|
+
//#endregion
|
|
647
|
+
//#region src/utils/should-auto-select-current-choice.ts
|
|
648
|
+
const shouldAutoSelectCurrentChoice = (choiceStates, cursor) => {
|
|
649
|
+
if (choiceStates.some((choiceState) => choiceState.selected)) return false;
|
|
650
|
+
const currentChoice = choiceStates[cursor];
|
|
651
|
+
return Boolean(currentChoice) && !currentChoice.disabled;
|
|
652
|
+
};
|
|
653
|
+
|
|
654
|
+
//#endregion
|
|
655
|
+
//#region src/utils/should-select-all-choices.ts
|
|
656
|
+
const shouldSelectAllChoices = (choiceStates) => {
|
|
657
|
+
return choiceStates.filter((choiceState) => !choiceState.disabled).some((choiceState) => choiceState.selected !== true);
|
|
658
|
+
};
|
|
659
|
+
|
|
660
|
+
//#endregion
|
|
661
|
+
//#region src/utils/prompts.ts
|
|
662
|
+
const require = createRequire(import.meta.url);
|
|
663
|
+
const PROMPTS_MULTISELECT_MODULE_PATH = "prompts/lib/elements/multiselect";
|
|
664
|
+
const PROMPTS_SELECT_MODULE_PATH = "prompts/lib/elements/select";
|
|
665
|
+
let didPatchMultiselectToggleAll = false;
|
|
666
|
+
let didPatchMultiselectSubmit = false;
|
|
667
|
+
let didPatchSelectBanner = false;
|
|
668
|
+
const selectBannerMap = /* @__PURE__ */ new Map();
|
|
669
|
+
const setSelectBanner = (banner, targetIndex) => {
|
|
670
|
+
selectBannerMap.set(targetIndex, banner);
|
|
671
|
+
};
|
|
672
|
+
const clearSelectBanner = () => {
|
|
673
|
+
selectBannerMap.clear();
|
|
674
|
+
};
|
|
675
|
+
const onCancel = () => {
|
|
676
|
+
logger.break();
|
|
677
|
+
logger.log("Cancelled.");
|
|
678
|
+
logger.dim("Run `npx react-doctor@latest --fix` to fix issues.");
|
|
679
|
+
logger.break();
|
|
680
|
+
process.exit(0);
|
|
681
|
+
};
|
|
682
|
+
const patchMultiselectToggleAll = () => {
|
|
683
|
+
if (didPatchMultiselectToggleAll) return;
|
|
684
|
+
didPatchMultiselectToggleAll = true;
|
|
685
|
+
const multiselectPromptConstructor = require(PROMPTS_MULTISELECT_MODULE_PATH);
|
|
686
|
+
multiselectPromptConstructor.prototype.toggleAll = function() {
|
|
687
|
+
const isCurrentChoiceDisabled = Boolean(this.value[this.cursor]?.disabled);
|
|
688
|
+
if (this.maxChoices !== void 0 || isCurrentChoiceDisabled) {
|
|
689
|
+
this.bell();
|
|
690
|
+
return;
|
|
691
|
+
}
|
|
692
|
+
const shouldSelectAllEnabledChoices = shouldSelectAllChoices(this.value);
|
|
693
|
+
for (const choiceState of this.value) {
|
|
694
|
+
if (choiceState.disabled) continue;
|
|
695
|
+
choiceState.selected = shouldSelectAllEnabledChoices;
|
|
696
|
+
}
|
|
697
|
+
this.render();
|
|
698
|
+
};
|
|
699
|
+
};
|
|
700
|
+
const patchMultiselectSubmit = () => {
|
|
701
|
+
if (didPatchMultiselectSubmit) return;
|
|
702
|
+
didPatchMultiselectSubmit = true;
|
|
703
|
+
const multiselectPromptConstructor = require(PROMPTS_MULTISELECT_MODULE_PATH);
|
|
704
|
+
const originalSubmit = multiselectPromptConstructor.prototype.submit;
|
|
705
|
+
multiselectPromptConstructor.prototype.submit = function() {
|
|
706
|
+
if (shouldAutoSelectCurrentChoice(this.value, this.cursor)) this.value[this.cursor].selected = true;
|
|
707
|
+
originalSubmit.call(this);
|
|
708
|
+
};
|
|
709
|
+
};
|
|
710
|
+
const patchSelectBanner = () => {
|
|
711
|
+
if (didPatchSelectBanner) return;
|
|
712
|
+
didPatchSelectBanner = true;
|
|
713
|
+
const selectConstructor = require(PROMPTS_SELECT_MODULE_PATH);
|
|
714
|
+
const promptsClear = require("prompts/lib/util/clear");
|
|
715
|
+
const originalRender = selectConstructor.prototype.render;
|
|
716
|
+
selectConstructor.prototype.render = function() {
|
|
717
|
+
originalRender.call(this);
|
|
718
|
+
const banner = selectBannerMap.get(this.cursor);
|
|
719
|
+
if (!banner || this.closed || this.done) return;
|
|
720
|
+
this.out.write(promptsClear(this.outputText, this.out.columns));
|
|
721
|
+
this.outputText = `${banner}\n\n${this.outputText}`;
|
|
722
|
+
this.out.write(this.outputText);
|
|
723
|
+
};
|
|
724
|
+
};
|
|
725
|
+
const prompts = (questions) => {
|
|
726
|
+
patchMultiselectToggleAll();
|
|
727
|
+
patchMultiselectSubmit();
|
|
728
|
+
patchSelectBanner();
|
|
729
|
+
return basePrompts(questions, { onCancel });
|
|
730
|
+
};
|
|
731
|
+
|
|
732
|
+
//#endregion
|
|
733
|
+
//#region src/utils/resolve-compatible-node.ts
|
|
734
|
+
const parseNodeVersion = (versionString) => {
|
|
735
|
+
const [major = 0, minor = 0, patch = 0] = versionString.replace(/^v/, "").trim().split(".").map(Number);
|
|
736
|
+
return {
|
|
737
|
+
major,
|
|
738
|
+
minor,
|
|
739
|
+
patch
|
|
740
|
+
};
|
|
741
|
+
};
|
|
742
|
+
const isNodeVersionCompatibleWithOxlint = ({ major, minor }) => {
|
|
743
|
+
if (major === 20 && minor >= 19) return true;
|
|
744
|
+
if (major === 22 && minor >= 12) return true;
|
|
745
|
+
if (major > 22) return true;
|
|
746
|
+
return false;
|
|
747
|
+
};
|
|
748
|
+
const isCurrentNodeCompatibleWithOxlint = () => isNodeVersionCompatibleWithOxlint(parseNodeVersion(process.version));
|
|
749
|
+
const getNvmDirectory = () => {
|
|
750
|
+
const envNvmDirectory = process.env.NVM_DIR;
|
|
751
|
+
if (envNvmDirectory && existsSync(envNvmDirectory)) return envNvmDirectory;
|
|
752
|
+
const defaultNvmDirectory = path.join(os.homedir(), ".nvm");
|
|
753
|
+
if (existsSync(defaultNvmDirectory)) return defaultNvmDirectory;
|
|
754
|
+
return null;
|
|
755
|
+
};
|
|
756
|
+
const isNvmInstalled = () => getNvmDirectory() !== null;
|
|
757
|
+
const findCompatibleNvmBinary = () => {
|
|
758
|
+
const nvmDirectory = getNvmDirectory();
|
|
759
|
+
if (!nvmDirectory) return null;
|
|
760
|
+
const versionsDirectory = path.join(nvmDirectory, "versions", "node");
|
|
761
|
+
if (!existsSync(versionsDirectory)) return null;
|
|
762
|
+
const compatibleVersions = readdirSync(versionsDirectory).filter((directoryName) => directoryName.startsWith("v")).map((directoryName) => ({
|
|
763
|
+
directoryName,
|
|
764
|
+
...parseNodeVersion(directoryName)
|
|
765
|
+
})).filter((version) => isNodeVersionCompatibleWithOxlint(version)).sort((versionA, versionB) => versionB.major - versionA.major || versionB.minor - versionA.minor || versionB.patch - versionA.patch);
|
|
766
|
+
if (compatibleVersions.length === 0) return null;
|
|
767
|
+
const bestVersion = compatibleVersions[0];
|
|
768
|
+
const binaryPath = path.join(versionsDirectory, bestVersion.directoryName, "bin", "node");
|
|
769
|
+
return existsSync(binaryPath) ? binaryPath : null;
|
|
770
|
+
};
|
|
771
|
+
const getNodeVersionFromBinary = (binaryPath) => {
|
|
772
|
+
try {
|
|
773
|
+
return execSync(`"${binaryPath}" --version`, { encoding: "utf-8" }).trim();
|
|
774
|
+
} catch {
|
|
775
|
+
return null;
|
|
776
|
+
}
|
|
777
|
+
};
|
|
778
|
+
const installNodeViaNvm = () => {
|
|
779
|
+
const nvmDirectory = getNvmDirectory();
|
|
780
|
+
if (!nvmDirectory) return false;
|
|
781
|
+
const nvmScript = path.join(nvmDirectory, "nvm.sh");
|
|
782
|
+
if (!existsSync(nvmScript)) return false;
|
|
783
|
+
try {
|
|
784
|
+
execSync(`bash -c ". '${nvmScript}' && nvm install ${OXLINT_RECOMMENDED_NODE_MAJOR}"`, { stdio: "inherit" });
|
|
785
|
+
return findCompatibleNvmBinary() !== null;
|
|
786
|
+
} catch {
|
|
787
|
+
return false;
|
|
788
|
+
}
|
|
789
|
+
};
|
|
790
|
+
const resolveNodeForOxlint = () => {
|
|
791
|
+
if (isCurrentNodeCompatibleWithOxlint()) return {
|
|
792
|
+
binaryPath: process.execPath,
|
|
793
|
+
isCurrentNode: true,
|
|
794
|
+
version: process.version
|
|
795
|
+
};
|
|
796
|
+
const nvmBinaryPath = findCompatibleNvmBinary();
|
|
797
|
+
if (!nvmBinaryPath) return null;
|
|
798
|
+
const version = getNodeVersionFromBinary(nvmBinaryPath);
|
|
799
|
+
if (!version) return null;
|
|
800
|
+
return {
|
|
801
|
+
binaryPath: nvmBinaryPath,
|
|
802
|
+
isCurrentNode: false,
|
|
803
|
+
version
|
|
804
|
+
};
|
|
805
|
+
};
|
|
806
|
+
|
|
644
807
|
//#endregion
|
|
645
808
|
//#region src/utils/run-knip.ts
|
|
646
809
|
const KNIP_CATEGORY_MAP = {
|
|
@@ -1102,8 +1265,8 @@ const batchIncludePaths = (baseArgs, includePaths) => {
|
|
|
1102
1265
|
if (currentBatch.length > 0) batches.push(currentBatch);
|
|
1103
1266
|
return batches;
|
|
1104
1267
|
};
|
|
1105
|
-
const spawnOxlint = (args, rootDirectory) => new Promise((resolve, reject) => {
|
|
1106
|
-
const child = spawn(
|
|
1268
|
+
const spawnOxlint = (args, rootDirectory, nodeBinaryPath) => new Promise((resolve, reject) => {
|
|
1269
|
+
const child = spawn(nodeBinaryPath, args, { cwd: rootDirectory });
|
|
1107
1270
|
const stdoutBuffers = [];
|
|
1108
1271
|
const stderrBuffers = [];
|
|
1109
1272
|
child.stdout.on("data", (buffer) => stdoutBuffers.push(buffer));
|
|
@@ -1146,7 +1309,7 @@ const parseOxlintOutput = (stdout) => {
|
|
|
1146
1309
|
};
|
|
1147
1310
|
});
|
|
1148
1311
|
};
|
|
1149
|
-
const runOxlint = async (rootDirectory, hasTypeScript, framework, hasReactCompiler, includePaths) => {
|
|
1312
|
+
const runOxlint = async (rootDirectory, hasTypeScript, framework, hasReactCompiler, includePaths, nodeBinaryPath = process.execPath) => {
|
|
1150
1313
|
if (includePaths !== void 0 && includePaths.length === 0) return [];
|
|
1151
1314
|
const configPath = path.join(os.tmpdir(), `react-doctor-oxlintrc-${process.pid}.json`);
|
|
1152
1315
|
const config = createOxlintConfig({
|
|
@@ -1168,7 +1331,7 @@ const runOxlint = async (rootDirectory, hasTypeScript, framework, hasReactCompil
|
|
|
1168
1331
|
const fileBatches = includePaths !== void 0 ? batchIncludePaths(baseArgs, includePaths) : [["."]];
|
|
1169
1332
|
const allDiagnostics = [];
|
|
1170
1333
|
for (const batch of fileBatches) {
|
|
1171
|
-
const stdout = await spawnOxlint([...baseArgs, ...batch], rootDirectory);
|
|
1334
|
+
const stdout = await spawnOxlint([...baseArgs, ...batch], rootDirectory, nodeBinaryPath);
|
|
1172
1335
|
allDiagnostics.push(...parseOxlintOutput(stdout));
|
|
1173
1336
|
}
|
|
1174
1337
|
return allDiagnostics;
|
|
@@ -1393,6 +1556,44 @@ const printSummary = (diagnostics, elapsedMilliseconds, scoreResult, projectName
|
|
|
1393
1556
|
logger.break();
|
|
1394
1557
|
logger.dim(` Share your results: ${highlighter.info(shareUrl)}`);
|
|
1395
1558
|
};
|
|
1559
|
+
const resolveOxlintNode = async (isLintEnabled, isScoreOnly) => {
|
|
1560
|
+
if (!isLintEnabled) return null;
|
|
1561
|
+
const nodeResolution = resolveNodeForOxlint();
|
|
1562
|
+
if (nodeResolution) {
|
|
1563
|
+
if (!nodeResolution.isCurrentNode && !isScoreOnly) {
|
|
1564
|
+
logger.warn(`Node ${process.version} is unsupported by oxlint. Using Node ${nodeResolution.version} from nvm.`);
|
|
1565
|
+
logger.break();
|
|
1566
|
+
}
|
|
1567
|
+
return nodeResolution.binaryPath;
|
|
1568
|
+
}
|
|
1569
|
+
if (isScoreOnly) return null;
|
|
1570
|
+
logger.warn(`Node ${process.version} is not compatible with oxlint (requires ${OXLINT_NODE_REQUIREMENT}). Lint checks will be skipped.`);
|
|
1571
|
+
if (isNvmInstalled() && process.stdin.isTTY) {
|
|
1572
|
+
const { shouldInstallNode } = await prompts({
|
|
1573
|
+
type: "confirm",
|
|
1574
|
+
name: "shouldInstallNode",
|
|
1575
|
+
message: `Install Node ${OXLINT_RECOMMENDED_NODE_MAJOR} via nvm to enable lint checks?`,
|
|
1576
|
+
initial: true
|
|
1577
|
+
});
|
|
1578
|
+
if (shouldInstallNode) {
|
|
1579
|
+
logger.break();
|
|
1580
|
+
const freshResolution = installNodeViaNvm() ? resolveNodeForOxlint() : null;
|
|
1581
|
+
if (freshResolution) {
|
|
1582
|
+
logger.break();
|
|
1583
|
+
logger.success(`Node ${freshResolution.version} installed. Using it for lint checks.`);
|
|
1584
|
+
logger.break();
|
|
1585
|
+
return freshResolution.binaryPath;
|
|
1586
|
+
}
|
|
1587
|
+
logger.break();
|
|
1588
|
+
logger.warn("Failed to install Node via nvm. Skipping lint checks.");
|
|
1589
|
+
logger.break();
|
|
1590
|
+
return null;
|
|
1591
|
+
}
|
|
1592
|
+
} else if (isNvmInstalled()) logger.dim(` Run: nvm install ${OXLINT_RECOMMENDED_NODE_MAJOR}`);
|
|
1593
|
+
else logger.dim(` Install nvm (https://github.com/nvm-sh/nvm) and run: nvm install ${OXLINT_RECOMMENDED_NODE_MAJOR}`);
|
|
1594
|
+
logger.break();
|
|
1595
|
+
return null;
|
|
1596
|
+
};
|
|
1396
1597
|
const mergeScanOptions = (inputOptions, userConfig) => ({
|
|
1397
1598
|
lint: inputOptions.lint ?? userConfig?.lint ?? true,
|
|
1398
1599
|
deadCode: inputOptions.deadCode ?? userConfig?.deadCode ?? true,
|
|
@@ -1428,19 +1629,24 @@ const scan = async (directory, inputOptions = {}) => {
|
|
|
1428
1629
|
const jsxIncludePaths = computeJsxIncludePaths(includePaths);
|
|
1429
1630
|
let didLintFail = false;
|
|
1430
1631
|
let didDeadCodeFail = false;
|
|
1431
|
-
const
|
|
1632
|
+
const resolvedNodeBinaryPath = await resolveOxlintNode(options.lint, options.scoreOnly);
|
|
1633
|
+
if (options.lint && !resolvedNodeBinaryPath) didLintFail = true;
|
|
1634
|
+
const lintPromise = resolvedNodeBinaryPath ? (async () => {
|
|
1432
1635
|
const lintSpinner = options.scoreOnly ? null : spinner("Running lint checks...").start();
|
|
1433
1636
|
try {
|
|
1434
|
-
const lintDiagnostics = await runOxlint(directory, projectInfo.hasTypeScript, projectInfo.framework, projectInfo.hasReactCompiler, jsxIncludePaths);
|
|
1637
|
+
const lintDiagnostics = await runOxlint(directory, projectInfo.hasTypeScript, projectInfo.framework, projectInfo.hasReactCompiler, jsxIncludePaths, resolvedNodeBinaryPath);
|
|
1435
1638
|
lintSpinner?.succeed("Running lint checks.");
|
|
1436
1639
|
return lintDiagnostics;
|
|
1437
1640
|
} catch (error) {
|
|
1438
1641
|
didLintFail = true;
|
|
1439
|
-
|
|
1440
|
-
if (
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
} else
|
|
1642
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1643
|
+
if (errorMessage.includes("native binding")) {
|
|
1644
|
+
lintSpinner?.fail(`Lint checks failed — oxlint native binding not found (Node ${process.version}).`);
|
|
1645
|
+
logger.dim(` Upgrade to Node ${OXLINT_NODE_REQUIREMENT} or run: npx -p oxlint@latest react-doctor@latest`);
|
|
1646
|
+
} else {
|
|
1647
|
+
lintSpinner?.fail("Lint checks failed (non-fatal, skipping).");
|
|
1648
|
+
logger.error(errorMessage);
|
|
1649
|
+
}
|
|
1444
1650
|
return [];
|
|
1445
1651
|
}
|
|
1446
1652
|
})() : Promise.resolve([]);
|
|
@@ -1603,92 +1809,6 @@ const handleError = (error, options = DEFAULT_HANDLE_ERROR_OPTIONS) => {
|
|
|
1603
1809
|
process.exitCode = 1;
|
|
1604
1810
|
};
|
|
1605
1811
|
|
|
1606
|
-
//#endregion
|
|
1607
|
-
//#region src/utils/should-auto-select-current-choice.ts
|
|
1608
|
-
const shouldAutoSelectCurrentChoice = (choiceStates, cursor) => {
|
|
1609
|
-
if (choiceStates.some((choiceState) => choiceState.selected)) return false;
|
|
1610
|
-
const currentChoice = choiceStates[cursor];
|
|
1611
|
-
return Boolean(currentChoice) && !currentChoice.disabled;
|
|
1612
|
-
};
|
|
1613
|
-
|
|
1614
|
-
//#endregion
|
|
1615
|
-
//#region src/utils/should-select-all-choices.ts
|
|
1616
|
-
const shouldSelectAllChoices = (choiceStates) => {
|
|
1617
|
-
return choiceStates.filter((choiceState) => !choiceState.disabled).some((choiceState) => choiceState.selected !== true);
|
|
1618
|
-
};
|
|
1619
|
-
|
|
1620
|
-
//#endregion
|
|
1621
|
-
//#region src/utils/prompts.ts
|
|
1622
|
-
const require = createRequire(import.meta.url);
|
|
1623
|
-
const PROMPTS_MULTISELECT_MODULE_PATH = "prompts/lib/elements/multiselect";
|
|
1624
|
-
const PROMPTS_SELECT_MODULE_PATH = "prompts/lib/elements/select";
|
|
1625
|
-
let didPatchMultiselectToggleAll = false;
|
|
1626
|
-
let didPatchMultiselectSubmit = false;
|
|
1627
|
-
let didPatchSelectBanner = false;
|
|
1628
|
-
const selectBannerMap = /* @__PURE__ */ new Map();
|
|
1629
|
-
const setSelectBanner = (banner, targetIndex) => {
|
|
1630
|
-
selectBannerMap.set(targetIndex, banner);
|
|
1631
|
-
};
|
|
1632
|
-
const clearSelectBanner = () => {
|
|
1633
|
-
selectBannerMap.clear();
|
|
1634
|
-
};
|
|
1635
|
-
const onCancel = () => {
|
|
1636
|
-
logger.break();
|
|
1637
|
-
logger.log("Cancelled.");
|
|
1638
|
-
logger.dim("Run `npx react-doctor@latest --fix` to fix issues.");
|
|
1639
|
-
logger.break();
|
|
1640
|
-
process.exit(0);
|
|
1641
|
-
};
|
|
1642
|
-
const patchMultiselectToggleAll = () => {
|
|
1643
|
-
if (didPatchMultiselectToggleAll) return;
|
|
1644
|
-
didPatchMultiselectToggleAll = true;
|
|
1645
|
-
const multiselectPromptConstructor = require(PROMPTS_MULTISELECT_MODULE_PATH);
|
|
1646
|
-
multiselectPromptConstructor.prototype.toggleAll = function() {
|
|
1647
|
-
const isCurrentChoiceDisabled = Boolean(this.value[this.cursor]?.disabled);
|
|
1648
|
-
if (this.maxChoices !== void 0 || isCurrentChoiceDisabled) {
|
|
1649
|
-
this.bell();
|
|
1650
|
-
return;
|
|
1651
|
-
}
|
|
1652
|
-
const shouldSelectAllEnabledChoices = shouldSelectAllChoices(this.value);
|
|
1653
|
-
for (const choiceState of this.value) {
|
|
1654
|
-
if (choiceState.disabled) continue;
|
|
1655
|
-
choiceState.selected = shouldSelectAllEnabledChoices;
|
|
1656
|
-
}
|
|
1657
|
-
this.render();
|
|
1658
|
-
};
|
|
1659
|
-
};
|
|
1660
|
-
const patchMultiselectSubmit = () => {
|
|
1661
|
-
if (didPatchMultiselectSubmit) return;
|
|
1662
|
-
didPatchMultiselectSubmit = true;
|
|
1663
|
-
const multiselectPromptConstructor = require(PROMPTS_MULTISELECT_MODULE_PATH);
|
|
1664
|
-
const originalSubmit = multiselectPromptConstructor.prototype.submit;
|
|
1665
|
-
multiselectPromptConstructor.prototype.submit = function() {
|
|
1666
|
-
if (shouldAutoSelectCurrentChoice(this.value, this.cursor)) this.value[this.cursor].selected = true;
|
|
1667
|
-
originalSubmit.call(this);
|
|
1668
|
-
};
|
|
1669
|
-
};
|
|
1670
|
-
const patchSelectBanner = () => {
|
|
1671
|
-
if (didPatchSelectBanner) return;
|
|
1672
|
-
didPatchSelectBanner = true;
|
|
1673
|
-
const selectConstructor = require(PROMPTS_SELECT_MODULE_PATH);
|
|
1674
|
-
const promptsClear = require("prompts/lib/util/clear");
|
|
1675
|
-
const originalRender = selectConstructor.prototype.render;
|
|
1676
|
-
selectConstructor.prototype.render = function() {
|
|
1677
|
-
originalRender.call(this);
|
|
1678
|
-
const banner = selectBannerMap.get(this.cursor);
|
|
1679
|
-
if (!banner || this.closed || this.done) return;
|
|
1680
|
-
this.out.write(promptsClear(this.outputText, this.out.columns));
|
|
1681
|
-
this.outputText = `${banner}\n\n${this.outputText}`;
|
|
1682
|
-
this.out.write(this.outputText);
|
|
1683
|
-
};
|
|
1684
|
-
};
|
|
1685
|
-
const prompts = (questions) => {
|
|
1686
|
-
patchMultiselectToggleAll();
|
|
1687
|
-
patchMultiselectSubmit();
|
|
1688
|
-
patchSelectBanner();
|
|
1689
|
-
return basePrompts(questions, { onCancel });
|
|
1690
|
-
};
|
|
1691
|
-
|
|
1692
1812
|
//#endregion
|
|
1693
1813
|
//#region src/utils/select-projects.ts
|
|
1694
1814
|
const selectProjects = async (rootDirectory, projectFlag, skipPrompts) => {
|
|
@@ -1909,7 +2029,7 @@ const maybePromptSkillInstall = async (shouldSkipPrompts) => {
|
|
|
1909
2029
|
|
|
1910
2030
|
//#endregion
|
|
1911
2031
|
//#region src/cli.ts
|
|
1912
|
-
const VERSION = "0.0.
|
|
2032
|
+
const VERSION = "0.0.28";
|
|
1913
2033
|
const exitWithFixHint = () => {
|
|
1914
2034
|
logger.break();
|
|
1915
2035
|
logger.log("Cancelled.");
|