@lzear/repo-lint 4.2.1 → 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",
97
- 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",
200
+ desc: "README",
103
201
  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
  {
@@ -249,19 +409,9 @@ var listSecrets = (repo) => {
249
409
  }
250
410
  };
251
411
  var REMOTE_CHECKS = [
252
- {
253
- id: "secret-npm-token",
254
- desc: "Secret NPM_TOKEN set",
255
- type: "remote",
256
- publishedOnly: true,
257
- check: (repo) => {
258
- const secrets = listSecrets(repo);
259
- return secrets?.includes("NPM_TOKEN") ?? false;
260
- }
261
- },
262
412
  {
263
413
  id: "secret-codacy-token",
264
- desc: "Secret CODACY_PROJECT_TOKEN set",
414
+ desc: "Codacy secret",
265
415
  type: "remote",
266
416
  publishedOnly: true,
267
417
  check: (repo) => {
@@ -282,19 +432,21 @@ var checkLocal = async (options = {}) => {
282
432
  stdio: ["ignore", "pipe", "ignore"]
283
433
  }).trim();
284
434
  const match = /github\.com[:/](.+?)(?:\.git)?$/.exec(remote);
285
- return match?.[1] ?? path2.basename(dir);
435
+ return match?.[1] ?? path3.basename(dir);
286
436
  } catch {
287
437
  return dir;
288
438
  }
289
439
  })();
290
- const published = hasPublishedPkg(dir);
440
+ const isPublished = hasPublishedPkg(dir);
291
441
  const localResults = await Promise.all(
292
- LOCAL_CHECKS.filter((c) => !c.publishedOnly || published).map(async (c) => {
293
- const raw = await c.check(dir);
294
- return typeof raw === "boolean" ? { id: c.id, desc: c.desc, pass: raw } : { id: c.id, desc: c.desc, ...raw };
295
- })
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
+ )
296
448
  );
297
- 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) => ({
298
450
  id: c.id,
299
451
  desc: c.desc,
300
452
  pass: c.check(repo)
@@ -304,11 +456,11 @@ var checkLocal = async (options = {}) => {
304
456
  var checkRepo = async (repo, options = {}) => {
305
457
  const {
306
458
  token,
307
- baseDir = path2.join(tmpdir(), "repo-lint"),
459
+ baseDir = path3.join(tmpdir(), "repo-lint"),
308
460
  skipRemote = false
309
461
  } = options;
310
- const dir = path2.join(baseDir, repo.replace("/", "__"));
311
- if (existsSync2(dir))
462
+ const dir = path3.join(baseDir, repo.replace("/", "__"));
463
+ if (existsSync3(dir))
312
464
  execSync(`git -C ${dir} pull --quiet`, { stdio: "ignore" });
313
465
  else {
314
466
  const url = token ? `https://${token}@github.com/${repo}.git` : `https://github.com/${repo}.git`;
@@ -339,24 +491,24 @@ var { values } = parseArgs({
339
491
  }
340
492
  });
341
493
  var printReport = (result) => {
342
- let anyFail = false;
494
+ let isAnyFail = false;
343
495
  const pass = result.results.filter((r) => r.pass).length;
344
496
  const total = result.results.length;
345
497
  for (const r of result.results) {
346
498
  console.log(` ${r.pass ? "\u2713" : "\u2717"} ${r.desc}`);
347
- if (!r.pass) anyFail = true;
499
+ if (!r.pass) isAnyFail = true;
348
500
  }
349
501
  console.log(`
350
502
  ${pass}/${total} checks passed`);
351
- return anyFail;
503
+ return isAnyFail;
352
504
  };
353
505
  var main = async () => {
354
- let anyFail = false;
506
+ let isAnyFail = false;
355
507
  if (values.local) {
356
508
  console.log(`
357
509
  \u2500\u2500 ${process.cwd()}`);
358
510
  const result = await checkLocal({ skipRemote: values["skip-remote"] });
359
- anyFail = printReport(result);
511
+ isAnyFail = printReport(result);
360
512
  } else {
361
513
  if (!values.repos) {
362
514
  console.error(
@@ -365,7 +517,8 @@ var main = async () => {
365
517
  process.exit(1);
366
518
  }
367
519
  mkdirSync(values.dir, { recursive: true });
368
- 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) {
369
522
  console.log(`
370
523
  \u2500\u2500 ${repo}`);
371
524
  try {
@@ -374,13 +527,13 @@ var main = async () => {
374
527
  baseDir: values.dir,
375
528
  skipRemote: values["skip-remote"]
376
529
  });
377
- if (printReport(result)) anyFail = true;
530
+ if (printReport(result)) isAnyFail = true;
378
531
  } catch {
379
532
  console.log(" \u2717 could not clone repo");
380
- anyFail = true;
533
+ isAnyFail = true;
381
534
  }
382
535
  }
383
536
  }
384
- process.exit(anyFail ? 1 : 0);
537
+ process.exit(isAnyFail ? 1 : 0);
385
538
  };
386
- void main();
539
+ await main();