-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
de8883a
commit 411cac8
Showing
54 changed files
with
6,253 additions
and
700 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import { toThisDir } from '@noshiro/mono-utils'; | ||
import * as nodePath from 'node:path'; | ||
import { defineConfig } from 'vitest/config'; | ||
|
||
const thisDir: string = toThisDir(import.meta.url); | ||
|
||
// https://github.com/vitest-dev/vitest/blob/v1.5.0/test/import-meta/vite.config.ts | ||
export default defineConfig({ | ||
test: { | ||
globals: true, | ||
dir: nodePath.resolve(thisDir, '../src'), | ||
includeSource: [nodePath.resolve(thisDir, '../src/**/*.mts')], | ||
typecheck: { | ||
tsconfig: nodePath.resolve(thisDir, 'tsconfig.test.json'), | ||
}, | ||
}, | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
export * from './total-functions/index.mjs'; | ||
export * from './tree-shakable/index.mjs'; |
8 changes: 8 additions & 0 deletions
8
packages/eslint-configs/src/plugins/total-functions/index.mts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
import { type Plugin } from '../../types/index.mjs'; | ||
import { rules } from './rules/index.mjs'; | ||
|
||
// forked from https://github.com/danielnixon/eslint-plugin-total-functions v7.1.0 | ||
|
||
export const eslintPluginTotalFunctions: Omit<Plugin, 'configs'> = { | ||
rules, | ||
} as const; |
79 changes: 79 additions & 0 deletions
79
packages/eslint-configs/src/plugins/total-functions/rules/common.mts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
import { | ||
AST_NODE_TYPES, | ||
ESLintUtils, | ||
type TSESTree, | ||
} from '@typescript-eslint/utils'; | ||
import { unionTypeParts } from 'tsutils'; | ||
import { type Type, type TypeChecker } from 'typescript'; | ||
|
||
export const createRule = ESLintUtils.RuleCreator( | ||
() => 'https://github.com/danielnixon/eslint-plugin-total-functions', | ||
); | ||
|
||
export const typeSymbolName = (type: Type): string | undefined => { | ||
try { | ||
// HACK despite what the type suggests, symbol can in fact be undefined | ||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition | ||
return type?.symbol?.name; | ||
} catch { | ||
// Accessing symbol can throw for reasons I don't fully understand. | ||
return undefined; | ||
} | ||
}; | ||
|
||
export type TypePair = Readonly<{ | ||
destinationType: Type; | ||
sourceType: Type; | ||
}>; | ||
|
||
/** | ||
* Breaks the supplied types into their union type parts and returns an array of | ||
* pairs of constituent types that are assignable. | ||
*/ | ||
export const assignableTypePairs = ( | ||
rawDestinationType: Type, | ||
rawSourceType: Type, | ||
checker: TypeChecker, | ||
): readonly TypePair[] => { | ||
const destinationTypeParts = unionTypeParts(rawDestinationType); | ||
|
||
const sourceTypeParts = unionTypeParts(rawSourceType); | ||
|
||
return sourceTypeParts.flatMap((sourceTypePart) => | ||
destinationTypeParts | ||
.filter((destinationTypePart) => | ||
checker.isTypeAssignableTo(sourceTypePart, destinationTypePart), | ||
) | ||
.map( | ||
(destinationTypePart) => | ||
({ | ||
sourceType: sourceTypePart, | ||
destinationType: destinationTypePart, | ||
}) as const, | ||
), | ||
); | ||
}; | ||
|
||
/** True if this expression is a literal, false otherwise. */ | ||
export const isLiteral = ( | ||
sourceNode: TSESTree.Expression | undefined, | ||
): boolean => { | ||
if (sourceNode === undefined) { | ||
return false; | ||
} | ||
|
||
if (sourceNode.type === AST_NODE_TYPES.ObjectExpression) { | ||
// empty object literal: {} | ||
return sourceNode.properties.length === 0; | ||
} | ||
|
||
if (sourceNode.type === AST_NODE_TYPES.ArrayExpression) { | ||
// empty object literal: [] | ||
return sourceNode.elements.length === 0; | ||
} | ||
|
||
// TODO: handle recursive case for both arrays and objects and | ||
// permit literals such as string and numbers as properties | ||
|
||
return false; | ||
}; |
56 changes: 56 additions & 0 deletions
56
packages/eslint-configs/src/plugins/total-functions/rules/fp-ts.mts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
import { type Type } from 'typescript'; | ||
import { typeSymbolName } from './common.mjs'; | ||
|
||
// Note: `Lazy` deliberately excluded even though it has the same signature as `IO`, its semantics | ||
// don't imply impurity like IO. | ||
export const effects: readonly string[] = [ | ||
'IO', | ||
'IOEither', | ||
'IOOption', | ||
'ReaderTask', | ||
'ReaderTaskEither', | ||
'StateReaderTaskEither', | ||
'Task', | ||
'TaskEither', | ||
'TaskOption', | ||
'TaskThese', | ||
] as const; | ||
|
||
export type FpTsEffectType = Readonly<{ | ||
effectType: Type; | ||
effectName: string; | ||
effectTypeParameter: Type | undefined; | ||
}>; | ||
|
||
const fpTsEffectTypeParameter = ( | ||
effectName: string, | ||
effectType: Type, | ||
): Type | undefined => { | ||
if (effectName === 'IO') { | ||
const signatures = effectType.getCallSignatures(); | ||
const signature = signatures[0]; | ||
|
||
if (signatures.length !== 1 || signature === undefined) { | ||
return undefined; | ||
} | ||
|
||
return signature.getReturnType(); | ||
} | ||
|
||
// TODO extract the type param from other effect types. | ||
return undefined; | ||
}; | ||
|
||
export const fpTsEffectType = (type: Type): FpTsEffectType | undefined => { | ||
const symbolName = typeSymbolName(type); | ||
|
||
if (symbolName === undefined || !effects.includes(symbolName)) { | ||
return undefined; | ||
} | ||
|
||
return { | ||
effectType: type, | ||
effectName: symbolName, | ||
effectTypeParameter: fpTsEffectTypeParameter(symbolName, type), | ||
} as const; | ||
}; |
28 changes: 28 additions & 0 deletions
28
packages/eslint-configs/src/plugins/total-functions/rules/index.mts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
import { type Plugin } from '../../../types/index.mjs'; | ||
import { noEnums } from './no-enums.mjs'; | ||
import { noHiddenTypeAssertions } from './no-hidden-type-assertions.mjs'; | ||
import { noNestedFpTsEffects } from './no-nested-fp-ts-effects.mjs'; | ||
import { noPartialArrayReduce } from './no-partial-array-reduce.mjs'; | ||
import { noPartialDivision } from './no-partial-division.mjs'; | ||
import { noPartialStringNormalize } from './no-partial-string-normalize.mjs'; | ||
import { noPartialUrlConstructor } from './no-partial-url-constructor.mjs'; | ||
import { noPrematureFpTsEffects } from './no-premature-fp-ts-effects.mjs'; | ||
import { noUnsafeMutableReadonlyAssignment } from './no-unsafe-mutable-readonly-assignment.mjs'; | ||
import { noUnsafeReadonlyMutableAssignment } from './no-unsafe-readonly-mutable-assignment.mjs'; | ||
import { noUnsafeTypeAssertion } from './no-unsafe-type-assertion.mjs'; | ||
import { requireStrictMode } from './require-strict-mode.mjs'; | ||
|
||
export const rules = { | ||
'require-strict-mode': requireStrictMode, | ||
'no-unsafe-type-assertion': noUnsafeTypeAssertion, | ||
'no-unsafe-readonly-mutable-assignment': noUnsafeReadonlyMutableAssignment, | ||
'no-unsafe-mutable-readonly-assignment': noUnsafeMutableReadonlyAssignment, | ||
'no-enums': noEnums, | ||
'no-partial-url-constructor': noPartialUrlConstructor, | ||
'no-partial-division': noPartialDivision, | ||
'no-partial-string-normalize': noPartialStringNormalize, | ||
'no-premature-fp-ts-effects': noPrematureFpTsEffects, | ||
'no-nested-fp-ts-effects': noNestedFpTsEffects, | ||
'no-partial-array-reduce': noPartialArrayReduce, | ||
'no-hidden-type-assertions': noHiddenTypeAssertions, | ||
} as const satisfies Plugin['rules']; |
26 changes: 26 additions & 0 deletions
26
packages/eslint-configs/src/plugins/total-functions/rules/no-enums.mts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
import { createRule } from './common.mjs'; | ||
|
||
/** An ESLint rule to ban enums. */ | ||
|
||
export const noEnums = createRule({ | ||
name: 'no-enums', | ||
meta: { | ||
type: 'problem', | ||
docs: { | ||
description: 'Bans enums.', | ||
}, | ||
messages: { | ||
errorStringGeneric: "Don't declare enums.", | ||
}, | ||
schema: [], | ||
}, | ||
create: (context) => ({ | ||
TSEnumDeclaration: (node) => { | ||
context.report({ | ||
node, | ||
messageId: 'errorStringGeneric', | ||
} as const); | ||
}, | ||
}), | ||
defaultOptions: [], | ||
} as const); |
Oops, something went wrong.