Multi-Repo Setup
This setup is for local development only. Production build configuration is handled separately.
Tranquil’s owned packages live in separate GitHub repos and are linked into tranquil-client for local development via Yarn 1’s link: feature, which creates live symlinks — no reinstall needed when you edit a package.
Repository Layout
~/Documents/Tranquil/Repos/
tranquil-client/ ← main app (this repo)
tranquil-automations/ ← login/session automation package
tranquil-browser/ ← embedded browser package
tranquil-config/ ← secrets/config package
tranquil-theme-dark/ ← dark UI theme
tranquil-theme-light/ ← light UI theme
tranquil-tips/ ← background tips shown when no editors are open All six directories must be siblings. The link:../ paths in tranquil-client/package.json resolve relative to the repo root.
GitHub Repos
| npm name | GitHub repo |
|---|---|
tranquil-automations | tranquillabs/tranquil-automations |
tranquil-browser | tranquillabs/tranquil-browser |
tranquil-config | tranquillabs/tranquil-config |
tranquil-theme-dark | tranquillabs/tranquil-theme-dark |
tranquil-theme-light | tranquillabs/tranquil-theme-light |
tranquil-tips | tranquillabs/tranquil-tips |
All repos are on the main branch.
First-Time Setup
Clone all owned repos alongside tranquil-client, then install:
cd /Users/david/Documents/Tranquil/Repos
git clone https://github.com/tranquillabs/tranquil-automations.git
git clone https://github.com/tranquillabs/tranquil-browser.git
git clone https://github.com/tranquillabs/tranquil-config.git
git clone https://github.com/tranquillabs/tranquil-theme-dark.git
git clone https://github.com/tranquillabs/tranquil-theme-light.git
git clone https://github.com/tranquillabs/tranquil-tips.git
cd tranquil-client
source ~/.nvm/nvm.sh && nvm use
yarn install
git submodule update --init ppm
cd ppm && yarn install && cd ..
# Install deps inside each sibling package so they can find their own modules
cd ../tranquil-automations && yarn install
cd ../tranquil-browser && yarn install
cd ../tranquil-tips && yarn install
cd ../tranquil-client After installing, verify the symlinks exist:
ls -la node_modules/tranquil-automations
node_modules/tranquil-browser
node_modules/tranquil-config
node_modules/tranquil-theme-dark
node_modules/tranquil-theme-light Each should point to ../../tranquil-* (two levels up, into the sibling directory).
Dev packages symlinks
Symlink the owned packages into ~/.tranquil/dev/packages/ so Tranquil discovers them as dev packages rather than core packages in the Settings view:
mkdir -p ~/.tranquil/dev/packages
ln -s /Users/david/Documents/Tranquil/Repos/tranquil-automations ~/.tranquil/dev/packages/tranquil-automations
ln -s /Users/david/Documents/Tranquil/Repos/tranquil-browser ~/.tranquil/dev/packages/tranquil-browser
ln -s /Users/david/Documents/Tranquil/Repos/tranquil-config ~/.tranquil/dev/packages/tranquil-config
ln -s /Users/david/Documents/Tranquil/Repos/tranquil-theme-dark ~/.tranquil/dev/packages/tranquil-theme-dark
ln -s /Users/david/Documents/Tranquil/Repos/tranquil-theme-light ~/.tranquil/dev/packages/tranquil-theme-light
ln -s /Users/david/Documents/Tranquil/Repos/tranquil-tips ~/.tranquil/dev/packages/tranquil-tips Pulsar/Atom scans dev/packages/ before processing packageDependencies, so packages found there get isBundled: false and appear under Dev Packages instead of Core Packages. In a production build the symlinks won’t exist, so the packages fall through to packageDependencies and load from node_modules as bundled — which is the correct behavior for a shipped binary.
How link: Works
Yarn 1’s link: creates a symlink in node_modules pointing to the given path. Unlike file:, which copies the package into .yarn-cache on install, link: is a live reference — editing files in ../tranquil-automations/lib/ takes effect immediately in the running app without any reinstall.
package.json entries:
"tranquil-automations": "link:../tranquil-automations",
"tranquil-browser": "link:../tranquil-browser",
"tranquil-config": "link:../tranquil-config",
"tranquil-theme-dark": "link:../tranquil-theme-dark",
"tranquil-theme-light": "link:../tranquil-theme-light" These entries appear in both dependencies and packageDependencies, except tranquil-config which is only in dependencies — it is a utility library, not an Atom plugin, so Atom should not activate it directly.
Module Resolution
Sibling repos live outside tranquil-client. When Node.js resolves a symlink to its real path, upward traversal for node_modules starts from that real path — so packages inside tranquil-automations/ can’t find deps in tranquil-client/node_modules/.
The fix is to run yarn install inside each sibling package that has its own dependencies. Yarn creates a local node_modules/ there (including a tranquil-config symlink inside tranquil-automations/node_modules/), so all require() calls resolve correctly from the real path.
This is why the First-Time Setup section includes yarn install steps for tranquil-automations and tranquil-browser, not just tranquil-client.
A corollary that bites when adding a new package: a top-level require() for a dependency that only exists in tranquil-client/node_modules will throw at load and silently abort the package’s activation. See Adding a New Owned Package for the safe options (vendor the dep in the package, or require only atom + your own files).
tranquil-config Setup
tranquil-config provides API keys (GitHub) via a generated file that is never committed. After cloning tranquil-config, you must generate this file before yarn start will work.
One-time setup:
cd /Users/david/Documents/Tranquil/Repos/tranquil-config
# Copy the example and fill in real values
cp .env.example .env
# edit .env with actual keys
# Write config.json from .env
node write_config.js --staging # or --production
# Generate src/generated/index.js from config.json
node generate.js The generated file is gitignored and must be re-run whenever keys change. If you just need the app to start without real keys (e.g. after a fresh clone), you can create a stub:
cd /Users/david/Documents/Tranquil/Repos/tranquil-config
echo '{"github_token": ""}' > config.json
node generate.js Updating an Owned Package
Work directly in the sibling directory, commit, and push to its own repo:
cd /Users/david/Documents/Tranquil/Repos/tranquil-automations
# ... make changes ...
git add -p
git commit -m "fix: ..."
git push No changes to tranquil-client are needed unless the package’s npm name or API changes.
Adding a New Owned Package
The one fact that keeps costing time: under yarn start an owned package is loaded as a bundled package from tranquil-client/node_modules/<name>. That node_modules/<name> symlink
is what actually loads the package. The ~/.tranquil/dev/packages/<name> symlink is cosmetic —
it only moves the package from “Core Packages” to “Dev Packages” in the Settings view; on its own it
does not load the package under yarn start. Create the package with only the dev/packages
symlink and it silently never loads.
Steps, in order:
Create the sibling repo
../tranquil-new-thing/with apackage.jsonwhosemainpoints at the entry file, e.g."main": "./lib/main". (Atom loads<main>; ifmainis wrong or missing,activate()never runs.)Add the
link:entry to both blocks intranquil-client/package.json—dependenciesandpackageDependencies:"tranquil-new-thing": "link:../tranquil-new-thing"(Utility libraries Atom must not activate — like
tranquil-config— go independenciesonly.)Create the
node_modules/<name>symlink — this is the load-bearing step. Either runyarn installintranquil-client, or create it directly to skip a full install:cd tranquil-client ln -sfn ../../tranquil-new-thing node_modules/tranquil-new-thingVerify it resolves into the sibling repo and exposes
main:node -e "console.log(require.resolve('tranquil-new-thing/package.json'))"(Optional, cosmetic) Symlink into
~/.tranquil/dev/packages/so it appears under Dev Packages rather than Core Packages in Settings:ln -s /Users/david/Documents/Tranquil/Repos/tranquil-new-thing ~/.tranquil/dev/packages/tranquil-new-thingFully restart
yarn start— not just a window reload. Package discovery re-reads the symlinks at startup; a window that was already open when the symlink was missing has cached a stub package with nomain, so reloading it won’t pick up the fix.Create the private GitHub repo
tranquillabs/tranquil-new-thingand push.
Verifying it loaded (and diagnosing when it didn’t)
In the editor DevTools console (⌘⌥I):
atom.packages.getActivePackage('tranquil-new-thing') - Missing
node_modules/<name>symlink → you still get a package object (its name is inpackageDependencies), but a stub:pathpoints at.../node_modules/tranquil-new-thing,metadatais{name}only (nomain),mainModulePath: undefined,mainModule: null,mainActivated: false. Atom found the name but the path didn’t exist, so it never required a main module andactivate()never ran. → Do step 3, then fully restart. - Healthy →
mainModuleis your module object andmainActivated: true; commands/features are registered.
Gotcha — what an owned package may require
A package loaded via the node_modules/<name> symlink resolves its own require()s from its real
sibling-repo path, not from tranquil-client/node_modules. So a top-level require('some-app-dependency') that lives only in tranquil-client/node_modules throws at module
load, and a throw at load silently aborts activation — no error surfaced, no commands
registered. (Same silent-crash failure mode as a missing dependency in a main-process file.) Safe
options:
- Require only
atom, your ownlib/, and Node built-ins; prefer a design with zero external deps (e.g. register one command per option instead of pulling in a picker library); or - Give the package its own dependency and run
yarn installinside it so the module lands in<pkg>/node_modules— this is whattranquil-browserdoes foratom-space-pen-views.
When “the commands don’t show up,” suspect a throwing top-level require first, then check getActivePackage(...) as above.
Why Separate Repos?
tranquil-client is forked from Pulsar (open source). Keeping owned packages in their own repos means:
- No proprietary code in the Pulsar fork’s history
- Each package can be versioned and released independently
- The
tranquil-clientremote stays clean