aboutsummaryrefslogtreecommitdiff
path: root/packages/iku/src
diff options
context:
space:
mode:
Diffstat (limited to 'packages/iku/src')
-rw-r--r--packages/iku/src/checker.test.ts57
-rw-r--r--packages/iku/src/checker.ts57
-rw-r--r--packages/iku/src/cli.ts24
-rw-r--r--packages/iku/src/index.ts2
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";