mirror of
https://github.com/hanzoai/sign.git
synced 2026-10-21 01:21:51 +00:00
fix(lint): read the config the way ESLint 10 reads one
ESLint was raised to 10, which reads eslint.config.* and nothing else. This repo still said it all in .eslintrc.cjs and .eslintignore, so every lint run ended at "couldn't find an eslint.config file" — and the pre-commit hook runs eslint over staged sources, so no commit touching a .ts file could be made. The config is the same config in the flat spelling: every rule, every ignore and the __tests__ override carried over, and the two legacy files are gone rather than left beside it to disagree. The transaction rule (inside $transaction, only `tx`) keeps both its callers. transaction.cjs is a whole flat config, so `lint:tx` still hands it to eslint alone — with --no-config-lookup, since --no-eslintrc and --ext no longer exist — and the shared config spreads it in. Its test now gives the linter the array AS SHIPPED instead of re-stating the parser beside a copy of the rules: a copy keeps passing after the original stops selecting files. 10 of 10, and `lint:tx` is clean over packages and apps. eslint-plugin-react finds the React version by asking the rule context for a filename through an API ESLint 10 removed, so detection threw on the first file. The version is stated, which never takes that path. Co-authored-by: Hanzo Dev <[email protected]>
This commit is contained in:
@@ -1,8 +0,0 @@
|
||||
# Config files
|
||||
*.config.js
|
||||
*.config.cjs
|
||||
|
||||
# Statically hosted javascript files
|
||||
apps/*/public/*.js
|
||||
apps/*/public/*.cjs
|
||||
scripts/
|
||||
@@ -1,16 +0,0 @@
|
||||
/** @type {import('eslint').Linter.Config} */
|
||||
module.exports = {
|
||||
root: true,
|
||||
extends: [require.resolve('@hanzo/esign-eslint-config')],
|
||||
rules: {
|
||||
'@next/next/no-img-element': 'off',
|
||||
'no-unreachable': 'error',
|
||||
'react-hooks/exhaustive-deps': 'off',
|
||||
},
|
||||
settings: {
|
||||
next: {
|
||||
rootDir: ['apps/*/'],
|
||||
},
|
||||
},
|
||||
ignorePatterns: ['lingui.config.ts', 'packages/lib/translations/**/*.js'],
|
||||
};
|
||||
@@ -0,0 +1,40 @@
|
||||
const shared = require('@hanzo/esign-eslint-config');
|
||||
|
||||
/** @type {import('eslint').Linter.Config[]} */
|
||||
module.exports = [
|
||||
{
|
||||
// What is never linted. These were `.eslintignore` and `ignorePatterns`; a
|
||||
// flat config keeps them in the one file that holds everything else.
|
||||
ignores: [
|
||||
// Config files
|
||||
'**/*.config.js',
|
||||
'**/*.config.cjs',
|
||||
'**/*.config.mjs',
|
||||
// Statically hosted javascript files
|
||||
'apps/*/public/*.js',
|
||||
'apps/*/public/*.cjs',
|
||||
'scripts/**',
|
||||
'**/lingui.config.ts',
|
||||
'packages/lib/translations/**/*.js',
|
||||
// Generated
|
||||
'packages/prisma/generated/**',
|
||||
],
|
||||
},
|
||||
|
||||
...shared,
|
||||
|
||||
{
|
||||
settings: {
|
||||
next: { rootDir: ['apps/*/'] },
|
||||
// Stated, not detected. eslint-plugin-react finds the version by asking the
|
||||
// rule context for a filename through an API ESLint 10 removed, so detection
|
||||
// throws on the first file; a declared version never takes that path.
|
||||
react: { version: '19.2.8' },
|
||||
},
|
||||
rules: {
|
||||
'@next/next/no-img-element': 'off',
|
||||
'no-unreachable': 'error',
|
||||
'react-hooks/exhaustive-deps': 'off',
|
||||
},
|
||||
},
|
||||
];
|
||||
@@ -2,22 +2,16 @@ const assert = require('node:assert/strict');
|
||||
const { test } = require('node:test');
|
||||
|
||||
const { Linter } = require('eslint');
|
||||
const parser = require('@typescript-eslint/parser');
|
||||
|
||||
const config = require('../transaction.cjs');
|
||||
|
||||
const linter = new Linter();
|
||||
linter.defineParser('ts', parser);
|
||||
// The config AS SHIPPED, handed to the linter whole: the same array `lint:tx`
|
||||
// gives eslint. Reading the rules out of it and re-stating the parser here would
|
||||
// test a copy, and a copy keeps passing after the original stops selecting files.
|
||||
const linter = new Linter({ configType: 'flat' });
|
||||
|
||||
/** @param {string} code */
|
||||
const check = (code) =>
|
||||
linter
|
||||
.verify(code, {
|
||||
parser: 'ts',
|
||||
parserOptions: config.parserOptions,
|
||||
rules: config.rules,
|
||||
})
|
||||
.map((m) => m.message);
|
||||
const check = (code) => linter.verify(code, config, { filename: 'probe.ts' }).map((m) => m.message);
|
||||
|
||||
test('a transaction that only touches tx is clean', () => {
|
||||
assert.deepEqual(
|
||||
|
||||
@@ -1,99 +1,109 @@
|
||||
module.exports = {
|
||||
extends: [
|
||||
require.resolve('./transaction.cjs'),
|
||||
'next',
|
||||
'turbo',
|
||||
'eslint:recommended',
|
||||
'plugin:@typescript-eslint/recommended',
|
||||
],
|
||||
const js = require('@eslint/js');
|
||||
const globals = require('globals');
|
||||
const next = require('eslint-config-next');
|
||||
const turbo = require('eslint-config-turbo/flat');
|
||||
const unusedImports = require('eslint-plugin-unused-imports');
|
||||
const tseslint = require('typescript-eslint');
|
||||
|
||||
plugins: ['unused-imports'],
|
||||
const transaction = require('./transaction.cjs');
|
||||
|
||||
env: {
|
||||
es2022: true,
|
||||
node: true,
|
||||
browser: true,
|
||||
},
|
||||
const source = ['**/*.ts', '**/*.tsx'];
|
||||
|
||||
parser: '@typescript-eslint/parser',
|
||||
// The shared lint config, as ESLint 10 reads one: an array, in order, later
|
||||
// entries winning. Everything the eslintrc form said is here — it changed
|
||||
// spelling, not meaning.
|
||||
module.exports = [
|
||||
...transaction,
|
||||
...next,
|
||||
...(turbo.default ?? turbo),
|
||||
js.configs.recommended,
|
||||
...tseslint.configs.recommended,
|
||||
|
||||
parserOptions: {
|
||||
tsconfigRootDir: __dirname,
|
||||
project: ['../../tsconfig.eslint.json'],
|
||||
ecmaVersion: 2022,
|
||||
ecmaFeatures: {
|
||||
jsx: true,
|
||||
},
|
||||
sourceType: 'module',
|
||||
},
|
||||
{
|
||||
name: 'esign/source',
|
||||
files: source,
|
||||
|
||||
rules: {
|
||||
'@next/next/no-html-link-for-pages': 'off',
|
||||
'react/no-unescaped-entities': 'off',
|
||||
|
||||
'@typescript-eslint/no-unused-vars': 'off',
|
||||
'unused-imports/no-unused-imports': 'warn',
|
||||
'unused-imports/no-unused-vars': [
|
||||
'warn',
|
||||
{
|
||||
vars: 'all',
|
||||
varsIgnorePattern: '^_',
|
||||
args: 'after-used',
|
||||
argsIgnorePattern: '^_',
|
||||
destructuredArrayIgnorePattern: '^_',
|
||||
languageOptions: {
|
||||
parser: tseslint.parser,
|
||||
parserOptions: {
|
||||
tsconfigRootDir: __dirname,
|
||||
project: ['../../tsconfig.eslint.json'],
|
||||
ecmaVersion: 2022,
|
||||
ecmaFeatures: { jsx: true },
|
||||
sourceType: 'module',
|
||||
},
|
||||
],
|
||||
globals: { ...globals.es2022, ...globals.node, ...globals.browser },
|
||||
},
|
||||
|
||||
'no-multi-spaces': [
|
||||
'error',
|
||||
{
|
||||
ignoreEOLComments: false,
|
||||
exceptions: {
|
||||
BinaryExpression: false,
|
||||
VariableDeclarator: false,
|
||||
ImportDeclaration: false,
|
||||
Property: false,
|
||||
plugins: { 'unused-imports': unusedImports },
|
||||
|
||||
rules: {
|
||||
'@next/next/no-html-link-for-pages': 'off',
|
||||
'react/no-unescaped-entities': 'off',
|
||||
|
||||
'@typescript-eslint/no-unused-vars': 'off',
|
||||
'unused-imports/no-unused-imports': 'warn',
|
||||
'unused-imports/no-unused-vars': [
|
||||
'warn',
|
||||
{
|
||||
vars: 'all',
|
||||
varsIgnorePattern: '^_',
|
||||
args: 'after-used',
|
||||
argsIgnorePattern: '^_',
|
||||
destructuredArrayIgnorePattern: '^_',
|
||||
},
|
||||
},
|
||||
],
|
||||
],
|
||||
|
||||
// Safety with promises so we aren't running with scissors
|
||||
'no-promise-executor-return': 'error',
|
||||
'prefer-promise-reject-errors': 'error',
|
||||
'require-atomic-updates': 'error',
|
||||
'@typescript-eslint/no-floating-promises': 'error',
|
||||
'@typescript-eslint/no-misused-promises': [
|
||||
'error',
|
||||
{ checksVoidReturn: { attributes: false } },
|
||||
],
|
||||
'@typescript-eslint/promise-function-async': 'error',
|
||||
'@typescript-eslint/require-await': 'error',
|
||||
'no-multi-spaces': [
|
||||
'error',
|
||||
{
|
||||
ignoreEOLComments: false,
|
||||
exceptions: {
|
||||
BinaryExpression: false,
|
||||
VariableDeclarator: false,
|
||||
ImportDeclaration: false,
|
||||
Property: false,
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
// We never want to use `as` but are required to on occasion to handle
|
||||
// shortcomings in third-party and generated types.
|
||||
//
|
||||
// To handle this we want this rule to catch usages and highlight them as
|
||||
// warnings so we can write appropriate interfaces and guards later.
|
||||
'@typescript-eslint/consistent-type-assertions': ['warn', { assertionStyle: 'never' }],
|
||||
// Safety with promises so we aren't running with scissors
|
||||
'no-promise-executor-return': 'error',
|
||||
'prefer-promise-reject-errors': 'error',
|
||||
'require-atomic-updates': 'error',
|
||||
'@typescript-eslint/no-floating-promises': 'error',
|
||||
'@typescript-eslint/no-misused-promises': [
|
||||
'error',
|
||||
{ checksVoidReturn: { attributes: false } },
|
||||
],
|
||||
'@typescript-eslint/promise-function-async': 'error',
|
||||
'@typescript-eslint/require-await': 'error',
|
||||
|
||||
'@typescript-eslint/consistent-type-imports': [
|
||||
'warn',
|
||||
{
|
||||
prefer: 'type-imports',
|
||||
fixStyle: 'separate-type-imports',
|
||||
disallowTypeAnnotations: false,
|
||||
},
|
||||
],
|
||||
// We never want to use `as` but are required to on occasion to handle
|
||||
// shortcomings in third-party and generated types.
|
||||
//
|
||||
// To handle this we want this rule to catch usages and highlight them as
|
||||
// warnings so we can write appropriate interfaces and guards later.
|
||||
'@typescript-eslint/consistent-type-assertions': ['warn', { assertionStyle: 'never' }],
|
||||
|
||||
'@typescript-eslint/consistent-type-imports': [
|
||||
'warn',
|
||||
{
|
||||
prefer: 'type-imports',
|
||||
fixStyle: 'separate-type-imports',
|
||||
disallowTypeAnnotations: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
|
||||
overrides: [
|
||||
{
|
||||
// `node:test` awaits the promise a describe/test call returns; a test
|
||||
// file states its cases rather than chaining them.
|
||||
files: ['**/__tests__/**/*.ts'],
|
||||
rules: {
|
||||
'@typescript-eslint/no-floating-promises': 'off',
|
||||
},
|
||||
{
|
||||
// `node:test` awaits the promise a describe/test call returns; a test
|
||||
// file states its cases rather than chaining them.
|
||||
name: 'esign/tests',
|
||||
files: ['**/__tests__/**/*.ts'],
|
||||
rules: {
|
||||
'@typescript-eslint/no-floating-promises': 'off',
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
];
|
||||
|
||||
@@ -68,18 +68,27 @@ const restricted = [
|
||||
},
|
||||
];
|
||||
|
||||
module.exports = {
|
||||
parser: '@typescript-eslint/parser',
|
||||
const parser = require('@typescript-eslint/parser');
|
||||
|
||||
parserOptions: {
|
||||
ecmaVersion: 2022,
|
||||
sourceType: 'module',
|
||||
ecmaFeatures: { jsx: true },
|
||||
// A flat config, and a whole one: `lint:tx` hands this file to eslint alone, so
|
||||
// it says for itself which files it reads and which it never does.
|
||||
module.exports = [
|
||||
{ ignores: ['**/build/**', '**/dist/**', '**/.react-router/**', '**/node_modules/**'] },
|
||||
{
|
||||
name: 'esign/transaction',
|
||||
files: ['**/*.ts', '**/*.tsx'],
|
||||
|
||||
languageOptions: {
|
||||
parser,
|
||||
parserOptions: {
|
||||
ecmaVersion: 2022,
|
||||
sourceType: 'module',
|
||||
ecmaFeatures: { jsx: true },
|
||||
},
|
||||
},
|
||||
|
||||
rules: {
|
||||
'no-restricted-syntax': ['error', ...restricted],
|
||||
},
|
||||
},
|
||||
|
||||
ignorePatterns: ['build', 'dist', '.react-router'],
|
||||
|
||||
rules: {
|
||||
'no-restricted-syntax': ['error', ...restricted],
|
||||
},
|
||||
};
|
||||
];
|
||||
|
||||
Reference in New Issue
Block a user