vimd 0.2.0 → 0.2.2
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 +13 -7
- package/dist/cli/commands/dev.d.ts.map +1 -1
- package/dist/cli/commands/dev.js +10 -5
- package/dist/cli/commands/kill.d.ts +7 -0
- package/dist/cli/commands/kill.d.ts.map +1 -0
- package/dist/cli/commands/kill.js +110 -0
- package/dist/cli/index.js +9 -1
- package/dist/config/defaults.js +1 -1
- package/dist/core/server.d.ts +8 -1
- package/dist/core/server.d.ts.map +1 -1
- package/dist/core/server.js +32 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -107,6 +107,7 @@ vimd config
|
|
|
107
107
|
| `vimd build <file>` | 静的HTMLを生成 |
|
|
108
108
|
| `vimd theme` | テーマを対話的に変更 |
|
|
109
109
|
| `vimd config` | 設定を対話的に編集 |
|
|
110
|
+
| `vimd kill` | 実行中のセッションを終了 |
|
|
110
111
|
|
|
111
112
|
### オプション
|
|
112
113
|
|
|
@@ -121,6 +122,10 @@ vimd dev draft.md --pandoc # pandocパーサーを使用
|
|
|
121
122
|
vimd build draft.md -o output.html # 出力先指定
|
|
122
123
|
vimd build draft.md --fast # markdown-itで高速ビルド
|
|
123
124
|
vimd build draft.md --theme dark # テーマ指定
|
|
125
|
+
|
|
126
|
+
# kill コマンド
|
|
127
|
+
vimd kill # 全セッションを終了
|
|
128
|
+
vimd kill --port 38080 # 特定ポートのセッションを終了
|
|
124
129
|
```
|
|
125
130
|
|
|
126
131
|
---
|
|
@@ -132,7 +137,7 @@ vimd build draft.md --theme dark # テーマ指定
|
|
|
132
137
|
```javascript
|
|
133
138
|
export default {
|
|
134
139
|
theme: 'github',
|
|
135
|
-
port:
|
|
140
|
+
port: 38080, // デフォルト: 38080(v0.2.1で変更)
|
|
136
141
|
open: true,
|
|
137
142
|
devParser: 'markdown-it', // dev用パーサー(デフォルト: markdown-it)
|
|
138
143
|
buildParser: 'pandoc', // build用パーサー(デフォルト: pandoc)
|
|
@@ -146,7 +151,7 @@ export default {
|
|
|
146
151
|
| `markdown-it` | 高速、pandoc不要 | 開発時のプレビュー |
|
|
147
152
|
| `pandoc` | 高品質、多機能 | 最終出力の生成 |
|
|
148
153
|
|
|
149
|
-
詳細な設定オプションは [docs/api.md](docs/api.md) を参照してください。
|
|
154
|
+
詳細な設定オプションは [docs/ja/api.md](docs/ja/api.md) を参照してください。
|
|
150
155
|
|
|
151
156
|
---
|
|
152
157
|
|
|
@@ -165,11 +170,12 @@ export default {
|
|
|
165
170
|
|
|
166
171
|
## ドキュメント
|
|
167
172
|
|
|
168
|
-
- [開発ガイド](docs/development.md) - 開発環境構築
|
|
169
|
-
- [アーキテクチャ](docs/architecture.md) - プロジェクト構造
|
|
170
|
-
- [APIリファレンス](docs/api.md) - 詳細なオプション
|
|
171
|
-
- [テスト](docs/testing.md) - テスト構成
|
|
172
|
-
- [トラブルシューティング](docs/troubleshooting.md) - よくある問題
|
|
173
|
+
- [開発ガイド](docs/ja/development.md) - 開発環境構築
|
|
174
|
+
- [アーキテクチャ](docs/ja/architecture.md) - プロジェクト構造
|
|
175
|
+
- [APIリファレンス](docs/ja/api.md) - 詳細なオプション
|
|
176
|
+
- [テスト](docs/ja/testing.md) - テスト構成
|
|
177
|
+
- [トラブルシューティング](docs/ja/troubleshooting.md) - よくある問題
|
|
178
|
+
- [v0.2.0 リリースノート](docs/ja/releases/v0.2.0.md) - Dual Parser System
|
|
173
179
|
|
|
174
180
|
---
|
|
175
181
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dev.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/dev.ts"],"names":[],"mappings":"AAaA,UAAU,UAAU;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,wBAAsB,UAAU,CAC9B,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,UAAU,GAClB,OAAO,CAAC,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"dev.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/dev.ts"],"names":[],"mappings":"AAaA,UAAU,UAAU;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,wBAAsB,UAAU,CAC9B,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,UAAU,GAClB,OAAO,CAAC,IAAI,CAAC,CA6Jf"}
|
package/dist/cli/commands/dev.js
CHANGED
|
@@ -87,11 +87,16 @@ export async function devCommand(filePath, options) {
|
|
|
87
87
|
open: config.open,
|
|
88
88
|
root: sourceDir,
|
|
89
89
|
});
|
|
90
|
-
await server.start(htmlPath);
|
|
91
|
-
//
|
|
90
|
+
const startResult = await server.start(htmlPath);
|
|
91
|
+
// Update port if live-server used a different one
|
|
92
|
+
const actualPort = startResult.actualPort;
|
|
93
|
+
if (startResult.portChanged) {
|
|
94
|
+
port = actualPort;
|
|
95
|
+
}
|
|
96
|
+
// 12. Save session with actual port
|
|
92
97
|
await SessionManager.saveSession({
|
|
93
98
|
pid: process.pid,
|
|
94
|
-
port:
|
|
99
|
+
port: actualPort,
|
|
95
100
|
htmlPath: htmlPath,
|
|
96
101
|
sourcePath: absolutePath,
|
|
97
102
|
startedAt: new Date().toISOString(),
|
|
@@ -128,8 +133,8 @@ export async function devCommand(filePath, options) {
|
|
|
128
133
|
catch {
|
|
129
134
|
// Ignore errors when removing file
|
|
130
135
|
}
|
|
131
|
-
// Remove session from registry
|
|
132
|
-
await SessionManager.removeSession(
|
|
136
|
+
// Remove session from registry (use actual port)
|
|
137
|
+
await SessionManager.removeSession(actualPort);
|
|
133
138
|
Logger.info('Cleanup complete');
|
|
134
139
|
});
|
|
135
140
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"kill.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/kill.ts"],"names":[],"mappings":"AAQA,UAAU,WAAW;IACnB,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAsDD,wBAAsB,WAAW,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAyErE"}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
// src/cli/commands/kill.ts
|
|
2
|
+
import { SessionManager } from '../../utils/session-manager.js';
|
|
3
|
+
import { Logger } from '../../utils/logger.js';
|
|
4
|
+
import { exec } from 'child_process';
|
|
5
|
+
import { promisify } from 'util';
|
|
6
|
+
const execAsync = promisify(exec);
|
|
7
|
+
/**
|
|
8
|
+
* Check if a PID belongs to a vimd/node process
|
|
9
|
+
*/
|
|
10
|
+
async function isVimdProcess(pid) {
|
|
11
|
+
try {
|
|
12
|
+
const { stdout } = await execAsync(`ps -p ${pid} -o comm=`);
|
|
13
|
+
const processName = stdout.trim().toLowerCase();
|
|
14
|
+
return processName.includes('node') || processName.includes('vimd');
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
// Process doesn't exist
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Kill a single session with validation
|
|
23
|
+
*/
|
|
24
|
+
async function killSession(session) {
|
|
25
|
+
const result = {
|
|
26
|
+
port: session.port,
|
|
27
|
+
pid: session.pid,
|
|
28
|
+
killed: false,
|
|
29
|
+
htmlRemoved: false,
|
|
30
|
+
};
|
|
31
|
+
// Check if process is alive
|
|
32
|
+
if (!SessionManager.isProcessAlive(session.pid)) {
|
|
33
|
+
result.reason = 'Process already dead';
|
|
34
|
+
return result;
|
|
35
|
+
}
|
|
36
|
+
// Validate that this is a vimd process (PID safety check)
|
|
37
|
+
const isVimd = await isVimdProcess(session.pid);
|
|
38
|
+
if (!isVimd) {
|
|
39
|
+
result.reason = 'PID reused by another process';
|
|
40
|
+
return result;
|
|
41
|
+
}
|
|
42
|
+
// Kill the process
|
|
43
|
+
result.killed = await SessionManager.killProcess(session.pid);
|
|
44
|
+
return result;
|
|
45
|
+
}
|
|
46
|
+
export async function killCommand(options) {
|
|
47
|
+
try {
|
|
48
|
+
// Clean up dead sessions first
|
|
49
|
+
const deadCleaned = await SessionManager.cleanDeadSessions();
|
|
50
|
+
if (deadCleaned > 0) {
|
|
51
|
+
Logger.info(`Cleaned ${deadCleaned} dead session(s)`);
|
|
52
|
+
}
|
|
53
|
+
// Load all sessions
|
|
54
|
+
const sessions = await SessionManager.loadSessions();
|
|
55
|
+
const sessionList = Object.values(sessions);
|
|
56
|
+
if (sessionList.length === 0) {
|
|
57
|
+
Logger.info('No active sessions found');
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
// Filter sessions if --port is specified
|
|
61
|
+
let targetSessions;
|
|
62
|
+
if (options.port) {
|
|
63
|
+
const port = parseInt(options.port, 10);
|
|
64
|
+
const session = sessions[port.toString()];
|
|
65
|
+
if (!session) {
|
|
66
|
+
Logger.warn(`No session found on port ${port}`);
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
targetSessions = [session];
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
// Kill all sessions (default behavior, same as --all)
|
|
73
|
+
targetSessions = sessionList;
|
|
74
|
+
}
|
|
75
|
+
Logger.info(`Found ${targetSessions.length} active session(s)`);
|
|
76
|
+
// Kill each session
|
|
77
|
+
const results = [];
|
|
78
|
+
let htmlRemovedCount = 0;
|
|
79
|
+
for (const session of targetSessions) {
|
|
80
|
+
const result = await killSession(session);
|
|
81
|
+
// Remove HTML file regardless of kill result
|
|
82
|
+
const cleanupResult = await SessionManager.cleanupSessionOnPort(session.port);
|
|
83
|
+
result.htmlRemoved = cleanupResult.htmlRemoved;
|
|
84
|
+
if (result.htmlRemoved) {
|
|
85
|
+
htmlRemovedCount++;
|
|
86
|
+
}
|
|
87
|
+
results.push(result);
|
|
88
|
+
// Log result
|
|
89
|
+
if (result.killed) {
|
|
90
|
+
Logger.success(`Killed session on port ${result.port} (PID: ${result.pid})`);
|
|
91
|
+
}
|
|
92
|
+
else if (result.reason) {
|
|
93
|
+
Logger.info(`Port ${result.port}: ${result.reason}`);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
// Summary
|
|
97
|
+
const killedCount = results.filter((r) => r.killed).length;
|
|
98
|
+
if (killedCount > 0 || htmlRemovedCount > 0) {
|
|
99
|
+
if (htmlRemovedCount > 0) {
|
|
100
|
+
Logger.success(`Removed ${htmlRemovedCount} preview file(s)`);
|
|
101
|
+
}
|
|
102
|
+
Logger.success('All sessions terminated');
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
catch (error) {
|
|
106
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
107
|
+
Logger.error(`Failed to kill sessions: ${errorMessage}`);
|
|
108
|
+
process.exit(1);
|
|
109
|
+
}
|
|
110
|
+
}
|
package/dist/cli/index.js
CHANGED
|
@@ -5,6 +5,7 @@ import { devCommand } from './commands/dev.js';
|
|
|
5
5
|
import { buildCommand } from './commands/build.js';
|
|
6
6
|
import { themeCommand } from './commands/theme.js';
|
|
7
7
|
import { configCommand } from './commands/config.js';
|
|
8
|
+
import { killCommand } from './commands/kill.js';
|
|
8
9
|
const require = createRequire(import.meta.url);
|
|
9
10
|
const packageJson = require('../../package.json');
|
|
10
11
|
const program = new Command();
|
|
@@ -16,7 +17,7 @@ program
|
|
|
16
17
|
program
|
|
17
18
|
.command('dev <file>')
|
|
18
19
|
.description('Start live preview server')
|
|
19
|
-
.option('-p, --port <port>', 'Port number'
|
|
20
|
+
.option('-p, --port <port>', 'Port number')
|
|
20
21
|
.option('-t, --theme <theme>', 'Theme name')
|
|
21
22
|
.option('--no-open', 'Do not open browser automatically')
|
|
22
23
|
.option('--pandoc', 'Use pandoc parser instead of markdown-it')
|
|
@@ -40,4 +41,11 @@ program
|
|
|
40
41
|
.description('Edit configuration interactively')
|
|
41
42
|
.option('-l, --list', 'List current configuration')
|
|
42
43
|
.action(configCommand);
|
|
44
|
+
// vimd kill
|
|
45
|
+
program
|
|
46
|
+
.command('kill')
|
|
47
|
+
.description('Kill vimd dev sessions')
|
|
48
|
+
.option('--all', 'Kill all sessions (default)')
|
|
49
|
+
.option('--port <port>', 'Kill session on specific port')
|
|
50
|
+
.action(killCommand);
|
|
43
51
|
program.parse(process.argv);
|
package/dist/config/defaults.js
CHANGED
package/dist/core/server.d.ts
CHANGED
|
@@ -1,11 +1,18 @@
|
|
|
1
1
|
import { ServerConfig } from '../config/types.js';
|
|
2
|
+
export interface ServerStartResult {
|
|
3
|
+
actualPort: number;
|
|
4
|
+
requestedPort: number;
|
|
5
|
+
portChanged: boolean;
|
|
6
|
+
}
|
|
2
7
|
export declare class LiveServer {
|
|
3
8
|
private config;
|
|
4
9
|
private running;
|
|
10
|
+
private actualPort;
|
|
5
11
|
constructor(config: ServerConfig);
|
|
6
|
-
start(htmlPath: string): Promise<
|
|
12
|
+
start(htmlPath: string): Promise<ServerStartResult>;
|
|
7
13
|
stop(): Promise<void>;
|
|
8
14
|
openBrowser(url: string): Promise<void>;
|
|
9
15
|
getURL(): string;
|
|
16
|
+
getActualPort(): number;
|
|
10
17
|
}
|
|
11
18
|
//# sourceMappingURL=server.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/core/server.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;
|
|
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
CHANGED
|
@@ -7,6 +7,7 @@ export class LiveServer {
|
|
|
7
7
|
constructor(config) {
|
|
8
8
|
this.config = config;
|
|
9
9
|
this.running = false;
|
|
10
|
+
this.actualPort = config.port;
|
|
10
11
|
}
|
|
11
12
|
async start(htmlPath) {
|
|
12
13
|
const root = path.dirname(htmlPath);
|
|
@@ -22,13 +23,38 @@ export class LiveServer {
|
|
|
22
23
|
watch: [root], // explicitly watch the root directory
|
|
23
24
|
};
|
|
24
25
|
try {
|
|
25
|
-
liveServer.start(params);
|
|
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;
|
|
26
43
|
this.running = true;
|
|
27
|
-
const
|
|
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
|
+
}
|
|
28
49
|
Logger.success(`Server started at ${url}`);
|
|
29
50
|
if (this.config.open) {
|
|
30
51
|
await this.openBrowser(url);
|
|
31
52
|
}
|
|
53
|
+
return {
|
|
54
|
+
actualPort,
|
|
55
|
+
requestedPort: this.config.port,
|
|
56
|
+
portChanged,
|
|
57
|
+
};
|
|
32
58
|
}
|
|
33
59
|
catch (error) {
|
|
34
60
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
@@ -53,6 +79,9 @@ export class LiveServer {
|
|
|
53
79
|
}
|
|
54
80
|
}
|
|
55
81
|
getURL() {
|
|
56
|
-
return `http://${this.config.host}:${this.
|
|
82
|
+
return `http://${this.config.host}:${this.actualPort}`;
|
|
83
|
+
}
|
|
84
|
+
getActualPort() {
|
|
85
|
+
return this.actualPort;
|
|
57
86
|
}
|
|
58
87
|
}
|