vimd 0.3.0 → 0.3.3
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/README.md +10 -6
- package/dist/core/websocket-server.d.ts +1 -0
- package/dist/core/websocket-server.d.ts.map +1 -1
- package/dist/core/websocket-server.js +38 -25
- package/package.json +1 -1
- package/dist/core/server.d.ts +0 -18
- package/dist/core/server.d.ts.map +0 -1
- package/dist/core/server.js +0 -87
package/README.md
CHANGED
|
@@ -65,10 +65,10 @@
|
|
|
65
65
|
### インストール
|
|
66
66
|
|
|
67
67
|
```bash
|
|
68
|
-
npm install -g vimd
|
|
68
|
+
npm install -g vimd@0.2.4
|
|
69
69
|
```
|
|
70
70
|
|
|
71
|
-
**現在の安定版: v0.
|
|
71
|
+
**現在の安定版: v0.2.4**
|
|
72
72
|
|
|
73
73
|
v0.2.0 からは **pandoc なしで利用可能** になりました。
|
|
74
74
|
高品質な出力が必要な場合のみ pandoc をインストールしてください。
|
|
@@ -103,14 +103,18 @@ vimd config
|
|
|
103
103
|
|
|
104
104
|
## バージョン情報
|
|
105
105
|
|
|
106
|
-
**安定版: v0.
|
|
106
|
+
**安定版: v0.2.4**
|
|
107
107
|
|
|
108
|
-
v0.3.
|
|
109
|
-
|
|
110
|
-
- バンドルサイズ 83% 削減
|
|
108
|
+
v0.3.x は内部アーキテクチャを刷新した実験的バージョンです。
|
|
109
|
+
安定した動作を求める場合は v0.2.4 をご利用ください。
|
|
111
110
|
|
|
112
111
|
安定版のインストール:
|
|
113
112
|
```bash
|
|
113
|
+
npm install -g vimd@0.2.4
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
最新版(実験的)のインストール:
|
|
117
|
+
```bash
|
|
114
118
|
npm install -g vimd@latest
|
|
115
119
|
```
|
|
116
120
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"websocket-server.d.ts","sourceRoot":"","sources":["../../src/core/websocket-server.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"websocket-server.d.ts","sourceRoot":"","sources":["../../src/core/websocket-server.ts"],"names":[],"mappings":"AAUA;;;GAGG;AACH,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;GAGG;AACH,MAAM,WAAW,iBAAiB;IAChC,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,OAAO,CAAC;CACtB;AA0CD;;;GAGG;AACH,qBAAa,eAAe;IAC1B,OAAO,CAAC,UAAU,CAA4B;IAC9C,OAAO,CAAC,QAAQ,CAAyB;IACzC,OAAO,CAAC,OAAO,CAA6B;IAC5C,OAAO,CAAC,OAAO,CAAyB;IACxC,OAAO,CAAC,KAAK,CAAS;gBAEV,OAAO,EAAE,sBAAsB;IAQ3C;;OAEG;IACH,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,iBAAiB,CAAC;IA4GzC;;;OAGG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAiC3B;;OAEG;IACH,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,IAAI;IAU7C;;OAEG;IACH,OAAO,CAAC,kBAAkB;CAc3B"}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
// src/core/websocket-server.ts
|
|
2
2
|
import http from 'http';
|
|
3
|
+
import fs from 'fs';
|
|
4
|
+
import path from 'path';
|
|
3
5
|
import polka from 'polka';
|
|
4
6
|
import sirv from 'sirv';
|
|
5
7
|
import { WebSocketServer as WSServer, WebSocket } from 'ws';
|
|
@@ -84,31 +86,38 @@ export class WebSocketServer {
|
|
|
84
86
|
// Create polka app with HTML injection middleware
|
|
85
87
|
const app = polka();
|
|
86
88
|
// Middleware to inject reload script into HTML
|
|
89
|
+
// For HTML files, read directly to avoid sirv streaming issues
|
|
87
90
|
app.use((req, res, next) => {
|
|
88
91
|
// Only process GET requests for HTML files
|
|
89
92
|
if (req.method !== 'GET' || !req.url?.endsWith('.html')) {
|
|
90
93
|
return serve(req, res, next);
|
|
91
94
|
}
|
|
92
|
-
//
|
|
93
|
-
|
|
94
|
-
const
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
return
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
95
|
+
// Parse the URL and resolve the file path
|
|
96
|
+
const urlPath = req.url || '/index.html';
|
|
97
|
+
const filePath = path.join(this.options.root, urlPath);
|
|
98
|
+
// Check if file exists
|
|
99
|
+
if (!fs.existsSync(filePath)) {
|
|
100
|
+
// Let sirv handle 404
|
|
101
|
+
return serve(req, res, next);
|
|
102
|
+
}
|
|
103
|
+
try {
|
|
104
|
+
// Read HTML file directly
|
|
105
|
+
const html = fs.readFileSync(filePath, 'utf8');
|
|
106
|
+
// Inject reload script
|
|
107
|
+
const injectedHtml = this.injectReloadScript(html);
|
|
108
|
+
// Send response with proper headers
|
|
109
|
+
res.writeHead(200, {
|
|
110
|
+
'Content-Type': 'text/html; charset=utf-8',
|
|
111
|
+
'Content-Length': Buffer.byteLength(injectedHtml, 'utf8'),
|
|
112
|
+
'Cache-Control': 'no-cache, no-store, must-revalidate',
|
|
113
|
+
});
|
|
114
|
+
res.end(injectedHtml);
|
|
115
|
+
}
|
|
116
|
+
catch (error) {
|
|
117
|
+
// On error, fall back to sirv
|
|
118
|
+
Logger.warn(`Failed to read HTML file: ${filePath}`);
|
|
119
|
+
serve(req, res, next);
|
|
120
|
+
}
|
|
112
121
|
});
|
|
113
122
|
// Create HTTP server
|
|
114
123
|
this.httpServer = http.createServer(app.handler);
|
|
@@ -151,27 +160,31 @@ export class WebSocketServer {
|
|
|
151
160
|
}
|
|
152
161
|
/**
|
|
153
162
|
* Stop the server
|
|
163
|
+
* Uses force termination to ensure immediate shutdown
|
|
154
164
|
*/
|
|
155
165
|
async stop() {
|
|
156
|
-
//
|
|
166
|
+
// Force terminate all WebSocket clients (immediate disconnect)
|
|
167
|
+
// Use terminate() instead of close() to avoid waiting for graceful handshake
|
|
157
168
|
for (const client of this.clients) {
|
|
158
169
|
try {
|
|
159
|
-
client.
|
|
170
|
+
client.terminate();
|
|
160
171
|
}
|
|
161
172
|
catch {
|
|
162
|
-
// Ignore
|
|
173
|
+
// Ignore termination errors
|
|
163
174
|
}
|
|
164
175
|
}
|
|
165
176
|
this.clients.clear();
|
|
166
|
-
// Close WebSocket server
|
|
177
|
+
// Close WebSocket server (should be instant now that clients are terminated)
|
|
167
178
|
if (this.wsServer) {
|
|
168
179
|
await new Promise((resolve) => {
|
|
169
180
|
this.wsServer.close(() => resolve());
|
|
170
181
|
});
|
|
171
182
|
this.wsServer = null;
|
|
172
183
|
}
|
|
173
|
-
//
|
|
184
|
+
// Force close all HTTP connections before closing server
|
|
185
|
+
// closeAllConnections() requires Node.js >= 18.2.0
|
|
174
186
|
if (this.httpServer) {
|
|
187
|
+
this.httpServer.closeAllConnections();
|
|
175
188
|
await new Promise((resolve) => {
|
|
176
189
|
this.httpServer.close(() => resolve());
|
|
177
190
|
});
|
package/package.json
CHANGED
package/dist/core/server.d.ts
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { ServerConfig } from '../config/types.js';
|
|
2
|
-
export interface ServerStartResult {
|
|
3
|
-
actualPort: number;
|
|
4
|
-
requestedPort: number;
|
|
5
|
-
portChanged: boolean;
|
|
6
|
-
}
|
|
7
|
-
export declare class LiveServer {
|
|
8
|
-
private config;
|
|
9
|
-
private running;
|
|
10
|
-
private actualPort;
|
|
11
|
-
constructor(config: ServerConfig);
|
|
12
|
-
start(htmlPath: string): Promise<ServerStartResult>;
|
|
13
|
-
stop(): Promise<void>;
|
|
14
|
-
openBrowser(url: string): Promise<void>;
|
|
15
|
-
getURL(): string;
|
|
16
|
-
getActualPort(): number;
|
|
17
|
-
}
|
|
18
|
-
//# sourceMappingURL=server.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/core/server.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAMlD,MAAM,WAAW,iBAAiB;IAChC,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,OAAO,CAAC;CACtB;AAED,qBAAa,UAAU;IAIT,OAAO,CAAC,MAAM;IAH1B,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,UAAU,CAAS;gBAEP,MAAM,EAAE,YAAY;IAIlC,KAAK,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC;IA8DnD,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAUrB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAS7C,MAAM,IAAI,MAAM;IAIhB,aAAa,IAAI,MAAM;CAGxB"}
|
package/dist/core/server.js
DELETED
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
// src/core/server.ts
|
|
2
|
-
import liveServer from 'live-server';
|
|
3
|
-
import { Logger } from '../utils/logger.js';
|
|
4
|
-
import * as path from 'path';
|
|
5
|
-
import open from 'open';
|
|
6
|
-
export class LiveServer {
|
|
7
|
-
constructor(config) {
|
|
8
|
-
this.config = config;
|
|
9
|
-
this.running = false;
|
|
10
|
-
this.actualPort = config.port;
|
|
11
|
-
}
|
|
12
|
-
async start(htmlPath) {
|
|
13
|
-
const root = path.dirname(htmlPath);
|
|
14
|
-
const file = path.basename(htmlPath);
|
|
15
|
-
const params = {
|
|
16
|
-
port: this.config.port,
|
|
17
|
-
host: this.config.host,
|
|
18
|
-
root: root,
|
|
19
|
-
file: file,
|
|
20
|
-
open: false, // manually open
|
|
21
|
-
wait: 50,
|
|
22
|
-
logLevel: 0, // silent
|
|
23
|
-
watch: [root], // explicitly watch the root directory
|
|
24
|
-
};
|
|
25
|
-
try {
|
|
26
|
-
const server = liveServer.start(params);
|
|
27
|
-
// Wait for server to start and get actual port
|
|
28
|
-
const actualPort = await new Promise((resolve, reject) => {
|
|
29
|
-
const timeout = setTimeout(() => {
|
|
30
|
-
reject(new Error('Server start timeout'));
|
|
31
|
-
}, 10000);
|
|
32
|
-
server.addListener('listening', () => {
|
|
33
|
-
clearTimeout(timeout);
|
|
34
|
-
const address = server.address();
|
|
35
|
-
resolve(address.port);
|
|
36
|
-
});
|
|
37
|
-
server.addListener('error', (err) => {
|
|
38
|
-
clearTimeout(timeout);
|
|
39
|
-
reject(err);
|
|
40
|
-
});
|
|
41
|
-
});
|
|
42
|
-
this.actualPort = actualPort;
|
|
43
|
-
this.running = true;
|
|
44
|
-
const portChanged = actualPort !== this.config.port;
|
|
45
|
-
const url = `http://${this.config.host}:${actualPort}`;
|
|
46
|
-
if (portChanged) {
|
|
47
|
-
Logger.warn(`Port ${this.config.port} was unavailable, using port ${actualPort}`);
|
|
48
|
-
}
|
|
49
|
-
Logger.success(`Server started at ${url}`);
|
|
50
|
-
if (this.config.open) {
|
|
51
|
-
await this.openBrowser(url);
|
|
52
|
-
}
|
|
53
|
-
return {
|
|
54
|
-
actualPort,
|
|
55
|
-
requestedPort: this.config.port,
|
|
56
|
-
portChanged,
|
|
57
|
-
};
|
|
58
|
-
}
|
|
59
|
-
catch (error) {
|
|
60
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
61
|
-
throw new Error(`Failed to start server: ${errorMessage}`);
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
async stop() {
|
|
65
|
-
if (!this.running) {
|
|
66
|
-
return;
|
|
67
|
-
}
|
|
68
|
-
liveServer.shutdown();
|
|
69
|
-
this.running = false;
|
|
70
|
-
Logger.info('Server stopped');
|
|
71
|
-
}
|
|
72
|
-
async openBrowser(url) {
|
|
73
|
-
try {
|
|
74
|
-
await open(url);
|
|
75
|
-
Logger.info('Browser opened');
|
|
76
|
-
}
|
|
77
|
-
catch (error) {
|
|
78
|
-
Logger.warn('Failed to open browser automatically');
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
getURL() {
|
|
82
|
-
return `http://${this.config.host}:${this.actualPort}`;
|
|
83
|
-
}
|
|
84
|
-
getActualPort() {
|
|
85
|
-
return this.actualPort;
|
|
86
|
-
}
|
|
87
|
-
}
|