depsentinel 0.1.5 → 0.1.6

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.
@@ -56,7 +56,7 @@ function collectFixPlans(cwd) {
56
56
  pkgDirty = true;
57
57
  }
58
58
  if (pkgDirty) {
59
- plans.push(planSafeFile(pkgPath, JSON.stringify(pkg, null, 2) + "\n"));
59
+ plans.push(planSafeFile(pkgPath, JSON.stringify(pkg, null, 2) + "\n", { backupOnUpdate: false }));
60
60
  }
61
61
  // Bunfig
62
62
  if (facts.packageManager === "bun") {
@@ -1,3 +1,4 @@
1
+ import { existsSync, readFileSync } from "node:fs";
1
2
  import path from "node:path";
2
3
  import { applySafePlan, planSafeFile } from "../core/safe-write.js";
3
4
  import { detectProjectFacts } from "../core/detector.js";
@@ -20,6 +21,26 @@ function buildNpmRc() {
20
21
  ""
21
22
  ].join("\n");
22
23
  }
24
+ function mergeNpmRc(existing) {
25
+ const lines = existing.split(/\r?\n/);
26
+ const keys = new Map();
27
+ for (let i = 0; i < lines.length; i += 1) {
28
+ const m = lines[i]?.match(/^\s*([a-zA-Z0-9-]+)\s*=\s*(.*)\s*$/);
29
+ if (m)
30
+ keys.set(m[1], i);
31
+ }
32
+ const required = [
33
+ ["ignore-scripts", "true"],
34
+ ["allow-git", "none"],
35
+ ["min-release-age", "3"]
36
+ ];
37
+ for (const [key, value] of required) {
38
+ if (!keys.has(key)) {
39
+ lines.push(`${key}=${value}`);
40
+ }
41
+ }
42
+ return `${lines.join("\n").replace(/\n*$/, "\n")}`;
43
+ }
23
44
  function buildPnpmWorkspace() {
24
45
  return [
25
46
  "packages:",
@@ -37,6 +58,36 @@ function buildPnpmWorkspace() {
37
58
  ""
38
59
  ].join("\n");
39
60
  }
61
+ function mergePnpmWorkspace(existing) {
62
+ const lines = existing.split(/\r?\n/);
63
+ const has = (k) => lines.some((line) => line.trim().startsWith(`${k}:`));
64
+ if (!has("minimumReleaseAge"))
65
+ lines.push("minimumReleaseAge: 43200");
66
+ if (!has("trustPolicy"))
67
+ lines.push("trustPolicy: no-downgrade");
68
+ if (!has("blockExoticSubdeps"))
69
+ lines.push("blockExoticSubdeps: true");
70
+ if (!has("strictDepBuilds"))
71
+ lines.push("strictDepBuilds: true");
72
+ const allowIdx = lines.findIndex((line) => line.trim() === "allowBuilds:");
73
+ if (allowIdx === -1) {
74
+ lines.push("allowBuilds:");
75
+ lines.push(" esbuild: true");
76
+ lines.push(" rolldown: true");
77
+ lines.push(" unrs-resolver: true");
78
+ }
79
+ else {
80
+ const addAllow = (pkg) => {
81
+ const exists = lines.some((line) => line.trim().startsWith(`${pkg}:`));
82
+ if (!exists)
83
+ lines.splice(allowIdx + 1, 0, ` ${pkg}: true`);
84
+ };
85
+ addAllow("esbuild");
86
+ addAllow("rolldown");
87
+ addAllow("unrs-resolver");
88
+ }
89
+ return `${lines.join("\n").replace(/\n*$/, "\n")}`;
90
+ }
40
91
  function buildBunfig() {
41
92
  return [
42
93
  "[install]",
@@ -111,12 +162,14 @@ export function runInit(options = {}) {
111
162
  };
112
163
  const planned = [
113
164
  planSafeFile(path.join(cwd, "depsentinel.json"), `${buildDepsentinelConfig(preset, context)}\n`),
114
- planSafeFile(path.join(cwd, ".npmrc"), buildNpmRc()),
165
+ planSafeFile(path.join(cwd, ".npmrc"), existsSync(path.join(cwd, ".npmrc")) ? mergeNpmRc(readFileSync(path.join(cwd, ".npmrc"), "utf8")) : buildNpmRc(), { backupOnUpdate: false }),
115
166
  planSafeFile(path.join(cwd, ".npmignore"), buildNpmIgnore()),
116
167
  planSafeFile(path.join(cwd, ".github", "workflows", "depsentinel-ci.yml"), `${buildCiWorkflow()}\n`)
117
168
  ];
118
169
  if (facts.packageManager === "pnpm" || facts.packageManager === "unknown") {
119
- planned.push(planSafeFile(path.join(cwd, "pnpm-workspace.yaml"), buildPnpmWorkspace()));
170
+ planned.push(planSafeFile(path.join(cwd, "pnpm-workspace.yaml"), existsSync(path.join(cwd, "pnpm-workspace.yaml"))
171
+ ? mergePnpmWorkspace(readFileSync(path.join(cwd, "pnpm-workspace.yaml"), "utf8"))
172
+ : buildPnpmWorkspace(), { backupOnUpdate: false }));
120
173
  }
121
174
  if (facts.packageManager === "bun") {
122
175
  planned.push(planSafeFile(path.join(cwd, "bunfig.toml"), buildBunfig()));
@@ -9,7 +9,7 @@ function nextBackupPath(filePath) {
9
9
  }
10
10
  return candidate;
11
11
  }
12
- export function planSafeFile(filePath, content) {
12
+ export function planSafeFile(filePath, content, options = {}) {
13
13
  if (!existsSync(filePath)) {
14
14
  return { path: filePath, content, status: "create" };
15
15
  }
@@ -21,7 +21,7 @@ export function planSafeFile(filePath, content) {
21
21
  path: filePath,
22
22
  content,
23
23
  status: "update",
24
- backupPath: nextBackupPath(filePath)
24
+ backupPath: options.backupOnUpdate === false ? undefined : nextBackupPath(filePath)
25
25
  };
26
26
  }
27
27
  export function applySafePlan(plan, options = {}) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "depsentinel",
3
- "version": "0.1.5",
3
+ "version": "0.1.6",
4
4
  "description": "JS/TS supply-chain hardening CLI — scan, secure, and enforce dependency policies",
5
5
  "license": "MIT",
6
6
  "author": "depsentinel contributors",