code-editor

Changes

app/elements/code-editor/injects/editor-init.txt 1(+0 -1)

app/elements/output-frame/file.ts 3(+0 -3)

app/elements/output-frame/injects/on-error.txt 3(+0 -3)

app/elements/output-frame/injects/try-catch.txt 9(+0 -9)

app/elements/output-frame/output-frame.ts 68(+0 -68)

run-servers.js 22(+22 -0)

shared/getdom.ts 10(+10 -0)

webpack.dev.js 14(+7 -7)

webpack.prod.js 18(+9 -9)

Details

diff --git a/.vscode/launch.json b/.vscode/launch.json
index 5efbc3b..cd9d3a8 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -11,8 +11,8 @@
             "type": "node-terminal"
         },
         {
-            "command": "npm run server",
-            "name": "2. Start server (w. hot)",
+            "command": "node run-servers.js",
+            "name": "2. Start servers (w. hot)",
             "request": "launch",
             "type": "node-terminal"
         },
diff --git a/codeeditor-app/elements/code-editor/injects/editor-init.txt b/codeeditor-app/elements/code-editor/injects/editor-init.txt
new file mode 100644
index 0000000..b6a8b43
--- /dev/null
+++ b/codeeditor-app/elements/code-editor/injects/editor-init.txt
@@ -0,0 +1,16 @@
+var myDiv = document.createElement("div");
+myDiv.innerText = "hello moto";
+
+document.body.appendChild(myDiv);
+
+
+var i = 0;
+
+while (i < 10) {
+    console.log("hello " + i);
+    i++;
+}
+
+var obj = { hello: true, moto: "enabled" };
+
+console.log(obj);
\ No newline at end of file
diff --git a/codeeditor-app/elements/output-frame/output-frame.ts b/codeeditor-app/elements/output-frame/output-frame.ts
new file mode 100644
index 0000000..bd850f0
--- /dev/null
+++ b/codeeditor-app/elements/output-frame/output-frame.ts
@@ -0,0 +1,84 @@
+import { BaseElement } from "../_base";
+import './output-frame.less';
+import { NotificationBubbles } from "../notification-bubbles/notification-bubbles";
+import { GetOutputFrameUrl } from "../../../shared/url-helpers";
+import { GetNotificationBubbles } from "../../../shared/getdom";
+import { postMessage } from "../../../shared/post-message";
+
+export class OutputFrame extends BaseElement {
+    public Iframe: HTMLIFrameElement | null = null;
+    public CurrentIframeScriptRemoveResponseListener: Function | null = null;
+
+    constructor() {
+        super();
+    }
+
+    onInit(): void {
+        this.reset();
+    }
+
+    onUpdate(): void {
+    }
+
+    reset(resetNotificationBubbles: boolean = true) {
+        if (this.CurrentIframeScriptRemoveResponseListener != null) {
+            this.CurrentIframeScriptRemoveResponseListener();
+            this.CurrentIframeScriptRemoveResponseListener = null;
+        }
+
+        if (this.Iframe && this.hasChild(this.Iframe)) {
+            this.removeChild(this.Iframe);
+        }
+
+        this.Iframe = document.createElement("iframe");
+        this.Iframe.setAttribute("sandbox", "allow-pointer-lock allow-same-origin allow-scripts");
+        this.Iframe.src = GetOutputFrameUrl();
+        this.appendChild(this.Iframe);
+
+        if (resetNotificationBubbles) {
+            var bubbles = GetNotificationBubbles();
+            if (bubbles && bubbles.reset) {
+                bubbles.reset();
+            }
+        }
+    }
+
+    setError() {
+        this.reset();
+        if (this.Iframe) {
+            this.Iframe.src = GetOutputFrameUrl() + "execution-time-error.html";
+        }
+    }
+
+    preventInfiniteLoop(value: string) {
+        return value.replace("while (true)", "while (false)");
+    }
+
+    setScript(value: string) {
+        this.reset();
+        this.onIframeLoaded(() => {
+            this.CurrentIframeScriptRemoveResponseListener = postMessage(this.Iframe?.contentWindow, "script", value, (executeTimeInMs, exceedTimeInMs) => {
+                if (executeTimeInMs === -1) {
+                    this.setError();
+                }
+                else {
+                    var bubbles = GetNotificationBubbles();
+                    bubbles.add("code executed in: " + executeTimeInMs + "ms", "info");
+                }
+            });
+        });
+    }
+
+    onIframeLoaded(fn: Function) {
+        if (this.Iframe) {
+            this.Iframe.onload = () => {
+                fn();
+            }
+        }
+        else {
+            this.waitFor(this.Iframe, () => {
+                this.onIframeLoaded(fn);
+            });
+        }
+    }
+}
\ No newline at end of file
diff --git a/outputframe/output.frame.bundle.js b/outputframe/output.frame.bundle.js
new file mode 100644
index 0000000..c38d2e2
--- /dev/null
+++ b/outputframe/output.frame.bundle.js
@@ -0,0 +1 @@
+(()=>{"use strict";var e={8971:(e,n,t)=>{t.d(n,{Z:()=>s});var r=t(8081),o=t.n(r),a=t(3645),i=t.n(a)()(o());i.push([e.id,'html,\nbody {\n  margin: 0;\n  width: 100%;\n  height: 100%;\n  overflow: hidden;\n}\nbody {\n  font-family: "Segoe UI", Frutiger, "Frutiger Linotype", "Dejavu Sans", "Helvetica Neue", Arial, sans-serif;\n  padding: 10px;\n}\n',""]);const s=i},3645:e=>{e.exports=function(e){var n=[];return n.toString=function(){return this.map((function(n){var t="",r=void 0!==n[5];return n[4]&&(t+="@supports (".concat(n[4],") {")),n[2]&&(t+="@media ".concat(n[2]," {")),r&&(t+="@layer".concat(n[5].length>0?" ".concat(n[5]):""," {")),t+=e(n),r&&(t+="}"),n[2]&&(t+="}"),n[4]&&(t+="}"),t})).join("")},n.i=function(e,t,r,o,a){"string"==typeof e&&(e=[[null,e,void 0]]);var i={};if(r)for(var s=0;s<this.length;s++){var c=this[s][0];null!=c&&(i[c]=!0)}for(var u=0;u<e.length;u++){var d=[].concat(e[u]);r&&i[d[0]]||(void 0!==a&&(void 0===d[5]||(d[1]="@layer".concat(d[5].length>0?" ".concat(d[5]):""," {").concat(d[1],"}")),d[5]=a),t&&(d[2]?(d[1]="@media ".concat(d[2]," {").concat(d[1],"}"),d[2]=t):d[2]=t),o&&(d[4]?(d[1]="@supports (".concat(d[4],") {").concat(d[1],"}"),d[4]=o):d[4]="".concat(o)),n.push(d))}},n}},8081:e=>{e.exports=function(e){return e[1]}},3379:e=>{var n=[];function t(e){for(var t=-1,r=0;r<n.length;r++)if(n[r].identifier===e){t=r;break}return t}function r(e,r){for(var a={},i=[],s=0;s<e.length;s++){var c=e[s],u=r.base?c[0]+r.base:c[0],d=a[u]||0,l="".concat(u," ").concat(d);a[u]=d+1;var p=t(l),f={css:c[1],media:c[2],sourceMap:c[3],supports:c[4],layer:c[5]};if(-1!==p)n[p].references++,n[p].updater(f);else{var v=o(f,r);r.byIndex=s,n.splice(s,0,{identifier:l,updater:v,references:1})}i.push(l)}return i}function o(e,n){var t=n.domAPI(n);return t.update(e),function(n){if(n){if(n.css===e.css&&n.media===e.media&&n.sourceMap===e.sourceMap&&n.supports===e.supports&&n.layer===e.layer)return;t.update(e=n)}else t.remove()}}e.exports=function(e,o){var a=r(e=e||[],o=o||{});return function(e){e=e||[];for(var i=0;i<a.length;i++){var s=t(a[i]);n[s].references--}for(var c=r(e,o),u=0;u<a.length;u++){var d=t(a[u]);0===n[d].references&&(n[d].updater(),n.splice(d,1))}a=c}}},569:e=>{var n={};e.exports=function(e,t){var r=function(e){if(void 0===n[e]){var t=document.querySelector(e);if(window.HTMLIFrameElement&&t instanceof window.HTMLIFrameElement)try{t=t.contentDocument.head}catch(e){t=null}n[e]=t}return n[e]}(e);if(!r)throw new Error("Couldn't find a style target. This probably means that the value for the 'insert' parameter is invalid.");r.appendChild(t)}},9216:e=>{e.exports=function(e){var n=document.createElement("style");return e.setAttributes(n,e.attributes),e.insert(n,e.options),n}},3565:(e,n,t)=>{e.exports=function(e){var n=t.nc;n&&e.setAttribute("nonce",n)}},7795:e=>{e.exports=function(e){var n=e.insertStyleElement(e);return{update:function(t){!function(e,n,t){var r="";t.supports&&(r+="@supports (".concat(t.supports,") {")),t.media&&(r+="@media ".concat(t.media," {"));var o=void 0!==t.layer;o&&(r+="@layer".concat(t.layer.length>0?" ".concat(t.layer):""," {")),r+=t.css,o&&(r+="}"),t.media&&(r+="}"),t.supports&&(r+="}");var a=t.sourceMap;a&&"undefined"!=typeof btoa&&(r+="\n/*# sourceMappingURL=data:application/json;base64,".concat(btoa(unescape(encodeURIComponent(JSON.stringify(a))))," */")),n.styleTagTransform(r,e,n.options)}(n,e,t)},remove:function(){!function(e){if(null===e.parentNode)return!1;e.parentNode.removeChild(e)}(n)}}}},4589:e=>{e.exports=function(e,n){if(n.styleSheet)n.styleSheet.cssText=e;else{for(;n.firstChild;)n.removeChild(n.firstChild);n.appendChild(document.createTextNode(e))}}}},n={};function t(r){var o=n[r];if(void 0!==o)return o.exports;var a=n[r]={id:r,exports:{}};return e[r](a,a.exports,t),a.exports}t.n=e=>{var n=e&&e.__esModule?()=>e.default:()=>e;return t.d(n,{a:n}),n},t.d=(e,n)=>{for(var r in n)t.o(n,r)&&!t.o(e,r)&&Object.defineProperty(e,r,{enumerable:!0,get:n[r]})},t.o=(e,n)=>Object.prototype.hasOwnProperty.call(e,n),(()=>{var e,n,r,o=t(3379),a=t.n(o),i=t(7795),s=t.n(i),c=t(569),u=t.n(c),d=t(3565),l=t.n(d),p=t(9216),f=t.n(p),v=t(4589),m=t.n(v),y=t(8971),h={};function g(e){var n=function(n){n.data&&""!==n.data&&"webpackOk"!==n.data.type&&e(n.data.type,n.data.value,n.data.id)};return window.addEventListener("message",n),()=>{window.removeEventListener("message",n)}}function w(e,n,t,r){var o=+new Date,a=5e3,i=!0;if(e&&n&&t){if(r){var s=null,c=g(((e,n,t)=>{"response"==e&&t==o&&(null!=s&&clearTimeout(s),c(),i&&r(+new Date-t,a))}));s=setTimeout((()=>{c(),null!=s&&(clearTimeout(s),i&&r(-1,a))}),a)}e.postMessage({type:n,value:"string"==typeof t||t instanceof String?t:JSON.stringify(t),id:o},"*")}return()=>{r&&(i=!1)}}h.styleTagTransform=m(),h.setAttributes=l(),h.insert=u().bind(null,"head"),h.domAPI=s(),h.insertStyleElement=f(),a()(y.Z,h),y.Z&&y.Z.locals&&y.Z.locals,document.body.innerHTML+="",e=(e,n)=>{w(parent,e,n)},n=window.console,r={log:function(t){n.log(t),e("log",t)},info:function(t){n.info(t),e("info",t)},warn:function(t){n.warn(t),e("warn",t)},error:function(t){n.error(t),e("error",t)}},window.console=r,g(((e,n,t)=>{"script"==e&&(function(e){var n=document.createElement("script");n.setAttribute("async",""),n.innerHTML=e,document.body.appendChild(n)}(n),function(e,n,t){e&&t&&e.postMessage({type:"response",value:JSON.stringify(t),id:n},"*")}(parent,t,n))})),window.onerror=function(e){w(parent,"syntax-error",e)}})()})();
\ No newline at end of file
diff --git a/outputframe-app/app.html b/outputframe-app/app.html
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/outputframe-app/app.html
diff --git a/outputframe-app/app.less b/outputframe-app/app.less
new file mode 100644
index 0000000..643672f
--- /dev/null
+++ b/outputframe-app/app.less
@@ -0,0 +1,11 @@
+html, body {
+    margin: 0;
+    width: 100%;
+    height: 100%;
+    overflow: hidden;
+}
+
+body {
+    font-family: "Segoe UI", Frutiger, "Frutiger Linotype", "Dejavu Sans", "Helvetica Neue", Arial, sans-serif;
+    padding: 10px;
+}
\ No newline at end of file
diff --git a/outputframe-app/boot.ts b/outputframe-app/boot.ts
new file mode 100644
index 0000000..777ba65
--- /dev/null
+++ b/outputframe-app/boot.ts
@@ -0,0 +1,22 @@
+import './app.less';
+import bootHtml from './app.html';
+import { watchConsole } from './injects/watch-console';
+import { setOnErrorListener } from './injects/on-error';
+import { createScript } from '../shared/create-script';
+import { messageListener } from '../shared/message-listener';
+import { postMessage, postResponseMessage } from '../shared/post-message';
+
+
+document.body.innerHTML += bootHtml;
+watchConsole((type, value) => {
+    postMessage(parent, type, value);
+});
+messageListener((type, value, id) => {
+    if (type == "script") {
+        createScript(value);
+        postResponseMessage(parent, id, value);
+    }
+});
+setOnErrorListener((type, value) => {
+    postMessage(parent, type, value);
+});
\ No newline at end of file
diff --git a/outputframe-app/injects/on-error.ts b/outputframe-app/injects/on-error.ts
new file mode 100644
index 0000000..c22f8da
--- /dev/null
+++ b/outputframe-app/injects/on-error.ts
@@ -0,0 +1,7 @@
+import { NotificationBubbles } from "../../codeeditor-app/elements/notification-bubbles/notification-bubbles";
+
+export function setOnErrorListener(fn: (type: 'syntax-error', value: any) => void) {
+    window.onerror = function (error) {
+        fn('syntax-error', error);
+    }
+}
\ No newline at end of file
diff --git a/outputframe-app/injects/try-catch.ts b/outputframe-app/injects/try-catch.ts
new file mode 100644
index 0000000..b786067
--- /dev/null
+++ b/outputframe-app/injects/try-catch.ts
@@ -0,0 +1,12 @@
+import { NotificationBubbles } from "../../codeeditor-app/elements/notification-bubbles/notification-bubbles";
+
+export function tryCatch(fn: Function) {
+    try {
+        fn();
+    }
+    catch (err) {
+        var bubbles = <NotificationBubbles>parent.document.body.getElementsByTagName("notification-bubbles")[0];
+        //@ts-ignore
+        bubbles.add(err, 'syntax-error');
+    }
+}
\ No newline at end of file
diff --git a/outputframe-app/static/execution-time-error.html b/outputframe-app/static/execution-time-error.html
new file mode 100644
index 0000000..56326b2
--- /dev/null
+++ b/outputframe-app/static/execution-time-error.html
@@ -0,0 +1,36 @@
+<html lang="en">
+
+<head>
+    <meta charset="UTF-8">
+    <meta name="viewport" id="master-viewport"
+        content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
+    <title>output error</title>
+    <base href="/" />
+    <style>
+        body {
+            font-family: "Segoe UI", Frutiger, "Frutiger Linotype", "Dejavu Sans", "Helvetica Neue", Arial, sans-serif;
+        }
+
+        .center {
+            position: absolute;
+            top: 50%;
+            left: 50%;
+            transform: translate(-50%, -50%);
+            font-size: 22px;
+            text-align: center;
+        }
+
+        small {
+            font-size: 0.6em;
+        }
+    </style>
+</head>
+
+<body>
+    <div class="center">
+        took too long to run?<br />
+        <small>why you infinite loopin?</small>
+    </div>
+</body>
+
+</html>
\ No newline at end of file
diff --git a/outputframe-app/static/index.html b/outputframe-app/static/index.html
new file mode 100644
index 0000000..106c903
--- /dev/null
+++ b/outputframe-app/static/index.html
@@ -0,0 +1,16 @@
+<html lang="en">
+
+<head>
+    <meta charset="UTF-8">
+    <meta name="viewport" id="master-viewport"
+        content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
+    <title>output</title>
+    <base href="/" />
+    
+</head>
+
+<body>
+    <script type="text/javascript" src="/output.frame.bundle.js?v=1"></script>
+</body>
+
+</html>
\ No newline at end of file

run-servers.js 22(+22 -0)

diff --git a/run-servers.js b/run-servers.js
new file mode 100644
index 0000000..a5900b5
--- /dev/null
+++ b/run-servers.js
@@ -0,0 +1,22 @@
+const WebpackDevServer = require("webpack-dev-server");
+const webpack = require("webpack");
+
+const codeeditorConfig = require("./webpack.dev.js");
+const outputframeConfig = require("./webpack.dev-outputframe.js");
+
+const codeeditorCompiler = webpack(codeeditorConfig);
+const outputframeCompiler = webpack(outputframeConfig);
+
+
+const server1 = new WebpackDevServer(codeeditorCompiler, {
+    hot: true,
+    historyApiFallback: true
+})
+
+const server2 = new WebpackDevServer(outputframeCompiler, {
+    hot: true,
+    historyApiFallback: true
+})
+
+server1.listen(8020, "localhost", function() { console.log("server 1 rdy"); })
+server2.listen(8021, "localhost", function() { console.log("server 2 rdy"); })
\ No newline at end of file
diff --git a/shared/create-script.ts b/shared/create-script.ts
new file mode 100644
index 0000000..0eed6df
--- /dev/null
+++ b/shared/create-script.ts
@@ -0,0 +1,7 @@
+export function createScript(value: string) {
+    var script: HTMLScriptElement = document.createElement("script");
+    script.setAttribute("async", "");
+    script.innerHTML = value;
+
+    document.body.appendChild(script);
+}
\ No newline at end of file

shared/getdom.ts 10(+10 -0)

diff --git a/shared/getdom.ts b/shared/getdom.ts
new file mode 100644
index 0000000..36b666b
--- /dev/null
+++ b/shared/getdom.ts
@@ -0,0 +1,10 @@
+import { NotificationBubbles } from "../codeeditor-app/elements/notification-bubbles/notification-bubbles";
+import { OutputFrame } from "../codeeditor-app/elements/output-frame/output-frame";
+
+export function GetNotificationBubbles(): NotificationBubbles {
+    return <NotificationBubbles>document.getElementsByTagName("notification-bubbles")[0];
+}
+
+export function GetOutputFrame(): OutputFrame {
+    return <OutputFrame>document.getElementsByTagName("output-frame")[0];
+}
\ No newline at end of file
diff --git a/shared/message-listener.ts b/shared/message-listener.ts
new file mode 100644
index 0000000..75c3a29
--- /dev/null
+++ b/shared/message-listener.ts
@@ -0,0 +1,16 @@
+export function messageListener(fn: (type: 'script' | 'log' | 'info' | 'warn' | 'error' | 'syntax-error' | 'time' | 'response', value: string, id: number) => void): Function {
+    
+    var evt = function(e: any) {
+        if (!e.data || e.data === '' || e.data.type === 'webpackOk') {
+            return;
+        }
+
+        fn(e.data.type, e.data.value, e.data.id);
+    };
+
+    window.addEventListener('message', evt);
+
+    return () => {
+        window.removeEventListener('message', evt);
+    }
+}
\ No newline at end of file
diff --git a/shared/post-message.ts b/shared/post-message.ts
new file mode 100644
index 0000000..4d50a7a
--- /dev/null
+++ b/shared/post-message.ts
@@ -0,0 +1,48 @@
+import { messageListener } from "./message-listener";
+
+export function postMessage(window: Window | null | undefined, type: string, value: any, onResponse?: (executeTimeInMs: number, exceedTimeInMs: number) => void): Function {
+    var postId = +new Date();
+    var responseTimeout = 5000;
+    var execResponse = true;
+
+    if (window && type && value) {
+        if (onResponse) {
+            var timeoutHandle: number | null = null;
+
+            var remListener = messageListener((type, value, id) => {
+                if (type == "response" && id == postId) {
+                    if (timeoutHandle != null)
+                        clearTimeout(timeoutHandle);
+
+                    remListener();
+                    if (execResponse)
+                        onResponse(+new Date() - id, responseTimeout);
+                }
+            });
+
+            timeoutHandle = setTimeout(() => {
+                remListener();
+                if (timeoutHandle != null) {
+                    clearTimeout(timeoutHandle);
+                    if (execResponse)
+                        onResponse(-1, responseTimeout);
+                }
+            }, responseTimeout);
+        }
+
+        window.postMessage({ type: type, value: (typeof value === 'string' || value instanceof String) ? value : JSON.stringify(value), id: postId }, "*");
+    }
+
+    return () => {
+        if (onResponse) {
+            execResponse = false;
+        }
+    };
+
+}
+
+export function postResponseMessage(window: Window | null | undefined, id: number, value: any) {
+    if (window && value) {
+        window.postMessage({ type: "response", value: JSON.stringify(value), id: id }, "*");
+    }
+}
\ No newline at end of file
diff --git a/shared/url-helpers.ts b/shared/url-helpers.ts
new file mode 100644
index 0000000..72c6907
--- /dev/null
+++ b/shared/url-helpers.ts
@@ -0,0 +1,16 @@
+export function GetUrl(): string {
+    return location.protocol + "//" + location.host + "/";
+}
+
+export function IsLocalhost(): boolean {
+    return location.href.indexOf("://localhost") != -1;
+}
+
+export function GetOutputFrameUrl(): string {
+    if (IsLocalhost()) {
+        return "http://127.0.0.1:8021/";
+    }
+    else {
+        return "https://outputframe.davidmeincke.dk/";
+    }
+}
\ No newline at end of file
diff --git a/webpack.common.js b/webpack.common.js
index 6b04ac8..7800114 100644
--- a/webpack.common.js
+++ b/webpack.common.js
@@ -1,12 +1,13 @@
 const path = require('path');
 module.exports = {
     entry: {
-        'main': path.resolve(__dirname, 'App', 'boot.ts'),
+        'main': path.resolve(__dirname, 'codeeditor-app', 'boot.ts'),
         'editor.worker': 'monaco-editor/esm/vs/editor/editor.worker.js',
 		'json.worker': 'monaco-editor/esm/vs/language/json/json.worker',
 		'css.worker': 'monaco-editor/esm/vs/language/css/css.worker',
 		'html.worker': 'monaco-editor/esm/vs/language/html/html.worker',
-		'ts.worker': 'monaco-editor/esm/vs/language/typescript/ts.worker'
+		'ts.worker': 'monaco-editor/esm/vs/language/typescript/ts.worker',
+        '../outputframe/output.frame': path.resolve(__dirname, 'outputframe-app', 'boot.ts')
     },
     resolve: {
         extensions: [".ts", ".js", ".json"]

webpack.dev.js 14(+7 -7)

diff --git a/webpack.dev.js b/webpack.dev.js
index ff7ce6a..190a0e1 100644
--- a/webpack.dev.js
+++ b/webpack.dev.js
@@ -2,7 +2,6 @@ const path = require('path');
 const { merge } = require('webpack-merge');
 const copyPlugin = require('copy-webpack-plugin');
 const common = require('./webpack.common.js');
-const MonacoWebpackPlugin = require('monaco-editor-webpack-plugin');
 
 const devFolderName = "dev-dist";
 
@@ -11,13 +10,10 @@ module.exports = merge(common, {
     output: {
         globalObject: 'self',
 		filename: '[name].bundle.js',
-        path: path.resolve(__dirname, devFolderName),
+        path: path.resolve(__dirname, devFolderName, 'codeeditor'),
         publicPath: "/"
     },
     devServer: {
-        static: {
-            directory: path.join(__dirname, devFolderName),
-        },
         historyApiFallback: true
     },
     plugins: [
@@ -25,8 +21,12 @@ module.exports = merge(common, {
             {
                 patterns: [
                     {
-                        from: path.resolve(__dirname, 'static'),
-                        to: path.resolve(__dirname, devFolderName, '[name][ext]')
+                        from: path.resolve(__dirname, 'codeeditor-app/static'),
+                        to: path.resolve(__dirname, devFolderName, 'codeeditor', '[name][ext]')
+                    },
+                    {
+                        from: path.resolve(__dirname, 'outputframe-app/static'),
+                        to: path.resolve(__dirname, devFolderName, 'outputframe', '[name][ext]')
                     }
                 ]
             }
diff --git a/webpack.dev-outputframe.js b/webpack.dev-outputframe.js
new file mode 100644
index 0000000..12ac5b6
--- /dev/null
+++ b/webpack.dev-outputframe.js
@@ -0,0 +1,35 @@
+const path = require('path');
+const { merge } = require('webpack-merge');
+const copyPlugin = require('copy-webpack-plugin');
+const common = require('./webpack.common.js');
+
+const devFolderName = "dev-dist";
+
+module.exports = merge(common, {
+    mode: 'development',
+    output: {
+        globalObject: 'self',
+		filename: '[name].bundle.js',
+        path: path.resolve(__dirname, devFolderName, 'outputframe'),
+        publicPath: "/"
+    },
+    devServer: {
+        historyApiFallback: true
+    },
+    plugins: [
+        new copyPlugin(
+            {
+                patterns: [
+                    {
+                        from: path.resolve(__dirname, 'codeeditor-app/static'),
+                        to: path.resolve(__dirname, devFolderName, 'codeeditor', '[name][ext]')
+                    },
+                    {
+                        from: path.resolve(__dirname, 'outputframe-app/static'),
+                        to: path.resolve(__dirname, devFolderName, 'outputframe', '[name][ext]')
+                    }
+                ]
+            }
+        )
+    ]
+});
\ No newline at end of file

webpack.prod.js 18(+9 -9)

diff --git a/webpack.prod.js b/webpack.prod.js
index 46de967..9599b43 100644
--- a/webpack.prod.js
+++ b/webpack.prod.js
@@ -3,7 +3,6 @@ const common = require('./webpack.common.js');
 const path = require('path');
 const ReplaceInFileWebpackPlugin = require('replace-in-file-webpack-plugin');
 const CopyWebpackPlugin = require('copy-webpack-plugin');
-const MonacoWebpackPlugin = require('monaco-editor-webpack-plugin');
 
 const prodFolderName = "prod-dist";
 
@@ -18,28 +17,29 @@ const replaceVersionInHtmlOption = {
                 return match.toString().substr(0, 3) +
                     +new Date();
             }
-        }
+        } 
     ]
 };
 
 module.exports = merge(common, {
     mode: "production",
     output: {
-        path: path.resolve(__dirname, prodFolderName),
-        publicPath: "/",
-        filename: 'main.js'
+        globalObject: 'self',
+		filename: '[name].bundle.js',
+        path: path.resolve(__dirname, prodFolderName, "codeeditor"),
+        publicPath: "/"
     },
     plugins: [
         new CopyWebpackPlugin(
             {
                 patterns: [
                     {
-                        from: path.resolve(__dirname, 'index.html'),
-                        to: path.resolve(__dirname, prodFolderName, '[name][ext]')
+                        from: path.resolve(__dirname, 'codeeditor-app/static'),
+                        to: path.resolve(__dirname, prodFolderName, 'codeeditor', '[name][ext]')
                     },
                     {
-                        from: path.resolve(__dirname, 'manifest.json'),
-                        to: path.resolve(__dirname, devFolderName, '[name][ext]')
+                        from: path.resolve(__dirname, 'outputframe-app/static'),
+                        to: path.resolve(__dirname, prodFolderName, 'outputframe', '[name][ext]')
                     }
                 ]
             }