Skip to content

Commit 4ad4efc

Browse files
committed
Merge remote-tracking branch 'origin/v6' into fix/correct-issues-with-rerended-of-config-editor
2 parents e736df0 + 648f6ad commit 4ad4efc

File tree

9 files changed

+136
-75
lines changed

9 files changed

+136
-75
lines changed

Diff for: packages/website-eslint/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
"@typescript-eslint/types": "5.57.0",
3232
"@typescript-eslint/utils": "5.57.0",
3333
"eslint": "*",
34+
"@eslint/js": "8.36.0",
3435
"esbuild": "~0.17.12",
3536
"esquery": "*",
3637
"semver": "^7.3.7"

Diff for: packages/website-eslint/src/index.js

+22-3
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ This saves us having to mock unnecessary things and reduces our bundle size.
55
*/
66
// @ts-check
77

8-
import { rules } from '@typescript-eslint/eslint-plugin';
8+
import eslintJs from '@eslint/js';
9+
import * as plugin from '@typescript-eslint/eslint-plugin';
910
import { analyze } from '@typescript-eslint/scope-manager';
1011
import {
1112
astConverter,
@@ -24,8 +25,26 @@ exports.esquery = esquery;
2425

2526
exports.createLinter = function () {
2627
const linter = new Linter();
27-
for (const name in rules) {
28-
linter.defineRule(`@typescript-eslint/${name}`, rules[name]);
28+
for (const name in plugin.rules) {
29+
linter.defineRule(`@typescript-eslint/${name}`, plugin.rules[name]);
2930
}
3031
return linter;
3132
};
33+
34+
/** @type {Record<string, unknown>} */
35+
const configs = {};
36+
37+
for (const [name, value] of Object.entries(eslintJs.configs)) {
38+
configs[`eslint:${name}`] = value;
39+
}
40+
41+
for (const [name, value] of Object.entries(plugin.configs)) {
42+
if (value.extends && Array.isArray(value.extends)) {
43+
value.extends = value.extends.map(name =>
44+
name.replace(/^\.\/configs\//, 'plugin:@typescript-eslint/'),
45+
);
46+
}
47+
configs[`plugin:@typescript-eslint/${name}`] = value;
48+
}
49+
50+
exports.configs = configs;

Diff for: packages/website-eslint/types/eslint-js.d.ts

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
declare module '@eslint/js' {
2+
declare const configs: Record<string, unknown>;
3+
export = {
4+
configs,
5+
};
6+
}

Diff for: packages/website/src/components/editor/LoadedEditor.tsx

+4-4
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ export const LoadedEditor: React.FC<LoadedEditorProps> = ({
109109
}, [sandboxInstance, tsconfig, webLinter]);
110110

111111
useEffect(() => {
112-
webLinter.updateRules(parseESLintRC(eslintrc).rules);
112+
webLinter.updateEslintConfig(parseESLintRC(eslintrc));
113113
}, [eslintrc, webLinter]);
114114

115115
useEffect(() => {
@@ -128,7 +128,7 @@ export const LoadedEditor: React.FC<LoadedEditorProps> = ({
128128

129129
const markers = parseLintResults(messages, codeActions, ruleId =>
130130
sandboxInstance.monaco.Uri.parse(
131-
webLinter.rulesUrl.get(ruleId) ?? '',
131+
webLinter.rulesMap.get(ruleId)?.url ?? '',
132132
),
133133
);
134134

@@ -185,7 +185,7 @@ export const LoadedEditor: React.FC<LoadedEditorProps> = ({
185185
{
186186
uri: sandboxInstance.monaco.Uri.file('eslint-schema.json').toString(), // id of the first schema
187187
fileMatch: [tabs.eslintrc.uri.toString()], // associate with our model
188-
schema: getEslintSchema(webLinter.ruleNames),
188+
schema: getEslintSchema(webLinter),
189189
},
190190
{
191191
uri: sandboxInstance.monaco.Uri.file('ts-schema.json').toString(), // id of the first schema
@@ -290,7 +290,7 @@ export const LoadedEditor: React.FC<LoadedEditorProps> = ({
290290
tabs.eslintrc,
291291
tabs.tsconfig,
292292
updateMarkers,
293-
webLinter.ruleNames,
293+
webLinter.rulesMap,
294294
]);
295295

296296
const resize = useMemo(() => {

Diff for: packages/website/src/components/editor/config.ts

+35-26
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import type { JSONSchema4 } from '@typescript-eslint/utils/json-schema';
22
import type Monaco from 'monaco-editor';
33

44
import { getTypescriptOptions } from '../config/utils';
5+
import type { WebLinter } from '../linter/WebLinter';
56

67
export function createCompilerOptions(
78
tsConfig: Record<string, unknown> = {},
@@ -34,37 +35,45 @@ export function createCompilerOptions(
3435
return options;
3536
}
3637

37-
export function getEslintSchema(
38-
rules: { name: string; description?: string }[],
39-
): JSONSchema4 {
40-
const properties = rules.reduce<Record<string, JSONSchema4>>(
41-
(rules, item) => {
42-
rules[item.name] = {
43-
description: item.description,
38+
export function getEslintSchema(linter: WebLinter): JSONSchema4 {
39+
const properties: Record<string, JSONSchema4> = {};
40+
41+
for (const [, item] of linter.rulesMap) {
42+
properties[item.name] = {
43+
description: `${item.description}\n ${item.url}`,
44+
title: item.name.startsWith('@typescript') ? 'Rules' : 'Core rules',
45+
default: 'off',
46+
oneOf: [
47+
{
48+
type: ['string', 'number'],
49+
enum: ['off', 'warn', 'error', 0, 1, 2],
50+
},
51+
{
52+
type: 'array',
53+
items: [
54+
{
55+
type: ['string', 'number'],
56+
enum: ['off', 'warn', 'error', 0, 1, 2],
57+
},
58+
],
59+
},
60+
],
61+
};
62+
}
63+
64+
return {
65+
type: 'object',
66+
properties: {
67+
extends: {
4468
oneOf: [
45-
{
46-
type: ['string', 'number'],
47-
enum: ['off', 'warn', 'error', 0, 1, 2],
48-
},
69+
{ type: 'string' },
4970
{
5071
type: 'array',
51-
items: [
52-
{
53-
type: ['string', 'number'],
54-
enum: ['off', 'warn', 'error', 0, 1, 2],
55-
},
56-
],
72+
items: { type: 'string', enum: Object.keys(linter.configs) },
73+
uniqueItems: true,
5774
},
5875
],
59-
};
60-
return rules;
61-
},
62-
{},
63-
);
64-
65-
return {
66-
type: 'object',
67-
properties: {
76+
},
6877
rules: {
6978
type: 'object',
7079
properties: properties,

Diff for: packages/website/src/components/editor/useSandboxServices.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ export const useSandboxServices = (
8585
const webLinter = new WebLinter(system, compilerOptions, lintUtils);
8686

8787
onLoaded(
88-
webLinter.ruleNames,
88+
Array.from(webLinter.rulesMap.values()),
8989
Array.from(
9090
new Set([...sandboxInstance.supportedVersions, window.ts.version]),
9191
)

Diff for: packages/website/src/components/linter/WebLinter.ts

+49-41
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
import { createVirtualCompilerHost } from '@site/src/components/linter/CompilerHost';
2-
import { parseSettings } from '@site/src/components/linter/config';
31
import type { analyze } from '@typescript-eslint/scope-manager';
42
import type { ParserOptions } from '@typescript-eslint/types';
53
import type {
@@ -8,14 +6,11 @@ import type {
86
} from '@typescript-eslint/typescript-estree/use-at-your-own-risk';
97
import type { TSESLint, TSESTree } from '@typescript-eslint/utils';
108
import type esquery from 'esquery';
11-
import type {
12-
CompilerHost,
13-
CompilerOptions,
14-
SourceFile,
15-
System,
16-
} from 'typescript';
9+
import type * as ts from 'typescript';
1710

18-
const PARSER_NAME = '@typescript-eslint/parser';
11+
import type { EslintRC, RuleDetails } from '../types';
12+
import { createVirtualCompilerHost } from './CompilerHost';
13+
import { eslintConfig, PARSER_NAME, parseSettings } from './config';
1914

2015
export interface LintUtils {
2116
createLinter: () => TSESLint.Linter;
@@ -24,37 +19,28 @@ export interface LintUtils {
2419
astConverter: typeof astConverter;
2520
getScriptKind: typeof getScriptKind;
2621
esquery: typeof esquery;
22+
configs: Record<string, TSESLint.Linter.Config>;
2723
}
2824

2925
export class WebLinter {
30-
private readonly host: CompilerHost;
26+
private readonly host: ts.CompilerHost;
3127

3228
public storedAST?: TSESTree.Program;
33-
public storedTsAST?: SourceFile;
29+
public storedTsAST?: ts.SourceFile;
3430
public storedScope?: Record<string, unknown>;
3531

36-
private compilerOptions: CompilerOptions;
37-
private readonly parserOptions: ParserOptions = {
38-
ecmaFeatures: {
39-
jsx: true,
40-
globalReturn: false,
41-
},
42-
comment: true,
43-
ecmaVersion: 'latest',
44-
project: ['./tsconfig.json'],
45-
sourceType: 'module',
46-
};
32+
private compilerOptions: ts.CompilerOptions;
33+
private eslintConfig = eslintConfig;
4734

4835
private linter: TSESLint.Linter;
4936
private lintUtils: LintUtils;
50-
private rules: TSESLint.Linter.RulesRecord = {};
5137

52-
public readonly ruleNames: { name: string; description?: string }[] = [];
53-
public readonly rulesUrl = new Map<string, string | undefined>();
38+
public readonly rulesMap = new Map<string, RuleDetails>();
39+
public readonly configs: Record<string, TSESLint.Linter.Config> = {};
5440

5541
constructor(
56-
system: System,
57-
compilerOptions: CompilerOptions,
42+
system: ts.System,
43+
compilerOptions: ts.CompilerOptions,
5844
lintUtils: LintUtils,
5945
) {
6046
this.compilerOptions = compilerOptions;
@@ -69,23 +55,17 @@ export class WebLinter {
6955
},
7056
});
7157

58+
this.configs = lintUtils.configs;
59+
7260
this.linter.getRules().forEach((item, name) => {
73-
this.ruleNames.push({
61+
this.rulesMap.set(name, {
7462
name: name,
7563
description: item.meta?.docs?.description,
64+
url: item.meta?.docs?.url,
7665
});
77-
this.rulesUrl.set(name, item.meta?.docs?.url);
7866
});
7967
}
8068

81-
get eslintConfig(): TSESLint.Linter.Config {
82-
return {
83-
parser: PARSER_NAME,
84-
parserOptions: this.parserOptions,
85-
rules: this.rules,
86-
};
87-
}
88-
8969
lint(code: string, filename: string): TSESLint.Linter.LintMessage[] {
9070
return this.linter.verify(code, this.eslintConfig, {
9171
filename: filename,
@@ -99,15 +79,17 @@ export class WebLinter {
9979
});
10080
}
10181

102-
updateRules(rules: TSESLint.Linter.RulesRecord): void {
103-
this.rules = rules;
82+
updateEslintConfig(config: EslintRC): void {
83+
const resolvedConfig = this.resolveEslintConfig(config);
84+
this.eslintConfig.rules = resolvedConfig.rules;
10485
}
10586

10687
updateParserOptions(sourceType?: TSESLint.SourceType): void {
107-
this.parserOptions.sourceType = sourceType ?? 'module';
88+
this.eslintConfig.parserOptions ??= {};
89+
this.eslintConfig.parserOptions.sourceType = sourceType ?? 'module';
10890
}
10991

110-
updateCompilerOptions(options: CompilerOptions = {}): void {
92+
updateCompilerOptions(options: ts.CompilerOptions = {}): void {
11193
this.compilerOptions = options;
11294
}
11395

@@ -161,4 +143,30 @@ export class WebLinter {
161143
visitorKeys: this.lintUtils.visitorKeys,
162144
};
163145
}
146+
147+
private resolveEslintConfig(
148+
cfg: Partial<TSESLint.Linter.Config>,
149+
): TSESLint.Linter.Config {
150+
const config = {
151+
rules: {},
152+
overrides: [],
153+
};
154+
if (cfg.extends) {
155+
const cfgExtends = Array.isArray(cfg.extends)
156+
? cfg.extends
157+
: [cfg.extends];
158+
for (const extendsName of cfgExtends) {
159+
if (typeof extendsName === 'string' && extendsName in this.configs) {
160+
const resolved = this.resolveEslintConfig(this.configs[extendsName]);
161+
if (resolved.rules) {
162+
Object.assign(config.rules, resolved.rules);
163+
}
164+
}
165+
}
166+
}
167+
if (cfg.rules) {
168+
Object.assign(config.rules, cfg.rules);
169+
}
170+
return config;
171+
}
164172
}

Diff for: packages/website/src/components/linter/config.ts

+17
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
import type { ParseSettings } from '@typescript-eslint/typescript-estree/use-at-your-own-risk';
2+
import type { TSESLint } from '@typescript-eslint/utils';
3+
4+
export const PARSER_NAME = '@typescript-eslint/parser';
25

36
export const parseSettings: ParseSettings = {
47
allowInvalidAST: false,
@@ -26,3 +29,17 @@ export const parseSettings: ParseSettings = {
2629
tsconfigMatchCache: new Map(),
2730
tsconfigRootDir: '/',
2831
};
32+
33+
export const eslintConfig: TSESLint.Linter.Config = {
34+
parser: PARSER_NAME,
35+
parserOptions: {
36+
ecmaFeatures: {
37+
jsx: false,
38+
globalReturn: false,
39+
},
40+
ecmaVersion: 'latest',
41+
project: ['./tsconfig.json'],
42+
sourceType: 'module',
43+
},
44+
rules: {},
45+
};

Diff for: packages/website/src/components/types.ts

+1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ export type RuleEntry = TSESLint.Linter.RuleEntry;
1111
export interface RuleDetails {
1212
name: string;
1313
description?: string;
14+
url?: string;
1415
}
1516

1617
export type TabType = 'code' | 'tsconfig' | 'eslintrc';

0 commit comments

Comments
 (0)