packages/plugin-docs/src2/DemoContainer/useEvalCode.tsx (90 lines of code) (raw):
import React, { useEffect, useLayoutEffect, useState } from "react";
import { ErrorBoundary, FallbackProps } from "react-error-boundary";
// @ts-ignore
import * as externaledDeps from "@breezr-doc-internals/externaled-deps";
interface IOpts {
code: string;
deps: any;
enable: boolean;
}
// 如果你的站点包含用户的敏感信息(比如cookie),那么不要用这个方法来eval**未知来源**的代码,以免XSS攻击。
// eval当前用户自己提供的代码是可以的;但是不要在A用户访问站点的时候eval B用户提供的代码。
export function useEvalCode({ code, deps: demoDeps, enable }: IOpts) {
const [transformedCode, setTransformedCode] = useState("");
const [evaluated, setEvaluated] = useState<any>({});
useEffect(() => {
if (!enable) return;
setTransformedCode("");
setEvaluated({});
Promise.all([
import("@babel/standalone"),
]).then(
([
babel,
]) => {
const res = babel
.transform(code, {
presets: [
[
'typescript',
{
isTSX: true,
allExtensions: true,
},
],
],
plugins: ['transform-modules-amd', 'transform-react-jsx'],
})
if (typeof res?.code === "string") {
setTransformedCode(res.code);
}
}
);
}, [code, enable]);
useLayoutEffect(() => {
if (!enable) return;
const exports: any = {};
try {
const fn = new Function("define", transformedCode);
fn(define);
setEvaluated(exports);
} catch (error) {
console.error("error when eval code:");
console.error(error);
}
function define(depsArr, factory) {
const deps = { ...externaledDeps, ...demoDeps };
const depsValue = depsArr.map((depName) => {
if (depName === "exports") {
return exports;
}
if (deps && deps.hasOwnProperty(depName)) {
return deps[depName];
}
throw new Error(`can not find dependency "${depName}"`);
});
factory(...depsValue);
}
}, [transformedCode, demoDeps, enable]);
const renderEvalCode = evaluated.default ? (
<WrapEvaledComponent retryKey={evaluated.default}>
<evaluated.default />
</WrapEvaledComponent>
) : null;
return { value: evaluated, transformedCode, renderEvalCode };
}
const WrapEvaledComponent: React.FC<{ retryKey: any }> = ({
children,
retryKey,
}) => {
return (
<ErrorBoundary FallbackComponent={ErrorFallback} resetKeys={[retryKey]}>
{children}
</ErrorBoundary>
);
};
function ErrorFallback({ error }: FallbackProps) {
return (
<div role="alert">
<p>Error in demo:</p>
{error && <pre>{error.message}</pre>}
</div>
);
}