@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,42 @@
|
|
|
1
|
+
import ts from 'typescript';
|
|
2
|
+
import { findCommentRanges } from '../../utils/find-comments.js';
|
|
3
|
+
// TypeScript's own real semantics require a `@ts-ignore` directive to sit
|
|
4
|
+
// on the line IMMEDIATELY above the one it protects — a blank line
|
|
5
|
+
// between them makes it a no-op. No special-casing that here: if the
|
|
6
|
+
// blank line breaks it, the real compiler already won't suppress
|
|
7
|
+
// anything there in the FIRST (as-is) pass, so comparing that against
|
|
8
|
+
// the SECOND (directive-blanked) pass naturally shows no new diagnostic
|
|
9
|
+
// either way — the two-pass delta in run.ts already gets this right
|
|
10
|
+
// without knowing about blank lines at all.
|
|
11
|
+
export function findTsIgnoreDirectives(text, sf) {
|
|
12
|
+
const directives = [];
|
|
13
|
+
for (const range of findCommentRanges(text, sf)) {
|
|
14
|
+
const [start, end] = range;
|
|
15
|
+
const commentText = text.slice(start, end);
|
|
16
|
+
if (!commentText.startsWith('//'))
|
|
17
|
+
continue;
|
|
18
|
+
const body = commentText.slice(2).trim();
|
|
19
|
+
if (!/^@ts-ignore\b/.test(body))
|
|
20
|
+
continue;
|
|
21
|
+
const line = sf.getLineAndCharacterOfPosition(start).line + 1;
|
|
22
|
+
directives.push({ line, targetLine: line + 1, range });
|
|
23
|
+
}
|
|
24
|
+
return directives;
|
|
25
|
+
}
|
|
26
|
+
// Replaces each directive's comment text with spaces of the SAME length
|
|
27
|
+
// (never touching newlines), same masking technique as
|
|
28
|
+
// `utils/find-comments.ts` — the line becomes blank/whitespace, so
|
|
29
|
+
// TypeScript no longer treats it as a suppressing directive at all, and
|
|
30
|
+
// every OTHER line in the file keeps its exact original position.
|
|
31
|
+
export function blankDirectives(text, directives) {
|
|
32
|
+
const chars = text.split('');
|
|
33
|
+
for (const { range } of directives) {
|
|
34
|
+
const [start, end] = range;
|
|
35
|
+
for (let i = start; i < end; i++) {
|
|
36
|
+
if (chars[i] !== '\n')
|
|
37
|
+
chars[i] = ' ';
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return chars.join('');
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=core.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"core.js","sourceRoot":"","sources":["../../../src/commands/stale-ts-ignore/core.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,YAAY,CAAA;AAC3B,OAAO,EAAE,iBAAiB,EAAc,MAAM,8BAA8B,CAAA;AAU5E,0EAA0E;AAC1E,mEAAmE;AACnE,qEAAqE;AACrE,iEAAiE;AACjE,sEAAsE;AACtE,wEAAwE;AACxE,oEAAoE;AACpE,4CAA4C;AAC5C,MAAM,UAAU,sBAAsB,CAAC,IAAY,EAAE,EAAiB;IACpE,MAAM,UAAU,GAAwB,EAAE,CAAA;IAC1C,KAAK,MAAM,KAAK,IAAI,iBAAiB,CAAC,IAAI,EAAE,EAAE,CAAC,EAAE,CAAC;QAChD,MAAM,CAAC,KAAK,EAAE,GAAG,CAAC,GAAG,KAAK,CAAA;QAC1B,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,GAAG,CAAC,CAAA;QAC1C,IAAI,CAAC,WAAW,CAAC,UAAU,CAAC,IAAI,CAAC;YAAE,SAAQ;QAC3C,MAAM,IAAI,GAAG,WAAW,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAA;QACxC,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC;YAAE,SAAQ;QACzC,MAAM,IAAI,GAAG,EAAE,CAAC,6BAA6B,CAAC,KAAK,CAAC,CAAC,IAAI,GAAG,CAAC,CAAA;QAC7D,UAAU,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,GAAG,CAAC,EAAE,KAAK,EAAE,CAAC,CAAA;IACxD,CAAC;IACD,OAAO,UAAU,CAAA;AACnB,CAAC;AAED,wEAAwE;AACxE,uDAAuD;AACvD,mEAAmE;AACnE,wEAAwE;AACxE,kEAAkE;AAClE,MAAM,UAAU,eAAe,CAAC,IAAY,EAAE,UAA+B;IAC3E,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAA;IAC5B,KAAK,MAAM,EAAE,KAAK,EAAE,IAAI,UAAU,EAAE,CAAC;QACnC,MAAM,CAAC,KAAK,EAAE,GAAG,CAAC,GAAG,KAAK,CAAA;QAC1B,KAAK,IAAI,CAAC,GAAG,KAAK,EAAE,CAAC,GAAG,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC;YACjC,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,IAAI;gBAAE,KAAK,CAAC,CAAC,CAAC,GAAG,GAAG,CAAA;QACvC,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;AACvB,CAAC"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { banner, cleanCelebration } from '../../format/vibes.js';
|
|
2
|
+
import { formatFindingsRows } from '../../format/findings-list.js';
|
|
3
|
+
export function renderStaleTsIgnoreReport(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 stale-ts-ignore: ${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(`Checked ${report.directivesChecked} @ts-ignore directive${report.directivesChecked === 1 ? '' : 's'}.`, '');
|
|
18
|
+
if (isClean) {
|
|
19
|
+
lines.push(cleanCelebration(options));
|
|
20
|
+
return lines.join('\n');
|
|
21
|
+
}
|
|
22
|
+
lines.push(`${report.findings.length} stale @ts-ignore${report.findings.length === 1 ? '' : 's'} found:`);
|
|
23
|
+
lines.push(...formatFindingsRows(report.findings.map((f) => ({
|
|
24
|
+
location: `${f.file}:${f.line}`,
|
|
25
|
+
name: '@ts-ignore',
|
|
26
|
+
tag: "doesn't suppress anything — the line below it typechecks cleanly without it",
|
|
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/stale-ts-ignore/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,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,kCAAkC,MAAM,CAAC,KAAK,EAAE,CAAC,CAAA;QAC5D,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,iBAAiB,wBAAwB,MAAM,CAAC,iBAAiB,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,GAAG,EACvG,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,CACR,GAAG,MAAM,CAAC,QAAQ,CAAC,MAAM,oBAAoB,MAAM,CAAC,QAAQ,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,SAAS,CAC9F,CAAA;IACD,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,YAAY;QAClB,GAAG,EAAE,6EAA6E;KACnF,CAAC,CAAC,EACH,OAAO,CACR,CACF,CAAA;IAED,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;AACzB,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export interface StaleTsIgnoreRunOptions {
|
|
2
|
+
/** package directory to check — where tsconfig.json is auto-detected from, and .vue files are walked under */
|
|
3
|
+
dir: string;
|
|
4
|
+
/** tsconfig.json path, relative to `dir` — auto-detected by default */
|
|
5
|
+
tsconfig?: string;
|
|
6
|
+
ignoreGlobs?: string[];
|
|
7
|
+
respectGitignore?: boolean;
|
|
8
|
+
/** called once, only when there's real typecheck work to do — a large project can take a while */
|
|
9
|
+
onProgress?: (message: string) => void;
|
|
10
|
+
}
|
|
11
|
+
export interface StaleTsIgnoreFinding {
|
|
12
|
+
file: string;
|
|
13
|
+
/** 1-based line the `// @ts-ignore` comment itself sits on */
|
|
14
|
+
line: number;
|
|
15
|
+
}
|
|
16
|
+
export interface StaleTsIgnoreReport {
|
|
17
|
+
directivesChecked: number;
|
|
18
|
+
findings: StaleTsIgnoreFinding[];
|
|
19
|
+
/** no tsconfig.json found, or it failed to parse — a different kind of problem than any single finding */
|
|
20
|
+
error: string | null;
|
|
21
|
+
exitCode: number;
|
|
22
|
+
}
|
|
23
|
+
export declare function runStaleTsIgnore(options: StaleTsIgnoreRunOptions): StaleTsIgnoreReport;
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import { dirname, join, relative, resolve } from 'node:path';
|
|
3
|
+
import ts from 'typescript';
|
|
4
|
+
import { loadTsconfig } from '../../utils/load-tsconfig.js';
|
|
5
|
+
import { walk } from '../../utils/walk.js';
|
|
6
|
+
import { findTsIgnoreDirectives, blankDirectives } from './core.js';
|
|
7
|
+
import { extractVueScript, findVueTsIgnoreDirectives } from './vue.js';
|
|
8
|
+
import { runProjectTypecheck, checkIsolatedFile, normalize } from './check.js';
|
|
9
|
+
function scriptKindFor(file) {
|
|
10
|
+
if (file.endsWith('.tsx'))
|
|
11
|
+
return ts.ScriptKind.TSX;
|
|
12
|
+
if (file.endsWith('.jsx'))
|
|
13
|
+
return ts.ScriptKind.JSX;
|
|
14
|
+
if (file.endsWith('.js') || file.endsWith('.mjs') || file.endsWith('.cjs'))
|
|
15
|
+
return ts.ScriptKind.JS;
|
|
16
|
+
return ts.ScriptKind.TS;
|
|
17
|
+
}
|
|
18
|
+
export function runStaleTsIgnore(options) {
|
|
19
|
+
const packageDir = resolve(options.dir);
|
|
20
|
+
const loaded = loadTsconfig(packageDir, options.tsconfig);
|
|
21
|
+
if ('error' in loaded) {
|
|
22
|
+
return { directivesChecked: 0, findings: [], error: loaded.error, exitCode: 1 };
|
|
23
|
+
}
|
|
24
|
+
const tsFiles = new Map();
|
|
25
|
+
for (const fileName of loaded.fileNames) {
|
|
26
|
+
let text;
|
|
27
|
+
try {
|
|
28
|
+
text = readFileSync(fileName, 'utf8');
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
33
|
+
const sf = ts.createSourceFile(fileName, text, ts.ScriptTarget.Latest, true, scriptKindFor(fileName));
|
|
34
|
+
const directives = findTsIgnoreDirectives(text, sf);
|
|
35
|
+
if (directives.length > 0)
|
|
36
|
+
tsFiles.set(normalize(fileName), { text, directives });
|
|
37
|
+
}
|
|
38
|
+
const vueFiles = new Map();
|
|
39
|
+
for (const absFile of walk(['.'], {
|
|
40
|
+
cwd: packageDir,
|
|
41
|
+
extensions: ['.vue'],
|
|
42
|
+
ignoreGlobs: options.ignoreGlobs ?? [],
|
|
43
|
+
...(options.respectGitignore !== undefined
|
|
44
|
+
? { respectGitignore: options.respectGitignore }
|
|
45
|
+
: {}),
|
|
46
|
+
})) {
|
|
47
|
+
const text = readFileSync(absFile, 'utf8');
|
|
48
|
+
const script = extractVueScript(text);
|
|
49
|
+
if (!script)
|
|
50
|
+
continue;
|
|
51
|
+
const directives = findVueTsIgnoreDirectives(script.body);
|
|
52
|
+
if (directives.length > 0) {
|
|
53
|
+
vueFiles.set(absFile, { body: script.body, lineOffset: script.lineOffset, directives });
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
const directivesChecked = [...tsFiles.values()].reduce((n, f) => n + f.directives.length, 0) +
|
|
57
|
+
[...vueFiles.values()].reduce((n, f) => n + f.directives.length, 0);
|
|
58
|
+
if (directivesChecked === 0) {
|
|
59
|
+
return { directivesChecked: 0, findings: [], error: null, exitCode: 0 };
|
|
60
|
+
}
|
|
61
|
+
options.onProgress?.('Running a project-wide typecheck, twice — this can take a while on a large project…');
|
|
62
|
+
const findings = [];
|
|
63
|
+
if (tsFiles.size > 0) {
|
|
64
|
+
const before = runProjectTypecheck(loaded.fileNames, loaded.options);
|
|
65
|
+
const overrides = new Map();
|
|
66
|
+
for (const [key, { text, directives }] of tsFiles) {
|
|
67
|
+
overrides.set(key, blankDirectives(text, directives));
|
|
68
|
+
}
|
|
69
|
+
const after = runProjectTypecheck(loaded.fileNames, loaded.options, overrides);
|
|
70
|
+
for (const [key, { directives }] of tsFiles) {
|
|
71
|
+
for (const d of directives) {
|
|
72
|
+
const wasAlreadyThere = before.get(key)?.has(d.targetLine) ?? false;
|
|
73
|
+
const appearedAfterBlanking = after.get(key)?.has(d.targetLine) ?? false;
|
|
74
|
+
if (appearedAfterBlanking && !wasAlreadyThere)
|
|
75
|
+
continue;
|
|
76
|
+
findings.push({ file: relative(packageDir, key).split('\\').join('/'), line: d.line });
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
for (const [absFile, { body, lineOffset, directives }] of vueFiles) {
|
|
81
|
+
const virtualFileName = normalize(join(dirname(absFile), '__devtoolz_stale_ts_ignore__.ts'));
|
|
82
|
+
const before = checkIsolatedFile(virtualFileName, body, loaded.options);
|
|
83
|
+
const after = checkIsolatedFile(virtualFileName, blankDirectives(body, directives), loaded.options);
|
|
84
|
+
for (const d of directives) {
|
|
85
|
+
if (after.has(d.targetLine) && !before.has(d.targetLine))
|
|
86
|
+
continue;
|
|
87
|
+
findings.push({
|
|
88
|
+
file: relative(packageDir, absFile).split('\\').join('/'),
|
|
89
|
+
line: d.line + lineOffset,
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
findings.sort((a, b) => a.file.localeCompare(b.file) || a.line - b.line);
|
|
94
|
+
return {
|
|
95
|
+
directivesChecked,
|
|
96
|
+
findings,
|
|
97
|
+
error: null,
|
|
98
|
+
exitCode: findings.length > 0 ? 1 : 0,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
//# sourceMappingURL=run.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run.js","sourceRoot":"","sources":["../../../src/commands/stale-ts-ignore/run.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type TsIgnoreDirective } from './core.js';
|
|
2
|
+
export interface VueScript {
|
|
3
|
+
/** the <script> block's own text, starting right after the opening tag */
|
|
4
|
+
body: string;
|
|
5
|
+
/** how many lines come before the block in the real .vue file */
|
|
6
|
+
lineOffset: number;
|
|
7
|
+
}
|
|
8
|
+
export declare function extractVueScript(text: string): VueScript | null;
|
|
9
|
+
export declare function findVueTsIgnoreDirectives(body: string): TsIgnoreDirective[];
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import ts from 'typescript';
|
|
2
|
+
import { findTsIgnoreDirectives } from './core.js';
|
|
3
|
+
const SCRIPT_RE = /(<script[^>]*>)([\s\S]*?)(<\/script>)/;
|
|
4
|
+
export function extractVueScript(text) {
|
|
5
|
+
const match = text.match(SCRIPT_RE);
|
|
6
|
+
if (!match)
|
|
7
|
+
return null;
|
|
8
|
+
const [, , body] = match;
|
|
9
|
+
const lineOffset = (text.slice(0, match.index).match(/\n/g) ?? []).length;
|
|
10
|
+
return { body, lineOffset };
|
|
11
|
+
}
|
|
12
|
+
export function findVueTsIgnoreDirectives(body) {
|
|
13
|
+
const sf = ts.createSourceFile('f.ts', body, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
|
|
14
|
+
return findTsIgnoreDirectives(body, sf);
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=vue.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vue.js","sourceRoot":"","sources":["../../../src/commands/stale-ts-ignore/vue.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,YAAY,CAAA;AAC3B,OAAO,EAAE,sBAAsB,EAA0B,MAAM,WAAW,CAAA;AAE1E,MAAM,SAAS,GAAG,uCAAuC,CAAA;AASzD,MAAM,UAAU,gBAAgB,CAAC,IAAY;IAC3C,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAA;IACnC,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAA;IACvB,MAAM,CAAC,EAAE,AAAD,EAAG,IAAI,CAAC,GAAG,KAAyC,CAAA;IAC5D,MAAM,UAAU,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAA;IACzE,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,CAAA;AAC7B,CAAC;AAED,MAAM,UAAU,yBAAyB,CAAC,IAAY;IACpD,MAAM,EAAE,GAAG,EAAE,CAAC,gBAAgB,CAAC,MAAM,EAAE,IAAI,EAAE,EAAE,CAAC,YAAY,CAAC,MAAM,EAAE,IAAI,EAAE,EAAE,CAAC,UAAU,CAAC,EAAE,CAAC,CAAA;IAC5F,OAAO,sBAAsB,CAAC,IAAI,EAAE,EAAE,CAAC,CAAA;AACzC,CAAC"}
|
|
@@ -15,59 +15,7 @@
|
|
|
15
15
|
// everything outside a comment is byte-identical to the input.
|
|
16
16
|
import ts from 'typescript';
|
|
17
17
|
import { applyDeletions, collapseBlankLineRuns } from './apply-deletions.js';
|
|
18
|
-
|
|
19
|
-
ts.SyntaxKind.StringLiteral,
|
|
20
|
-
ts.SyntaxKind.NoSubstitutionTemplateLiteral,
|
|
21
|
-
ts.SyntaxKind.TemplateHead,
|
|
22
|
-
ts.SyntaxKind.TemplateMiddle,
|
|
23
|
-
ts.SyntaxKind.TemplateTail,
|
|
24
|
-
ts.SyntaxKind.RegularExpressionLiteral,
|
|
25
|
-
ts.SyntaxKind.JsxText,
|
|
26
|
-
]);
|
|
27
|
-
function findLiteralRanges(sf) {
|
|
28
|
-
const ranges = [];
|
|
29
|
-
function visit(node) {
|
|
30
|
-
if (LITERAL_KINDS.has(node.kind)) {
|
|
31
|
-
ranges.push([node.getStart(sf), node.getEnd()]);
|
|
32
|
-
}
|
|
33
|
-
ts.forEachChild(node, visit);
|
|
34
|
-
}
|
|
35
|
-
visit(sf);
|
|
36
|
-
return ranges;
|
|
37
|
-
}
|
|
38
|
-
function maskLiterals(text, ranges) {
|
|
39
|
-
// `ts.SourceFile` positions are UTF-16 CODE UNIT offsets (same as
|
|
40
|
-
// `string.length`/`charAt`) — `[...text]`/`Array.from(text)` iterate by
|
|
41
|
-
// CODE POINT instead, collapsing any surrogate pair (an emoji, or any
|
|
42
|
-
// character outside the BMP) into a single array element. The moment a
|
|
43
|
-
// literal masked earlier in the file contains one, every position after
|
|
44
|
-
// it drifts by one (or more) index, and later deletions land on the
|
|
45
|
-
// wrong bytes entirely. `split('')` is UTF-16-code-unit based — it
|
|
46
|
-
// splits a surrogate pair into its two halves instead of merging them,
|
|
47
|
-
// which is exactly what keeps this array's indices aligned with the
|
|
48
|
-
// parser's.
|
|
49
|
-
const chars = text.split('');
|
|
50
|
-
for (const [s, e] of ranges) {
|
|
51
|
-
for (let i = s; i < e; i++) {
|
|
52
|
-
if (chars[i] !== '\n')
|
|
53
|
-
chars[i] = 'x';
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
return chars.join('');
|
|
57
|
-
}
|
|
58
|
-
function findCommentRanges(maskedText) {
|
|
59
|
-
const ranges = [];
|
|
60
|
-
const scanner = ts.createScanner(ts.ScriptTarget.Latest, false, ts.LanguageVariant.Standard, maskedText);
|
|
61
|
-
let tok = scanner.scan();
|
|
62
|
-
while (tok !== ts.SyntaxKind.EndOfFileToken) {
|
|
63
|
-
if (tok === ts.SyntaxKind.SingleLineCommentTrivia ||
|
|
64
|
-
tok === ts.SyntaxKind.MultiLineCommentTrivia) {
|
|
65
|
-
ranges.push([scanner.getTokenStart(), scanner.getTokenEnd()]);
|
|
66
|
-
}
|
|
67
|
-
tok = scanner.scan();
|
|
68
|
-
}
|
|
69
|
-
return ranges;
|
|
70
|
-
}
|
|
18
|
+
import { findCommentRanges } from '../../utils/find-comments.js';
|
|
71
19
|
// Every `/** ... */` immediately leading an `export`-modified declaration
|
|
72
20
|
// — these are what `keepJsdoc` protects from deletion. Only JSDoc-style
|
|
73
21
|
// (`/**`, not a plain `/*`) counts, matching the convention IDEs actually
|
|
@@ -94,9 +42,7 @@ function findExportedJsdocRanges(sf, text) {
|
|
|
94
42
|
export function stripComments(text, options = {}) {
|
|
95
43
|
const scriptKind = options.scriptKind ?? ts.ScriptKind.TS;
|
|
96
44
|
const sf = ts.createSourceFile('f.ts', text, ts.ScriptTarget.Latest, true, scriptKind);
|
|
97
|
-
|
|
98
|
-
const masked = maskLiterals(text, literalRanges);
|
|
99
|
-
let comments = findCommentRanges(masked);
|
|
45
|
+
let comments = findCommentRanges(text, sf);
|
|
100
46
|
if (comments.length === 0)
|
|
101
47
|
return { text, changed: false, count: 0 };
|
|
102
48
|
if (options.keepJsdoc) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"core.js","sourceRoot":"","sources":["../../../src/commands/strip-comments/core.ts"],"names":[],"mappings":"AAAA,wEAAwE;AACxE,oEAAoE;AACpE,0EAA0E;AAC1E,iEAAiE;AACjE,EAAE;AACF,yEAAyE;AACzE,wEAAwE;AACxE,0EAA0E;AAC1E,0EAA0E;AAC1E,wEAAwE;AACxE,wEAAwE;AACxE,mEAAmE;AACnE,0EAA0E;AAC1E,oEAAoE;AACpE,+DAA+D;AAE/D,OAAO,EAAE,MAAM,YAAY,CAAA;AAC3B,OAAO,EAAE,cAAc,EAAE,qBAAqB,EAAc,MAAM,sBAAsB,CAAA;
|
|
1
|
+
{"version":3,"file":"core.js","sourceRoot":"","sources":["../../../src/commands/strip-comments/core.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export interface TodoFinding {
|
|
2
|
+
line: number;
|
|
3
|
+
column: number;
|
|
4
|
+
tag: string;
|
|
5
|
+
text: string;
|
|
6
|
+
}
|
|
7
|
+
export declare const DEFAULT_TAGS: string[];
|
|
8
|
+
export declare function buildTagMatcher(tags: string[]): {
|
|
9
|
+
pattern: RegExp;
|
|
10
|
+
canonical: Map<string, string>;
|
|
11
|
+
};
|
|
12
|
+
export declare function collectNoteText(rawLines: string[], startIndex: number, clean: (line: string) => string, pattern: RegExp): {
|
|
13
|
+
text: string;
|
|
14
|
+
consumedThrough: number;
|
|
15
|
+
};
|
|
16
|
+
export declare function analyzeTodoReport(text: string, file: string, tags?: string[]): TodoFinding[];
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import ts from 'typescript';
|
|
2
|
+
import { findCommentRanges } from '../../utils/find-comments.js';
|
|
3
|
+
import { escapeRegExp } from '../../utils/escape-regexp.js';
|
|
4
|
+
export const DEFAULT_TAGS = ['TODO', 'FIXME', 'HACK'];
|
|
5
|
+
function scriptKindFor(file) {
|
|
6
|
+
return file.endsWith('.tsx')
|
|
7
|
+
? ts.ScriptKind.TSX
|
|
8
|
+
: file.endsWith('.jsx')
|
|
9
|
+
? ts.ScriptKind.JSX
|
|
10
|
+
: ts.ScriptKind.TS;
|
|
11
|
+
}
|
|
12
|
+
// Matches a configured tag as its own word anywhere in a comment line
|
|
13
|
+
// (`// see TODO above` counts, same as most editors' TODO highlighters) —
|
|
14
|
+
// case-insensitively, but the reported `tag` is always the CONFIGURED
|
|
15
|
+
// spelling/casing, not whatever casing happened to appear in the source.
|
|
16
|
+
export function buildTagMatcher(tags) {
|
|
17
|
+
const canonical = new Map();
|
|
18
|
+
for (const tag of tags)
|
|
19
|
+
canonical.set(tag.toLowerCase(), tag);
|
|
20
|
+
const alternation = tags.map(escapeRegExp).join('|');
|
|
21
|
+
return { pattern: new RegExp(`\\b(?:${alternation})\\b`, 'i'), canonical };
|
|
22
|
+
}
|
|
23
|
+
// Strips the comment syntax itself (`//`, `/*`, trailing `*/`, a leading
|
|
24
|
+
// `*` on a block comment's continuation lines) off one line of a comment,
|
|
25
|
+
// so the reported text is just what a human wrote.
|
|
26
|
+
function cleanCommentLine(line) {
|
|
27
|
+
return line
|
|
28
|
+
.replace(/\*\/\s*$/, '')
|
|
29
|
+
.replace(/^\s*\/\*+/, '')
|
|
30
|
+
.replace(/^\s*\/\/+/, '')
|
|
31
|
+
.replace(/^\s*\*/, '')
|
|
32
|
+
.trim();
|
|
33
|
+
}
|
|
34
|
+
// A note is often wrapped across several lines — a run of `//` comments,
|
|
35
|
+
// or one line of a `/* */` block — and only the FIRST line contains the
|
|
36
|
+
// tag. Reporting just that line cuts the sentence off mid-thought (found
|
|
37
|
+
// via real dogfooding against npm-center's own data files: a genuine
|
|
38
|
+
// wrapped TODO came back truncated at "see design.md ..." with no
|
|
39
|
+
// continuation). This keeps absorbing subsequent lines of the same
|
|
40
|
+
// comment as long as they're non-empty and don't themselves start a NEW
|
|
41
|
+
// tagged note, joining them into one readable line.
|
|
42
|
+
export function collectNoteText(rawLines, startIndex, clean, pattern) {
|
|
43
|
+
const parts = [clean(rawLines[startIndex])];
|
|
44
|
+
let j = startIndex + 1;
|
|
45
|
+
while (j < rawLines.length) {
|
|
46
|
+
const line = rawLines[j];
|
|
47
|
+
const cleaned = clean(line);
|
|
48
|
+
if (!cleaned || pattern.test(line))
|
|
49
|
+
break;
|
|
50
|
+
parts.push(cleaned);
|
|
51
|
+
j++;
|
|
52
|
+
}
|
|
53
|
+
return { text: parts.join(' '), consumedThrough: j };
|
|
54
|
+
}
|
|
55
|
+
// `findCommentRanges` gives each `//` line its own separate range (that's
|
|
56
|
+
// how the TS scanner tokenizes them) — merges adjacent ones (nothing but
|
|
57
|
+
// indentation and a single newline between them) into one logical block,
|
|
58
|
+
// the same unit a `/* */` comment already is, so a wrapped `//` note can
|
|
59
|
+
// be collected the same way.
|
|
60
|
+
function mergeAdjacentLineComments(text, ranges) {
|
|
61
|
+
const merged = [];
|
|
62
|
+
for (const range of ranges) {
|
|
63
|
+
const [s, e] = range;
|
|
64
|
+
const last = merged[merged.length - 1];
|
|
65
|
+
const isLineComment = text[s] === '/' && text[s + 1] === '/';
|
|
66
|
+
const lastIsLineComment = last && text[last[0]] === '/' && text[last[0] + 1] === '/';
|
|
67
|
+
if (last &&
|
|
68
|
+
isLineComment &&
|
|
69
|
+
lastIsLineComment &&
|
|
70
|
+
/^[ \t]*\n[ \t]*$/.test(text.slice(last[1], s))) {
|
|
71
|
+
last[1] = e;
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
merged.push([s, e]);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return merged;
|
|
78
|
+
}
|
|
79
|
+
export function analyzeTodoReport(text, file, tags = DEFAULT_TAGS) {
|
|
80
|
+
const sf = ts.createSourceFile(file, text, ts.ScriptTarget.Latest, true, scriptKindFor(file));
|
|
81
|
+
const { pattern, canonical } = buildTagMatcher(tags);
|
|
82
|
+
const findings = [];
|
|
83
|
+
const ranges = mergeAdjacentLineComments(text, findCommentRanges(text, sf));
|
|
84
|
+
for (const [start, end] of ranges) {
|
|
85
|
+
const commentText = text.slice(start, end);
|
|
86
|
+
const rawLines = commentText.split('\n');
|
|
87
|
+
let lineStart = start;
|
|
88
|
+
let i = 0;
|
|
89
|
+
while (i < rawLines.length) {
|
|
90
|
+
const rawLine = rawLines[i];
|
|
91
|
+
const match = pattern.exec(rawLine);
|
|
92
|
+
if (match) {
|
|
93
|
+
const pos = sf.getLineAndCharacterOfPosition(lineStart + match.index);
|
|
94
|
+
const { text: noteText, consumedThrough } = collectNoteText(rawLines, i, cleanCommentLine, pattern);
|
|
95
|
+
findings.push({
|
|
96
|
+
line: pos.line + 1,
|
|
97
|
+
column: pos.character + 1,
|
|
98
|
+
tag: canonical.get(match[0].toLowerCase()) ?? match[0],
|
|
99
|
+
text: noteText,
|
|
100
|
+
});
|
|
101
|
+
for (let k = i; k < consumedThrough; k++)
|
|
102
|
+
lineStart += rawLines[k].length + 1;
|
|
103
|
+
i = consumedThrough;
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
lineStart += rawLine.length + 1;
|
|
107
|
+
i++;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return findings;
|
|
111
|
+
}
|
|
112
|
+
//# sourceMappingURL=core.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"core.js","sourceRoot":"","sources":["../../../src/commands/todo-report/core.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { banner, cleanCelebration } from '../../format/vibes.js';
|
|
2
|
+
import { formatFindingsRows } from '../../format/findings-list.js';
|
|
3
|
+
export function renderTodoReportReport(report, options = {}) {
|
|
4
|
+
const isClean = report.findings.length === 0;
|
|
5
|
+
if (options.quiet && isClean)
|
|
6
|
+
return '';
|
|
7
|
+
const lines = [];
|
|
8
|
+
if (!options.quiet && !options.plain)
|
|
9
|
+
lines.push(banner(), '');
|
|
10
|
+
lines.push(`Scanned ${report.filesScanned} file${report.filesScanned === 1 ? '' : 's'}.`, '');
|
|
11
|
+
if (isClean) {
|
|
12
|
+
lines.push(cleanCelebration(options));
|
|
13
|
+
return lines.join('\n');
|
|
14
|
+
}
|
|
15
|
+
const tagSummary = Object.entries(report.countsByTag)
|
|
16
|
+
.map(([tag, count]) => `${tag}: ${count}`)
|
|
17
|
+
.join(', ');
|
|
18
|
+
lines.push(`${report.findings.length} comment${report.findings.length === 1 ? '' : 's'} found (${tagSummary}):`);
|
|
19
|
+
lines.push(...formatFindingsRows(report.findings.map((f) => ({
|
|
20
|
+
location: `${f.file}:${f.line}:${f.column}`,
|
|
21
|
+
name: f.tag,
|
|
22
|
+
tag: f.text,
|
|
23
|
+
})), options));
|
|
24
|
+
if (report.max > 0) {
|
|
25
|
+
lines.push('', report.exitCode === 0
|
|
26
|
+
? `Within the configured limit (--max ${report.max}).`
|
|
27
|
+
: `Over the configured limit (--max ${report.max}).`);
|
|
28
|
+
}
|
|
29
|
+
return lines.join('\n');
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=report.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"report.js","sourceRoot":"","sources":["../../../src/commands/todo-report/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,sBAAsB,CACpC,MAAwB,EACxB,UAAwB,EAAE;IAE1B,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,MAAM,UAAU,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,WAAW,CAAC;SAClD,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,KAAK,KAAK,EAAE,CAAC;SACzC,IAAI,CAAC,IAAI,CAAC,CAAA;IACb,KAAK,CAAC,IAAI,CACR,GAAG,MAAM,CAAC,QAAQ,CAAC,MAAM,WAAW,MAAM,CAAC,QAAQ,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,WAAW,UAAU,IAAI,CACrG,CAAA;IACD,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,GAAG;QACX,GAAG,EAAE,CAAC,CAAC,IAAI;KACZ,CAAC,CAAC,EACH,OAAO,CACR,CACF,CAAA;IAED,IAAI,MAAM,CAAC,GAAG,GAAG,CAAC,EAAE,CAAC;QACnB,KAAK,CAAC,IAAI,CACR,EAAE,EACF,MAAM,CAAC,QAAQ,KAAK,CAAC;YACnB,CAAC,CAAC,sCAAsC,MAAM,CAAC,GAAG,IAAI;YACtD,CAAC,CAAC,oCAAoC,MAAM,CAAC,GAAG,IAAI,CACvD,CAAA;IACH,CAAC;IAED,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;AACzB,CAAC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { type TodoFinding } from './core.js';
|
|
2
|
+
export interface TodoReportRunOptions {
|
|
3
|
+
paths: string[];
|
|
4
|
+
cwd: string;
|
|
5
|
+
extensions?: string[];
|
|
6
|
+
ignoreGlobs?: string[];
|
|
7
|
+
respectGitignore?: boolean;
|
|
8
|
+
tags?: string[];
|
|
9
|
+
/** don't fail (exit 1) unless findings exceed this count — a ratchet for projects living with existing debt */
|
|
10
|
+
max?: number;
|
|
11
|
+
}
|
|
12
|
+
export interface TodoFileFinding extends TodoFinding {
|
|
13
|
+
file: string;
|
|
14
|
+
}
|
|
15
|
+
export interface TodoReportReport {
|
|
16
|
+
filesScanned: number;
|
|
17
|
+
findings: TodoFileFinding[];
|
|
18
|
+
countsByTag: Record<string, number>;
|
|
19
|
+
max: number;
|
|
20
|
+
exitCode: number;
|
|
21
|
+
}
|
|
22
|
+
export declare function runTodoReport(options: TodoReportRunOptions): TodoReportReport;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import { relative } from 'node:path';
|
|
3
|
+
import { walk } from '../../utils/walk.js';
|
|
4
|
+
import { analyzeTodoReport, DEFAULT_TAGS } from './core.js';
|
|
5
|
+
import { analyzeTodoReportVue } from './vue.js';
|
|
6
|
+
const DEFAULT_EXTENSIONS = ['.ts', '.tsx', '.js', '.jsx', '.cjs', '.mjs', '.vue'];
|
|
7
|
+
export function runTodoReport(options) {
|
|
8
|
+
const extensions = options.extensions ?? DEFAULT_EXTENSIONS;
|
|
9
|
+
const tags = options.tags ?? DEFAULT_TAGS;
|
|
10
|
+
const max = options.max ?? 0;
|
|
11
|
+
const files = walk(options.paths, {
|
|
12
|
+
cwd: options.cwd,
|
|
13
|
+
extensions,
|
|
14
|
+
ignoreGlobs: options.ignoreGlobs ?? [],
|
|
15
|
+
...(options.respectGitignore !== undefined
|
|
16
|
+
? { respectGitignore: options.respectGitignore }
|
|
17
|
+
: {}),
|
|
18
|
+
});
|
|
19
|
+
const findings = [];
|
|
20
|
+
for (const file of files) {
|
|
21
|
+
const text = readFileSync(file, 'utf8');
|
|
22
|
+
const fileFindings = file.endsWith('.vue')
|
|
23
|
+
? analyzeTodoReportVue(text, file, tags)
|
|
24
|
+
: analyzeTodoReport(text, file, tags);
|
|
25
|
+
const relFile = relative(options.cwd, file).split('\\').join('/');
|
|
26
|
+
for (const f of fileFindings)
|
|
27
|
+
findings.push({ ...f, file: relFile });
|
|
28
|
+
}
|
|
29
|
+
findings.sort((a, b) => a.file.localeCompare(b.file) || a.line - b.line);
|
|
30
|
+
const countsByTag = {};
|
|
31
|
+
for (const f of findings)
|
|
32
|
+
countsByTag[f.tag] = (countsByTag[f.tag] ?? 0) + 1;
|
|
33
|
+
return {
|
|
34
|
+
filesScanned: files.length,
|
|
35
|
+
findings,
|
|
36
|
+
countsByTag,
|
|
37
|
+
max,
|
|
38
|
+
exitCode: findings.length > max ? 1 : 0,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=run.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run.js","sourceRoot":"","sources":["../../../src/commands/todo-report/run.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AACtC,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAA;AACpC,OAAO,EAAE,IAAI,EAAE,MAAM,qBAAqB,CAAA;AAC1C,OAAO,EAAE,iBAAiB,EAAE,YAAY,EAAoB,MAAM,WAAW,CAAA;AAC7E,OAAO,EAAE,oBAAoB,EAAE,MAAM,UAAU,CAAA;AAE/C,MAAM,kBAAkB,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;AAyBjF,MAAM,UAAU,aAAa,CAAC,OAA6B;IACzD,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,IAAI,kBAAkB,CAAA;IAC3D,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,IAAI,YAAY,CAAA;IACzC,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,CAAC,CAAA;IAC5B,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE;QAChC,GAAG,EAAE,OAAO,CAAC,GAAG;QAChB,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,QAAQ,GAAsB,EAAE,CAAA;IACtC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,IAAI,GAAG,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAA;QACvC,MAAM,YAAY,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC;YACxC,CAAC,CAAC,oBAAoB,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC;YACxC,CAAC,CAAC,iBAAiB,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,CAAA;QACvC,MAAM,OAAO,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;QACjE,KAAK,MAAM,CAAC,IAAI,YAAY;YAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,GAAG,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAA;IACtE,CAAC;IAED,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,CAAA;IAExE,MAAM,WAAW,GAA2B,EAAE,CAAA;IAC9C,KAAK,MAAM,CAAC,IAAI,QAAQ;QAAE,WAAW,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAA;IAE5E,OAAO;QACL,YAAY,EAAE,KAAK,CAAC,MAAM;QAC1B,QAAQ;QACR,WAAW;QACX,GAAG;QACH,QAAQ,EAAE,QAAQ,CAAC,MAAM,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;KACxC,CAAA;AACH,CAAC"}
|