@lzear/repo-lint 4.0.3 → 4.1.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/README.md ADDED
@@ -0,0 +1,82 @@
1
+ # @lzear/repo-lint
2
+
3
+ [![npm](https://img.shields.io/npm/v/@lzear/repo-lint)](https://www.npmjs.com/package/@lzear/repo-lint)
4
+ [![license](https://img.shields.io/npm/l/@lzear/repo-lint)](../../LICENSE)
5
+
6
+ Checks lzear repos against forge standards. Used internally by `forge check`.
7
+
8
+ ## Install
9
+
10
+ ```sh
11
+ npm install -D @lzear/repo-lint
12
+ # or
13
+ yarn add -D @lzear/repo-lint
14
+ ```
15
+
16
+ Requires Node ≥ 20.
17
+
18
+ ## CLI
19
+
20
+ ```sh
21
+ npx repo-lint # check current directory
22
+ ```
23
+
24
+ Most users should use `forge check` via [`@lzear/forge`](https://www.npmjs.com/package/@lzear/forge) instead.
25
+
26
+ ## Programmatic API
27
+
28
+ ```ts
29
+ import { checkLocal, checkRepo, CHECKS } from '@lzear/repo-lint'
30
+
31
+ // Check the current working directory
32
+ const report = await checkLocal()
33
+ console.log(report.results)
34
+ // [
35
+ // { pass: true, desc: 'README.md exists' },
36
+ // { pass: false, desc: 'publint (all published packages)', detail: '...' },
37
+ // ...
38
+ // ]
39
+
40
+ // Check a remote GitHub repo (clones via gh CLI)
41
+ const remote = await checkRepo('lzear/votes', { skipRemote: false })
42
+ ```
43
+
44
+ ### `checkLocal(options?)`
45
+
46
+ | Option | Type | Default | Description |
47
+ |---------------|-----------|---------|-----------------------------------|
48
+ | `skipRemote` | `boolean` | `false` | Skip GitHub secret checks |
49
+
50
+ Returns `Promise<RepoReport>`.
51
+
52
+ ### `RepoReport`
53
+
54
+ ```ts
55
+ interface RepoReport {
56
+ repo: string
57
+ results: { pass: boolean; desc: string; detail?: string }[]
58
+ }
59
+ ```
60
+
61
+ ## Checks performed
62
+
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 |
79
+
80
+ ## Part of forge
81
+
82
+ This package is part of [forge](https://github.com/lzear/forge) — shared dev tooling for lzear repos.
package/dist/bin.js CHANGED
@@ -13,7 +13,7 @@ import path2 from "path";
13
13
 
14
14
  // src/checks.ts
15
15
  import { spawnSync } from "child_process";
16
- import { existsSync, readFileSync, readdirSync } from "fs";
16
+ import { existsSync, readdirSync, readFileSync } from "fs";
17
17
  import path from "path";
18
18
  import { fileURLToPath } from "url";
19
19
  import { run as ncuRun } from "npm-check-updates";
@@ -81,6 +81,12 @@ var eachPublishedPkg = async (dir, fn) => {
81
81
  }
82
82
  return fn(dir);
83
83
  };
84
+ var hasPublishedPkg = (dir) => {
85
+ const pkg = readPkg(dir);
86
+ if (!pkg) return false;
87
+ if (pkg.private !== true) return true;
88
+ return getWorkspaceDirs(dir).some((d) => hasPublishedPkg(d));
89
+ };
84
90
  var readmeIncludes = (dir, needle) => {
85
91
  const f = path.join(dir, "README.md");
86
92
  return existsSync(f) && readFileSync(f, "utf8").includes(needle);
@@ -96,42 +102,42 @@ var LOCAL_CHECKS = [
96
102
  id: "readme-codacy-grade-badge",
97
103
  desc: "README has Codacy grade badge",
98
104
  type: "local",
105
+ publishedOnly: true,
99
106
  check: (dir) => readmeIncludes(dir, "project/badge/Grade/")
100
107
  },
101
108
  {
102
109
  id: "readme-codacy-coverage-badge",
103
110
  desc: "README has Codacy coverage badge",
104
111
  type: "local",
112
+ publishedOnly: true,
105
113
  check: (dir) => readmeIncludes(dir, "project/badge/Coverage/")
106
114
  },
107
115
  {
108
116
  id: "readme-npm-badge",
109
117
  desc: "README has npm badge",
110
118
  type: "local",
119
+ publishedOnly: true,
111
120
  check: (dir) => readmeIncludes(dir, "shields.io/npm/v/")
112
121
  },
113
- {
114
- id: "editorconfig",
115
- desc: ".editorconfig exists",
116
- type: "local",
117
- check: (dir) => existsSync(path.join(dir, ".editorconfig"))
118
- },
119
122
  {
120
123
  id: "codacy-config",
121
124
  desc: ".codacy.yml exists",
122
125
  type: "local",
126
+ publishedOnly: true,
123
127
  check: (dir) => existsSync(path.join(dir, ".codacy.yml"))
124
128
  },
125
129
  {
126
130
  id: "license",
127
131
  desc: "LICENSE exists",
128
132
  type: "local",
133
+ publishedOnly: true,
129
134
  check: (dir) => existsSync(path.join(dir, "LICENSE"))
130
135
  },
131
136
  {
132
137
  id: "ci-workflow",
133
138
  desc: "CI workflow exists",
134
139
  type: "local",
140
+ publishedOnly: true,
135
141
  check: (dir) => existsSync(path.join(dir, ".github/workflows/ci.yml"))
136
142
  },
137
143
  {
@@ -144,6 +150,7 @@ var LOCAL_CHECKS = [
144
150
  id: "pkg-publint",
145
151
  desc: "publint (all published packages)",
146
152
  type: "local",
153
+ publishedOnly: true,
147
154
  check: (dir) => eachPublishedPkg(dir, async (pkgDir) => {
148
155
  const { messages } = await publint({ pkgDir });
149
156
  const failures = messages.filter(
@@ -247,6 +254,7 @@ var REMOTE_CHECKS = [
247
254
  id: "secret-npm-token",
248
255
  desc: "Secret NPM_TOKEN set",
249
256
  type: "remote",
257
+ publishedOnly: true,
250
258
  check: (repo) => {
251
259
  const secrets = listSecrets(repo);
252
260
  return secrets?.includes("NPM_TOKEN") ?? false;
@@ -256,6 +264,7 @@ var REMOTE_CHECKS = [
256
264
  id: "secret-codacy-token",
257
265
  desc: "Secret CODACY_PROJECT_TOKEN set",
258
266
  type: "remote",
267
+ publishedOnly: true,
259
268
  check: (repo) => {
260
269
  const secrets = listSecrets(repo);
261
270
  return secrets?.includes("CODACY_PROJECT_TOKEN") ?? false;
@@ -279,13 +288,14 @@ var checkLocal = async (options = {}) => {
279
288
  return dir;
280
289
  }
281
290
  })();
291
+ const published = hasPublishedPkg(dir);
282
292
  const localResults = await Promise.all(
283
- LOCAL_CHECKS.map(async (c) => {
293
+ LOCAL_CHECKS.filter((c) => !c.publishedOnly || published).map(async (c) => {
284
294
  const raw = await c.check(dir);
285
295
  return typeof raw === "boolean" ? { id: c.id, desc: c.desc, pass: raw } : { id: c.id, desc: c.desc, ...raw };
286
296
  })
287
297
  );
288
- const remoteResults = skipRemote ? [] : REMOTE_CHECKS.map((c) => ({
298
+ const remoteResults = skipRemote ? [] : REMOTE_CHECKS.filter((c) => !c.publishedOnly || published).map((c) => ({
289
299
  id: c.id,
290
300
  desc: c.desc,
291
301
  pass: c.check(repo)
package/dist/index.d.ts CHANGED
@@ -7,12 +7,14 @@ interface LocalCheck {
7
7
  id: string;
8
8
  desc: string;
9
9
  type: 'local';
10
+ publishedOnly?: boolean;
10
11
  check: (dir: string) => CheckResult$1;
11
12
  }
12
13
  interface RemoteCheck {
13
14
  id: string;
14
15
  desc: string;
15
16
  type: 'remote';
17
+ publishedOnly?: boolean;
16
18
  check: (repo: string) => boolean;
17
19
  }
18
20
  type Check = LocalCheck | RemoteCheck;
package/dist/index.js CHANGED
@@ -6,7 +6,7 @@ import path2 from "path";
6
6
 
7
7
  // src/checks.ts
8
8
  import { spawnSync } from "child_process";
9
- import { existsSync, readFileSync, readdirSync } from "fs";
9
+ import { existsSync, readdirSync, readFileSync } from "fs";
10
10
  import path from "path";
11
11
  import { fileURLToPath } from "url";
12
12
  import { run as ncuRun } from "npm-check-updates";
@@ -74,6 +74,12 @@ var eachPublishedPkg = async (dir, fn) => {
74
74
  }
75
75
  return fn(dir);
76
76
  };
77
+ var hasPublishedPkg = (dir) => {
78
+ const pkg = readPkg(dir);
79
+ if (!pkg) return false;
80
+ if (pkg.private !== true) return true;
81
+ return getWorkspaceDirs(dir).some((d) => hasPublishedPkg(d));
82
+ };
77
83
  var readmeIncludes = (dir, needle) => {
78
84
  const f = path.join(dir, "README.md");
79
85
  return existsSync(f) && readFileSync(f, "utf8").includes(needle);
@@ -89,42 +95,42 @@ var LOCAL_CHECKS = [
89
95
  id: "readme-codacy-grade-badge",
90
96
  desc: "README has Codacy grade badge",
91
97
  type: "local",
98
+ publishedOnly: true,
92
99
  check: (dir) => readmeIncludes(dir, "project/badge/Grade/")
93
100
  },
94
101
  {
95
102
  id: "readme-codacy-coverage-badge",
96
103
  desc: "README has Codacy coverage badge",
97
104
  type: "local",
105
+ publishedOnly: true,
98
106
  check: (dir) => readmeIncludes(dir, "project/badge/Coverage/")
99
107
  },
100
108
  {
101
109
  id: "readme-npm-badge",
102
110
  desc: "README has npm badge",
103
111
  type: "local",
112
+ publishedOnly: true,
104
113
  check: (dir) => readmeIncludes(dir, "shields.io/npm/v/")
105
114
  },
106
- {
107
- id: "editorconfig",
108
- desc: ".editorconfig exists",
109
- type: "local",
110
- check: (dir) => existsSync(path.join(dir, ".editorconfig"))
111
- },
112
115
  {
113
116
  id: "codacy-config",
114
117
  desc: ".codacy.yml exists",
115
118
  type: "local",
119
+ publishedOnly: true,
116
120
  check: (dir) => existsSync(path.join(dir, ".codacy.yml"))
117
121
  },
118
122
  {
119
123
  id: "license",
120
124
  desc: "LICENSE exists",
121
125
  type: "local",
126
+ publishedOnly: true,
122
127
  check: (dir) => existsSync(path.join(dir, "LICENSE"))
123
128
  },
124
129
  {
125
130
  id: "ci-workflow",
126
131
  desc: "CI workflow exists",
127
132
  type: "local",
133
+ publishedOnly: true,
128
134
  check: (dir) => existsSync(path.join(dir, ".github/workflows/ci.yml"))
129
135
  },
130
136
  {
@@ -137,6 +143,7 @@ var LOCAL_CHECKS = [
137
143
  id: "pkg-publint",
138
144
  desc: "publint (all published packages)",
139
145
  type: "local",
146
+ publishedOnly: true,
140
147
  check: (dir) => eachPublishedPkg(dir, async (pkgDir) => {
141
148
  const { messages } = await publint({ pkgDir });
142
149
  const failures = messages.filter(
@@ -240,6 +247,7 @@ var REMOTE_CHECKS = [
240
247
  id: "secret-npm-token",
241
248
  desc: "Secret NPM_TOKEN set",
242
249
  type: "remote",
250
+ publishedOnly: true,
243
251
  check: (repo) => {
244
252
  const secrets = listSecrets(repo);
245
253
  return secrets?.includes("NPM_TOKEN") ?? false;
@@ -249,6 +257,7 @@ var REMOTE_CHECKS = [
249
257
  id: "secret-codacy-token",
250
258
  desc: "Secret CODACY_PROJECT_TOKEN set",
251
259
  type: "remote",
260
+ publishedOnly: true,
252
261
  check: (repo) => {
253
262
  const secrets = listSecrets(repo);
254
263
  return secrets?.includes("CODACY_PROJECT_TOKEN") ?? false;
@@ -272,13 +281,14 @@ var checkLocal = async (options = {}) => {
272
281
  return dir;
273
282
  }
274
283
  })();
284
+ const published = hasPublishedPkg(dir);
275
285
  const localResults = await Promise.all(
276
- LOCAL_CHECKS.map(async (c) => {
286
+ LOCAL_CHECKS.filter((c) => !c.publishedOnly || published).map(async (c) => {
277
287
  const raw = await c.check(dir);
278
288
  return typeof raw === "boolean" ? { id: c.id, desc: c.desc, pass: raw } : { id: c.id, desc: c.desc, ...raw };
279
289
  })
280
290
  );
281
- const remoteResults = skipRemote ? [] : REMOTE_CHECKS.map((c) => ({
291
+ const remoteResults = skipRemote ? [] : REMOTE_CHECKS.filter((c) => !c.publishedOnly || published).map((c) => ({
282
292
  id: c.id,
283
293
  desc: c.desc,
284
294
  pass: c.check(repo)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lzear/repo-lint",
3
- "version": "4.0.3",
3
+ "version": "4.1.1",
4
4
  "description": "Checks lzear repos against forge standards",
5
5
  "repository": {
6
6
  "type": "git",
@@ -35,7 +35,7 @@
35
35
  "publint": "^0.3"
36
36
  },
37
37
  "devDependencies": {
38
- "@lzear/configs": "4.0.3",
38
+ "@lzear/configs": "workspace:*",
39
39
  "@types/node": "^25",
40
40
  "@vitest/coverage-v8": "^4",
41
41
  "tsup": "^8",
@@ -48,4 +48,4 @@
48
48
  "publishConfig": {
49
49
  "access": "public"
50
50
  }
51
- }
51
+ }