diff --git a/packages/website/src/components/editor/LoadedEditor.tsx b/packages/website/src/components/editor/LoadedEditor.tsx index 994561e7c318..f8c88d3d5f0a 100644 --- a/packages/website/src/components/editor/LoadedEditor.tsx +++ b/packages/website/src/components/editor/LoadedEditor.tsx @@ -8,6 +8,7 @@ import { createCompilerOptions } from '../lib/createCompilerOptions'; import { debounce } from '../lib/debounce'; import { getEslintJsonSchema, + getRuleJsonSchemaWithErrorLevel, getTypescriptJsonSchema, } from '../lib/jsonSchema'; import { parseTSConfig, tryParseEslintModule } from '../lib/parseConfig'; @@ -147,16 +148,23 @@ export const LoadedEditor: React.FC = ({ }, [webLinter, onEsASTChange, onScopeChange, onTsASTChange]); useEffect(() => { + const createRuleUri = (name: string): string => + monaco.Uri.parse(`/rules/${name.replace('@', '')}.json`).toString(); + // configure the JSON language support with schemas and schema associations monaco.languages.json.jsonDefaults.setDiagnosticsOptions({ validate: true, enableSchemaRequest: false, allowComments: true, schemas: [ + ...Array.from(webLinter.rules.values()).map(rule => ({ + uri: createRuleUri(rule.name), + schema: getRuleJsonSchemaWithErrorLevel(rule.name, rule.schema), + })), { uri: monaco.Uri.file('eslint-schema.json').toString(), // id of the first schema fileMatch: ['/.eslintrc'], // associate with our model - schema: getEslintJsonSchema(webLinter), + schema: getEslintJsonSchema(webLinter, createRuleUri), }, { uri: monaco.Uri.file('ts-schema.json').toString(), // id of the first schema diff --git a/packages/website/src/components/editor/useSandboxServices.ts b/packages/website/src/components/editor/useSandboxServices.ts index 21a7f51ea471..027e12d918cf 100644 --- a/packages/website/src/components/editor/useSandboxServices.ts +++ b/packages/website/src/components/editor/useSandboxServices.ts @@ -125,6 +125,7 @@ export const useSandboxServices = ( }; // colorMode and jsx can't be reactive here because we don't want to force a recreation // updating of colorMode and jsx is handled in LoadedEditor + // eslint-disable-next-line react-hooks/exhaustive-deps }, []); return services; diff --git a/packages/website/src/components/lib/jsonSchema.ts b/packages/website/src/components/lib/jsonSchema.ts index b43b6ad72cb9..db11292c0847 100644 --- a/packages/website/src/components/lib/jsonSchema.ts +++ b/packages/website/src/components/lib/jsonSchema.ts @@ -3,11 +3,98 @@ import type * as ts from 'typescript'; import type { CreateLinter } from '../linter/createLinter'; +const defaultRuleSchema: JSONSchema4 = { + type: ['string', 'number'], + enum: ['off', 'warn', 'error', 0, 1, 2], +}; + +/** + * Add the error level to the rule schema items + * + * if you encounter issues with rule schema validation you can check the schema by using the following code in the console: + * monaco.languages.json.jsonDefaults.diagnosticsOptions.schemas.find(item => item.uri.includes('typescript-eslint/consistent-type-imports')) + * monaco.languages.json.jsonDefaults.diagnosticsOptions.schemas.find(item => item.uri.includes('no-unused-labels')) + * monaco.languages.json.jsonDefaults.diagnosticsOptions.schemas.filter(item => item.schema.type === 'array') + */ +export function getRuleJsonSchemaWithErrorLevel( + name: string, + ruleSchema: JSONSchema4 | JSONSchema4[], +): JSONSchema4 { + if (Array.isArray(ruleSchema)) { + return { + type: 'array', + items: [defaultRuleSchema, ...ruleSchema], + minItems: 1, + additionalItems: false, + }; + } + // TODO: delete this once we update schemas + // example: ban-ts-comment + if (Array.isArray(ruleSchema.prefixItems)) { + const { prefixItems, ...rest } = ruleSchema; + return { + ...rest, + items: [defaultRuleSchema, ...(prefixItems as JSONSchema4[])], + maxItems: ruleSchema.maxItems ? ruleSchema.maxItems + 1 : undefined, + minItems: ruleSchema.minItems ? ruleSchema.minItems + 1 : 1, + additionalItems: false, + }; + } + // example: explicit-member-accessibility + if (Array.isArray(ruleSchema.items)) { + return { + ...ruleSchema, + items: [defaultRuleSchema, ...ruleSchema.items], + maxItems: ruleSchema.maxItems ? ruleSchema.maxItems + 1 : undefined, + minItems: ruleSchema.minItems ? ruleSchema.minItems + 1 : 1, + additionalItems: false, + }; + } + if (typeof ruleSchema.items === 'object' && ruleSchema.items) { + // example: naming-convention rule + return { + ...ruleSchema, + items: [defaultRuleSchema], + additionalItems: ruleSchema.items, + }; + } + // example eqeqeq + if (Array.isArray(ruleSchema.anyOf)) { + return { + ...ruleSchema, + anyOf: ruleSchema.anyOf.map(item => + getRuleJsonSchemaWithErrorLevel(name, item), + ), + }; + } + // example logical-assignment-operators + if (Array.isArray(ruleSchema.oneOf)) { + return { + ...ruleSchema, + oneOf: ruleSchema.oneOf.map(item => + getRuleJsonSchemaWithErrorLevel(name, item), + ), + }; + } + if (typeof ruleSchema !== 'object' || Object.keys(ruleSchema).length) { + console.error('unsupported rule schema', name, ruleSchema); + } + return { + type: 'array', + items: [defaultRuleSchema], + minItems: 1, + additionalItems: false, + }; +} + /** * Get the JSON schema for the eslint config * Currently we only support the rules and extends */ -export function getEslintJsonSchema(linter: CreateLinter): JSONSchema4 { +export function getEslintJsonSchema( + linter: CreateLinter, + createRef: (name: string) => string, +): JSONSchema4 { const properties: Record = {}; for (const [, item] of linter.rules) { @@ -15,21 +102,7 @@ export function getEslintJsonSchema(linter: CreateLinter): JSONSchema4 { description: `${item.description}\n ${item.url}`, title: item.name.startsWith('@typescript') ? 'Rules' : 'Core rules', default: 'off', - oneOf: [ - { - type: ['string', 'number'], - enum: ['off', 'warn', 'error', 0, 1, 2], - }, - { - type: 'array', - items: [ - { - type: ['string', 'number'], - enum: ['off', 'warn', 'error', 0, 1, 2], - }, - ], - }, - ], + oneOf: [defaultRuleSchema, { $ref: createRef(item.name) }], }; } diff --git a/packages/website/src/components/linter/createLinter.ts b/packages/website/src/components/linter/createLinter.ts index 813fd10eaf24..30ba20fe1a43 100644 --- a/packages/website/src/components/linter/createLinter.ts +++ b/packages/website/src/components/linter/createLinter.ts @@ -1,5 +1,5 @@ import type * as tsvfs from '@site/src/vendor/typescript-vfs'; -import type { TSESLint } from '@typescript-eslint/utils'; +import type { JSONSchema, TSESLint } from '@typescript-eslint/utils'; import type * as ts from 'typescript'; import { createCompilerOptions } from '../lib/createCompilerOptions'; @@ -15,7 +15,15 @@ import type { } from './types'; export interface CreateLinter { - rules: Map; + rules: Map< + string, + { + name: string; + description?: string; + url?: string; + schema: JSONSchema.JSONSchema4; + } + >; configs: string[]; triggerFix(filename: string): TSESLint.Linter.FixReport | undefined; triggerLint(filename: string): void; @@ -56,6 +64,7 @@ export function createLinter( name: name, description: item.meta?.docs?.description, url: item.meta?.docs?.url, + schema: item.meta?.schema ?? [], }); }); diff --git a/packages/website/src/components/linter/utils.ts b/packages/website/src/components/linter/utils.ts index e2a4b3ed1c72..474e28cca8fb 100644 --- a/packages/website/src/components/linter/utils.ts +++ b/packages/website/src/components/linter/utils.ts @@ -102,8 +102,9 @@ export function parseMarkers( result[group].items.push({ message: - (marker.owner !== 'eslint' && code ? `${code.value}: ` : '') + - marker.message, + (marker.owner !== 'eslint' && marker.owner !== 'json' && code.value + ? `${code.value}: ` + : '') + marker.message, location: `${marker.startLineNumber}:${marker.startColumn} - ${marker.endLineNumber}:${marker.endColumn}`, severity: marker.severity, fixer: fixers.find(item => item.isPreferred), pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy