From e122a083da11915f39f6229794f9e318512f5f96 Mon Sep 17 00:00:00 2001 From: Armano Date: Mon, 23 May 2022 21:58:06 +0200 Subject: [PATCH 01/11] chore(website): add POC for error viewer in playground --- .../website/src/components/ErrorsViewer.tsx | 85 +++++++++++++++++++ .../src/components/Playground.module.css | 4 - .../website/src/components/Playground.tsx | 50 +++++------ .../src/components/editor/LoadedEditor.tsx | 8 ++ .../website/src/components/editor/types.ts | 1 + 5 files changed, 117 insertions(+), 31 deletions(-) create mode 100644 packages/website/src/components/ErrorsViewer.tsx diff --git a/packages/website/src/components/ErrorsViewer.tsx b/packages/website/src/components/ErrorsViewer.tsx new file mode 100644 index 000000000000..9c06aff0d9be --- /dev/null +++ b/packages/website/src/components/ErrorsViewer.tsx @@ -0,0 +1,85 @@ +import React from 'react'; +import type Monaco from 'monaco-editor'; + +import styles from './ast/ASTViewer.module.css'; +import clsx from 'clsx'; + +export interface ASTTsViewerProps { + readonly value?: Monaco.editor.IMarker[]; +} + +interface POCModel { + message: string; + location: string; + severity: string; +} + +function parseSeverity(severity: Monaco.MarkerSeverity): string { + switch (severity) { + case 8: + return 'alert--danger'; + case 4: + return 'alert--caution'; + case 2: + return 'alert--note'; + } + return 'alert--info'; +} + +export default function ErrorsViewer(props: ASTTsViewerProps): JSX.Element { + if (props.value) { + const values = Object.entries( + props.value.reduce>((acc, obj) => { + const isTypescript = obj.owner === 'typescript'; + const code = + typeof obj.code === 'string' ? obj.code : obj.code?.value ?? ''; + + const key = isTypescript ? obj.owner : code; + + if (!acc[key]) { + acc[key] = []; + } + + acc[key].push({ + message: (isTypescript ? `TS${code}: ` : '') + obj.message, + location: `${obj.startLineNumber}:${obj.startColumn} - ${obj.endLineNumber}:${obj.endColumn}`, + severity: parseSeverity(obj.severity), + }); + return acc; + }, {}), + ); + return ( +
+ {values.map(([group, data]) => { + return ( +
+

{group}

+
+ {data.map((item, index) => { + return ( +
+
+
+ + {item.message} {item.location} + +
+
+
+ ); + })} +
+
+ ); + })} +
+ ); + } + + return
; +} diff --git a/packages/website/src/components/Playground.module.css b/packages/website/src/components/Playground.module.css index ded7538d260f..cd1d8fafe78a 100644 --- a/packages/website/src/components/Playground.module.css +++ b/packages/website/src/components/Playground.module.css @@ -10,10 +10,6 @@ border: 1px solid var(--ifm-color-emphasis-100); } -.sourceCodeStandalone { - width: 100%; -} - .codeBlocks { display: flex; flex-direction: row; diff --git a/packages/website/src/components/Playground.tsx b/packages/website/src/components/Playground.tsx index 577d69022f6a..37d3baf6cee5 100644 --- a/packages/website/src/components/Playground.tsx +++ b/packages/website/src/components/Playground.tsx @@ -20,6 +20,7 @@ import type { RuleDetails, SelectedRange } from './types'; import type { TSESTree } from '@typescript-eslint/website-eslint'; import type { SourceFile } from 'typescript'; import ASTViewerScope from '@site/src/components/ASTViewerScope'; +import ErrorsViewer from '@site/src/components/ErrorsViewer'; function rangeReducer( prevState: T, @@ -52,6 +53,7 @@ function Playground(): JSX.Element { const [esAst, setEsAst] = useState(); const [tsAst, setTsAST] = useState(); const [scope, setScope] = useState | string | null>(); + const [markers, setMarkers] = useState(); const [ruleNames, setRuleNames] = useState([]); const [isLoading, setIsLoading] = useState(true); const [tsVersions, setTSVersion] = useState([]); @@ -70,12 +72,7 @@ function Playground(): JSX.Element { />
-
+
{isLoading && } setState({ code: code })} onLoaded={(ruleNames, tsVersions): void => { @@ -100,31 +98,29 @@ function Playground(): JSX.Element { onSelect={setPosition} />
- {state.showAST && ( -
- {(tsAst && state.showAST === 'ts' && ( - + {(tsAst && state.showAST === 'ts' && ( + + )) || + (state.showAST === 'scope' && scope && ( + )) || - (state.showAST === 'scope' && scope && ( - - )) || - (esAst && ( - - ))} -
- )} + (state.showAST === 'es' && esAst && ( + + )) || } +
); diff --git a/packages/website/src/components/editor/LoadedEditor.tsx b/packages/website/src/components/editor/LoadedEditor.tsx index 9e4baa04da5b..1ebe8269bccd 100644 --- a/packages/website/src/components/editor/LoadedEditor.tsx +++ b/packages/website/src/components/editor/LoadedEditor.tsx @@ -24,6 +24,7 @@ export const LoadedEditor: React.FC = ({ onEsASTChange, onScopeChange, onTsASTChange, + onMarkersChange, onChange, onSelect, rules, @@ -36,6 +37,13 @@ export const LoadedEditor: React.FC = ({ const [decorations, setDecorations] = useState([]); const fixes = useRef(new Map()).current; + useEffect(() => { + sandboxInstance.monaco.editor.onDidChangeMarkers(e => { + const markers = sandboxInstance.monaco.editor.getModelMarkers({}); + onMarkersChange(markers); + }); + }, []); + useEffect(() => { const config = { noResolve: true, diff --git a/packages/website/src/components/editor/types.ts b/packages/website/src/components/editor/types.ts index edcbcf842d38..7ab2d8a95091 100644 --- a/packages/website/src/components/editor/types.ts +++ b/packages/website/src/components/editor/types.ts @@ -10,5 +10,6 @@ export interface CommonEditorProps extends ConfigModel { readonly onTsASTChange: (value: string | SourceFile) => void; readonly onEsASTChange: (value: string | TSESTree.Program) => void; readonly onScopeChange: (value: string | Record) => void; + readonly onMarkersChange: (value: Monaco.editor.IMarker[]) => void; readonly onSelect: (position: Monaco.Position | null) => void; } From 236a440a322ceab651bb0b30f64a33025c489cdf Mon Sep 17 00:00:00 2001 From: Armano Date: Mon, 23 May 2022 22:09:48 +0200 Subject: [PATCH 02/11] chore(website): correct colors --- packages/website/src/components/ErrorsViewer.tsx | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/packages/website/src/components/ErrorsViewer.tsx b/packages/website/src/components/ErrorsViewer.tsx index 9c06aff0d9be..7ae3806cad69 100644 --- a/packages/website/src/components/ErrorsViewer.tsx +++ b/packages/website/src/components/ErrorsViewer.tsx @@ -2,7 +2,6 @@ import React from 'react'; import type Monaco from 'monaco-editor'; import styles from './ast/ASTViewer.module.css'; -import clsx from 'clsx'; export interface ASTTsViewerProps { readonly value?: Monaco.editor.IMarker[]; @@ -17,13 +16,13 @@ interface POCModel { function parseSeverity(severity: Monaco.MarkerSeverity): string { switch (severity) { case 8: - return 'alert--danger'; + return 'danger'; case 4: - return 'alert--caution'; + return 'caution'; case 2: - return 'alert--note'; + return 'note'; } - return 'alert--info'; + return 'info'; } export default function ErrorsViewer(props: ASTTsViewerProps): JSX.Element { @@ -59,10 +58,7 @@ export default function ErrorsViewer(props: ASTTsViewerProps): JSX.Element { return (
From 0471e160a86ebee4560d436e391d05536cf2f43a Mon Sep 17 00:00:00 2001 From: Armano Date: Tue, 24 May 2022 01:00:20 +0200 Subject: [PATCH 03/11] chore(website): correct issue with optional fixers --- .../website/src/components/ErrorsViewer.tsx | 66 ++++++++----------- .../src/components/editor/LoadedEditor.tsx | 14 ++-- .../editor/createProvideCodeActions.ts | 6 +- .../website/src/components/editor/lintCode.ts | 29 ++++---- .../website/src/components/editor/types.ts | 4 +- .../website/src/components/editor/utils.ts | 21 ++++++ packages/website/src/components/types.ts | 8 +++ 7 files changed, 80 insertions(+), 68 deletions(-) diff --git a/packages/website/src/components/ErrorsViewer.tsx b/packages/website/src/components/ErrorsViewer.tsx index 7ae3806cad69..51a0e07ff5ed 100644 --- a/packages/website/src/components/ErrorsViewer.tsx +++ b/packages/website/src/components/ErrorsViewer.tsx @@ -1,19 +1,14 @@ import React from 'react'; import type Monaco from 'monaco-editor'; +import type { ErrorItem } from './types'; import styles from './ast/ASTViewer.module.css'; -export interface ASTTsViewerProps { - readonly value?: Monaco.editor.IMarker[]; +export interface ErrorsViewerProps { + readonly value?: ErrorItem[]; } -interface POCModel { - message: string; - location: string; - severity: string; -} - -function parseSeverity(severity: Monaco.MarkerSeverity): string { +function severityClass(severity: Monaco.MarkerSeverity): string { switch (severity) { case 8: return 'danger'; @@ -25,25 +20,14 @@ function parseSeverity(severity: Monaco.MarkerSeverity): string { return 'info'; } -export default function ErrorsViewer(props: ASTTsViewerProps): JSX.Element { +export default function ErrorsViewer(props: ErrorsViewerProps): JSX.Element { if (props.value) { const values = Object.entries( - props.value.reduce>((acc, obj) => { - const isTypescript = obj.owner === 'typescript'; - const code = - typeof obj.code === 'string' ? obj.code : obj.code?.value ?? ''; - - const key = isTypescript ? obj.owner : code; - - if (!acc[key]) { - acc[key] = []; + props.value.reduce>((acc, obj) => { + if (!acc[obj.group]) { + acc[obj.group] = []; } - - acc[key].push({ - message: (isTypescript ? `TS${code}: ` : '') + obj.message, - location: `${obj.startLineNumber}:${obj.startColumn} - ${obj.endLineNumber}:${obj.endColumn}`, - severity: parseSeverity(obj.severity), - }); + acc[obj.group].push(obj); return acc; }, {}), ); @@ -53,23 +37,27 @@ export default function ErrorsViewer(props: ASTTsViewerProps): JSX.Element { return (

{group}

-
- {data.map((item, index) => { - return ( -
-
-
- - {item.message} {item.location} - + {data.map((item, index) => { + return ( +
+
+
+
+ {item.message} {item.location}
+ {item.hasFixers && ( +
Fixable
+ )}
- ); - })} -
+
+ ); + })}
); })} diff --git a/packages/website/src/components/editor/LoadedEditor.tsx b/packages/website/src/components/editor/LoadedEditor.tsx index 1ebe8269bccd..3df4aa59e598 100644 --- a/packages/website/src/components/editor/LoadedEditor.tsx +++ b/packages/website/src/components/editor/LoadedEditor.tsx @@ -8,6 +8,7 @@ import type { CommonEditorProps } from './types'; import { debounce } from '../lib/debounce'; import { lintCode, LintCodeAction } from './lintCode'; import { createProvideCodeActions } from './createProvideCodeActions'; +import { createURI, parseMarkers } from '@site/src/components/editor/utils'; export interface LoadedEditorProps extends CommonEditorProps { readonly main: typeof Monaco; @@ -35,14 +36,7 @@ export const LoadedEditor: React.FC = ({ webLinter, }) => { const [decorations, setDecorations] = useState([]); - const fixes = useRef(new Map()).current; - - useEffect(() => { - sandboxInstance.monaco.editor.onDidChangeMarkers(e => { - const markers = sandboxInstance.monaco.editor.getModelMarkers({}); - onMarkersChange(markers); - }); - }, []); + const fixes = useRef(new Map()).current; useEffect(() => { const config = { @@ -120,6 +114,10 @@ export const LoadedEditor: React.FC = ({ onChange(sandboxInstance.getModel().getValue()); }, 500), ), + sandboxInstance.monaco.editor.onDidChangeMarkers(() => { + const markers = sandboxInstance.monaco.editor.getModelMarkers({}); + onMarkersChange(parseMarkers(markers, fixes)); + }), ]; return (): void => { diff --git a/packages/website/src/components/editor/createProvideCodeActions.ts b/packages/website/src/components/editor/createProvideCodeActions.ts index b0930c19b322..8b52d91cd2ed 100644 --- a/packages/website/src/components/editor/createProvideCodeActions.ts +++ b/packages/website/src/components/editor/createProvideCodeActions.ts @@ -3,7 +3,7 @@ import { createURI } from './utils'; import type { LintCodeAction } from './lintCode'; export function createProvideCodeActions( - fixes: Map, + fixes: Map, ): Monaco.languages.CodeActionProvider { return { provideCodeActions( @@ -22,8 +22,8 @@ export function createProvideCodeActions( } const actions: Monaco.languages.CodeAction[] = []; for (const marker of context.markers) { - const message = fixes.get(createURI(marker)); - if (message) { + const messages = fixes.get(createURI(marker)) ?? []; + for (const message of messages) { const start = model.getPositionAt(message.fix.range[0]); const end = model.getPositionAt(message.fix.range[1]); actions.push({ diff --git a/packages/website/src/components/editor/lintCode.ts b/packages/website/src/components/editor/lintCode.ts index b601a6cdcb36..3955f9fc5548 100644 --- a/packages/website/src/components/editor/lintCode.ts +++ b/packages/website/src/components/editor/lintCode.ts @@ -10,7 +10,7 @@ export interface LintCodeAction { }; } -export type LintCodeActionGroup = [string, LintCodeAction]; +export type LintCodeActionGroup = [string, LintCodeAction[]]; export function lintCode( linter: WebLinter, @@ -58,27 +58,24 @@ export function lintCode( }; const markerUri = createURI(marker); + const fixes = []; if (message.fix) { - codeActions.push([ - markerUri, - { - message: `Fix this ${message.ruleId ?? 'unknown'} problem`, - fix: message.fix, - }, - ]); + fixes.push({ + message: `Fix this ${message.ruleId ?? 'unknown'} problem`, + fix: message.fix, + }); } if (message.suggestions) { for (const suggestion of message.suggestions) { - codeActions.push([ - markerUri, - { - message: `${suggestion.desc} (${message.ruleId ?? 'unknown'})`, - fix: suggestion.fix, - }, - ]); + fixes.push({ + message: `${suggestion.desc} (${message.ruleId ?? 'unknown'})`, + fix: suggestion.fix, + }); } } - + if (fixes.length > 0) { + codeActions.push([markerUri, fixes]); + } markers.push(marker); } diff --git a/packages/website/src/components/editor/types.ts b/packages/website/src/components/editor/types.ts index 7ab2d8a95091..708245bc8b09 100644 --- a/packages/website/src/components/editor/types.ts +++ b/packages/website/src/components/editor/types.ts @@ -1,5 +1,5 @@ import type Monaco from 'monaco-editor'; -import type { ConfigModel, SelectedRange } from '../types'; +import type { ConfigModel, SelectedRange, ErrorItem } from '../types'; import type { TSESTree } from '@typescript-eslint/website-eslint'; import type { SourceFile } from 'typescript'; @@ -10,6 +10,6 @@ export interface CommonEditorProps extends ConfigModel { readonly onTsASTChange: (value: string | SourceFile) => void; readonly onEsASTChange: (value: string | TSESTree.Program) => void; readonly onScopeChange: (value: string | Record) => void; - readonly onMarkersChange: (value: Monaco.editor.IMarker[]) => void; + readonly onMarkersChange: (value: ErrorItem[]) => void; readonly onSelect: (position: Monaco.Position | null) => void; } diff --git a/packages/website/src/components/editor/utils.ts b/packages/website/src/components/editor/utils.ts index ff8b70d05c8c..52c6f6b3519d 100644 --- a/packages/website/src/components/editor/utils.ts +++ b/packages/website/src/components/editor/utils.ts @@ -1,4 +1,6 @@ import type Monaco from 'monaco-editor'; +import { LintCodeAction } from '@site/src/components/editor/lintCode'; +import type { ErrorItem } from '@site/src/components/types'; export function ensurePositiveInt( value: number | undefined, @@ -17,3 +19,22 @@ export function createURI(marker: Monaco.editor.IMarkerData): string { (typeof marker.code === 'string' ? marker.code : marker.code?.value) ?? '', ].join('|')}]`; } + +export function parseMarkers( + markers: Monaco.editor.IMarker[], + fixes: Map, +): ErrorItem[] { + return markers.map(marker => { + const isTypescript = marker.owner === 'typescript'; + const code = + typeof marker.code === 'string' ? marker.code : marker.code?.value ?? ''; + + return { + group: isTypescript ? marker.owner : code, + message: (isTypescript ? `TS${code}: ` : '') + marker.message, + location: `${marker.startLineNumber}:${marker.startColumn} - ${marker.endLineNumber}:${marker.endColumn}`, + severity: marker.severity, + hasFixers: fixes.has(createURI(marker)), // TODO: expose fixers + }; + }); +} diff --git a/packages/website/src/components/types.ts b/packages/website/src/components/types.ts index bb774a436f49..7a6f9358cc8e 100644 --- a/packages/website/src/components/types.ts +++ b/packages/website/src/components/types.ts @@ -33,3 +33,11 @@ export interface SelectedRange { start: SelectedPosition; end: SelectedPosition; } + +export interface ErrorItem { + group: string; + message: string; + location: string; + severity: number; + hasFixers: boolean; +} From d541bcbef481f9c754b50b280df2008a6279d974 Mon Sep 17 00:00:00 2001 From: Armano Date: Tue, 24 May 2022 21:43:38 +0200 Subject: [PATCH 04/11] chore(website): expose fixers to errors-viewer --- .../src/components/ErrorsViewer.module.css | 23 +++++ .../website/src/components/ErrorsViewer.tsx | 90 ++++++++++++------- .../src/components/editor/LoadedEditor.tsx | 4 +- .../website/src/components/editor/utils.ts | 29 +++++- packages/website/src/components/types.ts | 1 + 5 files changed, 111 insertions(+), 36 deletions(-) create mode 100644 packages/website/src/components/ErrorsViewer.module.css diff --git a/packages/website/src/components/ErrorsViewer.module.css b/packages/website/src/components/ErrorsViewer.module.css new file mode 100644 index 000000000000..89e4350c2c71 --- /dev/null +++ b/packages/website/src/components/ErrorsViewer.module.css @@ -0,0 +1,23 @@ +.list { + font-family: var(--ifm-font-family-monospace); + background: transparent; + border: none; + padding-left: 1.5rem; + padding-right: 1.5rem; + font-size: 13px; + line-height: 18px; + letter-spacing: 0; + font-feature-settings: 'liga' 0, 'calt' 0; + box-sizing: border-box; + white-space: break-spaces; + margin: 0; +} + +.fixer { + margin: 0.5rem 1.5rem; + /*border-top: 1px solid var(--ifm-alert-border-color);*/ + display: flex; + justify-content: space-between; + align-items: center; +} + diff --git a/packages/website/src/components/ErrorsViewer.tsx b/packages/website/src/components/ErrorsViewer.tsx index 51a0e07ff5ed..c1560e24e347 100644 --- a/packages/website/src/components/ErrorsViewer.tsx +++ b/packages/website/src/components/ErrorsViewer.tsx @@ -1,8 +1,8 @@ -import React from 'react'; +import React, { useEffect, useState } from 'react'; import type Monaco from 'monaco-editor'; import type { ErrorItem } from './types'; -import styles from './ast/ASTViewer.module.css'; +import styles from './ErrorsViewer.module.css'; export interface ErrorsViewerProps { readonly value?: ErrorItem[]; @@ -20,43 +20,67 @@ function severityClass(severity: Monaco.MarkerSeverity): string { return 'info'; } -export default function ErrorsViewer(props: ErrorsViewerProps): JSX.Element { - if (props.value) { - const values = Object.entries( - props.value.reduce>((acc, obj) => { - if (!acc[obj.group]) { - acc[obj.group] = []; - } - acc[obj.group].push(obj); - return acc; - }, {}), - ); +function ErrorBlock({ item }: { item: ErrorItem }): JSX.Element { + return ( +
+
+
+
+ {item.message} {item.location} +
+ {item.hasFixers && ( +
+ {item.fixers.map((fixer, index) => ( +
+ > {fixer.message} + +
+ ))} +
+ )} +
+
+
+ ); +} + +export default function ErrorsViewer({ + value, +}: ErrorsViewerProps): JSX.Element { + const [model, setModel] = useState<[string, ErrorItem[]][]>(); + + useEffect(() => { + if (value) { + setModel( + Object.entries( + value.reduce>((acc, obj) => { + if (!acc[obj.group]) { + acc[obj.group] = []; + } + acc[obj.group].push(obj); + return acc; + }, {}), + ).sort(([a], [b]) => a.localeCompare(b)), + ); + } else { + setModel(undefined); + } + }, [value]); + + if (model) { return (
- {values.map(([group, data]) => { + {model.map(([group, data]) => { return (

{group}

{data.map((item, index) => { - return ( -
-
-
-
- {item.message} {item.location} -
- {item.hasFixers && ( -
Fixable
- )} -
-
-
- ); + return ; })}
); diff --git a/packages/website/src/components/editor/LoadedEditor.tsx b/packages/website/src/components/editor/LoadedEditor.tsx index 3df4aa59e598..d1732b67e2a4 100644 --- a/packages/website/src/components/editor/LoadedEditor.tsx +++ b/packages/website/src/components/editor/LoadedEditor.tsx @@ -8,7 +8,7 @@ import type { CommonEditorProps } from './types'; import { debounce } from '../lib/debounce'; import { lintCode, LintCodeAction } from './lintCode'; import { createProvideCodeActions } from './createProvideCodeActions'; -import { createURI, parseMarkers } from '@site/src/components/editor/utils'; +import { parseMarkers } from '@site/src/components/editor/utils'; export interface LoadedEditorProps extends CommonEditorProps { readonly main: typeof Monaco; @@ -116,7 +116,7 @@ export const LoadedEditor: React.FC = ({ ), sandboxInstance.monaco.editor.onDidChangeMarkers(() => { const markers = sandboxInstance.monaco.editor.getModelMarkers({}); - onMarkersChange(parseMarkers(markers, fixes)); + onMarkersChange(parseMarkers(markers, fixes, sandboxInstance.editor)); }), ]; diff --git a/packages/website/src/components/editor/utils.ts b/packages/website/src/components/editor/utils.ts index 52c6f6b3519d..c4c4c5692280 100644 --- a/packages/website/src/components/editor/utils.ts +++ b/packages/website/src/components/editor/utils.ts @@ -23,18 +23,45 @@ export function createURI(marker: Monaco.editor.IMarkerData): string { export function parseMarkers( markers: Monaco.editor.IMarker[], fixes: Map, + editor: Monaco.editor.IStandaloneCodeEditor, ): ErrorItem[] { return markers.map(marker => { const isTypescript = marker.owner === 'typescript'; const code = typeof marker.code === 'string' ? marker.code : marker.code?.value ?? ''; + const uri = createURI(marker); + + const fixers = + fixes.get(uri)?.map(item => { + item.fix; + return { + message: item.message, + fix(): void { + const model = editor.getModel()!; + const start = model.getPositionAt(item.fix.range[0]); + const end = model.getPositionAt(item.fix.range[1]); + editor.executeEdits(model.getValue(), [ + { + range: { + startLineNumber: start.lineNumber, + startColumn: start.column, + endLineNumber: end.lineNumber, + endColumn: end.column, + }, + text: item.fix.text, + }, + ]); + }, + }; + }) ?? []; return { group: isTypescript ? marker.owner : code, message: (isTypescript ? `TS${code}: ` : '') + marker.message, location: `${marker.startLineNumber}:${marker.startColumn} - ${marker.endLineNumber}:${marker.endColumn}`, severity: marker.severity, - hasFixers: fixes.has(createURI(marker)), // TODO: expose fixers + hasFixers: fixers.length > 0, + fixers: fixers, }; }); } diff --git a/packages/website/src/components/types.ts b/packages/website/src/components/types.ts index 7a6f9358cc8e..0cbef22a4ea2 100644 --- a/packages/website/src/components/types.ts +++ b/packages/website/src/components/types.ts @@ -40,4 +40,5 @@ export interface ErrorItem { location: string; severity: number; hasFixers: boolean; + fixers: { message: string; fix(): void }[]; } From 44206d1e27c696eb1da3328816c8d5070aeecc95 Mon Sep 17 00:00:00 2001 From: Armano Date: Tue, 24 May 2022 21:53:26 +0200 Subject: [PATCH 05/11] chore(website): add simple locking system for fixers --- .../website/src/components/ErrorsViewer.tsx | 34 +++++++++++++++---- 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/packages/website/src/components/ErrorsViewer.tsx b/packages/website/src/components/ErrorsViewer.tsx index c1560e24e347..ba5f230f7eff 100644 --- a/packages/website/src/components/ErrorsViewer.tsx +++ b/packages/website/src/components/ErrorsViewer.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from 'react'; +import React, { useCallback, useEffect, useState } from 'react'; import type Monaco from 'monaco-editor'; import type { ErrorItem } from './types'; @@ -8,6 +8,12 @@ export interface ErrorsViewerProps { readonly value?: ErrorItem[]; } +export interface ErrorBlockProps { + readonly item: ErrorItem; + readonly onClick: () => void; + readonly isLocked: boolean; +} + function severityClass(severity: Monaco.MarkerSeverity): string { switch (severity) { case 8: @@ -20,7 +26,7 @@ function severityClass(severity: Monaco.MarkerSeverity): string { return 'info'; } -function ErrorBlock({ item }: { item: ErrorItem }): JSX.Element { +function ErrorBlock({ item, onClick, isLocked }: ErrorBlockProps): JSX.Element { return (
@@ -35,7 +41,11 @@ function ErrorBlock({ item }: { item: ErrorItem }): JSX.Element { > {fixer.message} @@ -54,6 +64,12 @@ export default function ErrorsViewer({ }: ErrorsViewerProps): JSX.Element { const [model, setModel] = useState<[string, ErrorItem[]][]>(); + const [isLocked, setIsLocked] = useState(false); + + const onClick = useCallback(() => { + setIsLocked(true); + }, [isLocked]); + useEffect(() => { if (value) { setModel( @@ -70,6 +86,7 @@ export default function ErrorsViewer({ } else { setModel(undefined); } + setIsLocked(false); }, [value]); if (model) { @@ -79,9 +96,14 @@ export default function ErrorsViewer({ return (

{group}

- {data.map((item, index) => { - return ; - })} + {data.map((item, index) => ( + + ))}
); })} From d7c249a8d93e9796f19d1aeec175cb0c9ef1468e Mon Sep 17 00:00:00 2001 From: Armano Date: Wed, 25 May 2022 00:24:03 +0200 Subject: [PATCH 06/11] chore(website): change label apply to fix --- packages/website/src/components/ErrorsViewer.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/website/src/components/ErrorsViewer.tsx b/packages/website/src/components/ErrorsViewer.tsx index ba5f230f7eff..64fd937e39d5 100644 --- a/packages/website/src/components/ErrorsViewer.tsx +++ b/packages/website/src/components/ErrorsViewer.tsx @@ -47,7 +47,7 @@ function ErrorBlock({ item, onClick, isLocked }: ErrorBlockProps): JSX.Element { onClick(); }} > - apply + fix
))} From f7691a0e6c4b6e5f55991c3a330cea7288b79cfb Mon Sep 17 00:00:00 2001 From: Armano Date: Wed, 25 May 2022 03:25:37 +0200 Subject: [PATCH 07/11] fix: correct small issue with compilerOptions not updating in eslint --- packages/website/src/components/linter/WebLinter.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/website/src/components/linter/WebLinter.ts b/packages/website/src/components/linter/WebLinter.ts index d3c045eaf427..9fbb58a456b3 100644 --- a/packages/website/src/components/linter/WebLinter.ts +++ b/packages/website/src/components/linter/WebLinter.ts @@ -40,7 +40,7 @@ export class WebLinter { this.linter.defineParser(PARSER_NAME, { parseForESLint: (text, options?: ParserOptions) => { - return this.eslintParse(text, compilerOptions, options); + return this.eslintParse(text, options); }, }); @@ -70,7 +70,6 @@ export class WebLinter { eslintParse( code: string, - compilerOptions: CompilerOptions, eslintOptions: ParserOptions = {}, ): TSESLint.Linter.ESLintParseResult { const isJsx = eslintOptions?.ecmaFeatures?.jsx ?? false; @@ -80,7 +79,7 @@ export class WebLinter { const program = window.ts.createProgram( [fileName], - compilerOptions, + this.compilerOptions, this.host, ); const tsAst = program.getSourceFile(fileName)!; From f0ae860f729dfb9504d021521cd8244072d1c13c Mon Sep 17 00:00:00 2001 From: Armano Date: Wed, 25 May 2022 14:45:25 +0200 Subject: [PATCH 08/11] fix: apply changes requested in code review --- packages/website/src/components/ErrorsViewer.module.css | 2 -- packages/website/src/components/linter/utils.ts | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/website/src/components/ErrorsViewer.module.css b/packages/website/src/components/ErrorsViewer.module.css index 89e4350c2c71..fab012b69e88 100644 --- a/packages/website/src/components/ErrorsViewer.module.css +++ b/packages/website/src/components/ErrorsViewer.module.css @@ -15,9 +15,7 @@ .fixer { margin: 0.5rem 1.5rem; - /*border-top: 1px solid var(--ifm-alert-border-color);*/ display: flex; justify-content: space-between; align-items: center; } - diff --git a/packages/website/src/components/linter/utils.ts b/packages/website/src/components/linter/utils.ts index 35b8447b20c0..924210886b93 100644 --- a/packages/website/src/components/linter/utils.ts +++ b/packages/website/src/components/linter/utils.ts @@ -55,7 +55,7 @@ export function parseMarkers( }) ?? []; return { - group: isTypescript ? marker.owner : code, + group: isTypescript ? 'TypeScript' : code, message: (isTypescript ? `TS${code}: ` : '') + marker.message, location: `${marker.startLineNumber}:${marker.startColumn} - ${marker.endLineNumber}:${marker.endColumn}`, severity: marker.severity, From b7d93c77ea44f2ef7bf261eb8999edff4649e962 Mon Sep 17 00:00:00 2001 From: Armano Date: Wed, 25 May 2022 19:25:25 +0200 Subject: [PATCH 09/11] fix: apply changes from code review --- .../website/src/components/ErrorsViewer.tsx | 58 +++++++++---------- 1 file changed, 27 insertions(+), 31 deletions(-) diff --git a/packages/website/src/components/ErrorsViewer.tsx b/packages/website/src/components/ErrorsViewer.tsx index 64fd937e39d5..b1621cd36e76 100644 --- a/packages/website/src/components/ErrorsViewer.tsx +++ b/packages/website/src/components/ErrorsViewer.tsx @@ -1,4 +1,4 @@ -import React, { useCallback, useEffect, useState } from 'react'; +import React, { useEffect, useState } from 'react'; import type Monaco from 'monaco-editor'; import type { ErrorItem } from './types'; @@ -10,7 +10,7 @@ export interface ErrorsViewerProps { export interface ErrorBlockProps { readonly item: ErrorItem; - readonly onClick: () => void; + readonly setIsLocked: (value: boolean) => void; readonly isLocked: boolean; } @@ -26,7 +26,11 @@ function severityClass(severity: Monaco.MarkerSeverity): string { return 'info'; } -function ErrorBlock({ item, onClick, isLocked }: ErrorBlockProps): JSX.Element { +function ErrorBlock({ + item, + setIsLocked, + isLocked, +}: ErrorBlockProps): JSX.Element { return (
@@ -44,7 +48,7 @@ function ErrorBlock({ item, onClick, isLocked }: ErrorBlockProps): JSX.Element { disabled={isLocked} onClick={(): void => { fixer.fix(); - onClick(); + setIsLocked(true); }} > fix @@ -66,10 +70,6 @@ export default function ErrorsViewer({ const [isLocked, setIsLocked] = useState(false); - const onClick = useCallback(() => { - setIsLocked(true); - }, [isLocked]); - useEffect(() => { if (value) { setModel( @@ -89,27 +89,23 @@ export default function ErrorsViewer({ setIsLocked(false); }, [value]); - if (model) { - return ( -
- {model.map(([group, data]) => { - return ( -
-

{group}

- {data.map((item, index) => ( - - ))} -
- ); - })} -
- ); - } - - return
; + return ( +
+ {model?.map(([group, data]) => { + return ( +
+

{group}

+ {data.map((item, index) => ( + + ))} +
+ ); + })} +
+ ); } From 59ca8ca3773ed33f00a7b4b70fc24224a31bf0c6 Mon Sep 17 00:00:00 2001 From: Armano Date: Wed, 25 May 2022 19:31:44 +0200 Subject: [PATCH 10/11] fix: add useMemo to cache model --- .../website/src/components/ErrorsViewer.tsx | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/packages/website/src/components/ErrorsViewer.tsx b/packages/website/src/components/ErrorsViewer.tsx index b1621cd36e76..8101b6edd837 100644 --- a/packages/website/src/components/ErrorsViewer.tsx +++ b/packages/website/src/components/ErrorsViewer.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from 'react'; +import React, { useEffect, useMemo, useState } from 'react'; import type Monaco from 'monaco-editor'; import type { ErrorItem } from './types'; @@ -26,6 +26,18 @@ function severityClass(severity: Monaco.MarkerSeverity): string { return 'info'; } +function groupErrorItems(items: ErrorItem[]): [string, ErrorItem[]][] { + return Object.entries( + items.reduce>((acc, obj) => { + if (!acc[obj.group]) { + acc[obj.group] = []; + } + acc[obj.group].push(obj); + return acc; + }, {}), + ).sort(([a], [b]) => a.localeCompare(b)); +} + function ErrorBlock({ item, setIsLocked, @@ -66,26 +78,14 @@ function ErrorBlock({ export default function ErrorsViewer({ value, }: ErrorsViewerProps): JSX.Element { - const [model, setModel] = useState<[string, ErrorItem[]][]>(); + const model = useMemo( + () => (value ? groupErrorItems(value) : undefined), + [value], + ); const [isLocked, setIsLocked] = useState(false); useEffect(() => { - if (value) { - setModel( - Object.entries( - value.reduce>((acc, obj) => { - if (!acc[obj.group]) { - acc[obj.group] = []; - } - acc[obj.group].push(obj); - return acc; - }, {}), - ).sort(([a], [b]) => a.localeCompare(b)), - ); - } else { - setModel(undefined); - } setIsLocked(false); }, [value]); From 3694da268f34b08510260d3ac7716f54035c3a16 Mon Sep 17 00:00:00 2001 From: Armano Date: Thu, 26 May 2022 16:30:13 +0200 Subject: [PATCH 11/11] Update packages/website/src/components/ErrorsViewer.tsx Co-authored-by: Josh Goldberg --- packages/website/src/components/ErrorsViewer.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/website/src/components/ErrorsViewer.tsx b/packages/website/src/components/ErrorsViewer.tsx index 8101b6edd837..fa1b56c937fb 100644 --- a/packages/website/src/components/ErrorsViewer.tsx +++ b/packages/website/src/components/ErrorsViewer.tsx @@ -83,7 +83,7 @@ export default function ErrorsViewer({ [value], ); - const [isLocked, setIsLocked] = useState(false); + const [isLocked, setIsLocked] = useState(false); useEffect(() => { setIsLocked(false);