From 2a8bbbcc1ac4e23dde4c449216cbf2fe64f4142f Mon Sep 17 00:00:00 2001 From: Rebecca Stevens Date: Tue, 23 Apr 2024 00:20:30 +1200 Subject: [PATCH] part: set up overridable options --- package.json | 3 +- pnpm-lock.yaml | 3 ++ src/options/index.ts | 1 + src/options/overrides.ts | 68 ++++++++++++++++++++++++++++++++++++++++ src/utils/schemas.ts | 52 ++++++++++++++++++++++++++++++ 5 files changed, 126 insertions(+), 1 deletion(-) create mode 100644 src/options/overrides.ts create mode 100644 src/utils/schemas.ts diff --git a/package.json b/package.json index 1db54c0a5..9f4f52fbc 100644 --- a/package.json +++ b/package.json @@ -74,7 +74,8 @@ "deepmerge-ts": "^5.1.0", "escape-string-regexp": "^4.0.0", "is-immutable-type": "^3.1.0", - "ts-api-utils": "^1.3.0" + "ts-api-utils": "^1.3.0", + "ts-declaration-location": "^1.0.0" }, "devDependencies": { "@babel/eslint-parser": "7.24.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index dbecd0e71..94347cf4c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -23,6 +23,9 @@ importers: ts-api-utils: specifier: ^1.3.0 version: 1.3.0(typescript@5.4.5) + ts-declaration-location: + specifier: ^1.0.0 + version: 1.0.0(typescript@5.4.5) devDependencies: '@babel/eslint-parser': specifier: 7.24.1 diff --git a/src/options/index.ts b/src/options/index.ts index a2ca8f7c3..e5b888321 100644 --- a/src/options/index.ts +++ b/src/options/index.ts @@ -1 +1,2 @@ export * from "./ignore"; +export * from "./overrides"; diff --git a/src/options/overrides.ts b/src/options/overrides.ts new file mode 100644 index 000000000..735ca2256 --- /dev/null +++ b/src/options/overrides.ts @@ -0,0 +1,68 @@ +import { type TSESTree } from "@typescript-eslint/utils"; +import { type RuleContext } from "@typescript-eslint/utils/ts-eslint"; +import typeMatchesSpecifier, { + type TypeDeclarationSpecifier, +} from "ts-declaration-location"; + +import { getTypeOfNode } from "../utils/rule"; + +/** + * Options that can be overridden. + */ +export type OverridableOptions = CoreOptions & { + overrides?: Array< + { + specifiers: TypeDeclarationSpecifier | TypeDeclarationSpecifier[]; + } & ( + | { + options: CoreOptions; + disable?: false; + } + | { + disable: true; + } + ) + >; +}; + +/** + * Get the core options to use, taking into account overrides. + * + * @throws when there is a configuration error. + */ +export function getCoreOptions< + CoreOptions extends object, + Options extends readonly [Readonly>], +>( + node: TSESTree.Node, + context: Readonly>, + options: Readonly, +): CoreOptions | null { + const [optionsObject] = options; + + const program = context.sourceCode.parserServices?.program ?? undefined; + if (program === undefined) { + return optionsObject; + } + + const type = getTypeOfNode(node, context); + const found = optionsObject.overrides?.find((override) => + (Array.isArray(override.specifiers) + ? override.specifiers + : [override.specifiers] + ).some((specifier) => typeMatchesSpecifier(program, specifier, type)), + ); + + if (found !== undefined) { + if (found.disable === true) { + return null; + } + if (found.options === undefined) { + // eslint-disable-next-line functional/no-throw-statements + throw new Error("Configuration error: No options found for override."); + } + return found.options; + } + + return optionsObject; +} diff --git a/src/utils/schemas.ts b/src/utils/schemas.ts new file mode 100644 index 000000000..553ccaa61 --- /dev/null +++ b/src/utils/schemas.ts @@ -0,0 +1,52 @@ +import { type JSONSchema4 } from "@typescript-eslint/utils/json-schema"; + +const typeSpecifierSchema: JSONSchema4 = { + oneOf: [ + { + type: "object", + properties: { + from: { + type: "string", + enum: ["file"], + }, + path: { + type: "string", + }, + }, + additionalProperties: false, + }, + { + type: "object", + properties: { + from: { + type: "string", + enum: ["lib"], + }, + }, + additionalProperties: false, + }, + { + type: "object", + properties: { + from: { + type: "string", + enum: ["package"], + }, + package: { + type: "string", + }, + }, + additionalProperties: false, + }, + ], +}; + +export const typeSpecifiersSchema: JSONSchema4 = { + oneOf: [ + { + type: "array", + items: typeSpecifierSchema, + }, + typeSpecifierSchema, + ], +};