diff --git a/scripts/uninstall.js b/scripts/uninstall.js index 4a101e1..3cb170c 100644 --- a/scripts/uninstall.js +++ b/scripts/uninstall.js @@ -48,5 +48,12 @@ try { } } } catch (e) { - if (e.code !== 'ENOENT') throw e; + if (e.code === 'ENOENT') { + // no settings.json — nothing to clean + } else if (e instanceof SyntaxError) { + // ponytail: malformed settings.json — can't safely edit it; leave intact, warn + console.warn(`settings.json is malformed — could not remove the ponytail statusLine entry. Remove it manually from: ${settingsPath} (${e.message})`); + } else { + throw e; + } } diff --git a/tests/uninstall.test.js b/tests/uninstall.test.js index 7a5bac8..e4ff583 100644 --- a/tests/uninstall.test.js +++ b/tests/uninstall.test.js @@ -84,6 +84,28 @@ assert.equal( 'a combined statusLine must be left untouched, not partially destroyed', ); +// #434: a malformed settings.json must not crash the script mid-cleanup. It +// can't be safely edited, so uninstall warns and leaves the file byte-for-byte +// intact instead of throwing a SyntaxError after other state was already removed. +const malformedSettings = '{ "statusLine": { "command": "ponytail-statusline.sh", broken'; +fs.writeFileSync(settingsPath, malformedSettings); + +result = runUninstall(env); +assert.equal( + result.status, + 0, + `expected exit 0 on malformed settings.json, got:\n${result.stdout}${result.stderr}`, +); +assert.ok( + /malformed/i.test(result.stdout + result.stderr), + 'must warn that the statusLine entry could not be removed', +); +assert.equal( + fs.readFileSync(settingsPath, 'utf8'), + malformedSettings, + 'malformed settings.json must be left unchanged', +); + // Running on an already-clean machine must not throw. result = runUninstall({ HOME: path.join(temp, 'home-empty'), USERPROFILE: path.join(temp, 'home-empty') }); assert.equal(result.status, 0, result.stderr);