Skip to content

Rethink web app setup: decouple gscan.ghost.org server from the published library/CLI #730

@coderabbitai

Description

@coderabbitai

Background

Currently gscan ships as a single package that contains three distinct concerns in one package.json:

  1. The library (lib/) — the core gscan API consumed by other packages.
  2. The CLI (bin/cli.js) — the command-line tool published to npm.
  3. The web server (app/) — the gscan.ghost.org website, deployed from source.

This came up during dependency-update work in #729 and a comment thread at #729 (comment).

Problem

Because all three concerns share one package.json, 7 of 18 runtime dependencies are app-only and are never imported by lib/ or bin/:

App-only dependency Why it's app-only
@sentry/node app/middlewares/sentry.js only
@tryghost/config app/index.js only
@tryghost/logging app/middlewares/log-request.js only
@tryghost/server app/index.js only
express app/index.js only
express-handlebars app/index.js only
multer app/middlewares/upload-validation.js only

Every consumer who runs npm install gscan (as a library or CLI tool) installs all seven of these packages unnecessarily. This also means transitive security issues in those packages (e.g. node-loggly-bulk, axios) require Yarn resolutions workarounds that only protect the deployment server — a subtle, non-obvious coupling.

Suggested approaches

Option A — Move the web app to its own repository

Extract app/ into a new repo (e.g. TryGhost/gscan-web) that depends on the published gscan npm package. The web app gets its own package.json, lockfile, and deploy pipeline, fully decoupled from the library.

Pros: complete separation; library consumers get a lean install; security issues in web-server deps don't touch the library at all.
Cons: most migration effort; two repos to maintain.

Option B — Yarn/pnpm workspaces monorepo

Keep everything in one repo but split into workspaces:

packages/
  lib/        (published as gscan)
  cli/        (published as gscan-cli or bin entry)
  web/        (not published, deployed from source)

Each workspace has its own package.json with only the deps it actually needs.

Pros: one repo, clean dep boundaries, standard monorepo pattern.
Cons: requires restructuring the directory layout.

Option C — Separate package.json inside app/

Add an app/package.json listing only the app-specific deps, and deploy the site by running yarn install from inside app/ rather than the root. The root package.json retains only library and CLI deps.

Pros: minimal structural change; deploy script change only.
Cons: slightly unconventional; shared deps (e.g. fs-extra, lodash) would need to be listed in both files.

Acceptance criteria

  • Library consumers (npm install gscan) no longer install web-server-only dependencies.
  • The web server continues to work and deploy correctly.
  • Transitive dependency management (e.g. pinning node-loggly-bulk) is handled within the web app's own manifest, not the library's.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions