@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 +82 -0
- package/dist/bin.js +19 -9
- package/dist/index.d.ts +2 -0
- package/dist/index.js +19 -9
- package/package.json +3 -3
package/README.md
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# @lzear/repo-lint
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/@lzear/repo-lint)
|
|
4
|
+
[](../../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,
|
|
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,
|
|
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.
|
|
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": "
|
|
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
|
+
}
|