sfpm

Introduction

sfpm is pnpm and Turborepo for Salesforce metadata. It builds, caches, and deploys packages in dependency order.

sfpm is pnpm and Turborepo for Salesforce metadata. Each Salesforce package lives in a workspace directory with its own package.json. sfpm builds those packages in dependency order, skips packages whose source has not changed, and deploys or installs them across orgs.

The command is sfpm. It runs on your machine and in CI. The same flows are also available as GitHub Actions.

sfpm is pre-1.0. Commands and flags may change between releases. Check sfpm --help when you upgrade.

What it does

  • Dependency-ordered builds. A package builds after the packages it depends on. Pass --no-dependencies to build only the packages you name.
  • Change detection and caching. sfpm skips a package when its source has not changed. Pass --force to build it anyway. In a Turborepo workspace, the generated turbo.json also caches sfpm:build output.
  • A pnpm workspace. Every package has its own package.json. The sfpm field in that file holds the package type, source path, and options. The sfdx-project.json file is generated from these files by sfpm project sync. Do not edit it by hand.
  • Deploy and install. sfpm deploy deploys package source to an org. sfpm install installs built artifacts.
  • Script hooks. Run your own npm scripts or script files around build and install operations. sfpm does not compile LWC, TypeScript, or SCSS itself. Compile first, then run sfpm. See the section below.
  • Custom lifecycle hooks. You can write your own hooks against the LifecycleHooks type from @b64hub/sfpm-core, and register them in sfpm.config.ts with defineConfig. Built-in hooks ship in @b64hub/sfpm-hooks.
  • Local validation. When Nimbus is installed, sfpm build runs compile and dependency checks at the default local validation level.

Compile before sfpm

sfpm builds the source that is on disk when it runs. A script hook cannot add compiled output to a build. Run your compiler as its own step before sfpm. In a Turborepo workspace, make the compile task a dependency of the package's sfpm:build task, so Turborepo runs it first.

The package declares its compile script in its own package.json. The compiler must write its output into the sfpm.path directory, because that is the source sfpm builds:

{
  "name": "@myorg/core",
  "version": "1.0.0",
  "scripts": {
    "compile": "tsc -p tsconfig.json"
  },
  "sfpm": {
    "packageType": "unlocked",
    "path": "force-app"
  }
}

In the workspace root turbo.json, sfpm:build depends on compile:

{
  "tasks": {
    "compile": {
      "cache": false
    },
    "sfpm:build": {
      "cache": true,
      "dependsOn": ["compile", "^sfpm:build"],
      "env": ["SF_DEV_HUB", "SFPM_FORCE_BUILD", "SFDX_AUTH_URL", "SF_ACCESS_TOKEN"],
      "inputs": ["$TURBO_DEFAULT$"],
      "outputs": ["dist/**"]
    }
  }
}

Run the root sfpm:build script as usual. Turborepo runs each package's compile script before that package's build.

sfpm project init turbo writes the sfpm:build entry in turbo.json. If you run it again, add the compile dependency back.

A pre build script hook runs after sfpm has already copied the package into dist/, so anything it writes is not part of that build. A pre install script runs from the installed package directory, which has no tsconfig.json and no dev toolchain. Compile before you invoke sfpm in both cases.

Packages

PackagePurpose
@b64hub/sfpm-cliThe sfpm command. This is the package you install.
@b64hub/sfpm-coreBuild and install orchestration, the project model, and the types for sfpm.config.ts and lifecycle hooks.
@b64hub/sfpm-hooksBuilt-in lifecycle hooks, including script hooks.
@b64hub/sfpm-sfdmuData package support, through the SFDMU plugin for the Salesforce CLI.

When it fits

sfpm fits teams that maintain several Salesforce packages with dependencies between them, deploy those packages to more than one org, and want CI to build and deploy them in dependency order.

When it does not

A single org with one package and no internal dependencies usually does not need sfpm. The Salesforce CLI covers that case with less setup. sfpm adds a workspace layout, a generated sfdx-project.json, and a build step that you then maintain.

Where to next

On this page