Skip to content

feat: migrate to TypeScript with 100% compatibility #1179

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Apr 18, 2025
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .all-contributorsrc
Original file line number Diff line number Diff line change
Expand Up @@ -346,7 +346,10 @@
"name": "Jonathan Rehm",
"avatar_url": "https://avatars.githubusercontent.com/u/999845?v=4",
"profile": "https://jonathan.rehm.me/",
"contributions": ["bug", "code"]
"contributions": [
"bug",
"code"
]
},
{
"login": "gwhitney",
Expand Down
5 changes: 0 additions & 5 deletions .eslintignore

This file was deleted.

30 changes: 0 additions & 30 deletions .eslintrc.js

This file was deleted.

28 changes: 14 additions & 14 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,21 +21,21 @@ jobs:
fail-fast: false

steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v4

- name: Use Node.js ${{ matrix.node }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}
cache: yarn
- name: Use Node.js ${{ matrix.node }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}
cache: yarn

- name: Install dependencies
run: yarn --immutable
- name: Install dependencies
run: yarn --immutable

- name: Lint, test, and build
run: yarn start validate
- name: Lint, test, and build
run: yarn start validate

- name: Check test coverage
uses: codecov/codecov-action@v5
with:
token: ${{ secrets.CODECOV_TOKEN }} # not required for public repos
- name: Check test coverage
uses: codecov/codecov-action@v5
with:
token: ${{ secrets.CODECOV_TOKEN }} # not required for public repos
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ dist
.yarn/*
!.yarn/releases
!.yarn/plugins
!test/fixtures/paths/node_modules
5 changes: 0 additions & 5 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -1,6 +1 @@
.yarn
node_modules
.nyc_output
coverage
dist
tests/fixtures/
5 changes: 0 additions & 5 deletions .prettierrc.json

This file was deleted.

21 changes: 21 additions & 0 deletions .prettierrc.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// @ts-check

/** @import { Config, Plugin } from 'prettier' */

import base from '@1stg/prettier-config/semi';
import * as jsdoc from 'prettier-plugin-jsdoc';
import * as jsdocType from 'prettier-plugin-jsdoc-type';
import * as svelte from 'prettier-plugin-svelte';

/** @type {Config} */
const config = {
...base,
plugins: [
.../** @type {Plugin[]} */ (base.plugins),
jsdoc,
jsdocType,
svelte,
],
};

export default config;
2 changes: 1 addition & 1 deletion .yarnrc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@ nodeLinker: node-modules
plugins:
- checksum: 37b2361b1502b2054e6779788c0e9bdd6a90ce49852a8cad2feda79b0614ec94f06fb6e78951f5f95429c610d7934dd077caa47413a0227378a102c55161616d
path: .yarn/plugins/plugin-prepare-lifecycle.cjs
spec: "https://github.com/un-es/yarn-plugin-prepare-lifecycle/releases/download/v0.0.1/index.js"
spec: 'https://github.com/un-es/yarn-plugin-prepare-lifecycle/releases/download/v0.0.1/index.js'

yarnPath: .yarn/releases/yarn-4.9.1.cjs
File renamed without changes.
17 changes: 9 additions & 8 deletions other/EXAMPLES.md → EXAMPLES.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,20 +23,21 @@ gulp.task('js:format', function () {

```js
var through = require('through2');
var PluginError = require('plugin-error');
var prettierEslint = require('prettier-eslint');

const options = {
eslintConfig: {
parserOptions: {
ecmaVersion: 7
ecmaVersion: 7,
},
rules: {
semi: ['error', 'never']
}
semi: ['error', 'never'],
},
},
prettierOptions: {
bracketSpacing: true
}
bracketSpacing: true,
},
};

module.exports = function () {
Expand All @@ -49,14 +50,14 @@ module.exports = function () {

if (file.isStream()) {
return callback(
new utils.PluginError('prettier-eslint', "doesn't support Streams")
new PluginError('prettier-eslint', "doesn't support Streams"),
);
}

const sourceCode = file.contents.toString();
const formatted = prettierEslint({
...config,
text: sourceCode
...options,
text: sourceCode,
});

file.contents = new Buffer(formatted, encoding);
Expand Down
26 changes: 13 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ npm install --save-dev prettier-eslint

### Example

```javascript
```js
const format = require('prettier-eslint');

// notice, no semicolon in the original text
Expand All @@ -58,18 +58,18 @@ const options = {
text: sourceCode,
eslintConfig: {
parserOptions: {
ecmaVersion: 7
ecmaVersion: 7,
},
rules: {
semi: ['error', 'never']
}
semi: ['error', 'never'],
},
},
prettierOptions: {
bracketSpacing: true
bracketSpacing: true,
},
fallbackPrettierOptions: {
singleQuote: false
}
singleQuote: false,
},
};

const formatted = await format(options);
Expand Down Expand Up @@ -150,15 +150,15 @@ This allows you to use `eslint` to look for bugs and/or bad practices, and use

## Capturing ESLint messages

```javascript
```js
const { analyze } = require('prettier-eslint');

const text = 'var x = 0;';
const result = await analyze({
text,
eslintConfig: {
rules: { 'no-var': 'error' }
}
rules: { 'no-var': 'error' },
},
});
console.log(result.messages);
```
Expand Down Expand Up @@ -210,7 +210,7 @@ the inference is done for each option (what rule(s) are referenced, etc.) and
ESLint disabled entirely via `/* eslint-disable */` then prettier options will
fall back to the `prettier` defaults:

```javascript
```js
{
printWidth: 80,
tabWidth: 2,
Expand Down Expand Up @@ -291,8 +291,8 @@ solutions.
- [`prettier-atom`][atom-plugin] - Atom plugin (check the "ESlint integration"
checkbox in settings)
- [`vs-code-prettier-eslint`][vscode-plugin] - Visual Studio Code plugin
- [`eslint-plugin-prettier`](https://github.com/not-an-aardvark/eslint-plugin-prettier) -
ESLint plugin. While prettier-eslint uses `eslint --fix` to change the output of `prettier`, eslint-plugin-prettier keeps the `prettier` output as-is and integrates it with the regular ESLint workflow.
- [`eslint-plugin-prettier`](https://github.com/prettier/eslint-plugin-prettier) -
ESLint plugin. While `prettier-eslint` uses `eslint --fix` to change the output of `prettier`, `eslint-plugin-prettier` keeps the `prettier` output as-is and integrates it with the regular ESLint workflow.
- [`prettier-eslint-webpack-plugin`](https://github.com/danielterwiel/prettier-eslint-webpack-plugin) -
Prettier ESlint Webpack Plugin

Expand Down
104 changes: 104 additions & 0 deletions __mocks__/eslint.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
/**
* This mock file is so eslint doesn't attempt to actually search around the
* file system for stuff, we can not use `.ts` because it's called by
* `test/fixtures/node_modules/eslint/index.js`
*/

import eslint_, { type ESLint as ESLint_ } from 'eslint';

import type { ESLintLintText, MockESLint } from '../mock.js';

import type { ESLintConfig } from 'prettier-eslint';

const eslint = jest.requireActual<typeof eslint_>('eslint');

const { ESLint } = eslint;

const mockCalculateConfigForFileSpy = jest.fn(mockCalculateConfigForFile);

Object.assign(mockCalculateConfigForFileSpy, { overrides: {} });

const mockLintTextSpy = jest.fn(mockLintText);

export = Object.assign(eslint, {
ESLint: jest.fn(MockESLint),
mock: {
calculateConfigForFile: mockCalculateConfigForFileSpy,
lintText: mockLintTextSpy,
},
});

function MockESLint(options: ESLint_.Options): MockESLint {
globalThis.__PRETTIER_ESLINT_TEST_STATE__.eslintPath = __filename;
const eslintInstance = new ESLint(options) as MockESLint;
eslintInstance.calculateConfigForFile = mockCalculateConfigForFileSpy;
// eslint-disable-next-line @typescript-eslint/unbound-method
eslintInstance._originalLintText = eslintInstance.lintText;
eslintInstance.lintText = mockLintTextSpy;
return eslintInstance;
}

MockESLint.prototype = Object.create(ESLint.prototype) as ESLint_;

/**
* @throws If `throwError` is specifically set on the spy, or if the filePath is
* not handled
*/
// eslint-disable-next-line @typescript-eslint/require-await
async function mockCalculateConfigForFile(
filePath: string,
): Promise<ESLintConfig> {
if (
'throwError' in mockCalculateConfigForFileSpy &&
mockCalculateConfigForFileSpy.throwError instanceof Error
) {
throw mockCalculateConfigForFileSpy.throwError;
}
if (!filePath) {
return {
rules: {},
};
}
if (filePath.includes('default-config')) {
return {
rules: {
semi: [2, 'never'],
'max-len': [2, 120, 2],
indent: [2, 2, { SwitchCase: 1 }],
quotes: [
2,
'single',
{ avoidEscape: true, allowTemplateLiterals: true },
],
'comma-dangle': [
2,
{
arrays: 'always-multiline',
objects: 'always-multiline',
imports: 'always-multiline',
exports: 'always-multiline',
functions: 'always-multiline',
},
],
'arrow-parens': [2, 'as-needed'],
},
};
}
if (filePath.includes('fixtures/paths')) {
return { rules: {} };
}
throw new Error(
`Your mock filePath (${filePath})` +
' does not have a handler for finding the config',
);
}

function mockLintText(this: MockESLint, ...args: Parameters<ESLintLintText>) {
if (
'throwError' in mockLintTextSpy &&
mockLintTextSpy.throwError instanceof Error
) {
throw mockLintTextSpy.throwError;
}
return this._originalLintText(...args);
}
19 changes: 19 additions & 0 deletions __mocks__/fs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import type Fs_ from 'node:fs';

type Fs = typeof Fs_;

const fs = jest.requireActual<Fs>('fs');

// eslint-disable-next-line prefer-object-spread -- typing issue
export = Object.assign({}, fs, {
readFileSync: jest.fn((filename: string) => {
if (filename.endsWith('package.json')) {
return '{"name": "fake", "version": "0.0.0", "prettier": {}}';
}
if (/\.[jt]s$/.test(filename)) {
return 'var fake = true';
}

return '';
}),
});
Loading
Loading