diff --git a/server/src/discovery.js b/server/src/discovery.js index ad3b56a..e4a4801 100644 --- a/server/src/discovery.js +++ b/server/src/discovery.js @@ -185,6 +185,15 @@ function classifyLeaf(dir) { framework = 'Static HTML/CSS/JS'; runnable = true; defaultPort = 8000; + } else if (pkg && !devCommand && has(dir, 'index.html')) { + // A package.json with NO recognizable server framework and NO dev/start + // script, but an index.html at the root, is a static site that merely keeps + // a manifest (e.g. for a build dep or metadata). Serve it like any static + // site instead of mislabeling it a non-runnable "node-server". (fervon.) + type = 'html5-static'; + framework = 'Static HTML/CSS/JS'; + runnable = true; + defaultPort = 8000; } else if (pkg) { type = 'node-server'; framework = 'Node'; diff --git a/server/test/discovery.test.js b/server/test/discovery.test.js index ffc6616..d0a1e87 100644 --- a/server/test/discovery.test.js +++ b/server/test/discovery.test.js @@ -53,6 +53,10 @@ test('scanFilesystem lists real projects but skips worktrees and stray folders', mk('real-app', 'package.json', JSON.stringify({ name: 'real-app', scripts: { dev: 'vite' } })); mk('static-site', 'index.html', ''); fs.mkdirSync(path.join(mk('repo-only', null), '.git')); // own repo root counts as a project + // Static site that also keeps a manifest with no server framework / dev + // script — must be a runnable static site, not a dead "node-server". (fervon) + const staticWithPkg = mk('static-pkg', 'index.html', ''); + fs.writeFileSync(path.join(staticWithPkg, 'package.json'), JSON.stringify({ name: 'static-pkg', dependencies: { '@resvg/resvg-js': '^2' } })); // Noise — must be skipped. fs.mkdirSync(path.join(mk('wt-shell', null), 'node_modules')); // orphaned worktree shell (no markers) @@ -60,8 +64,13 @@ test('scanFilesystem lists real projects but skips worktrees and stray folders', fs.writeFileSync(path.join(liveWt, '.git'), 'gitdir: /repo/.git/worktrees/x'); // .git FILE = live worktree mk('empty-dir', null); - const ids = scanFilesystem(root).map((p) => p.id).sort(); - assert.deepEqual(ids, ['real-app', 'repo-only', 'static-site']); + const scanned = scanFilesystem(root); + const ids = scanned.map((p) => p.id).sort(); + assert.deepEqual(ids, ['real-app', 'repo-only', 'static-pkg', 'static-site']); + // The manifest-carrying static site is detected as runnable static, not node-server. + const staticPkg = scanned.find((p) => p.id === 'static-pkg'); + assert.equal(staticPkg.type, 'html5-static'); + assert.equal(staticPkg.runnable, true); } finally { fs.rmSync(root, { recursive: true, force: true }); }