使import可以解析node内部模块

This commit is contained in:
nonameShijian 2024-04-07 01:01:43 +08:00
parent 716a4bf526
commit 3ac1d92181
2 changed files with 164 additions and 127 deletions

View File

@ -6,6 +6,28 @@
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="viewport" content="user-scalable=no, viewport-fit=cover">
<title>无名杀</title>
<script>
if (typeof window.require == 'function' &&
typeof window.process == 'object' &&
typeof window.__dirname == 'string') {
// 使importMap解析node内置模块
const builtinModules = require('module').builtinModules;
if (Array.isArray(builtinModules)) {
const importMap = {
imports: {}
};
for (const module of builtinModules) {
importMap.imports[module] =
importMap.imports[`node:${module}`] =
`./noname-builtinModules/${module}`
}
const im = document.createElement('script');
im.type = 'importmap';
im.textContent = JSON.stringify(importMap);
document.currentScript.after(im);
}
}
</script>
<script>
"use strict";
(() => {

View File

@ -59,146 +59,161 @@ self.addEventListener('fetch', event => {
event.respondWith(rep);
return;
}
if (!['.ts', '.json', '.vue', 'css'].some(ext => request.url.endsWith(ext))) return;
if (!['.ts', '.json', '.vue', 'css'].some(ext => request.url.endsWith(ext)) && !request.url.replace(location.origin, '').startsWith('/noname-builtinModules/')) return;
if (request.url.endsWith('.d.ts')) return;
if (request.url.endsWith('.json') || request.url.endsWith('css')) {
if (!event.request.headers.get('origin')) return;
}
// 请求原文件
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, {
module: ts.ModuleKind.ES2015,
//@todo: ES2019 -> ES2020
target: ts.ScriptTarget.ES2019,
inlineSourceMap: true,
resolveJsonModule: true,
esModuleInterop: true,
}, 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 = [];
// 保存url并且拼接参数
const url = new URL(request.url);
const scriptSearchParams = new URLSearchParams(url.search.slice(1));
scriptSearchParams.append('type', 'script');
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, {
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, {
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重新指向
}, 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 = [];
// 保存url并且拼接参数
const url = new URL(request.url);
const scriptSearchParams = new URLSearchParams(url.search.slice(1));
scriptSearchParams.append('type', 'script');
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'`)
);
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,
}
});
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 { __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,
);
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);
}
});
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);`;
}
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);`;
}
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;
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;
})
})
})
.catch(e => {
console.error(request.url, '编译失败: ', e);
throw e;
})
);
.catch(e => {
console.error(request.url, '编译失败: ', e);
throw e;
})
);
}
});