@lzear/repo-lint 4.2.2 → 4.3.0

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 CHANGED
@@ -3,7 +3,7 @@
3
3
  [![npm](https://img.shields.io/npm/v/@lzear/repo-lint)](https://www.npmjs.com/package/@lzear/repo-lint)
4
4
  [![license](https://img.shields.io/npm/l/@lzear/repo-lint)](../../LICENSE)
5
5
 
6
- Checks lzear repos against forge standards. Used internally by `forge check`.
6
+ Checks lzear repos against forge standards and keeps them fresh. Used internally by `forge check` and `forge update`.
7
7
 
8
8
  ## Install
9
9
 
@@ -58,24 +58,41 @@ interface RepoReport {
58
58
  }
59
59
  ```
60
60
 
61
+ ### `runUpdate(options?)`
62
+
63
+ Powers `forge update`. Detects the package manager (npm, yarn, pnpm, bun) and updates dependency ranges (ncu, honoring `.ncurc{,.json,.js,.cjs,.mjs}`), the `packageManager` field, `.nvmrc`/`.node-version` (latest Node LTS), and `.bun-version`, then installs & refreshes the lockfile.
64
+
65
+ ```ts
66
+ import { detectPackageManager, runUpdate } from '@lzear/repo-lint'
67
+
68
+ const report = await runUpdate({ dry: true })
69
+ // { dir, packageManager: { name: 'yarn', version: '4.17.1', … }, results: [...] }
70
+ ```
71
+
72
+ | Option | Type | Default | Description |
73
+ |-----------|-----------|-----------------|------------------------------------|
74
+ | `dir` | `string` | `process.cwd()` | Repo to update |
75
+ | `dry` | `boolean` | `false` | Report changes without writing |
76
+ | `install` | `boolean` | `true` | Run install/lockfile refresh after |
77
+
61
78
  ## Checks performed
62
79
 
63
- | Check | Description |
64
- |--------------------------------|-------------------------------------------------------|
65
- | `readme-exists` | `README.md` exists |
66
- | `readme-npm-badge` | README has an npm badge |
67
- | `readme-codacy-grade-badge` | README has Codacy grade badge |
68
- | `readme-codacy-coverage-badge` | README has Codacy coverage badge |
69
- | `codacy-config` | `.codacy.yml` exists |
70
- | `license` | `LICENSE` exists |
71
- | `ci-workflow` | `.github/workflows/ci.yml` exists |
72
- | `renovate` | `renovate.json` exists |
73
- | `pkg-publint` | All published packages pass `publint` |
74
- | `pkg-attw` | All published packages pass `attw` (ESM-only profile) |
75
- | `pkg-knip` | No unused exports or dependencies (`knip`) |
76
- | `deps-fresh` | All dependencies up to date (`ncu`) |
77
- | `secret-npm-token` | GitHub secret `NPM_TOKEN` is set |
78
- | `secret-codacy-token` | GitHub secret `CODACY_PROJECT_TOKEN` is set |
80
+ | Check | Description |
81
+ |-----------------------|-----------------------------------------------------------|
82
+ | `readme-exists` | `README.md` exists |
83
+ | `readme-npm-badge` | README has an npm badge |
84
+ | `codacy` | Codacy grade & coverage badges in README, `.codacy.yml` |
85
+ | `license` | `LICENSE` exists |
86
+ | `ci-workflow` | `.github/workflows/ci.yml` exists |
87
+ | `renovate` | `renovate.json` exists |
88
+ | `pkg-publint` | All published packages pass `publint` |
89
+ | `pkg-attw` | All published packages pass `attw` (ESM-only profile) |
90
+ | `pkg-knip` | No unused exports or dependencies (`knip`) |
91
+ | `monorepo-lint` | Workspace consistency (`sherif`, monorepos only) |
92
+ | `deps-audit` | No known vulnerabilities (PM-native `audit`, prod, high+) |
93
+ | `deps-deprecated` | No direct dependency resolves to a deprecated version |
94
+ | `deps-fresh` | All dependencies up to date (`ncu`) |
95
+ | `secret-codacy-token` | GitHub secret `CODACY_PROJECT_TOKEN` is set |
79
96
 
80
97
  ## Part of forge
81
98
 
package/dist/bin.js CHANGED
@@ -7,150 +7,248 @@ import { parseArgs } from "util";
7
7
 
8
8
  // src/index.ts
9
9
  import { execSync } from "child_process";
10
- import { existsSync as existsSync2 } from "fs";
10
+ import { existsSync as existsSync3 } from "fs";
11
11
  import { tmpdir } from "os";
12
- import path2 from "path";
12
+ import path3 from "path";
13
13
 
14
14
  // src/checks.ts
15
+ import { spawnSync as spawnSync2 } from "child_process";
16
+ import { existsSync as existsSync2, readdirSync, readFileSync as readFileSync2 } from "fs";
17
+ import path2 from "path";
18
+ import { fileURLToPath } from "url";
19
+ import { run as ncuRun2 } from "npm-check-updates";
20
+ import { publint } from "publint";
21
+ import { maxSatisfying, satisfies } from "semver";
22
+
23
+ // src/update.ts
15
24
  import { spawnSync } from "child_process";
16
- import { existsSync, readdirSync, readFileSync } from "fs";
25
+ import { existsSync, readFileSync, writeFileSync } from "fs";
17
26
  import path from "path";
18
- import { fileURLToPath } from "url";
27
+ import { pathToFileURL } from "url";
19
28
  import { run as ncuRun } from "npm-check-updates";
20
- import { publint } from "publint";
21
- var _dirname = path.dirname(fileURLToPath(import.meta.url));
22
- var readPkg = (dir) => {
23
- const f = path.join(dir, "package.json");
24
- if (!existsSync(f)) return null;
29
+ var LOCKFILES = [
30
+ ["bun.lock", "bun"],
31
+ ["bun.lockb", "bun"],
32
+ ["pnpm-lock.yaml", "pnpm"],
33
+ ["yarn.lock", "yarn"],
34
+ ["package-lock.json", "npm"]
35
+ ];
36
+ var PM_FIELD_RE = /^(npm|yarn|pnpm|bun)@([^+\s]+)/;
37
+ var detectPackageManager = (dir) => {
38
+ const packageFile = path.join(dir, "package.json");
39
+ if (existsSync(packageFile))
40
+ try {
41
+ const package_ = JSON.parse(readFileSync(packageFile, "utf8"));
42
+ if (typeof package_.packageManager === "string") {
43
+ const match = PM_FIELD_RE.exec(package_.packageManager);
44
+ if (match?.[1] && match[2])
45
+ return {
46
+ name: match[1],
47
+ version: match[2],
48
+ source: "packageManager"
49
+ };
50
+ }
51
+ } catch {
52
+ }
53
+ for (const [file, name] of LOCKFILES)
54
+ if (existsSync(path.join(dir, file))) return { name, source: "lockfile" };
55
+ return { name: "npm", source: "default" };
56
+ };
57
+
58
+ // src/checks.ts
59
+ var _dirname = path2.dirname(fileURLToPath(import.meta.url));
60
+ var readPackage = (dir) => {
61
+ const f = path2.join(dir, "package.json");
62
+ if (!existsSync2(f)) return null;
25
63
  try {
26
- return JSON.parse(readFileSync(f, "utf8"));
64
+ return JSON.parse(readFileSync2(f, "utf8"));
27
65
  } catch {
28
66
  return null;
29
67
  }
30
68
  };
31
- var getWorkspacePatterns = (pkg) => {
32
- const ws = pkg.workspaces;
69
+ var getWorkspacePatterns = (package_) => {
70
+ const ws = package_.workspaces;
33
71
  if (Array.isArray(ws)) return ws;
34
- const wsPkg = ws;
35
- if (Array.isArray(wsPkg?.packages)) return wsPkg.packages;
72
+ const wsPackage = ws;
73
+ if (Array.isArray(wsPackage?.packages)) return wsPackage.packages;
36
74
  return [];
37
75
  };
38
76
  var patternToBase = (rootDir, pattern) => {
39
77
  const parts = pattern.split("/");
40
78
  if (parts.length === 2 && parts[1] === "*")
41
- return path.join(rootDir, parts[0] ?? "");
79
+ return path2.join(rootDir, parts[0] ?? "");
42
80
  if (parts.length === 1 && parts[0] === "*") return rootDir;
43
81
  return null;
44
82
  };
45
- var getWorkspaceDirs = (rootDir) => {
46
- const pkg = readPkg(rootDir);
47
- if (!pkg) return [];
48
- const dirs = [];
49
- for (const pattern of getWorkspacePatterns(pkg)) {
83
+ var getWorkspaceDirectories = (rootDir) => {
84
+ const package_ = readPackage(rootDir);
85
+ if (!package_) return [];
86
+ const directories = [];
87
+ for (const pattern of getWorkspacePatterns(package_)) {
50
88
  const base = patternToBase(rootDir, pattern);
51
- if (!base || !existsSync(base)) continue;
52
- for (const entry of readdirSync(base, { withFileTypes: true }))
53
- if (entry.isDirectory()) dirs.push(path.join(base, entry.name));
89
+ if (!base || !existsSync2(base)) continue;
90
+ const entries = readdirSync(base, { withFileTypes: true });
91
+ for (const entry of entries)
92
+ if (entry.isDirectory()) directories.push(path2.join(base, entry.name));
54
93
  }
55
- return dirs;
94
+ return directories;
56
95
  };
57
96
  var findBin = (name, startDir) => {
58
97
  let dir = startDir;
59
98
  while (true) {
60
- const bin = path.join(dir, "node_modules", ".bin", name);
61
- if (existsSync(bin)) return bin;
62
- const parent = path.join(dir, "..");
99
+ const bin = path2.join(dir, "node_modules", ".bin", name);
100
+ if (existsSync2(bin)) return bin;
101
+ const parent = path2.join(dir, "..");
63
102
  if (parent === dir) return null;
64
103
  dir = parent;
65
104
  }
66
105
  };
67
- var eachPublishedPkg = async (dir, fn) => {
68
- const pkg = readPkg(dir);
69
- if (!pkg) return { pass: true };
70
- if (pkg.private === true) {
71
- const wsDirs = getWorkspaceDirs(dir);
72
- if (wsDirs.length === 0) return { pass: true };
106
+ var eachPublishedPackage = async (dir, function_) => {
107
+ const package_ = readPackage(dir);
108
+ if (!package_) return { pass: true };
109
+ if (package_.private === true) {
110
+ const wsDirectories = getWorkspaceDirectories(dir);
111
+ if (wsDirectories.length === 0) return { pass: true };
73
112
  const results = await Promise.all(
74
- wsDirs.map((d) => eachPublishedPkg(d, fn))
113
+ wsDirectories.map((d) => eachPublishedPackage(d, function_))
75
114
  );
76
115
  const failures = results.filter((r) => !r.pass);
77
116
  if (failures.length === 0) return { pass: true };
78
117
  const detail = failures.flatMap((r) => r.detail ? [r.detail] : []).join("\n");
79
118
  return { pass: false, ...detail && { detail } };
80
119
  }
81
- return fn(dir);
120
+ return function_(dir);
82
121
  };
83
122
  var hasPublishedPkg = (dir) => {
84
- const pkg = readPkg(dir);
85
- if (!pkg) return false;
86
- if (pkg.private !== true) return true;
87
- return getWorkspaceDirs(dir).some((d) => hasPublishedPkg(d));
123
+ const package_ = readPackage(dir);
124
+ if (!package_) return false;
125
+ if (package_.private !== true) return true;
126
+ return getWorkspaceDirectories(dir).some((d) => hasPublishedPkg(d));
88
127
  };
89
128
  var readmeIncludes = (dir, needle) => {
90
- const f = path.join(dir, "README.md");
91
- return existsSync(f) && readFileSync(f, "utf8").includes(needle);
129
+ const f = path2.join(dir, "README.md");
130
+ return existsSync2(f) && readFileSync2(f, "utf8").includes(needle);
131
+ };
132
+ var auditCommand = (pm) => {
133
+ switch (pm.name) {
134
+ case "npm":
135
+ return ["npm", "audit", "--omit", "dev", "--audit-level", "high"];
136
+ case "pnpm":
137
+ return ["pnpm", "audit", "--prod", "--audit-level", "high"];
138
+ case "bun":
139
+ return ["bun", "audit"];
140
+ case "yarn":
141
+ return pm.version?.startsWith("1.") ? ["yarn", "audit", "--level", "high"] : [
142
+ "yarn",
143
+ "npm",
144
+ "audit",
145
+ "--environment",
146
+ "production",
147
+ "--severity",
148
+ "high"
149
+ ];
150
+ }
151
+ };
152
+ var DEP_FIELDS = [
153
+ "dependencies",
154
+ "devDependencies",
155
+ "optionalDependencies",
156
+ "peerDependencies"
157
+ ];
158
+ var NON_REGISTRY_RANGE_RE = /^(?:workspace:|file:|link:|portal:|catalog:|git|https?:)/;
159
+ var collectEntry = (dependencies, name, range) => {
160
+ if (typeof range !== "string" || NON_REGISTRY_RANGE_RE.test(range)) return;
161
+ const alias = /^npm:(.+)@([^@]+)$/.exec(range);
162
+ const target = alias?.[1] ?? name;
163
+ const targetRange = alias?.[2] ?? range;
164
+ if (!dependencies.has(target)) dependencies.set(target, targetRange);
165
+ };
166
+ var collectFromPackage = (dependencies, packageDir) => {
167
+ const package_ = readPackage(packageDir);
168
+ for (const field of DEP_FIELDS) {
169
+ const section = package_?.[field];
170
+ if (typeof section !== "object" || section === null) continue;
171
+ for (const [name, range] of Object.entries(section))
172
+ collectEntry(dependencies, name, range);
173
+ }
174
+ };
175
+ var collectDependencies = (dir) => {
176
+ const dependencies = /* @__PURE__ */ new Map();
177
+ for (const packageDir of [dir, ...getWorkspaceDirectories(dir)])
178
+ collectFromPackage(dependencies, packageDir);
179
+ return dependencies;
180
+ };
181
+ var findDeprecated = async (name, range) => {
182
+ const response = await fetch(
183
+ `https://registry.npmjs.org/${name.replace("/", "%2F")}`,
184
+ {
185
+ headers: { accept: "application/vnd.npm.install-v1+json" },
186
+ signal: AbortSignal.timeout(1e4)
187
+ }
188
+ );
189
+ if (!response.ok) throw new Error(`HTTP ${response.status}`);
190
+ const data = await response.json();
191
+ const latest = data["dist-tags"]?.latest;
192
+ const resolved = latest && satisfies(latest, range) ? latest : maxSatisfying(Object.keys(data.versions), range);
193
+ if (!resolved) return null;
194
+ const deprecated = data.versions[resolved]?.deprecated;
195
+ return deprecated ? `${name}@${resolved} \u2014 ${deprecated.slice(0, 120)}` : null;
92
196
  };
93
197
  var LOCAL_CHECKS = [
94
198
  {
95
199
  id: "readme-exists",
96
- desc: "README.md exists",
200
+ desc: "README",
97
201
  type: "local",
98
- check: (dir) => existsSync(path.join(dir, "README.md"))
99
- },
100
- {
101
- id: "readme-codacy-grade-badge",
102
- desc: "README has Codacy grade badge",
103
- type: "local",
104
- publishedOnly: true,
105
- check: (dir) => readmeIncludes(dir, "project/badge/Grade/")
106
- },
107
- {
108
- id: "readme-codacy-coverage-badge",
109
- desc: "README has Codacy coverage badge",
110
- type: "local",
111
- publishedOnly: true,
112
- check: (dir) => readmeIncludes(dir, "project/badge/Coverage/")
202
+ check: (dir) => existsSync2(path2.join(dir, "README.md"))
113
203
  },
114
204
  {
115
205
  id: "readme-npm-badge",
116
- desc: "README has npm badge",
206
+ desc: "npm badge",
117
207
  type: "local",
118
208
  publishedOnly: true,
119
209
  check: (dir) => readmeIncludes(dir, "shields.io/npm/v/")
120
210
  },
121
211
  {
122
- id: "codacy-config",
123
- desc: ".codacy.yml exists",
212
+ id: "codacy",
213
+ desc: "Codacy",
124
214
  type: "local",
125
215
  publishedOnly: true,
126
- check: (dir) => existsSync(path.join(dir, ".codacy.yml"))
216
+ check: (dir) => {
217
+ const missing = [
218
+ readmeIncludes(dir, "project/badge/Grade/") ? null : "grade badge missing in README",
219
+ readmeIncludes(dir, "project/badge/Coverage/") ? null : "coverage badge missing in README",
220
+ existsSync2(path2.join(dir, ".codacy.yml")) ? null : ".codacy.yml missing"
221
+ ].filter((m) => m !== null);
222
+ if (missing.length === 0) return true;
223
+ return { pass: false, detail: missing.join("\n") };
224
+ }
127
225
  },
128
226
  {
129
227
  id: "license",
130
- desc: "LICENSE exists",
228
+ desc: "LICENSE",
131
229
  type: "local",
132
230
  publishedOnly: true,
133
- check: (dir) => existsSync(path.join(dir, "LICENSE"))
231
+ check: (dir) => existsSync2(path2.join(dir, "LICENSE"))
134
232
  },
135
233
  {
136
234
  id: "ci-workflow",
137
- desc: "CI workflow exists",
235
+ desc: "CI workflow",
138
236
  type: "local",
139
237
  publishedOnly: true,
140
- check: (dir) => existsSync(path.join(dir, ".github/workflows/ci.yml"))
238
+ check: (dir) => existsSync2(path2.join(dir, ".github/workflows/ci.yml"))
141
239
  },
142
240
  {
143
241
  id: "renovate",
144
- desc: "renovate.json exists",
242
+ desc: "renovate.json",
145
243
  type: "local",
146
- check: (dir) => existsSync(path.join(dir, "renovate.json"))
244
+ check: (dir) => existsSync2(path2.join(dir, "renovate.json"))
147
245
  },
148
246
  {
149
247
  id: "pkg-publint",
150
- desc: "publint (all published packages)",
248
+ desc: "publint",
151
249
  type: "local",
152
250
  publishedOnly: true,
153
- check: (dir) => eachPublishedPkg(dir, async (pkgDir) => {
251
+ check: (dir) => eachPublishedPackage(dir, async (pkgDir) => {
154
252
  const { messages } = await publint({ pkgDir });
155
253
  const failures = messages.filter(
156
254
  (m) => m.type === "error" || m.type === "warning"
@@ -164,13 +262,13 @@ var LOCAL_CHECKS = [
164
262
  },
165
263
  {
166
264
  id: "pkg-attw",
167
- desc: "attw (all published packages)",
265
+ desc: "attw",
168
266
  type: "local",
169
267
  check: (dir) => {
170
268
  const bin = findBin("attw", _dirname);
171
269
  if (!bin) return { pass: false, detail: "attw not available" };
172
- return eachPublishedPkg(dir, (pkgDir) => {
173
- const r = spawnSync(
270
+ return eachPublishedPackage(dir, (pkgDir) => {
271
+ const r = spawnSync2(
174
272
  process.execPath,
175
273
  [bin, "--pack", "--profile", "esm-only"],
176
274
  {
@@ -186,12 +284,12 @@ var LOCAL_CHECKS = [
186
284
  },
187
285
  {
188
286
  id: "pkg-knip",
189
- desc: "knip (no unused exports/deps)",
287
+ desc: "knip",
190
288
  type: "local",
191
289
  check: (dir) => {
192
290
  const bin = findBin("knip", _dirname);
193
291
  if (!bin) return { pass: false, detail: "knip not available" };
194
- const r = spawnSync(process.execPath, [bin], {
292
+ const r = spawnSync2(process.execPath, [bin], {
195
293
  cwd: dir,
196
294
  encoding: "utf8",
197
295
  stdio: ["ignore", "pipe", "pipe"],
@@ -204,17 +302,80 @@ var LOCAL_CHECKS = [
204
302
  Run: npx knip` };
205
303
  }
206
304
  },
305
+ {
306
+ id: "monorepo-lint",
307
+ desc: "sherif (workspaces)",
308
+ type: "local",
309
+ check: (dir) => {
310
+ const package_ = readPackage(dir);
311
+ if (!package_ || getWorkspacePatterns(package_).length === 0)
312
+ return { pass: true, detail: "not a monorepo" };
313
+ const bin = findBin("sherif", _dirname);
314
+ if (!bin) return { pass: false, detail: "sherif not available" };
315
+ const r = spawnSync2(process.execPath, [bin], {
316
+ cwd: dir,
317
+ encoding: "utf8",
318
+ stdio: ["ignore", "pipe", "pipe"],
319
+ env: { ...process.env, NO_COLOR: "1" }
320
+ });
321
+ if (r.status === 0) return true;
322
+ const detail = (r.stdout + r.stderr).trim().split("\n").slice(0, 20).join("\n");
323
+ return { pass: false, detail };
324
+ }
325
+ },
326
+ {
327
+ id: "deps-audit",
328
+ desc: "audit",
329
+ type: "local",
330
+ check: (dir) => {
331
+ const pm = detectPackageManager(dir);
332
+ const [command, ...arguments_] = auditCommand(pm);
333
+ const r = spawnSync2(command, arguments_, {
334
+ cwd: dir,
335
+ encoding: "utf8",
336
+ stdio: ["ignore", "pipe", "pipe"]
337
+ });
338
+ if (r.error) return { pass: false, detail: String(r.error) };
339
+ if (r.status === 0) return true;
340
+ const detail = (r.stdout + r.stderr).trim().split("\n").slice(0, 20).join("\n");
341
+ return { pass: false, detail: detail || `audit exited ${r.status}` };
342
+ }
343
+ },
344
+ {
345
+ id: "deps-deprecated",
346
+ desc: "no deprecated deps",
347
+ type: "local",
348
+ check: async (dir) => {
349
+ const dependencies = collectDependencies(dir);
350
+ const failures = [];
351
+ await Promise.all(
352
+ [...dependencies].map(async ([name, range]) => {
353
+ try {
354
+ const deprecated = await findDeprecated(name, range);
355
+ if (deprecated) failures.push(deprecated);
356
+ } catch (error) {
357
+ failures.push(`${name} \u2014 check failed: ${String(error)}`);
358
+ }
359
+ })
360
+ );
361
+ if (failures.length === 0) return true;
362
+ return {
363
+ pass: false,
364
+ detail: failures.toSorted((a, b) => a.localeCompare(b)).join("\n")
365
+ };
366
+ }
367
+ },
207
368
  {
208
369
  id: "deps-fresh",
209
- desc: "dependencies up to date (ncu)",
370
+ desc: "deps up to date",
210
371
  type: "local",
211
372
  check: async (dir) => {
212
- const pkg = readPkg(dir);
213
- const hasWorkspaces = Array.isArray(pkg?.workspaces) && pkg.workspaces.length > 0;
373
+ const package_ = readPackage(dir);
374
+ const hasWorkspaces = Array.isArray(package_?.workspaces) && package_.workspaces.length > 0;
214
375
  try {
215
- const result = await ncuRun({
216
- packageFile: path.join(dir, "package.json"),
217
- ...hasWorkspaces ? { workspaces: true } : {},
376
+ const result = await ncuRun2({
377
+ packageFile: path2.join(dir, "package.json"),
378
+ ...hasWorkspaces && { workspaces: true },
218
379
  silent: true
219
380
  });
220
381
  const entries = hasWorkspaces ? Object.values(
@@ -222,10 +383,9 @@ Run: npx knip` };
222
383
  ).flatMap((x) => Object.entries(x)) : Object.entries(result);
223
384
  if (entries.length === 0) return true;
224
385
  const lines = entries.map(([k, v]) => `${k} \u2192 ${v}`).join("\n");
225
- const cmd = hasWorkspaces ? "yarn dlx npm-check-updates --dep dev,optional,peer,prod,packageManager -u --workspaces && yarn install" : "yarn dlx npm-check-updates --dep dev,optional,peer,prod,packageManager -u && yarn install";
226
386
  return { pass: false, detail: `${lines}
227
387
 
228
- Run: ${cmd}` };
388
+ Run: forge update` };
229
389
  } catch (error) {
230
390
  return { pass: false, detail: String(error) };
231
391
  }
@@ -234,7 +394,7 @@ Run: ${cmd}` };
234
394
  ];
235
395
  var listSecrets = (repo) => {
236
396
  try {
237
- const result = spawnSync(
397
+ const result = spawnSync2(
238
398
  "gh",
239
399
  ["secret", "list", "--repo", repo, "--json", "name"],
240
400
  {
@@ -251,7 +411,7 @@ var listSecrets = (repo) => {
251
411
  var REMOTE_CHECKS = [
252
412
  {
253
413
  id: "secret-codacy-token",
254
- desc: "Secret CODACY_PROJECT_TOKEN set",
414
+ desc: "Codacy secret",
255
415
  type: "remote",
256
416
  publishedOnly: true,
257
417
  check: (repo) => {
@@ -272,19 +432,21 @@ var checkLocal = async (options = {}) => {
272
432
  stdio: ["ignore", "pipe", "ignore"]
273
433
  }).trim();
274
434
  const match = /github\.com[:/](.+?)(?:\.git)?$/.exec(remote);
275
- return match?.[1] ?? path2.basename(dir);
435
+ return match?.[1] ?? path3.basename(dir);
276
436
  } catch {
277
437
  return dir;
278
438
  }
279
439
  })();
280
- const published = hasPublishedPkg(dir);
440
+ const isPublished = hasPublishedPkg(dir);
281
441
  const localResults = await Promise.all(
282
- LOCAL_CHECKS.filter((c) => !c.publishedOnly || published).map(async (c) => {
283
- const raw = await c.check(dir);
284
- return typeof raw === "boolean" ? { id: c.id, desc: c.desc, pass: raw } : { id: c.id, desc: c.desc, ...raw };
285
- })
442
+ LOCAL_CHECKS.filter((c) => !c.publishedOnly || isPublished).map(
443
+ async (c) => {
444
+ const raw = await c.check(dir);
445
+ return typeof raw === "boolean" ? { id: c.id, desc: c.desc, pass: raw } : { id: c.id, desc: c.desc, ...raw };
446
+ }
447
+ )
286
448
  );
287
- const remoteResults = skipRemote ? [] : REMOTE_CHECKS.filter((c) => !c.publishedOnly || published).map((c) => ({
449
+ const remoteResults = skipRemote ? [] : REMOTE_CHECKS.filter((c) => !c.publishedOnly || isPublished).map((c) => ({
288
450
  id: c.id,
289
451
  desc: c.desc,
290
452
  pass: c.check(repo)
@@ -294,11 +456,11 @@ var checkLocal = async (options = {}) => {
294
456
  var checkRepo = async (repo, options = {}) => {
295
457
  const {
296
458
  token,
297
- baseDir = path2.join(tmpdir(), "repo-lint"),
459
+ baseDir = path3.join(tmpdir(), "repo-lint"),
298
460
  skipRemote = false
299
461
  } = options;
300
- const dir = path2.join(baseDir, repo.replace("/", "__"));
301
- if (existsSync2(dir))
462
+ const dir = path3.join(baseDir, repo.replace("/", "__"));
463
+ if (existsSync3(dir))
302
464
  execSync(`git -C ${dir} pull --quiet`, { stdio: "ignore" });
303
465
  else {
304
466
  const url = token ? `https://${token}@github.com/${repo}.git` : `https://github.com/${repo}.git`;
@@ -329,24 +491,24 @@ var { values } = parseArgs({
329
491
  }
330
492
  });
331
493
  var printReport = (result) => {
332
- let anyFail = false;
494
+ let isAnyFail = false;
333
495
  const pass = result.results.filter((r) => r.pass).length;
334
496
  const total = result.results.length;
335
497
  for (const r of result.results) {
336
498
  console.log(` ${r.pass ? "\u2713" : "\u2717"} ${r.desc}`);
337
- if (!r.pass) anyFail = true;
499
+ if (!r.pass) isAnyFail = true;
338
500
  }
339
501
  console.log(`
340
502
  ${pass}/${total} checks passed`);
341
- return anyFail;
503
+ return isAnyFail;
342
504
  };
343
505
  var main = async () => {
344
- let anyFail = false;
506
+ let isAnyFail = false;
345
507
  if (values.local) {
346
508
  console.log(`
347
509
  \u2500\u2500 ${process.cwd()}`);
348
510
  const result = await checkLocal({ skipRemote: values["skip-remote"] });
349
- anyFail = printReport(result);
511
+ isAnyFail = printReport(result);
350
512
  } else {
351
513
  if (!values.repos) {
352
514
  console.error(
@@ -355,7 +517,8 @@ var main = async () => {
355
517
  process.exit(1);
356
518
  }
357
519
  mkdirSync(values.dir, { recursive: true });
358
- for (const repo of values.repos.split(",").map((r) => r.trim())) {
520
+ const repos = values.repos.split(",").map((r) => r.trim());
521
+ for (const repo of repos) {
359
522
  console.log(`
360
523
  \u2500\u2500 ${repo}`);
361
524
  try {
@@ -364,13 +527,13 @@ var main = async () => {
364
527
  baseDir: values.dir,
365
528
  skipRemote: values["skip-remote"]
366
529
  });
367
- if (printReport(result)) anyFail = true;
530
+ if (printReport(result)) isAnyFail = true;
368
531
  } catch {
369
532
  console.log(" \u2717 could not clone repo");
370
- anyFail = true;
533
+ isAnyFail = true;
371
534
  }
372
535
  }
373
536
  }
374
- process.exit(anyFail ? 1 : 0);
537
+ process.exit(isAnyFail ? 1 : 0);
375
538
  };
376
- void main();
539
+ await main();
package/dist/index.d.ts CHANGED
@@ -22,6 +22,32 @@ declare const LOCAL_CHECKS: LocalCheck[];
22
22
  declare const REMOTE_CHECKS: RemoteCheck[];
23
23
  declare const CHECKS: Check[];
24
24
 
25
+ type PackageManagerName = 'npm' | 'yarn' | 'pnpm' | 'bun';
26
+ interface PackageManager {
27
+ name: PackageManagerName;
28
+ version?: string;
29
+ source: 'packageManager' | 'lockfile' | 'default';
30
+ }
31
+ interface UpdateResult {
32
+ id: string;
33
+ desc: string;
34
+ pass: boolean;
35
+ changed: boolean;
36
+ detail?: string;
37
+ }
38
+ interface UpdateReport {
39
+ dir: string;
40
+ packageManager: PackageManager;
41
+ results: UpdateResult[];
42
+ }
43
+ interface UpdateOptions {
44
+ dir?: string;
45
+ dry?: boolean;
46
+ install?: boolean;
47
+ }
48
+ declare const detectPackageManager: (dir: string) => PackageManager;
49
+ declare const runUpdate: (options?: UpdateOptions) => Promise<UpdateReport>;
50
+
25
51
  interface CheckResult {
26
52
  id: string;
27
53
  desc: string;
@@ -45,4 +71,4 @@ interface CheckLocalOptions {
45
71
  declare const checkLocal: (options?: CheckLocalOptions) => Promise<RepoReport>;
46
72
  declare const checkRepo: (repo: string, options?: CheckRepoOptions) => Promise<RepoReport>;
47
73
 
48
- export { CHECKS, type Check, type CheckDetail, type CheckLocalOptions, type CheckRepoOptions, type CheckResult, LOCAL_CHECKS, type LocalCheck, REMOTE_CHECKS, type RemoteCheck, type RepoReport, checkLocal, checkRepo };
74
+ export { CHECKS, type Check, type CheckDetail, type CheckLocalOptions, type CheckRepoOptions, type CheckResult, LOCAL_CHECKS, type LocalCheck, type PackageManager, type PackageManagerName, REMOTE_CHECKS, type RemoteCheck, type RepoReport, type UpdateOptions, type UpdateReport, type UpdateResult, checkLocal, checkRepo, detectPackageManager, runUpdate };