eslint-plugin-ai-rules 0.1.3 → 0.1.4

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 CHANGED
@@ -161,14 +161,22 @@ Brief overview. See source in `src/rules/` for full logic.
161
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
- - **Ignores:** `index.{ts,tsx,js,jsx}`, `*.d.ts`, `*.css`
164
+ - **Ignores:** `index.{ts,tsx,js,jsx}`, `*.d.ts`, `*.css`, dotfiles
165
165
  - **Checks:** `path.basename` from `getPhysicalFilename()` / `context.filename`, `^[A-Z][a-zA-Z0-9]*$`, `kebab/snake -> PascalCase` conversion
166
+ - **Options:** `ignore` — array of regex strings matched against the basename and the path relative to `cwd` (same idea as `unicorn/filename-case`). Use it for tool-mandated names like `*.config.*`, tests/specs, generated or vendored code.
166
167
 
167
168
  ```ts
168
169
  // my-component.ts ❌ File name "my-component.ts" must be in PascalCase. Rename to "MyComponent.ts".
169
170
  // MyComponent.ts ✅
170
171
  ```
171
172
 
173
+ ```ts
174
+ 'ai-rules/filename-pascal-case': [
175
+ 'error',
176
+ { ignore: ['\\.config\\.', '\\.(test|spec)\\.', '^generated/'] },
177
+ ]
178
+ ```
179
+
172
180
  To scope or disable it, use your own config (as this repo does in `eslint.config.mjs`):
173
181
 
174
182
  ```ts
@@ -248,7 +256,7 @@ You passed an absolute path outside the config's `basePath`. Run `npx eslint .`
248
256
  This plugin exports `defineConfig` arrays. It does not support legacy `.eslintrc`. Use `eslint.config.{js,ts}`.
249
257
 
250
258
  **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).
259
+ The rule checks every linted file. Either pass the `ignore` option (`{ ignore: ['\\.config\\.', '\\.(test|spec)\\.'] }`) or disable `ai-rules/filename-pascal-case` for `**/*.test.*`, `**/*.spec.*`, `*.config.*`, generated code, or your own fixtures via an override (see Quick Start).
252
260
 
253
261
  ## Development
254
262
 
package/dist/index.cjs CHANGED
@@ -51,17 +51,43 @@ var filenamePascalCase = {
51
51
  meta: {
52
52
  type: "suggestion",
53
53
  docs: { description: "Enforce PascalCase for file names" },
54
- schema: [],
54
+ schema: [
55
+ {
56
+ type: "object",
57
+ properties: {
58
+ ignore: { type: "array", items: { type: "string" } }
59
+ },
60
+ additionalProperties: false
61
+ }
62
+ ],
55
63
  messages: {
56
64
  invalid: 'File name "{{basename}}" must be in PascalCase. Rename to "{{expected}}".'
57
65
  }
58
66
  },
59
67
  create(context) {
68
+ const options = context.options[0] ?? {};
69
+ const ignoreMatchers = (options.ignore ?? []).flatMap((pattern) => {
70
+ try {
71
+ return [pattern instanceof RegExp ? pattern : new RegExp(pattern)];
72
+ } catch {
73
+ return [];
74
+ }
75
+ });
60
76
  const physicalFilename = (
61
77
  // eslint 9 flat config
62
78
  context.getPhysicalFilename?.() ?? context.physicalFilename ?? context.filename ?? ""
63
79
  );
64
80
  const basename = import_node_path.default.basename(physicalFilename);
81
+ const cwd = process.cwd();
82
+ const relativePath = physicalFilename.startsWith(cwd) ? physicalFilename.slice(cwd.length + 1) : physicalFilename;
83
+ const isIgnored = ignoreMatchers.some(
84
+ (matcher) => [basename, relativePath].some((target) => {
85
+ matcher.lastIndex = 0;
86
+ return matcher.test(target);
87
+ })
88
+ );
89
+ if (isIgnored) return {};
90
+ if (basename.startsWith(".")) return {};
65
91
  if (basename === "index.ts" || basename === "index.tsx" || basename === "index.js" || basename === "index.jsx") return {};
66
92
  if (basename.endsWith(".d.ts")) return {};
67
93
  if (basename.endsWith(".css")) return {};
@@ -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 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"]}
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\ninterface IFilenamePascalCaseOptions {\n ignore?: Array<string | RegExp>\n}\n\nexport const filenamePascalCase: Rule.RuleModule = {\n meta: {\n type: 'suggestion',\n docs: { description: 'Enforce PascalCase for file names' },\n schema: [\n {\n type: 'object',\n properties: {\n ignore: { type: 'array', items: { type: 'string' } },\n },\n additionalProperties: false,\n },\n ],\n messages: {\n invalid: 'File name \"{{basename}}\" must be in PascalCase. Rename to \"{{expected}}\".',\n },\n },\n create(context) {\n const options = (context.options[0] ?? {}) as IFilenamePascalCaseOptions\n const ignoreMatchers = (options.ignore ?? []).flatMap((pattern) => {\n try {\n return [pattern instanceof RegExp ? pattern : new RegExp(pattern)]\n } catch {\n return []\n }\n })\n\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 const cwd = process.cwd()\n const relativePath = physicalFilename.startsWith(cwd)\n ? physicalFilename.slice(cwd.length + 1)\n : physicalFilename\n\n const isIgnored = ignoreMatchers.some((matcher) =>\n [basename, relativePath].some((target) => {\n matcher.lastIndex = 0\n return matcher.test(target)\n }),\n )\n\n if (isIgnored) return {}\n if (basename.startsWith('.')) return {}\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.js CHANGED
@@ -10,17 +10,43 @@ var filenamePascalCase = {
10
10
  meta: {
11
11
  type: "suggestion",
12
12
  docs: { description: "Enforce PascalCase for file names" },
13
- schema: [],
13
+ schema: [
14
+ {
15
+ type: "object",
16
+ properties: {
17
+ ignore: { type: "array", items: { type: "string" } }
18
+ },
19
+ additionalProperties: false
20
+ }
21
+ ],
14
22
  messages: {
15
23
  invalid: 'File name "{{basename}}" must be in PascalCase. Rename to "{{expected}}".'
16
24
  }
17
25
  },
18
26
  create(context) {
27
+ const options = context.options[0] ?? {};
28
+ const ignoreMatchers = (options.ignore ?? []).flatMap((pattern) => {
29
+ try {
30
+ return [pattern instanceof RegExp ? pattern : new RegExp(pattern)];
31
+ } catch {
32
+ return [];
33
+ }
34
+ });
19
35
  const physicalFilename = (
20
36
  // eslint 9 flat config
21
37
  context.getPhysicalFilename?.() ?? context.physicalFilename ?? context.filename ?? ""
22
38
  );
23
39
  const basename = path.basename(physicalFilename);
40
+ const cwd = process.cwd();
41
+ const relativePath = physicalFilename.startsWith(cwd) ? physicalFilename.slice(cwd.length + 1) : physicalFilename;
42
+ const isIgnored = ignoreMatchers.some(
43
+ (matcher) => [basename, relativePath].some((target) => {
44
+ matcher.lastIndex = 0;
45
+ return matcher.test(target);
46
+ })
47
+ );
48
+ if (isIgnored) return {};
49
+ if (basename.startsWith(".")) return {};
24
50
  if (basename === "index.ts" || basename === "index.tsx" || basename === "index.js" || basename === "index.jsx") return {};
25
51
  if (basename.endsWith(".d.ts")) return {};
26
52
  if (basename.endsWith(".css")) return {};
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 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":[]}
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\ninterface IFilenamePascalCaseOptions {\n ignore?: Array<string | RegExp>\n}\n\nexport const filenamePascalCase: Rule.RuleModule = {\n meta: {\n type: 'suggestion',\n docs: { description: 'Enforce PascalCase for file names' },\n schema: [\n {\n type: 'object',\n properties: {\n ignore: { type: 'array', items: { type: 'string' } },\n },\n additionalProperties: false,\n },\n ],\n messages: {\n invalid: 'File name \"{{basename}}\" must be in PascalCase. Rename to \"{{expected}}\".',\n },\n },\n create(context) {\n const options = (context.options[0] ?? {}) as IFilenamePascalCaseOptions\n const ignoreMatchers = (options.ignore ?? []).flatMap((pattern) => {\n try {\n return [pattern instanceof RegExp ? pattern : new RegExp(pattern)]\n } catch {\n return []\n }\n })\n\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 const cwd = process.cwd()\n const relativePath = physicalFilename.startsWith(cwd)\n ? physicalFilename.slice(cwd.length + 1)\n : physicalFilename\n\n const isIgnored = ignoreMatchers.some((matcher) =>\n [basename, relativePath].some((target) => {\n matcher.lastIndex = 0\n return matcher.test(target)\n }),\n )\n\n if (isIgnored) return {}\n if (basename.startsWith('.')) return {}\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",
3
+ "version": "0.1.4",
4
4
  "description": "ESLint plugin com regras úteis para projetos com ESLint - AI Rules",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",