reviw 0.10.1 → 0.10.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.
Files changed (2) hide show
  1. package/cli.cjs +51 -1
  2. package/package.json +1 -1
package/cli.cjs CHANGED
@@ -116,7 +116,7 @@ function runGitDiff() {
116
116
  });
117
117
  }
118
118
 
119
- // Validate all files exist (if files specified)
119
+ // Validate all files exist and are not directories (if files specified)
120
120
  const resolvedPaths = [];
121
121
  for (const fp of filePaths) {
122
122
  const resolved = path.resolve(fp);
@@ -124,6 +124,13 @@ for (const fp of filePaths) {
124
124
  console.error(`File not found: ${resolved}`);
125
125
  process.exit(1);
126
126
  }
127
+ const stat = fs.statSync(resolved);
128
+ if (stat.isDirectory()) {
129
+ console.error(`Cannot open directory: ${resolved}`);
130
+ console.error(`Usage: reviw <file> [file2...]`);
131
+ console.error(`Please specify a file, not a directory.`);
132
+ process.exit(1);
133
+ }
127
134
  resolvedPaths.push(resolved);
128
135
  }
129
136
 
@@ -477,6 +484,19 @@ function escapeHtmlChars(str) {
477
484
  }
478
485
 
479
486
  function loadData(filePath) {
487
+ // Check if path exists
488
+ if (!fs.existsSync(filePath)) {
489
+ throw new Error(`File not found: ${filePath}`);
490
+ }
491
+ // Check if path is a directory
492
+ const stat = fs.statSync(filePath);
493
+ if (stat.isDirectory()) {
494
+ throw new Error(
495
+ `Cannot open directory: ${filePath}\n` +
496
+ `Usage: reviw <file> [file2...]\n` +
497
+ `Please specify a file, not a directory.`
498
+ );
499
+ }
480
500
  const ext = path.extname(filePath).toLowerCase();
481
501
  if (ext === ".csv" || ext === ".tsv") {
482
502
  const data = loadCsv(filePath);
@@ -3827,6 +3847,20 @@ function htmlTemplate(dataRows, cols, title, mode, previewHtml) {
3827
3847
  \`;
3828
3848
  document.head.appendChild(style);
3829
3849
 
3850
+ // Helper: strip markdown formatting from text
3851
+ function stripMarkdown(text) {
3852
+ return text
3853
+ .replace(/^[-*+]\\s+/, '') // List markers: - * +
3854
+ .replace(/^\\d+\\.\\s+/, '') // Numbered list: 1. 2.
3855
+ .replace(/\\*\\*([^*]+)\\*\\*/g, '$1') // Bold: **text**
3856
+ .replace(/\\*([^*]+)\\*/g, '$1') // Italic: *text*
3857
+ .replace(/__([^_]+)__/g, '$1') // Bold: __text__
3858
+ .replace(/_([^_]+)_/g, '$1') // Italic: _text_
3859
+ .replace(/\`([^\`]+)\`/g, '$1') // Inline code: \`code\`
3860
+ .replace(/\\[([^\\]]+)\\]\\([^)]+\\)/g, '$1') // Links: [text](url)
3861
+ .trim();
3862
+ }
3863
+
3830
3864
  // Helper: find matching source line for text
3831
3865
  function findSourceLine(text) {
3832
3866
  if (!text) return -1;
@@ -3849,6 +3883,14 @@ function htmlTemplate(dataRows, cols, title, mode, previewHtml) {
3849
3883
  return i + 1;
3850
3884
  }
3851
3885
  }
3886
+
3887
+ // Check for markdown list items: strip list markers and formatting
3888
+ if (lineText.match(/^[-*+]\\s|^\\d+\\.\\s/)) {
3889
+ const strippedLine = stripMarkdown(lineText).replace(/\\s+/g, ' ').slice(0, 100);
3890
+ if (strippedLine === normalized) return i + 1;
3891
+ if (strippedLine.includes(normalized.slice(0, 30)) && normalized.length > 5) return i + 1;
3892
+ if (normalized.includes(strippedLine.slice(0, 30)) && strippedLine.length > 5) return i + 1;
3893
+ }
3852
3894
  }
3853
3895
  return -1;
3854
3896
  }
@@ -4286,6 +4328,10 @@ function createFileServer(filePath) {
4286
4328
 
4287
4329
  function tryListen(attemptPort, attempts = 0) {
4288
4330
  if (serverStarted) return; // Prevent double-start race condition
4331
+ // Clear previous listeners from failed attempts to avoid duplicate
4332
+ // "listening" callbacks firing after a later successful bind.
4333
+ ctx.server.removeAllListeners("error");
4334
+ ctx.server.removeAllListeners("listening");
4289
4335
  if (attempts >= MAX_PORT_ATTEMPTS) {
4290
4336
  console.error(
4291
4337
  `Could not find an available port for ${baseName} after ${MAX_PORT_ATTEMPTS} attempts.`,
@@ -4450,6 +4496,10 @@ function createDiffServer(diffContent) {
4450
4496
 
4451
4497
  function tryListen(attemptPort, attempts = 0) {
4452
4498
  if (serverStarted) return; // Prevent double-start race condition
4499
+ // Clear listeners from previous failed attempts to prevent stale
4500
+ // "listening" handlers from firing on the successful bind.
4501
+ ctx.server.removeAllListeners("error");
4502
+ ctx.server.removeAllListeners("listening");
4453
4503
  if (attempts >= MAX_PORT_ATTEMPTS) {
4454
4504
  console.error(
4455
4505
  `Could not find an available port for diff viewer after ${MAX_PORT_ATTEMPTS} attempts.`,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "reviw",
3
- "version": "0.10.1",
3
+ "version": "0.10.2",
4
4
  "description": "Lightweight file reviewer with in-browser comments for CSV, TSV, Markdown, and Git diffs.",
5
5
  "type": "module",
6
6
  "bin": {