Skip to content

Commit

Permalink
docs: run eslint in playground
Browse files Browse the repository at this point in the history
  • Loading branch information
azat-io committed Nov 30, 2024
1 parent bea3a1b commit 9db0693
Show file tree
Hide file tree
Showing 5 changed files with 245 additions and 94 deletions.
98 changes: 4 additions & 94 deletions docs/components/Editor/Editor.svelte
Original file line number Diff line number Diff line change
@@ -1,98 +1,15 @@
<script lang="ts">
import * as monaco from 'monaco-editor'
import { formatHex } from 'culori'
import { onMount } from 'svelte'
import type { Linter } from 'eslint'
import { colorTheme } from '../../utils/shiki-theme'
import EditorMonaco from './EditorMonaco.svelte'
import Button from '../Button.svelte'
let editor: monaco.editor.IStandaloneCodeEditor | null = null
let toRgb = (color: string): string => {
if (!color) {
return color
}
let hex = formatHex(color)
if (!hex) {
throw new Error(`Could not convert ${JSON.stringify(color)} to RGB`)
}
return hex
}
let resolveCssVariable = (variable: string): string => {
if (!variable) {
return variable
}
let styles = getComputedStyle(document.documentElement)
let cleanVariable = variable.replace(/^var\(/u, '').replace(/\)$/u, '')
return styles.getPropertyValue(cleanVariable).trim()
}
let defineMonacoTheme = (themeName: string): void => {
monaco.editor.defineTheme(themeName, {
rules: colorTheme.tokenColors
?.flatMap(token =>
(Array.isArray(token.scope) ? token.scope : [token.scope]).map(
scope => ({
foreground: toRgb(
resolveCssVariable(
token.settings.foreground ??
colorTheme.colors!['editor.foreground']!,
),
),
fontStyle: token.settings.fontStyle ?? 'normal',
token: scope,
}),
),
)
.filter(
({ foreground }) => !!foreground,
) as monaco.editor.ITokenThemeRule[],
colors: Object.fromEntries(
Object.entries(colorTheme.colors!)
.map(([key, value]) => [key, toRgb(resolveCssVariable(value))])
.filter(([_, value]) => !!value),
),
base: 'vs-dark',
inherit: false,
})
}
onMount(() => {
let container: HTMLElement | null = document.querySelector('.monaco')
if (container) {
defineMonacoTheme('css-variables')
monaco.languages.typescript.typescriptDefaults.setDiagnosticsOptions({
noSemanticValidation: true,
noSyntaxValidation: true,
})
editor = monaco.editor.create(container, {
minimap: {
enabled: false,
},
// @ts-ignore
'bracketPairColorization.enabled': false,
fontFamily: 'var(--font-family-code)',
language: 'typescript',
matchBrackets: 'never',
automaticLayout: true,
value: 'const a = 3',
lineHeight: 1.7,
fontSize: 15,
})
monaco.editor.setTheme('css-variables')
editor.onDidChangeModelContent(() => {})
}
})
let config: Linter.Config[] = []
</script>

<div class="wrapper">
<Button color="primary" content="Fix ESLint problems" />
<div class="monaco" />
<EditorMonaco {config} />
</div>

<style>
Expand All @@ -101,11 +18,4 @@
flex-direction: column;
gap: var(--space-l);
}
.monaco {
inline-size: 100%;
block-size: 600px;
overflow: hidden;
border-radius: var(--border-radius);
}
</style>
202 changes: 202 additions & 0 deletions docs/components/Editor/EditorMonaco.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
<script lang="ts">
import type { Linter as BrowserLinter } from 'eslint-linter-browserify'
import type { Linter } from 'eslint'
import * as monaco from 'monaco-editor'
import { formatHex } from 'culori'
import { dedent } from 'ts-dedent'
import { onMount } from 'svelte'
import ESLintWorker from '../../utils/eslint-worker?worker'
import { colorTheme } from '../../utils/shiki-theme'
export let config: Linter.Config[] = []
let editor: monaco.editor.IStandaloneCodeEditor | null = null
let eslintWorker = new ESLintWorker()
let baseValue = dedent`
import Button from '~/components/Button'
import type { FC } from 'react'
import {
useId,
useCallback,
useState,
useEffect,
} from 'react'
import Form from '~/component/Form'
import './style.css'
import Input from '~/components/Input'
import { FormValues } from '~/stores/auth'
interface Props {
className?: string
onSubmit: (values: FormValues) => void
id: string
resetFormValues: () => void
title: string
}
export const Auth: FC<Props> => (props) => (
<Form {...props}>
<Input
placeholder="Enter your email"
full
name="user-email"
validation={/^[^s@]+@[^s@]+.[^s@]+$/i}
type="email"
label="Email address"
/>
<Button
type="submit"
className="submit-button"
size="l"
color="secondary"
>
Submit
</Button>
</Form>
)
`
let toRgb = (color: string): string => {
if (!color) {
return color
}
let hex = formatHex(color)
if (!hex) {
throw new Error(`Could not convert ${JSON.stringify(color)} to RGB`)
}
return hex
}
let resolveCssVariable = (variable: string): string => {
if (!variable) {
return variable
}
let styles = getComputedStyle(document.documentElement)
let cleanVariable = variable.replace(/^var\(/u, '').replace(/\)$/u, '')
return styles.getPropertyValue(cleanVariable).trim()
}
let lintCode = (
code: string,
eslintConfig: Linter.Config[],
): Promise<BrowserLinter.LintMessage[] | undefined> =>
new Promise(resolve => {
eslintWorker.addEventListener('message', (event: MessageEvent) => {
resolve(event.data as BrowserLinter.LintMessage[] | undefined)
})
eslintWorker.postMessage({
config: eslintConfig,
code,
})
})
let defineMonacoTheme = (themeName: string): void => {
monaco.editor.defineTheme(themeName, {
rules: colorTheme.tokenColors
?.flatMap(token =>
(Array.isArray(token.scope) ? token.scope : [token.scope]).map(
scope => ({
foreground: toRgb(
resolveCssVariable(
token.settings.foreground ??
colorTheme.colors!['editor.foreground']!,
),
),
fontStyle: token.settings.fontStyle ?? 'normal',
token: scope,
}),
),
)
.filter(
({ foreground }) => !!foreground,
) as monaco.editor.ITokenThemeRule[],
colors: Object.fromEntries(
Object.entries(colorTheme.colors!)
.map(([key, value]) => [key, toRgb(resolveCssVariable(value))])
.filter(([_, value]) => !!value),
),
base: 'vs-dark',
inherit: false,
})
}
onMount(() => {
let container: HTMLElement | null = document.querySelector('.monaco')
if (container) {
defineMonacoTheme('css-variables')
monaco.languages.typescript.typescriptDefaults.setDiagnosticsOptions({
noSemanticValidation: true,
noSyntaxValidation: true,
})
monaco.languages.typescript.typescriptDefaults.setCompilerOptions({
target: monaco.languages.typescript.ScriptTarget.ESNext,
jsx: monaco.languages.typescript.JsxEmit.React,
allowNonTsExtensions: true,
})
editor = monaco.editor.create(container, {
minimap: {
enabled: false,
},
// @ts-ignore
'bracketPairColorization.enabled': false,
fontFamily: 'var(--font-family-code)',
language: 'typescript',
matchBrackets: 'never',
automaticLayout: true,
value: baseValue,
lineHeight: 1.7,
fontSize: 15,
})
monaco.editor.setTheme('css-variables')
let validateCode = async (): Promise<void> => {
let code = editor?.getValue() ?? ''
let lintResults = (await lintCode(code, config)) ?? []
let markers = lintResults.map(result => ({
severity:
result.severity === 2
? monaco.MarkerSeverity.Error
: monaco.MarkerSeverity.Warning,
endColumn: result.endColumn ?? result.column + 1,
endLineNumber: result.endLine ?? result.line,
startLineNumber: result.line,
startColumn: result.column,
message: result.message,
}))
let model = editor?.getModel()
if (model) {
monaco.editor.setModelMarkers(model, 'eslint', markers)
}
}
// eslint-disable-next-line @typescript-eslint/no-floating-promises
validateCode()
editor.onDidChangeModelContent(validateCode)
}
})
</script>

<div class="monaco" />

<style>
.monaco {
inline-size: 100%;
block-size: 600px;
}
</style>
30 changes: 30 additions & 0 deletions docs/utils/eslint-worker.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import type { Linter } from 'eslint-linter-browserify'
import type { Linter as TLinter } from 'eslint'

import * as ESLint from 'eslint-linter-browserify'

interface Data {
config: TLinter.Config
code: string
}

let eslint: Linter | null = null

self.addEventListener('message', (event: MessageEvent<Data>): void => {
let { config, code } = event.data
if (!eslint) {
eslint = new ESLint.Linter({
configType: 'flat',
})
}
try {
if (typeof code !== 'string') {
throw new TypeError('Code must be a string')
}
let messages = eslint.verify(code, config)
postMessage(messages)
} catch (error) {
console.error('ESLint Worker Error:', error)
postMessage([])
}
})
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@
"cspell": "^8.16.1",
"culori": "^4.0.1",
"eslint": "^9.15.0",
"eslint-linter-browserify": "^9.16.0",
"eslint-plugin-eslint-plugin": "^6.3.2",
"execa": "^9.5.1",
"keyux": "^0.10.0",
Expand Down
8 changes: 8 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 9db0693

Please sign in to comment.