Skip to content

Conversation

@denolfe
Copy link
Member

@denolfe denolfe commented Nov 17, 2025

Overview

Refactor create-payload-app from string manipulation to AST-based transformations. This will allow us to be more flexible in the future if we want to add more functionality.

Key Changes

  • AST transformation engine (packages/create-payload-app/src/lib/ast/)

    • Type-safe payload config manipulation (database/storage adapters, imports, plugins)
    • Package.json dependency management
    • Prettier integration for formatted output
  • Template normalization

    • Removed comment markers from all templates
    • Standardized config structures for consistent AST processing
  • Improved reliability

    • Replaced fragile regex patterns with structural code analysis
    • Proper handling of import aliases and property positions

Design Decisions

AST over string manipulation

String-based replacements were brittle and error-prone with variations in formatting, import order, and code
structure. Using ts-morph provides:

  • Type-aware transformations that understand TypeScript syntax
  • Preservation of code formatting via Prettier
  • Maintainable operations that won't break on whitespace changes

Adapter configuration centralization

Database and storage adapter configs are now centralized in adapter-config.ts rather than scattered across
template files and replacement logic. This reduces duplication and makes adding new adapters
straightforward.

Template cleanup

Removed comment markers (e.g., // database-adapter-import-start) from templates since AST can locate
insertion points structurally. Templates now serve as clean, production-ready code without scaffolding
artifacts.

Overall Flow

sequenceDiagram
    participant CLI as create-payload-app
    participant AST as AST Engine
    participant TS as ts-morph
    participant Template as Template Files

    CLI->>Template: Copy template
    CLI->>AST: configurePayloadConfig(options)
    AST->>TS: Parse payload.config.ts
    AST->>TS: Transform config structure
    Note over AST,TS: Replace DB/storage adapters, add/remove plugins, update imports
    TS->>AST: Modified AST
    AST->>AST: Format with Prettier
    AST->>Template: Write updated file
    AST->>Template: Update package.json
Loading

Implement AST-based detection of buildConfig call structure in Payload config files using TDD approach. Detects:
- buildConfig CallExpression
- Import declarations
- db property assignment
- plugins array

All tests pass (3/3).
Implements removeSharp function using TDD to remove sharp import and property from payload config files. This completes Phase 3 - Transformation Implementation.
Refactored configure-payload-config.ts to use AST-based transformations instead of string manipulation:

- Replaced string manipulation logic with calls to configurePayloadConfig() and updatePackageJson() from AST modules
- Added helper functions: mapDbType(), mapStorageAdapter(), and getEnvVarName()
- Enhanced AST payload-config module with removeCommentMarkers() to clean up template markers
- Fixed quote style conversion (double to single quotes)
- Added indentation normalization to handle ts-morph's formatting quirks
- Updated database and storage adapter templates with proper indentation
- All existing integration tests pass
Removed AST comment markers from all template payload.config.ts files:
- storage-adapter-import-placeholder
- database-adapter-import
- database-adapter-config-start
- database-adapter-config-end
- storage-adapter-placeholder
- sharp-import

Normalized template structures with clean imports and empty plugins arrays where applicable.

Templates cleaned:
- templates/_template/src/payload.config.ts
- templates/blank/src/payload.config.ts
- templates/website/src/payload.config.ts
- templates/ecommerce/src/payload.config.ts
- templates/with-cloudflare-d1/src/payload.config.ts
- templates/with-vercel-website/src/payload.config.ts
Removed string-based replacement utilities that were superseded by AST-based config manipulation. Updated test to verify adapter configuration directly instead of using replacement templates.
Fixed .js extension in AST utils import and updated pnpm-lock.yaml with prettier version.
Add integration tests for sampled template × database × storage combinations.
Tests verify AST transformations are correctly applied to payload.config.ts
and package.json for representative combinations:
- blank + mongodb + localDisk
- blank + postgres + vercelBlobStorage
- website + mongodb + s3Storage
- website + postgres + localDisk
- ecommerce + mongodb + localDisk
- ecommerce + postgres + r2Storage

Tests copy template to temp directory, apply transformations via
configurePayloadConfig(), and verify imports/config properties are
correctly modified. Skip TypeScript compilation for speed.
- Add .js extension to types import in package-json.ts
- Add type assertion for DB_PACKAGE_NAMES lookup
- Import QuoteKind enum from ts-morph
- Add null checks for regex match results in indentation normalization
- Remove invalid indentationText setting
…, azureStorage, gcsStorage, uploadthingStorage)
Convert all interfaces to types in AST implementation:
- types.ts: 7 interfaces (DetectionError, PayloadConfigStructures, etc.)
- adapter-config.ts: 2 interfaces (DatabaseAdapterConfig, StorageAdapterConfig)
- utils.ts: 2 internal interfaces (FormatErrorOptions, AddImportOptions)
- package-json.ts: 2 internal interfaces (PackageJsonTransformOptions, PackageJsonStructure)

ConfigureOptions now uses intersection type (WriteOptions & {...}) instead of extends.
…ations

Add debug logging throughout AST transformation pipeline when --debug flag is passed:

**payload-config.ts:**
- Detection phase: buildConfig found/missing, import counts, structure details
- Transformations: adapter types, imports added/removed, special imports (vercel-postgres, d1-sqlite)
- Validation: structure validation results
- Write phase: quote/indent normalization, prettier status, file operations

**utils.ts:**
- Import operations: additions to existing vs new imports, removals

**package-json.ts:**
- Dependency updates: adapter changes, sharp removal, package name updates

**Integration:**
- configure-payload-config.ts: accepts debugMode parameter
- create-project.ts: passes cliArgs['--debug'] to AST functions

All debug messages use [AST] prefix with status indicators (✓ success, ✗ failure, ⚠ warning).
… code

- Set process.env.DEBUG in main.ts when --debug flag is passed
- Update debug() function to check process.env.DEBUG internally
- Remove debugMode parameters from all AST functions (56 if-blocks eliminated)
- Update all call sites to remove debugMode arguments
- Remove debugMode from WriteOptions and ConfigureOptions types

This eliminates ~150 LOC and improves code readability by removing
conditional debug blocks throughout the codebase.
… with 2+ params

- addDatabaseAdapter: (sourceFile, adapter, envVarName) → (sourceFile, { adapter, envVarName })
- addStorageAdapter: (sourceFile, adapter) → (sourceFile, { adapter })
- findImportDeclaration: (sourceFile, moduleSpecifier) → (sourceFile, { moduleSpecifier })
- removeImportDeclaration: (sourceFile, moduleSpecifier) → (sourceFile, { moduleSpecifier })

Benefits:
- Improved backwards-compatibility (can add new options without breaking changes)
- More explicit at call sites
- Consistent with existing functions (addImportDeclaration, configurePayloadConfig)

All 64 tests passing.
… AST code

Replace dynamic prettier import with CLI execution to resolve Jest/ESM issues.

- Add detectPackageManager() for pnpm/npm/yarn/bun detection
- Refactor writeTransformedFile() to call prettier CLI after save
- Remove 50+ lines of manual normalization code
- Enable formatWithPrettier by default
- Update tests to use prettier (remove formatWithPrettier: false)
@github-actions
Copy link
Contributor

github-actions bot commented Nov 17, 2025

📦 esbuild Bundle Analysis for payload

This analysis was generated by esbuild-bundle-analyzer. 🤖

Meta File Out File Size (raw) Note
packages/next/meta_index.json esbuild/index.js 810.16 KB ✅ No change
packages/payload/meta_index.json esbuild/index.js 1.23 MB ⚠️ +171 B (+0.0%)
packages/payload/meta_shared.json esbuild/exports/shared.js 164.12 KB ✅ No change
packages/richtext-lexical/meta_client.json esbuild/exports/client_optimized/index.js 281.03 KB ✅ No change
packages/ui/meta_client.json esbuild/exports/client_optimized/index.js 1.16 MB ⚠️ +1.77 KB (+0.2%)
packages/ui/meta_shared.json esbuild/exports/shared_optimized/index.js 14.39 KB ✅ No change
Largest paths These visualization shows top 20 largest paths in the bundle.

Meta file: packages/next/meta_index.json, Out file: esbuild/index.js

Path Size
../../node_modules ${{\color{Goldenrod}{ ████████████████████▎ }}}$ 81.3%, 654.69 KB
dist/views/Version ${{\color{Goldenrod}{ █▌ }}}$ 6.2%, 50.26 KB
dist/views/Document ${{\color{Goldenrod}{ ▍ }}}$ 1.9%, 15.45 KB
dist/views/List ${{\color{Goldenrod}{ ▎ }}}$ 1.4%, 11.28 KB
dist/views/Root ${{\color{Goldenrod}{ ▎ }}}$ 1.1%, 8.97 KB
dist/views/API ${{\color{Goldenrod}{ ▏ }}}$ 0.7%, 6.01 KB
dist/views/Versions ${{\color{Goldenrod}{ ▏ }}}$ 0.7%, 5.96 KB
dist/elements/Nav ${{\color{Goldenrod}{ ▏ }}}$ 0.7%, 5.53 KB
dist/views/Account ${{\color{Goldenrod}{ ▏ }}}$ 0.7%, 5.32 KB
dist/elements/DocumentHeader ${{\color{Goldenrod}{ ▏ }}}$ 0.6%, 4.81 KB
dist/views/Login ${{\color{Goldenrod}{ ▏ }}}$ 0.5%, 4.39 KB
dist/views/Dashboard ${{\color{Goldenrod}{ ▏ }}}$ 0.5%, 3.69 KB
dist/views/ForgotPassword ${{\color{Goldenrod}{ }}}$ 0.4%, 3.09 KB
dist/layouts/Root ${{\color{Goldenrod}{ }}}$ 0.4%, 2.91 KB
dist/templates/Default ${{\color{Goldenrod}{ }}}$ 0.4%, 2.83 KB
dist/views/CreateFirstUser ${{\color{Goldenrod}{ }}}$ 0.3%, 2.76 KB
dist/views/BrowseByFolder ${{\color{Goldenrod}{ }}}$ 0.3%, 2.58 KB
dist/views/CollectionFolders ${{\color{Goldenrod}{ }}}$ 0.3%, 2.46 KB
dist/views/ResetPassword ${{\color{Goldenrod}{ }}}$ 0.3%, 2.41 KB
dist/views/Logout ${{\color{Goldenrod}{ }}}$ 0.2%, 1.92 KB
(other) ${{\color{Goldenrod}{ ████▋ }}}$ 18.7%, 150.79 KB

Meta file: packages/payload/meta_index.json, Out file: esbuild/index.js

Path Size
../../node_modules ${{\color{Goldenrod}{ █████████████████▏ }}}$ 68.7%, 841.11 KB
dist/fields/hooks ${{\color{Goldenrod}{ ▉ }}}$ 3.5%, 43.31 KB
dist/collections/operations ${{\color{Goldenrod}{ ▊ }}}$ 3.0%, 36.40 KB
dist/auth/operations ${{\color{Goldenrod}{ ▎ }}}$ 1.2%, 15.23 KB
dist/queues/operations ${{\color{Goldenrod}{ ▎ }}}$ 1.0%, 12.11 KB
dist/globals/operations ${{\color{Goldenrod}{ ▎ }}}$ 1.0%, 11.98 KB
dist/fields/config ${{\color{Goldenrod}{ ▎ }}}$ 1.0%, 11.95 KB
dist/utilities/configToJSONSchema.js ${{\color{Goldenrod}{ ▎ }}}$ 1.0%, 11.83 KB
dist/fields/validations.js ${{\color{Goldenrod}{ ▏ }}}$ 0.8%, 10.21 KB
dist/bin/generateImportMap ${{\color{Goldenrod}{ ▏ }}}$ 0.7%, 8.38 KB
dist/database/migrations ${{\color{Goldenrod}{ ▏ }}}$ 0.7%, 8.07 KB
dist/collections/config ${{\color{Goldenrod}{ ▏ }}}$ 0.6%, 7.90 KB
dist/uploads/fetchAPI-multipart ${{\color{Goldenrod}{ ▏ }}}$ 0.6%, 7.74 KB
dist/index.js ${{\color{Goldenrod}{ ▏ }}}$ 0.6%, 7.59 KB
dist/config/orderable ${{\color{Goldenrod}{ ▏ }}}$ 0.5%, 6.27 KB
dist/collections/endpoints ${{\color{Goldenrod}{ ▏ }}}$ 0.5%, 6.00 KB
dist/auth/strategies ${{\color{Goldenrod}{ }}}$ 0.4%, 5.50 KB
dist/config/sanitize.js ${{\color{Goldenrod}{ }}}$ 0.4%, 5.49 KB
dist/auth/endpoints ${{\color{Goldenrod}{ }}}$ 0.4%, 5.42 KB
dist/utilities/telemetry ${{\color{Goldenrod}{ }}}$ 0.4%, 5.31 KB
(other) ${{\color{Goldenrod}{ ███████▊ }}}$ 31.3%, 383.04 KB

Meta file: packages/payload/meta_shared.json, Out file: esbuild/exports/shared.js

Path Size
../../node_modules ${{\color{Goldenrod}{ ███████████████████▊ }}}$ 79.0%, 126.93 KB
dist/fields/validations.js ${{\color{Goldenrod}{ █▌ }}}$ 6.4%, 10.21 KB
dist/fields/baseFields ${{\color{Goldenrod}{ ▍ }}}$ 1.7%, 2.79 KB
dist/utilities/deepCopyObject.js ${{\color{Goldenrod}{ ▍ }}}$ 1.5%, 2.48 KB
dist/auth/cookies.js ${{\color{Goldenrod}{ ▎ }}}$ 1.0%, 1.55 KB
dist/utilities/flattenTopLevelFields.js ${{\color{Goldenrod}{ ▏ }}}$ 0.9%, 1.42 KB
dist/fields/config ${{\color{Goldenrod}{ ▏ }}}$ 0.8%, 1.28 KB
dist/utilities/flattenAllFields.js ${{\color{Goldenrod}{ ▏ }}}$ 0.6%, 943 B
dist/folders/utils ${{\color{Goldenrod}{ ▏ }}}$ 0.6%, 916 B
dist/utilities/getVersionsConfig.js ${{\color{Goldenrod}{ ▏ }}}$ 0.6%, 895 B
dist/utilities/unflatten.js ${{\color{Goldenrod}{ ▏ }}}$ 0.5%, 779 B
dist/utilities/sanitizeUserDataForEmail.js ${{\color{Goldenrod}{ }}}$ 0.4%, 713 B
dist/utilities/getFieldPermissions.js ${{\color{Goldenrod}{ }}}$ 0.4%, 651 B
dist/collections/config ${{\color{Goldenrod}{ }}}$ 0.4%, 570 B
dist/bin/generateImportMap ${{\color{Goldenrod}{ }}}$ 0.3%, 561 B
dist/auth/sessions.js ${{\color{Goldenrod}{ }}}$ 0.3%, 525 B
dist/utilities/getSafeRedirect.js ${{\color{Goldenrod}{ }}}$ 0.3%, 423 B
dist/utilities/deepMerge.js ${{\color{Goldenrod}{ }}}$ 0.3%, 413 B
dist/utilities/formatLabels.js ${{\color{Goldenrod}{ }}}$ 0.2%, 380 B
dist/utilities/appendUploadSelectFields.js ${{\color{Goldenrod}{ }}}$ 0.2%, 360 B
(other) ${{\color{Goldenrod}{ █████▎ }}}$ 21.0%, 33.69 KB

Meta file: packages/richtext-lexical/meta_client.json, Out file: esbuild/exports/client_optimized/index.js

Path Size
dist/features/blocks ${{\color{Goldenrod}{ ███▏ }}}$ 12.6%, 35.02 KB
dist/lexical/plugins ${{\color{Goldenrod}{ ██▉ }}}$ 11.5%, 32.00 KB
dist/lexical/ui ${{\color{Goldenrod}{ ██▏ }}}$ 8.8%, 24.36 KB
dist/features/experimental_table ${{\color{Goldenrod}{ ██▏ }}}$ 8.5%, 23.70 KB
dist/packages/@lexical ${{\color{Goldenrod}{ █▋ }}}$ 6.8%, 18.99 KB
dist/features/link ${{\color{Goldenrod}{ █▋ }}}$ 6.5%, 18.04 KB
dist/features/toolbars ${{\color{Goldenrod}{ █▌ }}}$ 6.4%, 17.75 KB
dist/features/upload ${{\color{Goldenrod}{ █▏ }}}$ 4.9%, 13.73 KB
dist/features/textState ${{\color{Goldenrod}{ █ }}}$ 4.0%, 11.08 KB
dist/features/relationship ${{\color{Goldenrod}{ ▊ }}}$ 3.2%, 8.96 KB
dist/lexical/utils ${{\color{Goldenrod}{ ▊ }}}$ 3.0%, 8.22 KB
dist/features/debug ${{\color{Goldenrod}{ ▋ }}}$ 2.7%, 7.39 KB
dist/utilities/fieldsDrawer ${{\color{Goldenrod}{ ▋ }}}$ 2.6%, 7.12 KB
dist/features/converters ${{\color{Goldenrod}{ ▋ }}}$ 2.5%, 7.04 KB
dist/lexical/config ${{\color{Goldenrod}{ ▍ }}}$ 1.8%, 5.08 KB
dist/features/lists ${{\color{Goldenrod}{ ▍ }}}$ 1.8%, 5.00 KB
dist/features/format ${{\color{Goldenrod}{ ▎ }}}$ 1.2%, 3.46 KB
dist/lexical/LexicalEditor.js ${{\color{Goldenrod}{ ▎ }}}$ 1.1%, 3.17 KB
dist/lexical/theme ${{\color{Goldenrod}{ ▏ }}}$ 0.9%, 2.62 KB
dist/features/indent ${{\color{Goldenrod}{ ▏ }}}$ 0.9%, 2.50 KB
(other) ${{\color{Goldenrod}{ █████████████████████▊ }}}$ 87.4%, 242.76 KB

Meta file: packages/ui/meta_client.json, Out file: esbuild/exports/client_optimized/index.js

Path Size
../../node_modules ${{\color{Goldenrod}{ ████████████▍ }}}$ 49.9%, 572.85 KB
dist/elements/FolderView ${{\color{Goldenrod}{ ▋ }}}$ 2.5%, 29.18 KB
dist/elements/BulkUpload ${{\color{Goldenrod}{ ▌ }}}$ 2.4%, 27.14 KB
dist/elements/WhereBuilder ${{\color{Goldenrod}{ ▍ }}}$ 1.5%, 16.85 KB
dist/views/Edit ${{\color{Goldenrod}{ ▎ }}}$ 1.4%, 16.01 KB
dist/fields/Relationship ${{\color{Goldenrod}{ ▎ }}}$ 1.4%, 15.75 KB
dist/elements/Table ${{\color{Goldenrod}{ ▎ }}}$ 1.3%, 15.46 KB
dist/forms/Form ${{\color{Goldenrod}{ ▎ }}}$ 1.3%, 15.08 KB
dist/fields/Upload ${{\color{Goldenrod}{ ▎ }}}$ 1.2%, 14.06 KB
dist/fields/Blocks ${{\color{Goldenrod}{ ▎ }}}$ 1.2%, 13.70 KB
dist/elements/QueryPresets ${{\color{Goldenrod}{ ▏ }}}$ 0.9%, 10.28 KB
dist/elements/PublishButton ${{\color{Goldenrod}{ ▏ }}}$ 0.8%, 8.68 KB
dist/providers/Folders ${{\color{Goldenrod}{ ▏ }}}$ 0.7%, 8.49 KB
dist/elements/LivePreview ${{\color{Goldenrod}{ ▏ }}}$ 0.7%, 8.38 KB
dist/elements/ListHeader ${{\color{Goldenrod}{ ▏ }}}$ 0.7%, 7.83 KB
dist/elements/HTMLDiff ${{\color{Goldenrod}{ ▏ }}}$ 0.7%, 7.81 KB
dist/fields/Array ${{\color{Goldenrod}{ ▏ }}}$ 0.7%, 7.55 KB
dist/views/CollectionFolder ${{\color{Goldenrod}{ ▏ }}}$ 0.6%, 7.37 KB
dist/elements/ReactSelect ${{\color{Goldenrod}{ ▏ }}}$ 0.6%, 7.31 KB
dist/views/List ${{\color{Goldenrod}{ ▏ }}}$ 0.6%, 6.96 KB
(other) ${{\color{Goldenrod}{ ████████████▌ }}}$ 50.1%, 575.66 KB

Meta file: packages/ui/meta_shared.json, Out file: esbuild/exports/shared_optimized/index.js

Path Size
dist/graphics/Logo ${{\color{Goldenrod}{ █████▋ }}}$ 22.6%, 3.12 KB
../../node_modules ${{\color{Goldenrod}{ ████▊ }}}$ 19.2%, 2.65 KB
dist/graphics/Icon ${{\color{Goldenrod}{ ██▊ }}}$ 11.0%, 1.52 KB
dist/utilities/formatDocTitle ${{\color{Goldenrod}{ ██▍ }}}$ 9.6%, 1.32 KB
dist/providers/TableColumns ${{\color{Goldenrod}{ █▌ }}}$ 6.2%, 862 B
dist/utilities/groupNavItems.js ${{\color{Goldenrod}{ █▍ }}}$ 5.9%, 814 B
dist/utilities/api.js ${{\color{Goldenrod}{ █▍ }}}$ 5.5%, 756 B
dist/elements/Translation ${{\color{Goldenrod}{ ▉ }}}$ 3.6%, 493 B
dist/utilities/handleTakeOver.js ${{\color{Goldenrod}{ ▊ }}}$ 3.2%, 440 B
dist/elements/withMergedProps ${{\color{Goldenrod}{ ▋ }}}$ 2.5%, 339 B
dist/elements/WithServerSideProps ${{\color{Goldenrod}{ ▍ }}}$ 1.7%, 232 B
dist/utilities/handleGoBack.js ${{\color{Goldenrod}{ ▎ }}}$ 1.2%, 168 B
dist/fields/mergeFieldStyles.js ${{\color{Goldenrod}{ ▎ }}}$ 1.2%, 159 B
dist/forms/Form ${{\color{Goldenrod}{ ▎ }}}$ 1.1%, 147 B
dist/utilities/abortAndIgnore.js ${{\color{Goldenrod}{ ▎ }}}$ 1.1%, 146 B
dist/utilities/hasSavePermission.js ${{\color{Goldenrod}{ ▎ }}}$ 1.0%, 136 B
dist/utilities/handleBackToDashboard.js ${{\color{Goldenrod}{ ▏ }}}$ 0.9%, 129 B
dist/utilities/findLocaleFromCode.js ${{\color{Goldenrod}{ ▏ }}}$ 0.6%, 84 B
dist/utilities/sanitizeID.js ${{\color{Goldenrod}{ ▏ }}}$ 0.6%, 77 B
dist/utilities/isEditing.js ${{\color{Goldenrod}{ }}}$ 0.4%, 59 B
(other) ${{\color{Goldenrod}{ ███████████████████▎ }}}$ 77.4%, 10.68 KB
Details

Next to the size is how much the size has increased or decreased compared with the base branch of this PR.

  • ‼️: Size increased by 20% or more. Special attention should be given to this.
  • ⚠️: Size increased in acceptable range (lower than 20%).
  • ✅: No change or even downsized.
  • 🗑️: The out file is deleted: not found in base branch.
  • 🆕: The out file is newly found: will be added to base branch.

@denolfe denolfe changed the title chore(create-payload-app): add ts-morph and create AST directory structure chore(cpa): add ts-morph and create AST directory structure Nov 26, 2025
@denolfe denolfe changed the title chore(cpa): add ts-morph and create AST directory structure chore(cpa): use AST for templates Nov 26, 2025
@denolfe denolfe requested a review from paulpopus November 26, 2025 18:18
@denolfe denolfe changed the title chore(cpa): use AST for templates perf(cpa): use AST for templates Nov 26, 2025
@denolfe denolfe marked this pull request as ready for review December 3, 2025 14:43
@denolfe
Copy link
Member Author

denolfe commented Dec 3, 2025

Manual testing notes:

  • cd packages/create-payload-app
  • pnpm build
  • node ~/<absolute/path>/packages/create-payload-app/bin/cli.js, optionally add --debug

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants