feat: 编译前端, 并丢弃 webpack

This commit is contained in:
CrescentLeaf
2025-08-30 19:34:25 +08:00
parent 4eeea5b230
commit f855b23465
7 changed files with 318 additions and 42 deletions

1
.gitignore vendored
View File

@@ -4,3 +4,4 @@ thewhitesilk_config.json
thewhitesilk_data/
deno.lock
node_modules/

View File

@@ -92,7 +92,7 @@
alert('很抱歉, 此应用无法在较旧的浏览器运行, 请使用基于 Chromium 89+ 的浏览器(内核)使用 :(')
</script>
<script type="module">
import App from './ui/App.js'
import App from './ui/App.jsx'
ReactDOM.createRoot(document.getElementById('app')).render(React.createElement(App, null))
let onResize = () => {

View File

@@ -1,7 +1,7 @@
{
"tasks": {
"main": "deno run --allow-read --allow-write --allow-env --allow-net server/main.ts",
"test": "deno run --allow-read --allow-write --allow-env --allow-net server/main_test.ts"
"main": "deno run --allow-read --allow-write --allow-env --allow-net --allow-sys ./server/main.ts",
"test": "deno run --allow-read --allow-write --allow-env --allow-net ./server/main_test.ts"
},
"imports": {
"chalk": "npm:chalk@5.4.1",
@@ -9,8 +9,9 @@
"express": "npm:express@5.1.0",
"socket.io": "npm:socket.io@4.8.1",
"webpack": "npm:webpack@5.101.2",
"@babel/preset-env": "npm:@babel/preset-env@^7.26.9",
"@babel/preset-react": "npm:@babel/preset-react@^7.26.3"
"@babel/core": "npm:@babel/core@^7.26.10",
"@babel/preset-env": "npm:@babel/preset-env@7.26.9",
"@babel/preset-react": "npm:@babel/preset-react@7.26.3",
"@babel/preset-typescript": "npm:@babel/preset-typescript@7.27.1"
}
}

263
server/compiler/io.js Normal file
View File

@@ -0,0 +1,263 @@
/*
* Simple File Access Library
* Author - @MoonLeeeaf <https://github.com/MoonLeeeaf>
*/
import fs from 'node:fs'
/**
* 简单文件类
*/
export default class io {
/**
* 构建函数
* @param { String } path
* @param { String } mode
*/
constructor(path, mode) {
this.path = path
this.r = mode.includes('r')
this.w = mode.includes('w')
}
/**
* 构建函数
* @param { String } path
* @param { String } mode
*/
static open(path, mode) {
if (!mode || mode == '')
throw new Error('当前文件对象未设置属性!')
return new io(path, mode)
}
/**
* 检测文件或目录是否存在
* @param { String } path
* @returns { Boolean }
*/
static exists(path) {
return fs.existsSync(path)
}
/**
* 枚举目录下所有文件
* @param { String } 扫描路径
* @param { Object } extra 额外参数
* @param { Function<String> } [extra.filter] 过滤器<文件路径>
* @param { Boolean } [extra.recursive] 是否搜索文件夹内的文件
* @param { Boolean } [extra.fullPath] 是否返回完整文件路径
* @returns { String[] } 文件路径列表
*/
static listFiles(path, { filter, recursive = false, fullPath = true } = {}) {
let a = fs.readdirSync(path, { recursive: recursive })
a.forEach(function (v, index, arrayThis) {
arrayThis[index] = `${path}//${v}`
})
a = a.filter(function (v) {
if (!fs.lstatSync(v).isFile()) return false
if (filter) return filter(v)
return true
})
if (!fullPath)
a.forEach(function (v, index, arrayThis) {
arrayThis[index] = v.substring(v.lastIndexOf('/') + 1)
})
return a
}
/**
* 枚举目录下所有文件夹
* @param { String } 扫描路径
* @param { Object } extra 额外参数
* @param { Function<String> } [extra.filter] 过滤器<文件夹路径>
* @param { Boolean } [extra.recursive] 是否搜索文件夹内的文件夹
* @param { Boolean } [extra.fullPath] 是否返回完整文件路径
* @returns { String[] } 文件夹路径列表
*/
static listFolders(path, { filter, recursive = false, fullPath = true } = {}) {
let a = fs.readdirSync(path, { recursive: recursive })
a.forEach(function (v, index, arrayThis) {
arrayThis[index] = `${path}//${v}`
})
a = a.filter(function (v) {
if (!fs.lstatSync(v).isDirectory()) return false
if (filter) return filter(v)
return true
})
if (!fullPath)
a.forEach(function (v, index, arrayThis) {
arrayThis[index] = v.substring(v.lastIndexOf('/') + 1)
})
return a
}
/**
* 获取文件(夹)的全名
* @param { String } path
* @returns { String } name
*/
static getName(path) {
let r = /\\|\//
let s = path.search(r)
while (s != -1) {
path = path.substring(s + 1)
s = path.search(r)
}
return path
}
/**
* 获取文件(夹)的父文件夹路径
* @param { String } path
* @returns { String } parentPath
*/
static getParent(path) {
return path.substring(0, path.lastIndexOf(this.getName(path)) - 1)
}
/**
* 复制某文件夹的全部内容, 自动创建文件夹
* @param { String } from
* @param { String } to
*/
static copyDir(from, to) {
this.mkdirs(to)
this.listFiles(from).forEach(function (v) {
io.open(v, 'r').pipe(io.open(`${to}//${io.getName(v)}`, 'w')).close()
})
this.listFolders(from).forEach(function (v) {
io.copyDir(v, `${to}//${io.getName(v)}`)
})
}
/**
* 删除文件
* @param { String } path
*/
static remove(f) {
fs.rmSync(f, { recursive: true })
}
/**
* 移动文件
* @param { String }} path
* @param { String } newPath
*/
static move(path, newPath) {
fs.renameSync(path, newPath)
}
/**
* 创建文件夹, 有则忽略
* @param { String } path
* @returns { String } path
*/
static mkdirs(path) {
if (!this.exists(path))
fs.mkdirSync(path, { recursive: true })
return path
}
/**
* 将文件内容写入到另一个文件
* @param { io } file
* @returns { io } this
*/
pipe(file) {
file.writeAll(this.readAll())
file.close()
return this
}
/**
* 检查文件是否存在, 若无则写入, 有则忽略
* @param { Buffer | String } 写入数据
* @returns { io } 对象自身
*/
checkExistsOrWrite(data) {
if (!io.exists(this.path))
this.writeAll(data)
return this
}
/**
* 检查文件是否存在, 若无则写入 JSON 数据, 有则忽略
* @param { Object } 写入数据
* @returns { io } 对象自身
*/
checkExistsOrWriteJson(data) {
if (!io.exists(this.path))
this.writeAllJson(data)
return this
}
/**
* 读取一个文件
* @returns { Buffer } 文件数据字节
*/
readAll() {
if (this.r)
return fs.readFileSync(this.path)
throw new Error('当前文件对象未设置可读')
}
/**
* 读取一个文件并关闭
* @returns { Buffer } 文件数据
*/
readAllAndClose() {
let r
if (this.r)
r = this.readAll()
else
throw new Error('当前文件对象未设置可读!')
this.close()
return r
}
/**
* 写入一个文件
* @param { Buffer | String } 写入数据
* @returns { io } 对象自身
*/
writeAll(data) {
if (this.w)
fs.writeFileSync(this.path, data)
else
throw new Error('当前文件对象未设置可写!')
return this
}
/**
* 写入一个JSON文件
* @param { Object } 写入数据
* @returns { io } 对象自身
*/
writeAllJson(data) {
if (!data instanceof Object)
throw new Error('你只能输入一个 JSON 对象!')
if (this.w)
this.writeAll(JSON.stringify(data))
else
throw new Error('当前文件对象未设置可写!')
return this
}
/**
* 读取一个JSON文件
* @returns { Object } 文件数据
*/
readAllJson() {
if (this.r)
return JSON.parse(this.readAll().toString())
throw new Error('当前文件对象未设置可读!')
}
/**
* 读取一个JSON文件并关闭
* @returns { Object } 文件数据
*/
readAllJsonAndClose() {
let r
if (this.r)
r = JSON.parse(this.readAll().toString())
else
throw new Error('当前文件对象未设置可读!')
this.close()
return r
}
/**
* 回收文件对象
*/
close() {
delete this.path
delete this.r
delete this.w
}
}

View File

@@ -0,0 +1,36 @@
import babel from '@babel/core'
import fs from 'node:fs/promises'
import io from './io.js'
async function compileJs(path: string) {
const result = await babel.transformFileAsync(path, {
presets: [
[
"@babel/preset-env", {
modules: false,
},
],
"@babel/preset-react",
"@babel/preset-typescript",
],
targets: {
chrome: "53",
android: "40",
},
sourceMaps: true,
})
await fs.writeFile(path, result.code + '\n' + `//@ sourceMappingURL=${io.getName(path)}.map`)
await fs.writeFile(path + '.map', JSON.stringify(result.map))
console.log(`编译: ${path}`)
}
export default function(source: string, output: string) {
io.copyDir(source, output)
io.listFiles(output, {
recursive: true,
fullPath: true,
}).forEach(function (v) {
if (/\.(t|j)sx?$/.test(v))
compileJs(v)
})
}

View File

@@ -5,9 +5,17 @@ import HttpServerLike from "./types/HttpServerLike.ts"
import config from './config.ts'
import http from 'node:http'
import https from 'node:https'
import web_packer from './web_packer.ts'
import transform from './compiler/transform.ts'
const app = express()
app.use((req, res, next) => {
const url = req.originalUrl || req.url
if (/\.(j|t)sx?/.test(url))
res.setHeader('Content-Type', 'application/javascript')
next()
})
app.use('/', express.static(config.data_path + '/page_compiled'))
const httpServer: HttpServerLike = (
((config.server.use == 'http') && http.createServer(app)) ||
((config.server.use == 'https') && https.createServer(config.server.https, app)) ||
@@ -23,8 +31,4 @@ ApiManager.initAllApis()
httpServer.listen(config.server.listen)
web_packer?.run((err, status) => {
if (err) throw err
console.log("前端頁面已編譯完成")
web_packer?.close(() => {})
})
transform('./client', config.data_path + '/page_compiled')

View File

@@ -1,29 +0,0 @@
import webpack from 'webpack'
import config from "./config.ts"
import path from 'node:path'
export default webpack({
mode: 'production',
devtool: 'source-map',
entry: './client/ui/App.jsx',
output: {
filename: 'bundle.js',
path: path.resolve(import.meta.dirname as string, config.data_path, 'page_compiled'),
},
module: {
rules: [
{
test: /\.(j|t)sx?$/,
use: {
loader: "babel-loader",
options: {
presets: [
'@babel/preset-env',
'@babel/preset-react',
],
},
},
},
],
},
})