eslint-plugin-ai-rules 0.1.4 → 0.2.0
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 +35 -15
- package/dist/index.cjs +26 -18
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +8 -21
- package/dist/index.d.ts +8 -21
- package/dist/index.js +25 -18
- package/dist/index.js.map +1 -1
- package/package.json +13 -4
package/README.md
CHANGED
|
@@ -19,7 +19,7 @@ Flat-config only ESLint plugin that bundles useful rules for AI-generated code.
|
|
|
19
19
|
## Requirements
|
|
20
20
|
|
|
21
21
|
- Node `>=18` (`package.json` `engines`)
|
|
22
|
-
- ESLint `^
|
|
22
|
+
- ESLint `^9.0.0` (peer) — flat config only
|
|
23
23
|
- TypeScript `>=5.0.0` (peer)
|
|
24
24
|
|
|
25
25
|
`@eslint/js`, `eslint-plugin-import-x`, `@stylistic/eslint-plugin`, `typescript-eslint`, `globals` are already `dependencies` — you don't need to install them.
|
|
@@ -44,20 +44,28 @@ bun add -d eslint-plugin-ai-rules eslint typescript
|
|
|
44
44
|
|
|
45
45
|
### `eslint.config.ts` (recommended) — you define `files`
|
|
46
46
|
|
|
47
|
-
`recommended` is file-agnostic (only `plugins` + `settings` + `rules`).
|
|
47
|
+
`recommended` is file-agnostic (only `plugins` + `settings` + `rules`) and does **not** configure a parser. Pair it with `typescript-eslint` (or `@typescript-eslint/parser`) and define `files`:
|
|
48
48
|
|
|
49
49
|
```ts
|
|
50
50
|
import aiRules from 'eslint-plugin-ai-rules'
|
|
51
|
+
import tseslint from 'typescript-eslint'
|
|
51
52
|
|
|
52
53
|
export default [
|
|
54
|
+
...tseslint.configs.recommended,
|
|
53
55
|
{ files: ['**/*.{ts,tsx}'], ...aiRules.configs.recommended[0] },
|
|
54
|
-
// or spread: ...aiRules.configs.recommended with your own files wrapper
|
|
55
56
|
]
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Alternative with `defineConfig` (ESLint >= 9.22):
|
|
56
60
|
|
|
57
|
-
|
|
61
|
+
```ts
|
|
58
62
|
import { defineConfig } from 'eslint/config'
|
|
63
|
+
import aiRules from 'eslint-plugin-ai-rules'
|
|
64
|
+
import tseslint from 'typescript-eslint'
|
|
65
|
+
|
|
59
66
|
export default defineConfig([
|
|
60
|
-
|
|
67
|
+
...tseslint.configs.recommended,
|
|
68
|
+
{ files: ['**/*.{ts,tsx}'], extends: aiRules.configs.recommended },
|
|
61
69
|
])
|
|
62
70
|
```
|
|
63
71
|
|
|
@@ -65,8 +73,10 @@ export default defineConfig([
|
|
|
65
73
|
|
|
66
74
|
```js
|
|
67
75
|
import aiRules from 'eslint-plugin-ai-rules'
|
|
76
|
+
import tseslint from 'typescript-eslint'
|
|
68
77
|
|
|
69
78
|
export default [
|
|
79
|
+
...tseslint.configs.recommended,
|
|
70
80
|
{ files: ['**/*.{js,ts,tsx}'], ...aiRules.configs.recommended[0] },
|
|
71
81
|
]
|
|
72
82
|
```
|
|
@@ -163,17 +173,24 @@ Enforces `PascalCase` for file names. The rule is file-agnostic: it validates ev
|
|
|
163
173
|
- **Type:** `suggestion`, no autofix, `messageId: invalid`
|
|
164
174
|
- **Ignores:** `index.{ts,tsx,js,jsx}`, `*.d.ts`, `*.css`, dotfiles
|
|
165
175
|
- **Checks:** `path.basename` from `getPhysicalFilename()` / `context.filename`, `^[A-Z][a-zA-Z0-9]*$`, `kebab/snake -> PascalCase` conversion
|
|
166
|
-
- **Options:**
|
|
176
|
+
- **Options:**
|
|
177
|
+
- `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.
|
|
178
|
+
- `ignoreMiddleExtensions` — `boolean`, default `true`. When `true`, only the base name before the first dot is validated, so `ValidationRanges.test.ts` is valid and `my-component.test.ts` is renamed to `MyComponent.test.ts`. When `false`, every dot-separated segment must be PascalCase, so `ValidationRanges.test.ts` is renamed to `ValidationRanges.Test.ts` (same idea as `unicorn/filename-case`).
|
|
167
179
|
|
|
168
180
|
```ts
|
|
169
|
-
// my-component.ts
|
|
170
|
-
//
|
|
181
|
+
// my-component.ts ❌ Rename to "MyComponent.ts"
|
|
182
|
+
// ValidationRanges.test.ts ✅ (ignoreMiddleExtensions: true, default)
|
|
183
|
+
// ValidationRanges.test.ts ❌ Rename to "ValidationRanges.Test.ts" (ignoreMiddleExtensions: false)
|
|
184
|
+
// MyComponent.ts ✅
|
|
171
185
|
```
|
|
172
186
|
|
|
173
187
|
```ts
|
|
174
188
|
'ai-rules/filename-pascal-case': [
|
|
175
189
|
'error',
|
|
176
|
-
{
|
|
190
|
+
{
|
|
191
|
+
ignore: ['\\.config\\.', '^generated/'],
|
|
192
|
+
ignoreMiddleExtensions: false,
|
|
193
|
+
},
|
|
177
194
|
]
|
|
178
195
|
```
|
|
179
196
|
|
|
@@ -253,10 +270,10 @@ Fix: `npm run build` first. This repo's `eslint.config.mjs` imports from `dist`
|
|
|
253
270
|
You passed an absolute path outside the config's `basePath`. Run `npx eslint .` from project root or use `npx eslint --no-config-lookup -c ./eslint.config.js ./src/file.ts` with correct cwd.
|
|
254
271
|
|
|
255
272
|
**Flat config only**
|
|
256
|
-
This plugin exports
|
|
273
|
+
This plugin exports flat config arrays. It does not support legacy `.eslintrc`. Use `eslint.config.{js,ts}`.
|
|
257
274
|
|
|
258
275
|
**Filename rule too strict for tests/configs**
|
|
259
|
-
The rule checks every linted file.
|
|
276
|
+
The rule checks every linted file. Test/spec files with a PascalCase base (e.g. `ValidationRanges.test.ts`) pass by default via `ignoreMiddleExtensions: true`. For tool-mandated names like `*.config.*`, generated or vendored code, pass the `ignore` option (`{ ignore: ['\\.config\\.', '^generated/'] }`) or disable `ai-rules/filename-pascal-case` for those files via an override (see Quick Start).
|
|
260
277
|
|
|
261
278
|
## Development
|
|
262
279
|
|
|
@@ -266,10 +283,12 @@ cd eslint-ai-guard
|
|
|
266
283
|
npm install
|
|
267
284
|
|
|
268
285
|
npm run typecheck # tsc --noEmit
|
|
286
|
+
npm test # node:test + dist build
|
|
269
287
|
npm run lint # eslint .
|
|
270
288
|
npm run lint -- --fix
|
|
271
289
|
npm run build # tsup -> dist/index.js + dist/index.cjs + dist/index.d.ts
|
|
272
290
|
npm run dev # tsup --watch
|
|
291
|
+
npm run verify # typecheck + test + lint + pack
|
|
273
292
|
npm pack --dry-run # verify files: dist only
|
|
274
293
|
```
|
|
275
294
|
|
|
@@ -281,13 +300,14 @@ eslint-ai-guard/ # package: eslint-plugin-ai-rules
|
|
|
281
300
|
│ ├── index.ts # re-exports plugin + recommended
|
|
282
301
|
│ ├── plugin.ts # plugin.meta + rules
|
|
283
302
|
│ ├── configs/
|
|
284
|
-
│ │ ├── recommended.ts #
|
|
303
|
+
│ │ ├── recommended.ts # flat-config preset (bundled rules)
|
|
285
304
|
│ │ └── index.ts
|
|
286
305
|
│ └── rules/
|
|
287
306
|
│ ├── FilenamePascalCase.ts
|
|
288
307
|
│ ├── NoMultilineImports.ts
|
|
289
308
|
│ ├── SortImports.ts # also exports getSortedContent
|
|
290
309
|
│ └── index.ts
|
|
310
|
+
├── test/ # node:test suites (run against dist)
|
|
291
311
|
├── eslint/ # deprecated original implementations
|
|
292
312
|
├── eslint.config.mjs # dogfooding: ...aiRules.configs.recommended from dist
|
|
293
313
|
├── tsconfig.json # ESNext/bundler, no .js extensions
|
|
@@ -302,14 +322,14 @@ eslint-ai-guard/ # package: eslint-plugin-ai-rules
|
|
|
302
322
|
2. Export it from `src/rules/index.ts`
|
|
303
323
|
3. Register in `src/plugin.ts` (`rules: { 'my-rule': myRule }`)
|
|
304
324
|
4. Enable in `src/configs/recommended.ts` (`'ai-rules/my-rule': 'error'`)
|
|
305
|
-
5. Add
|
|
306
|
-
6. `npm run
|
|
325
|
+
5. Add a `RuleTester` case in `test/` (add a fixture if needed)
|
|
326
|
+
6. `npm run verify`
|
|
307
327
|
|
|
308
328
|
### Publishing
|
|
309
329
|
|
|
310
330
|
```bash
|
|
311
331
|
npm version patch|minor|major
|
|
312
|
-
npm publish --access public # prepublishOnly runs verify (typecheck+
|
|
332
|
+
npm publish --access public # prepublishOnly runs verify (typecheck+test+lint+pack)
|
|
313
333
|
# or manually: npm run verify && npm publish --access public
|
|
314
334
|
```
|
|
315
335
|
|
package/dist/index.cjs
CHANGED
|
@@ -32,6 +32,7 @@ var index_exports = {};
|
|
|
32
32
|
__export(index_exports, {
|
|
33
33
|
default: () => index_default,
|
|
34
34
|
filenamePascalCase: () => filenamePascalCase,
|
|
35
|
+
getSortedContent: () => getSortedContent,
|
|
35
36
|
noMultilineImports: () => noMultilineImports,
|
|
36
37
|
plugin: () => plugin,
|
|
37
38
|
recommended: () => recommended,
|
|
@@ -42,7 +43,6 @@ module.exports = __toCommonJS(index_exports);
|
|
|
42
43
|
// src/configs/recommended.ts
|
|
43
44
|
var import_eslint_plugin_import_x = require("eslint-plugin-import-x");
|
|
44
45
|
var import_eslint_plugin = __toESM(require("@stylistic/eslint-plugin"), 1);
|
|
45
|
-
var import_config = require("eslint/config");
|
|
46
46
|
var import_typescript_eslint = __toESM(require("typescript-eslint"), 1);
|
|
47
47
|
|
|
48
48
|
// src/rules/FilenamePascalCase.ts
|
|
@@ -55,7 +55,8 @@ var filenamePascalCase = {
|
|
|
55
55
|
{
|
|
56
56
|
type: "object",
|
|
57
57
|
properties: {
|
|
58
|
-
ignore: { type: "array", items: { type: "string" } }
|
|
58
|
+
ignore: { type: "array", items: { type: "string" } },
|
|
59
|
+
ignoreMiddleExtensions: { type: "boolean" }
|
|
59
60
|
},
|
|
60
61
|
additionalProperties: false
|
|
61
62
|
}
|
|
@@ -66,7 +67,8 @@ var filenamePascalCase = {
|
|
|
66
67
|
},
|
|
67
68
|
create(context) {
|
|
68
69
|
const options = context.options[0] ?? {};
|
|
69
|
-
const
|
|
70
|
+
const { ignore = [], ignoreMiddleExtensions = true } = options;
|
|
71
|
+
const ignoreMatchers = ignore.flatMap((pattern) => {
|
|
70
72
|
try {
|
|
71
73
|
return [pattern instanceof RegExp ? pattern : new RegExp(pattern)];
|
|
72
74
|
} catch {
|
|
@@ -79,7 +81,7 @@ var filenamePascalCase = {
|
|
|
79
81
|
);
|
|
80
82
|
const basename = import_node_path.default.basename(physicalFilename);
|
|
81
83
|
const cwd = process.cwd();
|
|
82
|
-
const relativePath = physicalFilename.startsWith(cwd) ? physicalFilename.slice(cwd.length + 1) : physicalFilename;
|
|
84
|
+
const relativePath = physicalFilename.startsWith(cwd + import_node_path.default.sep) ? physicalFilename.slice(cwd.length + 1) : physicalFilename;
|
|
83
85
|
const isIgnored = ignoreMatchers.some(
|
|
84
86
|
(matcher) => [basename, relativePath].some((target) => {
|
|
85
87
|
matcher.lastIndex = 0;
|
|
@@ -91,17 +93,22 @@ var filenamePascalCase = {
|
|
|
91
93
|
if (basename === "index.ts" || basename === "index.tsx" || basename === "index.js" || basename === "index.jsx") return {};
|
|
92
94
|
if (basename.endsWith(".d.ts")) return {};
|
|
93
95
|
if (basename.endsWith(".css")) return {};
|
|
94
|
-
const
|
|
95
|
-
const
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
96
|
+
const extension = basename.match(/\.(ts|tsx|js|jsx)$/)?.[0] ?? "";
|
|
97
|
+
const namePart = extension ? basename.slice(0, -extension.length) : basename;
|
|
98
|
+
const baseName = namePart.split(".")[0];
|
|
99
|
+
const toPascalCase = (value) => value.split(/[-_]/).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
|
|
100
|
+
const segments = ignoreMiddleExtensions ? [baseName] : namePart.split(".");
|
|
101
|
+
const isValid = segments.every((segment) => /^[A-Z][a-zA-Z0-9]*$/.test(segment));
|
|
102
|
+
if (!isValid) {
|
|
103
|
+
const expectedName = ignoreMiddleExtensions ? `${toPascalCase(baseName)}${namePart.slice(baseName.length)}` : segments.map(toPascalCase).join(".");
|
|
104
|
+
const expected = `${expectedName}${extension}`;
|
|
105
|
+
if (expected !== basename) {
|
|
106
|
+
context.report({
|
|
107
|
+
loc: { line: 1, column: 0 },
|
|
108
|
+
messageId: "invalid",
|
|
109
|
+
data: { basename, expected }
|
|
110
|
+
});
|
|
111
|
+
}
|
|
105
112
|
}
|
|
106
113
|
return {};
|
|
107
114
|
}
|
|
@@ -275,7 +282,7 @@ function getSortedContent(content) {
|
|
|
275
282
|
var plugin = {
|
|
276
283
|
meta: {
|
|
277
284
|
name: "eslint-plugin-ai-rules",
|
|
278
|
-
version: "0.
|
|
285
|
+
version: "0.2.0"
|
|
279
286
|
},
|
|
280
287
|
rules: {
|
|
281
288
|
"filename-pascal-case": filenamePascalCase,
|
|
@@ -285,7 +292,7 @@ var plugin = {
|
|
|
285
292
|
};
|
|
286
293
|
|
|
287
294
|
// src/configs/recommended.ts
|
|
288
|
-
var recommended =
|
|
295
|
+
var recommended = [
|
|
289
296
|
{
|
|
290
297
|
plugins: {
|
|
291
298
|
"@stylistic": import_eslint_plugin.default,
|
|
@@ -323,7 +330,7 @@ var recommended = (0, import_config.defineConfig)([
|
|
|
323
330
|
]
|
|
324
331
|
}
|
|
325
332
|
}
|
|
326
|
-
]
|
|
333
|
+
];
|
|
327
334
|
|
|
328
335
|
// src/index.ts
|
|
329
336
|
var aiGuardPlugin = {
|
|
@@ -336,6 +343,7 @@ var index_default = aiGuardPlugin;
|
|
|
336
343
|
// Annotate the CommonJS export names for ESM import in node:
|
|
337
344
|
0 && (module.exports = {
|
|
338
345
|
filenamePascalCase,
|
|
346
|
+
getSortedContent,
|
|
339
347
|
noMultilineImports,
|
|
340
348
|
plugin,
|
|
341
349
|
recommended,
|
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\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"]}
|
|
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 type { ESLint } from 'eslint'\n\nimport { recommended } from './configs/recommended'\nimport { plugin } from './plugin'\n\nexport * from './rules/index'\n\n\nconst aiGuardPlugin: ESLint.Plugin = {\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 tseslint from 'typescript-eslint'\nimport type { Linter } from 'eslint'\n\nimport { plugin } from '../plugin'\n\n\nexport const recommended: Linter.Config[] = [\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 ignoreMiddleExtensions?: boolean\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 ignoreMiddleExtensions: { type: 'boolean' },\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 { ignore = [], ignoreMiddleExtensions = true } = options\n const ignoreMatchers = 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 + path.sep)\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 extension = basename.match(/\\.(ts|tsx|js|jsx)$/)?.[0] ?? ''\n const namePart = extension ? basename.slice(0, -extension.length) : basename\n const baseName = namePart.split('.')[0]\n\n const toPascalCase = (value: string): string =>\n value\n .split(/[-_]/)\n .map(part => part.charAt(0).toUpperCase() + part.slice(1))\n .join('')\n\n const segments = ignoreMiddleExtensions ? [baseName] : namePart.split('.')\n const isValid = segments.every(segment => /^[A-Z][a-zA-Z0-9]*$/.test(segment))\n\n if (!isValid) {\n const expectedName = ignoreMiddleExtensions\n ? `${toPascalCase(baseName)}${namePart.slice(baseName.length)}`\n : segments.map(toPascalCase).join('.')\n const expected = `${expectedName}${extension}`\n\n if (expected !== basename) {\n context.report({\n loc: { line: 1, column: 0 },\n messageId: 'invalid',\n data: { basename, expected },\n })\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.2.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"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
import * as eslint from 'eslint';
|
|
2
|
-
import { Rule } from 'eslint';
|
|
3
|
-
import * as eslint_config from 'eslint/config';
|
|
2
|
+
import { Linter, Rule, ESLint } from 'eslint';
|
|
4
3
|
|
|
5
|
-
declare const recommended:
|
|
4
|
+
declare const recommended: Linter.Config[];
|
|
6
5
|
|
|
7
6
|
declare const plugin: {
|
|
8
7
|
readonly meta: {
|
|
9
8
|
readonly name: "eslint-plugin-ai-rules";
|
|
10
|
-
readonly version: "0.
|
|
9
|
+
readonly version: "0.2.0";
|
|
11
10
|
};
|
|
12
11
|
readonly rules: {
|
|
13
12
|
readonly 'filename-pascal-case': eslint.Rule.RuleModule;
|
|
@@ -16,25 +15,13 @@ declare const plugin: {
|
|
|
16
15
|
};
|
|
17
16
|
};
|
|
18
17
|
|
|
18
|
+
declare const sortImports: Rule.RuleModule;
|
|
19
|
+
declare function getSortedContent(content: string): string | null;
|
|
20
|
+
|
|
19
21
|
declare const filenamePascalCase: Rule.RuleModule;
|
|
20
22
|
|
|
21
23
|
declare const noMultilineImports: Rule.RuleModule;
|
|
22
24
|
|
|
23
|
-
declare const
|
|
24
|
-
|
|
25
|
-
declare const aiGuardPlugin: {
|
|
26
|
-
configs: {
|
|
27
|
-
recommended: eslint_config.Config[];
|
|
28
|
-
};
|
|
29
|
-
meta: {
|
|
30
|
-
readonly name: "eslint-plugin-ai-rules";
|
|
31
|
-
readonly version: "0.1.2";
|
|
32
|
-
};
|
|
33
|
-
rules: {
|
|
34
|
-
readonly 'filename-pascal-case': eslint.Rule.RuleModule;
|
|
35
|
-
readonly 'no-multiline-imports': eslint.Rule.RuleModule;
|
|
36
|
-
readonly 'sort-imports': eslint.Rule.RuleModule;
|
|
37
|
-
};
|
|
38
|
-
};
|
|
25
|
+
declare const aiGuardPlugin: ESLint.Plugin;
|
|
39
26
|
|
|
40
|
-
export { aiGuardPlugin as default, filenamePascalCase, noMultilineImports, plugin, recommended, sortImports };
|
|
27
|
+
export { aiGuardPlugin as default, filenamePascalCase, getSortedContent, noMultilineImports, plugin, recommended, sortImports };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
import * as eslint from 'eslint';
|
|
2
|
-
import { Rule } from 'eslint';
|
|
3
|
-
import * as eslint_config from 'eslint/config';
|
|
2
|
+
import { Linter, Rule, ESLint } from 'eslint';
|
|
4
3
|
|
|
5
|
-
declare const recommended:
|
|
4
|
+
declare const recommended: Linter.Config[];
|
|
6
5
|
|
|
7
6
|
declare const plugin: {
|
|
8
7
|
readonly meta: {
|
|
9
8
|
readonly name: "eslint-plugin-ai-rules";
|
|
10
|
-
readonly version: "0.
|
|
9
|
+
readonly version: "0.2.0";
|
|
11
10
|
};
|
|
12
11
|
readonly rules: {
|
|
13
12
|
readonly 'filename-pascal-case': eslint.Rule.RuleModule;
|
|
@@ -16,25 +15,13 @@ declare const plugin: {
|
|
|
16
15
|
};
|
|
17
16
|
};
|
|
18
17
|
|
|
18
|
+
declare const sortImports: Rule.RuleModule;
|
|
19
|
+
declare function getSortedContent(content: string): string | null;
|
|
20
|
+
|
|
19
21
|
declare const filenamePascalCase: Rule.RuleModule;
|
|
20
22
|
|
|
21
23
|
declare const noMultilineImports: Rule.RuleModule;
|
|
22
24
|
|
|
23
|
-
declare const
|
|
24
|
-
|
|
25
|
-
declare const aiGuardPlugin: {
|
|
26
|
-
configs: {
|
|
27
|
-
recommended: eslint_config.Config[];
|
|
28
|
-
};
|
|
29
|
-
meta: {
|
|
30
|
-
readonly name: "eslint-plugin-ai-rules";
|
|
31
|
-
readonly version: "0.1.2";
|
|
32
|
-
};
|
|
33
|
-
rules: {
|
|
34
|
-
readonly 'filename-pascal-case': eslint.Rule.RuleModule;
|
|
35
|
-
readonly 'no-multiline-imports': eslint.Rule.RuleModule;
|
|
36
|
-
readonly 'sort-imports': eslint.Rule.RuleModule;
|
|
37
|
-
};
|
|
38
|
-
};
|
|
25
|
+
declare const aiGuardPlugin: ESLint.Plugin;
|
|
39
26
|
|
|
40
|
-
export { aiGuardPlugin as default, filenamePascalCase, noMultilineImports, plugin, recommended, sortImports };
|
|
27
|
+
export { aiGuardPlugin as default, filenamePascalCase, getSortedContent, noMultilineImports, plugin, recommended, sortImports };
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
// src/configs/recommended.ts
|
|
2
2
|
import { createNodeResolver, importX } from "eslint-plugin-import-x";
|
|
3
3
|
import stylistic from "@stylistic/eslint-plugin";
|
|
4
|
-
import { defineConfig } from "eslint/config";
|
|
5
4
|
import tseslint from "typescript-eslint";
|
|
6
5
|
|
|
7
6
|
// src/rules/FilenamePascalCase.ts
|
|
@@ -14,7 +13,8 @@ var filenamePascalCase = {
|
|
|
14
13
|
{
|
|
15
14
|
type: "object",
|
|
16
15
|
properties: {
|
|
17
|
-
ignore: { type: "array", items: { type: "string" } }
|
|
16
|
+
ignore: { type: "array", items: { type: "string" } },
|
|
17
|
+
ignoreMiddleExtensions: { type: "boolean" }
|
|
18
18
|
},
|
|
19
19
|
additionalProperties: false
|
|
20
20
|
}
|
|
@@ -25,7 +25,8 @@ var filenamePascalCase = {
|
|
|
25
25
|
},
|
|
26
26
|
create(context) {
|
|
27
27
|
const options = context.options[0] ?? {};
|
|
28
|
-
const
|
|
28
|
+
const { ignore = [], ignoreMiddleExtensions = true } = options;
|
|
29
|
+
const ignoreMatchers = ignore.flatMap((pattern) => {
|
|
29
30
|
try {
|
|
30
31
|
return [pattern instanceof RegExp ? pattern : new RegExp(pattern)];
|
|
31
32
|
} catch {
|
|
@@ -38,7 +39,7 @@ var filenamePascalCase = {
|
|
|
38
39
|
);
|
|
39
40
|
const basename = path.basename(physicalFilename);
|
|
40
41
|
const cwd = process.cwd();
|
|
41
|
-
const relativePath = physicalFilename.startsWith(cwd) ? physicalFilename.slice(cwd.length + 1) : physicalFilename;
|
|
42
|
+
const relativePath = physicalFilename.startsWith(cwd + path.sep) ? physicalFilename.slice(cwd.length + 1) : physicalFilename;
|
|
42
43
|
const isIgnored = ignoreMatchers.some(
|
|
43
44
|
(matcher) => [basename, relativePath].some((target) => {
|
|
44
45
|
matcher.lastIndex = 0;
|
|
@@ -50,17 +51,22 @@ var filenamePascalCase = {
|
|
|
50
51
|
if (basename === "index.ts" || basename === "index.tsx" || basename === "index.js" || basename === "index.jsx") return {};
|
|
51
52
|
if (basename.endsWith(".d.ts")) return {};
|
|
52
53
|
if (basename.endsWith(".css")) return {};
|
|
53
|
-
const
|
|
54
|
-
const
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
54
|
+
const extension = basename.match(/\.(ts|tsx|js|jsx)$/)?.[0] ?? "";
|
|
55
|
+
const namePart = extension ? basename.slice(0, -extension.length) : basename;
|
|
56
|
+
const baseName = namePart.split(".")[0];
|
|
57
|
+
const toPascalCase = (value) => value.split(/[-_]/).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
|
|
58
|
+
const segments = ignoreMiddleExtensions ? [baseName] : namePart.split(".");
|
|
59
|
+
const isValid = segments.every((segment) => /^[A-Z][a-zA-Z0-9]*$/.test(segment));
|
|
60
|
+
if (!isValid) {
|
|
61
|
+
const expectedName = ignoreMiddleExtensions ? `${toPascalCase(baseName)}${namePart.slice(baseName.length)}` : segments.map(toPascalCase).join(".");
|
|
62
|
+
const expected = `${expectedName}${extension}`;
|
|
63
|
+
if (expected !== basename) {
|
|
64
|
+
context.report({
|
|
65
|
+
loc: { line: 1, column: 0 },
|
|
66
|
+
messageId: "invalid",
|
|
67
|
+
data: { basename, expected }
|
|
68
|
+
});
|
|
69
|
+
}
|
|
64
70
|
}
|
|
65
71
|
return {};
|
|
66
72
|
}
|
|
@@ -234,7 +240,7 @@ function getSortedContent(content) {
|
|
|
234
240
|
var plugin = {
|
|
235
241
|
meta: {
|
|
236
242
|
name: "eslint-plugin-ai-rules",
|
|
237
|
-
version: "0.
|
|
243
|
+
version: "0.2.0"
|
|
238
244
|
},
|
|
239
245
|
rules: {
|
|
240
246
|
"filename-pascal-case": filenamePascalCase,
|
|
@@ -244,7 +250,7 @@ var plugin = {
|
|
|
244
250
|
};
|
|
245
251
|
|
|
246
252
|
// src/configs/recommended.ts
|
|
247
|
-
var recommended =
|
|
253
|
+
var recommended = [
|
|
248
254
|
{
|
|
249
255
|
plugins: {
|
|
250
256
|
"@stylistic": stylistic,
|
|
@@ -282,7 +288,7 @@ var recommended = defineConfig([
|
|
|
282
288
|
]
|
|
283
289
|
}
|
|
284
290
|
}
|
|
285
|
-
]
|
|
291
|
+
];
|
|
286
292
|
|
|
287
293
|
// src/index.ts
|
|
288
294
|
var aiGuardPlugin = {
|
|
@@ -295,6 +301,7 @@ var index_default = aiGuardPlugin;
|
|
|
295
301
|
export {
|
|
296
302
|
index_default as default,
|
|
297
303
|
filenamePascalCase,
|
|
304
|
+
getSortedContent,
|
|
298
305
|
noMultilineImports,
|
|
299
306
|
plugin,
|
|
300
307
|
recommended,
|
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\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":";AAAA,SAAS,oBAAoB,eAAe;AAC5C,OAAO,eAAe;AACtB,SAAS,oBAAoB;AAC7B,OAAO,cAAc;;;ACFrB,OAAO,UAAU;AAOV,IAAM,qBAAsC;AAAA,EACjD,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,MAAM,EAAE,aAAa,oCAAoC;AAAA,IACzD,QAAQ;AAAA,MACN;AAAA,QACE,MAAM;AAAA,QACN,YAAY;AAAA,UACV,QAAQ,EAAE,MAAM,SAAS,OAAO,EAAE,MAAM,SAAS,EAAE;AAAA,QACrD;AAAA,QACA,sBAAsB;AAAA,MACxB;AAAA,IACF;AAAA,IACA,UAAU;AAAA,MACR,SAAS;AAAA,IACX;AAAA,EACF;AAAA,EACA,OAAO,SAAS;AACd,UAAM,UAAW,QAAQ,QAAQ,CAAC,KAAK,CAAC;AACxC,UAAM,kBAAkB,QAAQ,UAAU,CAAC,GAAG,QAAQ,CAAC,YAAY;AACjE,UAAI;AACF,eAAO,CAAC,mBAAmB,SAAS,UAAU,IAAI,OAAO,OAAO,CAAC;AAAA,MACnE,QAAQ;AACN,eAAO,CAAC;AAAA,MACV;AAAA,IACF,CAAC;AAED,UAAM;AAAA;AAAA,MAEH,QAA8D,sBAAsB,KACpF,QAAqD,oBACtD,QAAQ,YACR;AAAA;AAEF,UAAM,WAAW,KAAK,SAAS,gBAAgB;AAC/C,UAAM,MAAM,QAAQ,IAAI;AACxB,UAAM,eAAe,iBAAiB,WAAW,GAAG,IAChD,iBAAiB,MAAM,IAAI,SAAS,CAAC,IACrC;AAEJ,UAAM,YAAY,eAAe;AAAA,MAAK,CAAC,YACrC,CAAC,UAAU,YAAY,EAAE,KAAK,CAAC,WAAW;AACxC,gBAAQ,YAAY;AACpB,eAAO,QAAQ,KAAK,MAAM;AAAA,MAC5B,CAAC;AAAA,IACH;AAEA,QAAI,UAAW,QAAO,CAAC;AACvB,QAAI,SAAS,WAAW,GAAG,EAAG,QAAO,CAAC;AACtC,QAAI,aAAa,cAAc,aAAa,eAAe,aAAa,cAAc,aAAa,YAAa,QAAO,CAAC;AACxH,QAAI,SAAS,SAAS,OAAO,EAAG,QAAO,CAAC;AACxC,QAAI,SAAS,SAAS,MAAM,EAAG,QAAO,CAAC;AAEvC,UAAM,MAAM,KAAK,QAAQ,QAAQ;AAGjC,UAAM,iBAAiB,SAAS,QAAQ,sBAAsB,EAAE;AAEhE,QAAI,CAAC,sBAAsB,KAAK,cAAc,GAAG;AAC/C,YAAM,aAAa,eAChB,MAAM,MAAM,EACZ,IAAI,UAAQ,KAAK,OAAO,CAAC,EAAE,YAAY,IAAI,KAAK,MAAM,CAAC,CAAC,EACxD,KAAK,EAAE;AAGV,YAAM,cAAc,SAAS,MAAM,eAAe,MAAM,KAAK;AAC7D,YAAM,WAAW,GAAG,UAAU,GAAG,WAAW;AAE5C,cAAQ,OAAO;AAAA,QACb,KAAK,EAAE,MAAM,GAAG,QAAQ,EAAE;AAAA,QAC1B,WAAW;AAAA,QACX,MAAM,EAAE,UAAU,SAAS;AAAA,MAC7B,CAAC;AAAA,IACH;AAEA,WAAO,CAAC;AAAA,EACV;AACF;;;AClFO,IAAM,qBAAsC;AAAA,EACjD,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,SAAS;AAAA,IACT,MAAM;AAAA,MACJ,aAAa;AAAA,IACf;AAAA,IACA,QAAQ,CAAC;AAAA,IACT,UAAU;AAAA,MACR,iBAAiB;AAAA,IACnB;AAAA,EACF;AAAA,EAEA,OAAO,SAAS;AACd,UAAM,aAAa,QAAQ;AAE3B,WAAO;AAAA,MACL,kBAAkB,MAAM;AACtB,cAAM,OAAO,WAAW,QAAQ,IAAI;AAEpC,YAAI,CAAC,KAAK,SAAS,IAAI,GAAG;AACxB;AAAA,QACF;AAEA,gBAAQ,OAAO;AAAA,UACb;AAAA,UACA,WAAW;AAAA,UAEX,IAAI,OAAO;AACT,kBAAM,aAAa,WAAW,cAAc,IAAI;AAChD,kBAAM,YAAY,WAAW,aAAa,IAAI;AAE9C,gBAAI,CAAC,cAAc,CAAC,WAAW;AAC7B,qBAAO;AAAA,YACT;AAEA,gBAAI,aAAa,WAAW,QAAQ,IAAI;AAExC,yBAAa,WACV,QAAQ,aAAa,KAAK,EAC1B,QAAQ,aAAa,KAAK,EAC1B,QAAQ,YAAY,IAAI,EACxB,QAAQ,QAAQ,GAAG,EACnB,QAAQ,SAAS,KAAK,EACtB,QAAQ,SAAS,KAAK,EACtB,QAAQ,eAAe,QAAQ,EAC/B,KAAK,EAEL,QAAQ,WAAW,IAAI,EACvB,QAAQ,WAAW,IAAI;AAE1B,mBAAO,MAAM,YAAY,MAAM,UAAU;AAAA,UAC3C;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AACF;;;ACzDO,IAAM,cAA+B;AAAA,EAC1C,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,MAAM;AAAA,MACJ,aAAa;AAAA,IACf;AAAA,IACA,SAAS;AAAA,IACT,QAAQ,CAAC;AAAA,IACT,UAAU;AAAA,MACR,cAAc;AAAA,IAChB;AAAA,EACF;AAAA,EACA,OAAO,SAAS;AACd,WAAO;AAAA,MACL,QAAQ,MAAM;AACZ,cAAM,aAAa,QAAQ;AAC3B,cAAM,UAAU,WAAW;AAE3B,cAAM,aAAa,iBAAiB,OAAO;AAE3C,YAAI,cAAc,eAAe,SAAS;AACxC,kBAAQ,OAAO;AAAA,YACb;AAAA,YACA,WAAW;AAAA,YACX,IAAI,OAAO;AACT,qBAAO,MAAM,YAAY,MAAM,UAAU;AAAA,YAC3C;AAAA,UACF,CAAC;AAAA,QACH;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;AAaO,SAAS,iBAAiB,SAAgC;AAC/D,QAAM,oBAAoB,CAAC,UAAkB;AAC3C,WAAO,MACJ,QAAQ,qBAAqB,EAAE,EAC/B,QAAQ,aAAa,EAAE,EACvB,QAAQ,QAAQ,GAAG;AAAA,EACxB;AAEA,QAAM,oBAAoB,QACvB,QAAQ,kDAAkD,iBAAiB,EAE3E,QAAQ,oFAAoF,iBAAiB;AAEhH,QAAM,QAAkB,kBAAkB,MAAM,IAAI;AAEpD,QAAM,iBAA2B,CAAC;AAClC,QAAM,iBAA2B,CAAC;AAClC,QAAM,gBAA+B,CAAC;AACtC,QAAM,gBAA+B,CAAC;AACtC,QAAM,gBAA+B,CAAC;AACtC,QAAM,iBAA2B,CAAC;AAElC,MAAI,gBAAgB;AACpB,MAAI,sBAAsB;AAE1B,WAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,UAAM,UAAU,MAAM,CAAC,EAAE,KAAK;AAE9B,QACE,mDAAmD,KAAK,OAAO,KAC9D,mBAAmB,KAAK,OAAO,KAAK,CAAC,aAAa,KAAK,OAAO,KAAK,CAAC,6BAA6B,KAAK,OAAO,KAAK,CAAC,eAAe,KAAK,OAAO,KAC/I,QAAQ,WAAW,GAAG,GACtB;AACA,4BAAsB;AAAA,IACxB;AAEA,QAAI,iBAAiB,YAAY,GAAI;AAErC,QAAI,uBAAuB,yCAAyC,KAAK,OAAO,GAAG;AACjF,qBAAe,KAAK,MAAM,CAAC,CAAC;AAC5B;AAAA,IACF;AAEA,UAAM,aACJ,8CAA8C,KAAK,OAAO,KAC1D,2CAA2C,KAAK,OAAO;AAEzD,QAAI,YAAY;AACd,YAAM,IAAI,MAAM,CAAC,EAAE,MAAM,yBAAyB;AAClD,UAAI,EAAG,eAAc,KAAK,EAAE,MAAM,MAAM,CAAC,GAAG,MAAM,EAAE,CAAC,EAAE,CAAC;AACxD,sBAAgB;AAChB,4BAAsB;AACtB;AAAA,IACF;AAEA,UAAM,WAAW,YAAY,KAAK,OAAO,KAAK,YAAY,KAAK,OAAO;AAEtE,QAAI,UAAU;AACZ,sBAAgB;AAChB,4BAAsB;AAEtB,YAAM,YAAY,MAAM,CAAC,EAAE,MAAM,yBAAyB;AAC1D,YAAM,UAAU,MAAM,CAAC,EAAE,MAAM,4BAA4B;AAC3D,YAAM,OAAO,YAAY,UAAU,CAAC,IAAI,UAAU,QAAQ,CAAC,IAAI;AAE/D,UAAI,CAAC,MAAM;AACT,uBAAe,KAAK,MAAM,CAAC,CAAC;AAC5B;AAAA,MACF;AAEA,YAAM,aAAa,mBAAmB,KAAK,OAAO;AAClD,YAAM,aAAa,KAAK,WAAW,GAAG,KAAK,KAAK,WAAW,IAAI;AAC/D,YAAM,eAAe,CAAC,eAAe,SAAS,WAAW,KAAK,WAAW,QAAQ;AAEjF,UAAI,cAAc;AAChB,uBAAe,KAAK,MAAM,CAAC,CAAC;AAAA,MAC9B,WAAW,YAAY;AACrB,sBAAc,KAAK,EAAE,MAAM,MAAM,CAAC,GAAG,KAAK,CAAC;AAAA,MAC7C,OAAO;AACL,sBAAc,KAAK,EAAE,MAAM,MAAM,CAAC,GAAG,KAAK,CAAC;AAAA,MAC7C;AACA;AAAA,IACF;AAEA,oBAAgB;AAChB,mBAAe,KAAK,MAAM,CAAC,CAAC;AAAA,EAC9B;AAEA,MAAI,eAAe,WAAW,KAAK,cAAc,WAAW,KAAK,cAAc,WAAW,KAAK,cAAc,WAAW,GAAG;AACzH,WAAO;AAAA,EACT;AAEA,QAAM,eAAe,CAAC,GAAgB,MACpC,EAAE,KAAK,WAAW,EAAE,KAAK,SAAS,EAAE,KAAK,SAAS,EAAE,KAAK,SAAS,EAAE,KAAK,cAAc,EAAE,IAAI;AAE/F,gBAAc,KAAK,YAAY;AAC/B,gBAAc,KAAK,YAAY;AAC/B,gBAAc,KAAK,YAAY;AAE/B,QAAM,SAAmB,CAAC;AAE1B,MAAI,eAAe,SAAS,EAAG,QAAO,KAAK,eAAe,KAAK,IAAI,CAAC;AAEpE,QAAM,gBAAgB,CAAC,GAAG,gBAAgB,GAAG,cAAc,IAAI,OAAK,EAAE,IAAI,CAAC;AAC3E,MAAI,cAAc,SAAS,GAAG;AAC5B,QAAI,OAAO,SAAS,EAAG,QAAO,KAAK,EAAE;AACrC,WAAO,KAAK,cAAc,KAAK,IAAI,CAAC;AAAA,EACtC;AAEA,MAAI,cAAc,SAAS,GAAG;AAC5B,QAAI,OAAO,SAAS,EAAG,QAAO,KAAK,EAAE;AACrC,WAAO,KAAK,cAAc,IAAI,OAAK,EAAE,IAAI,EAAE,KAAK,IAAI,CAAC;AAAA,EACvD;AAEA,MAAI,cAAc,SAAS,GAAG;AAC5B,QAAI,OAAO,SAAS,EAAG,QAAO,KAAK,EAAE;AACrC,WAAO,KAAK,cAAc,IAAI,OAAK,EAAE,IAAI,EAAE,KAAK,IAAI,CAAC;AAAA,EACvD;AAEA,QAAM,OAAO,eAAe,KAAK,IAAI,EAAE,QAAQ,QAAQ,EAAE;AAEzD,MAAI;AACJ,MAAI,OAAO,SAAS,KAAK,KAAK,SAAS,GAAG;AACxC,iBAAa,OAAO,KAAK,IAAI,IAAI,WAAW;AAAA,EAC9C,WAAW,OAAO,SAAS,GAAG;AAC5B,iBAAa,OAAO,KAAK,IAAI,IAAI;AAAA,EACnC,OAAO;AACL,iBAAa;AAAA,EACf;AAEA,SAAO;AACT;;;AC9KO,IAAM,SAAS;AAAA,EACpB,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,SAAS;AAAA,EACX;AAAA,EACA,OAAO;AAAA,IACL,wBAAwB;AAAA,IACxB,wBAAwB;AAAA,IACxB,gBAAgB;AAAA,EAClB;AACF;;;AJPO,IAAM,cAAc,aAAa;AAAA,EACtC;AAAA,IACE,SAAS;AAAA,MACP,cAAc;AAAA,MACd,YAAY;AAAA,MACZ,YAAY;AAAA,MACZ,sBAAsB,SAAS;AAAA,IACjC;AAAA,IACA,UAAU;AAAA,MACR,0BAA0B;AAAA,QACxB,mBAAmB;AAAA,UACjB,YAAY,CAAC,OAAO,QAAQ,SAAS,OAAO,MAAM;AAAA,QACpD,CAAC;AAAA,MACH;AAAA,IACF;AAAA,IACA,OAAO;AAAA,MACL,yBAAyB;AAAA,MACzB,iCAAiC;AAAA,MACjC,iCAAiC;AAAA,MACjC,WAAW;AAAA,MACX,0BAA0B;AAAA,MAC1B,8BAA8B;AAAA,MAC9B,mBAAmB,CAAC,SAAS,OAAO;AAAA,MACpC,8CAA8C;AAAA,QAC5C;AAAA,QACA,EAAE,QAAQ,eAAe;AAAA,MAC3B;AAAA,MACA,qCAAqC;AAAA,QACnC;AAAA,QACA,EAAE,mBAAmB,MAAM,mBAAmB,KAAK;AAAA,MACrD;AAAA,MACA,wCAAwC;AAAA,QACtC;AAAA,QACA,EAAE,UAAU,aAAa,QAAQ,CAAC,YAAY,GAAG,QAAQ,CAAC,GAAG,EAAE;AAAA,QAC/D,EAAE,UAAU,aAAa,QAAQ,CAAC,YAAY,GAAG,QAAQ,CAAC,GAAG,EAAE;AAAA,MACjE;AAAA,IACF;AAAA,EACF;AACF,CAAC;;;AKxCD,IAAM,gBAAgB;AAAA,EACpB,GAAG;AAAA,EACH,SAAS;AAAA,IACP;AAAA,EACF;AACF;AAEA,IAAO,gBAAQ;","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 tseslint from 'typescript-eslint'\nimport type { Linter } from 'eslint'\n\nimport { plugin } from '../plugin'\n\n\nexport const recommended: Linter.Config[] = [\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 ignoreMiddleExtensions?: boolean\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 ignoreMiddleExtensions: { type: 'boolean' },\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 { ignore = [], ignoreMiddleExtensions = true } = options\n const ignoreMatchers = 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 + path.sep)\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 extension = basename.match(/\\.(ts|tsx|js|jsx)$/)?.[0] ?? ''\n const namePart = extension ? basename.slice(0, -extension.length) : basename\n const baseName = namePart.split('.')[0]\n\n const toPascalCase = (value: string): string =>\n value\n .split(/[-_]/)\n .map(part => part.charAt(0).toUpperCase() + part.slice(1))\n .join('')\n\n const segments = ignoreMiddleExtensions ? [baseName] : namePart.split('.')\n const isValid = segments.every(segment => /^[A-Z][a-zA-Z0-9]*$/.test(segment))\n\n if (!isValid) {\n const expectedName = ignoreMiddleExtensions\n ? `${toPascalCase(baseName)}${namePart.slice(baseName.length)}`\n : segments.map(toPascalCase).join('.')\n const expected = `${expectedName}${extension}`\n\n if (expected !== basename) {\n context.report({\n loc: { line: 1, column: 0 },\n messageId: 'invalid',\n data: { basename, expected },\n })\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.2.0',\n },\n rules: {\n 'filename-pascal-case': filenamePascalCase,\n 'no-multiline-imports': noMultilineImports,\n 'sort-imports': sortImports,\n },\n} as const\n","import type { ESLint } from 'eslint'\n\nimport { recommended } from './configs/recommended'\nimport { plugin } from './plugin'\n\nexport * from './rules/index'\n\n\nconst aiGuardPlugin: ESLint.Plugin = {\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.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "ESLint plugin com regras úteis para projetos com ESLint - AI Rules",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -24,7 +24,8 @@
|
|
|
24
24
|
"prelint": "yarn build",
|
|
25
25
|
"lint": "eslint .",
|
|
26
26
|
"typecheck": "tsc --noEmit",
|
|
27
|
-
"
|
|
27
|
+
"test": "yarn build && node --test test/*.test.mjs",
|
|
28
|
+
"verify": "yarn typecheck && yarn test && yarn lint && npm pack --dry-run",
|
|
28
29
|
"prepack": "yarn build",
|
|
29
30
|
"prepublishOnly": "git diff --quiet || (echo '✖ uncommitted changes — commit before publishing' && exit 1) && yarn verify",
|
|
30
31
|
"postpublish": "echo '✔ published. Run: git push --follow-tags'"
|
|
@@ -38,10 +39,18 @@
|
|
|
38
39
|
"eslint-plugin",
|
|
39
40
|
"ai-rules"
|
|
40
41
|
],
|
|
41
|
-
"author": "",
|
|
42
|
+
"author": "Lucas Souza <ls18017@gmail.com>",
|
|
42
43
|
"license": "MIT",
|
|
44
|
+
"repository": {
|
|
45
|
+
"type": "git",
|
|
46
|
+
"url": "git+https://github.com/lvsouza/eslint-ai-guard.git"
|
|
47
|
+
},
|
|
48
|
+
"homepage": "https://github.com/lvsouza/eslint-ai-guard#readme",
|
|
49
|
+
"bugs": {
|
|
50
|
+
"url": "https://github.com/lvsouza/eslint-ai-guard/issues"
|
|
51
|
+
},
|
|
43
52
|
"peerDependencies": {
|
|
44
|
-
"eslint": "^
|
|
53
|
+
"eslint": "^9.0.0",
|
|
45
54
|
"typescript": ">=5.0.0"
|
|
46
55
|
},
|
|
47
56
|
"dependencies": {
|