mirror of
https://github.com/bendtherules/ts-transformer-visualizer.git
synced 2026-08-18 22:01:45 +00:00
Move library files to lib/
This commit is contained in:
@@ -0,0 +1,25 @@
|
||||
.enter {
|
||||
opacity: 0.5;
|
||||
transform: translateX(200px);
|
||||
background-color: lightgreen;
|
||||
}
|
||||
.enter.enterActive {
|
||||
opacity: 1;
|
||||
transform: translateX(0);
|
||||
transition: 1s all;
|
||||
}
|
||||
.enterDone {
|
||||
background-color: lightgreen;
|
||||
}
|
||||
.exit {
|
||||
opacity: 1;
|
||||
transform: translateX(0);
|
||||
background-color: pink;
|
||||
}
|
||||
.exit.exitActive {
|
||||
opacity: 0.5;
|
||||
transform: translateX(200px);
|
||||
transition: 1s all;
|
||||
}
|
||||
.exitDone {
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
import React from "react";
|
||||
import { connect } from "react-redux";
|
||||
import * as ts from "typescript";
|
||||
import { CSSTransition, TransitionGroup } from "react-transition-group";
|
||||
|
||||
import { AppState } from "../../store";
|
||||
import { forEachChild, syntaxKindNameMapping, ObjectHash } from "../../utils";
|
||||
|
||||
import styles from "./ASTOutline.css";
|
||||
|
||||
interface ASTOutlineProps {
|
||||
node?: ts.Node;
|
||||
sourceFile?: ts.SourceFile;
|
||||
}
|
||||
|
||||
function ASTOutline(props: ASTOutlineProps) {
|
||||
const { node, sourceFile } = props;
|
||||
if (node === undefined || sourceFile === undefined) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const output = (
|
||||
<>
|
||||
<li>
|
||||
{syntaxKindNameMapping[node.kind]}
|
||||
<ul>
|
||||
<TransitionGroup>
|
||||
{forEachChild(node).map(childNode => (
|
||||
<CSSTransition
|
||||
key={ObjectHash.getHash(childNode)}
|
||||
timeout={1000}
|
||||
classNames={styles}
|
||||
>
|
||||
<ASTOutlineConnected node={childNode} />
|
||||
</CSSTransition>
|
||||
))}
|
||||
</TransitionGroup>
|
||||
</ul>
|
||||
</li>
|
||||
</>
|
||||
);
|
||||
|
||||
if (ts.isSourceFile(node)) {
|
||||
return (
|
||||
<ul>
|
||||
<TransitionGroup>{output}</TransitionGroup>
|
||||
</ul>
|
||||
);
|
||||
} else {
|
||||
return output;
|
||||
}
|
||||
}
|
||||
|
||||
type ASTOutlineStateProps = ASTOutlineProps;
|
||||
|
||||
const mapStateToProps = (state: AppState) => ({
|
||||
node: state.sourceFiles.currentSourceFile,
|
||||
sourceFile: state.sourceFiles.currentSourceFile
|
||||
});
|
||||
// currentSourceFile from ownProps should take preceedence
|
||||
const mergeProps = (
|
||||
stateProps: ASTOutlineStateProps,
|
||||
_dispatchProps: never,
|
||||
ownProps: ASTOutlineProps
|
||||
) => {
|
||||
return {
|
||||
...stateProps,
|
||||
...ownProps
|
||||
};
|
||||
};
|
||||
|
||||
export { ASTOutline as ASTOutlineUnconnected, ASTOutlineProps };
|
||||
const ASTOutlineConnected = connect(
|
||||
mapStateToProps,
|
||||
null,
|
||||
mergeProps
|
||||
)(ASTOutline);
|
||||
export default ASTOutlineConnected;
|
||||
@@ -0,0 +1,7 @@
|
||||
import ASTOutline, {
|
||||
ASTOutlineUnconnected,
|
||||
ASTOutlineProps
|
||||
} from "./ASTOutline";
|
||||
|
||||
export { ASTOutlineUnconnected, ASTOutlineProps };
|
||||
export default ASTOutline;
|
||||
@@ -0,0 +1,43 @@
|
||||
import React from "react";
|
||||
import { connect } from "react-redux";
|
||||
import * as ts from "typescript";
|
||||
import ReactDiffViewer from "react-diff-viewer";
|
||||
|
||||
import { AppState } from "../../store";
|
||||
import { getCodeString } from "../../utils";
|
||||
|
||||
interface CodeOutputProps {
|
||||
inititalSourceFile?: ts.SourceFile;
|
||||
currentSourceFile?: ts.SourceFile;
|
||||
}
|
||||
|
||||
function CodeOutputDiff(props: CodeOutputProps) {
|
||||
const { inititalSourceFile, currentSourceFile } = props;
|
||||
|
||||
if (inititalSourceFile === undefined || currentSourceFile === undefined) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const initialCodeString = getCodeString(inititalSourceFile);
|
||||
const currentCodeString = getCodeString(currentSourceFile);
|
||||
|
||||
return (
|
||||
<ReactDiffViewer
|
||||
oldValue={initialCodeString}
|
||||
newValue={currentCodeString}
|
||||
splitView={true}
|
||||
showDiffOnly={false}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
type CodeOutputStateProps = CodeOutputProps;
|
||||
|
||||
const mapStateToProps = (state: AppState): CodeOutputStateProps => ({
|
||||
...state.sourceFiles
|
||||
});
|
||||
|
||||
const CodeOutputDiffConnected = connect(mapStateToProps)(CodeOutputDiff);
|
||||
export default CodeOutputDiffConnected;
|
||||
|
||||
export { CodeOutputDiff as CodeOutputUnconnected, CodeOutputProps };
|
||||
@@ -0,0 +1,7 @@
|
||||
import CodeOutputDiff, {
|
||||
CodeOutputUnconnected,
|
||||
CodeOutputProps
|
||||
} from "./CodeOutput";
|
||||
|
||||
export default CodeOutputDiff;
|
||||
export { CodeOutputUnconnected, CodeOutputProps };
|
||||
@@ -0,0 +1,65 @@
|
||||
import * as React from "react";
|
||||
import { bindActionCreators } from "redux";
|
||||
import { Provider } from "react-redux";
|
||||
import { Grid, Row, Col } from "react-flexbox-grid";
|
||||
|
||||
import ASTOutline from "./components/ASTOutline";
|
||||
import CodeOutputDiff from "./components/CodeOutput";
|
||||
|
||||
import configureStore from "./store";
|
||||
import {
|
||||
initSourceFile,
|
||||
updateSourceFile,
|
||||
clearSourceFile
|
||||
} from "./store/sourceFiles/actions";
|
||||
|
||||
import styles from "./styles.css";
|
||||
|
||||
const store = configureStore();
|
||||
|
||||
export function ASTVisualizer() {
|
||||
return (
|
||||
<Provider store={store}>
|
||||
<Grid fluid className={styles.wrapperWhole}>
|
||||
<Row>
|
||||
<Col xs={12}>
|
||||
<h1 className={styles.title}>AST Transformation visualizer</h1>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row>
|
||||
<Col xs={12} md={6} className={styles.hideOverflow}>
|
||||
<Row>
|
||||
<h2 className={styles.titleLight}>AST Inline Diff</h2>
|
||||
</Row>
|
||||
<ASTOutline />
|
||||
</Col>
|
||||
<Col xs={12} md={6}>
|
||||
<Row>
|
||||
<h2 className={styles.titleLight}>Code output Diff</h2>
|
||||
</Row>
|
||||
<Row>
|
||||
<Col xs={6} className={styles.titleLight}>Input</Col>
|
||||
<Col xs={6} className={styles.titleLight}>Output</Col>
|
||||
</Row>
|
||||
<CodeOutputDiff />
|
||||
</Col>
|
||||
</Row>
|
||||
</Grid>
|
||||
</Provider>
|
||||
);
|
||||
}
|
||||
|
||||
const {
|
||||
initSourceFile: initSourceFileConnected,
|
||||
updateSourceFile: updateSourceFileConnected,
|
||||
clearSourceFile: clearSourceFileConnected
|
||||
} = bindActionCreators(
|
||||
{ initSourceFile, updateSourceFile, clearSourceFile },
|
||||
store.dispatch
|
||||
);
|
||||
|
||||
export {
|
||||
initSourceFileConnected as initSourceFile,
|
||||
updateSourceFileConnected as updateSourceFile,
|
||||
clearSourceFileConnected as clearSourceFile
|
||||
};
|
||||
@@ -0,0 +1,27 @@
|
||||
import {
|
||||
createStore,
|
||||
combineReducers,
|
||||
applyMiddleware,
|
||||
Middleware
|
||||
} from "redux";
|
||||
import { composeWithDevTools } from "redux-devtools-extension";
|
||||
|
||||
import { sourceFilesReducer } from "./sourceFiles/reducer";
|
||||
|
||||
const rootReducer = combineReducers({
|
||||
sourceFiles: sourceFilesReducer
|
||||
});
|
||||
|
||||
export type AppState = ReturnType<typeof rootReducer>;
|
||||
|
||||
export default function configureStore() {
|
||||
const middlewares: Middleware[] = [];
|
||||
const middleWareEnhancer = applyMiddleware(...middlewares);
|
||||
|
||||
const store = createStore(
|
||||
rootReducer,
|
||||
composeWithDevTools(middleWareEnhancer)
|
||||
);
|
||||
|
||||
return store;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
import * as ts from "typescript";
|
||||
import { INIT_SOURCEFILE, UPDATE_SOURCEFILE, CLEAR_SOURCEFILE } from "./types";
|
||||
|
||||
export function initSourceFile(sourceFile: ts.SourceFile) {
|
||||
return {
|
||||
type: INIT_SOURCEFILE,
|
||||
payload: { sourceFile }
|
||||
};
|
||||
}
|
||||
|
||||
export function updateSourceFile(sourceFile: ts.SourceFile) {
|
||||
return {
|
||||
type: UPDATE_SOURCEFILE,
|
||||
payload: { sourceFile }
|
||||
};
|
||||
}
|
||||
|
||||
export function clearSourceFile() {
|
||||
return {
|
||||
type: CLEAR_SOURCEFILE,
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
import {
|
||||
INIT_SOURCEFILE,
|
||||
UPDATE_SOURCEFILE,
|
||||
CLEAR_SOURCEFILE,
|
||||
SourceFilesState,
|
||||
SourceFilesActionTypes
|
||||
} from "./types";
|
||||
|
||||
const initialState: SourceFilesState = {
|
||||
inititalSourceFile: undefined,
|
||||
currentSourceFile: undefined
|
||||
};
|
||||
|
||||
export function sourceFilesReducer(
|
||||
state = initialState,
|
||||
action: SourceFilesActionTypes
|
||||
): SourceFilesState {
|
||||
switch (action.type) {
|
||||
case INIT_SOURCEFILE:
|
||||
return {
|
||||
...state,
|
||||
inititalSourceFile: action.payload.sourceFile,
|
||||
currentSourceFile: action.payload.sourceFile
|
||||
};
|
||||
case UPDATE_SOURCEFILE:
|
||||
return {
|
||||
...state,
|
||||
currentSourceFile: action.payload.sourceFile
|
||||
};
|
||||
case CLEAR_SOURCEFILE:
|
||||
return initialState;
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
import * as ts from "typescript";
|
||||
|
||||
// Describing the shape of the sourceFile's slice of state
|
||||
export interface SourceFilesState {
|
||||
inititalSourceFile?: ts.SourceFile;
|
||||
currentSourceFile?: ts.SourceFile;
|
||||
}
|
||||
|
||||
// Describing the different ACTION NAMES available
|
||||
export const INIT_SOURCEFILE = "INIT_SOURCEFILE";
|
||||
export const UPDATE_SOURCEFILE = "UPDATE_SOURCEFILE";
|
||||
export const CLEAR_SOURCEFILE = "CLEAR_SOURCEFILE";
|
||||
|
||||
interface InitSourceFileAction {
|
||||
type: typeof INIT_SOURCEFILE;
|
||||
payload: { sourceFile: ts.SourceFile };
|
||||
}
|
||||
|
||||
interface UpdateSourceFileAction {
|
||||
type: typeof UPDATE_SOURCEFILE;
|
||||
payload: { sourceFile: ts.SourceFile };
|
||||
}
|
||||
|
||||
interface ClearSourceFileAction {
|
||||
type: typeof CLEAR_SOURCEFILE;
|
||||
}
|
||||
|
||||
export type SourceFilesActionTypes =
|
||||
| InitSourceFileAction
|
||||
| UpdateSourceFileAction
|
||||
| ClearSourceFileAction;
|
||||
@@ -0,0 +1,20 @@
|
||||
.wrapperWhole {
|
||||
margin: 1em;
|
||||
}
|
||||
|
||||
.title {
|
||||
margin: 8px 0;
|
||||
padding-bottom: 8px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.titleLight {
|
||||
background-color: lightgray;
|
||||
padding: 4px 8px;
|
||||
margin-bottom: 4px;
|
||||
border-bottom: 2px solid darkgray;
|
||||
}
|
||||
|
||||
.hideOverflow {
|
||||
overflow: hidden;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import ExampleComponent from "./";
|
||||
|
||||
describe("ExampleComponent", () => {
|
||||
it("is truthy", () => {
|
||||
expect(ExampleComponent).toBeTruthy();
|
||||
});
|
||||
});
|
||||
Vendored
+18
@@ -0,0 +1,18 @@
|
||||
/**
|
||||
* Default CSS definition for typescript,
|
||||
* will be overridden with file-specific definitions by rollup
|
||||
*/
|
||||
declare module "*.css" {
|
||||
const content: { [className: string]: string };
|
||||
export default content;
|
||||
}
|
||||
|
||||
interface SvgrComponent
|
||||
extends React.StatelessComponent<React.SVGAttributes<SVGElement>> {}
|
||||
|
||||
declare module "*.svg" {
|
||||
const svgUrl: string;
|
||||
const svgComponent: SvgrComponent;
|
||||
export default svgUrl;
|
||||
export { svgComponent as ReactComponent };
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
import * as ts from "typescript";
|
||||
|
||||
// Copied from ts-ast-viewer
|
||||
export function forEachChild(node: ts.Node) {
|
||||
const nodes: ts.Node[] = [];
|
||||
node.forEachChild(child => {
|
||||
nodes.push(child);
|
||||
return undefined;
|
||||
});
|
||||
return nodes;
|
||||
}
|
||||
|
||||
// Copied from ts-ast-viewer
|
||||
function getSyntaxKindNameMapping() {
|
||||
// some SyntaxKinds are repeated, so only use the first one
|
||||
const kindNames: { [kind: number]: string } = {};
|
||||
for (const name of Object.keys(ts.SyntaxKind).filter(k =>
|
||||
isNaN(parseInt(k, 10))
|
||||
)) {
|
||||
const value = (ts.SyntaxKind as any)[name] as number;
|
||||
if (kindNames[value] == null) kindNames[value] = name;
|
||||
}
|
||||
return kindNames;
|
||||
}
|
||||
|
||||
export const syntaxKindNameMapping = getSyntaxKindNameMapping();
|
||||
|
||||
export class ObjectHash {
|
||||
static ObjectIDMap = new WeakMap<Object, number>();
|
||||
static maxID = 0;
|
||||
|
||||
static getHash(obj: Object) {
|
||||
let returnID = ObjectHash.ObjectIDMap.get(obj);
|
||||
if (returnID === undefined) {
|
||||
this.maxID++;
|
||||
returnID = this.maxID;
|
||||
|
||||
this.ObjectIDMap.set(obj, returnID);
|
||||
}
|
||||
|
||||
return returnID;
|
||||
}
|
||||
}
|
||||
|
||||
export function getCodeString(sourceFile?: ts.SourceFile) {
|
||||
if (sourceFile === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const printer = ts.createPrinter();
|
||||
const codeString = printer.printFile(sourceFile);
|
||||
|
||||
return codeString;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user