Skip to content

Commit

Permalink
Add source map debugging to website (#793)
Browse files Browse the repository at this point in the history
Progress toward #541, prep work to help make #759 (and any future source map
work) more manually testable.

This PR splits out token information into a new collapased-by-default debug
section, then adds source map info to that section. The source map toggle does
three things:
* Show the actual mappings from the source map by line/column index.
* Show the JSON value of the source map.
* Extend the normal code output to include an inline source map with the full
  original source embedded, then link to the very helpful tool
  https://evanw.github.io/source-map-visualization/ , which accepts pasting the
  output code.
  • Loading branch information
alangpierce committed Apr 8, 2023
1 parent 99c4f71 commit 7e5b06a
Show file tree
Hide file tree
Showing 13 changed files with 282 additions and 97 deletions.
1 change: 1 addition & 0 deletions website/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"homepage": "https://sucrase.io",
"dependencies": {
"@babel/standalone": "^7.18.5",
"@jridgewell/trace-mapping": "^0.3.17",
"@sucrase/webpack-loader": "^2.0.0",
"@types/base64-js": "^1.2.5",
"@types/gzip-js": "^0.3.1",
Expand Down
56 changes: 41 additions & 15 deletions website/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,35 @@ import {css, StyleSheet} from "aphrodite";
import {Component} from "react";
import {hot} from "react-hot-loader/root";

import CompareOptionsBox from "./CompareOptionsBox";
import {
DEFAULT_DISPLAY_OPTIONS,
DebugOptions,
DEFAULT_DEBUG_OPTIONS,
DEFAULT_COMPARE_OPTIONS,
DEFAULT_OPTIONS,
DisplayOptions,
CompareOptions,
HydratedOptions,
INITIAL_CODE,
} from "./Constants";
import DisplayOptionsBox from "./DisplayOptionsBox";
import DebugOptionsBox from "./DebugOptionsBox";
import EditorWrapper from "./EditorWrapper";
import SucraseOptionsBox from "./SucraseOptionsBox";
import {loadHashState, saveHashState} from "./URLHashState";
import * as WorkerClient from "./WorkerClient";

interface State {
code: string;
displayOptions: DisplayOptions;
sucraseOptions: HydratedOptions;
compareOptions: CompareOptions;
debugOptions: DebugOptions;
sucraseCode: string;
sucraseTimeMs: number | null | "LOADING";
babelCode: string;
babelTimeMs: number | null | "LOADING";
typeScriptCode: string;
typeScriptTimeMs: number | null | "LOADING";
tokensStr: string;
sourceMapStr: string;
showMore: boolean;
babelLoaded: boolean;
typeScriptLoaded: boolean;
Expand All @@ -36,15 +41,17 @@ class App extends Component<unknown, State> {
super(props);
this.state = {
code: INITIAL_CODE,
displayOptions: DEFAULT_DISPLAY_OPTIONS,
sucraseOptions: DEFAULT_OPTIONS,
compareOptions: DEFAULT_COMPARE_OPTIONS,
debugOptions: DEFAULT_DEBUG_OPTIONS,
sucraseCode: "",
sucraseTimeMs: null,
babelCode: "",
babelTimeMs: null,
typeScriptCode: "",
typeScriptTimeMs: null,
tokensStr: "",
sourceMapStr: "",
showMore: false,
babelLoaded: false,
typeScriptLoaded: false,
Expand All @@ -65,7 +72,8 @@ class App extends Component<unknown, State> {
code: this.state.code,
compressedCode,
sucraseOptions: this.state.sucraseOptions,
displayOptions: this.state.displayOptions,
compareOptions: this.state.compareOptions,
debugOptions: this.state.debugOptions,
});
},
});
Expand All @@ -76,7 +84,8 @@ class App extends Component<unknown, State> {
if (
this.state.code !== prevState.code ||
this.state.sucraseOptions !== prevState.sucraseOptions ||
this.state.displayOptions !== prevState.displayOptions ||
this.state.compareOptions !== prevState.compareOptions ||
this.state.debugOptions !== prevState.debugOptions ||
this.state.babelLoaded !== prevState.babelLoaded ||
this.state.typeScriptLoaded !== prevState.typeScriptLoaded
) {
Expand All @@ -89,7 +98,8 @@ class App extends Component<unknown, State> {
WorkerClient.updateConfig({
code: this.state.code,
sucraseOptions: this.state.sucraseOptions,
displayOptions: this.state.displayOptions,
compareOptions: this.state.compareOptions,
debugOptions: this.state.debugOptions,
});
}

Expand All @@ -108,6 +118,7 @@ class App extends Component<unknown, State> {
typeScriptCode,
typeScriptTimeMs,
tokensStr,
sourceMapStr,
} = this.state;
return (
<div className={css(styles.app)}>
Expand All @@ -126,10 +137,16 @@ class App extends Component<unknown, State> {
this.setState({sucraseOptions});
}}
/>
<DisplayOptionsBox
displayOptions={this.state.displayOptions}
onUpdateDisplayOptions={(displayOptions: DisplayOptions) => {
this.setState({displayOptions});
<CompareOptionsBox
compareOptions={this.state.compareOptions}
onUpdateCompareOptions={(compareOptions: CompareOptions) => {
this.setState({compareOptions});
}}
/>
<DebugOptionsBox
debugOptions={this.state.debugOptions}
onUpdateDebugOptions={(debugOptions: DebugOptions) => {
this.setState({debugOptions});
}}
/>
</div>
Expand All @@ -148,7 +165,7 @@ class App extends Component<unknown, State> {
isReadOnly={true}
babelLoaded={this.state.babelLoaded}
/>
{this.state.displayOptions.compareWithBabel && (
{this.state.compareOptions.compareWithBabel && (
<EditorWrapper
label="Transformed with Babel"
code={babelCode}
Expand All @@ -157,7 +174,7 @@ class App extends Component<unknown, State> {
babelLoaded={this.state.babelLoaded}
/>
)}
{this.state.displayOptions.compareWithTypeScript && (
{this.state.compareOptions.compareWithTypeScript && (
<EditorWrapper
label="Transformed with TypeScript"
code={typeScriptCode}
Expand All @@ -166,7 +183,7 @@ class App extends Component<unknown, State> {
babelLoaded={this.state.babelLoaded}
/>
)}
{this.state.displayOptions.showTokens && (
{this.state.debugOptions.showTokens && (
<EditorWrapper
label="Tokens"
code={tokensStr}
Expand All @@ -178,6 +195,15 @@ class App extends Component<unknown, State> {
babelLoaded={this.state.babelLoaded}
/>
)}
{this.state.debugOptions.showSourceMap && (
<EditorWrapper
label="Source Map"
code={sourceMapStr}
isReadOnly={true}
isPlaintext={true}
babelLoaded={this.state.babelLoaded}
/>
)}
</div>
<span className={css(styles.footer)}>
<a className={css(styles.link)} href="https://www.npmjs.com/package/sucrase">
Expand Down
49 changes: 49 additions & 0 deletions website/src/CompareOptionsBox.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import {css, StyleSheet} from "aphrodite";

import CheckBox from "./CheckBox";
import type {CompareOptions} from "./Constants";
import OptionsBox from "./OptionsBox";

interface CompareOptionsBoxProps {
compareOptions: CompareOptions;
onUpdateCompareOptions: (compareOptions: CompareOptions) => void;
}

export default function CompareOptionsBox({
compareOptions,
onUpdateCompareOptions,
}: CompareOptionsBoxProps): JSX.Element {
return (
<OptionsBox>
<div className={css(styles.optionBox)}>
<span className={css(styles.title)}>Compare</span>
<CheckBox
label="Babel"
checked={compareOptions.compareWithBabel}
onChange={(checked) => {
onUpdateCompareOptions({...compareOptions, compareWithBabel: checked});
}}
/>
<CheckBox
label="TypeScript"
checked={compareOptions.compareWithTypeScript}
onChange={(checked) => {
onUpdateCompareOptions({...compareOptions, compareWithTypeScript: checked});
}}
/>
</div>
</OptionsBox>
);
}

const styles = StyleSheet.create({
optionBox: {
display: "flex",
flexWrap: "wrap",
alignItems: "center",
},
title: {
fontSize: "1.2em",
padding: 6,
},
});
12 changes: 10 additions & 2 deletions website/src/Constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,14 +65,22 @@ export const DEFAULT_OPTIONS: HydratedOptions = {
enableLegacyBabel5ModuleInterop: false,
};

export interface DisplayOptions {
export interface CompareOptions {
compareWithBabel: boolean;
compareWithTypeScript: boolean;
}

export interface DebugOptions {
showTokens: boolean;
showSourceMap: boolean;
}

export const DEFAULT_DISPLAY_OPTIONS: DisplayOptions = {
export const DEFAULT_COMPARE_OPTIONS: CompareOptions = {
compareWithBabel: true,
compareWithTypeScript: false,
};

export const DEFAULT_DEBUG_OPTIONS: DebugOptions = {
showTokens: false,
showSourceMap: false,
};
70 changes: 70 additions & 0 deletions website/src/DebugOptionsBox.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import {css, StyleSheet} from "aphrodite";
import {useState} from "react";

import CheckBox from "./CheckBox";
import type {DebugOptions} from "./Constants";
import OptionsBox from "./OptionsBox";

interface DebugOptionsBoxProps {
debugOptions: DebugOptions;
onUpdateDebugOptions: (debugOptions: DebugOptions) => void;
}

export default function DebugOptionsBox({
debugOptions,
onUpdateDebugOptions,
}: DebugOptionsBoxProps): JSX.Element {
const [enabled, setEnabled] = useState(false);
if (!enabled) {
return (
<a
href="#debug"
className={css(styles.link)}
onClick={(e) => {
setEnabled(true);
e.preventDefault();
}}
>
Debug...
</a>
);
}
return (
<OptionsBox>
<div className={css(styles.optionBox)}>
<span className={css(styles.title)}>Debug</span>
<CheckBox
label="Tokens"
checked={debugOptions.showTokens}
onChange={(checked) => {
onUpdateDebugOptions({...debugOptions, showTokens: checked});
}}
/>
<CheckBox
label="Source Map"
checked={debugOptions.showSourceMap}
onChange={(checked) => {
onUpdateDebugOptions({...debugOptions, showSourceMap: checked});
}}
/>
</div>
</OptionsBox>
);
}

const styles = StyleSheet.create({
optionBox: {
display: "flex",
flexWrap: "wrap",
alignItems: "center",
},
link: {
color: "#CCCCCC",
marginLeft: 6,
marginRight: 6,
},
title: {
fontSize: "1.2em",
padding: 6,
},
});
56 changes: 0 additions & 56 deletions website/src/DisplayOptionsBox.tsx

This file was deleted.

0 comments on commit 7e5b06a

Please sign in to comment.