Skip to content

Commit

Permalink
improve(transform): use sucrase instead of @swc/wasm-web to improve t…
Browse files Browse the repository at this point in the history
…ranscoding performance (#38)
  • Loading branch information
simonguo authored Aug 1, 2022
1 parent e4b58de commit 23effcc
Show file tree
Hide file tree
Showing 7 changed files with 84 additions and 68 deletions.
3 changes: 1 addition & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,12 +99,11 @@ The source code is written in markdown, refer to [example.md](https://raw.github
| afterCompile | (code: string) => string | | Executed after compiling the code |
| beforeCompile | (code: string) => string | | Executed before compiling the code |
| children | any | | The code to be rendered is executed. Usually imported via markdown-loader |
| compiler | (code: string) => string | | A compiler that transforms the code. Use swc.transformSync by default |
| dependencies | object | | Dependent objects required by the executed code |
| editable | boolean | false | Renders a code editor that can modify the source code |
| editor | object | | Editor properties |
| onChange | (code?: string) => void | | Callback triggered after code change |
| renderToolbar | (buttons: ReactNode) => ReactNode | | Customize the rendering toolbar |
| sourceCode | string | | The code to be rendered is executed |
| theme | 'light' , 'dark' | 'light' | Code editor theme, applied to CodeMirror |
| compileOptions | object | defaultTransformOptions | swc configuration https://swc.rs/docs/configuration/compilation |
| compileOptions | object | defaultTransformOptions | https://github.com/alangpierce/sucrase#transforms |
2 changes: 1 addition & 1 deletion docs/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ const App = () => {
theme="dark"
dependencies={{ Button }}
afterCompile={(code: string) => {
return code.replace(/import\ [\w\,\{\}\ ]+\ from\ "[\w-]+";/gi, '');
return code.replace(/import\ [\*\w\,\{\}\ ]+\ from\ ?[\."'@/\w-]+;/gi, '');
}}
>
{example}
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,13 @@
"dependencies": {
"@babel/runtime": "^7.18.6",
"@rsuite/icons": "^1.0.2",
"@swc/wasm-web": "^1.2.208",
"@types/codemirror": "5.60.5",
"classnames": "^2.2.5",
"codemirror": "5.65.6",
"highlight.js": "^11.5.1",
"html-loader": "^3.1.2",
"marked": "^4.0.17"
"marked": "^4.0.17",
"sucrase": "^3.24.0"
},
"peerDependencies": {
"react": ">=16.8.0",
Expand Down
64 changes: 55 additions & 9 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 0 additions & 2 deletions src/CodeView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ const CodeView = React.forwardRef((props: CodeViewProps, ref: React.Ref<HTMLDivE
renderToolbar,
onChange,
beforeCompile,
compiler,
afterCompile,
...rest
} = props;
Expand All @@ -47,7 +46,6 @@ const CodeView = React.forwardRef((props: CodeViewProps, ref: React.Ref<HTMLDivE
renderToolbar={renderToolbar}
onChange={onChange}
beforeCompile={beforeCompile}
compiler={compiler}
afterCompile={afterCompile}
editor={editor}
/>
Expand Down
70 changes: 18 additions & 52 deletions src/Renderer.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
/* eslint-disable @typescript-eslint/no-var-requires */
import { useEffect, useState, useCallback, useRef } from 'react';
import { useEffect, useState, useCallback } from 'react';
import CodeIcon from '@rsuite/icons/Code';
import classNames from 'classnames';
import { transform as transformCode, Options } from 'sucrase';
import CodeEditor from './CodeEditor';
import Preview from './Preview';
import canUseDOM from './utils/canUseDOM';
import evalCode from './utils/evalCode';

const React = require('react');
const ReactDOM = require('react-dom');
Expand Down Expand Up @@ -37,38 +39,24 @@ export interface RendererProps extends Omit<React.HTMLAttributes<HTMLElement>, '
};

/**
* swc configuration
* https://swc.rs/docs/configuration/compilation
* https://github.com/alangpierce/sucrase#transforms
*/
transformOptions?: object;
transformOptions?: Options;

/** Customize the rendering toolbar */
renderToolbar?: (buttons: React.ReactNode) => React.ReactNode;

/** Callback triggered after code change */
onChange?: (code?: string) => void;

/**
* A compiler that transforms the code. Use swc.transformSync by default
* See https://swc.rs/docs/usage/wasm
*/
compiler?: (code: string) => string;

/** Executed before compiling the code */
beforeCompile?: (code: string) => string;

/** Executed after compiling the code */
afterCompile?: (code: string) => string;
}

const defaultTransformOptions = {
jsc: {
parser: {
syntax: 'ecmascript',
jsx: true
}
}
};
const defaultTransformOptions: Options = { transforms: ['jsx'] };

const Renderer = React.forwardRef((props: RendererProps, ref: React.Ref<HTMLDivElement>) => {
const {
Expand All @@ -81,7 +69,6 @@ const Renderer = React.forwardRef((props: RendererProps, ref: React.Ref<HTMLDivE
renderToolbar,
onChange,
beforeCompile,
compiler,
afterCompile,
...rest
} = props;
Expand All @@ -94,21 +81,6 @@ const Renderer = React.forwardRef((props: RendererProps, ref: React.Ref<HTMLDivE
...editorProps
} = editor;

const [initialized, setInitialized] = useState(false);
const transfrom = useRef<any>(null);

useEffect(() => {
if (!canUseDOM) {
return;
}

import('@swc/wasm-web').then(async module => {
await module.default();
transfrom.current = module.transformSync;
setInitialized(true);
});
}, []);

const [editable, setEditable] = useState(isEditable);
const [hasError, setHasError] = useState(false);
const [errorMessage, setErrorMessage] = useState(null);
Expand Down Expand Up @@ -139,18 +111,16 @@ const Renderer = React.forwardRef((props: RendererProps, ref: React.Ref<HTMLDivE
};

try {
const statement = dependencies
? Object.keys(dependencies).map(key => `var ${key}= dependencies.${key};`)
: [];

const beforeCompileCode = beforeCompile?.(pendCode) || pendCode;

if (beforeCompileCode) {
const { code: compiledCode } = compiler
? compiler(beforeCompileCode)
: transfrom.current?.(beforeCompileCode, transformOptions);
const { code: compiledCode } = transformCode(beforeCompileCode, transformOptions);

eval(`${statement.join('\n')} ${afterCompile?.(compiledCode) || compiledCode}`);
evalCode(afterCompile?.(compiledCode) || compiledCode, {
React,
ReactDOM,
...dependencies
});
}
} catch (err) {
console.error(err);
Expand All @@ -159,26 +129,22 @@ const Renderer = React.forwardRef((props: RendererProps, ref: React.Ref<HTMLDivE
ReactDOM.render = originalRender;
}
},
[code, dependencies, beforeCompile, compiler, transformOptions, afterCompile]
[code, dependencies, beforeCompile, transformOptions, afterCompile]
);

useEffect(() => {
if (initialized) {
executeCode(code);
}
}, [initialized, code, executeCode]);
executeCode(code);
}, [code, executeCode]);

const handleCodeChange = useCallback(
(code?: string) => {
setHasError(false);
setErrorMessage(null);
onChange?.(code);

if (initialized) {
executeCode(code);
}
executeCode(code);
},
[executeCode, initialized, onChange]
[executeCode, onChange]
);

const codeButton = (
Expand All @@ -197,7 +163,7 @@ const Renderer = React.forwardRef((props: RendererProps, ref: React.Ref<HTMLDivE
</button>
);

const showCodeEditor = editable && code && initialized;
const showCodeEditor = editable && code;

return (
<div className="rcv-container" {...rest} ref={ref}>
Expand Down
7 changes: 7 additions & 0 deletions src/utils/evalCode.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
const evalCode = (code, scope) => {
const scopeKeys = Object.keys(scope);
const scopeValues = scopeKeys.map(key => scope[key]);
return new Function(...scopeKeys, code)(...scopeValues);
};

export default evalCode;

0 comments on commit 23effcc

Please sign in to comment.