UNPKG

ai-linter

Version:

A linter tool to analyze JavaScript, TypeScript, and Vue code, with a focus on AI-generated code, unused imports, alias resolution, and potential hazards.

427 lines (346 loc) 16.4 kB
# Advanced Linter for AI-Generated Code `ai-linter` is a powerful static analysis tool designed specifically to validate and enhance JavaScript, TypeScript, and Vue code, particularly code generated by AI. It helps ensure code quality, maintainability, and security by identifying potential issues, unused code, and inconsistencies. ## Key Features - **AI-Generated Code Validation:** Tailored to scrutinize code produced by AI tools, catching common pitfalls and ensuring adherence to best practices. - **Unused Import Detection & Cleaning:** Identifies and helps remove unused import statements, keeping your codebase clean and optimized. - **Alias Resolution Verification:** Checks and resolves path aliases (e.g., `@/components`, `~/utils`) defined in `jsconfig.json`, `tsconfig.json`, Babel, Webpack, Vite, or Next.js configurations, ensuring imports are correctly mapped. - **Potential Hazard Detection:** Flags potentially problematic code constructs such as: - `eval()` usage - `console.log` and other `console` method calls - `debugger` statements - `TODO` and `FIXME` comments, prompting for review and action. - **Import Syntax Error Identification:** Detects and reports errors in import statements, including unresolved modules and incorrect named/default imports. - **Export Validation:** Verifies named exports and re-exports, ensuring that what's imported is actually available from the source module. - **Undeclared Identifier Detection:** Finds variables or functions used without prior declaration. - **Support for Multiple File Types:** Analyzes `.js`, `.jsx`, `.ts`, `.tsx`, and `.vue` files. - **Configurable Analysis:** Use a `.analyzerconfig.json` file or command-line options to customize target directories/files, ignored patterns, output format, and more. ## Why ai-linter? As AI-assisted development becomes more prevalent, ensuring the quality and reliability of generated code is crucial. `ai-linter` provides a focused solution to: - **Improve Code Quality:** By catching errors and bad practices early. - **Enhance Maintainability:** By promoting cleaner, more understandable code. - **Reduce Bundle Sizes:** By helping eliminate unused code. - **Streamline Code Reviews:** By automating the detection of common issues. ## Getting Started ### Prerequisites - Node.js (version 14 or higher recommended) ### Installation Currently, `ai-linter` can be run directly from its source or integrated as a module. ```bash # Clone the repository (if you haven't already) git clone <repository-url> cd ai-linter # Install dependencies yarn install # or npm install ``` ### Usage `ai-linter` offers two primary ways to use its analysis capabilities: #### 1. Command Line Interface (CLI) For quick analysis and integration into build scripts, the CLI is the recommended approach. After installation, you might need to make `index.js` executable or call it via `node`. If you've set up the `bin` field in `package.json` and installed the package globally or linked it (`npm link`), you can run: ```bash ai-linter <file|directory> [options] ``` Otherwise, run from the project root: ```bash node index.js <file|directory> [options] ``` **Examples:** - Analyze all supported files in the `src` directory: ```bash node index.js src/ ``` - Analyze a specific file: ```bash node index.js src/components/MyComponent.vue ``` - Analyze the current directory, output results in JSON format to `report.json`: ```bash node index.js . --format json --output report.json ``` - Analyze `src` but ignore test files: ```bash node index.js src --ignore "**/*.test.js,**/*.spec.js" ``` **Options:** - `--recursive`, `-r`: Scan subdirectories (default: true, configurable via `.analyzerconfig.json`). - `--extensions`, `-e`: File extensions to analyze (default: `.js,.jsx,.ts,.tsx,.vue`, configurable). - `--ignore`, `-i`: Glob patterns to ignore (configurable). - `--format`, `-f`: Output format (`json` | `table` | `summary`) (default: `table`, configurable). - `--output`, `-o`: File to write output to (configurable). - `--help`, `-h`: Display help message. #### 2. Programmatic Usage (Analyzer Module) For more complex integrations or custom analysis workflows, you can use the `Analyzer` module directly in your Node.js projects. The `Analyzer` constructor accepts an optional `cwd` (current working directory) parameter. If not provided, it defaults to `process.cwd()`. This `cwd` is used as the base for resolving relative paths for configuration files (like `jsconfig.json`, `babel.config.js`), analysis target files/directories, and path aliases. ```javascript const { Analyzer } = require('ai-linter'); // Or adjust path if used locally // Example 1: Default behavior (uses process.cwd()) const analyzer1 = new Analyzer(); // Analyzes './src/someFile.js' relative to process.cwd() const report1 = analyzer1.analyzeFile('./src/someFile.js'); console.log(JSON.stringify(report1, null, 2)); // Example 2: Specifying a custom current working directory const projectRoot = '/path/to/your/project'; // Replace with actual path const analyzer2 = new Analyzer(projectRoot); // Analyze a single file relative to 'projectRoot' // e.g., resolves to '/path/to/your/project/src/components/MyComponent.js' const fileReport = analyzer2.analyzeFile('./src/components/MyComponent.js'); console.log(JSON.stringify(fileReport, null, 2)); /* Example fileReport output: { "file": "/path/to/your/project/src/components/MyComponent.js", "status": "ok", // or "error", "warning" "syntaxError": null, // or { message: "...", line: X, column: Y } "imports": [ { "path": "react", "specifiers": [{ "name": "default", "alias": "React", "type": "ImportDefaultSpecifier" }], "line": 1, "resolvedPath": "react", // Path to the module in node_modules or core module name "status": "resolved", "isExternal": true } ], "dynamicImports": [], "exports": [ { "type": "default", "line": 5 } ], "unusedImports": [], // e.g., ["unusedIdentifier"] "issues": [] // e.g., [{ "type": "console-usage", "message": "Usage of 'console.log'.", "line": 10 }] } */ // Analyze a directory relative to 'projectRoot' // e.g., analyzes files within '/path/to/your/project/src' const directoryReport = analyzer2.analyzeDirectory('./src', { recursive: true, extensions: ['.js', '.ts'], ignore: ['node_modules/**'] // Paths in ignore are also relative to the analyzer's cwd or absolute }); console.log(JSON.stringify(directoryReport, null, 2)); /* Example directoryReport output: { "totalFiles": 5, "filesWithErrors": 1, "filesWithWarnings": 0, "totalImports": 12, "unresolvedImports": 1, "unusedImports": 2, "totalUndeclaredIdentifiers": 0, "details": [ // Array of fileReport objects, one for each analyzed file { "file": "/path/to/your/project/src/file1.js", "status": "ok", // ... other properties as in fileReport ... }, { "file": "/path/to/your/project/src/fileWithError.js", "status": "error", "syntaxError": { "message": "Unexpected token", "line": 3, "column": 10 }, // ... other properties ... } // ... more file reports ... ] } */ ``` This approach gives you direct access to the analysis results as JavaScript objects, allowing for deeper integration with other tools or custom reporting mechanisms. ### Configuration File You can configure `ai-linter` using a `.analyzerconfig.json` file in your project root (or the current working directory from which the tool is run): ```json { "target": "src", "recursive": true, "extensions": [".js", ".jsx", ".ts", ".tsx", ".vue"], "ignore": [ "node_modules/**", "dist/**", "**/*.test.js" ], "format": "summary", "output": "analysis-report.txt" } ``` Command-line arguments will override settings in the configuration file when using the CLI. The `target` and `output` paths in the configuration file are resolved relative to the location of the configuration file itself (which is typically the project root or `cwd`). ## How It Works `ai-linter` parses your code into an Abstract Syntax Tree (AST) using Babel parser. It then traverses this tree to: 1. **Identify Imports & Exports:** Collects all import and export statements. 2. **Resolve Aliases:** Uses configurations from common tools (`jsconfig.json`, `tsconfig.json`, Webpack, Vite, etc.)—searched relative to the current working directory (or the `cwd` provided to the `Analyzer` instance)—to map aliased paths to their actual file locations. 3. **Validate Imports:** Checks if imported modules exist and if named/default imports are correctly specified based on the exports of the source file. 4. **Detect Unused Imports:** Compares imported identifiers against their usage within the file. 5. **Scan for Code Issues:** Looks for patterns like `eval()`, `console.*`, `debugger`, `TODO`/`FIXME` comments, undeclared variables, and duplicate object keys. ## Detected Issue Types and Examples `ai-linter` can detect a variety of issues in your codebase. Here's a list of common issue types, their meanings, and examples of code that would trigger them. ### Import/Export Issues 1. **`unresolved-import`** * **Description:** The linter could not find or resolve the module specified in an import statement. * **Example:** ```javascript import something from './non-existent-module'; ``` 2. **`error-in-imported-file`** * **Description:** An imported file contains a syntax error, preventing it from being parsed. * **Example:** ```javascript // main.js import config from './broken-config'; // broken-config.js export default { setting: true // Missing semicolon or comma, causing syntax error } ``` 3. **`unresolved-named-import`** * **Description:** A named import (e.g., `import { specificThing } from 'module';`) could not be found among the exports of the target module. * **Example:** ```javascript // utils.js export const helper = () => {}; // main.js import { nonExistentHelper } from './utils'; // 'nonExistentHelper' is not exported ``` 4. **`unresolved-default-import`** * **Description:** A default import (e.g., `import myModule from 'module';`) was used, but the target module does not have a default export. * **Example:** ```javascript // config.js export const setting = true; // No default export // main.js import appConfig from './config'; // './config' has no default export ``` 5. **`unresolved-dynamic-import`** * **Description:** The linter could not find or resolve the module specified in a dynamic `import()` call. * **Example:** ```javascript async function loadFeature() { const feature = await import('./non-existent-feature'); // ... } ``` 6. **`unresolved-re-export-source`** * **Description:** The source module specified in a re-export statement (e.g., `export { name } from './source';`) could not be found. * **Example:** ```javascript // index.js export { utility } from './missing-utils'; // './missing-utils' does not exist ``` 7. **`error-in-re-exported-file`** * **Description:** A file being re-exported from contains a syntax error. * **Example:** ```javascript // index.js export { configValue } from './broken-module'; // broken-module.js export const configValue = "test" // Syntax error here ``` 8. **`missing-re-exported-name`** * **Description:** A named entity being re-exported (e.g., `export { specificName } from './source';`) is not actually exported by the source module. * **Example:** ```javascript // source.js export const oneThing = 1; // index.js export { anotherThing } from './source'; // 'anotherThing' is not in 'source.js' ``` 9. **`unresolved-re-export-all-source`** * **Description:** The source module in an `export * from './source';` statement could not be found. * **Example:** ```javascript // aggregator.js export * from './non-existent-lib'; ``` 10. **`error-in-re-exported-all-source`** * **Description:** The source module in an `export * from './source';` statement contains a syntax error. * **Example:** ```javascript // aggregator.js export * from './faulty-lib'; // faulty-lib.js export const item = 1 // Syntax error ``` 11. **`unused-imports`** * **Description:** An imported module or identifier is not used anywhere in the file. * **Example:** ```javascript import { usefulFunction, unusedFunction } from './lib'; usefulFunction(); // 'unusedFunction' is imported but not called or referenced ``` ### Code Quality & Potential Hazards 12. **`eval-usage`** * **Description:** Use of the `eval()` function, which can be a security risk and hinder performance. * **Example:** ```javascript const result = eval('1 + 1'); ``` 13. **`console-usage`** * **Description:** Presence of `console.log()`, `console.warn()`, etc., which are often left over from debugging. * **Example:** ```javascript function myFunc(data) { console.log('Debugging data:', data); return data; } ``` 14. **`debugger-statement`** * **Description:** A `debugger;` statement found, which can halt execution if developer tools are open. * **Example:** ```javascript if (criticalCondition) { debugger; // Pauses execution here } ``` 15. **`duplicate-object-key`** * **Description:** An object literal contains duplicate keys, where later keys will overwrite earlier ones. * **Example:** ```javascript const config = { url: 'http://example.com', timeout: 5000, url: 'http://test.com' // 'url' key is duplicated }; ``` ### Variable & Identifier Issues 16. **`undeclared-identifier`** * **Description:** A variable or function is used without being declared in the current scope or imported. * **Example:** ```javascript function process() { const result = myUndeclaredVariable * 10; // 'myUndeclaredVariable' is not defined return result; } ``` 17. **`undeclared-jsx-component`** * **Description:** A JSX component (typically starting with an uppercase letter) is used without being declared or imported. * **Example:** ```jsx // MyComponent.jsx (assuming MyButton is not imported or defined) function MyComponent() { return <MyButton label="Click Me" />; } ``` ### File System Issues 18. **`file-not-found`** (when analyzing a single file directly or an import resolves to a non-existent file) * **Description:** The target file specified for analysis or an imported file path does not exist. * **Example (CLI):** `ai-linter ./path/to/non-existent-file.js` * **Example (Import):** ```javascript import settings from './missing-settings'; // if './missing-settings.js' doesn't exist ``` ### Comment-Based Issues 19. **`todo-comment`** * **Description:** A `// TODO:` or `/* TODO: */` comment is found, indicating pending work. * **Example:** ```javascript // TODO: Refactor this function for better performance function oldFunction() { /* ... */ } ``` 20. **`fixme-comment`** * **Description:** A `// FIXME:` or `/* FIXME: */` comment is found, indicating a known bug or issue that needs fixing. * **Example:** ```javascript // FIXME: This calculation is sometimes incorrect under X condition const value = complexCalculation(); ``` ## Contributing Contributions are welcome! Please feel free to submit issues, feature requests, or pull requests. ## License This project is open-source and available under the [MIT License](LICENSE.md).