Open5
ブラウザーで動くNode.jsを自作する

require関数
鬼門となるのが、Node.js固有の関数であるrequire
関数
これはmemfsとかでメモリ上でファイルを管理することで解決できそう!
以下思いついたコード
export function runCode(filePath: string, fs: IFs) {
function customRequire(filePath: string) {
let tmpFilePath = filePath;
// 拡張子がない場合、様々な拡張子を試す
if (!/\.[^/.]+$/.test(filePath)) {
let flag = false;
for (const ext of [".js", ".mjs", ".cjs"]) {
try {
tmpFilePath = trimFirstDot(filePath) + ext;
if (fs.existsSync(tmpFilePath)) {
flag = true;
break;
}
} catch (e) {
console.log(e);
// ファイルが見つからなかった場合、次の拡張子を試す
}
}
if (!flag) {
throw new Error(`Cannot find module '${filePath}'`);
}
}
const exports = {};
const module = { exports };
const code = fs.readFileSync(tmpFilePath, "utf8");
const wrappedCode = code;
const script = new Function("require", "module", "exports", wrappedCode);
script(customRequire, module, exports);
return module.exports;
}
customRequire(filePath);
}
例えば以下のようなファイルがあったときに
const files = {
"/main.js": `
const a = require('./hello');
a.hello();
`,
"/hello.js": `
module.exports = {
hello: () => console.log('hello')
};
`,
};
以下のように実行できる
const { fs } = memfs(files, "/");
runCode("/main.js", fs);

npm install
次に鬼門になるのがブラウザーで動かすnpm install
ただこれはnaruway氏がnpmにCORSが設定されていないことを発見してライブラリを作っている
同じようにmemfsを使って仮想メモリ上にファイルを展開しているので上記のやり方と相性が良さそう

ターミナル
Xterm.jsを利用する
memfsとの連携ができそうな予感!
この辺りが参考になりそう

ダウンロードしたソースコードの展開
import { Volume } from 'memfs';
import JSZip from 'jszip';
async function downloadAndUnzipToMemfs(url, vol) {
// ZIPファイルをダウンロード
const response = await fetch(url);
const blob = await response.blob();
// JSZipでZIPファイルを読み込み
const zip = await JSZip.loadAsync(blob);
// ZIPファイル内の各ファイルをmemfsに書き込み
for (const [filename, zipEntry] of Object.entries(zip.files)) {
if (!zipEntry.dir) {
const content = await zipEntry.async('nodebuffer');
vol.writeFileSync('/' + filename, content);
}
}
}

node_modules内のパスはどうやって解決しようかな