vimd 0.2.0 → 0.2.1

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
@@ -146,7 +146,7 @@ export default {
146
146
  | `markdown-it` | 高速、pandoc不要 | 開発時のプレビュー |
147
147
  | `pandoc` | 高品質、多機能 | 最終出力の生成 |
148
148
 
149
- 詳細な設定オプションは [docs/api.md](docs/api.md) を参照してください。
149
+ 詳細な設定オプションは [docs/ja/api.md](docs/ja/api.md) を参照してください。
150
150
 
151
151
  ---
152
152
 
@@ -165,11 +165,12 @@ export default {
165
165
 
166
166
  ## ドキュメント
167
167
 
168
- - [開発ガイド](docs/development.md) - 開発環境構築
169
- - [アーキテクチャ](docs/architecture.md) - プロジェクト構造
170
- - [APIリファレンス](docs/api.md) - 詳細なオプション
171
- - [テスト](docs/testing.md) - テスト構成
172
- - [トラブルシューティング](docs/troubleshooting.md) - よくある問題
168
+ - [開発ガイド](docs/ja/development.md) - 開発環境構築
169
+ - [アーキテクチャ](docs/ja/architecture.md) - プロジェクト構造
170
+ - [APIリファレンス](docs/ja/api.md) - 詳細なオプション
171
+ - [テスト](docs/ja/testing.md) - テスト構成
172
+ - [トラブルシューティング](docs/ja/troubleshooting.md) - よくある問題
173
+ - [v0.2.0 リリースノート](docs/ja/releases/v0.2.0.md) - Dual Parser System
173
174
 
174
175
  ---
175
176
 
@@ -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,CAuJf"}
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"}
@@ -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
- // 12. Save session
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: 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(port);
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,7 @@
1
+ interface KillOptions {
2
+ all?: boolean;
3
+ port?: string;
4
+ }
5
+ export declare function killCommand(options: KillOptions): Promise<void>;
6
+ export {};
7
+ //# sourceMappingURL=kill.d.ts.map
@@ -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', '8080')
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);
@@ -1,6 +1,6 @@
1
1
  export const DEFAULT_CONFIG = {
2
2
  theme: 'github',
3
- port: 8080,
3
+ port: 38080,
4
4
  host: 'localhost',
5
5
  open: true,
6
6
  pandoc: {
@@ -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<void>;
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;AAKlD,qBAAa,UAAU;IAGT,OAAO,CAAC,MAAM;IAF1B,OAAO,CAAC,OAAO,CAAS;gBAEJ,MAAM,EAAE,YAAY;IAElC,KAAK,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA+BtC,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAUrB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAS7C,MAAM,IAAI,MAAM;CAGjB"}
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"}
@@ -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 url = `http://${this.config.host}:${this.config.port}`;
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.config.port}`;
82
+ return `http://${this.config.host}:${this.actualPort}`;
83
+ }
84
+ getActualPort() {
85
+ return this.actualPort;
57
86
  }
58
87
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vimd",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "Real-time Markdown preview tool with pandoc (view markdown)",
5
5
  "type": "module",
6
6
  "keywords": [