Skip to content

Commit aa6b4ef

Browse files
fix new changes and complete examples
1 parent b02953d commit aa6b4ef

File tree

9 files changed

+89
-68
lines changed

9 files changed

+89
-68
lines changed

example/stories/env-preset/README.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
```jsx
2+
import StringToReactComponent from 'string-to-react-component';
3+
function App() {
4+
return (
5+
<StringToReactComponent babelOptions={{presets: ['react', ['env', {modules: false}]]}}>
6+
{`(props)=>{
7+
const [counter,setCounter]=React.useState(0);
8+
const increase=()=>{
9+
setCounter(counter+1);
10+
};
11+
return (<>
12+
<button onClick={increase}>+</button>
13+
<span style={{padding:'0px 10px'}}>{'count : '+ counter}</span>
14+
</>);
15+
}`}
16+
</StringToReactComponent>
17+
);
18+
}
19+
<App />;
20+
```

example/stories/typescript/README.md

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,10 @@ import StringToReactComponent from 'string-to-react-component';
44
function App() {
55
return (
66
<StringToReactComponent
7-
babelOptions={{filename: 'counter.ts', presets: [['typescript', {allExtensions: true, isTSX: true}]]}}>
8-
{`(props)=>{
7+
babelOptions={{filename: 'counter.ts', presets: ['react', ['typescript', {allExtensions: true, isTSX: true}]]}}>
8+
{`function (props:any):React.ReactElement{
99
const [counter,setCounter]=React.useState<number>(0);
1010
const increase=()=>{
11-
1211
setCounter(counter+1);
1312
};
1413
return (<>

example/stories/usage/README.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,8 @@ function App() {
44
return (
55
<StringToReactComponent>
66
{`(props)=>{
7-
const [counter,setCounter]=React.useState(0);
7+
const [counter,setCounter]=React.useState(0);//by default React object is emmbed into your string code
88
const increase=()=>{
9-
109
setCounter(counter+1);
1110
};
1211
return (<>

src/ctx.tsx

Lines changed: 48 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,15 @@ import type {TBabel, TReact, IStringToReactApi} from './types.d';
33
import {FC} from 'react';
44
class Ctx implements IStringToReactApi {
55
_temp: string = '';
6-
_parentTemp: string = `"use strict";\nreturn @temp;`;
6+
_blob: Blob | undefined = undefined;
7+
_rerender: (state: {}) => void = () => {};
78
_com: FC = function () {
89
return null;
910
};
1011
_getBabel: () => TBabel;
1112
_getReact: () => TReact;
12-
constructor(React: TReact, Babel: TBabel) {
13+
constructor(React: TReact, Babel: TBabel, rerender: (state: {}) => void) {
14+
this._rerender = rerender;
1315
this._getReact = () => React;
1416
if (!Babel) {
1517
throw new Error(
@@ -22,6 +24,9 @@ class Ctx implements IStringToReactApi {
2224
if (Object.prototype.toString.call(babelOptions) !== '[object Object]') {
2325
throw new Error(`babelOptions prop of string-to-react-component element should be an object.`);
2426
}
27+
if (Object.prototype.hasOwnProperty.call(babelOptions, 'sourceMaps') === false) {
28+
babelOptions.sourceMaps = 'inline';
29+
}
2530
if (Object.prototype.hasOwnProperty.call(babelOptions, 'presets') === false) {
2631
babelOptions.presets = ['react'];
2732
} else {
@@ -34,49 +39,44 @@ class Ctx implements IStringToReactApi {
3439
}
3540
}
3641
}
37-
getModule(code: string, babelOptions: TransformOptions): Promise<any> {
38-
this._checkBabelOptions(babelOptions);
39-
code = `import React from "react";\nexport default ${code}`;
40-
const resultObj = this._getBabel().transform(code, babelOptions);
41-
// 1. Define your module code as a string
42-
code = resultObj.code || '';
43-
code = code
42+
_prependCode(template: string): IStringToReactApi {
43+
this._temp = `import React from "react";\nexport default ${template}`;
44+
return this;
45+
}
46+
_postpendCode(): string {
47+
return this._temp
4448
.replace('export default', 'export default (React)=>')
4549
.replace('import React from "react";', '//import React from "react";');
46-
47-
// 2. Create a Blob containing the module code
48-
const blob = new Blob([code], {type: 'application/javascript'});
49-
// 3. Create a URL for the Blob
50+
}
51+
_getBlob(temp: string): Blob {
52+
return new Blob([temp], {type: 'application/javascript'});
53+
}
54+
_getModule(blob: Blob): Promise<FC> {
5055
const moduleUrl = URL.createObjectURL(blob);
51-
// 4. Dynamically import the module using import()
5256
return import(/* webpackIgnore: true */ moduleUrl)
5357
.then((module) => {
54-
// Clean up by revoking the object URL
5558
URL.revokeObjectURL(moduleUrl);
56-
5759
return Promise.resolve((module?.default || module)(this._getReact()));
5860
})
5961
.catch((error) => {
60-
console.error('Error loading module:', error);
62+
URL.revokeObjectURL(moduleUrl);
63+
const errorTitle: string = 'string-to-react-component loading module is failed:';
64+
console.error(errorTitle, error);
65+
throw new Error(errorTitle);
6166
});
6267
}
63-
_transpile(babelOptions: TransformOptions): string {
64-
// make sure react presets is registered in babelOptions
68+
_transpile(babelOptions: TransformOptions): IStringToReactApi {
6569
this._checkBabelOptions(babelOptions);
6670
const resultObj = this._getBabel().transform(this._temp, babelOptions);
67-
const filename = babelOptions.filename;
6871
let code = resultObj.code;
69-
if (filename) {
70-
code = resultObj.code + `\n//# sourceURL=${filename}`;
71-
}
72-
return code || 'null';
73-
}
74-
_generateCom(babelOptions: any) {
75-
this._com = Function(this._parentTemp.replace('@temp', this._transpile(babelOptions)))();
76-
this._validateCodeInsideTheTemp();
72+
// if (babelOptions.filename) {
73+
// code = resultObj.code + `\n//# sourceURL=${babelOptions.filename}`;
74+
// }
75+
this._temp = code || 'null';
76+
return this;
7777
}
78-
_validateCodeInsideTheTemp() {
79-
if (typeof this._com !== 'function') {
78+
_validateCodeInsideTheTemp(com: any): void {
79+
if (typeof com !== 'function') {
8080
throw new Error(`code inside the passed string into string-to-react-component, should be a function`);
8181
}
8282
}
@@ -88,13 +88,26 @@ class Ctx implements IStringToReactApi {
8888
throw new Error(`passed string into string-to-react-component element can not be empty`);
8989
}
9090
}
91-
updateTemplate(template: string, babelOptions: TransformOptions): IStringToReactApi {
92-
this._validateTemplate(template);
91+
/** update transpiled code */
92+
_updateTemplate(template: string, babelOptions: TransformOptions): string {
9393
if (template !== this._temp) {
94-
this._temp = template;
95-
this._generateCom(babelOptions);
94+
this._validateTemplate(template);
95+
return this._prependCode(template)._transpile(babelOptions)._postpendCode();
9696
}
97-
return this;
97+
return this._temp;
98+
}
99+
update(template: string, babelOptions: TransformOptions): void {
100+
this._updateComponent(this._updateTemplate(template, babelOptions), babelOptions);
101+
}
102+
_onChangeComponent(): void {
103+
this._rerender({});
104+
}
105+
_updateComponent(template: string, babelOptions: TransformOptions): void {
106+
this._getModule(this._getBlob(template)).then((com: FC) => {
107+
this._validateCodeInsideTheTemp(com);
108+
this._com = com;
109+
this._onChangeComponent();
110+
});
98111
}
99112
getComponent(): FC {
100113
return this._com;

src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,5 @@ import Ctx from './ctx';
33
import * as Babel from '@babel/standalone';
44
import type { TBabel, TReact, IStringToReactApi } from './types.d';
55
import StringToReact from './strintToReact';
6-
const getCtx: (React: TReact, Babel: TBabel) => IStringToReactApi = (React: TReact, Babel: TBabel) => new Ctx(React, Babel);
6+
const getCtx: (React: TReact, Babel: TBabel, rerender: (state: {}) => void) => IStringToReactApi = (React: TReact, Babel: TBabel, rerender: (state: {}) => void) => new Ctx(React, Babel, rerender);
77
export default StringToReact.bind(null, { getCtx: getCtx, Babel: Babel, react: React });

src/strintToReact.tsx

Lines changed: 14 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,24 @@
1-
import React, {useRef, useEffect, useCallback, useState, type FC} from 'react';
1+
import React, {useRef, useState, useEffect, type FC} from 'react';
22
import type {StringToReactComponentProps, IStringToReactApi, TBabel, TReact} from './types.d';
33
function StringToReactComponent(
4-
deps: {getCtx: (react: TReact, Babel: TBabel) => IStringToReactApi; react: TReact; Babel: TBabel},
4+
deps: {
5+
getCtx: (react: TReact, Babel: TBabel, rerender: (state: {}) => void) => IStringToReactApi;
6+
react: TReact;
7+
Babel: TBabel;
8+
},
59
props: StringToReactComponentProps,
610
) {
711
const {getCtx, Babel, react} = deps;
8-
let ref = useRef<any>(null);
9-
ref.current = ref.current || getCtx(react, Babel);
12+
const [, rerender] = useState<object>({});
13+
let ref = useRef<IStringToReactApi | null>(null);
14+
ref.current = ref.current || getCtx(react, Babel, rerender);
1015
const api = ref.current as IStringToReactApi;
11-
const babelOptions = props.babelOptions || {};
12-
const stringCode = props.children || '() => null';
1316
const data = props.data || {};
14-
//const GeneratedComponent = api.updateTemplate(props.children || '', babelOptions).getComponent();
15-
16-
//
17-
const ModuleComponent = useCallback(
18-
(data: any) => {
19-
const ref = useRef<{Com: FC<any>}>({Com: () => <p>Loading...</p>});
20-
const [, rerender] = useState<object>({});
21-
useEffect(() => {
22-
api.getModule(stringCode, babelOptions).then((Mod: FC) => {
23-
ref.current.Com = Mod;
24-
rerender({});
25-
});
26-
}, []);
27-
const Com = ref.current.Com;
28-
return <Com {...data} />;
29-
},
30-
[stringCode, babelOptions],
31-
);
32-
return <ModuleComponent {...data} />;
17+
useEffect(() => {
18+
api.update(props.children || '()=>null', props.babelOptions || {});
19+
}, [props.children, props.babelOptions]);
20+
const Com: FC = api.getComponent();
21+
return <Com {...data} />;
3322
}
3423

3524
export default StringToReactComponent;

src/types.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { TransformOptions } from "@babel/core";
44
import React, { FunctionComponent, FC, PropsWithChildren } from 'react';
55
import { TransformOptions } from "@babel/core";
66
export interface IStringToReactApi {
7-
updateTemplate: (template: string, babelOptions: TransformOptions) => IStringToReactApi;
7+
update: (template: string, babelOptions: TransformOptions) => void;
88
getComponent: () => FC<PropsWithChildren<{}>>;
99
[x: string]: any;
1010
}

styleguide.config.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,11 @@ module.exports = {
3333
// assetsDir: "example/stories/assets",
3434
sections: [
3535
{name: 'Minimal Usage', content: 'example/stories/usage/README.md', sectionDepth: 1},
36-
{name: 'Using useState, useEffect and ...', content: 'example/stories/using-useState-useEffect/README.md'},
3736
{name: 'Using Unknown Elements', content: 'example/stories/using-unkown-elements/README.md', sectionDepth: 1},
37+
{name: 'Using React Hooks', content: 'example/stories/using-react-hooks/README.md'},
3838
{name: 'filename option', content: 'example/stories/filename-option/README.md', sectionDepth: 1},
3939
{name: 'Using Typescript', content: 'example/stories/typescript/README.md', sectionDepth: 1},
40+
{name: 'Using env preset', content: 'example/stories/env-preset/README.md', sectionDepth: 1},
4041
],
4142
styleguideComponents: {},
4243
pagePerSection: true,

0 commit comments

Comments
 (0)