Skip to main content

Development

Monorepo​

The repository is an Nx monorepo with these projects:

  • plugin: the payload-crowdin-sync package.
  • document: the payload-localized-fields package — Payload field walks used by the plugin and usable by other import/export plugins.
  • adapters: the payload-localization-adapters package — vendor-neutral SourceWriter / TranslationReader contracts. Phrase and Lokalise plugins in this monorepo will depend on it.

How those three packages call each other, and what a "document slice" is, is in packages.

  • dev: a Payload install that uses the plugin, for integration tests. Tests that need a different plugin config pass pluginOptionsOverride to initPayloadInt.
  • docs-site: this documentation site.

Build​

In the root of the repository:

  • nx build plugin builds to dist/plugin.
  • nx build document builds to dist/document.
  • nx build adapters builds to dist/adapters.
  • nx build plugin --watch rebuilds when files change.
  • nx run document:native-esm (and adapters / plugin) builds, rewrites published entrypoints, and loads the package with Node's native ESM resolver. Production relative imports use .js specifiers so that import works without a bundler.

Lint​

  • nx lint plugin
  • nx lint dev

Test​

Tests use Vitest. In the root of the repository:

  • npm run test runs all tests.
  • npm run test:coverage runs all tests with coverage. Reports are written to coverage/<project>.
  • nx test plugin runs plugin unit tests only.
  • nx test document runs payload-localized-fields unit tests.
  • nx test adapters runs payload-localization-adapters unit tests.
  • nx test dev runs integration tests against the Payload install.

Plugin unit tests (*.spec.ts) sit next to the code they test in plugin/src. payload-localized-fields specs sit in document/src. Writer/reader specs sit in adapters/src. Payload field walks and the json/html document slice (getLocalizedFields, traverseFields, restoreOrder, buildJsonObject, buildHtmlObject, buildUpdateObject) live in the payload-localized-fields package (document/). Vendor-neutral upload/download orchestration (syncDocumentSource, applyDocumentSlice) lives in payload-localization-adapters (adapters/). Crowdin wrappers and rich-text HTML conversion stay in plugin/src/lib/utilities/. The Crowdin plugin re-exports the adapter contracts.

Integration tests (*.test.ts) are in dev/src/tests. They use Payload's Local API against a real Payload install with an in-memory MongoDB database. Calls to the Crowdin API are intercepted with nock, so tests check which requests the plugin makes without needing a Crowdin account.

To start Payload with a different plugin config, pass pluginOptionsOverride to initPayloadInt. The override is shallow-merged over the default test options, so { collections: undefined } replaces the default collections list. Pass the same override to pluginConfig when setting up Crowdin mocks.

CI runs npm run test:coverage and uploads the results to Codecov. Plugin coverage also includes document/src and adapters/src so Crowdin wrapper specs still count hits on the shared packages. This needs the CODECOV_TOKEN repository secret.

Import maps​

Payload finds custom admin components through an import map. After changing admin components in dev/, regenerate it:

npm run generate:importmaps

CI fails if the committed import map is missing or out of date.

Running the integration tests can overwrite dev/src/app/(payload)/admin/importMap.js with an empty map. Restore it with git restore before committing.

Run a Payload install locally​

cd dev
npm run dev

A local install is useful for trying out changes in the admin panel, and for generating fixtures for integration tests.