oipage 1.7.0-alpha.5 → 1.7.0-alpha.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG +159 -159
- package/bin/help.js +1 -0
- package/bin/proxy.js +75 -79
- package/bin/run +129 -128
- package/bin/serve.js +179 -179
- package/nodejs/animation/index.js +1 -1
- package/nodejs/cmdlog/index.js +1 -1
- package/nodejs/disk/index.js +1 -1
- package/nodejs/format/index.js +1 -1
- package/nodejs/json/index.js +1 -1
- package/nodejs/logform/index.js +1 -1
- package/nodejs/reader/index.js +1 -1
- package/nodejs/remote/index.d.ts +2 -0
- package/nodejs/remote/index.js +35 -5
- package/nodejs/throttle/index.js +1 -1
- package/package.json +2 -2
- package/types/index.d.ts +2 -2
- package/web/XMLHttpRequest/index.js +1 -1
- package/web/animation/index.js +1 -1
- package/web/format/index.js +1 -1
- package/web/json/index.js +1 -1
- package/web/onReady/index.js +1 -1
- package/web/performChunk/index.js +1 -1
- package/web/reader/index.js +1 -1
- package/web/style/index.js +1 -1
- package/web/throttle/index.js +1 -1
package/bin/run
CHANGED
|
@@ -1,128 +1,129 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
'use strict';
|
|
4
|
-
|
|
5
|
-
process.title = 'OIPage';
|
|
6
|
-
|
|
7
|
-
const { spawn } = require('child_process');
|
|
8
|
-
const { join } = require("path");
|
|
9
|
-
const { existsSync, lstatSync } = require("fs");
|
|
10
|
-
const { mergeOption } = require("vislite");
|
|
11
|
-
const { formatArgv } = require("./tools/format.js");
|
|
12
|
-
|
|
13
|
-
// 开发服务器
|
|
14
|
-
if (process.argv[2] === "serve") {
|
|
15
|
-
|
|
16
|
-
let argvObj = formatArgv(process.argv, {
|
|
17
|
-
"-p": "--port",
|
|
18
|
-
"-c": "--config"
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
let config = {
|
|
22
|
-
devServer: {
|
|
23
|
-
port: 8080,
|
|
24
|
-
baseUrl: "./",
|
|
25
|
-
cache: true,
|
|
26
|
-
proxy: {},
|
|
27
|
-
intercept: []
|
|
28
|
-
},
|
|
29
|
-
module: {
|
|
30
|
-
rules: []
|
|
31
|
-
}
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
if (argvObj["--proxy"] && argvObj["--proxy"].length === 2) {
|
|
35
|
-
config.devServer.proxy[argvObj["--proxy"][0]] = {
|
|
36
|
-
target: argvObj["--proxy"][1]
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
if ((argvObj["--
|
|
54
|
-
if ((argvObj["--
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
"-
|
|
65
|
-
"-
|
|
66
|
-
"-
|
|
67
|
-
"-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
//
|
|
96
|
-
//
|
|
97
|
-
|
|
98
|
-
let
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
//
|
|
102
|
-
//
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
//
|
|
110
|
-
//
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
'use strict';
|
|
4
|
+
|
|
5
|
+
process.title = 'OIPage';
|
|
6
|
+
|
|
7
|
+
const { spawn } = require('child_process');
|
|
8
|
+
const { join } = require("path");
|
|
9
|
+
const { existsSync, lstatSync } = require("fs");
|
|
10
|
+
const { mergeOption } = require("vislite");
|
|
11
|
+
const { formatArgv } = require("./tools/format.js");
|
|
12
|
+
|
|
13
|
+
// 开发服务器
|
|
14
|
+
if (process.argv[2] === "serve") {
|
|
15
|
+
|
|
16
|
+
let argvObj = formatArgv(process.argv, {
|
|
17
|
+
"-p": "--port",
|
|
18
|
+
"-c": "--config"
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
let config = {
|
|
22
|
+
devServer: {
|
|
23
|
+
port: 8080,
|
|
24
|
+
baseUrl: "./",
|
|
25
|
+
cache: true,
|
|
26
|
+
proxy: {},
|
|
27
|
+
intercept: []
|
|
28
|
+
},
|
|
29
|
+
module: {
|
|
30
|
+
rules: []
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
if (argvObj["--proxy"] && argvObj["--proxy"].length === 2) {
|
|
35
|
+
config.devServer.proxy[argvObj["--proxy"][0]] = {
|
|
36
|
+
target: argvObj["--proxy"][1],
|
|
37
|
+
pathRewrite: { ['^' + argvObj["--proxy"][0]]: '' }
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// 如果设置了配置文件
|
|
42
|
+
if (argvObj["--config"]) {
|
|
43
|
+
let configPath = join(process.cwd(), argvObj["--config"][0] || "./oipage.config.js");
|
|
44
|
+
if (existsSync(configPath) && !lstatSync(configPath).isDirectory()) {
|
|
45
|
+
let configValue = require(configPath);
|
|
46
|
+
mergeOption(config, configValue);
|
|
47
|
+
} else {
|
|
48
|
+
console.log("\x1b[0m\x1b[31m" + configPath + "\x1b[0m");
|
|
49
|
+
throw new Error("OIPage: The configuration file does not exist or is not a file.");
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if ((argvObj["--port"] || [])[0]) config.devServer.port = (argvObj["--port"] || [])[0];
|
|
54
|
+
if ((argvObj["--baseUrl"] || [])[0]) config.devServer.baseUrl = (argvObj["--baseUrl"] || [])[0];
|
|
55
|
+
if ((argvObj["--cache"] || [])[0]) config.devServer.cache = (argvObj["--cache"] || [])[0] === "true";
|
|
56
|
+
|
|
57
|
+
require("./serve.js")(config);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// 磁盘操作
|
|
61
|
+
else if (process.argv[2] === "disk") {
|
|
62
|
+
|
|
63
|
+
let argvObj = formatArgv(process.argv, {
|
|
64
|
+
"-f": "--force",
|
|
65
|
+
"-d": "--delete",
|
|
66
|
+
"-m": "--move",
|
|
67
|
+
"-c": "--copy",
|
|
68
|
+
"-l": "--link"
|
|
69
|
+
}, true);
|
|
70
|
+
|
|
71
|
+
require("./disk.js")(argvObj);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// 运行多命令
|
|
75
|
+
else if (process.argv[2] === "run") {
|
|
76
|
+
|
|
77
|
+
let argvObj = formatArgv(process.argv, {});
|
|
78
|
+
|
|
79
|
+
let tasks = [], runArgs = [];
|
|
80
|
+
for (let index = 0; index < argvObj.args.length; index++) {
|
|
81
|
+
|
|
82
|
+
// 参数
|
|
83
|
+
if (/^:/.test(argvObj.args[index])) {
|
|
84
|
+
runArgs.push(argvObj.args[index].replace(/^:/, ""));
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// 任务
|
|
88
|
+
else {
|
|
89
|
+
tasks.push(argvObj.args[index]);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
for (let index = 0; index < tasks.length; index++) {
|
|
94
|
+
|
|
95
|
+
// 补充动态参数的支持
|
|
96
|
+
// npm run bug:issue5 ":-p 9090"
|
|
97
|
+
// 2025年12月1日 于南京
|
|
98
|
+
let task = (tasks[index] + (runArgs[index] ? (" " + runArgs[index]) : "")).split(" ");
|
|
99
|
+
let command = task.shift();
|
|
100
|
+
|
|
101
|
+
// 修复exec无法正常使用logform问题
|
|
102
|
+
// npm run bug:issue4
|
|
103
|
+
// 2025年12月2日 于南京
|
|
104
|
+
spawn(command, task, {
|
|
105
|
+
|
|
106
|
+
// https://nodejs.org/api/child_process.html#child_process_options_stdio
|
|
107
|
+
stdio: 'inherit',
|
|
108
|
+
|
|
109
|
+
// 修复https://github.com/oi-contrib/OIPage/issues/8
|
|
110
|
+
// 在Unix系统中使用`/bin/sh`,在Windows系统中使用`process.env.ComSpec`
|
|
111
|
+
// 2025年12月28日 于南京
|
|
112
|
+
shell: true
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// 网络相关
|
|
119
|
+
else if (process.argv[2] === "network") {
|
|
120
|
+
|
|
121
|
+
let argvObj = formatArgv(process.argv, {}, true);
|
|
122
|
+
|
|
123
|
+
require("./network.js")(argvObj);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// 默认,帮助
|
|
127
|
+
else {
|
|
128
|
+
require("./help.js")();
|
|
129
|
+
}
|
package/bin/serve.js
CHANGED
|
@@ -1,180 +1,180 @@
|
|
|
1
|
-
const { join } = require("path");
|
|
2
|
-
const { existsSync, lstatSync, statSync, createReadStream } = require("fs");
|
|
3
|
-
const { createServer } = require('http');
|
|
4
|
-
const packageValue = require("../package.json");
|
|
5
|
-
const network = require("./tools/network.js");
|
|
6
|
-
const mineTypes = require("./data/mineTypes.json");
|
|
7
|
-
const resolve404 = require("./tools/resolve404.js");
|
|
8
|
-
const resolveImportFactory = require("./tools/resolveImport.js");
|
|
9
|
-
const { doIntercept } = require("./intercept.js");
|
|
10
|
-
const proxy = require("./proxy.js");
|
|
11
|
-
|
|
12
|
-
const websiteIntercept = require("./website-plugins/intercept/index.js");
|
|
13
|
-
const websiteLoader = require("./website-plugins/loader/index.js");
|
|
14
|
-
|
|
15
|
-
const WebSocketClass = require("./WebSocket/index.js");
|
|
16
|
-
|
|
17
|
-
// 开发服务器
|
|
18
|
-
module.exports = function (config) {
|
|
19
|
-
let startTime = new Date().valueOf();
|
|
20
|
-
|
|
21
|
-
const cache = "cache" in config.devServer ? config.devServer.cache : true;
|
|
22
|
-
const port = +config.devServer.port; // 端口号
|
|
23
|
-
const basePath = (/^\./.test(config.devServer.baseUrl)) ? join(process.cwd(), config.devServer.baseUrl) : config.devServer.baseUrl; // 服务器根路径
|
|
24
|
-
|
|
25
|
-
const name = (config.name || "OIPage") + "-http-server";
|
|
26
|
-
const version = config.version || packageValue.version;
|
|
27
|
-
|
|
28
|
-
const wsHandler = WebSocketClass(port + 1, (config.name || "OIPage") + "-ws-server", version);
|
|
29
|
-
|
|
30
|
-
let Server = createServer(function (request, response) {
|
|
31
|
-
let headers = request.headers;
|
|
32
|
-
let url = decodeURIComponent(request.url);
|
|
33
|
-
|
|
34
|
-
let urlArray = url.split("?");
|
|
35
|
-
url = urlArray[0];
|
|
36
|
-
|
|
37
|
-
// 请求的文件路径
|
|
38
|
-
let filePath;
|
|
39
|
-
|
|
40
|
-
let isWebsite = /^\/_oipage_website_\//.test(url);
|
|
41
|
-
if (isWebsite) {
|
|
42
|
-
filePath = join(__dirname, "./website-htmls/", url.replace(/^\/_oipage_website_\//, ""));
|
|
43
|
-
} else {
|
|
44
|
-
filePath = join(basePath, url == "/" ? "index.html" : url.replace(/^\//, ""));
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
let proxyIntercept = proxy({
|
|
48
|
-
name,
|
|
49
|
-
version
|
|
50
|
-
}, config.devServer.proxy || {});
|
|
51
|
-
delete config.devServer.proxy;
|
|
52
|
-
for (let i = 0; i < proxyIntercept.length; i++) {
|
|
53
|
-
config.devServer.intercept.push(proxyIntercept[i]);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
// 请求拦截
|
|
57
|
-
if (doIntercept(url.replace(/^\/_oipage_website_/, "").replace(/^\/@modules\//, ""), isWebsite ? websiteIntercept : config.devServer.intercept, request, response, wsHandler)) {
|
|
58
|
-
console.log("<i> \x1b[1m\x1b[32m[" + name + "] intercept: " + url + '\x1b[0m ' + new Date().toLocaleString());
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
// 如果存在且是文件
|
|
62
|
-
else if (existsSync(filePath) && !lstatSync(filePath).isDirectory()) {
|
|
63
|
-
|
|
64
|
-
// 判断是否是请求而无需进一步解析
|
|
65
|
-
// 2025年12月5日 于南京
|
|
66
|
-
let isXHR = headers["sec-fetch-dest"] === "empty";
|
|
67
|
-
|
|
68
|
-
let dotName = /\./.test(filePath) ? filePath.match(/\.([^.]+)$/)[1] : "";
|
|
69
|
-
let fileType = mineTypes[dotName]; // 文件类型
|
|
70
|
-
let fileInfo = statSync(filePath);
|
|
71
|
-
|
|
72
|
-
let fileModifiedTime = new Date(fileInfo.mtime).toGMTString();
|
|
73
|
-
|
|
74
|
-
let responseHeader = {
|
|
75
|
-
'Content-Type': (fileType || "text/plain") + ";charset=utf-8",
|
|
76
|
-
'Access-Control-Allow-Origin': '*',
|
|
77
|
-
'Server': 'Powered by ' + name + '@' + version,
|
|
78
|
-
'Cache-Control': 'no-cache',
|
|
79
|
-
// 'Content-Length': fileInfo.size, // 会导致拦截修改的文本内容不对
|
|
80
|
-
|
|
81
|
-
// 与if-modified-since配合使用,做缓存验证
|
|
82
|
-
'Last-Modified': fileModifiedTime,
|
|
83
|
-
|
|
84
|
-
// 与if-none-match配合使用,做缓存验证
|
|
85
|
-
// 'ETag': basePath + "-" + new Date(fileInfo.mtime).toString()
|
|
86
|
-
};
|
|
87
|
-
|
|
88
|
-
if (cache && headers["if-modified-since"]) {
|
|
89
|
-
let ifModifiedSince = new Date(headers["if-modified-since"]).valueOf()
|
|
90
|
-
let lastModified = new Date(fileModifiedTime).valueOf()
|
|
91
|
-
|
|
92
|
-
// 注意这里不能使用<=,否则
|
|
93
|
-
// 1、会出现时区问题
|
|
94
|
-
// 2、作为开发服务器,可能同一个url地址对应的文件不同,被误判为未修改
|
|
95
|
-
// 2025年12月29日 于南京
|
|
96
|
-
// if (lastModified <= ifModifiedSince) {}
|
|
97
|
-
if (lastModified === ifModifiedSince) {
|
|
98
|
-
response.writeHead('304', responseHeader);
|
|
99
|
-
response.end();
|
|
100
|
-
console.log("<i> \x1b[1m\x1b[32m[" + name + "] Cache File: " + url + "\x1b[0m " + new Date().toLocaleString() + "\x1b[33m\x1b[1m 304" + (isXHR ? " 请求" : "") + "\x1b[0m");
|
|
101
|
-
return;
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
let sendType = "";
|
|
106
|
-
let entry = headers.accept !== "*/*";
|
|
107
|
-
|
|
108
|
-
// 如果文件小于10M,认为不大,直接读取
|
|
109
|
-
if (fileInfo.size < 10 * 1024 * 1024) {
|
|
110
|
-
let { source, resolveImport } = resolveImportFactory(basePath, filePath, entry, isWebsite ? websiteIntercept : config.devServer.intercept, urlArray[1] === "download", isWebsite)
|
|
111
|
-
|
|
112
|
-
// 只处理非下载文件
|
|
113
|
-
// 过大的也不进行处理
|
|
114
|
-
// (对website无效)
|
|
115
|
-
if (urlArray[1] !== "download") {
|
|
116
|
-
let loaders = isWebsite ? websiteLoader : config.module;
|
|
117
|
-
|
|
118
|
-
for (let i = 0; i < loaders.rules.length; i++) {
|
|
119
|
-
if (loaders.rules[i].test.test(filePath)) {
|
|
120
|
-
source = loaders.rules[i].use.call({
|
|
121
|
-
root: basePath, // 服务器根路径
|
|
122
|
-
path: filePath.replace(basePath, ""), // 文件相对路径
|
|
123
|
-
entry, // 是否是浏览器地址栏直接访问
|
|
124
|
-
setFileType(_fileType) { // 设置文件类型
|
|
125
|
-
fileType = _fileType;
|
|
126
|
-
responseHeader['Content-Type'] = _fileType + ";charset=utf-8";
|
|
127
|
-
}
|
|
128
|
-
}, source);
|
|
129
|
-
break;
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
sendType = "Read";
|
|
136
|
-
response.writeHead('200', responseHeader);
|
|
137
|
-
response.write(isXHR ? source : resolveImport(source, fileType !== "application/javascript"));
|
|
138
|
-
response.end();
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
// 对于大文件,使用流读取
|
|
142
|
-
else {
|
|
143
|
-
sendType = "Stream";
|
|
144
|
-
|
|
145
|
-
responseHeader['Content-Length'] = fileInfo.size
|
|
146
|
-
|
|
147
|
-
response.writeHead('200', responseHeader);
|
|
148
|
-
createReadStream(filePath).pipe(response);
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
console.log("<i> \x1b[1m\x1b[32m[" + name + "] " + sendType + " File: " + url + '\x1b[0m ' + new Date().toLocaleString() + "\x1b[33m\x1b[1m" + (isXHR ? " 请求" : "") + "\x1b[0m");
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
// 否则就是404
|
|
155
|
-
else {
|
|
156
|
-
response.writeHead(404, {
|
|
157
|
-
'Content-Type': "text/html;charset=utf-8",
|
|
158
|
-
'Access-Control-Allow-Origin': '*',
|
|
159
|
-
'Server': 'Powered by ' + name + '@' + version
|
|
160
|
-
});
|
|
161
|
-
response.write(resolve404(filePath, url));
|
|
162
|
-
response.end();
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
});
|
|
166
|
-
|
|
167
|
-
Server.listen(port);
|
|
168
|
-
|
|
169
|
-
// 获取网络信息
|
|
170
|
-
let networkValue = network();
|
|
171
|
-
|
|
172
|
-
// 打印成功提示
|
|
173
|
-
console.log('<i> \x1b[1m\x1b[32m[' + name + '] Project is running at:\x1b[0m');
|
|
174
|
-
console.log('<i> \x1b[1m\x1b[32m[' + name + '] Loopback: \x1b[36mhttp://localhost:' + port + '/\x1b[0m');
|
|
175
|
-
for (let ipv4Item of networkValue.IPv4) console.log('<i> \x1b[1m\x1b[32m[' + name + '] On Your Network (IPv4):\x1b[36m http://' + ipv4Item.address + ':' + port + '/\x1b[0m');
|
|
176
|
-
for (let ipv6Item of networkValue.IPv6) console.log('<i> \x1b[1m\x1b[32m[' + name + '] On Your Network (IPv6): \x1b[36mhttp://[' + ipv6Item.address + ']:' + port + '/\x1b[0m');
|
|
177
|
-
console.log('<i> \x1b[1m\x1b[32m[' + name + '] Content not from ' + (config.name || "OIPage") + ' is served from \x1b[36m"' + basePath + '" \x1b[0mdirectory');
|
|
178
|
-
if (!cache) console.log('<i> \x1b[1m\x1b[32m[' + name + '] Cancel 304 Cache!\x1b[0m');
|
|
179
|
-
console.log('\n' + (config.name || "OIPage") + ' ' + version + ' compiled \x1b[1m\x1b[32msuccessfully\x1b[0m in ' + (new Date().valueOf() - startTime) + ' ms\n')
|
|
1
|
+
const { join } = require("path");
|
|
2
|
+
const { existsSync, lstatSync, statSync, createReadStream } = require("fs");
|
|
3
|
+
const { createServer } = require('http');
|
|
4
|
+
const packageValue = require("../package.json");
|
|
5
|
+
const network = require("./tools/network.js");
|
|
6
|
+
const mineTypes = require("./data/mineTypes.json");
|
|
7
|
+
const resolve404 = require("./tools/resolve404.js");
|
|
8
|
+
const resolveImportFactory = require("./tools/resolveImport.js");
|
|
9
|
+
const { doIntercept } = require("./intercept.js");
|
|
10
|
+
const proxy = require("./proxy.js");
|
|
11
|
+
|
|
12
|
+
const websiteIntercept = require("./website-plugins/intercept/index.js");
|
|
13
|
+
const websiteLoader = require("./website-plugins/loader/index.js");
|
|
14
|
+
|
|
15
|
+
const WebSocketClass = require("./WebSocket/index.js");
|
|
16
|
+
|
|
17
|
+
// 开发服务器
|
|
18
|
+
module.exports = function (config) {
|
|
19
|
+
let startTime = new Date().valueOf();
|
|
20
|
+
|
|
21
|
+
const cache = "cache" in config.devServer ? config.devServer.cache : true;
|
|
22
|
+
const port = +config.devServer.port; // 端口号
|
|
23
|
+
const basePath = (/^\./.test(config.devServer.baseUrl)) ? join(process.cwd(), config.devServer.baseUrl) : config.devServer.baseUrl; // 服务器根路径
|
|
24
|
+
|
|
25
|
+
const name = (config.name || "OIPage") + "-http-server";
|
|
26
|
+
const version = config.version || packageValue.version;
|
|
27
|
+
|
|
28
|
+
const wsHandler = WebSocketClass(port + 1, (config.name || "OIPage") + "-ws-server", version);
|
|
29
|
+
|
|
30
|
+
let Server = createServer(function (request, response) {
|
|
31
|
+
let headers = request.headers;
|
|
32
|
+
let url = decodeURIComponent(request.url);
|
|
33
|
+
|
|
34
|
+
let urlArray = url.split("?");
|
|
35
|
+
url = urlArray[0];
|
|
36
|
+
|
|
37
|
+
// 请求的文件路径
|
|
38
|
+
let filePath;
|
|
39
|
+
|
|
40
|
+
let isWebsite = /^\/_oipage_website_\//.test(url);
|
|
41
|
+
if (isWebsite) {
|
|
42
|
+
filePath = join(__dirname, "./website-htmls/", url.replace(/^\/_oipage_website_\//, ""));
|
|
43
|
+
} else {
|
|
44
|
+
filePath = join(basePath, url == "/" ? "index.html" : url.replace(/^\//, ""));
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
let proxyIntercept = proxy({
|
|
48
|
+
name,
|
|
49
|
+
version
|
|
50
|
+
}, config.devServer.proxy || {});
|
|
51
|
+
delete config.devServer.proxy;
|
|
52
|
+
for (let i = 0; i < proxyIntercept.length; i++) {
|
|
53
|
+
config.devServer.intercept.push(proxyIntercept[i]);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// 请求拦截
|
|
57
|
+
if (doIntercept(url.replace(/^\/_oipage_website_/, "").replace(/^\/@modules\//, ""), isWebsite ? websiteIntercept : config.devServer.intercept, request, response, wsHandler)) {
|
|
58
|
+
console.log("<i> \x1b[1m\x1b[32m[" + name + "] intercept: " + url + '\x1b[0m ' + new Date().toLocaleString());
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// 如果存在且是文件
|
|
62
|
+
else if (existsSync(filePath) && !lstatSync(filePath).isDirectory()) {
|
|
63
|
+
|
|
64
|
+
// 判断是否是请求而无需进一步解析
|
|
65
|
+
// 2025年12月5日 于南京
|
|
66
|
+
let isXHR = headers["sec-fetch-dest"] === "empty";
|
|
67
|
+
|
|
68
|
+
let dotName = /\./.test(filePath) ? filePath.match(/\.([^.]+)$/)[1] : "";
|
|
69
|
+
let fileType = mineTypes[dotName]; // 文件类型
|
|
70
|
+
let fileInfo = statSync(filePath);
|
|
71
|
+
|
|
72
|
+
let fileModifiedTime = new Date(fileInfo.mtime).toGMTString();
|
|
73
|
+
|
|
74
|
+
let responseHeader = {
|
|
75
|
+
'Content-Type': (fileType || "text/plain") + ";charset=utf-8",
|
|
76
|
+
'Access-Control-Allow-Origin': '*',
|
|
77
|
+
'Server': 'Powered by ' + name + '@' + version,
|
|
78
|
+
'Cache-Control': 'no-cache',
|
|
79
|
+
// 'Content-Length': fileInfo.size, // 会导致拦截修改的文本内容不对
|
|
80
|
+
|
|
81
|
+
// 与if-modified-since配合使用,做缓存验证
|
|
82
|
+
'Last-Modified': fileModifiedTime,
|
|
83
|
+
|
|
84
|
+
// 与if-none-match配合使用,做缓存验证
|
|
85
|
+
// 'ETag': basePath + "-" + new Date(fileInfo.mtime).toString()
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
if (cache && headers["if-modified-since"]) {
|
|
89
|
+
let ifModifiedSince = new Date(headers["if-modified-since"]).valueOf()
|
|
90
|
+
let lastModified = new Date(fileModifiedTime).valueOf()
|
|
91
|
+
|
|
92
|
+
// 注意这里不能使用<=,否则
|
|
93
|
+
// 1、会出现时区问题
|
|
94
|
+
// 2、作为开发服务器,可能同一个url地址对应的文件不同,被误判为未修改
|
|
95
|
+
// 2025年12月29日 于南京
|
|
96
|
+
// if (lastModified <= ifModifiedSince) {}
|
|
97
|
+
if (lastModified === ifModifiedSince) {
|
|
98
|
+
response.writeHead('304', responseHeader);
|
|
99
|
+
response.end();
|
|
100
|
+
console.log("<i> \x1b[1m\x1b[32m[" + name + "] Cache File: " + url + "\x1b[0m " + new Date().toLocaleString() + "\x1b[33m\x1b[1m 304" + (isXHR ? " 请求" : "") + "\x1b[0m");
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
let sendType = "";
|
|
106
|
+
let entry = headers.accept !== "*/*";
|
|
107
|
+
|
|
108
|
+
// 如果文件小于10M,认为不大,直接读取
|
|
109
|
+
if (fileInfo.size < 10 * 1024 * 1024) {
|
|
110
|
+
let { source, resolveImport } = resolveImportFactory(basePath, filePath, entry, isWebsite ? websiteIntercept : config.devServer.intercept, urlArray[1] === "download", isWebsite)
|
|
111
|
+
|
|
112
|
+
// 只处理非下载文件
|
|
113
|
+
// 过大的也不进行处理
|
|
114
|
+
// (对website无效)
|
|
115
|
+
if (urlArray[1] !== "download") {
|
|
116
|
+
let loaders = isWebsite ? websiteLoader : config.module;
|
|
117
|
+
|
|
118
|
+
for (let i = 0; i < loaders.rules.length; i++) {
|
|
119
|
+
if (loaders.rules[i].test.test(filePath)) {
|
|
120
|
+
source = loaders.rules[i].use.call({
|
|
121
|
+
root: basePath, // 服务器根路径
|
|
122
|
+
path: filePath.replace(basePath, ""), // 文件相对路径
|
|
123
|
+
entry, // 是否是浏览器地址栏直接访问
|
|
124
|
+
setFileType(_fileType) { // 设置文件类型
|
|
125
|
+
fileType = _fileType;
|
|
126
|
+
responseHeader['Content-Type'] = _fileType + ";charset=utf-8";
|
|
127
|
+
}
|
|
128
|
+
}, source);
|
|
129
|
+
break;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
sendType = "Read";
|
|
136
|
+
response.writeHead('200', responseHeader);
|
|
137
|
+
response.write(isXHR ? source : resolveImport(source, fileType !== "application/javascript"));
|
|
138
|
+
response.end();
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// 对于大文件,使用流读取
|
|
142
|
+
else {
|
|
143
|
+
sendType = "Stream";
|
|
144
|
+
|
|
145
|
+
responseHeader['Content-Length'] = fileInfo.size
|
|
146
|
+
|
|
147
|
+
response.writeHead('200', responseHeader);
|
|
148
|
+
createReadStream(filePath).pipe(response);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
console.log("<i> \x1b[1m\x1b[32m[" + name + "] " + sendType + " File: " + url + '\x1b[0m ' + new Date().toLocaleString() + "\x1b[33m\x1b[1m" + (isXHR ? " 请求" : "") + "\x1b[0m");
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// 否则就是404
|
|
155
|
+
else {
|
|
156
|
+
response.writeHead(404, {
|
|
157
|
+
'Content-Type': "text/html;charset=utf-8",
|
|
158
|
+
'Access-Control-Allow-Origin': '*',
|
|
159
|
+
'Server': 'Powered by ' + name + '@' + version
|
|
160
|
+
});
|
|
161
|
+
response.write(resolve404(filePath, url));
|
|
162
|
+
response.end();
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
Server.listen(port);
|
|
168
|
+
|
|
169
|
+
// 获取网络信息
|
|
170
|
+
let networkValue = network();
|
|
171
|
+
|
|
172
|
+
// 打印成功提示
|
|
173
|
+
console.log('<i> \x1b[1m\x1b[32m[' + name + '] Project is running at:\x1b[0m');
|
|
174
|
+
console.log('<i> \x1b[1m\x1b[32m[' + name + '] Loopback: \x1b[36mhttp://localhost:' + port + '/\x1b[0m');
|
|
175
|
+
for (let ipv4Item of networkValue.IPv4) console.log('<i> \x1b[1m\x1b[32m[' + name + '] On Your Network (IPv4):\x1b[36m http://' + ipv4Item.address + ':' + port + '/\x1b[0m');
|
|
176
|
+
for (let ipv6Item of networkValue.IPv6) console.log('<i> \x1b[1m\x1b[32m[' + name + '] On Your Network (IPv6): \x1b[36mhttp://[' + ipv6Item.address + ']:' + port + '/\x1b[0m');
|
|
177
|
+
console.log('<i> \x1b[1m\x1b[32m[' + name + '] Content not from ' + (config.name || "OIPage") + ' is served from \x1b[36m"' + basePath + '" \x1b[0mdirectory');
|
|
178
|
+
if (!cache) console.log('<i> \x1b[1m\x1b[32m[' + name + '] Cancel 304 Cache!\x1b[0m');
|
|
179
|
+
console.log('\n' + (config.name || "OIPage") + ' ' + version + ' compiled \x1b[1m\x1b[32msuccessfully\x1b[0m in ' + (new Date().valueOf() - startTime) + ' ms\n')
|
|
180
180
|
};
|
package/nodejs/cmdlog/index.js
CHANGED
package/nodejs/disk/index.js
CHANGED