mirror of
https://github.com/bendtherules/ts-transformer-visualizer.git
synced 2026-08-18 13:51:48 +00:00
Move library files to lib/
This commit is contained in:
@@ -0,0 +1,4 @@
|
||||
language: node_js
|
||||
node_js:
|
||||
- 9
|
||||
- 8
|
||||
@@ -0,0 +1,31 @@
|
||||
# ts-transformer-visualizer
|
||||
|
||||
> Visualize AST changes caused by a typescript transformer, live. Very helpful for demo.
|
||||
|
||||
[](https://www.npmjs.com/package/ts-transformer-visualizer) [](https://standardjs.com)
|
||||
|
||||
## Install
|
||||
|
||||
```bash
|
||||
npm install --save ts-transformer-visualizer
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
```tsx
|
||||
import * as React from 'react'
|
||||
|
||||
import MyComponent from 'ts-transformer-visualizer'
|
||||
|
||||
class Example extends React.Component {
|
||||
render () {
|
||||
return (
|
||||
<MyComponent />
|
||||
)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
MIT © [bendtherules](https://github.com/bendtherules)
|
||||
@@ -0,0 +1,65 @@
|
||||
{
|
||||
"name": "ts-transformer-visualizer",
|
||||
"version": "1.0.0",
|
||||
"description": "Visualize AST changes caused by a typescript transformer, live. Very helpful for demo.",
|
||||
"author": "bendtherules",
|
||||
"license": "MIT",
|
||||
"repository": "bendtherules/ts-transformer-visualizer",
|
||||
"main": "dist/index.js",
|
||||
"module": "dist/index.es.js",
|
||||
"jsnext:main": "dist/index.es.js",
|
||||
"engines": {
|
||||
"node": ">=8",
|
||||
"npm": ">=5"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "cross-env CI=1 react-scripts-ts test --env=jsdom",
|
||||
"test:watch": "react-scripts-ts test --env=jsdom",
|
||||
"build": "rollup -c",
|
||||
"start": "rollup -c -w",
|
||||
"prepare": "yarn run build",
|
||||
"predeploy": "cd example && yarn install && yarn run build",
|
||||
"deploy": "gh-pages -d example/build",
|
||||
"format": "prettier --write \"{src,example/src}/**/*.{js,ts}*\""
|
||||
},
|
||||
"dependencies": {
|
||||
"react-diff-viewer": "3.0.1",
|
||||
"react-flexbox-grid": "^2.1.2",
|
||||
"react-redux": "^7.1.3",
|
||||
"react-transition-group": "^4.3.0",
|
||||
"redux": "^4.0.5",
|
||||
"redux-devtools-extension": "^2.13.8"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0",
|
||||
"react-dom": "^16.8.0",
|
||||
"typescript": "^3.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@svgr/rollup": "^2.4.1",
|
||||
"@types/jest": "^23.1.5",
|
||||
"@types/react": "^16.3.13",
|
||||
"@types/react-dom": "^16.0.5",
|
||||
"@types/react-redux": "^7.1.7",
|
||||
"@types/react-transition-group": "^4.2.3",
|
||||
"babel-core": "^6.26.3",
|
||||
"babel-runtime": "^6.26.0",
|
||||
"cross-env": "^5.1.4",
|
||||
"gh-pages": "^1.2.0",
|
||||
"prettier": "^1.19.1",
|
||||
"prop-types": "^15.7.2",
|
||||
"react-scripts-ts": "^3.1.0",
|
||||
"rollup": "^0.62.0",
|
||||
"rollup-plugin-babel": "^3.0.7",
|
||||
"rollup-plugin-commonjs": "^9.1.3",
|
||||
"rollup-plugin-node-resolve": "^3.3.0",
|
||||
"rollup-plugin-peer-deps-external": "^2.2.0",
|
||||
"rollup-plugin-postcss": "^1.6.2",
|
||||
"rollup-plugin-typescript2": "^0.17.0",
|
||||
"rollup-plugin-url": "^1.4.0",
|
||||
"typescript": "^3.7.4"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
import typescript from "rollup-plugin-typescript2";
|
||||
import commonjs from "rollup-plugin-commonjs";
|
||||
import external from "rollup-plugin-peer-deps-external";
|
||||
// import postcss from 'rollup-plugin-postcss-modules'
|
||||
import postcss from "rollup-plugin-postcss";
|
||||
import resolve from "rollup-plugin-node-resolve";
|
||||
import url from "rollup-plugin-url";
|
||||
import svgr from "@svgr/rollup";
|
||||
|
||||
import pkg from "./package.json";
|
||||
|
||||
export default {
|
||||
input: "src/index.tsx",
|
||||
output: [
|
||||
{
|
||||
file: pkg.main,
|
||||
format: "cjs",
|
||||
exports: "named",
|
||||
sourcemap: true
|
||||
},
|
||||
{
|
||||
file: pkg.module,
|
||||
format: "es",
|
||||
exports: "named",
|
||||
sourcemap: true
|
||||
}
|
||||
],
|
||||
plugins: [
|
||||
external(),
|
||||
postcss({
|
||||
modules: true
|
||||
}),
|
||||
url(),
|
||||
svgr(),
|
||||
resolve(),
|
||||
typescript({
|
||||
rollupCommonJSResolveHack: true,
|
||||
clean: true
|
||||
}),
|
||||
commonjs({
|
||||
namedExports: {
|
||||
// left-hand side can be an absolute path, a path
|
||||
// relative to the current directory, or the name
|
||||
// of a module in node_modules
|
||||
"react-is": ["isValidElementType", "isContextConsumer"]
|
||||
}
|
||||
})
|
||||
]
|
||||
};
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"outDir": "build",
|
||||
"module": "esnext",
|
||||
"target": "es5",
|
||||
"lib": ["es6", "dom", "es2016", "es2017"],
|
||||
"sourceMap": true,
|
||||
"allowJs": false,
|
||||
"jsx": "react",
|
||||
"declaration": true,
|
||||
"moduleResolution": "node",
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"noImplicitReturns": true,
|
||||
"noImplicitThis": true,
|
||||
"noImplicitAny": true,
|
||||
"strictNullChecks": true,
|
||||
"suppressImplicitAnyIndexErrors": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"allowSyntheticDefaultImports": true
|
||||
},
|
||||
"include": ["src"],
|
||||
"exclude": ["node_modules", "build", "dist", "example", "rollup.config.js"]
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"module": "commonjs"
|
||||
}
|
||||
}
|
||||
+10674
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user