eslint-plugin-ai-rules 0.1.2 → 0.1.3
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 +8 -8
- package/dist/index.cjs +2 -7
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -7
- package/dist/index.js.map +1 -1
- package/package.json +6 -6
package/README.md
CHANGED
|
@@ -114,7 +114,7 @@ npx eslint --fix . # autofix for sort-imports / no-multiline-imports
|
|
|
114
114
|
|
|
115
115
|
| Rule | Default | Notes |
|
|
116
116
|
|------|---------|-------|
|
|
117
|
-
| `ai-rules/filename-pascal-case` | `error` |
|
|
117
|
+
| `ai-rules/filename-pascal-case` | `error` | File-agnostic: checks every file ESLint lints. See below |
|
|
118
118
|
| `ai-rules/sort-imports` | `error` | Groups + length desc, collapses multiline |
|
|
119
119
|
| `ai-rules/no-multiline-imports` | `error` | Single-line imports |
|
|
120
120
|
| `import-x/no-duplicates` | `error` | |
|
|
@@ -158,18 +158,18 @@ Brief overview. See source in `src/rules/` for full logic.
|
|
|
158
158
|
|
|
159
159
|
### `ai-rules/filename-pascal-case`
|
|
160
160
|
|
|
161
|
-
Enforces `PascalCase` for
|
|
161
|
+
Enforces `PascalCase` for file names. The rule is file-agnostic: it validates every file ESLint decides to lint, following the flat-config `files`/`ignores` defined in the consumer's config (same pattern as other filename rules).
|
|
162
162
|
|
|
163
163
|
- **Type:** `suggestion`, no autofix, `messageId: invalid`
|
|
164
164
|
- **Ignores:** `index.{ts,tsx,js,jsx}`, `*.d.ts`, `*.css`
|
|
165
|
-
- **Checks:** `path.basename`
|
|
165
|
+
- **Checks:** `path.basename` from `getPhysicalFilename()` / `context.filename`, `^[A-Z][a-zA-Z0-9]*$`, `kebab/snake -> PascalCase` conversion
|
|
166
166
|
|
|
167
167
|
```ts
|
|
168
|
-
//
|
|
169
|
-
//
|
|
168
|
+
// my-component.ts ❌ File name "my-component.ts" must be in PascalCase. Rename to "MyComponent.ts".
|
|
169
|
+
// MyComponent.ts ✅
|
|
170
170
|
```
|
|
171
171
|
|
|
172
|
-
To disable
|
|
172
|
+
To scope or disable it, use your own config (as this repo does in `eslint.config.mjs`):
|
|
173
173
|
|
|
174
174
|
```ts
|
|
175
175
|
import { defineConfig } from 'eslint/config'
|
|
@@ -247,8 +247,8 @@ You passed an absolute path outside the config's `basePath`. Run `npx eslint .`
|
|
|
247
247
|
**Flat config only**
|
|
248
248
|
This plugin exports `defineConfig` arrays. It does not support legacy `.eslintrc`. Use `eslint.config.{js,ts}`.
|
|
249
249
|
|
|
250
|
-
**Filename rule too strict for tests**
|
|
251
|
-
|
|
250
|
+
**Filename rule too strict for tests/configs**
|
|
251
|
+
The rule checks every linted file. Disable `ai-rules/filename-pascal-case` for `**/*.test.*`, `**/*.spec.*`, `*.config.*`, generated code, or your own fixtures via an override (see Quick Start).
|
|
252
252
|
|
|
253
253
|
## Development
|
|
254
254
|
|
package/dist/index.cjs
CHANGED
|
@@ -61,12 +61,7 @@ var filenamePascalCase = {
|
|
|
61
61
|
// eslint 9 flat config
|
|
62
62
|
context.getPhysicalFilename?.() ?? context.physicalFilename ?? context.filename ?? ""
|
|
63
63
|
);
|
|
64
|
-
const
|
|
65
|
-
const normalizedPhysical = physicalFilename.split(import_node_path.default.sep).join("/");
|
|
66
|
-
const normalizedCwd = cwd.split(import_node_path.default.sep).join("/");
|
|
67
|
-
const relativePath = normalizedPhysical.startsWith(normalizedCwd) ? normalizedPhysical.slice(normalizedCwd.length + 1) : normalizedPhysical;
|
|
68
|
-
if (!relativePath.startsWith("src/") && !relativePath.startsWith("scripts/")) return {};
|
|
69
|
-
const basename = import_node_path.default.basename(relativePath);
|
|
64
|
+
const basename = import_node_path.default.basename(physicalFilename);
|
|
70
65
|
if (basename === "index.ts" || basename === "index.tsx" || basename === "index.js" || basename === "index.jsx") return {};
|
|
71
66
|
if (basename.endsWith(".d.ts")) return {};
|
|
72
67
|
if (basename.endsWith(".css")) return {};
|
|
@@ -254,7 +249,7 @@ function getSortedContent(content) {
|
|
|
254
249
|
var plugin = {
|
|
255
250
|
meta: {
|
|
256
251
|
name: "eslint-plugin-ai-rules",
|
|
257
|
-
version: "0.1.
|
|
252
|
+
version: "0.1.2"
|
|
258
253
|
},
|
|
259
254
|
rules: {
|
|
260
255
|
"filename-pascal-case": filenamePascalCase,
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/configs/recommended.ts","../src/rules/FilenamePascalCase.ts","../src/rules/NoMultilineImports.ts","../src/rules/SortImports.ts","../src/plugin.ts"],"sourcesContent":["import { recommended } from './configs/recommended'\nimport { plugin } from './plugin'\n\nexport * from './rules/index'\n\n\nconst aiGuardPlugin = {\n ...plugin,\n configs: {\n recommended,\n },\n}\n\nexport default aiGuardPlugin\nexport { plugin }\nexport { recommended }","import { createNodeResolver, importX } from 'eslint-plugin-import-x'\nimport stylistic from '@stylistic/eslint-plugin'\nimport { defineConfig } from 'eslint/config'\nimport tseslint from 'typescript-eslint'\n\nimport { plugin } from '../plugin'\n\n\nexport const recommended = defineConfig([\n {\n plugins: {\n '@stylistic': stylistic,\n 'import-x': importX,\n 'ai-rules': plugin,\n '@typescript-eslint': tseslint.plugin,\n },\n settings: {\n 'import-x/resolver-next': [\n createNodeResolver({\n extensions: ['.ts', '.tsx', '.d.ts', '.js', '.jsx'],\n }),\n ],\n },\n rules: {\n 'ai-rules/sort-imports': 'error',\n 'ai-rules/no-multiline-imports': 'error',\n 'ai-rules/filename-pascal-case': 'error',\n 'max-len': 'off',\n 'import-x/no-duplicates': 'error',\n 'import-x/no-default-export': 'off',\n '@stylistic/semi': ['error', 'never'],\n '@typescript-eslint/consistent-type-imports': [\n 'error',\n { prefer: 'type-imports' },\n ],\n '@typescript-eslint/no-unused-vars': [\n 'error',\n { argsIgnorePattern: '^_', varsIgnorePattern: '^_' },\n ],\n '@typescript-eslint/naming-convention': [\n 'error',\n { selector: 'interface', format: ['PascalCase'], prefix: ['I'] },\n { selector: 'typeAlias', format: ['PascalCase'], prefix: ['T'] },\n ],\n },\n },\n])\n\nexport default recommended\n","import type { Rule } from 'eslint'\nimport path from 'node:path'\n\n\nexport const filenamePascalCase: Rule.RuleModule = {\n meta: {\n type: 'suggestion',\n docs: { description: 'Enforce PascalCase for file names' },\n schema: [],\n messages: {\n invalid: 'File name \"{{basename}}\" must be in PascalCase. Rename to \"{{expected}}\".',\n },\n },\n create(context) {\n const physicalFilename =\n // eslint 9 flat config\n (context as unknown as { getPhysicalFilename?: () => string }).getPhysicalFilename?.() ??\n (context as unknown as { physicalFilename?: string }).physicalFilename ??\n context.filename ??\n ''\n\n const cwd =\n (context as unknown as { getCwd?: () => string }).getCwd?.() ?? process.cwd()\n\n // Normaliza para posix para comparar com src/ e scripts/\n const normalizedPhysical = physicalFilename.split(path.sep).join('/')\n const normalizedCwd = cwd.split(path.sep).join('/')\n\n const relativePath = normalizedPhysical.startsWith(normalizedCwd)\n ? normalizedPhysical.slice(normalizedCwd.length + 1)\n : normalizedPhysical\n\n if (!relativePath.startsWith('src/') && !relativePath.startsWith('scripts/')) return {}\n\n const basename = path.basename(relativePath)\n\n if (basename === 'index.ts' || basename === 'index.tsx' || basename === 'index.js' || basename === 'index.jsx') return {}\n if (basename.endsWith('.d.ts')) return {}\n if (basename.endsWith('.css')) return {}\n\n const ext = path.extname(basename) // .ts\n // Para casos como Component.spec.ts -> ext .ts, nameWithoutExt Component.spec - tratamos mantendo só base antes do primeiro ponto?\n // Mantemos compat com original: remove apenas última extensão .ts/.tsx/.js/.jsx\n const nameWithoutExt = basename.replace(/\\.(ts|tsx|js|jsx)$/, '')\n\n if (!/^[A-Z][a-zA-Z0-9]*$/.test(nameWithoutExt)) {\n const pascalCase = nameWithoutExt\n .split(/[-_]/)\n .map(part => part.charAt(0).toUpperCase() + part.slice(1))\n .join('')\n\n // Preserva extensão original (ex: .tsx)\n const originalExt = basename.slice(nameWithoutExt.length) || ext\n const expected = `${pascalCase}${originalExt}`\n\n context.report({\n loc: { line: 1, column: 0 },\n messageId: 'invalid',\n data: { basename, expected },\n })\n }\n\n return {}\n },\n}\n","import type { Rule } from 'eslint'\n\n\nexport const noMultilineImports: Rule.RuleModule = {\n meta: {\n type: 'layout',\n fixable: 'code',\n docs: {\n description: 'Disallow multiline imports',\n },\n schema: [],\n messages: {\n multilineImport: 'Imports must be written on a single line.',\n },\n },\n\n create(context) {\n const sourceCode = context.sourceCode\n\n return {\n ImportDeclaration(node) {\n const text = sourceCode.getText(node)\n\n if (!text.includes('\\n')) {\n return\n }\n\n context.report({\n node,\n messageId: 'multilineImport',\n\n fix(fixer) {\n const firstToken = sourceCode.getFirstToken(node)\n const lastToken = sourceCode.getLastToken(node)\n\n if (!firstToken || !lastToken) {\n return null\n }\n\n let importText = sourceCode.getText(node)\n\n importText = importText\n .replace(/\\s*\\{\\s*/g, ' { ')\n .replace(/\\s*\\}\\s*/g, ' } ')\n .replace(/\\s*,\\s*/g, ', ')\n .replace(/\\s+/g, ' ')\n .replace(/ \\{ /g, ' { ')\n .replace(/ \\} /g, ' } ')\n .replace(/\\s+from\\s+/g, ' from ')\n .trim()\n // Corrige espaços colapsados em torno de { e }\n .replace(/\\{ \\s+/g, '{ ')\n .replace(/\\s+ \\}/g, ' }')\n\n return fixer.replaceText(node, importText)\n },\n })\n },\n }\n },\n}\n","import type { Rule } from 'eslint'\n\n\nexport const sortImports: Rule.RuleModule = {\n meta: {\n type: 'layout',\n docs: {\n description: 'Sort imports by group and line length descending, collapsing multiline imports',\n },\n fixable: 'code',\n schema: [],\n messages: {\n sortRequired: 'As importações devem estar ordenadas por grupo e tamanho da linha, ocupando uma única linha.',\n },\n },\n create(context) {\n return {\n Program(node) {\n const sourceCode = context.sourceCode\n const content = sourceCode.text\n\n const newContent = getSortedContent(content)\n\n if (newContent && newContent !== content) {\n context.report({\n node,\n messageId: 'sortRequired',\n fix(fixer) {\n return fixer.replaceText(node, newContent)\n },\n })\n }\n },\n }\n },\n}\n\n// ---------------------------------------------------------------------------\n// Tipos auxiliares\n// ---------------------------------------------------------------------------\ntype TImportItem = {\n line: string\n from: string\n}\n\n// ---------------------------------------------------------------------------\n// Lógica Core - exportada para teste e reutilização\n// ---------------------------------------------------------------------------\nexport function getSortedContent(content: string): string | null {\n const collapseMultiline = (match: string) => {\n return match\n .replace(/\\/\\*[\\s\\S]*?\\*\\//g, '')\n .replace(/\\/\\/.*$/gm, '')\n .replace(/\\s+/g, ' ')\n }\n\n const normalizedContent = content\n .replace(/import\\s+([^'\";]+?)\\s+from\\s+(['\"][^'\"]+['\"])/g, collapseMultiline)\n // Apenas re-exports válidos: export { ... } from / export * from / export * as X from / export type { ... } from\n .replace(/export\\s+(?:type\\s+)?(?:\\{[^}]*\\}|\\*(?:\\s+as\\s+\\w+)?)\\s+from\\s+(['\"][^'\"]+['\"])/g, collapseMultiline)\n\n const lines: string[] = normalizedContent.split('\\n')\n\n const directiveLines: string[] = []\n const reactFirstLine: string[] = []\n const externalLines: TImportItem[] = []\n const internalLines: TImportItem[] = []\n const reExportLines: TImportItem[] = []\n const nonImportLines: string[] = []\n\n let inImportBlock = false\n let canAcceptDirectives = true\n\n for (let i = 0; i < lines.length; i++) {\n const trimmed = lines[i].trim()\n\n if (\n /^(function|const|let|var|class|interface|enum)\\b/.test(trimmed) ||\n (/^(type|export)\\b/.test(trimmed) && !/ from ['\"]/.test(trimmed) && !/^export\\s*\\{[^}]*\\}\\s*from/.test(trimmed) && !/^export\\s*\\*/.test(trimmed)) ||\n trimmed.startsWith('}')\n ) {\n canAcceptDirectives = false\n }\n\n if (inImportBlock && trimmed === '') continue\n\n if (canAcceptDirectives && /^['\"]use (client|server|strict)['\"];?$/.test(trimmed)) {\n directiveLines.push(lines[i])\n continue\n }\n\n const isReExport =\n /^export\\s+(type\\s+)?\\{[^}]*\\}\\s+from\\s+['\"]/.test(trimmed) ||\n /^export\\s+\\*(\\s+as\\s+\\w+)?\\s+from\\s+['\"]/.test(trimmed)\n\n if (isReExport) {\n const m = lines[i].match(/from\\s+['\"]([^'\"]+)['\"]/)\n if (m) reExportLines.push({ line: lines[i], from: m[1] })\n inImportBlock = true\n canAcceptDirectives = false\n continue\n }\n\n const isImport = /^import\\s/.test(trimmed) || /^import\\{/.test(trimmed)\n\n if (isImport) {\n inImportBlock = true\n canAcceptDirectives = false\n\n const fromMatch = lines[i].match(/from\\s+['\"]([^'\"]+)['\"]/)\n const seMatch = lines[i].match(/^import\\s+['\"]([^'\"]+)['\"]/)\n const from = fromMatch ? fromMatch[1] : seMatch ? seMatch[1] : null\n\n if (!from) {\n nonImportLines.push(lines[i])\n continue\n }\n\n const isTypeOnly = /^import\\s+type\\b/.test(trimmed)\n const isInternal = from.startsWith('.') || from.startsWith('@/')\n const isReactEntry = !isTypeOnly && (from === 'react' || from.startsWith('react/'))\n\n if (isReactEntry) {\n reactFirstLine.push(lines[i])\n } else if (isInternal) {\n internalLines.push({ line: lines[i], from })\n } else {\n externalLines.push({ line: lines[i], from })\n }\n continue\n }\n\n inImportBlock = false\n nonImportLines.push(lines[i])\n }\n\n if (reactFirstLine.length === 0 && externalLines.length === 0 && internalLines.length === 0 && reExportLines.length === 0) {\n return null\n }\n\n const byLengthDesc = (a: TImportItem, b: TImportItem) =>\n b.line.length !== a.line.length ? b.line.length - a.line.length : a.line.localeCompare(b.line)\n\n externalLines.sort(byLengthDesc)\n internalLines.sort(byLengthDesc)\n reExportLines.sort(byLengthDesc)\n\n const blocks: string[] = []\n\n if (directiveLines.length > 0) blocks.push(directiveLines.join('\\n'))\n\n const externalBlock = [...reactFirstLine, ...externalLines.map(i => i.line)]\n if (externalBlock.length > 0) {\n if (blocks.length > 0) blocks.push('')\n blocks.push(externalBlock.join('\\n'))\n }\n\n if (internalLines.length > 0) {\n if (blocks.length > 0) blocks.push('')\n blocks.push(internalLines.map(i => i.line).join('\\n'))\n }\n\n if (reExportLines.length > 0) {\n if (blocks.length > 0) blocks.push('')\n blocks.push(reExportLines.map(i => i.line).join('\\n'))\n }\n\n const rest = nonImportLines.join('\\n').replace(/^\\n+/, '')\n\n let newContent: string\n if (blocks.length > 0 && rest.length > 0) {\n newContent = blocks.join('\\n') + '\\n\\n\\n' + rest\n } else if (blocks.length > 0) {\n newContent = blocks.join('\\n') + '\\n'\n } else {\n newContent = rest\n }\n\n return newContent\n}\n","import { filenamePascalCase } from './rules/FilenamePascalCase'\nimport { noMultilineImports } from './rules/NoMultilineImports'\nimport { sortImports } from './rules/SortImports'\n\n\nexport const plugin = {\n meta: {\n name: 'eslint-plugin-ai-rules',\n version: '0.1.0',\n },\n rules: {\n 'filename-pascal-case': filenamePascalCase,\n 'no-multiline-imports': noMultilineImports,\n 'sort-imports': sortImports,\n },\n} as const\n"],"mappings":"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","names":["path","stylistic","tseslint"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/configs/recommended.ts","../src/rules/FilenamePascalCase.ts","../src/rules/NoMultilineImports.ts","../src/rules/SortImports.ts","../src/plugin.ts"],"sourcesContent":["import { recommended } from './configs/recommended'\nimport { plugin } from './plugin'\n\nexport * from './rules/index'\n\n\nconst aiGuardPlugin = {\n ...plugin,\n configs: {\n recommended,\n },\n}\n\nexport default aiGuardPlugin\nexport { plugin }\nexport { recommended }","import { createNodeResolver, importX } from 'eslint-plugin-import-x'\nimport stylistic from '@stylistic/eslint-plugin'\nimport { defineConfig } from 'eslint/config'\nimport tseslint from 'typescript-eslint'\n\nimport { plugin } from '../plugin'\n\n\nexport const recommended = defineConfig([\n {\n plugins: {\n '@stylistic': stylistic,\n 'import-x': importX,\n 'ai-rules': plugin,\n '@typescript-eslint': tseslint.plugin,\n },\n settings: {\n 'import-x/resolver-next': [\n createNodeResolver({\n extensions: ['.ts', '.tsx', '.d.ts', '.js', '.jsx'],\n }),\n ],\n },\n rules: {\n 'ai-rules/sort-imports': 'error',\n 'ai-rules/no-multiline-imports': 'error',\n 'ai-rules/filename-pascal-case': 'error',\n 'max-len': 'off',\n 'import-x/no-duplicates': 'error',\n 'import-x/no-default-export': 'off',\n '@stylistic/semi': ['error', 'never'],\n '@typescript-eslint/consistent-type-imports': [\n 'error',\n { prefer: 'type-imports' },\n ],\n '@typescript-eslint/no-unused-vars': [\n 'error',\n { argsIgnorePattern: '^_', varsIgnorePattern: '^_' },\n ],\n '@typescript-eslint/naming-convention': [\n 'error',\n { selector: 'interface', format: ['PascalCase'], prefix: ['I'] },\n { selector: 'typeAlias', format: ['PascalCase'], prefix: ['T'] },\n ],\n },\n },\n])\n\nexport default recommended\n","import type { Rule } from 'eslint'\nimport path from 'node:path'\n\n\nexport const filenamePascalCase: Rule.RuleModule = {\n meta: {\n type: 'suggestion',\n docs: { description: 'Enforce PascalCase for file names' },\n schema: [],\n messages: {\n invalid: 'File name \"{{basename}}\" must be in PascalCase. Rename to \"{{expected}}\".',\n },\n },\n create(context) {\n const physicalFilename =\n // eslint 9 flat config\n (context as unknown as { getPhysicalFilename?: () => string }).getPhysicalFilename?.() ??\n (context as unknown as { physicalFilename?: string }).physicalFilename ??\n context.filename ??\n ''\n\n const basename = path.basename(physicalFilename)\n\n if (basename === 'index.ts' || basename === 'index.tsx' || basename === 'index.js' || basename === 'index.jsx') return {}\n if (basename.endsWith('.d.ts')) return {}\n if (basename.endsWith('.css')) return {}\n\n const ext = path.extname(basename) // .ts\n // Para casos como Component.spec.ts -> ext .ts, nameWithoutExt Component.spec - tratamos mantendo só base antes do primeiro ponto?\n // Mantemos compat com original: remove apenas última extensão .ts/.tsx/.js/.jsx\n const nameWithoutExt = basename.replace(/\\.(ts|tsx|js|jsx)$/, '')\n\n if (!/^[A-Z][a-zA-Z0-9]*$/.test(nameWithoutExt)) {\n const pascalCase = nameWithoutExt\n .split(/[-_]/)\n .map(part => part.charAt(0).toUpperCase() + part.slice(1))\n .join('')\n\n // Preserva extensão original (ex: .tsx)\n const originalExt = basename.slice(nameWithoutExt.length) || ext\n const expected = `${pascalCase}${originalExt}`\n\n context.report({\n loc: { line: 1, column: 0 },\n messageId: 'invalid',\n data: { basename, expected },\n })\n }\n\n return {}\n },\n}\n","import type { Rule } from 'eslint'\n\n\nexport const noMultilineImports: Rule.RuleModule = {\n meta: {\n type: 'layout',\n fixable: 'code',\n docs: {\n description: 'Disallow multiline imports',\n },\n schema: [],\n messages: {\n multilineImport: 'Imports must be written on a single line.',\n },\n },\n\n create(context) {\n const sourceCode = context.sourceCode\n\n return {\n ImportDeclaration(node) {\n const text = sourceCode.getText(node)\n\n if (!text.includes('\\n')) {\n return\n }\n\n context.report({\n node,\n messageId: 'multilineImport',\n\n fix(fixer) {\n const firstToken = sourceCode.getFirstToken(node)\n const lastToken = sourceCode.getLastToken(node)\n\n if (!firstToken || !lastToken) {\n return null\n }\n\n let importText = sourceCode.getText(node)\n\n importText = importText\n .replace(/\\s*\\{\\s*/g, ' { ')\n .replace(/\\s*\\}\\s*/g, ' } ')\n .replace(/\\s*,\\s*/g, ', ')\n .replace(/\\s+/g, ' ')\n .replace(/ \\{ /g, ' { ')\n .replace(/ \\} /g, ' } ')\n .replace(/\\s+from\\s+/g, ' from ')\n .trim()\n // Corrige espaços colapsados em torno de { e }\n .replace(/\\{ \\s+/g, '{ ')\n .replace(/\\s+ \\}/g, ' }')\n\n return fixer.replaceText(node, importText)\n },\n })\n },\n }\n },\n}\n","import type { Rule } from 'eslint'\n\n\nexport const sortImports: Rule.RuleModule = {\n meta: {\n type: 'layout',\n docs: {\n description: 'Sort imports by group and line length descending, collapsing multiline imports',\n },\n fixable: 'code',\n schema: [],\n messages: {\n sortRequired: 'As importações devem estar ordenadas por grupo e tamanho da linha, ocupando uma única linha.',\n },\n },\n create(context) {\n return {\n Program(node) {\n const sourceCode = context.sourceCode\n const content = sourceCode.text\n\n const newContent = getSortedContent(content)\n\n if (newContent && newContent !== content) {\n context.report({\n node,\n messageId: 'sortRequired',\n fix(fixer) {\n return fixer.replaceText(node, newContent)\n },\n })\n }\n },\n }\n },\n}\n\n// ---------------------------------------------------------------------------\n// Tipos auxiliares\n// ---------------------------------------------------------------------------\ntype TImportItem = {\n line: string\n from: string\n}\n\n// ---------------------------------------------------------------------------\n// Lógica Core - exportada para teste e reutilização\n// ---------------------------------------------------------------------------\nexport function getSortedContent(content: string): string | null {\n const collapseMultiline = (match: string) => {\n return match\n .replace(/\\/\\*[\\s\\S]*?\\*\\//g, '')\n .replace(/\\/\\/.*$/gm, '')\n .replace(/\\s+/g, ' ')\n }\n\n const normalizedContent = content\n .replace(/import\\s+([^'\";]+?)\\s+from\\s+(['\"][^'\"]+['\"])/g, collapseMultiline)\n // Apenas re-exports válidos: export { ... } from / export * from / export * as X from / export type { ... } from\n .replace(/export\\s+(?:type\\s+)?(?:\\{[^}]*\\}|\\*(?:\\s+as\\s+\\w+)?)\\s+from\\s+(['\"][^'\"]+['\"])/g, collapseMultiline)\n\n const lines: string[] = normalizedContent.split('\\n')\n\n const directiveLines: string[] = []\n const reactFirstLine: string[] = []\n const externalLines: TImportItem[] = []\n const internalLines: TImportItem[] = []\n const reExportLines: TImportItem[] = []\n const nonImportLines: string[] = []\n\n let inImportBlock = false\n let canAcceptDirectives = true\n\n for (let i = 0; i < lines.length; i++) {\n const trimmed = lines[i].trim()\n\n if (\n /^(function|const|let|var|class|interface|enum)\\b/.test(trimmed) ||\n (/^(type|export)\\b/.test(trimmed) && !/ from ['\"]/.test(trimmed) && !/^export\\s*\\{[^}]*\\}\\s*from/.test(trimmed) && !/^export\\s*\\*/.test(trimmed)) ||\n trimmed.startsWith('}')\n ) {\n canAcceptDirectives = false\n }\n\n if (inImportBlock && trimmed === '') continue\n\n if (canAcceptDirectives && /^['\"]use (client|server|strict)['\"];?$/.test(trimmed)) {\n directiveLines.push(lines[i])\n continue\n }\n\n const isReExport =\n /^export\\s+(type\\s+)?\\{[^}]*\\}\\s+from\\s+['\"]/.test(trimmed) ||\n /^export\\s+\\*(\\s+as\\s+\\w+)?\\s+from\\s+['\"]/.test(trimmed)\n\n if (isReExport) {\n const m = lines[i].match(/from\\s+['\"]([^'\"]+)['\"]/)\n if (m) reExportLines.push({ line: lines[i], from: m[1] })\n inImportBlock = true\n canAcceptDirectives = false\n continue\n }\n\n const isImport = /^import\\s/.test(trimmed) || /^import\\{/.test(trimmed)\n\n if (isImport) {\n inImportBlock = true\n canAcceptDirectives = false\n\n const fromMatch = lines[i].match(/from\\s+['\"]([^'\"]+)['\"]/)\n const seMatch = lines[i].match(/^import\\s+['\"]([^'\"]+)['\"]/)\n const from = fromMatch ? fromMatch[1] : seMatch ? seMatch[1] : null\n\n if (!from) {\n nonImportLines.push(lines[i])\n continue\n }\n\n const isTypeOnly = /^import\\s+type\\b/.test(trimmed)\n const isInternal = from.startsWith('.') || from.startsWith('@/')\n const isReactEntry = !isTypeOnly && (from === 'react' || from.startsWith('react/'))\n\n if (isReactEntry) {\n reactFirstLine.push(lines[i])\n } else if (isInternal) {\n internalLines.push({ line: lines[i], from })\n } else {\n externalLines.push({ line: lines[i], from })\n }\n continue\n }\n\n inImportBlock = false\n nonImportLines.push(lines[i])\n }\n\n if (reactFirstLine.length === 0 && externalLines.length === 0 && internalLines.length === 0 && reExportLines.length === 0) {\n return null\n }\n\n const byLengthDesc = (a: TImportItem, b: TImportItem) =>\n b.line.length !== a.line.length ? b.line.length - a.line.length : a.line.localeCompare(b.line)\n\n externalLines.sort(byLengthDesc)\n internalLines.sort(byLengthDesc)\n reExportLines.sort(byLengthDesc)\n\n const blocks: string[] = []\n\n if (directiveLines.length > 0) blocks.push(directiveLines.join('\\n'))\n\n const externalBlock = [...reactFirstLine, ...externalLines.map(i => i.line)]\n if (externalBlock.length > 0) {\n if (blocks.length > 0) blocks.push('')\n blocks.push(externalBlock.join('\\n'))\n }\n\n if (internalLines.length > 0) {\n if (blocks.length > 0) blocks.push('')\n blocks.push(internalLines.map(i => i.line).join('\\n'))\n }\n\n if (reExportLines.length > 0) {\n if (blocks.length > 0) blocks.push('')\n blocks.push(reExportLines.map(i => i.line).join('\\n'))\n }\n\n const rest = nonImportLines.join('\\n').replace(/^\\n+/, '')\n\n let newContent: string\n if (blocks.length > 0 && rest.length > 0) {\n newContent = blocks.join('\\n') + '\\n\\n\\n' + rest\n } else if (blocks.length > 0) {\n newContent = blocks.join('\\n') + '\\n'\n } else {\n newContent = rest\n }\n\n return newContent\n}\n","import { filenamePascalCase } from './rules/FilenamePascalCase'\nimport { noMultilineImports } from './rules/NoMultilineImports'\nimport { sortImports } from './rules/SortImports'\n\n\nexport const plugin = {\n meta: {\n name: 'eslint-plugin-ai-rules',\n version: '0.1.2',\n },\n rules: {\n 'filename-pascal-case': filenamePascalCase,\n 'no-multiline-imports': noMultilineImports,\n 'sort-imports': sortImports,\n },\n} as const\n"],"mappings":"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","names":["path","stylistic","tseslint"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -7,7 +7,7 @@ declare const recommended: eslint_config.Config[];
|
|
|
7
7
|
declare const plugin: {
|
|
8
8
|
readonly meta: {
|
|
9
9
|
readonly name: "eslint-plugin-ai-rules";
|
|
10
|
-
readonly version: "0.1.
|
|
10
|
+
readonly version: "0.1.2";
|
|
11
11
|
};
|
|
12
12
|
readonly rules: {
|
|
13
13
|
readonly 'filename-pascal-case': eslint.Rule.RuleModule;
|
|
@@ -28,7 +28,7 @@ declare const aiGuardPlugin: {
|
|
|
28
28
|
};
|
|
29
29
|
meta: {
|
|
30
30
|
readonly name: "eslint-plugin-ai-rules";
|
|
31
|
-
readonly version: "0.1.
|
|
31
|
+
readonly version: "0.1.2";
|
|
32
32
|
};
|
|
33
33
|
rules: {
|
|
34
34
|
readonly 'filename-pascal-case': eslint.Rule.RuleModule;
|
package/dist/index.d.ts
CHANGED
|
@@ -7,7 +7,7 @@ declare const recommended: eslint_config.Config[];
|
|
|
7
7
|
declare const plugin: {
|
|
8
8
|
readonly meta: {
|
|
9
9
|
readonly name: "eslint-plugin-ai-rules";
|
|
10
|
-
readonly version: "0.1.
|
|
10
|
+
readonly version: "0.1.2";
|
|
11
11
|
};
|
|
12
12
|
readonly rules: {
|
|
13
13
|
readonly 'filename-pascal-case': eslint.Rule.RuleModule;
|
|
@@ -28,7 +28,7 @@ declare const aiGuardPlugin: {
|
|
|
28
28
|
};
|
|
29
29
|
meta: {
|
|
30
30
|
readonly name: "eslint-plugin-ai-rules";
|
|
31
|
-
readonly version: "0.1.
|
|
31
|
+
readonly version: "0.1.2";
|
|
32
32
|
};
|
|
33
33
|
rules: {
|
|
34
34
|
readonly 'filename-pascal-case': eslint.Rule.RuleModule;
|
package/dist/index.js
CHANGED
|
@@ -20,12 +20,7 @@ var filenamePascalCase = {
|
|
|
20
20
|
// eslint 9 flat config
|
|
21
21
|
context.getPhysicalFilename?.() ?? context.physicalFilename ?? context.filename ?? ""
|
|
22
22
|
);
|
|
23
|
-
const
|
|
24
|
-
const normalizedPhysical = physicalFilename.split(path.sep).join("/");
|
|
25
|
-
const normalizedCwd = cwd.split(path.sep).join("/");
|
|
26
|
-
const relativePath = normalizedPhysical.startsWith(normalizedCwd) ? normalizedPhysical.slice(normalizedCwd.length + 1) : normalizedPhysical;
|
|
27
|
-
if (!relativePath.startsWith("src/") && !relativePath.startsWith("scripts/")) return {};
|
|
28
|
-
const basename = path.basename(relativePath);
|
|
23
|
+
const basename = path.basename(physicalFilename);
|
|
29
24
|
if (basename === "index.ts" || basename === "index.tsx" || basename === "index.js" || basename === "index.jsx") return {};
|
|
30
25
|
if (basename.endsWith(".d.ts")) return {};
|
|
31
26
|
if (basename.endsWith(".css")) return {};
|
|
@@ -213,7 +208,7 @@ function getSortedContent(content) {
|
|
|
213
208
|
var plugin = {
|
|
214
209
|
meta: {
|
|
215
210
|
name: "eslint-plugin-ai-rules",
|
|
216
|
-
version: "0.1.
|
|
211
|
+
version: "0.1.2"
|
|
217
212
|
},
|
|
218
213
|
rules: {
|
|
219
214
|
"filename-pascal-case": filenamePascalCase,
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/configs/recommended.ts","../src/rules/FilenamePascalCase.ts","../src/rules/NoMultilineImports.ts","../src/rules/SortImports.ts","../src/plugin.ts","../src/index.ts"],"sourcesContent":["import { createNodeResolver, importX } from 'eslint-plugin-import-x'\nimport stylistic from '@stylistic/eslint-plugin'\nimport { defineConfig } from 'eslint/config'\nimport tseslint from 'typescript-eslint'\n\nimport { plugin } from '../plugin'\n\n\nexport const recommended = defineConfig([\n {\n plugins: {\n '@stylistic': stylistic,\n 'import-x': importX,\n 'ai-rules': plugin,\n '@typescript-eslint': tseslint.plugin,\n },\n settings: {\n 'import-x/resolver-next': [\n createNodeResolver({\n extensions: ['.ts', '.tsx', '.d.ts', '.js', '.jsx'],\n }),\n ],\n },\n rules: {\n 'ai-rules/sort-imports': 'error',\n 'ai-rules/no-multiline-imports': 'error',\n 'ai-rules/filename-pascal-case': 'error',\n 'max-len': 'off',\n 'import-x/no-duplicates': 'error',\n 'import-x/no-default-export': 'off',\n '@stylistic/semi': ['error', 'never'],\n '@typescript-eslint/consistent-type-imports': [\n 'error',\n { prefer: 'type-imports' },\n ],\n '@typescript-eslint/no-unused-vars': [\n 'error',\n { argsIgnorePattern: '^_', varsIgnorePattern: '^_' },\n ],\n '@typescript-eslint/naming-convention': [\n 'error',\n { selector: 'interface', format: ['PascalCase'], prefix: ['I'] },\n { selector: 'typeAlias', format: ['PascalCase'], prefix: ['T'] },\n ],\n },\n },\n])\n\nexport default recommended\n","import type { Rule } from 'eslint'\nimport path from 'node:path'\n\n\nexport const filenamePascalCase: Rule.RuleModule = {\n meta: {\n type: 'suggestion',\n docs: { description: 'Enforce PascalCase for file names' },\n schema: [],\n messages: {\n invalid: 'File name \"{{basename}}\" must be in PascalCase. Rename to \"{{expected}}\".',\n },\n },\n create(context) {\n const physicalFilename =\n // eslint 9 flat config\n (context as unknown as { getPhysicalFilename?: () => string }).getPhysicalFilename?.() ??\n (context as unknown as { physicalFilename?: string }).physicalFilename ??\n context.filename ??\n ''\n\n const cwd =\n (context as unknown as { getCwd?: () => string }).getCwd?.() ?? process.cwd()\n\n // Normaliza para posix para comparar com src/ e scripts/\n const normalizedPhysical = physicalFilename.split(path.sep).join('/')\n const normalizedCwd = cwd.split(path.sep).join('/')\n\n const relativePath = normalizedPhysical.startsWith(normalizedCwd)\n ? normalizedPhysical.slice(normalizedCwd.length + 1)\n : normalizedPhysical\n\n if (!relativePath.startsWith('src/') && !relativePath.startsWith('scripts/')) return {}\n\n const basename = path.basename(relativePath)\n\n if (basename === 'index.ts' || basename === 'index.tsx' || basename === 'index.js' || basename === 'index.jsx') return {}\n if (basename.endsWith('.d.ts')) return {}\n if (basename.endsWith('.css')) return {}\n\n const ext = path.extname(basename) // .ts\n // Para casos como Component.spec.ts -> ext .ts, nameWithoutExt Component.spec - tratamos mantendo só base antes do primeiro ponto?\n // Mantemos compat com original: remove apenas última extensão .ts/.tsx/.js/.jsx\n const nameWithoutExt = basename.replace(/\\.(ts|tsx|js|jsx)$/, '')\n\n if (!/^[A-Z][a-zA-Z0-9]*$/.test(nameWithoutExt)) {\n const pascalCase = nameWithoutExt\n .split(/[-_]/)\n .map(part => part.charAt(0).toUpperCase() + part.slice(1))\n .join('')\n\n // Preserva extensão original (ex: .tsx)\n const originalExt = basename.slice(nameWithoutExt.length) || ext\n const expected = `${pascalCase}${originalExt}`\n\n context.report({\n loc: { line: 1, column: 0 },\n messageId: 'invalid',\n data: { basename, expected },\n })\n }\n\n return {}\n },\n}\n","import type { Rule } from 'eslint'\n\n\nexport const noMultilineImports: Rule.RuleModule = {\n meta: {\n type: 'layout',\n fixable: 'code',\n docs: {\n description: 'Disallow multiline imports',\n },\n schema: [],\n messages: {\n multilineImport: 'Imports must be written on a single line.',\n },\n },\n\n create(context) {\n const sourceCode = context.sourceCode\n\n return {\n ImportDeclaration(node) {\n const text = sourceCode.getText(node)\n\n if (!text.includes('\\n')) {\n return\n }\n\n context.report({\n node,\n messageId: 'multilineImport',\n\n fix(fixer) {\n const firstToken = sourceCode.getFirstToken(node)\n const lastToken = sourceCode.getLastToken(node)\n\n if (!firstToken || !lastToken) {\n return null\n }\n\n let importText = sourceCode.getText(node)\n\n importText = importText\n .replace(/\\s*\\{\\s*/g, ' { ')\n .replace(/\\s*\\}\\s*/g, ' } ')\n .replace(/\\s*,\\s*/g, ', ')\n .replace(/\\s+/g, ' ')\n .replace(/ \\{ /g, ' { ')\n .replace(/ \\} /g, ' } ')\n .replace(/\\s+from\\s+/g, ' from ')\n .trim()\n // Corrige espaços colapsados em torno de { e }\n .replace(/\\{ \\s+/g, '{ ')\n .replace(/\\s+ \\}/g, ' }')\n\n return fixer.replaceText(node, importText)\n },\n })\n },\n }\n },\n}\n","import type { Rule } from 'eslint'\n\n\nexport const sortImports: Rule.RuleModule = {\n meta: {\n type: 'layout',\n docs: {\n description: 'Sort imports by group and line length descending, collapsing multiline imports',\n },\n fixable: 'code',\n schema: [],\n messages: {\n sortRequired: 'As importações devem estar ordenadas por grupo e tamanho da linha, ocupando uma única linha.',\n },\n },\n create(context) {\n return {\n Program(node) {\n const sourceCode = context.sourceCode\n const content = sourceCode.text\n\n const newContent = getSortedContent(content)\n\n if (newContent && newContent !== content) {\n context.report({\n node,\n messageId: 'sortRequired',\n fix(fixer) {\n return fixer.replaceText(node, newContent)\n },\n })\n }\n },\n }\n },\n}\n\n// ---------------------------------------------------------------------------\n// Tipos auxiliares\n// ---------------------------------------------------------------------------\ntype TImportItem = {\n line: string\n from: string\n}\n\n// ---------------------------------------------------------------------------\n// Lógica Core - exportada para teste e reutilização\n// ---------------------------------------------------------------------------\nexport function getSortedContent(content: string): string | null {\n const collapseMultiline = (match: string) => {\n return match\n .replace(/\\/\\*[\\s\\S]*?\\*\\//g, '')\n .replace(/\\/\\/.*$/gm, '')\n .replace(/\\s+/g, ' ')\n }\n\n const normalizedContent = content\n .replace(/import\\s+([^'\";]+?)\\s+from\\s+(['\"][^'\"]+['\"])/g, collapseMultiline)\n // Apenas re-exports válidos: export { ... } from / export * from / export * as X from / export type { ... } from\n .replace(/export\\s+(?:type\\s+)?(?:\\{[^}]*\\}|\\*(?:\\s+as\\s+\\w+)?)\\s+from\\s+(['\"][^'\"]+['\"])/g, collapseMultiline)\n\n const lines: string[] = normalizedContent.split('\\n')\n\n const directiveLines: string[] = []\n const reactFirstLine: string[] = []\n const externalLines: TImportItem[] = []\n const internalLines: TImportItem[] = []\n const reExportLines: TImportItem[] = []\n const nonImportLines: string[] = []\n\n let inImportBlock = false\n let canAcceptDirectives = true\n\n for (let i = 0; i < lines.length; i++) {\n const trimmed = lines[i].trim()\n\n if (\n /^(function|const|let|var|class|interface|enum)\\b/.test(trimmed) ||\n (/^(type|export)\\b/.test(trimmed) && !/ from ['\"]/.test(trimmed) && !/^export\\s*\\{[^}]*\\}\\s*from/.test(trimmed) && !/^export\\s*\\*/.test(trimmed)) ||\n trimmed.startsWith('}')\n ) {\n canAcceptDirectives = false\n }\n\n if (inImportBlock && trimmed === '') continue\n\n if (canAcceptDirectives && /^['\"]use (client|server|strict)['\"];?$/.test(trimmed)) {\n directiveLines.push(lines[i])\n continue\n }\n\n const isReExport =\n /^export\\s+(type\\s+)?\\{[^}]*\\}\\s+from\\s+['\"]/.test(trimmed) ||\n /^export\\s+\\*(\\s+as\\s+\\w+)?\\s+from\\s+['\"]/.test(trimmed)\n\n if (isReExport) {\n const m = lines[i].match(/from\\s+['\"]([^'\"]+)['\"]/)\n if (m) reExportLines.push({ line: lines[i], from: m[1] })\n inImportBlock = true\n canAcceptDirectives = false\n continue\n }\n\n const isImport = /^import\\s/.test(trimmed) || /^import\\{/.test(trimmed)\n\n if (isImport) {\n inImportBlock = true\n canAcceptDirectives = false\n\n const fromMatch = lines[i].match(/from\\s+['\"]([^'\"]+)['\"]/)\n const seMatch = lines[i].match(/^import\\s+['\"]([^'\"]+)['\"]/)\n const from = fromMatch ? fromMatch[1] : seMatch ? seMatch[1] : null\n\n if (!from) {\n nonImportLines.push(lines[i])\n continue\n }\n\n const isTypeOnly = /^import\\s+type\\b/.test(trimmed)\n const isInternal = from.startsWith('.') || from.startsWith('@/')\n const isReactEntry = !isTypeOnly && (from === 'react' || from.startsWith('react/'))\n\n if (isReactEntry) {\n reactFirstLine.push(lines[i])\n } else if (isInternal) {\n internalLines.push({ line: lines[i], from })\n } else {\n externalLines.push({ line: lines[i], from })\n }\n continue\n }\n\n inImportBlock = false\n nonImportLines.push(lines[i])\n }\n\n if (reactFirstLine.length === 0 && externalLines.length === 0 && internalLines.length === 0 && reExportLines.length === 0) {\n return null\n }\n\n const byLengthDesc = (a: TImportItem, b: TImportItem) =>\n b.line.length !== a.line.length ? b.line.length - a.line.length : a.line.localeCompare(b.line)\n\n externalLines.sort(byLengthDesc)\n internalLines.sort(byLengthDesc)\n reExportLines.sort(byLengthDesc)\n\n const blocks: string[] = []\n\n if (directiveLines.length > 0) blocks.push(directiveLines.join('\\n'))\n\n const externalBlock = [...reactFirstLine, ...externalLines.map(i => i.line)]\n if (externalBlock.length > 0) {\n if (blocks.length > 0) blocks.push('')\n blocks.push(externalBlock.join('\\n'))\n }\n\n if (internalLines.length > 0) {\n if (blocks.length > 0) blocks.push('')\n blocks.push(internalLines.map(i => i.line).join('\\n'))\n }\n\n if (reExportLines.length > 0) {\n if (blocks.length > 0) blocks.push('')\n blocks.push(reExportLines.map(i => i.line).join('\\n'))\n }\n\n const rest = nonImportLines.join('\\n').replace(/^\\n+/, '')\n\n let newContent: string\n if (blocks.length > 0 && rest.length > 0) {\n newContent = blocks.join('\\n') + '\\n\\n\\n' + rest\n } else if (blocks.length > 0) {\n newContent = blocks.join('\\n') + '\\n'\n } else {\n newContent = rest\n }\n\n return newContent\n}\n","import { filenamePascalCase } from './rules/FilenamePascalCase'\nimport { noMultilineImports } from './rules/NoMultilineImports'\nimport { sortImports } from './rules/SortImports'\n\n\nexport const plugin = {\n meta: {\n name: 'eslint-plugin-ai-rules',\n version: '0.1.0',\n },\n rules: {\n 'filename-pascal-case': filenamePascalCase,\n 'no-multiline-imports': noMultilineImports,\n 'sort-imports': sortImports,\n },\n} as const\n","import { recommended } from './configs/recommended'\nimport { plugin } from './plugin'\n\nexport * from './rules/index'\n\n\nconst aiGuardPlugin = {\n ...plugin,\n configs: {\n recommended,\n },\n}\n\nexport default aiGuardPlugin\nexport { plugin }\nexport { recommended }"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/configs/recommended.ts","../src/rules/FilenamePascalCase.ts","../src/rules/NoMultilineImports.ts","../src/rules/SortImports.ts","../src/plugin.ts","../src/index.ts"],"sourcesContent":["import { createNodeResolver, importX } from 'eslint-plugin-import-x'\nimport stylistic from '@stylistic/eslint-plugin'\nimport { defineConfig } from 'eslint/config'\nimport tseslint from 'typescript-eslint'\n\nimport { plugin } from '../plugin'\n\n\nexport const recommended = defineConfig([\n {\n plugins: {\n '@stylistic': stylistic,\n 'import-x': importX,\n 'ai-rules': plugin,\n '@typescript-eslint': tseslint.plugin,\n },\n settings: {\n 'import-x/resolver-next': [\n createNodeResolver({\n extensions: ['.ts', '.tsx', '.d.ts', '.js', '.jsx'],\n }),\n ],\n },\n rules: {\n 'ai-rules/sort-imports': 'error',\n 'ai-rules/no-multiline-imports': 'error',\n 'ai-rules/filename-pascal-case': 'error',\n 'max-len': 'off',\n 'import-x/no-duplicates': 'error',\n 'import-x/no-default-export': 'off',\n '@stylistic/semi': ['error', 'never'],\n '@typescript-eslint/consistent-type-imports': [\n 'error',\n { prefer: 'type-imports' },\n ],\n '@typescript-eslint/no-unused-vars': [\n 'error',\n { argsIgnorePattern: '^_', varsIgnorePattern: '^_' },\n ],\n '@typescript-eslint/naming-convention': [\n 'error',\n { selector: 'interface', format: ['PascalCase'], prefix: ['I'] },\n { selector: 'typeAlias', format: ['PascalCase'], prefix: ['T'] },\n ],\n },\n },\n])\n\nexport default recommended\n","import type { Rule } from 'eslint'\nimport path from 'node:path'\n\n\nexport const filenamePascalCase: Rule.RuleModule = {\n meta: {\n type: 'suggestion',\n docs: { description: 'Enforce PascalCase for file names' },\n schema: [],\n messages: {\n invalid: 'File name \"{{basename}}\" must be in PascalCase. Rename to \"{{expected}}\".',\n },\n },\n create(context) {\n const physicalFilename =\n // eslint 9 flat config\n (context as unknown as { getPhysicalFilename?: () => string }).getPhysicalFilename?.() ??\n (context as unknown as { physicalFilename?: string }).physicalFilename ??\n context.filename ??\n ''\n\n const basename = path.basename(physicalFilename)\n\n if (basename === 'index.ts' || basename === 'index.tsx' || basename === 'index.js' || basename === 'index.jsx') return {}\n if (basename.endsWith('.d.ts')) return {}\n if (basename.endsWith('.css')) return {}\n\n const ext = path.extname(basename) // .ts\n // Para casos como Component.spec.ts -> ext .ts, nameWithoutExt Component.spec - tratamos mantendo só base antes do primeiro ponto?\n // Mantemos compat com original: remove apenas última extensão .ts/.tsx/.js/.jsx\n const nameWithoutExt = basename.replace(/\\.(ts|tsx|js|jsx)$/, '')\n\n if (!/^[A-Z][a-zA-Z0-9]*$/.test(nameWithoutExt)) {\n const pascalCase = nameWithoutExt\n .split(/[-_]/)\n .map(part => part.charAt(0).toUpperCase() + part.slice(1))\n .join('')\n\n // Preserva extensão original (ex: .tsx)\n const originalExt = basename.slice(nameWithoutExt.length) || ext\n const expected = `${pascalCase}${originalExt}`\n\n context.report({\n loc: { line: 1, column: 0 },\n messageId: 'invalid',\n data: { basename, expected },\n })\n }\n\n return {}\n },\n}\n","import type { Rule } from 'eslint'\n\n\nexport const noMultilineImports: Rule.RuleModule = {\n meta: {\n type: 'layout',\n fixable: 'code',\n docs: {\n description: 'Disallow multiline imports',\n },\n schema: [],\n messages: {\n multilineImport: 'Imports must be written on a single line.',\n },\n },\n\n create(context) {\n const sourceCode = context.sourceCode\n\n return {\n ImportDeclaration(node) {\n const text = sourceCode.getText(node)\n\n if (!text.includes('\\n')) {\n return\n }\n\n context.report({\n node,\n messageId: 'multilineImport',\n\n fix(fixer) {\n const firstToken = sourceCode.getFirstToken(node)\n const lastToken = sourceCode.getLastToken(node)\n\n if (!firstToken || !lastToken) {\n return null\n }\n\n let importText = sourceCode.getText(node)\n\n importText = importText\n .replace(/\\s*\\{\\s*/g, ' { ')\n .replace(/\\s*\\}\\s*/g, ' } ')\n .replace(/\\s*,\\s*/g, ', ')\n .replace(/\\s+/g, ' ')\n .replace(/ \\{ /g, ' { ')\n .replace(/ \\} /g, ' } ')\n .replace(/\\s+from\\s+/g, ' from ')\n .trim()\n // Corrige espaços colapsados em torno de { e }\n .replace(/\\{ \\s+/g, '{ ')\n .replace(/\\s+ \\}/g, ' }')\n\n return fixer.replaceText(node, importText)\n },\n })\n },\n }\n },\n}\n","import type { Rule } from 'eslint'\n\n\nexport const sortImports: Rule.RuleModule = {\n meta: {\n type: 'layout',\n docs: {\n description: 'Sort imports by group and line length descending, collapsing multiline imports',\n },\n fixable: 'code',\n schema: [],\n messages: {\n sortRequired: 'As importações devem estar ordenadas por grupo e tamanho da linha, ocupando uma única linha.',\n },\n },\n create(context) {\n return {\n Program(node) {\n const sourceCode = context.sourceCode\n const content = sourceCode.text\n\n const newContent = getSortedContent(content)\n\n if (newContent && newContent !== content) {\n context.report({\n node,\n messageId: 'sortRequired',\n fix(fixer) {\n return fixer.replaceText(node, newContent)\n },\n })\n }\n },\n }\n },\n}\n\n// ---------------------------------------------------------------------------\n// Tipos auxiliares\n// ---------------------------------------------------------------------------\ntype TImportItem = {\n line: string\n from: string\n}\n\n// ---------------------------------------------------------------------------\n// Lógica Core - exportada para teste e reutilização\n// ---------------------------------------------------------------------------\nexport function getSortedContent(content: string): string | null {\n const collapseMultiline = (match: string) => {\n return match\n .replace(/\\/\\*[\\s\\S]*?\\*\\//g, '')\n .replace(/\\/\\/.*$/gm, '')\n .replace(/\\s+/g, ' ')\n }\n\n const normalizedContent = content\n .replace(/import\\s+([^'\";]+?)\\s+from\\s+(['\"][^'\"]+['\"])/g, collapseMultiline)\n // Apenas re-exports válidos: export { ... } from / export * from / export * as X from / export type { ... } from\n .replace(/export\\s+(?:type\\s+)?(?:\\{[^}]*\\}|\\*(?:\\s+as\\s+\\w+)?)\\s+from\\s+(['\"][^'\"]+['\"])/g, collapseMultiline)\n\n const lines: string[] = normalizedContent.split('\\n')\n\n const directiveLines: string[] = []\n const reactFirstLine: string[] = []\n const externalLines: TImportItem[] = []\n const internalLines: TImportItem[] = []\n const reExportLines: TImportItem[] = []\n const nonImportLines: string[] = []\n\n let inImportBlock = false\n let canAcceptDirectives = true\n\n for (let i = 0; i < lines.length; i++) {\n const trimmed = lines[i].trim()\n\n if (\n /^(function|const|let|var|class|interface|enum)\\b/.test(trimmed) ||\n (/^(type|export)\\b/.test(trimmed) && !/ from ['\"]/.test(trimmed) && !/^export\\s*\\{[^}]*\\}\\s*from/.test(trimmed) && !/^export\\s*\\*/.test(trimmed)) ||\n trimmed.startsWith('}')\n ) {\n canAcceptDirectives = false\n }\n\n if (inImportBlock && trimmed === '') continue\n\n if (canAcceptDirectives && /^['\"]use (client|server|strict)['\"];?$/.test(trimmed)) {\n directiveLines.push(lines[i])\n continue\n }\n\n const isReExport =\n /^export\\s+(type\\s+)?\\{[^}]*\\}\\s+from\\s+['\"]/.test(trimmed) ||\n /^export\\s+\\*(\\s+as\\s+\\w+)?\\s+from\\s+['\"]/.test(trimmed)\n\n if (isReExport) {\n const m = lines[i].match(/from\\s+['\"]([^'\"]+)['\"]/)\n if (m) reExportLines.push({ line: lines[i], from: m[1] })\n inImportBlock = true\n canAcceptDirectives = false\n continue\n }\n\n const isImport = /^import\\s/.test(trimmed) || /^import\\{/.test(trimmed)\n\n if (isImport) {\n inImportBlock = true\n canAcceptDirectives = false\n\n const fromMatch = lines[i].match(/from\\s+['\"]([^'\"]+)['\"]/)\n const seMatch = lines[i].match(/^import\\s+['\"]([^'\"]+)['\"]/)\n const from = fromMatch ? fromMatch[1] : seMatch ? seMatch[1] : null\n\n if (!from) {\n nonImportLines.push(lines[i])\n continue\n }\n\n const isTypeOnly = /^import\\s+type\\b/.test(trimmed)\n const isInternal = from.startsWith('.') || from.startsWith('@/')\n const isReactEntry = !isTypeOnly && (from === 'react' || from.startsWith('react/'))\n\n if (isReactEntry) {\n reactFirstLine.push(lines[i])\n } else if (isInternal) {\n internalLines.push({ line: lines[i], from })\n } else {\n externalLines.push({ line: lines[i], from })\n }\n continue\n }\n\n inImportBlock = false\n nonImportLines.push(lines[i])\n }\n\n if (reactFirstLine.length === 0 && externalLines.length === 0 && internalLines.length === 0 && reExportLines.length === 0) {\n return null\n }\n\n const byLengthDesc = (a: TImportItem, b: TImportItem) =>\n b.line.length !== a.line.length ? b.line.length - a.line.length : a.line.localeCompare(b.line)\n\n externalLines.sort(byLengthDesc)\n internalLines.sort(byLengthDesc)\n reExportLines.sort(byLengthDesc)\n\n const blocks: string[] = []\n\n if (directiveLines.length > 0) blocks.push(directiveLines.join('\\n'))\n\n const externalBlock = [...reactFirstLine, ...externalLines.map(i => i.line)]\n if (externalBlock.length > 0) {\n if (blocks.length > 0) blocks.push('')\n blocks.push(externalBlock.join('\\n'))\n }\n\n if (internalLines.length > 0) {\n if (blocks.length > 0) blocks.push('')\n blocks.push(internalLines.map(i => i.line).join('\\n'))\n }\n\n if (reExportLines.length > 0) {\n if (blocks.length > 0) blocks.push('')\n blocks.push(reExportLines.map(i => i.line).join('\\n'))\n }\n\n const rest = nonImportLines.join('\\n').replace(/^\\n+/, '')\n\n let newContent: string\n if (blocks.length > 0 && rest.length > 0) {\n newContent = blocks.join('\\n') + '\\n\\n\\n' + rest\n } else if (blocks.length > 0) {\n newContent = blocks.join('\\n') + '\\n'\n } else {\n newContent = rest\n }\n\n return newContent\n}\n","import { filenamePascalCase } from './rules/FilenamePascalCase'\nimport { noMultilineImports } from './rules/NoMultilineImports'\nimport { sortImports } from './rules/SortImports'\n\n\nexport const plugin = {\n meta: {\n name: 'eslint-plugin-ai-rules',\n version: '0.1.2',\n },\n rules: {\n 'filename-pascal-case': filenamePascalCase,\n 'no-multiline-imports': noMultilineImports,\n 'sort-imports': sortImports,\n },\n} as const\n","import { recommended } from './configs/recommended'\nimport { plugin } from './plugin'\n\nexport * from './rules/index'\n\n\nconst aiGuardPlugin = {\n ...plugin,\n configs: {\n recommended,\n },\n}\n\nexport default aiGuardPlugin\nexport { plugin }\nexport { recommended }"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eslint-plugin-ai-rules",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "ESLint plugin com regras úteis para projetos com ESLint - AI Rules",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -18,15 +18,15 @@
|
|
|
18
18
|
],
|
|
19
19
|
"scripts": {
|
|
20
20
|
"clean": "rm -rf dist",
|
|
21
|
-
"prebuild": "
|
|
21
|
+
"prebuild": "yarn clean",
|
|
22
22
|
"build": "tsup",
|
|
23
23
|
"dev": "tsup --watch",
|
|
24
|
-
"prelint": "
|
|
24
|
+
"prelint": "yarn build",
|
|
25
25
|
"lint": "eslint .",
|
|
26
26
|
"typecheck": "tsc --noEmit",
|
|
27
|
-
"verify": "
|
|
28
|
-
"prepack": "
|
|
29
|
-
"prepublishOnly": "git diff --quiet || (echo '✖ uncommitted changes — commit before publishing' && exit 1) &&
|
|
27
|
+
"verify": "yarn typecheck && yarn build && yarn lint && npm pack --dry-run",
|
|
28
|
+
"prepack": "yarn build",
|
|
29
|
+
"prepublishOnly": "git diff --quiet || (echo '✖ uncommitted changes — commit before publishing' && exit 1) && yarn verify",
|
|
30
30
|
"postpublish": "echo '✔ published. Run: git push --follow-tags'"
|
|
31
31
|
},
|
|
32
32
|
"publishConfig": {
|