Symfony UX is a monorepo of PHP (Symfony bundles) + TypeScript/JS (Stimulus controllers) packages.
Each package lives under src/<Package>/ with PHP code in src/ and JS assets in assets/.
Package manager: pnpm (v10, via Corepack). Node 22. PHP >= 8.4.
# JS/TS — from repo root
pnpm install
pnpm run build # build all packages (tsdown)
# JS/TS — single package
pnpm run build --filter @symfony/ux-autocomplete
# PHP — per-package
cd src/Autocomplete && composer install# All tests for a package
cd src/Autocomplete
php vendor/bin/phpunit
# Single test file
php vendor/bin/phpunit tests/Unit/SomeTest.php
# Single test method
php vendor/bin/phpunit --filter testMethodName# All packages from root
pnpm run test:unit
# Single package
cd src/Autocomplete/assets
pnpm run test:unit
# Single test file
pnpm exec vitest --run test/unit/some.test.ts# All packages from root
pnpm run test:browser
# Single package
cd src/Autocomplete/assets
pnpm run test:browser
# Single test file
pnpm exec playwright test test/browser/some.test.ts
# Interactive UI mode
pnpm run test:browser:ui# JS/TS lint (oxlint)
pnpm run lint
pnpm run lint:fix
# JS/TS/MD format (oxfmt)
pnpm run fmt
pnpm run fmt:check
# PHP code style (PHP-CS-Fixer, @Symfony ruleset)
php vendor/bin/php-cs-fixer fix
# Twig templates
php vendor/bin/twig-cs-fixer lint- Ruleset:
@Symfony+@Symfony:riskyvia PHP-CS-Fixer (.php-cs-fixer.dist.php) declare(strict_types=1): not enforced globally, but follow existing file conventions- Namespaces: PSR-4, e.g.
Symfony\UX\Autocomplete\... - Classes: prefer
finalclasses. Use PascalCase. Noreadonlyclasses. - Methods: camelCase, always typed parameters and return types
- Properties: typed, use constructor promotion and
readonlywhere appropriate - Imports: one
useper line, grouped (PHP classes, then traits, then interfaces). No aliasing unless conflicts. - File header: every PHP file must have the Symfony license header comment (auto-fixed by CS fixer):
/* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */
- Error handling: throw specific exceptions (InvalidArgumentException, LogicException, RuntimeException). No generic
\Exception. - Doc comments: use
@authoron classes. PHPDoc only when types can't express the contract (generics, union details). Don't duplicate type info already in signatures.
- Formatter: oxfmt (
.oxfmtrc.json) - Linter: oxlint (
.oxlintrc.json), default rules - Module system: ESM (
"type": "module") - Imports: named imports preferred,
typekeyword for type-only imports (import type { ... }) - Naming: camelCase for variables/functions, PascalCase for classes/interfaces/types
- Stimulus controllers: extend
Controllerfrom@hotwired/stimulus, usestatic values = {}pattern,declare readonlyfor value properties - Tests: Vitest with
@testing-library/dom+@testing-library/jest-dommatchers. Playwright for browser tests.
src/
<Package>/
.github/ # CI workflows, PR template, for subtree split
.gitignore
.gitattributes
src/ # PHP source (PSR-4)
tests/ # PHPUnit tests
assets/ # Frontend assets
src/ # TypeScript source
dist/ # Built output (committed!)
test/ # JS tests (unit/ and browser/)
package.json
vitest.config.mjs
playwright.config.ts
config/ # Symfony DI service definitions
composer.json
phpunit.xml.dist
apps/
demo-native/ # Symfony app for manual testing and demos of Symfony UX Native
e2e/ # Symfony app for Playwright browser tests
encore/ # Symfony app for testing Webpack Encore and `npm install` integration
- Dist files are committed: after changing TS source, run
pnpm run buildand commit thedist/output. - pnpm workspaces: packages are
src/*/assetsandsrc/*/src/Bridge/*/assets. - Peer dependency matrix: JS unit tests may run against multiple versions of peer deps (handled by
bin/unit_test_package.sh). - PHPStan: currently only configured for
src/Turbo(phpstan.dist.neon). - Snapshot tests: some packages (Toolkit) use PHPUnit snapshots — update with
php vendor/bin/simple-phpunit -d --update-snapshots.
- Ensure to have written tests for any new features or bug fixes, and that all tests pass locally, if it makes sense to do so (e.g. for a pure JS change, running PHP tests is not required).
- Run
oxfmtandoxlintto ensure non-PHP code style and linting are clean, - Run
php-cs-fixerto ensure PHP code style is clean, - Run
twig-cs-fixerto ensure Twig templates are properly formatted, - Run all tests for the affected package(s) (PHPUnit, Vitest, Playwright) to ensure no regressions:
- Be careful with JS unit-tests, the
package.jsoncan be modified to run tests against multiple versions of peer dependencies, so make sure to not accidentally commit temporary changes topackage.json
- Be careful with JS unit-tests, the