forked from Cellivar/WebZLP
-
Notifications
You must be signed in to change notification settings - Fork 0
/
demo-sw.js
110 lines (95 loc) · 4.28 KB
/
demo-sw.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
// Has to be a hard version so the URL doesn't redirect, which prevents loading.
importScripts('https://unpkg.com/[email protected]/lib/typescript.js');
const log = (...obj) => console.log('SHENANIGANS', ...obj);
self.addEventListener('install', function(event) {
event.waitUntil(self.skipWaiting()); // Activate worker immediately
});
// Call clients.claim so that we intercept requests even on initial page load.
self.addEventListener('activate', () => self.clients.claim());
// Intercept fetch requests for modules and compile the intercepted typescript.
self.addEventListener('fetch', (event) => {
// Start off easy: if it's a request for a TS file, transpile it.
if (event.request.url.endsWith('.ts')) {
log('Fetching', event.request.url, 'as just typescript');
event.respondWith(transpileTypeScript(event.request.url));
return;
}
// Next up is 'no extension'. In classical TS imports you omit any extension
// when pulling in a file reference, interpret those as TS and be okay if it
// ends up 404'ing.
if (!event.request.url.endsWith('.ts')
&& !event.request.url.endsWith('.js')
&& event.request.destination === 'script'
&& event.isTrusted) {
log("Interpreting", event.request.url, "as TypeScript request");
// TS import statements elide the .ts extension, but this fetch is destined for a script
// and it's not a javascript file. Assume it's actually a TS module.
// This is _mostly_ safe as js run through the ts compiler is unmodified.
event.respondWith(transpileTypeScript(event.request.url + '.ts'));
return;
}
// Because TypeScript has chosen violence we have to use .js extensions in
// import statements. This is considered correct behavior. Sure okay.
// https://github.com/microsoft/TypeScript/issues/16577#issuecomment-703190339
// As such, we must test the fetch URL and see if it 404s, if so retry with
// a .ts extension instead.
if (event.request.url.endsWith('.js')
&& event.request.destination === 'script'
&& event.isTrusted) {
log('Testing', event.request.url, 'as maybe javascript');
event.respondWith(maybeFetchJs(event.request.url));
return;
}
});
// Perform on-demand compile of typescript when it's requested by the page.
self.addEventListener('message', async ({data: [sourceUrl, sourceCode]}) => {
const transpiled = await runTranspile(sourceUrl, sourceCode);
self.clients.matchAll({
includeUncontrolled: true,
type: 'window'
}).then((clients) => {
if (clients && clients.length) {
clients[0].postMessage(transpiled);
}
});
});
const maybeFetchJs = async (requestUrl) => {
const maybeJs = await fetch(requestUrl);
if (maybeJs.status !== 404) {
return maybeJs;
}
// Try it again with a TS extension
const tsUrl = requestUrl.substr(0, requestUrl.lastIndexOf('.')) + '.ts';
log('Rewrote JS URL to', tsUrl, 'and fetching as typescript');
return transpileTypeScript(tsUrl);
}
const runTranspile = async (src, code) => {
log('Compiling:', src);
return await self.ts.transpile(code, {
"module": "es6",
"target": "esnext",
"lib": ["dom", "esnext"]
});
}
const transpileTypeScript = async (requestUrl) => {
const response = await fetch(requestUrl); // Fetch the TypeScript code
const code = await response.text(); // Get the TypeScript code as a string
const typescriptCache = await caches.open('typescript');
const cachedResponse = await typescriptCache.match(requestUrl);
if (cachedResponse && response.headers.get('etag') === cachedResponse.headers.get('etag')) {
log('Using compile cache:', requestUrl);
return cachedResponse;
}
const transpiledCode = await runTranspile(requestUrl, code);
const responseOptions = { // Return the transpiled code as the appropriate content type
headers: {
'Content-Type': 'application/javascript',
etag: response.headers.get('etag')
},
};
const transpiledResponse = new Response(transpiledCode, responseOptions);
if (!requestUrl.startsWith('https://localhost')) {
typescriptCache.put(requestUrl, transpiledResponse.clone());
}
return transpiledResponse;
}