From 840c2eb6d963a66687485a17bc2d0a62d71ae346 Mon Sep 17 00:00:00 2001 From: Dmitry Vasilev Date: Mon, 28 Nov 2022 23:12:55 +0800 Subject: [PATCH] load html pages from service worker --- index.html | 13 ------------- service_worker.js | 41 +++++++++++++++++++++++++++++------------ src/editor/ui.js | 2 +- src/effects.js | 11 +++++++---- src/index.js | 8 ++++++-- 5 files changed, 43 insertions(+), 32 deletions(-) diff --git a/index.html b/index.html index 00ea511..8e5fd17 100644 --- a/index.html +++ b/index.html @@ -382,19 +382,6 @@ if(new URLSearchParams(window.location.search).get('leporello') == null) { await import('./src/launch.js'); - // TODO remove? - /* - const {init} = await import('./src/index.js'); - ( - document.readyState == 'complete' - ? Promise.resolve() - : new Promise(resolve => - window.addEventListener('load', resolve) - ) - ).then(() => { - init(document.getElementById('app')) - }) - */ } diff --git a/service_worker.js b/service_worker.js index 6867862..3d230f7 100644 --- a/service_worker.js +++ b/service_worker.js @@ -29,21 +29,38 @@ self.addEventListener('message', async function(e) { e.ports[0].postMessage(reply) }) -// Fake URL base prepended by code responsible for module loading -const FAKE_URL_BASE = 'https://leporello.import/' +// Fake directory, http requests to this directory intercepted by service_worker +const FILES_ROOT = '__leporello_files' self.addEventListener("fetch", event => { - if(event.request.url.startsWith(FAKE_URL_BASE)) { - if(dir_handle != null) { - const headers = new Headers([ - ['Content-Type', 'text/javascript'] - ]) - const path = event.request.url.replace(FAKE_URL_BASE, '') - const response = read_file(dir_handle, path).then(file => - new Response(file, {headers}) - ) - event.respondWith(response) + const url = new URL(event.request.url) + if(url.pathname.startsWith('/' + FILES_ROOT)) { + const path = url.pathname.replace('/' + FILES_ROOT + '/', '') + + let file + + if(path == '__leporello_blank.html') { + file = Promise.resolve('') + } else if(dir_handle != null) { + file = read_file(dir_handle, path) + } else { + // Delegate request to browser + return } + + const headers = new Headers([ + [ + 'Content-Type', + path.endsWith('.js') || path.endsWith('.mjs') + ? 'text/javascript' + : 'text/html' + ] + ]) + + const response = file.then(file => + new Response(file, {headers}) + ) + event.respondWith(response) } }) diff --git a/src/editor/ui.js b/src/editor/ui.js index 876968b..6880711 100644 --- a/src/editor/ui.js +++ b/src/editor/ui.js @@ -227,7 +227,7 @@ export class UI { state.html_file == f ? { value: f, selected: true } : { value: f}, - f == '' ? 'abount:blank' : f + f == '' ? 'about:blank' : f ) ) ), diff --git a/src/effects.js b/src/effects.js index 772b4ae..e418304 100644 --- a/src/effects.js +++ b/src/effects.js @@ -6,7 +6,7 @@ import { get_async_calls } from './calltree.js' import {FLAGS} from './feature_flags.js' -import {exec} from './index.js' +import {exec, FILES_ROOT} from './index.js' // Imports in the context of `run_window`, so global variables in loaded // modules refer to that window's context @@ -26,9 +26,12 @@ const load_external_imports = async state => { /^\w+:\/\//.test(u) ? // starts with protocol, import as is u - : //local path, load using File System Access API, see service_worker.js - // Append fake host that will be intercepted in service worker - 'https://leporello.import/' + u + : // local path, load using File System Access API, see service_worker.js + // Append special URL segment that will be intercepted in service worker + // Note that we use the same origin as current page (where Leporello + // is hosted), so Leporello can access window object for custom + // `html_file` + window.location.origin + '/' + FILES_ROOT + '/' + u )) ) const modules = Object.fromEntries( diff --git a/src/index.js b/src/index.js index db46492..81373e2 100644 --- a/src/index.js +++ b/src/index.js @@ -8,6 +8,9 @@ const EXAMPLE = `const fib = n => : fib(n - 1) + fib(n - 2) fib(6)` +// Fake directory, http requests to this directory intercepted by service_worker +export const FILES_ROOT = '__leporello_files' + const set_error_handler = w => { // TODO err.message w.onerror = (msg, src, lineNum, colNum, err) => { @@ -19,9 +22,10 @@ const set_error_handler = w => { } const get_html_url = state => { + const base = window.location.origin + '/' + FILES_ROOT + '/' return state.html_file == '' - ? 'about:blank' - : window.location.origin + '/' + state.html_file + '?leporello' + ? base + '__leporello_blank.html' + : base + state.html_file + '?leporello' } // By default run code in hidden iframe, until user explicitly opens visible