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 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.3.0**
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.3.0**(最新版と同一)
106
+ **安定版: v0.2.4**
107
107
 
108
- v0.3.0 では内部アーキテクチャを刷新し、パフォーマンスを改善しました:
109
- - WebSocket 直接通信によるライブリロード(live-server 依存を削除)
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
 
@@ -37,6 +37,7 @@ export declare class WebSocketServer {
37
37
  start(): Promise<ServerStartResult>;
38
38
  /**
39
39
  * Stop the server
40
+ * Uses force termination to ensure immediate shutdown
40
41
  */
41
42
  stop(): Promise<void>;
42
43
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"websocket-server.d.ts","sourceRoot":"","sources":["../../src/core/websocket-server.ts"],"names":[],"mappings":"AAQA;;;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;IAwGzC;;OAEG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IA8B3B;;OAEG;IACH,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,IAAI;IAU7C;;OAEG;IACH,OAAO,CAAC,kBAAkB;CAc3B"}
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
- // For HTML files, we need to inject the script
93
- // Override res.end to modify the response
94
- const originalEnd = res.end.bind(res);
95
- let body = '';
96
- res.write = (chunk) => {
97
- body += chunk.toString();
98
- return true;
99
- };
100
- res.end = ((chunk) => {
101
- if (chunk) {
102
- body += chunk.toString();
103
- }
104
- // Inject reload script before </body> or </html>
105
- const injectedBody = this.injectReloadScript(body);
106
- // Update Content-Length header
107
- const contentLength = Buffer.byteLength(injectedBody, 'utf8');
108
- res.setHeader('Content-Length', contentLength);
109
- return originalEnd(injectedBody);
110
- });
111
- serve(req, res, next);
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
- // Close all WebSocket connections
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.close();
170
+ client.terminate();
160
171
  }
161
172
  catch {
162
- // Ignore close errors
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
- // Close HTTP server
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vimd",
3
- "version": "0.3.0",
3
+ "version": "0.3.3",
4
4
  "description": "Real-time Markdown preview tool with pandoc (view markdown)",
5
5
  "type": "module",
6
6
  "keywords": [
@@ -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"}
@@ -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
- }