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.
22 |
--------------------------------------------------------------------------------
/bin/index.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 | import '../dist/cli.js'
3 |
--------------------------------------------------------------------------------
/eslint.config.ts:
--------------------------------------------------------------------------------
1 | import styleMigrate from '@stylistic/eslint-plugin-migrate'
2 |
3 | import { antfu } from './src'
4 |
5 | export default antfu(
6 | {
7 | vue: {
8 | a11y: true,
9 | },
10 | react: true,
11 | solid: true,
12 | svelte: true,
13 | astro: true,
14 | typescript: true,
15 | formatters: true,
16 | pnpm: true,
17 | type: 'lib',
18 | },
19 | {
20 | ignores: [
21 | 'fixtures',
22 | '_fixtures',
23 | '**/constants-generated.ts',
24 | ],
25 | },
26 | {
27 | files: ['src/**/*.ts'],
28 | rules: {
29 | 'perfectionist/sort-objects': 'error',
30 | },
31 | },
32 | {
33 | files: ['src/configs/*.ts'],
34 | plugins: {
35 | 'style-migrate': styleMigrate,
36 | },
37 | rules: {
38 | 'style-migrate/migrate': ['error', { namespaceTo: 'style' }],
39 | },
40 | },
41 | )
42 |
--------------------------------------------------------------------------------
/fixtures/input/astro.astro:
--------------------------------------------------------------------------------
1 | ---
2 | const isJsx = true
3 | const content = "hi!";
4 | ---
5 |
6 |
7 | {content}
8 |
9 | {isJsx && (
10 |
{content}
11 | )}
12 |
13 |
14 |
15 |
16 |
22 |
--------------------------------------------------------------------------------
/fixtures/input/css.css:
--------------------------------------------------------------------------------
1 | @media (max-width: 480px) {
2 | .bd-examples {margin-right: -.75rem;margin-left: -.75rem
3 | }
4 |
5 | .bd-examples>[class^="col-"] {
6 | padding-right: .75rem;
7 | padding-left: .75rem;
8 |
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/fixtures/input/html.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | My tITlE
6 |
7 |
8 |
9 | Hello world!
This is HTML5 Boilerplate.
10 |
14 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/fixtures/input/javascript.js:
--------------------------------------------------------------------------------
1 | // This file is generated by ChatGPT
2 |
3 | // eslint-disable-next-line no-console
4 | var log = console.log
5 |
6 | // Define a class using ES6 class syntax
7 | class Person {
8 | constructor(name, age) {
9 | this.name = name;
10 | this.age = age;
11 | }
12 |
13 | // Define a method within the class
14 | sayHello() {
15 | log(`Hello, my name is ${this.name} and I am ${this.age} years old.`);
16 | }
17 | }
18 |
19 | // Create an array of objects
20 | const people = [
21 | new Person('Alice', 30),
22 | new Person('Bob', 25),
23 | new Person('Charlie', 35)
24 | ];
25 |
26 | // Use the forEach method to iterate over the array
27 | people.forEach(person => {
28 | person.sayHello();
29 | });
30 |
31 | // Use a template literal to create a multiline string
32 | const multilineString = `
33 | This is a multiline string
34 | that spans multiple lines.
35 | `;
36 |
37 | // Use destructuring assignment to extract values from an object
38 | const { name, age } = people[0];
39 | log(`First person in the array is ${name} and they are ${age} years old.`, multilineString);
40 |
41 | // Use the spread operator to create a new array
42 | const numbers = [1, 2, 3];
43 | const newNumbers = [...numbers, 4, 5];
44 | log(newNumbers);
45 |
46 | // Use a try-catch block for error handling
47 | try {
48 | // Attempt to parse an invalid JSON string
49 | JSON.parse('invalid JSON');
50 | } catch (error) {
51 | console.error('Error parsing JSON:', error.message);
52 | }
53 |
54 | // Use a ternary conditional operator
55 | const isEven = num => num % 2 === 0;
56 | const number = 7;
57 | log(`${number} is ${isEven(number) ? 'even' : 'odd'}.`);
58 |
59 | // Use a callback function with setTimeout for asynchronous code
60 | setTimeout(() => {
61 | log('This code runs after a delay of 2 seconds.');
62 | }, 2000);
63 |
64 | let a, b, c, d, foo
65 |
66 | if (a
67 | || b
68 | || c || d
69 | || (d && b)
70 | ) {
71 | foo()
72 | }
73 |
--------------------------------------------------------------------------------
/fixtures/input/jsx.jsx:
--------------------------------------------------------------------------------
1 | export function HelloWorld({
2 | greeting = "hello", greeted = '"World"', silent = false, onMouseOver,}) {
3 |
4 | if(!greeting){
5 | return null};
6 |
7 | // TODO: Don't use random in render
8 | let num = Math
9 | .floor (Math.random() * 1E+7).toString()
10 | .replace(/\.\d+/ig, "")
11 |
12 | return
13 | { greeting.slice( 0, 1 ).toUpperCase() + greeting.slice(1).toLowerCase() }
14 | {greeting.endsWith(",")
15 | ? " " : ", " }
16 |
17 | { greeted }
18 |
19 | { (silent)? ".": "!"}
20 |
;
21 |
22 | }
23 |
--------------------------------------------------------------------------------
/fixtures/input/markdown.md:
--------------------------------------------------------------------------------
1 | Header
2 | ======
3 |
4 | _Look,_ code blocks are formatted *too!*
5 |
6 | ```js
7 | // This should be handled by ESLint instead of Prettier
8 | function identity(x) {
9 | if (foo) {
10 | console.log('bar');
11 | }
12 | }
13 | ```
14 |
15 | ```css
16 | /* This should be handled by Prettier */
17 | .foo { color:red;}
18 | ```
19 |
20 | Pilot|Airport|Hours
21 | --|:--:|--:
22 | John Doe|SKG|1338
23 | Jane Roe|JFK|314
24 |
25 | - - - - - - - - - - - - - - -
26 |
27 | + List
28 | + with a [link] (/to/somewhere)
29 | + and [another one]
30 |
31 |
32 | [another one]: http://example.com 'Example title'
33 |
34 | Lorem ipsum dolor sit amet, consectetur adipiscing elit.
35 | Curabitur consectetur maximus risus, sed maximus tellus tincidunt et.
36 |
--------------------------------------------------------------------------------
/fixtures/input/svelte.svelte:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
7 | {@html content}
8 |
--------------------------------------------------------------------------------
/fixtures/input/svg.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/fixtures/input/toml.toml:
--------------------------------------------------------------------------------
1 | comma = [
2 | 1
3 | ,2
4 | ,3,
5 | ]
6 |
7 | [foo]
8 | b = 1
9 | c = "hello"
10 | a = {answer = 42}
11 |
12 | "indent" = [
13 | 1,
14 | 2
15 | ]
16 |
17 | ['a-table']
18 | apple.type = "fruit"
19 | orange.type = "fruit"
20 | apple.skin = "thin"
21 | orange.skin = "thick"
22 |
23 | apple.color = "red"
24 | orange.color = "orange"
25 |
--------------------------------------------------------------------------------
/fixtures/input/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ESNext",
4 | "module": "ESNext",
5 | "moduleResolution": "Bundler",
6 | "strict": true,
7 | "skipDefaultLibCheck": true,
8 | "skipLibCheck": true
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/fixtures/input/tsx.tsx:
--------------------------------------------------------------------------------
1 | export function Component1() {
2 | return ;
3 | }
4 |
5 | export function jsx2() {
6 | const props = {a:1,
7 | b:2}
8 | return < a foo= 'bar' bar={`foo` } >
9 | Inline Text
12 |
13 | Block Text
14 |
15 |
16 | Mixed
17 |
Foo
18 | Text
Bar
19 |
20 |
21 | foobarbaz
22 |
23 | a >
24 | }
25 |
--------------------------------------------------------------------------------
/fixtures/input/typescript.ts:
--------------------------------------------------------------------------------
1 | // Define a TypeScript interface
2 | interface Person {
3 | name: string; age: number;
4 | }
5 |
6 | // Create an array of objects with the defined interface
7 | const people: Person[] = [
8 | { name: 'Alice', age: 30 },
9 | { name: 'Bob', age: 25 },
10 | { name: 'Charlie',
11 | age: 35 }
12 | ];
13 |
14 | // eslint-disable-next-line no-console
15 | var log = console.log
16 |
17 | // Use a for...of loop to iterate over the array
18 | for (const person of people) {
19 | log(`Hello, my name is ${person.name} and I am ${person.age} years old.`);
20 | }
21 |
22 | // Define a generic function
23 | function identity< T >(arg: T): T {
24 | return arg;
25 | }
26 |
27 | // Use the generic function with type inference
28 | const result = identity(
29 | 'TypeScript is awesome');
30 | log(result);
31 |
32 | // Use optional properties in an interface
33 | interface Car {
34 | make: string;
35 | model?: string;
36 | }
37 |
38 | // Create objects using the interface
39 | const car1: Car = { make: 'Toyota' };
40 | const car2: Car = {
41 | make: 'Ford', model: 'Focus' };
42 |
43 | // Use union types
44 | type Fruit = 'apple' | 'banana' | 'orange';
45 | const favoriteFruit: Fruit = 'apple';
46 |
47 | // Use a type assertion to tell TypeScript about the type
48 | const inputValue: any = '42';
49 | const numericValue = inputValue as number;
50 |
51 | // Define a class with access modifiers
52 | class Animal {
53 | private name: string;
54 | constructor(name: string) {
55 | this.name = name;
56 | }
57 | protected makeSound(sound: string) {
58 | log(`${this.name} says ${sound}`);
59 | }
60 | }
61 |
62 | // Extend a class
63 | class Dog extends Animal {
64 | constructor(private alias: string) {
65 | super(alias);
66 | }
67 | bark() {
68 | this.makeSound('Woof!');
69 | }
70 | }
71 |
72 | const dog = new Dog('Buddy');
73 | dog.bark();
74 |
75 | var fn = (): string => {
76 | return 'hello' + 1
77 | }
78 |
79 | log(car1, car2, favoriteFruit, numericValue, fn())
80 |
81 | // Generator
82 | export function* generator1() {
83 | let id = 0;
84 | while (id < 100) {
85 | yield id++;
86 | }
87 | }
88 | export function * generator2() {
89 | yield* generator1()
90 | }
91 |
--------------------------------------------------------------------------------
/fixtures/input/vue-ts.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
{{ greeting }}
4 |
6 |
Counter: {{ counter }}
7 |
8 |
9 |
10 |
22 |
23 |
26 |
27 |
35 |
--------------------------------------------------------------------------------
/fixtures/input/vue.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | {{ greeting }}
5 |
6 |
Counter: {{ counter }}
7 |
8 |
9 |
10 |
25 |
--------------------------------------------------------------------------------
/fixtures/input/xml.xml:
--------------------------------------------------------------------------------
1 |
2 | Effective Java45.00
3 | Bluetooth Speaker120.00
4 | Clean Code
5 | 33.50
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/fixtures/output/all/astro.astro:
--------------------------------------------------------------------------------
1 | ---
2 | const isJsx = true
3 | const content = 'hi!';
4 | ---
5 |
6 |
7 | {content}
8 |
9 | {isJsx && (
10 |
{content}
11 | )}
12 |
13 |
14 |
15 |
16 |
22 |
--------------------------------------------------------------------------------
/fixtures/output/all/javascript.js:
--------------------------------------------------------------------------------
1 | // This file is generated by ChatGPT
2 |
3 | // eslint-disable-next-line no-console
4 | const log = console.log
5 |
6 | // Define a class using ES6 class syntax
7 | class Person {
8 | constructor(name, age) {
9 | this.name = name
10 | this.age = age
11 | }
12 |
13 | // Define a method within the class
14 | sayHello() {
15 | log(`Hello, my name is ${this.name} and I am ${this.age} years old.`)
16 | }
17 | }
18 |
19 | // Create an array of objects
20 | const people = [
21 | new Person('Alice', 30),
22 | new Person('Bob', 25),
23 | new Person('Charlie', 35),
24 | ]
25 |
26 | // Use the forEach method to iterate over the array
27 | people.forEach((person) => {
28 | person.sayHello()
29 | })
30 |
31 | // Use a template literal to create a multiline string
32 | const multilineString = `
33 | This is a multiline string
34 | that spans multiple lines.
35 | `
36 |
37 | // Use destructuring assignment to extract values from an object
38 | const { name, age } = people[0]
39 | log(`First person in the array is ${name} and they are ${age} years old.`, multilineString)
40 |
41 | // Use the spread operator to create a new array
42 | const numbers = [1, 2, 3]
43 | const newNumbers = [...numbers, 4, 5]
44 | log(newNumbers)
45 |
46 | // Use a try-catch block for error handling
47 | try {
48 | // Attempt to parse an invalid JSON string
49 | JSON.parse('invalid JSON')
50 | }
51 | catch (error) {
52 | console.error('Error parsing JSON:', error.message)
53 | }
54 |
55 | // Use a ternary conditional operator
56 | const isEven = num => num % 2 === 0
57 | const number = 7
58 | log(`${number} is ${isEven(number) ? 'even' : 'odd'}.`)
59 |
60 | // Use a callback function with setTimeout for asynchronous code
61 | setTimeout(() => {
62 | log('This code runs after a delay of 2 seconds.')
63 | }, 2000)
64 |
65 | let a, b, c, d, foo
66 |
67 | if (a
68 | || b
69 | || c || d
70 | || (d && b)
71 | ) {
72 | foo()
73 | }
74 |
--------------------------------------------------------------------------------
/fixtures/output/all/jsx.jsx:
--------------------------------------------------------------------------------
1 | export function HelloWorld({
2 | greeting = 'hello',
3 | greeted = '"World"',
4 | silent = false,
5 | onMouseOver,
6 | }) {
7 | if (!greeting) {
8 | return null
9 | };
10 |
11 | // TODO: Don't use random in render
12 | const num = Math
13 | .floor (Math.random() * 1e+7)
14 | .toString()
15 | .replace(/\.\d+/g, '')
16 |
17 | return (
18 |
19 | { greeting.slice(0, 1).toUpperCase() + greeting.slice(1).toLowerCase() }
20 | {greeting.endsWith(',')
21 | ? ' '
22 | : ", " }
23 |
24 | { greeted }
25 |
26 | { (silent) ? '.' : '!'}
27 |
28 | )
29 | }
30 |
--------------------------------------------------------------------------------
/fixtures/output/all/markdown.md:
--------------------------------------------------------------------------------
1 | Header
2 | ======
3 |
4 | _Look,_ code blocks are formatted *too!*
5 |
6 | ```js
7 | // This should be handled by ESLint instead of Prettier
8 | function identity(x) {
9 | if (foo) {
10 | console.log('bar')
11 | }
12 | }
13 | ```
14 |
15 | ```css
16 | /* This should be handled by Prettier */
17 | .foo { color:red;}
18 | ```
19 |
20 | Pilot|Airport|Hours
21 | --|:--:|--:
22 | John Doe|SKG|1338
23 | Jane Roe|JFK|314
24 |
25 | - - - - - - - - - - - - - - -
26 |
27 | + List
28 | + with a [link] (/to/somewhere)
29 | + and [another one]
30 |
31 | [another one]: http://example.com 'Example title'
32 |
33 | Lorem ipsum dolor sit amet, consectetur adipiscing elit.
34 | Curabitur consectetur maximus risus, sed maximus tellus tincidunt et.
35 |
--------------------------------------------------------------------------------
/fixtures/output/all/svelte.svelte:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
7 | {@html content}
8 |
9 |
--------------------------------------------------------------------------------
/fixtures/output/all/toml.toml:
--------------------------------------------------------------------------------
1 | comma = [
2 | 1,
3 | 2,
4 | 3,
5 | ]
6 |
7 | [foo]
8 | b = 1
9 | c = "hello"
10 | a = { answer = 42 }
11 | indent = [
12 | 1,
13 | 2
14 | ]
15 |
16 | [a-table]
17 | apple.type = "fruit"
18 | apple.skin = "thin"
19 | apple.color = "red"
20 |
21 | orange.type = "fruit"
22 | orange.skin = "thick"
23 | orange.color = "orange"
24 |
--------------------------------------------------------------------------------
/fixtures/output/all/tsx.tsx:
--------------------------------------------------------------------------------
1 | export function Component1() {
2 | return
3 | }
4 |
5 | export function jsx2() {
6 | const props = { a: 1, b: 2 }
7 | return (
8 |
9 |
14 | Inline Text
15 |
16 |
17 | Block Text
18 |
19 |
20 | Mixed
21 |
Foo
22 | Text
23 |
Bar
24 |
25 |
26 | foo
27 | bar
28 | baz
29 |
30 |
31 | )
32 | }
33 |
--------------------------------------------------------------------------------
/fixtures/output/all/typescript.ts:
--------------------------------------------------------------------------------
1 | // Define a TypeScript interface
2 | interface Person {
3 | name: string
4 | age: number
5 | }
6 |
7 | // Create an array of objects with the defined interface
8 | const people: Person[] = [
9 | { name: 'Alice', age: 30 },
10 | { name: 'Bob', age: 25 },
11 | { name: 'Charlie', age: 35 },
12 | ]
13 |
14 | // eslint-disable-next-line no-console
15 | const log = console.log
16 |
17 | // Use a for...of loop to iterate over the array
18 | for (const person of people) {
19 | log(`Hello, my name is ${person.name} and I am ${person.age} years old.`)
20 | }
21 |
22 | // Define a generic function
23 | function identity(arg: T): T {
24 | return arg
25 | }
26 |
27 | // Use the generic function with type inference
28 | const result = identity(
29 | 'TypeScript is awesome',
30 | )
31 | log(result)
32 |
33 | // Use optional properties in an interface
34 | interface Car {
35 | make: string
36 | model?: string
37 | }
38 |
39 | // Create objects using the interface
40 | const car1: Car = { make: 'Toyota' }
41 | const car2: Car = {
42 | make: 'Ford',
43 | model: 'Focus',
44 | }
45 |
46 | // Use union types
47 | type Fruit = 'apple' | 'banana' | 'orange'
48 | const favoriteFruit: Fruit = 'apple'
49 |
50 | // Use a type assertion to tell TypeScript about the type
51 | const inputValue: any = '42'
52 | const numericValue = inputValue as number
53 |
54 | // Define a class with access modifiers
55 | class Animal {
56 | private name: string
57 | constructor(name: string) {
58 | this.name = name
59 | }
60 |
61 | protected makeSound(sound: string) {
62 | log(`${this.name} says ${sound}`)
63 | }
64 | }
65 |
66 | // Extend a class
67 | class Dog extends Animal {
68 | constructor(private alias: string) {
69 | super(alias)
70 | }
71 |
72 | bark() {
73 | this.makeSound('Woof!')
74 | }
75 | }
76 |
77 | const dog = new Dog('Buddy')
78 | dog.bark()
79 |
80 | function fn(): string {
81 | return `hello${1}`
82 | }
83 |
84 | log(car1, car2, favoriteFruit, numericValue, fn())
85 |
86 | // Generator
87 | export function* generator1() {
88 | let id = 0
89 | while (id < 100) {
90 | yield id++
91 | }
92 | }
93 | export function* generator2() {
94 | yield* generator1()
95 | }
96 |
--------------------------------------------------------------------------------
/fixtures/output/all/vue-ts.vue:
--------------------------------------------------------------------------------
1 |
13 |
14 |
15 |
16 |
{{ greeting }}
17 |
20 |
Counter: {{ counter }}
21 |
22 |
23 |
24 |
27 |
28 |
36 |
--------------------------------------------------------------------------------
/fixtures/output/all/vue.vue:
--------------------------------------------------------------------------------
1 |
16 |
17 |
18 |
19 |
20 | {{ greeting }}
21 |
22 |
25 |
Counter: {{ counter }}
26 |
27 |
28 |
--------------------------------------------------------------------------------
/fixtures/output/js/javascript.js:
--------------------------------------------------------------------------------
1 | // This file is generated by ChatGPT
2 |
3 | // eslint-disable-next-line no-console
4 | const log = console.log
5 |
6 | // Define a class using ES6 class syntax
7 | class Person {
8 | constructor(name, age) {
9 | this.name = name
10 | this.age = age
11 | }
12 |
13 | // Define a method within the class
14 | sayHello() {
15 | log(`Hello, my name is ${this.name} and I am ${this.age} years old.`)
16 | }
17 | }
18 |
19 | // Create an array of objects
20 | const people = [
21 | new Person('Alice', 30),
22 | new Person('Bob', 25),
23 | new Person('Charlie', 35),
24 | ]
25 |
26 | // Use the forEach method to iterate over the array
27 | people.forEach((person) => {
28 | person.sayHello()
29 | })
30 |
31 | // Use a template literal to create a multiline string
32 | const multilineString = `
33 | This is a multiline string
34 | that spans multiple lines.
35 | `
36 |
37 | // Use destructuring assignment to extract values from an object
38 | const { name, age } = people[0]
39 | log(`First person in the array is ${name} and they are ${age} years old.`, multilineString)
40 |
41 | // Use the spread operator to create a new array
42 | const numbers = [1, 2, 3]
43 | const newNumbers = [...numbers, 4, 5]
44 | log(newNumbers)
45 |
46 | // Use a try-catch block for error handling
47 | try {
48 | // Attempt to parse an invalid JSON string
49 | JSON.parse('invalid JSON')
50 | }
51 | catch (error) {
52 | console.error('Error parsing JSON:', error.message)
53 | }
54 |
55 | // Use a ternary conditional operator
56 | const isEven = num => num % 2 === 0
57 | const number = 7
58 | log(`${number} is ${isEven(number) ? 'even' : 'odd'}.`)
59 |
60 | // Use a callback function with setTimeout for asynchronous code
61 | setTimeout(() => {
62 | log('This code runs after a delay of 2 seconds.')
63 | }, 2000)
64 |
65 | let a, b, c, d, foo
66 |
67 | if (a
68 | || b
69 | || c || d
70 | || (d && b)
71 | ) {
72 | foo()
73 | }
74 |
--------------------------------------------------------------------------------
/fixtures/output/js/jsx.jsx:
--------------------------------------------------------------------------------
1 | export function HelloWorld({
2 | greeting = 'hello',
3 | greeted = '"World"',
4 | silent = false,
5 | onMouseOver,
6 | }) {
7 | if (!greeting) {
8 | return null
9 | };
10 |
11 | // TODO: Don't use random in render
12 | const num = Math
13 | .floor (Math.random() * 1e+7)
14 | .toString()
15 | .replace(/\.\d+/g, '')
16 |
17 | return (
18 |
19 | { greeting.slice(0, 1).toUpperCase() + greeting.slice(1).toLowerCase() }
20 | {greeting.endsWith(',')
21 | ? ' '
22 | : ", " }
23 |
24 | { greeted }
25 |
26 | { (silent) ? '.' : '!'}
27 |
28 | )
29 | }
30 |
--------------------------------------------------------------------------------
/fixtures/output/js/markdown.md:
--------------------------------------------------------------------------------
1 | Header
2 | ======
3 |
4 | _Look,_ code blocks are formatted *too!*
5 |
6 | ```js
7 | // This should be handled by ESLint instead of Prettier
8 | function identity(x) {
9 | if (foo) {
10 | console.log('bar')
11 | }
12 | }
13 | ```
14 |
15 | ```css
16 | /* This should be handled by Prettier */
17 | .foo { color:red;}
18 | ```
19 |
20 | Pilot|Airport|Hours
21 | --|:--:|--:
22 | John Doe|SKG|1338
23 | Jane Roe|JFK|314
24 |
25 | - - - - - - - - - - - - - - -
26 |
27 | + List
28 | + with a [link] (/to/somewhere)
29 | + and [another one]
30 |
31 | [another one]: http://example.com 'Example title'
32 |
33 | Lorem ipsum dolor sit amet, consectetur adipiscing elit.
34 | Curabitur consectetur maximus risus, sed maximus tellus tincidunt et.
35 |
--------------------------------------------------------------------------------
/fixtures/output/js/toml.toml:
--------------------------------------------------------------------------------
1 | comma = [
2 | 1,
3 | 2,
4 | 3,
5 | ]
6 |
7 | [foo]
8 | b = 1
9 | c = "hello"
10 | a = { answer = 42 }
11 | indent = [
12 | 1,
13 | 2
14 | ]
15 |
16 | [a-table]
17 | apple.type = "fruit"
18 | apple.skin = "thin"
19 | apple.color = "red"
20 |
21 | orange.type = "fruit"
22 | orange.skin = "thick"
23 | orange.color = "orange"
24 |
--------------------------------------------------------------------------------
/fixtures/output/js/tsx.tsx:
--------------------------------------------------------------------------------
1 | export function Component1() {
2 | return
3 | }
4 |
5 | export function jsx2() {
6 | const props = { a: 1, b: 2 }
7 | return (
8 |
9 |
14 | Inline Text
15 |
16 |
17 | Block Text
18 |
19 |
20 | Mixed
21 |
Foo
22 | Text
23 |
Bar
24 |
25 |
26 | foo
27 | bar
28 | baz
29 |
30 |
31 | )
32 | }
33 |
--------------------------------------------------------------------------------
/fixtures/output/no-markdown-with-formatters/javascript.js:
--------------------------------------------------------------------------------
1 | // This file is generated by ChatGPT
2 |
3 | // eslint-disable-next-line no-console
4 | const log = console.log
5 |
6 | // Define a class using ES6 class syntax
7 | class Person {
8 | constructor(name, age) {
9 | this.name = name
10 | this.age = age
11 | }
12 |
13 | // Define a method within the class
14 | sayHello() {
15 | log(`Hello, my name is ${this.name} and I am ${this.age} years old.`)
16 | }
17 | }
18 |
19 | // Create an array of objects
20 | const people = [
21 | new Person('Alice', 30),
22 | new Person('Bob', 25),
23 | new Person('Charlie', 35),
24 | ]
25 |
26 | // Use the forEach method to iterate over the array
27 | people.forEach((person) => {
28 | person.sayHello()
29 | })
30 |
31 | // Use a template literal to create a multiline string
32 | const multilineString = `
33 | This is a multiline string
34 | that spans multiple lines.
35 | `
36 |
37 | // Use destructuring assignment to extract values from an object
38 | const { name, age } = people[0]
39 | log(`First person in the array is ${name} and they are ${age} years old.`, multilineString)
40 |
41 | // Use the spread operator to create a new array
42 | const numbers = [1, 2, 3]
43 | const newNumbers = [...numbers, 4, 5]
44 | log(newNumbers)
45 |
46 | // Use a try-catch block for error handling
47 | try {
48 | // Attempt to parse an invalid JSON string
49 | JSON.parse('invalid JSON')
50 | }
51 | catch (error) {
52 | console.error('Error parsing JSON:', error.message)
53 | }
54 |
55 | // Use a ternary conditional operator
56 | const isEven = num => num % 2 === 0
57 | const number = 7
58 | log(`${number} is ${isEven(number) ? 'even' : 'odd'}.`)
59 |
60 | // Use a callback function with setTimeout for asynchronous code
61 | setTimeout(() => {
62 | log('This code runs after a delay of 2 seconds.')
63 | }, 2000)
64 |
65 | let a, b, c, d, foo
66 |
67 | if (a
68 | || b
69 | || c || d
70 | || (d && b)
71 | ) {
72 | foo()
73 | }
74 |
--------------------------------------------------------------------------------
/fixtures/output/no-markdown-with-formatters/markdown.md:
--------------------------------------------------------------------------------
1 | # Header
2 |
3 | _Look,_ code blocks are formatted _too!_
4 |
5 | ```js
6 | // This should be handled by ESLint instead of Prettier
7 | function identity(x) {
8 | if (foo) {
9 | console.log('bar');
10 | }
11 | }
12 | ```
13 |
14 | ```css
15 | /* This should be handled by Prettier */
16 | .foo { color:red;}
17 | ```
18 |
19 | | Pilot | Airport | Hours |
20 | | -------- | :-----: | ----: |
21 | | John Doe | SKG | 1338 |
22 | | Jane Roe | JFK | 314 |
23 |
24 | ---
25 |
26 | - List
27 | - with a [link] (/to/somewhere)
28 | - and [another one]
29 |
30 | [another one]: http://example.com 'Example title'
31 |
32 | Lorem ipsum dolor sit amet, consectetur adipiscing elit.
33 | Curabitur consectetur maximus risus, sed maximus tellus tincidunt et.
34 |
--------------------------------------------------------------------------------
/fixtures/output/no-markdown-with-formatters/toml.toml:
--------------------------------------------------------------------------------
1 | comma = [
2 | 1,
3 | 2,
4 | 3,
5 | ]
6 |
7 | [foo]
8 | b = 1
9 | c = "hello"
10 | a = { answer = 42 }
11 | indent = [
12 | 1,
13 | 2
14 | ]
15 |
16 | [a-table]
17 | apple.type = "fruit"
18 | apple.skin = "thin"
19 | apple.color = "red"
20 |
21 | orange.type = "fruit"
22 | orange.skin = "thick"
23 | orange.color = "orange"
24 |
--------------------------------------------------------------------------------
/fixtures/output/no-markdown-with-formatters/tsx.tsx:
--------------------------------------------------------------------------------
1 | export function Component1() {
2 | return
3 | }
4 |
5 | export function jsx2() {
6 | const props = { a: 1, b: 2 }
7 | return < a foo= 'bar' bar={`foo` } >
8 | Inline Text
9 |
10 | Block Text
11 |
12 |
13 | Mixed
14 |
Foo
15 | Text
Bar
16 |
17 |
18 | foobarbaz
19 |
20 | a >
21 | }
22 |
--------------------------------------------------------------------------------
/fixtures/output/no-markdown-with-formatters/typescript.ts:
--------------------------------------------------------------------------------
1 | // Define a TypeScript interface
2 | interface Person {
3 | name: string
4 | age: number
5 | }
6 |
7 | // Create an array of objects with the defined interface
8 | const people: Person[] = [
9 | { name: 'Alice', age: 30 },
10 | { name: 'Bob', age: 25 },
11 | { name: 'Charlie', age: 35 },
12 | ]
13 |
14 | // eslint-disable-next-line no-console
15 | const log = console.log
16 |
17 | // Use a for...of loop to iterate over the array
18 | for (const person of people) {
19 | log(`Hello, my name is ${person.name} and I am ${person.age} years old.`)
20 | }
21 |
22 | // Define a generic function
23 | function identity(arg: T): T {
24 | return arg
25 | }
26 |
27 | // Use the generic function with type inference
28 | const result = identity(
29 | 'TypeScript is awesome',
30 | )
31 | log(result)
32 |
33 | // Use optional properties in an interface
34 | interface Car {
35 | make: string
36 | model?: string
37 | }
38 |
39 | // Create objects using the interface
40 | const car1: Car = { make: 'Toyota' }
41 | const car2: Car = {
42 | make: 'Ford',
43 | model: 'Focus',
44 | }
45 |
46 | // Use union types
47 | type Fruit = 'apple' | 'banana' | 'orange'
48 | const favoriteFruit: Fruit = 'apple'
49 |
50 | // Use a type assertion to tell TypeScript about the type
51 | const inputValue: any = '42'
52 | const numericValue = inputValue as number
53 |
54 | // Define a class with access modifiers
55 | class Animal {
56 | private name: string
57 | constructor(name: string) {
58 | this.name = name
59 | }
60 |
61 | protected makeSound(sound: string) {
62 | log(`${this.name} says ${sound}`)
63 | }
64 | }
65 |
66 | // Extend a class
67 | class Dog extends Animal {
68 | constructor(private alias: string) {
69 | super(alias)
70 | }
71 |
72 | bark() {
73 | this.makeSound('Woof!')
74 | }
75 | }
76 |
77 | const dog = new Dog('Buddy')
78 | dog.bark()
79 |
80 | function fn(): string {
81 | return `hello${1}`
82 | }
83 |
84 | log(car1, car2, favoriteFruit, numericValue, fn())
85 |
86 | // Generator
87 | export function* generator1() {
88 | let id = 0
89 | while (id < 100) {
90 | yield id++
91 | }
92 | }
93 | export function* generator2() {
94 | yield* generator1()
95 | }
96 |
--------------------------------------------------------------------------------
/fixtures/output/no-style/javascript.js:
--------------------------------------------------------------------------------
1 | // This file is generated by ChatGPT
2 |
3 | // eslint-disable-next-line no-console
4 | const log = console.log
5 |
6 | // Define a class using ES6 class syntax
7 | class Person {
8 | constructor(name, age) {
9 | this.name = name;
10 | this.age = age;
11 | }
12 |
13 | // Define a method within the class
14 | sayHello() {
15 | log(`Hello, my name is ${this.name} and I am ${this.age} years old.`);
16 | }
17 | }
18 |
19 | // Create an array of objects
20 | const people = [
21 | new Person('Alice', 30),
22 | new Person('Bob', 25),
23 | new Person('Charlie', 35)
24 | ];
25 |
26 | // Use the forEach method to iterate over the array
27 | people.forEach(person => {
28 | person.sayHello();
29 | });
30 |
31 | // Use a template literal to create a multiline string
32 | const multilineString = `
33 | This is a multiline string
34 | that spans multiple lines.
35 | `;
36 |
37 | // Use destructuring assignment to extract values from an object
38 | const { name, age } = people[0];
39 | log(`First person in the array is ${name} and they are ${age} years old.`, multilineString);
40 |
41 | // Use the spread operator to create a new array
42 | const numbers = [1, 2, 3];
43 | const newNumbers = [...numbers, 4, 5];
44 | log(newNumbers);
45 |
46 | // Use a try-catch block for error handling
47 | try {
48 | // Attempt to parse an invalid JSON string
49 | JSON.parse('invalid JSON');
50 | } catch (error) {
51 | console.error('Error parsing JSON:', error.message);
52 | }
53 |
54 | // Use a ternary conditional operator
55 | const isEven = num => num % 2 === 0;
56 | const number = 7;
57 | log(`${number} is ${isEven(number) ? 'even' : 'odd'}.`);
58 |
59 | // Use a callback function with setTimeout for asynchronous code
60 | setTimeout(() => {
61 | log('This code runs after a delay of 2 seconds.');
62 | }, 2000);
63 |
64 | let a, b, c, d, foo
65 |
66 | if (a
67 | || b
68 | || c || d
69 | || (d && b)
70 | ) {
71 | foo()
72 | }
73 |
--------------------------------------------------------------------------------
/fixtures/output/no-style/jsx.jsx:
--------------------------------------------------------------------------------
1 | export function HelloWorld({
2 | greeting = "hello", greeted = '"World"', silent = false, onMouseOver,}) {
3 |
4 | if(!greeting){
5 | return null};
6 |
7 | // TODO: Don't use random in render
8 | const num = Math
9 | .floor (Math.random() * 1e+7).toString()
10 | .replace(/\.\d+/g, "")
11 |
12 | return
13 | { greeting.slice( 0, 1 ).toUpperCase() + greeting.slice(1).toLowerCase() }
14 | {greeting.endsWith(",")
15 | ? " " : ", " }
16 |
17 | { greeted }
18 |
19 | { (silent)? ".": "!"}
20 |
;
21 |
22 | }
23 |
--------------------------------------------------------------------------------
/fixtures/output/no-style/toml.toml:
--------------------------------------------------------------------------------
1 | comma = [
2 | 1,
3 | 2,
4 | 3,
5 | ]
6 |
7 | [foo]
8 | b = 1
9 | c = "hello"
10 | a = {answer = 42}
11 |
12 | "indent" = [
13 | 1,
14 | 2
15 | ]
16 |
17 | ['a-table']
18 | apple.type = "fruit"
19 | apple.skin = "thin"
20 | apple.color = "red"
21 | orange.type = "fruit"
22 | orange.skin = "thick"
23 | orange.color = "orange"
24 |
--------------------------------------------------------------------------------
/fixtures/output/no-style/typescript.ts:
--------------------------------------------------------------------------------
1 | // Define a TypeScript interface
2 | interface Person {
3 | name: string; age: number;
4 | }
5 |
6 | // Create an array of objects with the defined interface
7 | const people: Person[] = [
8 | { name: 'Alice', age: 30 },
9 | { name: 'Bob', age: 25 },
10 | { name: 'Charlie',
11 | age: 35 }
12 | ];
13 |
14 | // eslint-disable-next-line no-console
15 | const log = console.log
16 |
17 | // Use a for...of loop to iterate over the array
18 | for (const person of people) {
19 | log(`Hello, my name is ${person.name} and I am ${person.age} years old.`);
20 | }
21 |
22 | // Define a generic function
23 | function identity< T >(arg: T): T {
24 | return arg;
25 | }
26 |
27 | // Use the generic function with type inference
28 | const result = identity(
29 | 'TypeScript is awesome');
30 | log(result);
31 |
32 | // Use optional properties in an interface
33 | interface Car {
34 | make: string;
35 | model?: string;
36 | }
37 |
38 | // Create objects using the interface
39 | const car1: Car = { make: 'Toyota' };
40 | const car2: Car = {
41 | make: 'Ford', model: 'Focus' };
42 |
43 | // Use union types
44 | type Fruit = 'apple' | 'banana' | 'orange';
45 | const favoriteFruit: Fruit = 'apple';
46 |
47 | // Use a type assertion to tell TypeScript about the type
48 | const inputValue: any = '42';
49 | const numericValue = inputValue as number;
50 |
51 | // Define a class with access modifiers
52 | class Animal {
53 | private name: string;
54 | constructor(name: string) {
55 | this.name = name;
56 | }
57 | protected makeSound(sound: string) {
58 | log(`${this.name} says ${sound}`);
59 | }
60 | }
61 |
62 | // Extend a class
63 | class Dog extends Animal {
64 | constructor(private alias: string) {
65 | super(alias);
66 | }
67 | bark() {
68 | this.makeSound('Woof!');
69 | }
70 | }
71 |
72 | const dog = new Dog('Buddy');
73 | dog.bark();
74 |
75 | const fn = (): string => {
76 | return `hello${ 1}`
77 | }
78 |
79 | log(car1, car2, favoriteFruit, numericValue, fn())
80 |
81 | // Generator
82 | export function* generator1() {
83 | let id = 0;
84 | while (id < 100) {
85 | yield id++;
86 | }
87 | }
88 | export function * generator2() {
89 | yield* generator1()
90 | }
91 |
--------------------------------------------------------------------------------
/fixtures/output/no-style/vue-ts.vue:
--------------------------------------------------------------------------------
1 |
13 |
14 |
15 |
16 |
{{ greeting }}
17 |
20 |
Counter: {{ counter }}
21 |
22 |
23 |
24 |
27 |
28 |
36 |
--------------------------------------------------------------------------------
/fixtures/output/no-style/vue.vue:
--------------------------------------------------------------------------------
1 |
16 |
17 |
18 |
19 |
20 | {{ greeting }}
21 |
22 |
25 |
Counter: {{ counter }}
26 |
27 |
28 |
--------------------------------------------------------------------------------
/fixtures/output/tab-double-quotes/javascript.js:
--------------------------------------------------------------------------------
1 | // This file is generated by ChatGPT
2 |
3 | // eslint-disable-next-line no-console
4 | const log = console.log
5 |
6 | // Define a class using ES6 class syntax
7 | class Person {
8 | constructor(name, age) {
9 | this.name = name
10 | this.age = age
11 | }
12 |
13 | // Define a method within the class
14 | sayHello() {
15 | log(`Hello, my name is ${this.name} and I am ${this.age} years old.`)
16 | }
17 | }
18 |
19 | // Create an array of objects
20 | const people = [
21 | new Person("Alice", 30),
22 | new Person("Bob", 25),
23 | new Person("Charlie", 35),
24 | ]
25 |
26 | // Use the forEach method to iterate over the array
27 | people.forEach((person) => {
28 | person.sayHello()
29 | })
30 |
31 | // Use a template literal to create a multiline string
32 | const multilineString = `
33 | This is a multiline string
34 | that spans multiple lines.
35 | `
36 |
37 | // Use destructuring assignment to extract values from an object
38 | const { name, age } = people[0]
39 | log(`First person in the array is ${name} and they are ${age} years old.`, multilineString)
40 |
41 | // Use the spread operator to create a new array
42 | const numbers = [1, 2, 3]
43 | const newNumbers = [...numbers, 4, 5]
44 | log(newNumbers)
45 |
46 | // Use a try-catch block for error handling
47 | try {
48 | // Attempt to parse an invalid JSON string
49 | JSON.parse("invalid JSON")
50 | }
51 | catch (error) {
52 | console.error("Error parsing JSON:", error.message)
53 | }
54 |
55 | // Use a ternary conditional operator
56 | const isEven = num => num % 2 === 0
57 | const number = 7
58 | log(`${number} is ${isEven(number) ? "even" : "odd"}.`)
59 |
60 | // Use a callback function with setTimeout for asynchronous code
61 | setTimeout(() => {
62 | log("This code runs after a delay of 2 seconds.")
63 | }, 2000)
64 |
65 | let a, b, c, d, foo
66 |
67 | if (a
68 | || b
69 | || c || d
70 | || (d && b)
71 | ) {
72 | foo()
73 | }
74 |
--------------------------------------------------------------------------------
/fixtures/output/tab-double-quotes/jsx.jsx:
--------------------------------------------------------------------------------
1 | export function HelloWorld({
2 | greeting = "hello",
3 | greeted = "\"World\"",
4 | silent = false,
5 | onMouseOver,
6 | }) {
7 | if (!greeting) {
8 | return null
9 | };
10 |
11 | // TODO: Don't use random in render
12 | const num = Math
13 | .floor (Math.random() * 1e+7)
14 | .toString()
15 | .replace(/\.\d+/g, "")
16 |
17 | return (
18 |
19 | { greeting.slice(0, 1).toUpperCase() + greeting.slice(1).toLowerCase() }
20 | {greeting.endsWith(",")
21 | ? " "
22 | : ", " }
23 |
24 | { greeted }
25 |
26 | { (silent) ? "." : "!"}
27 |
28 | )
29 | }
30 |
--------------------------------------------------------------------------------
/fixtures/output/tab-double-quotes/markdown.md:
--------------------------------------------------------------------------------
1 | Header
2 | ======
3 |
4 | _Look,_ code blocks are formatted *too!*
5 |
6 | ```js
7 | // This should be handled by ESLint instead of Prettier
8 | function identity(x) {
9 | if (foo) {
10 | console.log("bar")
11 | }
12 | }
13 | ```
14 |
15 | ```css
16 | /* This should be handled by Prettier */
17 | .foo { color:red;}
18 | ```
19 |
20 | Pilot|Airport|Hours
21 | --|:--:|--:
22 | John Doe|SKG|1338
23 | Jane Roe|JFK|314
24 |
25 | - - - - - - - - - - - - - - -
26 |
27 | + List
28 | + with a [link] (/to/somewhere)
29 | + and [another one]
30 |
31 | [another one]: http://example.com 'Example title'
32 |
33 | Lorem ipsum dolor sit amet, consectetur adipiscing elit.
34 | Curabitur consectetur maximus risus, sed maximus tellus tincidunt et.
35 |
--------------------------------------------------------------------------------
/fixtures/output/tab-double-quotes/toml.toml:
--------------------------------------------------------------------------------
1 | comma = [
2 | 1,
3 | 2,
4 | 3,
5 | ]
6 |
7 | [foo]
8 | b = 1
9 | c = "hello"
10 | a = { answer = 42 }
11 | indent = [
12 | 1,
13 | 2
14 | ]
15 |
16 | [a-table]
17 | apple.type = "fruit"
18 | apple.skin = "thin"
19 | apple.color = "red"
20 |
21 | orange.type = "fruit"
22 | orange.skin = "thick"
23 | orange.color = "orange"
24 |
--------------------------------------------------------------------------------
/fixtures/output/tab-double-quotes/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ESNext",
4 | "module": "ESNext",
5 | "moduleResolution": "Bundler",
6 | "strict": true,
7 | "skipDefaultLibCheck": true,
8 | "skipLibCheck": true
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/fixtures/output/tab-double-quotes/tsx.tsx:
--------------------------------------------------------------------------------
1 | export function Component1() {
2 | return
3 | }
4 |
5 | export function jsx2() {
6 | const props = { a: 1, b: 2 }
7 | return (
8 |
9 |
14 | Inline Text
15 |
16 |
17 | Block Text
18 |
19 |
20 | Mixed
21 |
Foo
22 | Text
23 |
Bar
24 |
25 |
26 | foo
27 | bar
28 | baz
29 |
30 |
31 | )
32 | }
33 |
--------------------------------------------------------------------------------
/fixtures/output/tab-double-quotes/typescript.ts:
--------------------------------------------------------------------------------
1 | // Define a TypeScript interface
2 | interface Person {
3 | name: string
4 | age: number
5 | }
6 |
7 | // Create an array of objects with the defined interface
8 | const people: Person[] = [
9 | { name: "Alice", age: 30 },
10 | { name: "Bob", age: 25 },
11 | { name: "Charlie", age: 35 },
12 | ]
13 |
14 | // eslint-disable-next-line no-console
15 | const log = console.log
16 |
17 | // Use a for...of loop to iterate over the array
18 | for (const person of people) {
19 | log(`Hello, my name is ${person.name} and I am ${person.age} years old.`)
20 | }
21 |
22 | // Define a generic function
23 | function identity(arg: T): T {
24 | return arg
25 | }
26 |
27 | // Use the generic function with type inference
28 | const result = identity(
29 | "TypeScript is awesome",
30 | )
31 | log(result)
32 |
33 | // Use optional properties in an interface
34 | interface Car {
35 | make: string
36 | model?: string
37 | }
38 |
39 | // Create objects using the interface
40 | const car1: Car = { make: "Toyota" }
41 | const car2: Car = {
42 | make: "Ford",
43 | model: "Focus",
44 | }
45 |
46 | // Use union types
47 | type Fruit = "apple" | "banana" | "orange"
48 | const favoriteFruit: Fruit = "apple"
49 |
50 | // Use a type assertion to tell TypeScript about the type
51 | const inputValue: any = "42"
52 | const numericValue = inputValue as number
53 |
54 | // Define a class with access modifiers
55 | class Animal {
56 | private name: string
57 | constructor(name: string) {
58 | this.name = name
59 | }
60 |
61 | protected makeSound(sound: string) {
62 | log(`${this.name} says ${sound}`)
63 | }
64 | }
65 |
66 | // Extend a class
67 | class Dog extends Animal {
68 | constructor(private alias: string) {
69 | super(alias)
70 | }
71 |
72 | bark() {
73 | this.makeSound("Woof!")
74 | }
75 | }
76 |
77 | const dog = new Dog("Buddy")
78 | dog.bark()
79 |
80 | function fn(): string {
81 | return `hello${1}`
82 | }
83 |
84 | log(car1, car2, favoriteFruit, numericValue, fn())
85 |
86 | // Generator
87 | export function* generator1() {
88 | let id = 0
89 | while (id < 100) {
90 | yield id++
91 | }
92 | }
93 | export function* generator2() {
94 | yield* generator1()
95 | }
96 |
--------------------------------------------------------------------------------
/fixtures/output/tab-double-quotes/vue-ts.vue:
--------------------------------------------------------------------------------
1 |
13 |
14 |
15 |
16 |
{{ greeting }}
17 |
20 |
Counter: {{ counter }}
21 |
22 |
23 |
24 |
27 |
28 |
36 |
--------------------------------------------------------------------------------
/fixtures/output/tab-double-quotes/vue.vue:
--------------------------------------------------------------------------------
1 |
16 |
17 |
18 |
19 |
20 | {{ greeting }}
21 |
22 |
25 |
Counter: {{ counter }}
26 |
27 |
28 |
--------------------------------------------------------------------------------
/fixtures/output/ts-override/javascript.js:
--------------------------------------------------------------------------------
1 | // This file is generated by ChatGPT
2 |
3 | // eslint-disable-next-line no-console
4 | const log = console.log
5 |
6 | // Define a class using ES6 class syntax
7 | class Person {
8 | constructor(name, age) {
9 | this.name = name
10 | this.age = age
11 | }
12 |
13 | // Define a method within the class
14 | sayHello() {
15 | log(`Hello, my name is ${this.name} and I am ${this.age} years old.`)
16 | }
17 | }
18 |
19 | // Create an array of objects
20 | const people = [
21 | new Person('Alice', 30),
22 | new Person('Bob', 25),
23 | new Person('Charlie', 35),
24 | ]
25 |
26 | // Use the forEach method to iterate over the array
27 | people.forEach((person) => {
28 | person.sayHello()
29 | })
30 |
31 | // Use a template literal to create a multiline string
32 | const multilineString = `
33 | This is a multiline string
34 | that spans multiple lines.
35 | `
36 |
37 | // Use destructuring assignment to extract values from an object
38 | const { name, age } = people[0]
39 | log(`First person in the array is ${name} and they are ${age} years old.`, multilineString)
40 |
41 | // Use the spread operator to create a new array
42 | const numbers = [1, 2, 3]
43 | const newNumbers = [...numbers, 4, 5]
44 | log(newNumbers)
45 |
46 | // Use a try-catch block for error handling
47 | try {
48 | // Attempt to parse an invalid JSON string
49 | JSON.parse('invalid JSON')
50 | }
51 | catch (error) {
52 | console.error('Error parsing JSON:', error.message)
53 | }
54 |
55 | // Use a ternary conditional operator
56 | const isEven = num => num % 2 === 0
57 | const number = 7
58 | log(`${number} is ${isEven(number) ? 'even' : 'odd'}.`)
59 |
60 | // Use a callback function with setTimeout for asynchronous code
61 | setTimeout(() => {
62 | log('This code runs after a delay of 2 seconds.')
63 | }, 2000)
64 |
65 | let a, b, c, d, foo
66 |
67 | if (a
68 | || b
69 | || c || d
70 | || (d && b)
71 | ) {
72 | foo()
73 | }
74 |
--------------------------------------------------------------------------------
/fixtures/output/ts-override/jsx.jsx:
--------------------------------------------------------------------------------
1 | export function HelloWorld({
2 | greeting = 'hello',
3 | greeted = '"World"',
4 | silent = false,
5 | onMouseOver,
6 | }) {
7 | if (!greeting) {
8 | return null
9 | };
10 |
11 | // TODO: Don't use random in render
12 | const num = Math
13 | .floor (Math.random() * 1e+7)
14 | .toString()
15 | .replace(/\.\d+/g, '')
16 |
17 | return (
18 |
19 | { greeting.slice(0, 1).toUpperCase() + greeting.slice(1).toLowerCase() }
20 | {greeting.endsWith(',')
21 | ? ' '
22 | : ", " }
23 |
24 | { greeted }
25 |
26 | { (silent) ? '.' : '!'}
27 |
28 | )
29 | }
30 |
--------------------------------------------------------------------------------
/fixtures/output/ts-override/markdown.md:
--------------------------------------------------------------------------------
1 | Header
2 | ======
3 |
4 | _Look,_ code blocks are formatted *too!*
5 |
6 | ```js
7 | // This should be handled by ESLint instead of Prettier
8 | function identity(x) {
9 | if (foo) {
10 | console.log('bar')
11 | }
12 | }
13 | ```
14 |
15 | ```css
16 | /* This should be handled by Prettier */
17 | .foo { color:red;}
18 | ```
19 |
20 | Pilot|Airport|Hours
21 | --|:--:|--:
22 | John Doe|SKG|1338
23 | Jane Roe|JFK|314
24 |
25 | - - - - - - - - - - - - - - -
26 |
27 | + List
28 | + with a [link] (/to/somewhere)
29 | + and [another one]
30 |
31 | [another one]: http://example.com 'Example title'
32 |
33 | Lorem ipsum dolor sit amet, consectetur adipiscing elit.
34 | Curabitur consectetur maximus risus, sed maximus tellus tincidunt et.
35 |
--------------------------------------------------------------------------------
/fixtures/output/ts-override/toml.toml:
--------------------------------------------------------------------------------
1 | comma = [
2 | 1,
3 | 2,
4 | 3,
5 | ]
6 |
7 | [foo]
8 | b = 1
9 | c = "hello"
10 | a = { answer = 42 }
11 | indent = [
12 | 1,
13 | 2
14 | ]
15 |
16 | [a-table]
17 | apple.type = "fruit"
18 | apple.skin = "thin"
19 | apple.color = "red"
20 |
21 | orange.type = "fruit"
22 | orange.skin = "thick"
23 | orange.color = "orange"
24 |
--------------------------------------------------------------------------------
/fixtures/output/ts-override/tsx.tsx:
--------------------------------------------------------------------------------
1 | export function Component1() {
2 | return
3 | }
4 |
5 | export function jsx2() {
6 | const props = { a: 1, b: 2 }
7 | return (
8 |
9 |
14 | Inline Text
15 |
16 |
17 | Block Text
18 |
19 |
20 | Mixed
21 |
Foo
22 | Text
23 |
Bar
24 |
25 |
26 | foo
27 | bar
28 | baz
29 |
30 |
31 | )
32 | }
33 |
--------------------------------------------------------------------------------
/fixtures/output/ts-override/typescript.ts:
--------------------------------------------------------------------------------
1 | // Define a TypeScript interface
2 | type Person = {
3 | name: string
4 | age: number
5 | }
6 |
7 | // Create an array of objects with the defined interface
8 | const people: Person[] = [
9 | { name: 'Alice', age: 30 },
10 | { name: 'Bob', age: 25 },
11 | { name: 'Charlie', age: 35 },
12 | ]
13 |
14 | // eslint-disable-next-line no-console
15 | const log = console.log
16 |
17 | // Use a for...of loop to iterate over the array
18 | for (const person of people) {
19 | log(`Hello, my name is ${person.name} and I am ${person.age} years old.`)
20 | }
21 |
22 | // Define a generic function
23 | function identity(arg: T): T {
24 | return arg
25 | }
26 |
27 | // Use the generic function with type inference
28 | const result = identity(
29 | 'TypeScript is awesome',
30 | )
31 | log(result)
32 |
33 | // Use optional properties in an interface
34 | type Car = {
35 | make: string
36 | model?: string
37 | }
38 |
39 | // Create objects using the interface
40 | const car1: Car = { make: 'Toyota' }
41 | const car2: Car = {
42 | make: 'Ford',
43 | model: 'Focus',
44 | }
45 |
46 | // Use union types
47 | type Fruit = 'apple' | 'banana' | 'orange'
48 | const favoriteFruit: Fruit = 'apple'
49 |
50 | // Use a type assertion to tell TypeScript about the type
51 | const inputValue: any = '42'
52 | const numericValue = inputValue as number
53 |
54 | // Define a class with access modifiers
55 | class Animal {
56 | private name: string
57 | constructor(name: string) {
58 | this.name = name
59 | }
60 |
61 | protected makeSound(sound: string) {
62 | log(`${this.name} says ${sound}`)
63 | }
64 | }
65 |
66 | // Extend a class
67 | class Dog extends Animal {
68 | constructor(private alias: string) {
69 | super(alias)
70 | }
71 |
72 | bark() {
73 | this.makeSound('Woof!')
74 | }
75 | }
76 |
77 | const dog = new Dog('Buddy')
78 | dog.bark()
79 |
80 | function fn(): string {
81 | return `hello${1}`
82 | }
83 |
84 | log(car1, car2, favoriteFruit, numericValue, fn())
85 |
86 | // Generator
87 | export function* generator1() {
88 | let id = 0
89 | while (id < 100) {
90 | yield id++
91 | }
92 | }
93 | export function* generator2() {
94 | yield* generator1()
95 | }
96 |
--------------------------------------------------------------------------------
/fixtures/output/ts-override/vue-ts.vue:
--------------------------------------------------------------------------------
1 |
13 |
14 |
15 |
16 |
{{ greeting }}
17 |
20 |
Counter: {{ counter }}
21 |
22 |
23 |
24 |
27 |
28 |
36 |
--------------------------------------------------------------------------------
/fixtures/output/ts-override/vue.vue:
--------------------------------------------------------------------------------
1 |
16 |
17 |
18 |
19 |
20 | {{ greeting }}
21 |
22 |
25 |
Counter: {{ counter }}
26 |
27 |
28 |
--------------------------------------------------------------------------------
/fixtures/output/ts-strict-with-react/javascript.js:
--------------------------------------------------------------------------------
1 | // This file is generated by ChatGPT
2 |
3 | // eslint-disable-next-line no-console
4 | const log = console.log
5 |
6 | // Define a class using ES6 class syntax
7 | class Person {
8 | constructor(name, age) {
9 | this.name = name
10 | this.age = age
11 | }
12 |
13 | // Define a method within the class
14 | sayHello() {
15 | log(`Hello, my name is ${this.name} and I am ${this.age} years old.`)
16 | }
17 | }
18 |
19 | // Create an array of objects
20 | const people = [
21 | new Person('Alice', 30),
22 | new Person('Bob', 25),
23 | new Person('Charlie', 35),
24 | ]
25 |
26 | // Use the forEach method to iterate over the array
27 | people.forEach((person) => {
28 | person.sayHello()
29 | })
30 |
31 | // Use a template literal to create a multiline string
32 | const multilineString = `
33 | This is a multiline string
34 | that spans multiple lines.
35 | `
36 |
37 | // Use destructuring assignment to extract values from an object
38 | const { name, age } = people[0]
39 | log(`First person in the array is ${name} and they are ${age} years old.`, multilineString)
40 |
41 | // Use the spread operator to create a new array
42 | const numbers = [1, 2, 3]
43 | const newNumbers = [...numbers, 4, 5]
44 | log(newNumbers)
45 |
46 | // Use a try-catch block for error handling
47 | try {
48 | // Attempt to parse an invalid JSON string
49 | JSON.parse('invalid JSON')
50 | }
51 | catch (error) {
52 | console.error('Error parsing JSON:', error.message)
53 | }
54 |
55 | // Use a ternary conditional operator
56 | const isEven = num => num % 2 === 0
57 | const number = 7
58 | log(`${number} is ${isEven(number) ? 'even' : 'odd'}.`)
59 |
60 | // Use a callback function with setTimeout for asynchronous code
61 | setTimeout(() => {
62 | log('This code runs after a delay of 2 seconds.')
63 | }, 2000)
64 |
65 | let a, b, c, d, foo
66 |
67 | if (a
68 | || b
69 | || c || d
70 | || (d && b)
71 | ) {
72 | foo()
73 | }
74 |
--------------------------------------------------------------------------------
/fixtures/output/ts-strict-with-react/jsx.jsx:
--------------------------------------------------------------------------------
1 | export function HelloWorld({
2 | greeting = 'hello',
3 | greeted = '"World"',
4 | silent = false,
5 | onMouseOver,
6 | }) {
7 | if (!greeting) {
8 | return null
9 | };
10 |
11 | // TODO: Don't use random in render
12 | const num = Math
13 | .floor (Math.random() * 1e+7)
14 | .toString()
15 | .replace(/\.\d+/g, '')
16 |
17 | return (
18 |
19 | { greeting.slice(0, 1).toUpperCase() + greeting.slice(1).toLowerCase() }
20 | {greeting.endsWith(',')
21 | ? ' '
22 | : ", " }
23 |
24 | { greeted }
25 |
26 | { (silent) ? '.' : '!'}
27 |
28 | )
29 | }
30 |
--------------------------------------------------------------------------------
/fixtures/output/ts-strict-with-react/markdown.md:
--------------------------------------------------------------------------------
1 | Header
2 | ======
3 |
4 | _Look,_ code blocks are formatted *too!*
5 |
6 | ```js
7 | // This should be handled by ESLint instead of Prettier
8 | function identity(x) {
9 | if (foo) {
10 | console.log('bar')
11 | }
12 | }
13 | ```
14 |
15 | ```css
16 | /* This should be handled by Prettier */
17 | .foo { color:red;}
18 | ```
19 |
20 | Pilot|Airport|Hours
21 | --|:--:|--:
22 | John Doe|SKG|1338
23 | Jane Roe|JFK|314
24 |
25 | - - - - - - - - - - - - - - -
26 |
27 | + List
28 | + with a [link] (/to/somewhere)
29 | + and [another one]
30 |
31 | [another one]: http://example.com 'Example title'
32 |
33 | Lorem ipsum dolor sit amet, consectetur adipiscing elit.
34 | Curabitur consectetur maximus risus, sed maximus tellus tincidunt et.
35 |
--------------------------------------------------------------------------------
/fixtures/output/ts-strict-with-react/toml.toml:
--------------------------------------------------------------------------------
1 | comma = [
2 | 1,
3 | 2,
4 | 3,
5 | ]
6 |
7 | [foo]
8 | b = 1
9 | c = "hello"
10 | a = { answer = 42 }
11 | indent = [
12 | 1,
13 | 2
14 | ]
15 |
16 | [a-table]
17 | apple.type = "fruit"
18 | apple.skin = "thin"
19 | apple.color = "red"
20 |
21 | orange.type = "fruit"
22 | orange.skin = "thick"
23 | orange.color = "orange"
24 |
--------------------------------------------------------------------------------
/fixtures/output/ts-strict-with-react/tsx.tsx:
--------------------------------------------------------------------------------
1 | export function Component1() {
2 | return
3 | }
4 |
5 | export function jsx2() {
6 | const props = { a: 1, b: 2 }
7 | return (
8 |
9 |
14 | Inline Text
15 |
16 |
17 | Block Text
18 |
19 |
20 | Mixed
21 |
Foo
22 | Text
23 |
Bar
24 |
25 |
26 | foo
27 | bar
28 | baz
29 |
30 |
31 | )
32 | }
33 |
--------------------------------------------------------------------------------
/fixtures/output/ts-strict-with-react/typescript.ts:
--------------------------------------------------------------------------------
1 | // Define a TypeScript interface
2 | interface Person {
3 | name: string
4 | age: number
5 | }
6 |
7 | // Create an array of objects with the defined interface
8 | const people: Person[] = [
9 | { name: 'Alice', age: 30 },
10 | { name: 'Bob', age: 25 },
11 | { name: 'Charlie', age: 35 },
12 | ]
13 |
14 | // eslint-disable-next-line no-console
15 | const log = console.log
16 |
17 | // Use a for...of loop to iterate over the array
18 | for (const person of people) {
19 | log(`Hello, my name is ${person.name} and I am ${person.age} years old.`)
20 | }
21 |
22 | // Define a generic function
23 | function identity(arg: T): T {
24 | return arg
25 | }
26 |
27 | // Use the generic function with type inference
28 | const result = identity(
29 | 'TypeScript is awesome',
30 | )
31 | log(result)
32 |
33 | // Use optional properties in an interface
34 | interface Car {
35 | make: string
36 | model?: string
37 | }
38 |
39 | // Create objects using the interface
40 | const car1: Car = { make: 'Toyota' }
41 | const car2: Car = {
42 | make: 'Ford',
43 | model: 'Focus',
44 | }
45 |
46 | // Use union types
47 | type Fruit = 'apple' | 'banana' | 'orange'
48 | const favoriteFruit: Fruit = 'apple'
49 |
50 | // Use a type assertion to tell TypeScript about the type
51 | const inputValue: any = '42'
52 | const numericValue = inputValue as number
53 |
54 | // Define a class with access modifiers
55 | class Animal {
56 | private name: string
57 | constructor(name: string) {
58 | this.name = name
59 | }
60 |
61 | protected makeSound(sound: string) {
62 | log(`${this.name} says ${sound}`)
63 | }
64 | }
65 |
66 | // Extend a class
67 | class Dog extends Animal {
68 | constructor(private alias: string) {
69 | super(alias)
70 | }
71 |
72 | bark() {
73 | this.makeSound('Woof!')
74 | }
75 | }
76 |
77 | const dog = new Dog('Buddy')
78 | dog.bark()
79 |
80 | function fn(): string {
81 | return `hello${1}`
82 | }
83 |
84 | log(car1, car2, favoriteFruit, numericValue, fn())
85 |
86 | // Generator
87 | export function* generator1() {
88 | let id = 0
89 | while (id < 100) {
90 | yield id++
91 | }
92 | }
93 | export function* generator2() {
94 | yield* generator1()
95 | }
96 |
--------------------------------------------------------------------------------
/fixtures/output/ts-strict-with-react/vue-ts.vue:
--------------------------------------------------------------------------------
1 |
13 |
14 |
15 |
16 |
{{ greeting }}
17 |
20 |
Counter: {{ counter }}
21 |
22 |
23 |
24 |
27 |
28 |
36 |
--------------------------------------------------------------------------------
/fixtures/output/ts-strict-with-react/vue.vue:
--------------------------------------------------------------------------------
1 |
16 |
17 |
18 |
19 |
20 | {{ greeting }}
21 |
22 |
25 |
Counter: {{ counter }}
26 |
27 |
28 |
--------------------------------------------------------------------------------
/fixtures/output/ts-strict/javascript.js:
--------------------------------------------------------------------------------
1 | // This file is generated by ChatGPT
2 |
3 | // eslint-disable-next-line no-console
4 | const log = console.log
5 |
6 | // Define a class using ES6 class syntax
7 | class Person {
8 | constructor(name, age) {
9 | this.name = name
10 | this.age = age
11 | }
12 |
13 | // Define a method within the class
14 | sayHello() {
15 | log(`Hello, my name is ${this.name} and I am ${this.age} years old.`)
16 | }
17 | }
18 |
19 | // Create an array of objects
20 | const people = [
21 | new Person('Alice', 30),
22 | new Person('Bob', 25),
23 | new Person('Charlie', 35),
24 | ]
25 |
26 | // Use the forEach method to iterate over the array
27 | people.forEach((person) => {
28 | person.sayHello()
29 | })
30 |
31 | // Use a template literal to create a multiline string
32 | const multilineString = `
33 | This is a multiline string
34 | that spans multiple lines.
35 | `
36 |
37 | // Use destructuring assignment to extract values from an object
38 | const { name, age } = people[0]
39 | log(`First person in the array is ${name} and they are ${age} years old.`, multilineString)
40 |
41 | // Use the spread operator to create a new array
42 | const numbers = [1, 2, 3]
43 | const newNumbers = [...numbers, 4, 5]
44 | log(newNumbers)
45 |
46 | // Use a try-catch block for error handling
47 | try {
48 | // Attempt to parse an invalid JSON string
49 | JSON.parse('invalid JSON')
50 | }
51 | catch (error) {
52 | console.error('Error parsing JSON:', error.message)
53 | }
54 |
55 | // Use a ternary conditional operator
56 | const isEven = num => num % 2 === 0
57 | const number = 7
58 | log(`${number} is ${isEven(number) ? 'even' : 'odd'}.`)
59 |
60 | // Use a callback function with setTimeout for asynchronous code
61 | setTimeout(() => {
62 | log('This code runs after a delay of 2 seconds.')
63 | }, 2000)
64 |
65 | let a, b, c, d, foo
66 |
67 | if (a
68 | || b
69 | || c || d
70 | || (d && b)
71 | ) {
72 | foo()
73 | }
74 |
--------------------------------------------------------------------------------
/fixtures/output/ts-strict/jsx.jsx:
--------------------------------------------------------------------------------
1 | export function HelloWorld({
2 | greeting = 'hello',
3 | greeted = '"World"',
4 | silent = false,
5 | onMouseOver,
6 | }) {
7 | if (!greeting) {
8 | return null
9 | };
10 |
11 | // TODO: Don't use random in render
12 | const num = Math
13 | .floor (Math.random() * 1e+7)
14 | .toString()
15 | .replace(/\.\d+/g, '')
16 |
17 | return (
18 |
19 | { greeting.slice(0, 1).toUpperCase() + greeting.slice(1).toLowerCase() }
20 | {greeting.endsWith(',')
21 | ? ' '
22 | : ", " }
23 |
24 | { greeted }
25 |
26 | { (silent) ? '.' : '!'}
27 |
28 | )
29 | }
30 |
--------------------------------------------------------------------------------
/fixtures/output/ts-strict/markdown.md:
--------------------------------------------------------------------------------
1 | Header
2 | ======
3 |
4 | _Look,_ code blocks are formatted *too!*
5 |
6 | ```js
7 | // This should be handled by ESLint instead of Prettier
8 | function identity(x) {
9 | if (foo) {
10 | console.log('bar')
11 | }
12 | }
13 | ```
14 |
15 | ```css
16 | /* This should be handled by Prettier */
17 | .foo { color:red;}
18 | ```
19 |
20 | Pilot|Airport|Hours
21 | --|:--:|--:
22 | John Doe|SKG|1338
23 | Jane Roe|JFK|314
24 |
25 | - - - - - - - - - - - - - - -
26 |
27 | + List
28 | + with a [link] (/to/somewhere)
29 | + and [another one]
30 |
31 | [another one]: http://example.com 'Example title'
32 |
33 | Lorem ipsum dolor sit amet, consectetur adipiscing elit.
34 | Curabitur consectetur maximus risus, sed maximus tellus tincidunt et.
35 |
--------------------------------------------------------------------------------
/fixtures/output/ts-strict/toml.toml:
--------------------------------------------------------------------------------
1 | comma = [
2 | 1,
3 | 2,
4 | 3,
5 | ]
6 |
7 | [foo]
8 | b = 1
9 | c = "hello"
10 | a = { answer = 42 }
11 | indent = [
12 | 1,
13 | 2
14 | ]
15 |
16 | [a-table]
17 | apple.type = "fruit"
18 | apple.skin = "thin"
19 | apple.color = "red"
20 |
21 | orange.type = "fruit"
22 | orange.skin = "thick"
23 | orange.color = "orange"
24 |
--------------------------------------------------------------------------------
/fixtures/output/ts-strict/tsx.tsx:
--------------------------------------------------------------------------------
1 | export function Component1() {
2 | return
3 | }
4 |
5 | export function jsx2() {
6 | const props = { a: 1, b: 2 }
7 | return (
8 |
9 |
14 | Inline Text
15 |
16 |
17 | Block Text
18 |
19 |
20 | Mixed
21 |
Foo
22 | Text
23 |
Bar
24 |
25 |
26 | foo
27 | bar
28 | baz
29 |
30 |
31 | )
32 | }
33 |
--------------------------------------------------------------------------------
/fixtures/output/ts-strict/typescript.ts:
--------------------------------------------------------------------------------
1 | // Define a TypeScript interface
2 | interface Person {
3 | name: string
4 | age: number
5 | }
6 |
7 | // Create an array of objects with the defined interface
8 | const people: Person[] = [
9 | { name: 'Alice', age: 30 },
10 | { name: 'Bob', age: 25 },
11 | { name: 'Charlie', age: 35 },
12 | ]
13 |
14 | // eslint-disable-next-line no-console
15 | const log = console.log
16 |
17 | // Use a for...of loop to iterate over the array
18 | for (const person of people) {
19 | log(`Hello, my name is ${person.name} and I am ${person.age} years old.`)
20 | }
21 |
22 | // Define a generic function
23 | function identity(arg: T): T {
24 | return arg
25 | }
26 |
27 | // Use the generic function with type inference
28 | const result = identity(
29 | 'TypeScript is awesome',
30 | )
31 | log(result)
32 |
33 | // Use optional properties in an interface
34 | interface Car {
35 | make: string
36 | model?: string
37 | }
38 |
39 | // Create objects using the interface
40 | const car1: Car = { make: 'Toyota' }
41 | const car2: Car = {
42 | make: 'Ford',
43 | model: 'Focus',
44 | }
45 |
46 | // Use union types
47 | type Fruit = 'apple' | 'banana' | 'orange'
48 | const favoriteFruit: Fruit = 'apple'
49 |
50 | // Use a type assertion to tell TypeScript about the type
51 | const inputValue: any = '42'
52 | const numericValue = inputValue as number
53 |
54 | // Define a class with access modifiers
55 | class Animal {
56 | private name: string
57 | constructor(name: string) {
58 | this.name = name
59 | }
60 |
61 | protected makeSound(sound: string) {
62 | log(`${this.name} says ${sound}`)
63 | }
64 | }
65 |
66 | // Extend a class
67 | class Dog extends Animal {
68 | constructor(private alias: string) {
69 | super(alias)
70 | }
71 |
72 | bark() {
73 | this.makeSound('Woof!')
74 | }
75 | }
76 |
77 | const dog = new Dog('Buddy')
78 | dog.bark()
79 |
80 | function fn(): string {
81 | return `hello${1}`
82 | }
83 |
84 | log(car1, car2, favoriteFruit, numericValue, fn())
85 |
86 | // Generator
87 | export function* generator1() {
88 | let id = 0
89 | while (id < 100) {
90 | yield id++
91 | }
92 | }
93 | export function* generator2() {
94 | yield* generator1()
95 | }
96 |
--------------------------------------------------------------------------------
/fixtures/output/ts-strict/vue-ts.vue:
--------------------------------------------------------------------------------
1 |
13 |
14 |
15 |
16 |
{{ greeting }}
17 |
20 |
Counter: {{ counter }}
21 |
22 |
23 |
24 |
27 |
28 |
36 |
--------------------------------------------------------------------------------
/fixtures/output/ts-strict/vue.vue:
--------------------------------------------------------------------------------
1 |
16 |
17 |
18 |
19 |
20 | {{ greeting }}
21 |
22 |
25 |
Counter: {{ counter }}
26 |
27 |
28 |
--------------------------------------------------------------------------------
/fixtures/output/with-formatters/astro.astro:
--------------------------------------------------------------------------------
1 | ---
2 | const isJsx = true
3 | const content = 'hi!'
4 | ---
5 |
6 |
7 | {content}
8 |
9 | {isJsx &&
{content}
}
10 |
11 |
12 |
13 |
19 |
--------------------------------------------------------------------------------
/fixtures/output/with-formatters/css.css:
--------------------------------------------------------------------------------
1 | @media (max-width: 480px) {
2 | .bd-examples {
3 | margin-right: -0.75rem;
4 | margin-left: -0.75rem;
5 | }
6 |
7 | .bd-examples > [class^='col-'] {
8 | padding-right: 0.75rem;
9 | padding-left: 0.75rem;
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/fixtures/output/with-formatters/html.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | My tITlE
6 |
7 |
8 |
9 |
10 | Hello world!
11 | This is HTML5 Boilerplate.
12 |
13 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/fixtures/output/with-formatters/javascript.js:
--------------------------------------------------------------------------------
1 | // This file is generated by ChatGPT
2 |
3 | // eslint-disable-next-line no-console
4 | const log = console.log
5 |
6 | // Define a class using ES6 class syntax
7 | class Person {
8 | constructor(name, age) {
9 | this.name = name
10 | this.age = age
11 | }
12 |
13 | // Define a method within the class
14 | sayHello() {
15 | log(`Hello, my name is ${this.name} and I am ${this.age} years old.`)
16 | }
17 | }
18 |
19 | // Create an array of objects
20 | const people = [
21 | new Person('Alice', 30),
22 | new Person('Bob', 25),
23 | new Person('Charlie', 35),
24 | ]
25 |
26 | // Use the forEach method to iterate over the array
27 | people.forEach((person) => {
28 | person.sayHello()
29 | })
30 |
31 | // Use a template literal to create a multiline string
32 | const multilineString = `
33 | This is a multiline string
34 | that spans multiple lines.
35 | `
36 |
37 | // Use destructuring assignment to extract values from an object
38 | const { name, age } = people[0]
39 | log(`First person in the array is ${name} and they are ${age} years old.`, multilineString)
40 |
41 | // Use the spread operator to create a new array
42 | const numbers = [1, 2, 3]
43 | const newNumbers = [...numbers, 4, 5]
44 | log(newNumbers)
45 |
46 | // Use a try-catch block for error handling
47 | try {
48 | // Attempt to parse an invalid JSON string
49 | JSON.parse('invalid JSON')
50 | }
51 | catch (error) {
52 | console.error('Error parsing JSON:', error.message)
53 | }
54 |
55 | // Use a ternary conditional operator
56 | const isEven = num => num % 2 === 0
57 | const number = 7
58 | log(`${number} is ${isEven(number) ? 'even' : 'odd'}.`)
59 |
60 | // Use a callback function with setTimeout for asynchronous code
61 | setTimeout(() => {
62 | log('This code runs after a delay of 2 seconds.')
63 | }, 2000)
64 |
65 | let a, b, c, d, foo
66 |
67 | if (a
68 | || b
69 | || c || d
70 | || (d && b)
71 | ) {
72 | foo()
73 | }
74 |
--------------------------------------------------------------------------------
/fixtures/output/with-formatters/jsx.jsx:
--------------------------------------------------------------------------------
1 | export function HelloWorld({
2 | greeting = 'hello',
3 | greeted = '"World"',
4 | silent = false,
5 | onMouseOver,
6 | }) {
7 | if (!greeting) {
8 | return null
9 | };
10 |
11 | // TODO: Don't use random in render
12 | const num = Math
13 | .floor (Math.random() * 1e+7)
14 | .toString()
15 | .replace(/\.\d+/g, '')
16 |
17 | return (
18 |
19 | { greeting.slice(0, 1).toUpperCase() + greeting.slice(1).toLowerCase() }
20 | {greeting.endsWith(',')
21 | ? ' '
22 | : ", " }
23 |
24 | { greeted }
25 |
26 | { (silent) ? '.' : '!'}
27 |
28 | )
29 | }
30 |
--------------------------------------------------------------------------------
/fixtures/output/with-formatters/markdown.md:
--------------------------------------------------------------------------------
1 | # Header
2 |
3 | _Look,_ code blocks are formatted _too!_
4 |
5 | ```js
6 | // This should be handled by ESLint instead of Prettier
7 | function identity(x) {
8 | if (foo) {
9 | console.log('bar')
10 | }
11 | }
12 | ```
13 |
14 | ```css
15 | /* This should be handled by Prettier */
16 | .foo {
17 | color: red;
18 | }
19 | ```
20 |
21 | | Pilot | Airport | Hours |
22 | | -------- | :-----: | ----: |
23 | | John Doe | SKG | 1338 |
24 | | Jane Roe | JFK | 314 |
25 |
26 | ---
27 |
28 | - List
29 | - with a [link] (/to/somewhere)
30 | - and [another one]
31 |
32 | [another one]: http://example.com 'Example title'
33 |
34 | Lorem ipsum dolor sit amet, consectetur adipiscing elit.
35 | Curabitur consectetur maximus risus, sed maximus tellus tincidunt et.
36 |
--------------------------------------------------------------------------------
/fixtures/output/with-formatters/svg.svg:
--------------------------------------------------------------------------------
1 |
18 |
--------------------------------------------------------------------------------
/fixtures/output/with-formatters/toml.toml:
--------------------------------------------------------------------------------
1 | comma = [
2 | 1,
3 | 2,
4 | 3,
5 | ]
6 |
7 | [foo]
8 | b = 1
9 | c = "hello"
10 | a = { answer = 42 }
11 | indent = [
12 | 1,
13 | 2
14 | ]
15 |
16 | [a-table]
17 | apple.type = "fruit"
18 | apple.skin = "thin"
19 | apple.color = "red"
20 |
21 | orange.type = "fruit"
22 | orange.skin = "thick"
23 | orange.color = "orange"
24 |
--------------------------------------------------------------------------------
/fixtures/output/with-formatters/tsx.tsx:
--------------------------------------------------------------------------------
1 | export function Component1() {
2 | return
3 | }
4 |
5 | export function jsx2() {
6 | const props = { a: 1, b: 2 }
7 | return (
8 |
9 |
14 | Inline Text
15 |
16 |
17 | Block Text
18 |
19 |
20 | Mixed
21 |
Foo
22 | Text
23 |
Bar
24 |
25 |
26 | foo
27 | bar
28 | baz
29 |
30 |
31 | )
32 | }
33 |
--------------------------------------------------------------------------------
/fixtures/output/with-formatters/typescript.ts:
--------------------------------------------------------------------------------
1 | // Define a TypeScript interface
2 | interface Person {
3 | name: string
4 | age: number
5 | }
6 |
7 | // Create an array of objects with the defined interface
8 | const people: Person[] = [
9 | { name: 'Alice', age: 30 },
10 | { name: 'Bob', age: 25 },
11 | { name: 'Charlie', age: 35 },
12 | ]
13 |
14 | // eslint-disable-next-line no-console
15 | const log = console.log
16 |
17 | // Use a for...of loop to iterate over the array
18 | for (const person of people) {
19 | log(`Hello, my name is ${person.name} and I am ${person.age} years old.`)
20 | }
21 |
22 | // Define a generic function
23 | function identity(arg: T): T {
24 | return arg
25 | }
26 |
27 | // Use the generic function with type inference
28 | const result = identity(
29 | 'TypeScript is awesome',
30 | )
31 | log(result)
32 |
33 | // Use optional properties in an interface
34 | interface Car {
35 | make: string
36 | model?: string
37 | }
38 |
39 | // Create objects using the interface
40 | const car1: Car = { make: 'Toyota' }
41 | const car2: Car = {
42 | make: 'Ford',
43 | model: 'Focus',
44 | }
45 |
46 | // Use union types
47 | type Fruit = 'apple' | 'banana' | 'orange'
48 | const favoriteFruit: Fruit = 'apple'
49 |
50 | // Use a type assertion to tell TypeScript about the type
51 | const inputValue: any = '42'
52 | const numericValue = inputValue as number
53 |
54 | // Define a class with access modifiers
55 | class Animal {
56 | private name: string
57 | constructor(name: string) {
58 | this.name = name
59 | }
60 |
61 | protected makeSound(sound: string) {
62 | log(`${this.name} says ${sound}`)
63 | }
64 | }
65 |
66 | // Extend a class
67 | class Dog extends Animal {
68 | constructor(private alias: string) {
69 | super(alias)
70 | }
71 |
72 | bark() {
73 | this.makeSound('Woof!')
74 | }
75 | }
76 |
77 | const dog = new Dog('Buddy')
78 | dog.bark()
79 |
80 | function fn(): string {
81 | return `hello${1}`
82 | }
83 |
84 | log(car1, car2, favoriteFruit, numericValue, fn())
85 |
86 | // Generator
87 | export function* generator1() {
88 | let id = 0
89 | while (id < 100) {
90 | yield id++
91 | }
92 | }
93 | export function* generator2() {
94 | yield* generator1()
95 | }
96 |
--------------------------------------------------------------------------------
/fixtures/output/with-formatters/vue-ts.vue:
--------------------------------------------------------------------------------
1 |
13 |
14 |
15 |
16 |
{{ greeting }}
17 |
20 |
Counter: {{ counter }}
21 |
22 |
23 |
24 |
29 |
30 |
39 |
--------------------------------------------------------------------------------
/fixtures/output/with-formatters/vue.vue:
--------------------------------------------------------------------------------
1 |
16 |
17 |
18 |
19 |
20 | {{ greeting }}
21 |
22 |
25 |
Counter: {{ counter }}
26 |
27 |
28 |
--------------------------------------------------------------------------------
/fixtures/output/with-formatters/xml.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Effective Java
4 | 45.00
5 |
6 |
7 | Bluetooth Speaker
8 | 120.00
9 |
10 |
11 | Clean Code
12 | 33.50
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/netlify.toml:
--------------------------------------------------------------------------------
1 | [build]
2 | publish = ".eslint-config-inspector"
3 | command = "pnpm run build:inspector"
4 |
5 | [build.environment]
6 | NODE_VERSION = "22"
7 |
8 | [[redirects]]
9 | from = "/*"
10 | to = "/index.html"
11 | status = 200
12 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@antfu/eslint-config",
3 | "type": "module",
4 | "version": "4.13.2",
5 | "packageManager": "pnpm@10.11.0",
6 | "description": "Anthony's ESLint config",
7 | "author": "Anthony Fu (https://github.com/antfu/)",
8 | "license": "MIT",
9 | "funding": "https://github.com/sponsors/antfu",
10 | "homepage": "https://github.com/antfu/eslint-config",
11 | "repository": {
12 | "type": "git",
13 | "url": "git+https://github.com/antfu/eslint-config.git"
14 | },
15 | "bugs": {
16 | "url": "https://github.com/antfu/eslint-config/issues"
17 | },
18 | "keywords": [
19 | "eslint-config"
20 | ],
21 | "exports": {
22 | ".": "./dist/index.js"
23 | },
24 | "main": "./dist/index.js",
25 | "types": "./dist/index.d.ts",
26 | "bin": "./bin/index.js",
27 | "files": [
28 | "bin",
29 | "dist"
30 | ],
31 | "scripts": {
32 | "build": "nr gen && tsdown --clean --dts",
33 | "stub": "tsdown",
34 | "dev": "npx @eslint/config-inspector --config eslint.config.ts",
35 | "build:inspector": "pnpm build && npx @eslint/config-inspector build",
36 | "watch": "tsdown --watch",
37 | "lint": "eslint",
38 | "gen": "tsx scripts/typegen.ts && tsx scripts/versiongen.ts",
39 | "prepack": "nr build",
40 | "release": "bumpp && pnpm publish",
41 | "test": "vitest",
42 | "typecheck": "tsc --noEmit",
43 | "prepare": "simple-git-hooks"
44 | },
45 | "peerDependencies": {
46 | "@eslint-react/eslint-plugin": "^1.38.4",
47 | "@prettier/plugin-xml": "^3.4.1",
48 | "@unocss/eslint-plugin": ">=0.50.0",
49 | "astro-eslint-parser": "^1.0.2",
50 | "eslint": "^9.10.0",
51 | "eslint-plugin-astro": "^1.2.0",
52 | "eslint-plugin-format": ">=0.1.0",
53 | "eslint-plugin-react-hooks": "^5.2.0",
54 | "eslint-plugin-react-refresh": "^0.4.19",
55 | "eslint-plugin-solid": "^0.14.3",
56 | "eslint-plugin-svelte": ">=2.35.1",
57 | "eslint-plugin-vuejs-accessibility": "^2.4.1",
58 | "prettier-plugin-astro": "^0.14.0",
59 | "prettier-plugin-slidev": "^1.0.5",
60 | "svelte-eslint-parser": ">=0.37.0"
61 | },
62 | "peerDependenciesMeta": {
63 | "@eslint-react/eslint-plugin": {
64 | "optional": true
65 | },
66 | "@prettier/plugin-xml": {
67 | "optional": true
68 | },
69 | "@unocss/eslint-plugin": {
70 | "optional": true
71 | },
72 | "astro-eslint-parser": {
73 | "optional": true
74 | },
75 | "eslint-plugin-astro": {
76 | "optional": true
77 | },
78 | "eslint-plugin-format": {
79 | "optional": true
80 | },
81 | "eslint-plugin-react-hooks": {
82 | "optional": true
83 | },
84 | "eslint-plugin-react-refresh": {
85 | "optional": true
86 | },
87 | "eslint-plugin-solid": {
88 | "optional": true
89 | },
90 | "eslint-plugin-svelte": {
91 | "optional": true
92 | },
93 | "eslint-plugin-vuejs-accessibility": {
94 | "optional": true
95 | },
96 | "prettier-plugin-astro": {
97 | "optional": true
98 | },
99 | "prettier-plugin-slidev": {
100 | "optional": true
101 | },
102 | "svelte-eslint-parser": {
103 | "optional": true
104 | }
105 | },
106 | "dependencies": {
107 | "@antfu/install-pkg": "catalog:prod",
108 | "@clack/prompts": "catalog:prod",
109 | "@eslint-community/eslint-plugin-eslint-comments": "catalog:prod",
110 | "@eslint/markdown": "catalog:prod",
111 | "@stylistic/eslint-plugin": "catalog:prod",
112 | "@typescript-eslint/eslint-plugin": "catalog:prod",
113 | "@typescript-eslint/parser": "catalog:prod",
114 | "@vitest/eslint-plugin": "catalog:prod",
115 | "ansis": "catalog:prod",
116 | "cac": "catalog:prod",
117 | "eslint-config-flat-gitignore": "catalog:prod",
118 | "eslint-flat-config-utils": "catalog:prod",
119 | "eslint-merge-processors": "catalog:prod",
120 | "eslint-plugin-antfu": "catalog:prod",
121 | "eslint-plugin-command": "catalog:prod",
122 | "eslint-plugin-import-x": "catalog:prod",
123 | "eslint-plugin-jsdoc": "catalog:prod",
124 | "eslint-plugin-jsonc": "catalog:prod",
125 | "eslint-plugin-n": "catalog:prod",
126 | "eslint-plugin-no-only-tests": "catalog:prod",
127 | "eslint-plugin-perfectionist": "catalog:prod",
128 | "eslint-plugin-pnpm": "catalog:prod",
129 | "eslint-plugin-regexp": "catalog:prod",
130 | "eslint-plugin-toml": "catalog:prod",
131 | "eslint-plugin-unicorn": "catalog:prod",
132 | "eslint-plugin-unused-imports": "catalog:prod",
133 | "eslint-plugin-vue": "catalog:prod",
134 | "eslint-plugin-yml": "catalog:prod",
135 | "eslint-processor-vue-blocks": "catalog:prod",
136 | "globals": "catalog:prod",
137 | "jsonc-eslint-parser": "catalog:prod",
138 | "local-pkg": "catalog:prod",
139 | "parse-gitignore": "catalog:prod",
140 | "toml-eslint-parser": "catalog:prod",
141 | "vue-eslint-parser": "catalog:prod",
142 | "yaml-eslint-parser": "catalog:prod"
143 | },
144 | "devDependencies": {
145 | "@antfu/eslint-config": "workspace:*",
146 | "@antfu/ni": "catalog:dev",
147 | "@eslint-react/eslint-plugin": "catalog:peer",
148 | "@eslint/config-inspector": "catalog:dev",
149 | "@prettier/plugin-xml": "catalog:peer",
150 | "@stylistic/eslint-plugin-migrate": "catalog:dev",
151 | "@types/node": "catalog:dev",
152 | "@unocss/eslint-plugin": "catalog:peer",
153 | "astro-eslint-parser": "catalog:peer",
154 | "bumpp": "catalog:dev",
155 | "eslint": "catalog:peer",
156 | "eslint-plugin-astro": "catalog:peer",
157 | "eslint-plugin-format": "catalog:peer",
158 | "eslint-plugin-react-hooks": "catalog:peer",
159 | "eslint-plugin-react-refresh": "catalog:peer",
160 | "eslint-plugin-solid": "catalog:peer",
161 | "eslint-plugin-svelte": "catalog:peer",
162 | "eslint-plugin-vuejs-accessibility": "catalog:peer",
163 | "eslint-typegen": "catalog:dev",
164 | "execa": "catalog:dev",
165 | "jiti": "catalog:dev",
166 | "lint-staged": "catalog:dev",
167 | "pnpm-workspace-yaml": "catalog:dev",
168 | "prettier-plugin-astro": "catalog:peer",
169 | "prettier-plugin-slidev": "catalog:peer",
170 | "simple-git-hooks": "catalog:dev",
171 | "svelte": "catalog:peer",
172 | "svelte-eslint-parser": "catalog:peer",
173 | "tinyglobby": "catalog:dev",
174 | "tsdown": "catalog:dev",
175 | "tsx": "catalog:dev",
176 | "typescript": "catalog:dev",
177 | "vitest": "catalog:dev",
178 | "vue": "catalog:peer"
179 | },
180 | "simple-git-hooks": {
181 | "pre-commit": "npx lint-staged"
182 | },
183 | "lint-staged": {
184 | "*": "eslint --fix"
185 | }
186 | }
187 |
--------------------------------------------------------------------------------
/pnpm-workspace.yaml:
--------------------------------------------------------------------------------
1 | packages:
2 | - fixtures/*
3 | overrides:
4 | '@eslint-community/eslint-utils': ^4.7.0
5 | '@typescript-eslint/utils': ^8.32.1
6 | eslint: ^9.27.0
7 | tsx: ^4.19.4
8 | catalogs:
9 | dev:
10 | '@antfu/ni': ^24.4.0
11 | '@eslint/config-inspector': ^1.0.2
12 | '@stylistic/eslint-plugin-migrate': ^4.2.0
13 | '@types/node': ^22.15.21
14 | bumpp: ^10.1.1
15 | eslint-typegen: ^2.2.0
16 | execa: ^9.5.3
17 | jiti: ^2.4.2
18 | lint-staged: ^16.0.0
19 | pnpm-workspace-yaml: ^0.3.1
20 | simple-git-hooks: ^2.13.0
21 | tinyglobby: ^0.2.13
22 | tsdown: ^0.11.13
23 | tsx: ^4.19.4
24 | typescript: ^5.8.3
25 | vitest: ^3.1.4
26 | peer:
27 | '@eslint-react/eslint-plugin': ^1.49.0
28 | '@prettier/plugin-xml': ^3.4.1
29 | '@unocss/eslint-plugin': ^66.1.2
30 | astro-eslint-parser: ^1.2.2
31 | eslint: ^9.27.0
32 | eslint-plugin-astro: ^1.3.1
33 | eslint-plugin-format: ^1.0.1
34 | eslint-plugin-react-hooks: ^5.2.0
35 | eslint-plugin-react-refresh: ^0.4.20
36 | eslint-plugin-solid: ^0.14.5
37 | eslint-plugin-svelte: ^3.8.2
38 | eslint-plugin-vuejs-accessibility: ^2.4.1
39 | prettier-plugin-astro: ^0.14.1
40 | prettier-plugin-slidev: ^1.0.5
41 | svelte: ^5.32.1
42 | svelte-eslint-parser: ^1.2.0
43 | vue: ^3.5.14
44 | prod:
45 | '@antfu/install-pkg': ^1.1.0
46 | '@clack/prompts': ^0.10.1
47 | '@eslint-community/eslint-plugin-eslint-comments': ^4.5.0
48 | '@eslint/markdown': ^6.4.0
49 | '@stylistic/eslint-plugin': ^4.2.0
50 | '@typescript-eslint/eslint-plugin': ^8.32.1
51 | '@typescript-eslint/parser': ^8.32.1
52 | '@vitest/eslint-plugin': ^1.2.0
53 | ansis: ^4.0.0
54 | cac: ^6.7.14
55 | eslint-config-flat-gitignore: ^2.1.0
56 | eslint-flat-config-utils: ^2.1.0
57 | eslint-merge-processors: ^2.0.0
58 | eslint-plugin-antfu: ^3.1.1
59 | eslint-plugin-command: ^3.2.0
60 | eslint-plugin-import-x: ^4.12.2
61 | eslint-plugin-jsdoc: ^50.6.17
62 | eslint-plugin-jsonc: ^2.20.1
63 | eslint-plugin-n: ^17.18.0
64 | eslint-plugin-no-only-tests: ^3.3.0
65 | eslint-plugin-perfectionist: ^4.13.0
66 | eslint-plugin-pnpm: ^0.3.1
67 | eslint-plugin-regexp: ^2.7.0
68 | eslint-plugin-toml: ^0.12.0
69 | eslint-plugin-unicorn: ^59.0.1
70 | eslint-plugin-unused-imports: ^4.1.4
71 | eslint-plugin-vue: ^10.1.0
72 | eslint-plugin-yml: ^1.18.0
73 | eslint-processor-vue-blocks: ^2.0.0
74 | globals: ^16.1.0
75 | jsonc-eslint-parser: ^2.4.0
76 | local-pkg: ^1.1.1
77 | parse-gitignore: ^2.0.0
78 | toml-eslint-parser: ^0.10.0
79 | vue-eslint-parser: ^10.1.3
80 | yaml-eslint-parser: ^1.3.0
81 | onlyBuiltDependencies:
82 | - esbuild
83 | - simple-git-hooks
84 | - unrs-resolver
85 |
--------------------------------------------------------------------------------
/scripts/typegen.ts:
--------------------------------------------------------------------------------
1 | import fs from 'node:fs/promises'
2 |
3 | import { flatConfigsToRulesDTS } from 'eslint-typegen/core'
4 | import { builtinRules } from 'eslint/use-at-your-own-risk'
5 |
6 | import { astro, combine, comments, formatters, imports, javascript, jsdoc, jsonc, jsx, markdown, node, perfectionist, react, regexp, solid, sortPackageJson, stylistic, svelte, test, toml, typescript, unicorn, unocss, vue, yaml } from '../src'
7 |
8 | const configs = await combine(
9 | {
10 | plugins: {
11 | '': {
12 | rules: Object.fromEntries(builtinRules.entries()),
13 | },
14 | },
15 | },
16 | astro(),
17 | comments(),
18 | formatters(),
19 | imports(),
20 | javascript(),
21 | jsx(),
22 | jsdoc(),
23 | jsonc(),
24 | markdown(),
25 | node(),
26 | perfectionist(),
27 | react(),
28 | solid(),
29 | sortPackageJson(),
30 | stylistic(),
31 | svelte(),
32 | test(),
33 | toml(),
34 | regexp(),
35 | typescript(),
36 | unicorn(),
37 | unocss(),
38 | vue(),
39 | yaml(),
40 | )
41 |
42 | const configNames = configs.map(i => i.name).filter(Boolean) as string[]
43 |
44 | let dts = await flatConfigsToRulesDTS(configs, {
45 | includeAugmentation: false,
46 | })
47 |
48 | dts += `
49 | // Names of all the configs
50 | export type ConfigNames = ${configNames.map(i => `'${i}'`).join(' | ')}
51 | `
52 |
53 | await fs.writeFile('src/typegen.d.ts', dts)
54 |
--------------------------------------------------------------------------------
/scripts/versiongen.ts:
--------------------------------------------------------------------------------
1 | import fs from 'node:fs/promises'
2 | import { parsePnpmWorkspaceYaml } from 'pnpm-workspace-yaml'
3 | import { dependenciesMap } from '../src/cli/constants'
4 |
5 | const names = new Set([
6 | 'eslint',
7 | ...Object.values(dependenciesMap).flat(),
8 | ])
9 |
10 | const yaml = parsePnpmWorkspaceYaml(await fs.readFile(new URL('../pnpm-workspace.yaml', import.meta.url), 'utf-8')).toJSON()
11 | const catalogs = Object.values({
12 | default: yaml.catalog || {},
13 | ...yaml.catalogs,
14 | })
15 |
16 | const versions = Object.fromEntries(Array.from(names).map((name) => {
17 | const version = catalogs.map(c => c[name]).filter(Boolean)[0]
18 | if (!version)
19 | throw new Error(`Package ${name} not found`)
20 | return [name, version]
21 | }).sort((a, b) => a[0].localeCompare(b[0])))
22 |
23 | await fs.writeFile(new URL('../src/cli/constants-generated.ts', import.meta.url), `export const versionsMap = ${JSON.stringify(versions, null, 2)}`)
24 |
--------------------------------------------------------------------------------
/src/cli.ts:
--------------------------------------------------------------------------------
1 | export * from './cli/index'
2 |
--------------------------------------------------------------------------------
/src/cli/constants-generated.ts:
--------------------------------------------------------------------------------
1 | export const versionsMap = {
2 | "@eslint-react/eslint-plugin": "^1.49.0",
3 | "@unocss/eslint-plugin": "^66.1.2",
4 | "astro-eslint-parser": "^1.2.2",
5 | "eslint": "^9.26.0",
6 | "eslint-plugin-astro": "^1.3.1",
7 | "eslint-plugin-format": "^1.0.1",
8 | "eslint-plugin-react-hooks": "^5.2.0",
9 | "eslint-plugin-react-refresh": "^0.4.20",
10 | "eslint-plugin-solid": "^0.14.5",
11 | "eslint-plugin-svelte": "^3.7.0",
12 | "prettier-plugin-astro": "^0.14.1",
13 | "prettier-plugin-slidev": "^1.0.5",
14 | "svelte-eslint-parser": "^1.2.0"
15 | }
--------------------------------------------------------------------------------
/src/cli/constants.ts:
--------------------------------------------------------------------------------
1 | import type { ExtraLibrariesOption, FrameworkOption, PromItem } from './types'
2 |
3 | import c from 'ansis'
4 |
5 | export const vscodeSettingsString = `
6 | // Disable the default formatter, use eslint instead
7 | "prettier.enable": false,
8 | "editor.formatOnSave": false,
9 |
10 | // Auto fix
11 | "editor.codeActionsOnSave": {
12 | "source.fixAll.eslint": "explicit",
13 | "source.organizeImports": "never"
14 | },
15 |
16 | // Silent the stylistic rules in you IDE, but still auto fix them
17 | "eslint.rules.customizations": [
18 | { "rule": "style/*", "severity": "off", "fixable": true },
19 | { "rule": "format/*", "severity": "off", "fixable": true },
20 | { "rule": "*-indent", "severity": "off", "fixable": true },
21 | { "rule": "*-spacing", "severity": "off", "fixable": true },
22 | { "rule": "*-spaces", "severity": "off", "fixable": true },
23 | { "rule": "*-order", "severity": "off", "fixable": true },
24 | { "rule": "*-dangle", "severity": "off", "fixable": true },
25 | { "rule": "*-newline", "severity": "off", "fixable": true },
26 | { "rule": "*quotes", "severity": "off", "fixable": true },
27 | { "rule": "*semi", "severity": "off", "fixable": true }
28 | ],
29 |
30 | // Enable eslint for all supported languages
31 | "eslint.validate": [
32 | "javascript",
33 | "javascriptreact",
34 | "typescript",
35 | "typescriptreact",
36 | "vue",
37 | "html",
38 | "markdown",
39 | "json",
40 | "json5",
41 | "jsonc",
42 | "yaml",
43 | "toml",
44 | "xml",
45 | "gql",
46 | "graphql",
47 | "astro",
48 | "svelte",
49 | "css",
50 | "less",
51 | "scss",
52 | "pcss",
53 | "postcss"
54 | ]
55 | `
56 |
57 | export const frameworkOptions: PromItem[] = [
58 | {
59 | label: c.green('Vue'),
60 | value: 'vue',
61 | },
62 | {
63 | label: c.cyan('React'),
64 | value: 'react',
65 | },
66 | {
67 | label: c.red('Svelte'),
68 | value: 'svelte',
69 | },
70 | {
71 | label: c.magenta('Astro'),
72 | value: 'astro',
73 | },
74 | {
75 | label: c.cyan('Solid'),
76 | value: 'solid',
77 | },
78 | {
79 | label: c.blue('Slidev'),
80 | value: 'slidev',
81 | },
82 | ]
83 |
84 | export const frameworks: FrameworkOption[] = frameworkOptions.map(({ value }) => (value))
85 |
86 | export const extraOptions: PromItem[] = [
87 | {
88 | hint: 'Use external formatters (Prettier and/or dprint) to format files that ESLint cannot handle yet (.css, .html, etc)',
89 | label: c.red('Formatter'),
90 | value: 'formatter',
91 | },
92 | {
93 | label: c.cyan('UnoCSS'),
94 | value: 'unocss',
95 | },
96 | ]
97 |
98 | export const extra: ExtraLibrariesOption[] = extraOptions.map(({ value }) => (value))
99 |
100 | export const dependenciesMap = {
101 | astro: [
102 | 'eslint-plugin-astro',
103 | 'astro-eslint-parser',
104 | ],
105 | formatter: [
106 | 'eslint-plugin-format',
107 | ],
108 | formatterAstro: [
109 | 'prettier-plugin-astro',
110 | ],
111 | react: [
112 | '@eslint-react/eslint-plugin',
113 | 'eslint-plugin-react-hooks',
114 | 'eslint-plugin-react-refresh',
115 | ],
116 | slidev: [
117 | 'prettier-plugin-slidev',
118 | ],
119 | solid: [
120 | 'eslint-plugin-solid',
121 | ],
122 | svelte: [
123 | 'eslint-plugin-svelte',
124 | 'svelte-eslint-parser',
125 | ],
126 | unocss: [
127 | '@unocss/eslint-plugin',
128 | ],
129 | vue: [],
130 | } as const
131 |
--------------------------------------------------------------------------------
/src/cli/index.ts:
--------------------------------------------------------------------------------
1 | import process from 'node:process'
2 |
3 | import * as p from '@clack/prompts'
4 | import c from 'ansis'
5 | import { cac } from 'cac'
6 |
7 | import { version } from '../../package.json'
8 | import { run } from './run'
9 |
10 | function header(): void {
11 | console.log('\n')
12 | p.intro(`${c.green`@antfu/eslint-config `}${c.dim`v${version}`}`)
13 | }
14 |
15 | const cli = cac('@antfu/eslint-config')
16 |
17 | cli
18 | .command('', 'Run the initialization or migration')
19 | .option('--yes, -y', 'Skip prompts and use default values', { default: false })
20 | .option('--template, -t ', 'Use the framework template for optimal customization: vue / react / svelte / astro', { type: [] })
21 | .option('--extra, -e ', 'Use the extra utils: formatter / perfectionist / unocss', { type: [] })
22 | .action(async (args) => {
23 | header()
24 | try {
25 | await run(args)
26 | }
27 | catch (error) {
28 | p.log.error(c.inverse.red(' Failed to migrate '))
29 | p.log.error(c.red`✘ ${String(error)}`)
30 | process.exit(1)
31 | }
32 | })
33 |
34 | cli.help()
35 | cli.version(version)
36 | cli.parse()
37 |
--------------------------------------------------------------------------------
/src/cli/run.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable perfectionist/sort-objects */
2 | import type { ExtraLibrariesOption, FrameworkOption, PromptResult } from './types'
3 |
4 | import fs from 'node:fs'
5 | import path from 'node:path'
6 | import process from 'node:process'
7 | import * as p from '@clack/prompts'
8 | import c from 'ansis'
9 |
10 | import { extra, extraOptions, frameworkOptions, frameworks } from './constants'
11 | import { updateEslintFiles } from './stages/update-eslint-files'
12 | import { updatePackageJson } from './stages/update-package-json'
13 | import { updateVscodeSettings } from './stages/update-vscode-settings'
14 |
15 | import { isGitClean } from './utils'
16 |
17 | export interface CliRunOptions {
18 | /**
19 | * Skip prompts and use default values
20 | */
21 | yes?: boolean
22 | /**
23 | * Use the framework template for optimal customization: vue / react / svelte / astro
24 | */
25 | frameworks?: string[]
26 | /**
27 | * Use the extra utils: formatter / perfectionist / unocss
28 | */
29 | extra?: string[]
30 | }
31 |
32 | export async function run(options: CliRunOptions = {}): Promise {
33 | const argSkipPrompt = !!process.env.SKIP_PROMPT || options.yes
34 | const argTemplate = options.frameworks?.map(m => m?.trim()).filter(Boolean)
35 | const argExtra = options.extra?.map(m => m?.trim()).filter(Boolean)
36 |
37 | if (fs.existsSync(path.join(process.cwd(), 'eslint.config.js'))) {
38 | p.log.warn(c.yellow`eslint.config.js already exists, migration wizard exited.`)
39 | return process.exit(1)
40 | }
41 |
42 | // Set default value for promptResult if `argSkipPrompt` is enabled
43 | let result: PromptResult = {
44 | extra: argExtra ?? [],
45 | frameworks: argTemplate ?? [],
46 | uncommittedConfirmed: false,
47 | updateVscodeSettings: true,
48 | }
49 |
50 | if (!argSkipPrompt) {
51 | result = await p.group({
52 | uncommittedConfirmed: () => {
53 | if (argSkipPrompt || isGitClean())
54 | return Promise.resolve(true)
55 |
56 | return p.confirm({
57 | initialValue: false,
58 | message: 'There are uncommitted changes in the current repository, are you sure to continue?',
59 | })
60 | },
61 | frameworks: ({ results }) => {
62 | const isArgTemplateValid = typeof argTemplate === 'string' && !!frameworks.includes(argTemplate)
63 |
64 | if (!results.uncommittedConfirmed || isArgTemplateValid)
65 | return
66 |
67 | const message = !isArgTemplateValid && argTemplate
68 | ? `"${argTemplate}" isn't a valid template. Please choose from below: `
69 | : 'Select a framework:'
70 |
71 | return p.multiselect({
72 | message: c.reset(message),
73 | options: frameworkOptions,
74 | required: false,
75 | })
76 | },
77 | extra: ({ results }) => {
78 | const isArgExtraValid = argExtra?.length && !argExtra.filter(element => !extra.includes(element)).length
79 |
80 | if (!results.uncommittedConfirmed || isArgExtraValid)
81 | return
82 |
83 | const message = !isArgExtraValid && argExtra
84 | ? `"${argExtra}" isn't a valid extra util. Please choose from below: `
85 | : 'Select a extra utils:'
86 |
87 | return p.multiselect({
88 | message: c.reset(message),
89 | options: extraOptions,
90 | required: false,
91 | })
92 | },
93 |
94 | updateVscodeSettings: ({ results }) => {
95 | if (!results.uncommittedConfirmed)
96 | return
97 |
98 | return p.confirm({
99 | initialValue: true,
100 | message: 'Update .vscode/settings.json for better VS Code experience?',
101 | })
102 | },
103 | }, {
104 | onCancel: () => {
105 | p.cancel('Operation cancelled.')
106 | process.exit(0)
107 | },
108 | }) as PromptResult
109 |
110 | if (!result.uncommittedConfirmed)
111 | return process.exit(1)
112 | }
113 |
114 | await updatePackageJson(result)
115 | await updateEslintFiles(result)
116 | await updateVscodeSettings(result)
117 |
118 | p.log.success(c.green`Setup completed`)
119 | p.outro(`Now you can update the dependencies by run ${c.blue('pnpm install')} and run ${c.blue('eslint --fix')}\n`)
120 | }
121 |
--------------------------------------------------------------------------------
/src/cli/stages/update-eslint-files.ts:
--------------------------------------------------------------------------------
1 | import type { PromptResult } from '../types'
2 |
3 | import fs from 'node:fs'
4 | import fsp from 'node:fs/promises'
5 | import path from 'node:path'
6 | import process from 'node:process'
7 | import * as p from '@clack/prompts'
8 | import c from 'ansis'
9 | // @ts-expect-error missing types
10 | import parse from 'parse-gitignore'
11 |
12 | import { getEslintConfigContent } from '../utils'
13 |
14 | export async function updateEslintFiles(result: PromptResult): Promise {
15 | const cwd = process.cwd()
16 | const pathESLintIgnore = path.join(cwd, '.eslintignore')
17 | const pathPackageJSON = path.join(cwd, 'package.json')
18 |
19 | const pkgContent = await fsp.readFile(pathPackageJSON, 'utf-8')
20 | const pkg: Record = JSON.parse(pkgContent)
21 |
22 | const configFileName = pkg.type === 'module' ? 'eslint.config.js' : 'eslint.config.mjs'
23 | const pathFlatConfig = path.join(cwd, configFileName)
24 |
25 | const eslintIgnores: string[] = []
26 | if (fs.existsSync(pathESLintIgnore)) {
27 | p.log.step(c.cyan`Migrating existing .eslintignore`)
28 | const content = await fsp.readFile(pathESLintIgnore, 'utf-8')
29 | const parsed = parse(content)
30 | const globs = parsed.globs()
31 |
32 | for (const glob of globs) {
33 | if (glob.type === 'ignore')
34 | eslintIgnores.push(...glob.patterns)
35 | else if (glob.type === 'unignore')
36 | eslintIgnores.push(...glob.patterns.map((pattern: string) => `!${pattern}`))
37 | }
38 | }
39 |
40 | const configLines: string[] = []
41 |
42 | if (eslintIgnores.length)
43 | configLines.push(`ignores: ${JSON.stringify(eslintIgnores)},`)
44 |
45 | if (result.extra.includes('formatter'))
46 | configLines.push(`formatters: true,`)
47 |
48 | if (result.extra.includes('unocss'))
49 | configLines.push(`unocss: true,`)
50 |
51 | for (const framework of result.frameworks)
52 | configLines.push(`${framework}: true,`)
53 |
54 | const mainConfig = configLines.map(i => ` ${i}`).join('\n')
55 | const additionalConfig: string[] = []
56 |
57 | const eslintConfigContent: string = getEslintConfigContent(mainConfig, additionalConfig)
58 |
59 | await fsp.writeFile(pathFlatConfig, eslintConfigContent)
60 | p.log.success(c.green`Created ${configFileName}`)
61 |
62 | const files = fs.readdirSync(cwd)
63 | const legacyConfig: string[] = []
64 | files.forEach((file) => {
65 | if (/eslint|prettier/.test(file) && !/eslint\.config\./.test(file))
66 | legacyConfig.push(file)
67 | })
68 |
69 | if (legacyConfig.length)
70 | p.note(c.dim(legacyConfig.join(', ')), 'You can now remove those files manually')
71 | }
72 |
--------------------------------------------------------------------------------
/src/cli/stages/update-package-json.ts:
--------------------------------------------------------------------------------
1 | import type { ExtraLibrariesOption, PromptResult } from '../types'
2 | import fsp from 'node:fs/promises'
3 | import path from 'node:path'
4 |
5 | import process from 'node:process'
6 | import * as p from '@clack/prompts'
7 |
8 | import c from 'ansis'
9 |
10 | import { version } from '../../../package.json'
11 | import { dependenciesMap } from '../constants'
12 | import { versionsMap } from '../constants-generated'
13 |
14 | export async function updatePackageJson(result: PromptResult): Promise {
15 | const cwd = process.cwd()
16 |
17 | const pathPackageJSON = path.join(cwd, 'package.json')
18 |
19 | p.log.step(c.cyan`Bumping @antfu/eslint-config to v${version}`)
20 |
21 | const pkgContent = await fsp.readFile(pathPackageJSON, 'utf-8')
22 | const pkg: Record = JSON.parse(pkgContent)
23 |
24 | pkg.devDependencies ??= {}
25 | pkg.devDependencies['@antfu/eslint-config'] = `^${version}`
26 | pkg.devDependencies.eslint ??= versionsMap.eslint
27 |
28 | const addedPackages: string[] = []
29 |
30 | if (result.extra.length) {
31 | result.extra.forEach((item: ExtraLibrariesOption) => {
32 | switch (item) {
33 | case 'formatter':
34 | ([
35 | ...dependenciesMap.formatter,
36 | ...(result.frameworks.includes('astro') ? dependenciesMap.formatterAstro : []),
37 | ]).forEach((f) => {
38 | if (!f)
39 | return
40 | pkg.devDependencies[f] = versionsMap[f as keyof typeof versionsMap]
41 | addedPackages.push(f)
42 | })
43 | break
44 | case 'unocss':
45 | dependenciesMap.unocss.forEach((f) => {
46 | pkg.devDependencies[f] = versionsMap[f as keyof typeof versionsMap]
47 | addedPackages.push(f)
48 | })
49 | break
50 | }
51 | })
52 | }
53 |
54 | for (const framework of result.frameworks) {
55 | const deps = dependenciesMap[framework]
56 | if (deps) {
57 | deps.forEach((f) => {
58 | pkg.devDependencies[f] = versionsMap[f as keyof typeof versionsMap]
59 | addedPackages.push(f)
60 | })
61 | }
62 | }
63 |
64 | if (addedPackages.length)
65 | p.note(c.dim(addedPackages.join(', ')), 'Added packages')
66 |
67 | await fsp.writeFile(pathPackageJSON, JSON.stringify(pkg, null, 2))
68 | p.log.success(c.green`Changes wrote to package.json`)
69 | }
70 |
--------------------------------------------------------------------------------
/src/cli/stages/update-vscode-settings.ts:
--------------------------------------------------------------------------------
1 | import type { PromptResult } from '../types'
2 | import fs from 'node:fs'
3 | import fsp from 'node:fs/promises'
4 | import path from 'node:path'
5 |
6 | import process from 'node:process'
7 | import * as p from '@clack/prompts'
8 |
9 | import { green } from 'ansis'
10 |
11 | import { vscodeSettingsString } from '../constants'
12 |
13 | export async function updateVscodeSettings(result: PromptResult): Promise {
14 | const cwd = process.cwd()
15 |
16 | if (!result.updateVscodeSettings)
17 | return
18 |
19 | const dotVscodePath: string = path.join(cwd, '.vscode')
20 | const settingsPath: string = path.join(dotVscodePath, 'settings.json')
21 |
22 | if (!fs.existsSync(dotVscodePath))
23 | await fsp.mkdir(dotVscodePath, { recursive: true })
24 |
25 | if (!fs.existsSync(settingsPath)) {
26 | await fsp.writeFile(settingsPath, `{${vscodeSettingsString}}\n`, 'utf-8')
27 | p.log.success(green`Created .vscode/settings.json`)
28 | }
29 | else {
30 | let settingsContent = await fsp.readFile(settingsPath, 'utf8')
31 |
32 | settingsContent = settingsContent.trim().replace(/\s*\}$/, '')
33 | settingsContent += settingsContent.endsWith(',') || settingsContent.endsWith('{') ? '' : ','
34 | settingsContent += `${vscodeSettingsString}}\n`
35 |
36 | await fsp.writeFile(settingsPath, settingsContent, 'utf-8')
37 | p.log.success(green`Updated .vscode/settings.json`)
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/cli/types.ts:
--------------------------------------------------------------------------------
1 | export interface PromItem {
2 | label: string
3 | value: T
4 | hint?: string
5 | }
6 |
7 | export type FrameworkOption = 'vue' | 'react' | 'svelte' | 'astro' | 'solid' | 'slidev'
8 |
9 | export type ExtraLibrariesOption = 'formatter' | 'unocss'
10 |
11 | export interface PromptResult {
12 | uncommittedConfirmed: boolean
13 | frameworks: FrameworkOption[]
14 | extra: ExtraLibrariesOption[]
15 | updateVscodeSettings: unknown
16 | }
17 |
--------------------------------------------------------------------------------
/src/cli/utils.ts:
--------------------------------------------------------------------------------
1 | import { execSync } from 'node:child_process'
2 |
3 | export function isGitClean(): boolean {
4 | try {
5 | execSync('git diff-index --quiet HEAD --')
6 | return true
7 | }
8 | catch {
9 | return false
10 | }
11 | }
12 |
13 | export function getEslintConfigContent(
14 | mainConfig: string,
15 | additionalConfigs?: string[],
16 | ): string {
17 | return `
18 | import antfu from '@antfu/eslint-config'
19 |
20 | export default antfu({
21 | ${mainConfig}
22 | }${additionalConfigs?.map(config => `,{\n${config}\n}`)})
23 | `.trimStart()
24 | }
25 |
--------------------------------------------------------------------------------
/src/configs/astro.ts:
--------------------------------------------------------------------------------
1 | import type { OptionsFiles, OptionsOverrides, OptionsStylistic, TypedFlatConfigItem } from '../types'
2 |
3 | import { GLOB_ASTRO } from '../globs'
4 | import { interopDefault } from '../utils'
5 |
6 | export async function astro(
7 | options: OptionsOverrides & OptionsStylistic & OptionsFiles = {},
8 | ): Promise {
9 | const {
10 | files = [GLOB_ASTRO],
11 | overrides = {},
12 | stylistic = true,
13 | } = options
14 |
15 | const [
16 | pluginAstro,
17 | parserAstro,
18 | parserTs,
19 | ] = await Promise.all([
20 | interopDefault(import('eslint-plugin-astro')),
21 | interopDefault(import('astro-eslint-parser')),
22 | interopDefault(import('@typescript-eslint/parser')),
23 | ] as const)
24 |
25 | return [
26 | {
27 | name: 'antfu/astro/setup',
28 | plugins: {
29 | astro: pluginAstro,
30 | },
31 | },
32 | {
33 | files,
34 | languageOptions: {
35 | globals: pluginAstro.environments.astro.globals,
36 | parser: parserAstro,
37 | parserOptions: {
38 | extraFileExtensions: ['.astro'],
39 | parser: parserTs,
40 | },
41 | sourceType: 'module',
42 | },
43 | name: 'antfu/astro/rules',
44 | processor: 'astro/client-side-ts',
45 | rules: {
46 | // Astro uses top level await for e.g. data fetching
47 | // https://docs.astro.build/en/guides/data-fetching/#fetch-in-astro
48 | 'antfu/no-top-level-await': 'off',
49 |
50 | // use recommended rules
51 | 'astro/missing-client-only-directive-value': 'error',
52 | 'astro/no-conflict-set-directives': 'error',
53 | 'astro/no-deprecated-astro-canonicalurl': 'error',
54 | 'astro/no-deprecated-astro-fetchcontent': 'error',
55 | 'astro/no-deprecated-astro-resolve': 'error',
56 | 'astro/no-deprecated-getentrybyslug': 'error',
57 | 'astro/no-set-html-directive': 'off',
58 | 'astro/no-unused-define-vars-in-style': 'error',
59 | 'astro/semi': 'off',
60 | 'astro/valid-compile': 'error',
61 |
62 | ...stylistic
63 | ? {
64 | 'style/indent': 'off',
65 | 'style/jsx-closing-tag-location': 'off',
66 | 'style/jsx-one-expression-per-line': 'off',
67 | 'style/no-multiple-empty-lines': 'off',
68 | }
69 | : {},
70 |
71 | ...overrides,
72 | },
73 | },
74 | ]
75 | }
76 |
--------------------------------------------------------------------------------
/src/configs/command.ts:
--------------------------------------------------------------------------------
1 | import type { TypedFlatConfigItem } from '../types'
2 |
3 | import createCommand from 'eslint-plugin-command/config'
4 |
5 | export async function command(): Promise {
6 | return [
7 | {
8 | ...createCommand(),
9 | name: 'antfu/command/rules',
10 | },
11 | ]
12 | }
13 |
--------------------------------------------------------------------------------
/src/configs/comments.ts:
--------------------------------------------------------------------------------
1 | import type { TypedFlatConfigItem } from '../types'
2 |
3 | import { pluginComments } from '../plugins'
4 |
5 | export async function comments(): Promise {
6 | return [
7 | {
8 | name: 'antfu/eslint-comments/rules',
9 | plugins: {
10 | 'eslint-comments': pluginComments,
11 | },
12 | rules: {
13 | 'eslint-comments/no-aggregating-enable': 'error',
14 | 'eslint-comments/no-duplicate-disable': 'error',
15 | 'eslint-comments/no-unlimited-disable': 'error',
16 | 'eslint-comments/no-unused-enable': 'error',
17 | },
18 | },
19 | ]
20 | }
21 |
--------------------------------------------------------------------------------
/src/configs/disables.ts:
--------------------------------------------------------------------------------
1 | import type { TypedFlatConfigItem } from '../types'
2 |
3 | import { GLOB_SRC, GLOB_SRC_EXT } from '../globs'
4 |
5 | export async function disables(): Promise {
6 | return [
7 | {
8 | files: [`**/scripts/${GLOB_SRC}`],
9 | name: 'antfu/disables/scripts',
10 | rules: {
11 | 'antfu/no-top-level-await': 'off',
12 | 'no-console': 'off',
13 | 'ts/explicit-function-return-type': 'off',
14 | },
15 | },
16 | {
17 | files: [`**/cli/${GLOB_SRC}`, `**/cli.${GLOB_SRC_EXT}`],
18 | name: 'antfu/disables/cli',
19 | rules: {
20 | 'antfu/no-top-level-await': 'off',
21 | 'no-console': 'off',
22 | },
23 | },
24 | {
25 | files: ['**/bin/**/*', `**/bin.${GLOB_SRC_EXT}`],
26 | name: 'antfu/disables/bin',
27 | rules: {
28 | 'antfu/no-import-dist': 'off',
29 | 'antfu/no-import-node-modules-by-path': 'off',
30 | },
31 | },
32 | {
33 | files: ['**/*.d.?([cm])ts'],
34 | name: 'antfu/disables/dts',
35 | rules: {
36 | 'eslint-comments/no-unlimited-disable': 'off',
37 | 'import/no-duplicates': 'off',
38 | 'no-restricted-syntax': 'off',
39 | 'unused-imports/no-unused-vars': 'off',
40 | },
41 | },
42 | {
43 | files: ['**/*.js', '**/*.cjs'],
44 | name: 'antfu/disables/cjs',
45 | rules: {
46 | 'ts/no-require-imports': 'off',
47 | },
48 | },
49 | {
50 | files: [`**/*.config.${GLOB_SRC_EXT}`, `**/*.config.*.${GLOB_SRC_EXT}`],
51 | name: 'antfu/disables/config-files',
52 | rules: {
53 | 'antfu/no-top-level-await': 'off',
54 | 'no-console': 'off',
55 | 'ts/explicit-function-return-type': 'off',
56 | },
57 | },
58 | ]
59 | }
60 |
--------------------------------------------------------------------------------
/src/configs/ignores.ts:
--------------------------------------------------------------------------------
1 | import type { TypedFlatConfigItem } from '../types'
2 |
3 | import { GLOB_EXCLUDE } from '../globs'
4 |
5 | export async function ignores(userIgnores: string[] = []): Promise {
6 | return [
7 | {
8 | ignores: [
9 | ...GLOB_EXCLUDE,
10 | ...userIgnores,
11 | ],
12 | name: 'antfu/ignores',
13 | },
14 | ]
15 | }
16 |
--------------------------------------------------------------------------------
/src/configs/imports.ts:
--------------------------------------------------------------------------------
1 | import type { OptionsStylistic, TypedFlatConfigItem } from '../types'
2 |
3 | import { pluginAntfu, pluginImport } from '../plugins'
4 |
5 | export async function imports(options: OptionsStylistic = {}): Promise {
6 | const {
7 | stylistic = true,
8 | } = options
9 |
10 | return [
11 | {
12 | name: 'antfu/imports/rules',
13 | plugins: {
14 | antfu: pluginAntfu,
15 | import: pluginImport,
16 | },
17 | rules: {
18 | 'antfu/import-dedupe': 'error',
19 | 'antfu/no-import-dist': 'error',
20 | 'antfu/no-import-node-modules-by-path': 'error',
21 |
22 | 'import/consistent-type-specifier-style': ['error', 'prefer-top-level'],
23 | 'import/first': 'error',
24 | 'import/no-duplicates': 'error',
25 | 'import/no-mutable-exports': 'error',
26 | 'import/no-named-default': 'error',
27 | 'import/no-self-import': 'error',
28 | 'import/no-webpack-loader-syntax': 'error',
29 |
30 | ...stylistic
31 | ? {
32 | 'import/newline-after-import': ['error', { count: 1 }],
33 | }
34 | : {},
35 | },
36 | },
37 | ]
38 | }
39 |
--------------------------------------------------------------------------------
/src/configs/index.ts:
--------------------------------------------------------------------------------
1 | export * from './astro'
2 | export * from './command'
3 | export * from './comments'
4 | export * from './disables'
5 | export * from './formatters'
6 | export * from './ignores'
7 | export * from './imports'
8 | export * from './javascript'
9 | export * from './jsdoc'
10 | export * from './jsonc'
11 | export * from './jsx'
12 | export * from './markdown'
13 | export * from './node'
14 | export * from './perfectionist'
15 | export * from './pnpm'
16 | export * from './react'
17 | export * from './regexp'
18 | export * from './solid'
19 | export * from './sort'
20 | export * from './stylistic'
21 | export * from './svelte'
22 | export * from './test'
23 | export * from './toml'
24 | export * from './typescript'
25 | export * from './unicorn'
26 | export * from './unocss'
27 | export * from './vue'
28 | export * from './yaml'
29 |
--------------------------------------------------------------------------------
/src/configs/javascript.ts:
--------------------------------------------------------------------------------
1 | import type { OptionsIsInEditor, OptionsOverrides, TypedFlatConfigItem } from '../types'
2 |
3 | import globals from 'globals'
4 |
5 | import { pluginAntfu, pluginUnusedImports } from '../plugins'
6 |
7 | export async function javascript(
8 | options: OptionsIsInEditor & OptionsOverrides = {},
9 | ): Promise {
10 | const {
11 | isInEditor = false,
12 | overrides = {},
13 | } = options
14 |
15 | return [
16 | {
17 | languageOptions: {
18 | ecmaVersion: 2022,
19 | globals: {
20 | ...globals.browser,
21 | ...globals.es2021,
22 | ...globals.node,
23 | document: 'readonly',
24 | navigator: 'readonly',
25 | window: 'readonly',
26 | },
27 | parserOptions: {
28 | ecmaFeatures: {
29 | jsx: true,
30 | },
31 | ecmaVersion: 2022,
32 | sourceType: 'module',
33 | },
34 | sourceType: 'module',
35 | },
36 | linterOptions: {
37 | reportUnusedDisableDirectives: true,
38 | },
39 | name: 'antfu/javascript/setup',
40 | },
41 | {
42 | name: 'antfu/javascript/rules',
43 | plugins: {
44 | 'antfu': pluginAntfu,
45 | 'unused-imports': pluginUnusedImports,
46 | },
47 | rules: {
48 | 'accessor-pairs': ['error', { enforceForClassMembers: true, setWithoutGet: true }],
49 |
50 | 'antfu/no-top-level-await': 'error',
51 |
52 | 'array-callback-return': 'error',
53 | 'block-scoped-var': 'error',
54 | 'constructor-super': 'error',
55 | 'default-case-last': 'error',
56 | 'dot-notation': ['error', { allowKeywords: true }],
57 | 'eqeqeq': ['error', 'smart'],
58 | 'new-cap': ['error', { capIsNew: false, newIsCap: true, properties: true }],
59 | 'no-alert': 'error',
60 | 'no-array-constructor': 'error',
61 | 'no-async-promise-executor': 'error',
62 | 'no-caller': 'error',
63 | 'no-case-declarations': 'error',
64 | 'no-class-assign': 'error',
65 | 'no-compare-neg-zero': 'error',
66 | 'no-cond-assign': ['error', 'always'],
67 | 'no-console': ['error', { allow: ['warn', 'error'] }],
68 | 'no-const-assign': 'error',
69 | 'no-control-regex': 'error',
70 | 'no-debugger': 'error',
71 | 'no-delete-var': 'error',
72 | 'no-dupe-args': 'error',
73 | 'no-dupe-class-members': 'error',
74 | 'no-dupe-keys': 'error',
75 | 'no-duplicate-case': 'error',
76 | 'no-empty': ['error', { allowEmptyCatch: true }],
77 | 'no-empty-character-class': 'error',
78 | 'no-empty-pattern': 'error',
79 | 'no-eval': 'error',
80 | 'no-ex-assign': 'error',
81 | 'no-extend-native': 'error',
82 | 'no-extra-bind': 'error',
83 | 'no-extra-boolean-cast': 'error',
84 | 'no-fallthrough': 'error',
85 | 'no-func-assign': 'error',
86 | 'no-global-assign': 'error',
87 | 'no-implied-eval': 'error',
88 | 'no-import-assign': 'error',
89 | 'no-invalid-regexp': 'error',
90 | 'no-irregular-whitespace': 'error',
91 | 'no-iterator': 'error',
92 | 'no-labels': ['error', { allowLoop: false, allowSwitch: false }],
93 | 'no-lone-blocks': 'error',
94 | 'no-loss-of-precision': 'error',
95 | 'no-misleading-character-class': 'error',
96 | 'no-multi-str': 'error',
97 | 'no-new': 'error',
98 | 'no-new-func': 'error',
99 | 'no-new-native-nonconstructor': 'error',
100 | 'no-new-wrappers': 'error',
101 | 'no-obj-calls': 'error',
102 | 'no-octal': 'error',
103 | 'no-octal-escape': 'error',
104 | 'no-proto': 'error',
105 | 'no-prototype-builtins': 'error',
106 | 'no-redeclare': ['error', { builtinGlobals: false }],
107 | 'no-regex-spaces': 'error',
108 | 'no-restricted-globals': [
109 | 'error',
110 | { message: 'Use `globalThis` instead.', name: 'global' },
111 | { message: 'Use `globalThis` instead.', name: 'self' },
112 | ],
113 | 'no-restricted-properties': [
114 | 'error',
115 | { message: 'Use `Object.getPrototypeOf` or `Object.setPrototypeOf` instead.', property: '__proto__' },
116 | { message: 'Use `Object.defineProperty` instead.', property: '__defineGetter__' },
117 | { message: 'Use `Object.defineProperty` instead.', property: '__defineSetter__' },
118 | { message: 'Use `Object.getOwnPropertyDescriptor` instead.', property: '__lookupGetter__' },
119 | { message: 'Use `Object.getOwnPropertyDescriptor` instead.', property: '__lookupSetter__' },
120 | ],
121 | 'no-restricted-syntax': [
122 | 'error',
123 | 'TSEnumDeclaration[const=true]',
124 | 'TSExportAssignment',
125 | ],
126 | 'no-self-assign': ['error', { props: true }],
127 | 'no-self-compare': 'error',
128 | 'no-sequences': 'error',
129 | 'no-shadow-restricted-names': 'error',
130 | 'no-sparse-arrays': 'error',
131 | 'no-template-curly-in-string': 'error',
132 | 'no-this-before-super': 'error',
133 | 'no-throw-literal': 'error',
134 | 'no-undef': 'error',
135 | 'no-undef-init': 'error',
136 | 'no-unexpected-multiline': 'error',
137 | 'no-unmodified-loop-condition': 'error',
138 | 'no-unneeded-ternary': ['error', { defaultAssignment: false }],
139 | 'no-unreachable': 'error',
140 | 'no-unreachable-loop': 'error',
141 | 'no-unsafe-finally': 'error',
142 | 'no-unsafe-negation': 'error',
143 | 'no-unused-expressions': ['error', {
144 | allowShortCircuit: true,
145 | allowTaggedTemplates: true,
146 | allowTernary: true,
147 | }],
148 | 'no-unused-vars': ['error', {
149 | args: 'none',
150 | caughtErrors: 'none',
151 | ignoreRestSiblings: true,
152 | vars: 'all',
153 | }],
154 | 'no-use-before-define': ['error', { classes: false, functions: false, variables: true }],
155 | 'no-useless-backreference': 'error',
156 | 'no-useless-call': 'error',
157 | 'no-useless-catch': 'error',
158 | 'no-useless-computed-key': 'error',
159 | 'no-useless-constructor': 'error',
160 | 'no-useless-rename': 'error',
161 | 'no-useless-return': 'error',
162 | 'no-var': 'error',
163 | 'no-with': 'error',
164 | 'object-shorthand': [
165 | 'error',
166 | 'always',
167 | {
168 | avoidQuotes: true,
169 | ignoreConstructors: false,
170 | },
171 | ],
172 | 'one-var': ['error', { initialized: 'never' }],
173 | 'prefer-arrow-callback': [
174 | 'error',
175 | {
176 | allowNamedFunctions: false,
177 | allowUnboundThis: true,
178 | },
179 | ],
180 | 'prefer-const': [
181 | isInEditor ? 'warn' : 'error',
182 | {
183 | destructuring: 'all',
184 | ignoreReadBeforeAssign: true,
185 | },
186 | ],
187 | 'prefer-exponentiation-operator': 'error',
188 | 'prefer-promise-reject-errors': 'error',
189 | 'prefer-regex-literals': ['error', { disallowRedundantWrapping: true }],
190 | 'prefer-rest-params': 'error',
191 | 'prefer-spread': 'error',
192 | 'prefer-template': 'error',
193 | 'symbol-description': 'error',
194 | 'unicode-bom': ['error', 'never'],
195 | 'unused-imports/no-unused-imports': isInEditor ? 'warn' : 'error',
196 | 'unused-imports/no-unused-vars': [
197 | 'error',
198 | {
199 | args: 'after-used',
200 | argsIgnorePattern: '^_',
201 | ignoreRestSiblings: true,
202 | vars: 'all',
203 | varsIgnorePattern: '^_',
204 | },
205 | ],
206 | 'use-isnan': ['error', { enforceForIndexOf: true, enforceForSwitchCase: true }],
207 | 'valid-typeof': ['error', { requireStringLiterals: true }],
208 | 'vars-on-top': 'error',
209 | 'yoda': ['error', 'never'],
210 |
211 | ...overrides,
212 | },
213 | },
214 | ]
215 | }
216 |
--------------------------------------------------------------------------------
/src/configs/jsdoc.ts:
--------------------------------------------------------------------------------
1 | import type { OptionsStylistic, TypedFlatConfigItem } from '../types'
2 |
3 | import { interopDefault } from '../utils'
4 |
5 | export async function jsdoc(options: OptionsStylistic = {}): Promise {
6 | const {
7 | stylistic = true,
8 | } = options
9 |
10 | return [
11 | {
12 | name: 'antfu/jsdoc/rules',
13 | plugins: {
14 | jsdoc: await interopDefault(import('eslint-plugin-jsdoc')),
15 | },
16 | rules: {
17 | 'jsdoc/check-access': 'warn',
18 | 'jsdoc/check-param-names': 'warn',
19 | 'jsdoc/check-property-names': 'warn',
20 | 'jsdoc/check-types': 'warn',
21 | 'jsdoc/empty-tags': 'warn',
22 | 'jsdoc/implements-on-classes': 'warn',
23 | 'jsdoc/no-defaults': 'warn',
24 | 'jsdoc/no-multi-asterisks': 'warn',
25 | 'jsdoc/require-param-name': 'warn',
26 | 'jsdoc/require-property': 'warn',
27 | 'jsdoc/require-property-description': 'warn',
28 | 'jsdoc/require-property-name': 'warn',
29 | 'jsdoc/require-returns-check': 'warn',
30 | 'jsdoc/require-returns-description': 'warn',
31 | 'jsdoc/require-yields-check': 'warn',
32 |
33 | ...stylistic
34 | ? {
35 | 'jsdoc/check-alignment': 'warn',
36 | 'jsdoc/multiline-blocks': 'warn',
37 | }
38 | : {},
39 | },
40 | },
41 | ]
42 | }
43 |
--------------------------------------------------------------------------------
/src/configs/jsonc.ts:
--------------------------------------------------------------------------------
1 | import type { OptionsFiles, OptionsOverrides, OptionsStylistic, TypedFlatConfigItem } from '../types'
2 |
3 | import { GLOB_JSON, GLOB_JSON5, GLOB_JSONC } from '../globs'
4 | import { interopDefault } from '../utils'
5 |
6 | export async function jsonc(
7 | options: OptionsFiles & OptionsStylistic & OptionsOverrides = {},
8 | ): Promise {
9 | const {
10 | files = [GLOB_JSON, GLOB_JSON5, GLOB_JSONC],
11 | overrides = {},
12 | stylistic = true,
13 | } = options
14 |
15 | const {
16 | indent = 2,
17 | } = typeof stylistic === 'boolean' ? {} : stylistic
18 |
19 | const [
20 | pluginJsonc,
21 | parserJsonc,
22 | ] = await Promise.all([
23 | interopDefault(import('eslint-plugin-jsonc')),
24 | interopDefault(import('jsonc-eslint-parser')),
25 | ] as const)
26 |
27 | return [
28 | {
29 | name: 'antfu/jsonc/setup',
30 | plugins: {
31 | jsonc: pluginJsonc as any,
32 | },
33 | },
34 | {
35 | files,
36 | languageOptions: {
37 | parser: parserJsonc,
38 | },
39 | name: 'antfu/jsonc/rules',
40 | rules: {
41 | 'jsonc/no-bigint-literals': 'error',
42 | 'jsonc/no-binary-expression': 'error',
43 | 'jsonc/no-binary-numeric-literals': 'error',
44 | 'jsonc/no-dupe-keys': 'error',
45 | 'jsonc/no-escape-sequence-in-identifier': 'error',
46 | 'jsonc/no-floating-decimal': 'error',
47 | 'jsonc/no-hexadecimal-numeric-literals': 'error',
48 | 'jsonc/no-infinity': 'error',
49 | 'jsonc/no-multi-str': 'error',
50 | 'jsonc/no-nan': 'error',
51 | 'jsonc/no-number-props': 'error',
52 | 'jsonc/no-numeric-separators': 'error',
53 | 'jsonc/no-octal': 'error',
54 | 'jsonc/no-octal-escape': 'error',
55 | 'jsonc/no-octal-numeric-literals': 'error',
56 | 'jsonc/no-parenthesized': 'error',
57 | 'jsonc/no-plus-sign': 'error',
58 | 'jsonc/no-regexp-literals': 'error',
59 | 'jsonc/no-sparse-arrays': 'error',
60 | 'jsonc/no-template-literals': 'error',
61 | 'jsonc/no-undefined-value': 'error',
62 | 'jsonc/no-unicode-codepoint-escapes': 'error',
63 | 'jsonc/no-useless-escape': 'error',
64 | 'jsonc/space-unary-ops': 'error',
65 | 'jsonc/valid-json-number': 'error',
66 | 'jsonc/vue-custom-block/no-parsing-error': 'error',
67 |
68 | ...stylistic
69 | ? {
70 | 'jsonc/array-bracket-spacing': ['error', 'never'],
71 | 'jsonc/comma-dangle': ['error', 'never'],
72 | 'jsonc/comma-style': ['error', 'last'],
73 | 'jsonc/indent': ['error', indent],
74 | 'jsonc/key-spacing': ['error', { afterColon: true, beforeColon: false }],
75 | 'jsonc/object-curly-newline': ['error', { consistent: true, multiline: true }],
76 | 'jsonc/object-curly-spacing': ['error', 'always'],
77 | 'jsonc/object-property-newline': ['error', { allowMultiplePropertiesPerLine: true }],
78 | 'jsonc/quote-props': 'error',
79 | 'jsonc/quotes': 'error',
80 | }
81 | : {},
82 |
83 | ...overrides,
84 | },
85 | },
86 | ]
87 | }
88 |
--------------------------------------------------------------------------------
/src/configs/jsx.ts:
--------------------------------------------------------------------------------
1 | import type { TypedFlatConfigItem } from '../types'
2 |
3 | import { GLOB_JSX, GLOB_TSX } from '../globs'
4 |
5 | export async function jsx(): Promise {
6 | return [
7 | {
8 | files: [GLOB_JSX, GLOB_TSX],
9 | languageOptions: {
10 | parserOptions: {
11 | ecmaFeatures: {
12 | jsx: true,
13 | },
14 | },
15 | },
16 | name: 'antfu/jsx/setup',
17 | },
18 | ]
19 | }
20 |
--------------------------------------------------------------------------------
/src/configs/markdown.ts:
--------------------------------------------------------------------------------
1 | import type { OptionsComponentExts, OptionsFiles, OptionsOverrides, TypedFlatConfigItem } from '../types'
2 |
3 | import { mergeProcessors, processorPassThrough } from 'eslint-merge-processors'
4 | import { GLOB_MARKDOWN, GLOB_MARKDOWN_CODE, GLOB_MARKDOWN_IN_MARKDOWN } from '../globs'
5 |
6 | import { interopDefault, parserPlain } from '../utils'
7 |
8 | export async function markdown(
9 | options: OptionsFiles & OptionsComponentExts & OptionsOverrides = {},
10 | ): Promise {
11 | const {
12 | componentExts = [],
13 | files = [GLOB_MARKDOWN],
14 | overrides = {},
15 | } = options
16 |
17 | const markdown = await interopDefault(import('@eslint/markdown'))
18 |
19 | return [
20 | {
21 | name: 'antfu/markdown/setup',
22 | plugins: {
23 | markdown,
24 | },
25 | },
26 | {
27 | files,
28 | ignores: [GLOB_MARKDOWN_IN_MARKDOWN],
29 | name: 'antfu/markdown/processor',
30 | // `eslint-plugin-markdown` only creates virtual files for code blocks,
31 | // but not the markdown file itself. We use `eslint-merge-processors` to
32 | // add a pass-through processor for the markdown file itself.
33 | processor: mergeProcessors([
34 | markdown.processors!.markdown,
35 | processorPassThrough,
36 | ]),
37 | },
38 | {
39 | files,
40 | languageOptions: {
41 | parser: parserPlain,
42 | },
43 | name: 'antfu/markdown/parser',
44 | },
45 | {
46 | files: [
47 | GLOB_MARKDOWN_CODE,
48 | ...componentExts.map(ext => `${GLOB_MARKDOWN}/**/*.${ext}`),
49 | ],
50 | languageOptions: {
51 | parserOptions: {
52 | ecmaFeatures: {
53 | impliedStrict: true,
54 | },
55 | },
56 | },
57 | name: 'antfu/markdown/disables',
58 | rules: {
59 | 'antfu/no-top-level-await': 'off',
60 |
61 | 'import/newline-after-import': 'off',
62 |
63 | 'no-alert': 'off',
64 | 'no-console': 'off',
65 | 'no-labels': 'off',
66 | 'no-lone-blocks': 'off',
67 | 'no-restricted-syntax': 'off',
68 | 'no-undef': 'off',
69 | 'no-unused-expressions': 'off',
70 | 'no-unused-labels': 'off',
71 |
72 | 'no-unused-vars': 'off',
73 | 'node/prefer-global/process': 'off',
74 | 'style/comma-dangle': 'off',
75 |
76 | 'style/eol-last': 'off',
77 | 'ts/consistent-type-imports': 'off',
78 | 'ts/explicit-function-return-type': 'off',
79 | 'ts/no-namespace': 'off',
80 | 'ts/no-redeclare': 'off',
81 | 'ts/no-require-imports': 'off',
82 | 'ts/no-unused-expressions': 'off',
83 | 'ts/no-unused-vars': 'off',
84 | 'ts/no-use-before-define': 'off',
85 |
86 | 'unicode-bom': 'off',
87 | 'unused-imports/no-unused-imports': 'off',
88 | 'unused-imports/no-unused-vars': 'off',
89 |
90 | ...overrides,
91 | },
92 | },
93 | ]
94 | }
95 |
--------------------------------------------------------------------------------
/src/configs/node.ts:
--------------------------------------------------------------------------------
1 | import type { TypedFlatConfigItem } from '../types'
2 |
3 | import { pluginNode } from '../plugins'
4 |
5 | export async function node(): Promise {
6 | return [
7 | {
8 | name: 'antfu/node/rules',
9 | plugins: {
10 | node: pluginNode,
11 | },
12 | rules: {
13 | 'node/handle-callback-err': ['error', '^(err|error)$'],
14 | 'node/no-deprecated-api': 'error',
15 | 'node/no-exports-assign': 'error',
16 | 'node/no-new-require': 'error',
17 | 'node/no-path-concat': 'error',
18 | 'node/prefer-global/buffer': ['error', 'never'],
19 | 'node/prefer-global/process': ['error', 'never'],
20 | 'node/process-exit-as-throw': 'error',
21 | },
22 | },
23 | ]
24 | }
25 |
--------------------------------------------------------------------------------
/src/configs/perfectionist.ts:
--------------------------------------------------------------------------------
1 | import type { TypedFlatConfigItem } from '../types'
2 |
3 | import { pluginPerfectionist } from '../plugins'
4 |
5 | /**
6 | * Perfectionist plugin for props and items sorting.
7 | *
8 | * @see https://github.com/azat-io/eslint-plugin-perfectionist
9 | */
10 | export async function perfectionist(): Promise {
11 | return [
12 | {
13 | name: 'antfu/perfectionist/setup',
14 | plugins: {
15 | perfectionist: pluginPerfectionist,
16 | },
17 | rules: {
18 | 'perfectionist/sort-exports': ['error', { order: 'asc', type: 'natural' }],
19 | 'perfectionist/sort-imports': ['error', {
20 | groups: [
21 | 'type',
22 | ['parent-type', 'sibling-type', 'index-type', 'internal-type'],
23 |
24 | 'builtin',
25 | 'external',
26 | 'internal',
27 | ['parent', 'sibling', 'index'],
28 | 'side-effect',
29 | 'object',
30 | 'unknown',
31 | ],
32 | newlinesBetween: 'ignore',
33 | order: 'asc',
34 | type: 'natural',
35 | }],
36 | 'perfectionist/sort-named-exports': ['error', { order: 'asc', type: 'natural' }],
37 | 'perfectionist/sort-named-imports': ['error', { order: 'asc', type: 'natural' }],
38 | },
39 | },
40 | ]
41 | }
42 |
--------------------------------------------------------------------------------
/src/configs/pnpm.ts:
--------------------------------------------------------------------------------
1 | import type { TypedFlatConfigItem } from '../types'
2 |
3 | import { interopDefault } from '../utils'
4 |
5 | export async function pnpm(): Promise {
6 | const [
7 | pluginPnpm,
8 | yamlParser,
9 | jsoncParser,
10 | ] = await Promise.all([
11 | interopDefault(import('eslint-plugin-pnpm')),
12 | interopDefault(import('yaml-eslint-parser')),
13 | interopDefault(import('jsonc-eslint-parser')),
14 | ])
15 |
16 | return [
17 | {
18 | files: [
19 | 'package.json',
20 | '**/package.json',
21 | ],
22 | languageOptions: {
23 | parser: jsoncParser,
24 | },
25 | name: 'antfu/pnpm/package-json',
26 | plugins: {
27 | pnpm: pluginPnpm,
28 | },
29 | rules: {
30 | 'pnpm/json-enforce-catalog': 'error',
31 | 'pnpm/json-prefer-workspace-settings': 'error',
32 | 'pnpm/json-valid-catalog': 'error',
33 | },
34 | },
35 | {
36 | files: ['pnpm-workspace.yaml'],
37 | languageOptions: {
38 | parser: yamlParser,
39 | },
40 | name: 'antfu/pnpm/pnpm-workspace-yaml',
41 | plugins: {
42 | pnpm: pluginPnpm,
43 | },
44 | rules: {
45 | 'pnpm/yaml-no-duplicate-catalog-item': 'error',
46 | 'pnpm/yaml-no-unused-catalog-item': 'error',
47 | },
48 | },
49 | ]
50 | }
51 |
--------------------------------------------------------------------------------
/src/configs/react.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable perfectionist/sort-objects */
2 | import type { OptionsFiles, OptionsOverrides, OptionsTypeScriptParserOptions, OptionsTypeScriptWithTypes, TypedFlatConfigItem } from '../types'
3 |
4 | import { isPackageExists } from 'local-pkg'
5 | import { GLOB_ASTRO_TS, GLOB_MARKDOWN, GLOB_SRC, GLOB_TS, GLOB_TSX } from '../globs'
6 |
7 | import { ensurePackages, interopDefault } from '../utils'
8 |
9 | // react refresh
10 | const ReactRefreshAllowConstantExportPackages = [
11 | 'vite',
12 | ]
13 | const RemixPackages = [
14 | '@remix-run/node',
15 | '@remix-run/react',
16 | '@remix-run/serve',
17 | '@remix-run/dev',
18 | ]
19 | const ReactRouterPackages = [
20 | '@react-router/node',
21 | '@react-router/react',
22 | '@react-router/serve',
23 | '@react-router/dev',
24 | ]
25 | const NextJsPackages = [
26 | 'next',
27 | ]
28 |
29 | export async function react(
30 | options: OptionsTypeScriptParserOptions & OptionsTypeScriptWithTypes & OptionsOverrides & OptionsFiles = {},
31 | ): Promise {
32 | const {
33 | files = [GLOB_SRC],
34 | filesTypeAware = [GLOB_TS, GLOB_TSX],
35 | ignoresTypeAware = [
36 | `${GLOB_MARKDOWN}/**`,
37 | GLOB_ASTRO_TS,
38 | ],
39 | overrides = {},
40 | tsconfigPath,
41 | } = options
42 |
43 | await ensurePackages([
44 | '@eslint-react/eslint-plugin',
45 | 'eslint-plugin-react-hooks',
46 | 'eslint-plugin-react-refresh',
47 | ])
48 |
49 | const isTypeAware = !!tsconfigPath
50 |
51 | const typeAwareRules: TypedFlatConfigItem['rules'] = {
52 | 'react/no-leaked-conditional-rendering': 'warn',
53 | }
54 |
55 | const [
56 | pluginReact,
57 | pluginReactHooks,
58 | pluginReactRefresh,
59 | ] = await Promise.all([
60 | interopDefault(import('@eslint-react/eslint-plugin')),
61 | interopDefault(import('eslint-plugin-react-hooks')),
62 | interopDefault(import('eslint-plugin-react-refresh')),
63 | ] as const)
64 |
65 | const isAllowConstantExport = ReactRefreshAllowConstantExportPackages.some(i => isPackageExists(i))
66 | const isUsingRemix = RemixPackages.some(i => isPackageExists(i))
67 | const isUsingReactRouter = ReactRouterPackages.some(i => isPackageExists(i))
68 | const isUsingNext = NextJsPackages.some(i => isPackageExists(i))
69 |
70 | const plugins = pluginReact.configs.all.plugins
71 |
72 | return [
73 | {
74 | name: 'antfu/react/setup',
75 | plugins: {
76 | 'react': plugins['@eslint-react'],
77 | 'react-dom': plugins['@eslint-react/dom'],
78 | 'react-hooks': pluginReactHooks,
79 | 'react-hooks-extra': plugins['@eslint-react/hooks-extra'],
80 | 'react-naming-convention': plugins['@eslint-react/naming-convention'],
81 | 'react-refresh': pluginReactRefresh,
82 | 'react-web-api': plugins['@eslint-react/web-api'],
83 | },
84 | },
85 | {
86 | files,
87 | languageOptions: {
88 | parserOptions: {
89 | ecmaFeatures: {
90 | jsx: true,
91 | },
92 | },
93 | sourceType: 'module',
94 | },
95 | name: 'antfu/react/rules',
96 | rules: {
97 | // recommended rules from eslint-plugin-react-x https://eslint-react.xyz/docs/rules/overview#core-rules
98 | 'react/jsx-no-duplicate-props': 'warn',
99 | 'react/jsx-uses-vars': 'warn',
100 | 'react/no-access-state-in-setstate': 'error',
101 | 'react/no-array-index-key': 'warn',
102 | 'react/no-children-count': 'warn',
103 | 'react/no-children-for-each': 'warn',
104 | 'react/no-children-map': 'warn',
105 | 'react/no-children-only': 'warn',
106 | 'react/no-children-to-array': 'warn',
107 | 'react/no-clone-element': 'warn',
108 | 'react/no-comment-textnodes': 'warn',
109 | 'react/no-component-will-mount': 'error',
110 | 'react/no-component-will-receive-props': 'error',
111 | 'react/no-component-will-update': 'error',
112 | 'react/no-context-provider': 'warn',
113 | 'react/no-create-ref': 'error',
114 | 'react/no-default-props': 'error',
115 | 'react/no-direct-mutation-state': 'error',
116 | 'react/no-duplicate-key': 'warn',
117 | 'react/no-forward-ref': 'warn',
118 | 'react/no-implicit-key': 'warn',
119 | 'react/no-missing-key': 'error',
120 | 'react/no-nested-component-definitions': 'error',
121 | 'react/no-prop-types': 'error',
122 | 'react/no-redundant-should-component-update': 'error',
123 | 'react/no-set-state-in-component-did-mount': 'warn',
124 | 'react/no-set-state-in-component-did-update': 'warn',
125 | 'react/no-set-state-in-component-will-update': 'warn',
126 | 'react/no-string-refs': 'error',
127 | 'react/no-unsafe-component-will-mount': 'warn',
128 | 'react/no-unsafe-component-will-receive-props': 'warn',
129 | 'react/no-unsafe-component-will-update': 'warn',
130 | 'react/no-unstable-context-value': 'warn',
131 | 'react/no-unstable-default-props': 'warn',
132 | 'react/no-unused-class-component-members': 'warn',
133 | 'react/no-unused-state': 'warn',
134 | 'react/no-use-context': 'warn',
135 | 'react/no-useless-forward-ref': 'warn',
136 |
137 | // recommended rules from eslint-plugin-react-dom https://eslint-react.xyz/docs/rules/overview#dom-rules
138 | 'react-dom/no-dangerously-set-innerhtml': 'warn',
139 | 'react-dom/no-dangerously-set-innerhtml-with-children': 'error',
140 | 'react-dom/no-find-dom-node': 'error',
141 | 'react-dom/no-flush-sync': 'error',
142 | 'react-dom/no-hydrate': 'error',
143 | 'react-dom/no-missing-button-type': 'warn',
144 | 'react-dom/no-missing-iframe-sandbox': 'warn',
145 | 'react-dom/no-namespace': 'error',
146 | 'react-dom/no-render': 'error',
147 | 'react-dom/no-render-return-value': 'error',
148 | 'react-dom/no-script-url': 'warn',
149 | 'react-dom/no-unsafe-iframe-sandbox': 'warn',
150 | 'react-dom/no-unsafe-target-blank': 'warn',
151 | 'react-dom/no-use-form-state': 'error',
152 | 'react-dom/no-void-elements-with-children': 'error',
153 |
154 | // recommended rules eslint-plugin-react-hooks https://github.com/facebook/react/tree/main/packages/eslint-plugin-react-hooks/src/rules
155 | 'react-hooks/exhaustive-deps': 'warn',
156 | 'react-hooks/rules-of-hooks': 'error',
157 |
158 | // recommended rules from eslint-plugin-react-hooks-extra https://eslint-react.xyz/docs/rules/overview#hooks-extra-rules
159 | 'react-hooks-extra/no-direct-set-state-in-use-effect': 'warn',
160 | 'react-hooks-extra/no-unnecessary-use-prefix': 'warn',
161 | 'react-hooks-extra/prefer-use-state-lazy-initialization': 'warn',
162 |
163 | // recommended rules from eslint-plugin-react-web-api https://eslint-react.xyz/docs/rules/overview#web-api-rules
164 | 'react-web-api/no-leaked-event-listener': 'warn',
165 | 'react-web-api/no-leaked-interval': 'warn',
166 | 'react-web-api/no-leaked-resize-observer': 'warn',
167 | 'react-web-api/no-leaked-timeout': 'warn',
168 |
169 | // preconfigured rules from eslint-plugin-react-refresh https://github.com/ArnaudBarre/eslint-plugin-react-refresh/tree/main/src
170 | 'react-refresh/only-export-components': [
171 | 'warn',
172 | {
173 | allowConstantExport: isAllowConstantExport,
174 | allowExportNames: [
175 | ...(isUsingNext
176 | ? [
177 | 'dynamic',
178 | 'dynamicParams',
179 | 'revalidate',
180 | 'fetchCache',
181 | 'runtime',
182 | 'preferredRegion',
183 | 'maxDuration',
184 | 'config',
185 | 'generateStaticParams',
186 | 'metadata',
187 | 'generateMetadata',
188 | 'viewport',
189 | 'generateViewport',
190 | ]
191 | : []),
192 | ...(isUsingRemix || isUsingReactRouter
193 | ? [
194 | 'meta',
195 | 'links',
196 | 'headers',
197 | 'loader',
198 | 'action',
199 | ]
200 | : []),
201 | ],
202 | },
203 | ],
204 |
205 | // overrides
206 | ...overrides,
207 | },
208 | },
209 | ...isTypeAware
210 | ? [{
211 | files: filesTypeAware,
212 | ignores: ignoresTypeAware,
213 | name: 'antfu/react/type-aware-rules',
214 | rules: {
215 | ...typeAwareRules,
216 | },
217 | }]
218 | : [],
219 | ]
220 | }
221 |
--------------------------------------------------------------------------------
/src/configs/regexp.ts:
--------------------------------------------------------------------------------
1 | import type { OptionsOverrides, OptionsRegExp, TypedFlatConfigItem } from '../types'
2 |
3 | import { configs } from 'eslint-plugin-regexp'
4 |
5 | export async function regexp(
6 | options: OptionsRegExp & OptionsOverrides = {},
7 | ): Promise {
8 | const config = configs['flat/recommended'] as TypedFlatConfigItem
9 |
10 | const rules = {
11 | ...config.rules,
12 | }
13 |
14 | if (options.level === 'warn') {
15 | for (const key in rules) {
16 | if (rules[key] === 'error')
17 | rules[key] = 'warn'
18 | }
19 | }
20 |
21 | return [
22 | {
23 | ...config,
24 | name: 'antfu/regexp/rules',
25 | rules: {
26 | ...rules,
27 | ...options.overrides,
28 | },
29 | },
30 | ]
31 | }
32 |
--------------------------------------------------------------------------------
/src/configs/solid.ts:
--------------------------------------------------------------------------------
1 | import type { OptionsFiles, OptionsHasTypeScript, OptionsOverrides, OptionsTypeScriptWithTypes, TypedFlatConfigItem } from '../types'
2 | import { GLOB_JSX, GLOB_TSX } from '../globs'
3 |
4 | import { ensurePackages, interopDefault, toArray } from '../utils'
5 |
6 | export async function solid(
7 | options: OptionsHasTypeScript & OptionsOverrides & OptionsFiles & OptionsTypeScriptWithTypes = {},
8 | ): Promise {
9 | const {
10 | files = [GLOB_JSX, GLOB_TSX],
11 | overrides = {},
12 | typescript = true,
13 | } = options
14 |
15 | await ensurePackages([
16 | 'eslint-plugin-solid',
17 | ])
18 |
19 | const tsconfigPath = options?.tsconfigPath
20 | ? toArray(options.tsconfigPath)
21 | : undefined
22 | const isTypeAware = !!tsconfigPath
23 |
24 | const [
25 | pluginSolid,
26 | parserTs,
27 | ] = await Promise.all([
28 | interopDefault(import('eslint-plugin-solid')),
29 | interopDefault(import('@typescript-eslint/parser')),
30 | ] as const)
31 |
32 | return [
33 | {
34 | name: 'antfu/solid/setup',
35 | plugins: {
36 | solid: pluginSolid,
37 | },
38 | },
39 | {
40 | files,
41 | languageOptions: {
42 | parser: parserTs,
43 | parserOptions: {
44 | ecmaFeatures: {
45 | jsx: true,
46 | },
47 | ...isTypeAware ? { project: tsconfigPath } : {},
48 | },
49 | sourceType: 'module',
50 | },
51 | name: 'antfu/solid/rules',
52 | rules: {
53 | // reactivity
54 | 'solid/components-return-once': 'warn',
55 | 'solid/event-handlers': ['error', {
56 | // if true, don't warn on ambiguously named event handlers like `onclick` or `onchange`
57 | ignoreCase: false,
58 | // if true, warn when spreading event handlers onto JSX. Enable for Solid < v1.6.
59 | warnOnSpread: false,
60 | }],
61 | // these rules are mostly style suggestions
62 | 'solid/imports': 'error',
63 | // identifier usage is important
64 | 'solid/jsx-no-duplicate-props': 'error',
65 | 'solid/jsx-no-script-url': 'error',
66 | 'solid/jsx-no-undef': 'error',
67 | 'solid/jsx-uses-vars': 'error',
68 | 'solid/no-destructure': 'error',
69 | // security problems
70 | 'solid/no-innerhtml': ['error', { allowStatic: true }],
71 | 'solid/no-react-deps': 'error',
72 | 'solid/no-react-specific-props': 'error',
73 | 'solid/no-unknown-namespaces': 'error',
74 | 'solid/prefer-for': 'error',
75 | 'solid/reactivity': 'warn',
76 | 'solid/self-closing-comp': 'error',
77 | 'solid/style-prop': ['error', { styleProps: ['style', 'css'] }],
78 | ...typescript
79 | ? {
80 | 'solid/jsx-no-undef': ['error', { typescriptEnabled: true }],
81 | 'solid/no-unknown-namespaces': 'off',
82 | }
83 | : {},
84 | // overrides
85 | ...overrides,
86 | },
87 | },
88 | ]
89 | }
90 |
--------------------------------------------------------------------------------
/src/configs/sort.ts:
--------------------------------------------------------------------------------
1 | import type { TypedFlatConfigItem } from '../types'
2 |
3 | /**
4 | * Sort package.json
5 | *
6 | * Requires `jsonc` config
7 | */
8 | export async function sortPackageJson(): Promise {
9 | return [
10 | {
11 | files: ['**/package.json'],
12 | name: 'antfu/sort/package-json',
13 | rules: {
14 | 'jsonc/sort-array-values': [
15 | 'error',
16 | {
17 | order: { type: 'asc' },
18 | pathPattern: '^files$',
19 | },
20 | ],
21 | 'jsonc/sort-keys': [
22 | 'error',
23 | {
24 | order: [
25 | 'publisher',
26 | 'name',
27 | 'displayName',
28 | 'type',
29 | 'version',
30 | 'private',
31 | 'packageManager',
32 | 'description',
33 | 'author',
34 | 'contributors',
35 | 'license',
36 | 'funding',
37 | 'homepage',
38 | 'repository',
39 | 'bugs',
40 | 'keywords',
41 | 'categories',
42 | 'sideEffects',
43 | 'imports',
44 | 'exports',
45 | 'main',
46 | 'module',
47 | 'unpkg',
48 | 'jsdelivr',
49 | 'types',
50 | 'typesVersions',
51 | 'bin',
52 | 'icon',
53 | 'files',
54 | 'engines',
55 | 'activationEvents',
56 | 'contributes',
57 | 'scripts',
58 | 'peerDependencies',
59 | 'peerDependenciesMeta',
60 | 'dependencies',
61 | 'optionalDependencies',
62 | 'devDependencies',
63 | 'pnpm',
64 | 'overrides',
65 | 'resolutions',
66 | 'husky',
67 | 'simple-git-hooks',
68 | 'lint-staged',
69 | 'eslintConfig',
70 | ],
71 | pathPattern: '^$',
72 | },
73 | {
74 | order: { type: 'asc' },
75 | pathPattern: '^(?:dev|peer|optional|bundled)?[Dd]ependencies(Meta)?$',
76 | },
77 | {
78 | order: { type: 'asc' },
79 | pathPattern: '^(?:resolutions|overrides|pnpm.overrides)$',
80 | },
81 | {
82 | order: [
83 | 'types',
84 | 'import',
85 | 'require',
86 | 'default',
87 | ],
88 | pathPattern: '^exports.*$',
89 | },
90 | {
91 | order: [
92 | // client hooks only
93 | 'pre-commit',
94 | 'prepare-commit-msg',
95 | 'commit-msg',
96 | 'post-commit',
97 | 'pre-rebase',
98 | 'post-rewrite',
99 | 'post-checkout',
100 | 'post-merge',
101 | 'pre-push',
102 | 'pre-auto-gc',
103 | ],
104 | pathPattern: '^(?:gitHooks|husky|simple-git-hooks)$',
105 | },
106 | ],
107 | },
108 | },
109 | ]
110 | }
111 | /**
112 | * Sort tsconfig.json
113 | *
114 | * Requires `jsonc` config
115 | */
116 |
117 | export function sortTsconfig(): TypedFlatConfigItem[] {
118 | return [
119 | {
120 | files: ['**/tsconfig.json', '**/tsconfig.*.json'],
121 | name: 'antfu/sort/tsconfig-json',
122 | rules: {
123 | 'jsonc/sort-keys': [
124 | 'error',
125 | {
126 | order: [
127 | 'extends',
128 | 'compilerOptions',
129 | 'references',
130 | 'files',
131 | 'include',
132 | 'exclude',
133 | ],
134 | pathPattern: '^$',
135 | },
136 | {
137 | order: [
138 | /* Projects */
139 | 'incremental',
140 | 'composite',
141 | 'tsBuildInfoFile',
142 | 'disableSourceOfProjectReferenceRedirect',
143 | 'disableSolutionSearching',
144 | 'disableReferencedProjectLoad',
145 | /* Language and Environment */
146 | 'target',
147 | 'jsx',
148 | 'jsxFactory',
149 | 'jsxFragmentFactory',
150 | 'jsxImportSource',
151 | 'lib',
152 | 'moduleDetection',
153 | 'noLib',
154 | 'reactNamespace',
155 | 'useDefineForClassFields',
156 | 'emitDecoratorMetadata',
157 | 'experimentalDecorators',
158 | 'libReplacement',
159 | /* Modules */
160 | 'baseUrl',
161 | 'rootDir',
162 | 'rootDirs',
163 | 'customConditions',
164 | 'module',
165 | 'moduleResolution',
166 | 'moduleSuffixes',
167 | 'noResolve',
168 | 'paths',
169 | 'resolveJsonModule',
170 | 'resolvePackageJsonExports',
171 | 'resolvePackageJsonImports',
172 | 'typeRoots',
173 | 'types',
174 | 'allowArbitraryExtensions',
175 | 'allowImportingTsExtensions',
176 | 'allowUmdGlobalAccess',
177 | /* JavaScript Support */
178 | 'allowJs',
179 | 'checkJs',
180 | 'maxNodeModuleJsDepth',
181 | /* Type Checking */
182 | 'strict',
183 | 'strictBindCallApply',
184 | 'strictFunctionTypes',
185 | 'strictNullChecks',
186 | 'strictPropertyInitialization',
187 | 'allowUnreachableCode',
188 | 'allowUnusedLabels',
189 | 'alwaysStrict',
190 | 'exactOptionalPropertyTypes',
191 | 'noFallthroughCasesInSwitch',
192 | 'noImplicitAny',
193 | 'noImplicitOverride',
194 | 'noImplicitReturns',
195 | 'noImplicitThis',
196 | 'noPropertyAccessFromIndexSignature',
197 | 'noUncheckedIndexedAccess',
198 | 'noUnusedLocals',
199 | 'noUnusedParameters',
200 | 'useUnknownInCatchVariables',
201 | /* Emit */
202 | 'declaration',
203 | 'declarationDir',
204 | 'declarationMap',
205 | 'downlevelIteration',
206 | 'emitBOM',
207 | 'emitDeclarationOnly',
208 | 'importHelpers',
209 | 'importsNotUsedAsValues',
210 | 'inlineSourceMap',
211 | 'inlineSources',
212 | 'mapRoot',
213 | 'newLine',
214 | 'noEmit',
215 | 'noEmitHelpers',
216 | 'noEmitOnError',
217 | 'outDir',
218 | 'outFile',
219 | 'preserveConstEnums',
220 | 'preserveValueImports',
221 | 'removeComments',
222 | 'sourceMap',
223 | 'sourceRoot',
224 | 'stripInternal',
225 | /* Interop Constraints */
226 | 'allowSyntheticDefaultImports',
227 | 'esModuleInterop',
228 | 'forceConsistentCasingInFileNames',
229 | 'isolatedDeclarations',
230 | 'isolatedModules',
231 | 'preserveSymlinks',
232 | 'verbatimModuleSyntax',
233 | 'erasableSyntaxOnly',
234 | /* Completeness */
235 | 'skipDefaultLibCheck',
236 | 'skipLibCheck',
237 | ],
238 | pathPattern: '^compilerOptions$',
239 | },
240 | ],
241 | },
242 | },
243 | ]
244 | }
245 |
--------------------------------------------------------------------------------
/src/configs/stylistic.ts:
--------------------------------------------------------------------------------
1 | import type { OptionsOverrides, StylisticConfig, TypedFlatConfigItem } from '../types'
2 |
3 | import { pluginAntfu } from '../plugins'
4 | import { interopDefault } from '../utils'
5 |
6 | export const StylisticConfigDefaults: StylisticConfig = {
7 | indent: 2,
8 | jsx: true,
9 | quotes: 'single',
10 | semi: false,
11 | }
12 |
13 | export interface StylisticOptions extends StylisticConfig, OptionsOverrides {
14 | lessOpinionated?: boolean
15 | }
16 |
17 | export async function stylistic(
18 | options: StylisticOptions = {},
19 | ): Promise {
20 | const {
21 | indent,
22 | jsx,
23 | lessOpinionated = false,
24 | overrides = {},
25 | quotes,
26 | semi,
27 | } = {
28 | ...StylisticConfigDefaults,
29 | ...options,
30 | }
31 |
32 | const pluginStylistic = await interopDefault(import('@stylistic/eslint-plugin'))
33 |
34 | const config = pluginStylistic.configs.customize({
35 | indent,
36 | jsx,
37 | pluginName: 'style',
38 | quotes,
39 | semi,
40 | })
41 |
42 | return [
43 | {
44 | name: 'antfu/stylistic/rules',
45 | plugins: {
46 | antfu: pluginAntfu,
47 | style: pluginStylistic,
48 | },
49 | rules: {
50 | ...config.rules,
51 |
52 | 'antfu/consistent-chaining': 'error',
53 | 'antfu/consistent-list-newline': 'error',
54 |
55 | ...(lessOpinionated
56 | ? {
57 | curly: ['error', 'all'],
58 | }
59 | : {
60 | 'antfu/curly': 'error',
61 | 'antfu/if-newline': 'error',
62 | 'antfu/top-level-function': 'error',
63 | }
64 | ),
65 |
66 | 'style/generator-star-spacing': ['error', { after: true, before: false }],
67 | 'style/yield-star-spacing': ['error', { after: true, before: false }],
68 |
69 | ...overrides,
70 | },
71 | },
72 | ]
73 | }
74 |
--------------------------------------------------------------------------------
/src/configs/svelte.ts:
--------------------------------------------------------------------------------
1 | import type { OptionsFiles, OptionsHasTypeScript, OptionsOverrides, OptionsStylistic, TypedFlatConfigItem } from '../types'
2 |
3 | import { GLOB_SVELTE } from '../globs'
4 | import { ensurePackages, interopDefault } from '../utils'
5 |
6 | export async function svelte(
7 | options: OptionsHasTypeScript & OptionsOverrides & OptionsStylistic & OptionsFiles = {},
8 | ): Promise {
9 | const {
10 | files = [GLOB_SVELTE],
11 | overrides = {},
12 | stylistic = true,
13 | } = options
14 |
15 | const {
16 | indent = 2,
17 | quotes = 'single',
18 | } = typeof stylistic === 'boolean' ? {} : stylistic
19 |
20 | await ensurePackages([
21 | 'eslint-plugin-svelte',
22 | ])
23 |
24 | const [
25 | pluginSvelte,
26 | parserSvelte,
27 | ] = await Promise.all([
28 | interopDefault(import('eslint-plugin-svelte')),
29 | interopDefault(import('svelte-eslint-parser')),
30 | ] as const)
31 |
32 | return [
33 | {
34 | name: 'antfu/svelte/setup',
35 | plugins: {
36 | svelte: pluginSvelte,
37 | },
38 | },
39 | {
40 | files,
41 | languageOptions: {
42 | parser: parserSvelte,
43 | parserOptions: {
44 | extraFileExtensions: ['.svelte'],
45 | parser: options.typescript
46 | ? await interopDefault(import('@typescript-eslint/parser')) as any
47 | : null,
48 | },
49 | },
50 | name: 'antfu/svelte/rules',
51 | processor: pluginSvelte.processors['.svelte'],
52 | rules: {
53 | 'import/no-mutable-exports': 'off',
54 | 'no-undef': 'off', // incompatible with most recent (attribute-form) generic types RFC
55 | 'no-unused-vars': ['error', {
56 | args: 'none',
57 | caughtErrors: 'none',
58 | ignoreRestSiblings: true,
59 | vars: 'all',
60 | varsIgnorePattern: '^(\\$\\$Props$|\\$\\$Events$|\\$\\$Slots$)',
61 | }],
62 |
63 | 'svelte/comment-directive': 'error',
64 | 'svelte/no-at-debug-tags': 'warn',
65 | 'svelte/no-at-html-tags': 'error',
66 | 'svelte/no-dupe-else-if-blocks': 'error',
67 | 'svelte/no-dupe-style-properties': 'error',
68 | 'svelte/no-dupe-use-directives': 'error',
69 | 'svelte/no-dynamic-slot-name': 'error',
70 | 'svelte/no-export-load-in-svelte-module-in-kit-pages': 'error',
71 | 'svelte/no-inner-declarations': 'error',
72 | 'svelte/no-not-function-handler': 'error',
73 | 'svelte/no-object-in-text-mustaches': 'error',
74 | 'svelte/no-reactive-functions': 'error',
75 | 'svelte/no-reactive-literals': 'error',
76 | 'svelte/no-shorthand-style-property-overrides': 'error',
77 | 'svelte/no-unknown-style-directive-property': 'error',
78 | 'svelte/no-unused-svelte-ignore': 'error',
79 | 'svelte/no-useless-mustaches': 'error',
80 | 'svelte/require-store-callbacks-use-set-param': 'error',
81 | 'svelte/system': 'error',
82 | 'svelte/valid-each-key': 'error',
83 |
84 | 'unused-imports/no-unused-vars': [
85 | 'error',
86 | {
87 | args: 'after-used',
88 | argsIgnorePattern: '^_',
89 | vars: 'all',
90 | varsIgnorePattern: '^(_|\\$\\$Props$|\\$\\$Events$|\\$\\$Slots$)',
91 | },
92 | ],
93 |
94 | ...stylistic
95 | ? {
96 | 'style/indent': 'off', // superseded by svelte/indent
97 | 'style/no-trailing-spaces': 'off', // superseded by svelte/no-trailing-spaces
98 | 'svelte/derived-has-same-inputs-outputs': 'error',
99 | 'svelte/html-closing-bracket-spacing': 'error',
100 | 'svelte/html-quotes': ['error', { prefer: quotes === 'backtick' ? 'double' : quotes }],
101 | 'svelte/indent': ['error', { alignAttributesVertically: true, indent }],
102 | 'svelte/mustache-spacing': 'error',
103 | 'svelte/no-spaces-around-equal-signs-in-attribute': 'error',
104 | 'svelte/no-trailing-spaces': 'error',
105 | 'svelte/spaced-html-comment': 'error',
106 | }
107 | : {},
108 |
109 | ...overrides,
110 | },
111 | },
112 | ]
113 | }
114 |
--------------------------------------------------------------------------------
/src/configs/test.ts:
--------------------------------------------------------------------------------
1 | import type { OptionsFiles, OptionsIsInEditor, OptionsOverrides, TypedFlatConfigItem } from '../types'
2 |
3 | import { GLOB_TESTS } from '../globs'
4 | import { interopDefault } from '../utils'
5 |
6 | // Hold the reference so we don't redeclare the plugin on each call
7 | let _pluginTest: any
8 |
9 | export async function test(
10 | options: OptionsFiles & OptionsIsInEditor & OptionsOverrides = {},
11 | ): Promise {
12 | const {
13 | files = GLOB_TESTS,
14 | isInEditor = false,
15 | overrides = {},
16 | } = options
17 |
18 | const [
19 | pluginVitest,
20 | pluginNoOnlyTests,
21 | ] = await Promise.all([
22 | interopDefault(import('@vitest/eslint-plugin')),
23 | // @ts-expect-error missing types
24 | interopDefault(import('eslint-plugin-no-only-tests')),
25 | ] as const)
26 |
27 | _pluginTest = _pluginTest || {
28 | ...pluginVitest,
29 | rules: {
30 | ...pluginVitest.rules,
31 | // extend `test/no-only-tests` rule
32 | ...pluginNoOnlyTests.rules,
33 | },
34 | }
35 |
36 | return [
37 | {
38 | name: 'antfu/test/setup',
39 | plugins: {
40 | test: _pluginTest,
41 | },
42 | },
43 | {
44 | files,
45 | name: 'antfu/test/rules',
46 | rules: {
47 | 'test/consistent-test-it': ['error', { fn: 'it', withinDescribe: 'it' }],
48 | 'test/no-identical-title': 'error',
49 | 'test/no-import-node-test': 'error',
50 | 'test/no-only-tests': isInEditor ? 'warn' : 'error',
51 |
52 | 'test/prefer-hooks-in-order': 'error',
53 | 'test/prefer-lowercase-title': 'error',
54 |
55 | // Disables
56 | ...{
57 | 'antfu/no-top-level-await': 'off',
58 | 'no-unused-expressions': 'off',
59 | 'node/prefer-global/process': 'off',
60 | 'ts/explicit-function-return-type': 'off',
61 | },
62 |
63 | ...overrides,
64 | },
65 | },
66 | ]
67 | }
68 |
--------------------------------------------------------------------------------
/src/configs/toml.ts:
--------------------------------------------------------------------------------
1 | import type { OptionsFiles, OptionsOverrides, OptionsStylistic, TypedFlatConfigItem } from '../types'
2 |
3 | import { GLOB_TOML } from '../globs'
4 | import { interopDefault } from '../utils'
5 |
6 | export async function toml(
7 | options: OptionsOverrides & OptionsStylistic & OptionsFiles = {},
8 | ): Promise {
9 | const {
10 | files = [GLOB_TOML],
11 | overrides = {},
12 | stylistic = true,
13 | } = options
14 |
15 | const {
16 | indent = 2,
17 | } = typeof stylistic === 'boolean' ? {} : stylistic
18 |
19 | const [
20 | pluginToml,
21 | parserToml,
22 | ] = await Promise.all([
23 | interopDefault(import('eslint-plugin-toml')),
24 | interopDefault(import('toml-eslint-parser')),
25 | ] as const)
26 |
27 | return [
28 | {
29 | name: 'antfu/toml/setup',
30 | plugins: {
31 | toml: pluginToml,
32 | },
33 | },
34 | {
35 | files,
36 | languageOptions: {
37 | parser: parserToml,
38 | },
39 | name: 'antfu/toml/rules',
40 | rules: {
41 | 'style/spaced-comment': 'off',
42 |
43 | 'toml/comma-style': 'error',
44 | 'toml/keys-order': 'error',
45 | 'toml/no-space-dots': 'error',
46 | 'toml/no-unreadable-number-separator': 'error',
47 | 'toml/precision-of-fractional-seconds': 'error',
48 | 'toml/precision-of-integer': 'error',
49 | 'toml/tables-order': 'error',
50 |
51 | 'toml/vue-custom-block/no-parsing-error': 'error',
52 |
53 | ...stylistic
54 | ? {
55 | 'toml/array-bracket-newline': 'error',
56 | 'toml/array-bracket-spacing': 'error',
57 | 'toml/array-element-newline': 'error',
58 | 'toml/indent': ['error', indent === 'tab' ? 2 : indent],
59 | 'toml/inline-table-curly-spacing': 'error',
60 | 'toml/key-spacing': 'error',
61 | 'toml/padding-line-between-pairs': 'error',
62 | 'toml/padding-line-between-tables': 'error',
63 | 'toml/quoted-keys': 'error',
64 | 'toml/spaced-comment': 'error',
65 | 'toml/table-bracket-spacing': 'error',
66 | }
67 | : {},
68 |
69 | ...overrides,
70 | },
71 | },
72 | ]
73 | }
74 |
--------------------------------------------------------------------------------
/src/configs/typescript.ts:
--------------------------------------------------------------------------------
1 | import type {
2 | OptionsComponentExts,
3 | OptionsFiles,
4 | OptionsOverrides,
5 | OptionsProjectType,
6 | OptionsTypeScriptParserOptions,
7 | OptionsTypeScriptWithTypes,
8 | TypedFlatConfigItem,
9 | } from '../types'
10 |
11 | import process from 'node:process'
12 | import { GLOB_ASTRO_TS, GLOB_MARKDOWN, GLOB_TS, GLOB_TSX } from '../globs'
13 | import { pluginAntfu } from '../plugins'
14 | import { interopDefault, renameRules } from '../utils'
15 |
16 | export async function typescript(
17 | options: OptionsFiles & OptionsComponentExts & OptionsOverrides & OptionsTypeScriptWithTypes & OptionsTypeScriptParserOptions & OptionsProjectType = {},
18 | ): Promise {
19 | const {
20 | componentExts = [],
21 | overrides = {},
22 | overridesTypeAware = {},
23 | parserOptions = {},
24 | type = 'app',
25 | } = options
26 |
27 | const files = options.files ?? [
28 | GLOB_TS,
29 | GLOB_TSX,
30 | ...componentExts.map(ext => `**/*.${ext}`),
31 | ]
32 |
33 | const filesTypeAware = options.filesTypeAware ?? [GLOB_TS, GLOB_TSX]
34 | const ignoresTypeAware = options.ignoresTypeAware ?? [
35 | `${GLOB_MARKDOWN}/**`,
36 | GLOB_ASTRO_TS,
37 | ]
38 | const tsconfigPath = options?.tsconfigPath
39 | ? options.tsconfigPath
40 | : undefined
41 | const isTypeAware = !!tsconfigPath
42 |
43 | const typeAwareRules: TypedFlatConfigItem['rules'] = {
44 | 'dot-notation': 'off',
45 | 'no-implied-eval': 'off',
46 | 'ts/await-thenable': 'error',
47 | 'ts/dot-notation': ['error', { allowKeywords: true }],
48 | 'ts/no-floating-promises': 'error',
49 | 'ts/no-for-in-array': 'error',
50 | 'ts/no-implied-eval': 'error',
51 | 'ts/no-misused-promises': 'error',
52 | 'ts/no-unnecessary-type-assertion': 'error',
53 | 'ts/no-unsafe-argument': 'error',
54 | 'ts/no-unsafe-assignment': 'error',
55 | 'ts/no-unsafe-call': 'error',
56 | 'ts/no-unsafe-member-access': 'error',
57 | 'ts/no-unsafe-return': 'error',
58 | 'ts/promise-function-async': 'error',
59 | 'ts/restrict-plus-operands': 'error',
60 | 'ts/restrict-template-expressions': 'error',
61 | 'ts/return-await': ['error', 'in-try-catch'],
62 | 'ts/strict-boolean-expressions': ['error', { allowNullableBoolean: true, allowNullableObject: true }],
63 | 'ts/switch-exhaustiveness-check': 'error',
64 | 'ts/unbound-method': 'error',
65 | }
66 |
67 | const [
68 | pluginTs,
69 | parserTs,
70 | ] = await Promise.all([
71 | interopDefault(import('@typescript-eslint/eslint-plugin')),
72 | interopDefault(import('@typescript-eslint/parser')),
73 | ] as const)
74 |
75 | function makeParser(typeAware: boolean, files: string[], ignores?: string[]): TypedFlatConfigItem {
76 | return {
77 | files,
78 | ...ignores ? { ignores } : {},
79 | languageOptions: {
80 | parser: parserTs,
81 | parserOptions: {
82 | extraFileExtensions: componentExts.map(ext => `.${ext}`),
83 | sourceType: 'module',
84 | ...typeAware
85 | ? {
86 | projectService: {
87 | allowDefaultProject: ['./*.js'],
88 | defaultProject: tsconfigPath,
89 | },
90 | tsconfigRootDir: process.cwd(),
91 | }
92 | : {},
93 | ...parserOptions as any,
94 | },
95 | },
96 | name: `antfu/typescript/${typeAware ? 'type-aware-parser' : 'parser'}`,
97 | }
98 | }
99 |
100 | return [
101 | {
102 | // Install the plugins without globs, so they can be configured separately.
103 | name: 'antfu/typescript/setup',
104 | plugins: {
105 | antfu: pluginAntfu,
106 | ts: pluginTs as any,
107 | },
108 | },
109 | // assign type-aware parser for type-aware files and type-unaware parser for the rest
110 | ...isTypeAware
111 | ? [
112 | makeParser(false, files),
113 | makeParser(true, filesTypeAware, ignoresTypeAware),
114 | ]
115 | : [
116 | makeParser(false, files),
117 | ],
118 | {
119 | files,
120 | name: 'antfu/typescript/rules',
121 | rules: {
122 | ...renameRules(
123 | pluginTs.configs['eslint-recommended'].overrides![0].rules!,
124 | { '@typescript-eslint': 'ts' },
125 | ),
126 | ...renameRules(
127 | pluginTs.configs.strict.rules!,
128 | { '@typescript-eslint': 'ts' },
129 | ),
130 | 'no-dupe-class-members': 'off',
131 | 'no-redeclare': 'off',
132 | 'no-use-before-define': 'off',
133 | 'no-useless-constructor': 'off',
134 | 'ts/ban-ts-comment': ['error', { 'ts-expect-error': 'allow-with-description' }],
135 | 'ts/consistent-type-definitions': ['error', 'interface'],
136 | 'ts/consistent-type-imports': ['error', {
137 | disallowTypeAnnotations: false,
138 | fixStyle: 'separate-type-imports',
139 | prefer: 'type-imports',
140 | }],
141 |
142 | 'ts/method-signature-style': ['error', 'property'], // https://www.totaltypescript.com/method-shorthand-syntax-considered-harmful
143 | 'ts/no-dupe-class-members': 'error',
144 | 'ts/no-dynamic-delete': 'off',
145 | 'ts/no-empty-object-type': ['error', { allowInterfaces: 'always' }],
146 | 'ts/no-explicit-any': 'off',
147 | 'ts/no-extraneous-class': 'off',
148 | 'ts/no-import-type-side-effects': 'error',
149 | 'ts/no-invalid-void-type': 'off',
150 | 'ts/no-non-null-assertion': 'off',
151 | 'ts/no-redeclare': ['error', { builtinGlobals: false }],
152 | 'ts/no-require-imports': 'error',
153 | 'ts/no-unused-expressions': ['error', {
154 | allowShortCircuit: true,
155 | allowTaggedTemplates: true,
156 | allowTernary: true,
157 | }],
158 | 'ts/no-unused-vars': 'off',
159 | 'ts/no-use-before-define': ['error', { classes: false, functions: false, variables: true }],
160 | 'ts/no-useless-constructor': 'off',
161 | 'ts/no-wrapper-object-types': 'error',
162 | 'ts/triple-slash-reference': 'off',
163 | 'ts/unified-signatures': 'off',
164 |
165 | ...(type === 'lib'
166 | ? {
167 | 'ts/explicit-function-return-type': ['error', {
168 | allowExpressions: true,
169 | allowHigherOrderFunctions: true,
170 | allowIIFEs: true,
171 | }],
172 | }
173 | : {}
174 | ),
175 | ...overrides,
176 | },
177 | },
178 | ...isTypeAware
179 | ? [{
180 | files: filesTypeAware,
181 | ignores: ignoresTypeAware,
182 | name: 'antfu/typescript/rules-type-aware',
183 | rules: {
184 | ...typeAwareRules,
185 | ...overridesTypeAware,
186 | },
187 | }]
188 | : [],
189 | ]
190 | }
191 |
--------------------------------------------------------------------------------
/src/configs/unicorn.ts:
--------------------------------------------------------------------------------
1 | import type { OptionsUnicorn, TypedFlatConfigItem } from '../types'
2 |
3 | import { pluginUnicorn } from '../plugins'
4 |
5 | export async function unicorn(options: OptionsUnicorn = {}): Promise {
6 | const {
7 | allRecommended = false,
8 | overrides = {},
9 | } = options
10 | return [
11 | {
12 | name: 'antfu/unicorn/rules',
13 | plugins: {
14 | unicorn: pluginUnicorn,
15 | },
16 | rules: {
17 | ...(allRecommended
18 | ? pluginUnicorn.configs.recommended.rules
19 | : {
20 | 'unicorn/consistent-empty-array-spread': 'error',
21 | 'unicorn/error-message': 'error',
22 | 'unicorn/escape-case': 'error',
23 | 'unicorn/new-for-builtins': 'error',
24 | 'unicorn/no-instanceof-builtins': 'error',
25 | 'unicorn/no-new-array': 'error',
26 | 'unicorn/no-new-buffer': 'error',
27 | 'unicorn/number-literal-case': 'error',
28 | 'unicorn/prefer-dom-node-text-content': 'error',
29 | 'unicorn/prefer-includes': 'error',
30 | 'unicorn/prefer-node-protocol': 'error',
31 | 'unicorn/prefer-number-properties': 'error',
32 | 'unicorn/prefer-string-starts-ends-with': 'error',
33 | 'unicorn/prefer-type-error': 'error',
34 | 'unicorn/throw-new-error': 'error',
35 | }),
36 | ...overrides,
37 | },
38 | },
39 | ]
40 | }
41 |
--------------------------------------------------------------------------------
/src/configs/unocss.ts:
--------------------------------------------------------------------------------
1 | import type { OptionsUnoCSS, TypedFlatConfigItem } from '../types'
2 |
3 | import { ensurePackages, interopDefault } from '../utils'
4 |
5 | export async function unocss(
6 | options: OptionsUnoCSS = {},
7 | ): Promise {
8 | const {
9 | attributify = true,
10 | strict = false,
11 | } = options
12 |
13 | await ensurePackages([
14 | '@unocss/eslint-plugin',
15 | ])
16 |
17 | const [
18 | pluginUnoCSS,
19 | ] = await Promise.all([
20 | interopDefault(import('@unocss/eslint-plugin')),
21 | ] as const)
22 |
23 | return [
24 | {
25 | name: 'antfu/unocss',
26 | plugins: {
27 | unocss: pluginUnoCSS,
28 | },
29 | rules: {
30 | 'unocss/order': 'warn',
31 | ...attributify
32 | ? {
33 | 'unocss/order-attributify': 'warn',
34 | }
35 | : {},
36 | ...strict
37 | ? {
38 | 'unocss/blocklist': 'error',
39 | }
40 | : {},
41 | },
42 | },
43 | ]
44 | }
45 |
--------------------------------------------------------------------------------
/src/configs/yaml.ts:
--------------------------------------------------------------------------------
1 | import type { OptionsFiles, OptionsOverrides, OptionsStylistic, TypedFlatConfigItem } from '../types'
2 | import { GLOB_YAML } from '../globs'
3 |
4 | import { interopDefault } from '../utils'
5 |
6 | export async function yaml(
7 | options: OptionsOverrides & OptionsStylistic & OptionsFiles = {},
8 | ): Promise {
9 | const {
10 | files = [GLOB_YAML],
11 | overrides = {},
12 | stylistic = true,
13 | } = options
14 |
15 | const {
16 | indent = 2,
17 | quotes = 'single',
18 | } = typeof stylistic === 'boolean' ? {} : stylistic
19 |
20 | const [
21 | pluginYaml,
22 | parserYaml,
23 | ] = await Promise.all([
24 | interopDefault(import('eslint-plugin-yml')),
25 | interopDefault(import('yaml-eslint-parser')),
26 | ] as const)
27 |
28 | return [
29 | {
30 | name: 'antfu/yaml/setup',
31 | plugins: {
32 | yaml: pluginYaml,
33 | },
34 | },
35 | {
36 | files,
37 | languageOptions: {
38 | parser: parserYaml,
39 | },
40 | name: 'antfu/yaml/rules',
41 | rules: {
42 | 'style/spaced-comment': 'off',
43 |
44 | 'yaml/block-mapping': 'error',
45 | 'yaml/block-sequence': 'error',
46 | 'yaml/no-empty-key': 'error',
47 | 'yaml/no-empty-sequence-entry': 'error',
48 | 'yaml/no-irregular-whitespace': 'error',
49 | 'yaml/plain-scalar': 'error',
50 |
51 | 'yaml/vue-custom-block/no-parsing-error': 'error',
52 |
53 | ...stylistic
54 | ? {
55 | 'yaml/block-mapping-question-indicator-newline': 'error',
56 | 'yaml/block-sequence-hyphen-indicator-newline': 'error',
57 | 'yaml/flow-mapping-curly-newline': 'error',
58 | 'yaml/flow-mapping-curly-spacing': 'error',
59 | 'yaml/flow-sequence-bracket-newline': 'error',
60 | 'yaml/flow-sequence-bracket-spacing': 'error',
61 | 'yaml/indent': ['error', indent === 'tab' ? 2 : indent],
62 | 'yaml/key-spacing': 'error',
63 | 'yaml/no-tab-indent': 'error',
64 | 'yaml/quotes': ['error', { avoidEscape: true, prefer: quotes === 'backtick' ? 'single' : quotes }],
65 | 'yaml/spaced-comment': 'error',
66 | }
67 | : {},
68 |
69 | ...overrides,
70 | },
71 | },
72 | {
73 | files: ['pnpm-workspace.yaml'],
74 | name: 'antfu/yaml/pnpm-workspace',
75 | rules: {
76 | 'yaml/sort-keys': [
77 | 'error',
78 | {
79 | order: [
80 | 'packages',
81 | 'overrides',
82 | 'patchedDependencies',
83 | 'hoistPattern',
84 | 'catalog',
85 | 'catalogs',
86 |
87 | 'allowedDeprecatedVersions',
88 | 'allowNonAppliedPatches',
89 | 'configDependencies',
90 | 'ignoredBuiltDependencies',
91 | 'ignoredOptionalDependencies',
92 | 'neverBuiltDependencies',
93 | 'onlyBuiltDependencies',
94 | 'onlyBuiltDependenciesFile',
95 | 'packageExtensions',
96 | 'peerDependencyRules',
97 | 'supportedArchitectures',
98 | ],
99 | pathPattern: '^$',
100 | },
101 | {
102 | order: { type: 'asc' },
103 | pathPattern: '.*',
104 | },
105 | ],
106 | },
107 | },
108 | ]
109 | }
110 |
--------------------------------------------------------------------------------
/src/globs.ts:
--------------------------------------------------------------------------------
1 | export const GLOB_SRC_EXT = '?([cm])[jt]s?(x)'
2 | export const GLOB_SRC = '**/*.?([cm])[jt]s?(x)'
3 |
4 | export const GLOB_JS = '**/*.?([cm])js'
5 | export const GLOB_JSX = '**/*.?([cm])jsx'
6 |
7 | export const GLOB_TS = '**/*.?([cm])ts'
8 | export const GLOB_TSX = '**/*.?([cm])tsx'
9 |
10 | export const GLOB_STYLE = '**/*.{c,le,sc}ss'
11 | export const GLOB_CSS = '**/*.css'
12 | export const GLOB_POSTCSS = '**/*.{p,post}css'
13 | export const GLOB_LESS = '**/*.less'
14 | export const GLOB_SCSS = '**/*.scss'
15 |
16 | export const GLOB_JSON = '**/*.json'
17 | export const GLOB_JSON5 = '**/*.json5'
18 | export const GLOB_JSONC = '**/*.jsonc'
19 |
20 | export const GLOB_MARKDOWN = '**/*.md'
21 | export const GLOB_MARKDOWN_IN_MARKDOWN = '**/*.md/*.md'
22 | export const GLOB_SVELTE = '**/*.svelte'
23 | export const GLOB_VUE = '**/*.vue'
24 | export const GLOB_YAML = '**/*.y?(a)ml'
25 | export const GLOB_TOML = '**/*.toml'
26 | export const GLOB_XML = '**/*.xml'
27 | export const GLOB_SVG = '**/*.svg'
28 | export const GLOB_HTML = '**/*.htm?(l)'
29 | export const GLOB_ASTRO = '**/*.astro'
30 | export const GLOB_ASTRO_TS = '**/*.astro/*.ts'
31 | export const GLOB_GRAPHQL = '**/*.{g,graph}ql'
32 |
33 | export const GLOB_MARKDOWN_CODE = `${GLOB_MARKDOWN}/${GLOB_SRC}`
34 |
35 | export const GLOB_TESTS = [
36 | `**/__tests__/**/*.${GLOB_SRC_EXT}`,
37 | `**/*.spec.${GLOB_SRC_EXT}`,
38 | `**/*.test.${GLOB_SRC_EXT}`,
39 | `**/*.bench.${GLOB_SRC_EXT}`,
40 | `**/*.benchmark.${GLOB_SRC_EXT}`,
41 | ]
42 |
43 | export const GLOB_ALL_SRC = [
44 | GLOB_SRC,
45 | GLOB_STYLE,
46 | GLOB_JSON,
47 | GLOB_JSON5,
48 | GLOB_MARKDOWN,
49 | GLOB_SVELTE,
50 | GLOB_VUE,
51 | GLOB_YAML,
52 | GLOB_XML,
53 | GLOB_HTML,
54 | ]
55 |
56 | export const GLOB_EXCLUDE = [
57 | '**/node_modules',
58 | '**/dist',
59 | '**/package-lock.json',
60 | '**/yarn.lock',
61 | '**/pnpm-lock.yaml',
62 | '**/bun.lockb',
63 |
64 | '**/output',
65 | '**/coverage',
66 | '**/temp',
67 | '**/.temp',
68 | '**/tmp',
69 | '**/.tmp',
70 | '**/.history',
71 | '**/.vitepress/cache',
72 | '**/.nuxt',
73 | '**/.next',
74 | '**/.svelte-kit',
75 | '**/.vercel',
76 | '**/.changeset',
77 | '**/.idea',
78 | '**/.cache',
79 | '**/.output',
80 | '**/.vite-inspect',
81 | '**/.yarn',
82 | '**/vite.config.*.timestamp-*',
83 |
84 | '**/CHANGELOG*.md',
85 | '**/*.min.*',
86 | '**/LICENSE*',
87 | '**/__snapshots__',
88 | '**/auto-import?(s).d.ts',
89 | '**/components.d.ts',
90 | ]
91 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | import { antfu } from './factory'
2 |
3 | export * from './configs'
4 | export * from './factory'
5 | export * from './globs'
6 | export * from './types'
7 | export * from './utils'
8 |
9 | export default antfu
10 |
--------------------------------------------------------------------------------
/src/plugins.ts:
--------------------------------------------------------------------------------
1 | // eslint-disable-next-line ts/ban-ts-comment
2 | // @ts-nocheck
3 |
4 | export { default as pluginComments } from '@eslint-community/eslint-plugin-eslint-comments'
5 | export { default as pluginAntfu } from 'eslint-plugin-antfu'
6 | export * as pluginImport from 'eslint-plugin-import-x'
7 | export { default as pluginNode } from 'eslint-plugin-n'
8 | export { default as pluginPerfectionist } from 'eslint-plugin-perfectionist'
9 | export { default as pluginUnicorn } from 'eslint-plugin-unicorn'
10 | export { default as pluginUnusedImports } from 'eslint-plugin-unused-imports'
11 |
--------------------------------------------------------------------------------
/src/utils.ts:
--------------------------------------------------------------------------------
1 | import type { Awaitable, TypedFlatConfigItem } from './types'
2 |
3 | import process from 'node:process'
4 | import { fileURLToPath } from 'node:url'
5 | import { isPackageExists } from 'local-pkg'
6 |
7 | const scopeUrl = fileURLToPath(new URL('.', import.meta.url))
8 | const isCwdInScope = isPackageExists('@antfu/eslint-config')
9 |
10 | export const parserPlain = {
11 | meta: {
12 | name: 'parser-plain',
13 | },
14 | parseForESLint: (code: string) => ({
15 | ast: {
16 | body: [],
17 | comments: [],
18 | loc: { end: code.length, start: 0 },
19 | range: [0, code.length],
20 | tokens: [],
21 | type: 'Program',
22 | },
23 | scopeManager: null,
24 | services: { isPlain: true },
25 | visitorKeys: {
26 | Program: [],
27 | },
28 | }),
29 | }
30 |
31 | /**
32 | * Combine array and non-array configs into a single array.
33 | */
34 | export async function combine(...configs: Awaitable[]): Promise {
35 | const resolved = await Promise.all(configs)
36 | return resolved.flat()
37 | }
38 |
39 | /**
40 | * Rename plugin prefixes in a rule object.
41 | * Accepts a map of prefixes to rename.
42 | *
43 | * @example
44 | * ```ts
45 | * import { renameRules } from '@antfu/eslint-config'
46 | *
47 | * export default [{
48 | * rules: renameRules(
49 | * {
50 | * '@typescript-eslint/indent': 'error'
51 | * },
52 | * { '@typescript-eslint': 'ts' }
53 | * )
54 | * }]
55 | * ```
56 | */
57 | export function renameRules(
58 | rules: Record,
59 | map: Record,
60 | ): Record {
61 | return Object.fromEntries(
62 | Object.entries(rules)
63 | .map(([key, value]) => {
64 | for (const [from, to] of Object.entries(map)) {
65 | if (key.startsWith(`${from}/`))
66 | return [to + key.slice(from.length), value]
67 | }
68 | return [key, value]
69 | }),
70 | )
71 | }
72 |
73 | /**
74 | * Rename plugin names a flat configs array
75 | *
76 | * @example
77 | * ```ts
78 | * import { renamePluginInConfigs } from '@antfu/eslint-config'
79 | * import someConfigs from './some-configs'
80 | *
81 | * export default renamePluginInConfigs(someConfigs, {
82 | * '@typescript-eslint': 'ts',
83 | * 'import-x': 'import',
84 | * })
85 | * ```
86 | */
87 | export function renamePluginInConfigs(configs: TypedFlatConfigItem[], map: Record): TypedFlatConfigItem[] {
88 | return configs.map((i) => {
89 | const clone = { ...i }
90 | if (clone.rules)
91 | clone.rules = renameRules(clone.rules, map)
92 | if (clone.plugins) {
93 | clone.plugins = Object.fromEntries(
94 | Object.entries(clone.plugins)
95 | .map(([key, value]) => {
96 | if (key in map)
97 | return [map[key], value]
98 | return [key, value]
99 | }),
100 | )
101 | }
102 | return clone
103 | })
104 | }
105 |
106 | export function toArray(value: T | T[]): T[] {
107 | return Array.isArray(value) ? value : [value]
108 | }
109 |
110 | export async function interopDefault(m: Awaitable): Promise {
111 | const resolved = await m
112 | return (resolved as any).default || resolved
113 | }
114 |
115 | export function isPackageInScope(name: string): boolean {
116 | return isPackageExists(name, { paths: [scopeUrl] })
117 | }
118 |
119 | export async function ensurePackages(packages: (string | undefined)[]): Promise {
120 | if (process.env.CI || process.stdout.isTTY === false || isCwdInScope === false)
121 | return
122 |
123 | const nonExistingPackages = packages.filter(i => i && !isPackageInScope(i)) as string[]
124 | if (nonExistingPackages.length === 0)
125 | return
126 |
127 | const p = await import('@clack/prompts')
128 | const result = await p.confirm({
129 | message: `${nonExistingPackages.length === 1 ? 'Package is' : 'Packages are'} required for this config: ${nonExistingPackages.join(', ')}. Do you want to install them?`,
130 | })
131 | if (result)
132 | await import('@antfu/install-pkg').then(i => i.installPackage(nonExistingPackages, { dev: true }))
133 | }
134 |
135 | export function isInEditorEnv(): boolean {
136 | if (process.env.CI)
137 | return false
138 | if (isInGitHooksOrLintStaged())
139 | return false
140 | return !!(false
141 | || process.env.VSCODE_PID
142 | || process.env.VSCODE_CWD
143 | || process.env.JETBRAINS_IDE
144 | || process.env.VIM
145 | || process.env.NVIM
146 | )
147 | }
148 |
149 | export function isInGitHooksOrLintStaged(): boolean {
150 | return !!(false
151 | || process.env.GIT_PARAMS
152 | || process.env.VSCODE_GIT_COMMAND
153 | || process.env.npm_lifecycle_script?.startsWith('lint-staged')
154 | )
155 | }
156 |
--------------------------------------------------------------------------------
/src/vender/prettier-types.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Vendor types from Prettier so we don't rely on the dependency.
3 | */
4 |
5 | export type VendoredPrettierOptions = Partial
6 |
7 | export type VendoredPrettierRuleOptions = VendoredPrettierOptions & {
8 | parser?: BuiltInParserName | ExternalParserName
9 | [k: string]: unknown | undefined
10 | }
11 |
12 | export interface VendoredPrettierOptionsRequired {
13 | /**
14 | * Specify the line length that the printer will wrap on.
15 | * @default 120
16 | */
17 | printWidth: number
18 | /**
19 | * Specify the number of spaces per indentation-level.
20 | */
21 | tabWidth: number
22 | /**
23 | * Indent lines with tabs instead of spaces
24 | */
25 | useTabs?: boolean
26 | /**
27 | * Print semicolons at the ends of statements.
28 | */
29 | semi: boolean
30 | /**
31 | * Use single quotes instead of double quotes.
32 | */
33 | singleQuote: boolean
34 | /**
35 | * Use single quotes in JSX.
36 | */
37 | jsxSingleQuote: boolean
38 | /**
39 | * Print trailing commas wherever possible.
40 | */
41 | trailingComma: 'none' | 'es5' | 'all'
42 | /**
43 | * Print spaces between brackets in object literals.
44 | */
45 | bracketSpacing: boolean
46 | /**
47 | * Put the `>` of a multi-line HTML (HTML, XML, JSX, Vue, Angular) element at the end of the last line instead of being
48 | * alone on the next line (does not apply to self closing elements).
49 | */
50 | bracketSameLine: boolean
51 | /**
52 | * Put the `>` of a multi-line JSX element at the end of the last line instead of being alone on the next line.
53 | * @deprecated use bracketSameLine instead
54 | */
55 | jsxBracketSameLine: boolean
56 | /**
57 | * Format only a segment of a file.
58 | */
59 | rangeStart: number
60 | /**
61 | * Format only a segment of a file.
62 | * @default Number.POSITIVE_INFINITY
63 | */
64 | rangeEnd: number
65 | /**
66 | * By default, Prettier will wrap markdown text as-is since some services use a linebreak-sensitive renderer.
67 | * In some cases you may want to rely on editor/viewer soft wrapping instead, so this option allows you to opt out.
68 | * @default "preserve"
69 | */
70 | proseWrap: 'always' | 'never' | 'preserve'
71 | /**
72 | * Include parentheses around a sole arrow function parameter.
73 | * @default "always"
74 | */
75 | arrowParens: 'avoid' | 'always'
76 | /**
77 | * Provide ability to support new languages to prettier.
78 | */
79 | plugins: Array
80 | /**
81 | * How to handle whitespaces in HTML.
82 | * @default "css"
83 | */
84 | htmlWhitespaceSensitivity: 'css' | 'strict' | 'ignore'
85 | /**
86 | * Which end of line characters to apply.
87 | * @default "lf"
88 | */
89 | endOfLine: 'auto' | 'lf' | 'crlf' | 'cr'
90 | /**
91 | * Change when properties in objects are quoted.
92 | * @default "as-needed"
93 | */
94 | quoteProps: 'as-needed' | 'consistent' | 'preserve'
95 | /**
96 | * Whether or not to indent the code inside