Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
node_modules
dist
output
.git
Dockerfile
.dockerignore
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ extension/
!extension/英雄杀/

dist
output
node_modules
package-lock.json

Expand Down
29 changes: 29 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# -------- 构建阶段 --------
FROM node:lts AS builder

# 安装 pnpm(轻量)
RUN npm install -g pnpm@9

WORKDIR /app

# 只复制依赖定义文件(能最大化缓存利用率)
COPY pnpm-lock.yaml package.json ./

# 安装依赖(包括 devDependencies 用于构建)
RUN pnpm install --frozen-lockfile

# 复制项目源码并构建
COPY . .
RUN pnpm build:full

# -------- 运行阶段 --------
FROM node:lts-alpine AS runner

WORKDIR /app

COPY --from=builder /app/dist ./

EXPOSE 8080
EXPOSE 8089

CMD ["node", "noname-server.cjs"]
8 changes: 8 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
name: noname
version: "3.9"

services:
app:
build: .
ports:
- "8089:8089"
6 changes: 6 additions & 0 deletions docs/how-to-start.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,12 @@ pnpm install
```bash
pnpm start
```

或在构建完项目后执行:

```bash
pnpm serve
```

浏览器会自动打开,占用本地的8080端口。

Expand Down
231 changes: 0 additions & 231 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -104,237 +104,6 @@
};
})();
</script>
<script>
"use strict";
if (location.href.startsWith("http") && typeof window.initReadWriteFunction != "function" && !window.require && !window.__dirname) {
window.initReadWriteFunction = function (game) {
return new Promise((resolve, reject) => {
fetch(`/checkFile?fileName=noname.js`)
.then(response => {
return response.json();
})
.then(result => {
if (result?.success) {
callback();
} else {
reject(result.errorMsg);
}
})
.catch(reject);

return;

function callback() {
/**
* 检查指定的路径是否是一个文件
*
* @param {string} fileName - 需要查询的路径
* @param {(result: -1 | 0 | 1) => void} [callback] - 回调函数;接受的参数意义如下:
* - `-1`: 路径不存在或无法访问
* - `0`: 路径的内容不是文件
* - `1`: 路径的内容是文件
* @param {(err: Error) => void} [onerror] - 接收错误的回调函数
* @return {void} - 由于三端的异步需求和历史原因,文件管理必须为回调异步函数
*/
game.checkFile = function checkFile(fileName, callback, onerror) {
fetch(`/checkFile?fileName=${fileName}`)
.then(response => response.json())
.then(result => {
if (result) {
if (result.success) {
callback?.(1);
return;
}

if (result.code === 404) {
if (result.errorMsg === "不是一个文件") {
callback?.(0);
return;
} else if (result.errorMsg === "文件不存在或无法访问") {
callback?.(-1);
return;
}
}
}

onerror?.(result?.errorMsg);
})
.catch(onerror);
};

/**
* 检查指定的路径是否是一个目录
*
* @param {string} dir - 需要查询的路径
* @param {(result: -1 | 0 | 1) => void} [callback] - 回调函数;接受的参数意义如下:
* - `-1`: 路径不存在或无法访问
* - `0`: 路径的内容不是目录
* - `1`: 路径的内容是目录
* @param {(err: Error) => void} [onerror] - 接收错误的回调函数
* @return {void} - 由于三端的异步需求和历史原因,文件管理必须为回调异步函数
*/
game.checkDir = function checkDir(dir, callback, onerror) {
fetch(`/checkDir?dir=${dir}`)
.then(response => response.json())
.then(result => {
if (result) {
if (result.success) {
callback?.(1);
return;
}

if (result.code === 404) {
if (result.errorMsg === "不是一个文件夹") {
return void callback?.(0);
} else if (result.errorMsg === "文件夹不存在或无法访问") {
return void callback?.(-1);
}
}
}

onerror?.(result?.errorMsg);
})
.catch(onerror);
};

game.readFile = function readFile(fileName, callback = () => {}, error = () => {}) {
fetch(`/readFile?fileName=${fileName}`)
.then(response => response.json())
.then(result => {
if (result?.success) {
const data = result.data;

/** @type {Uint8Array} */
let buffer;
if (typeof data == "string") {
buffer = Uint8Array.fromBase64(data);
} else if (Array.isArray(data)) {
buffer = new Uint8Array(data);
}

callback(buffer.buffer);
} else {
error(result?.errorMsg);
}
})
.catch(error);
};

game.readFileAsText = function readFileAsText(fileName, callback = () => {}, error = () => {}) {
fetch(`/readFileAsText?fileName=${fileName}`)
.then(response => response.json())
.then(result => {
if (result?.success) {
callback(result.data);
} else {
error(result?.errorMsg);
}
})
.catch(error);
};

game.writeFile = function writeFile(data, path, name, callback = () => {}) {
game.ensureDirectory(path, () => {
if (Object.prototype.toString.call(data) == "[object File]") {
const fileReader = new FileReader();
fileReader.onload = event => {
game.writeFile(event.target.result, path, name, callback);
};
fileReader.readAsArrayBuffer(data, "UTF-8");
} else {
let filePath = path;
if (path.endsWith("/")) {
filePath += name;
} else if (path == "") {
filePath += name;
} else {
filePath += "/" + name;
}

fetch(`/writeFile`, {
method: "post",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
data: typeof data == "string" ? data : Array.prototype.slice.call(new Uint8Array(data)),
path: filePath,
}),
})
.then(response => response.json())
.then(result => {
if (result?.success) {
callback();
} else {
callback(result?.errorMsg);
}
});
}
});
};

game.removeFile = function removeFile(fileName, callback = () => {}) {
fetch(`/removeFile?fileName=${fileName}`)
.then(response => response.json())
.then(result => {
callback(result.errorMsg);
})
.catch(error);
};

game.getFileList = function getFileList(dir, callback = () => {}, onerror) {
fetch(`/getFileList?dir=${dir}`)
.then(response => response.json())
.then(result => {
if (!result) {
throw new Error("Cannot get available resource.");
}

if (result.success) {
callback(result.data.folders, result.data.files);
} else if (onerror) {
onerror(new Error(result.errorMsg));
}
});
};

game.ensureDirectory = function ensureDirectory(list, callback = () => {}, file = false) {
let pathArray = typeof list == "string" ? list.split("/") : list;
if (file) {
pathArray = pathArray.slice(0, -1);
}
game.createDir(pathArray.join("/"), callback, console.error);
};

game.createDir = function createDir(directory, successCallback = () => {}, errorCallback = () => {}) {
fetch(`/createDir?dir=${directory}`)
.then(response => response.json())
.then(result => {
if (result?.success) {
successCallback();
} else {
errorCallback(new Error("创建文件夹失败"));
}
})
.catch(errorCallback);
};
game.removeDir = function removeDir(directory, successCallback = () => {}, errorCallback = () => {}) {
fetch(`/removeDir?dir=${directory}`)
.then(response => response.json())
.then(result => {
if (result?.success) {
successCallback();
} else {
errorCallback(new Error("创建文件夹失败"));
}
})
.catch(errorCallback);
};

resolve();
}
});
};
}
</script>
<script type="module" src="game/update.js"></script>
<script type="module" src="game/config.js"></script>
<script type="module" src="game/package.js"></script>
Expand Down
1 change: 0 additions & 1 deletion jit/compile-strategy.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import ts from "typescript";
import * as sfc from "@vue/compiler-sfc";
import dedent from "dedent";
import "vue/dist/vue.esm-browser.js";
import { compile } from "./ts-compiler";

console.log(`ts loaded`, ts.version);
Expand Down
58 changes: 58 additions & 0 deletions jit/vite-plugin-importmap.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { normalizePath, Plugin } from "vite";
import fs from "fs";
import path from "path";
import { createRequire } from 'module';
const require = createRequire(import.meta.url);

export default function vitePluginJIT(importMap: Record<string, string> = {}): Plugin {
let root = process.cwd();
let isBuild = false;
const resolvedImportMap: Record<string, string> = {};

return {
name: "vite-plugin-jit",

configResolved(config) {
isBuild = config.command === "build";
root = config.root;
},

async buildStart() {
if (!isBuild) return;
for (const key in importMap) {
try {
const resolved = require.resolve(importMap[key]);
resolvedImportMap[key] = normalizePath("/" + path.relative(root, resolved));
} catch (e) {
resolvedImportMap[key] = importMap[key];
}
}
},

closeBundle() {
const gameJs = path.resolve("dist/game/game.js");
fs.mkdirSync(path.dirname(gameJs), { recursive: true });
fs.writeFileSync(
gameJs,
`"use strict"

const im = document.createElement("script");
im.type = "importmap";
im.textContent = \`${JSON.stringify({ imports: resolvedImportMap }, null, 2)}\`;
document.currentScript.after(im);

const script = document.createElement("script");
script.type = "module";
script.src = "/noname/entry.js";
document.head.appendChild(script);
`
);
},

transformIndexHtml(html) {
if (!isBuild) return;
const script = `<script type="importmap">\n${JSON.stringify({ imports: resolvedImportMap }, null, 2)}\n</script>`;
return html.replace("</head>", `${script}\n</head>`);
},
};
}
Loading