Improved typescript visualizer. Opens browser window in chrome appmode by default.
This commit is contained in:
@@ -70,6 +70,7 @@ export interface TreeNode<TExtraData> {
|
||||
id: string | undefined;
|
||||
name: string;
|
||||
value: string | undefined;
|
||||
emphasizedValue: string | undefined;
|
||||
children: TreeNode<TExtraData>[];
|
||||
data: TExtraData;
|
||||
isMarked: boolean;
|
||||
|
||||
@@ -29,8 +29,9 @@ export class TypeScriptAstDataExtractor
|
||||
var member = object[key];
|
||||
if (member === value) return key;
|
||||
|
||||
if (Array.isArray(member) && member.indexOf(value) !== -1)
|
||||
if (Array.isArray(member) && member.indexOf(value) !== -1) {
|
||||
return key;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
@@ -39,14 +40,14 @@ export class TypeScriptAstDataExtractor
|
||||
function toTreeNode(
|
||||
node: ts.Node,
|
||||
memberName: string,
|
||||
marked: Set<ts.Node>
|
||||
marked: Set<ts.Node>,
|
||||
emphasizedValueFn: (node: ts.Node) => string | undefined
|
||||
): CommonDataTypes.AstData["root"] {
|
||||
const name = tsApi.SyntaxKind[node.kind];
|
||||
const children = node
|
||||
.getChildren()
|
||||
.map((childNode, idx) => {
|
||||
let parentPropertyName = findKey(childNode, node) || "";
|
||||
|
||||
if (childNode.kind == tsApi.SyntaxKind.SyntaxList) {
|
||||
childNode.getChildren().some(c => {
|
||||
parentPropertyName = findKey(c, node) || "";
|
||||
@@ -60,7 +61,12 @@ export class TypeScriptAstDataExtractor
|
||||
parentPropertyName = "" + idx;
|
||||
}
|
||||
|
||||
return toTreeNode(childNode, parentPropertyName, marked);
|
||||
return toTreeNode(
|
||||
childNode,
|
||||
parentPropertyName,
|
||||
marked,
|
||||
emphasizedValueFn
|
||||
);
|
||||
})
|
||||
.filter(c => c !== null);
|
||||
|
||||
@@ -68,6 +74,8 @@ export class TypeScriptAstDataExtractor
|
||||
|
||||
if (tsApi.isIdentifier(node)) {
|
||||
value = node.text;
|
||||
} else if (tsApi.isLiteralExpression(node)) {
|
||||
value = node.text;
|
||||
}
|
||||
|
||||
return {
|
||||
@@ -78,6 +86,7 @@ export class TypeScriptAstDataExtractor
|
||||
length: 0,
|
||||
position: 0,
|
||||
},
|
||||
emphasizedValue: emphasizedValueFn(node),
|
||||
isMarked: marked.has(node),
|
||||
value,
|
||||
// startPos: node.pos,
|
||||
@@ -89,15 +98,36 @@ export class TypeScriptAstDataExtractor
|
||||
return typeof node === "object" && (tsApi as any).isNode(node);
|
||||
}
|
||||
|
||||
if (isNode(data) || (Array.isArray(data) && data.every(isNode))) {
|
||||
if (
|
||||
isNode(data) ||
|
||||
(Array.isArray(data) && data.every(isNode)) ||
|
||||
(typeof data === "object" &&
|
||||
data &&
|
||||
Object.entries(data).every(([k, v]) => k === "fn" || isNode(v)))
|
||||
) {
|
||||
let root: ts.SourceFile;
|
||||
let marked: Set<ts.Node>;
|
||||
let fn: (n: ts.Node) => string | undefined = (n: ts.Node) =>
|
||||
undefined;
|
||||
if (Array.isArray(data)) {
|
||||
root = (data[0] as ts.Node).getSourceFile();
|
||||
marked = new Set(data);
|
||||
} else {
|
||||
} else if (isNode(data)) {
|
||||
root = data.getSourceFile();
|
||||
marked = new Set([data]);
|
||||
} else {
|
||||
marked = new Set();
|
||||
const map = new Map<ts.Node, string>();
|
||||
fn = (n: ts.Node) => map.get(n);
|
||||
for (const [k, v] of Object.entries(data)) {
|
||||
if (k === "fn") {
|
||||
fn = v;
|
||||
} else {
|
||||
root = v.getSourceFile();
|
||||
marked.add(v);
|
||||
map.set(v, k);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
collector.addExtraction({
|
||||
@@ -107,7 +137,7 @@ export class TypeScriptAstDataExtractor
|
||||
extractData() {
|
||||
return {
|
||||
kind: { text: true, tree: true, ast: true },
|
||||
root: toTreeNode(root, "root", marked),
|
||||
root: toTreeNode(root, "root", marked, fn),
|
||||
text: root.text,
|
||||
fileType: "ts",
|
||||
};
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
"@hediet/typed-json-rpc-websocket-server": "^0.7.7",
|
||||
"@types/express": "^4.17.0",
|
||||
"@types/serve-static": "^1.13.2",
|
||||
"chrome-launcher": "^0.11.2",
|
||||
"express": "^4.17.1",
|
||||
"open": "^6.4.0",
|
||||
"serve-static": "^1.14.1",
|
||||
|
||||
@@ -43,7 +43,11 @@ export class ConnectionHandler {
|
||||
);
|
||||
},
|
||||
openInBrowser: async ({}) => {
|
||||
open(server.indexUrl);
|
||||
try {
|
||||
await launchChrome(server.indexUrl);
|
||||
} catch (e) {
|
||||
open(server.indexUrl);
|
||||
}
|
||||
},
|
||||
setPreferredDataExtractor: async ({ dataExtractorId }) => {
|
||||
if (this.watcher) {
|
||||
@@ -70,3 +74,13 @@ export class ConnectionHandler {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
import chromeLauncher = require("chrome-launcher");
|
||||
|
||||
async function launchChrome(url: string): Promise<void> {
|
||||
const chrome = await chromeLauncher.launch({
|
||||
startingUrl: url,
|
||||
// `--window-size=${width},${height}`
|
||||
chromeFlags: ["--app=" + url],
|
||||
});
|
||||
}
|
||||
|
||||
@@ -26,12 +26,11 @@ export class Server {
|
||||
}
|
||||
|
||||
public get indexUrl(): string {
|
||||
return `http://localhost:${this.port}/index.html?serverPort=${
|
||||
this.port
|
||||
}`;
|
||||
const port = process.env.USE_DEV_UI ? 8080 : this.port;
|
||||
return `http://localhost:${port}/index.html?serverPort=${this.port}`;
|
||||
}
|
||||
|
||||
public get mainBundle(): string {
|
||||
public get mainBundleUrl(): string {
|
||||
return `http://localhost:${this.port}/main.js`;
|
||||
}
|
||||
|
||||
|
||||
@@ -53,9 +53,6 @@ export class WebViews {
|
||||
}
|
||||
|
||||
export class WebView {
|
||||
private expression: string | null = null;
|
||||
private lastFrameId: number | null = null;
|
||||
|
||||
constructor(private readonly webviewPanel: WebviewPanel) {}
|
||||
}
|
||||
|
||||
@@ -89,11 +86,9 @@ export function getHtml(server: Server) {
|
||||
</script>
|
||||
${
|
||||
isDev
|
||||
? `<iframe src="http://localhost:8080/?serverPort=${
|
||||
server.port
|
||||
}" tabindex="0"></iframe>`
|
||||
? `<iframe src="${server.indexUrl}"></iframe>`
|
||||
: `<script type="text/javascript" src="${
|
||||
server.mainBundle
|
||||
server.mainBundleUrl
|
||||
}"></script>`
|
||||
}
|
||||
</body>
|
||||
|
||||
@@ -5,7 +5,11 @@ import {
|
||||
registerUpdateReconciler,
|
||||
getReloadCount,
|
||||
} from "@hediet/node-reload";
|
||||
enableHotReload({ entryModule: module, loggingEnabled: true });
|
||||
|
||||
if (process.env.USE_DEV_UI) {
|
||||
enableHotReload({ entryModule: module, loggingEnabled: true });
|
||||
}
|
||||
|
||||
import { Disposable } from "@hediet/std/disposable";
|
||||
|
||||
import { createJsDebuggerSource } from "./DataSource/JsDebuggerSource";
|
||||
@@ -27,9 +31,11 @@ export class Extension {
|
||||
private readonly views = this.dispose.track(new WebViews(this.server));
|
||||
|
||||
constructor() {
|
||||
const i = this.dispose.track(window.createStatusBarItem());
|
||||
i.text = "reload" + getReloadCount(module);
|
||||
i.show();
|
||||
if (getReloadCount(module) > 0) {
|
||||
const i = this.dispose.track(window.createStatusBarItem());
|
||||
i.text = "reload" + getReloadCount(module);
|
||||
i.show();
|
||||
}
|
||||
this.views.createNew();
|
||||
}
|
||||
}
|
||||
|
||||
5
extension/src/types.d.ts
vendored
Normal file
5
extension/src/types.d.ts
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
declare namespace NodeJS {
|
||||
declare interface ProcessEnv {
|
||||
USE_DEV_UI?: "true";
|
||||
}
|
||||
}
|
||||
@@ -92,6 +92,7 @@ export class TreeNodeViewModel {
|
||||
public readonly id: string | undefined,
|
||||
public readonly name: string,
|
||||
public readonly value: string | undefined,
|
||||
public readonly emphasizedValue: string | undefined,
|
||||
public readonly children: TreeNodeViewModel[]
|
||||
) {}
|
||||
|
||||
@@ -167,6 +168,20 @@ export class TreeNodeViewModel {
|
||||
}
|
||||
}
|
||||
|
||||
const isValidFunctionName = (function() {
|
||||
var validName = /^[$A-Z_][0-9A-Z_$]*$/i;
|
||||
var reserved = {
|
||||
abstract: true,
|
||||
boolean: true,
|
||||
// ...
|
||||
with: true,
|
||||
} as any;
|
||||
return function(s: string) {
|
||||
// Ensure a valid name and not reserved.
|
||||
return validName.test(s) && !reserved[s];
|
||||
};
|
||||
})();
|
||||
|
||||
@observer
|
||||
export class TreeWithPathView extends React.Component<{
|
||||
model: TreeViewModel;
|
||||
@@ -178,7 +193,39 @@ export class TreeWithPathView extends React.Component<{
|
||||
<div className="part-path">
|
||||
<span>
|
||||
{model.selected ? (
|
||||
<span>{model.selected.path.join(" / ")}</span>
|
||||
<span>
|
||||
{model.selected.path.reduce((acc, v) => {
|
||||
acc = acc.slice();
|
||||
function add() {
|
||||
acc.push(
|
||||
<span
|
||||
className="part-path-item"
|
||||
key={acc.length}
|
||||
>
|
||||
{v}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
if (isValidFunctionName(v)) {
|
||||
if (acc.length > 0) {
|
||||
acc.push(
|
||||
<span key={acc.length}>.</span>
|
||||
);
|
||||
}
|
||||
add();
|
||||
} else {
|
||||
acc.push(
|
||||
<span key={acc.length}>[</span>
|
||||
);
|
||||
add();
|
||||
acc.push(
|
||||
<span key={acc.length}>]</span>
|
||||
);
|
||||
}
|
||||
|
||||
return acc;
|
||||
}, new Array<React.ReactElement>())}
|
||||
</span>
|
||||
) : (
|
||||
"(Nothing Selected)"
|
||||
)}
|
||||
@@ -428,6 +475,11 @@ export class TreeNodeView extends React.Component<{
|
||||
{model.value && (
|
||||
<span className="part-value">{model.value}</span>
|
||||
)}
|
||||
{model.emphasizedValue && (
|
||||
<span className="part-emphasized-value">
|
||||
{model.emphasizedValue}
|
||||
</span>
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
import { TreeViewModel, TreeNodeViewModel, TreeView } from "./Views";
|
||||
import {
|
||||
TreeViewModel,
|
||||
TreeNodeViewModel,
|
||||
TreeView,
|
||||
TreeWithPathView,
|
||||
} from "./Views";
|
||||
import {
|
||||
CommonDataTypes,
|
||||
ExtractedData,
|
||||
@@ -31,6 +36,7 @@ export function createTreeViewModelFromTreeNodeData(
|
||||
node.id,
|
||||
node.name,
|
||||
node.value,
|
||||
node.emphasizedValue,
|
||||
children
|
||||
);
|
||||
model.isMarked = node.isMarked;
|
||||
@@ -53,7 +59,7 @@ export class TreeVisualizer extends VisualizationProvider {
|
||||
priority: 100,
|
||||
render() {
|
||||
const m = createTreeViewModelFromTreeNodeData(data.root);
|
||||
return <TreeView model={m} />;
|
||||
return <TreeWithPathView model={m} />;
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@@ -58,6 +58,19 @@
|
||||
}
|
||||
|
||||
.part-value {
|
||||
color: darkgreen;
|
||||
text-decoration: underline;
|
||||
|
||||
/*&::before,
|
||||
&::after {
|
||||
content: '"';
|
||||
}*/
|
||||
|
||||
padding: 1px 3px;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.part-emphasized-value {
|
||||
color: darkred;
|
||||
border: black solid 1px;
|
||||
padding: 1px 3px;
|
||||
@@ -99,6 +112,14 @@
|
||||
white-space: pre;
|
||||
padding: 5px 10px;
|
||||
border-bottom: solid gray 1px;
|
||||
|
||||
color: lightgray;
|
||||
|
||||
.part-path-item {
|
||||
color: black;
|
||||
margin-left: 1px;
|
||||
margin-right: 1px;
|
||||
}
|
||||
}
|
||||
.part-tree {
|
||||
overflow: auto;
|
||||
|
||||
@@ -833,6 +833,17 @@ chownr@^1.1.1:
|
||||
resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.2.tgz#a18f1e0b269c8a6a5d3c86eb298beb14c3dd7bf6"
|
||||
integrity sha512-GkfeAQh+QNy3wquu9oIZr6SS5x7wGdSgNQvD10X3r+AZr1Oys22HW8kAmDMvNg2+Dm0TeGaEuO8gFwdBXxwO8A==
|
||||
|
||||
chrome-launcher@^0.11.2:
|
||||
version "0.11.2"
|
||||
resolved "https://registry.yarnpkg.com/chrome-launcher/-/chrome-launcher-0.11.2.tgz#c9a248dbccd3a08565553acf61adff879bcc982c"
|
||||
integrity sha512-jx0kJDCXdB2ARcDMwNCtrf04oY1Up4rOmVu+fqJ5MTPOOIG8EhRcEU9NZfXZc6dMw9FU8o1r21PNp8V2M0zQ+g==
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
is-wsl "^2.1.0"
|
||||
lighthouse-logger "^1.0.0"
|
||||
mkdirp "0.5.1"
|
||||
rimraf "^2.6.1"
|
||||
|
||||
chrome-trace-event@^1.0.0:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.2.tgz#234090ee97c7d4ad1a2c4beae27505deffc608a4"
|
||||
@@ -1059,7 +1070,7 @@ date-now@^0.1.4:
|
||||
resolved "https://registry.yarnpkg.com/date-now/-/date-now-0.1.4.tgz#eaf439fd4d4848ad74e5cc7dbef200672b9e345b"
|
||||
integrity sha1-6vQ5/U1ISK105cx9vvIAZyueNFs=
|
||||
|
||||
debug@2.6.9, debug@^2.2.0, debug@^2.3.3:
|
||||
debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.8:
|
||||
version "2.6.9"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
|
||||
integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==
|
||||
@@ -1981,6 +1992,11 @@ is-wsl@^1.1.0:
|
||||
resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-1.1.0.tgz#1f16e4aa22b04d1336b66188a66af3c600c3a66d"
|
||||
integrity sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=
|
||||
|
||||
is-wsl@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.1.0.tgz#94369bbeb2249ef07b831b1b08590e686330ccbb"
|
||||
integrity sha512-pFTjpv/x5HRj8kbZ/Msxi9VrvtOMRBqaDi3OIcbwPI3OuH+r3lLxVWukLITBaOGJIbA/w2+M1eVmVa4XNQlAmQ==
|
||||
|
||||
isarray@0.0.1:
|
||||
version "0.0.1"
|
||||
resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf"
|
||||
@@ -2079,6 +2095,14 @@ lcid@^2.0.0:
|
||||
dependencies:
|
||||
invert-kv "^2.0.0"
|
||||
|
||||
lighthouse-logger@^1.0.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/lighthouse-logger/-/lighthouse-logger-1.2.0.tgz#b76d56935e9c137e86a04741f6bb9b2776e886ca"
|
||||
integrity sha512-wzUvdIeJZhRsG6gpZfmSCfysaxNEr43i+QT+Hie94wvHDKFLi4n7C2GqZ4sTC+PH5b5iktmXJvU87rWvhP3lHw==
|
||||
dependencies:
|
||||
debug "^2.6.8"
|
||||
marky "^1.2.0"
|
||||
|
||||
loader-runner@^2.3.0:
|
||||
version "2.4.0"
|
||||
resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-2.4.0.tgz#ed47066bfe534d7e84c4c7b9998c2a75607d9357"
|
||||
@@ -2150,6 +2174,11 @@ map-visit@^1.0.0:
|
||||
dependencies:
|
||||
object-visit "^1.0.0"
|
||||
|
||||
marky@^1.2.0:
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/marky/-/marky-1.2.1.tgz#a3fcf82ffd357756b8b8affec9fdbf3a30dc1b02"
|
||||
integrity sha512-md9k+Gxa3qLH6sUKpeC2CNkJK/Ld+bEz5X96nYwloqphQE0CKCVEKco/6jxEZixinqNdz5RFi/KaCyfbMDMAXQ==
|
||||
|
||||
md5.js@^1.3.4:
|
||||
version "1.3.5"
|
||||
resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f"
|
||||
@@ -2306,7 +2335,7 @@ mixin-deep@^1.2.0:
|
||||
for-in "^1.0.2"
|
||||
is-extendable "^1.0.1"
|
||||
|
||||
mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0:
|
||||
mkdirp@0.5.1, mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0:
|
||||
version "0.5.1"
|
||||
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903"
|
||||
integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=
|
||||
|
||||
Reference in New Issue
Block a user