Skip to content

Commit

Permalink
feat(eslint-config): add a configuration preset for Prettier (#695)
Browse files Browse the repository at this point in the history
  • Loading branch information
marcusrbrown authored Nov 30, 2024
1 parent ebe5b31 commit 039cd90
Show file tree
Hide file tree
Showing 15 changed files with 231 additions and 41 deletions.
6 changes: 6 additions & 0 deletions .changeset/weak-cooks-remember.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@bfra.me/eslint-config": minor
---

Add a configuration preset for Prettier to format whilst linting.

34 changes: 26 additions & 8 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,25 +1,43 @@
{
"[json]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},

"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit",
"source.organizeImports": "never"
},
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true,
"editor.formatOnType": false,
"editor.formatOnSave": false,

"eslint.options": {
"flags": ["unstable_config_lookup_from_file", "unstable_ts_config"]
},

// Silent the stylistic rules in you IDE, but still auto fix them
"eslint.rules.customizations": [
{"rule": "prettier/prettier", "severity": "off", "fixable": true}
],

"eslint.runtime": "node",

// Enable eslint for all supported languages
"eslint.validate": [
"javascript",
"javascriptreact",
"typescript",
"typescriptreact",
"vue",
"html",
"markdown",
"json",
"json5",
"jsonc",
"yaml",
"toml",
"xml"
],

"explorer.fileNesting.enabled": false,

"cSpell.words": ["bfra", "frameworkers", "readmeio"],

"prettier.enable": true,
"prettier.enable": false,

"typescript.enablePromptUseWorkspaceTsdk": true,
"typescript.tsdk": "node_modules/typescript/lib"
Expand Down
9 changes: 9 additions & 0 deletions packages/eslint-config/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,12 +64,15 @@
"@bfra.me/tsconfig": "workspace:*",
"@eslint/config-inspector": "0.5.6",
"@eslint/js": "9.15.0",
"@types/eslint-config-prettier": "6.11.3",
"@types/fs-extra": "11.0.4",
"@types/node": "22.10.0",
"@typescript-eslint/types": "8.16.0",
"@vitest/eslint-plugin": "1.1.11",
"eslint": "9.15.0",
"eslint-config-prettier": "9.1.0",
"eslint-plugin-no-only-tests": "3.3.0",
"eslint-plugin-prettier": "5.2.1",
"eslint-typegen": "0.3.2",
"execa": "9.5.1",
"fast-glob": "3.3.2",
Expand All @@ -89,8 +92,14 @@
"@vitest/eslint-plugin": {
"optional": true
},
"eslint-config-prettier": {
"optional": true
},
"eslint-plugin-no-only-tests": {
"optional": true
},
"eslint-plugin-prettier": {
"optional": true
}
},
"publishConfig": {
Expand Down
2 changes: 1 addition & 1 deletion packages/eslint-config/scripts/generate-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ const configDts = `import type {Linter} from 'eslint'
import type {FlatConfigComposer, ResolvableFlatConfig} from 'eslint-flat-config-utils'
import type {${rulesTypeName}} from './rules'
export type {FlatConfigComposer, ResolvableFlatConfig, ${rulesTypeName}}
export type {FlatConfigComposer, ResolvableFlatConfig}
/**
* Represents a value that resolves to one or more ESLint flat configurations.
Expand Down
3 changes: 2 additions & 1 deletion packages/eslint-config/src/config.d.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions packages/eslint-config/src/configs/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@ export * from './imports'
export * from './javascript'
export * from './jsdoc'
export * from './perfectionist'
export * from './prettier'
export * from './typescript'
export * from './vitest'
38 changes: 38 additions & 0 deletions packages/eslint-config/src/configs/prettier.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import type {Config} from '../config'
import type {Flatten, OptionsIsInEditor, OptionsOverrides} from '../options'
import {interopDefault} from '../plugins'

/**
* Represents the options for the ESLint Prettier configuration.
*/
export type PrettierOptions = Flatten<OptionsIsInEditor & OptionsOverrides>

/**
* Generates an ESLint configuration for Prettier.
* @param options - The options for the Prettier configuration.
* @returns An array of ESLint configurations.
*/
export async function prettier(options: PrettierOptions = {}): Promise<Config[]> {
const {isInEditor, overrides} = options
const [configPrettier, pluginPrettier] = await Promise.all([
interopDefault(import('eslint-config-prettier')),
interopDefault(import('eslint-plugin-prettier')),
])

return [
{
name: '@bfra.me/prettier',
plugins: {
prettier: pluginPrettier,
},
rules: {
...configPrettier.rules,
...(pluginPrettier.configs?.recommended as Config).rules,

...(isInEditor ? {} : {'prettier/prettier': 'error'}),

...overrides,
},
},
]
}
11 changes: 11 additions & 0 deletions packages/eslint-config/src/define-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
javascript,
jsdoc,
perfectionist,
prettier,
typescript,
vitest,
} from './configs'
Expand Down Expand Up @@ -49,6 +50,7 @@ export async function defineConfig(
const {
gitignore: enableGitignore = true,
perfectionist: enablePerfectionist = true,
prettier: enablePrettier = isPackageExists('prettier'),
typescript: enableTypeScript = isPackageExists('typescript'),
} = options

Expand Down Expand Up @@ -83,6 +85,15 @@ export async function defineConfig(
command(),
)

if (enablePrettier) {
configs.push(
prettier({
isInEditor,
overrides: getOverrides(options, 'prettier'),
}),
)
}

if (enablePerfectionist) {
configs.push(
perfectionist({
Expand Down
5 changes: 5 additions & 0 deletions packages/eslint-config/src/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,11 @@ export type Options = Flatten<
*/
perfectionist?: boolean | OptionsPerfectionist

/**
* Options to override the behavior of the Prettier code formatter.
*/
prettier?: boolean | OptionsOverrides

/**
* Enable TypeScript support.
*
Expand Down
2 changes: 1 addition & 1 deletion packages/eslint-config/src/plugins.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ export async function interopDefault<T>(
m: Awaitable<T>,
): Promise<T extends {default: infer U} ? U : T> {
const resolved = await m
return (resolved as any).default || resolved
return 'default' in resolved ? interopDefault(resolved.default) : resolved
}

// @ts-expect-error - No types
Expand Down
16 changes: 16 additions & 0 deletions packages/eslint-config/src/rules.d.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

24 changes: 24 additions & 0 deletions packages/eslint-config/test/fixtures/output/default/tsx.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
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>
)
}
60 changes: 30 additions & 30 deletions packages/eslint-config/test/fixtures/output/default/typescript.ts
Original file line number Diff line number Diff line change
@@ -1,76 +1,76 @@
// Define a TypeScript interface
interface Person {
name: string; age: number;
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 }
];
{name: 'Alice', age: 30},
{name: 'Bob', age: 25},
{name: 'Charlie', age: 35},
]


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.`);
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;
function identity<T>(arg: T): T {
return arg
}

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

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

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

// Use union types
type Fruit = 'apple' | 'banana' | 'orange';
const favoriteFruit: Fruit = 'apple';
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;
const inputValue: any = '42'
const numericValue = inputValue as number

// Define a class with access modifiers
class Animal {
private name: string;
private name: string
constructor(name: string) {
this.name = name;
this.name = name
}
protected makeSound(sound: string) {
log(`${this.name} says ${sound}`);
log(`${this.name} says ${sound}`)
}
}

// Extend a class
class Dog extends Animal {
constructor(private alias: string) {
super(alias);
super(alias)
}
bark() {
this.makeSound('Woof!');
this.makeSound('Woof!')
}
}

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

const fn = (): string => {
return 'hello' + 1
Expand Down
1 change: 1 addition & 0 deletions packages/eslint-config/tsup.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export default defineConfig({
clean: true,
dts: true,
entry: ['src/index.ts'],
external: ['eslint-config-prettier', 'eslint-plugin-prettier'],
format: ['esm'],
outDir: 'lib',
sourcemap: true,
Expand Down
Loading

0 comments on commit 039cd90

Please sign in to comment.