Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

chore(website): [playground] add types tab to playground#6843

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged
JoshuaKGoldberg merged 17 commits intomainfromchore/playground-type-inspector
Sep 11, 2023
Merged
Show file tree
Hide file tree
Changes fromall commits
Commits
Show all changes
17 commits
Select commitHold shift + click to select a range
5cf6bcd
chore(website): [playground] add types tab to playground
armano2Apr 4, 2023
5a74892
chore(website): [playground] add missing styles
armano2Apr 4, 2023
45a6b97
Merge branch 'v6' into chore/playground-type-inspector
armano2Apr 5, 2023
d721e16
Merge remote-tracking branch 'origin/v6' into chore/playground-type-i…
armano2Apr 7, 2023
14e4323
fix: remove unnecessary code after merge
armano2Apr 7, 2023
bec2c64
Merge branch 'v6' into chore/playground-type-inspector
armano2Apr 29, 2023
808b578
Merge remote-tracking branch 'origin/v6' into chore/playground-type-i…
armano2May 18, 2023
969800c
Merge remote-tracking branch 'origin/main' into chore/playground-type…
armano2Aug 15, 2023
af69a7e
fix: correct package file
armano2Aug 15, 2023
ed72ba3
fix: apply changes after merge
armano2Aug 15, 2023
dc87da0
Merge remote-tracking branch 'origin/main' into chore/playground-type…
armano2Aug 21, 2023
f1c0cdb
fix: unify playgroundInfoContainer with tabCode
armano2Aug 21, 2023
df49214
Update packages/website/src/components/typeDetails/TypeInfo.tsx
armano2Aug 21, 2023
fc44aa2
Merge remote-tracking branch 'origin/main' into chore/playground-type…
armano2Aug 21, 2023
70fc82c
Merge branch 'main' into chore/playground-type-inspector
armano2Aug 29, 2023
266f8d1
Merge branch 'main' into chore/playground-type-inspector
armano2Sep 8, 2023
f36c655
Merge branch 'main' into chore/playground-type-inspector
JoshuaKGoldbergSep 11, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletionspackages/website/src/components/Playground.module.css
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -52,6 +52,7 @@

.tabCode {
height: calc(100% - 41px);
overflow: auto;
}

.hidden {
Expand Down
41 changes: 22 additions & 19 deletionspackages/website/src/components/Playground.tsx
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
import type { TSESTree } from '@typescript-eslint/utils';
import clsx from 'clsx';
import type * as ESQuery from 'esquery';
import React, { useCallback, useState } from 'react';
import type { SourceFile } from 'typescript';

import ASTViewer from './ast/ASTViewer';
import ConfigEslint from './config/ConfigEslint';
Expand All@@ -14,17 +12,17 @@ import { ESQueryFilter } from './ESQueryFilter';
import useHashState from './hooks/useHashState';
import EditorTabs from './layout/EditorTabs';
import Loader from './layout/Loader';
import type { UpdateModel } from './linter/types';
import { defaultConfig, detailTabs } from './options';
import OptionsSelector from './OptionsSelector';
import styles from './Playground.module.css';
import ConditionalSplitPane from './SplitPane/ConditionalSplitPane';
import { TypesDetails } from './typeDetails/TypesDetails';
import type { ErrorGroup, RuleDetails, SelectedRange, TabType } from './types';

function Playground(): React.JSX.Element {
const [state, setState] = useHashState(defaultConfig);
const [esAst, setEsAst] = useState<TSESTree.Program | null>();
const [tsAst, setTsAST] = useState<SourceFile | null>();
const [scope, setScope] = useState<Record<string, unknown> | null>();
const [astModel, setAstModel] = useState<UpdateModel>();
const [markers, setMarkers] = useState<ErrorGroup[]>();
const [ruleNames, setRuleNames] = useState<RuleDetails[]>([]);
const [isLoading, setIsLoading] = useState<boolean>(true);
Expand DownExpand Up@@ -62,15 +60,6 @@ function Playground(): React.JSX.Element {
}
}, []);

const astToShow =
state.showAST === 'ts'
? tsAst
: state.showAST === 'scope'
? scope
: state.showAST === 'es'
? esAst
: undefined;

return (
<div className={styles.codeContainer}>
<div className={styles.codeBlocks}>
Expand DownExpand Up@@ -137,9 +126,7 @@ function Playground(): React.JSX.Element {
eslintrc={state.eslintrc}
sourceType={state.sourceType}
showAST={state.showAST}
onEsASTChange={setEsAst}
onTsASTChange={setTsAST}
onScopeChange={setScope}
onASTChange={setAstModel}
onMarkersChange={setMarkers}
selectedRange={selectedRange}
onChange={setState}
Expand DownExpand Up@@ -169,11 +156,27 @@ function Playground(): React.JSX.Element {
value={esQueryError}
/>
)) ||
(state.showAST && astToShow && (
(state.showAST === 'types' && astModel?.storedTsAST && (
<TypesDetails
typeChecker={astModel.typeChecker}
value={astModel.storedTsAST}
onHoverNode={setSelectedRange}
cursorPosition={position}
/>
)) ||
(state.showAST && astModel && (
Comment on lines +159 to +167
Copy link
CollaboratorAuthor

@armano2armano2Apr 4, 2023
edited
Loading

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

i have a feeling that i should extract this to separate component, thisand/or condition chain is getting complex

JoshuaKGoldberg reacted with thumbs up emoji
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

yeah this is probably getting to the point where we might want to extract the condition into a bit of state machine logic which outputs an enum value so that we can simplify the render logic.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Agreed. I don't personally think of it as a blocker but it would be a nice to have. Maybe a good follow-upgood first issue for a potential contributor?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Filed#7630. On second thought I think it's probably not agood first issue given it's touching a bunch of code.

<ASTViewer
key={String(state.showAST)}
filter={state.showAST === 'es' ? esQueryFilter : undefined}
value={astToShow}
value={
state.showAST === 'ts'
? astModel.storedTsAST
: state.showAST === 'scope'
? astModel.storedScope
: state.showAST === 'es'
? astModel.storedAST
: undefined
}
showTokens={state.showTokens}
enableScrolling={state.scroll}
cursorPosition={position}
Expand Down
10 changes: 3 additions & 7 deletionspackages/website/src/components/editor/LoadedEditor.tsx
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -39,9 +39,7 @@ export const LoadedEditor: React.FC<LoadedEditorProps> = ({
eslintrc,
selectedRange,
fileType,
onEsASTChange,
onScopeChange,
onTsASTChange,
onASTChange,
onMarkersChange,
onChange,
onSelect,
Expand DownExpand Up@@ -140,12 +138,10 @@ export const LoadedEditor: React.FC<LoadedEditorProps> = ({

useEffect(() => {
const disposable = webLinter.onParse((uri, model) => {
onEsASTChange(model.storedAST);
onScopeChange(model.storedScope as Record<string, unknown> | undefined);
onTsASTChange(model.storedTsAST);
onASTChange(model);
});
return () => disposable();
}, [webLinter,onEsASTChange, onScopeChange, onTsASTChange]);
}, [webLinter,onASTChange]);

useEffect(() => {
const createRuleUri = (name: string): string =>
Expand Down
8 changes: 2 additions & 6 deletionspackages/website/src/components/editor/types.ts
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,11 @@
import type { TSESTree } from '@typescript-eslint/utils';
import type { SourceFile } from 'typescript';

import type { UpdateModel } from '../linter/types';
import type { ConfigModel, ErrorGroup, SelectedRange, TabType } from '../types';

export interface CommonEditorProps extends ConfigModel {
readonly activeTab: TabType;
readonly selectedRange?: SelectedRange;
readonly onChange: (cfg: Partial<ConfigModel>) => void;
readonly onTsASTChange: (value: SourceFile | undefined) => void;
readonly onEsASTChange: (value: TSESTree.Program | undefined) => void;
readonly onScopeChange: (value: Record<string, unknown> | undefined) => void;
readonly onASTChange: (value: undefined | UpdateModel) => void;
readonly onMarkersChange: (value: ErrorGroup[]) => void;
readonly onSelect: (position?: number) => void;
}
1 change: 1 addition & 0 deletionspackages/website/src/components/hooks/useHashState.ts
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -21,6 +21,7 @@ function readShowAST(value: string | null): ConfigShowAst {
case 'es':
case 'ts':
case 'scope':
case 'types':
return value;
}
return value ? 'es' : false;
Expand Down
2 changes: 1 addition & 1 deletionpackages/website/src/components/linter/types.ts
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -8,7 +8,7 @@ export type { ParseSettings } from '@typescript-eslint/typescript-estree/use-at-

export interface UpdateModel {
storedAST?: TSESTree.Program;
storedTsAST?: ts.SourceFile;
storedTsAST?: ts.Node;
storedScope?: ScopeManager;
typeChecker?: ts.TypeChecker;
}
Expand Down
1 change: 1 addition & 0 deletionspackages/website/src/components/options.ts
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -6,6 +6,7 @@ export const detailTabs: { value: ConfigShowAst; label: string }[] = [
{ value: 'es', label: 'ESTree' },
{ value: 'ts', label: 'TypeScript' },
{ value: 'scope', label: 'Scope' },
{ value: 'types', label: 'Types' },
];

/**
Expand Down
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
import clsx from 'clsx';
import React, { useCallback, useMemo } from 'react';
import type * as ts from 'typescript';

import styles from '../ast/ASTViewer.module.css';
import PropertyName from '../ast/PropertyName';
import { tsEnumToString } from '../ast/tsUtils';
import type { OnHoverNodeFn } from '../ast/types';
import { getRange, isTSNode } from '../ast/utils';

export interface SimplifiedTreeViewProps {
readonly value: ts.Node;
readonly selectedNode: ts.Node | undefined;
readonly onSelect: (value: ts.Node) => void;
readonly onHoverNode?: OnHoverNodeFn;
}

function SimplifiedItem({
value,
onSelect,
selectedNode,
onHoverNode,
}: SimplifiedTreeViewProps): React.JSX.Element {
const items = useMemo(() => {
const result: ts.Node[] = [];
value.forEachChild(child => {
result.push(child);
});
return result;
}, [value]);

const onHover = useCallback(
(v: boolean) => {
if (isTSNode(value) && onHoverNode) {
return onHoverNode(v ? getRange(value, 'tsNode') : undefined);
}
},
[onHoverNode, value],
);

return (
<div className={styles.nonExpand}>
<span className={selectedNode === value ? styles.selected : ''}>
<PropertyName
value={tsEnumToString('SyntaxKind', value.kind)}
className={styles.propName}
onHover={onHover}
onClick={(): void => {
onSelect(value);
}}
/>
</span>

<div className={clsx(styles.subList, 'padding-left--md')}>
{items.map((item, index) => (
<SimplifiedItem
onHoverNode={onHoverNode}
selectedNode={selectedNode}
value={item}
onSelect={onSelect}
key={index.toString()}
/>
))}
</div>
</div>
);
}

export function SimplifiedTreeView(
params: SimplifiedTreeViewProps,
): React.JSX.Element {
return (
<div className={clsx(styles.list, 'padding-left--sm')}>
<SimplifiedItem {...params} />
</div>
);
}
141 changes: 141 additions & 0 deletionspackages/website/src/components/typeDetails/TypeInfo.tsx
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
import React, { useMemo } from 'react';
import type * as ts from 'typescript';

import ASTViewer from '../ast/ASTViewer';
import astStyles from '../ast/ASTViewer.module.css';
import type { OnHoverNodeFn } from '../ast/types';

export interface TypeInfoProps {
readonly value: ts.Node;
readonly typeChecker?: ts.TypeChecker;
readonly onHoverNode?: OnHoverNodeFn;
}

interface InfoModel {
type?: unknown;
typeString?: string;
contextualType?: unknown;
contextualTypeString?: string;
symbol?: unknown;
signature?: unknown;
flowNode?: unknown;
}

interface SimpleFieldProps {
readonly value: string | undefined;
readonly label: string;
}

interface TypeGroupProps {
readonly label: string;
readonly type?: unknown;
readonly string?: string;
readonly onHoverNode?: OnHoverNodeFn;
}

function SimpleField(props: SimpleFieldProps): React.JSX.Element {
return (
<div className={astStyles.list}>
<span className={astStyles.propClass}>{props.label}</span>
<span>: </span>
<span className={astStyles.propString}>{String(props.value)}</span>
</div>
);
}

function TypeGroup(props: TypeGroupProps): React.JSX.Element {
return (
<>
<h4 className="padding--sm margin--none">{props.label}</h4>
{props.type ? (
<>
{props.string && (
<SimpleField value={props.string} label="typeToString()" />
)}
<ASTViewer onHoverNode={props.onHoverNode} value={props.type} />
</>
) : (
<div className={astStyles.list}>None</div>
)}
</>
);
}

export function TypeInfo({
value,
typeChecker,
onHoverNode,
}: TypeInfoProps): React.JSX.Element {
const computed = useMemo(() => {
if (!typeChecker || !value) {
return undefined;
}
const info: InfoModel = {};
try {
const type = typeChecker.getTypeAtLocation(value);
info.type = type;
info.typeString = typeChecker.typeToString(type);
info.symbol = type.getSymbol();
let signature = type.getCallSignatures();
if (signature.length === 0) {
signature = type.getConstructSignatures();
}
info.signature = signature.length > 0 ? signature : undefined;
// @ts-expect-error not part of public api
info.flowNode = value.flowNode ?? value.endFlowNode ?? undefined;
} catch (e: unknown) {
info.type = e;
}
try {
// @ts-expect-error just fail if a node type is not correct
const contextualType = typeChecker.getContextualType(value);
info.contextualType = contextualType;
if (contextualType) {
info.contextualTypeString = typeChecker.typeToString(contextualType);
}
} catch {
info.contextualType = undefined;
}
return info;
}, [value, typeChecker]);

if (!typeChecker || !computed) {
return <div>TypeChecker not available</div>;
}

return (
<div>
<>
<h4 className="padding--sm margin--none">Node</h4>
<ASTViewer onHoverNode={onHoverNode} value={value} />
<TypeGroup
label="Type"
type={computed.type}
string={computed.typeString}
onHoverNode={onHoverNode}
/>
<TypeGroup
label="Contextual Type"
type={computed.contextualType}
string={computed.contextualTypeString}
onHoverNode={onHoverNode}
/>
<TypeGroup
label="Symbol"
type={computed.symbol}
onHoverNode={onHoverNode}
/>
<TypeGroup
label="Signature"
type={computed.signature}
onHoverNode={onHoverNode}
/>
<TypeGroup
label="FlowNode"
type={computed.flowNode}
onHoverNode={onHoverNode}
/>
</>
</div>
);
}
Loading

[8]ページ先頭

©2009-2025 Movatter.jp