Skip to content

fix(data): let the New field dialog accept camelCase ids - #436

Merged
DavidBabinec merged 1 commit into
CoreBunch:mainfrom
lovepixel-git:fix/field-id-camelcase
Aug 30, 2026
Merged

fix(data): let the New field dialog accept camelCase ids#436
DavidBabinec merged 1 commit into
CoreBunch:mainfrom
lovepixel-git:fix/field-id-camelcase

Conversation

@lovepixel-git

Copy link
Copy Markdown
Contributor

Fixes #434.

The dialog is the only place that holds this rule

FIELD_ID_PATTERN was /^[a-z][a-z0-9_]*$/, and fieldIdError gates canCreate, so firmaUrl blocked creation outright. I checked what else agrees with that rule, and nothing does:

Layer Accepts camelCase?
FieldCommonProps.id in src/core/data/schemas.ts yes — bare Type.String(), no pattern
PATCH /admin/api/cms/data/tables/{id} yes — stores it, and it resolves in loops and {currentEntry.*}
Built-in post-type fields already arefeaturedMedia, seoTitle, seoDescription
RESOURCE_FIELD_ID_PATTERN in src/core/plugins/manifest.ts yes — /^[a-zA-Z_][a-zA-Z0-9_-]*$/

That last row is worth calling out: the codebase already has a field-id pattern, in the plugin manifest, and it permits camelCase. The Data dialog was the outlier against its own storage layer, its own API, its own built-ins, and its own sibling validator.

The practical cost is the part of the report I found most convincing. An author ends up with aufstartseite next to seoDescription in one table, every binding has to be written against whichever convention that particular field happened to land in, and a mistyped token renders as an empty node with no warning.

Change

The body of the pattern accepts any letter case. The first character still has to be a lowercase letter, which keeps ids safe as token and binding keys and matches every built-in, so this loosens exactly one thing.

The error message also described a rule the pattern did not implement — it said "use letters, numbers, underscores only" while rejecting uppercase letters. It now matches the behaviour.

Deliberately not in scope

fieldIdFromLabel still derives snake_case from a label. Auto-derivation produced a valid id before and still does, and changing the suggested convention is a different decision from accepting what an author types. Say the word if you want the suggestion switched to camelCase too — that is a one-line follow-up, but it changes what every future field is named by default, which felt like yours to call rather than something to slip into a bug fix.

I also did not take the issue's second option (enforce snake_case at the schema and API layer, rename or grandfather the built-ins). That would be a breaking change to stored data for the sake of the stricter convention, and the built-ins picking camelCase reads as the intended house style.

Tests

Seven cases added to src/__tests__/data/newFieldDialogModel.test.ts, covering camelCase acceptance, the actual built-in constants imported from @core/data/schemas (so a rename upstream keeps this honest), snake_case still passing, the lowercase-first rule still holding, unsafe characters still rejected, and duplicate detection still taking precedence.

Verified they fail on main: 3 of the new tests fail before the change, all 11 pass after.

  • bun run lint clean
  • bunx tsc -b exit 0
  • bun run build clean
  • bun test 6676 pass, 0 fail

The dialog enforced `/^[a-z][a-z0-9_]*$/`, so `firmaUrl` was rejected and
Create stayed disabled. Nothing below the dialog agrees with that rule:

  - the storage schema puts no pattern on the id at all
    (`FieldCommonProps.id` is a bare `Type.String()`)
  - the API accepts camelCase and stores it, and the field then works
    normally in loops and `{currentEntry.*}` tokens
  - three built-in post-type fields ARE camelCase: `featuredMedia`,
    `seoTitle`, `seoDescription`
  - the codebase's other field-id pattern, `RESOURCE_FIELD_ID_PATTERN` in
    `src/core/plugins/manifest.ts`, already allows camelCase

So the dialog was the only thing forcing a second convention into tables
that already carried the first, and CoreBunch#434 reports the practical cost:
`aufstartseite` and `seoDescription` side by side, with every binding
written against whichever convention that field happened to land in, and
a mistyped token rendering as a silent empty node.

The body of the pattern now accepts any letter case. The first character
still has to be a lowercase letter, which keeps ids safe as token and
binding keys and matches every built-in.

The old message ("use letters, numbers, underscores only") also described
a rule the pattern did not implement, since uppercase letters were
rejected. It now matches the behaviour.

Deliberately unchanged: `fieldIdFromLabel` still derives snake_case from
a label. Auto-derivation produces a valid id either way, and changing the
suggested convention is a different decision from accepting what an
author types.

Fixes CoreBunch#434
@DavidBabinec
DavidBabinec merged commit 7a07337 into CoreBunch:main Aug 30, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: New field dialog rejects camelCase field IDs, but Instatic's own built-in fields are camelCase

2 participants