Skip to content

Commit 46fbfcf

Browse files
committed
feat(lint): initial release of deno-lint plugin collection 🎉
0 parents  commit 46fbfcf

28 files changed

Lines changed: 2577 additions & 0 deletions

.github/workflows/publish.yml

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
name: Publish
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
8+
jobs:
9+
publish:
10+
runs-on: ubuntu-latest
11+
permissions:
12+
contents: read
13+
id-token: write
14+
15+
steps:
16+
- uses: actions/checkout@v4
17+
- uses: denoland/setup-deno@v2
18+
with:
19+
deno-version: v2.5.4
20+
21+
- name: Run lint and check
22+
run: deno lint src/ && deno check src/
23+
24+
- name: Run lint and tests
25+
run: deno lint tests/ && deno test --no-check
26+
27+
- name: Publish package
28+
run: deno publish --allow-net --allow-slow-types

.gitignore

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# Deno
2+
.deno/
3+
4+
# IDE
5+
.vscode/
6+
.idea/
7+
8+
# OS
9+
.DS_Store
10+
Thumbs.db

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2025 NeaByteLab
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# @neabyte/deno-lint
2+
3+
Deno lint plugin collection for identifying and reporting on patterns found in code
4+
5+
## Available Rules
6+
7+
- [`async-function-naming`](./examples/async-function-naming.md) - Enforces async functions to have 'Async' suffix
8+
- [`explicit-parameter-types`](./examples/explicit-parameter-types.md) - Requires explicit type annotations on parameters
9+
- [`explicit-return-types`](./examples/explicit-return-types.md) - Requires explicit return type annotations
10+
- [`require-error-handling`](./examples/require-error-handling.md) - Ensures Deno file operations are properly awaited
11+
12+
## Installation
13+
14+
To exclude specific rules, configure them in your `deno.json`:
15+
16+
```json
17+
{
18+
"lint": {
19+
"plugins": [
20+
"jsr:@neabyte/deno-lint@0.1.0" // add this module
21+
],
22+
"rules": {
23+
"exclude": [
24+
"deno-lint/require-error-handling",
25+
"deno-lint/explicit-parameter-types"
26+
// Exclude any rules you don't want to use
27+
]
28+
}
29+
}
30+
}
31+
```
32+
33+
## License
34+
35+
MIT License - see [LICENSE](LICENSE) file for details.

deno.json

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
{
2+
"name": "@neabyte/deno-lint",
3+
"description": "Deno lint plugin collection for identifying and reporting on patterns found in code",
4+
"version": "0.1.0",
5+
"type": "module",
6+
"license": "MIT",
7+
"exports": "./src/mod.ts",
8+
"compilerOptions": {
9+
"allowUnreachableCode": false,
10+
"allowUnusedLabels": false,
11+
"checkJs": false,
12+
"exactOptionalPropertyTypes": true,
13+
"jsx": "react",
14+
"lib": ["deno.ns"],
15+
"noErrorTruncation": true,
16+
"noFallthroughCasesInSwitch": true,
17+
"noImplicitAny": true,
18+
"noImplicitOverride": true,
19+
"noImplicitReturns": true,
20+
"noImplicitThis": true,
21+
"noPropertyAccessFromIndexSignature": true,
22+
"noUncheckedIndexedAccess": true,
23+
"noUnusedLocals": true,
24+
"noUnusedParameters": true,
25+
"strict": true,
26+
"strictBindCallApply": true,
27+
"strictFunctionTypes": true,
28+
"strictNullChecks": true,
29+
"strictPropertyInitialization": true,
30+
"useUnknownInCatchVariables": true
31+
},
32+
"fmt": {
33+
"indentWidth": 2,
34+
"lineWidth": 100,
35+
"proseWrap": "preserve",
36+
"semiColons": false,
37+
"singleQuote": true,
38+
"useTabs": false,
39+
"trailingCommas": "never"
40+
},
41+
"lint": {
42+
"include": ["src/"],
43+
"rules": {
44+
"tags": ["fresh", "jsr", "jsx", "react", "recommended", "workspace"],
45+
"include": [
46+
"ban-untagged-todo",
47+
"camelcase",
48+
"default-param-last",
49+
"eqeqeq",
50+
"explicit-function-return-type",
51+
"explicit-module-boundary-types",
52+
"guard-for-in",
53+
"no-await-in-loop",
54+
"no-boolean-literal-for-arguments",
55+
"no-const-assign",
56+
"no-eval",
57+
"no-implicit-declare-namespace-export",
58+
"no-inferrable-types",
59+
"no-invalid-triple-slash-reference",
60+
"no-non-null-asserted-optional-chain",
61+
"no-non-null-assertion",
62+
"no-self-compare",
63+
"no-sparse-arrays",
64+
"no-sync-fn-in-async-fn",
65+
"no-throw-literal",
66+
"no-undef",
67+
"no-useless-rename",
68+
"no-top-level-await",
69+
"single-var-declarator"
70+
],
71+
"exclude": ["no-console", "no-external-import", "prefer-ascii", "prefer-primordials"]
72+
}
73+
},
74+
"lock": true,
75+
"test": {
76+
"include": ["tests/**/*.ts"],
77+
"exclude": ["tests/**/*.d.ts"]
78+
},
79+
"tasks": {
80+
"check": "deno fmt src/ && deno lint src/ && deno check src/",
81+
"test": "deno fmt tests/ && deno lint tests/ && deno test --no-check"
82+
},
83+
"imports": {
84+
"@std/assert": "jsr:@std/assert@^1.0.15",
85+
"@app/": "./src/",
86+
"@shared/": "./src/shared/",
87+
"@rules/": "./src/rules/",
88+
"@tests/": "./tests/"
89+
},
90+
"publish": {
91+
"exclude": [
92+
"tests/",
93+
"dist/",
94+
"coverage/",
95+
"*.test.ts",
96+
"*.spec.ts",
97+
"deno.json",
98+
"issue-testing/",
99+
"examples/",
100+
".github/"
101+
]
102+
}
103+
}

deno.lock

Lines changed: 23 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

examples/async-function-naming.md

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
# Expected Behavior: `async-function-naming` Rule
2+
3+
This rule enforces that async function declarations must have the 'Async' suffix in their name.
4+
5+
## ❌ Invalid Examples
6+
7+
```typescript
8+
// Async function declarations without 'Async' suffix
9+
async function fetchUser() {
10+
const response = await fetch('/api/user')
11+
return response.json()
12+
}
13+
14+
async function processData(data: any[]) {
15+
return data.map(item => item.processed)
16+
}
17+
18+
async function saveToDatabase(record: User) {
19+
await db.users.insert(record)
20+
}
21+
22+
async function validateInput(input: string) {
23+
return input.length > 0
24+
}
25+
26+
// Complex async functions without 'Async' suffix
27+
async function getUserByIdAndUpdate(id: string, updates: Partial<User>) {
28+
const user = await db.users.findById(id)
29+
if (user) {
30+
await db.users.update(id, updates)
31+
}
32+
return user
33+
}
34+
35+
async function handleRequest(req: Request) {
36+
const body = await req.json()
37+
const result = await processRequest(body)
38+
return new Response(JSON.stringify(result))
39+
}
40+
```
41+
42+
## ✅ Valid Examples
43+
44+
```typescript
45+
// Async function declarations with 'Async' suffix
46+
async function fetchUserAsync() {
47+
const response = await fetch('/api/user')
48+
return response.json()
49+
}
50+
51+
async function processDataAsync(data: any[]) {
52+
return data.map(item => item.processed)
53+
}
54+
55+
async function saveToDatabaseAsync(record: User) {
56+
await db.users.insert(record)
57+
}
58+
59+
async function validateInputAsync(input: string) {
60+
return input.length > 0
61+
}
62+
63+
// Complex async functions with 'Async' suffix
64+
async function getUserByIdAndUpdateAsync(id: string, updates: Partial<User>) {
65+
const user = await db.users.findById(id)
66+
if (user) {
67+
await db.users.update(id, updates)
68+
}
69+
return user
70+
}
71+
72+
async function handleRequestAsync(req: Request) {
73+
const body = await req.json()
74+
const result = await processRequest(body)
75+
return new Response(JSON.stringify(result))
76+
}
77+
78+
// Non-async functions (not affected by this rule)
79+
function calculateSum(a: number, b: number) {
80+
return a + b
81+
}
82+
83+
const processData = (data: any[]) => {
84+
return data.map(item => item.processed)
85+
}
86+
87+
// Arrow functions and function expressions (not affected by this rule)
88+
const fetchUser = async () => {
89+
const response = await fetch('/api/user')
90+
return response.json()
91+
}
92+
93+
const handler = async function (event: Event) {
94+
event.preventDefault()
95+
}
96+
97+
// Class methods (not affected by this rule)
98+
class UserService {
99+
async fetchUser() {
100+
const response = await fetch('/api/user')
101+
return response.json()
102+
}
103+
104+
async saveUser(user: User) {
105+
await db.users.insert(user)
106+
}
107+
}
108+
```
109+
110+
## Rule Scope
111+
112+
This rule **only applies to**:
113+
114+
-`async function` declarations with names
115+
116+
This rule **does NOT apply to**:
117+
118+
- ❌ Arrow functions (`const fn = async () => {}`)
119+
- ❌ Function expressions (`const fn = async function() {}`)
120+
- ❌ Class methods (`async method() {}`)
121+
- ❌ Non-async functions
122+
- ❌ Anonymous functions
123+
124+
## Auto-fix Behavior
125+
126+
The rule provides auto-fix suggestions that add the 'Async' suffix:
127+
128+
- `async function fetchUser()``async function fetchUserAsync()`
129+
- `async function processData()``async function processDataAsync()`
130+
- `async function saveToDatabase()``async function saveToDatabaseAsync()`

0 commit comments

Comments
 (0)