From 08b945d4fe852ff2fec7803951b1628162ca4ef7 Mon Sep 17 00:00:00 2001 From: Maksim Nesterenko Date: Tue, 22 Jun 2021 15:22:12 +0200 Subject: [PATCH] feat: add transpileOptions prop to LiveProvider Closes #252 --- package.json | 1 + src/components/Live/LiveProvider.js | 9 ++++++--- src/utils/test/transpile.test.js | 17 +++++++++++++++++ src/utils/transpile/index.js | 15 +++++++++++---- src/utils/transpile/transform.js | 20 ++++++++++++-------- typings/react-live.d.ts | 6 +++++- yarn.lock | 7 +++++++ 7 files changed, 59 insertions(+), 16 deletions(-) diff --git a/package.json b/package.json index 9a677126..c9fd1e47 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ "lint": "eslint --config .eslintrc \"./**/*.js\"" }, "dependencies": { + "@types/buble": "^0.20.0", "buble": "0.19.6", "core-js": "^3.14.0", "dom-iterator": "^1.0.0", diff --git a/src/components/Live/LiveProvider.js b/src/components/Live/LiveProvider.js index 386ff4ae..ef904ec9 100644 --- a/src/components/Live/LiveProvider.js +++ b/src/components/Live/LiveProvider.js @@ -12,6 +12,7 @@ function LiveProvider({ disabled, scope, transformCode, + transpileOptions, noInline = false }) { const [state, setState] = useState({ @@ -23,7 +24,8 @@ function LiveProvider({ // Transpilation arguments const input = { code: transformCode ? transformCode(newCode) : newCode, - scope + scope, + transpileOptions }; const errorCallback = error => @@ -45,7 +47,7 @@ function LiveProvider({ useEffect(() => { transpile(code); - }, [code, scope, noInline, transformCode]); + }, [code, scope, noInline, transformCode, transpileOptions]); const onChange = newCode => transpile(newCode); @@ -76,7 +78,8 @@ LiveProvider.propTypes = { noInline: PropTypes.bool, scope: PropTypes.object, theme: PropTypes.object, - transformCode: PropTypes.func + transformCode: PropTypes.node, + transpileOptions: PropTypes.object }; LiveProvider.defaultProps = { diff --git a/src/utils/test/transpile.test.js b/src/utils/test/transpile.test.js index 461b1ebc..854c7b76 100644 --- a/src/utils/test/transpile.test.js +++ b/src/utils/test/transpile.test.js @@ -59,6 +59,23 @@ describe('transpile', () => { expect(wrapper.text()).toBe('Hello World!'); }); + + it('adopts code generation based on transpileOptions', () => { + const code = `(() => { + class Foo { async bar() { await Promise.resolve(1); } } + })();`; + + expect(() => generateElement({ code })).toThrow(); + + expect(() => + generateElement({ + code, + transpileOptions: { + transforms: { classes: false, asyncAwait: false } + } + }) + ).not.toThrow(); + }); }); describe('renderElementAsync', () => { diff --git a/src/utils/transpile/index.js b/src/utils/transpile/index.js index c2ef718a..88e55c0e 100644 --- a/src/utils/transpile/index.js +++ b/src/utils/transpile/index.js @@ -2,17 +2,24 @@ import transform from './transform'; import errorBoundary from './errorBoundary'; import evalCode from './evalCode'; -export const generateElement = ({ code = '', scope = {} }, errorCallback) => { +export const generateElement = ( + { code = '', scope = {}, transpileOptions }, + errorCallback +) => { // NOTE: Remove trailing semicolon to get an actual expression. const codeTrimmed = code.trim().replace(/;$/, ''); // NOTE: Workaround for classes and arrow functions. - const transformed = transform(`return (${codeTrimmed})`).trim(); + const transformed = transform( + `return (${codeTrimmed})`, + transpileOptions + ).trim(); + return errorBoundary(evalCode(transformed, scope), errorCallback); }; export const renderElementAsync = ( - { code = '', scope = {} }, + { code = '', scope = {}, transpileOptions }, resultCallback, errorCallback // eslint-disable-next-line consistent-return @@ -31,5 +38,5 @@ export const renderElementAsync = ( ); } - evalCode(transform(code), { ...scope, render }); + evalCode(transform(code, transpileOptions), { ...scope, render }); }; diff --git a/src/utils/transpile/transform.js b/src/utils/transpile/transform.js index 6412c18f..1a19f7be 100644 --- a/src/utils/transpile/transform.js +++ b/src/utils/transpile/transform.js @@ -3,12 +3,16 @@ import assign from 'core-js/features/object/assign'; export const _poly = { assign }; -const opts = { - objectAssign: '_poly.assign', - transforms: { - dangerousForOf: true, - dangerousTaggedTemplateString: true - } -}; +export default (code, transpileOptions = {}) => { + const opts = { + ...transpileOptions, + objectAssign: '_poly.assign', + transforms: { + dangerousForOf: true, + dangerousTaggedTemplateString: true, + ...transpileOptions.transforms + } + }; -export default code => _transform(code, opts).code; + return _transform(code, opts).code; +}; diff --git a/typings/react-live.d.ts b/typings/react-live.d.ts index 75b53943..23eef692 100644 --- a/typings/react-live.d.ts +++ b/typings/react-live.d.ts @@ -1,5 +1,8 @@ -import { ComponentClass, StatelessComponent, HTMLProps, ComponentType, Context } from 'react' +import { ComponentClass, HTMLProps, ComponentType, Context } from 'react' import { PrismTheme, Language } from 'prism-react-renderer'; +import { TransformOptions } from 'buble'; + +type TranspileOptions = TransformOptions & { transforms: {asyncAwait?: boolean}} // Helper types type Omit = Pick> @@ -14,6 +17,7 @@ export type LiveProviderProps = Omit & { code?: string; noInline?: boolean; transformCode?: (code: string) => string; + transpileOptions?: TranspileOptions; language?: Language; disabled?: boolean; theme?: PrismTheme; diff --git a/yarn.lock b/yarn.lock index 60bc47d6..1b501bfe 100644 --- a/yarn.lock +++ b/yarn.lock @@ -383,6 +383,13 @@ react-split-pane "^0.1.77" react-treebeard "^2.1.0" +"@types/buble@^0.20.0": + version "0.20.0" + resolved "https://registry.yarnpkg.com/@types/buble/-/buble-0.20.0.tgz#8c076a500bb297a3955bd832ae4a1ec5ccf25753" + integrity sha512-tS+DwolzYtDuayJspUABudwjeF8t0mGzchixMDcxN9Vn96IL23AFCsZVoX3gbPkYbZnvD3W+nWCtB/HyxUaeXg== + dependencies: + magic-string "^0.25.0" + "@types/node@*": version "12.7.2" resolved "https://registry.yarnpkg.com/@types/node/-/node-12.7.2.tgz#c4e63af5e8823ce9cc3f0b34f7b998c2171f0c44"