Skip to content

feat(cli): lint rule for a nullish env default - #723

Open
7nohe wants to merge 1 commit into
claude/serene-bouman-a9a6f2from
claude/no-nullish-env-default
Open

feat(cli): lint rule for a nullish env default#723
7nohe wants to merge 1 commit into
claude/serene-bouman-a9a6f2from
claude/no-nullish-env-default

Conversation

@7nohe

@7nohe 7nohe commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

Stacked on #714. Review that one first; this PR's diff against it is the rule
plus the sites #714 did not cover.

Why

#714 fixed process.env.FOO ?? 'default' in six scaffolded configs, each found
by hand, one report at a time. ?? falls back only on undefined, so a key
present but blank keeps '' and names something that does not exist — a
session store called '' that failed the boot, a cache store called '', an
SMTP port of 0 from Number('').

Nothing stopped the seventh. This adds the rule that does, and applies it.

The rule

guren/no-nullish-env-default reports process.env.FOO ?? <non-empty string or number literal>.

What it deliberately does not report is what keeps it at zero false
positives across the 35 sites it found here:

  • ?? '' — both operators behave identically, so the operator cannot be the bug
  • a non-literal fallback (?? somePort, ?? process.env.OTHER) — not judgeable
    from syntax
  • || — already the fix

A nullish chain reports once, on the operand that holds the literal, and
converts whole: A ?? B ?? 'x' becomes A || B || 'x', because mixing ??
and || without parentheses is a syntax error.

Applied

35 sites, across packages/server, packages/create-app/templates,
packages/cli/templates, examples/, scripts/ and web/. The ones that
matter beyond cosmetics:

Site FOO= blank, before After
lambda/index.ts AWS_LAMBDA_FUNCTION_MEMORY_SIZE parseInt('')NaN 128
lambda/index.ts AWS_REGION ?? AWS_DEFAULT_REGION '' us-east-1
dev-assets.ts defines process.env.NODE_ENV as "" "development"
InertiaEngine.ts GUREN_INERTIA_ENTRY '' the default entry
HOST (5 templates + commands.ts) binds '' 0.0.0.0
DATABASE_URL / SQLITE_DATABASE_PATH (12 sites) '' path the default

The NODE_ENV sites were checked against the --define rule in
common-pitfalls.md: a define replaces the process.env.NODE_ENV member
expression, which the surrounding operator does not affect, and none of these
are the pinned MCP / docs-viewer gates.

The one exemption is a mail from display name, where an empty value is a real
choice; it carries oxlint-disable-next-line with that reason.

It ships to apps

Enabled in this repo and in the .oxlintrc.json that the app templates and
guren add lint write, because the defect it was written for lives in scaffold
output — an app that never reads this repo still gets told.
oxlint-export.test.ts now proves the rule reaches through the published
@guren/cli/oxlint subpath, against dist.

Verified

  • rule tests (4) drive the real oxlint binary, like the sibling rules
  • reverting one fixed site → lint fails; deleting the one disable comment →
    lint fails
  • lint, typecheck, build, audit:starter-template, audit:core-first,
    audit:docs, audit:template-deps, audit:core-semver — exit 0
  • @guren/cli 2329 pass, @guren/server 2965 pass, create-guren-app 112
    pass, test:examples 214 pass — 0 fail
  • smoke:starter exit 0

`process.env.FOO ?? 'default'` falls back only on `undefined`, so a key present
but blank keeps '' and names something that does not exist. Six generated
configs shipped that — a session store called '', a cache store called '', an
SMTP port of 0 from `Number('')` — and the fix for each was found by hand, one
report at a time. `guren/no-nullish-env-default` finds the rest.

Reports a non-empty string or numeric fallback only. `?? ''` behaves the same
under either operator, and a non-literal fallback cannot be judged from syntax,
so neither is reported; that is what keeps the rule at zero false positives
across the 35 sites it found here. A nullish chain reports once, on the operand
holding the literal, and converts whole — `A ?? B ?? 'x'` becomes `A || B ||
'x'`, since mixing the operators is a syntax error.

Enabled in this repo and in the .oxlintrc.json the app templates and
`guren add lint` ship: the defect it was written for lives in scaffold output,
so an app that never reads this repo still gets told.

The one exemption is a mail `from` display name, where an empty value is a
choice; it carries a disable comment saying so.
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.

1 participant