Skip to content

Commit

Permalink
feat: introduce formatters options (#349)
Browse files Browse the repository at this point in the history
  • Loading branch information
antfu committed Dec 4, 2023
1 parent 1153a92 commit 2d0641a
Show file tree
Hide file tree
Showing 22 changed files with 650 additions and 143 deletions.
53 changes: 40 additions & 13 deletions README.md
Expand Up @@ -267,17 +267,17 @@ Check out the [configs](https://github.com/antfu/eslint-config/blob/main/src/con
### Plugins Renaming

Since flat config requires us to explicitly provide the plugin names (instead of mandatory convention from npm package name), we renamed some plugins to make overall scope more consistent and easier to write.

| New Prefix | Original Prefix | Source Plugin |
| --- | --- | --- |
| `import/*` | `i/*` | [eslint-plugin-i](https://github.com/un-es/eslint-plugin-i) |
| `node/*` | `n/*` | [eslint-plugin-n](https://github.com/eslint-community/eslint-plugin-n) |
| `yaml/*` | `yml/*` | [eslint-plugin-yml](https://github.com/ota-meshi/eslint-plugin-yml) |
| `ts/*` | `@typescript-eslint/*` | [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint) |
| `style/*` | `@stylistic/*` | [@stylistic/eslint-plugin](https://github.com/eslint-stylistic/eslint-stylistic) |
| `test/*` | `vitest/*` | [eslint-plugin-vitest](https://github.com/veritem/eslint-plugin-vitest) |
| `test/*` | `no-only-tests/*` | [eslint-plugin-no-only-tests](https://github.com/levibuzolic/eslint-plugin-no-only-tests) |
Since flat config requires us to explicitly provide the plugin names (instead of the mandatory convention from npm package name), we renamed some plugins to make the overall scope more consistent and easier to write.

| New Prefix | Original Prefix | Source Plugin |
| ---------- | ---------------------- | ------------------------------------------------------------------------------------------ |
| `import/*` | `i/*` | [eslint-plugin-i](https://github.com/un-es/eslint-plugin-i) |
| `node/*` | `n/*` | [eslint-plugin-n](https://github.com/eslint-community/eslint-plugin-n) |
| `yaml/*` | `yml/*` | [eslint-plugin-yml](https://github.com/ota-meshi/eslint-plugin-yml) |
| `ts/*` | `@typescript-eslint/*` | [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint) |
| `style/*` | `@stylistic/*` | [@stylistic/eslint-plugin](https://github.com/eslint-stylistic/eslint-stylistic) |
| `test/*` | `vitest/*` | [eslint-plugin-vitest](https://github.com/veritem/eslint-plugin-vitest) |
| `test/*` | `no-only-tests/*` | [eslint-plugin-no-only-tests](https://github.com/levibuzolic/eslint-plugin-no-only-tests) |

When you want to override rules, or disable them inline, you need to update to the new prefix:

Expand Down Expand Up @@ -337,9 +337,36 @@ export default antfu({

We provide some optional configs for specific use cases, that we don't include their dependencies by default.

#### Formatters

> [!WARNING]
> Experimental feature, changes might not follow semver.
Use external formatters to format files that ESLint cannot handle yet (`.css`, `.html`, etc). Powered by [`eslint-plugin-format`](https://github.com/antfu/eslint-plugin-format).

```js
// eslint.config.js
import antfu from '@antfu/eslint-config'

export default antfu({
formatters: {
css: true, // by default use Prettier
html: true, // by default use Prettier
toml: 'dprint', // use dprint for TOML
markdown: 'prettier' // use prettier for markdown
}
})
```

Running `npx eslint` should prompt you to install the required dependencies, otherwise, you can install them manually:

```bash
npm i -D eslint-plugin-format
```

#### React

To enable React support you need to explicitly turn it on:
To enable React support, you need to explicitly turn it on:

```js
// eslint.config.js
Expand Down Expand Up @@ -383,7 +410,7 @@ This config also provides some optional plugins/rules for extended usages.

This plugin [`eslint-plugin-perfectionist`](https://github.com/azat-io/eslint-plugin-perfectionist) allows you to sorted object keys, imports, etc, with auto-fix.

The plugin is installed but no rules are enabled by default.
The plugin is installed but no rules are enabled by default.

It's recommended to opt-in on each file individually using [configuration comments](https://eslint.org/docs/latest/use/configure/rules#using-configuration-comments-1).

Expand Down
5 changes: 5 additions & 0 deletions eslint.config.js
Expand Up @@ -11,6 +11,11 @@ export default antfu(
'fixtures',
'_fixtures',
],
formatters: {
css: true,
html: true,
markdown: true,
},
},
{
files: ['src/**/*.ts'],
Expand Down
3 changes: 3 additions & 0 deletions fixtures/input/toml.toml
@@ -0,0 +1,3 @@
[foo]
b = 1
c = "hello"
11 changes: 11 additions & 0 deletions fixtures/output/with-formatters/css.css
@@ -0,0 +1,11 @@
@media (max-width: 480px) {
.bd-examples {
margin-right: -0.75rem;
margin-left: -0.75rem;
}

.bd-examples > [class^='col-'] {
padding-right: 0.75rem;
padding-left: 0.75rem;
}
}
28 changes: 28 additions & 0 deletions fixtures/output/with-formatters/html.html
@@ -0,0 +1,28 @@
<!doctype html>
<html class="no-js mY-ClAsS">
<head>
<meta charset="utf-8" />
<title>My tITlE</title>
<meta name="description" content="My CoNtEnT" />
</head>
<body>
<p>
Hello world!<br />
This is HTML5 Boilerplate.
</p>
<script>
window.ga = function () {
ga.q.push(arguments)
}
ga.q = []
ga.l = +new Date()
ga('create', 'UA-XXXXX-Y', 'auto')
ga('send', 'pageview')
</script>
<script
src="https://www.google-analytics.com/analytics.js"
async
defer
></script>
</body>
</html>
72 changes: 72 additions & 0 deletions fixtures/output/with-formatters/javascript.js
@@ -0,0 +1,72 @@
// This file is generated by ChatGPT

// eslint-disable-next-line no-console
const log = console.log

// Define a class using ES6 class syntax
class Person {
constructor(name, age) {
this.name = name
this.age = age
}

// Define a method within the class
sayHello() {
log(`Hello, my name is ${this.name} and I am ${this.age} years old.`)
}
}

// Create an array of objects
const people = [
new Person('Alice', 30),
new Person('Bob', 25),
new Person('Charlie', 35),
]

// Use the forEach method to iterate over the array
people.forEach((person) => {
person.sayHello()
})

// Use a template literal to create a multiline string
const multilineString = `
This is a multiline string
that spans multiple lines.
`

// Use destructuring assignment to extract values from an object
const { name, age } = people[0]
log(`First person in the array is ${name} and they are ${age} years old.`, multilineString)

// Use the spread operator to create a new array
const numbers = [1, 2, 3]
const newNumbers = [...numbers, 4, 5]
log(newNumbers)

// Use a try-catch block for error handling
try {
// Attempt to parse an invalid JSON string
JSON.parse('invalid JSON')
}
catch (error) {
console.error('Error parsing JSON:', error.message)
}

// Use a ternary conditional operator
const isEven = num => num % 2 === 0
const number = 7
log(`${number} is ${isEven(number) ? 'even' : 'odd'}.`)

// Use a callback function with setTimeout for asynchronous code
setTimeout(() => {
log('This code runs after a delay of 2 seconds.')
}, 2000)

let a, b, c, d, foo

if (a
|| b
|| c || d
|| (d && b)
)
foo()
26 changes: 26 additions & 0 deletions fixtures/output/with-formatters/jsx.jsx
@@ -0,0 +1,26 @@
export function HelloWorld({
greeting = 'hello',
greeted = '"World"',
silent = false,
onMouseOver,
}) {
if (!greeting)
return null

// TODO: Don't use random in render
const num = Math.floor (Math.random() * 1e+7).toString()
.replace(/\.\d+/ig, '')

return (
<div className="HelloWorld" title={`You are visitor number ${num}`} onMouseOver={onMouseOver}>
<strong>{ greeting.slice(0, 1).toUpperCase() + greeting.slice(1).toLowerCase() }</strong>
{greeting.endsWith(',')
? ' '
: <span style={{ color: '\grey' }}>", "</span> }
<em>
{ greeted }
</em>
{ (silent) ? '.' : '!'}
</div>
)
}
34 changes: 34 additions & 0 deletions fixtures/output/with-formatters/markdown.md
@@ -0,0 +1,34 @@
# Header

_Look,_ code blocks are formatted _too!_

```js
// This should be handled by ESLint instead of Prettier
function identity(x) {
if (foo)
console.log('bar')
}
```

```css
/* This should be handled by Prettier */
.foo {
color: red;
}
```

| Pilot | Airport | Hours |
| -------- | :-----: | ----: |
| John Doe | SKG | 1338 |
| Jane Roe | JFK | 314 |

---

- List
- with a [link] (/to/somewhere)
- and [another one]

[another one]: http://example.com 'Example title'

Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Curabitur consectetur maximus risus, sed maximus tellus tincidunt et.
3 changes: 3 additions & 0 deletions fixtures/output/with-formatters/toml.toml
@@ -0,0 +1,3 @@
[foo]
b = 1
c = "hello"
32 changes: 32 additions & 0 deletions fixtures/output/with-formatters/tsx.tsx
@@ -0,0 +1,32 @@
export function Component1() {
return <div />
}

export function jsx2() {
const props = { a: 1, b: 2 }
return (
<a foo="bar" bar="foo">
<div
{...props}
a={1}
b="2"
>
Inline Text
</div>
<Component1>
Block Text
</Component1>
<div>
Mixed
<div>Foo</div>
Text
<b> Bar</b>
</div>
<p>
foo
<i>bar</i>
<b>baz</b>
</p>
</a>
)
}
83 changes: 83 additions & 0 deletions fixtures/output/with-formatters/typescript.ts
@@ -0,0 +1,83 @@
// Define a TypeScript interface
interface Person {
name: string
age: number
}

// Create an array of objects with the defined interface
const people: Person[] = [
{ name: 'Alice', age: 30 },
{ name: 'Bob', age: 25 },
{ name: 'Charlie', age: 35 },
]

// eslint-disable-next-line no-console
const log = console.log

// Use a for...of loop to iterate over the array
for (const person of people)
log(`Hello, my name is ${person.name} and I am ${person.age} years old.`)

// Define a generic function
function identity< T >(arg: T): T {
return arg
}

// Use the generic function with type inference
const result = identity(
'TypeScript is awesome',
)
log(result)

// Use optional properties in an interface
interface Car {
make: string
model?: string
}

// Create objects using the interface
const car1: Car = { make: 'Toyota' }
const car2: Car = {
make: 'Ford',
model: 'Focus',
}

// Use union types
type Fruit = 'apple' | 'banana' | 'orange'
const favoriteFruit: Fruit = 'apple'

// Use a type assertion to tell TypeScript about the type
const inputValue: any = '42'
const numericValue = inputValue as number

// Define a class with access modifiers
class Animal {
private name: string
constructor(name: string) {
this.name = name
}

protected makeSound(sound: string) {
log(`${this.name} says ${sound}`)
}
}

// Extend a class
class Dog extends Animal {
constructor(private alias: string) {
super(alias)
}

bark() {
this.makeSound('Woof!')
}
}

const dog = new Dog('Buddy')
dog.bark()

function fn(): string {
return `hello${1}`
}

log(car1, car2, favoriteFruit, numericValue, fn())

0 comments on commit 2d0641a

Please sign in to comment.