Skip to content

Commit 01f93cd

Browse files
feat: initial commit
0 parents  commit 01f93cd

29 files changed

+9543
-0
lines changed

.editorconfig

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# editorconfig.org
2+
root = true
3+
4+
[*]
5+
charset = utf-8
6+
end_of_line = lf
7+
indent_style = tab
8+
insert_final_newline = true
9+
tab_width = 4
10+
trim_trailing_whitespace = true

.eslint-doc-generatorrc.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import type { GenerateOptions } from "eslint-doc-generator";
2+
3+
import packageJson from "./package.json" with { type: "json" };
4+
5+
const repoUrl = packageJson.repository.url.replace(/\.git$/, "");
6+
7+
const config: GenerateOptions = {
8+
ignoreConfig: ["recommended"],
9+
ignoreDeprecatedRules: true,
10+
pathRuleDoc: "./src/rules/{name}/documentation.md",
11+
pathRuleList: "./README.md",
12+
ruleDocTitleFormat: "desc",
13+
ruleListColumns: ["name", "description", "fixable", "hasSuggestions", "requiresTypeChecking"],
14+
urlConfigs: repoUrl,
15+
};
16+
17+
export default config;

.github/workflows/ci.yaml

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
name: CI
2+
3+
on:
4+
pull_request:
5+
branches:
6+
- main
7+
8+
push:
9+
branches:
10+
- main
11+
12+
jobs:
13+
lint:
14+
runs-on: ubuntu-latest
15+
steps:
16+
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
17+
18+
- uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4
19+
with:
20+
run_install: false
21+
22+
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
23+
with:
24+
cache: pnpm
25+
node-version: lts/*
26+
27+
- run: pnpm i -g @antfu/ni
28+
- run: nci
29+
- run: nr lint
30+
- run: nr typecheck
31+
32+
test:
33+
runs-on: ${{ matrix.os }}
34+
35+
steps:
36+
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
37+
- uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4
38+
with:
39+
run_install: false
40+
41+
- name: Set node ${{ matrix.node }}
42+
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
43+
with:
44+
cache: pnpm
45+
node-version: ${{ matrix.node }}
46+
47+
- run: pnpm i -g @antfu/ni
48+
- run: nci
49+
- run: nr build
50+
- run: nr test
51+
52+
strategy:
53+
matrix:
54+
node: [lts/*]
55+
os: [ubuntu-latest, windows-latest, macos-latest]
56+
fail-fast: true

.github/workflows/release.yaml

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
name: Release
2+
3+
on:
4+
push:
5+
tags:
6+
- "v*"
7+
8+
permissions:
9+
contents: write
10+
id-token: write
11+
12+
jobs:
13+
release:
14+
runs-on: ubuntu-latest
15+
steps:
16+
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
17+
with:
18+
fetch-depth: 0
19+
20+
- uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4
21+
22+
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
23+
with:
24+
node-version: lts/*
25+
registry-url: https://registry.npmjs.org/
26+
27+
- env:
28+
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
29+
run: pnpm dlx changelogithub
30+
31+
- run: pnpm install
32+
33+
- env:
34+
NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}
35+
NPM_CONFIG_PROVENANCE: true
36+
run: pnpm publish --no-git-checks -r --access public

.gitignore

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
.cache
2+
.DS_Store
3+
.idea
4+
*.log
5+
*.tgz
6+
coverage
7+
dist
8+
lib-cov
9+
logs
10+
node_modules
11+
temp
12+
.eslintcache

.vscode/settings.json

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
{
2+
// Disable the default formatter, use eslint instead
3+
"prettier.enable": false,
4+
"editor.formatOnSave": false,
5+
6+
// Auto fix
7+
"editor.codeActionsOnSave": {
8+
"source.fixAll.eslint": "explicit",
9+
"source.organizeImports": "never"
10+
},
11+
12+
// Silent the stylistic rules in you IDE, but still auto fix them
13+
"eslint.rules.customizations": [
14+
{ "rule": "style/*", "severity": "off", "fixable": true },
15+
{ "rule": "format/*", "severity": "off", "fixable": true },
16+
{ "rule": "*-indent", "severity": "off", "fixable": true },
17+
{ "rule": "*-spacing", "severity": "off", "fixable": true },
18+
{ "rule": "*-spaces", "severity": "off", "fixable": true },
19+
{ "rule": "*-order", "severity": "off", "fixable": true },
20+
{ "rule": "*-dangle", "severity": "off", "fixable": true },
21+
{ "rule": "*-newline", "severity": "off", "fixable": true },
22+
{ "rule": "*quotes", "severity": "off", "fixable": true },
23+
{ "rule": "*semi", "severity": "off", "fixable": true }
24+
],
25+
26+
// Enable eslint for all supported languages
27+
"eslint.validate": [
28+
"github-actions-workflow",
29+
"javascript",
30+
"json",
31+
"jsonc",
32+
"luau",
33+
"markdown",
34+
"json",
35+
"toml",
36+
"typescript",
37+
"typescriptreact",
38+
"yaml"
39+
]
40+
}

LICENSE

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

README.md

Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
# eslint-plugin-template
2+
3+
> A modern, TypeScript-first template for building ESLint plugins (Flat Config
4+
> ready)
5+
6+
[![npm version][npm-version-src]][npm-version-href]
7+
[![npm downloads][npm-downloads-src]][npm-downloads-href]
8+
[![License][license-src]][license-href]
9+
10+
This repository is a starter template for creating your own ESLint plugin with:
11+
12+
- ESLint 9 Flat Config support out of the box
13+
- TypeScript with strong typing for rules and options
14+
- Vitest-based rule tests via eslint-vitest-rule-tester
15+
- Rule scaffolding script to generate rule code, tests, and docs
16+
- Auto-generated README rules section via eslint-doc-generator
17+
18+
Use it as a “Use this template” on GitHub or fork and rename.
19+
20+
## Quick start
21+
22+
1. Create your repo from this template
23+
24+
- Click “Use this template” on GitHub, or
25+
- Degit locally: `degit christopher-buss/eslint-plugin-template my-plugin`
26+
27+
2. Install dependencies
28+
29+
```pwsh
30+
pnpm i
31+
```
32+
33+
3. Rename the package and plugin
34+
35+
Update these fields to your plugin name (e.g., `eslint-plugin-awesome`):
36+
37+
- `package.json``name`, `description`, `repository`, `author`, `license`
38+
- README title and badges
39+
40+
The runtime plugin name (used in config) is derived from the package name by
41+
removing the `eslint-plugin-` prefix. For `eslint-plugin-awesome` the plugin key
42+
becomes `awesome`.
43+
44+
4. Scaffold your first rule
45+
46+
```pwsh
47+
pnpm create-rule my-new-rule
48+
```
49+
50+
This generates:
51+
52+
- `src/rules/my-new-rule/rule.ts` – rule implementation
53+
- `src/rules/my-new-rule/rule.spec.ts` – tests
54+
- `src/rules/my-new-rule/documentation.md` – rule docs
55+
56+
5. Run tests and docs
57+
58+
```pwsh
59+
pnpm test
60+
pnpm eslint-docs
61+
```
62+
63+
The docs command updates the auto-generated rules list in this README.
64+
65+
## Using your plugin
66+
67+
Once published to npm as `eslint-plugin-awesome`, you can enable it in a
68+
project.
69+
70+
### Flat Config (ESLint 9+)
71+
72+
```js
73+
// eslint.config.js / eslint.config.mjs / eslint.config.ts
74+
import yourPlugin from "eslint-plugin-awesome";
75+
76+
export default [
77+
// Enable all recommended rules from your plugin
78+
yourPlugin.configs.recommended,
79+
80+
// Or wire it manually
81+
{
82+
plugins: {
83+
awesome: yourPlugin,
84+
},
85+
rules: {
86+
"awesome/my-new-rule": "error",
87+
},
88+
},
89+
];
90+
```
91+
92+
### Legacy Config (.eslintrc)
93+
94+
```json
95+
{
96+
"extends": ["plugin:yourname/recommended"]
97+
}
98+
```
99+
100+
## Development
101+
102+
Scripts you’ll use during development:
103+
104+
- `pnpm dev` – fast stub build for local iteration
105+
- `pnpm build` – type-safe build with d.ts via tsdown
106+
- `pnpm test` – run Vitest tests
107+
- `pnpm lint` – run ESLint on this repo
108+
- `pnpm typecheck` – run `tsc --noEmit`
109+
- `pnpm eslint-docs` – regenerate README rules list
110+
- `pnpm release` – bump version via bumpp
111+
112+
Requirements:
113+
114+
- Node.js >= 20
115+
- pnpm >= 10
116+
- ESLint >= 9.15.0 (peer dep for consumers)
117+
118+
## Project structure
119+
120+
```text
121+
src/
122+
configs/ # Flat config presets (e.g., recommended)
123+
rules/ # Your rules (each in its own folder)
124+
plugin.ts # Plugin host (name, version, rules)
125+
util.ts # Rule creator with docs links
126+
index.ts # Entry combining plugin + configs (default export)
127+
scripts/
128+
create-rule.ts # Scaffolds a new rule (code, tests, docs)
129+
template/ # Rule templates used by the script
130+
```
131+
132+
{ "extends": ["plugin:awesome/recommended"] }
133+
134+
- The plugin key is computed from your package name (see `src/plugin.ts`).
135+
- `src/configs/recommended` is provided for convenience; add your rules there
136+
when ready.
137+
138+
## Scaffolding a rule
139+
140+
```pwsh
141+
pnpm create-rule my-new-rule
142+
```
143+
144+
What happens:
145+
146+
1. Creates `src/rules/my-new-rule/` with `rule.ts`, `rule.spec.ts`,
147+
`documentation.md`.
148+
2. Attempts to register the rule. If automatic edit cannot be applied, the
149+
script prints the exact import and entry you can paste into your plugin/index
150+
file.
151+
3. Run `pnpm test` to validate, then `pnpm eslint-docs` to refresh this README.
152+
153+
## Publishing
154+
155+
Typical flow:
156+
157+
```pwsh
158+
pnpm test
159+
pnpm build
160+
pnpm release # chooses the next semver and commits tags
161+
# CI publishes to npm
162+
```
163+
164+
## Rules reference
165+
166+
Generate this section with:
167+
168+
```pwsh
169+
pnpm eslint-docs
170+
```
171+
172+
<!-- begin auto-generated rules list -->
173+
<!-- end auto-generated rules list -->
174+
175+
## Contributing
176+
177+
PRs and issues welcome. If you’re using this as a template, adapt the sections
178+
to your needs and replace the badges and links.
179+
180+
## License
181+
182+
[MIT](./LICENSE) © 2025 [Christopher Buss](https://github.com/christopher-buss)
183+
184+
<!-- Badges -->
185+
186+
[npm-version-src]: https://img.shields.io/npm/v/eslint-plugin-template
187+
[npm-version-href]: https://npmjs.com/package/eslint-plugin-template
188+
[npm-downloads-src]: https://img.shields.io/npm/dm/eslint-plugin-template
189+
[npm-downloads-href]: https://npmjs.com/package/eslint-plugin-template
190+
[license-src]:
191+
https://img.shields.io/github/license/christopher-buss/eslint-plugin-template.svg
192+
[license-href]: ./LICENSE

0 commit comments

Comments
 (0)