sfpm
Getting started

Migrate an existing project

Convert an sfdx project with package directories into the sfpm workspace layout.

Use this page when your project already has an sfdx-project.json. The migration reads that file and writes workspace files next to it. Your metadata stays where it is.

Before you start

  • Commit your current state, or work on a branch. The migration writes files into your project.
  • Check the packageDirectories in sfdx-project.json. sfpm migrates only the entries that have both a package name and a versionNumber. Other entries are skipped, and the migration does not report them.

Run the migration

sfpm project init turbo --migrate --npm-scope @myorg
FlagDescription
--migrate, -mMigrate from the existing sfdx-project.json. Without this flag, the command scaffolds a new workspace. If the file exists and you run interactively without this flag, sfpm asks whether to migrate instead.
--npm-scope <scope>npm scope for package names, for example @myorg. sfpm adds the @ if it is missing. Without the flag, sfpm reads the scope from the root package.json name or the .npmrc registry line. A non-interactive run fails when no scope is found.
--package-manager <pm>pnpm (default), npm, or yarn. This guide covers pnpm workspaces only.
--yes, -ySkip the prompts and use the defaults.
--jsonPrint the result as JSON. Like --yes, this skips the prompts.

Do not pass --workspace-dir. The flag is accepted, but the workspace it produces cannot find its packages, so it does not build. Migrate without it. Each package keeps its original directory and gets its own package.json there. If you run the migration interactively without --yes, it asks where package directories should live; choose "Keep original paths", not "packages/" or "Custom..." (those are the same broken layout as passing --workspace-dir directly).

What the migration writes

Project root

  • package.json is created, or updated if it exists. The root is marked private, turbo is added as a dev dependency, and the sfpm:build, sfpm:deploy, and sfpm:install scripts are added. Those scripts run through Turborepo.
  • pnpm-workspace.yaml lists the directories that hold packages. Directories under the same parent collapse into one glob, such as packages/*.
  • turbo.json defines the sfpm:build, sfpm:deploy, and sfpm:install tasks. Build output goes to dist/, and build results are cached. The migration does not ask for input directories, so the build inputs use Turborepo's default (all git-tracked files).
  • .gitignore gets sfdx-project.json and dist added, if they are not already listed.

The root package.json also gets a sfpm:sync script that runs sfpm sync. That command does not exist. Change the script to run sfpm project sync.

Each package

For each package in sfdx-project.json, the migration writes a package.json:

  • Location. The file goes in the package's existing directory, next to its source.
  • name is <npm scope>/<package name>.
  • version comes from versionNumber. A build segment other than NEXT or 0 is kept as a prerelease suffix. For example, 1.2.0.LATEST becomes 1.2.0-LATEST. Check the version after migrating.
  • sfpm.packageType is unlocked unless the packageDirectories entry has a type key.
  • sfpm.path is the source path relative to the package directory. It is left out when the source sits at the package root.
  • sfpm.packageId is copied from packageAliases when the alias is a Package2 ID.
  • Dependencies on other migrated packages become workspace:^<version>.
  • Scripts sfpm:build, sfpm:deploy, and sfpm:install run sfpm build, sfpm deploy, and sfpm install for that package with --turbo.

If a package.json already exists in the target directory, the migration merges into it. Existing scripts and dependencies entries stay, except for the sfpm script names. Top-level fields with the same names, such as name, version, and sfpm, are replaced.

Every package is written as unlocked unless its packageDirectories entry has a type key. Open each package.json and set packageType to source or data where that is the real type.

What the migration does not do

  • It does not move or copy metadata. The source stays where it is. Each package's sfpm.path points at its existing source directory.
  • It does not delete or rewrite sfdx-project.json. The next sfpm project sync rewrites it. If git already tracks the file, it stays tracked until you run git rm --cached sfdx-project.json.
  • It does not carry over the namespace. If a package has a namespace, add "namespace": "<prefix>" to that package's sfpm block.

After the migration

pnpm install
sfpm project sync
git status

Review each package.json, paying attention to packageType, version, and sfpm.path. Commit the workspace files and the package.json files. Do not commit sfdx-project.json, because it is generated.

For the flags on each command, see the project commands reference.

On this page