mirror of
https://github.com/chinchang/web-maker.git
synced 2025-08-01 11:00:28 +02:00
Merge pull request #364 from chinchang/subdomain
render file preview iframe in subdomain
This commit is contained in:
@@ -12,6 +12,7 @@ const child_process = require('child_process');
|
||||
const merge = require('merge-stream');
|
||||
const zip = require('gulp-zip');
|
||||
var packageJson = JSON.parse(fs.readFileSync('./package.json'));
|
||||
const connect = require('gulp-connect');
|
||||
|
||||
function minifyJs(fileName) {
|
||||
const content = fs.readFileSync(fileName, 'utf8');
|
||||
@@ -199,6 +200,14 @@ gulp.task('cleanup', function() {
|
||||
return child_process.execSync('rm -rf build');
|
||||
});
|
||||
|
||||
gulp.task('start-preview-server', function() {
|
||||
connect.server({
|
||||
root: 'preview',
|
||||
port: 7888,
|
||||
https: false
|
||||
});
|
||||
});
|
||||
|
||||
gulp.task('release', function(callback) {
|
||||
runSequence(
|
||||
'runWebpack',
|
||||
|
@@ -3,10 +3,10 @@
|
||||
"version": "3.6.2",
|
||||
"description": "A blazing fast & offline web playground",
|
||||
"scripts": {
|
||||
"start": "if-env NODE_ENV=production && npm run -s serve || npm run -s dev",
|
||||
"start": "npm run -s dev && gulp start-preview-server",
|
||||
"build": "preact build --template src/index.html --no-prerender --service-worker false",
|
||||
"serve": "preact build && preact serve",
|
||||
"dev": "preact watch --template src/index.html --https --no-prerender",
|
||||
"dev": "preact watch --template src/index.html --no-prerender",
|
||||
"lint": "eslint src",
|
||||
"test": "jest",
|
||||
"precommit": "lint-staged",
|
||||
@@ -65,6 +65,7 @@
|
||||
"copy-webpack-plugin": "^4.5.1",
|
||||
"esprima": "^4.0.0",
|
||||
"firebase": "^5.5.8",
|
||||
"gulp-connect": "^5.7.0",
|
||||
"jszip": "^3.1.5",
|
||||
"preact": "^8.2.6",
|
||||
"preact-compat": "^3.17.0",
|
||||
|
1
preview/index.html
Normal file
1
preview/index.html
Normal file
@@ -0,0 +1 @@
|
||||
Loading...
|
60
preview/service-worker.js
Normal file
60
preview/service-worker.js
Normal file
@@ -0,0 +1,60 @@
|
||||
const CACHE_NAME = 'webmaker-vfiles';
|
||||
self.addEventListener('fetch', function(event) {
|
||||
// console.log('fetch event', event.request.url, event.request);
|
||||
event.respondWith(
|
||||
caches.open(CACHE_NAME).then(function(cache) {
|
||||
return cache
|
||||
.match(event.request)
|
||||
.then(response => {
|
||||
// console.log('responding with ', response);
|
||||
if (response !== undefined) {
|
||||
return response;
|
||||
}
|
||||
return fetch(event.request);
|
||||
})
|
||||
.catch(function(e) {
|
||||
// Fall back to just fetch()ing the request if some unexpected error
|
||||
// prevented the cached response from being valid.
|
||||
console.warn(
|
||||
'Couldn\'t serve response for "%s" from cache: %O',
|
||||
event.request.url,
|
||||
e
|
||||
);
|
||||
return fetch(event.request);
|
||||
});
|
||||
})
|
||||
);
|
||||
// }
|
||||
});
|
||||
|
||||
function getContentType(url) {
|
||||
if (url.match(/\.html$/)) {
|
||||
return 'text/html; charset=UTF-8';
|
||||
} else if (url.match(/\.css$/)) {
|
||||
return 'text/css; charset=UTF-8';
|
||||
} else if (url.match(/\.js$/)) {
|
||||
return 'application/javascript; charset=UTF-8';
|
||||
} else if (url.match(/\.json$/)) {
|
||||
return 'application/json; charset=UTF-8';
|
||||
}
|
||||
return 'text/html; charset=UTF-8';
|
||||
}
|
||||
|
||||
self.addEventListener('message', function(e) {
|
||||
// console.log('message aya sw main', e.data);
|
||||
caches.open(CACHE_NAME).then(function(cache) {
|
||||
for (const url in e.data) {
|
||||
if (Object.prototype.hasOwnProperty.call(e.data, url)) {
|
||||
// console.log('Received data', url, e.data[url])
|
||||
cache.put(
|
||||
url,
|
||||
new Response(e.data[url], {
|
||||
headers: {
|
||||
'Content-Type': getContentType(url)
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
52
preview/talk.html
Normal file
52
preview/talk.html
Normal file
@@ -0,0 +1,52 @@
|
||||
Hello
|
||||
|
||||
<script>
|
||||
if (
|
||||
'serviceWorker' in navigator
|
||||
) {
|
||||
// Delay registration until after the page has loaded, to ensure that our
|
||||
// precaching requests don't degrade the first visit experience.
|
||||
// See https://developers.google.com/web/fundamentals/instant-and-offline/service-worker/registration
|
||||
window.addEventListener('load', function () {
|
||||
navigator.serviceWorker
|
||||
.register('service-worker.js')
|
||||
.then(function (reg) {
|
||||
// updatefound is fired if service-worker.js changes.
|
||||
reg.onupdatefound = function () {
|
||||
// The updatefound event implies that reg.installing is set; see
|
||||
// https://w3c.github.io/ServiceWorker/#service-worker-registration-updatefound-event
|
||||
var installingWorker = reg.installing;
|
||||
|
||||
installingWorker.onstatechange = function () {
|
||||
/* eslint-disable default-case */
|
||||
switch (installingWorker.state) {
|
||||
case 'installed':
|
||||
console.log('New or updated content is available.');
|
||||
break;
|
||||
|
||||
case 'redundant':
|
||||
console.error(
|
||||
'The installing service worker became redundant.'
|
||||
);
|
||||
break;
|
||||
}
|
||||
};
|
||||
};
|
||||
})
|
||||
.catch(function (e) {
|
||||
console.error('Error during service worker registration:', e);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
<script>
|
||||
console.log('controller', navigator.serviceWorker.controller)
|
||||
window.addEventListener('message', (e) => {
|
||||
console.log(88, e.data);
|
||||
navigator.serviceWorker.controller.postMessage(e.data);
|
||||
});
|
||||
|
||||
</script>
|
@@ -244,8 +244,6 @@ export default class CodeEditor extends Component {
|
||||
}
|
||||
|
||||
async initEditor() {
|
||||
console.log('init editor');
|
||||
|
||||
this.editorReadyDeferred = deferred();
|
||||
await this.loadDeps();
|
||||
|
||||
|
@@ -69,6 +69,13 @@ export default class ContentWrap extends Component {
|
||||
}
|
||||
componentDidMount() {
|
||||
this.props.onRef(this);
|
||||
|
||||
// Listen for logs from preview frame
|
||||
window.addEventListener('message', e => {
|
||||
if (e.data && e.data.logs) {
|
||||
this.onMessageFromConsole(...e.data.logs);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
onHtmlCodeChange(editor, change) {
|
||||
@@ -160,7 +167,7 @@ export default class ContentWrap extends Component {
|
||||
: `${location.origin}`;
|
||||
var src = `filesystem:${origin}/temporary/preview.html`;
|
||||
if (this.detachedWindow) {
|
||||
this.detachedWindow.postMessage(src, '*');
|
||||
this.detachedWindow.postMessage({ url: src }, '*');
|
||||
} else {
|
||||
this.frame.src = src;
|
||||
}
|
||||
|
@@ -22,6 +22,9 @@ import { commandPaletteService } from '../commandPaletteService';
|
||||
import { PreviewDimension } from './PreviewDimension';
|
||||
|
||||
const minCodeWrapSize = 33;
|
||||
const PREVIEW_FRAME_HOST = window.DEBUG
|
||||
? 'http://localhost:7888'
|
||||
: `https://preview.${location.host}`;
|
||||
|
||||
/* global htmlCodeEl
|
||||
*/
|
||||
@@ -114,6 +117,13 @@ export default class ContentWrapFiles extends Component {
|
||||
}
|
||||
componentDidMount() {
|
||||
this.props.onRef(this);
|
||||
|
||||
// Listen for logs from preview frame
|
||||
window.addEventListener('message', e => {
|
||||
if (e.data && e.data.logs) {
|
||||
this.onMessageFromConsole(...e.data.logs);
|
||||
}
|
||||
});
|
||||
this.commandPaletteSubscriptions = [];
|
||||
this.commandPaletteSubscriptions.push(
|
||||
commandPaletteService.subscribe(SWITCH_FILE_EVENT, file => {
|
||||
@@ -226,7 +236,7 @@ export default class ContentWrapFiles extends Component {
|
||||
);
|
||||
// Namespace all file paths to '/user' because thats what the service worker
|
||||
// recognizes.
|
||||
const files = linearizeFiles(assignFilePaths(duplicateFiles, '/user'));
|
||||
const files = linearizeFiles(assignFilePaths(duplicateFiles, ''));
|
||||
|
||||
files.forEach(file => {
|
||||
obj[file.path] = file.content || '';
|
||||
@@ -245,13 +255,19 @@ export default class ContentWrapFiles extends Component {
|
||||
}
|
||||
});
|
||||
|
||||
navigator.serviceWorker.controller.postMessage(obj);
|
||||
// navigator.serviceWorker.controller.postMessage(obj);
|
||||
window.talkFrame.contentWindow.postMessage(obj, '*');
|
||||
|
||||
if (this.detachedWindow) {
|
||||
log('✉️ Sending message to detached window');
|
||||
this.detachedWindow.postMessage({ contents: '/user/index.html' }, '*');
|
||||
this.detachedWindow.postMessage(
|
||||
{ url: `${PREVIEW_FRAME_HOST}/index.html` },
|
||||
'*'
|
||||
);
|
||||
} else {
|
||||
this.frame.src = '/user/index.html';
|
||||
setTimeout(() => {
|
||||
this.frame.src = `${PREVIEW_FRAME_HOST}/index.html`;
|
||||
}, 10);
|
||||
}
|
||||
}
|
||||
cleanupErrors() {
|
||||
@@ -516,6 +532,38 @@ export default class ContentWrapFiles extends Component {
|
||||
this.editor.focus();
|
||||
}
|
||||
|
||||
detachPreview() {
|
||||
if (this.detachedWindow) {
|
||||
this.detachedWindow.focus();
|
||||
return;
|
||||
}
|
||||
const iframeBounds = this.frame.getBoundingClientRect();
|
||||
const iframeWidth = iframeBounds.width;
|
||||
const iframeHeight = iframeBounds.height;
|
||||
document.body.classList.add('is-detached-mode');
|
||||
|
||||
this.detachedWindow = window.open(
|
||||
'./preview.html',
|
||||
'Web Maker',
|
||||
`width=${iframeWidth},height=${iframeHeight},resizable,scrollbars=yes,status=1`
|
||||
);
|
||||
// Trigger initial render in detached window
|
||||
setTimeout(() => {
|
||||
this.setPreviewContent(true);
|
||||
}, 1500);
|
||||
|
||||
var intervalID = window.setInterval(checkWindow => {
|
||||
if (this.detachedWindow && this.detachedWindow.closed) {
|
||||
clearInterval(intervalID);
|
||||
document.body.classList.remove('is-detached-mode');
|
||||
this.detachedWindow = null;
|
||||
// Update main frame preview to get latest changes (which were not
|
||||
// getting reflected while detached window was open)
|
||||
this.setPreviewContent(true);
|
||||
}
|
||||
}, 500);
|
||||
}
|
||||
|
||||
onMessageFromConsole() {
|
||||
const logs = [...arguments].map(arg => {
|
||||
if (
|
||||
@@ -570,7 +618,7 @@ export default class ContentWrapFiles extends Component {
|
||||
this.onMessageFromConsole('> ' + e.target.value);
|
||||
|
||||
/* eslint-disable no-underscore-dangle */
|
||||
this.frame.contentWindow._wmEvaluate(e.target.value);
|
||||
this.frame.contentWindow.postMessage({ exprToEval: e.target.value }, '*');
|
||||
|
||||
/* eslint-enable no-underscore-dangle */
|
||||
|
||||
@@ -650,11 +698,12 @@ export default class ContentWrapFiles extends Component {
|
||||
<div class="demo-side" id="js-demo-side" style="">
|
||||
<iframe
|
||||
ref={el => (this.frame = el)}
|
||||
src="/user/index.html"
|
||||
src={`${PREVIEW_FRAME_HOST}/index.html`}
|
||||
frameborder="0"
|
||||
id="demo-frame"
|
||||
allowfullscreen
|
||||
/>
|
||||
<iframe src={`${PREVIEW_FRAME_HOST}/talk.html`} id="talkFrame" />
|
||||
<PreviewDimension ref={comp => (this.previewDimension = comp)} />
|
||||
<Console
|
||||
logs={this.state.logs}
|
||||
@@ -669,3 +718,4 @@ export default class ContentWrapFiles extends Component {
|
||||
);
|
||||
}
|
||||
}
|
||||
2;
|
||||
|
@@ -1,5 +1,5 @@
|
||||
window.addEventListener('message', e => {
|
||||
// Web app
|
||||
// Recieving from app window
|
||||
if (e.data && e.data.contents && e.data.contents.match(/<html/)) {
|
||||
const frame = document.querySelector('iframe');
|
||||
frame.src = frame.src;
|
||||
@@ -8,8 +8,13 @@ window.addEventListener('message', e => {
|
||||
frame.contentDocument.write(e.data.contents);
|
||||
frame.contentDocument.close();
|
||||
}, 10);
|
||||
} else if (e.data && e.data.match(/preview\.html/)) {
|
||||
// Chrome extension
|
||||
document.querySelector('iframe').src = e.data;
|
||||
}
|
||||
if (e.data && e.data.url && e.data.match(/preview\.html/)) {
|
||||
document.querySelector('iframe').src = e.data.url;
|
||||
}
|
||||
|
||||
// Recieving from preview iframe
|
||||
if (e.data && e.data.logs) {
|
||||
window.opener.postMessage(e.data, '*');
|
||||
}
|
||||
});
|
||||
|
@@ -1,6 +1,28 @@
|
||||
var mainWindow = window.parent.onMessageFromConsole
|
||||
? window.parent
|
||||
: window.parent.opener;
|
||||
let mainWindow = window.parent;
|
||||
|
||||
function sanitizeDomNode(node) {
|
||||
const fakeNode = {
|
||||
nodeName: node.nodeName,
|
||||
nodeType: node.nodeType,
|
||||
tagName: node.tagName,
|
||||
childNodes: [...node.childNodes].map(child => sanitizeDomNode(child)),
|
||||
textContent: node.textContent
|
||||
}
|
||||
if(node.attributes) {
|
||||
fakeNode.attributes = [...node.attributes].map(attribute => ({name:attribute.name, value:attribute.value}))
|
||||
}
|
||||
return fakeNode;
|
||||
}
|
||||
function sendLog(...args) {
|
||||
const sanitizedArgs = [...args].map(arg => {
|
||||
if(arg && arg instanceof HTMLElement) {
|
||||
return sanitizeDomNode(arg)
|
||||
}
|
||||
return arg;
|
||||
})
|
||||
mainWindow.postMessage({ logs: sanitizedArgs },"*");
|
||||
}
|
||||
|
||||
(function() {
|
||||
var logEl,
|
||||
isInitialized = false,
|
||||
@@ -191,16 +213,21 @@ var mainWindow = window.parent.onMessageFromConsole
|
||||
})();
|
||||
screenLog.init({
|
||||
noUi: true,
|
||||
proxyCallback: function() {
|
||||
mainWindow.onMessageFromConsole.apply(null, arguments);
|
||||
proxyCallback: function(...args) {
|
||||
sendLog(...args);
|
||||
}
|
||||
});
|
||||
window._wmEvaluate = function _wmEvaluate(expr) {
|
||||
try {
|
||||
var result = eval(expr);
|
||||
} catch (e) {
|
||||
mainWindow.onMessageFromConsole.call(null, e);
|
||||
sendLog(e.stack || e.message);
|
||||
return;
|
||||
}
|
||||
mainWindow.onMessageFromConsole.call(null, result);
|
||||
sendLog(result)
|
||||
};
|
||||
window.addEventListener('message', e => {
|
||||
if(e.data && e.data.exprToEval) {
|
||||
_wmEvaluate(e.data.exprToEval);
|
||||
}
|
||||
})
|
||||
|
Reference in New Issue
Block a user