PR Checks / Frontend Typecheck + Tests (pull_request) Successful in 1m12s
PR Checks / Backend Smoke (pull_request) Successful in 8s
PR Checks / Build Backend (no push) (pull_request) Successful in 11s
PR Checks / Build Frontend (no push) (pull_request) Successful in 1m9s
PR Checks / Build Pipeline (no push) (pull_request) Successful in 10s
PR Checks / AI Code Review (Claude) (pull_request) Successful in 1m4s
Creating a post in the admin panel showed no Content editor, and saving failed validation on the field the writer was never shown. The admin panel does not import field components. The server hands the client a path per field, and resolves it through the generated map at app/(payload)/admin/importMap.js. A richText field's path is @payloadcms/richtext-lexical/rsc#RscEntryLexicalField. The committed map held one entry, @payloadcms/next/rsc#CollectionCards, generated before the blog collections existed and never re-run. A path missing from the map is not an error the panel reports: the field simply does not render, while required is still enforced server-side on save. next build does not regenerate the map, so the stale copy shipped in the image and the editor was equally broken on staging and production. Regenerated with payload generate:importmap, which adds the lexical RSC field, cell and diff components, BlocksFeatureClient for the Callout block, and the default toolbar features. Two things stop it drifting again. There was no script to run, so package.json gets generate:importmap. And a test asserts the map carries an entry for each thing the config asks for, in the source-reading style of the other payload suites; against the old map all five fail. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DXnXQKnPpZBBP61fBQiFkq
68 lines
2.6 KiB
TypeScript
68 lines
2.6 KiB
TypeScript
/**
|
|
* The admin panel does not import field components directly. Payload sends the
|
|
* client a *path* for each one — a richText field's is
|
|
* `@payloadcms/richtext-lexical/rsc#RscEntryLexicalField` — and resolves it
|
|
* through this generated map. An entry that is missing from the map is not an
|
|
* error the panel reports: the field simply does not render.
|
|
*
|
|
* That failure is quietly awful, because `required: true` is enforced on the
|
|
* server regardless. A writer gets a new-post form with no Content editor and
|
|
* a save that refuses on a field they were never shown.
|
|
*
|
|
* The map is generated by `npx payload generate:importmap`, so it drifts every
|
|
* time a field or a lexical feature is added and nobody re-runs it. These
|
|
* assert the entries the current config needs.
|
|
*/
|
|
import fs from 'fs';
|
|
import path from 'path';
|
|
|
|
const MAP = fs.readFileSync(
|
|
path.join(__dirname, '..', '..', 'app', '(payload)', 'admin', 'importMap.js'),
|
|
'utf8',
|
|
);
|
|
const POSTS = fs.readFileSync(
|
|
path.join(__dirname, '..', '..', 'collections', 'Posts.ts'),
|
|
'utf8',
|
|
);
|
|
|
|
describe('admin import map', () => {
|
|
it('resolves the richText field, so Content renders in the editor', () => {
|
|
// Guarded because Posts.content is required: without this entry the field
|
|
// is invisible and the post is unsaveable.
|
|
expect(POSTS).toMatch(/type:\s*'richText'/);
|
|
expect(MAP).toContain('@payloadcms/richtext-lexical/rsc#RscEntryLexicalField');
|
|
});
|
|
|
|
it('resolves the richText cell, so the list view can render the column', () => {
|
|
expect(MAP).toContain('@payloadcms/richtext-lexical/rsc#RscEntryLexicalCell');
|
|
});
|
|
|
|
it('resolves the diff component, which the drafts UI needs', () => {
|
|
// versions.drafts is on, so the panel offers version comparison.
|
|
expect(POSTS).toMatch(/drafts:\s*true/);
|
|
expect(MAP).toContain('@payloadcms/richtext-lexical/rsc#LexicalDiffComponent');
|
|
});
|
|
|
|
it('resolves BlocksFeature, so the Callout block is insertable', () => {
|
|
expect(POSTS).toContain('BlocksFeature');
|
|
expect(MAP).toContain('@payloadcms/richtext-lexical/client#BlocksFeatureClient');
|
|
});
|
|
|
|
it('resolves the default toolbar features the editor is built with', () => {
|
|
// defaultFeatures is spread into the editor config; each one contributes a
|
|
// client component the toolbar cannot render without.
|
|
for (const feature of [
|
|
'BoldFeatureClient',
|
|
'ItalicFeatureClient',
|
|
'HeadingFeatureClient',
|
|
'LinkFeatureClient',
|
|
'UploadFeatureClient',
|
|
'UnorderedListFeatureClient',
|
|
'OrderedListFeatureClient',
|
|
'InlineToolbarFeatureClient',
|
|
]) {
|
|
expect(MAP).toContain(`@payloadcms/richtext-lexical/client#${feature}`);
|
|
}
|
|
});
|
|
});
|