Skip to content

Commit

Permalink
adopt worker typings and get rid of any's, #75061
Browse files Browse the repository at this point in the history
  • Loading branch information
jrieken committed Jul 10, 2019
1 parent 65ca26a commit d00ec27
Show file tree
Hide file tree
Showing 3 changed files with 88 additions and 105 deletions.
113 changes: 63 additions & 50 deletions src/vs/workbench/contrib/resources/browser/resourceServiceWorker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,76 +7,89 @@ import { URI } from 'vs/base/common/uri';
import { generateUuid } from 'vs/base/common/uuid';
import { getMediaMime } from 'vs/base/common/mime';

const cacheName = 'vscode-resources';
//https://stackoverflow.com/questions/56356655/structuring-a-typescript-project-with-workers/56374158#56374158
declare var self: ServiceWorkerGlobalScope;

declare const clients: { get(s: string): Promise<any> };
//#region --- installing/activating

self.addEventListener('install', event => {
event.waitUntil(self.skipWaiting());
});

const _pending = new Map<string, Function>();
self.addEventListener('activate', event => {

export function handleMessageEvent(event: MessageEvent): void {
const fn = _pending.get(event.data.token);
event.waitUntil((async () => {
if (self.registration.navigationPreload) {
await self.registration.navigationPreload.enable(); // Enable navigation preloads!
}
await self.clients.claim(); // Become available to all pages
})());
});

//#endregion

//#region --- fetching/caching

const _cacheName = 'vscode-resources';
const _resourcePrefix = '/vscode-resources/fetch';
const _pendingFetch = new Map<string, Function>();

self.addEventListener('message', event => {
const fn = _pendingFetch.get(event.data.token);
if (fn) {
fn(event.data.data, event.data.isExtensionResource);
_pending.delete(event.data.token);
_pendingFetch.delete(event.data.token);
}
}

export async function handleFetchEvent(event: any): Promise<Response | undefined> {
});

const url = URI.parse(event.request.url);
self.addEventListener('fetch', async (event: FetchEvent) => {

if (url.path !== '/vscode-resources/fetch') {
return undefined;
const uri = URI.parse(event.request.url);
if (uri.path !== _resourcePrefix) {
// not a /vscode-resources/fetch-url and therefore
// not (yet?) interesting for us
respondWithDefault(event);
return;
}

if (!event.clientId) {
return undefined;
}
respondWithResource(event, uri);
});

async function respondWithDefault(event: FetchEvent): Promise<Response> {
return await event.preloadResponse || await fetch(event.request);
}

const cachedValue = await caches.open(cacheName).then(cache => cache.match(event.request));
async function respondWithResource(event: FetchEvent, uri: URI): Promise<Response> {
const cachedValue = await caches.open(_cacheName).then(cache => cache.match(event.request));
if (cachedValue) {
return cachedValue;
}

// console.log('fetch', url.query);
try {
return new Promise<Response>(resolve => {

const token = generateUuid();
return new Promise<Response>(async resolve => {

const handle = setTimeout(() => {
resolve(new Response(undefined, { status: 500, statusText: 'timeout' }));
_pending.delete(token);
}, 5000);

_pending.set(token, (data: ArrayBuffer, isExtensionResource: boolean) => {
clearTimeout(handle);
const res = new Response(data, {
status: 200,
headers: { 'Content-Type': getMediaMime(URI.parse(url.query).path) || 'text/plain' }
});

if (!isExtensionResource) {
// only cache extension resources but not other
// resources, esp not workspace resources
resolve(res);

} else {
caches.open(cacheName).then(cache => {
cache.put(event.request, res.clone());
resolve(res);
});
}
const resourceUri = URI.parse(uri.query);

_pendingFetch.set(token, async (data: ArrayBuffer, isExtensionResource: boolean) => {

const res = new Response(data, {
status: 200,
headers: { 'Content-Type': getMediaMime(resourceUri.path) || 'text/plain' }
});

const client = await clients.get(event.clientId);
client.postMessage({ uri: url.query, token });
});
if (isExtensionResource) {
// only cache extension resources but not other
// resources, esp not workspace resources
await caches.open(_cacheName).then(cache => cache.put(event.request, res.clone()));
}

return resolve(res);
});

} catch (err) {
console.error(err);
return new Response(err, { status: 500 });
}
self.clients.get(event.clientId).then(client => {
client.postMessage({ uri: resourceUri, token });
});
});
}

//#endregion
Original file line number Diff line number Diff line change
Expand Up @@ -42,16 +42,17 @@ class ResourceServiceWorker {

private _initFetchHandler(): void {

const fetchListener: (this: ServiceWorkerContainer, ev: MessageEvent) => void = event => {
const uri = URI.parse(event.data.uri);
const fetchListener: (this: ServiceWorkerContainer, ev: ExtendableMessageEvent) => void = event => {
const uri = URI.revive(event.data.uri);

Promise.all([
this._fileService.readFile(uri),
this._isExtensionResource(uri)
]).then(([file, isExtensionResource]) => {

// todo@joh typings
(<any>event.source).postMessage({
if (!event.source) {
return;
}
event.source.postMessage({
token: event.data.token,
data: file.value.buffer.buffer,
isExtensionResource
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,55 +3,24 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

// trigger service worker updates
const _tag = '586d4b79-f5c4-4aff-9a14-2139ddfbb486';

(function () {

type Handler = {
handleFetchEvent(event: Event): Promise<Response | undefined>;
handleMessageEvent(event: MessageEvent): void;
};

const handlerPromise = new Promise<Handler>((resolve, reject) => {
// load loader
const baseUrl = '../../../../../';
importScripts(baseUrl + 'vs/loader.js');
require.config({
baseUrl,
catchError: true
});
require(['vs/workbench/contrib/resources/browser/resourceServiceWorker'], resolve, reject);
});
// This file is to bootstrap AMD so that we can program as usual
// Note the fetch, install, activate event handler must be registered
// when loading the service worker and despite AMD's async nature this
// works. That's because the/our AMD loader uses the sync importScripts
// statement.

self.addEventListener('message', event => {
handlerPromise.then(handler => {
handler.handleMessageEvent(event);
});
});

self.addEventListener('fetch', (event: any) => {
event.respondWith(handlerPromise.then(async handler => {
// try handler
const value = await handler.handleFetchEvent(event);
if (value instanceof Response) {
return value;
}
// try the network (prefetch or fetch)
return await event.preloadResponse || await fetch(event.request);
}));
});
self.addEventListener('install', (event: any) => {
event.waitUntil((self as any).skipWaiting());
});

self.addEventListener('activate', (event: any) => {
// trigger service worker updates
const _tag = '18bd699b-788f-409b-80e0-c5e80b858687';

// loader world
const baseUrl = '../../../../../';
importScripts(baseUrl + 'vs/loader.js');
require.config({
baseUrl,
catchError: true
});
require(['vs/workbench/contrib/resources/browser/resourceServiceWorker'],
() => { },
err => console.error(err)
);

event.waitUntil((async () => {
if ((self as any).registration.navigationPreload) {
await (self as any).registration.navigationPreload.enable(); // Enable navigation preloads!
}
await (self as any).clients.claim(); // Become available to all pages
})());
});
})();

0 comments on commit d00ec27

Please sign in to comment.