File size: 4,037 Bytes
6a2bc3b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
import fs from 'node:fs';
import path from 'node:path';
import ts from 'typescript';

const ROOT = path.resolve(import.meta.dirname, '..');
const PACKAGES = ['packages/agent-core-v2', 'packages/kap-server', 'packages/transcript'];
const DIRS = ['src', 'test', 'scripts'];

function collectLeaves(node, leaves, jsdocNodes) {
  if (ts.isJSDoc(node)) {
    jsdocNodes.push(node);
    return;
  }
  const children = node.getChildren();
  if (children.length === 0) {
    if (node.getWidth() > 0) leaves.push(node);
    return;
  }
  for (const c of children) collectLeaves(c, leaves, jsdocNodes);
}

function extractGapComments(gap, offset, out) {
  let i = 0;
  while (i < gap.length) {
    const ch = gap[i];
    if (' \t\n\r\f\v'.includes(ch)) {
      i++;
      continue;
    }
    if (ch === '/' && gap[i + 1] === '/') {
      let j = gap.indexOf('\n', i);
      if (j === -1) j = gap.length;
      out.push({ pos: offset + i, end: offset + j, jsdoc: false });
      i = j;
      continue;
    }
    if (ch === '/' && gap[i + 1] === '*') {
      const close = gap.indexOf('*/', i + 2);
      const j = close === -1 ? gap.length : close + 2;
      out.push({ pos: offset + i, end: offset + j, jsdoc: gap.startsWith('/**', i) });
      i = j;
      continue;
    }
    break;
  }
}

function checkFile(file) {
  const text = fs.readFileSync(file, 'utf8');
  const sf = ts.createSourceFile(file, text, ts.ScriptTarget.Latest, true);

  const leaves = [];
  const jsdocNodes = [];
  collectLeaves(sf, leaves, jsdocNodes);
  leaves.sort((a, b) => a.getStart(sf) - b.getStart(sf));

  const comments = [];
  let cursor = 0;
  if (text.startsWith('#!')) {
    const nl = text.indexOf('\n');
    cursor = nl === -1 ? text.length : nl + 1;
  }
  for (const leaf of leaves) {
    const s = leaf.getStart(sf);
    if (s > cursor) extractGapComments(text.slice(cursor, s), cursor, comments);
    cursor = Math.max(cursor, leaf.getEnd());
  }
  if (cursor < text.length) extractGapComments(text.slice(cursor), cursor, comments);
  for (const d of jsdocNodes) {
    comments.push({ pos: d.getStart(sf), end: d.getEnd(), jsdoc: true });
  }

  const seen = new Set();
  const violations = [];
  for (const c of comments) {
    const key = `${c.pos}:${c.end}`;
    if (seen.has(key)) continue;
    seen.add(key);
    const line = text.slice(0, c.pos).split('\n').length;
    const snippet = text.slice(c.pos, Math.min(c.end, c.pos + 60)).replaceAll(/\s+/g, ' ');
    if (/(?:oxlint|eslint)-disable/.test(snippet)) continue;
    const isDirective = /@ts-(expect-error|ignore|nocheck)|prettier-ignore|istanbul|c8 ignore/.test(
      snippet,
    );
    violations.push({ line, snippet, isDirective, jsdoc: c.jsdoc });
  }
  return violations;
}

const files = [];
for (const pkg of PACKAGES) {
  for (const dir of DIRS) {
    const root = path.join(ROOT, pkg, dir);
    if (!fs.existsSync(root)) continue;
    const stack = [root];
    while (stack.length > 0) {
      const d = stack.pop();
      for (const e of fs.readdirSync(d, { withFileTypes: true })) {
        const p = path.join(d, e.name);
        if (e.isDirectory()) {
          if (e.name !== 'node_modules') stack.push(p);
        } else if (/\.(ts|tsx|mts|mjs)$/.test(e.name)) {
          files.push(p);
        }
      }
    }
  }
}

let total = 0;
for (const f of files) {
  const violations = checkFile(f);
  for (const v of violations) {
    total++;
    const rel = path.relative(ROOT, f);
    if (v.isDirective) {
      console.error(`${rel}:${v.line}: tooling directives are not allowed — fix the underlying lint/type problem instead: ${v.snippet}`);
    } else if (v.jsdoc) {
      console.error(`${rel}:${v.line}: JSDoc is not allowed in this package: ${v.snippet}`);
    } else {
      console.error(`${rel}:${v.line}: comments are not allowed in this package: ${v.snippet}`);
    }
  }
}

if (total > 0) {
  console.error(`check-no-comments: ${total} violation(s) in ${PACKAGES.join(', ')}`);
  process.exit(1);
}
console.log(`check-no-comments: OK (${files.length} files)`);