@macrulez/devtoolz 0.1.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/LICENSE +21 -0
- package/README.md +262 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +330 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/case-check/core.d.ts +17 -0
- package/dist/commands/case-check/core.js +46 -0
- package/dist/commands/case-check/core.js.map +1 -0
- package/dist/commands/case-check/parse.d.ts +10 -0
- package/dist/commands/case-check/parse.js +52 -0
- package/dist/commands/case-check/parse.js.map +1 -0
- package/dist/commands/case-check/report.d.ts +3 -0
- package/dist/commands/case-check/report.js +38 -0
- package/dist/commands/case-check/report.js.map +1 -0
- package/dist/commands/case-check/resolve-case.d.ts +14 -0
- package/dist/commands/case-check/resolve-case.js +163 -0
- package/dist/commands/case-check/resolve-case.js.map +1 -0
- package/dist/commands/case-check/run.d.ts +31 -0
- package/dist/commands/case-check/run.js +72 -0
- package/dist/commands/case-check/run.js.map +1 -0
- package/dist/commands/case-check/tsconfig-paths.d.ts +19 -0
- package/dist/commands/case-check/tsconfig-paths.js +119 -0
- package/dist/commands/case-check/tsconfig-paths.js.map +1 -0
- package/dist/commands/case-check/vue.d.ts +8 -0
- package/dist/commands/case-check/vue.js +26 -0
- package/dist/commands/case-check/vue.js.map +1 -0
- package/dist/commands/console-strip/core.d.ts +23 -0
- package/dist/commands/console-strip/core.js +104 -0
- package/dist/commands/console-strip/core.js.map +1 -0
- package/dist/commands/console-strip/report.d.ts +3 -0
- package/dist/commands/console-strip/report.js +47 -0
- package/dist/commands/console-strip/report.js.map +1 -0
- package/dist/commands/console-strip/run.d.ts +30 -0
- package/dist/commands/console-strip/run.js +55 -0
- package/dist/commands/console-strip/run.js.map +1 -0
- package/dist/commands/console-strip/vue.d.ts +8 -0
- package/dist/commands/console-strip/vue.js +28 -0
- package/dist/commands/console-strip/vue.js.map +1 -0
- package/dist/commands/dead-exports/core.d.ts +19 -0
- package/dist/commands/dead-exports/core.js +136 -0
- package/dist/commands/dead-exports/core.js.map +1 -0
- package/dist/commands/dead-exports/entry.d.ts +2 -0
- package/dist/commands/dead-exports/entry.js +93 -0
- package/dist/commands/dead-exports/entry.js.map +1 -0
- package/dist/commands/dead-exports/parse.d.ts +30 -0
- package/dist/commands/dead-exports/parse.js +174 -0
- package/dist/commands/dead-exports/parse.js.map +1 -0
- package/dist/commands/dead-exports/report.d.ts +3 -0
- package/dist/commands/dead-exports/report.js +27 -0
- package/dist/commands/dead-exports/report.js.map +1 -0
- package/dist/commands/dead-exports/resolve.d.ts +10 -0
- package/dist/commands/dead-exports/resolve.js +58 -0
- package/dist/commands/dead-exports/resolve.js.map +1 -0
- package/dist/commands/dead-exports/run.d.ts +21 -0
- package/dist/commands/dead-exports/run.js +51 -0
- package/dist/commands/dead-exports/run.js.map +1 -0
- package/dist/commands/dead-exports/workspace.d.ts +14 -0
- package/dist/commands/dead-exports/workspace.js +99 -0
- package/dist/commands/dead-exports/workspace.js.map +1 -0
- package/dist/commands/exports-doctor/core.d.ts +13 -0
- package/dist/commands/exports-doctor/core.js +85 -0
- package/dist/commands/exports-doctor/core.js.map +1 -0
- package/dist/commands/exports-doctor/parse.d.ts +20 -0
- package/dist/commands/exports-doctor/parse.js +80 -0
- package/dist/commands/exports-doctor/parse.js.map +1 -0
- package/dist/commands/exports-doctor/report.d.ts +3 -0
- package/dist/commands/exports-doctor/report.js +36 -0
- package/dist/commands/exports-doctor/report.js.map +1 -0
- package/dist/commands/exports-doctor/resolve-path.d.ts +10 -0
- package/dist/commands/exports-doctor/resolve-path.js +46 -0
- package/dist/commands/exports-doctor/resolve-path.js.map +1 -0
- package/dist/commands/exports-doctor/run.d.ts +13 -0
- package/dist/commands/exports-doctor/run.js +25 -0
- package/dist/commands/exports-doctor/run.js.map +1 -0
- package/dist/commands/readme-check/core.d.ts +22 -0
- package/dist/commands/readme-check/core.js +92 -0
- package/dist/commands/readme-check/core.js.map +1 -0
- package/dist/commands/readme-check/extract.d.ts +9 -0
- package/dist/commands/readme-check/extract.js +48 -0
- package/dist/commands/readme-check/extract.js.map +1 -0
- package/dist/commands/readme-check/lang.d.ts +8 -0
- package/dist/commands/readme-check/lang.js +21 -0
- package/dist/commands/readme-check/lang.js.map +1 -0
- package/dist/commands/readme-check/report.d.ts +3 -0
- package/dist/commands/readme-check/report.js +31 -0
- package/dist/commands/readme-check/report.js.map +1 -0
- package/dist/commands/readme-check/run.d.ts +19 -0
- package/dist/commands/readme-check/run.js +34 -0
- package/dist/commands/readme-check/run.js.map +1 -0
- package/dist/commands/readme-check/tsconfig.d.ts +9 -0
- package/dist/commands/readme-check/tsconfig.js +26 -0
- package/dist/commands/readme-check/tsconfig.js.map +1 -0
- package/dist/commands/readme-check/typecheck.d.ts +31 -0
- package/dist/commands/readme-check/typecheck.js +82 -0
- package/dist/commands/readme-check/typecheck.js.map +1 -0
- package/dist/commands/strip-comments/apply-deletions.d.ts +17 -0
- package/dist/commands/strip-comments/apply-deletions.js +74 -0
- package/dist/commands/strip-comments/apply-deletions.js.map +1 -0
- package/dist/commands/strip-comments/core.d.ts +15 -0
- package/dist/commands/strip-comments/core.js +111 -0
- package/dist/commands/strip-comments/core.js.map +1 -0
- package/dist/commands/strip-comments/report.d.ts +6 -0
- package/dist/commands/strip-comments/report.js +30 -0
- package/dist/commands/strip-comments/report.js.map +1 -0
- package/dist/commands/strip-comments/run.d.ts +28 -0
- package/dist/commands/strip-comments/run.js +50 -0
- package/dist/commands/strip-comments/run.js.map +1 -0
- package/dist/commands/strip-comments/vue.d.ts +6 -0
- package/dist/commands/strip-comments/vue.js +65 -0
- package/dist/commands/strip-comments/vue.js.map +1 -0
- package/dist/format/colors.d.ts +8 -0
- package/dist/format/colors.js +14 -0
- package/dist/format/colors.js.map +1 -0
- package/dist/format/diff-render.d.ts +2 -0
- package/dist/format/diff-render.js +35 -0
- package/dist/format/diff-render.js.map +1 -0
- package/dist/format/file-list.d.ts +14 -0
- package/dist/format/file-list.js +26 -0
- package/dist/format/file-list.js.map +1 -0
- package/dist/format/findings-list.d.ts +14 -0
- package/dist/format/findings-list.js +23 -0
- package/dist/format/findings-list.js.map +1 -0
- package/dist/format/vibes.d.ts +10 -0
- package/dist/format/vibes.js +67 -0
- package/dist/format/vibes.js.map +1 -0
- package/dist/index.d.ts +47 -0
- package/dist/index.js +29 -0
- package/dist/index.js.map +1 -0
- package/dist/utils/diff.d.ts +1 -0
- package/dist/utils/diff.js +13 -0
- package/dist/utils/diff.js.map +1 -0
- package/dist/utils/walk.d.ts +19 -0
- package/dist/utils/walk.js +96 -0
- package/dist/utils/walk.js.map +1 -0
- package/package.json +57 -0
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export interface DeclaredPath {
|
|
2
|
+
/** e.g. 'main', 'bin.devtoolz', 'exports["./cdn"].types' */
|
|
3
|
+
location: string;
|
|
4
|
+
/** as written in package.json, e.g. './dist/index.js' */
|
|
5
|
+
path: string;
|
|
6
|
+
/**
|
|
7
|
+
* True when this entry came from inside a real conditions object (e.g.
|
|
8
|
+
* `{ import: ..., require: ... }`), where a missing `types` sibling is a
|
|
9
|
+
* genuine gap. False for a bare string/array subpath value, where there
|
|
10
|
+
* are no siblings at all and TypeScript falls back to a colocated
|
|
11
|
+
* `.d.ts` file instead — flagging those as "types missing" would be a
|
|
12
|
+
* false positive.
|
|
13
|
+
*/
|
|
14
|
+
isConditionEntry: boolean;
|
|
15
|
+
}
|
|
16
|
+
export declare function collectDeclaredPaths(pkg: Record<string, unknown>): DeclaredPath[];
|
|
17
|
+
/** `location`s that name a `bin` entry — these additionally need a shebang. */
|
|
18
|
+
export declare function isBinLocation(location: string): boolean;
|
|
19
|
+
/** `location`s that are the `types`/`typings` half of a pair — see core.ts's types-vs-runtime check. */
|
|
20
|
+
export declare function isTypesLocation(location: string): boolean;
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
// Enumerates every file path `package.json` declares — main/module/types/
|
|
2
|
+
// typings/bin/exports — each tagged with WHERE it came from (e.g.
|
|
3
|
+
// `exports["./cdn"].types`), so a finding can point at the exact field to
|
|
4
|
+
// fix, not just "package.json is wrong somewhere".
|
|
5
|
+
function isConditionsObject(value) {
|
|
6
|
+
// An `exports` value is either a subpaths map (every key starts with
|
|
7
|
+
// '.') or a conditions map for the implicit '.' entry (keys are
|
|
8
|
+
// condition names like import/require/types/default/node/browser,
|
|
9
|
+
// none starting with '.') — same heuristic Node/bundlers use.
|
|
10
|
+
const keys = Object.keys(value);
|
|
11
|
+
return keys.length > 0 && keys.every((k) => !k.startsWith('.'));
|
|
12
|
+
}
|
|
13
|
+
function walkExportsValue(value, exportsKey, conditionPath, out) {
|
|
14
|
+
if (typeof value === 'string') {
|
|
15
|
+
const suffix = conditionPath.length > 0 ? `.${conditionPath.join('.')}` : '';
|
|
16
|
+
out.push({
|
|
17
|
+
location: `exports["${exportsKey}"]${suffix}`,
|
|
18
|
+
path: value,
|
|
19
|
+
isConditionEntry: conditionPath.length > 0,
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
else if (Array.isArray(value)) {
|
|
23
|
+
// A fallback array — any entry could be the one actually picked at
|
|
24
|
+
// runtime depending on the consumer's environment, so all are checked.
|
|
25
|
+
for (const entry of value)
|
|
26
|
+
walkExportsValue(entry, exportsKey, conditionPath, out);
|
|
27
|
+
}
|
|
28
|
+
else if (value && typeof value === 'object') {
|
|
29
|
+
for (const [key, nested] of Object.entries(value)) {
|
|
30
|
+
walkExportsValue(nested, exportsKey, [...conditionPath, key], out);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
function collectExportsPaths(exportsField) {
|
|
35
|
+
const out = [];
|
|
36
|
+
if (typeof exportsField === 'string') {
|
|
37
|
+
out.push({ location: 'exports', path: exportsField, isConditionEntry: false });
|
|
38
|
+
return out;
|
|
39
|
+
}
|
|
40
|
+
if (!exportsField || typeof exportsField !== 'object')
|
|
41
|
+
return out;
|
|
42
|
+
if (isConditionsObject(exportsField)) {
|
|
43
|
+
walkExportsValue(exportsField, '.', [], out);
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
for (const [subpath, value] of Object.entries(exportsField)) {
|
|
47
|
+
walkExportsValue(value, subpath, [], out);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return out;
|
|
51
|
+
}
|
|
52
|
+
function collectBinPaths(binField) {
|
|
53
|
+
if (typeof binField === 'string')
|
|
54
|
+
return [{ location: 'bin', path: binField, isConditionEntry: false }];
|
|
55
|
+
if (binField && typeof binField === 'object') {
|
|
56
|
+
return Object.entries(binField)
|
|
57
|
+
.filter((entry) => typeof entry[1] === 'string')
|
|
58
|
+
.map(([name, path]) => ({ location: `bin.${name}`, path, isConditionEntry: false }));
|
|
59
|
+
}
|
|
60
|
+
return [];
|
|
61
|
+
}
|
|
62
|
+
export function collectDeclaredPaths(pkg) {
|
|
63
|
+
const out = [];
|
|
64
|
+
for (const field of ['main', 'module', 'types', 'typings']) {
|
|
65
|
+
if (typeof pkg[field] === 'string')
|
|
66
|
+
out.push({ location: field, path: pkg[field], isConditionEntry: false });
|
|
67
|
+
}
|
|
68
|
+
out.push(...collectBinPaths(pkg.bin));
|
|
69
|
+
out.push(...collectExportsPaths(pkg.exports));
|
|
70
|
+
return out;
|
|
71
|
+
}
|
|
72
|
+
/** `location`s that name a `bin` entry — these additionally need a shebang. */
|
|
73
|
+
export function isBinLocation(location) {
|
|
74
|
+
return location === 'bin' || location.startsWith('bin.');
|
|
75
|
+
}
|
|
76
|
+
/** `location`s that are the `types`/`typings` half of a pair — see core.ts's types-vs-runtime check. */
|
|
77
|
+
export function isTypesLocation(location) {
|
|
78
|
+
return location === 'types' || location === 'typings' || location.endsWith('.types');
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=parse.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parse.js","sourceRoot":"","sources":["../../../src/commands/exports-doctor/parse.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { banner, cleanCelebration } from '../../format/vibes.js';
|
|
2
|
+
import { formatFindingsRows } from '../../format/findings-list.js';
|
|
3
|
+
const KIND_LABEL = {
|
|
4
|
+
missing: () => 'file does not exist',
|
|
5
|
+
'case-mismatch': (_path, realPath) => `wrong case — real path is ${realPath}`,
|
|
6
|
+
'missing-shebang': () => "missing '#!/usr/bin/env node' — won't run as a bin",
|
|
7
|
+
'types-missing': () => 'runtime resolves fine, but no types declared for this entry at all',
|
|
8
|
+
};
|
|
9
|
+
export function renderExportsDoctorReport(report, options = {}) {
|
|
10
|
+
if (report.error) {
|
|
11
|
+
const lines = [];
|
|
12
|
+
if (!options.quiet && !options.plain)
|
|
13
|
+
lines.push(banner(), '');
|
|
14
|
+
lines.push(`Could not read package.json: ${report.error}`);
|
|
15
|
+
return lines.join('\n');
|
|
16
|
+
}
|
|
17
|
+
const isClean = report.findings.length === 0;
|
|
18
|
+
if (options.quiet && isClean)
|
|
19
|
+
return '';
|
|
20
|
+
const lines = [];
|
|
21
|
+
if (!options.quiet && !options.plain)
|
|
22
|
+
lines.push(banner(), '');
|
|
23
|
+
lines.push(`Checked ${report.packageName ?? 'this package'}'s declared exports.`, '');
|
|
24
|
+
if (isClean) {
|
|
25
|
+
lines.push(cleanCelebration(options));
|
|
26
|
+
return lines.join('\n');
|
|
27
|
+
}
|
|
28
|
+
lines.push(`${report.findings.length} problem${report.findings.length === 1 ? '' : 's'} found:`);
|
|
29
|
+
lines.push(...formatFindingsRows(report.findings.map((f) => ({
|
|
30
|
+
location: f.location,
|
|
31
|
+
name: f.path,
|
|
32
|
+
tag: KIND_LABEL[f.kind]?.(f.path, f.realPath) ?? f.kind,
|
|
33
|
+
})), options));
|
|
34
|
+
return lines.join('\n');
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=report.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"report.js","sourceRoot":"","sources":["../../../src/commands/exports-doctor/report.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,gBAAgB,EAAqB,MAAM,uBAAuB,CAAA;AACnF,OAAO,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAA;AAGlE,MAAM,UAAU,GAAgE;IAC9E,OAAO,EAAE,GAAG,EAAE,CAAC,qBAAqB;IACpC,eAAe,EAAE,CAAC,KAAK,EAAE,QAAQ,EAAE,EAAE,CAAC,6BAA6B,QAAQ,EAAE;IAC7E,iBAAiB,EAAE,GAAG,EAAE,CAAC,oDAAoD;IAC7E,eAAe,EAAE,GAAG,EAAE,CAAC,oEAAoE;CAC5F,CAAA;AAED,MAAM,UAAU,yBAAyB,CACvC,MAA2B,EAC3B,UAAwB,EAAE;IAE1B,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;QACjB,MAAM,KAAK,GAAa,EAAE,CAAA;QAC1B,IAAI,CAAC,OAAO,CAAC,KAAK,IAAI,CAAC,OAAO,CAAC,KAAK;YAAE,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,EAAE,CAAC,CAAA;QAC9D,KAAK,CAAC,IAAI,CAAC,gCAAgC,MAAM,CAAC,KAAK,EAAE,CAAC,CAAA;QAC1D,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IACzB,CAAC;IAED,MAAM,OAAO,GAAG,MAAM,CAAC,QAAQ,CAAC,MAAM,KAAK,CAAC,CAAA;IAC5C,IAAI,OAAO,CAAC,KAAK,IAAI,OAAO;QAAE,OAAO,EAAE,CAAA;IAEvC,MAAM,KAAK,GAAa,EAAE,CAAA;IAC1B,IAAI,CAAC,OAAO,CAAC,KAAK,IAAI,CAAC,OAAO,CAAC,KAAK;QAAE,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,EAAE,CAAC,CAAA;IAE9D,KAAK,CAAC,IAAI,CAAC,WAAW,MAAM,CAAC,WAAW,IAAI,cAAc,sBAAsB,EAAE,EAAE,CAAC,CAAA;IAErF,IAAI,OAAO,EAAE,CAAC;QACZ,KAAK,CAAC,IAAI,CAAC,gBAAgB,CAAC,OAAO,CAAC,CAAC,CAAA;QACrC,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IACzB,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,QAAQ,CAAC,MAAM,WAAW,MAAM,CAAC,QAAQ,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC,CAAA;IAChG,KAAK,CAAC,IAAI,CACR,GAAG,kBAAkB,CACnB,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QAC1B,QAAQ,EAAE,CAAC,CAAC,QAAQ;QACpB,IAAI,EAAE,CAAC,CAAC,IAAI;QACZ,GAAG,EAAE,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,IAAI;KACxD,CAAC,CAAC,EACH,OAAO,CACR,CACF,CAAA;IAED,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;AACzB,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export type PathCheckResult = {
|
|
2
|
+
status: 'ok';
|
|
3
|
+
} | {
|
|
4
|
+
status: 'missing';
|
|
5
|
+
} | {
|
|
6
|
+
status: 'case-mismatch';
|
|
7
|
+
realPath: string;
|
|
8
|
+
};
|
|
9
|
+
export declare function checkDeclaredPath(packageDir: string, declaredPath: string): PathCheckResult;
|
|
10
|
+
export declare function resolveDeclaredPath(packageDir: string, declaredPath: string): string;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
// Checks one declared path against what's REALLY on disk, segment by
|
|
2
|
+
// segment, via `readdirSync` — same discipline as case-check, but here a
|
|
3
|
+
// missing file is the PRIMARY thing being looked for (case-check treats
|
|
4
|
+
// "doesn't resolve at all" as out of scope; here it's the main case),
|
|
5
|
+
// so the result is three-way instead of a plain yes/no.
|
|
6
|
+
import { readdirSync } from 'node:fs';
|
|
7
|
+
import { join, resolve } from 'node:path';
|
|
8
|
+
export function checkDeclaredPath(packageDir, declaredPath) {
|
|
9
|
+
const segments = declaredPath
|
|
10
|
+
.replace(/^\.\//, '')
|
|
11
|
+
.split('/')
|
|
12
|
+
.filter((s) => s.length > 0);
|
|
13
|
+
if (segments.length === 0)
|
|
14
|
+
return { status: 'missing' };
|
|
15
|
+
let currentDir = packageDir;
|
|
16
|
+
const corrected = [];
|
|
17
|
+
let hasMismatch = false;
|
|
18
|
+
for (const segment of segments) {
|
|
19
|
+
let entries;
|
|
20
|
+
try {
|
|
21
|
+
entries = readdirSync(currentDir);
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
return { status: 'missing' };
|
|
25
|
+
}
|
|
26
|
+
if (entries.includes(segment)) {
|
|
27
|
+
corrected.push(segment);
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
const lower = segment.toLowerCase();
|
|
31
|
+
const match = entries.find((e) => e.toLowerCase() === lower);
|
|
32
|
+
if (!match)
|
|
33
|
+
return { status: 'missing' };
|
|
34
|
+
hasMismatch = true;
|
|
35
|
+
corrected.push(match);
|
|
36
|
+
}
|
|
37
|
+
currentDir = join(currentDir, corrected[corrected.length - 1]);
|
|
38
|
+
}
|
|
39
|
+
if (hasMismatch)
|
|
40
|
+
return { status: 'case-mismatch', realPath: `./${corrected.join('/')}` };
|
|
41
|
+
return { status: 'ok' };
|
|
42
|
+
}
|
|
43
|
+
export function resolveDeclaredPath(packageDir, declaredPath) {
|
|
44
|
+
return resolve(packageDir, declaredPath);
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=resolve-path.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolve-path.js","sourceRoot":"","sources":["../../../src/commands/exports-doctor/resolve-path.ts"],"names":[],"mappings":"AAAA,qEAAqE;AACrE,yEAAyE;AACzE,wEAAwE;AACxE,sEAAsE;AACtE,wDAAwD;AAExD,OAAO,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AACrC,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAKzC,MAAM,UAAU,iBAAiB,CAAC,UAAkB,EAAE,YAAoB;IACxE,MAAM,QAAQ,GAAG,YAAY;SAC1B,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC;SACpB,KAAK,CAAC,GAAG,CAAC;SACV,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAA;IAC9B,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,CAAA;IAEvD,IAAI,UAAU,GAAG,UAAU,CAAA;IAC3B,MAAM,SAAS,GAAa,EAAE,CAAA;IAC9B,IAAI,WAAW,GAAG,KAAK,CAAA;IAEvB,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAC/B,IAAI,OAAiB,CAAA;QACrB,IAAI,CAAC;YACH,OAAO,GAAG,WAAW,CAAC,UAAU,CAAC,CAAA;QACnC,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,CAAA;QAC9B,CAAC;QAED,IAAI,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;YAC9B,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;QACzB,CAAC;aAAM,CAAC;YACN,MAAM,KAAK,GAAG,OAAO,CAAC,WAAW,EAAE,CAAA;YACnC,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,KAAK,KAAK,CAAC,CAAA;YAC5D,IAAI,CAAC,KAAK;gBAAE,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,CAAA;YACxC,WAAW,GAAG,IAAI,CAAA;YAClB,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;QACvB,CAAC;QACD,UAAU,GAAG,IAAI,CAAC,UAAU,EAAE,SAAS,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,CAAW,CAAC,CAAA;IAC1E,CAAC;IAED,IAAI,WAAW;QAAE,OAAO,EAAE,MAAM,EAAE,eAAe,EAAE,QAAQ,EAAE,KAAK,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAA;IACzF,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,CAAA;AACzB,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,UAAkB,EAAE,YAAoB;IAC1E,OAAO,OAAO,CAAC,UAAU,EAAE,YAAY,CAAC,CAAA;AAC1C,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type ExportsDoctorFinding } from './core.js';
|
|
2
|
+
export interface ExportsDoctorRunOptions {
|
|
3
|
+
/** package directory to check */
|
|
4
|
+
dir: string;
|
|
5
|
+
}
|
|
6
|
+
export interface ExportsDoctorReport {
|
|
7
|
+
packageName: string | null;
|
|
8
|
+
findings: ExportsDoctorFinding[];
|
|
9
|
+
/** package.json couldn't be read/parsed at all — a different kind of problem than any single finding */
|
|
10
|
+
error: string | null;
|
|
11
|
+
exitCode: number;
|
|
12
|
+
}
|
|
13
|
+
export declare function runExportsDoctor(options: ExportsDoctorRunOptions): ExportsDoctorReport;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { checkPackage } from './core.js';
|
|
4
|
+
export function runExportsDoctor(options) {
|
|
5
|
+
let pkg;
|
|
6
|
+
try {
|
|
7
|
+
pkg = JSON.parse(readFileSync(join(options.dir, 'package.json'), 'utf8'));
|
|
8
|
+
}
|
|
9
|
+
catch (err) {
|
|
10
|
+
return {
|
|
11
|
+
packageName: null,
|
|
12
|
+
findings: [],
|
|
13
|
+
error: err instanceof Error ? err.message : String(err),
|
|
14
|
+
exitCode: 1,
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
const result = checkPackage(pkg, options.dir);
|
|
18
|
+
return {
|
|
19
|
+
packageName: result.packageName,
|
|
20
|
+
findings: result.findings,
|
|
21
|
+
error: null,
|
|
22
|
+
exitCode: result.findings.length > 0 ? 1 : 0,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=run.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run.js","sourceRoot":"","sources":["../../../src/commands/exports-doctor/run.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AACtC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAChC,OAAO,EAAE,YAAY,EAA6B,MAAM,WAAW,CAAA;AAenE,MAAM,UAAU,gBAAgB,CAAC,OAAgC;IAC/D,IAAI,GAA4B,CAAA;IAChC,IAAI,CAAC;QACH,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,cAAc,CAAC,EAAE,MAAM,CAAC,CAGvE,CAAA;IACH,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO;YACL,WAAW,EAAE,IAAI;YACjB,QAAQ,EAAE,EAAE;YACZ,KAAK,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;YACvD,QAAQ,EAAE,CAAC;SACZ,CAAA;IACH,CAAC;IAED,MAAM,MAAM,GAAG,YAAY,CAAC,GAAG,EAAE,OAAO,CAAC,GAAG,CAAC,CAAA;IAE7C,OAAO;QACL,WAAW,EAAE,MAAM,CAAC,WAAW;QAC/B,QAAQ,EAAE,MAAM,CAAC,QAAQ;QACzB,KAAK,EAAE,IAAI;QACX,QAAQ,EAAE,MAAM,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;KAC7C,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type ts from 'typescript';
|
|
2
|
+
export interface ReadmeCheckFinding {
|
|
3
|
+
file: string;
|
|
4
|
+
/** 1-based, real line in the markdown file */
|
|
5
|
+
line: number;
|
|
6
|
+
/** 1-based column */
|
|
7
|
+
column: number;
|
|
8
|
+
message: string;
|
|
9
|
+
lang: string;
|
|
10
|
+
}
|
|
11
|
+
export interface ReadmeCheckFileResult {
|
|
12
|
+
file: string;
|
|
13
|
+
blocksChecked: number;
|
|
14
|
+
blocksSkipped: number;
|
|
15
|
+
findings: ReadmeCheckFinding[];
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Each block is typechecked in isolation — never accumulated with other
|
|
19
|
+
* blocks in the same file. See `isNoise` above for what that isolation
|
|
20
|
+
* costs and how it's mitigated.
|
|
21
|
+
*/
|
|
22
|
+
export declare function checkMarkdownFile(file: string, markdown: string, packageDir: string, compilerOptions: ts.CompilerOptions, langs: string[]): ReadmeCheckFileResult;
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { extractCodeBlocks } from './extract.js';
|
|
2
|
+
import { typecheckBlock } from './typecheck.js';
|
|
3
|
+
// "Cannot find name" (2304) and its "did you mean" variant (2552) — a doc
|
|
4
|
+
// snippet is routinely a FRAGMENT that assumes a variable set up in
|
|
5
|
+
// surrounding prose or an earlier, unrelated block ("inside your
|
|
6
|
+
// component:", "given a `router` instance, ..."). Checking each block in
|
|
7
|
+
// isolation (see below) can't tell that apart from a genuinely wrong
|
|
8
|
+
// example, so it doesn't try — these codes are dropped rather than
|
|
9
|
+
// reported as unreliable noise. Concatenating blocks together to give
|
|
10
|
+
// them shared context was tried and reverted: real dogfooding against
|
|
11
|
+
// every README in this workspace showed it trades this false-positive
|
|
12
|
+
// class for a WORSE one (two independent, alternative examples that
|
|
13
|
+
// happen to reuse a name like `toast`/`useForm` read as "Cannot redeclare
|
|
14
|
+
// block-scoped variable" — an alarming false bug report, not an obvious
|
|
15
|
+
// tool limitation the way "Cannot find name" reads).
|
|
16
|
+
//
|
|
17
|
+
// "X is declared but its value is never read" (6133) — a doc snippet
|
|
18
|
+
// routinely defines a handler function that's wired up somewhere NOT
|
|
19
|
+
// shown in the fenced block (a template's `@click`, an event listener
|
|
20
|
+
// registered elsewhere in prose) — real dogfooding hit this exact shape
|
|
21
|
+
// in vue-toast-kit's README (`function deleteFile(id) { ... }`, never
|
|
22
|
+
// called within its own isolated block). Also fires as a pure cascade of
|
|
23
|
+
// an already-suppressed 2304/2307 upstream (an unresolvable import
|
|
24
|
+
// leaves nothing to meaningfully call the declaration "unused" against).
|
|
25
|
+
const SUPPRESSED_CODES = new Set([2304, 2552, 6133]);
|
|
26
|
+
// "Cannot find module './X'" (2307) for a RELATIVE specifier is almost
|
|
27
|
+
// always a hypothetical file in the reader's own project ("your
|
|
28
|
+
// App.vue"), not something that could ever exist in this package — not
|
|
29
|
+
// checkable, so not reported. A BARE specifier (the package's own name,
|
|
30
|
+
// or one of its subpath exports) is kept: that's the one historically
|
|
31
|
+
// valuable case this command exists for (vue-image-kit's /cdn export
|
|
32
|
+
// resolving to nothing at runtime while nobody noticed).
|
|
33
|
+
const MODULE_NOT_FOUND_CODE = 2307;
|
|
34
|
+
const RELATIVE_MODULE_RE = /Cannot find module '(\.\.?\/[^']*)'/;
|
|
35
|
+
// A DIFFERENT code (2792, not 2307) for the case where the package's own
|
|
36
|
+
// dev tsconfig doesn't set `moduleResolution: bundler/node16/nodenext` at
|
|
37
|
+
// all — self-referencing its own name can never resolve there regardless
|
|
38
|
+
// of what the README says, and TypeScript's message says so explicitly
|
|
39
|
+
// ("Did you mean to set the 'moduleResolution' option..."). That's how
|
|
40
|
+
// this is told apart from a genuine bad specifier under a properly
|
|
41
|
+
// bundler-configured tsconfig (plain 2307, no such suffix) — the real
|
|
42
|
+
// vue-state-machine bug this command caught: its README's code examples
|
|
43
|
+
// still imported the unscoped `vue-state-machine` in five places after a
|
|
44
|
+
// scope-prefix fix elsewhere had already landed.
|
|
45
|
+
const UNSUPPORTED_RESOLUTION_CODE = 2792;
|
|
46
|
+
function isNoise(message, code) {
|
|
47
|
+
if (SUPPRESSED_CODES.has(code))
|
|
48
|
+
return true;
|
|
49
|
+
if (code === MODULE_NOT_FOUND_CODE && RELATIVE_MODULE_RE.test(message))
|
|
50
|
+
return true;
|
|
51
|
+
if (code === UNSUPPORTED_RESOLUTION_CODE)
|
|
52
|
+
return true;
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Each block is typechecked in isolation — never accumulated with other
|
|
57
|
+
* blocks in the same file. See `isNoise` above for what that isolation
|
|
58
|
+
* costs and how it's mitigated.
|
|
59
|
+
*/
|
|
60
|
+
export function checkMarkdownFile(file, markdown, packageDir, compilerOptions, langs) {
|
|
61
|
+
const blocks = extractCodeBlocks(markdown);
|
|
62
|
+
const findings = [];
|
|
63
|
+
let blocksChecked = 0;
|
|
64
|
+
let blocksSkipped = 0;
|
|
65
|
+
for (const block of blocks) {
|
|
66
|
+
if (block.noCheck || !langs.includes(block.lang)) {
|
|
67
|
+
blocksSkipped++;
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
const diagnostics = typecheckBlock(block.code, block.lang, packageDir, compilerOptions);
|
|
71
|
+
if (diagnostics === null) {
|
|
72
|
+
blocksSkipped++;
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
blocksChecked++;
|
|
76
|
+
for (const d of diagnostics) {
|
|
77
|
+
if (d.category !== 'error')
|
|
78
|
+
continue;
|
|
79
|
+
if (isNoise(d.message, d.code))
|
|
80
|
+
continue;
|
|
81
|
+
findings.push({
|
|
82
|
+
file,
|
|
83
|
+
line: block.startLine + d.line,
|
|
84
|
+
column: d.character + 1,
|
|
85
|
+
message: d.message,
|
|
86
|
+
lang: block.lang,
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return { file, blocksChecked, blocksSkipped, findings };
|
|
91
|
+
}
|
|
92
|
+
//# sourceMappingURL=core.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"core.js","sourceRoot":"","sources":["../../../src/commands/readme-check/core.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export interface CodeBlock {
|
|
2
|
+
lang: string;
|
|
3
|
+
/** 1-based line number of the first line of code, in the ORIGINAL markdown file */
|
|
4
|
+
startLine: number;
|
|
5
|
+
code: string;
|
|
6
|
+
/** an explicit `no-check` word in the fence's info string opts a block out */
|
|
7
|
+
noCheck: boolean;
|
|
8
|
+
}
|
|
9
|
+
export declare function extractCodeBlocks(markdown: string): CodeBlock[];
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
// Extracts fenced code blocks from a markdown file, tracking the block's
|
|
2
|
+
// language tag and the real 1-based line number where its content starts
|
|
3
|
+
// (right after the opening fence), so a typecheck error inside the block
|
|
4
|
+
// can be reported at the exact markdown line, not an offset into a
|
|
5
|
+
// throwaway virtual file.
|
|
6
|
+
const FENCE_START = /^(\s*)([`~]{3,})(.*)$/;
|
|
7
|
+
export function extractCodeBlocks(markdown) {
|
|
8
|
+
const lines = markdown.split(/\r\n|\r|\n/);
|
|
9
|
+
const blocks = [];
|
|
10
|
+
let i = 0;
|
|
11
|
+
while (i < lines.length) {
|
|
12
|
+
const line = lines[i];
|
|
13
|
+
const openMatch = FENCE_START.exec(line);
|
|
14
|
+
if (!openMatch) {
|
|
15
|
+
i++;
|
|
16
|
+
continue;
|
|
17
|
+
}
|
|
18
|
+
const fenceChars = openMatch[2];
|
|
19
|
+
const fenceChar = fenceChars[0];
|
|
20
|
+
const fenceLen = fenceChars.length;
|
|
21
|
+
const info = openMatch[3].trim();
|
|
22
|
+
const infoWords = info.split(/\s+/).filter(Boolean);
|
|
23
|
+
const lang = (infoWords[0] ?? '').toLowerCase();
|
|
24
|
+
const noCheck = infoWords.slice(1).includes('no-check');
|
|
25
|
+
// A closing fence must use the same character and be at least as long
|
|
26
|
+
// — a shorter run of the same character inside the block is literal
|
|
27
|
+
// content, not a close (standard GFM fence-length rule).
|
|
28
|
+
const closeRe = new RegExp(`^\\s*${fenceChar === '`' ? '`' : '~'}{${fenceLen},}\\s*$`);
|
|
29
|
+
let j = i + 1;
|
|
30
|
+
while (j < lines.length && !closeRe.test(lines[j]))
|
|
31
|
+
j++;
|
|
32
|
+
if (j >= lines.length) {
|
|
33
|
+
// Unterminated fence — malformed markdown, nothing sensible to
|
|
34
|
+
// extract; stop treating the rest of the file as inside this block.
|
|
35
|
+
i++;
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
blocks.push({
|
|
39
|
+
lang,
|
|
40
|
+
startLine: i + 2,
|
|
41
|
+
code: lines.slice(i + 1, j).join('\n'),
|
|
42
|
+
noCheck,
|
|
43
|
+
});
|
|
44
|
+
i = j + 1;
|
|
45
|
+
}
|
|
46
|
+
return blocks;
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=extract.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extract.js","sourceRoot":"","sources":["../../../src/commands/readme-check/extract.ts"],"names":[],"mappings":"AAAA,yEAAyE;AACzE,yEAAyE;AACzE,yEAAyE;AACzE,mEAAmE;AACnE,0BAA0B;AAW1B,MAAM,WAAW,GAAG,uBAAuB,CAAA;AAE3C,MAAM,UAAU,iBAAiB,CAAC,QAAgB;IAChD,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,YAAY,CAAC,CAAA;IAC1C,MAAM,MAAM,GAAgB,EAAE,CAAA;IAC9B,IAAI,CAAC,GAAG,CAAC,CAAA;IAET,OAAO,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC;QACxB,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAW,CAAA;QAC/B,MAAM,SAAS,GAAG,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QACxC,IAAI,CAAC,SAAS,EAAE,CAAC;YACf,CAAC,EAAE,CAAA;YACH,SAAQ;QACV,CAAC;QAED,MAAM,UAAU,GAAG,SAAS,CAAC,CAAC,CAAW,CAAA;QACzC,MAAM,SAAS,GAAG,UAAU,CAAC,CAAC,CAAW,CAAA;QACzC,MAAM,QAAQ,GAAG,UAAU,CAAC,MAAM,CAAA;QAClC,MAAM,IAAI,GAAI,SAAS,CAAC,CAAC,CAAY,CAAC,IAAI,EAAE,CAAA;QAC5C,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAA;QACnD,MAAM,IAAI,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,CAAA;QAC/C,MAAM,OAAO,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAA;QAEvD,sEAAsE;QACtE,oEAAoE;QACpE,yDAAyD;QACzD,MAAM,OAAO,GAAG,IAAI,MAAM,CAAC,QAAQ,SAAS,KAAK,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,IAAI,QAAQ,SAAS,CAAC,CAAA;QACtF,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;QACb,OAAO,CAAC,GAAG,KAAK,CAAC,MAAM,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAW,CAAC;YAAE,CAAC,EAAE,CAAA;QAEjE,IAAI,CAAC,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;YACtB,+DAA+D;YAC/D,oEAAoE;YACpE,CAAC,EAAE,CAAA;YACH,SAAQ;QACV,CAAC;QAED,MAAM,CAAC,IAAI,CAAC;YACV,IAAI;YACJ,SAAS,EAAE,CAAC,GAAG,CAAC;YAChB,IAAI,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;YACtC,OAAO;SACR,CAAC,CAAA;QACF,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;IACX,CAAC;IAED,OAAO,MAAM,CAAA;AACf,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import ts from 'typescript';
|
|
2
|
+
const LANG_MAP = {
|
|
3
|
+
ts: { scriptKind: ts.ScriptKind.TS, extension: '.ts', needsAllowJs: false },
|
|
4
|
+
typescript: { scriptKind: ts.ScriptKind.TS, extension: '.ts', needsAllowJs: false },
|
|
5
|
+
tsx: { scriptKind: ts.ScriptKind.TSX, extension: '.tsx', needsAllowJs: false },
|
|
6
|
+
js: { scriptKind: ts.ScriptKind.JS, extension: '.js', needsAllowJs: true },
|
|
7
|
+
javascript: { scriptKind: ts.ScriptKind.JS, extension: '.js', needsAllowJs: true },
|
|
8
|
+
jsx: { scriptKind: ts.ScriptKind.JSX, extension: '.jsx', needsAllowJs: true },
|
|
9
|
+
};
|
|
10
|
+
export function resolveLangInfo(lang) {
|
|
11
|
+
return LANG_MAP[lang] ?? null;
|
|
12
|
+
}
|
|
13
|
+
// tsx is deliberately NOT in the default set — real-world dogfooding
|
|
14
|
+
// against every package in this workspace showed EVERY tsx block failing
|
|
15
|
+
// universally with "Cannot use JSX unless the '--jsx' flag is provided"
|
|
16
|
+
// (Vue packages don't configure JSX/React types, since the block is
|
|
17
|
+
// illustrative "if you're in a React app" pseudocode, not code this
|
|
18
|
+
// package could ever compile). Opt in explicitly with `--lang ts,tsx` for
|
|
19
|
+
// a package that actually configures JSX in its own tsconfig.
|
|
20
|
+
export const DEFAULT_LANGS = ['ts'];
|
|
21
|
+
//# sourceMappingURL=lang.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lang.js","sourceRoot":"","sources":["../../../src/commands/readme-check/lang.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,YAAY,CAAA;AAQ3B,MAAM,QAAQ,GAA6B;IACzC,EAAE,EAAE,EAAE,UAAU,EAAE,EAAE,CAAC,UAAU,CAAC,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,YAAY,EAAE,KAAK,EAAE;IAC3E,UAAU,EAAE,EAAE,UAAU,EAAE,EAAE,CAAC,UAAU,CAAC,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,YAAY,EAAE,KAAK,EAAE;IACnF,GAAG,EAAE,EAAE,UAAU,EAAE,EAAE,CAAC,UAAU,CAAC,GAAG,EAAE,SAAS,EAAE,MAAM,EAAE,YAAY,EAAE,KAAK,EAAE;IAC9E,EAAE,EAAE,EAAE,UAAU,EAAE,EAAE,CAAC,UAAU,CAAC,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,YAAY,EAAE,IAAI,EAAE;IAC1E,UAAU,EAAE,EAAE,UAAU,EAAE,EAAE,CAAC,UAAU,CAAC,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,YAAY,EAAE,IAAI,EAAE;IAClF,GAAG,EAAE,EAAE,UAAU,EAAE,EAAE,CAAC,UAAU,CAAC,GAAG,EAAE,SAAS,EAAE,MAAM,EAAE,YAAY,EAAE,IAAI,EAAE;CAC9E,CAAA;AAED,MAAM,UAAU,eAAe,CAAC,IAAY;IAC1C,OAAO,QAAQ,CAAC,IAAI,CAAC,IAAI,IAAI,CAAA;AAC/B,CAAC;AAED,qEAAqE;AACrE,yEAAyE;AACzE,wEAAwE;AACxE,oEAAoE;AACpE,oEAAoE;AACpE,0EAA0E;AAC1E,8DAA8D;AAC9D,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,IAAI,CAAC,CAAA"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { banner, cleanCelebration } from '../../format/vibes.js';
|
|
2
|
+
import { formatFindingsRows } from '../../format/findings-list.js';
|
|
3
|
+
export function renderReadmeCheckReport(report, options = {}) {
|
|
4
|
+
if (report.error) {
|
|
5
|
+
const lines = [];
|
|
6
|
+
if (!options.quiet && !options.plain)
|
|
7
|
+
lines.push(banner(), '');
|
|
8
|
+
lines.push(`Could not run readme-check: ${report.error}`);
|
|
9
|
+
return lines.join('\n');
|
|
10
|
+
}
|
|
11
|
+
const blocksChecked = report.fileResults.reduce((sum, f) => sum + f.blocksChecked, 0);
|
|
12
|
+
const isClean = report.findings.length === 0;
|
|
13
|
+
if (options.quiet && isClean)
|
|
14
|
+
return '';
|
|
15
|
+
const lines = [];
|
|
16
|
+
if (!options.quiet && !options.plain)
|
|
17
|
+
lines.push(banner(), '');
|
|
18
|
+
lines.push(`Typechecked ${blocksChecked} code block${blocksChecked === 1 ? '' : 's'} across ${report.fileResults.length} file${report.fileResults.length === 1 ? '' : 's'}.`, '');
|
|
19
|
+
if (isClean) {
|
|
20
|
+
lines.push(cleanCelebration(options));
|
|
21
|
+
return lines.join('\n');
|
|
22
|
+
}
|
|
23
|
+
lines.push(`${report.findings.length} problem${report.findings.length === 1 ? '' : 's'} found:`);
|
|
24
|
+
lines.push(...formatFindingsRows(report.findings.map((f) => ({
|
|
25
|
+
location: `${f.file}:${f.line}:${f.column}`,
|
|
26
|
+
name: f.lang,
|
|
27
|
+
tag: f.message,
|
|
28
|
+
})), options));
|
|
29
|
+
return lines.join('\n');
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=report.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"report.js","sourceRoot":"","sources":["../../../src/commands/readme-check/report.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,gBAAgB,EAAqB,MAAM,uBAAuB,CAAA;AACnF,OAAO,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAA;AAGlE,MAAM,UAAU,uBAAuB,CACrC,MAAyB,EACzB,UAAwB,EAAE;IAE1B,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;QACjB,MAAM,KAAK,GAAa,EAAE,CAAA;QAC1B,IAAI,CAAC,OAAO,CAAC,KAAK,IAAI,CAAC,OAAO,CAAC,KAAK;YAAE,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,EAAE,CAAC,CAAA;QAC9D,KAAK,CAAC,IAAI,CAAC,+BAA+B,MAAM,CAAC,KAAK,EAAE,CAAC,CAAA;QACzD,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IACzB,CAAC;IAED,MAAM,aAAa,GAAG,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,aAAa,EAAE,CAAC,CAAC,CAAA;IACrF,MAAM,OAAO,GAAG,MAAM,CAAC,QAAQ,CAAC,MAAM,KAAK,CAAC,CAAA;IAC5C,IAAI,OAAO,CAAC,KAAK,IAAI,OAAO;QAAE,OAAO,EAAE,CAAA;IAEvC,MAAM,KAAK,GAAa,EAAE,CAAA;IAC1B,IAAI,CAAC,OAAO,CAAC,KAAK,IAAI,CAAC,OAAO,CAAC,KAAK;QAAE,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,EAAE,CAAC,CAAA;IAE9D,KAAK,CAAC,IAAI,CACR,eAAe,aAAa,cAAc,aAAa,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,WAAW,MAAM,CAAC,WAAW,CAAC,MAAM,QAAQ,MAAM,CAAC,WAAW,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,GAAG,EACjK,EAAE,CACH,CAAA;IAED,IAAI,OAAO,EAAE,CAAC;QACZ,KAAK,CAAC,IAAI,CAAC,gBAAgB,CAAC,OAAO,CAAC,CAAC,CAAA;QACrC,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IACzB,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,QAAQ,CAAC,MAAM,WAAW,MAAM,CAAC,QAAQ,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC,CAAA;IAChG,KAAK,CAAC,IAAI,CACR,GAAG,kBAAkB,CACnB,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QAC1B,QAAQ,EAAE,GAAG,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,MAAM,EAAE;QAC3C,IAAI,EAAE,CAAC,CAAC,IAAI;QACZ,GAAG,EAAE,CAAC,CAAC,OAAO;KACf,CAAC,CAAC,EACH,OAAO,CACR,CACF,CAAA;IAED,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;AACzB,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { type ReadmeCheckFinding, type ReadmeCheckFileResult } from './core.js';
|
|
2
|
+
export interface ReadmeCheckRunOptions {
|
|
3
|
+
/** package directory to check against */
|
|
4
|
+
dir: string;
|
|
5
|
+
/** markdown files to check, relative to `dir` — default: ['README.md'] */
|
|
6
|
+
files?: string[];
|
|
7
|
+
/** fenced-block languages to typecheck — default: ['ts', 'tsx'] */
|
|
8
|
+
langs?: string[];
|
|
9
|
+
/** tsconfig.json path, relative to `dir` — auto-detected by default */
|
|
10
|
+
tsconfig?: string;
|
|
11
|
+
}
|
|
12
|
+
export interface ReadmeCheckReport {
|
|
13
|
+
fileResults: ReadmeCheckFileResult[];
|
|
14
|
+
findings: ReadmeCheckFinding[];
|
|
15
|
+
/** couldn't load a tsconfig or read a target file at all — a different kind of problem than any single finding */
|
|
16
|
+
error: string | null;
|
|
17
|
+
exitCode: number;
|
|
18
|
+
}
|
|
19
|
+
export declare function runReadmeCheck(options: ReadmeCheckRunOptions): ReadmeCheckReport;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import { resolve } from 'node:path';
|
|
3
|
+
import { checkMarkdownFile } from './core.js';
|
|
4
|
+
import { loadCompilerOptions } from './tsconfig.js';
|
|
5
|
+
import { DEFAULT_LANGS } from './lang.js';
|
|
6
|
+
export function runReadmeCheck(options) {
|
|
7
|
+
const packageDir = resolve(options.dir);
|
|
8
|
+
const targetFiles = options.files && options.files.length > 0 ? options.files : ['README.md'];
|
|
9
|
+
const langs = options.langs && options.langs.length > 0 ? options.langs : DEFAULT_LANGS;
|
|
10
|
+
const loaded = loadCompilerOptions(packageDir, options.tsconfig);
|
|
11
|
+
if ('error' in loaded) {
|
|
12
|
+
return { fileResults: [], findings: [], error: loaded.error, exitCode: 1 };
|
|
13
|
+
}
|
|
14
|
+
const fileResults = [];
|
|
15
|
+
for (const relFile of targetFiles) {
|
|
16
|
+
const absPath = resolve(packageDir, relFile);
|
|
17
|
+
let markdown;
|
|
18
|
+
try {
|
|
19
|
+
markdown = readFileSync(absPath, 'utf8');
|
|
20
|
+
}
|
|
21
|
+
catch (err) {
|
|
22
|
+
return {
|
|
23
|
+
fileResults: [],
|
|
24
|
+
findings: [],
|
|
25
|
+
error: `could not read ${relFile}: ${err instanceof Error ? err.message : String(err)}`,
|
|
26
|
+
exitCode: 1,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
fileResults.push(checkMarkdownFile(relFile, markdown, packageDir, loaded.options, langs));
|
|
30
|
+
}
|
|
31
|
+
const findings = fileResults.flatMap((f) => f.findings);
|
|
32
|
+
return { fileResults, findings, error: null, exitCode: findings.length > 0 ? 1 : 0 };
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=run.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run.js","sourceRoot":"","sources":["../../../src/commands/readme-check/run.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AACtC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACnC,OAAO,EAAE,iBAAiB,EAAuD,MAAM,WAAW,CAAA;AAClG,OAAO,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAA;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAA;AAqBzC,MAAM,UAAU,cAAc,CAAC,OAA8B;IAC3D,MAAM,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;IACvC,MAAM,WAAW,GAAG,OAAO,CAAC,KAAK,IAAI,OAAO,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAA;IAC7F,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,OAAO,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,aAAa,CAAA;IAEvF,MAAM,MAAM,GAAG,mBAAmB,CAAC,UAAU,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAA;IAChE,IAAI,OAAO,IAAI,MAAM,EAAE,CAAC;QACtB,OAAO,EAAE,WAAW,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAA;IAC5E,CAAC;IAED,MAAM,WAAW,GAA4B,EAAE,CAAA;IAC/C,KAAK,MAAM,OAAO,IAAI,WAAW,EAAE,CAAC;QAClC,MAAM,OAAO,GAAG,OAAO,CAAC,UAAU,EAAE,OAAO,CAAC,CAAA;QAC5C,IAAI,QAAgB,CAAA;QACpB,IAAI,CAAC;YACH,QAAQ,GAAG,YAAY,CAAC,OAAO,EAAE,MAAM,CAAC,CAAA;QAC1C,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO;gBACL,WAAW,EAAE,EAAE;gBACf,QAAQ,EAAE,EAAE;gBACZ,KAAK,EAAE,kBAAkB,OAAO,KAAK,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE;gBACvF,QAAQ,EAAE,CAAC;aACZ,CAAA;QACH,CAAC;QACD,WAAW,CAAC,IAAI,CAAC,iBAAiB,CAAC,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC,CAAA;IAC3F,CAAC;IAED,MAAM,QAAQ,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAA;IACvD,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAA;AACtF,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import ts from 'typescript';
|
|
2
|
+
export interface LoadedCompilerOptions {
|
|
3
|
+
options: ts.CompilerOptions;
|
|
4
|
+
configPath: string;
|
|
5
|
+
}
|
|
6
|
+
export type LoadCompilerOptionsResult = LoadedCompilerOptions | {
|
|
7
|
+
error: string;
|
|
8
|
+
};
|
|
9
|
+
export declare function loadCompilerOptions(packageDir: string, tsconfigOverride?: string): LoadCompilerOptionsResult;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import ts from 'typescript';
|
|
2
|
+
import { dirname, resolve } from 'node:path';
|
|
3
|
+
export function loadCompilerOptions(packageDir, tsconfigOverride) {
|
|
4
|
+
const configPath = tsconfigOverride
|
|
5
|
+
? resolve(packageDir, tsconfigOverride)
|
|
6
|
+
: ts.findConfigFile(packageDir, ts.sys.fileExists, 'tsconfig.json');
|
|
7
|
+
if (!configPath || !ts.sys.fileExists(configPath)) {
|
|
8
|
+
return {
|
|
9
|
+
error: `no tsconfig.json found under ${packageDir} — nothing to typecheck README code blocks against`,
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
const read = ts.readConfigFile(configPath, ts.sys.readFile);
|
|
13
|
+
if (read.error) {
|
|
14
|
+
return { error: ts.flattenDiagnosticMessageText(read.error.messageText, '\n') };
|
|
15
|
+
}
|
|
16
|
+
const parsed = ts.parseJsonConfigFileContent(read.config, ts.sys, dirname(configPath));
|
|
17
|
+
if (parsed.errors.length > 0) {
|
|
18
|
+
return {
|
|
19
|
+
error: parsed.errors
|
|
20
|
+
.map((e) => ts.flattenDiagnosticMessageText(e.messageText, '\n'))
|
|
21
|
+
.join('\n'),
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
return { options: parsed.options, configPath };
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=tsconfig.js.map
|