Skip to content

feat(headers-parser): replace dependencies with native APIs - #7147

Open
43081j wants to merge 1 commit into
netlify:mainfrom
43081j:jg/the-purge-headers-parser
Open

feat(headers-parser): replace dependencies with native APIs#7147
43081j wants to merge 1 commit into
netlify:mainfrom
43081j:jg/the-purge-headers-parser

Conversation

@43081j

@43081j 43081j commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

This replaces the following in headers-parser:

  • is-plain-object with a basic typeof check (as we are in control of the inputs)
  • map-obj with Object.entries(obj).map(fn)
  • path-exists with fs.access
  • fast-safe-stringify with JSON.stringify

On the last point, fast-safe-stringify really exists to handle things like circular references when stringifying. However, we don't use that functionality and only use the stableStringify export.

We use the stableStringify export to basically sort the keys so we can have a stable string representation, and we handle RegExp by toString-ing it.

All of this can be done without fast-safe-stringify as we're in full control of the type (MinimalHeader). So, this changes it to use JSON.stringify of the keys we know, and sorts the header values directly.


For us to review and ship your PR efficiently, please perform the following steps:

  • Open a bug/issue before writing your code 🧑‍💻. This ensures
    we can discuss the changes and get feedback from everyone that should be involved. If you`re fixing a typo or
    something that`s on fire 🔥 (e.g. incident related), you can skip this step.
  • Read the contribution guidelines 📖. This ensures
    your code follows our style guide and passes our tests.
  • Update or add tests (if any source code was changed or added) 🧪
  • Update or add documentation (if features were changed or added) 📝
  • Make sure the status checks below are successful ✅

A picture of a cute animal (not mandatory, but encouraged)

This replaces the following in `headers-parser`:

- `is-plain-object` with a basic `typeof` check (as we are in control of the inputs)
- `map-obj` with `Object.entries(obj).map(fn)`
- `path-exists` with `fs.access`
- `fast-safe-stringify` with `JSON.stringify`

On the last point, `fast-safe-stringify` really exists to handle things like
circular references when stringifying. However, we don't use that functionality
and only use the `stableStringify` export.

We use the `stableStringify` export to basically sort the keys so we can have
a stable string representation, and we handle `RegExp` by `toString`-ing it.

All of this can be done without `fast-safe-stringify` as we're in full control
of the type (`MinimalHeader`). So, this changes it to use `JSON.stringify` of
the keys we know, and sorts the header `values` directly.
@43081j
43081j requested a review from a team as a code owner July 30, 2026 17:09
@coderabbitai

coderabbitai Bot commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Summary by CodeRabbit

  • Bug Fixes

    • Improved handling of missing or inaccessible header configuration files.
    • Enhanced duplicate header detection for more consistent results.
    • Strengthened validation and normalization of header values.
  • Maintenance

    • Reduced reliance on internal helper packages without changing the public API.

Walkthrough

The headers parser removes several utility dependencies, adds local object validation and value normalization, and changes duplicate detection to use deterministic JSON keys. Header and Netlify configuration parsing now uses fs.access with failure handling to return empty results for inaccessible files.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Suggested reviewers: pieh

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description covers the motivation, summary, and checklist, but it omits the required Fixes # reference. Add a Fixes # line in the Summary section and keep the checklist aligned with the template.
✅ Passed checks (4 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title is concise and accurately describes the main change: replacing headers-parser dependencies with native APIs.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

Copy link
Copy Markdown
Contributor

e18e dependency analysis

No dependency warnings found.

@pkg-pr-new

pkg-pr-new Bot commented Jul 30, 2026

Copy link
Copy Markdown

Open in StackBlitz

@netlify/build

npm i https://pkg.pr.new/@netlify/build@7147

@netlify/build-info

npm i https://pkg.pr.new/@netlify/build-info@7147

@netlify/cache-utils

npm i https://pkg.pr.new/@netlify/cache-utils@7147

@netlify/config

npm i https://pkg.pr.new/@netlify/config@7147

@netlify/edge-bundler

npm i https://pkg.pr.new/@netlify/edge-bundler@7147

@netlify/functions-utils

npm i https://pkg.pr.new/@netlify/functions-utils@7147

@netlify/git-utils

npm i https://pkg.pr.new/@netlify/git-utils@7147

@netlify/headers-parser

npm i https://pkg.pr.new/@netlify/headers-parser@7147

@netlify/api

npm i https://pkg.pr.new/@netlify/api@7147

@netlify/nock-udp

npm i https://pkg.pr.new/@netlify/nock-udp@7147

@netlify/opentelemetry-sdk-setup

npm i https://pkg.pr.new/@netlify/opentelemetry-sdk-setup@7147

@netlify/opentelemetry-utils

npm i https://pkg.pr.new/@netlify/opentelemetry-utils@7147

@netlify/redirect-parser

npm i https://pkg.pr.new/@netlify/redirect-parser@7147

@netlify/run-utils

npm i https://pkg.pr.new/@netlify/run-utils@7147

@netlify/zip-it-and-ship-it

npm i https://pkg.pr.new/@netlify/zip-it-and-ship-it@7147

commit: 5a27939

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/headers-parser/src/line_parser.ts`:
- Around line 24-26: Replace the fs.access preflight in the header parsing flow
at packages/headers-parser/src/line_parser.ts:24-26 with direct-read error
handling, allowing the existing read/TOML parse path to determine empty-result
behavior. Apply the same change to the corresponding preflight in
packages/headers-parser/src/netlify_config_parser.ts:12-16; remove both access
checks without changing successful parsing behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: b53f7312-f17e-4983-9e72-dc09fe508439

📥 Commits

Reviewing files that changed from the base of the PR and between af94ccf and 5a27939.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (5)
  • packages/headers-parser/package.json
  • packages/headers-parser/src/line_parser.ts
  • packages/headers-parser/src/merge.ts
  • packages/headers-parser/src/netlify_config_parser.ts
  • packages/headers-parser/src/normalize.ts
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • netlify/blueprints (manual)

Comment on lines +24 to +26
try {
await fs.access(headersFile)
} catch {

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.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== locate files =="
fd -p 'packages/headers-parser/src/(line_parser|netlify_config_parser)\.ts$' . || true

echo "== relevant line_parser.ts =="
if [ -f packages/headers-parser/src/line_parser.ts ]; then
  nl -ba packages/headers-parser/src/line_parser.ts | sed -n '1,80p'
fi

echo "== relevant netlify_config_parser.ts =="
if [ -f packages/headers-parser/src/netlify_config_parser.ts ]; then
  nl -ba packages/headers-parser/src/netlify_config_parser.ts | sed -n '1,120p'
fi

echo "== search fs.access and parseConfig usages =="
rg -n "fs\.access|parseConfig|splitResults|parseHeadersFile|read text|readFileSync" packages/headers-parser/src packages/headers-parser -S || true

Repository: netlify/build

Length of output: 340


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== package files mentioning headers-parser =="
rg -n "headers-parser|line_parser|netlify_config_parser|parseHeadersFile" package.json pnpm-workspace.yaml packages -S || true

echo "== TypeScript fs access definitions availability =="
pnpm list fs-extra 2>/dev/null | head -20 || true
rg -n "from '(fs-extra|fs)'|require\('fs'|require\('fs-extra'\)" packages/headers-parser/src/tsconfig.json -S || true

Repository: netlify/build

Length of output: 15650


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== line_parser.ts relevant sections =="
awk 'NR>=1 && NR<=120 { printf "%4d  %s\n", NR, $0 }' packages/headers-parser/src/line_parser.ts

echo "== netlify_config_parser.ts relevant sections =="
awk 'NR>=1 && NR<=160 { printf "%4d  %s\n", NR, $0 }' packages/headers-parser/src/netlify_config_parser.ts

echo "== all fs.access/access/read usages in headers-parser source =="
python3 - <<'PY'
from pathlib import Path
for path in sorted(Path("packages/headers-parser/src").rglob("*.ts")):
    text = path.read_text()
    lines = text.splitlines()
    for i, line in enumerate(lines, 1):
        if "fs.access" in line or "access(" in line or "readTextFile" in line or "promises.readFile" in line:
            start=max(1,i-3); end=min(len(lines),i+4)
            print(f"\n--- {path}:{start}-{end} ---")
            for n in range(start,end+1):
                print(f"{n:4d}  {lines[n-1]}")
PY

Repository: netlify/build

Length of output: 6586


🌐 Web query:

Node.js fs.access default flags F_OK read permissions R_OK file permission error documentation

💡 Result:

In Node.js, the fs.access function is used to test a user's permissions for a file or directory [1][2]. The mode argument is an optional integer that specifies the accessibility checks to be performed, with a default value of fs.constants.F_OK [3][4]. Key details regarding fs.access constants and usage: Default Behavior: The default value for the mode argument is fs.constants.F_OK, which indicates that the file is visible to the calling process (i.e., it exists) [3][4]. Permission Constants: The mode argument can also be a bitwise OR of any of the following constants: fs.constants.F_OK: Visible to the calling process [3][4]. fs.constants.R_OK: Readable by the calling process [1][5]. fs.constants.W_OK: Writable by the calling process [1][5]. fs.constants.X_OK: Executable by the calling process [1][5]. Error Handling: If the accessibility check fails, the function returns a rejected promise (when using fsPromises.access) or invokes the callback with an error object (when using fs.access) [1][2]. Important Recommendation: Node.js documentation explicitly advises against using fs.access to check for accessibility before performing operations like fs.open, fs.readFile, or fs.writeFile [1][2]. Doing so creates a race condition, as the file's state could change between the access check and the actual operation [1][6]. Instead, it is best practice to attempt the operation directly and handle any errors that occur [1][4]. Additionally, users should be aware that on Windows, fs.access does not check Access Control Lists (ACLs), so it may return success even if ACLs would otherwise restrict the user from reading or writing to the file [7][8].

Citations:


Replace the fs.access preflight with direct-read error handling.

These checks default to F_OK, so they don’t reliably detect unreadable files and still race with the later fs.readFile/TOML parse calls. Remove the preflight and let the actual read/parse path handle the intended empty-result behavior consistently.

📍 Affects 2 files
  • packages/headers-parser/src/line_parser.ts#L24-L26 (this comment)
  • packages/headers-parser/src/netlify_config_parser.ts#L12-L16
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/headers-parser/src/line_parser.ts` around lines 24 - 26, Replace the
fs.access preflight in the header parsing flow at
packages/headers-parser/src/line_parser.ts:24-26 with direct-read error
handling, allowing the existing read/TOML parse path to determine empty-result
behavior. Apply the same change to the corresponding preflight in
packages/headers-parser/src/netlify_config_parser.ts:12-16; remove both access
checks without changing successful parsing behavior.

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