@macrulez/devtoolz 0.1.0 → 0.2.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 +164 -3
- package/dist/cli.js +233 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/circular-imports/core.d.ts +15 -0
- package/dist/commands/circular-imports/core.js +95 -0
- package/dist/commands/circular-imports/core.js.map +1 -0
- package/dist/commands/circular-imports/report.d.ts +3 -0
- package/dist/commands/circular-imports/report.js +41 -0
- package/dist/commands/circular-imports/report.js.map +1 -0
- package/dist/commands/circular-imports/run.d.ts +19 -0
- package/dist/commands/circular-imports/run.js +29 -0
- package/dist/commands/circular-imports/run.js.map +1 -0
- package/dist/commands/dead-exports/core.d.ts +1 -1
- package/dist/commands/dead-exports/core.js +2 -2
- package/dist/commands/dead-exports/core.js.map +1 -1
- package/dist/commands/dead-exports/entry.d.ts +0 -1
- package/dist/commands/dead-exports/entry.js +1 -14
- package/dist/commands/dead-exports/entry.js.map +1 -1
- package/dist/commands/dead-exports/run.js +3 -2
- package/dist/commands/dead-exports/run.js.map +1 -1
- package/dist/commands/empty-catch/core.d.ts +8 -0
- package/dist/commands/empty-catch/core.js +61 -0
- package/dist/commands/empty-catch/core.js.map +1 -0
- package/dist/commands/empty-catch/report.d.ts +3 -0
- package/dist/commands/empty-catch/report.js +27 -0
- package/dist/commands/empty-catch/report.js.map +1 -0
- package/dist/commands/empty-catch/run.d.ts +17 -0
- package/dist/commands/empty-catch/run.js +34 -0
- package/dist/commands/empty-catch/run.js.map +1 -0
- package/dist/commands/empty-catch/vue.d.ts +2 -0
- package/dist/commands/empty-catch/vue.js +16 -0
- package/dist/commands/empty-catch/vue.js.map +1 -0
- package/dist/commands/orphan-tests/core.d.ts +9 -0
- package/dist/commands/orphan-tests/core.js +42 -0
- package/dist/commands/orphan-tests/core.js.map +1 -0
- package/dist/commands/orphan-tests/report.d.ts +3 -0
- package/dist/commands/orphan-tests/report.js +30 -0
- package/dist/commands/orphan-tests/report.js.map +1 -0
- package/dist/commands/orphan-tests/run.d.ts +25 -0
- package/dist/commands/orphan-tests/run.js +55 -0
- package/dist/commands/orphan-tests/run.js.map +1 -0
- package/dist/commands/readme-check/run.js +8 -3
- package/dist/commands/readme-check/run.js.map +1 -1
- package/dist/commands/scripts-check/core.d.ts +18 -0
- package/dist/commands/scripts-check/core.js +121 -0
- package/dist/commands/scripts-check/core.js.map +1 -0
- package/dist/commands/scripts-check/report.d.ts +3 -0
- package/dist/commands/scripts-check/report.js +34 -0
- package/dist/commands/scripts-check/report.js.map +1 -0
- package/dist/commands/scripts-check/run.d.ts +15 -0
- package/dist/commands/scripts-check/run.js +81 -0
- package/dist/commands/scripts-check/run.js.map +1 -0
- package/dist/commands/stale-ts-ignore/check.d.ts +23 -0
- package/dist/commands/stale-ts-ignore/check.js +104 -0
- package/dist/commands/stale-ts-ignore/check.js.map +1 -0
- package/dist/commands/stale-ts-ignore/core.d.ts +11 -0
- package/dist/commands/stale-ts-ignore/core.js +42 -0
- package/dist/commands/stale-ts-ignore/core.js.map +1 -0
- package/dist/commands/stale-ts-ignore/report.d.ts +3 -0
- package/dist/commands/stale-ts-ignore/report.js +30 -0
- package/dist/commands/stale-ts-ignore/report.js.map +1 -0
- package/dist/commands/stale-ts-ignore/run.d.ts +23 -0
- package/dist/commands/stale-ts-ignore/run.js +101 -0
- package/dist/commands/stale-ts-ignore/run.js.map +1 -0
- package/dist/commands/stale-ts-ignore/vue.d.ts +9 -0
- package/dist/commands/stale-ts-ignore/vue.js +16 -0
- package/dist/commands/stale-ts-ignore/vue.js.map +1 -0
- package/dist/commands/strip-comments/core.js +2 -56
- package/dist/commands/strip-comments/core.js.map +1 -1
- package/dist/commands/todo-report/core.d.ts +16 -0
- package/dist/commands/todo-report/core.js +112 -0
- package/dist/commands/todo-report/core.js.map +1 -0
- package/dist/commands/todo-report/report.d.ts +3 -0
- package/dist/commands/todo-report/report.js +31 -0
- package/dist/commands/todo-report/report.js.map +1 -0
- package/dist/commands/todo-report/run.d.ts +22 -0
- package/dist/commands/todo-report/run.js +41 -0
- package/dist/commands/todo-report/run.js.map +1 -0
- package/dist/commands/todo-report/vue.d.ts +2 -0
- package/dist/commands/todo-report/vue.js +71 -0
- package/dist/commands/todo-report/vue.js.map +1 -0
- package/dist/commands/unused-deps/config-files.d.ts +9 -0
- package/dist/commands/unused-deps/config-files.js +64 -0
- package/dist/commands/unused-deps/config-files.js.map +1 -0
- package/dist/commands/unused-deps/core.d.ts +25 -0
- package/dist/commands/unused-deps/core.js +121 -0
- package/dist/commands/unused-deps/core.js.map +1 -0
- package/dist/commands/unused-deps/disk-resolve.d.ts +11 -0
- package/dist/commands/unused-deps/disk-resolve.js +40 -0
- package/dist/commands/unused-deps/disk-resolve.js.map +1 -0
- package/dist/commands/unused-deps/package-name.d.ts +8 -0
- package/dist/commands/unused-deps/package-name.js +20 -0
- package/dist/commands/unused-deps/package-name.js.map +1 -0
- package/dist/commands/unused-deps/report.d.ts +3 -0
- package/dist/commands/unused-deps/report.js +32 -0
- package/dist/commands/unused-deps/report.js.map +1 -0
- package/dist/commands/unused-deps/run.d.ts +18 -0
- package/dist/commands/unused-deps/run.js +68 -0
- package/dist/commands/unused-deps/run.js.map +1 -0
- package/dist/index.d.ts +51 -5
- package/dist/index.js +33 -3
- package/dist/index.js.map +1 -1
- package/dist/utils/escape-regexp.d.ts +1 -0
- package/dist/utils/escape-regexp.js +4 -0
- package/dist/utils/escape-regexp.js.map +1 -0
- package/dist/utils/find-comments.d.ts +10 -0
- package/dist/utils/find-comments.js +62 -0
- package/dist/utils/find-comments.js.map +1 -0
- package/dist/utils/find-package-dir.d.ts +2 -0
- package/dist/utils/find-package-dir.js +15 -0
- package/dist/utils/find-package-dir.js.map +1 -0
- package/dist/utils/line-of.d.ts +2 -0
- package/dist/utils/line-of.js +12 -0
- package/dist/utils/line-of.js.map +1 -0
- package/dist/utils/load-tsconfig.d.ts +20 -0
- package/dist/utils/load-tsconfig.js +33 -0
- package/dist/utils/load-tsconfig.js.map +1 -0
- package/dist/utils/parse-module.d.ts +32 -0
- package/dist/utils/parse-module.js +178 -0
- package/dist/utils/parse-module.js.map +1 -0
- package/dist/utils/resolve-specifier.d.ts +15 -0
- package/dist/utils/resolve-specifier.js +63 -0
- package/dist/utils/resolve-specifier.js.map +1 -0
- package/dist/utils/walk.js +20 -1
- package/dist/utils/walk.js.map +1 -1
- package/dist/utils/workspace.d.ts +14 -0
- package/dist/utils/workspace.js +101 -0
- package/dist/utils/workspace.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { banner, cleanCelebration } from '../../format/vibes.js';
|
|
2
|
+
import { formatFindingsRows } from '../../format/findings-list.js';
|
|
3
|
+
export function renderOrphanTestsReport(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 orphan-tests: ${report.error}`);
|
|
9
|
+
return lines.join('\n');
|
|
10
|
+
}
|
|
11
|
+
const isClean = report.findings.length === 0;
|
|
12
|
+
if (options.quiet && isClean)
|
|
13
|
+
return '';
|
|
14
|
+
const lines = [];
|
|
15
|
+
if (!options.quiet && !options.plain)
|
|
16
|
+
lines.push(banner(), '');
|
|
17
|
+
lines.push(`Scanned ${report.filesScanned} file${report.filesScanned === 1 ? '' : 's'}.`, '');
|
|
18
|
+
if (isClean) {
|
|
19
|
+
lines.push(cleanCelebration(options));
|
|
20
|
+
return lines.join('\n');
|
|
21
|
+
}
|
|
22
|
+
lines.push(`${report.findings.length} orphan test${report.findings.length === 1 ? '' : 's'} found:`);
|
|
23
|
+
lines.push(...formatFindingsRows(report.findings.map((f) => ({
|
|
24
|
+
location: f.file,
|
|
25
|
+
name: 'orphan',
|
|
26
|
+
tag: `no matching source found (tried ${f.triedExtensions.join(', ')})`,
|
|
27
|
+
})), options));
|
|
28
|
+
return lines.join('\n');
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=report.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"report.js","sourceRoot":"","sources":["../../../src/commands/orphan-tests/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,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,YAAY,QAAQ,MAAM,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,GAAG,EAAE,EAAE,CAAC,CAAA;IAE7F,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,CACR,GAAG,MAAM,CAAC,QAAQ,CAAC,MAAM,eAAe,MAAM,CAAC,QAAQ,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,SAAS,CACzF,CAAA;IACD,KAAK,CAAC,IAAI,CACR,GAAG,kBAAkB,CACnB,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QAC1B,QAAQ,EAAE,CAAC,CAAC,IAAI;QAChB,IAAI,EAAE,QAAQ;QACd,GAAG,EAAE,mCAAmC,CAAC,CAAC,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG;KACxE,CAAC,CAAC,EACH,OAAO,CACR,CACF,CAAA;IAED,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;AACzB,CAAC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { type OrphanTestFinding } from './core.js';
|
|
2
|
+
export interface OrphanTestsRunOptions {
|
|
3
|
+
paths: string[];
|
|
4
|
+
cwd: string;
|
|
5
|
+
/** which files to consider as POSSIBLE test files while walking — default: .ts,.tsx,.js,.jsx,.mjs,.cjs */
|
|
6
|
+
extensions?: string[];
|
|
7
|
+
ignoreGlobs?: string[];
|
|
8
|
+
respectGitignore?: boolean;
|
|
9
|
+
/** suffix(es) that mark a file as a test file — default: ['.test', '.spec'] */
|
|
10
|
+
testSuffixes?: string[];
|
|
11
|
+
/** extensions tried for a matching source file — default: .ts,.tsx,.js,.jsx,.mjs,.cjs,.vue */
|
|
12
|
+
sourceExtensions?: string[];
|
|
13
|
+
/** mirrored layout: source root, relative to `cwd` — must be given together with `testDir` */
|
|
14
|
+
sourceDir?: string;
|
|
15
|
+
/** mirrored layout: test root, relative to `cwd` — must be given together with `sourceDir` */
|
|
16
|
+
testDir?: string;
|
|
17
|
+
}
|
|
18
|
+
export interface OrphanTestsReport {
|
|
19
|
+
filesScanned: number;
|
|
20
|
+
findings: OrphanTestFinding[];
|
|
21
|
+
/** --source-dir/--test-dir given without its pair — a different kind of problem than any single finding */
|
|
22
|
+
error: string | null;
|
|
23
|
+
exitCode: number;
|
|
24
|
+
}
|
|
25
|
+
export declare function runOrphanTests(options: OrphanTestsRunOptions): OrphanTestsReport;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { basename, dirname, join, relative, resolve } from 'node:path';
|
|
2
|
+
import { walk } from '../../utils/walk.js';
|
|
3
|
+
import { parseTestFile, hasMatchingSource, candidateSourceDirs, } from './core.js';
|
|
4
|
+
const DEFAULT_TEST_SUFFIXES = ['.test', '.spec'];
|
|
5
|
+
const DEFAULT_SOURCE_EXTENSIONS = ['.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs', '.vue'];
|
|
6
|
+
const DEFAULT_EXTENSIONS = ['.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs'];
|
|
7
|
+
export function runOrphanTests(options) {
|
|
8
|
+
const cwd = resolve(options.cwd);
|
|
9
|
+
const testSuffixes = options.testSuffixes ?? DEFAULT_TEST_SUFFIXES;
|
|
10
|
+
const sourceExtensions = options.sourceExtensions ?? DEFAULT_SOURCE_EXTENSIONS;
|
|
11
|
+
const extensions = options.extensions ?? DEFAULT_EXTENSIONS;
|
|
12
|
+
if (Boolean(options.sourceDir) !== Boolean(options.testDir)) {
|
|
13
|
+
return {
|
|
14
|
+
filesScanned: 0,
|
|
15
|
+
findings: [],
|
|
16
|
+
error: '--source-dir and --test-dir must be given together',
|
|
17
|
+
exitCode: 1,
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
const mirrored = Boolean(options.sourceDir && options.testDir);
|
|
21
|
+
const walkRoots = mirrored ? [options.testDir] : options.paths;
|
|
22
|
+
const testFiles = walk(walkRoots, {
|
|
23
|
+
cwd,
|
|
24
|
+
extensions,
|
|
25
|
+
ignoreGlobs: options.ignoreGlobs ?? [],
|
|
26
|
+
...(options.respectGitignore !== undefined
|
|
27
|
+
? { respectGitignore: options.respectGitignore }
|
|
28
|
+
: {}),
|
|
29
|
+
});
|
|
30
|
+
const testDirAbs = mirrored ? resolve(cwd, options.testDir) : null;
|
|
31
|
+
const sourceDirAbs = mirrored ? resolve(cwd, options.sourceDir) : null;
|
|
32
|
+
const findings = [];
|
|
33
|
+
for (const absFile of testFiles) {
|
|
34
|
+
const parsed = parseTestFile(basename(absFile), testSuffixes);
|
|
35
|
+
if (!parsed)
|
|
36
|
+
continue;
|
|
37
|
+
const found = mirrored && testDirAbs && sourceDirAbs
|
|
38
|
+
? hasMatchingSource(join(sourceDirAbs, relative(testDirAbs, dirname(absFile)), parsed.base), sourceExtensions)
|
|
39
|
+
: candidateSourceDirs(dirname(absFile)).some((dir) => hasMatchingSource(join(dir, parsed.base), sourceExtensions));
|
|
40
|
+
if (found)
|
|
41
|
+
continue;
|
|
42
|
+
findings.push({
|
|
43
|
+
file: relative(cwd, absFile).split('\\').join('/'),
|
|
44
|
+
triedExtensions: sourceExtensions,
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
findings.sort((a, b) => a.file.localeCompare(b.file));
|
|
48
|
+
return {
|
|
49
|
+
filesScanned: testFiles.length,
|
|
50
|
+
findings,
|
|
51
|
+
error: null,
|
|
52
|
+
exitCode: findings.length > 0 ? 1 : 0,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=run.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run.js","sourceRoot":"","sources":["../../../src/commands/orphan-tests/run.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACtE,OAAO,EAAE,IAAI,EAAE,MAAM,qBAAqB,CAAA;AAC1C,OAAO,EACL,aAAa,EACb,iBAAiB,EACjB,mBAAmB,GAEpB,MAAM,WAAW,CAAA;AAElB,MAAM,qBAAqB,GAAG,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;AAChD,MAAM,yBAAyB,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;AACxF,MAAM,kBAAkB,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;AA2BzE,MAAM,UAAU,cAAc,CAAC,OAA8B;IAC3D,MAAM,GAAG,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;IAChC,MAAM,YAAY,GAAG,OAAO,CAAC,YAAY,IAAI,qBAAqB,CAAA;IAClE,MAAM,gBAAgB,GAAG,OAAO,CAAC,gBAAgB,IAAI,yBAAyB,CAAA;IAC9E,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,IAAI,kBAAkB,CAAA;IAE3D,IAAI,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC,KAAK,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;QAC5D,OAAO;YACL,YAAY,EAAE,CAAC;YACf,QAAQ,EAAE,EAAE;YACZ,KAAK,EAAE,oDAAoD;YAC3D,QAAQ,EAAE,CAAC;SACZ,CAAA;IACH,CAAC;IAED,MAAM,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC,SAAS,IAAI,OAAO,CAAC,OAAO,CAAC,CAAA;IAC9D,MAAM,SAAS,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,OAAiB,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAA;IAExE,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,EAAE;QAChC,GAAG;QACH,UAAU;QACV,WAAW,EAAE,OAAO,CAAC,WAAW,IAAI,EAAE;QACtC,GAAG,CAAC,OAAO,CAAC,gBAAgB,KAAK,SAAS;YACxC,CAAC,CAAC,EAAE,gBAAgB,EAAE,OAAO,CAAC,gBAAgB,EAAE;YAChD,CAAC,CAAC,EAAE,CAAC;KACR,CAAC,CAAA;IAEF,MAAM,UAAU,GAAG,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE,OAAO,CAAC,OAAiB,CAAC,CAAC,CAAC,CAAC,IAAI,CAAA;IAC5E,MAAM,YAAY,GAAG,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE,OAAO,CAAC,SAAmB,CAAC,CAAC,CAAC,CAAC,IAAI,CAAA;IAEhF,MAAM,QAAQ,GAAwB,EAAE,CAAA;IACxC,KAAK,MAAM,OAAO,IAAI,SAAS,EAAE,CAAC;QAChC,MAAM,MAAM,GAAG,aAAa,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,YAAY,CAAC,CAAA;QAC7D,IAAI,CAAC,MAAM;YAAE,SAAQ;QAErB,MAAM,KAAK,GACT,QAAQ,IAAI,UAAU,IAAI,YAAY;YACpC,CAAC,CAAC,iBAAiB,CACf,IAAI,CAAC,YAAY,EAAE,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC,EAAE,MAAM,CAAC,IAAI,CAAC,EACvE,gBAAgB,CACjB;YACH,CAAC,CAAC,mBAAmB,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CACjD,iBAAiB,CAAC,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC,IAAI,CAAC,EAAE,gBAAgB,CAAC,CAC5D,CAAA;QAEP,IAAI,KAAK;YAAE,SAAQ;QACnB,QAAQ,CAAC,IAAI,CAAC;YACZ,IAAI,EAAE,QAAQ,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC;YAClD,eAAe,EAAE,gBAAgB;SAClC,CAAC,CAAA;IACJ,CAAC;IAED,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAA;IAErD,OAAO;QACL,YAAY,EAAE,SAAS,CAAC,MAAM;QAC9B,QAAQ;QACR,KAAK,EAAE,IAAI;QACX,QAAQ,EAAE,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;KACtC,CAAA;AACH,CAAC"}
|
|
@@ -1,15 +1,20 @@
|
|
|
1
1
|
import { readFileSync } from 'node:fs';
|
|
2
2
|
import { resolve } from 'node:path';
|
|
3
3
|
import { checkMarkdownFile } from './core.js';
|
|
4
|
-
import {
|
|
4
|
+
import { loadTsconfig } from '../../utils/load-tsconfig.js';
|
|
5
5
|
import { DEFAULT_LANGS } from './lang.js';
|
|
6
6
|
export function runReadmeCheck(options) {
|
|
7
7
|
const packageDir = resolve(options.dir);
|
|
8
8
|
const targetFiles = options.files && options.files.length > 0 ? options.files : ['README.md'];
|
|
9
9
|
const langs = options.langs && options.langs.length > 0 ? options.langs : DEFAULT_LANGS;
|
|
10
|
-
const loaded =
|
|
10
|
+
const loaded = loadTsconfig(packageDir, options.tsconfig);
|
|
11
11
|
if ('error' in loaded) {
|
|
12
|
-
return {
|
|
12
|
+
return {
|
|
13
|
+
fileResults: [],
|
|
14
|
+
findings: [],
|
|
15
|
+
error: `${loaded.error} — nothing to typecheck README code blocks against`,
|
|
16
|
+
exitCode: 1,
|
|
17
|
+
};
|
|
13
18
|
}
|
|
14
19
|
const fileResults = [];
|
|
15
20
|
for (const relFile of targetFiles) {
|
|
@@ -1 +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,
|
|
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,YAAY,EAAE,MAAM,8BAA8B,CAAA;AAC3D,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,YAAY,CAAC,UAAU,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAA;IACzD,IAAI,OAAO,IAAI,MAAM,EAAE,CAAC;QACtB,OAAO;YACL,WAAW,EAAE,EAAE;YACf,QAAQ,EAAE,EAAE;YACZ,KAAK,EAAE,GAAG,MAAM,CAAC,KAAK,oDAAoD;YAC1E,QAAQ,EAAE,CAAC;SACZ,CAAA;IACH,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,18 @@
|
|
|
1
|
+
export interface ScriptsCheckFinding {
|
|
2
|
+
kind: 'missing' | 'undocumented';
|
|
3
|
+
name: string;
|
|
4
|
+
file: string;
|
|
5
|
+
line: number;
|
|
6
|
+
}
|
|
7
|
+
export interface ScriptSource {
|
|
8
|
+
file: string;
|
|
9
|
+
text: string;
|
|
10
|
+
}
|
|
11
|
+
export declare function maskOutOfScopeCiSteps(text: string): string;
|
|
12
|
+
export declare function extractMentionedScripts(text: string): {
|
|
13
|
+
name: string;
|
|
14
|
+
index: number;
|
|
15
|
+
}[];
|
|
16
|
+
export declare function isScriptMentioned(name: string, text: string): boolean;
|
|
17
|
+
export declare function isReservedLifecycleScript(name: string, allNames: Set<string>): boolean;
|
|
18
|
+
export declare function analyzeScriptsCheck(scripts: Record<string, string>, packageJsonFile: string, packageJsonText: string, sources: ScriptSource[]): ScriptsCheckFinding[];
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { escapeRegExp } from '../../utils/escape-regexp.js';
|
|
2
|
+
import { lineOf } from '../../utils/line-of.js';
|
|
3
|
+
// npm's own no-`run`-needed aliases — the ONLY bare `npm <word>` forms
|
|
4
|
+
// that unambiguously mean "run this script". Any other bare word after
|
|
5
|
+
// `npm` is a real npm subcommand (`npm install`, `npm publish`, ...), not
|
|
6
|
+
// a script invocation.
|
|
7
|
+
const NPM_BARE_ALIASES = new Set(['test', 'start']);
|
|
8
|
+
// Forms unambiguous enough to EXTRACT an arbitrary script name from —
|
|
9
|
+
// each requires an explicit `run`/`run-script`, or is one of npm's own
|
|
10
|
+
// bare aliases above, so there's no risk of misreading an unrelated
|
|
11
|
+
// subcommand (`yarn add`, `pnpm install`) as a script name. Used for
|
|
12
|
+
// direction 1 (finding a mention of a script that doesn't exist).
|
|
13
|
+
const EXTRACT_PATTERNS = [
|
|
14
|
+
/\bnpm\s+run(?:-script)?\s+([\w:./@-]+)/g,
|
|
15
|
+
/\byarn\s+run\s+([\w:./@-]+)/g,
|
|
16
|
+
/\bpnpm\s+run\s+([\w:./@-]+)/g,
|
|
17
|
+
/\b(?:npm|yarn|pnpm)\s+(test|start)\b/g,
|
|
18
|
+
];
|
|
19
|
+
// A GitHub Actions step with its own `working-directory:` runs against a
|
|
20
|
+
// DIFFERENT package.json (a `demo/` subproject, a workspace package) —
|
|
21
|
+
// found for real while dogfooding against vue-virtual-scroller-kit: its
|
|
22
|
+
// `e2e` job runs `npm run test:e2e` under `working-directory: demo`,
|
|
23
|
+
// where that script genuinely exists in `demo/package.json`, but not in
|
|
24
|
+
// the root one this command checks. No real YAML parsing here (same
|
|
25
|
+
// text-scan-not-AST spirit as everything else in this command) — a step
|
|
26
|
+
// boundary is approximated as everything within a few lines of a
|
|
27
|
+
// `working-directory:` line, which is blanked out (same length, so line
|
|
28
|
+
// numbers for anything NOT blanked stay correct) before either direction
|
|
29
|
+
// gets to look at it.
|
|
30
|
+
const WORKING_DIRECTORY_RE = /^\s*working-directory\s*:/;
|
|
31
|
+
const STEP_SCOPE_WINDOW = 3;
|
|
32
|
+
export function maskOutOfScopeCiSteps(text) {
|
|
33
|
+
const lines = text.split('\n');
|
|
34
|
+
const hasWorkingDir = lines.map((l) => WORKING_DIRECTORY_RE.test(l));
|
|
35
|
+
if (!hasWorkingDir.some(Boolean))
|
|
36
|
+
return text;
|
|
37
|
+
return lines
|
|
38
|
+
.map((line, i) => {
|
|
39
|
+
const start = Math.max(0, i - STEP_SCOPE_WINDOW);
|
|
40
|
+
const end = Math.min(lines.length - 1, i + STEP_SCOPE_WINDOW);
|
|
41
|
+
for (let j = start; j <= end; j++) {
|
|
42
|
+
if (hasWorkingDir[j])
|
|
43
|
+
return line.replace(/./g, 'x');
|
|
44
|
+
}
|
|
45
|
+
return line;
|
|
46
|
+
})
|
|
47
|
+
.join('\n');
|
|
48
|
+
}
|
|
49
|
+
export function extractMentionedScripts(text) {
|
|
50
|
+
const mentions = [];
|
|
51
|
+
for (const pattern of EXTRACT_PATTERNS) {
|
|
52
|
+
pattern.lastIndex = 0;
|
|
53
|
+
let match;
|
|
54
|
+
while ((match = pattern.exec(text))) {
|
|
55
|
+
mentions.push({ name: match[1], index: match.index });
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return mentions;
|
|
59
|
+
}
|
|
60
|
+
// Direction 2 (is an ALREADY-KNOWN script name mentioned anywhere) can
|
|
61
|
+
// afford to also accept yarn/pnpm's own bare `<manager> <name>` form —
|
|
62
|
+
// real for those two package managers, unlike plain npm — since we're
|
|
63
|
+
// testing one specific known name, not extracting an arbitrary one out
|
|
64
|
+
// of free text, so a stray `yarn add`/`pnpm install` can't be misread as
|
|
65
|
+
// a match for a script actually named "add"/"install" (vanishingly rare
|
|
66
|
+
// in practice, and a false NEGATIVE here — reported as undocumented when
|
|
67
|
+
// it technically isn't — is the safe direction for a weak signal).
|
|
68
|
+
export function isScriptMentioned(name, text) {
|
|
69
|
+
const n = escapeRegExp(name);
|
|
70
|
+
const pattern = NPM_BARE_ALIASES.has(name)
|
|
71
|
+
? new RegExp(`\\b(?:npm|yarn|pnpm)(?:\\s+run(?:-script)?)?\\s+${n}\\b`)
|
|
72
|
+
: new RegExp(`\\b(?:npm\\s+run(?:-script)?|yarn(?:\\s+run)?|pnpm(?:\\s+run)?)\\s+${n}\\b`);
|
|
73
|
+
return pattern.test(text);
|
|
74
|
+
}
|
|
75
|
+
// npm auto-runs `pre<X>`/`post<X>` around either another declared script
|
|
76
|
+
// or one of npm's own built-in lifecycle events — documenting them "for
|
|
77
|
+
// a human" is pointless, npm calls them itself. `prepublishOnly`,
|
|
78
|
+
// `preinstall`, `postinstall`, `prepare` are npm's own implicit events
|
|
79
|
+
// (no peer script named `install`/`publishOnly` need exist for these to
|
|
80
|
+
// fire), so they're a fixed exemption; any other `pre*`/`post*` is only
|
|
81
|
+
// exempt when its own peer script is actually declared.
|
|
82
|
+
const RESERVED_LIFECYCLE_NAMES = new Set(['prepublishOnly', 'preinstall', 'postinstall', 'prepare']);
|
|
83
|
+
export function isReservedLifecycleScript(name, allNames) {
|
|
84
|
+
if (RESERVED_LIFECYCLE_NAMES.has(name))
|
|
85
|
+
return true;
|
|
86
|
+
const pre = /^pre(.+)/.exec(name);
|
|
87
|
+
if (pre && allNames.has(pre[1]))
|
|
88
|
+
return true;
|
|
89
|
+
const post = /^post(.+)/.exec(name);
|
|
90
|
+
if (post && allNames.has(post[1]))
|
|
91
|
+
return true;
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
export function analyzeScriptsCheck(scripts, packageJsonFile, packageJsonText, sources) {
|
|
95
|
+
const scriptNames = new Set(Object.keys(scripts));
|
|
96
|
+
const findings = [];
|
|
97
|
+
const reportedMissing = new Set();
|
|
98
|
+
for (const source of sources) {
|
|
99
|
+
for (const { name, index } of extractMentionedScripts(source.text)) {
|
|
100
|
+
if (scriptNames.has(name))
|
|
101
|
+
continue;
|
|
102
|
+
const key = `${source.file}:${name}`;
|
|
103
|
+
if (reportedMissing.has(key))
|
|
104
|
+
continue;
|
|
105
|
+
reportedMissing.add(key);
|
|
106
|
+
findings.push({ kind: 'missing', name, file: source.file, line: lineOf(source.text, index) });
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
for (const name of scriptNames) {
|
|
110
|
+
if (isReservedLifecycleScript(name, scriptNames))
|
|
111
|
+
continue;
|
|
112
|
+
const mentioned = sources.some((s) => isScriptMentioned(name, s.text));
|
|
113
|
+
if (mentioned)
|
|
114
|
+
continue;
|
|
115
|
+
const keyMatch = new RegExp(`"${escapeRegExp(name)}"\\s*:`).exec(packageJsonText);
|
|
116
|
+
const line = keyMatch ? lineOf(packageJsonText, keyMatch.index) : 1;
|
|
117
|
+
findings.push({ kind: 'undocumented', name, file: packageJsonFile, line });
|
|
118
|
+
}
|
|
119
|
+
return findings;
|
|
120
|
+
}
|
|
121
|
+
//# sourceMappingURL=core.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"core.js","sourceRoot":"","sources":["../../../src/commands/scripts-check/core.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { banner, cleanCelebration } from '../../format/vibes.js';
|
|
2
|
+
import { formatFindingsRows } from '../../format/findings-list.js';
|
|
3
|
+
const KIND_TAG = {
|
|
4
|
+
missing: 'mentioned here, but not in package.json scripts',
|
|
5
|
+
undocumented: 'in package.json scripts, but not mentioned anywhere checked',
|
|
6
|
+
};
|
|
7
|
+
export function renderScriptsCheckReport(report, options = {}) {
|
|
8
|
+
if (report.error) {
|
|
9
|
+
const lines = [];
|
|
10
|
+
if (!options.quiet && !options.plain)
|
|
11
|
+
lines.push(banner(), '');
|
|
12
|
+
lines.push(`Could not run scripts-check: ${report.error}`);
|
|
13
|
+
return lines.join('\n');
|
|
14
|
+
}
|
|
15
|
+
const isClean = report.findings.length === 0;
|
|
16
|
+
if (options.quiet && isClean)
|
|
17
|
+
return '';
|
|
18
|
+
const lines = [];
|
|
19
|
+
if (!options.quiet && !options.plain)
|
|
20
|
+
lines.push(banner(), '');
|
|
21
|
+
lines.push(`Checked ${report.sourcesScanned.length} source${report.sourcesScanned.length === 1 ? '' : 's'} against package.json's scripts.`, '');
|
|
22
|
+
if (isClean) {
|
|
23
|
+
lines.push(cleanCelebration(options));
|
|
24
|
+
return lines.join('\n');
|
|
25
|
+
}
|
|
26
|
+
lines.push(`${report.findings.length} problem${report.findings.length === 1 ? '' : 's'} found:`);
|
|
27
|
+
lines.push(...formatFindingsRows(report.findings.map((f) => ({
|
|
28
|
+
location: `${f.file}:${f.line}`,
|
|
29
|
+
name: f.name,
|
|
30
|
+
tag: KIND_TAG[f.kind] ?? f.kind,
|
|
31
|
+
})), options));
|
|
32
|
+
return lines.join('\n');
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=report.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"report.js","sourceRoot":"","sources":["../../../src/commands/scripts-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,QAAQ,GAA2B;IACvC,OAAO,EAAE,iDAAiD;IAC1D,YAAY,EAAE,6DAA6D;CAC5E,CAAA;AAED,MAAM,UAAU,wBAAwB,CACtC,MAA0B,EAC1B,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,CACR,WAAW,MAAM,CAAC,cAAc,CAAC,MAAM,UAAU,MAAM,CAAC,cAAc,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,kCAAkC,EAChI,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,EAAE;QAC/B,IAAI,EAAE,CAAC,CAAC,IAAI;QACZ,GAAG,EAAE,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI;KAChC,CAAC,CAAC,EACH,OAAO,CACR,CACF,CAAA;IAED,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;AACzB,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type ScriptsCheckFinding } from './core.js';
|
|
2
|
+
export interface ScriptsCheckRunOptions {
|
|
3
|
+
/** package directory to check — package.json, --file docs, and .github/workflows are all resolved from here */
|
|
4
|
+
dir: string;
|
|
5
|
+
/** markdown/doc files to check, relative to `dir` — default: ['README.md'] */
|
|
6
|
+
files?: string[];
|
|
7
|
+
}
|
|
8
|
+
export interface ScriptsCheckReport {
|
|
9
|
+
findings: ScriptsCheckFinding[];
|
|
10
|
+
sourcesScanned: string[];
|
|
11
|
+
/** package.json or an explicitly-requested doc file couldn't be read at all — a different kind of problem than any single finding */
|
|
12
|
+
error: string | null;
|
|
13
|
+
exitCode: number;
|
|
14
|
+
}
|
|
15
|
+
export declare function runScriptsCheck(options: ScriptsCheckRunOptions): ScriptsCheckReport;
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { readFileSync, readdirSync } from 'node:fs';
|
|
2
|
+
import { resolve, join } from 'node:path';
|
|
3
|
+
import { analyzeScriptsCheck, maskOutOfScopeCiSteps, } from './core.js';
|
|
4
|
+
function readWorkflowSources(packageDir) {
|
|
5
|
+
let entries;
|
|
6
|
+
try {
|
|
7
|
+
entries = readdirSync(join(packageDir, '.github', 'workflows'));
|
|
8
|
+
}
|
|
9
|
+
catch {
|
|
10
|
+
return [];
|
|
11
|
+
}
|
|
12
|
+
const sources = [];
|
|
13
|
+
for (const entry of entries) {
|
|
14
|
+
if (!/\.ya?ml$/.test(entry))
|
|
15
|
+
continue;
|
|
16
|
+
const relFile = `.github/workflows/${entry}`;
|
|
17
|
+
try {
|
|
18
|
+
const text = maskOutOfScopeCiSteps(readFileSync(join(packageDir, relFile), 'utf8'));
|
|
19
|
+
sources.push({ file: relFile, text });
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
// unreadable — skip rather than fail the whole command over one file
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return sources;
|
|
26
|
+
}
|
|
27
|
+
export function runScriptsCheck(options) {
|
|
28
|
+
const packageDir = resolve(options.dir);
|
|
29
|
+
let packageJsonText;
|
|
30
|
+
try {
|
|
31
|
+
packageJsonText = readFileSync(join(packageDir, 'package.json'), 'utf8');
|
|
32
|
+
}
|
|
33
|
+
catch (err) {
|
|
34
|
+
return {
|
|
35
|
+
findings: [],
|
|
36
|
+
sourcesScanned: [],
|
|
37
|
+
error: `could not read package.json: ${err instanceof Error ? err.message : String(err)}`,
|
|
38
|
+
exitCode: 1,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
let pkg;
|
|
42
|
+
try {
|
|
43
|
+
pkg = JSON.parse(packageJsonText);
|
|
44
|
+
}
|
|
45
|
+
catch (err) {
|
|
46
|
+
return {
|
|
47
|
+
findings: [],
|
|
48
|
+
sourcesScanned: [],
|
|
49
|
+
error: `could not parse package.json: ${err instanceof Error ? err.message : String(err)}`,
|
|
50
|
+
exitCode: 1,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
const scripts = pkg.scripts ?? {};
|
|
54
|
+
const docFiles = options.files && options.files.length > 0 ? options.files : ['README.md'];
|
|
55
|
+
const sources = [];
|
|
56
|
+
for (const relFile of docFiles) {
|
|
57
|
+
let text;
|
|
58
|
+
try {
|
|
59
|
+
text = readFileSync(join(packageDir, relFile), 'utf8');
|
|
60
|
+
}
|
|
61
|
+
catch (err) {
|
|
62
|
+
return {
|
|
63
|
+
findings: [],
|
|
64
|
+
sourcesScanned: [],
|
|
65
|
+
error: `could not read ${relFile}: ${err instanceof Error ? err.message : String(err)}`,
|
|
66
|
+
exitCode: 1,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
sources.push({ file: relFile, text });
|
|
70
|
+
}
|
|
71
|
+
sources.push(...readWorkflowSources(packageDir));
|
|
72
|
+
const findings = analyzeScriptsCheck(scripts, 'package.json', packageJsonText, sources);
|
|
73
|
+
findings.sort((a, b) => a.kind.localeCompare(b.kind) || a.name.localeCompare(b.name));
|
|
74
|
+
return {
|
|
75
|
+
findings,
|
|
76
|
+
sourcesScanned: sources.map((s) => s.file),
|
|
77
|
+
error: null,
|
|
78
|
+
exitCode: findings.length > 0 ? 1 : 0,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
//# sourceMappingURL=run.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run.js","sourceRoot":"","sources":["../../../src/commands/scripts-check/run.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import ts from 'typescript';
|
|
2
|
+
export declare function normalize(fileName: string): string;
|
|
3
|
+
/**
|
|
4
|
+
* One real `ts.createProgram` run over `rootNames`, with `overrides`
|
|
5
|
+
* (absolute path → replacement source text) substituted in for whichever
|
|
6
|
+
* of those files it names — every other file resolves against the real
|
|
7
|
+
* filesystem via `ts.createCompilerHost`. Returns every diagnostic
|
|
8
|
+
* grouped by file and 1-based line, which is all `run.ts` ever needs:
|
|
9
|
+
* whether SOME diagnostic landed on a specific line, not its message.
|
|
10
|
+
*/
|
|
11
|
+
export declare function runProjectTypecheck(rootNames: string[], options: ts.CompilerOptions, overrides?: Map<string, string>): Map<string, Set<number>>;
|
|
12
|
+
/**
|
|
13
|
+
* Typechecks a single piece of source IN ISOLATION, against the real
|
|
14
|
+
* package's real types on disk (relative/self-referencing imports both
|
|
15
|
+
* resolve normally) — same virtual-single-file technique as
|
|
16
|
+
* `readme-check`'s `typecheckBlock`, reused here for `.vue` files, which
|
|
17
|
+
* a plain `ts.Program` can't include in a whole-project run at all (no
|
|
18
|
+
* SFC support without pulling in `vue-tsc`'s own machinery). This misses
|
|
19
|
+
* diagnostics that would only appear from OTHER files' real usage of
|
|
20
|
+
* this one — an accepted, documented gap, same class as `readme-check`'s
|
|
21
|
+
* own isolated-block limitation.
|
|
22
|
+
*/
|
|
23
|
+
export declare function checkIsolatedFile(virtualFileName: string, code: string, baseOptions: ts.CompilerOptions): Set<number>;
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import ts from 'typescript';
|
|
2
|
+
// TypeScript normalizes every file path it hands to a `CompilerHost` to
|
|
3
|
+
// forward slashes internally, even on Windows — matching against a
|
|
4
|
+
// `path`-built key (backslashes on win32) would silently never hit these
|
|
5
|
+
// overrides. Same gotcha `readme-check`'s own `typecheck.ts` documents.
|
|
6
|
+
export function normalize(fileName) {
|
|
7
|
+
return fileName.replace(/\\/g, '/');
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* One real `ts.createProgram` run over `rootNames`, with `overrides`
|
|
11
|
+
* (absolute path → replacement source text) substituted in for whichever
|
|
12
|
+
* of those files it names — every other file resolves against the real
|
|
13
|
+
* filesystem via `ts.createCompilerHost`. Returns every diagnostic
|
|
14
|
+
* grouped by file and 1-based line, which is all `run.ts` ever needs:
|
|
15
|
+
* whether SOME diagnostic landed on a specific line, not its message.
|
|
16
|
+
*/
|
|
17
|
+
export function runProjectTypecheck(rootNames, options, overrides) {
|
|
18
|
+
const checkOptions = { ...options, noEmit: true, skipLibCheck: true };
|
|
19
|
+
const baseHost = ts.createCompilerHost(checkOptions);
|
|
20
|
+
const host = overrides
|
|
21
|
+
? {
|
|
22
|
+
...baseHost,
|
|
23
|
+
getSourceFile(fileName, languageVersionOrOptions, onError, shouldCreateNewSourceFile) {
|
|
24
|
+
const override = overrides.get(normalize(fileName));
|
|
25
|
+
if (override !== undefined) {
|
|
26
|
+
const version = typeof languageVersionOrOptions === 'object'
|
|
27
|
+
? languageVersionOrOptions.languageVersion
|
|
28
|
+
: languageVersionOrOptions;
|
|
29
|
+
return ts.createSourceFile(fileName, override, version, true);
|
|
30
|
+
}
|
|
31
|
+
return baseHost.getSourceFile(fileName, languageVersionOrOptions, onError, shouldCreateNewSourceFile);
|
|
32
|
+
},
|
|
33
|
+
readFile(fileName) {
|
|
34
|
+
const override = overrides.get(normalize(fileName));
|
|
35
|
+
if (override !== undefined)
|
|
36
|
+
return override;
|
|
37
|
+
return baseHost.readFile(fileName);
|
|
38
|
+
},
|
|
39
|
+
}
|
|
40
|
+
: baseHost;
|
|
41
|
+
const program = ts.createProgram({ rootNames, options: checkOptions, host });
|
|
42
|
+
const diagnostics = ts.getPreEmitDiagnostics(program);
|
|
43
|
+
const byFile = new Map();
|
|
44
|
+
for (const d of diagnostics) {
|
|
45
|
+
if (!d.file || d.start === undefined)
|
|
46
|
+
continue;
|
|
47
|
+
const line = d.file.getLineAndCharacterOfPosition(d.start).line + 1;
|
|
48
|
+
const key = normalize(d.file.fileName);
|
|
49
|
+
let lines = byFile.get(key);
|
|
50
|
+
if (!lines) {
|
|
51
|
+
lines = new Set();
|
|
52
|
+
byFile.set(key, lines);
|
|
53
|
+
}
|
|
54
|
+
lines.add(line);
|
|
55
|
+
}
|
|
56
|
+
return byFile;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Typechecks a single piece of source IN ISOLATION, against the real
|
|
60
|
+
* package's real types on disk (relative/self-referencing imports both
|
|
61
|
+
* resolve normally) — same virtual-single-file technique as
|
|
62
|
+
* `readme-check`'s `typecheckBlock`, reused here for `.vue` files, which
|
|
63
|
+
* a plain `ts.Program` can't include in a whole-project run at all (no
|
|
64
|
+
* SFC support without pulling in `vue-tsc`'s own machinery). This misses
|
|
65
|
+
* diagnostics that would only appear from OTHER files' real usage of
|
|
66
|
+
* this one — an accepted, documented gap, same class as `readme-check`'s
|
|
67
|
+
* own isolated-block limitation.
|
|
68
|
+
*/
|
|
69
|
+
export function checkIsolatedFile(virtualFileName, code, baseOptions) {
|
|
70
|
+
const options = { ...baseOptions, noEmit: true, skipLibCheck: true };
|
|
71
|
+
const baseHost = ts.createCompilerHost(options);
|
|
72
|
+
const virtualSourceFile = ts.createSourceFile(virtualFileName, code, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
|
|
73
|
+
const host = {
|
|
74
|
+
...baseHost,
|
|
75
|
+
getSourceFile(fileName, languageVersionOrOptions, onError, shouldCreateNewSourceFile) {
|
|
76
|
+
if (fileName === virtualFileName)
|
|
77
|
+
return virtualSourceFile;
|
|
78
|
+
return baseHost.getSourceFile(fileName, languageVersionOrOptions, onError, shouldCreateNewSourceFile);
|
|
79
|
+
},
|
|
80
|
+
fileExists(fileName) {
|
|
81
|
+
if (fileName === virtualFileName)
|
|
82
|
+
return true;
|
|
83
|
+
return baseHost.fileExists(fileName);
|
|
84
|
+
},
|
|
85
|
+
readFile(fileName) {
|
|
86
|
+
if (fileName === virtualFileName)
|
|
87
|
+
return code;
|
|
88
|
+
return baseHost.readFile(fileName);
|
|
89
|
+
},
|
|
90
|
+
writeFile() { },
|
|
91
|
+
};
|
|
92
|
+
const program = ts.createProgram({ rootNames: [virtualFileName], options, host });
|
|
93
|
+
const diagnostics = ts
|
|
94
|
+
.getPreEmitDiagnostics(program)
|
|
95
|
+
.filter((d) => d.file?.fileName === virtualFileName);
|
|
96
|
+
const lines = new Set();
|
|
97
|
+
for (const d of diagnostics) {
|
|
98
|
+
if (d.start === undefined || !d.file)
|
|
99
|
+
continue;
|
|
100
|
+
lines.add(d.file.getLineAndCharacterOfPosition(d.start).line + 1);
|
|
101
|
+
}
|
|
102
|
+
return lines;
|
|
103
|
+
}
|
|
104
|
+
//# sourceMappingURL=check.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"check.js","sourceRoot":"","sources":["../../../src/commands/stale-ts-ignore/check.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import ts from 'typescript';
|
|
2
|
+
import { type Range } from '../../utils/find-comments.js';
|
|
3
|
+
export interface TsIgnoreDirective {
|
|
4
|
+
/** 1-based line the `// @ts-ignore` comment itself sits on */
|
|
5
|
+
line: number;
|
|
6
|
+
/** 1-based line it's meant to suppress a diagnostic on — line + 1, unconditionally */
|
|
7
|
+
targetLine: number;
|
|
8
|
+
range: Range;
|
|
9
|
+
}
|
|
10
|
+
export declare function findTsIgnoreDirectives(text: string, sf: ts.SourceFile): TsIgnoreDirective[];
|
|
11
|
+
export declare function blankDirectives(text: string, directives: TsIgnoreDirective[]): string;
|