2024-02-01 11:22:37 +00:00
|
|
|
|
/**
|
2024-03-26 08:20:53 +00:00
|
|
|
|
* @type { ServiceWorkerGlobalScope } 提供ServiceWorker的代码提示
|
2024-02-01 11:22:37 +00:00
|
|
|
|
*/
|
2024-03-26 08:20:53 +00:00
|
|
|
|
// @ts-ignore
|
|
|
|
|
var self = globalThis;
|
|
|
|
|
// 以副作用导入typescript,以保证require也可以同步使用
|
|
|
|
|
import './game/typescript.js';
|
2024-03-08 16:37:33 +00:00
|
|
|
|
/**
|
2024-03-26 08:20:53 +00:00
|
|
|
|
* @type { import('typescript') }
|
2024-03-08 16:37:33 +00:00
|
|
|
|
*/
|
2024-03-26 08:20:53 +00:00
|
|
|
|
var ts = globalThis.ts;
|
|
|
|
|
// sfc以正常的esmodule使用
|
|
|
|
|
import * as sfc from './game/compiler-sfc.esm-browser.js';
|
2024-02-01 11:22:37 +00:00
|
|
|
|
if (typeof ts != 'undefined') {
|
2024-03-26 08:20:53 +00:00
|
|
|
|
console.log(`ts loaded`, ts.version);
|
2024-02-01 11:22:37 +00:00
|
|
|
|
} else {
|
2024-03-26 08:20:53 +00:00
|
|
|
|
console.error(`ts undefined`);
|
2024-02-01 11:22:37 +00:00
|
|
|
|
}
|
2024-03-26 08:20:53 +00:00
|
|
|
|
|
2024-03-08 16:37:33 +00:00
|
|
|
|
if (typeof sfc != 'undefined') {
|
2024-03-26 08:20:53 +00:00
|
|
|
|
console.log(`sfc loaded`, sfc.version);
|
2024-03-08 16:37:33 +00:00
|
|
|
|
sfc.registerTS(() => ts);
|
|
|
|
|
} else {
|
2024-03-26 08:20:53 +00:00
|
|
|
|
console.error(`sfc undefined`);
|
2024-03-08 16:37:33 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
console.log('serviceWorker version 2.3');
|
2024-02-05 10:17:53 +00:00
|
|
|
|
|
|
|
|
|
self.addEventListener("install", (event) => {
|
|
|
|
|
// The promise that skipWaiting() returns can be safely ignored.
|
|
|
|
|
self.skipWaiting();
|
|
|
|
|
});
|
2024-02-01 11:22:37 +00:00
|
|
|
|
|
2024-02-16 10:54:49 +00:00
|
|
|
|
self.addEventListener("activate", (event) => {
|
|
|
|
|
// 当一个 service worker 被初始注册时,页面在下次加载之前不会使用它。 claim() 方法会立即控制这些页面
|
2024-03-26 08:20:53 +00:00
|
|
|
|
event.waitUntil(self.clients.claim());
|
2024-02-16 10:54:49 +00:00
|
|
|
|
});
|
|
|
|
|
|
2024-02-01 11:22:37 +00:00
|
|
|
|
self.addEventListener('message', event => {
|
|
|
|
|
console.log(event.data);
|
|
|
|
|
});
|
|
|
|
|
|
2024-03-08 16:37:33 +00:00
|
|
|
|
/**
|
|
|
|
|
* 将vue编译的结果放在这里,调用的时候直接返回就好了
|
|
|
|
|
*/
|
|
|
|
|
const vueFileMap = new Map();
|
|
|
|
|
|
2024-02-01 11:22:37 +00:00
|
|
|
|
self.addEventListener('fetch', event => {
|
|
|
|
|
const request = event.request;
|
|
|
|
|
if (typeof request.url != 'string') return console.log(request);
|
2024-03-08 16:37:33 +00:00
|
|
|
|
if (vueFileMap.has(request.url)) {
|
|
|
|
|
const rep = new Response(new Blob([vueFileMap.get(request.url)], { type: "text/javascript" }), {
|
|
|
|
|
status: 200,
|
|
|
|
|
statusText: "OK",
|
|
|
|
|
headers: new Headers({
|
|
|
|
|
"Content-Type": "text/javascript"
|
|
|
|
|
}),
|
|
|
|
|
});
|
|
|
|
|
event.respondWith(rep);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2024-04-06 17:01:43 +00:00
|
|
|
|
if (!['.ts', '.json', '.vue', 'css'].some(ext => request.url.endsWith(ext)) && !request.url.replace(location.origin, '').startsWith('/noname-builtinModules/')) return;
|
2024-02-05 10:17:53 +00:00
|
|
|
|
if (request.url.endsWith('.d.ts')) return;
|
2024-03-19 12:48:12 +00:00
|
|
|
|
if (request.url.endsWith('.json') || request.url.endsWith('css')) {
|
2024-02-05 10:17:53 +00:00
|
|
|
|
if (!event.request.headers.get('origin')) return;
|
|
|
|
|
}
|
2024-04-06 17:01:43 +00:00
|
|
|
|
if (request.url.replace(location.origin, '').startsWith('/noname-builtinModules/')) {
|
|
|
|
|
const moduleName = request.url.replace(location.origin + '/noname-builtinModules/', '');
|
|
|
|
|
console.log('正在编译', moduleName);
|
|
|
|
|
let js = `const module = require('${ moduleName }');\nexport default module;`;
|
|
|
|
|
const rep = new Response(new Blob([js], { type: "text/javascript" }), {
|
|
|
|
|
status: 200,
|
|
|
|
|
statusText: "OK",
|
|
|
|
|
headers: new Headers({
|
|
|
|
|
"Content-Type": "text/javascript"
|
|
|
|
|
}),
|
|
|
|
|
});
|
|
|
|
|
console.log(moduleName, '编译成功');
|
|
|
|
|
event.respondWith(Promise.resolve(rep));
|
|
|
|
|
} else {
|
|
|
|
|
// 请求原文件
|
|
|
|
|
const res = fetch(request.url, {
|
|
|
|
|
method: request.method,
|
|
|
|
|
mode: "no-cors",
|
|
|
|
|
headers: new Headers({
|
|
|
|
|
"Content-Type": "text/plain"
|
|
|
|
|
}),
|
|
|
|
|
});
|
|
|
|
|
// 修改请求结果
|
|
|
|
|
event.respondWith(
|
|
|
|
|
res.then(res => {
|
|
|
|
|
if (res.status != 200) return res;
|
|
|
|
|
console.log('正在编译', request.url);
|
|
|
|
|
return res.text().then(text => {
|
|
|
|
|
let js = '';
|
|
|
|
|
if (request.url.endsWith('.json')) {
|
|
|
|
|
js = `export default ${ text }`;
|
|
|
|
|
} else if (request.url.endsWith('.ts')) {
|
|
|
|
|
js = ts.transpile(text, {
|
2024-03-14 13:37:37 +00:00
|
|
|
|
module: ts.ModuleKind.ES2015,
|
2024-03-26 08:20:53 +00:00
|
|
|
|
//@todo: ES2019 -> ES2020
|
2024-03-14 13:37:37 +00:00
|
|
|
|
target: ts.ScriptTarget.ES2019,
|
|
|
|
|
inlineSourceMap: true,
|
|
|
|
|
resolveJsonModule: true,
|
|
|
|
|
esModuleInterop: true,
|
2024-04-06 17:01:43 +00:00
|
|
|
|
}, request.url);
|
|
|
|
|
} else if (request.url.endsWith('.vue')) {
|
|
|
|
|
const id = Date.now().toString();
|
|
|
|
|
const scopeId = `data-v-${ id }`;
|
|
|
|
|
// 后续处理sourceMap合并
|
|
|
|
|
const { descriptor } = sfc.parse(text, { filename: request.url, sourceMap: true });
|
|
|
|
|
// console.log({ descriptor });
|
|
|
|
|
const hasScoped = descriptor.styles.some(s => s.scoped);
|
|
|
|
|
// 编译 script,因为可能有 script setup,还要进行 css 变量注入
|
|
|
|
|
const script = sfc.compileScript(descriptor, {
|
|
|
|
|
id: scopeId,
|
|
|
|
|
inlineTemplate: true,
|
|
|
|
|
templateOptions: {
|
|
|
|
|
scoped: hasScoped,
|
|
|
|
|
compilerOptions: {
|
|
|
|
|
scopeId: hasScoped ? scopeId : undefined,
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
// 用于存放代码,最后 join('\n') 合并成一份完整代码
|
|
|
|
|
const codeList = [];
|
2024-03-26 08:20:53 +00:00
|
|
|
|
|
2024-04-06 17:01:43 +00:00
|
|
|
|
// 保存url并且拼接参数
|
|
|
|
|
const url = new URL(request.url);
|
|
|
|
|
const scriptSearchParams = new URLSearchParams(url.search.slice(1));
|
|
|
|
|
scriptSearchParams.append('type', 'script');
|
2024-03-08 16:37:33 +00:00
|
|
|
|
|
2024-04-06 17:01:43 +00:00
|
|
|
|
const templateSearchParams = new URLSearchParams(url.search.slice(1));
|
|
|
|
|
templateSearchParams.append('type', 'template');
|
|
|
|
|
|
|
|
|
|
vueFileMap.set(
|
|
|
|
|
url.origin + url.pathname + '?' + scriptSearchParams.toString(),
|
|
|
|
|
// 重写 default
|
|
|
|
|
sfc.rewriteDefault(script.attrs && script.attrs.lang == 'ts' ? ts.transpile(script.content, {
|
|
|
|
|
module: ts.ModuleKind.ES2015,
|
|
|
|
|
//@todo: ES2019 -> ES2020
|
|
|
|
|
target: ts.ScriptTarget.ES2019,
|
|
|
|
|
inlineSourceMap: true,
|
|
|
|
|
resolveJsonModule: true,
|
|
|
|
|
esModuleInterop: true,
|
|
|
|
|
}, url.origin + url.pathname + '?' + scriptSearchParams.toString()) : script.content, "__sfc_main__")
|
|
|
|
|
.replace(`const __sfc_main__`, `export const __sfc_main__`)
|
|
|
|
|
// import vue重新指向
|
|
|
|
|
.replaceAll(`from "vue"`, `from "/game/vue.esm-browser.js"`)
|
|
|
|
|
.replaceAll(`from 'vue'`, `from '/game/vue.esm-browser.js'`)
|
|
|
|
|
);
|
2024-03-08 16:37:33 +00:00
|
|
|
|
|
2024-04-06 17:01:43 +00:00
|
|
|
|
codeList.push(`import { __sfc_main__ } from '${ url.origin + url.pathname + '?' + scriptSearchParams.toString() }'`);
|
|
|
|
|
codeList.push(`__sfc_main__.__scopeId = '${ scopeId }'`);
|
|
|
|
|
|
|
|
|
|
// 编译模板,转换成 render 函数
|
|
|
|
|
const template = sfc.compileTemplate({
|
|
|
|
|
source: descriptor.template ? descriptor.template.content : '',
|
|
|
|
|
filename: request.url, // 用于错误提示
|
|
|
|
|
id: scopeId,
|
|
|
|
|
scoped: hasScoped,
|
|
|
|
|
compilerOptions: {
|
|
|
|
|
scopeId: hasScoped ? scopeId : undefined,
|
|
|
|
|
}
|
2024-03-08 16:37:33 +00:00
|
|
|
|
});
|
2024-04-06 17:01:43 +00:00
|
|
|
|
|
|
|
|
|
vueFileMap.set(
|
|
|
|
|
url.origin + url.pathname + '?' + templateSearchParams.toString(),
|
|
|
|
|
template.code
|
|
|
|
|
// .replace(`function render(_ctx, _cache) {`, str => str + 'console.log(_ctx);')
|
|
|
|
|
.replaceAll(`from "vue"`, `from "/game/vue.esm-browser.js"`)
|
|
|
|
|
.replaceAll(`from 'vue'`, `from '/game/vue.esm-browser.js'`)
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
codeList.push(`import { render } from '${ url.origin + url.pathname + '?' + templateSearchParams.toString() }'`);
|
|
|
|
|
codeList.push(`__sfc_main__.render = render;`);
|
|
|
|
|
codeList.push(`export default __sfc_main__;`);
|
|
|
|
|
// 一个 Vue 文件,可能有多个 style 标签
|
|
|
|
|
let styleIndex = 0;
|
|
|
|
|
for (const styleBlock of descriptor.styles) {
|
|
|
|
|
const styleCode = sfc.compileStyle({
|
|
|
|
|
source: styleBlock.content,
|
|
|
|
|
id,
|
|
|
|
|
filename: request.url,
|
|
|
|
|
scoped: styleBlock.scoped,
|
|
|
|
|
});
|
|
|
|
|
const varName = `el${ styleIndex }`;
|
|
|
|
|
const styleDOM = `let ${ varName } = document.createElement('style');\n${ varName }.innerHTML = \`${ styleCode.code }\`;\ndocument.body.append(${ varName });`;
|
|
|
|
|
codeList.push(styleDOM);
|
|
|
|
|
}
|
|
|
|
|
js = codeList.join('\n');
|
|
|
|
|
// console.log(js);
|
|
|
|
|
} else if (request.url.endsWith('css')) {
|
|
|
|
|
const id = Date.now().toString();
|
|
|
|
|
const scopeId = `data-v-${ id }`;
|
|
|
|
|
js = `const style = document.createElement('style');
|
|
|
|
|
style.setAttribute('type', 'text/css');
|
|
|
|
|
style.setAttribute('data-vue-dev-id', \`${ scopeId }\`);
|
|
|
|
|
style.textContent = ${ JSON.stringify(text) };
|
|
|
|
|
document.head.appendChild(style);`;
|
2024-03-08 16:37:33 +00:00
|
|
|
|
}
|
2024-04-06 17:01:43 +00:00
|
|
|
|
const rep = new Response(new Blob([js], { type: "text/javascript" }), {
|
|
|
|
|
status: 200,
|
|
|
|
|
statusText: "OK",
|
|
|
|
|
headers: new Headers({
|
|
|
|
|
"Content-Type": "text/javascript"
|
|
|
|
|
}),
|
|
|
|
|
});
|
|
|
|
|
console.log(request.url, '编译成功');
|
|
|
|
|
return rep;
|
|
|
|
|
})
|
2024-02-02 12:49:32 +00:00
|
|
|
|
})
|
2024-04-06 17:01:43 +00:00
|
|
|
|
.catch(e => {
|
|
|
|
|
console.error(request.url, '编译失败: ', e);
|
|
|
|
|
throw e;
|
|
|
|
|
})
|
|
|
|
|
);
|
|
|
|
|
}
|
2024-02-01 11:22:37 +00:00
|
|
|
|
});
|