diff --git a/eslint.config.js b/eslint.config.js
index 149f951..386c61a 100644
--- a/eslint.config.js
+++ b/eslint.config.js
@@ -2,4 +2,10 @@
// eslint-disable-next-line antfu/no-import-dist
import { xwbx } from "./dist/index.js";
-export default xwbx({ typescript: true, vue: true, astro: true, unocss: true });
+export default xwbx({
+ ignores: ["fixtures"],
+ typescript: true,
+ vue: true,
+ astro: true,
+ unocss: true,
+});
diff --git a/fixtures/input/astro.astro b/fixtures/input/astro.astro
new file mode 100644
index 0000000..df4e8e5
--- /dev/null
+++ b/fixtures/input/astro.astro
@@ -0,0 +1,21 @@
+---
+const isJsx = true
+const content = "hi!";
+---
+
+
+ {content}
+
+ {isJsx && (
+
{content}
+ )}
+
+
+
+
+
diff --git a/fixtures/input/css.css b/fixtures/input/css.css
new file mode 100644
index 0000000..12a4724
--- /dev/null
+++ b/fixtures/input/css.css
@@ -0,0 +1,10 @@
+@media (max-width: 480px) {
+ .bd-examples {margin-right: -.75rem;margin-left: -.75rem
+ }
+
+ .bd-examples>[class^="col-"] {
+ padding-right: .75rem;
+ padding-left: .75rem;
+
+ }
+}
diff --git a/fixtures/input/html.html b/fixtures/input/html.html
new file mode 100644
index 0000000..1516dca
--- /dev/null
+++ b/fixtures/input/html.html
@@ -0,0 +1,17 @@
+
+
+
+
+ My tITlE
+
+
+
+ Hello world! This is HTML5 Boilerplate.
+
+
+
+
diff --git a/fixtures/input/javascript.js b/fixtures/input/javascript.js
new file mode 100644
index 0000000..ab9c71f
--- /dev/null
+++ b/fixtures/input/javascript.js
@@ -0,0 +1,72 @@
+// This file is generated by ChatGPT
+
+// eslint-disable-next-line no-console
+var 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()
+ }
diff --git a/fixtures/input/jsx.jsx b/fixtures/input/jsx.jsx
new file mode 100644
index 0000000..d5adb05
--- /dev/null
+++ b/fixtures/input/jsx.jsx
@@ -0,0 +1,21 @@
+export function HelloWorld({
+ greeting = "hello", greeted = '"World"', silent = false, onMouseOver,}) {
+
+ if(!greeting){
+ return null};
+
+ // TODO: Don't use random in render
+ let num = Math.floor (Math.random() * 1E+7).toString()
+ .replace(/\.\d+/ig, "")
+
+ return
+ { greeting.slice( 0, 1 ).toUpperCase() + greeting.slice(1).toLowerCase() }
+ {greeting.endsWith(",")
+ ? " " : ", " }
+
+ { greeted }
+
+ { (silent)? ".": "!"}
+
;
+
+}
diff --git a/fixtures/input/markdown.md b/fixtures/input/markdown.md
new file mode 100644
index 0000000..a7f61a3
--- /dev/null
+++ b/fixtures/input/markdown.md
@@ -0,0 +1,35 @@
+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.
diff --git a/fixtures/input/svelte.svelte b/fixtures/input/svelte.svelte
new file mode 100644
index 0000000..bf114b9
--- /dev/null
+++ b/fixtures/input/svelte.svelte
@@ -0,0 +1,8 @@
+
+
+
+
+ {@html content}
+
\ No newline at end of file
diff --git a/fixtures/input/toml.toml b/fixtures/input/toml.toml
new file mode 100644
index 0000000..31e10f1
--- /dev/null
+++ b/fixtures/input/toml.toml
@@ -0,0 +1,24 @@
+comma = [
+ 1
+ ,2
+ ,3,
+]
+
+[foo]
+ b = 1
+ c = "hello"
+ a = {answer = 42}
+
+"indent" = [
+1,
+ 2
+]
+
+['a-table']
+apple.type = "fruit"
+orange.type = "fruit"
+apple.skin = "thin"
+orange.skin = "thick"
+
+apple.color = "red"
+orange.color = "orange"
diff --git a/fixtures/input/tsconfig.json b/fixtures/input/tsconfig.json
new file mode 100644
index 0000000..66372fb
--- /dev/null
+++ b/fixtures/input/tsconfig.json
@@ -0,0 +1,10 @@
+{
+ "compilerOptions": {
+ "target": "ESNext",
+ "module": "ESNext",
+ "moduleResolution": "Bundler",
+ "strict": true,
+ "skipDefaultLibCheck": true,
+ "skipLibCheck": true
+ }
+}
diff --git a/fixtures/input/tsx.tsx b/fixtures/input/tsx.tsx
new file mode 100644
index 0000000..97ca089
--- /dev/null
+++ b/fixtures/input/tsx.tsx
@@ -0,0 +1,24 @@
+export function Component1() {
+ return
;
+}
+
+export function jsx2() {
+ const props = {a:1,
+ b:2}
+ return < a foo= 'bar' bar={`foo` } >
+ Inline Text
+
+ Block Text
+
+
+ Mixed
+
Foo
+ Text
Bar
+
+
+ foobar baz
+
+ a >
+}
diff --git a/fixtures/input/typescript.ts b/fixtures/input/typescript.ts
new file mode 100644
index 0000000..97bbe6d
--- /dev/null
+++ b/fixtures/input/typescript.ts
@@ -0,0 +1,80 @@
+// 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
+var 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();
+
+var fn = (): string => {
+ return 'hello' + 1
+}
+
+log(car1, car2, favoriteFruit, numericValue, fn())
+
diff --git a/fixtures/input/vue-ts.vue b/fixtures/input/vue-ts.vue
new file mode 100644
index 0000000..61ed8c6
--- /dev/null
+++ b/fixtures/input/vue-ts.vue
@@ -0,0 +1,34 @@
+
+
+
{{ greeting }}
+
Click me!
+
+
Counter: {{ counter }}
+
+
+
+
+
+
+
+
diff --git a/fixtures/input/vue.vue b/fixtures/input/vue.vue
new file mode 100644
index 0000000..b540d06
--- /dev/null
+++ b/fixtures/input/vue.vue
@@ -0,0 +1,24 @@
+
+
+
+ {{ greeting }}
+
Click me!
+
Counter: {{ counter }}
+
+
+
+
diff --git a/fixtures/input/xml.xml b/fixtures/input/xml.xml
new file mode 100644
index 0000000..8fbc57b
--- /dev/null
+++ b/fixtures/input/xml.xml
@@ -0,0 +1,9 @@
+
+Effective Java 45.00
+ Bluetooth Speaker 120.00
+ Clean Code
+ 33.50
+
+
+
+
diff --git a/fixtures/output/all/astro.astro b/fixtures/output/all/astro.astro
new file mode 100644
index 0000000..fb1c8f9
--- /dev/null
+++ b/fixtures/output/all/astro.astro
@@ -0,0 +1,18 @@
+---
+const isJsx = true;
+const content = "hi!";
+---
+
+
+ {content}
+
+ {isJsx &&
{content} }
+
+
+
+
diff --git a/fixtures/output/all/css.css b/fixtures/output/all/css.css
new file mode 100644
index 0000000..077c760
--- /dev/null
+++ b/fixtures/output/all/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;
+ }
+}
diff --git a/fixtures/output/all/html.html b/fixtures/output/all/html.html
new file mode 100644
index 0000000..30e8a3a
--- /dev/null
+++ b/fixtures/output/all/html.html
@@ -0,0 +1,28 @@
+
+
+
+
+ My tITlE
+
+
+
+
+ Hello world!
+ This is HTML5 Boilerplate.
+
+
+
+
+
diff --git a/fixtures/output/all/javascript.js b/fixtures/output/all/javascript.js
new file mode 100644
index 0000000..eda3378
--- /dev/null
+++ b/fixtures/output/all/javascript.js
@@ -0,0 +1,71 @@
+// This file is generated by ChatGPT
+
+// eslint-disable-next-line no-console
+var 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();
+}
diff --git a/fixtures/output/all/jsx.jsx b/fixtures/output/all/jsx.jsx
new file mode 100644
index 0000000..250b285
--- /dev/null
+++ b/fixtures/output/all/jsx.jsx
@@ -0,0 +1,34 @@
+export function HelloWorld({
+ greeting = "hello",
+ greeted = '"World"',
+ silent = false,
+ onMouseOver,
+}) {
+ if (!greeting) {
+ return null;
+ }
+
+ // TODO: Don't use random in render
+ let num = Math.floor(Math.random() * 1e7)
+ .toString()
+ .replace(/\.\d+/gi, "");
+
+ return (
+
+
+ {greeting.slice(0, 1).toUpperCase() + greeting.slice(1).toLowerCase()}
+
+ {greeting.endsWith(",") ? (
+ " "
+ ) : (
+ ", "
+ )}
+ {greeted}
+ {silent ? "." : "!"}
+
+ );
+}
diff --git a/fixtures/output/all/markdown.md b/fixtures/output/all/markdown.md
new file mode 100644
index 0000000..6b24021
--- /dev/null
+++ b/fixtures/output/all/markdown.md
@@ -0,0 +1,35 @@
+# 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.
diff --git a/fixtures/output/all/toml.toml b/fixtures/output/all/toml.toml
new file mode 100644
index 0000000..a28003c
--- /dev/null
+++ b/fixtures/output/all/toml.toml
@@ -0,0 +1,23 @@
+comma = [
+ 1,
+ 2,
+ 3,
+]
+
+[foo]
+ b = 1
+ c = "hello"
+ a = {answer = 42}
+
+"indent" = [
+1,
+ 2
+]
+
+['a-table']
+apple.type = "fruit"
+apple.skin = "thin"
+apple.color = "red"
+orange.type = "fruit"
+orange.skin = "thick"
+orange.color = "orange"
diff --git a/fixtures/output/all/tsx.tsx b/fixtures/output/all/tsx.tsx
new file mode 100644
index 0000000..6fa1309
--- /dev/null
+++ b/fixtures/output/all/tsx.tsx
@@ -0,0 +1,24 @@
+export function Component1() {
+ return
;
+}
+
+export function jsx2() {
+ const props = { a: 1, b: 2 };
+ return (
+
+
+ Inline Text
+
+ Block Text
+
+ Mixed
+
Foo
+ Text
Bar
+
+
+ foobar
+ baz
+
+
+ );
+}
diff --git a/fixtures/output/all/typescript.ts b/fixtures/output/all/typescript.ts
new file mode 100644
index 0000000..1ef3132
--- /dev/null
+++ b/fixtures/output/all/typescript.ts
@@ -0,0 +1,80 @@
+// 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(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());
diff --git a/fixtures/output/all/vue-ts.vue b/fixtures/output/all/vue-ts.vue
new file mode 100644
index 0000000..9772479
--- /dev/null
+++ b/fixtures/output/all/vue-ts.vue
@@ -0,0 +1,36 @@
+
+
+
{{ greeting }}
+
Click me!
+
Counter: {{ counter }}
+
+
+
+
+
+
+
+
diff --git a/fixtures/output/all/vue.vue b/fixtures/output/all/vue.vue
new file mode 100644
index 0000000..aeca5aa
--- /dev/null
+++ b/fixtures/output/all/vue.vue
@@ -0,0 +1,25 @@
+
+
+
+ {{ greeting }}
+
+
Click me!
+
Counter: {{ counter }}
+
+
+
+
diff --git a/fixtures/output/no-style/astro.astro b/fixtures/output/no-style/astro.astro
new file mode 100644
index 0000000..14bedf6
--- /dev/null
+++ b/fixtures/output/no-style/astro.astro
@@ -0,0 +1,21 @@
+---
+const isJsx = true
+const content = "hi!";
+---
+
+
+ {content}
+
+ {isJsx && (
+
{content}
+ )}
+
+
+
+
+
diff --git a/fixtures/output/no-style/javascript.js b/fixtures/output/no-style/javascript.js
new file mode 100644
index 0000000..bf2406e
--- /dev/null
+++ b/fixtures/output/no-style/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
+for (const person of people) {
+ 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()
+ }
diff --git a/fixtures/output/no-style/jsx.jsx b/fixtures/output/no-style/jsx.jsx
new file mode 100644
index 0000000..f5f1f2c
--- /dev/null
+++ b/fixtures/output/no-style/jsx.jsx
@@ -0,0 +1,21 @@
+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()
+ .replaceAll(/\.\d+/g, "")
+
+ return
+ { greeting.slice( 0, 1 ).toUpperCase() + greeting.slice(1).toLowerCase() }
+ {greeting.endsWith(",")
+ ? " " : ", " }
+
+ { greeted }
+
+ { (silent)? ".": "!"}
+
;
+
+}
diff --git a/fixtures/output/no-style/toml.toml b/fixtures/output/no-style/toml.toml
new file mode 100644
index 0000000..a28003c
--- /dev/null
+++ b/fixtures/output/no-style/toml.toml
@@ -0,0 +1,23 @@
+comma = [
+ 1,
+ 2,
+ 3,
+]
+
+[foo]
+ b = 1
+ c = "hello"
+ a = {answer = 42}
+
+"indent" = [
+1,
+ 2
+]
+
+['a-table']
+apple.type = "fruit"
+apple.skin = "thin"
+apple.color = "red"
+orange.type = "fruit"
+orange.skin = "thick"
+orange.color = "orange"
diff --git a/fixtures/output/no-style/typescript.ts b/fixtures/output/no-style/typescript.ts
new file mode 100644
index 0000000..18c8252
--- /dev/null
+++ b/fixtures/output/no-style/typescript.ts
@@ -0,0 +1,80 @@
+// 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())
+
diff --git a/fixtures/output/no-style/vue-ts.vue b/fixtures/output/no-style/vue-ts.vue
new file mode 100644
index 0000000..f55c567
--- /dev/null
+++ b/fixtures/output/no-style/vue-ts.vue
@@ -0,0 +1,34 @@
+
+
+
{{ greeting }}
+
Click me!
+
+
Counter: {{ counter }}
+
+
+
+
+
+
+
+
diff --git a/fixtures/output/no-style/vue.vue b/fixtures/output/no-style/vue.vue
new file mode 100644
index 0000000..e0774d6
--- /dev/null
+++ b/fixtures/output/no-style/vue.vue
@@ -0,0 +1,24 @@
+
+
+
+ {{ greeting }}
+
Click me!
+
Counter: {{ counter }}
+
+
+
+
diff --git a/fixtures/output/tab-single-quotes/astro.astro b/fixtures/output/tab-single-quotes/astro.astro
new file mode 100644
index 0000000..5f25d96
--- /dev/null
+++ b/fixtures/output/tab-single-quotes/astro.astro
@@ -0,0 +1,18 @@
+---
+const isJsx = true;
+const content = 'hi!';
+---
+
+
+ {content}
+
+ {isJsx &&
{content} }
+
+
+
+
diff --git a/fixtures/output/tab-single-quotes/css.css b/fixtures/output/tab-single-quotes/css.css
new file mode 100644
index 0000000..5f19626
--- /dev/null
+++ b/fixtures/output/tab-single-quotes/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;
+ }
+}
diff --git a/fixtures/output/tab-single-quotes/html.html b/fixtures/output/tab-single-quotes/html.html
new file mode 100644
index 0000000..aaa5e79
--- /dev/null
+++ b/fixtures/output/tab-single-quotes/html.html
@@ -0,0 +1,28 @@
+
+
+
+
+ My tITlE
+
+
+
+
+ Hello world!
+ This is HTML5 Boilerplate.
+
+
+
+
+
diff --git a/fixtures/output/tab-single-quotes/javascript.js b/fixtures/output/tab-single-quotes/javascript.js
new file mode 100644
index 0000000..096216e
--- /dev/null
+++ b/fixtures/output/tab-single-quotes/javascript.js
@@ -0,0 +1,71 @@
+// This file is generated by ChatGPT
+
+// eslint-disable-next-line no-console
+var 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();
+}
diff --git a/fixtures/output/tab-single-quotes/jsx.jsx b/fixtures/output/tab-single-quotes/jsx.jsx
new file mode 100644
index 0000000..4d403d1
--- /dev/null
+++ b/fixtures/output/tab-single-quotes/jsx.jsx
@@ -0,0 +1,34 @@
+export function HelloWorld({
+ greeting = 'hello',
+ greeted = '"World"',
+ silent = false,
+ onMouseOver,
+}) {
+ if (!greeting) {
+ return null;
+ }
+
+ // TODO: Don't use random in render
+ let num = Math.floor(Math.random() * 1e7)
+ .toString()
+ .replace(/\.\d+/gi, '');
+
+ return (
+
+
+ {greeting.slice(0, 1).toUpperCase() + greeting.slice(1).toLowerCase()}
+
+ {greeting.endsWith(',') ? (
+ ' '
+ ) : (
+ ", "
+ )}
+ {greeted}
+ {silent ? '.' : '!'}
+
+ );
+}
diff --git a/fixtures/output/tab-single-quotes/markdown.md b/fixtures/output/tab-single-quotes/markdown.md
new file mode 100644
index 0000000..7eceb11
--- /dev/null
+++ b/fixtures/output/tab-single-quotes/markdown.md
@@ -0,0 +1,35 @@
+# 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.
diff --git a/fixtures/output/tab-single-quotes/toml.toml b/fixtures/output/tab-single-quotes/toml.toml
new file mode 100644
index 0000000..a28003c
--- /dev/null
+++ b/fixtures/output/tab-single-quotes/toml.toml
@@ -0,0 +1,23 @@
+comma = [
+ 1,
+ 2,
+ 3,
+]
+
+[foo]
+ b = 1
+ c = "hello"
+ a = {answer = 42}
+
+"indent" = [
+1,
+ 2
+]
+
+['a-table']
+apple.type = "fruit"
+apple.skin = "thin"
+apple.color = "red"
+orange.type = "fruit"
+orange.skin = "thick"
+orange.color = "orange"
diff --git a/fixtures/output/tab-single-quotes/tsconfig.json b/fixtures/output/tab-single-quotes/tsconfig.json
new file mode 100644
index 0000000..75dd2b2
--- /dev/null
+++ b/fixtures/output/tab-single-quotes/tsconfig.json
@@ -0,0 +1,10 @@
+{
+ "compilerOptions": {
+ "target": "ESNext",
+ "module": "ESNext",
+ "moduleResolution": "Bundler",
+ "strict": true,
+ "skipDefaultLibCheck": true,
+ "skipLibCheck": true
+ }
+}
diff --git a/fixtures/output/tab-single-quotes/tsx.tsx b/fixtures/output/tab-single-quotes/tsx.tsx
new file mode 100644
index 0000000..79b70ef
--- /dev/null
+++ b/fixtures/output/tab-single-quotes/tsx.tsx
@@ -0,0 +1,24 @@
+export function Component1() {
+ return
;
+}
+
+export function jsx2() {
+ const props = { a: 1, b: 2 };
+ return (
+
+
+ Inline Text
+
+ Block Text
+
+ Mixed
+
Foo
+ Text
Bar
+
+
+ foobar
+ baz
+
+
+ );
+}
diff --git a/fixtures/output/tab-single-quotes/typescript.ts b/fixtures/output/tab-single-quotes/typescript.ts
new file mode 100644
index 0000000..9473a71
--- /dev/null
+++ b/fixtures/output/tab-single-quotes/typescript.ts
@@ -0,0 +1,80 @@
+// 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(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());
diff --git a/fixtures/output/tab-single-quotes/vue-ts.vue b/fixtures/output/tab-single-quotes/vue-ts.vue
new file mode 100644
index 0000000..e52d917
--- /dev/null
+++ b/fixtures/output/tab-single-quotes/vue-ts.vue
@@ -0,0 +1,36 @@
+
+
+
{{ greeting }}
+
Click me!
+
Counter: {{ counter }}
+
+
+
+
+
+
+
+
diff --git a/fixtures/output/tab-single-quotes/vue.vue b/fixtures/output/tab-single-quotes/vue.vue
new file mode 100644
index 0000000..5d412ce
--- /dev/null
+++ b/fixtures/output/tab-single-quotes/vue.vue
@@ -0,0 +1,25 @@
+
+
+
+ {{ greeting }}
+
+
Click me!
+
Counter: {{ counter }}
+
+
+
+
diff --git a/package.json b/package.json
index 40081ea..4aaf480 100644
--- a/package.json
+++ b/package.json
@@ -24,6 +24,7 @@
"build:inspector": "pnpm build && npx @eslint/config-inspector build",
"watch": "tsup --format esm,cjs --watch",
"lint": "eslint .",
+ "test": "vitest",
"typegen": "esno scripts/typegen.ts",
"prepack": "pnpm run build",
"release": "bumpp && pnpm publish",
@@ -91,6 +92,7 @@
"devDependencies": {
"@eslint/config-inspector": "^0.5.2",
"@types/eslint": "^9.6.0",
+ "@types/fs-extra": "^11.0.4",
"@types/node": "^22.2.0",
"@unocss/eslint-plugin": "^0.62.1",
"astro-eslint-parser": "^1.0.2",
@@ -99,12 +101,15 @@
"eslint-plugin-astro": "^1.2.3",
"eslint-typegen": "^0.3.0",
"esno": "^4.7.0",
+ "execa": "^9.3.1",
"fast-glob": "^3.3.2",
+ "fs-extra": "^11.2.0",
"husky": "^9.1.4",
"lint-staged": "^15.2.8",
"prettier-plugin-astro": "^0.14.1",
"tsup": "^8.2.4",
- "typescript": "^5.5.4"
+ "typescript": "^5.5.4",
+ "vitest": "^2.0.5"
},
"lint-staged": {
"*": "eslint --fix"
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index eab5175..cf4cc9d 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -120,6 +120,9 @@ importers:
'@types/eslint':
specifier: ^9.6.0
version: 9.6.0
+ '@types/fs-extra':
+ specifier: ^11.0.4
+ version: 11.0.4
'@types/node':
specifier: ^22.2.0
version: 22.2.0
@@ -144,9 +147,15 @@ importers:
esno:
specifier: ^4.7.0
version: 4.7.0
+ execa:
+ specifier: ^9.3.1
+ version: 9.3.1
fast-glob:
specifier: ^3.3.2
version: 3.3.2
+ fs-extra:
+ specifier: ^11.2.0
+ version: 11.2.0
husky:
specifier: ^9.1.4
version: 9.1.4
@@ -162,6 +171,9 @@ importers:
typescript:
specifier: ^5.5.4
version: 5.5.4
+ vitest:
+ specifier: ^2.0.5
+ version: 2.0.5(@types/node@22.2.0)
packages:
@@ -710,6 +722,13 @@ packages:
cpu: [x64]
os: [win32]
+ '@sec-ant/readable-stream@0.4.1':
+ resolution: {integrity: sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==}
+
+ '@sindresorhus/merge-streams@4.0.0':
+ resolution: {integrity: sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==}
+ engines: {node: '>=18'}
+
'@types/eslint@8.56.11':
resolution: {integrity: sha512-sVBpJMf7UPo/wGecYOpk2aQya2VUGeHhe38WG7/mN5FufNSubf5VT9Uh9Uyp8/eLJpu1/tuhJ/qTo4mhSB4V4Q==}
@@ -719,9 +738,15 @@ packages:
'@types/estree@1.0.5':
resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==}
+ '@types/fs-extra@11.0.4':
+ resolution: {integrity: sha512-yTbItCNreRooED33qjunPthRcSjERP1r4MqCZc7wv0u2sUkzTFp45tgUfS5+r7FrZPdmCCNflLhVSP/o+SemsQ==}
+
'@types/json-schema@7.0.15':
resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==}
+ '@types/jsonfile@6.1.4':
+ resolution: {integrity: sha512-D5qGUYwjvnNNextdU59/+fI+spnwtTFmyQP0h+PfIOSkNfpU6AOICUOkm4i0OnSk+NyjdPJrxCDro0sJsWlRpQ==}
+
'@types/mdast@3.0.15':
resolution: {integrity: sha512-LnwD+mUEfxWMa1QpDraczIn6k0Ee3SMicuYSSzS6ZYl2gKS09EClnJYGd8Du6rfc5r/GZEk5o1mRb8TaTj03sQ==}
@@ -1490,6 +1515,10 @@ packages:
resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==}
engines: {node: '>=16.17'}
+ execa@9.3.1:
+ resolution: {integrity: sha512-gdhefCCNy/8tpH/2+ajP9IQc14vXchNdd0weyzSJEFURhRMGncQ+zKFxwjAufIewPEJm9BPOaJnvg2UtlH2gPQ==}
+ engines: {node: ^18.19.0 || >=20.5.0}
+
fast-deep-equal@3.1.3:
resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
@@ -1509,6 +1538,10 @@ packages:
fastq@1.17.1:
resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==}
+ figures@6.1.0:
+ resolution: {integrity: sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==}
+ engines: {node: '>=18'}
+
file-entry-cache@8.0.0:
resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==}
engines: {node: '>=16.0.0'}
@@ -1544,6 +1577,10 @@ packages:
resolution: {integrity: sha512-PXUUyLqrR2XCWICfv6ukppP96sdFwWbNEnfEMt7jNsISjMsvaLNinAHNDYyvkyU+SZG2BTSbT5NjG+vZslfGTA==}
engines: {node: '>=14'}
+ fs-extra@11.2.0:
+ resolution: {integrity: sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==}
+ engines: {node: '>=14.14'}
+
fs-minipass@2.1.0:
resolution: {integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==}
engines: {node: '>= 8'}
@@ -1574,6 +1611,10 @@ packages:
resolution: {integrity: sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==}
engines: {node: '>=16'}
+ get-stream@9.0.1:
+ resolution: {integrity: sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==}
+ engines: {node: '>=18'}
+
get-tsconfig@4.7.6:
resolution: {integrity: sha512-ZAqrLlu18NbDdRaHq+AKXzAmqIUPswPWKUchfytdAjiRFnCe5ojG2bstg6mRiZabkKfCoL/e98pbBELIV/YCeA==}
@@ -1641,6 +1682,10 @@ packages:
resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==}
engines: {node: '>=16.17.0'}
+ human-signals@8.0.0:
+ resolution: {integrity: sha512-/1/GPCpDUCCYwlERiYjxoczfP0zfvZMU/OWgQPMya9AbAE24vseigFdhAMObpc8Q4lc/kjutPfUddDYyAmejnA==}
+ engines: {node: '>=18.18.0'}
+
husky@9.1.4:
resolution: {integrity: sha512-bho94YyReb4JV7LYWRWxZ/xr6TtOTt8cMfmQ39MQYJ7f/YE268s3GdghGwi+y4zAeqewE5zYLvuhV0M0ijsDEA==}
engines: {node: '>=18'}
@@ -1733,6 +1778,10 @@ packages:
resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==}
engines: {node: '>=8'}
+ is-plain-obj@4.1.0:
+ resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==}
+ engines: {node: '>=12'}
+
is-stream@2.0.1:
resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==}
engines: {node: '>=8'}
@@ -1741,6 +1790,14 @@ packages:
resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==}
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
+ is-stream@4.0.1:
+ resolution: {integrity: sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==}
+ engines: {node: '>=18'}
+
+ is-unicode-supported@2.0.0:
+ resolution: {integrity: sha512-FRdAyx5lusK1iHG0TWpVtk9+1i+GjrzRffhDg4ovQ7mcidMQ6mj+MhKPmvh7Xwyv5gIS06ns49CA7Sqg7lC22Q==}
+ engines: {node: '>=18'}
+
is-wsl@3.1.0:
resolution: {integrity: sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==}
engines: {node: '>=16'}
@@ -1809,6 +1866,9 @@ packages:
jsonc-parser@3.3.1:
resolution: {integrity: sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==}
+ jsonfile@6.1.0:
+ resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==}
+
keyv@4.5.4:
resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==}
@@ -2081,6 +2141,10 @@ packages:
resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==}
engines: {node: '>=8'}
+ parse-ms@4.0.0:
+ resolution: {integrity: sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==}
+ engines: {node: '>=18'}
+
path-exists@4.0.0:
resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==}
engines: {node: '>=8'}
@@ -2184,6 +2248,10 @@ packages:
engines: {node: '>=14'}
hasBin: true
+ pretty-ms@9.1.0:
+ resolution: {integrity: sha512-o1piW0n3tgKIKCwk2vpM/vOV13zjJzvP37Ioze54YlTHE06m4tjEbzg9WsKkvTuyYln2DHjo5pY4qrZGI0otpw==}
+ engines: {node: '>=18'}
+
prompts@2.4.2:
resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==}
engines: {node: '>= 6'}
@@ -2386,6 +2454,10 @@ packages:
resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==}
engines: {node: '>=12'}
+ strip-final-newline@4.0.0:
+ resolution: {integrity: sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw==}
+ engines: {node: '>=18'}
+
strip-indent@3.0.0:
resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==}
engines: {node: '>=8'}
@@ -2560,6 +2632,10 @@ packages:
unist-util-stringify-position@2.0.3:
resolution: {integrity: sha512-3faScn5I+hy9VleOq/qNbAd6pAx7iH5jYBMS9I1HgQVijz/4mv5Bvw5iw1sC/90CODiKo81G/ps8AJrISn687g==}
+ universalify@2.0.1:
+ resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==}
+ engines: {node: '>= 10.0.0'}
+
update-browserslist-db@1.1.0:
resolution: {integrity: sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==}
hasBin: true
@@ -2710,13 +2786,16 @@ packages:
resolution: {integrity: sha512-b4JR1PFR10y1mKjhHY9LaGo6tmrgjit7hxVIeAmyMw3jegXR4dhYqLaQF5zMXZxY7tLpMyJeLjr1C4rLmkVe8g==}
engines: {node: '>=12.20'}
+ yoctocolors@2.1.1:
+ resolution: {integrity: sha512-GQHQqAopRhwU8Kt1DDM8NjibDXHC8eoh1erhGAJPEyveY9qqVeXvVikNKrDz69sHowPMorbPUrH/mx8c50eiBQ==}
+ engines: {node: '>=18'}
+
snapshots:
'@ampproject/remapping@2.3.0':
dependencies:
'@jridgewell/gen-mapping': 0.3.5
'@jridgewell/trace-mapping': 0.3.25
- optional: true
'@antfu/install-pkg@0.3.4':
dependencies:
@@ -3111,6 +3190,10 @@ snapshots:
'@rollup/rollup-win32-x64-msvc@4.20.0':
optional: true
+ '@sec-ant/readable-stream@0.4.1': {}
+
+ '@sindresorhus/merge-streams@4.0.0': {}
+
'@types/eslint@8.56.11':
dependencies:
'@types/estree': 1.0.5
@@ -3123,8 +3206,17 @@ snapshots:
'@types/estree@1.0.5': {}
+ '@types/fs-extra@11.0.4':
+ dependencies:
+ '@types/jsonfile': 6.1.4
+ '@types/node': 22.2.0
+
'@types/json-schema@7.0.15': {}
+ '@types/jsonfile@6.1.4':
+ dependencies:
+ '@types/node': 22.2.0
+
'@types/mdast@3.0.15':
dependencies:
'@types/unist': 2.0.10
@@ -3291,30 +3383,25 @@ snapshots:
'@vitest/utils': 2.0.5
chai: 5.1.1
tinyrainbow: 1.2.0
- optional: true
'@vitest/pretty-format@2.0.5':
dependencies:
tinyrainbow: 1.2.0
- optional: true
'@vitest/runner@2.0.5':
dependencies:
'@vitest/utils': 2.0.5
pathe: 1.1.2
- optional: true
'@vitest/snapshot@2.0.5':
dependencies:
'@vitest/pretty-format': 2.0.5
magic-string: 0.30.11
pathe: 1.1.2
- optional: true
'@vitest/spy@2.0.5':
dependencies:
tinyspy: 3.0.0
- optional: true
'@vitest/utils@2.0.5':
dependencies:
@@ -3322,7 +3409,6 @@ snapshots:
estree-walker: 3.0.3
loupe: 3.1.1
tinyrainbow: 1.2.0
- optional: true
'@voxpelli/config-array-find-files@0.1.2(@eslint/config-array@0.17.1)':
dependencies:
@@ -3405,8 +3491,7 @@ snapshots:
array-union@2.1.0: {}
- assertion-error@2.0.1:
- optional: true
+ assertion-error@2.0.1: {}
astro-eslint-parser@1.0.2(typescript@5.5.4):
dependencies:
@@ -3518,7 +3603,6 @@ snapshots:
deep-eql: 5.0.2
loupe: 3.1.1
pathval: 2.0.0
- optional: true
chalk@2.4.2:
dependencies:
@@ -3539,8 +3623,7 @@ snapshots:
character-reference-invalid@1.1.4: {}
- check-error@2.1.1:
- optional: true
+ check-error@2.1.1: {}
chokidar@3.6.0:
dependencies:
@@ -3625,8 +3708,7 @@ snapshots:
dependencies:
ms: 2.1.2
- deep-eql@5.0.2:
- optional: true
+ deep-eql@5.0.2: {}
deep-is@0.1.4: {}
@@ -4077,7 +4159,6 @@ snapshots:
estree-walker@3.0.3:
dependencies:
'@types/estree': 1.0.5
- optional: true
esutils@2.0.3: {}
@@ -4107,6 +4188,21 @@ snapshots:
signal-exit: 4.1.0
strip-final-newline: 3.0.0
+ execa@9.3.1:
+ dependencies:
+ '@sindresorhus/merge-streams': 4.0.0
+ cross-spawn: 7.0.3
+ figures: 6.1.0
+ get-stream: 9.0.1
+ human-signals: 8.0.0
+ is-plain-obj: 4.1.0
+ is-stream: 4.0.1
+ npm-run-path: 5.3.0
+ pretty-ms: 9.1.0
+ signal-exit: 4.1.0
+ strip-final-newline: 4.0.0
+ yoctocolors: 2.1.1
+
fast-deep-equal@3.1.3: {}
fast-diff@1.3.0: {}
@@ -4127,6 +4223,10 @@ snapshots:
dependencies:
reusify: 1.0.4
+ figures@6.1.0:
+ dependencies:
+ is-unicode-supported: 2.0.0
+
file-entry-cache@8.0.0:
dependencies:
flat-cache: 4.0.1
@@ -4165,6 +4265,12 @@ snapshots:
cross-spawn: 7.0.3
signal-exit: 4.1.0
+ fs-extra@11.2.0:
+ dependencies:
+ graceful-fs: 4.2.11
+ jsonfile: 6.1.0
+ universalify: 2.0.1
+
fs-minipass@2.1.0:
dependencies:
minipass: 3.3.6
@@ -4176,8 +4282,7 @@ snapshots:
get-east-asian-width@1.2.0: {}
- get-func-name@2.0.2:
- optional: true
+ get-func-name@2.0.2: {}
get-port-please@3.1.2: {}
@@ -4185,6 +4290,11 @@ snapshots:
get-stream@8.0.1: {}
+ get-stream@9.0.1:
+ dependencies:
+ '@sec-ant/readable-stream': 0.4.1
+ is-stream: 4.0.1
+
get-tsconfig@4.7.6:
dependencies:
resolve-pkg-maps: 1.0.0
@@ -4267,6 +4377,8 @@ snapshots:
human-signals@5.0.0: {}
+ human-signals@8.0.0: {}
+
husky@9.1.4: {}
ignore@5.3.1: {}
@@ -4344,10 +4456,16 @@ snapshots:
is-path-inside@3.0.3: {}
+ is-plain-obj@4.1.0: {}
+
is-stream@2.0.1: {}
is-stream@3.0.0: {}
+ is-stream@4.0.1: {}
+
+ is-unicode-supported@2.0.0: {}
+
is-wsl@3.1.0:
dependencies:
is-inside-container: 1.0.0
@@ -4402,6 +4520,12 @@ snapshots:
jsonc-parser@3.3.1: {}
+ jsonfile@6.1.0:
+ dependencies:
+ universalify: 2.0.1
+ optionalDependencies:
+ graceful-fs: 4.2.11
+
keyv@4.5.4:
dependencies:
json-buffer: 3.0.1
@@ -4477,7 +4601,6 @@ snapshots:
loupe@3.1.1:
dependencies:
get-func-name: 2.0.2
- optional: true
lru-cache@10.4.3: {}
@@ -4694,6 +4817,8 @@ snapshots:
json-parse-even-better-errors: 2.3.1
lines-and-columns: 1.2.4
+ parse-ms@4.0.0: {}
+
path-exists@4.0.0: {}
path-exists@5.0.0: {}
@@ -4713,8 +4838,7 @@ snapshots:
pathe@1.1.2: {}
- pathval@2.0.0:
- optional: true
+ pathval@2.0.0: {}
perfect-debounce@1.0.0: {}
@@ -4768,6 +4892,10 @@ snapshots:
prettier@3.3.3: {}
+ pretty-ms@9.1.0:
+ dependencies:
+ parse-ms: 4.0.0
+
prompts@2.4.2:
dependencies:
kleur: 3.0.3
@@ -4887,8 +5015,7 @@ snapshots:
shebang-regex@3.0.0: {}
- siginfo@2.0.0:
- optional: true
+ siginfo@2.0.0: {}
signal-exit@3.0.7: {}
@@ -4937,11 +5064,9 @@ snapshots:
stable-hash@0.0.4: {}
- stackback@0.0.2:
- optional: true
+ stackback@0.0.2: {}
- std-env@3.7.0:
- optional: true
+ std-env@3.7.0: {}
string-argv@0.3.2: {}
@@ -4975,6 +5100,8 @@ snapshots:
strip-final-newline@3.0.0: {}
+ strip-final-newline@4.0.0: {}
+
strip-indent@3.0.0:
dependencies:
min-indent: 1.0.1
@@ -5035,19 +5162,15 @@ snapshots:
dependencies:
any-promise: 1.3.0
- tinybench@2.9.0:
- optional: true
+ tinybench@2.9.0: {}
tinyexec@0.1.4: {}
- tinypool@1.0.0:
- optional: true
+ tinypool@1.0.0: {}
- tinyrainbow@1.2.0:
- optional: true
+ tinyrainbow@1.2.0: {}
- tinyspy@3.0.0:
- optional: true
+ tinyspy@3.0.0: {}
to-fast-properties@2.0.0: {}
@@ -5149,6 +5272,8 @@ snapshots:
dependencies:
'@types/unist': 2.0.10
+ universalify@2.0.1: {}
+
update-browserslist-db@1.1.0(browserslist@4.23.3):
dependencies:
browserslist: 4.23.3
@@ -5183,7 +5308,6 @@ snapshots:
- sugarss
- supports-color
- terser
- optional: true
vite@5.4.0(@types/node@22.2.0):
dependencies:
@@ -5193,7 +5317,6 @@ snapshots:
optionalDependencies:
'@types/node': 22.2.0
fsevents: 2.3.3
- optional: true
vitest@2.0.5(@types/node@22.2.0):
dependencies:
@@ -5227,7 +5350,6 @@ snapshots:
- sugarss
- supports-color
- terser
- optional: true
vue-eslint-parser@9.4.3(eslint@9.9.0(jiti@1.21.6)):
dependencies:
@@ -5258,7 +5380,6 @@ snapshots:
dependencies:
siginfo: 2.0.0
stackback: 0.0.2
- optional: true
word-wrap@1.2.5: {}
@@ -5297,3 +5418,5 @@ snapshots:
yocto-queue@0.1.0: {}
yocto-queue@1.1.1: {}
+
+ yoctocolors@2.1.1: {}
diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml
new file mode 100644
index 0000000..c186cf5
--- /dev/null
+++ b/pnpm-workspace.yaml
@@ -0,0 +1,2 @@
+packages:
+ - 'fixtures/*'
diff --git a/src/configs/formatters.ts b/src/configs/formatters.ts
index 25db28f..5b36579 100644
--- a/src/configs/formatters.ts
+++ b/src/configs/formatters.ts
@@ -22,7 +22,7 @@ import type { OptionsFormatters, TypedFlatConfigItem } from "../types";
import type { Options as PrettierOptions } from "prettier";
export async function formatters(
- options: OptionsFormatters | true = {},
+ options?: OptionsFormatters,
): Promise {
let prettierOptions: PrettierOptions = {
tabWidth: 2,
@@ -40,7 +40,7 @@ export async function formatters(
slidev: isPackageExists("@slidev/cli"),
xml: isPackageExists("@prettier/plugin-xml"),
};
- if (options === true) {
+ if (!options) {
options = {
...defaultOptions,
};
diff --git a/src/factory.ts b/src/factory.ts
index dca1356..0a581b4 100644
--- a/src/factory.ts
+++ b/src/factory.ts
@@ -27,7 +27,6 @@ import {
yaml,
} from "./configs";
import { interopDefault, isInEditorEnv } from "./utils";
-// import { formatters } from './configs/formatters'
import type { RuleOptions } from "./typegen";
import type {
Awaitable,
@@ -137,9 +136,14 @@ export function xwbx(
command(),
sortImport(),
- formatters(options.formatters ?? true),
);
-
+ if (options.formatters ?? true) {
+ configs.push(
+ formatters(
+ typeof options.formatters === "object" ? options.formatters : undefined,
+ ),
+ );
+ }
if (enableVue) {
componentExts.push("vue");
}
diff --git a/src/types.ts b/src/types.ts
index d2f3e9d..1a7e74e 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -353,8 +353,12 @@ export interface OptionsConfig
*
* Requires installing:
* - `eslint-plugin-format`
+ *
+ * When set to `true`, it will enable all formatters.
+ *
+ * @default true
*/
- formatters?: OptionsFormatters;
+ formatters?: boolean | OptionsFormatters;
/**
* Control to disable some rules in editors.
diff --git a/test/fixtures.test.ts b/test/fixtures.test.ts
new file mode 100644
index 0000000..ee104d8
--- /dev/null
+++ b/test/fixtures.test.ts
@@ -0,0 +1,94 @@
+import { join, resolve } from "node:path";
+import { execa } from "execa";
+import fg from "fast-glob";
+import fs from "fs-extra";
+import { afterAll, beforeAll, it } from "vitest";
+import type { OptionsConfig, TypedFlatConfigItem } from "../src/types";
+
+beforeAll(async () => {
+ await fs.rm("_fixtures", { recursive: true, force: true });
+});
+afterAll(async () => {
+ await fs.rm("_fixtures", { recursive: true, force: true });
+});
+
+runWithConfig("all", {
+ typescript: true,
+ vue: true,
+ astro: true,
+ formatters: true,
+});
+runWithConfig("no-style", {
+ typescript: true,
+ vue: true,
+ astro: true,
+ formatters: false,
+});
+runWithConfig("tab-single-quotes", {
+ typescript: true,
+ vue: true,
+ astro: true,
+ formatters: {
+ prettierOptions: {
+ useTabs: true,
+ singleQuote: true,
+ },
+ },
+});
+
+function runWithConfig(
+ name: string,
+ configs: OptionsConfig,
+ ...items: TypedFlatConfigItem[]
+) {
+ it.concurrent(
+ name,
+ async ({ expect }) => {
+ const from = resolve("fixtures/input");
+ const output = resolve("fixtures/output", name);
+ const target = resolve("_fixtures", name);
+
+ await fs.copy(from, target, {
+ filter: (src) => {
+ return !src.includes("node_modules");
+ },
+ });
+ await fs.writeFile(
+ join(target, "eslint.config.js"),
+ `
+// @eslint-disable
+import xwbx from '@xwbx/eslint-config'
+
+export default xwbx(
+ ${JSON.stringify(configs)},
+ ...${JSON.stringify(items) ?? []},
+)
+ `,
+ );
+
+ await execa("npx", ["eslint", ".", "--fix"], {
+ cwd: target,
+ stdio: "pipe",
+ });
+
+ const files = await fg("**/*", {
+ ignore: ["node_modules", "eslint.config.js"],
+ cwd: target,
+ });
+
+ await Promise.all(
+ files.map(async (file) => {
+ const content = await fs.readFile(join(target, file), "utf-8");
+ const source = await fs.readFile(join(from, file), "utf-8");
+ const outputPath = join(output, file);
+ if (content === source) {
+ if (fs.existsSync(outputPath)) await fs.remove(outputPath);
+ return;
+ }
+ await expect.soft(content).toMatchFileSnapshot(join(output, file));
+ }),
+ );
+ },
+ 30_000,
+ );
+}
diff --git a/test/types.ts b/test/types.ts
new file mode 100644
index 0000000..b5000c3
--- /dev/null
+++ b/test/types.ts
@@ -0,0 +1,6 @@
+import type { TypedFlatConfigItem } from "../src";
+import type { Linter } from "eslint";
+
+// Make sure they are compatible
+((): Linter.Config => ({}) as TypedFlatConfigItem)();
+((): TypedFlatConfigItem => ({}) as Linter.Config)();