From 8d960ad13808b18b161d2b8c9c3fe5e26eb729ce Mon Sep 17 00:00:00 2001 From: Armano Date: Mon, 8 Nov 2021 20:04:21 +0100 Subject: [PATCH 01/39] chore(website): migrate playground from #3147 --- .cspell.json | 2 + .eslintignore | 10 +- .prettierignore | 1 + package.json | 3 +- packages/website/docusaurus.config.js | 7 + packages/website/package.json | 10 +- .../src/components/ast-viewer.module.css | 70 ++++ .../website/src/components/ast-viewer.tsx | 240 +++++++++++++ packages/website/src/components/editor.tsx | 327 ++++++++++++++++++ .../src/components/expander.module.css | 52 +++ packages/website/src/components/expander.tsx | 32 ++ packages/website/src/components/icons.tsx | 76 ++++ packages/website/src/components/lib/action.ts | 92 +++++ packages/website/src/components/lib/config.ts | 26 ++ .../website/src/components/lib/debounce.ts | 15 + .../src/components/lib/load-sandbox.ts | 81 +++++ .../website/src/components/lib/scroll-into.ts | 31 ++ .../website/src/components/lib/selection.ts | 72 ++++ .../src/components/lib/use-hash-state.ts | 133 +++++++ packages/website/src/components/lib/utils.ts | 37 ++ .../src/components/linter/CompilerHost.ts | 44 +++ .../components/linter/create-ast-program.ts | 50 +++ .../website/src/components/linter/linter.ts | 73 ++++ .../website/src/components/linter/parser.ts | 65 ++++ .../website/src/components/loader.module.css | 47 +++ packages/website/src/components/loader.tsx | 15 + .../website/src/components/modal.module.css | 62 ++++ packages/website/src/components/modal.tsx | 40 +++ .../components/options-selector.module.css | 76 ++++ .../src/components/options-selector.tsx | 170 +++++++++ .../src/components/playground.module.css | 42 +++ .../website/src/components/playground.tsx | 144 ++++++++ packages/website/src/modules/eslint-rules.js | 5 + packages/website/src/modules/eslint.js | 12 + packages/website/src/modules/eslintrc.js | 1 + packages/website/src/modules/globby.js | 5 + packages/website/src/modules/is-glob.js | 1 + packages/website/src/modules/resolve-from.js | 12 + packages/website/src/modules/semver.js | 7 + packages/website/src/pages/index.tsx | 21 +- packages/website/src/pages/repl.tsx | 31 ++ .../src/vendor/ds/createDesignSystem.d.ts | 71 ++++ packages/website/src/vendor/playground.d.ts | 138 ++++++++ packages/website/src/vendor/pluginUtils.d.ts | 60 ++++ packages/website/src/vendor/sandbox.d.ts | 288 +++++++++++++++ packages/website/src/vendor/tsWorker.d.ts | 98 ++++++ .../website/src/vendor/typescript-vfs.d.ts | 130 +++++++ packages/website/tsconfig.json | 2 +- packages/website/webpack.plugin.js | 117 +++++++ tools/generate-website-dts.ts | 118 +++++++ yarn.lock | 100 +++++- 51 files changed, 3352 insertions(+), 10 deletions(-) create mode 100644 packages/website/src/components/ast-viewer.module.css create mode 100644 packages/website/src/components/ast-viewer.tsx create mode 100644 packages/website/src/components/editor.tsx create mode 100644 packages/website/src/components/expander.module.css create mode 100644 packages/website/src/components/expander.tsx create mode 100644 packages/website/src/components/icons.tsx create mode 100644 packages/website/src/components/lib/action.ts create mode 100644 packages/website/src/components/lib/config.ts create mode 100644 packages/website/src/components/lib/debounce.ts create mode 100644 packages/website/src/components/lib/load-sandbox.ts create mode 100644 packages/website/src/components/lib/scroll-into.ts create mode 100644 packages/website/src/components/lib/selection.ts create mode 100644 packages/website/src/components/lib/use-hash-state.ts create mode 100644 packages/website/src/components/lib/utils.ts create mode 100644 packages/website/src/components/linter/CompilerHost.ts create mode 100644 packages/website/src/components/linter/create-ast-program.ts create mode 100644 packages/website/src/components/linter/linter.ts create mode 100644 packages/website/src/components/linter/parser.ts create mode 100644 packages/website/src/components/loader.module.css create mode 100644 packages/website/src/components/loader.tsx create mode 100644 packages/website/src/components/modal.module.css create mode 100644 packages/website/src/components/modal.tsx create mode 100644 packages/website/src/components/options-selector.module.css create mode 100644 packages/website/src/components/options-selector.tsx create mode 100644 packages/website/src/components/playground.module.css create mode 100644 packages/website/src/components/playground.tsx create mode 100644 packages/website/src/modules/eslint-rules.js create mode 100644 packages/website/src/modules/eslint.js create mode 100644 packages/website/src/modules/eslintrc.js create mode 100644 packages/website/src/modules/globby.js create mode 100644 packages/website/src/modules/is-glob.js create mode 100644 packages/website/src/modules/resolve-from.js create mode 100644 packages/website/src/modules/semver.js create mode 100644 packages/website/src/pages/repl.tsx create mode 100644 packages/website/src/vendor/ds/createDesignSystem.d.ts create mode 100644 packages/website/src/vendor/playground.d.ts create mode 100644 packages/website/src/vendor/pluginUtils.d.ts create mode 100644 packages/website/src/vendor/sandbox.d.ts create mode 100644 packages/website/src/vendor/tsWorker.d.ts create mode 100644 packages/website/src/vendor/typescript-vfs.d.ts create mode 100644 packages/website/webpack.plugin.js create mode 100644 tools/generate-website-dts.ts diff --git a/.cspell.json b/.cspell.json index 4a8dc8b7cb3..d3ca9b40c21 100644 --- a/.cspell.json +++ b/.cspell.json @@ -68,6 +68,7 @@ "IIFE", "IIFEs", "linebreaks", + "lzstring", "necroing", "nocheck", "nullish", @@ -96,6 +97,7 @@ "transpiled", "transpiles", "transpiling", + "tsvfs", "tsconfigs", "tsutils", "typedef", diff --git a/.eslintignore b/.eslintignore index e6e508a5e2c..17bfdf1a12d 100644 --- a/.eslintignore +++ b/.eslintignore @@ -8,8 +8,14 @@ __snapshots__ packages/eslint-plugin-tslint/tests -packages/website/**/*.js -packages/website/**/*.d.ts +packages/website/build +packages/website/.docusaurus +packages/website/sidebars +packages/website/webpack.plugin.js +packages/website/babel.config.js +packages/website/docusaurus.config.js +packages/website/src/modules +packages/website/src/vendor # Files copied as part of the build packages/types/src/ast-spec.ts diff --git a/.prettierignore b/.prettierignore index 3b8eebd1684..8c4dce359d7 100644 --- a/.prettierignore +++ b/.prettierignore @@ -18,6 +18,7 @@ CHANGELOG.md packages/website/.docusaurus packages/website/build +packages/website/src/vendor # TODO - remove this once prettier supports TS4.1 packages/scope-manager/tests/fixtures/type-declaration/literal-type1.ts diff --git a/package.json b/package.json index 1fac8603db6..9e065424a56 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,7 @@ "url": "https://github.com/typescript-eslint/typescript-eslint/issues" }, "scripts": { - "build": "npx nx prebuild @typescript-eslint/types && nx run-many --target=build --all --parallel", + "build": "npx nx prebuild @typescript-eslint/types && nx run-many --target=build --all --parallel --exclude website", "check-clean-workspace-after-install": "git diff --quiet --exit-code", "check-configs": "nx run-many --target=check-configs --all --parallel", "check-docs": "nx run-many --target=check-docs --all --parallel", @@ -33,6 +33,7 @@ "format": "prettier --write \"./**/*.{ts,tsx,js,jsx,json,md,css}\"", "generate-contributors": "yarn ts-node --transpile-only ./tools/generate-contributors.ts && yarn all-contributors generate", "generate-sponsors": "yarn ts-node --transpile-only ./tools/generate-sponsors.ts", + "generate-website-dts": "yarn ts-node --transpile-only ./tools/generate-website-dts.ts", "lint-fix": "eslint . --ext .js,.ts --fix", "lint-markdown-fix": "yarn lint-markdown --fix", "lint-markdown": "markdownlint \"**/*.md\" --config=.markdownlint.json --ignore-path=.markdownlintignore", diff --git a/packages/website/docusaurus.config.js b/packages/website/docusaurus.config.js index c7af4ddcf7b..3b937438927 100644 --- a/packages/website/docusaurus.config.js +++ b/packages/website/docusaurus.config.js @@ -27,6 +27,7 @@ const config = { sponsors, }, plugins: [ + require.resolve('./webpack.plugin'), '@docusaurus/plugin-debug', [ '@docusaurus/theme-classic', @@ -84,6 +85,12 @@ const config = { label: 'Rules', position: 'left', }, + { + to: 'repl', + activeBasePath: 'repl', + position: 'right', + label: 'Playground', + }, { href: githubUrl, position: 'right', diff --git a/packages/website/package.json b/packages/website/package.json index 627d68c7fcd..5568ef92e8b 100644 --- a/packages/website/package.json +++ b/packages/website/package.json @@ -29,14 +29,20 @@ "eslint": "*", "react-dom": "^17.0.2", "react": "^17.0.2", - "typescript": "*" + "typescript": "*", + "path-browserify": "1.0.1", + "os-browserify": "0.3.0", + "util": "0.12.4", + "assert": "2.0.0" }, "devDependencies": { "@docusaurus/module-type-aliases": "^2.0.0-beta.9", + "@types/react": "^17.0.34", "@types/react-helmet": "^6.1.4", "@types/react-router-dom": "^5.3.2", - "@types/react": "^17.0.34", "globby": "^11.0.4", + "monaco-editor": "^0.29.1", + "string-replace-loader": "^3.0.3", "webpack": "^5.61.0" }, "browserslist": { diff --git a/packages/website/src/components/ast-viewer.module.css b/packages/website/src/components/ast-viewer.module.css new file mode 100644 index 00000000000..b1f7075f33d --- /dev/null +++ b/packages/website/src/components/ast-viewer.module.css @@ -0,0 +1,70 @@ +.list, +.subList { + cursor: default; + box-sizing: border-box; + font-family: monospace; + margin: 0; + padding-left: 0; + list-style: none; +} + +.subList { + padding-left: 1rem; +} + +.nonExpand, +.expand { + border-left: 0.1rem dashed var(--ifm-color-emphasis-200); + padding-left: 0.9rem; +} + +.expand.open::before { + content: '+'; +} + +.selected { + background-color: var(--code-line-decoration); +} + +.expand::before { + content: '-'; + margin-left: -1rem; + width: 1rem; + display: inline-block; +} + +.valueBody { + min-width: 300px; + width: fit-content; +} + +.tokenName { + color: #2aa198; +} + +.propName { + color: #b58900; +} + +.propNumber { + color: #268bd2; +} + +.propBoolean { + color: #b58900; +} + +.propString { + color: #15aa15; +} + +.hidden { + color: var(--ifm-color-emphasis-400); +} + +.clickable { + cursor: pointer; +} +.clickable:hover { + text-decoration: underline; +} diff --git a/packages/website/src/components/ast-viewer.tsx b/packages/website/src/components/ast-viewer.tsx new file mode 100644 index 00000000000..4c6c3b4d4ce --- /dev/null +++ b/packages/website/src/components/ast-viewer.tsx @@ -0,0 +1,240 @@ +import React, { + SyntheticEvent, + useCallback, + useEffect, + useRef, + useState, +} from 'react'; +import styles from './ast-viewer.module.css'; +import type { TSESTree } from '@typescript-eslint/types'; +import clsx from 'clsx'; +import { scrollIntoViewIfNeeded } from './lib/scroll-into'; +import { filterRecord } from './lib/selection'; + +interface GenericParams { + propName?: string; + name?: string; + value: V; + level: string; + selection?: TSESTree.Node | null; + onSelectNode: (node: TSESTree.Node | null) => void; +} + +const PropertyName = React.memo(function PropertyName(props: { + name?: string; + propName?: string; + onClick?: (e: SyntheticEvent) => void; + onMouseEnter?: (e: SyntheticEvent) => void; +}) { + return ( + + {props.propName && ( + + {props.propName} + + )} + {props.propName && : } + {props.name && ( + + {props.name} + + )} + + ); +}); + +const PropertyValue = React.memo(function PropertyValue(props: { + value: unknown; +}) { + if (typeof props.value === 'string') { + return ( + {JSON.stringify(props.value)} + ); + } else if (typeof props.value === 'number') { + return {props.value}; + } else if (typeof props.value === 'bigint') { + return {String(props.value)}n; + } else if (typeof props.value === 'boolean') { + return ( + + {props.value ? 'true' : 'false'} + + ); + } + return {String(props.value)}; +}); + +function ElementArray(props: GenericParams): JSX.Element { + const isComplex = props.value.some( + item => typeof item === 'object' && item !== null, + ); + const [isExpanded, setIsExpanded] = useState(isComplex); + + return ( +
+ setIsExpanded(!isExpanded)} + /> + [ + {isExpanded ? ( +
+ {props.value.map((item, index) => { + return ( + + ); + })} +
+ ) : !isComplex ? ( + + {props.value.map((item, index) => ( + + {index > 0 && ', '} + + + ))} + + ) : ( + + {props.value.length} {props.value.length > 1 ? 'elements' : 'element'} + + )} + ] +
+ ); +} + +function ElementObject( + props: GenericParams>, +): JSX.Element { + const [isExpanded, setIsExpanded] = useState(true); + const listItem = useRef(null); + + const isSelected = props.selection === props.value; + + const onMouseEnter = useCallback((e: SyntheticEvent) => { + if ('type' in props.value && 'loc' in props.value) { + props.onSelectNode(props.value as TSESTree.Node); + e.stopPropagation(); + e.preventDefault(); + } + }, []); + + const onMouseLeave = useCallback((e: SyntheticEvent) => { + if ('type' in props.value && 'loc' in props.value) { + props.onSelectNode(null); + e.stopPropagation(); + e.preventDefault(); + } + }, []); + + useEffect(() => { + if (listItem.current && isSelected) { + scrollIntoViewIfNeeded(listItem.current); + setIsExpanded(true); + } + }, [props.selection, props.value, listItem]); + + return ( +
+ setIsExpanded(!isExpanded)} + /> + {'{'} + {isExpanded ? ( +
+ {filterRecord(props.value).map((item, index) => ( + + ))} +
+ ) : ( + + {filterRecord(props.value) + .map(item => item[0]) + .join(', ')} + + )} + {'}'} +
+ ); +} + +function ElementItem(props: GenericParams): JSX.Element { + if (Array.isArray(props.value)) { + return ( + + ); + } else if ( + typeof props.value === 'object' && + props.value !== null && + !(props.value instanceof RegExp) + ) { + return ( + } + selection={props.selection} + onSelectNode={props.onSelectNode} + /> + ); + } + return ( +
+ {props.name && {props.name}} + {props.name && : } + +
+ ); +} + +function ASTViewer(props: { + ast: TSESTree.Node | string; + selection?: TSESTree.Node | null; + onSelectNode: (node: TSESTree.Node | null) => void; +}): JSX.Element { + return typeof props.ast === 'string' ? ( +
{props.ast}
+ ) : ( +
+ +
+ ); +} + +export default ASTViewer; diff --git a/packages/website/src/components/editor.tsx b/packages/website/src/components/editor.tsx new file mode 100644 index 00000000000..9ac58722420 --- /dev/null +++ b/packages/website/src/components/editor.tsx @@ -0,0 +1,327 @@ +import React from 'react'; +import type { + createTypeScriptSandbox, + PlaygroundConfig, +} from '../vendor/sandbox'; +import type { TSESTree } from '@typescript-eslint/types'; +import type Monaco from 'monaco-editor'; + +import { sandboxSingleton } from './lib/load-sandbox'; +import { loadLinter, WebLinter } from './linter/linter'; +import { createProvideCodeActions } from './lib/action'; +import { createURI, messageToMarker } from './lib/utils'; +import { debounce } from './lib/debounce'; +import { HashStateOptions } from './lib/use-hash-state'; +import type { Linter } from '@typescript-eslint/experimental-utils/dist/ts-eslint/Linter'; + +interface EditorProps extends HashStateOptions { + darkTheme: boolean; + decoration?: TSESTree.Node | null; + onChange?: ( + value: string, + event: Monaco.editor.IModelContentChangedEvent, + ) => void; + onASTChange?: ( + value: string | TSESTree.Program, + position: Monaco.Position | null, + ) => void; + onLoadRule?: (value: string[]) => void; + onSelect?: (position: Monaco.Position) => void; + onLoaded?: (tsVersions: readonly string[]) => void; +} + +class Editor extends React.Component { + private sandboxInstance?: ReturnType; + private linter?: WebLinter; + + private _subscriptions: Monaco.IDisposable[]; + private _resize?: () => void; + private readonly _lint: () => void; + private _codeIsUpdating: boolean; + private _decorations: string[]; + + private readonly fixes: Map; + + constructor(props: EditorProps) { + super(props); + this.fixes = new Map(); + this._codeIsUpdating = false; + this._decorations = []; + this._subscriptions = []; + this._lint = debounce((): void => this.lintCode(), 100); + } + + async componentDidMount(): Promise { + await this.loadEditor(); + this._resize = debounce((): void => this.updateLayout(), 1); + window.addEventListener('resize', this._resize); + } + + componentWillUnmount(): void { + if (this._resize) { + window.removeEventListener('resize', this._resize); + } + this.fixes.clear(); + for (const subscription of this._subscriptions) { + subscription.dispose(); + } + this._subscriptions = []; + + if (this.sandboxInstance) { + this.sandboxInstance.monaco.editor.setModelMarkers( + this.sandboxInstance.editor.getModel()!, + this.sandboxInstance.editor.getId(), + [], + ); + this.sandboxInstance.editor.dispose(); + const model = this.sandboxInstance.editor.getModel(); + if (model) { + model.dispose(); + } + const models = this.sandboxInstance.monaco.editor.getModels(); + for (const model of models) { + model.dispose(); + } + } + } + + componentDidUpdate(prevProps: EditorProps): void { + if (this.sandboxInstance) { + const { editor } = this.sandboxInstance; + let shouldLint = false; + if (this.props.jsx !== prevProps.jsx) { + this.updateConfig(); + shouldLint = true; + } + if (this.props.sourceType !== prevProps.sourceType) { + shouldLint = true; + } + if (this.props.rules !== prevProps.rules) { + shouldLint = true; + } + if (this.props.showAST !== prevProps.showAST) { + this.updateDecorations(); + this.updateLayout(); + } + if ( + this.props.code !== editor.getValue() && + prevProps.code !== this.props.code + ) { + this.updateCode(); + shouldLint = true; + } + if (this.props.decoration !== prevProps.decoration) { + this.updateDecorations(); + } + if (prevProps.darkTheme !== this.props.darkTheme) { + this.updateTheme(); + } + if (shouldLint) { + this._lint(); + } + } + } + + render(): JSX.Element { + return
; + } + + async loadEditor(): Promise { + const sandboxConfig: Partial = { + text: '', + monacoSettings: { + minimap: { enabled: false }, + fontSize: 13, + wordWrap: 'off', + scrollBeyondLastLine: false, + smoothScrolling: true, + }, + compilerOptions: { + noResolve: true, + strict: true, + target: 99, + jsx: this.props.jsx ? 2 : undefined, + module: 99, + }, + domID: 'monaco-editor-embed', + }; + const { main, sandboxFactory, ts } = await sandboxSingleton(this.props.ts); + this.sandboxInstance = sandboxFactory.createTypeScriptSandbox( + sandboxConfig, + main, + ts, + ); + this.updateTheme(); + this.updateCode(); + this.linter = await loadLinter(); + if (this.props.onLoadRule) { + this.props.onLoadRule(this.linter.ruleNames); + } + + this._subscriptions.push( + main.languages.registerCodeActionProvider( + 'typescript', + createProvideCodeActions(this.fixes), + ), + ); + this._subscriptions.push( + this.sandboxInstance.editor.onMouseDown(() => { + this.updateCursor(); + }), + ); + this._subscriptions.push( + this.sandboxInstance.editor.onKeyUp(e => { + // console.log(e.keyCode); + if (e.keyCode >= 15 && e.keyCode <= 18) { + this.updateCursor(); + } + }), + ); + this._subscriptions.push( + this.sandboxInstance.editor.onDidChangeModelContent(event => { + if (this.sandboxInstance && this.props.onChange) { + this._lint(); + if (!this._codeIsUpdating) { + const model = this.sandboxInstance.getModel().getValue(); + this.props.onChange(model, event); + } + } + }), + ); + this._lint(); + this.updateLayout(); + if (this.props.onLoaded) { + this.props.onLoaded(this.sandboxInstance.supportedVersions); + } + } + + private lintCode(): void { + if (!this.sandboxInstance || !this.linter) { + return; + } + const messages = this.linter.lint( + this.props.code, + { + ecmaFeatures: { + jsx: this.props.jsx ?? false, + globalReturn: false, + }, + ecmaVersion: 2020, + project: ['./tsconfig.json'], + sourceType: this.props.sourceType ?? 'module', + }, + this.props.rules, + ); + const markers: Monaco.editor.IMarkerData[] = []; + this.fixes.clear(); + let fatalMessage: string | undefined = undefined; + for (const message of messages) { + if (!message.ruleId) { + fatalMessage = message.message; + } + const marker = messageToMarker(message); + markers.push(marker); + this.fixes.set(createURI(marker), message); + } + this.sandboxInstance.monaco.editor.setModelMarkers( + this.sandboxInstance.editor.getModel()!, + this.sandboxInstance.editor.getId(), + markers, + ); + if (this.props.onASTChange) { + if (fatalMessage) { + this._decorations = this.sandboxInstance.editor.deltaDecorations( + this._decorations, + [], + ); + } + + this.props.onASTChange( + fatalMessage ?? this.linter.getAst(), + this.sandboxInstance.editor.getPosition(), + ); + this.updateCursor(); + } + } + + private updateCursor(): void { + // console.log('updateCursor'); + if (this.props.onSelect && this.sandboxInstance) { + const position = this.sandboxInstance.editor.getPosition(); + if (position) { + this.props.onSelect(position); + } + } + } + + private updateLayout(): void { + if (this.sandboxInstance) { + this.sandboxInstance.editor.layout(); + } + } + + private updateTheme(): void { + if (this.sandboxInstance) { + this.sandboxInstance.monaco.editor.setTheme( + this.props.darkTheme ? 'vs-dark' : 'vs-light', + ); + } + } + + private updateCode(): void { + if (this.sandboxInstance) { + this._codeIsUpdating = true; + const model = this.sandboxInstance.editor.getModel()!; + if (model.getValue() !== this.props.code) { + this.sandboxInstance.editor.executeEdits(model.getValue(), [ + { + range: model.getFullModelRange(), + text: this.props.code, + }, + ]); + } + this._codeIsUpdating = false; + } + } + + private updateConfig(): void { + if (this.sandboxInstance) { + // TODO: add more options + this.sandboxInstance.setCompilerSettings({ + jsx: this.props.jsx ? 2 : 0, + }); + } + } + + private updateDecorations(): void { + if (this.sandboxInstance) { + if (this.props.decoration && this.props.showAST) { + const loc = this.props.decoration.loc; + this._decorations = this.sandboxInstance.editor.deltaDecorations( + this._decorations, + [ + { + range: new this.sandboxInstance.monaco.Range( + loc.start.line, + loc.start.column + 1, + loc.end.line, + loc.end.column + 1, + ), + options: { + inlineClassName: 'myLineDecoration', + stickiness: 1, + }, + }, + ], + ); + } else { + this._decorations = this.sandboxInstance.editor.deltaDecorations( + this._decorations, + [], + ); + } + } + } +} + +export default Editor; diff --git a/packages/website/src/components/expander.module.css b/packages/website/src/components/expander.module.css new file mode 100644 index 00000000000..d554e1c2b40 --- /dev/null +++ b/packages/website/src/components/expander.module.css @@ -0,0 +1,52 @@ +.expander { + flex: 1 0 1.5rem; + cursor: default; + border-bottom: 1px solid var(--ifm-color-emphasis-100); + user-select: none; +} + +.heading { + display: flex; + flex-direction: row; + align-items: center; + transition: background-color var(--ifm-transition-fast) + var(--ifm-transition-timing-default), + color var(--ifm-transition-fast) var(--ifm-transition-timing-default); + color: var(--ifm-color-emphasis-900); + cursor: pointer; + padding: 0.4rem 0.8rem; +} + +.heading:hover { + background-color: var(--ifm-color-emphasis-100); + color: var(--ifm-color-emphasis-800); +} + +.headerLabel { + flex: 1; + font-size: 0.75rem; + font-weight: 700; + text-transform: uppercase; +} + +.arrow { + margin: 0 0.5rem 0 0; + transform: rotateZ(-90deg); + transition: transform 250ms ease-in-out; + width: 1rem; + height: 1rem; +} + +.expandedArrow { + transform: rotateZ(0deg); +} + +.path { + fill: currentColor; +} + +.children { + display: flex; + flex-direction: column; + padding: 0 0.5rem 0.5rem; +} diff --git a/packages/website/src/components/expander.tsx b/packages/website/src/components/expander.tsx new file mode 100644 index 00000000000..7d48c4b377b --- /dev/null +++ b/packages/website/src/components/expander.tsx @@ -0,0 +1,32 @@ +import React, { useState } from 'react'; +import styles from './expander.module.css'; +import { ArrowIcon } from './icons'; + +interface MyProps { + children?: React.ReactNode; + className?: string; + label: string; +} + +export default function Expander(props: MyProps): JSX.Element { + const [isExpanded, setIsExpanded] = useState(true); + + const handleToggle = () => { + setIsExpanded(!isExpanded); + }; + + return ( +
+
+ +
{props.label}
+
+ {isExpanded &&
{props.children}
} +
+ ); +} diff --git a/packages/website/src/components/icons.tsx b/packages/website/src/components/icons.tsx new file mode 100644 index 00000000000..06041897b34 --- /dev/null +++ b/packages/website/src/components/icons.tsx @@ -0,0 +1,76 @@ +import React, { MouseEvent } from 'react'; + +interface IconBaseProps { + className?: string; + pathClass?: string; + width?: number; + height?: number; + size?: number; + fill?: string; + onClick?(e: MouseEvent): void; +} + +interface IconSVGProps extends IconBaseProps { + path: string; +} + +export function SVGIcon(props: IconSVGProps): JSX.Element { + return ( + { + props.onClick?.(e); + }} + > + + + + ); +} + +export function EditIcon(props: IconBaseProps): JSX.Element { + return ( + + ); +} + +export function DeleteIcon(props: IconBaseProps): JSX.Element { + return ( + + ); +} + +export function ArrowIcon(props: IconBaseProps): JSX.Element { + return ( + + ); +} + +export function AddIcon(props: IconBaseProps): JSX.Element { + return ( + + ); +} + +export function CloseIcon(props: IconBaseProps): JSX.Element { + return ( + + ); +} diff --git a/packages/website/src/components/lib/action.ts b/packages/website/src/components/lib/action.ts new file mode 100644 index 00000000000..d5dc7687943 --- /dev/null +++ b/packages/website/src/components/lib/action.ts @@ -0,0 +1,92 @@ +import type { languages, editor } from 'monaco-editor'; +import type { Linter } from '@typescript-eslint/experimental-utils/dist/ts-eslint/Linter'; +import type { RuleFix } from '@typescript-eslint/experimental-utils/dist/ts-eslint/Rule'; +import { createURI } from './utils'; + +export function createQuickfixCodeAction( + title: string, + marker: editor.IMarkerData, + model: editor.ITextModel, + fix: RuleFix, +): languages.CodeAction { + const start = model.getPositionAt(fix.range[0]); + const end = model.getPositionAt(fix.range[1]); + return { + title, + diagnostics: [marker], + kind: 'quickfix', + edit: { + edits: [ + { + resource: model.uri, + edit: { + range: { + startLineNumber: start.lineNumber, + startColumn: start.column, + endLineNumber: end.lineNumber, + endColumn: end.column, + }, + text: fix.text, + }, + }, + ], + }, + }; +} + +export function createProvideCodeActions( + fixes: Map, +): languages.CodeActionProvider { + return { + provideCodeActions( + model, + _range, + context, + _token, + ): languages.ProviderResult { + if (context.only !== 'quickfix') { + return { + actions: [], + dispose(): void { + /* nop */ + }, + }; + } + const actions: languages.CodeAction[] = []; + for (const marker of context.markers) { + const message = fixes.get(createURI(marker)); + if (!message) { + continue; + } + if (message.fix) { + actions.push( + createQuickfixCodeAction( + `Fix this ${message.ruleId ?? 'unknown'} problem`, + marker, + model, + message.fix, + ), + ); + } + if (message.suggestions) { + for (const suggestion of message.suggestions) { + actions.push( + createQuickfixCodeAction( + `${suggestion.desc} (${message.ruleId ?? 'unknown'})`, + marker, + model, + suggestion.fix, + ), + ); + } + } + } + return { + actions, + dispose(): void { + /* nop */ + }, + }; + }, + }; +} diff --git a/packages/website/src/components/lib/config.ts b/packages/website/src/components/lib/config.ts new file mode 100644 index 00000000000..f4a33eda679 --- /dev/null +++ b/packages/website/src/components/lib/config.ts @@ -0,0 +1,26 @@ +import type { Extra } from '@typescript-eslint/typescript-estree/dist/parser-options'; + +export const extra: Extra = { + code: '', + comment: true, + comments: [], + createDefaultProgram: false, + debugLevel: new Set(), + errorOnTypeScriptSyntacticAndSemanticIssues: false, + errorOnUnknownASTType: false, + extraFileExtensions: [], + filePath: '', + jsx: false, + loc: true, + log: console.log, + preserveNodeMaps: true, + projects: [], + range: true, + strict: false, + tokens: [], + tsconfigRootDir: '/', + EXPERIMENTAL_useSourceOfProjectReferenceRedirect: false, + singleRun: false, + programs: null, + moduleResolver: '', +}; diff --git a/packages/website/src/components/lib/debounce.ts b/packages/website/src/components/lib/debounce.ts new file mode 100644 index 00000000000..87feff49f65 --- /dev/null +++ b/packages/website/src/components/lib/debounce.ts @@ -0,0 +1,15 @@ +export function debounce( + func: (...args: X) => void, + wait: number, +): (...args: X) => void { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + let timeout: any | undefined; + return function (...args: X): void { + // eslint-disable-next-line @typescript-eslint/no-unsafe-argument + clearTimeout(timeout); + timeout = setTimeout(() => { + timeout = undefined; + func.call(null, ...args); + }, wait); + }; +} diff --git a/packages/website/src/components/lib/load-sandbox.ts b/packages/website/src/components/lib/load-sandbox.ts new file mode 100644 index 00000000000..4566c790bae --- /dev/null +++ b/packages/website/src/components/lib/load-sandbox.ts @@ -0,0 +1,81 @@ +/* eslint-disable @typescript-eslint/ban-ts-comment */ +/* eslint-disable @typescript-eslint/no-unsafe-assignment */ +/* eslint-disable @typescript-eslint/no-unsafe-call */ +/* eslint-disable @typescript-eslint/no-unsafe-return */ +import type * as TsWorker from '../../vendor/tsWorker'; +import type * as SandboxFactory from '../../vendor/sandbox'; + +export type Monaco = typeof import('monaco-editor'); +export type TS = typeof import('typescript'); + +export interface SandboxModel { + main: Monaco; + tsWorker: typeof TsWorker; + sandboxFactory: typeof SandboxFactory; + ts: TS; +} + +function loadSandbox(tsVersion: string): Promise { + return new Promise((resolve, reject): void => { + const getLoaderScript = document.createElement('script'); + getLoaderScript.src = 'https://www.typescriptlang.org/js/vs.loader.js'; + getLoaderScript.async = true; + getLoaderScript.onload = (): void => { + // For the monaco version you can use unpkg or the TypeScript web infra CDN + // You can see the available releases for TypeScript here: + // https://typescript.azureedge.net/indexes/releases.json + // @ts-ignore + window.require.config({ + paths: { + vs: `https://typescript.azureedge.net/cdn/${tsVersion}/monaco/min/vs`, + // vs: 'https://unpkg.com/@typescript-deploys/monaco-editor@4.2.2/min/vs', + sandbox: 'https://www.typescriptlang.org/js/sandbox', + }, + // This is something you need for monaco to work + ignoreDuplicateModules: ['vs/editor/editor.main'], + }); + + // Grab a copy of monaco, TypeScript and the sandbox + window.require( + [ + 'vs/editor/editor.main', + 'vs/language/typescript/tsWorker', + 'sandbox/index', + ], + // @ts-ignore + (main, tsWorker, sandboxFactory) => { + // @ts-ignore + const isOK = main && window.ts && sandboxFactory; + // @ts-ignore + window.ts.__esModule = true; + // window.ts.SyntaxKind; + if (isOK) { + resolve({ + main, + tsWorker, + sandboxFactory, + // @ts-ignore + ts: window.ts, + }); + } else { + reject( + new Error( + 'Could not get all the dependencies of sandbox set up!', + ), + ); + } + }, + ); + }; + document.body.appendChild(getLoaderScript); + }); +} + +let instance; + +export const sandboxSingleton = (version: string): Promise => { + if (instance) { + return instance; + } + return (instance = loadSandbox(version)); +}; diff --git a/packages/website/src/components/lib/scroll-into.ts b/packages/website/src/components/lib/scroll-into.ts new file mode 100644 index 00000000000..83c221fbbaa --- /dev/null +++ b/packages/website/src/components/lib/scroll-into.ts @@ -0,0 +1,31 @@ +export function scrollIntoViewIfNeeded(target: HTMLElement): void { + const rect = target.getBoundingClientRect(); + const isBelow = rect.top < 0; + const isAbove = rect.bottom > window.innerHeight; + if ((isAbove && isBelow) || rect.height > window.innerHeight) { + target.scrollIntoView({ + block: 'start', + inline: 'start', + behavior: 'smooth', + }); + return; + } + // Target is outside the viewport from the bottom + if (isAbove) { + target.scrollIntoView({ + block: 'center', + inline: 'center', + behavior: 'smooth', + }); + return; + } + // Target is outside the view from the top + if (isBelow) { + target.scrollIntoView({ + block: 'center', + inline: 'center', + behavior: 'smooth', + }); + return; + } +} diff --git a/packages/website/src/components/lib/selection.ts b/packages/website/src/components/lib/selection.ts new file mode 100644 index 00000000000..70429fbea48 --- /dev/null +++ b/packages/website/src/components/lib/selection.ts @@ -0,0 +1,72 @@ +/* eslint-disable @typescript-eslint/ban-ts-comment */ +import { TSESTree } from '@typescript-eslint/types'; + +export const propsToFilter = ['parent', 'comments', 'tokens', 'loc']; + +export interface Position { + line: number; + column: number; +} + +export function filterRecord( + values: TSESTree.Node | Record, +): [string, unknown][] { + return Object.entries(values).filter( + item => !propsToFilter.includes(item[0]), + ); +} + +export function isNode(node: unknown): node is TSESTree.Node { + return Boolean( + typeof node === 'object' && node && 'type' in node && 'loc' in node, + ); +} + +export function isWithinNode( + loc: Position, + start: Position, + end: Position, +): boolean { + const canStart = + start.line < loc.line || + (start.line === loc.line && start.column <= loc.column); + const canEnd = + end.line > loc.line || (end.line === loc.line && end.column >= loc.column); + return canStart && canEnd; +} + +export function findNode( + loc: Position, + node: TSESTree.Node, +): TSESTree.Node | null { + if (isNode(node) && isWithinNode(loc, node.loc.start, node.loc.end)) { + for (const it in node) { + if (!propsToFilter.includes(it)) { + const nodeEl = + node[ + it as Exclude< + keyof typeof node, + 'parent' | 'comments' | 'tokens' | 'loc' + > + ]!; + if (Array.isArray(nodeEl)) { + const isFound2 = nodeEl.find( + e => isNode(e) && isWithinNode(loc, e.loc.start, e.loc.end), + ); + if (isFound2) { + // @ts-ignore + return findNode(loc, isFound2); + } + } else { + // @ts-ignore + const isFound = findNode(loc, nodeEl); + if (isFound) { + return isFound; + } + } + } + } + return node; + } + return null; +} diff --git a/packages/website/src/components/lib/use-hash-state.ts b/packages/website/src/components/lib/use-hash-state.ts new file mode 100644 index 00000000000..d3bd4755335 --- /dev/null +++ b/packages/website/src/components/lib/use-hash-state.ts @@ -0,0 +1,133 @@ +import { useRef, useState, useEffect } from 'react'; +import { ParserOptions } from '@typescript-eslint/parser'; +import { debounce } from './debounce'; +import type { Linter } from '@typescript-eslint/experimental-utils/dist/ts-eslint/Linter'; + +export interface HashStateOptions { + jsx?: boolean; + sourceType?: ParserOptions['sourceType']; + rules?: Linter.RulesRecord; + code: string; + ts: string; + showAST?: boolean; +} + +function writeQueryParam(value?: unknown): string { + return btoa(encodeURIComponent(JSON.stringify(value))); +} + +function readQueryParam(value: string): unknown { + return JSON.parse(decodeURIComponent(atob(value))); +} + +const parseStateFromUrl = (): HashStateOptions | undefined => { + if (typeof window === 'undefined') { + return; + } + + const hash = window.location.hash.slice(1); + if (!hash) { + return; + } + + try { + const searchParams = new URLSearchParams(hash); + return { + ts: searchParams.get('ts') ?? process.env.TS_VERSION, + jsx: searchParams.has('jsx'), + showAST: searchParams.has('showAST'), + sourceType: + searchParams.has('sourceType') && + searchParams.get('sourceType') === 'script' + ? 'script' + : 'module', + code: searchParams.has('code') + ? (readQueryParam(searchParams.get('code')!) as string) + : '', + rules: searchParams.has('rules') + ? (readQueryParam(searchParams.get('rules')!) as Record< + string, + Linter.RuleEntry + >) + : undefined, + }; + } catch (e) { + console.warn(e); + } + return undefined; +}; + +const writeStateToUrl = debounce( + (newState: HashStateOptions, refresh = false): void => { + if (typeof window === 'undefined') { + return; + } + const json: string = Object.entries({ + ts: newState.ts, + jsx: newState.jsx, + sourceType: newState.sourceType, + showAST: newState.showAST, + rules: newState.rules ? writeQueryParam(newState.rules) : undefined, + code: newState.code ? writeQueryParam(newState.code) : undefined, + }) + .filter(item => item[1]) + .map(item => `${encodeURIComponent(item[0])}=${item[1]}`) + .join('&'); + + if (refresh) { + window.location.replace(`${window.location.pathname}#${json}`); + window.location.reload(); + } else { + window.history.pushState( + undefined, + document.title, + `${window.location.pathname}#${json}`, + ); + } + }, + 100, +); + +function useHashState( + initialState: HashStateOptions, +): [HashStateOptions, (key: keyof HashStateOptions, value: unknown) => void] { + const guard = useRef(false); + + if (!guard.current) { + const parsedState = parseStateFromUrl(); + if (parsedState) { + initialState = parsedState; + } else if (initialState) { + writeStateToUrl(initialState); + } + guard.current = true; + } + + const [state, setState] = useState(initialState); + const onHashChange = (): void => { + const parsedState = parseStateFromUrl(); + if (parsedState) { + setState(parsedState); + } + }; + + useEffect(() => { + window.addEventListener('hashchange', onHashChange); + return (): void => { + window.removeEventListener('hashchange', onHashChange); + }; + }, []); + + return [ + state, + (key: keyof HashStateOptions, value: unknown): void => { + setState(prevState => { + const newState = { ...prevState, [key]: value }; + writeStateToUrl(newState, key === 'ts'); + return newState; + }); + }, + ]; +} + +export default useHashState; diff --git a/packages/website/src/components/lib/utils.ts b/packages/website/src/components/lib/utils.ts new file mode 100644 index 00000000000..ad1d0c78b37 --- /dev/null +++ b/packages/website/src/components/lib/utils.ts @@ -0,0 +1,37 @@ +import type { editor } from 'monaco-editor'; +import type { TSESLint } from '@typescript-eslint/experimental-utils'; + +const ensurePositiveInt = ( + value: number | undefined, + defaultValue: number, +): number => { + return Math.max(1, (value !== undefined ? value : defaultValue) | 0); +}; + +export function messageToMarker( + message: TSESLint.Linter.LintMessage, +): editor.IMarkerData { + const startLineNumber = ensurePositiveInt(message.line, 1); + const startColumn = ensurePositiveInt(message.column, 1); + return { + code: message.ruleId ?? 'FATAL', + severity: 8, // MarkerSeverity.Error, + source: 'ESLint', + message: message.message, + startLineNumber, + startColumn, + endLineNumber: ensurePositiveInt(message.endLine, startLineNumber), + endColumn: ensurePositiveInt(message.endColumn, startColumn + 1), + }; +} + +export function createURI(marker: editor.IMarkerData): string { + return `[${[ + marker.startLineNumber, + marker.startColumn, + marker.startColumn, + marker.endLineNumber, + marker.endColumn, + (typeof marker.code === 'string' ? marker.code : marker.code?.value) ?? '', + ].join('|')}]`; +} diff --git a/packages/website/src/components/linter/CompilerHost.ts b/packages/website/src/components/linter/CompilerHost.ts new file mode 100644 index 00000000000..cd5db22548d --- /dev/null +++ b/packages/website/src/components/linter/CompilerHost.ts @@ -0,0 +1,44 @@ +import type { + CompilerHost as ICompilerHost, + CompilerOptions, + SourceFile, +} from 'typescript'; +import { getDefaultLibFileName } from 'typescript'; + +export class CompilerHost implements ICompilerHost { + constructor( + private files: Record, + private sourceFiles: Record, + ) {} + + fileExists(name: string): boolean { + return !!this.files[name]; + } + getCanonicalFileName(name: string): string { + return name; + } + getCurrentDirectory(): string { + return '/'; + } + getDirectories(): string[] { + return []; + } + getDefaultLibFileName(options: CompilerOptions): string { + return '/' + getDefaultLibFileName(options); + } + getNewLine(): string { + return '\n'; + } + useCaseSensitiveFileNames(): boolean { + return true; + } + writeFile(): null { + return null; + } + readFile(name: string): string | undefined { + return this.files[name]; + } + getSourceFile(name: string): SourceFile | undefined { + return this.sourceFiles[name]; + } +} diff --git a/packages/website/src/components/linter/create-ast-program.ts b/packages/website/src/components/linter/create-ast-program.ts new file mode 100644 index 00000000000..1d9e8d921fa --- /dev/null +++ b/packages/website/src/components/linter/create-ast-program.ts @@ -0,0 +1,50 @@ +import type { SourceFile, Program } from 'typescript'; +import type { ParserOptions } from '@typescript-eslint/types'; +import { + createProgram, + createSourceFile, + ScriptTarget, + ScriptKind, + JsxEmit, + ModuleKind, +} from 'typescript'; +import { CompilerHost } from './CompilerHost'; + +interface ASTAndProgram { + ast?: SourceFile; + program: Program; +} + +export function createASTProgram( + code: string, + parserOptions: ParserOptions, +): ASTAndProgram { + const isJsx = !!parserOptions?.ecmaFeatures?.jsx; + const fileName = isJsx ? '/demo.tsx' : '/demo.ts'; + const files = { [fileName]: code }; + const sourceFiles = { + [fileName]: createSourceFile( + fileName, + code, + ScriptTarget.Latest, + true, + isJsx ? ScriptKind.TSX : ScriptKind.TS, + ), + }; + + const compilerHost = new CompilerHost(files, sourceFiles); + const compilerOptions = { + noResolve: true, + strict: true, + target: ScriptTarget.Latest, + jsx: isJsx ? JsxEmit.React : undefined, + module: ModuleKind.ES2015, + }; + const program = createProgram( + Object.keys(files), + compilerOptions, + compilerHost, + ); + const ast = program.getSourceFile(fileName); + return { ast, program }; +} diff --git a/packages/website/src/components/linter/linter.ts b/packages/website/src/components/linter/linter.ts new file mode 100644 index 00000000000..0f5e045ecaf --- /dev/null +++ b/packages/website/src/components/linter/linter.ts @@ -0,0 +1,73 @@ +import type { ParserOptions } from '@typescript-eslint/types'; +import type { TSESLint } from '@typescript-eslint/experimental-utils'; +import type { + RuleCreateFunction, + RuleModule, +} from '@typescript-eslint/experimental-utils/dist/ts-eslint'; +import type { ParseForESLintResult } from './parser'; + +const PARSER_NAME = '@typescript-eslint/parser'; + +export interface WebLinter { + ruleNames: string[]; + + getAst(): ParseForESLintResult['ast']; + + lint( + code: string, + parserOptions: ParserOptions, + rules?: TSESLint.Linter.RulesRecord, + ): TSESLint.Linter.LintMessage[]; +} + +interface distRules { + default: Record>; +} + +export async function loadLinter(): Promise { + const rules = ( + (await import('@typescript-eslint/eslint-plugin/dist/rules')) as distRules + ).default; + const { parseForESLint } = await import(`./parser`); + const { Linter } = await import('eslint'); + const linter = new Linter() as unknown as TSESLint.Linter; + let storedAST: ParseForESLintResult['ast']; + + linter.defineParser(PARSER_NAME, { + parseForESLint(code: string, options: ParserOptions): ParseForESLintResult { + const toParse = parseForESLint(code, options); + storedAST = toParse.ast; + return toParse; + }, + // parse(code: string, options: ParserOptions): ParseForESLintResult['ast'] { + // const toParse = parseForESLint(code, options); + // storedAST = toParse.ast; + // return toParse.ast; + // }, + }); + for (const name of Object.keys(rules)) { + linter.defineRule(`@typescript-eslint/${name}`, rules[name]); + } + + const ruleNames = Object.keys(rules).map( + name => `@typescript-eslint/${name}`, + ); + + return { + ruleNames, + getAst(): TSESLint.Linter.ESLintParseResult['ast'] { + return storedAST; + }, + lint( + code: string, + parserOptions: ParserOptions, + rules?: TSESLint.Linter.RulesRecord, + ): TSESLint.Linter.LintMessage[] { + return linter.verify(code, { + parser: PARSER_NAME, + parserOptions, + rules, + }); + }, + }; +} diff --git a/packages/website/src/components/linter/parser.ts b/packages/website/src/components/linter/parser.ts new file mode 100644 index 00000000000..8ee5c13c036 --- /dev/null +++ b/packages/website/src/components/linter/parser.ts @@ -0,0 +1,65 @@ +import type { AST } from '@typescript-eslint/typescript-estree/dist/parser'; +import { analyze } from '@typescript-eslint/scope-manager/dist/analyze'; +import { visitorKeys } from '@typescript-eslint/visitor-keys/dist/visitor-keys'; +import { astConverter } from '@typescript-eslint/typescript-estree/dist/ast-converter'; +import { createASTProgram } from './create-ast-program'; +import type { ParserOptions } from '@typescript-eslint/types'; +import type { Linter } from '@typescript-eslint/experimental-utils/dist/ts-eslint/Linter'; +import type { + ParserServices, + TSESTreeOptions, +} from '@typescript-eslint/typescript-estree/dist/parser-options'; +import { extra } from '../lib/config'; + +export type ParseForESLintResult = Linter.ESLintParseResult; + +interface ParseAndGenerateServicesResult { + ast: AST; + services: ParserServices; +} + +function parseAndGenerateServices( + code: string, + options: T, +): ParseAndGenerateServicesResult { + const { ast, program } = createASTProgram(code, options); + const { estree, astMaps } = astConverter( + ast!, + { + ...extra, + code, + jsx: options.jsx ?? false, + }, + true, + ); + return { + ast: estree as AST, + services: { + hasFullTypeInformation: true, + program, + esTreeNodeToTSNodeMap: astMaps.esTreeNodeToTSNodeMap, + tsNodeToESTreeNodeMap: astMaps.tsNodeToESTreeNodeMap, + }, + }; +} + +export function parseForESLint( + code: string, + parserOptions: ParserOptions, +): ParseForESLintResult { + const { ast, services } = parseAndGenerateServices(code, { + ...parserOptions, + jsx: parserOptions.ecmaFeatures?.jsx ?? false, + useJSXTextNode: true, + projectFolderIgnoreList: [], + }); + const scopeManager = analyze(ast, { + ecmaVersion: + parserOptions.ecmaVersion === 'latest' ? 1e8 : parserOptions.ecmaVersion, + globalReturn: parserOptions.ecmaFeatures?.globalReturn ?? false, + sourceType: parserOptions.sourceType ?? 'script', + }); + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + return { ast, services, scopeManager, visitorKeys }; +} diff --git a/packages/website/src/components/loader.module.css b/packages/website/src/components/loader.module.css new file mode 100644 index 00000000000..ae7d360432e --- /dev/null +++ b/packages/website/src/components/loader.module.css @@ -0,0 +1,47 @@ +@keyframes loader-bounce { + 33% { + transform: translateY(10px); + } + 66% { + transform: translateY(-10px); + } + 100% { + transform: translateY(0px); + } +} + +.loaderContainer { + font-size: 18px; + letter-spacing: 1px; + position: absolute; + left: 50%; + top: 50%; +} + +.loader { + animation-name: loader-bounce; + animation-duration: 0.6s; + animation-timing-function: ease-in-out; + animation-iteration-count: infinite; + animation-direction: normal; + animation-fill-mode: both; + animation-play-state: running; + background-color: var(--ifm-color-secondary-darkest); + width: 1rem; + height: 1rem; + margin: 0.5rem; + border-radius: 100%; + display: inline-block; +} + +.loader1 { + animation-delay: 0.07s; +} + +.loader2 { + animation-delay: 0.14s; +} + +.loader3 { + animation-delay: 0.21s; +} diff --git a/packages/website/src/components/loader.tsx b/packages/website/src/components/loader.tsx new file mode 100644 index 00000000000..83d75a4e3d0 --- /dev/null +++ b/packages/website/src/components/loader.tsx @@ -0,0 +1,15 @@ +import * as React from 'react'; +import clsx from 'clsx'; +import styles from './loader.module.css'; + +function Loader(): JSX.Element { + return ( + + + + + + ); +} + +export default Loader; diff --git a/packages/website/src/components/modal.module.css b/packages/website/src/components/modal.module.css new file mode 100644 index 00000000000..45c0099482d --- /dev/null +++ b/packages/website/src/components/modal.module.css @@ -0,0 +1,62 @@ +.modal { + display: none; + position: fixed; + z-index: 1; + padding-top: 100px; + left: 0; + top: 0; + width: 100%; + height: 100%; + overflow: auto; + background-color: rgba(0, 0, 0, 0.4); +} + +.modal.open { + display: block; +} + +.modalContent { + position: relative; + background-color: var(--ifm-background-surface-color); + border-radius: var(--ifm-global-radius); + margin: auto; + padding: 0; + width: calc(var(--ifm-container-width) * 0.7); + animation-name: animatetop; + animation-duration: 0.4s; +} + +@keyframes animatetop { + from { + top: -30rem; + opacity: 0; + } + to { + top: 0; + opacity: 1; + } +} + +.modalClose { + cursor: pointer; +} + +.modalClose:hover, +.modalClose:focus { + color: var(--ifm-color-primary); +} + +.modalHeader { + display: flex; + padding: 1rem 1.5rem; + justify-content: space-between; + align-items: baseline; +} + +.modalHeader h2 { + margin: 0; +} + +.modalBody { + padding: 1rem 1.5rem; +} diff --git a/packages/website/src/components/modal.tsx b/packages/website/src/components/modal.tsx new file mode 100644 index 00000000000..f5a67d51008 --- /dev/null +++ b/packages/website/src/components/modal.tsx @@ -0,0 +1,40 @@ +import React from 'react'; +import clsx from 'clsx'; +import styles from './modal.module.css'; +import { CloseIcon } from './icons'; + +interface ModalProps { + header: string; + children: JSX.Element; + isOpen: boolean; + onClose: () => void; +} + +function Modal(props: ModalProps): JSX.Element { + return ( +
+
{ + e.preventDefault(); + e.stopPropagation(); + }} + > +
+

{props.header}

+ +
+
{props.children}
+
+
+ ); +} + +export default Modal; diff --git a/packages/website/src/components/options-selector.module.css b/packages/website/src/components/options-selector.module.css new file mode 100644 index 00000000000..4be50a8362f --- /dev/null +++ b/packages/website/src/components/options-selector.module.css @@ -0,0 +1,76 @@ +.optionLabel { + cursor: pointer; +} + +.optionItem, +.optionLabel { + align-items: center; + display: flex; + flex: 0 0 1.5rem; + flex-direction: row; + font-size: 0.75rem; + margin: 0 0; + padding: 0.4rem 0.8rem; + transition: background-color var(--ifm-transition-fast) + var(--ifm-transition-timing-default), + color var(--ifm-transition-fast) var(--ifm-transition-timing-default); + color: var(--ifm-font-color-secondary); + justify-content: space-between; +} + +.optionLabel:hover { + background-color: var(--ifm-color-emphasis-100); + color: var(--ifm-font-color-primary); +} + +.clickableIcon { + cursor: pointer; +} + +.clickableIcon:hover { + color: var(--ifm-color-primary); +} + +.optionInput { + display: block; + width: 90%; + padding: 0.4rem 0.6rem; + line-height: 1; + font-size: 0.8rem; + font-weight: 500; + font-family: inherit; + border-radius: 6px; + -webkit-appearance: none; + color: var(--ifm-font-color-secondary); + border: 1px solid var(--ifm-color-emphasis-100); + background: var(--ifm-color-emphasis-200); + transition: border 0.3s ease; +} +.optionInput::placeholder { + color: var(--ifm-color-emphasis-700); +} +.optionInput:focus { + outline: none; + border-color: var(--ifm-color-primary); +} + +.optionSelect { + line-height: 1; + font-size: 0.8rem; + font-weight: 500; + border-radius: 6px; + font-family: inherit; + width: 50%; + box-shadow: none; + background-image: none; + padding: 0.4rem 0.6rem; + appearance: none; + color: var(--ifm-font-color-secondary); + border: 1px solid var(--ifm-color-emphasis-100); + background: var(--ifm-color-emphasis-200); + transition: border 0.3s ease; +} +.optionSelect:focus { + outline: none; + border-color: var(--ifm-color-primary); +} diff --git a/packages/website/src/components/options-selector.tsx b/packages/website/src/components/options-selector.tsx new file mode 100644 index 00000000000..75a77932fd0 --- /dev/null +++ b/packages/website/src/components/options-selector.tsx @@ -0,0 +1,170 @@ +import Expander from './expander'; +import React, { useCallback, useState } from 'react'; +import styles from './options-selector.module.css'; +import { DeleteIcon, AddIcon } from './icons'; +import { HashStateOptions } from './lib/use-hash-state'; +import clsx from 'clsx'; + +function computeRuleOptions( + rules: Record, + ruleNames: string[], +): string[] { + const keys = Object.keys(rules); + return ruleNames.filter(name => !keys.includes(name)); +} + +interface OptionsSelectorParams { + ruleOptions: string[]; + state: T; + setState: (key: keyof T, value: unknown) => void; + tsVersions: readonly string[]; +} + +function OptionsSelector({ + ruleOptions, + state, + setState, + tsVersions, +}: OptionsSelectorParams): JSX.Element { + const [ruleName, setRuleName] = useState(''); + + const removeRule = useCallback( + (item: string) => { + const { [item]: _, ...newRules } = state.rules ?? {}; + setState('rules', newRules); + }, + [state], + ); + + const computedRules = computeRuleOptions(state.rules ?? {}, ruleOptions); + + const addRule = useCallback(() => { + if (computedRules.length) { + const newRules = { + ...state.rules, + [ruleName || computedRules[0]]: ['error'], + }; + setState('rules', newRules); + setRuleName(''); + } + }, [state, ruleName]); + + const updateTS = useCallback((version: string) => { + setState('ts', version); + }, []); + + return ( + <> + + + + + + + + + + + + {Object.entries(state.rules ?? {}).map(([rule]) => ( + + ))} +
+ + {computedRules.length ? ( +
+ + +
+ ) : ( +
+ )} +
+ + + ); +} + +export default OptionsSelector; diff --git a/packages/website/src/components/playground.module.css b/packages/website/src/components/playground.module.css new file mode 100644 index 00000000000..95c2b206d7a --- /dev/null +++ b/packages/website/src/components/playground.module.css @@ -0,0 +1,42 @@ +.options { + width: 20rem; + background: var(--ifm-background-surface-color); + overflow: auto; +} + +.sourceCode { + height: 100%; + width: 50%; + border: 1px solid var(--ifm-color-emphasis-100); +} + +.sourceCodeStandalone { + width: 100%; +} + +.codeBlocks { + display: flex; + flex-direction: row; + height: 100%; + width: calc(100vw - 20rem); +} + +.astViewer { + height: 100%; + width: 50%; + border: 1px solid var(--ifm-color-emphasis-100); + overflow: auto; + background: var(--ifm-background-surface-color); + word-wrap: initial; + white-space: nowrap; +} + +.codeContainer { + display: flex; + flex-direction: row; + position: fixed; + width: 100%; + height: 100%; + top: 0; + padding-top: 60px; /* navbar 60 + driver 52 */ +} diff --git a/packages/website/src/components/playground.tsx b/packages/website/src/components/playground.tsx new file mode 100644 index 00000000000..124e1724446 --- /dev/null +++ b/packages/website/src/components/playground.tsx @@ -0,0 +1,144 @@ +import React, { useCallback, useState } from 'react'; +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore +import useThemeContext from '@theme/hooks/useThemeContext'; +import styles from './playground.module.css'; +import Loader from './loader'; + +import useHashState from './lib/use-hash-state'; + +import type { ParseForESLintResult } from './linter/parser'; +import type { TSESTree } from '@typescript-eslint/types'; +import type Monaco from 'monaco-editor'; +import OptionsSelector from './options-selector'; +import ASTViewer from './ast-viewer'; +import clsx from 'clsx'; +import Editor from './editor'; +import { findNode } from './lib/selection'; + +function Playground(): JSX.Element { + const [state, setState] = useHashState({ + jsx: false, + showAST: false, + sourceType: 'module' as 'script' | 'module', + code: '', + ts: process.env.TS_VERSION, + rules: {}, + }); + // eslint-disable-next-line @typescript-eslint/no-unsafe-call,@typescript-eslint/no-unsafe-assignment + const { isDarkTheme } = useThemeContext(); + const [ast, setAST] = useState(); + const [ruleNames, setRuleNames] = useState([]); + const [isLoading, setIsLoading] = useState(true); + const [tsVersions, setTSVersion] = useState([]); + const [selectedNode, setSelectedNode] = useState(null); + const [highlightedNode, setHighlightedNode] = useState( + null, + ); + + const updatePosition = useCallback( + (position: Monaco.Position | null) => { + if (typeof ast === 'object' && ast && position) { + setHighlightedNode( + findNode( + { + line: position.lineNumber, + column: position.column - 1, + }, + ast, + ), + ); + } + }, + [ast], + ); + + const updateSelectedNode = useCallback( + (node: TSESTree.Node | null) => { + if ( + selectedNode !== node && + (!node || + !selectedNode || + selectedNode.range[0] !== node.range[0] || + selectedNode.range[1] !== node.range[1]) + ) { + setSelectedNode(node); + } + }, + [selectedNode], + ); + + const updateAST = useCallback( + (value: string | TSESTree.Program, position: Monaco.Position | null) => { + if (typeof value === 'object' && value) { + setAST(value); + if (position) { + setHighlightedNode( + findNode( + { + line: position.lineNumber, + column: position.column - 1, + }, + value, + ), + ); + } + } + }, + [], + ); + + return ( +
+
+ +
+
+
+ {isLoading && } + setState('code', code)} + onLoaded={(tsVersions): void => { + setTSVersion(tsVersions); + setIsLoading(true); + }} + onSelect={updatePosition} + /> +
+ {state.showAST && ( +
+ {ast && ( + + )} +
+ )} +
+
+ ); +} + +export default Playground; diff --git a/packages/website/src/modules/eslint-rules.js b/packages/website/src/modules/eslint-rules.js new file mode 100644 index 00000000000..a0c1d90fbc0 --- /dev/null +++ b/packages/website/src/modules/eslint-rules.js @@ -0,0 +1,5 @@ +import builtinRules from 'eslint/lib/rules'; + +const FileEnumerator = {}; + +export { builtinRules, FileEnumerator }; diff --git a/packages/website/src/modules/eslint.js b/packages/website/src/modules/eslint.js new file mode 100644 index 00000000000..c8f20a01838 --- /dev/null +++ b/packages/website/src/modules/eslint.js @@ -0,0 +1,12 @@ +import { Linter } from 'eslint/lib/linter/linter'; + +//----------------------------------------------------------------------------- +// Exports +//----------------------------------------------------------------------------- + +class RuleTester {} + +const ESLint = {}; +const SourceCode = {}; + +export { Linter, ESLint, RuleTester, SourceCode }; diff --git a/packages/website/src/modules/eslintrc.js b/packages/website/src/modules/eslintrc.js new file mode 100644 index 00000000000..19c9ce0fa24 --- /dev/null +++ b/packages/website/src/modules/eslintrc.js @@ -0,0 +1 @@ +module.exports = require('@eslint/eslintrc/dist/eslintrc-universal.cjs'); diff --git a/packages/website/src/modules/globby.js b/packages/website/src/modules/globby.js new file mode 100644 index 00000000000..dd317fb086b --- /dev/null +++ b/packages/website/src/modules/globby.js @@ -0,0 +1,5 @@ +module.exports = { + sync() { + return ['./tsconfig.json']; + }, +}; diff --git a/packages/website/src/modules/is-glob.js b/packages/website/src/modules/is-glob.js new file mode 100644 index 00000000000..0ba64710234 --- /dev/null +++ b/packages/website/src/modules/is-glob.js @@ -0,0 +1 @@ +module.exports = () => false; diff --git a/packages/website/src/modules/resolve-from.js b/packages/website/src/modules/resolve-from.js new file mode 100644 index 00000000000..23b45ce717b --- /dev/null +++ b/packages/website/src/modules/resolve-from.js @@ -0,0 +1,12 @@ +'use strict'; +const resolveFrom = (/*fromDir, moduleId, silent*/) => { + return { + id: 'id', + filename: 'filename', + paths: 'test', + }; +}; + +module.exports = (fromDir, moduleId) => resolveFrom(fromDir, moduleId); +module.exports.silent = (fromDir, moduleId) => + resolveFrom(fromDir, moduleId, true); diff --git a/packages/website/src/modules/semver.js b/packages/website/src/modules/semver.js new file mode 100644 index 00000000000..c49e54bcbb7 --- /dev/null +++ b/packages/website/src/modules/semver.js @@ -0,0 +1,7 @@ +module.exports.satisfies = () => true; +module.exports.major = version => { + const v = version.match( + /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(-[a-zA-Z\d][-a-zA-Z.\d]*)?(\+[a-zA-Z\d][-a-zA-Z.\d]*)?$/, + ); + return v && v[1] ? v[1] : null; +}; diff --git a/packages/website/src/pages/index.tsx b/packages/website/src/pages/index.tsx index a8ccdd6fc82..0373171828f 100644 --- a/packages/website/src/pages/index.tsx +++ b/packages/website/src/pages/index.tsx @@ -129,6 +129,15 @@ function Feature({ imageUrl, title, description }: FeatureItem): JSX.Element { ); } +interface SponsorsModel { + tier: string; + name: string; + slug: string; + website: string; + image: string; + description: string; +} + function Sponsors(props: { tier: string; title: string; @@ -136,9 +145,9 @@ function Sponsors(props: { }): JSX.Element { const { siteConfig } = useDocusaurusContext(); - const tierSponsors = siteConfig.customFields.sponsors.filter( - sponsor => sponsor.tier === props.tier, - ); + const tierSponsors = ( + siteConfig.customFields!.sponsors as SponsorsModel[] + ).filter(sponsor => sponsor.tier === props.tier); return (
    @@ -171,6 +180,12 @@ function Home(): JSX.Element { Get Started + + Playground +
diff --git a/packages/website/src/pages/repl.tsx b/packages/website/src/pages/repl.tsx new file mode 100644 index 00000000000..3b8d4436630 --- /dev/null +++ b/packages/website/src/pages/repl.tsx @@ -0,0 +1,31 @@ +import React, { lazy, Suspense } from 'react'; +import Layout from '@theme/Layout'; + +/** + * we do not want to load playground for ssr + */ +const Playground = !process.env.IS_SERVER + ? lazy(() => import('../components/playground')) + : (): JSX.Element =>
; + +/** + * This is a hack for stuff that are bad in docusaurus + * https://reactjs.org/docs/error-decoder.html?invariant=294 + */ +const SSRSuspense = !process.env.IS_SERVER + ? Suspense + : (props: { children: JSX.Element }): JSX.Element => props.children; + +function Repl(): JSX.Element { + return ( + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + + + + + + ); +} + +export default Repl; diff --git a/packages/website/src/vendor/ds/createDesignSystem.d.ts b/packages/website/src/vendor/ds/createDesignSystem.d.ts new file mode 100644 index 00000000000..766a4bbae4e --- /dev/null +++ b/packages/website/src/vendor/ds/createDesignSystem.d.ts @@ -0,0 +1,71 @@ +import type { Sandbox } from '../sandbox'; +import type { DiagnosticRelatedInformation, Node } from 'typescript'; +export declare type LocalStorageOption = { + blurb: string; + flag: string; + display: string; + emptyImpliesEnabled?: true; + oneline?: true; + requireRestart?: true; + onchange?: (newValue: boolean) => void; +}; +export declare type OptionsListConfig = { + style: 'separated' | 'rows'; + requireRestart?: true; +}; +export declare const createDesignSystem: ( + sandbox: Sandbox, +) => ( + container: Element, +) => { + /** Clear the sidebar */ + clear: () => void; + /** Present code in a pre > code */ + code: (code: string) => HTMLElement; + /** Ideally only use this once, and maybe even prefer using subtitles everywhere */ + title: (title: string) => HTMLElement; + /** Used to denote sections, give info etc */ + subtitle: (subtitle: string) => HTMLElement; + /** Used to show a paragraph */ + p: (subtitle: string) => HTMLElement; + /** When you can't do something, or have nothing to show */ + showEmptyScreen: (message: string) => HTMLDivElement; + /** + * Shows a list of hoverable, and selectable items (errors, highlights etc) which have code representation. + * The type is quite small, so it should be very feasible for you to massage other data to fit into this function + */ + listDiags: ( + model: import('monaco-editor').editor.ITextModel, + diags: DiagnosticRelatedInformation[], + ) => HTMLUListElement; + /** Shows a single option in local storage (adds an li to the container BTW) */ + localStorageOption: (setting: LocalStorageOption) => HTMLLIElement; + /** Uses localStorageOption to create a list of options */ + showOptionList: ( + options: LocalStorageOption[], + style: OptionsListConfig, + ) => void; + /** Shows a full-width text input */ + createTextInput: (config: { + id: string; + placeholder: string; + onChanged?: ((text: string, input: HTMLInputElement) => void) | undefined; + onEnter: (text: string, input: HTMLInputElement) => void; + value?: string | undefined; + keepValueAcrossReloads?: true | undefined; + isEnabled?: ((input: HTMLInputElement) => boolean) | undefined; + }) => HTMLFormElement; + /** Renders an AST tree */ + createASTTree: (node: Node) => HTMLDivElement; + /** Creates an input button */ + button: (settings: { + label: string; + onclick?: ((ev: MouseEvent) => void) | undefined; + }) => HTMLInputElement; + /** Used to re-create a UI like the tab bar at the top of the plugins section */ + createTabBar: () => HTMLDivElement; + /** Used with createTabBar to add buttons */ + createTabButton: (text: string) => HTMLButtonElement; + /** A general "restart your browser" message */ + declareRestartRequired: (i?: ((key: string) => string) | undefined) => void; +}; diff --git a/packages/website/src/vendor/playground.d.ts b/packages/website/src/vendor/playground.d.ts new file mode 100644 index 00000000000..ef10edc3e27 --- /dev/null +++ b/packages/website/src/vendor/playground.d.ts @@ -0,0 +1,138 @@ +declare type Sandbox = import('./sandbox').Sandbox; +declare type Monaco = typeof import('monaco-editor'); +import { PluginUtils } from './pluginUtils'; +import type React from 'react'; +export { PluginUtils } from './pluginUtils'; +export declare type PluginFactory = { + ( + i: (key: string, components?: any) => string, + utils: PluginUtils, + ): PlaygroundPlugin; +}; +/** The interface of all sidebar plugins */ +export interface PlaygroundPlugin { + /** Not public facing, but used by the playground to uniquely identify plugins */ + id: string; + /** To show in the tabs */ + displayName: string; + /** Should this plugin be selected when the plugin is first loaded? Lets you check for query vars etc to load a particular plugin */ + shouldBeSelected?: () => boolean; + /** Before we show the tab, use this to set up your HTML - it will all be removed by the playground when someone navigates off the tab */ + willMount?: (sandbox: Sandbox, container: HTMLDivElement) => void; + /** After we show the tab */ + didMount?: (sandbox: Sandbox, container: HTMLDivElement) => void; + /** Model changes while this plugin is actively selected */ + modelChanged?: ( + sandbox: Sandbox, + model: import('monaco-editor').editor.ITextModel, + container: HTMLDivElement, + ) => void; + /** Delayed model changes while this plugin is actively selected, useful when you are working with the TS API because it won't run on every keypress */ + modelChangedDebounce?: ( + sandbox: Sandbox, + model: import('monaco-editor').editor.ITextModel, + container: HTMLDivElement, + ) => void; + /** Before we remove the tab */ + willUnmount?: (sandbox: Sandbox, container: HTMLDivElement) => void; + /** After we remove the tab */ + didUnmount?: (sandbox: Sandbox, container: HTMLDivElement) => void; + /** An object you can use to keep data around in the scope of your plugin object */ + data?: any; +} +interface PlaygroundConfig { + /** Language like "en" / "ja" etc */ + lang: string; + /** Site prefix, like "v2" during the pre-release */ + prefix: string; + /** Optional plugins so that we can re-use the playground with different sidebars */ + plugins?: PluginFactory[]; + /** Should this playground load up custom plugins from localStorage? */ + supportCustomPlugins: boolean; +} +export declare const setupPlayground: ( + sandbox: Sandbox, + monaco: Monaco, + config: PlaygroundConfig, + i: (key: string) => string, + react: typeof React, +) => { + exporter: { + openProjectInStackBlitz: () => void; + openProjectInCodeSandbox: () => void; + copyAsMarkdownIssue: ( + e: React.MouseEvent, + ) => Promise; + copyForChat: (e: React.MouseEvent) => boolean; + copyForChatWithPreview: ( + e: React.MouseEvent, + ) => boolean; + openInTSAST: () => void; + openInBugWorkbench: () => void; + exportAsTweet: () => void; + }; + // ui: import("./createUI").UI; + registerPlugin: (plugin: PlaygroundPlugin) => void; + plugins: PlaygroundPlugin[]; + getCurrentPlugin: () => PlaygroundPlugin; + tabs: HTMLButtonElement[]; + setDidUpdateTab: ( + func: ( + newPlugin: PlaygroundPlugin, + previousPlugin: PlaygroundPlugin, + ) => void, + ) => void; + createUtils: ( + sb: any, + react: typeof React, + ) => { + el: (str: string, elementType: string, container: Element) => HTMLElement; + requireURL: (path: string) => string; + react: typeof React; + createDesignSystem: ( + container: Element, + ) => { + clear: () => void; + code: (code: string) => HTMLElement; + title: (title: string) => HTMLElement; + subtitle: (subtitle: string) => HTMLElement; + p: (subtitle: string) => HTMLElement; + showEmptyScreen: (message: string) => HTMLDivElement; + listDiags: ( + model: import('monaco-editor').editor.ITextModel, + diags: import('typescript').DiagnosticRelatedInformation[], + ) => HTMLUListElement; + localStorageOption: ( + setting: import('./ds/createDesignSystem').LocalStorageOption, + ) => HTMLLIElement; + showOptionList: ( + options: import('./ds/createDesignSystem').LocalStorageOption[], + style: import('./ds/createDesignSystem').OptionsListConfig, + ) => void; + createTextInput: (config: { + id: string; + placeholder: string; + onChanged?: + | ((text: string, input: HTMLInputElement) => void) + | undefined; + onEnter: (text: string, input: HTMLInputElement) => void; + value?: string | undefined; + keepValueAcrossReloads?: true | undefined; + isEnabled?: ((input: HTMLInputElement) => boolean) | undefined; + }) => HTMLFormElement; + createASTTree: (node: import('typescript').Node) => HTMLDivElement; + button: (settings: { + label: string; + onclick?: ((ev: MouseEvent) => void) | undefined; + }) => HTMLInputElement; + createTabBar: () => HTMLDivElement; + createTabButton: (text: string) => HTMLButtonElement; + declareRestartRequired: ( + i?: ((key: string) => string) | undefined, + ) => void; + }; + flashHTMLElement: (element: HTMLElement) => void; + setNotifications: (pluginID: string, amount: number) => void; + }; +}; +export declare type Playground = ReturnType; diff --git a/packages/website/src/vendor/pluginUtils.d.ts b/packages/website/src/vendor/pluginUtils.d.ts new file mode 100644 index 00000000000..09eee2e702a --- /dev/null +++ b/packages/website/src/vendor/pluginUtils.d.ts @@ -0,0 +1,60 @@ +import type React from 'react'; +/** Creates a set of util functions which is exposed to Plugins to make it easier to build consistent UIs */ +export declare const createUtils: ( + sb: any, + react: typeof React, +) => { + /** Use this to make a few dumb element generation funcs */ + el: (str: string, elementType: string, container: Element) => HTMLElement; + /** Get a relative URL for something in your dist folder depending on if you're in dev mode or not */ + requireURL: (path: string) => string; + /** The Gatsby copy of React */ + react: typeof React; + /** + * The playground plugin design system. Calling any of the functions will append the + * element to the container you pass into the first param, and return the HTMLElement + */ + createDesignSystem: ( + container: Element, + ) => { + clear: () => void; + code: (code: string) => HTMLElement; + title: (title: string) => HTMLElement; + subtitle: (subtitle: string) => HTMLElement; + p: (subtitle: string) => HTMLElement; + showEmptyScreen: (message: string) => HTMLDivElement; + listDiags: ( + model: import('monaco-editor').editor.ITextModel, + diags: import('typescript').DiagnosticRelatedInformation[], + ) => HTMLUListElement; + localStorageOption: ( + setting: import('./ds/createDesignSystem').LocalStorageOption, + ) => HTMLLIElement; + showOptionList: ( + options: import('./ds/createDesignSystem').LocalStorageOption[], + style: import('./ds/createDesignSystem').OptionsListConfig, + ) => void; + createTextInput: (config: { + id: string; + placeholder: string; + onChanged?: ((text: string, input: HTMLInputElement) => void) | undefined; + onEnter: (text: string, input: HTMLInputElement) => void; + value?: string | undefined; + keepValueAcrossReloads?: true | undefined; + isEnabled?: ((input: HTMLInputElement) => boolean) | undefined; + }) => HTMLFormElement; + createASTTree: (node: import('typescript').Node) => HTMLDivElement; + button: (settings: { + label: string; + onclick?: ((ev: MouseEvent) => void) | undefined; + }) => HTMLInputElement; + createTabBar: () => HTMLDivElement; + createTabButton: (text: string) => HTMLButtonElement; + declareRestartRequired: (i?: ((key: string) => string) | undefined) => void; + }; + /** Flashes a HTML Element */ + flashHTMLElement: (element: HTMLElement) => void; + /** Add a little red button in the top corner of a plugin tab with a number */ + setNotifications: (pluginID: string, amount: number) => void; +}; +export declare type PluginUtils = ReturnType; diff --git a/packages/website/src/vendor/sandbox.d.ts b/packages/website/src/vendor/sandbox.d.ts new file mode 100644 index 00000000000..bf6ebfde659 --- /dev/null +++ b/packages/website/src/vendor/sandbox.d.ts @@ -0,0 +1,288 @@ +import { TypeScriptWorker } from './tsWorker'; // import { TypeScriptWorker } from "./tsWorker"; +// import lzstring from "./vendor/lzstring.min"; + +import * as tsvfs from './typescript-vfs'; +declare type CompilerOptions = import('monaco-editor').languages.typescript.CompilerOptions; +declare type Monaco = typeof import('monaco-editor'); +/** + * These are settings for the playground which are the equivalent to props in React + * any changes to it should require a new setup of the playground + */ +export declare type PlaygroundConfig = { + /** The default source code for the playground */ + text: string; + /** Should it run the ts or js IDE services */ + useJavaScript: boolean; + /** Compiler options which are automatically just forwarded on */ + compilerOptions: CompilerOptions; + /** Optional monaco settings overrides */ + monacoSettings?: import('monaco-editor').editor.IEditorOptions; + /** Acquire types via type acquisition */ + acquireTypes: boolean; + /** Support twoslash compiler options */ + supportTwoslashCompilerOptions: boolean; + /** Get the text via query params and local storage, useful when the editor is the main experience */ + suppressAutomaticallyGettingDefaultText?: true; + /** Suppress setting compiler options from the compiler flags from query params */ + suppressAutomaticallyGettingCompilerFlags?: true; + /** Logging system */ + logger: { + log: (...args: any[]) => void; + error: (...args: any[]) => void; + groupCollapsed: (...args: any[]) => void; + groupEnd: (...args: any[]) => void; + }; +} & ( + | { + domID: string; + } + | { + elementToAppend: HTMLElement; + } +); +/** The default settings which we apply a partial over */ +export declare function defaultPlaygroundSettings(): { + /** The default source code for the playground */ + text: string; + /** Should it run the ts or js IDE services */ + useJavaScript: boolean; + /** Compiler options which are automatically just forwarded on */ + compilerOptions: import('monaco-editor').languages.typescript.CompilerOptions; + /** Optional monaco settings overrides */ + monacoSettings?: import('monaco-editor').editor.IEditorOptions | undefined; + /** Acquire types via type acquisition */ + acquireTypes: boolean; + /** Support twoslash compiler options */ + supportTwoslashCompilerOptions: boolean; + /** Get the text via query params and local storage, useful when the editor is the main experience */ + suppressAutomaticallyGettingDefaultText?: true | undefined; + /** Suppress setting compiler options from the compiler flags from query params */ + suppressAutomaticallyGettingCompilerFlags?: true | undefined; + /** Logging system */ + logger: { + log: (...args: any[]) => void; + error: (...args: any[]) => void; + groupCollapsed: (...args: any[]) => void; + groupEnd: (...args: any[]) => void; + }; +} & { + domID: string; +}; +/** Creates a sandbox editor, and returns a set of useful functions and the editor */ +export declare const createTypeScriptSandbox: ( + partialConfig: Partial, + monaco: Monaco, + ts: typeof import('typescript'), +) => { + /** The same config you passed in */ + config: { + text: string; + useJavaScript: boolean; + compilerOptions: CompilerOptions; + monacoSettings?: import('monaco-editor').editor.IEditorOptions | undefined; + acquireTypes: boolean; + supportTwoslashCompilerOptions: boolean; + suppressAutomaticallyGettingDefaultText?: true | undefined; + suppressAutomaticallyGettingCompilerFlags?: true | undefined; + logger: { + log: (...args: any[]) => void; + error: (...args: any[]) => void; + groupCollapsed: (...args: any[]) => void; + groupEnd: (...args: any[]) => void; + }; + domID: string; + }; + /** A list of TypeScript versions you can use with the TypeScript sandbox */ + supportedVersions: readonly [ + '4.2.2', + '4.1.5', + '4.0.5', + '3.9.7', + '3.8.3', + '3.7.5', + '3.6.3', + '3.5.1', + '3.3.3', + '3.1.6', + '3.0.1', + '2.8.1', + '2.7.2', + '2.4.1', + ]; + /** The monaco editor instance */ + editor: import('monaco-editor').editor.IStandaloneCodeEditor; + /** Either "typescript" or "javascript" depending on your config */ + language: string; + /** The outer monaco module, the result of require("monaco-editor") */ + monaco: typeof import('monaco-editor'); + /** Gets a monaco-typescript worker, this will give you access to a language server. Note: prefer this for language server work because it happens on a webworker . */ + getWorkerProcess: () => Promise; + /** A copy of require("@typescript/vfs") this can be used to quickly set up an in-memory compiler runs for ASTs, or to get complex language server results (anything above has to be serialized when passed)*/ + tsvfs: typeof tsvfs; + /** Get all the different emitted files after TypeScript is run */ + getEmitResult: () => Promise; + /** Gets just the JavaScript for your sandbox, will transpile if in TS only */ + getRunnableJS: () => Promise; + /** Gets the DTS output of the main code in the editor */ + getDTSForCode: () => Promise; + /** The monaco-editor dom node, used for showing/hiding the editor */ + getDomNode: () => HTMLElement; + /** The model is an object which monaco uses to keep track of text in the editor. Use this to directly modify the text in the editor */ + getModel: () => import('monaco-editor').editor.ITextModel; + /** Gets the text of the main model, which is the text in the editor */ + getText: () => string; + /** Shortcut for setting the model's text content which would update the editor */ + setText: (text: string) => void; + /** Gets the AST of the current text in monaco - uses `createTSProgram`, so the performance caveat applies there too */ + getAST: () => Promise; + /** The module you get from require("typescript") */ + ts: typeof import('typescript'); + /** Create a new Program, a TypeScript data model which represents the entire project. As well as some of the + * primitive objects you would normally need to do work with the files. + * + * The first time this is called it has to download all the DTS files which is needed for an exact compiler run. Which + * at max is about 1.5MB - after that subsequent downloads of dts lib files come from localStorage. + * + * Try to use this sparingly as it can be computationally expensive, at the minimum you should be using the debounced setup. + * + * TODO: It would be good to create an easy way to have a single program instance which is updated for you + * when the monaco model changes. + */ + setupTSVFS: () => Promise<{ + program: import('typescript').Program; + system: import('typescript').System; + host: { + compilerHost: import('typescript').CompilerHost; + updateFile: (sourceFile: import('typescript').SourceFile) => boolean; + }; + fsMap: Map; + }>; + /** Uses the above call setupTSVFS, but only returns the program */ + createTSProgram: () => Promise; + /** The Sandbox's default compiler options */ + compilerDefaults: { + [x: string]: + | string + | number + | boolean + | string[] + | (string | number)[] + | import('monaco-editor').languages.typescript.MapLike + | null + | undefined; + allowJs?: boolean | undefined; + allowSyntheticDefaultImports?: boolean | undefined; + allowUmdGlobalAccess?: boolean | undefined; + allowUnreachableCode?: boolean | undefined; + allowUnusedLabels?: boolean | undefined; + alwaysStrict?: boolean | undefined; + baseUrl?: string | undefined; + charset?: string | undefined; + checkJs?: boolean | undefined; + declaration?: boolean | undefined; + declarationMap?: boolean | undefined; + emitDeclarationOnly?: boolean | undefined; + declarationDir?: string | undefined; + disableSizeLimit?: boolean | undefined; + disableSourceOfProjectReferenceRedirect?: boolean | undefined; + downlevelIteration?: boolean | undefined; + emitBOM?: boolean | undefined; + emitDecoratorMetadata?: boolean | undefined; + experimentalDecorators?: boolean | undefined; + forceConsistentCasingInFileNames?: boolean | undefined; + importHelpers?: boolean | undefined; + inlineSourceMap?: boolean | undefined; + inlineSources?: boolean | undefined; + isolatedModules?: boolean | undefined; + jsx?: import('monaco-editor').languages.typescript.JsxEmit | undefined; + keyofStringsOnly?: boolean | undefined; + lib?: string[] | undefined; + locale?: string | undefined; + mapRoot?: string | undefined; + maxNodeModuleJsDepth?: number | undefined; + module?: + | import('monaco-editor').languages.typescript.ModuleKind + | undefined; + moduleResolution?: + | import('monaco-editor').languages.typescript.ModuleResolutionKind + | undefined; + newLine?: + | import('monaco-editor').languages.typescript.NewLineKind + | undefined; + noEmit?: boolean | undefined; + noEmitHelpers?: boolean | undefined; + noEmitOnError?: boolean | undefined; + noErrorTruncation?: boolean | undefined; + noFallthroughCasesInSwitch?: boolean | undefined; + noImplicitAny?: boolean | undefined; + noImplicitReturns?: boolean | undefined; + noImplicitThis?: boolean | undefined; + noStrictGenericChecks?: boolean | undefined; + noUnusedLocals?: boolean | undefined; + noUnusedParameters?: boolean | undefined; + noImplicitUseStrict?: boolean | undefined; + noLib?: boolean | undefined; + noResolve?: boolean | undefined; + out?: string | undefined; + outDir?: string | undefined; + outFile?: string | undefined; + paths?: + | import('monaco-editor').languages.typescript.MapLike + | undefined; + preserveConstEnums?: boolean | undefined; + preserveSymlinks?: boolean | undefined; + project?: string | undefined; + reactNamespace?: string | undefined; + jsxFactory?: string | undefined; + composite?: boolean | undefined; + removeComments?: boolean | undefined; + rootDir?: string | undefined; + rootDirs?: string[] | undefined; + skipLibCheck?: boolean | undefined; + skipDefaultLibCheck?: boolean | undefined; + sourceMap?: boolean | undefined; + sourceRoot?: string | undefined; + strict?: boolean | undefined; + strictFunctionTypes?: boolean | undefined; + strictBindCallApply?: boolean | undefined; + strictNullChecks?: boolean | undefined; + strictPropertyInitialization?: boolean | undefined; + stripInternal?: boolean | undefined; + suppressExcessPropertyErrors?: boolean | undefined; + suppressImplicitAnyIndexErrors?: boolean | undefined; + target?: + | import('monaco-editor').languages.typescript.ScriptTarget + | undefined; + traceResolution?: boolean | undefined; + resolveJsonModule?: boolean | undefined; + types?: string[] | undefined; + typeRoots?: string[] | undefined; + esModuleInterop?: boolean | undefined; + useDefineForClassFields?: boolean | undefined; + }; + /** The Sandbox's current compiler options */ + getCompilerOptions: () => import('monaco-editor').languages.typescript.CompilerOptions; + /** Replace the Sandbox's compiler options */ + setCompilerSettings: (opts: CompilerOptions) => void; + /** Overwrite the Sandbox's compiler options */ + updateCompilerSetting: (key: keyof CompilerOptions, value: any) => void; + /** Update a single compiler option in the SAndbox */ + updateCompilerSettings: (opts: CompilerOptions) => void; + /** A way to get callbacks when compiler settings have changed */ + setDidUpdateCompilerSettings: (func: (opts: CompilerOptions) => void) => void; + /** A copy of lzstring, which is used to archive/unarchive code */ + // lzstring: typeof lzstring; + /** Returns compiler options found in the params of the current page */ + createURLQueryWithCompilerOptions: ( + sandbox: any, + paramOverrides?: any, + ) => string; + /** Returns compiler options in the source code using twoslash notation */ + getTwoSlashComplierOptions: (code: string) => any; + /** Gets to the current monaco-language, this is how you talk to the background webworkers */ + languageServiceDefaults: import('monaco-editor').languages.typescript.LanguageServiceDefaults; + /** The path which represents the current file using the current compiler options */ + filepath: string; +}; +export declare type Sandbox = ReturnType; +export {}; diff --git a/packages/website/src/vendor/tsWorker.d.ts b/packages/website/src/vendor/tsWorker.d.ts new file mode 100644 index 00000000000..3ceeece9e09 --- /dev/null +++ b/packages/website/src/vendor/tsWorker.d.ts @@ -0,0 +1,98 @@ +import ts from 'typescript'; +export declare class TypeScriptWorker implements ts.LanguageServiceHost { + private _ctx; + private _extraLibs; + private _languageService; + private _compilerOptions; + constructor(ctx: any, createData: any); + getCompilationSettings(): ts.CompilerOptions; + getScriptFileNames(): string[]; + private _getModel; + getScriptVersion(fileName: string): string; + getScriptSnapshot(fileName: string): ts.IScriptSnapshot | undefined; + getScriptKind?(fileName: string): ts.ScriptKind; + getCurrentDirectory(): string; + getDefaultLibFileName(options: ts.CompilerOptions): string; + isDefaultLibFileName(fileName: string): boolean; + private static clearFiles; + getSyntacticDiagnostics(fileName: string): Promise; + getSemanticDiagnostics(fileName: string): Promise; + getSuggestionDiagnostics( + fileName: string, + ): Promise; + getCompilerOptionsDiagnostics(fileName: string): Promise; + getCompletionsAtPosition( + fileName: string, + position: number, + ): Promise; + getCompletionEntryDetails( + fileName: string, + position: number, + entry: string, + ): Promise; + getSignatureHelpItems( + fileName: string, + position: number, + ): Promise; + getQuickInfoAtPosition( + fileName: string, + position: number, + ): Promise; + getOccurrencesAtPosition( + fileName: string, + position: number, + ): Promise | undefined>; + getDefinitionAtPosition( + fileName: string, + position: number, + ): Promise | undefined>; + getReferencesAtPosition( + fileName: string, + position: number, + ): Promise; + getNavigationBarItems(fileName: string): Promise; + getFormattingEditsForDocument( + fileName: string, + options: ts.FormatCodeOptions, + ): Promise; + getFormattingEditsForRange( + fileName: string, + start: number, + end: number, + options: ts.FormatCodeOptions, + ): Promise; + getFormattingEditsAfterKeystroke( + fileName: string, + postion: number, + ch: string, + options: ts.FormatCodeOptions, + ): Promise; + findRenameLocations( + fileName: string, + positon: number, + findInStrings: boolean, + findInComments: boolean, + providePrefixAndSuffixTextForRename: boolean, + ): Promise; + getRenameInfo( + fileName: string, + positon: number, + options: ts.RenameInfoOptions, + ): Promise; + getEmitOutput(fileName: string): Promise; + getCodeFixesAtPosition( + fileName: string, + start: number, + end: number, + errorCodes: number[], + formatOptions: ts.FormatCodeOptions, + ): Promise>; + updateExtraLibs(extraLibs: IExtraLibs): void; +} +export interface IExtraLib { + content: string; + version: number; +} +export interface IExtraLibs { + [path: string]: IExtraLib; +} diff --git a/packages/website/src/vendor/typescript-vfs.d.ts b/packages/website/src/vendor/typescript-vfs.d.ts new file mode 100644 index 00000000000..5a5db30651a --- /dev/null +++ b/packages/website/src/vendor/typescript-vfs.d.ts @@ -0,0 +1,130 @@ +declare type System = import('typescript').System; +declare type CompilerOptions = import('typescript').CompilerOptions; +declare type CustomTransformers = import('typescript').CustomTransformers; +declare type LanguageServiceHost = import('typescript').LanguageServiceHost; +declare type CompilerHost = import('typescript').CompilerHost; +declare type SourceFile = import('typescript').SourceFile; +declare type TS = typeof import('typescript'); +export interface VirtualTypeScriptEnvironment { + sys: System; + languageService: import('typescript').LanguageService; + getSourceFile: ( + fileName: string, + ) => import('typescript').SourceFile | undefined; + createFile: (fileName: string, content: string) => void; + updateFile: ( + fileName: string, + content: string, + replaceTextSpan?: import('typescript').TextSpan, + ) => void; +} +/** + * Makes a virtual copy of the TypeScript environment. This is the main API you want to be using with + * @typescript/vfs. A lot of the other exposed functions are used by this function to get set up. + * + * @param sys an object which conforms to the TS Sys (a shim over read/write access to the fs) + * @param rootFiles a list of files which are considered inside the project + * @param ts a copy pf the TypeScript module + * @param compilerOptions the options for this compiler run + * @param customTransformers custom transformers for this compiler run + */ +export declare function createVirtualTypeScriptEnvironment( + sys: System, + rootFiles: string[], + ts: TS, + compilerOptions?: CompilerOptions, + customTransformers?: CustomTransformers, +): VirtualTypeScriptEnvironment; +/** + * Grab the list of lib files for a particular target, will return a bit more than necessary (by including + * the dom) but that's OK + * + * @param target The compiler settings target baseline + * @param ts A copy of the TypeScript module + */ +export declare const knownLibFilesForCompilerOptions: ( + compilerOptions: CompilerOptions, + ts: TS, +) => string[]; +/** + * Sets up a Map with lib contents by grabbing the necessary files from + * the local copy of typescript via the file system. + */ +export declare const createDefaultMapFromNodeModules: ( + compilerOptions: CompilerOptions, + ts?: typeof import('typescript') | undefined, +) => Map; +/** + * Adds recursively files from the FS into the map based on the folder + */ +export declare const addAllFilesFromFolder: ( + map: Map, + workingDir: string, +) => void; +/** Adds all files from node_modules/@types into the FS Map */ +export declare const addFilesForTypesIntoFolder: ( + map: Map, +) => void; +/** + * Create a virtual FS Map with the lib files from a particular TypeScript + * version based on the target, Always includes dom ATM. + * + * @param options The compiler target, which dictates the libs to set up + * @param version the versions of TypeScript which are supported + * @param cache should the values be stored in local storage + * @param ts a copy of the typescript import + * @param lzstring an optional copy of the lz-string import + * @param fetcher an optional replacement for the global fetch function (tests mainly) + * @param storer an optional replacement for the localStorage global (tests mainly) + */ +export declare const createDefaultMapFromCDN: ( + options: CompilerOptions, + version: string, + cache: boolean, + ts: TS, + lzstring?: any | undefined, + fetcher?: typeof fetch | undefined, + storer?: Storage | undefined, +) => Promise>; +/** + * Creates an in-memory System object which can be used in a TypeScript program, this + * is what provides read/write aspects of the virtual fs + */ +export declare function createSystem(files: Map): System; +/** + * Creates a file-system backed System object which can be used in a TypeScript program, you provide + * a set of virtual files which are prioritised over the FS versions, then a path to the root of your + * project (basically the folder your node_modules lives) + */ +export declare function createFSBackedSystem( + files: Map, + _projectRoot: string, + ts: TS, +): System; +/** + * Creates an in-memory CompilerHost -which is essentially an extra wrapper to System + * which works with TypeScript objects - returns both a compiler host, and a way to add new SourceFile + * instances to the in-memory file system. + */ +export declare function createVirtualCompilerHost( + sys: System, + compilerOptions: CompilerOptions, + ts: TS, +): { + compilerHost: CompilerHost; + updateFile: (sourceFile: SourceFile) => boolean; +}; +/** + * Creates an object which can host a language service against the virtual file-system + */ +export declare function createVirtualLanguageServiceHost( + sys: System, + rootFiles: string[], + compilerOptions: CompilerOptions, + ts: TS, + customTransformers?: CustomTransformers, +): { + languageServiceHost: LanguageServiceHost; + updateFile: (sourceFile: import('typescript').SourceFile) => void; +}; +export {}; diff --git a/packages/website/tsconfig.json b/packages/website/tsconfig.json index 5459ccb421e..8d740772b7c 100644 --- a/packages/website/tsconfig.json +++ b/packages/website/tsconfig.json @@ -1,5 +1,5 @@ { - "extends": "../tsconfig.base.json", + "extends": "../../tsconfig.base.json", "compilerOptions": { "module": "commonjs", "moduleResolution": "node", diff --git a/packages/website/webpack.plugin.js b/packages/website/webpack.plugin.js new file mode 100644 index 00000000000..cbfa2a442fa --- /dev/null +++ b/packages/website/webpack.plugin.js @@ -0,0 +1,117 @@ +const webpack = require('webpack'); +const path = require('path'); + +module.exports = function (/*context, options*/) { + return { + name: 'webpack-custom-plugin', + configureWebpack(cfg, isServer) { + return { + module: { + rules: [ + { + test: /\.js$/, + loader: 'string-replace-loader', + options: { + multiple: [ + { + search: '__importStar(require("typescript"))', + replace: 'window.ts', + }, + ], + }, + }, + ], + }, + resolve: { + exportsFields: [], + alias: { + '@typescript-eslint/experimental-utils/dist/ts-eslint/index.js': false, + '@typescript-eslint/typescript-estree/dist/create-program/createWatchProgram.js': false, + '@typescript-eslint/typescript-estree/dist/create-program/createProjectProgram.js': false, + '@typescript-eslint/typescript-estree/dist/create-program/createIsolatedProgram.js': false, + '@typescript-eslint/experimental-utils/dist/eslint-utils/RuleTester.js': false, + + 'eslint/lib/cli-engine/cli-engine': false, + 'eslint/lib/cli.js': false, + 'eslint/lib/eslint': false, + 'eslint/lib/eslint/index.js': false, + 'eslint/lib/eslint/eslint.js': false, + 'eslint/lib/cli-engine/cli-engine.js': false, + 'eslint/lib/cli-engine/load-rules.js': false, + 'eslint/lib/rule-tester/index.js': false, + 'eslint/lib/rule-tester/rule-tester.js': false, + 'eslint/lib/shared/ajv.js': false, + 'eslint/lib/shared/runtime-info.js': false, + 'eslint/lib/init/autoconfig.js': false, + 'eslint/lib/init/config-file.js': false, + 'eslint/lib/init/config-initializer.js': false, + 'eslint/lib/init/config-rule.js': false, + 'eslint/lib/init/npm-utils.js': false, + 'eslint/lib/init/source-code-utils.js': false, + }, + fallback: { + assert: require.resolve('assert'), + fs: false, + os: require.resolve('os-browserify/browser'), + tty: false, + path: require.resolve('path-browserify'), + util: require.resolve('util'), + crypto: false, + }, + }, + externals: { + typescript: 'window.ts', + }, + plugins: [ + new webpack.DefinePlugin({ + 'process.env': { + IS_SERVER: JSON.stringify(isServer), + TS_VERSION: JSON.stringify( + require('typescript/package.json').version, + ), + ESLINT_VERSION: JSON.stringify( + require('eslint/package.json').version, + ), + TS_ESLINT_VERSION: JSON.stringify( + require('@typescript-eslint/eslint-plugin/package.json') + .version, + ), + }, + }), + new webpack.NormalModuleReplacementPlugin( + /globby/, + path.resolve(__dirname, 'src/modules/globby.js'), + ), + new webpack.NormalModuleReplacementPlugin( + /resolve-from/, + path.resolve(__dirname, 'src/modules/resolve-from.js'), + ), + new webpack.NormalModuleReplacementPlugin( + /eslint$/, + path.resolve(__dirname, 'src/modules/eslint.js'), + ), + new webpack.NormalModuleReplacementPlugin( + /eslint\/use-at-your-own-risk$/, + path.resolve(__dirname, 'src/modules/eslint-rules.js'), + ), + new webpack.NormalModuleReplacementPlugin( + /@eslint\/eslintrc\/universal$/, + path.resolve(__dirname, 'src/modules/eslintrc.js'), + ), + new webpack.NormalModuleReplacementPlugin( + /esquery/, + require.resolve('esquery/dist/esquery.js'), + ), + new webpack.NormalModuleReplacementPlugin( + /semver/, + path.resolve(__dirname, 'src/modules/semver.js'), + ), + new webpack.NormalModuleReplacementPlugin( + /is-glob/, + path.resolve(__dirname, 'src/modules/is-glob.js'), + ), + ], + }; + }, + }; +}; diff --git a/tools/generate-website-dts.ts b/tools/generate-website-dts.ts new file mode 100644 index 00000000000..a0e2763db9a --- /dev/null +++ b/tools/generate-website-dts.ts @@ -0,0 +1,118 @@ +import * as fs from 'fs'; +import fetch from 'node-fetch'; +import * as path from 'path'; + +const baseHost = 'https://www.staging-typescript.org'; + +async function getFileAndStoreLocally( + url: string, + path: string, + editFunc: (arg: string) => string = (text: string): string => text, +): Promise { + const response = await fetch(baseHost + url, { + method: 'GET', + headers: { 'Content-Type': 'application/json' }, + }); + const contents = await response.text(); + fs.writeFileSync(path, editFunc(contents), 'utf8'); +} + +async function run(): Promise { + const vendor = path.join( + __dirname, + '..', + 'packages', + 'website', + 'src', + 'vendor', + ); + const ds = path.join(vendor, 'ds'); + + if (!fs.existsSync(vendor)) { + fs.mkdirSync(vendor); + } + if (!fs.existsSync(ds)) { + fs.mkdirSync(ds); + } + + // The API for the monaco typescript worker + await getFileAndStoreLocally( + '/js/sandbox/tsWorker.d.ts', + path.join(vendor, 'tsWorker.d.ts'), + ); + + // The Design System DTS + await getFileAndStoreLocally( + '/js/playground/ds/createDesignSystem.d.ts', + path.join(ds, 'createDesignSystem.d.ts'), + text => { + return text.replace('typescriptlang-org/static/js/sandbox', '../sandbox'); + }, + ); + + // Util funcs + await getFileAndStoreLocally( + '/js/playground/pluginUtils.d.ts', + path.join(vendor, 'pluginUtils.d.ts'), + text => { + return text.replace('from "@typescript/sandbox"', 'from "./sandbox"'); + }, + ); + + // TS-VFS + await getFileAndStoreLocally( + '/js/sandbox/vendor/typescript-vfs.d.ts', + path.join(vendor, 'typescript-vfs.d.ts'), + text => { + const removeImports = text.replace( + '/// ', + '', + ); + return removeImports.replace('import("lz-string").LZStringStatic', 'any'); + }, + ); + + // Sandbox + await getFileAndStoreLocally( + '/js/sandbox/index.d.ts', + path.join(vendor, 'sandbox.d.ts'), + text => { + const removeImports = text + .replace(/^import/g, '// import') + .replace(/\nimport/g, '\n// import'); + const replaceTSVFS = removeImports.replace( + '// import * as tsvfs from "./vendor/typescript-vfs"', + "\nimport * as tsvfs from './typescript-vfs'", + ); + const removedLZ = replaceTSVFS.replace( + 'lzstring: typeof lzstring', + '// lzstring: typeof lzstring', + ); + return 'import { TypeScriptWorker } from "./tsWorker";' + removedLZ; + }, + ); + + // Playground + await getFileAndStoreLocally( + '/js/playground/index.d.ts', + path.join(vendor, '/playground.d.ts'), + text => { + const replaceSandbox = text.replace(/@typescript\/sandbox/g, './sandbox'); + const replaceTSVFS = replaceSandbox.replace( + /typescriptlang-org\/static\/js\/sandbox\/vendor\/typescript-vfs/g, + './typescript-vfs', + ); + const removedLZ = replaceTSVFS.replace( + 'lzstring: typeof', + '// lzstring: typeof', + ); + const removedWorker = removedLZ.replace( + 'getWorkerProcess', + '// getWorkerProcess', + ); + return removedWorker.replace('ui:', '// ui:'); + }, + ); +} + +run(); diff --git a/yarn.lock b/yarn.lock index ae247d51954..27ed2c0ca17 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4250,6 +4250,16 @@ assert-plus@1.0.0, assert-plus@^1.0.0: resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU= +assert@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/assert/-/assert-2.0.0.tgz#95fc1c616d48713510680f2eaf2d10dd22e02d32" + integrity sha512-se5Cd+js9dXJnu6Ag2JFc00t+HmHOen+8Q+L7O9zI0PqQXr20uk2J0XQqMxZEeo5U50o8Nvmmx7dZrl+Ufr35A== + dependencies: + es6-object-assign "^1.1.0" + is-nan "^1.2.1" + object-is "^1.0.1" + util "^0.12.0" + assign-symbols@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" @@ -4309,6 +4319,11 @@ autoprefixer@^10.3.5, autoprefixer@^10.3.7: picocolors "^1.0.0" postcss-value-parser "^4.1.0" +available-typed-arrays@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz#92f95616501069d07d10edb2fc37d3e1c65123b7" + integrity sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw== + aws-sign2@~0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" @@ -6543,7 +6558,7 @@ error-ex@^1.2.0, error-ex@^1.3.1: dependencies: is-arrayish "^0.2.1" -es-abstract@^1.17.2, es-abstract@^1.19.0, es-abstract@^1.19.1: +es-abstract@^1.17.2, es-abstract@^1.18.5, es-abstract@^1.19.0, es-abstract@^1.19.1: version "1.19.1" resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.19.1.tgz#d4885796876916959de78edaa0df456627115ec3" integrity sha512-2vJ6tjA/UfqLm2MPs7jxVybLoB8i1t1Jd9R3kISld20sIxPcTbLuggQOUxeWeAvIUkduv/CfMjuh4WmiXr2v9w== @@ -6583,6 +6598,11 @@ es-to-primitive@^1.2.1: is-date-object "^1.0.1" is-symbol "^1.0.2" +es6-object-assign@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/es6-object-assign/-/es6-object-assign-1.1.0.tgz#c2c3582656247c39ea107cb1e6652b6f9f24523c" + integrity sha1-wsNYJlYkfDnqEHyx5mUrb58kUjw= + es6-promise@^4.0.3: version "4.2.8" resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.8.tgz#4eb21594c972bc40553d276e510539143db53e0a" @@ -7374,6 +7394,11 @@ for-in@^1.0.2: resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" integrity sha1-gQaNKVqBQuwKxybG4iAMMPttXoA= +foreach@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/foreach/-/foreach-2.0.5.tgz#0bee005018aeb260d0a3af3ae658dd0136ec1b99" + integrity sha1-C+4AUBiusmDQo6865ljdATbsG5k= + forever-agent@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" @@ -8834,6 +8859,13 @@ is-generator-fn@^2.0.0: resolved "https://registry.yarnpkg.com/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118" integrity sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ== +is-generator-function@^1.0.7: + version "1.0.10" + resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.0.10.tgz#f1558baf1ac17e0deea7c0415c438351ff2b3c72" + integrity sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A== + dependencies: + has-tostringtag "^1.0.0" + is-glob@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-3.1.0.tgz#7ba5ae24217804ac70707b96922567486cc3e84a" @@ -8868,6 +8900,14 @@ is-ip@^3.1.0: dependencies: ip-regex "^4.0.0" +is-nan@^1.2.1: + version "1.3.2" + resolved "https://registry.yarnpkg.com/is-nan/-/is-nan-1.3.2.tgz#043a54adea31748b55b6cd4e09aadafa69bd9e1d" + integrity sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w== + dependencies: + call-bind "^1.0.0" + define-properties "^1.1.3" + is-negative-zero@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.1.tgz#3de746c18dda2319241a53675908d8f766f11c24" @@ -9015,6 +9055,17 @@ is-text-path@^1.0.1: dependencies: text-extensions "^1.0.0" +is-typed-array@^1.1.3, is-typed-array@^1.1.7: + version "1.1.8" + resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.8.tgz#cbaa6585dc7db43318bc5b89523ea384a6f65e79" + integrity sha512-HqH41TNZq2fgtGT8WHVFVJhBVGuY3AnP3Q36K8JKXUxSxRgk/d+7NjmwG2vo2mYmXK8UYZKu0qH8bVP5gEisjA== + dependencies: + available-typed-arrays "^1.0.5" + call-bind "^1.0.2" + es-abstract "^1.18.5" + foreach "^2.0.5" + has-tostringtag "^1.0.0" + is-typedarray@^1.0.0, is-typedarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" @@ -10727,6 +10778,11 @@ modify-values@^1.0.0: resolved "https://registry.yarnpkg.com/modify-values/-/modify-values-1.0.1.tgz#b3939fa605546474e3e3e3c63d64bd43b4ee6022" integrity sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw== +monaco-editor@^0.29.1: + version "0.29.1" + resolved "https://registry.yarnpkg.com/monaco-editor/-/monaco-editor-0.29.1.tgz#6ee93d8a5320704d48fd7058204deed72429c020" + integrity sha512-rguaEG/zrPQSaKzQB7IfX/PpNa0qxF1FY8ZXRkN4WIl8qZdTQRSRJCtRto7IMcSgrU6H53RXI+fTcywOBC4aVw== + move-concurrently@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/move-concurrently/-/move-concurrently-1.0.1.tgz#be2c005fda32e0b29af1f05d7c4b33214c701f92" @@ -11294,6 +11350,11 @@ optionator@^0.9.1: type-check "^0.4.0" word-wrap "^1.2.3" +os-browserify@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.3.0.tgz#854373c7f5c2315914fc9bfc6bd8238fdda1ec27" + integrity sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc= + os-homedir@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" @@ -11595,6 +11656,11 @@ pascalcase@^0.1.1: resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" integrity sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ= +path-browserify@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-1.0.1.tgz#d98454a9c3753d5790860f16f68867b9e46be1fd" + integrity sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g== + path-dirname@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/path-dirname/-/path-dirname-1.0.2.tgz#cc33d24d525e099a5388c0336c6e32b9160609e0" @@ -13820,6 +13886,14 @@ string-length@^4.0.1: char-regex "^1.0.2" strip-ansi "^6.0.0" +string-replace-loader@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/string-replace-loader/-/string-replace-loader-3.0.3.tgz#bdbb4c9879ebb1d88185acd34afc4fb62334b433" + integrity sha512-8c26Dl6H9XmKNj3mFBvaUYR7ImOxQ4YRBFuUju78wXpa1cDpyDYvKmqGg8mfkxdYexQ/BBogB7PELlLnmR08nw== + dependencies: + loader-utils "^2.0.0" + schema-utils "^3.0.0" + string-width@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" @@ -14952,6 +15026,18 @@ util.promisify@~1.0.0: has-symbols "^1.0.1" object.getownpropertydescriptors "^2.1.0" +util@0.12.4, util@^0.12.0: + version "0.12.4" + resolved "https://registry.yarnpkg.com/util/-/util-0.12.4.tgz#66121a31420df8f01ca0c464be15dfa1d1850253" + integrity sha512-bxZ9qtSlGUWSOy9Qa9Xgk11kSslpuZwaxCg4sNIDj6FLucDab2JxnHwyNTCpHMtK1MjoQiWQ6DiUMZYbSrO+Sw== + dependencies: + inherits "^2.0.3" + is-arguments "^1.0.4" + is-generator-function "^1.0.7" + is-typed-array "^1.1.3" + safe-buffer "^5.1.2" + which-typed-array "^1.1.2" + utila@~0.4: version "0.4.0" resolved "https://registry.yarnpkg.com/utila/-/utila-0.4.0.tgz#8a16a05d445657a3aea5eecc5b12a4fa5379772c" @@ -15332,6 +15418,18 @@ which-module@^2.0.0: resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= +which-typed-array@^1.1.2: + version "1.1.7" + resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.7.tgz#2761799b9a22d4b8660b3c1b40abaa7739691793" + integrity sha512-vjxaB4nfDqwKI0ws7wZpxIlde1XrLX5uB0ZjpfshgmapJMD7jJWhZI+yToJTqaFByF0eNBcYxbjmCzoRP7CfEw== + dependencies: + available-typed-arrays "^1.0.5" + call-bind "^1.0.2" + es-abstract "^1.18.5" + foreach "^2.0.5" + has-tostringtag "^1.0.0" + is-typed-array "^1.1.7" + which@^1.2.14, which@^1.2.9, which@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" From 09613a1213e224de85b3697187a2f7d516cddcf6 Mon Sep 17 00:00:00 2001 From: Armano Date: Tue, 9 Nov 2021 01:56:38 +0100 Subject: [PATCH 02/39] chore(website): playground compilation fix, not fully working --- .eslintignore | 10 +- .../src/rules/no-restricted-imports.ts | 2 +- packages/website/package.json | 2 +- .../src/components/lib/use-hash-state.ts | 2 +- packages/website/src/modules/empty.js | 2 + packages/website/src/modules/eslint-rules.js | 5 - packages/website/src/modules/eslint.js | 12 -- packages/website/src/modules/eslintrc.js | 1 - .../website/src/modules/getESLintCoreRule.js | 17 +++ packages/website/src/modules/import-fresh.js | 1 + packages/website/webpack.plugin.js | 118 +++++++++--------- yarn.lock | 8 +- 12 files changed, 88 insertions(+), 92 deletions(-) create mode 100644 packages/website/src/modules/empty.js delete mode 100644 packages/website/src/modules/eslint-rules.js delete mode 100644 packages/website/src/modules/eslint.js delete mode 100644 packages/website/src/modules/eslintrc.js create mode 100644 packages/website/src/modules/getESLintCoreRule.js create mode 100644 packages/website/src/modules/import-fresh.js diff --git a/.eslintignore b/.eslintignore index 17bfdf1a12d..e6e508a5e2c 100644 --- a/.eslintignore +++ b/.eslintignore @@ -8,14 +8,8 @@ __snapshots__ packages/eslint-plugin-tslint/tests -packages/website/build -packages/website/.docusaurus -packages/website/sidebars -packages/website/webpack.plugin.js -packages/website/babel.config.js -packages/website/docusaurus.config.js -packages/website/src/modules -packages/website/src/vendor +packages/website/**/*.js +packages/website/**/*.d.ts # Files copied as part of the build packages/types/src/ast-spec.ts diff --git a/packages/eslint-plugin/src/rules/no-restricted-imports.ts b/packages/eslint-plugin/src/rules/no-restricted-imports.ts index d7437cab76b..2429795240e 100644 --- a/packages/eslint-plugin/src/rules/no-restricted-imports.ts +++ b/packages/eslint-plugin/src/rules/no-restricted-imports.ts @@ -1,5 +1,5 @@ import { TSESTree } from '@typescript-eslint/experimental-utils'; -import { +import type { ArrayOfStringOrObject, ArrayOfStringOrObjectPatterns, } from 'eslint/lib/rules/no-restricted-imports'; diff --git a/packages/website/package.json b/packages/website/package.json index f13aec8134f..b882e8ee6da 100644 --- a/packages/website/package.json +++ b/packages/website/package.json @@ -41,7 +41,7 @@ "@types/react-helmet": "^6.1.4", "@types/react-router-dom": "^5.3.2", "globby": "^11.0.4", - "monaco-editor": "^0.29.1", + "monaco-editor": "^0.30.0", "string-replace-loader": "^3.0.3", "webpack": "^5.61.0" }, diff --git a/packages/website/src/components/lib/use-hash-state.ts b/packages/website/src/components/lib/use-hash-state.ts index d3bd4755335..06a06d3eb1f 100644 --- a/packages/website/src/components/lib/use-hash-state.ts +++ b/packages/website/src/components/lib/use-hash-state.ts @@ -1,5 +1,5 @@ import { useRef, useState, useEffect } from 'react'; -import { ParserOptions } from '@typescript-eslint/parser'; +import type { ParserOptions } from '@typescript-eslint/parser'; import { debounce } from './debounce'; import type { Linter } from '@typescript-eslint/experimental-utils/dist/ts-eslint/Linter'; diff --git a/packages/website/src/modules/empty.js b/packages/website/src/modules/empty.js new file mode 100644 index 00000000000..670c5431e11 --- /dev/null +++ b/packages/website/src/modules/empty.js @@ -0,0 +1,2 @@ +export {}; +export default {}; diff --git a/packages/website/src/modules/eslint-rules.js b/packages/website/src/modules/eslint-rules.js deleted file mode 100644 index a0c1d90fbc0..00000000000 --- a/packages/website/src/modules/eslint-rules.js +++ /dev/null @@ -1,5 +0,0 @@ -import builtinRules from 'eslint/lib/rules'; - -const FileEnumerator = {}; - -export { builtinRules, FileEnumerator }; diff --git a/packages/website/src/modules/eslint.js b/packages/website/src/modules/eslint.js deleted file mode 100644 index c8f20a01838..00000000000 --- a/packages/website/src/modules/eslint.js +++ /dev/null @@ -1,12 +0,0 @@ -import { Linter } from 'eslint/lib/linter/linter'; - -//----------------------------------------------------------------------------- -// Exports -//----------------------------------------------------------------------------- - -class RuleTester {} - -const ESLint = {}; -const SourceCode = {}; - -export { Linter, ESLint, RuleTester, SourceCode }; diff --git a/packages/website/src/modules/eslintrc.js b/packages/website/src/modules/eslintrc.js deleted file mode 100644 index 19c9ce0fa24..00000000000 --- a/packages/website/src/modules/eslintrc.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('@eslint/eslintrc/dist/eslintrc-universal.cjs'); diff --git a/packages/website/src/modules/getESLintCoreRule.js b/packages/website/src/modules/getESLintCoreRule.js new file mode 100644 index 00000000000..a2e5aea0547 --- /dev/null +++ b/packages/website/src/modules/getESLintCoreRule.js @@ -0,0 +1,17 @@ +import { nullThrows } from '@typescript-eslint/eslint-plugin/src/util'; + +export function getESLintCoreRule(ruleId) { + return nullThrows( + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call + require('eslint/use-at-your-own-risk').builtinRules.get(ruleId), + `ESLint's core rule '${ruleId}' not found.`, + ); +} + +export function maybeGetESLintCoreRule(ruleId) { + try { + return getESLintCoreRule(ruleId); + } catch { + return null; + } +} diff --git a/packages/website/src/modules/import-fresh.js b/packages/website/src/modules/import-fresh.js new file mode 100644 index 00000000000..2dbcad76e1f --- /dev/null +++ b/packages/website/src/modules/import-fresh.js @@ -0,0 +1 @@ +module.exports = moduleId => require(moduleId); diff --git a/packages/website/webpack.plugin.js b/packages/website/webpack.plugin.js index cbfa2a442fa..f8a403f29a1 100644 --- a/packages/website/webpack.plugin.js +++ b/packages/website/webpack.plugin.js @@ -5,17 +5,57 @@ module.exports = function (/*context, options*/) { return { name: 'webpack-custom-plugin', configureWebpack(cfg, isServer) { + const emptyModules = [ + // TODO; verify wy this is not working, we don't want to import those files at all in build + '@typescript-eslint/experimental-utils/dist/ts-eslint/index.js', + '@typescript-eslint/typescript-estree/dist/create-program/createWatchProgram.js', + '@typescript-eslint/typescript-estree/dist/create-program/createProjectProgram.js', + '@typescript-eslint/typescript-estree/dist/create-program/createIsolatedProgram.js', + '@typescript-eslint/experimental-utils/dist/eslint-utils/RuleTester.js', + + 'eslint/lib/cli.js', + 'eslint/lib/eslint', + 'eslint/lib/eslint/index.js', + 'eslint/lib/eslint/eslint.js', + 'eslint/lib/cli-engine/cli-engine.js', + 'eslint/lib/cli-engine/load-rules.js', + 'eslint/lib/rule-tester/index.js', + 'eslint/lib/rule-tester/rule-tester.js', + 'eslint/lib/shared/ajv.js', + 'eslint/lib/shared/runtime-info.js', + 'eslint/lib/init/autoconfig.js', + 'eslint/lib/init/config-file.js', + 'eslint/lib/init/config-initializer.js', + 'eslint/lib/init/config-rule.js', + 'eslint/lib/init/npm-utils.js', + 'eslint/lib/init/source-code-utils.js', + ]; + + const modules = { + getESLintCoreRule: 'src/modules/getESLintCoreRule.js', + globby: 'src/modules/globby.js', + 'resolve-from': 'src/modules/resolve-from.js', + 'import-fresh': 'src/modules/import-fresh.js', + semver: 'src/modules/semver.js', + 'is-glob': 'src/modules/is-glob.js', + }; + return { module: { rules: [ { - test: /\.js$/, + test: /\.[tj]sx?$/, loader: 'string-replace-loader', + include: [ + path.resolve('src'), + path.resolve('../../node_modules'), + ], + enforce: 'pre', options: { multiple: [ { - search: '__importStar(require("typescript"))', - replace: 'window.ts', + search: 'semver.major(package_json_1.version) >= 8', + replace: 'true', }, ], }, @@ -23,37 +63,12 @@ module.exports = function (/*context, options*/) { ], }, resolve: { - exportsFields: [], - alias: { - '@typescript-eslint/experimental-utils/dist/ts-eslint/index.js': false, - '@typescript-eslint/typescript-estree/dist/create-program/createWatchProgram.js': false, - '@typescript-eslint/typescript-estree/dist/create-program/createProjectProgram.js': false, - '@typescript-eslint/typescript-estree/dist/create-program/createIsolatedProgram.js': false, - '@typescript-eslint/experimental-utils/dist/eslint-utils/RuleTester.js': false, - - 'eslint/lib/cli-engine/cli-engine': false, - 'eslint/lib/cli.js': false, - 'eslint/lib/eslint': false, - 'eslint/lib/eslint/index.js': false, - 'eslint/lib/eslint/eslint.js': false, - 'eslint/lib/cli-engine/cli-engine.js': false, - 'eslint/lib/cli-engine/load-rules.js': false, - 'eslint/lib/rule-tester/index.js': false, - 'eslint/lib/rule-tester/rule-tester.js': false, - 'eslint/lib/shared/ajv.js': false, - 'eslint/lib/shared/runtime-info.js': false, - 'eslint/lib/init/autoconfig.js': false, - 'eslint/lib/init/config-file.js': false, - 'eslint/lib/init/config-initializer.js': false, - 'eslint/lib/init/config-rule.js': false, - 'eslint/lib/init/npm-utils.js': false, - 'eslint/lib/init/source-code-utils.js': false, - }, fallback: { assert: require.resolve('assert'), fs: false, os: require.resolve('os-browserify/browser'), tty: false, + module: false, path: require.resolve('path-browserify'), util: require.resolve('util'), crypto: false, @@ -78,37 +93,22 @@ module.exports = function (/*context, options*/) { ), }, }), - new webpack.NormalModuleReplacementPlugin( - /globby/, - path.resolve(__dirname, 'src/modules/globby.js'), - ), - new webpack.NormalModuleReplacementPlugin( - /resolve-from/, - path.resolve(__dirname, 'src/modules/resolve-from.js'), - ), - new webpack.NormalModuleReplacementPlugin( - /eslint$/, - path.resolve(__dirname, 'src/modules/eslint.js'), - ), - new webpack.NormalModuleReplacementPlugin( - /eslint\/use-at-your-own-risk$/, - path.resolve(__dirname, 'src/modules/eslint-rules.js'), - ), - new webpack.NormalModuleReplacementPlugin( - /@eslint\/eslintrc\/universal$/, - path.resolve(__dirname, 'src/modules/eslintrc.js'), - ), - new webpack.NormalModuleReplacementPlugin( - /esquery/, - require.resolve('esquery/dist/esquery.js'), - ), - new webpack.NormalModuleReplacementPlugin( - /semver/, - path.resolve(__dirname, 'src/modules/semver.js'), + ...emptyModules.map( + item => + new webpack.NormalModuleReplacementPlugin( + new RegExp(item), + path.resolve( + __dirname, + path.resolve(__dirname, 'src/modules/empty.js'), + ), + ), ), - new webpack.NormalModuleReplacementPlugin( - /is-glob/, - path.resolve(__dirname, 'src/modules/is-glob.js'), + ...Object.entries(modules).map( + item => + new webpack.NormalModuleReplacementPlugin( + new RegExp('(.*)' + item[0]), + path.resolve(__dirname, path.resolve(__dirname, item[1])), + ), ), ], }; diff --git a/yarn.lock b/yarn.lock index 27ed2c0ca17..341c85c49c7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10778,10 +10778,10 @@ modify-values@^1.0.0: resolved "https://registry.yarnpkg.com/modify-values/-/modify-values-1.0.1.tgz#b3939fa605546474e3e3e3c63d64bd43b4ee6022" integrity sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw== -monaco-editor@^0.29.1: - version "0.29.1" - resolved "https://registry.yarnpkg.com/monaco-editor/-/monaco-editor-0.29.1.tgz#6ee93d8a5320704d48fd7058204deed72429c020" - integrity sha512-rguaEG/zrPQSaKzQB7IfX/PpNa0qxF1FY8ZXRkN4WIl8qZdTQRSRJCtRto7IMcSgrU6H53RXI+fTcywOBC4aVw== +monaco-editor@^0.30.0: + version "0.30.0" + resolved "https://registry.yarnpkg.com/monaco-editor/-/monaco-editor-0.30.0.tgz#1c7f9ba1d18c21868ce3a5413ef56351f9df7933" + integrity sha512-/k++/ofRmwnwWTpOWYOMGVcqBrqrlt3MP0Mt/cRTQojW7A9fnekcvPQ2iIFA0YSZdPWPN9yYXrYq0xqiUuxT/A== move-concurrently@^1.0.1: version "1.0.1" From a121f035fa47d70eacb0c751c06b1b955fe8d605 Mon Sep 17 00:00:00 2001 From: Armano Date: Thu, 11 Nov 2021 09:10:37 +0100 Subject: [PATCH 03/39] docs: allow playground to be executed --- .../src/referencer/PatternVisitor.ts | 2 +- packages/website-eslint/package.json | 27 ++ packages/website-eslint/project.json | 5 + .../website-eslint/rollup-plugin/replace.js | 94 +++++++ packages/website-eslint/rollup.config.js | 83 ++++++ .../website-eslint/src/linter/CompilerHost.js | 48 ++++ packages/website-eslint/src/linter/config.js | 24 ++ .../src/linter/create-ast-program.js} | 22 +- packages/website-eslint/src/linter/linter.js | 45 ++++ packages/website-eslint/src/linter/parser.js | 46 ++++ packages/website-eslint/src/mock/assert.js | 79 ++++++ packages/website-eslint/src/mock/empty.js | 1 + packages/website-eslint/src/mock/path.js | 242 ++++++++++++++++++ packages/website-eslint/src/mock/util.js | 7 + packages/website/package.json | 15 +- packages/website/project.json | 2 +- packages/website/src/components/editor.tsx | 24 +- .../src/components/lib/load-sandbox.ts | 2 +- .../website/src/components/lib/selection.ts | 2 +- .../src/components/linter/CompilerHost.ts | 44 ---- .../website/src/components/linter/linter.ts | 73 ------ .../website/src/components/linter/parser.ts | 65 ----- packages/website/webpack.plugin.js | 109 +------- workspace.json | 1 + yarn.lock | 186 +++++++------- 25 files changed, 836 insertions(+), 412 deletions(-) create mode 100644 packages/website-eslint/package.json create mode 100644 packages/website-eslint/project.json create mode 100644 packages/website-eslint/rollup-plugin/replace.js create mode 100644 packages/website-eslint/rollup.config.js create mode 100644 packages/website-eslint/src/linter/CompilerHost.js create mode 100644 packages/website-eslint/src/linter/config.js rename packages/{website/src/components/linter/create-ast-program.ts => website-eslint/src/linter/create-ast-program.js} (70%) create mode 100644 packages/website-eslint/src/linter/linter.js create mode 100644 packages/website-eslint/src/linter/parser.js create mode 100644 packages/website-eslint/src/mock/assert.js create mode 100644 packages/website-eslint/src/mock/empty.js create mode 100644 packages/website-eslint/src/mock/path.js create mode 100644 packages/website-eslint/src/mock/util.js delete mode 100644 packages/website/src/components/linter/CompilerHost.ts delete mode 100644 packages/website/src/components/linter/linter.ts delete mode 100644 packages/website/src/components/linter/parser.ts diff --git a/packages/scope-manager/src/referencer/PatternVisitor.ts b/packages/scope-manager/src/referencer/PatternVisitor.ts index eaca842cbd5..eb34103457d 100644 --- a/packages/scope-manager/src/referencer/PatternVisitor.ts +++ b/packages/scope-manager/src/referencer/PatternVisitor.ts @@ -1,5 +1,5 @@ import { TSESTree, AST_NODE_TYPES } from '@typescript-eslint/types'; -import { VisitorBase, VisitorOptions } from './Visitor'; +import { VisitorBase, VisitorOptions } from './VisitorBase'; type PatternVisitorCallback = ( pattern: TSESTree.Identifier, diff --git a/packages/website-eslint/package.json b/packages/website-eslint/package.json new file mode 100644 index 00000000000..77bab88f9d3 --- /dev/null +++ b/packages/website-eslint/package.json @@ -0,0 +1,27 @@ +{ + "name": "@typescript-eslint/website-eslint", + "version": "5.3.1", + "private": true, + "description": "ESLint which works in browsers.", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "main": "dist/index.js", + "files": [ + "dist" + ], + "scripts": { + "build": "rollup --config=rollup.config.js" + }, + "dependencies": { + "eslint": "*" + }, + "devDependencies": { + "eslint": "*", + "rollup": "^2.59.0", + "@rollup/plugin-commonjs": "^21.0.1", + "@rollup/plugin-json": "^4.1.0", + "@rollup/plugin-node-resolve": "^13.0.6", + "@rollup/pluginutils": "^3.1.0" + } +} diff --git a/packages/website-eslint/project.json b/packages/website-eslint/project.json new file mode 100644 index 00000000000..ac6ff89fcb8 --- /dev/null +++ b/packages/website-eslint/project.json @@ -0,0 +1,5 @@ +{ + "root": "packages/website-eslint", + "type": "library", + "implicitDependencies": [] +} diff --git a/packages/website-eslint/rollup-plugin/replace.js b/packages/website-eslint/rollup-plugin/replace.js new file mode 100644 index 00000000000..b8f3143c905 --- /dev/null +++ b/packages/website-eslint/rollup-plugin/replace.js @@ -0,0 +1,94 @@ +const path = require('path'); +const Module = require('module'); +const rollupPluginUtils = require('@rollup/pluginutils'); +const MagicString = require('magic-string'); + +function toAbsolute(id) { + return id.startsWith('./') ? path.resolve(id) : require.resolve(id); +} + +function log(opts, message, type = 'info') { + if (opts.verbose) { + console.log('rollup-plugin-replace > [' + type + ']', message); + } +} + +function createMatcher(it) { + if (typeof it === 'function') { + return it; + } else { + return rollupPluginUtils.createFilter(it); + } +} + +module.exports = (options = {}) => { + const aliasesCache = new Map(); + const aliases = (options.alias || []).map(item => { + return { + match: item.match, + matcher: createMatcher(item.match), + target: item.target, + absoluteTarget: toAbsolute(item.target), + }; + }); + const replaces = (options.replace || []).map(item => { + return { + match: item.match, + test: item.test, + replace: + typeof item.replace === 'string' ? () => item.replace : item.replace, + + matcher: createMatcher(item.match), + }; + }); + + return { + name: 'rollup-plugin-replace', + resolveId(id, importerPath) { + const importeePath = + id.startsWith('./') || id.startsWith('../') + ? Module.createRequire(importerPath).resolve(id) + : id; + + let result = aliasesCache.get(importeePath); + if (result) { + return result; + } + + result = aliases.find(item => item.matcher(importeePath)); + if (result) { + aliasesCache.set(importeePath, result.absoluteTarget); + log(options, `${importeePath} as ${result.target}`, 'resolve'); + return result.absoluteTarget; + } + + return null; + }, + transform(code, id) { + let hasReplacements = false; + let magicString = new MagicString(code); + + replaces.forEach(item => { + if (item.matcher && !item.matcher(id)) { + return; + } + + let match = item.test.exec(code); + let start, end; + while (match) { + hasReplacements = true; + start = match.index; + end = start + match[0].length; + magicString.overwrite(start, end, item.replace(match)); + match = item.test.global ? item.test.exec(code) : null; + } + }); + + if (!hasReplacements) { + return; + } + log(options, id, 'replace'); + return { code: magicString.toString() }; + }, + }; +}; diff --git a/packages/website-eslint/rollup.config.js b/packages/website-eslint/rollup.config.js new file mode 100644 index 00000000000..6f782046b64 --- /dev/null +++ b/packages/website-eslint/rollup.config.js @@ -0,0 +1,83 @@ +import commonjs from '@rollup/plugin-commonjs'; +import json from '@rollup/plugin-json'; +import resolve from '@rollup/plugin-node-resolve'; +const replace = require('./rollup-plugin/replace'); + +module.exports = { + input: 'src/linter/linter.js', + output: { + format: 'module', + interop: 'auto', + freeze: false, + file: 'dist/index.js', + }, + external: ['typescript'], + plugins: [ + replace({ + // verbose: true, + alias: [ + { + match: [ + /eslint\/lib\/(rule-tester|eslint|cli-engine|init)\//u, + /eslint\/lib\/cli\.js$/, + /experimental-utils\/dist\/eslint-utils\/RuleTester\.js$/, + /experimental-utils\/dist\/ts-eslint\/CLIEngine\.js$/, + /experimental-utils\/dist\/ts-eslint\/RuleTester\.js$/, + // 'typescript-estree/dist/create-program/createWatchProgram.js', + // 'typescript-estree/dist/create-program/createProjectProgram.js', + // 'typescript-estree/dist/create-program/createIsolatedProgram.js', + // 'eslint/lib/shared/ajv.js', + // 'eslint/lib/shared/runtime-info.js', + ], + target: './src/mock/empty.js', + }, + { + match: /^assert$/u, + target: './src/mock/assert.js', + }, + { + match: /^path$/u, + target: './src/mock/path.js', + }, + { + match: /^util$/u, + target: './src/mock/util.js', + }, + ], + replace: [ + { + match: /eslint\/lib\/linter\/rules\.js$/u, + test: /require\(this\._rules\[ruleId\]\)/u, + replace: 'null', + }, + { + test: /esquery\.parse\(/u, + replace: 'esquery.default.parse(', + }, + { + test: /esquery\.matches\(/u, + replace: 'esquery.default.matches(', + }, + { + test: /process\.env\.NODE_DEBUG/u, + replace: 'false', + }, + { + test: /process\.env\.TIMING/u, + replace: 'false', + }, + { + test: /process\.env\.IGNORE_TEST_WIN32/u, + replace: 'true', + }, + ], + }), + // nodePolyfills(), + resolve({ + browser: true, + preferBuiltins: false, + }), + commonjs(), + json({ preferConst: true }), + ], +}; diff --git a/packages/website-eslint/src/linter/CompilerHost.js b/packages/website-eslint/src/linter/CompilerHost.js new file mode 100644 index 00000000000..2b7fdec25d0 --- /dev/null +++ b/packages/website-eslint/src/linter/CompilerHost.js @@ -0,0 +1,48 @@ +import { getDefaultLibFileName } from 'typescript'; + +export class CompilerHost { + constructor(files, sourceFiles) { + this.files = files; + this.sourceFiles = sourceFiles; + } + + fileExists(name) { + return !!this.files[name]; + } + + getCanonicalFileName(name) { + return name; + } + + getCurrentDirectory() { + return '/'; + } + + getDirectories() { + return []; + } + + getDefaultLibFileName(options) { + return '/' + getDefaultLibFileName(options); + } + + getNewLine() { + return '\n'; + } + + useCaseSensitiveFileNames() { + return true; + } + + writeFile() { + return null; + } + + readFile(name) { + return this.files[name]; + } + + getSourceFile(name) { + return this.sourceFiles[name]; + } +} diff --git a/packages/website-eslint/src/linter/config.js b/packages/website-eslint/src/linter/config.js new file mode 100644 index 00000000000..06e9ec28b3e --- /dev/null +++ b/packages/website-eslint/src/linter/config.js @@ -0,0 +1,24 @@ +export const extra = { + code: '', + comment: true, + comments: [], + createDefaultProgram: false, + debugLevel: new Set(), + errorOnTypeScriptSyntacticAndSemanticIssues: false, + errorOnUnknownASTType: false, + extraFileExtensions: [], + filePath: '', + jsx: false, + loc: true, + log: console.log, + preserveNodeMaps: true, + projects: [], + range: true, + strict: false, + tokens: [], + tsconfigRootDir: '/', + EXPERIMENTAL_useSourceOfProjectReferenceRedirect: false, + singleRun: false, + programs: null, + moduleResolver: '', +}; diff --git a/packages/website/src/components/linter/create-ast-program.ts b/packages/website-eslint/src/linter/create-ast-program.js similarity index 70% rename from packages/website/src/components/linter/create-ast-program.ts rename to packages/website-eslint/src/linter/create-ast-program.js index 1d9e8d921fa..ea0444fd249 100644 --- a/packages/website/src/components/linter/create-ast-program.ts +++ b/packages/website-eslint/src/linter/create-ast-program.js @@ -1,5 +1,3 @@ -import type { SourceFile, Program } from 'typescript'; -import type { ParserOptions } from '@typescript-eslint/types'; import { createProgram, createSourceFile, @@ -10,18 +8,12 @@ import { } from 'typescript'; import { CompilerHost } from './CompilerHost'; -interface ASTAndProgram { - ast?: SourceFile; - program: Program; -} - -export function createASTProgram( - code: string, - parserOptions: ParserOptions, -): ASTAndProgram { +export function createASTProgram(code, parserOptions) { const isJsx = !!parserOptions?.ecmaFeatures?.jsx; const fileName = isJsx ? '/demo.tsx' : '/demo.ts'; - const files = { [fileName]: code }; + const files = { + [fileName]: code, + }; const sourceFiles = { [fileName]: createSourceFile( fileName, @@ -31,7 +23,6 @@ export function createASTProgram( isJsx ? ScriptKind.TSX : ScriptKind.TS, ), }; - const compilerHost = new CompilerHost(files, sourceFiles); const compilerOptions = { noResolve: true, @@ -46,5 +37,8 @@ export function createASTProgram( compilerHost, ); const ast = program.getSourceFile(fileName); - return { ast, program }; + return { + ast, + program, + }; } diff --git a/packages/website-eslint/src/linter/linter.js b/packages/website-eslint/src/linter/linter.js new file mode 100644 index 00000000000..110226fe36d --- /dev/null +++ b/packages/website-eslint/src/linter/linter.js @@ -0,0 +1,45 @@ +import { parseForESLint } from './parser'; +import { Linter } from 'eslint'; +import rules from '@typescript-eslint/eslint-plugin/dist/rules'; + +const PARSER_NAME = '@typescript-eslint/parser'; + +export function loadLinter() { + const linter = new Linter(); + let storedAST; + + linter.defineParser(PARSER_NAME, { + parseForESLint(code, options) { + const toParse = parseForESLint(code, options); + storedAST = toParse.ast; + return toParse; + }, // parse(code: string, options: ParserOptions): ParseForESLintResult['ast'] { + // const toParse = parseForESLint(code, options); + // storedAST = toParse.ast; + // return toParse.ast; + // }, + }); + + for (const name of Object.keys(rules)) { + linter.defineRule(`@typescript-eslint/${name}`, rules[name]); + } + + const ruleNames = Object.keys(rules).map( + name => `@typescript-eslint/${name}`, + ); + return { + ruleNames, + + getAst() { + return storedAST; + }, + + lint(code, parserOptions, rules) { + return linter.verify(code, { + parser: PARSER_NAME, + parserOptions, + rules, + }); + }, + }; +} diff --git a/packages/website-eslint/src/linter/parser.js b/packages/website-eslint/src/linter/parser.js new file mode 100644 index 00000000000..1ba9d6a9987 --- /dev/null +++ b/packages/website-eslint/src/linter/parser.js @@ -0,0 +1,46 @@ +import { analyze } from '@typescript-eslint/scope-manager/dist/analyze'; +import { visitorKeys } from '@typescript-eslint/visitor-keys/dist/visitor-keys'; +import { astConverter } from '@typescript-eslint/typescript-estree/dist/ast-converter'; +import { createASTProgram } from './create-ast-program.js'; +import { extra } from './config.js'; + +function parseAndGenerateServices(code, options) { + const { ast, program } = createASTProgram(code, options); + const { estree, astMaps } = astConverter( + ast, + { ...extra, code, jsx: options.jsx ?? false }, + true, + ); + return { + ast: estree, + services: { + hasFullTypeInformation: true, + program, + esTreeNodeToTSNodeMap: astMaps.esTreeNodeToTSNodeMap, + tsNodeToESTreeNodeMap: astMaps.tsNodeToESTreeNodeMap, + }, + }; +} + +export function parseForESLint(code, parserOptions) { + const { ast, services } = parseAndGenerateServices(code, { + ...parserOptions, + jsx: parserOptions.ecmaFeatures?.jsx ?? false, + useJSXTextNode: true, + projectFolderIgnoreList: [], + }); + const scopeManager = analyze(ast, { + ecmaVersion: + parserOptions.ecmaVersion === 'latest' ? 1e8 : parserOptions.ecmaVersion, + globalReturn: parserOptions.ecmaFeatures?.globalReturn ?? false, + sourceType: parserOptions.sourceType ?? 'script', + }); // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + + return { + ast, + services, + scopeManager, + visitorKeys, + }; +} diff --git a/packages/website-eslint/src/mock/assert.js b/packages/website-eslint/src/mock/assert.js new file mode 100644 index 00000000000..4ba85f2e6aa --- /dev/null +++ b/packages/website-eslint/src/mock/assert.js @@ -0,0 +1,79 @@ +class AssertionError extends Error { + constructor(options) { + super(options); + + this.actual = options.actual; + this.expected = options.expected; + this.operator = options.operator; + if (options.message) { + this.message = options.message; + this.generatedMessage = false; + } else { + this.message = ''; + this.generatedMessage = true; + } + var stackStartFunction = options.stackStartFunction || fail; + if (Error.captureStackTrace) { + Error.captureStackTrace(this, stackStartFunction); + } else { + // non v8 browsers so we can have a stacktrace + var err = new Error(); + if (err.stack) { + var out = err.stack; + + // try to strip useless frames + var fn_name = getName(stackStartFunction); + var idx = out.indexOf('\n' + fn_name); + if (idx >= 0) { + // once we have located the function frame + // we need to strip out everything before it (and its line) + var next_line = out.indexOf('\n', idx + 1); + out = out.substring(next_line + 1); + } + + this.stack = out; + } + } + } +} + +function fail(actual, expected, message, operator, stackStartFunction) { + throw new AssertionError({ + message: message, + actual: actual, + expected: expected, + operator: operator, + stackStartFunction: stackStartFunction, + }); +} + +function assert(value, message) { + if (!value) { + fail(value, true, message, '==', assert); + } +} +assert.equal = function equal(actual, expected, message) { + if (actual != expected) { + fail(actual, expected, message, '==', equal); + } +}; +assert.strictEqual = function strictEqual(actual, expected, message) { + if (actual !== expected) { + fail(actual, expected, message, '===', strictEqual); + } +}; +assert.notStrictEqual = function notStrictEqual(actual, expected, message) { + if (actual === expected) { + fail(actual, expected, message, '!==', notStrictEqual); + } +}; +assert.notEqual = function notEqual(actual, expected, message) { + if (actual == expected) { + fail(actual, expected, message, '!=', notEqual); + } +}; +assert.assert = assert.ok = assert; +assert.fail = fail; +assert.AssertionError = AssertionError; + +module.exports = assert; diff --git a/packages/website-eslint/src/mock/empty.js b/packages/website-eslint/src/mock/empty.js new file mode 100644 index 00000000000..ff8b4c56321 --- /dev/null +++ b/packages/website-eslint/src/mock/empty.js @@ -0,0 +1 @@ +export default {}; diff --git a/packages/website-eslint/src/mock/path.js b/packages/website-eslint/src/mock/path.js new file mode 100644 index 00000000000..4ca0df7214a --- /dev/null +++ b/packages/website-eslint/src/mock/path.js @@ -0,0 +1,242 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +// resolves . and .. elements in a path array with directory names there +// must be no slashes, empty elements, or device names (c:\) in the array +// (so also no leading and trailing slashes - it does not distinguish +// relative and absolute paths) +function normalizeArray(parts, allowAboveRoot) { + // if the path tries to go above the root, `up` ends up > 0 + var up = 0; + for (var i = parts.length - 1; i >= 0; i--) { + var last = parts[i]; + if (last === '.') { + parts.splice(i, 1); + } else if (last === '..') { + parts.splice(i, 1); + up++; + } else if (up) { + parts.splice(i, 1); + up--; + } + } + + // if the path is allowed to go above the root, restore leading ..s + if (allowAboveRoot) { + for (; up--; up) { + parts.unshift('..'); + } + } + + return parts; +} + +// Split a filename into [root, dir, basename, ext], unix version +// 'root' is just a slash, or nothing. +var splitPathRe = + /^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/; +var splitPath = function (filename) { + return splitPathRe.exec(filename).slice(1); +}; + +// path.resolve([from ...], to) +// posix version +export function resolve() { + var resolvedPath = '', + resolvedAbsolute = false; + + for (var i = arguments.length - 1; i >= -1 && !resolvedAbsolute; i--) { + var path = i >= 0 ? arguments[i] : '/'; + + // Skip empty and invalid entries + if (typeof path !== 'string') { + throw new TypeError('Arguments to path.resolve must be strings'); + } else if (!path) { + continue; + } + + resolvedPath = path + '/' + resolvedPath; + resolvedAbsolute = path.charAt(0) === '/'; + } + + // At this point the path should be resolved to a full absolute path, but + // handle relative paths to be safe (might happen when process.cwd() fails) + + // Normalize the path + resolvedPath = normalizeArray( + filter(resolvedPath.split('/'), function (p) { + return !!p; + }), + !resolvedAbsolute, + ).join('/'); + + return (resolvedAbsolute ? '/' : '') + resolvedPath || '.'; +} + +// path.normalize(path) +// posix version +export function normalize(path) { + var isPathAbsolute = isAbsolute(path), + trailingSlash = substr(path, -1) === '/'; + + // Normalize the path + path = normalizeArray( + filter(path.split('/'), function (p) { + return !!p; + }), + !isPathAbsolute, + ).join('/'); + + if (!path && !isPathAbsolute) { + path = '.'; + } + if (path && trailingSlash) { + path += '/'; + } + + return (isPathAbsolute ? '/' : '') + path; +} + +// posix version +export function isAbsolute(path) { + return path.charAt(0) === '/'; +} + +// posix version +export function join() { + var paths = Array.prototype.slice.call(arguments, 0); + return normalize( + filter(paths, function (p, index) { + if (typeof p !== 'string') { + throw new TypeError('Arguments to path.join must be strings'); + } + return p; + }).join('/'), + ); +} + +// path.relative(from, to) +// posix version +export function relative(from, to) { + from = resolve(from).substr(1); + to = resolve(to).substr(1); + + function trim(arr) { + var start = 0; + for (; start < arr.length; start++) { + if (arr[start] !== '') break; + } + + var end = arr.length - 1; + for (; end >= 0; end--) { + if (arr[end] !== '') break; + } + + if (start > end) return []; + return arr.slice(start, end - start + 1); + } + + var fromParts = trim(from.split('/')); + var toParts = trim(to.split('/')); + + var length = Math.min(fromParts.length, toParts.length); + var samePartsLength = length; + for (var i = 0; i < length; i++) { + if (fromParts[i] !== toParts[i]) { + samePartsLength = i; + break; + } + } + + var outputParts = []; + for (var i = samePartsLength; i < fromParts.length; i++) { + outputParts.push('..'); + } + + outputParts = outputParts.concat(toParts.slice(samePartsLength)); + + return outputParts.join('/'); +} + +export var sep = '/'; +export var delimiter = ':'; + +export function dirname(path) { + var result = splitPath(path), + root = result[0], + dir = result[1]; + + if (!root && !dir) { + // No dirname whatsoever + return '.'; + } + + if (dir) { + // It has a dirname, strip trailing slash + dir = dir.substr(0, dir.length - 1); + } + + return root + dir; +} + +export function basename(path, ext) { + var f = splitPath(path)[2]; + // TODO: make this comparison case-insensitive on windows? + if (ext && f.substr(-1 * ext.length) === ext) { + f = f.substr(0, f.length - ext.length); + } + return f; +} + +export function extname(path) { + return splitPath(path)[3]; +} +export default { + extname: extname, + basename: basename, + dirname: dirname, + sep: sep, + delimiter: delimiter, + relative: relative, + join: join, + isAbsolute: isAbsolute, + normalize: normalize, + resolve: resolve, +}; +function filter(xs, f) { + if (xs.filter) return xs.filter(f); + var res = []; + for (var i = 0; i < xs.length; i++) { + if (f(xs[i], i, xs)) res.push(xs[i]); + } + return res; +} + +// String.prototype.substr - negative index don't work in IE8 +var substr = + 'ab'.substr(-1) === 'b' + ? function (str, start, len) { + return str.substr(start, len); + } + : function (str, start, len) { + if (start < 0) start = str.length + start; + return str.substr(start, len); + }; diff --git a/packages/website-eslint/src/mock/util.js b/packages/website-eslint/src/mock/util.js new file mode 100644 index 00000000000..3e5cd5e0e71 --- /dev/null +++ b/packages/website-eslint/src/mock/util.js @@ -0,0 +1,7 @@ +const util = {}; + +util.inspect = function (value) { + return value; +}; + +export default util; diff --git a/packages/website/package.json b/packages/website/package.json index b882e8ee6da..0bbf52ffc33 100644 --- a/packages/website/package.json +++ b/packages/website/package.json @@ -19,21 +19,13 @@ "@docusaurus/remark-plugin-npm2yarn": "^2.0.0-beta.9", "@docusaurus/theme-classic": "^2.0.0-beta.9", "@mdx-js/react": "1.6.22", - "@typescript-eslint/eslint-plugin": "5.3.1", - "@typescript-eslint/parser": "5.3.1", - "@typescript-eslint/scope-manager": "5.3.1", - "@typescript-eslint/types": "5.3.1", - "@typescript-eslint/typescript-estree": "5.3.1", - "@typescript-eslint/visitor-keys": "5.3.1", + "@typescript-eslint/experimental-utils": "5.3.1", + "@typescript-eslint/website-eslint": "5.3.1", "clsx": "^1.1.1", "eslint": "*", "react": "^17.0.2", "react-dom": "^17.0.2", - "typescript": "*", - "path-browserify": "1.0.1", - "os-browserify": "0.3.0", - "util": "0.12.4", - "assert": "2.0.0" + "typescript": "*" }, "devDependencies": { "@docusaurus/module-type-aliases": "^2.0.0-beta.9", @@ -42,7 +34,6 @@ "@types/react-router-dom": "^5.3.2", "globby": "^11.0.4", "monaco-editor": "^0.30.0", - "string-replace-loader": "^3.0.3", "webpack": "^5.61.0" }, "browserslist": { diff --git a/packages/website/project.json b/packages/website/project.json index 9bfd9e8a8aa..ac36b5c6378 100644 --- a/packages/website/project.json +++ b/packages/website/project.json @@ -1,5 +1,5 @@ { "root": "packages/website", - "type": "library", + "type": "application", "implicitDependencies": [] } diff --git a/packages/website/src/components/editor.tsx b/packages/website/src/components/editor.tsx index 9ac58722420..b53f1a5222c 100644 --- a/packages/website/src/components/editor.tsx +++ b/packages/website/src/components/editor.tsx @@ -3,16 +3,28 @@ import type { createTypeScriptSandbox, PlaygroundConfig, } from '../vendor/sandbox'; -import type { TSESTree } from '@typescript-eslint/types'; import type Monaco from 'monaco-editor'; +import type { TSESTree, ParserOptions } from '@typescript-eslint/types'; +import type { TSESLint } from '@typescript-eslint/experimental-utils'; + import { sandboxSingleton } from './lib/load-sandbox'; -import { loadLinter, WebLinter } from './linter/linter'; import { createProvideCodeActions } from './lib/action'; import { createURI, messageToMarker } from './lib/utils'; import { debounce } from './lib/debounce'; import { HashStateOptions } from './lib/use-hash-state'; -import type { Linter } from '@typescript-eslint/experimental-utils/dist/ts-eslint/Linter'; + +export interface WebLinter { + ruleNames: string[]; + + getAst(): TSESLint.Linter.ESLintParseResult['ast']; + + lint( + code: string, + parserOptions: ParserOptions, + rules?: TSESLint.Linter.RulesRecord, + ): TSESLint.Linter.LintMessage[]; +} interface EditorProps extends HashStateOptions { darkTheme: boolean; @@ -40,7 +52,7 @@ class Editor extends React.Component { private _codeIsUpdating: boolean; private _decorations: string[]; - private readonly fixes: Map; + private readonly fixes: Map; constructor(props: EditorProps) { super(props); @@ -153,7 +165,9 @@ class Editor extends React.Component { ); this.updateTheme(); this.updateCode(); - this.linter = await loadLinter(); + this.linter = ( + await import('@typescript-eslint/website-eslint') + ).loadLinter(); if (this.props.onLoadRule) { this.props.onLoadRule(this.linter.ruleNames); } diff --git a/packages/website/src/components/lib/load-sandbox.ts b/packages/website/src/components/lib/load-sandbox.ts index 4566c790bae..fafff16dc0a 100644 --- a/packages/website/src/components/lib/load-sandbox.ts +++ b/packages/website/src/components/lib/load-sandbox.ts @@ -47,7 +47,7 @@ function loadSandbox(tsVersion: string): Promise { // @ts-ignore const isOK = main && window.ts && sandboxFactory; // @ts-ignore - window.ts.__esModule = true; + // window.ts.__esModule = true; // window.ts.SyntaxKind; if (isOK) { resolve({ diff --git a/packages/website/src/components/lib/selection.ts b/packages/website/src/components/lib/selection.ts index 70429fbea48..33ac1de77f0 100644 --- a/packages/website/src/components/lib/selection.ts +++ b/packages/website/src/components/lib/selection.ts @@ -1,5 +1,5 @@ /* eslint-disable @typescript-eslint/ban-ts-comment */ -import { TSESTree } from '@typescript-eslint/types'; +import type { TSESTree } from '@typescript-eslint/types'; export const propsToFilter = ['parent', 'comments', 'tokens', 'loc']; diff --git a/packages/website/src/components/linter/CompilerHost.ts b/packages/website/src/components/linter/CompilerHost.ts deleted file mode 100644 index cd5db22548d..00000000000 --- a/packages/website/src/components/linter/CompilerHost.ts +++ /dev/null @@ -1,44 +0,0 @@ -import type { - CompilerHost as ICompilerHost, - CompilerOptions, - SourceFile, -} from 'typescript'; -import { getDefaultLibFileName } from 'typescript'; - -export class CompilerHost implements ICompilerHost { - constructor( - private files: Record, - private sourceFiles: Record, - ) {} - - fileExists(name: string): boolean { - return !!this.files[name]; - } - getCanonicalFileName(name: string): string { - return name; - } - getCurrentDirectory(): string { - return '/'; - } - getDirectories(): string[] { - return []; - } - getDefaultLibFileName(options: CompilerOptions): string { - return '/' + getDefaultLibFileName(options); - } - getNewLine(): string { - return '\n'; - } - useCaseSensitiveFileNames(): boolean { - return true; - } - writeFile(): null { - return null; - } - readFile(name: string): string | undefined { - return this.files[name]; - } - getSourceFile(name: string): SourceFile | undefined { - return this.sourceFiles[name]; - } -} diff --git a/packages/website/src/components/linter/linter.ts b/packages/website/src/components/linter/linter.ts deleted file mode 100644 index 0f5e045ecaf..00000000000 --- a/packages/website/src/components/linter/linter.ts +++ /dev/null @@ -1,73 +0,0 @@ -import type { ParserOptions } from '@typescript-eslint/types'; -import type { TSESLint } from '@typescript-eslint/experimental-utils'; -import type { - RuleCreateFunction, - RuleModule, -} from '@typescript-eslint/experimental-utils/dist/ts-eslint'; -import type { ParseForESLintResult } from './parser'; - -const PARSER_NAME = '@typescript-eslint/parser'; - -export interface WebLinter { - ruleNames: string[]; - - getAst(): ParseForESLintResult['ast']; - - lint( - code: string, - parserOptions: ParserOptions, - rules?: TSESLint.Linter.RulesRecord, - ): TSESLint.Linter.LintMessage[]; -} - -interface distRules { - default: Record>; -} - -export async function loadLinter(): Promise { - const rules = ( - (await import('@typescript-eslint/eslint-plugin/dist/rules')) as distRules - ).default; - const { parseForESLint } = await import(`./parser`); - const { Linter } = await import('eslint'); - const linter = new Linter() as unknown as TSESLint.Linter; - let storedAST: ParseForESLintResult['ast']; - - linter.defineParser(PARSER_NAME, { - parseForESLint(code: string, options: ParserOptions): ParseForESLintResult { - const toParse = parseForESLint(code, options); - storedAST = toParse.ast; - return toParse; - }, - // parse(code: string, options: ParserOptions): ParseForESLintResult['ast'] { - // const toParse = parseForESLint(code, options); - // storedAST = toParse.ast; - // return toParse.ast; - // }, - }); - for (const name of Object.keys(rules)) { - linter.defineRule(`@typescript-eslint/${name}`, rules[name]); - } - - const ruleNames = Object.keys(rules).map( - name => `@typescript-eslint/${name}`, - ); - - return { - ruleNames, - getAst(): TSESLint.Linter.ESLintParseResult['ast'] { - return storedAST; - }, - lint( - code: string, - parserOptions: ParserOptions, - rules?: TSESLint.Linter.RulesRecord, - ): TSESLint.Linter.LintMessage[] { - return linter.verify(code, { - parser: PARSER_NAME, - parserOptions, - rules, - }); - }, - }; -} diff --git a/packages/website/src/components/linter/parser.ts b/packages/website/src/components/linter/parser.ts deleted file mode 100644 index 8ee5c13c036..00000000000 --- a/packages/website/src/components/linter/parser.ts +++ /dev/null @@ -1,65 +0,0 @@ -import type { AST } from '@typescript-eslint/typescript-estree/dist/parser'; -import { analyze } from '@typescript-eslint/scope-manager/dist/analyze'; -import { visitorKeys } from '@typescript-eslint/visitor-keys/dist/visitor-keys'; -import { astConverter } from '@typescript-eslint/typescript-estree/dist/ast-converter'; -import { createASTProgram } from './create-ast-program'; -import type { ParserOptions } from '@typescript-eslint/types'; -import type { Linter } from '@typescript-eslint/experimental-utils/dist/ts-eslint/Linter'; -import type { - ParserServices, - TSESTreeOptions, -} from '@typescript-eslint/typescript-estree/dist/parser-options'; -import { extra } from '../lib/config'; - -export type ParseForESLintResult = Linter.ESLintParseResult; - -interface ParseAndGenerateServicesResult { - ast: AST; - services: ParserServices; -} - -function parseAndGenerateServices( - code: string, - options: T, -): ParseAndGenerateServicesResult { - const { ast, program } = createASTProgram(code, options); - const { estree, astMaps } = astConverter( - ast!, - { - ...extra, - code, - jsx: options.jsx ?? false, - }, - true, - ); - return { - ast: estree as AST, - services: { - hasFullTypeInformation: true, - program, - esTreeNodeToTSNodeMap: astMaps.esTreeNodeToTSNodeMap, - tsNodeToESTreeNodeMap: astMaps.tsNodeToESTreeNodeMap, - }, - }; -} - -export function parseForESLint( - code: string, - parserOptions: ParserOptions, -): ParseForESLintResult { - const { ast, services } = parseAndGenerateServices(code, { - ...parserOptions, - jsx: parserOptions.ecmaFeatures?.jsx ?? false, - useJSXTextNode: true, - projectFolderIgnoreList: [], - }); - const scopeManager = analyze(ast, { - ecmaVersion: - parserOptions.ecmaVersion === 'latest' ? 1e8 : parserOptions.ecmaVersion, - globalReturn: parserOptions.ecmaFeatures?.globalReturn ?? false, - sourceType: parserOptions.sourceType ?? 'script', - }); - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - return { ast, services, scopeManager, visitorKeys }; -} diff --git a/packages/website/webpack.plugin.js b/packages/website/webpack.plugin.js index f8a403f29a1..73ecbc1c394 100644 --- a/packages/website/webpack.plugin.js +++ b/packages/website/webpack.plugin.js @@ -1,115 +1,26 @@ const webpack = require('webpack'); -const path = require('path'); module.exports = function (/*context, options*/) { return { name: 'webpack-custom-plugin', configureWebpack(cfg, isServer) { - const emptyModules = [ - // TODO; verify wy this is not working, we don't want to import those files at all in build - '@typescript-eslint/experimental-utils/dist/ts-eslint/index.js', - '@typescript-eslint/typescript-estree/dist/create-program/createWatchProgram.js', - '@typescript-eslint/typescript-estree/dist/create-program/createProjectProgram.js', - '@typescript-eslint/typescript-estree/dist/create-program/createIsolatedProgram.js', - '@typescript-eslint/experimental-utils/dist/eslint-utils/RuleTester.js', - - 'eslint/lib/cli.js', - 'eslint/lib/eslint', - 'eslint/lib/eslint/index.js', - 'eslint/lib/eslint/eslint.js', - 'eslint/lib/cli-engine/cli-engine.js', - 'eslint/lib/cli-engine/load-rules.js', - 'eslint/lib/rule-tester/index.js', - 'eslint/lib/rule-tester/rule-tester.js', - 'eslint/lib/shared/ajv.js', - 'eslint/lib/shared/runtime-info.js', - 'eslint/lib/init/autoconfig.js', - 'eslint/lib/init/config-file.js', - 'eslint/lib/init/config-initializer.js', - 'eslint/lib/init/config-rule.js', - 'eslint/lib/init/npm-utils.js', - 'eslint/lib/init/source-code-utils.js', - ]; - - const modules = { - getESLintCoreRule: 'src/modules/getESLintCoreRule.js', - globby: 'src/modules/globby.js', - 'resolve-from': 'src/modules/resolve-from.js', - 'import-fresh': 'src/modules/import-fresh.js', - semver: 'src/modules/semver.js', - 'is-glob': 'src/modules/is-glob.js', - }; - return { - module: { - rules: [ - { - test: /\.[tj]sx?$/, - loader: 'string-replace-loader', - include: [ - path.resolve('src'), - path.resolve('../../node_modules'), - ], - enforce: 'pre', - options: { - multiple: [ - { - search: 'semver.major(package_json_1.version) >= 8', - replace: 'true', - }, - ], - }, - }, - ], - }, - resolve: { - fallback: { - assert: require.resolve('assert'), - fs: false, - os: require.resolve('os-browserify/browser'), - tty: false, - module: false, - path: require.resolve('path-browserify'), - util: require.resolve('util'), - crypto: false, - }, - }, externals: { typescript: 'window.ts', }, plugins: [ new webpack.DefinePlugin({ - 'process.env': { - IS_SERVER: JSON.stringify(isServer), - TS_VERSION: JSON.stringify( - require('typescript/package.json').version, - ), - ESLINT_VERSION: JSON.stringify( - require('eslint/package.json').version, - ), - TS_ESLINT_VERSION: JSON.stringify( - require('@typescript-eslint/eslint-plugin/package.json') - .version, - ), - }, + 'process.env.IS_SERVER': JSON.stringify(isServer), + 'process.env.TS_VERSION': JSON.stringify( + require('typescript/package.json').version, + ), + 'process.env.ESLINT_VERSION': JSON.stringify( + require('eslint/package.json').version, + ), + 'process.env.TS_ESLINT_VERSION': JSON.stringify( + require('@typescript-eslint/eslint-plugin/package.json').version, + ), }), - ...emptyModules.map( - item => - new webpack.NormalModuleReplacementPlugin( - new RegExp(item), - path.resolve( - __dirname, - path.resolve(__dirname, 'src/modules/empty.js'), - ), - ), - ), - ...Object.entries(modules).map( - item => - new webpack.NormalModuleReplacementPlugin( - new RegExp('(.*)' + item[0]), - path.resolve(__dirname, path.resolve(__dirname, item[1])), - ), - ), ], }; }, diff --git a/workspace.json b/workspace.json index 5c2ae71a89e..f6b206e61ed 100644 --- a/workspace.json +++ b/workspace.json @@ -12,6 +12,7 @@ "@typescript-eslint/types": "packages/types", "@typescript-eslint/typescript-estree": "packages/typescript-estree", "@typescript-eslint/visitor-keys": "packages/visitor-keys", + "@typescript-eslint/website-eslint": "packages/website-eslint", "website": "packages/website" } } diff --git a/yarn.lock b/yarn.lock index 341c85c49c7..2ecc3e3a530 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3164,6 +3164,47 @@ resolved "https://registry.yarnpkg.com/@polka/url/-/url-1.0.0-next.21.tgz#5de5a2385a35309427f6011992b544514d559aa1" integrity sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g== +"@rollup/plugin-commonjs@^21.0.1": + version "21.0.1" + resolved "https://registry.yarnpkg.com/@rollup/plugin-commonjs/-/plugin-commonjs-21.0.1.tgz#1e57c81ae1518e4df0954d681c642e7d94588fee" + integrity sha512-EA+g22lbNJ8p5kuZJUYyhhDK7WgJckW5g4pNN7n4mAFUM96VuwUnNT3xr2Db2iCZPI1pJPbGyfT5mS9T1dHfMg== + dependencies: + "@rollup/pluginutils" "^3.1.0" + commondir "^1.0.1" + estree-walker "^2.0.1" + glob "^7.1.6" + is-reference "^1.2.1" + magic-string "^0.25.7" + resolve "^1.17.0" + +"@rollup/plugin-json@^4.1.0": + version "4.1.0" + resolved "https://registry.yarnpkg.com/@rollup/plugin-json/-/plugin-json-4.1.0.tgz#54e09867ae6963c593844d8bd7a9c718294496f3" + integrity sha512-yfLbTdNS6amI/2OpmbiBoW12vngr5NW2jCJVZSBEz+H5KfUJZ2M7sDjk0U6GOOdCWFVScShte29o9NezJ53TPw== + dependencies: + "@rollup/pluginutils" "^3.0.8" + +"@rollup/plugin-node-resolve@^13.0.6": + version "13.0.6" + resolved "https://registry.yarnpkg.com/@rollup/plugin-node-resolve/-/plugin-node-resolve-13.0.6.tgz#29629070bb767567be8157f575cfa8f2b8e9ef77" + integrity sha512-sFsPDMPd4gMqnh2gS0uIxELnoRUp5kBl5knxD2EO0778G1oOJv4G1vyT2cpWz75OU2jDVcXhjVUuTAczGyFNKA== + dependencies: + "@rollup/pluginutils" "^3.1.0" + "@types/resolve" "1.17.1" + builtin-modules "^3.1.0" + deepmerge "^4.2.2" + is-module "^1.0.0" + resolve "^1.19.0" + +"@rollup/pluginutils@^3.0.8", "@rollup/pluginutils@^3.1.0": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-3.1.0.tgz#706b4524ee6dc8b103b3c995533e5ad680c02b9b" + integrity sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg== + dependencies: + "@types/estree" "0.0.39" + estree-walker "^1.0.1" + picomatch "^2.2.2" + "@rushstack/node-core-library@3.42.3": version "3.42.3" resolved "https://registry.yarnpkg.com/@rushstack/node-core-library/-/node-core-library-3.42.3.tgz#e9bc8aee4ba047d1858afcb7822b5aaf973b4fd8" @@ -3460,6 +3501,11 @@ resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.50.tgz#1e0caa9364d3fccd2931c3ed96fdbeaa5d4cca83" integrity sha512-C6N5s2ZFtuZRj54k2/zyRhNDjJwwcViAM3Nbm8zjBpbqAdZ00mr0CFxvSKeO8Y/e03WVFLpQMdHYVfUd6SB+Hw== +"@types/estree@0.0.39": + version "0.0.39" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.39.tgz#e177e699ee1b8c22d23174caaa7422644389509f" + integrity sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw== + "@types/github-slugger@^1.3.0": version "1.3.0" resolved "https://registry.yarnpkg.com/@types/github-slugger/-/github-slugger-1.3.0.tgz#16ab393b30d8ae2a111ac748a015ac05a1fc5524" @@ -3669,6 +3715,13 @@ "@types/scheduler" "*" csstype "^3.0.2" +"@types/resolve@1.17.1": + version "1.17.1" + resolved "https://registry.yarnpkg.com/@types/resolve/-/resolve-1.17.1.tgz#3afd6ad8967c77e4376c598a82ddd58f46ec45d6" + integrity sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw== + dependencies: + "@types/node" "*" + "@types/retry@^0.12.0": version "0.12.1" resolved "https://registry.yarnpkg.com/@types/retry/-/retry-0.12.1.tgz#d8f1c0d0dc23afad6dc16a9e993a0865774b4065" @@ -4250,16 +4303,6 @@ assert-plus@1.0.0, assert-plus@^1.0.0: resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU= -assert@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/assert/-/assert-2.0.0.tgz#95fc1c616d48713510680f2eaf2d10dd22e02d32" - integrity sha512-se5Cd+js9dXJnu6Ag2JFc00t+HmHOen+8Q+L7O9zI0PqQXr20uk2J0XQqMxZEeo5U50o8Nvmmx7dZrl+Ufr35A== - dependencies: - es6-object-assign "^1.1.0" - is-nan "^1.2.1" - object-is "^1.0.1" - util "^0.12.0" - assign-symbols@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" @@ -4319,11 +4362,6 @@ autoprefixer@^10.3.5, autoprefixer@^10.3.7: picocolors "^1.0.0" postcss-value-parser "^4.1.0" -available-typed-arrays@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz#92f95616501069d07d10edb2fc37d3e1c65123b7" - integrity sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw== - aws-sign2@~0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" @@ -4666,6 +4704,11 @@ builtin-modules@^1.1.1: resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" integrity sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8= +builtin-modules@^3.1.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-3.2.0.tgz#45d5db99e7ee5e6bc4f362e008bf917ab5049887" + integrity sha512-lGzLKcioL90C7wMczpkY0n/oART3MbBa8R9OFGE1rJxoVI86u4WAGfEk8Wjv10eKSyTHVGkSo3bvBylCEtk7LA== + builtins@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/builtins/-/builtins-1.0.3.tgz#cb94faeb61c8696451db36534e1422f94f0aee88" @@ -6558,7 +6601,7 @@ error-ex@^1.2.0, error-ex@^1.3.1: dependencies: is-arrayish "^0.2.1" -es-abstract@^1.17.2, es-abstract@^1.18.5, es-abstract@^1.19.0, es-abstract@^1.19.1: +es-abstract@^1.17.2, es-abstract@^1.19.0, es-abstract@^1.19.1: version "1.19.1" resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.19.1.tgz#d4885796876916959de78edaa0df456627115ec3" integrity sha512-2vJ6tjA/UfqLm2MPs7jxVybLoB8i1t1Jd9R3kISld20sIxPcTbLuggQOUxeWeAvIUkduv/CfMjuh4WmiXr2v9w== @@ -6598,11 +6641,6 @@ es-to-primitive@^1.2.1: is-date-object "^1.0.1" is-symbol "^1.0.2" -es6-object-assign@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/es6-object-assign/-/es6-object-assign-1.1.0.tgz#c2c3582656247c39ea107cb1e6652b6f9f24523c" - integrity sha1-wsNYJlYkfDnqEHyx5mUrb58kUjw= - es6-promise@^4.0.3: version "4.2.8" resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.8.tgz#4eb21594c972bc40553d276e510539143db53e0a" @@ -6903,6 +6941,16 @@ estraverse@^5.1.0, estraverse@^5.2.0: resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.2.0.tgz#307df42547e6cc7324d3cf03c155d5cdb8c53880" integrity sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ== +estree-walker@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-1.0.1.tgz#31bc5d612c96b704106b477e6dd5d8aa138cb700" + integrity sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg== + +estree-walker@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-2.0.2.tgz#52f010178c2a4c117a7757cfe942adb7d2da4cac" + integrity sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w== + esutils@^2.0.2: version "2.0.3" resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" @@ -7394,11 +7442,6 @@ for-in@^1.0.2: resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" integrity sha1-gQaNKVqBQuwKxybG4iAMMPttXoA= -foreach@^2.0.5: - version "2.0.5" - resolved "https://registry.yarnpkg.com/foreach/-/foreach-2.0.5.tgz#0bee005018aeb260d0a3af3ae658dd0136ec1b99" - integrity sha1-C+4AUBiusmDQo6865ljdATbsG5k= - forever-agent@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" @@ -8859,13 +8902,6 @@ is-generator-fn@^2.0.0: resolved "https://registry.yarnpkg.com/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118" integrity sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ== -is-generator-function@^1.0.7: - version "1.0.10" - resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.0.10.tgz#f1558baf1ac17e0deea7c0415c438351ff2b3c72" - integrity sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A== - dependencies: - has-tostringtag "^1.0.0" - is-glob@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-3.1.0.tgz#7ba5ae24217804ac70707b96922567486cc3e84a" @@ -8900,13 +8936,10 @@ is-ip@^3.1.0: dependencies: ip-regex "^4.0.0" -is-nan@^1.2.1: - version "1.3.2" - resolved "https://registry.yarnpkg.com/is-nan/-/is-nan-1.3.2.tgz#043a54adea31748b55b6cd4e09aadafa69bd9e1d" - integrity sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w== - dependencies: - call-bind "^1.0.0" - define-properties "^1.1.3" +is-module@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-module/-/is-module-1.0.0.tgz#3258fb69f78c14d5b815d664336b4cffb6441591" + integrity sha1-Mlj7afeMFNW4FdZkM2tM/7ZEFZE= is-negative-zero@^2.0.1: version "2.0.1" @@ -8989,6 +9022,13 @@ is-potential-custom-element-name@^1.0.1: resolved "https://registry.yarnpkg.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz#171ed6f19e3ac554394edf78caa05784a45bebb5" integrity sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ== +is-reference@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/is-reference/-/is-reference-1.2.1.tgz#8b2dac0b371f4bc994fdeaba9eb542d03002d0b7" + integrity sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ== + dependencies: + "@types/estree" "*" + is-regex@^1.0.4, is-regex@^1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958" @@ -9055,17 +9095,6 @@ is-text-path@^1.0.1: dependencies: text-extensions "^1.0.0" -is-typed-array@^1.1.3, is-typed-array@^1.1.7: - version "1.1.8" - resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.8.tgz#cbaa6585dc7db43318bc5b89523ea384a6f65e79" - integrity sha512-HqH41TNZq2fgtGT8WHVFVJhBVGuY3AnP3Q36K8JKXUxSxRgk/d+7NjmwG2vo2mYmXK8UYZKu0qH8bVP5gEisjA== - dependencies: - available-typed-arrays "^1.0.5" - call-bind "^1.0.2" - es-abstract "^1.18.5" - foreach "^2.0.5" - has-tostringtag "^1.0.0" - is-typedarray@^1.0.0, is-typedarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" @@ -10292,7 +10321,7 @@ macos-release@^2.2.0: resolved "https://registry.yarnpkg.com/macos-release/-/macos-release-2.5.0.tgz#067c2c88b5f3fb3c56a375b2ec93826220fa1ff2" integrity sha512-EIgv+QZ9r+814gjJj0Bt5vSLJLzswGmSUbUpbi9AIr/fsN2IWFBl2NucV9PAiek+U1STK468tEkxmVYUtuAN3g== -magic-string@^0.25.3: +magic-string@^0.25.3, magic-string@^0.25.7: version "0.25.7" resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.7.tgz#3f497d6fd34c669c6798dcb821f2ef31f5445051" integrity sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA== @@ -11350,11 +11379,6 @@ optionator@^0.9.1: type-check "^0.4.0" word-wrap "^1.2.3" -os-browserify@0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.3.0.tgz#854373c7f5c2315914fc9bfc6bd8238fdda1ec27" - integrity sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc= - os-homedir@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" @@ -11656,11 +11680,6 @@ pascalcase@^0.1.1: resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" integrity sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ= -path-browserify@1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-1.0.1.tgz#d98454a9c3753d5790860f16f68867b9e46be1fd" - integrity sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g== - path-dirname@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/path-dirname/-/path-dirname-1.0.2.tgz#cc33d24d525e099a5388c0336c6e32b9160609e0" @@ -11761,7 +11780,7 @@ picocolors@^1.0.0: resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== -picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.3: +picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.2, picomatch@^2.2.3: version "2.3.0" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.0.tgz#f1f061de8f6a4bf022892e2d128234fb98302972" integrity sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw== @@ -13099,7 +13118,7 @@ resolve.exports@^1.1.0: resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-1.1.0.tgz#5ce842b94b05146c0e03076985d1d0e7e48c90c9" integrity sha512-J1l+Zxxp4XK3LUDZ9m60LRJF/mAe4z6a4xyabPHk7pvK5t35dACV32iIjJDFeWZFfZlO29w6SZ67knR0tHzJtQ== -resolve@^1.1.6, resolve@^1.10.0, resolve@^1.14.2, resolve@^1.20.0, resolve@^1.3.2: +resolve@^1.1.6, resolve@^1.10.0, resolve@^1.14.2, resolve@^1.17.0, resolve@^1.19.0, resolve@^1.20.0, resolve@^1.3.2: version "1.20.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975" integrity sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A== @@ -13179,6 +13198,13 @@ rimraf@^2.5.4, rimraf@^2.6.2, rimraf@^2.6.3: dependencies: glob "^7.1.3" +rollup@^2.59.0: + version "2.59.0" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.59.0.tgz#108c61b0fa0a37ebc8d1f164f281622056f0db59" + integrity sha512-l7s90JQhCQ6JyZjKgo7Lq1dKh2RxatOM+Jr6a9F7WbS9WgKbocyUSeLmZl8evAse7y96Ae98L2k1cBOwWD8nHw== + optionalDependencies: + fsevents "~2.3.2" + rtl-detect@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/rtl-detect/-/rtl-detect-1.0.4.tgz#40ae0ea7302a150b96bc75af7d749607392ecac6" @@ -13886,14 +13912,6 @@ string-length@^4.0.1: char-regex "^1.0.2" strip-ansi "^6.0.0" -string-replace-loader@^3.0.3: - version "3.0.3" - resolved "https://registry.yarnpkg.com/string-replace-loader/-/string-replace-loader-3.0.3.tgz#bdbb4c9879ebb1d88185acd34afc4fb62334b433" - integrity sha512-8c26Dl6H9XmKNj3mFBvaUYR7ImOxQ4YRBFuUju78wXpa1cDpyDYvKmqGg8mfkxdYexQ/BBogB7PELlLnmR08nw== - dependencies: - loader-utils "^2.0.0" - schema-utils "^3.0.0" - string-width@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" @@ -15026,18 +15044,6 @@ util.promisify@~1.0.0: has-symbols "^1.0.1" object.getownpropertydescriptors "^2.1.0" -util@0.12.4, util@^0.12.0: - version "0.12.4" - resolved "https://registry.yarnpkg.com/util/-/util-0.12.4.tgz#66121a31420df8f01ca0c464be15dfa1d1850253" - integrity sha512-bxZ9qtSlGUWSOy9Qa9Xgk11kSslpuZwaxCg4sNIDj6FLucDab2JxnHwyNTCpHMtK1MjoQiWQ6DiUMZYbSrO+Sw== - dependencies: - inherits "^2.0.3" - is-arguments "^1.0.4" - is-generator-function "^1.0.7" - is-typed-array "^1.1.3" - safe-buffer "^5.1.2" - which-typed-array "^1.1.2" - utila@~0.4: version "0.4.0" resolved "https://registry.yarnpkg.com/utila/-/utila-0.4.0.tgz#8a16a05d445657a3aea5eecc5b12a4fa5379772c" @@ -15418,18 +15424,6 @@ which-module@^2.0.0: resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= -which-typed-array@^1.1.2: - version "1.1.7" - resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.7.tgz#2761799b9a22d4b8660b3c1b40abaa7739691793" - integrity sha512-vjxaB4nfDqwKI0ws7wZpxIlde1XrLX5uB0ZjpfshgmapJMD7jJWhZI+yToJTqaFByF0eNBcYxbjmCzoRP7CfEw== - dependencies: - available-typed-arrays "^1.0.5" - call-bind "^1.0.2" - es-abstract "^1.18.5" - foreach "^2.0.5" - has-tostringtag "^1.0.0" - is-typed-array "^1.1.7" - which@^1.2.14, which@^1.2.9, which@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" From 5961541b71aa8bb98d1d321d60f6a7bb8963c796 Mon Sep 17 00:00:00 2001 From: Armano Date: Thu, 11 Nov 2021 18:37:44 +0100 Subject: [PATCH 04/39] docs: fix circular modules --- package.json | 1 + .../src/util/collectUnusedVariables.ts | 11 ++++++----- .../eslint-plugin/src/util/getWrappingFixer.ts | 4 ++-- .../eslint-plugin/src/util/isTypeReadonly.ts | 3 ++- packages/website-eslint/package.json | 1 + packages/website-eslint/rollup.config.js | 6 +++++- packages/website-eslint/src/mock/semver.js | 4 ++++ packages/website/src/modules/empty.js | 2 -- .../website/src/modules/getESLintCoreRule.js | 17 ----------------- packages/website/src/modules/globby.js | 5 ----- packages/website/src/modules/import-fresh.js | 1 - packages/website/src/modules/is-glob.js | 1 - packages/website/src/modules/resolve-from.js | 12 ------------ packages/website/src/modules/semver.js | 7 ------- 14 files changed, 21 insertions(+), 54 deletions(-) create mode 100644 packages/website-eslint/src/mock/semver.js delete mode 100644 packages/website/src/modules/empty.js delete mode 100644 packages/website/src/modules/getESLintCoreRule.js delete mode 100644 packages/website/src/modules/globby.js delete mode 100644 packages/website/src/modules/import-fresh.js delete mode 100644 packages/website/src/modules/is-glob.js delete mode 100644 packages/website/src/modules/resolve-from.js delete mode 100644 packages/website/src/modules/semver.js diff --git a/package.json b/package.json index 9e065424a56..f55039f67c5 100644 --- a/package.json +++ b/package.json @@ -128,6 +128,7 @@ "prettier": "2.4.1", "pretty-format": "^27.3.1", "rimraf": "^3.0.2", + "semver": "^7.3.5", "tmp": "^0.2.1", "ts-jest": "^27.0.5", "ts-node": "^10.4.0", diff --git a/packages/eslint-plugin/src/util/collectUnusedVariables.ts b/packages/eslint-plugin/src/util/collectUnusedVariables.ts index a1cfac27232..f62d7588753 100644 --- a/packages/eslint-plugin/src/util/collectUnusedVariables.ts +++ b/packages/eslint-plugin/src/util/collectUnusedVariables.ts @@ -1,11 +1,12 @@ import { AST_NODE_TYPES, TSESLint, + ASTUtils, TSESTree, } from '@typescript-eslint/experimental-utils'; import { ImplicitLibVariable } from '@typescript-eslint/scope-manager'; import { Visitor } from '@typescript-eslint/scope-manager/dist/referencer/Visitor'; -import * as util from '.'; +import { nullThrows } from './nullThrows'; class UnusedVarsVisitor< TMessageIds extends string, @@ -24,7 +25,7 @@ class UnusedVarsVisitor< visitChildrenEvenIfSelectorExists: true, }); - this.#scopeManager = util.nullThrows( + this.#scopeManager = nullThrows( context.getSourceCode().scopeManager, 'Missing required scope manager', ); @@ -545,11 +546,11 @@ function isUsedVariable(variable: TSESLint.Scope.Variable): boolean { function isInLoop(node: TSESTree.Node): boolean { let currentNode: TSESTree.Node | undefined = node; while (currentNode) { - if (util.isFunction(currentNode)) { + if (ASTUtils.isFunction(currentNode)) { break; } - if (util.isLoop(currentNode)) { + if (ASTUtils.isLoop(currentNode)) { return true; } @@ -620,7 +621,7 @@ function isUsedVariable(variable: TSESLint.Scope.Variable): boolean { function getUpperFunction(node: TSESTree.Node): TSESTree.Node | null { let currentNode: TSESTree.Node | undefined = node; while (currentNode) { - if (util.isFunction(currentNode)) { + if (ASTUtils.isFunction(currentNode)) { return currentNode; } currentNode = currentNode.parent; diff --git a/packages/eslint-plugin/src/util/getWrappingFixer.ts b/packages/eslint-plugin/src/util/getWrappingFixer.ts index 00c00e74882..d5efa8bfca7 100644 --- a/packages/eslint-plugin/src/util/getWrappingFixer.ts +++ b/packages/eslint-plugin/src/util/getWrappingFixer.ts @@ -1,10 +1,10 @@ import { AST_NODE_TYPES, TSESLint, + ASTUtils, TSESTree, } from '@typescript-eslint/experimental-utils'; import { SourceCode } from '@typescript-eslint/experimental-utils/src/ts-eslint'; -import * as util from '../util'; interface WrappingFixerParams { /** Source code. */ @@ -57,7 +57,7 @@ export function getWrappingFixer( if (isWeakPrecedenceParent(node)) { // we wrapped the node in some expression which very likely has a different precedence than original wrapped node // let's wrap the whole expression in parens just in case - if (!util.isParenthesized(node, sourceCode)) { + if (!ASTUtils.isParenthesized(node, sourceCode)) { code = `(${code})`; } } diff --git a/packages/eslint-plugin/src/util/isTypeReadonly.ts b/packages/eslint-plugin/src/util/isTypeReadonly.ts index 8231455c294..efb6966d676 100644 --- a/packages/eslint-plugin/src/util/isTypeReadonly.ts +++ b/packages/eslint-plugin/src/util/isTypeReadonly.ts @@ -7,7 +7,8 @@ import { isSymbolFlagSet, } from 'tsutils'; import * as ts from 'typescript'; -import { getTypeOfPropertyOfType, nullThrows, NullThrowsReasons } from '.'; +import { nullThrows, NullThrowsReasons } from './nullThrows'; +import { getTypeOfPropertyOfType } from './propertyTypes'; const enum Readonlyness { /** the type cannot be handled by the function */ diff --git a/packages/website-eslint/package.json b/packages/website-eslint/package.json index 77bab88f9d3..3f713ee504a 100644 --- a/packages/website-eslint/package.json +++ b/packages/website-eslint/package.json @@ -19,6 +19,7 @@ "devDependencies": { "eslint": "*", "rollup": "^2.59.0", + "semver": "^7.3.5", "@rollup/plugin-commonjs": "^21.0.1", "@rollup/plugin-json": "^4.1.0", "@rollup/plugin-node-resolve": "^13.0.6", diff --git a/packages/website-eslint/rollup.config.js b/packages/website-eslint/rollup.config.js index 6f782046b64..9a799df0926 100644 --- a/packages/website-eslint/rollup.config.js +++ b/packages/website-eslint/rollup.config.js @@ -11,7 +11,7 @@ module.exports = { freeze: false, file: 'dist/index.js', }, - external: ['typescript'], + external: ['typescript', 'semver'], plugins: [ replace({ // verbose: true, @@ -43,6 +43,10 @@ module.exports = { match: /^util$/u, target: './src/mock/util.js', }, + { + match: /^semver$/u, + target: './src/mock/semver.js', + }, ], replace: [ { diff --git a/packages/website-eslint/src/mock/semver.js b/packages/website-eslint/src/mock/semver.js new file mode 100644 index 00000000000..ba292927fe9 --- /dev/null +++ b/packages/website-eslint/src/mock/semver.js @@ -0,0 +1,4 @@ +import satisfies from 'semver/functions/satisfies'; +import major from 'semver/functions/major'; + +export { satisfies, major }; diff --git a/packages/website/src/modules/empty.js b/packages/website/src/modules/empty.js deleted file mode 100644 index 670c5431e11..00000000000 --- a/packages/website/src/modules/empty.js +++ /dev/null @@ -1,2 +0,0 @@ -export {}; -export default {}; diff --git a/packages/website/src/modules/getESLintCoreRule.js b/packages/website/src/modules/getESLintCoreRule.js deleted file mode 100644 index a2e5aea0547..00000000000 --- a/packages/website/src/modules/getESLintCoreRule.js +++ /dev/null @@ -1,17 +0,0 @@ -import { nullThrows } from '@typescript-eslint/eslint-plugin/src/util'; - -export function getESLintCoreRule(ruleId) { - return nullThrows( - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call - require('eslint/use-at-your-own-risk').builtinRules.get(ruleId), - `ESLint's core rule '${ruleId}' not found.`, - ); -} - -export function maybeGetESLintCoreRule(ruleId) { - try { - return getESLintCoreRule(ruleId); - } catch { - return null; - } -} diff --git a/packages/website/src/modules/globby.js b/packages/website/src/modules/globby.js deleted file mode 100644 index dd317fb086b..00000000000 --- a/packages/website/src/modules/globby.js +++ /dev/null @@ -1,5 +0,0 @@ -module.exports = { - sync() { - return ['./tsconfig.json']; - }, -}; diff --git a/packages/website/src/modules/import-fresh.js b/packages/website/src/modules/import-fresh.js deleted file mode 100644 index 2dbcad76e1f..00000000000 --- a/packages/website/src/modules/import-fresh.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = moduleId => require(moduleId); diff --git a/packages/website/src/modules/is-glob.js b/packages/website/src/modules/is-glob.js deleted file mode 100644 index 0ba64710234..00000000000 --- a/packages/website/src/modules/is-glob.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = () => false; diff --git a/packages/website/src/modules/resolve-from.js b/packages/website/src/modules/resolve-from.js deleted file mode 100644 index 23b45ce717b..00000000000 --- a/packages/website/src/modules/resolve-from.js +++ /dev/null @@ -1,12 +0,0 @@ -'use strict'; -const resolveFrom = (/*fromDir, moduleId, silent*/) => { - return { - id: 'id', - filename: 'filename', - paths: 'test', - }; -}; - -module.exports = (fromDir, moduleId) => resolveFrom(fromDir, moduleId); -module.exports.silent = (fromDir, moduleId) => - resolveFrom(fromDir, moduleId, true); diff --git a/packages/website/src/modules/semver.js b/packages/website/src/modules/semver.js deleted file mode 100644 index c49e54bcbb7..00000000000 --- a/packages/website/src/modules/semver.js +++ /dev/null @@ -1,7 +0,0 @@ -module.exports.satisfies = () => true; -module.exports.major = version => { - const v = version.match( - /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(-[a-zA-Z\d][-a-zA-Z.\d]*)?(\+[a-zA-Z\d][-a-zA-Z.\d]*)?$/, - ); - return v && v[1] ? v[1] : null; -}; From 40c5e38d32fc457b54a49de2e725f9404ebf902b Mon Sep 17 00:00:00 2001 From: Armano Date: Thu, 11 Nov 2021 19:02:04 +0100 Subject: [PATCH 05/39] docs(website): regenerate vendors --- .../src/vendor/ds/createDesignSystem.d.ts | 24 +++++-- packages/website/src/vendor/playground.d.ts | 20 ++++-- packages/website/src/vendor/pluginUtils.d.ts | 17 +++-- packages/website/src/vendor/sandbox.d.ts | 63 ++++++++++++------- packages/website/src/vendor/tsWorker.d.ts | 34 ++++++++++ .../website/src/vendor/typescript-vfs.d.ts | 8 +++ 6 files changed, 133 insertions(+), 33 deletions(-) diff --git a/packages/website/src/vendor/ds/createDesignSystem.d.ts b/packages/website/src/vendor/ds/createDesignSystem.d.ts index 766a4bbae4e..542fd4712e4 100644 --- a/packages/website/src/vendor/ds/createDesignSystem.d.ts +++ b/packages/website/src/vendor/ds/createDesignSystem.d.ts @@ -13,11 +13,14 @@ export declare type OptionsListConfig = { style: 'separated' | 'rows'; requireRestart?: true; }; -export declare const createDesignSystem: ( - sandbox: Sandbox, -) => ( +export declare type DesignSystem = ReturnType< + ReturnType +>; +export declare const createDesignSystem: (sandbox: Sandbox) => ( container: Element, ) => { + /** The element of the design system */ + container: Element; /** Clear the sidebar */ clear: () => void; /** Present code in a pre > code */ @@ -38,6 +41,8 @@ export declare const createDesignSystem: ( model: import('monaco-editor').editor.ITextModel, diags: DiagnosticRelatedInformation[], ) => HTMLUListElement; + /** Lets you remove the hovers from listDiags etc */ + clearDeltaDecorators: (force?: true | undefined) => void; /** Shows a single option in local storage (adds an li to the container BTW) */ localStorageOption: (setting: LocalStorageOption) => HTMLLIElement; /** Uses localStorageOption to create a list of options */ @@ -56,7 +61,14 @@ export declare const createDesignSystem: ( isEnabled?: ((input: HTMLInputElement) => boolean) | undefined; }) => HTMLFormElement; /** Renders an AST tree */ - createASTTree: (node: Node) => HTMLDivElement; + createASTTree: ( + node: Node, + settings?: + | { + closedByDefault?: true | undefined; + } + | undefined, + ) => HTMLDivElement; /** Creates an input button */ button: (settings: { label: string; @@ -68,4 +80,8 @@ export declare const createDesignSystem: ( createTabButton: (text: string) => HTMLButtonElement; /** A general "restart your browser" message */ declareRestartRequired: (i?: ((key: string) => string) | undefined) => void; + /** Create a new Design System instance and add it to the container. You'll need to cast + * this after usage, because otherwise the type-system circularly references itself + */ + createSubDesignSystem: () => any; }; diff --git a/packages/website/src/vendor/playground.d.ts b/packages/website/src/vendor/playground.d.ts index ef10edc3e27..763a068e74c 100644 --- a/packages/website/src/vendor/playground.d.ts +++ b/packages/website/src/vendor/playground.d.ts @@ -2,6 +2,7 @@ declare type Sandbox = import('./sandbox').Sandbox; declare type Monaco = typeof import('monaco-editor'); import { PluginUtils } from './pluginUtils'; import type React from 'react'; + export { PluginUtils } from './pluginUtils'; export declare type PluginFactory = { ( @@ -9,6 +10,7 @@ export declare type PluginFactory = { utils: PluginUtils, ): PlaygroundPlugin; }; + /** The interface of all sidebar plugins */ export interface PlaygroundPlugin { /** Not public facing, but used by the playground to uniquely identify plugins */ @@ -40,6 +42,7 @@ export interface PlaygroundPlugin { /** An object you can use to keep data around in the scope of your plugin object */ data?: any; } + interface PlaygroundConfig { /** Language like "en" / "ja" etc */ lang: string; @@ -50,6 +53,7 @@ interface PlaygroundConfig { /** Should this playground load up custom plugins from localStorage? */ supportCustomPlugins: boolean; } + export declare const setupPlayground: ( sandbox: Sandbox, monaco: Monaco, @@ -89,9 +93,8 @@ export declare const setupPlayground: ( el: (str: string, elementType: string, container: Element) => HTMLElement; requireURL: (path: string) => string; react: typeof React; - createDesignSystem: ( - container: Element, - ) => { + createDesignSystem: (container: Element) => { + container: Element; clear: () => void; code: (code: string) => HTMLElement; title: (title: string) => HTMLElement; @@ -102,6 +105,7 @@ export declare const setupPlayground: ( model: import('monaco-editor').editor.ITextModel, diags: import('typescript').DiagnosticRelatedInformation[], ) => HTMLUListElement; + clearDeltaDecorators: (force?: true | undefined) => void; localStorageOption: ( setting: import('./ds/createDesignSystem').LocalStorageOption, ) => HTMLLIElement; @@ -120,7 +124,14 @@ export declare const setupPlayground: ( keepValueAcrossReloads?: true | undefined; isEnabled?: ((input: HTMLInputElement) => boolean) | undefined; }) => HTMLFormElement; - createASTTree: (node: import('typescript').Node) => HTMLDivElement; + createASTTree: ( + node: import('typescript').Node, + settings?: + | { + closedByDefault?: true | undefined; + } + | undefined, + ) => HTMLDivElement; button: (settings: { label: string; onclick?: ((ev: MouseEvent) => void) | undefined; @@ -130,6 +141,7 @@ export declare const setupPlayground: ( declareRestartRequired: ( i?: ((key: string) => string) | undefined, ) => void; + createSubDesignSystem: () => any; }; flashHTMLElement: (element: HTMLElement) => void; setNotifications: (pluginID: string, amount: number) => void; diff --git a/packages/website/src/vendor/pluginUtils.d.ts b/packages/website/src/vendor/pluginUtils.d.ts index 09eee2e702a..3d69a7a5c66 100644 --- a/packages/website/src/vendor/pluginUtils.d.ts +++ b/packages/website/src/vendor/pluginUtils.d.ts @@ -1,4 +1,5 @@ import type React from 'react'; + /** Creates a set of util functions which is exposed to Plugins to make it easier to build consistent UIs */ export declare const createUtils: ( sb: any, @@ -14,9 +15,8 @@ export declare const createUtils: ( * The playground plugin design system. Calling any of the functions will append the * element to the container you pass into the first param, and return the HTMLElement */ - createDesignSystem: ( - container: Element, - ) => { + createDesignSystem: (container: Element) => { + container: Element; clear: () => void; code: (code: string) => HTMLElement; title: (title: string) => HTMLElement; @@ -27,6 +27,7 @@ export declare const createUtils: ( model: import('monaco-editor').editor.ITextModel, diags: import('typescript').DiagnosticRelatedInformation[], ) => HTMLUListElement; + clearDeltaDecorators: (force?: true | undefined) => void; localStorageOption: ( setting: import('./ds/createDesignSystem').LocalStorageOption, ) => HTMLLIElement; @@ -43,7 +44,14 @@ export declare const createUtils: ( keepValueAcrossReloads?: true | undefined; isEnabled?: ((input: HTMLInputElement) => boolean) | undefined; }) => HTMLFormElement; - createASTTree: (node: import('typescript').Node) => HTMLDivElement; + createASTTree: ( + node: import('typescript').Node, + settings?: + | { + closedByDefault?: true | undefined; + } + | undefined, + ) => HTMLDivElement; button: (settings: { label: string; onclick?: ((ev: MouseEvent) => void) | undefined; @@ -51,6 +59,7 @@ export declare const createUtils: ( createTabBar: () => HTMLDivElement; createTabButton: (text: string) => HTMLButtonElement; declareRestartRequired: (i?: ((key: string) => string) | undefined) => void; + createSubDesignSystem: () => any; }; /** Flashes a HTML Element */ flashHTMLElement: (element: HTMLElement) => void; diff --git a/packages/website/src/vendor/sandbox.d.ts b/packages/website/src/vendor/sandbox.d.ts index bf6ebfde659..234696b7730 100644 --- a/packages/website/src/vendor/sandbox.d.ts +++ b/packages/website/src/vendor/sandbox.d.ts @@ -2,17 +2,21 @@ import { TypeScriptWorker } from './tsWorker'; // import { TypeScriptWorker } fr // import lzstring from "./vendor/lzstring.min"; import * as tsvfs from './typescript-vfs'; -declare type CompilerOptions = import('monaco-editor').languages.typescript.CompilerOptions; + +declare type CompilerOptions = + import('monaco-editor').languages.typescript.CompilerOptions; declare type Monaco = typeof import('monaco-editor'); /** * These are settings for the playground which are the equivalent to props in React * any changes to it should require a new setup of the playground */ -export declare type PlaygroundConfig = { +export declare type SandboxConfig = { /** The default source code for the playground */ text: string; - /** Should it run the ts or js IDE services */ - useJavaScript: boolean; + /** @deprecated */ + useJavaScript?: boolean; + /** The default file for the playground */ + filetype: 'js' | 'ts' | 'd.ts'; /** Compiler options which are automatically just forwarded on */ compilerOptions: CompilerOptions; /** Optional monaco settings overrides */ @@ -25,6 +29,8 @@ export declare type PlaygroundConfig = { suppressAutomaticallyGettingDefaultText?: true; /** Suppress setting compiler options from the compiler flags from query params */ suppressAutomaticallyGettingCompilerFlags?: true; + /** Optional path to TypeScript worker wrapper class script, see https://github.com/microsoft/monaco-typescript/pull/65 */ + customTypeScriptWorkerPath?: string; /** Logging system */ logger: { log: (...args: any[]) => void; @@ -40,12 +46,15 @@ export declare type PlaygroundConfig = { elementToAppend: HTMLElement; } ); + /** The default settings which we apply a partial over */ export declare function defaultPlaygroundSettings(): { /** The default source code for the playground */ text: string; - /** Should it run the ts or js IDE services */ - useJavaScript: boolean; + /** @deprecated */ + useJavaScript?: boolean | undefined; + /** The default file for the playground */ + filetype: 'js' | 'ts' | 'd.ts'; /** Compiler options which are automatically just forwarded on */ compilerOptions: import('monaco-editor').languages.typescript.CompilerOptions; /** Optional monaco settings overrides */ @@ -58,6 +67,8 @@ export declare function defaultPlaygroundSettings(): { suppressAutomaticallyGettingDefaultText?: true | undefined; /** Suppress setting compiler options from the compiler flags from query params */ suppressAutomaticallyGettingCompilerFlags?: true | undefined; + /** Optional path to TypeScript worker wrapper class script, see https://github.com/microsoft/monaco-typescript/pull/65 */ + customTypeScriptWorkerPath?: string | undefined; /** Logging system */ logger: { log: (...args: any[]) => void; @@ -68,22 +79,25 @@ export declare function defaultPlaygroundSettings(): { } & { domID: string; }; + /** Creates a sandbox editor, and returns a set of useful functions and the editor */ export declare const createTypeScriptSandbox: ( - partialConfig: Partial, + partialConfig: Partial, monaco: Monaco, ts: typeof import('typescript'), ) => { /** The same config you passed in */ config: { text: string; - useJavaScript: boolean; + useJavaScript?: boolean | undefined; + filetype: 'js' | 'ts' | 'd.ts'; compilerOptions: CompilerOptions; monacoSettings?: import('monaco-editor').editor.IEditorOptions | undefined; acquireTypes: boolean; supportTwoslashCompilerOptions: boolean; suppressAutomaticallyGettingDefaultText?: true | undefined; suppressAutomaticallyGettingCompilerFlags?: true | undefined; + customTypeScriptWorkerPath?: string | undefined; logger: { log: (...args: any[]) => void; error: (...args: any[]) => void; @@ -94,7 +108,10 @@ export declare const createTypeScriptSandbox: ( }; /** A list of TypeScript versions you can use with the TypeScript sandbox */ supportedVersions: readonly [ - '4.2.2', + '4.5.0-beta', + '4.4.4', + '4.3.5', + '4.2.3', '4.1.5', '4.0.5', '3.9.7', @@ -145,10 +162,12 @@ export declare const createTypeScriptSandbox: ( * * Try to use this sparingly as it can be computationally expensive, at the minimum you should be using the debounced setup. * + * Accepts an optional fsMap which you can use to add any files, or overwrite the default file. + * * TODO: It would be good to create an easy way to have a single program instance which is updated for you * when the monaco model changes. */ - setupTSVFS: () => Promise<{ + setupTSVFS: (fsMapAdditions?: Map | undefined) => Promise<{ program: import('typescript').Program; system: import('typescript').System; host: { @@ -161,15 +180,9 @@ export declare const createTypeScriptSandbox: ( createTSProgram: () => Promise; /** The Sandbox's default compiler options */ compilerDefaults: { - [x: string]: - | string - | number - | boolean - | string[] - | (string | number)[] - | import('monaco-editor').languages.typescript.MapLike - | null - | undefined; + [ + x: string + ]: import('monaco-editor').languages.typescript.CompilerOptionsValue; allowJs?: boolean | undefined; allowSyntheticDefaultImports?: boolean | undefined; allowUmdGlobalAccess?: boolean | undefined; @@ -274,15 +287,23 @@ export declare const createTypeScriptSandbox: ( // lzstring: typeof lzstring; /** Returns compiler options found in the params of the current page */ createURLQueryWithCompilerOptions: ( - sandbox: any, + _sandbox: any, paramOverrides?: any, ) => string; - /** Returns compiler options in the source code using twoslash notation */ + /** + * @deprecated Use `getTwoSlashCompilerOptions` instead. + * + * Returns compiler options in the source code using twoslash notation + */ getTwoSlashComplierOptions: (code: string) => any; + /** Returns compiler options in the source code using twoslash notation */ + getTwoSlashCompilerOptions: (code: string) => any; /** Gets to the current monaco-language, this is how you talk to the background webworkers */ languageServiceDefaults: import('monaco-editor').languages.typescript.LanguageServiceDefaults; /** The path which represents the current file using the current compiler options */ filepath: string; + /** Adds a file to the vfs used by the editor */ + addLibraryToRuntime: (code: string, _path: string) => void; }; export declare type Sandbox = ReturnType; export {}; diff --git a/packages/website/src/vendor/tsWorker.d.ts b/packages/website/src/vendor/tsWorker.d.ts index 3ceeece9e09..ab7099c30c7 100644 --- a/packages/website/src/vendor/tsWorker.d.ts +++ b/packages/website/src/vendor/tsWorker.d.ts @@ -1,72 +1,100 @@ import ts from 'typescript'; + export declare class TypeScriptWorker implements ts.LanguageServiceHost { private _ctx; private _extraLibs; private _languageService; private _compilerOptions; + constructor(ctx: any, createData: any); + getCompilationSettings(): ts.CompilerOptions; + getScriptFileNames(): string[]; + private _getModel; + getScriptVersion(fileName: string): string; + getScriptSnapshot(fileName: string): ts.IScriptSnapshot | undefined; + getScriptKind?(fileName: string): ts.ScriptKind; + getCurrentDirectory(): string; + getDefaultLibFileName(options: ts.CompilerOptions): string; + isDefaultLibFileName(fileName: string): boolean; + private static clearFiles; + getSyntacticDiagnostics(fileName: string): Promise; + getSemanticDiagnostics(fileName: string): Promise; + getSuggestionDiagnostics( fileName: string, ): Promise; + getCompilerOptionsDiagnostics(fileName: string): Promise; + getCompletionsAtPosition( fileName: string, position: number, ): Promise; + getCompletionEntryDetails( fileName: string, position: number, entry: string, ): Promise; + getSignatureHelpItems( fileName: string, position: number, ): Promise; + getQuickInfoAtPosition( fileName: string, position: number, ): Promise; + getOccurrencesAtPosition( fileName: string, position: number, ): Promise | undefined>; + getDefinitionAtPosition( fileName: string, position: number, ): Promise | undefined>; + getReferencesAtPosition( fileName: string, position: number, ): Promise; + getNavigationBarItems(fileName: string): Promise; + getFormattingEditsForDocument( fileName: string, options: ts.FormatCodeOptions, ): Promise; + getFormattingEditsForRange( fileName: string, start: number, end: number, options: ts.FormatCodeOptions, ): Promise; + getFormattingEditsAfterKeystroke( fileName: string, postion: number, ch: string, options: ts.FormatCodeOptions, ): Promise; + findRenameLocations( fileName: string, positon: number, @@ -74,12 +102,15 @@ export declare class TypeScriptWorker implements ts.LanguageServiceHost { findInComments: boolean, providePrefixAndSuffixTextForRename: boolean, ): Promise; + getRenameInfo( fileName: string, positon: number, options: ts.RenameInfoOptions, ): Promise; + getEmitOutput(fileName: string): Promise; + getCodeFixesAtPosition( fileName: string, start: number, @@ -87,12 +118,15 @@ export declare class TypeScriptWorker implements ts.LanguageServiceHost { errorCodes: number[], formatOptions: ts.FormatCodeOptions, ): Promise>; + updateExtraLibs(extraLibs: IExtraLibs): void; } + export interface IExtraLib { content: string; version: number; } + export interface IExtraLibs { [path: string]: IExtraLib; } diff --git a/packages/website/src/vendor/typescript-vfs.d.ts b/packages/website/src/vendor/typescript-vfs.d.ts index 5a5db30651a..01cd017433e 100644 --- a/packages/website/src/vendor/typescript-vfs.d.ts +++ b/packages/website/src/vendor/typescript-vfs.d.ts @@ -5,6 +5,7 @@ declare type LanguageServiceHost = import('typescript').LanguageServiceHost; declare type CompilerHost = import('typescript').CompilerHost; declare type SourceFile = import('typescript').SourceFile; declare type TS = typeof import('typescript'); + export interface VirtualTypeScriptEnvironment { sys: System; languageService: import('typescript').LanguageService; @@ -18,6 +19,7 @@ export interface VirtualTypeScriptEnvironment { replaceTextSpan?: import('typescript').TextSpan, ) => void; } + /** * Makes a virtual copy of the TypeScript environment. This is the main API you want to be using with * @typescript/vfs. A lot of the other exposed functions are used by this function to get set up. @@ -35,6 +37,7 @@ export declare function createVirtualTypeScriptEnvironment( compilerOptions?: CompilerOptions, customTransformers?: CustomTransformers, ): VirtualTypeScriptEnvironment; + /** * Grab the list of lib files for a particular target, will return a bit more than necessary (by including * the dom) but that's OK @@ -86,11 +89,13 @@ export declare const createDefaultMapFromCDN: ( fetcher?: typeof fetch | undefined, storer?: Storage | undefined, ) => Promise>; + /** * Creates an in-memory System object which can be used in a TypeScript program, this * is what provides read/write aspects of the virtual fs */ export declare function createSystem(files: Map): System; + /** * Creates a file-system backed System object which can be used in a TypeScript program, you provide * a set of virtual files which are prioritised over the FS versions, then a path to the root of your @@ -101,6 +106,7 @@ export declare function createFSBackedSystem( _projectRoot: string, ts: TS, ): System; + /** * Creates an in-memory CompilerHost -which is essentially an extra wrapper to System * which works with TypeScript objects - returns both a compiler host, and a way to add new SourceFile @@ -114,6 +120,7 @@ export declare function createVirtualCompilerHost( compilerHost: CompilerHost; updateFile: (sourceFile: SourceFile) => boolean; }; + /** * Creates an object which can host a language service against the virtual file-system */ @@ -127,4 +134,5 @@ export declare function createVirtualLanguageServiceHost( languageServiceHost: LanguageServiceHost; updateFile: (sourceFile: import('typescript').SourceFile) => void; }; + export {}; From 4339a4664098b85f16dcc396eb06c979b41d3e2a Mon Sep 17 00:00:00 2001 From: Armano Date: Mon, 15 Nov 2021 22:56:06 +0100 Subject: [PATCH 06/39] fix: update playground --- .gitignore | 1 + packages/website-eslint/package.json | 2 +- packages/website-eslint/rollup.config.js | 17 ++- packages/website-eslint/src/linter/linter.js | 1 + .../website-eslint/src/mock/typescript.js | 1 + packages/website/package.json | 7 +- packages/website/src/components/editor.tsx | 15 +-- .../src/components/lib/load-sandbox.ts | 10 +- packages/website/webpack.plugin.js | 9 ++ yarn.lock | 124 +++++++++++++++--- 10 files changed, 147 insertions(+), 40 deletions(-) create mode 100644 packages/website-eslint/src/mock/typescript.js diff --git a/.gitignore b/.gitignore index b9ffa3b3dc8..c455b1df3ce 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,7 @@ yarn-error.log* packages/website/.docusaurus packages/website/.cache-loader packages/website/build +packages/website/static/sandbox # Runtime data pids diff --git a/packages/website-eslint/package.json b/packages/website-eslint/package.json index 3f713ee504a..53239364f0c 100644 --- a/packages/website-eslint/package.json +++ b/packages/website-eslint/package.json @@ -1,6 +1,6 @@ { "name": "@typescript-eslint/website-eslint", - "version": "5.3.1", + "version": "5.4.0", "private": true, "description": "ESLint which works in browsers.", "engines": { diff --git a/packages/website-eslint/rollup.config.js b/packages/website-eslint/rollup.config.js index 9a799df0926..3876ec6f8d5 100644 --- a/packages/website-eslint/rollup.config.js +++ b/packages/website-eslint/rollup.config.js @@ -6,12 +6,12 @@ const replace = require('./rollup-plugin/replace'); module.exports = { input: 'src/linter/linter.js', output: { - format: 'module', + format: 'amd', interop: 'auto', freeze: false, file: 'dist/index.js', }, - external: ['typescript', 'semver'], + external: ['vs/language/typescript/tsWorker'], plugins: [ replace({ // verbose: true, @@ -23,14 +23,19 @@ module.exports = { /experimental-utils\/dist\/eslint-utils\/RuleTester\.js$/, /experimental-utils\/dist\/ts-eslint\/CLIEngine\.js$/, /experimental-utils\/dist\/ts-eslint\/RuleTester\.js$/, - // 'typescript-estree/dist/create-program/createWatchProgram.js', - // 'typescript-estree/dist/create-program/createProjectProgram.js', - // 'typescript-estree/dist/create-program/createIsolatedProgram.js', + /typescript-estree\/dist\/create-program\/createWatchProgram\.js/, + /typescript-estree\/dist\/create-program\/createProjectProgram\.js/, + /typescript-estree\/dist\/create-program\/createIsolatedProgram\.js/, + /experimental-utils\/dist\/ts-eslint\/ESLint\.js/, // 'eslint/lib/shared/ajv.js', // 'eslint/lib/shared/runtime-info.js', ], target: './src/mock/empty.js', }, + { + match: /typescript$/u, + target: './src/mock/typescript.js', + }, { match: /^assert$/u, target: './src/mock/assert.js', @@ -44,7 +49,7 @@ module.exports = { target: './src/mock/util.js', }, { - match: /^semver$/u, + match: /semver$/u, target: './src/mock/semver.js', }, ], diff --git a/packages/website-eslint/src/linter/linter.js b/packages/website-eslint/src/linter/linter.js index 110226fe36d..e171fe37e84 100644 --- a/packages/website-eslint/src/linter/linter.js +++ b/packages/website-eslint/src/linter/linter.js @@ -1,3 +1,4 @@ +import 'vs/language/typescript/tsWorker'; import { parseForESLint } from './parser'; import { Linter } from 'eslint'; import rules from '@typescript-eslint/eslint-plugin/dist/rules'; diff --git a/packages/website-eslint/src/mock/typescript.js b/packages/website-eslint/src/mock/typescript.js new file mode 100644 index 00000000000..324b844294b --- /dev/null +++ b/packages/website-eslint/src/mock/typescript.js @@ -0,0 +1 @@ +module.exports = window.ts; diff --git a/packages/website/package.json b/packages/website/package.json index 0f6a620e77f..ed6bb950846 100644 --- a/packages/website/package.json +++ b/packages/website/package.json @@ -19,8 +19,8 @@ "@docusaurus/remark-plugin-npm2yarn": "^2.0.0-beta.9", "@docusaurus/theme-classic": "^2.0.0-beta.9", "@mdx-js/react": "1.6.22", - "@typescript-eslint/experimental-utils": "5.3.1", - "@typescript-eslint/website-eslint": "5.3.1", + "@typescript-eslint/experimental-utils": "5.4.0", + "@typescript-eslint/website-eslint": "5.4.0", "clsx": "^1.1.1", "eslint": "*", "konamimojisplosion": "^0.5.1", @@ -35,7 +35,8 @@ "@types/react-router-dom": "^5.3.2", "globby": "^11.0.4", "monaco-editor": "^0.30.0", - "webpack": "^5.61.0" + "webpack": "^5.61.0", + "copy-webpack-plugin": "^9.1.0" }, "browserslist": { "production": [ diff --git a/packages/website/src/components/editor.tsx b/packages/website/src/components/editor.tsx index b53f1a5222c..4985f88dfe5 100644 --- a/packages/website/src/components/editor.tsx +++ b/packages/website/src/components/editor.tsx @@ -1,8 +1,5 @@ import React from 'react'; -import type { - createTypeScriptSandbox, - PlaygroundConfig, -} from '../vendor/sandbox'; +import type { createTypeScriptSandbox, SandboxConfig } from '../vendor/sandbox'; import type Monaco from 'monaco-editor'; import type { TSESTree, ParserOptions } from '@typescript-eslint/types'; @@ -139,7 +136,7 @@ class Editor extends React.Component { } async loadEditor(): Promise { - const sandboxConfig: Partial = { + const sandboxConfig: Partial = { text: '', monacoSettings: { minimap: { enabled: false }, @@ -157,7 +154,9 @@ class Editor extends React.Component { }, domID: 'monaco-editor-embed', }; - const { main, sandboxFactory, ts } = await sandboxSingleton(this.props.ts); + const { main, sandboxFactory, ts, linter } = await sandboxSingleton( + this.props.ts, + ); this.sandboxInstance = sandboxFactory.createTypeScriptSandbox( sandboxConfig, main, @@ -165,9 +164,7 @@ class Editor extends React.Component { ); this.updateTheme(); this.updateCode(); - this.linter = ( - await import('@typescript-eslint/website-eslint') - ).loadLinter(); + this.linter = linter.loadLinter(); if (this.props.onLoadRule) { this.props.onLoadRule(this.linter.ruleNames); } diff --git a/packages/website/src/components/lib/load-sandbox.ts b/packages/website/src/components/lib/load-sandbox.ts index fafff16dc0a..15931241491 100644 --- a/packages/website/src/components/lib/load-sandbox.ts +++ b/packages/website/src/components/lib/load-sandbox.ts @@ -4,6 +4,7 @@ /* eslint-disable @typescript-eslint/no-unsafe-return */ import type * as TsWorker from '../../vendor/tsWorker'; import type * as SandboxFactory from '../../vendor/sandbox'; +import { WebLinter } from '../editor'; export type Monaco = typeof import('monaco-editor'); export type TS = typeof import('typescript'); @@ -13,6 +14,9 @@ export interface SandboxModel { tsWorker: typeof TsWorker; sandboxFactory: typeof SandboxFactory; ts: TS; + linter: { + loadLinter(): WebLinter; + }; } function loadSandbox(tsVersion: string): Promise { @@ -28,8 +32,8 @@ function loadSandbox(tsVersion: string): Promise { window.require.config({ paths: { vs: `https://typescript.azureedge.net/cdn/${tsVersion}/monaco/min/vs`, - // vs: 'https://unpkg.com/@typescript-deploys/monaco-editor@4.2.2/min/vs', sandbox: 'https://www.typescriptlang.org/js/sandbox', + linter: '/sandbox', }, // This is something you need for monaco to work ignoreDuplicateModules: ['vs/editor/editor.main'], @@ -41,9 +45,10 @@ function loadSandbox(tsVersion: string): Promise { 'vs/editor/editor.main', 'vs/language/typescript/tsWorker', 'sandbox/index', + 'linter/index', ], // @ts-ignore - (main, tsWorker, sandboxFactory) => { + (main, tsWorker, sandboxFactory, linter) => { // @ts-ignore const isOK = main && window.ts && sandboxFactory; // @ts-ignore @@ -56,6 +61,7 @@ function loadSandbox(tsVersion: string): Promise { sandboxFactory, // @ts-ignore ts: window.ts, + linter, }); } else { reject( diff --git a/packages/website/webpack.plugin.js b/packages/website/webpack.plugin.js index 73ecbc1c394..1210149e15f 100644 --- a/packages/website/webpack.plugin.js +++ b/packages/website/webpack.plugin.js @@ -1,4 +1,5 @@ const webpack = require('webpack'); +const CopyPlugin = require('copy-webpack-plugin'); module.exports = function (/*context, options*/) { return { @@ -21,6 +22,14 @@ module.exports = function (/*context, options*/) { require('@typescript-eslint/eslint-plugin/package.json').version, ), }), + new CopyPlugin({ + patterns: [ + { + from: require.resolve('@typescript-eslint/website-eslint'), + to: './sandbox/index.js', + }, + ], + }), ], }; }, diff --git a/yarn.lock b/yarn.lock index fb1ccb72e12..f452e0a0de3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -285,12 +285,7 @@ chalk "^2.0.0" js-tokens "^4.0.0" -"@babel/parser@*", "@babel/parser@^7.1.0", "@babel/parser@^7.16.0", "@babel/parser@^7.7.2": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.16.0.tgz#cf147d7ada0a3655e79bf4b08ee846f00a00a295" - integrity sha512-TEHWXf0xxpi9wKVyBCmRcSSDjbJ/cl6LUdlbYUHEaNQUJGhreJbZrXT6sR4+fZLxVUJqNRB4KyOvjuy/D9009A== - -"@babel/parser@^7.12.16", "@babel/parser@^7.12.7", "@babel/parser@^7.16.2": +"@babel/parser@*", "@babel/parser@^7.1.0", "@babel/parser@^7.12.16", "@babel/parser@^7.12.7", "@babel/parser@^7.16.0", "@babel/parser@^7.16.2", "@babel/parser@^7.7.2": version "7.16.2" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.16.2.tgz#3723cd5c8d8773eef96ce57ea1d9b7faaccd12ac" integrity sha512-RUVpT0G2h6rOZwqLDTrKk7ksNv7YpAilTnYe1/Q+eDjxEceRMKVWbCsX7t8h6C1qCFi/1Y8WZjcEPBAFG27GPw== @@ -3137,6 +3132,47 @@ resolved "https://registry.yarnpkg.com/@polka/url/-/url-1.0.0-next.21.tgz#5de5a2385a35309427f6011992b544514d559aa1" integrity sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g== +"@rollup/plugin-commonjs@^21.0.1": + version "21.0.1" + resolved "https://registry.yarnpkg.com/@rollup/plugin-commonjs/-/plugin-commonjs-21.0.1.tgz#1e57c81ae1518e4df0954d681c642e7d94588fee" + integrity sha512-EA+g22lbNJ8p5kuZJUYyhhDK7WgJckW5g4pNN7n4mAFUM96VuwUnNT3xr2Db2iCZPI1pJPbGyfT5mS9T1dHfMg== + dependencies: + "@rollup/pluginutils" "^3.1.0" + commondir "^1.0.1" + estree-walker "^2.0.1" + glob "^7.1.6" + is-reference "^1.2.1" + magic-string "^0.25.7" + resolve "^1.17.0" + +"@rollup/plugin-json@^4.1.0": + version "4.1.0" + resolved "https://registry.yarnpkg.com/@rollup/plugin-json/-/plugin-json-4.1.0.tgz#54e09867ae6963c593844d8bd7a9c718294496f3" + integrity sha512-yfLbTdNS6amI/2OpmbiBoW12vngr5NW2jCJVZSBEz+H5KfUJZ2M7sDjk0U6GOOdCWFVScShte29o9NezJ53TPw== + dependencies: + "@rollup/pluginutils" "^3.0.8" + +"@rollup/plugin-node-resolve@^13.0.6": + version "13.0.6" + resolved "https://registry.yarnpkg.com/@rollup/plugin-node-resolve/-/plugin-node-resolve-13.0.6.tgz#29629070bb767567be8157f575cfa8f2b8e9ef77" + integrity sha512-sFsPDMPd4gMqnh2gS0uIxELnoRUp5kBl5knxD2EO0778G1oOJv4G1vyT2cpWz75OU2jDVcXhjVUuTAczGyFNKA== + dependencies: + "@rollup/pluginutils" "^3.1.0" + "@types/resolve" "1.17.1" + builtin-modules "^3.1.0" + deepmerge "^4.2.2" + is-module "^1.0.0" + resolve "^1.19.0" + +"@rollup/pluginutils@^3.0.8", "@rollup/pluginutils@^3.1.0": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-3.1.0.tgz#706b4524ee6dc8b103b3c995533e5ad680c02b9b" + integrity sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg== + dependencies: + "@types/estree" "0.0.39" + estree-walker "^1.0.1" + picomatch "^2.2.2" + "@rushstack/node-core-library@3.43.2": version "3.43.2" resolved "https://registry.yarnpkg.com/@rushstack/node-core-library/-/node-core-library-3.43.2.tgz#f067371a94fd92ed8f9d9aa8201c5e9e17a19f0f" @@ -3433,6 +3469,11 @@ resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.50.tgz#1e0caa9364d3fccd2931c3ed96fdbeaa5d4cca83" integrity sha512-C6N5s2ZFtuZRj54k2/zyRhNDjJwwcViAM3Nbm8zjBpbqAdZ00mr0CFxvSKeO8Y/e03WVFLpQMdHYVfUd6SB+Hw== +"@types/estree@0.0.39": + version "0.0.39" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.39.tgz#e177e699ee1b8c22d23174caaa7422644389509f" + integrity sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw== + "@types/github-slugger@^1.3.0": version "1.3.0" resolved "https://registry.yarnpkg.com/@types/github-slugger/-/github-slugger-1.3.0.tgz#16ab393b30d8ae2a111ac748a015ac05a1fc5524" @@ -3642,6 +3683,13 @@ "@types/scheduler" "*" csstype "^3.0.2" +"@types/resolve@1.17.1": + version "1.17.1" + resolved "https://registry.yarnpkg.com/@types/resolve/-/resolve-1.17.1.tgz#3afd6ad8967c77e4376c598a82ddd58f46ec45d6" + integrity sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw== + dependencies: + "@types/node" "*" + "@types/retry@^0.12.0": version "0.12.1" resolved "https://registry.yarnpkg.com/@types/retry/-/retry-0.12.1.tgz#d8f1c0d0dc23afad6dc16a9e993a0865774b4065" @@ -4527,6 +4575,11 @@ builtin-modules@^1.1.1: resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" integrity sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8= +builtin-modules@^3.1.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-3.2.0.tgz#45d5db99e7ee5e6bc4f362e008bf917ab5049887" + integrity sha512-lGzLKcioL90C7wMczpkY0n/oART3MbBa8R9OFGE1rJxoVI86u4WAGfEk8Wjv10eKSyTHVGkSo3bvBylCEtk7LA== + builtins@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/builtins/-/builtins-1.0.3.tgz#cb94faeb61c8696451db36534e1422f94f0aee88" @@ -5269,17 +5322,16 @@ copy-text-to-clipboard@^3.0.1: resolved "https://registry.yarnpkg.com/copy-text-to-clipboard/-/copy-text-to-clipboard-3.0.1.tgz#8cbf8f90e0a47f12e4a24743736265d157bce69c" integrity sha512-rvVsHrpFcL4F2P8ihsoLdFHmd404+CMg71S756oRSeQgqk51U3kicGdnvfkrxva0xXH92SjGS62B0XIJsbh+9Q== -copy-webpack-plugin@^9.0.1: - version "9.0.1" - resolved "https://registry.yarnpkg.com/copy-webpack-plugin/-/copy-webpack-plugin-9.0.1.tgz#b71d21991599f61a4ee00ba79087b8ba279bbb59" - integrity sha512-14gHKKdYIxF84jCEgPgYXCPpldbwpxxLbCmA7LReY7gvbaT555DgeBWBgBZM116tv/fO6RRJrsivBqRyRlukhw== +copy-webpack-plugin@^9.0.1, copy-webpack-plugin@^9.1.0: + version "9.1.0" + resolved "https://registry.yarnpkg.com/copy-webpack-plugin/-/copy-webpack-plugin-9.1.0.tgz#2d2c460c4c4695ec0a58afb2801a1205256c4e6b" + integrity sha512-rxnR7PaGigJzhqETHGmAcxKnLZSR5u1Y3/bcIv/1FnqXedcL/E2ewK7ZCNrArJKCiSv8yVXhTqetJh8inDvfsA== dependencies: - fast-glob "^3.2.5" - glob-parent "^6.0.0" + fast-glob "^3.2.7" + glob-parent "^6.0.1" globby "^11.0.3" normalize-path "^3.0.0" - p-limit "^3.1.0" - schema-utils "^3.0.0" + schema-utils "^3.1.1" serialize-javascript "^6.0.0" core-js-compat@^3.18.0, core-js-compat@^3.19.0: @@ -6575,6 +6627,16 @@ estraverse@^5.1.0, estraverse@^5.2.0: resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.2.0.tgz#307df42547e6cc7324d3cf03c155d5cdb8c53880" integrity sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ== +estree-walker@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-1.0.1.tgz#31bc5d612c96b704106b477e6dd5d8aa138cb700" + integrity sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg== + +estree-walker@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-2.0.2.tgz#52f010178c2a4c117a7757cfe942adb7d2da4cac" + integrity sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w== + esutils@^2.0.2: version "2.0.3" resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" @@ -6718,7 +6780,7 @@ fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== -fast-glob@^3.1.1, fast-glob@^3.2.5: +fast-glob@^3.1.1, fast-glob@^3.2.7: version "3.2.7" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.7.tgz#fd6cb7a2d7e9aa7a7846111e85a196d6b2f766a1" integrity sha512-rYGMRwip6lUMvYD3BTScMwT1HtAs2d71SMv66Vrxs0IekGZEjhM0pcMfjQPnknBt2zeCwQMEupiN02ZP4DiT1Q== @@ -7268,7 +7330,7 @@ glob-parent@^5.1.1, glob-parent@^5.1.2, glob-parent@~5.1.2: dependencies: is-glob "^4.0.1" -glob-parent@^6.0.0, glob-parent@^6.0.1: +glob-parent@^6.0.1: version "6.0.2" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3" integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== @@ -8246,6 +8308,11 @@ is-lambda@^1.0.1: resolved "https://registry.yarnpkg.com/is-lambda/-/is-lambda-1.0.1.tgz#3d9877899e6a53efc0160504cde15f82e6f061d5" integrity sha1-PZh3iZ5qU+/AFgUEzeFfgubwYdU= +is-module@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-module/-/is-module-1.0.0.tgz#3258fb69f78c14d5b815d664336b4cffb6441591" + integrity sha1-Mlj7afeMFNW4FdZkM2tM/7ZEFZE= + is-negative-zero@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.1.tgz#3de746c18dda2319241a53675908d8f766f11c24" @@ -8320,6 +8387,13 @@ is-potential-custom-element-name@^1.0.1: resolved "https://registry.yarnpkg.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz#171ed6f19e3ac554394edf78caa05784a45bebb5" integrity sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ== +is-reference@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/is-reference/-/is-reference-1.2.1.tgz#8b2dac0b371f4bc994fdeaba9eb542d03002d0b7" + integrity sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ== + dependencies: + "@types/estree" "*" + is-regex@^1.0.4, is-regex@^1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958" @@ -9572,7 +9646,7 @@ lru-cache@^6.0.0: dependencies: yallist "^4.0.0" -magic-string@^0.25.3: +magic-string@^0.25.3, magic-string@^0.25.7: version "0.25.7" resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.7.tgz#3f497d6fd34c669c6798dcb821f2ef31f5445051" integrity sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA== @@ -10027,6 +10101,11 @@ modify-values@^1.0.0: resolved "https://registry.yarnpkg.com/modify-values/-/modify-values-1.0.1.tgz#b3939fa605546474e3e3e3c63d64bd43b4ee6022" integrity sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw== +monaco-editor@^0.30.0: + version "0.30.1" + resolved "https://registry.yarnpkg.com/monaco-editor/-/monaco-editor-0.30.1.tgz#47f8d18a0aa2264fc5654581741ab8d7bec01689" + integrity sha512-B/y4+b2O5G2gjuxIFtCE2EkM17R2NM7/3F8x0qcPsqy4V83bitJTIO4TIeZpYlzu/xy6INiY/+84BEm6+7Cmzg== + ms@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" @@ -10936,7 +11015,7 @@ picocolors@^1.0.0: resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== -picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.3: +picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.2, picomatch@^2.2.3: version "2.3.0" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.0.tgz#f1f061de8f6a4bf022892e2d128234fb98302972" integrity sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw== @@ -12170,7 +12249,7 @@ resolve.exports@^1.1.0: resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-1.1.0.tgz#5ce842b94b05146c0e03076985d1d0e7e48c90c9" integrity sha512-J1l+Zxxp4XK3LUDZ9m60LRJF/mAe4z6a4xyabPHk7pvK5t35dACV32iIjJDFeWZFfZlO29w6SZ67knR0tHzJtQ== -resolve@^1.1.6, resolve@^1.10.0, resolve@^1.14.2, resolve@^1.20.0, resolve@^1.3.2: +resolve@^1.1.6, resolve@^1.10.0, resolve@^1.14.2, resolve@^1.17.0, resolve@^1.19.0, resolve@^1.20.0, resolve@^1.3.2: version "1.20.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975" integrity sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A== @@ -12245,6 +12324,13 @@ rimraf@^2.6.3: dependencies: glob "^7.1.3" +rollup@^2.59.0: + version "2.60.0" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.60.0.tgz#4ee60ab7bdd0356763f87d7099f413e5460fc193" + integrity sha512-cHdv9GWd58v58rdseC8e8XIaPUo8a9cgZpnCMMDGZFDZKEODOiPPEQFXLriWr/TjXzhPPmG5bkAztPsOARIcGQ== + optionalDependencies: + fsevents "~2.3.2" + rtl-detect@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/rtl-detect/-/rtl-detect-1.0.4.tgz#40ae0ea7302a150b96bc75af7d749607392ecac6" From fc364011d1fccf6963ba3f00ae31d4258a325839 Mon Sep 17 00:00:00 2001 From: Armano Date: Tue, 16 Nov 2021 17:54:45 +0100 Subject: [PATCH 07/39] fix: expose linter types from website-eslint --- .eslintignore | 2 ++ .eslintrc.js | 2 +- packages/website-eslint/package.json | 4 ++- packages/website-eslint/types/index.d.ts | 30 +++++++++++++++++++ packages/website/package.json | 1 - .../website/src/components/ast-viewer.tsx | 2 +- packages/website/src/components/editor.tsx | 21 ++++--------- packages/website/src/components/lib/action.ts | 6 ++-- packages/website/src/components/lib/config.ts | 26 ---------------- .../src/components/lib/load-sandbox.ts | 2 +- .../website/src/components/lib/selection.ts | 2 +- .../src/components/lib/use-hash-state.ts | 11 ++++--- packages/website/src/components/lib/utils.ts | 6 ++-- .../website/src/components/playground.tsx | 7 +++-- packages/website/src/pages/index.tsx | 15 ++-------- tools/generate-website-dts.ts | 7 +++-- 16 files changed, 69 insertions(+), 75 deletions(-) create mode 100644 packages/website-eslint/types/index.d.ts delete mode 100644 packages/website/src/components/lib/config.ts diff --git a/.eslintignore b/.eslintignore index d47d9f2f23e..ef0a0aa19a9 100644 --- a/.eslintignore +++ b/.eslintignore @@ -11,6 +11,8 @@ packages/eslint-plugin-tslint/tests packages/website/**/*.js packages/website/**/*.d.ts +packages/website-eslint/**/*.js +packages/website-eslint/**/*.d.ts # Files copied as part of the build packages/types/src/ast-spec.ts diff --git a/.eslintrc.js b/.eslintrc.js index 303f8d18b2a..e131f7cb8f2 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -205,7 +205,7 @@ module.exports = { 'jest/no-deprecated-functions': 'error', }, }, - // test utility scripts + // test utility scripts and website js files { files: ['tests/**/*.js'], rules: { diff --git a/packages/website-eslint/package.json b/packages/website-eslint/package.json index 53239364f0c..a54cd07b69c 100644 --- a/packages/website-eslint/package.json +++ b/packages/website-eslint/package.json @@ -6,6 +6,7 @@ "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, + "types": "types/index.d.ts", "main": "dist/index.js", "files": [ "dist" @@ -14,7 +15,8 @@ "build": "rollup --config=rollup.config.js" }, "dependencies": { - "eslint": "*" + "@typescript-eslint/experimental-utils": "^5.4.0", + "@typescript-eslint/types": "^5.4.0" }, "devDependencies": { "eslint": "*", diff --git a/packages/website-eslint/types/index.d.ts b/packages/website-eslint/types/index.d.ts new file mode 100644 index 00000000000..295d3261aa1 --- /dev/null +++ b/packages/website-eslint/types/index.d.ts @@ -0,0 +1,30 @@ +import type { TSESLint } from '@typescript-eslint/experimental-utils'; +import type { ParserOptions } from '@typescript-eslint/types'; + +export type LintMessage = TSESLint.Linter.LintMessage; +export type RuleFix = TSESLint.RuleFix; +export type RulesRecord = TSESLint.Linter.RulesRecord; +export type RuleEntry = TSESLint.Linter.RuleEntry; +export type ParseForESLintResult = TSESLint.Linter.ESLintParseResult; +export type ESLintAST = ParseForESLintResult['ast']; + +export interface WebLinter { + ruleNames: string[]; + + getAst(): ESLintAST; + + lint( + code: string, + parserOptions: ParserOptions, + rules?: RulesRecord, + ): LintMessage[]; +} + +export type { TSESTree } from '@typescript-eslint/types'; + +export type { + DebugLevel, + EcmaVersion, + ParserOptions, + SourceType, +} from '@typescript-eslint/types'; diff --git a/packages/website/package.json b/packages/website/package.json index ed6bb950846..9cc18917e6d 100644 --- a/packages/website/package.json +++ b/packages/website/package.json @@ -19,7 +19,6 @@ "@docusaurus/remark-plugin-npm2yarn": "^2.0.0-beta.9", "@docusaurus/theme-classic": "^2.0.0-beta.9", "@mdx-js/react": "1.6.22", - "@typescript-eslint/experimental-utils": "5.4.0", "@typescript-eslint/website-eslint": "5.4.0", "clsx": "^1.1.1", "eslint": "*", diff --git a/packages/website/src/components/ast-viewer.tsx b/packages/website/src/components/ast-viewer.tsx index 4c6c3b4d4ce..2e9b6aa4103 100644 --- a/packages/website/src/components/ast-viewer.tsx +++ b/packages/website/src/components/ast-viewer.tsx @@ -6,7 +6,7 @@ import React, { useState, } from 'react'; import styles from './ast-viewer.module.css'; -import type { TSESTree } from '@typescript-eslint/types'; +import type { TSESTree } from '@typescript-eslint/website-eslint'; import clsx from 'clsx'; import { scrollIntoViewIfNeeded } from './lib/scroll-into'; import { filterRecord } from './lib/selection'; diff --git a/packages/website/src/components/editor.tsx b/packages/website/src/components/editor.tsx index 4985f88dfe5..9881785b2ed 100644 --- a/packages/website/src/components/editor.tsx +++ b/packages/website/src/components/editor.tsx @@ -2,8 +2,11 @@ import React from 'react'; import type { createTypeScriptSandbox, SandboxConfig } from '../vendor/sandbox'; import type Monaco from 'monaco-editor'; -import type { TSESTree, ParserOptions } from '@typescript-eslint/types'; -import type { TSESLint } from '@typescript-eslint/experimental-utils'; +import type { + WebLinter, + LintMessage, + TSESTree, +} from '@typescript-eslint/website-eslint'; import { sandboxSingleton } from './lib/load-sandbox'; import { createProvideCodeActions } from './lib/action'; @@ -11,18 +14,6 @@ import { createURI, messageToMarker } from './lib/utils'; import { debounce } from './lib/debounce'; import { HashStateOptions } from './lib/use-hash-state'; -export interface WebLinter { - ruleNames: string[]; - - getAst(): TSESLint.Linter.ESLintParseResult['ast']; - - lint( - code: string, - parserOptions: ParserOptions, - rules?: TSESLint.Linter.RulesRecord, - ): TSESLint.Linter.LintMessage[]; -} - interface EditorProps extends HashStateOptions { darkTheme: boolean; decoration?: TSESTree.Node | null; @@ -49,7 +40,7 @@ class Editor extends React.Component { private _codeIsUpdating: boolean; private _decorations: string[]; - private readonly fixes: Map; + private readonly fixes: Map; constructor(props: EditorProps) { super(props); diff --git a/packages/website/src/components/lib/action.ts b/packages/website/src/components/lib/action.ts index d5dc7687943..fb8ae1cf9db 100644 --- a/packages/website/src/components/lib/action.ts +++ b/packages/website/src/components/lib/action.ts @@ -1,6 +1,6 @@ import type { languages, editor } from 'monaco-editor'; -import type { Linter } from '@typescript-eslint/experimental-utils/dist/ts-eslint/Linter'; -import type { RuleFix } from '@typescript-eslint/experimental-utils/dist/ts-eslint/Rule'; +import type { LintMessage, RuleFix } from '@typescript-eslint/website-eslint'; + import { createURI } from './utils'; export function createQuickfixCodeAction( @@ -35,7 +35,7 @@ export function createQuickfixCodeAction( } export function createProvideCodeActions( - fixes: Map, + fixes: Map, ): languages.CodeActionProvider { return { provideCodeActions( diff --git a/packages/website/src/components/lib/config.ts b/packages/website/src/components/lib/config.ts deleted file mode 100644 index f4a33eda679..00000000000 --- a/packages/website/src/components/lib/config.ts +++ /dev/null @@ -1,26 +0,0 @@ -import type { Extra } from '@typescript-eslint/typescript-estree/dist/parser-options'; - -export const extra: Extra = { - code: '', - comment: true, - comments: [], - createDefaultProgram: false, - debugLevel: new Set(), - errorOnTypeScriptSyntacticAndSemanticIssues: false, - errorOnUnknownASTType: false, - extraFileExtensions: [], - filePath: '', - jsx: false, - loc: true, - log: console.log, - preserveNodeMaps: true, - projects: [], - range: true, - strict: false, - tokens: [], - tsconfigRootDir: '/', - EXPERIMENTAL_useSourceOfProjectReferenceRedirect: false, - singleRun: false, - programs: null, - moduleResolver: '', -}; diff --git a/packages/website/src/components/lib/load-sandbox.ts b/packages/website/src/components/lib/load-sandbox.ts index 15931241491..2564612cd77 100644 --- a/packages/website/src/components/lib/load-sandbox.ts +++ b/packages/website/src/components/lib/load-sandbox.ts @@ -4,7 +4,7 @@ /* eslint-disable @typescript-eslint/no-unsafe-return */ import type * as TsWorker from '../../vendor/tsWorker'; import type * as SandboxFactory from '../../vendor/sandbox'; -import { WebLinter } from '../editor'; +import type { WebLinter } from '@typescript-eslint/website-eslint'; export type Monaco = typeof import('monaco-editor'); export type TS = typeof import('typescript'); diff --git a/packages/website/src/components/lib/selection.ts b/packages/website/src/components/lib/selection.ts index 33ac1de77f0..43865a74da3 100644 --- a/packages/website/src/components/lib/selection.ts +++ b/packages/website/src/components/lib/selection.ts @@ -1,5 +1,5 @@ /* eslint-disable @typescript-eslint/ban-ts-comment */ -import type { TSESTree } from '@typescript-eslint/types'; +import type { TSESTree } from '@typescript-eslint/website-eslint'; export const propsToFilter = ['parent', 'comments', 'tokens', 'loc']; diff --git a/packages/website/src/components/lib/use-hash-state.ts b/packages/website/src/components/lib/use-hash-state.ts index 06a06d3eb1f..0d349d6f988 100644 --- a/packages/website/src/components/lib/use-hash-state.ts +++ b/packages/website/src/components/lib/use-hash-state.ts @@ -1,12 +1,15 @@ import { useRef, useState, useEffect } from 'react'; -import type { ParserOptions } from '@typescript-eslint/parser'; +import type { + ParserOptions, + RulesRecord, + RuleEntry, +} from '@typescript-eslint/website-eslint'; import { debounce } from './debounce'; -import type { Linter } from '@typescript-eslint/experimental-utils/dist/ts-eslint/Linter'; export interface HashStateOptions { jsx?: boolean; sourceType?: ParserOptions['sourceType']; - rules?: Linter.RulesRecord; + rules?: RulesRecord; code: string; ts: string; showAST?: boolean; @@ -47,7 +50,7 @@ const parseStateFromUrl = (): HashStateOptions | undefined => { rules: searchParams.has('rules') ? (readQueryParam(searchParams.get('rules')!) as Record< string, - Linter.RuleEntry + RuleEntry >) : undefined, }; diff --git a/packages/website/src/components/lib/utils.ts b/packages/website/src/components/lib/utils.ts index ad1d0c78b37..f5cabd9aae9 100644 --- a/packages/website/src/components/lib/utils.ts +++ b/packages/website/src/components/lib/utils.ts @@ -1,5 +1,5 @@ import type { editor } from 'monaco-editor'; -import type { TSESLint } from '@typescript-eslint/experimental-utils'; +import type { LintMessage } from '@typescript-eslint/website-eslint'; const ensurePositiveInt = ( value: number | undefined, @@ -8,9 +8,7 @@ const ensurePositiveInt = ( return Math.max(1, (value !== undefined ? value : defaultValue) | 0); }; -export function messageToMarker( - message: TSESLint.Linter.LintMessage, -): editor.IMarkerData { +export function messageToMarker(message: LintMessage): editor.IMarkerData { const startLineNumber = ensurePositiveInt(message.line, 1); const startColumn = ensurePositiveInt(message.column, 1); return { diff --git a/packages/website/src/components/playground.tsx b/packages/website/src/components/playground.tsx index 124e1724446..23a3bd33975 100644 --- a/packages/website/src/components/playground.tsx +++ b/packages/website/src/components/playground.tsx @@ -6,9 +6,10 @@ import styles from './playground.module.css'; import Loader from './loader'; import useHashState from './lib/use-hash-state'; - -import type { ParseForESLintResult } from './linter/parser'; -import type { TSESTree } from '@typescript-eslint/types'; +import type { + ParseForESLintResult, + TSESTree, +} from '@typescript-eslint/website-eslint'; import type Monaco from 'monaco-editor'; import OptionsSelector from './options-selector'; import ASTViewer from './ast-viewer'; diff --git a/packages/website/src/pages/index.tsx b/packages/website/src/pages/index.tsx index 5f38a32cd9c..0145140f672 100644 --- a/packages/website/src/pages/index.tsx +++ b/packages/website/src/pages/index.tsx @@ -119,15 +119,6 @@ function Feature({ title, description }: FeatureItem): JSX.Element { ); } -interface SponsorsModel { - tier: string; - name: string; - slug: string; - website: string; - image: string; - description: string; -} - function Sponsors(props: { tier: string; title: string; @@ -135,9 +126,9 @@ function Sponsors(props: { }): JSX.Element { const { siteConfig } = useDocusaurusContext(); - const tierSponsors = ( - siteConfig.customFields!.sponsors as SponsorsModel[] - ).filter(sponsor => sponsor.tier === props.tier); + const tierSponsors = siteConfig.customFields.sponsors.filter( + sponsor => sponsor.tier === props.tier, + ); return (
    diff --git a/tools/generate-website-dts.ts b/tools/generate-website-dts.ts index a0e2763db9a..d0ac96c684c 100644 --- a/tools/generate-website-dts.ts +++ b/tools/generate-website-dts.ts @@ -17,7 +17,7 @@ async function getFileAndStoreLocally( fs.writeFileSync(path, editFunc(contents), 'utf8'); } -async function run(): Promise { +async function main(): Promise { const vendor = path.join( __dirname, '..', @@ -115,4 +115,7 @@ async function run(): Promise { ); } -run(); +main().catch(error => { + console.error(error); + process.exitCode = 1; +}); From 0f7439f826097b53fc256e71ff879003a2cf4f9d Mon Sep 17 00:00:00 2001 From: Armano Date: Thu, 18 Nov 2021 07:44:01 +0100 Subject: [PATCH 08/39] fix(website): add extended eslint config editor --- .../src/components/ast/PropertyName.tsx | 26 +++ .../src/components/ast/PropertyValue.tsx | 21 ++ .../{ => ast}/ast-viewer.module.css | 0 .../src/components/{ => ast}/ast-viewer.tsx | 59 +----- packages/website/src/components/ast/types.ts | 10 + packages/website/src/components/checkbox.tsx | 37 ++++ packages/website/src/components/dropdown.tsx | 32 +++ .../src/components/lib/load-sandbox.ts | 36 ++-- .../components/modals/modal-eslint.module.css | 66 ++++++ .../src/components/modals/modal-eslint.tsx | 190 ++++++++++++++++++ .../components/modals/modal-typescript.tsx | 21 ++ .../components/{ => modals}/modal.module.css | 0 .../src/components/{ => modals}/modal.tsx | 3 +- .../src/components/options-selector.tsx | 163 ++++++--------- .../src/components/playground.module.css | 2 +- .../website/src/components/playground.tsx | 5 +- packages/website/tsconfig.json | 2 +- 17 files changed, 499 insertions(+), 174 deletions(-) create mode 100644 packages/website/src/components/ast/PropertyName.tsx create mode 100644 packages/website/src/components/ast/PropertyValue.tsx rename packages/website/src/components/{ => ast}/ast-viewer.module.css (100%) rename packages/website/src/components/{ => ast}/ast-viewer.tsx (76%) create mode 100644 packages/website/src/components/ast/types.ts create mode 100644 packages/website/src/components/checkbox.tsx create mode 100644 packages/website/src/components/dropdown.tsx create mode 100644 packages/website/src/components/modals/modal-eslint.module.css create mode 100644 packages/website/src/components/modals/modal-eslint.tsx create mode 100644 packages/website/src/components/modals/modal-typescript.tsx rename packages/website/src/components/{ => modals}/modal.module.css (100%) rename packages/website/src/components/{ => modals}/modal.tsx (92%) diff --git a/packages/website/src/components/ast/PropertyName.tsx b/packages/website/src/components/ast/PropertyName.tsx new file mode 100644 index 00000000000..ac94dbf98c7 --- /dev/null +++ b/packages/website/src/components/ast/PropertyName.tsx @@ -0,0 +1,26 @@ +import React, { SyntheticEvent } from 'react'; +import clsx from 'clsx'; +import styles from './ast-viewer.module.css'; + +export default function PropertyName(props: { + name?: string; + propName?: string; + onClick?: (e: SyntheticEvent) => void; + onMouseEnter?: (e: SyntheticEvent) => void; +}): JSX.Element { + return ( + + {props.propName && ( + + {props.propName} + + )} + {props.propName && : } + {props.name && ( + + {props.name} + + )} + + ); +} diff --git a/packages/website/src/components/ast/PropertyValue.tsx b/packages/website/src/components/ast/PropertyValue.tsx new file mode 100644 index 00000000000..3aaf81cc693 --- /dev/null +++ b/packages/website/src/components/ast/PropertyValue.tsx @@ -0,0 +1,21 @@ +import styles from './ast-viewer.module.css'; +import React from 'react'; + +export default function PropertyValue(props: { value: unknown }): JSX.Element { + if (typeof props.value === 'string') { + return ( + {JSON.stringify(props.value)} + ); + } else if (typeof props.value === 'number') { + return {props.value}; + } else if (typeof props.value === 'bigint') { + return {String(props.value)}n; + } else if (typeof props.value === 'boolean') { + return ( + + {props.value ? 'true' : 'false'} + + ); + } + return {String(props.value)}; +} diff --git a/packages/website/src/components/ast-viewer.module.css b/packages/website/src/components/ast/ast-viewer.module.css similarity index 100% rename from packages/website/src/components/ast-viewer.module.css rename to packages/website/src/components/ast/ast-viewer.module.css diff --git a/packages/website/src/components/ast-viewer.tsx b/packages/website/src/components/ast/ast-viewer.tsx similarity index 76% rename from packages/website/src/components/ast-viewer.tsx rename to packages/website/src/components/ast/ast-viewer.tsx index 2e9b6aa4103..631c9ffd8bd 100644 --- a/packages/website/src/components/ast-viewer.tsx +++ b/packages/website/src/components/ast/ast-viewer.tsx @@ -8,61 +8,16 @@ import React, { import styles from './ast-viewer.module.css'; import type { TSESTree } from '@typescript-eslint/website-eslint'; import clsx from 'clsx'; -import { scrollIntoViewIfNeeded } from './lib/scroll-into'; -import { filterRecord } from './lib/selection'; +import { scrollIntoViewIfNeeded } from '../lib/scroll-into'; +import { filterRecord } from '../lib/selection'; -interface GenericParams { - propName?: string; - name?: string; - value: V; - level: string; - selection?: TSESTree.Node | null; - onSelectNode: (node: TSESTree.Node | null) => void; -} +import type { GenericParams } from './types'; -const PropertyName = React.memo(function PropertyName(props: { - name?: string; - propName?: string; - onClick?: (e: SyntheticEvent) => void; - onMouseEnter?: (e: SyntheticEvent) => void; -}) { - return ( - - {props.propName && ( - - {props.propName} - - )} - {props.propName && : } - {props.name && ( - - {props.name} - - )} - - ); -}); +import PropertyNameComp from './PropertyName'; +import PropertyValueComp from './PropertyValue'; -const PropertyValue = React.memo(function PropertyValue(props: { - value: unknown; -}) { - if (typeof props.value === 'string') { - return ( - {JSON.stringify(props.value)} - ); - } else if (typeof props.value === 'number') { - return {props.value}; - } else if (typeof props.value === 'bigint') { - return {String(props.value)}n; - } else if (typeof props.value === 'boolean') { - return ( - - {props.value ? 'true' : 'false'} - - ); - } - return {String(props.value)}; -}); +const PropertyName = React.memo(PropertyNameComp); +const PropertyValue = React.memo(PropertyValueComp); function ElementArray(props: GenericParams): JSX.Element { const isComplex = props.value.some( diff --git a/packages/website/src/components/ast/types.ts b/packages/website/src/components/ast/types.ts new file mode 100644 index 00000000000..096128278df --- /dev/null +++ b/packages/website/src/components/ast/types.ts @@ -0,0 +1,10 @@ +import type { TSESTree } from '@typescript-eslint/website-eslint'; + +export interface GenericParams { + propName?: string; + name?: string; + value: V; + level: string; + selection?: TSESTree.Node | null; + onSelectNode: (node: TSESTree.Node | null) => void; +} diff --git a/packages/website/src/components/checkbox.tsx b/packages/website/src/components/checkbox.tsx new file mode 100644 index 00000000000..54e8c324878 --- /dev/null +++ b/packages/website/src/components/checkbox.tsx @@ -0,0 +1,37 @@ +import React, { useEffect, useRef } from 'react'; + +interface CheckboxProps { + name: string; + value: string; + onChange: (checked: boolean, value: string) => void; + indeterminate?: boolean; + checked: boolean; + className?: string; +} + +function Checkbox(props: CheckboxProps): JSX.Element { + const checkboxRef = useRef(); + + useEffect(() => { + if (!checkboxRef.current) { + return; + } + + if (props.indeterminate !== checkboxRef.current.indeterminate) { + checkboxRef.current.indeterminate = props.indeterminate ?? false; + } + }, [props.indeterminate]); + + return ( + props.onChange(e.target.checked, props.value)} + /> + ); +} + +export default Checkbox; diff --git a/packages/website/src/components/dropdown.tsx b/packages/website/src/components/dropdown.tsx new file mode 100644 index 00000000000..b939beedf7d --- /dev/null +++ b/packages/website/src/components/dropdown.tsx @@ -0,0 +1,32 @@ +import styles from './options-selector.module.css'; +import React from 'react'; +import clsx from 'clsx'; + +interface DropdownProps { + readonly onChange: (value: string) => void; + readonly options: string[]; + readonly value: string | undefined; + readonly name: string; + readonly className?: string; +} + +function Dropdown(props: DropdownProps): JSX.Element { + return ( + + ); +} + +export default Dropdown; diff --git a/packages/website/src/components/lib/load-sandbox.ts b/packages/website/src/components/lib/load-sandbox.ts index 2564612cd77..0dba2bfff87 100644 --- a/packages/website/src/components/lib/load-sandbox.ts +++ b/packages/website/src/components/lib/load-sandbox.ts @@ -1,7 +1,3 @@ -/* eslint-disable @typescript-eslint/ban-ts-comment */ -/* eslint-disable @typescript-eslint/no-unsafe-assignment */ -/* eslint-disable @typescript-eslint/no-unsafe-call */ -/* eslint-disable @typescript-eslint/no-unsafe-return */ import type * as TsWorker from '../../vendor/tsWorker'; import type * as SandboxFactory from '../../vendor/sandbox'; import type { WebLinter } from '@typescript-eslint/website-eslint'; @@ -9,6 +5,29 @@ import type { WebLinter } from '@typescript-eslint/website-eslint'; export type Monaco = typeof import('monaco-editor'); export type TS = typeof import('typescript'); +declare global { + type WindowRequireCb = ( + main: Monaco, + tsWorker: typeof TsWorker, + sandboxFactory: typeof SandboxFactory, + linter: { + loadLinter(): WebLinter; + }, + ) => void; + interface WindowRequire { + (files: string[], cb: WindowRequireCb): void; + config: (arg: { + paths?: Record; + ignoreDuplicateModules?: string[]; + }) => void; + } + + interface Window { + ts: TS; + require: WindowRequire; + } +} + export interface SandboxModel { main: Monaco; tsWorker: typeof TsWorker; @@ -28,7 +47,6 @@ function loadSandbox(tsVersion: string): Promise { // For the monaco version you can use unpkg or the TypeScript web infra CDN // You can see the available releases for TypeScript here: // https://typescript.azureedge.net/indexes/releases.json - // @ts-ignore window.require.config({ paths: { vs: `https://typescript.azureedge.net/cdn/${tsVersion}/monaco/min/vs`, @@ -47,19 +65,13 @@ function loadSandbox(tsVersion: string): Promise { 'sandbox/index', 'linter/index', ], - // @ts-ignore (main, tsWorker, sandboxFactory, linter) => { - // @ts-ignore const isOK = main && window.ts && sandboxFactory; - // @ts-ignore - // window.ts.__esModule = true; - // window.ts.SyntaxKind; if (isOK) { resolve({ main, tsWorker, sandboxFactory, - // @ts-ignore ts: window.ts, linter, }); @@ -77,7 +89,7 @@ function loadSandbox(tsVersion: string): Promise { }); } -let instance; +let instance: Promise | undefined; export const sandboxSingleton = (version: string): Promise => { if (instance) { diff --git a/packages/website/src/components/modals/modal-eslint.module.css b/packages/website/src/components/modals/modal-eslint.module.css new file mode 100644 index 00000000000..9b843ef9acd --- /dev/null +++ b/packages/website/src/components/modals/modal-eslint.module.css @@ -0,0 +1,66 @@ +.search { + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + background: var(--ifm-navbar-search-input-background-color) + var(--ifm-navbar-search-input-icon) no-repeat 0.75rem center / 1rem 1rem; + border: none; + color: var(--ifm-navbar-search-input-color); + cursor: text; + display: inline-block; + height: 2rem; + padding: 0 0.5rem 0 2.25rem; + font-size: 0.9rem; + flex: 1; + border-radius: 0.2rem; +} + +.searchResult { + align-items: center; + display: flex; + flex: 0 0 1.5rem; + flex-direction: row; + font-size: 0.75rem; + margin: 0 0; + padding: 0.4rem 0.8rem; + transition: background-color var(--ifm-transition-fast) + var(--ifm-transition-timing-default), + color var(--ifm-transition-fast) var(--ifm-transition-timing-default); + color: var(--ifm-font-color-secondary); + justify-content: space-between; +} + +.searchResult:nth-child(even) { + background: var(--ifm-color-emphasis-100); +} + +.searchResult:hover { + background: var(--ifm-color-emphasis-200); +} + +.searchResultContainer { + overflow: auto; + height: 50vh; +} + +.editJson { + height: 2rem; +} + +.topBar { + display: flex; + column-gap: 0.5rem; + margin-bottom: 0.5rem; + justify-content: flex-end; +} + +.textarea { + background: var(--ifm-navbar-search-input-background-color); + border: none; + color: var(--ifm-navbar-search-input-color); + cursor: text; + width: 100%; + max-width: 100%; + min-width: 100%; + padding: 1rem; +} diff --git a/packages/website/src/components/modals/modal-eslint.tsx b/packages/website/src/components/modals/modal-eslint.tsx new file mode 100644 index 00000000000..13f4ade2d04 --- /dev/null +++ b/packages/website/src/components/modals/modal-eslint.tsx @@ -0,0 +1,190 @@ +import React, { useCallback, useEffect, useState } from 'react'; +import clsx from 'clsx'; +import type { RulesRecord, RuleEntry } from '@typescript-eslint/website-eslint'; + +import Modal from './modal'; +import Checkbox from '../checkbox'; + +import styles from './modal-eslint.module.css'; + +interface ModalEslintProps { + isOpen: boolean; + onClose: (rules: RulesRecord) => void; + ruleOptions: string[]; + rules: RulesRecord; +} + +interface RuleModel { + name: string; + isEnabled: boolean; + isCustom: boolean; + value: RuleEntry; +} + +function mapRecords(rules: RulesRecord): RuleModel[] { + return Object.entries(rules).map(item => { + const value = item[1]!; + return { + name: item[0], + isEnabled: + value !== 0 && + value !== 'off' && + !(Array.isArray(value) && (value[0] === 'off' || value[0] === 0)), + isCustom: value === 1 || (Array.isArray(value) && value.length > 1), + value: value, + }; + }); +} + +function buildRules(rules: RulesRecord, ruleOptions: string[]): RuleModel[] { + return mapRecords({ + ...ruleOptions.reduce((acc, item) => { + acc[item] = 0; + return acc; + }, {}), + ...rules, + }).sort((a, b) => { + return a.name.localeCompare(b.name); + }); +} + +function buildRulesRecord(rules: RuleModel[], short = true): RulesRecord { + const ruleError = short ? 2 : 'error'; + const ruleOff = short ? 0 : 'off'; + return rules + .filter(item => item.isEnabled || item.isCustom) + .reduce((acc, item) => { + acc[item.name] = item.isCustom + ? item.value + : item.isEnabled + ? ruleError + : ruleOff; + return acc; + }, {}); +} + +function isRecord(data: unknown): data is Record { + return Boolean(data && typeof data === 'object'); +} + +function ModalEslint(props: ModalEslintProps): JSX.Element { + const [filter, setFilter] = useState(''); + const [editJson, setEditJson] = useState(false); + const [rules, setRules] = useState([]); + const [rulesCode, setRulesCode] = useState(''); + + useEffect(() => { + setRules(buildRules(props.rules ?? {}, props.ruleOptions ?? [])); + }, [props.rules, props.ruleOptions]); + + const updateRule = useCallback( + (checked: boolean, name: string) => { + setRules( + rules.map(item => { + if (item.name === name) { + item.isEnabled = checked; + item.isCustom = false; + } + return item; + }), + // TODO: do we want sorting here? + // .sort((a, b) => { + // return a.isEnabled === b.isEnabled + // ? a.name.localeCompare(b.name) + // : a.isEnabled + // ? 1 + // : -1; + // }) + ); + }, + [rules], + ); + const changeEditType = useCallback(() => { + if (editJson) { + try { + const data: unknown = JSON.parse(rulesCode); + if (isRecord(data) && 'rules' in data && isRecord(data.rules)) { + // @ts-expect-error: unsafe code + const parsed = buildRules(data.rules, props.ruleOptions ?? []); + setRules(parsed); + } + } catch { + console.error('ERROR parsing json'); + } + } else { + setRulesCode( + JSON.stringify( + { + rules: buildRulesRecord(rules, false), + }, + null, + 2, + ), + ); + } + setEditJson(!editJson); + }, [editJson, rules, rulesCode]); + + const onClose = useCallback(() => { + props.onClose(buildRulesRecord(rules)); + }, [props, rules]); + + return ( + + <> +
    + {!editJson && ( + setFilter(e.target.value)} + /> + )} + +
    + {editJson && ( +