diff options
| author | Fuwn <[email protected]> | 2026-02-03 06:12:59 -0800 |
|---|---|---|
| committer | Fuwn <[email protected]> | 2026-02-03 19:34:51 -0800 |
| commit | b40ce35db997942f44ef0af184b94bcfe80d070d (patch) | |
| tree | f6504cf7c4f30cafd26fe6b76192d6c3ecb90e9b | |
| parent | test(iku): Add comprehensive checker tests (diff) | |
| download | archived-imemio-b40ce35db997942f44ef0af184b94bcfe80d070d.tar.xz archived-imemio-b40ce35db997942f44ef0af184b94bcfe80d070d.zip | |
feat(iku): Add comment detection with --allow-comments flag
| -rw-r--r-- | packages/iku/src/checker.test.ts | 57 | ||||
| -rw-r--r-- | packages/iku/src/checker.ts | 57 | ||||
| -rw-r--r-- | packages/iku/src/cli.ts | 24 | ||||
| -rw-r--r-- | packages/iku/src/index.ts | 2 |
4 files changed, 133 insertions, 7 deletions
diff --git a/packages/iku/src/checker.test.ts b/packages/iku/src/checker.test.ts index 58a861c..457d956 100644 --- a/packages/iku/src/checker.test.ts +++ b/packages/iku/src/checker.test.ts @@ -280,4 +280,61 @@ console.log(a); expect(errors).toHaveLength(3); }); }); + describe("comments", () => { + it("detects single-line comments by default", () => { + const content = `const a = 1; +// this is a comment +const b = 2; +`; + const filePath = createTempFile(content); + const errors = checkFile(filePath); + + cleanupTempFile(filePath); + expect(errors.some((error) => error.includes("Comment detected"))).toBe(true); + }); + it("detects multi-line comments by default", () => { + const content = `const a = 1; +/* this is a + multi-line comment */ +const b = 2; +`; + const filePath = createTempFile(content); + const errors = checkFile(filePath); + + cleanupTempFile(filePath); + expect(errors.some((error) => error.includes("Comment detected"))).toBe(true); + }); + it("detects trailing comments by default", () => { + const content = `const a = 1; // trailing comment +`; + const filePath = createTempFile(content); + const errors = checkFile(filePath); + + cleanupTempFile(filePath); + expect(errors.some((error) => error.includes("Comment detected"))).toBe(true); + }); + it("allows comments when noComments is false", () => { + const content = `const a = 1; +// this is a comment +const b = 2; +`; + const filePath = createTempFile(content); + const errors = checkFile(filePath, { noComments: false }); + + cleanupTempFile(filePath); + expect(errors.some((error) => error.includes("Comment detected"))).toBe(false); + }); + it("detects JSDoc comments by default", () => { + const content = `/** + * This is a JSDoc comment + */ +function test() {} +`; + const filePath = createTempFile(content); + const errors = checkFile(filePath); + + cleanupTempFile(filePath); + expect(errors.some((error) => error.includes("Comment detected"))).toBe(true); + }); + }); }); diff --git a/packages/iku/src/checker.ts b/packages/iku/src/checker.ts index d541028..93781ec 100644 --- a/packages/iku/src/checker.ts +++ b/packages/iku/src/checker.ts @@ -2,6 +2,14 @@ import { readdirSync, statSync } from "node:fs"; import { extname, join } from "node:path"; import * as typescript from "typescript"; +export interface CheckerOptions { + noComments?: boolean; +} + +const defaultOptions: CheckerOptions = { + noComments: true, +}; + type StatementCategory = | "import" | "variableDeclaration" @@ -188,7 +196,41 @@ function visitBlocks( }); } -export function checkFile(filePath: string): string[] { +function checkForComments(sourceFile: typescript.SourceFile, filePath: string): string[] { + const errors: string[] = []; + const text = sourceFile.getFullText(); + + function visit(node: typescript.Node): void { + const leadingComments = typescript.getLeadingCommentRanges(text, node.getFullStart()); + const trailingComments = typescript.getTrailingCommentRanges(text, node.getEnd()); + + if (leadingComments) { + for (const comment of leadingComments) { + const line = sourceFile.getLineAndCharacterOfPosition(comment.pos).line + 1; + + errors.push(`${filePath}:${line}: Comment detected`); + } + } + + if (trailingComments) { + for (const comment of trailingComments) { + const line = sourceFile.getLineAndCharacterOfPosition(comment.pos).line + 1; + + errors.push(`${filePath}:${line}: Comment detected`); + } + } + + typescript.forEachChild(node, visit); + } + + visit(sourceFile); + + const uniqueErrors = [...new Set(errors)]; + + return uniqueErrors; +} + +export function checkFile(filePath: string, options: CheckerOptions = defaultOptions): string[] { const program = typescript.createProgram([filePath], { target: typescript.ScriptTarget.ESNext, module: typescript.ModuleKind.ESNext, @@ -210,6 +252,12 @@ export function checkFile(filePath: string): string[] { errors.push(...topLevelErrors); visitBlocks(sourceFile, sourceFile, filePath, errors); + if (options.noComments) { + const commentErrors = checkForComments(sourceFile, filePath); + + errors.push(...commentErrors); + } + return errors; } @@ -232,12 +280,15 @@ export function walkDirectory(directory: string): string[] { return files; } -export function checkDirectory(directory: string): { errors: string[]; hasErrors: boolean } { +export function checkDirectory( + directory: string, + options: CheckerOptions = defaultOptions, +): { errors: string[]; hasErrors: boolean } { const files = walkDirectory(directory); const allErrors: string[] = []; for (const file of files) { - const errors = checkFile(file); + const errors = checkFile(file, options); allErrors.push(...errors); } diff --git a/packages/iku/src/cli.ts b/packages/iku/src/cli.ts index c0a85fc..8ea07a4 100644 --- a/packages/iku/src/cli.ts +++ b/packages/iku/src/cli.ts @@ -2,7 +2,7 @@ import { existsSync } from "node:fs"; import { dirname, join } from "node:path"; -import { checkDirectory } from "./checker.js"; +import { checkDirectory, type CheckerOptions } from "./checker.js"; function findMonorepoRoot(startDirectory: string): string | null { let current = startDirectory; @@ -21,6 +21,23 @@ function findMonorepoRoot(startDirectory: string): string | null { return null; } +function parseArguments(arguments_: string[]): { directory?: string; options: CheckerOptions } { + const options: CheckerOptions = { + noComments: true, + }; + let directory: string | undefined; + + for (const argument of arguments_) { + if (argument === "--allow-comments") { + options.noComments = false; + } else if (!argument.startsWith("-")) { + directory = argument; + } + } + + return { directory, options }; +} + const monorepoRoot = findMonorepoRoot(process.cwd()); if (!monorepoRoot) { @@ -28,8 +45,9 @@ if (!monorepoRoot) { process.exit(1); } -const targetDirectory = process.argv[2] ?? join(monorepoRoot, "packages"); -const { errors, hasErrors } = checkDirectory(targetDirectory); +const { directory, options } = parseArguments(process.argv.slice(2)); +const targetDirectory = directory ?? join(monorepoRoot, "packages"); +const { errors, hasErrors } = checkDirectory(targetDirectory, options); for (const error of errors) { console.error(error); diff --git a/packages/iku/src/index.ts b/packages/iku/src/index.ts index dc64d12..d3d29ae 100644 --- a/packages/iku/src/index.ts +++ b/packages/iku/src/index.ts @@ -1 +1 @@ -export { checkFile, walkDirectory, checkDirectory } from "./checker.js"; +export { checkFile, walkDirectory, checkDirectory, type CheckerOptions } from "./checker.js"; |