mirror of
https://github.com/chinchang/web-maker.git
synced 2025-08-02 19:37:29 +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 merge = require('merge-stream');
|
||||||
const zip = require('gulp-zip');
|
const zip = require('gulp-zip');
|
||||||
var packageJson = JSON.parse(fs.readFileSync('./package.json'));
|
var packageJson = JSON.parse(fs.readFileSync('./package.json'));
|
||||||
|
const connect = require('gulp-connect');
|
||||||
|
|
||||||
function minifyJs(fileName) {
|
function minifyJs(fileName) {
|
||||||
const content = fs.readFileSync(fileName, 'utf8');
|
const content = fs.readFileSync(fileName, 'utf8');
|
||||||
@@ -199,6 +200,14 @@ gulp.task('cleanup', function() {
|
|||||||
return child_process.execSync('rm -rf build');
|
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) {
|
gulp.task('release', function(callback) {
|
||||||
runSequence(
|
runSequence(
|
||||||
'runWebpack',
|
'runWebpack',
|
||||||
|
@@ -3,10 +3,10 @@
|
|||||||
"version": "3.6.2",
|
"version": "3.6.2",
|
||||||
"description": "A blazing fast & offline web playground",
|
"description": "A blazing fast & offline web playground",
|
||||||
"scripts": {
|
"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",
|
"build": "preact build --template src/index.html --no-prerender --service-worker false",
|
||||||
"serve": "preact build && preact serve",
|
"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",
|
"lint": "eslint src",
|
||||||
"test": "jest",
|
"test": "jest",
|
||||||
"precommit": "lint-staged",
|
"precommit": "lint-staged",
|
||||||
@@ -65,6 +65,7 @@
|
|||||||
"copy-webpack-plugin": "^4.5.1",
|
"copy-webpack-plugin": "^4.5.1",
|
||||||
"esprima": "^4.0.0",
|
"esprima": "^4.0.0",
|
||||||
"firebase": "^5.5.8",
|
"firebase": "^5.5.8",
|
||||||
|
"gulp-connect": "^5.7.0",
|
||||||
"jszip": "^3.1.5",
|
"jszip": "^3.1.5",
|
||||||
"preact": "^8.2.6",
|
"preact": "^8.2.6",
|
||||||
"preact-compat": "^3.17.0",
|
"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() {
|
async initEditor() {
|
||||||
console.log('init editor');
|
|
||||||
|
|
||||||
this.editorReadyDeferred = deferred();
|
this.editorReadyDeferred = deferred();
|
||||||
await this.loadDeps();
|
await this.loadDeps();
|
||||||
|
|
||||||
|
@@ -69,6 +69,13 @@ export default class ContentWrap extends Component {
|
|||||||
}
|
}
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
this.props.onRef(this);
|
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) {
|
onHtmlCodeChange(editor, change) {
|
||||||
@@ -160,7 +167,7 @@ export default class ContentWrap extends Component {
|
|||||||
: `${location.origin}`;
|
: `${location.origin}`;
|
||||||
var src = `filesystem:${origin}/temporary/preview.html`;
|
var src = `filesystem:${origin}/temporary/preview.html`;
|
||||||
if (this.detachedWindow) {
|
if (this.detachedWindow) {
|
||||||
this.detachedWindow.postMessage(src, '*');
|
this.detachedWindow.postMessage({ url: src }, '*');
|
||||||
} else {
|
} else {
|
||||||
this.frame.src = src;
|
this.frame.src = src;
|
||||||
}
|
}
|
||||||
|
@@ -22,6 +22,9 @@ import { commandPaletteService } from '../commandPaletteService';
|
|||||||
import { PreviewDimension } from './PreviewDimension';
|
import { PreviewDimension } from './PreviewDimension';
|
||||||
|
|
||||||
const minCodeWrapSize = 33;
|
const minCodeWrapSize = 33;
|
||||||
|
const PREVIEW_FRAME_HOST = window.DEBUG
|
||||||
|
? 'http://localhost:7888'
|
||||||
|
: `https://preview.${location.host}`;
|
||||||
|
|
||||||
/* global htmlCodeEl
|
/* global htmlCodeEl
|
||||||
*/
|
*/
|
||||||
@@ -114,6 +117,13 @@ export default class ContentWrapFiles extends Component {
|
|||||||
}
|
}
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
this.props.onRef(this);
|
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 = [];
|
||||||
this.commandPaletteSubscriptions.push(
|
this.commandPaletteSubscriptions.push(
|
||||||
commandPaletteService.subscribe(SWITCH_FILE_EVENT, file => {
|
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
|
// Namespace all file paths to '/user' because thats what the service worker
|
||||||
// recognizes.
|
// recognizes.
|
||||||
const files = linearizeFiles(assignFilePaths(duplicateFiles, '/user'));
|
const files = linearizeFiles(assignFilePaths(duplicateFiles, ''));
|
||||||
|
|
||||||
files.forEach(file => {
|
files.forEach(file => {
|
||||||
obj[file.path] = file.content || '';
|
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) {
|
if (this.detachedWindow) {
|
||||||
log('✉️ Sending message to detached window');
|
log('✉️ Sending message to detached window');
|
||||||
this.detachedWindow.postMessage({ contents: '/user/index.html' }, '*');
|
this.detachedWindow.postMessage(
|
||||||
|
{ url: `${PREVIEW_FRAME_HOST}/index.html` },
|
||||||
|
'*'
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
this.frame.src = '/user/index.html';
|
setTimeout(() => {
|
||||||
|
this.frame.src = `${PREVIEW_FRAME_HOST}/index.html`;
|
||||||
|
}, 10);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
cleanupErrors() {
|
cleanupErrors() {
|
||||||
@@ -516,6 +532,38 @@ export default class ContentWrapFiles extends Component {
|
|||||||
this.editor.focus();
|
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() {
|
onMessageFromConsole() {
|
||||||
const logs = [...arguments].map(arg => {
|
const logs = [...arguments].map(arg => {
|
||||||
if (
|
if (
|
||||||
@@ -570,7 +618,7 @@ export default class ContentWrapFiles extends Component {
|
|||||||
this.onMessageFromConsole('> ' + e.target.value);
|
this.onMessageFromConsole('> ' + e.target.value);
|
||||||
|
|
||||||
/* eslint-disable no-underscore-dangle */
|
/* 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 */
|
/* eslint-enable no-underscore-dangle */
|
||||||
|
|
||||||
@@ -650,11 +698,12 @@ export default class ContentWrapFiles extends Component {
|
|||||||
<div class="demo-side" id="js-demo-side" style="">
|
<div class="demo-side" id="js-demo-side" style="">
|
||||||
<iframe
|
<iframe
|
||||||
ref={el => (this.frame = el)}
|
ref={el => (this.frame = el)}
|
||||||
src="/user/index.html"
|
src={`${PREVIEW_FRAME_HOST}/index.html`}
|
||||||
frameborder="0"
|
frameborder="0"
|
||||||
id="demo-frame"
|
id="demo-frame"
|
||||||
allowfullscreen
|
allowfullscreen
|
||||||
/>
|
/>
|
||||||
|
<iframe src={`${PREVIEW_FRAME_HOST}/talk.html`} id="talkFrame" />
|
||||||
<PreviewDimension ref={comp => (this.previewDimension = comp)} />
|
<PreviewDimension ref={comp => (this.previewDimension = comp)} />
|
||||||
<Console
|
<Console
|
||||||
logs={this.state.logs}
|
logs={this.state.logs}
|
||||||
@@ -669,3 +718,4 @@ export default class ContentWrapFiles extends Component {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
2;
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
window.addEventListener('message', e => {
|
window.addEventListener('message', e => {
|
||||||
// Web app
|
// Recieving from app window
|
||||||
if (e.data && e.data.contents && e.data.contents.match(/<html/)) {
|
if (e.data && e.data.contents && e.data.contents.match(/<html/)) {
|
||||||
const frame = document.querySelector('iframe');
|
const frame = document.querySelector('iframe');
|
||||||
frame.src = frame.src;
|
frame.src = frame.src;
|
||||||
@@ -8,8 +8,13 @@ window.addEventListener('message', e => {
|
|||||||
frame.contentDocument.write(e.data.contents);
|
frame.contentDocument.write(e.data.contents);
|
||||||
frame.contentDocument.close();
|
frame.contentDocument.close();
|
||||||
}, 10);
|
}, 10);
|
||||||
} else if (e.data && e.data.match(/preview\.html/)) {
|
}
|
||||||
// Chrome extension
|
if (e.data && e.data.url && e.data.match(/preview\.html/)) {
|
||||||
document.querySelector('iframe').src = e.data;
|
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
|
let mainWindow = window.parent;
|
||||||
? window.parent
|
|
||||||
: window.parent.opener;
|
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() {
|
(function() {
|
||||||
var logEl,
|
var logEl,
|
||||||
isInitialized = false,
|
isInitialized = false,
|
||||||
@@ -191,16 +213,21 @@ var mainWindow = window.parent.onMessageFromConsole
|
|||||||
})();
|
})();
|
||||||
screenLog.init({
|
screenLog.init({
|
||||||
noUi: true,
|
noUi: true,
|
||||||
proxyCallback: function() {
|
proxyCallback: function(...args) {
|
||||||
mainWindow.onMessageFromConsole.apply(null, arguments);
|
sendLog(...args);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
window._wmEvaluate = function _wmEvaluate(expr) {
|
window._wmEvaluate = function _wmEvaluate(expr) {
|
||||||
try {
|
try {
|
||||||
var result = eval(expr);
|
var result = eval(expr);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
mainWindow.onMessageFromConsole.call(null, e);
|
sendLog(e.stack || e.message);
|
||||||
return;
|
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