Skip to content

feat(config): read and write config.jsonc and commented JSON configs (CLI-2860, CLI-2861) - #7488

Merged
Coly010 merged 11 commits into
developfrom
columferry/cli-2860-discover-configjsonc-and-parse-json-configs-as-jsonc
Nov 5, 2026
Merged

Coly010 merged 11 commits into
developfrom
columferry/cli-2860-discover-configjsonc-and-parse-json-configs-as-jsonc

Conversation

@Coly010

@Coly010 Coly010 commented Nov 4, 2026 •

Copy link
Copy Markdown
Contributor

Summary

The CLI now reads and writes supabase/config.jsonc, and treats config.json the same way, so comments and trailing commas work and survive every write the CLI makes to the config.

Reading:

  • The loader looks for config.json, config.jsonc and config.toml in supabase/. config.jsonc reports format: "json", so everything that already handles a JSON config treats it the same way
  • Both JSON files go through jsonc-parser. A syntax error now names its position, e.g. Comma expected (line 4, column 3), instead of the runtime's JSON.parse message
  • The CLI warns once per command when a JSON config shadows config.toml: Ignoring supabase/config.toml because supabase/config.json takes precedence. Delete the file you don't use.
  • The two comment strippers the CLI had (VS Code settings in init, Deno import maps in functions deploy) use the same parser now, and the experimental-feature reader and the upgrade notice's project walk-up know about config.jsonc

Writing:

  • config pull edits a JSON config in place through jsonc-parser's modify instead of re-serialising the whole file, so comments, trailing commas and untouched values stay as they are. A new key goes after the previous property's whole line, so a trailing comment stays with its own key. ADR 0023 has an addendum for the new JSON strategy
  • saveCliConfig applies the same minimal edits when it saves over an existing config.json/config.jsonc, and removes a dropped key by its whole line. Removing the last key of an object also drops the comma before it, so a config.json stays strict JSON for older CLIs. If the edited text doesn't re-parse to exactly the saved config, it falls back to writing the regenerated file. A TOML save keeps the file's #:schema line
  • saveCliConfig writes back to config.jsonc when that's the project's JSON file, and still removes the other config files after the write
  • compute new (experimental) records the new compute in a JSON config through the same editor, instead of refusing
  • functions new and the declarative db pull write their functions.<slug> and db.migrations.schema_paths entries into a JSON config through the same editor. Before, both appended TOML to a new config.toml that the JSON file shadowed, so the setting never applied
  • init treats an existing config.json, config.jsonc or config.toml as an existing config and names that file when it refuses. init --force and a blank bootstrap remove config.json/config.jsonc when they write the fresh config.toml, so the new template is the file that gets read

Messages:

  • Errors, hints and suggestions that named supabase/config.toml or gave TOML syntax now name the project's own config file and write the key in its syntax. A TOML project still sees [db] major_version = 17, and a JSON project sees `db.major_version` to `17` in supabase/config.jsonc. Where the command has no config loaded, the text says "your config file" or lists all three file names
  • Command help says "the project config file" and uses dotted keys such as db.seed.sql_paths
  • db-config.toml-read.ts is renamed to db-config.read.ts, since it reads every format

jsonc-parser is a new runtime dependency of @supabase/config (MIT, no dependencies of its own). Its parse() turns a __proto__ key into the object's prototype, so parseJsonc builds values through its visit API with own-property assignment instead.

Behaviour changes for existing projects

  • A config.jsonc next to config.toml now takes over from it. Before, config.jsonc was never read. The new warning names config.toml as ignored, so the switch shouldn't go unnoticed
  • config.json and config.jsonc in the same supabase/ directory is now an error: Found both supabase/config.json and supabase/config.jsonc. Keep one and delete the other. Before, config.jsonc was ignored
  • Projects that already had config.json next to config.toml now get the warning above. config.json already won there, so which file is read doesn't change
  • config pull and saveCliConfig on a config.json now only change what they write. Before, both rewrote the whole file
  • The stack's advice to pin a service version now reads pin `local.services.<service>.version` to "<version>" in your config file for every project, including TOML ones
  • Errors for a missing project config name supabase/config.toml, config.json or config.jsonc instead of only config.toml
  • init in a project with only a config.json/config.jsonc now reports the config as already there. Before, it wrote a config.toml that the JSON file then shadowed. init --force in such a project now removes the JSON file

Linked issue

Closes CLI-2859
Closes CLI-2860
Closes CLI-2861

🤖 Generated with Claude Code

…LI-2860)

The loader now discovers supabase/config.jsonc and parses both config.json
and config.jsonc with jsonc-parser, so comments and trailing commas work and
parse errors report a line and column. config.json and config.jsonc together
fail with CliConfigFileConflictError. A JSON file next to config.toml still
wins, and the CLI now warns that config.toml is ignored. Saves go back to
config.jsonc when that is the project's JSON file.

The CLI's two hand-rolled comment strippers (VS Code settings in init, deno
import maps in functions deploy) use the same parser, as do the experimental
feature reader, the upgrade-notice project walk-up and the compute guards.
…-2860)

applyConfigEdits now edits config.json and config.jsonc in place with
jsonc-parser's modify instead of re-serialising the whole document, so
config pull works on JSON configs with comments or trailing commas and
leaves untouched text alone. A new key in an existing multi-line object
goes after the previous property's line, so a trailing comment stays with
its own key and the file's trailing-comma style carries over. The
re-parse verification gate is unchanged. ADR 0023 gains an addendum.
saveCliConfig now applies minimal jsonc-parser edits when it saves over an
existing config.json or config.jsonc, removing dropped keys by their whole
line and keeping strict JSON strict, and falls back to the regenerated
text when the edited file does not re-parse to the saved config. A TOML
save keeps the file's #:schema directive.

init treats an existing config.json or config.jsonc as an existing config
and names it when it refuses; init --force and a blank bootstrap remove
them when writing the fresh config.toml. compute new records a compute in
a JSON config through applyConfigEdits instead of refusing, so the unused
ComputeJsonConfigUnsupportedError is removed.
@Coly010 Coly010 changed the title feat(config): discover config.jsonc and parse JSON configs as JSONC (CLI-2860) feat(config): read and write config.jsonc and commented JSON configs (CLI-2860, CLI-2861) Nov 4, 2026
…CLI-2861)

On a project whose config is config.json or config.jsonc, `functions new`
and the declarative `db pull` path appended TOML to a new config.toml that
the JSON file shadows, so the setting never took effect. Both now span-edit
the JSON file through applyConfigEdits, keeping its comments, and write it
atomically. TOML projects are unchanged.
…2859)

Messages, hints and help text that named supabase/config.toml or gave
TOML syntax now name the file the project uses and write the key in that
file's syntax: `[db] major_version = 17` for config.toml, `db.major_version`
to `17` for config.json/config.jsonc. A TOML project sees the same text as
before. Where no config is in reach, the text says "your config file" or
lists all three file names.

- config-advice.ts renders a setting or key for either format, and
  config-file-ref.ts finds the project's file on paths that have not
  loaded the config
- db-config.toml-read.ts is now db-config.read.ts, since it reads every
  format
- Command help says "the project config file" and uses dotted keys
- The stack's version-pin advice now names
  `local.services.<service>.version` in "your config file" for every
  project
- The published config schema is regenerated for the reworded
  migration-checks description
@Coly010 Coly010 self-assigned this Nov 4, 2026
@Coly010
Coly010 marked this pull request as ready for review November 4, 2026 14:20
@Coly010
Coly010 requested review from a team and supabase-oss as code owners November 4, 2026 14:20
@Coly010
Coly010 requested a review from mdonnalley November 4, 2026 14:20
…discover-configjsonc-and-parse-json-configs-as-jsonc

# Conflicts:
#	apps/cli/src/command-internal/config-validate.parity.unit.test.ts
#	packages/config/AGENTS.md
@github-actions

github-actions Bot commented Nov 4, 2026

Copy link
Copy Markdown
Contributor

CLI benchmark (Lightweight Commands): no changes found

Commit cdafb04 against develop at ab7154a · 10 runs each · report only, never blocks merging

Command Workload Before After Change
supabase --version version 194 ms 201 ms no real change
supabase --help help 220 ms 219 ms no real change
supabase init init 216 ms 214 ms no real change
supabase migration new bench migration-new 201 ms 209 ms no real change
supabase gen types --lang typescript --project-id replayfixtureprojref gen-types 241 ms 238 ms no real change
supabase projects list projects-list 231 ms 222 ms no real change
supabase functions list --project-ref replayfixtureprojref functions-list 235 ms 239 ms no real change

API calls: unchanged for all 7 commands.

Binary size: 134.7 MB, up 96.0 KB from 134.6 MB.

Details: timing ranges, API calls per command, slowest phases

Timing ranges

Command Workload Before range After range Change Verdict
supabase --version version 180-235 ms 183-248 ms +7 ms (+4%) within noise
supabase --help help 191-302 ms 184-284 ms -1 ms (0%) within noise
supabase init init 193-308 ms 203-233 ms -2 ms (-1%) within noise
supabase migration new bench migration-new 188-250 ms 193-228 ms +8 ms (+4%) within noise
supabase gen types --lang typescript --project-id replayfixtureprojref gen-types 219-311 ms 227-269 ms -3 ms (-1%) within noise
supabase projects list projects-list 203-258 ms 216-239 ms -9 ms (-4%) within noise
supabase functions list --project-ref replayfixtureprojref functions-list 218-260 ms 220-256 ms +4 ms (+2%) within noise

API calls per command

Command Workload Before After Endpoint changes
supabase --version version 0 0
supabase --help help 0 0
supabase init init 0 0
supabase migration new bench migration-new 0 0
supabase gen types --lang typescript --project-id replayfixtureprojref gen-types 2 2
supabase projects list projects-list 1 1
supabase functions list --project-ref replayfixtureprojref functions-list 2 2

Slowest phases

No command moved beyond noise, so there are no phase deltas.

Workflow run

Comment /bench to run the lightweight benchmark (this comment) again on the latest commit, or /bench full to also run the integration-heavy benchmark, which posts in its own comment.

@Coly010

Coly010 commented Nov 4, 2026

Copy link
Copy Markdown
Contributor Author

/ai-review

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 AI Review

Verified all three findings from both available reviews. Confirmed the breaking behavior changes and Windows display-path inconsistency. Refuted the config-write fallback finding because both command paths validate configuration before reaching their writers.

Findings

Severity Location Category Sources Claim
🟠 MAJOR packages/config/src/io.ts:655 breaking-change claude Existing projects change behavior: config.json alongside config.jsonc now causes a conflict error; config.jsonc alongside config.toml now takes precedence; init without --force now refuses projects containing only JSON config; and init --force or blank bootstrap now deletes JSON config files. Please change the title to feat(config)!:, add a BREAKING CHANGE: line to the PR description, and retarget the PR to next, since it is currently on develop.
⚪ NIT apps/cli/src/config/cli-config-values.layer.ts:261 cross-platform claude The new config-precedence warning and functions new JSON messages display native backslashes on Windows, unlike the normalized forward-slash paths used by the config advice helper.
Refuted findings (kept for transparency, not posted as review comments)
  • apps/cli/src/commands/functions/new/new.handler.ts:64 (error-handling): JSON config load failures are treated as absence, so functions new can silently write its configuration to an ignored config.toml.
    Refuted: Malformed, conflicting, or unreadable JSON configuration fails the mandatory earlier load. CliConfigValues.load parses through cli-config-values.layer.ts:242–256 without suppressing those errors. functions new reaches that load before creating files or calling appendFunctionConfig. The database path also loads configuration before invoking updateDeclarativeSchemaPathsConfig at db-pull-run.ts:297–302. Thus the reported existing-invalid-config scenario does not reach either TOML fallback.

Stats

Claude findings: 2 · Codex findings: 1 · Confirmed: 2 · Refuted: 1 · Uncertain: 0


Models: claude-opus-5-5 + gpt-6.1-sol · Trigger: manual · Workflow run

This review runs once per PR. A maintainer can request another with a /ai-review comment.

Comment thread packages/config/src/io.ts Resolved

@jgoux jgoux left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good. The JSONC editing holds up across the edge cases I tried (last-key removal, trailing comments, CRLF, comment lines before }), and precedence and conflict handling match the description. Two small P3 notes inline.

Comment thread apps/cli/src/shared/functions/deploy.ts Resolved
…oml (CLI-2861)

The functions deploy import_map hint also names a JSON config with forward slashes on Windows.
…discover-configjsonc-and-parse-json-configs-as-jsonc

# Conflicts:
#	apps/cli/src/commands/functions/serve/serve.stack.handler.ts
@Coly010
Coly010 added this pull request to the merge queue Nov 5, 2026
Merged via the queue into develop with commit 3217fb4 Nov 5, 2026
49 checks passed
@Coly010
Coly010 deleted the columferry/cli-2860-discover-configjsonc-and-parse-json-configs-as-jsonc branch November 5, 2026 10:17
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.

2 participants