@rainy-updates/cli 0.5.7 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +134 -0
- package/README.md +90 -31
- package/dist/bin/cli.js +11 -126
- package/dist/bin/dispatch.js +35 -32
- package/dist/bin/help.js +79 -2
- package/dist/bin/main.d.ts +1 -0
- package/dist/bin/main.js +126 -0
- package/dist/cache/cache.js +13 -11
- package/dist/commands/audit/parser.js +38 -2
- package/dist/commands/audit/runner.js +41 -61
- package/dist/commands/audit/targets.js +13 -13
- package/dist/commands/bisect/oracle.js +31 -11
- package/dist/commands/bisect/parser.js +3 -3
- package/dist/commands/bisect/runner.js +16 -8
- package/dist/commands/changelog/fetcher.js +11 -5
- package/dist/commands/dashboard/parser.js +144 -1
- package/dist/commands/dashboard/runner.d.ts +2 -2
- package/dist/commands/dashboard/runner.js +67 -37
- package/dist/commands/doctor/parser.js +53 -4
- package/dist/commands/doctor/runner.js +2 -2
- package/dist/commands/ga/parser.js +43 -4
- package/dist/commands/ga/runner.js +22 -13
- package/dist/commands/health/parser.js +38 -2
- package/dist/commands/health/runner.js +5 -1
- package/dist/commands/hook/parser.d.ts +2 -0
- package/dist/commands/hook/parser.js +40 -0
- package/dist/commands/hook/runner.d.ts +2 -0
- package/dist/commands/hook/runner.js +174 -0
- package/dist/commands/licenses/parser.js +39 -0
- package/dist/commands/licenses/runner.js +9 -5
- package/dist/commands/resolve/graph/builder.js +5 -1
- package/dist/commands/resolve/parser.js +39 -0
- package/dist/commands/resolve/runner.js +14 -4
- package/dist/commands/review/parser.js +101 -4
- package/dist/commands/review/runner.js +31 -5
- package/dist/commands/snapshot/parser.js +39 -0
- package/dist/commands/snapshot/runner.js +21 -18
- package/dist/commands/snapshot/store.d.ts +0 -12
- package/dist/commands/snapshot/store.js +26 -38
- package/dist/commands/unused/parser.js +39 -0
- package/dist/commands/unused/runner.js +10 -8
- package/dist/commands/unused/scanner.d.ts +2 -1
- package/dist/commands/unused/scanner.js +65 -52
- package/dist/config/loader.d.ts +2 -2
- package/dist/config/loader.js +2 -5
- package/dist/config/policy.js +20 -11
- package/dist/core/analysis/run-silenced.js +0 -1
- package/dist/core/artifacts.js +6 -5
- package/dist/core/baseline.js +3 -5
- package/dist/core/check.js +7 -3
- package/dist/core/ci.js +52 -1
- package/dist/core/decision-plan.d.ts +14 -0
- package/dist/core/decision-plan.js +107 -0
- package/dist/core/doctor/result.js +8 -5
- package/dist/core/fix-pr-batch.js +38 -28
- package/dist/core/fix-pr.js +27 -24
- package/dist/core/init-ci.js +34 -28
- package/dist/core/options.d.ts +4 -1
- package/dist/core/options.js +152 -4
- package/dist/core/review-model.js +3 -0
- package/dist/core/summary.js +6 -0
- package/dist/core/upgrade.js +64 -2
- package/dist/core/verification.d.ts +2 -0
- package/dist/core/verification.js +108 -0
- package/dist/core/warm-cache.js +7 -3
- package/dist/generated/version.d.ts +1 -0
- package/dist/generated/version.js +2 -0
- package/dist/git/scope.d.ts +19 -0
- package/dist/git/scope.js +167 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.js +1 -0
- package/dist/output/format.js +15 -0
- package/dist/output/github.js +6 -0
- package/dist/output/sarif.js +12 -18
- package/dist/parsers/package-json.js +2 -4
- package/dist/pm/detect.d.ts +40 -1
- package/dist/pm/detect.js +152 -9
- package/dist/pm/install.d.ts +3 -1
- package/dist/pm/install.js +18 -17
- package/dist/registry/npm.js +34 -76
- package/dist/rup +0 -0
- package/dist/types/index.d.ts +134 -5
- package/dist/ui/tui.d.ts +4 -1
- package/dist/ui/tui.js +156 -67
- package/dist/utils/io.js +5 -6
- package/dist/utils/lockfile.js +24 -19
- package/dist/utils/runtime-paths.d.ts +4 -0
- package/dist/utils/runtime-paths.js +35 -0
- package/dist/utils/runtime.d.ts +7 -0
- package/dist/utils/runtime.js +32 -0
- package/dist/workspace/discover.d.ts +7 -1
- package/dist/workspace/discover.js +67 -54
- package/package.json +24 -19
- package/dist/ui/dashboard/DashboardTUI.d.ts +0 -6
- package/dist/ui/dashboard/DashboardTUI.js +0 -34
- package/dist/ui/dashboard/components/DetailPanel.d.ts +0 -4
- package/dist/ui/dashboard/components/DetailPanel.js +0 -30
- package/dist/ui/dashboard/components/Footer.d.ts +0 -4
- package/dist/ui/dashboard/components/Footer.js +0 -9
- package/dist/ui/dashboard/components/Header.d.ts +0 -4
- package/dist/ui/dashboard/components/Header.js +0 -12
- package/dist/ui/dashboard/components/Sidebar.d.ts +0 -4
- package/dist/ui/dashboard/components/Sidebar.js +0 -23
- package/dist/ui/dashboard/store.d.ts +0 -34
- package/dist/ui/dashboard/store.js +0 -148
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { readManifest } from "../parsers/package-json.js";
|
|
3
|
+
import { buildWorkspaceGraph } from "../workspace/graph.js";
|
|
4
|
+
export function hasGitScope(options) {
|
|
5
|
+
return (options.onlyChanged === true ||
|
|
6
|
+
options.affected === true ||
|
|
7
|
+
options.staged === true ||
|
|
8
|
+
typeof options.baseRef === "string" ||
|
|
9
|
+
typeof options.headRef === "string" ||
|
|
10
|
+
typeof options.sinceRef === "string");
|
|
11
|
+
}
|
|
12
|
+
export async function scopePackageDirsByGit(cwd, packageDirs, options, config = {}) {
|
|
13
|
+
if (!hasGitScope(options)) {
|
|
14
|
+
return {
|
|
15
|
+
packageDirs,
|
|
16
|
+
warnings: [],
|
|
17
|
+
changedFiles: [],
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
const changedFiles = await listChangedFiles(cwd, options);
|
|
21
|
+
if ("warnings" in changedFiles) {
|
|
22
|
+
return {
|
|
23
|
+
packageDirs,
|
|
24
|
+
warnings: changedFiles.warnings,
|
|
25
|
+
changedFiles: [],
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
const changedPackageDirs = mapFilesToPackageDirs(cwd, packageDirs, changedFiles.files);
|
|
29
|
+
if (changedPackageDirs.length === 0) {
|
|
30
|
+
return {
|
|
31
|
+
packageDirs: [],
|
|
32
|
+
warnings: [],
|
|
33
|
+
changedFiles: changedFiles.files,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
if (!config.includeDependents) {
|
|
37
|
+
return {
|
|
38
|
+
packageDirs: changedPackageDirs,
|
|
39
|
+
warnings: [],
|
|
40
|
+
changedFiles: changedFiles.files,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
const affectedPackageDirs = await expandToDependents(changedPackageDirs, packageDirs, config.includeKinds ?? ["dependencies", "devDependencies"]);
|
|
44
|
+
return {
|
|
45
|
+
packageDirs: affectedPackageDirs,
|
|
46
|
+
warnings: [],
|
|
47
|
+
changedFiles: changedFiles.files,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
async function listChangedFiles(cwd, options) {
|
|
51
|
+
const primaryArgs = buildGitDiffArgs(options);
|
|
52
|
+
const diff = await runGit(cwd, primaryArgs);
|
|
53
|
+
if (!diff.ok) {
|
|
54
|
+
return {
|
|
55
|
+
warnings: [
|
|
56
|
+
`Git scope could not be resolved (${diff.error}). Falling back to full workspace scan.`,
|
|
57
|
+
],
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
const untracked = await runGit(cwd, ["ls-files", "--others", "--exclude-standard"]);
|
|
61
|
+
const files = new Set();
|
|
62
|
+
for (const value of [...diff.lines, ...(untracked.ok ? untracked.lines : [])]) {
|
|
63
|
+
const trimmed = value.trim();
|
|
64
|
+
if (trimmed.length === 0)
|
|
65
|
+
continue;
|
|
66
|
+
files.add(trimmed);
|
|
67
|
+
}
|
|
68
|
+
return {
|
|
69
|
+
files: Array.from(files).sort((left, right) => left.localeCompare(right)),
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
function buildGitDiffArgs(options) {
|
|
73
|
+
if (options.staged) {
|
|
74
|
+
return ["diff", "--name-only", "--cached"];
|
|
75
|
+
}
|
|
76
|
+
if (options.baseRef && options.headRef) {
|
|
77
|
+
return ["diff", "--name-only", `${options.baseRef}...${options.headRef}`];
|
|
78
|
+
}
|
|
79
|
+
if (options.baseRef) {
|
|
80
|
+
return ["diff", "--name-only", `${options.baseRef}...HEAD`];
|
|
81
|
+
}
|
|
82
|
+
if (options.sinceRef) {
|
|
83
|
+
return ["diff", "--name-only", `${options.sinceRef}..HEAD`];
|
|
84
|
+
}
|
|
85
|
+
return ["diff", "--name-only", "HEAD"];
|
|
86
|
+
}
|
|
87
|
+
async function runGit(cwd, args) {
|
|
88
|
+
try {
|
|
89
|
+
const proc = Bun.spawn(["git", ...args], {
|
|
90
|
+
cwd,
|
|
91
|
+
stdout: "pipe",
|
|
92
|
+
stderr: "pipe",
|
|
93
|
+
});
|
|
94
|
+
const [stdout, stderr, exitCode] = await Promise.all([
|
|
95
|
+
new Response(proc.stdout).text(),
|
|
96
|
+
new Response(proc.stderr).text(),
|
|
97
|
+
proc.exited,
|
|
98
|
+
]);
|
|
99
|
+
if (exitCode !== 0) {
|
|
100
|
+
const message = stderr.trim() || `git ${args.join(" ")} exited with code ${exitCode}`;
|
|
101
|
+
return { ok: false, error: message };
|
|
102
|
+
}
|
|
103
|
+
return {
|
|
104
|
+
ok: true,
|
|
105
|
+
lines: stdout.split(/\r?\n/).filter(Boolean),
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
catch (error) {
|
|
109
|
+
return { ok: false, error: String(error) };
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
function mapFilesToPackageDirs(cwd, packageDirs, files) {
|
|
113
|
+
const sortedDirs = [...packageDirs].sort((left, right) => right.length - left.length);
|
|
114
|
+
const matched = new Set();
|
|
115
|
+
for (const file of files) {
|
|
116
|
+
const absoluteFile = path.resolve(cwd, file);
|
|
117
|
+
let bestMatch;
|
|
118
|
+
for (const packageDir of sortedDirs) {
|
|
119
|
+
const relative = path.relative(packageDir, absoluteFile);
|
|
120
|
+
if (relative === "" || (!relative.startsWith("..") && !path.isAbsolute(relative))) {
|
|
121
|
+
bestMatch = packageDir;
|
|
122
|
+
break;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
if (bestMatch) {
|
|
126
|
+
matched.add(bestMatch);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
return Array.from(matched).sort((left, right) => left.localeCompare(right));
|
|
130
|
+
}
|
|
131
|
+
async function expandToDependents(changedPackageDirs, packageDirs, includeKinds) {
|
|
132
|
+
const manifestsByPath = new Map();
|
|
133
|
+
for (const packageDir of packageDirs) {
|
|
134
|
+
try {
|
|
135
|
+
manifestsByPath.set(packageDir, await readManifest(packageDir));
|
|
136
|
+
}
|
|
137
|
+
catch {
|
|
138
|
+
// Skip unreadable manifests; callers already handle manifest read failures.
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
const graph = buildWorkspaceGraph(manifestsByPath, includeKinds);
|
|
142
|
+
const pathByName = new Map(graph.nodes.map((node) => [node.packageName, node.packagePath]));
|
|
143
|
+
const nameByPath = new Map(graph.nodes.map((node) => [node.packagePath, node.packageName]));
|
|
144
|
+
const dependents = new Map();
|
|
145
|
+
for (const node of graph.nodes) {
|
|
146
|
+
for (const dependency of node.dependsOn) {
|
|
147
|
+
const list = dependents.get(dependency) ?? [];
|
|
148
|
+
list.push(node.packageName);
|
|
149
|
+
dependents.set(dependency, list);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
const selected = new Set(changedPackageDirs);
|
|
153
|
+
const queue = changedPackageDirs
|
|
154
|
+
.map((packageDir) => nameByPath.get(packageDir))
|
|
155
|
+
.filter((value) => typeof value === "string");
|
|
156
|
+
while (queue.length > 0) {
|
|
157
|
+
const current = queue.shift();
|
|
158
|
+
for (const dependent of dependents.get(current) ?? []) {
|
|
159
|
+
const dependentPath = pathByName.get(dependent);
|
|
160
|
+
if (!dependentPath || selected.has(dependentPath))
|
|
161
|
+
continue;
|
|
162
|
+
selected.add(dependentPath);
|
|
163
|
+
queue.push(dependent);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
return Array.from(selected).sort((left, right) => left.localeCompare(right));
|
|
167
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -4,9 +4,10 @@ export { warmCache } from "./core/warm-cache.js";
|
|
|
4
4
|
export { runCi } from "./core/ci.js";
|
|
5
5
|
export { initCiWorkflow } from "./core/init-ci.js";
|
|
6
6
|
export { saveBaseline, diffBaseline } from "./core/baseline.js";
|
|
7
|
+
export { runHook } from "./commands/hook/runner.js";
|
|
7
8
|
export { createSarifReport } from "./output/sarif.js";
|
|
8
9
|
export { writeGitHubOutput, renderGitHubAnnotations } from "./output/github.js";
|
|
9
10
|
export { renderPrReport } from "./output/pr-report.js";
|
|
10
11
|
export { buildReviewResult, createDoctorResult } from "./core/review-model.js";
|
|
11
12
|
export { applyRiskAssessments } from "./risk/index.js";
|
|
12
|
-
export type { CheckOptions, CheckResult, CiProfile, DependencyKind, FailOnLevel, GroupBy, OutputFormat, PackageUpdate, RunOptions, TargetLevel, UpgradeOptions, UpgradeResult, ReviewOptions, ReviewResult, DoctorOptions, DoctorResult, Verdict, RiskLevel, RiskCategory, RiskAssessment, RiskFactor, MaintainerChurnStatus, } from "./types/index.js";
|
|
13
|
+
export type { CheckOptions, CheckResult, CiProfile, DependencyKind, FailOnLevel, GroupBy, OutputFormat, PackageUpdate, RunOptions, TargetLevel, UpgradeOptions, UpgradeResult, ReviewOptions, ReviewResult, DoctorOptions, DoctorResult, HookOptions, HookResult, Verdict, RiskLevel, RiskCategory, RiskAssessment, RiskFactor, MaintainerChurnStatus, } from "./types/index.js";
|
package/dist/index.js
CHANGED
|
@@ -4,6 +4,7 @@ export { warmCache } from "./core/warm-cache.js";
|
|
|
4
4
|
export { runCi } from "./core/ci.js";
|
|
5
5
|
export { initCiWorkflow } from "./core/init-ci.js";
|
|
6
6
|
export { saveBaseline, diffBaseline } from "./core/baseline.js";
|
|
7
|
+
export { runHook } from "./commands/hook/runner.js";
|
|
7
8
|
export { createSarifReport } from "./output/sarif.js";
|
|
8
9
|
export { writeGitHubOutput, renderGitHubAnnotations } from "./output/github.js";
|
|
9
10
|
export { renderPrReport } from "./output/pr-report.js";
|
package/dist/output/format.js
CHANGED
|
@@ -74,6 +74,12 @@ export function renderResult(result, format, display = {}) {
|
|
|
74
74
|
`primary_finding_code=${result.summary.primaryFindingCode ?? ""}`,
|
|
75
75
|
`primary_finding_category=${result.summary.primaryFindingCategory ?? ""}`,
|
|
76
76
|
`next_action_reason=${result.summary.nextActionReason ?? ""}`,
|
|
77
|
+
`suggested_command=${result.summary.suggestedCommand ?? ""}`,
|
|
78
|
+
`decision_plan=${result.summary.decisionPlan ?? ""}`,
|
|
79
|
+
`interactive_surface=${result.summary.interactiveSurface ?? ""}`,
|
|
80
|
+
`queue_focus=${result.summary.queueFocus ?? ""}`,
|
|
81
|
+
`verification_state=${result.summary.verificationState ?? "not-run"}`,
|
|
82
|
+
`verification_failures=${result.summary.verificationFailures ?? 0}`,
|
|
77
83
|
].join("\n");
|
|
78
84
|
}
|
|
79
85
|
const lines = [];
|
|
@@ -137,6 +143,15 @@ export function renderResult(result, format, display = {}) {
|
|
|
137
143
|
if (typeof result.summary.dependencyHealthScore === "number") {
|
|
138
144
|
lines.push(`DependencyHealthScore=${result.summary.dependencyHealthScore}, primaryFinding=${result.summary.primaryFindingCode ?? "none"}, category=${result.summary.primaryFindingCategory ?? "none"}`);
|
|
139
145
|
}
|
|
146
|
+
if (result.summary.suggestedCommand) {
|
|
147
|
+
lines.push(`SuggestedCommand=${result.summary.suggestedCommand}`);
|
|
148
|
+
}
|
|
149
|
+
if (result.summary.decisionPlan) {
|
|
150
|
+
lines.push(`DecisionPlan=${result.summary.decisionPlan}, surface=${result.summary.interactiveSurface ?? "none"}, focus=${result.summary.queueFocus ?? "all"}`);
|
|
151
|
+
}
|
|
152
|
+
if (result.summary.verificationState && result.summary.verificationState !== "not-run") {
|
|
153
|
+
lines.push(`Verification=${result.summary.verificationState}, failures=${result.summary.verificationFailures ?? 0}`);
|
|
154
|
+
}
|
|
140
155
|
if (result.summary.runId) {
|
|
141
156
|
lines.push(`RunId=${result.summary.runId}, artifactManifest=${result.summary.artifactManifest ?? "none"}, blockedPackages=${result.summary.blockedPackages ?? 0}, reviewPackages=${result.summary.reviewPackages ?? 0}, monitorPackages=${result.summary.monitorPackages ?? 0}`);
|
|
142
157
|
}
|
package/dist/output/github.js
CHANGED
|
@@ -38,6 +38,12 @@ export async function writeGitHubOutput(filePath, result) {
|
|
|
38
38
|
`primary_finding_code=${result.summary.primaryFindingCode ?? ""}`,
|
|
39
39
|
`primary_finding_category=${result.summary.primaryFindingCategory ?? ""}`,
|
|
40
40
|
`next_action_reason=${result.summary.nextActionReason ?? ""}`,
|
|
41
|
+
`suggested_command=${result.summary.suggestedCommand ?? ""}`,
|
|
42
|
+
`decision_plan=${result.summary.decisionPlan ?? ""}`,
|
|
43
|
+
`interactive_surface=${result.summary.interactiveSurface ?? ""}`,
|
|
44
|
+
`queue_focus=${result.summary.queueFocus ?? ""}`,
|
|
45
|
+
`verification_state=${result.summary.verificationState ?? "not-run"}`,
|
|
46
|
+
`verification_failures=${result.summary.verificationFailures ?? 0}`,
|
|
41
47
|
`fix_pr_applied=${result.summary.fixPrApplied === true ? "1" : "0"}`,
|
|
42
48
|
`fix_pr_branches_created=${result.summary.fixPrBranchesCreated}`,
|
|
43
49
|
`fix_pr_branch=${result.summary.fixBranchName ?? ""}`,
|
package/dist/output/sarif.js
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
import { fileURLToPath } from "node:url";
|
|
1
|
+
import { CLI_VERSION } from "../generated/version.js";
|
|
4
2
|
export function createSarifReport(result) {
|
|
5
3
|
const dependencyRuleId = "rainy-updates/dependency-update";
|
|
6
4
|
const runtimeRuleId = "rainy-updates/runtime-error";
|
|
@@ -46,7 +44,9 @@ export function createSarifReport(result) {
|
|
|
46
44
|
runId: result.summary.runId,
|
|
47
45
|
},
|
|
48
46
|
}));
|
|
49
|
-
const errorResults = [...result.errors]
|
|
47
|
+
const errorResults = [...result.errors]
|
|
48
|
+
.sort((a, b) => a.localeCompare(b))
|
|
49
|
+
.map((error) => ({
|
|
50
50
|
ruleId: runtimeRuleId,
|
|
51
51
|
level: "error",
|
|
52
52
|
message: {
|
|
@@ -66,12 +66,16 @@ export function createSarifReport(result) {
|
|
|
66
66
|
{
|
|
67
67
|
id: dependencyRuleId,
|
|
68
68
|
shortDescription: { text: "Dependency update available" },
|
|
69
|
-
fullDescription: {
|
|
69
|
+
fullDescription: {
|
|
70
|
+
text: "A dependency has a newer version according to configured target.",
|
|
71
|
+
},
|
|
70
72
|
},
|
|
71
73
|
{
|
|
72
74
|
id: runtimeRuleId,
|
|
73
75
|
shortDescription: { text: "Dependency resolution error" },
|
|
74
|
-
fullDescription: {
|
|
76
|
+
fullDescription: {
|
|
77
|
+
text: "The resolver could not fetch or parse package metadata.",
|
|
78
|
+
},
|
|
75
79
|
},
|
|
76
80
|
],
|
|
77
81
|
},
|
|
@@ -114,16 +118,6 @@ let TOOL_VERSION_CACHE = null;
|
|
|
114
118
|
function getToolVersion() {
|
|
115
119
|
if (TOOL_VERSION_CACHE)
|
|
116
120
|
return TOOL_VERSION_CACHE;
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
const packageJsonPath = path.resolve(path.dirname(currentFile), "../../package.json");
|
|
120
|
-
const content = readFileSync(packageJsonPath, "utf8");
|
|
121
|
-
const parsed = JSON.parse(content);
|
|
122
|
-
TOOL_VERSION_CACHE = parsed.version ?? "0.0.0";
|
|
123
|
-
return TOOL_VERSION_CACHE;
|
|
124
|
-
}
|
|
125
|
-
catch {
|
|
126
|
-
TOOL_VERSION_CACHE = "0.0.0";
|
|
127
|
-
return TOOL_VERSION_CACHE;
|
|
128
|
-
}
|
|
121
|
+
TOOL_VERSION_CACHE = CLI_VERSION;
|
|
122
|
+
return TOOL_VERSION_CACHE;
|
|
129
123
|
}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { promises as fs } from "node:fs";
|
|
2
1
|
import path from "node:path";
|
|
3
2
|
const DEPENDENCY_KINDS = [
|
|
4
3
|
"dependencies",
|
|
@@ -11,13 +10,12 @@ export function getPackageJsonPath(cwd) {
|
|
|
11
10
|
}
|
|
12
11
|
export async function readManifest(cwd) {
|
|
13
12
|
const filePath = getPackageJsonPath(cwd);
|
|
14
|
-
|
|
15
|
-
return JSON.parse(content);
|
|
13
|
+
return (await Bun.file(filePath).json());
|
|
16
14
|
}
|
|
17
15
|
export async function writeManifest(cwd, manifest) {
|
|
18
16
|
const filePath = getPackageJsonPath(cwd);
|
|
19
17
|
const content = JSON.stringify(manifest, null, 2) + "\n";
|
|
20
|
-
await
|
|
18
|
+
await Bun.write(filePath, content);
|
|
21
19
|
}
|
|
22
20
|
export function collectDependencies(manifest, includeKinds) {
|
|
23
21
|
const deps = [];
|
package/dist/pm/detect.d.ts
CHANGED
|
@@ -1 +1,40 @@
|
|
|
1
|
-
|
|
1
|
+
import type { DetectedPackageManager, SelectedPackageManager, SupportedPackageManager } from "../types/index.js";
|
|
2
|
+
export type PackageManagerDetectionSource = "packageManager-field" | "lockfile" | "fallback";
|
|
3
|
+
export type YarnFlavor = "classic" | "berry" | "unknown";
|
|
4
|
+
export interface PackageManagerDetection {
|
|
5
|
+
manager: DetectedPackageManager;
|
|
6
|
+
source: PackageManagerDetectionSource;
|
|
7
|
+
lockfile?: string;
|
|
8
|
+
packageManagerField?: string;
|
|
9
|
+
yarnFlavor?: YarnFlavor;
|
|
10
|
+
}
|
|
11
|
+
export interface PackageManagerProfile {
|
|
12
|
+
manager: SupportedPackageManager;
|
|
13
|
+
command: SupportedPackageManager;
|
|
14
|
+
source: PackageManagerDetectionSource;
|
|
15
|
+
lockfile?: string;
|
|
16
|
+
packageManagerField?: string;
|
|
17
|
+
yarnFlavor?: YarnFlavor;
|
|
18
|
+
}
|
|
19
|
+
export declare function detectPackageManager(cwd: string): Promise<DetectedPackageManager>;
|
|
20
|
+
export declare function detectPackageManagerDetails(cwd: string): Promise<PackageManagerDetection>;
|
|
21
|
+
export declare function resolvePackageManager(requested: SelectedPackageManager, detected: DetectedPackageManager, fallback?: SupportedPackageManager): SupportedPackageManager;
|
|
22
|
+
export declare function resolvePackageManagerProfile(cwd: string, requested: SelectedPackageManager, fallback?: SupportedPackageManager): Promise<PackageManagerProfile>;
|
|
23
|
+
export declare function createPackageManagerProfile(requested: SelectedPackageManager, detected: PackageManagerDetection, fallback?: SupportedPackageManager): PackageManagerProfile;
|
|
24
|
+
export declare function buildInstallInvocation(profile: PackageManagerProfile, options?: {
|
|
25
|
+
frozen?: boolean;
|
|
26
|
+
ci?: boolean;
|
|
27
|
+
}): {
|
|
28
|
+
command: string;
|
|
29
|
+
args: string[];
|
|
30
|
+
display: string;
|
|
31
|
+
};
|
|
32
|
+
export declare function buildAddInvocation(profile: PackageManagerProfile, packages: string[], options?: {
|
|
33
|
+
exact?: boolean;
|
|
34
|
+
noSave?: boolean;
|
|
35
|
+
}): {
|
|
36
|
+
command: string;
|
|
37
|
+
args: string[];
|
|
38
|
+
display: string;
|
|
39
|
+
};
|
|
40
|
+
export declare function buildTestCommand(profile: PackageManagerProfile): string;
|
package/dist/pm/detect.js
CHANGED
|
@@ -1,20 +1,163 @@
|
|
|
1
|
-
import { access } from "node:fs/promises";
|
|
2
1
|
import path from "node:path";
|
|
2
|
+
const PACKAGE_MANAGER_LOCKFILES = [
|
|
3
|
+
["bun.lock", "bun"],
|
|
4
|
+
["bun.lockb", "bun"],
|
|
5
|
+
["pnpm-lock.yaml", "pnpm"],
|
|
6
|
+
["package-lock.json", "npm"],
|
|
7
|
+
["npm-shrinkwrap.json", "npm"],
|
|
8
|
+
["yarn.lock", "yarn"],
|
|
9
|
+
];
|
|
3
10
|
export async function detectPackageManager(cwd) {
|
|
4
|
-
|
|
5
|
-
|
|
11
|
+
return (await detectPackageManagerDetails(cwd)).manager;
|
|
12
|
+
}
|
|
13
|
+
export async function detectPackageManagerDetails(cwd) {
|
|
14
|
+
const packageManagerField = await readPackageManagerField(cwd);
|
|
15
|
+
if (packageManagerField) {
|
|
16
|
+
const parsed = parsePackageManagerField(packageManagerField);
|
|
17
|
+
if (parsed) {
|
|
18
|
+
return {
|
|
19
|
+
manager: parsed.manager,
|
|
20
|
+
source: "packageManager-field",
|
|
21
|
+
packageManagerField,
|
|
22
|
+
yarnFlavor: parsed.yarnFlavor,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
for (const [lockfile, packageManager] of PACKAGE_MANAGER_LOCKFILES) {
|
|
27
|
+
if (await fileExists(path.join(cwd, lockfile))) {
|
|
28
|
+
return {
|
|
29
|
+
manager: packageManager,
|
|
30
|
+
source: "lockfile",
|
|
31
|
+
lockfile,
|
|
32
|
+
yarnFlavor: packageManager === "yarn" ? "unknown" : undefined,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return {
|
|
37
|
+
manager: "unknown",
|
|
38
|
+
source: "fallback",
|
|
39
|
+
packageManagerField: packageManagerField ?? undefined,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
export function resolvePackageManager(requested, detected, fallback = "npm") {
|
|
43
|
+
if (requested !== "auto")
|
|
44
|
+
return requested;
|
|
45
|
+
if (detected !== "unknown")
|
|
46
|
+
return detected;
|
|
47
|
+
return fallback;
|
|
48
|
+
}
|
|
49
|
+
export async function resolvePackageManagerProfile(cwd, requested, fallback = "npm") {
|
|
50
|
+
const detected = await detectPackageManagerDetails(cwd);
|
|
51
|
+
return createPackageManagerProfile(requested, detected, fallback);
|
|
52
|
+
}
|
|
53
|
+
export function createPackageManagerProfile(requested, detected, fallback = "npm") {
|
|
54
|
+
const manager = resolvePackageManager(requested, detected.manager, fallback);
|
|
55
|
+
return {
|
|
56
|
+
manager,
|
|
57
|
+
command: manager,
|
|
58
|
+
source: requested === "auto" && detected.manager !== "unknown"
|
|
59
|
+
? detected.source
|
|
60
|
+
: "fallback",
|
|
61
|
+
lockfile: detected.lockfile,
|
|
62
|
+
packageManagerField: detected.packageManagerField,
|
|
63
|
+
yarnFlavor: manager === "yarn" ? detected.yarnFlavor ?? inferYarnFlavor(undefined) : undefined,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
export function buildInstallInvocation(profile, options = {}) {
|
|
67
|
+
const args = profile.manager === "bun"
|
|
68
|
+
? ["install", ...(options.frozen ? ["--frozen-lockfile"] : [])]
|
|
69
|
+
: profile.manager === "pnpm"
|
|
70
|
+
? ["install", ...(options.frozen ? ["--frozen-lockfile"] : [])]
|
|
71
|
+
: profile.manager === "yarn"
|
|
72
|
+
? [
|
|
73
|
+
"install",
|
|
74
|
+
...(options.frozen
|
|
75
|
+
? [
|
|
76
|
+
profile.yarnFlavor === "berry"
|
|
77
|
+
? "--immutable"
|
|
78
|
+
: "--frozen-lockfile",
|
|
79
|
+
]
|
|
80
|
+
: []),
|
|
81
|
+
]
|
|
82
|
+
: options.ci || options.frozen
|
|
83
|
+
? ["ci"]
|
|
84
|
+
: ["install"];
|
|
85
|
+
return {
|
|
86
|
+
command: profile.command,
|
|
87
|
+
args,
|
|
88
|
+
display: [profile.command, ...args].join(" "),
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
export function buildAddInvocation(profile, packages, options = {}) {
|
|
92
|
+
const args = profile.manager === "bun"
|
|
93
|
+
? [
|
|
94
|
+
"add",
|
|
95
|
+
...(options.exact ? ["--exact"] : []),
|
|
96
|
+
...(options.noSave ? ["--no-save"] : []),
|
|
97
|
+
...packages,
|
|
98
|
+
]
|
|
99
|
+
: profile.manager === "pnpm"
|
|
100
|
+
? [
|
|
101
|
+
"add",
|
|
102
|
+
...(options.exact ? ["--save-exact"] : []),
|
|
103
|
+
...(options.noSave ? ["--no-save"] : []),
|
|
104
|
+
...packages,
|
|
105
|
+
]
|
|
106
|
+
: profile.manager === "yarn"
|
|
107
|
+
? ["add", ...(options.exact ? ["--exact"] : []), ...packages]
|
|
108
|
+
: [
|
|
109
|
+
"install",
|
|
110
|
+
...(options.noSave ? ["--no-save"] : []),
|
|
111
|
+
...(options.exact ? ["--save-exact"] : []),
|
|
112
|
+
...packages,
|
|
113
|
+
];
|
|
114
|
+
return {
|
|
115
|
+
command: profile.command,
|
|
116
|
+
args,
|
|
117
|
+
display: [profile.command, ...args].join(" "),
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
export function buildTestCommand(profile) {
|
|
121
|
+
return `${profile.command} test`;
|
|
122
|
+
}
|
|
123
|
+
async function fileExists(filePath) {
|
|
6
124
|
try {
|
|
7
|
-
await
|
|
8
|
-
return "pnpm";
|
|
125
|
+
return await Bun.file(filePath).exists();
|
|
9
126
|
}
|
|
10
127
|
catch {
|
|
11
|
-
|
|
128
|
+
return false;
|
|
12
129
|
}
|
|
130
|
+
}
|
|
131
|
+
async function readPackageManagerField(cwd) {
|
|
132
|
+
const packageJsonPath = path.join(cwd, "package.json");
|
|
13
133
|
try {
|
|
14
|
-
await
|
|
15
|
-
return "
|
|
134
|
+
const manifest = (await Bun.file(packageJsonPath).json());
|
|
135
|
+
return typeof manifest.packageManager === "string"
|
|
136
|
+
? manifest.packageManager
|
|
137
|
+
: null;
|
|
16
138
|
}
|
|
17
139
|
catch {
|
|
18
|
-
return
|
|
140
|
+
return null;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
function parsePackageManagerField(value) {
|
|
144
|
+
const trimmed = value.trim();
|
|
145
|
+
if (trimmed.length === 0)
|
|
146
|
+
return null;
|
|
147
|
+
const [name, version] = trimmed.split("@", 2);
|
|
148
|
+
if (name !== "bun" && name !== "npm" && name !== "pnpm" && name !== "yarn") {
|
|
149
|
+
return null;
|
|
19
150
|
}
|
|
151
|
+
return {
|
|
152
|
+
manager: name,
|
|
153
|
+
yarnFlavor: name === "yarn" ? inferYarnFlavor(version) : undefined,
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
function inferYarnFlavor(version) {
|
|
157
|
+
if (!version)
|
|
158
|
+
return "unknown";
|
|
159
|
+
const match = version.match(/^(\d+)/);
|
|
160
|
+
if (!match)
|
|
161
|
+
return "unknown";
|
|
162
|
+
return Number(match[1]) >= 2 ? "berry" : "classic";
|
|
20
163
|
}
|
package/dist/pm/install.d.ts
CHANGED
|
@@ -1 +1,3 @@
|
|
|
1
|
-
|
|
1
|
+
import type { DetectedPackageManager, SelectedPackageManager } from "../types/index.js";
|
|
2
|
+
import { type PackageManagerDetection } from "./detect.js";
|
|
3
|
+
export declare function installDependencies(cwd: string, packageManager: SelectedPackageManager, detected: DetectedPackageManager | PackageManagerDetection): Promise<void>;
|
package/dist/pm/install.js
CHANGED
|
@@ -1,21 +1,22 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { buildInstallInvocation, createPackageManagerProfile, } from "./detect.js";
|
|
2
2
|
export async function installDependencies(cwd, packageManager, detected) {
|
|
3
|
-
const
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
const detection = typeof detected === "string"
|
|
4
|
+
? { manager: detected, source: "fallback" }
|
|
5
|
+
: detected;
|
|
6
|
+
const invocation = buildInstallInvocation(createPackageManagerProfile(packageManager, detection));
|
|
7
|
+
try {
|
|
8
|
+
const proc = Bun.spawn([invocation.command, ...invocation.args], {
|
|
8
9
|
cwd,
|
|
9
|
-
|
|
10
|
-
|
|
10
|
+
stdin: "inherit",
|
|
11
|
+
stdout: "inherit",
|
|
12
|
+
stderr: "inherit",
|
|
11
13
|
});
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
});
|
|
14
|
+
const code = await proc.exited;
|
|
15
|
+
if (code !== 0) {
|
|
16
|
+
throw new Error(`${invocation.display} failed with exit code ${code}`);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
catch (err) {
|
|
20
|
+
throw err instanceof Error ? err : new Error(String(err));
|
|
21
|
+
}
|
|
21
22
|
}
|