fuzzyworld

fuzzyworld-tools / commit

Find package.json above vite's root; v1.0.2

de3a84832ad148e50d1d4d94f0cef3ad395a1575 · Pavel Safronov · 2026-10-26 18:20:58 -0700 · parents 45b1fe3805ad

Claude Code usage: this commit ~93.8K out / ~47.7M cached · cost unknown (no per-model trailer) · session 1 tally 1.6K in / 875.7K out / 206.3M cached · usage by commit and session

Find package.json above vite's root; v1.0.2

Apps with root: 'src' (north-star, word-waltz) failed because the plugin
looked for src/package.json. It now uses the nearest package.json with a
"fuzzyworld" section, searching upwards from vite's root.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Usage: 1.6K in / 875.7K out / 206.3M cached (session tally)
rtk:   2.3M saved, 55.3% over 5004 cmds (global/lifetime)
 package.json    |  2 +-
 vite/index.d.ts |  2 +-
 vite/index.js   | 22 +++++++++++++++++-----
 3 files changed, 19 insertions(+), 7 deletions(-)

diff --git a/package.json b/package.json
index 995515c..1449830 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "fuzzyworld-tools",
-  "version": "1.0.1",
+  "version": "1.0.2",
   "description": "Tooling for code.fuzzyworld.net and apps.fuzzyworld.net: fw command, deploy, vite plugin.",
   "type": "module",
   "bin": {
diff --git a/vite/index.d.ts b/vite/index.d.ts
index 5e67545..ea683a1 100644
--- a/vite/index.d.ts
+++ b/vite/index.d.ts
@@ -11,7 +11,7 @@ export interface FuzzyworldAppConfig {
   out?: string;
 }
 
-/** Reads the "fuzzyworld" section of package.json in `root` (default: cwd). */
+/** Reads the "fuzzyworld" section of the nearest package.json at or above `root` (default: cwd). */
 export function readAppConfig(root?: string): FuzzyworldAppConfig;
 
 /** Sets `base` to `/<app>/` for builds, from package.json `fuzzyworld.app`. */
diff --git a/vite/index.js b/vite/index.js
index 15e57cc..a286937 100644
--- a/vite/index.js
+++ b/vite/index.js
@@ -9,12 +9,24 @@
 import fs from 'node:fs';
 import path from 'node:path';
 
+// Nearest package.json with a "fuzzyworld" section, from `start` upwards. Vite's `root`
+// is often a subfolder (e.g. root: 'src'), while package.json sits at the repo root.
+function findPackageJson(start) {
+  for (let dir = path.resolve(start); ; dir = path.dirname(dir)) {
+    const p = path.join(dir, 'package.json');
+    if (fs.existsSync(p) && JSON.parse(fs.readFileSync(p, 'utf8')).fuzzyworld) return p;
+    if (path.dirname(dir) === dir) return null;
+  }
+}
+
 export function readAppConfig(root = process.cwd()) {
-  const pkgPath = path.join(root, 'package.json');
-  const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
-  const cfg = pkg.fuzzyworld;
-  if (!cfg || !cfg.app) {
-    throw new Error(`fuzzyworld: add "fuzzyworld": { "app": "<name>" } to ${pkgPath}`);
+  const pkgPath = findPackageJson(root);
+  if (!pkgPath) {
+    throw new Error(`fuzzyworld: add "fuzzyworld": { "app": "<name>" } to package.json (searched upwards from ${root})`);
+  }
+  const cfg = JSON.parse(fs.readFileSync(pkgPath, 'utf8')).fuzzyworld;
+  if (!cfg.app) {
+    throw new Error(`fuzzyworld: add "app" to the "fuzzyworld" section of ${pkgPath}`);
   }
   if (!/^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(cfg.app)) {
     throw new Error(`fuzzyworld: invalid app name "${cfg.app}"`);