Skip to content

Commit 5d9727e

Browse files
committed
implementing iframe
1 parent 57c97eb commit 5d9727e

File tree

8 files changed

+233
-22
lines changed

8 files changed

+233
-22
lines changed

app/electron/main.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -76,11 +76,11 @@ async function createWindow() {
7676
// enable devtools when in development mode
7777
devTools: true,
7878
// crucial security feature - blocks rendering process from having access to node modules
79-
nodeIntegration: false,
79+
nodeIntegration: true,
8080
// web workers will not have access to node
81-
nodeIntegrationInWorker: false,
81+
nodeIntegrationInWorker: true,
8282
// disallow experimental feature to allow node.js support in sub-frames (i-frames/child windows)
83-
nodeIntegrationInSubFrames: false,
83+
nodeIntegrationInSubFrames: true,
8484
// runs electron apis and preload script in a separate JS context
8585
// separate context has access to document/window but has it's own built-ins and is isolate from changes to global environment by located page
8686
// Electron API only available from preload, not loaded page

app/src/components/bottom/CodePreview.tsx

Lines changed: 76 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,32 @@ import 'ace-builds/src-noconflict/theme-terminal';
1212
import { Component } from '../../interfaces/Interfaces';
1313
import useResizeObserver from '../../tree/useResizeObserver';
1414
import { string } from 'prop-types';
15+
import { unpkgPathPlugin } from '../../plugins/unpkg-path-plugin';
16+
import { fetchPlugin } from '../../plugins/fetch-plugin';
17+
import * as esbuild from 'esbuild-wasm';
18+
import { useSelector, useDispatch } from 'react-redux';
19+
import { store } from './../../index';
1520

1621
const CodePreview: React.FC<{
1722
theme: string | null;
1823
setTheme: any | null;
1924
}> = ({ theme, setTheme }) => {
25+
26+
27+
28+
29+
const ref = useRef<any>();
30+
const iframe = useRef<any>();
31+
const [input, setInput] = useState('');
32+
const [code, setCode] = useState('');
33+
34+
const startService = async () => {
35+
ref.current = await esbuild.startService({
36+
worker: true,
37+
wasmURL: 'https://unpkg.com/[email protected]/esbuild.wasm',
38+
})
39+
}
40+
2041
const wrapper = useRef();
2142
const dimensions = useResizeObserver(wrapper);
2243
const { width, height } =
@@ -30,11 +51,55 @@ const CodePreview: React.FC<{
3051

3152
const handleCodeSnipChange = (val) => {
3253
currentComponent.code = val;
54+
3355
};
3456

57+
useEffect(() => {
58+
startService();
59+
}, []);
60+
61+
console.log('saved code', store.getState().code)
3562
useEffect(() => {
3663
setDivHeight(height);
3764
}, [height])
65+
66+
useEffect(() =>
67+
{
68+
69+
70+
}, [code])
71+
72+
const handleChange = (event) => {
73+
setInput(event)
74+
// iframe.current.contentWindow.postMessage(result.outputFiles[0].text, '*');
75+
};
76+
77+
const handleClick = async () => {
78+
if(!ref.current) {
79+
return;
80+
}
81+
const result = await ref.current.build({
82+
entryPoints: ['index.js'],
83+
bundle: true,
84+
write: false,
85+
plugins: [
86+
unpkgPathPlugin(),
87+
fetchPlugin(input)
88+
],
89+
define: {
90+
'process.env.NODE_ENV': '"production"',
91+
global: 'window'
92+
}
93+
})
94+
store.dispatch({type: "SAVE", payload: result.outputFiles[0].text});
95+
//setCode();
96+
97+
98+
// dispatch({type: 'code-preview'})
99+
}
100+
101+
102+
38103
return (
39104
<div
40105
ref={wrapper}
@@ -47,17 +112,24 @@ const CodePreview: React.FC<{
47112
<AceEditor
48113
mode="javascript"
49114
theme="monokai"
50-
width="100%"
51-
height="100%"
52-
onChange={handleCodeSnipChange}
53-
value={currentComponent.code}
115+
width="90%"
116+
height="50%"
117+
onChange={handleChange}
118+
// value={currentComponent.code}
54119
name="Code_div"
55120
readOnly={false}
56121
fontSize={16}
57122
tabSize={2}
58123
/>
124+
<button onClick={() => handleClick()}>Fetch</button>
59125
</div>
60126
);
61127
};
62128

63129
export default CodePreview;
130+
131+
132+
// const App = () => {
133+
// return <h1>hello</h1>
134+
// }
135+

app/src/components/main/DemoRender.tsx

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,18 @@
11

22
import React, {
3-
useState, useCallback, useContext, useEffect,
3+
useState, useCallback, useContext, useEffect, useRef
44
} from 'react';
55
import { BrowserRouter as Router, Switch, Route, Link } from "react-router-dom";
66
import Button from '@material-ui/core/Button';
77
import Box from '@material-ui/core/Box';
88
import StateContext from '../../context/context';
99
import cssRefresher from '../../helperFunctions/cssRefresh';
10-
10+
import {store} from '../../../src/index';
1111
// DemoRender is the full sandbox demo of our user's custom built React components. DemoRender references the design specifications stored in state to construct
1212
// real react components that utilize hot module reloading to depict the user's prototype application.
1313
const DemoRender = (props): JSX.Element => {
14+
// const ref = useRef<any>();
15+
const iframe = useRef<any>();
1416
const [components, setComponents] = useState([]);
1517
const [state, dispatch] = useContext(StateContext);
1618
const demoContainerStyle = {
@@ -19,6 +21,21 @@ const DemoRender = (props): JSX.Element => {
1921
border: '2px Solid grey',
2022
};
2123

24+
const html = `
25+
<html>
26+
<head></head>
27+
<body>
28+
<div id='root'></div>
29+
<script>
30+
window.addEventListener('message', (e) => {
31+
eval(event.data);
32+
}, false);
33+
</script>
34+
</body>
35+
</html>
36+
`;
37+
38+
2239
// This function is the heart of DemoRender it will take the array of components stored in state and dynamically construct the desired React component for the live demo
2340
// Material UI is utilized to incorporate the apporpriate tags with specific configuration designs as necessitated by HTML standards.
2441
const componentBuilder = (array: object, key: number = 0) => {
@@ -48,7 +65,7 @@ const DemoRender = (props): JSX.Element => {
4865
}
4966
return componentsToRender;
5067
};
51-
68+
5269
useEffect(() => {
5370
const focusIndex = state.canvasFocus.componentId - 1;
5471
const childrenArray = state.components[focusIndex].children;
@@ -60,11 +77,15 @@ const DemoRender = (props): JSX.Element => {
6077
cssRefresher();
6178
}, [])
6279

80+
// iframe.current.contentWindow.postMessage(store.getState(), '*');
81+
console.log('in store line 81',store.getState())
6382

6483
return (
6584
<Router>
85+
6686
<div id={'renderFocus'} style={demoContainerStyle}>
67-
{components.map((component, index) => component)}
87+
<iframe ref={iframe} sandbox='allow-scripts' srcDoc={html}/>
88+
{/* {components.map((component, index) => component)} */}
6889
</div>
6990
</Router>
7091

app/src/index.js

Lines changed: 28 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import {
77
} from 'react-router-dom';
88
import 'babel-polyfill';
99
import React from 'react';
10+
import { createStore } from 'redux';
11+
import { Provider } from 'react-redux';
1012
import ReactDOM from 'react-dom';
1113
import Cookies from 'js-cookie';
1214
import App from './components/App.tsx';
@@ -22,6 +24,19 @@ const client = new ApolloClient({
2224
uri: 'https://reactype-caret.herokuapp.com/graphql',
2325
cache: new InMemoryCache()
2426
});
27+
const initialState = {code: "initialState"}
28+
const rootReducer = (state = initialState, action) => {
29+
console.log('action', action);
30+
switch (action.type) {
31+
case 'SAVE':
32+
return {...state, code:action.payload};
33+
default:
34+
return state;
35+
}
36+
37+
}
38+
39+
export const store = createStore(rootReducer)
2540

2641
const PrivateRoute = ({ component: Component, ...rest }) => (
2742
<Route
@@ -38,17 +53,19 @@ const PrivateRoute = ({ component: Component, ...rest }) => (
3853

3954
ReactDOM.render(
4055
<ApolloProvider client={client}>
41-
<Router>
42-
<Switch>
43-
<Route exact path="/login" component={SignIn} />
44-
<Route exact path="/signup" component={SignUp} />
45-
<Route exact path="/password" component={FBPassWord} />
46-
<PrivateRoute exact path="/" component={App} />
47-
<Route exact path="/dashboard" component={ProjectDashboard} />
48-
<Route exact path="/tutorial" component={Tutorial} />
49-
<Route exact path="/tutorialPage/:learn" component={TutorialPage} />
50-
</Switch>
51-
</Router>
56+
<Provider store={store}>
57+
<Router>
58+
<Switch>
59+
<Route exact path="/login" component={SignIn} />
60+
<Route exact path="/signup" component={SignUp} />
61+
<Route exact path="/password" component={FBPassWord} />
62+
<PrivateRoute exact path="/" component={App} />
63+
<Route exact path="/dashboard" component={ProjectDashboard} />
64+
<Route exact path="/tutorial" component={Tutorial} />
65+
<Route exact path="/tutorialPage/:learn" component={TutorialPage} />
66+
</Switch>
67+
</Router>
68+
</Provider>
5269
</ ApolloProvider>,
5370
document.getElementById('app'),
5471
);

app/src/plugins/fetch-plugin.ts

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import * as esbuild from 'esbuild-wasm';
2+
import axios from 'axios';
3+
import localForage from 'localforage';
4+
5+
const fileCache = localForage.createInstance({
6+
name: 'filecache'
7+
});
8+
9+
export const fetchPlugin = (inputCode: string) => {
10+
return {
11+
name: 'fetch-plugin',
12+
setup(build: esbuild.PluginBuild) {
13+
build.onLoad({ filter: /(^index\.js$)/ }, () => {
14+
return {
15+
loader: 'jsx',
16+
contents: inputCode,
17+
};
18+
});
19+
20+
build.onLoad({ filter: /.*/}, async (args: any) => {
21+
const cachedResult = await fileCache.getItem<esbuild.OnLoadResult>(args.path);
22+
if(cachedResult) {
23+
return cachedResult;
24+
}
25+
});
26+
27+
build.onLoad({ filter: /.css$/ }, async (args: any) => {
28+
const { data, request } = await axios.get(args.path);
29+
30+
const escaped = data
31+
.replace(/\n/g, '')
32+
.replace(/"/g, '\\"')
33+
.replace(/'/g, "\\'")
34+
const contents =
35+
`
36+
const style = document.createElement('style');
37+
style.innerText = '${escaped}';
38+
document.head.appendChild(style)
39+
`;
40+
41+
const result: esbuild.OnLoadResult = {
42+
loader: 'jsx',
43+
contents,
44+
resolveDir: new URL('./', request.responseURL).pathname
45+
}
46+
await fileCache.setItem(args.path, result);
47+
return result;
48+
});
49+
50+
51+
build.onLoad({ filter: /.*/ }, async (args: any) => {
52+
console.log('line 55 of fetch', args)
53+
const cachedResult = await fileCache.getItem<esbuild.OnLoadResult>(args.path);
54+
if(cachedResult) {
55+
return cachedResult;
56+
}
57+
const { data, request } = await axios.get(args.path);
58+
59+
const result: esbuild.OnLoadResult = {
60+
loader: 'jsx',
61+
contents: data,
62+
resolveDir: new URL('./', request.responseURL).pathname
63+
}
64+
await fileCache.setItem(args.path, result);
65+
return result;
66+
});
67+
}
68+
}
69+
}

app/src/plugins/unpkg-path-plugin.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import * as esbuild from 'esbuild-wasm';
2+
3+
export const unpkgPathPlugin = () => {
4+
return {
5+
name: 'unpkg-path-plugin',
6+
setup(build: esbuild.PluginBuild) {
7+
// Handle root entry file of index.js
8+
build.onResolve({ filter: /(^index\.js$)/ }, () => {
9+
return { path: 'index.js', namespace: 'a' };
10+
});
11+
// Handle relative paths in a module
12+
build.onResolve({ filter: /^\.+\// }, (args: any) => {
13+
const seeURL = new URL(args.path, 'https://unpkg.com' + args.resolveDir + '/').href;
14+
return {
15+
namespace: 'a',
16+
path: new URL(args.path, 'https://unpkg.com' + args.resolveDir + '/').href
17+
};
18+
});
19+
// Handle main file of a module
20+
build.onResolve({ filter: /.*/ }, async (args: any) => {
21+
return {
22+
namespace: 'a',
23+
path: `https://unpkg.com/${args.path}`
24+
}
25+
});
26+
},
27+
};
28+
};

app/src/store.js

Whitespace-only changes.

package.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@
133133
"electron-debug": "^3.2.0",
134134
"electron-devtools-installer": "^3.2.0",
135135
"electron-window-manager": "^1.0.6",
136+
"esbuild-wasm": "^0.8.27",
136137
"eslint-plugin-react-hooks": "^4.2.0",
137138
"express-graphql": "^0.12.0",
138139
"graphql": "^15.5.0",
@@ -176,6 +177,7 @@
176177
"@types/chai": "^4.2.11",
177178
"@types/jest": "^25.2.3",
178179
"apollo": "^2.32.5",
180+
"axios": "^0.24.0",
179181
"babel-eslint": "^10.1.0",
180182
"babel-jest": "^27.3.1",
181183
"babel-loader": "^8.2.3",
@@ -208,6 +210,8 @@
208210
"react": "^17.0.2",
209211
"react-ace": "^9.5.0",
210212
"react-dom": "^17.0.2",
213+
"react-redux": "^7.2.6",
214+
"redux": "^4.1.2",
211215
"style-loader": "^0.20.3",
212216
"styled-components": "^5.3.3",
213217
"supertest": "^4.0.2",

0 commit comments

Comments
 (0)