ppm and Core Packages
Overview
Tranquil uses a forked version of ppm (Pulsar’s package manager) called tranquil-ppm, tracked as a git submodule at ppm/. Settings-view calls ppm ls --json to populate the Packages settings panel. The output has four sections: core, dev, user, git.
The _atomPackages problem
In a production Pulsar build, the bundler writes a _atomPackages field into the root package.json (inside the asar). This is a complete snapshot of every bundled package’s metadata, and ppm reads it to populate the core list.
In dev builds (running from source), no _atomPackages exists. The upstream ppm silently returns an empty core list when the field is missing.
Do not add _atomPackages to the root package.json manually. At startup, src/package-manager.js sets this.packagesCache directly from _atomPackages. A partial list (e.g. only our 5 owned packages) causes all other core packages — tree-view, status-bar, settings-view, etc. — to be invisible to the package manager, resulting in a blank screen.
The fix in tranquil-ppm
tranquil-ppm patches src/list.js to fall back to packageDependencies when _atomPackages is absent:
if (!atomPackages && packageJSON.packageDependencies) {
packagesMeta = Object.keys(packageJSON.packageDependencies)
.map(name => {
try {
return JSON.parse(fs.readFileSync(
path.join(resourcePath, 'node_modules', name, 'package.json')
));
} catch (_) { return null; }
})
.filter(Boolean)
.filter(metadata => this.isPackageVisible(options, metadata));
} packageDependencies in package.json lists every bundled package (Pulsar core packages plus our owned packages). Their metadata is read live from node_modules/, so owned packages (which are symlinked via link:../) always reflect the current source.
New-package corollary. This fallback reads
node_modules/<name>/package.jsonfor eachpackageDependenciesentry. A newly added owned package that lacks itsnode_modules/<name>symlink is skipped by thetry/catch(the read throws), so it is invisible in Settings — the same missing symlink also stops it from loading at all underyarn start. Creating thenode_modules/<name>symlink fixes both. See Adding a New Owned Package.
ATOM_RESOURCE_PATH
ppm determines which package.json to read via ATOM_RESOURCE_PATH. Without it, ppm runs mdfind to locate the installed app bundle — which in development finds whatever Tranquil build is in /Applications, and reads its (potentially outdated) _atomPackages.
scripts/dev.js sets this env var explicitly before spawning Electron:
ATOM_RESOURCE_PATH: path.join(__dirname, '..'), This points ppm at tranquil-client’s own package.json, where the fallback reads current metadata from node_modules/.
Note: ATOM_RESOURCE_PATH is only read by ppm. Electron’s main process determines its resource path via command-line arguments passed by the launcher; it does not read this env var.
settings-view patches (packages/settings-view/lib/package-card.js)
The upstream package-card has been patched to support Tranquil-owned packages:
- Local logo: checks for
resources/logo.pngin the loaded package’s directory and uses it as the avatar instead of fetching from GitHub - Author display: uses
package.json’sauthor.namefield as the display name (falls back to GitHub username extracted fromrepository) - Author link: uses
author.urlfor the click target (falls back topulsar-edit.dev/users/...)
All owned packages (tranquil-automations, tranquil-browser, tranquil-config, tranquil-theme-dark, tranquil-theme-light) have author, repository, resources/logo.png, and resources/logo.svg set accordingly.
Production builds (TBD)
A production build will need to generate _atomPackages and write it into package.json before bundling into an asar. At that point, the tranquil-ppm fallback path will not be reached (production builds have _atomPackages), and ATOM_RESOURCE_PATH will not need to be set (the asar contains the correct resource path).
The generation step — iterating packageDependencies, reading each package’s metadata, and writing _atomPackages — needs to be added to the build pipeline. Owned package metadata should be taken from their repos at the pinned version used in the build.