Skip to content

Commit

Permalink
added new style rules
Browse files Browse the repository at this point in the history
  • Loading branch information
MP281X committed May 10, 2024
1 parent 2350304 commit f7dc30f
Show file tree
Hide file tree
Showing 11 changed files with 114 additions and 75 deletions.
7 changes: 4 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"type": "module",
"version": "1.1.13",
"version": "1.1.14",
"name": "@mp281x/shared-config",
"publishConfig": { "registry": "https://npm.pkg.github.com/@mp281x" },

Expand Down Expand Up @@ -34,9 +34,10 @@
"eslint": "9.2.0",
"@eslint/js": "9.2.0",
"typescript-eslint": "7.8.0",
"eslint-config-prettier": "9.1.0",
"eslint-plugin-svelte": "2.39.0",
"eslint-plugin-unicorn": "53.0.0",
"eslint-plugin-svelte": "2.39.0"
"eslint-config-prettier": "9.1.0",
"eslint-plugin-perfectionist": "2.10.0"
},

"peerDependencies": {
Expand Down
147 changes: 90 additions & 57 deletions src/eslint.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/* eslint-disable perfectionist/sort-imports */
/* eslint-disable @typescript-eslint/naming-convention */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */

// @ts-expect-error: no type definitions
import eslint from '@eslint/js'
Expand All @@ -11,6 +12,8 @@ import type { ConfigWithExtends } from 'typescript-eslint'
import prettier from 'eslint-config-prettier'
// @ts-expect-error: no type definitions
import unicorn from 'eslint-plugin-unicorn'
// @ts-expect-error: no type definitions
import perfectionist from 'eslint-plugin-perfectionist'

import svelte from 'eslint-plugin-svelte'
import svelteParser from 'svelte-eslint-parser'
Expand All @@ -21,90 +24,120 @@ export default ts.config(
{ ignores: parseConfig<string[]>('.gitignore') ?? [] },
// typescript
{
files: ['**/*.js', '**/*.jsx', '**/*.ts', '**/*.tsx', '**/*.svelte'],
extends: [...ts.configs.strictTypeChecked],
files: ['**/*.js', '**/*.jsx', '**/*.ts', '**/*.tsx', '**/*.svelte'],
rules: {
'@typescript-eslint/array-type': 'error', // use X[] instead of Array<X>
'@typescript-eslint/ban-types': 'error', // disable problematic types -> {}
'@typescript-eslint/consistent-generic-constructors': 'error', // specify types on constructor instead of on the variable
'@typescript-eslint/consistent-indexed-object-style': 'error', // use Record<string,string> instead of {[key: string]:string}
'@typescript-eslint/ban-ts-comment': ['error', { 'ts-expect-error': false }], // allow only the @ts-expect-error
'@typescript-eslint/consistent-type-assertions': ['error', { assertionStyle: 'as' }], // disable as (as const still work)
'@typescript-eslint/no-shadow': 'error', // disable variable in inner scope with the same name o another variable
'@typescript-eslint/array-type': 'error', // use X[] instead of Array<X>
'@typescript-eslint/no-unused-vars': 'off', // disable error for unused variables or params (already checked by typescript)
'@typescript-eslint/unbound-method': 'off', // cause errors with the typescript compiler api
'@typescript-eslint/consistent-type-definitions': ['error', 'type'], // disable interface
'@typescript-eslint/consistent-type-exports': 'error', // separate type export
'@typescript-eslint/no-unnecessary-boolean-literal-compare': 'off', // allow writing if (var1 === false) instead of only if (!var1)
'@typescript-eslint/no-confusing-void-expression': 'off', // enable returning void -> ()=>console.log("ok")
'@typescript-eslint/method-signature-style': 'error', // allow only arrow functions in types
'@typescript-eslint/prefer-for-of': 'error', // use for (const x of []) instead of normal for loop
'@typescript-eslint/no-unsafe-argument': 'off', // the rules doesn't work as expected
'@typescript-eslint/no-require-imports': 'error', // disable require
'@typescript-eslint/no-inferrable-types': 'error', // don't specify type for inferrable types
'@typescript-eslint/no-meaningless-void-operator': 'off', // allow returning void, return void console.log("ok")
'@typescript-eslint/no-non-null-assertion': 'off', // allow non null assestion
'@typescript-eslint/no-require-imports': 'error', // disable require
'@typescript-eslint/no-unnecessary-condition': ['error', { allowConstantLoopConditions: true }], // diable const condition exept for loops
'@typescript-eslint/prefer-for-of': 'error', // use for (const x of []) instead of normal for loop
'@typescript-eslint/prefer-function-type': 'error', // disable defining function with this { (): string } instead of ()=>string
'@typescript-eslint/method-signature-style': 'error', // allow only arrow functions in types
'@typescript-eslint/consistent-type-exports': 'error', // separate type export
'@typescript-eslint/no-confusing-void-expression': 'off', // enable returning void -> ()=>console.log("ok")
'@typescript-eslint/no-meaningless-void-operator': 'off', // allow returning void, return void console.log("ok")
'@typescript-eslint/require-array-sort-compare': 'error', // require compare function as sort argument
'@typescript-eslint/switch-exhaustiveness-check': 'error', // make switch check all cases
'@typescript-eslint/no-unused-vars': 'off', // disable error for unused variables or params (already checked by typescript)
'@typescript-eslint/consistent-generic-constructors': 'error', // specify types on constructor instead of on the variable
'@typescript-eslint/consistent-indexed-object-style': 'error', // use Record<string,string> instead of {[key: string]:string}
'@typescript-eslint/no-unnecessary-boolean-literal-compare': 'off', // allow writing if (var1 === false) instead of only if (!var1)
'@typescript-eslint/consistent-type-definitions': ['error', 'type'], // disable interface
'@typescript-eslint/ban-ts-comment': ['error', { 'ts-expect-error': false }], // allow only the @ts-expect-error
'@typescript-eslint/consistent-type-assertions': ['error', { assertionStyle: 'as' }], // disable as (as const still work)
'@typescript-eslint/restrict-template-expressions': ['error', { allowNumber: true }], // enable using number and other
'@typescript-eslint/no-unsafe-argument': 'off', // the rules doesn't work as expected
'@typescript-eslint/no-shadow': 'error', // disable variable in inner scope with the same name o another variable
'@typescript-eslint/no-unnecessary-condition': ['error', { allowConstantLoopConditions: true }], // diable const condition exept for loops

// enforce variable naming style
'@typescript-eslint/naming-convention': ['error', { selector: 'objectLiteralProperty', format: ['camelCase', 'snake_case', 'UPPER_CASE'] }]
}
},
// js/ts
{
files: ['**/*.js', '**/*.jsx', '**/*.ts', '**/*.tsx', '**/*.svelte'],
plugins: { unicorn, perfectionist },
extends: [prettier, eslint.configs.recommended],
plugins: { unicorn },
files: ['**/*.js', '**/*.jsx', '**/*.ts', '**/*.tsx', '**/*.svelte'],
rules: {
'no-var': 'error', // disable var keyword
'no-undef': 'off', // already checked by typescript
'no-constant-condition': 'off', // already checked by typescript
'no-unused-vars': 'off', // already checked by typescript
'spaced-comment': ['error', 'always'], // space after comment
'array-callback-return': ['error', { checkForEach: true, allowVoid: true, allowImplicit: false }], // check for return type in map, foreach, ...
'no-restricted-syntax': ['error', 'TryStatement > FinallyClause'], // disable try-finally
'no-promise-executor-return': 'error', // no return in custom promise
'no-self-compare': 'error', // don't compare variable to itself
'no-template-curly-in-string': 'error', // error if ${} it's used in a normal string (not template)
'curly': ['error', 'multi-or-nest'], // disable {} if it can stay inline
'eqeqeq': ['error', 'always'], // require triple =
'grouped-accessor-pairs': ['error', 'getBeforeSet'], // require getters and setters to be near each other
'no-array-constructor': 'error', // force consistent array of x element definitions
'no-var': 'error', // disable var keyword
'no-shadow': 'off', // already checked by the eslint typescript plugin
'no-bitwise': 'error', // error if using | or & instead of || or &&
'no-plusplus': 'error', // disable ++ and --
'no-sequences': 'error', // disable multiple operation inline separated by ,
'no-unused-vars': 'off', // already checked by typescript
'no-else-return': 'error', // disable else if the if has a return
'no-empty': ['error', { allowEmptyCatch: true }], // no empty block (catch excluded)
'no-extend-native': 'error', // don't add property to global object
'no-extra-boolean-cast': ['error', { enforceForLogicalOperands: true }], // don't use more than one ! before a variable
'no-implicit-coercion': 'error', // no implicit type conversion 1+"1" -> string
'no-multi-assign': 'error', // disable multi assing const a = b = 1
'no-negated-condition': 'error', // no else after negation in if. if (!var1)
'no-plusplus': 'error', // disable ++ and --
'no-self-compare': 'error', // don't compare variable to itself
'yoda': ['error', 'never'], // variable to check before literal -> var1 === true
'no-extend-native': 'error', // don't add property to global object
'no-return-assign': 'error', // no variable assignment in return
'no-sequences': 'error', // disable multiple operation inline separated by ,
'no-shadow': 'off', // already checked by the eslint typescript plugin
'object-shorthand': 'error', // disable object value with the same name as the key
'operator-assignment': 'error', // use x+=1 instead of x = x+1
'eqeqeq': ['error', 'always'], // require triple =
'symbol-description': 'error', // allow only sibols with description ok: Symbol("a"), err: Symbol()
'yoda': ['error', 'never'], // variable to check before literal -> var1 === true
'func-style': ['error', 'expression', { allowArrowFunctions: true }], // allow only const a = () =>{} or const a = function a() {}
'no-constant-condition': 'off', // already checked by typescript
'operator-assignment': 'error', // use x+=1 instead of x = x+1
'no-array-constructor': 'error', // force consistent array of x element definitions
'no-implicit-coercion': 'error', // no implicit type conversion 1+"1" -> string
'no-negated-condition': 'error', // no else after negation in if. if (!var1)
'curly': ['error', 'multi-or-nest'], // disable {} if it can stay inline
'no-promise-executor-return': 'error', // no return in custom promise
'spaced-comment': ['error', 'always'], // space after comment
'no-template-curly-in-string': 'error', // error if ${} it's used in a normal string (not template)
'arrow-body-style': ['error', 'as-needed'], // don't use {} for inline return
'no-empty': ['error', { allowEmptyCatch: true }], // no empty block (catch excluded)
'grouped-accessor-pairs': ['error', 'getBeforeSet'], // require getters and setters to be near each other
'no-restricted-syntax': ['error', 'TryStatement > FinallyClause'], // disable try-finally
'prefer-arrow-callback': ['error', { allowNamedFunctions: true }], // allow only arrow functions in callback
'func-style': ['error', 'expression', { allowArrowFunctions: true }], // allow only const a = () =>{} or const a = function a() {}
'no-extra-boolean-cast': ['error', { enforceForLogicalOperands: true }], // don't use more than one ! before a variable
'array-callback-return': ['error', { allowVoid: true, checkForEach: true, allowImplicit: false }], // check for return type in map, foreach, ...

// other js/ts rules from the unicorn package
'unicorn/consistent-destructuring': 'error', // consistent deconstruct (deconstruct all or nothing)
'unicorn/no-null': 'error', // disable null (it works inside of triple = if)
'unicorn/no-for-loop': 'error', // disable for loop when it can be replaced with a for-of
'unicorn/no-thenable': 'error', // disable .then
'unicorn/no-new-array': 'error', // creare new array of x elements only with Array.from({length: lenght}) instead of new Array(100)
'unicorn/error-message': 'error', // require message when creating an error new Error("msg")
'unicorn/new-for-builtins': 'error', // require the new keyword when necessary and removes it when is not
'unicorn/no-for-loop': 'error', // disable for loop when it can be replaced with a for-of
'unicorn/no-instanceof-array': 'error', // prevents bug when checking if a variable is an array
'unicorn/no-new-array': 'error', // creare new array of x elements only with Array.from({length: lenght}) instead of new Array(100)
'unicorn/no-null': 'error', // disable null (it works inside of triple = if)
'unicorn/no-thenable': 'error', // disable .then
'unicorn/consistent-destructuring': 'error', // consistent deconstruct (deconstruct all or nothing)
'unicorn/prefer-string-trim-start-end': 'error', // use trimStart/trimEnd instead of trimLeft/trimRight
'unicorn/prefer-string-starts-ends-with': 'error', // use "".startsWith or "".endsWith instead of regex
'unicorn/prefer-string-trim-start-end': 'error' // use trimStart/trimEnd instead of trimLeft/trimRight

// style rules
'perfectionist/sort-maps': ['error', { type: 'line-length' }],
'perfectionist/sort-classes': ['error', { type: 'line-length' }],
'perfectionist/sort-exports': ['error', { type: 'line-length' }],
'perfectionist/sort-jsx-props': ['error', { type: 'line-length' }],
'perfectionist/sort-intersection-types': ['error', { type: 'line-length' }],
'perfectionist/sort-union-types': ['error', { 'nullable-last': true, 'type': 'line-length' }],
'perfectionist/sort-array-includes': ['error', { 'spread-last': true, 'type': 'line-length' }],
'perfectionist/sort-named-exports': ['error', { 'type': 'line-length', 'group-kind': 'types-first' }],
'perfectionist/sort-named-imports': ['error', { 'type': 'line-length', 'group-kind': 'types-first' }],
'perfectionist/sort-object-types': ['error', { 'type': 'line-length', 'groups': ['multiline'], 'partition-by-new-line': true }],
// 'perfectionist/sort-objects': ['error', { 'type': 'line-length', 'partition-by-comment': true, 'partition-by-new-line': true }],
'perfectionist/sort-svelte-attributes': ['error', { type: 'line-length', groups: ['multiline', 'shorthand', 'svelte-shorthand'] }],
'perfectionist/sort-imports': [
'error',
{
'type': 'line-length',
'internal-pattern': ['$lib/**', '~/**'],
'groups': [
['builtin-type', 'external-type'],
['builtin', 'external'],

['internal-type', 'parent-type', 'sibling-type', 'index-type'],
['internal', 'parent', 'sibling', 'index'],

['side-effect', 'object', 'unknown', 'style', 'side-effect-style']
]
}
]
}
},
// svelte
Expand All @@ -114,15 +147,15 @@ export default ts.config(
extends: svelte.configs['flat/recommended'],
languageOptions: { parser: svelteParser, parserOptions: { parser: ts.parser } },
rules: {
'svelte/infinite-reactive-loop': 'error', // prevent reactivity bug
'svelte/no-export-load-in-svelte-module-in-kit-pages': 'error', // no function called load in script
'svelte/no-reactive-reassign': 'error', // don't readding derived reactive values
'svelte/no-store-async': 'error', // disable async await in stores
'svelte/sort-attributes': 'error', // html attributes needs to be sorted
'svelte/no-reactive-reassign': 'error', // don't readding derived reactive values
'svelte/no-useless-mustaches': 'error', // don't allow useless {}
'svelte/infinite-reactive-loop': 'error', // prevent reactivity bug
'svelte/valid-prop-names-in-kit-pages': 'error', // disable invalid exports in +page.svelte file
'svelte/block-lang': ['error', { enforceScriptPresent: true, script: ['ts'] }], // require lang="ts" in the script tag
'svelte/no-immutable-reactive-statements': 'error', // disable reactive statement for const values
'svelte/no-useless-mustaches': 'error', // don't allow useless {}
'svelte/sort-attributes': 'error', // html attributes needs to be sorted
'svelte/no-export-load-in-svelte-module-in-kit-pages': 'error', // no function called load in script
'svelte/block-lang': ['error', { script: ['ts'], enforceScriptPresent: true }], // require lang="ts" in the script tag

// rules that doesn't work in svelte 5
'@typescript-eslint/no-unsafe-call': 'off',
Expand Down
2 changes: 1 addition & 1 deletion src/lib/cliHandlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export const handleKeypress = () => {
// handle console clear and exit
readline.emitKeypressEvents(process.stdin)
process.stdin.setRawMode(true)
process.stdin.on('keypress', (_, key: { ctrl: boolean; name: string }) => {
process.stdin.on('keypress', (_, key: { name: string; ctrl: boolean }) => {
if (key.ctrl && key.name === 'c') return process.exit()
if (key.name === 'return') {
console.log('\n'.repeat(10000))
Expand Down
9 changes: 5 additions & 4 deletions src/lib/execCmd.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { log } from './logger.ts'
import { spawn } from 'child_process'
import fs from 'fs'
import { spawn } from 'child_process'

import { log } from './logger.ts'

export const getPackageManager = () => {
const currentDir = fs.readdirSync('.')
Expand All @@ -15,7 +16,7 @@ export const getPackageManager = () => {

export const asyncCommands: Promise<void>[] = []

type ExecCmd = { title: string; cmd: string[]; customCmd?: string; mode: 'sync' | 'async'; cwd?: string }
type ExecCmd = { cwd?: string; title: string; cmd: string[]; customCmd?: string; mode: 'sync' | 'async' }
export const execCmd = async ({ title, cmd, customCmd, mode, cwd }: ExecCmd) => {
const execPromise = new Promise<void>((resolve, _) => {
const output = spawn(customCmd ?? getPackageManager(), cmd, {
Expand All @@ -40,7 +41,7 @@ export const execCmd = async ({ title, cmd, customCmd, mode, cwd }: ExecCmd) =>
return void asyncCommands.push(execPromise)
}

type ReadLogFile = { title: string; cwd: string }
type ReadLogFile = { cwd: string; title: string }
export const readLogFile = async ({ title, cwd }: ReadLogFile) => {
// clear the log file
const logFile = `${cwd}/lsp-plugin.log`
Expand Down
1 change: 1 addition & 0 deletions src/lib/findGlob.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ declare module 'node:fs' {
}

import fs from 'node:fs'

import { parseConfig } from './parseConfig'

const gitIgnore = parseConfig<string[]>(`${process.cwd()}/.gitignore`) ?? []
Expand Down
5 changes: 3 additions & 2 deletions src/lib/findProjects.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import fs from 'node:fs'
import { parseConfig } from './parseConfig.ts'

import { findGlob } from './findGlob.ts'
import { parseConfig } from './parseConfig.ts'

const getWorkspaceProjects = (dir: string) => {
const globs: string[] = []
Expand All @@ -15,7 +16,7 @@ const getWorkspaceProjects = (dir: string) => {
return globs.flatMap((glob) => findGlob(glob, { cwd: dir })).filter((path) => fs.existsSync(`${dir}/${path}/package.json`))
}

export type Project = { name: string; scripts: string[]; lspPlugin: boolean; cwd: string; type: 'svelte' | 'node' }
export type Project = { cwd: string; name: string; scripts: string[]; lspPlugin: boolean; type: 'node' | 'svelte' }
// find all the projects in a the monorepo/repo
export const findProjects = (dir: string = process.cwd()): Project[] => {
const projects = getWorkspaceProjects(dir).flatMap((project) => findProjects(project))
Expand Down
4 changes: 2 additions & 2 deletions src/lib/logger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,14 +113,14 @@ const vitestLogFormatter = (input: string, printLog: (txt: string) => void) => {

const parseVitestOutput = (input: string): { name: string; errors: string[] }[] => {
type VitestJSON = {
success: boolean
testResults: [
{
name: string
status: 'passed' | 'failed'
assertionResults: [{ status: 'passed' | 'failed'; title: string; failureMessages: string[] }]
assertionResults: [{ title: string; failureMessages: string[]; status: 'passed' | 'failed' }]
}
]
success: boolean
}
const vitestData = JSON.parse(input) as VitestJSON
if (vitestData.success) return []
Expand Down
2 changes: 1 addition & 1 deletion src/lib/parseConfig.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import fs from 'node:fs'
import yaml from 'yaml'
import fs from 'node:fs'

// parse json/yaml/.gitignore and add the custom return type
export const parseConfig = <Res>(path: string): Res | undefined => {
Expand Down
Loading

0 comments on commit f7dc30f

Please sign in to comment.