diff --git a/package-lock.json b/package-lock.json index f0fb618770..56822c60a2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -23314,10 +23314,6 @@ "license": "MIT", "dependencies": { "escape-string-regexp": "^5.0.0", - "fast-safe-stringify": "^2.0.7", - "is-plain-obj": "^4.0.0", - "map-obj": "^5.0.0", - "path-exists": "^5.0.0", "smol-toml": "^1.5.2" }, "devDependencies": { diff --git a/packages/headers-parser/package.json b/packages/headers-parser/package.json index dab51c540f..6f149a1a41 100644 --- a/packages/headers-parser/package.json +++ b/packages/headers-parser/package.json @@ -27,11 +27,7 @@ "license": "MIT", "dependencies": { "smol-toml": "^1.5.2", - "escape-string-regexp": "^5.0.0", - "fast-safe-stringify": "^2.0.7", - "is-plain-obj": "^4.0.0", - "map-obj": "^5.0.0", - "path-exists": "^5.0.0" + "escape-string-regexp": "^5.0.0" }, "devDependencies": { "@types/node": "^22.12.0", diff --git a/packages/headers-parser/src/line_parser.ts b/packages/headers-parser/src/line_parser.ts index fc61ba4958..874927b4e3 100644 --- a/packages/headers-parser/src/line_parser.ts +++ b/packages/headers-parser/src/line_parser.ts @@ -1,7 +1,5 @@ import fs from 'fs/promises' -import { pathExists } from 'path-exists' - import { splitResults } from './results.js' import type { MinimalHeader } from './types.js' @@ -23,7 +21,9 @@ export const parseFileHeaders = async function (headersFile: string): Promise { - if (!(await pathExists(headersFile))) { + try { + await fs.access(headersFile) + } catch { return [] } diff --git a/packages/headers-parser/src/merge.ts b/packages/headers-parser/src/merge.ts index f408981f39..66fb49682b 100644 --- a/packages/headers-parser/src/merge.ts +++ b/packages/headers-parser/src/merge.ts @@ -1,5 +1,3 @@ -import stringify from 'fast-safe-stringify' - import { splitResults } from './results.js' import type { Header, MinimalHeader } from './types.js' @@ -47,12 +45,12 @@ const removeDuplicates = function (headers: MinimalHeader[] | Header[]) { return result.reverse() } -// We generate a unique header key based on JSON stringify. However, because some -// properties can be regexes, we need to replace those by their toString representation -// given the default will be and empty object +// We generate a unique header key based on JSON stringify. The `values` keys +// are sorted so that key order does not affect the result. `forRegExp` is +// derived from `for`, so it does not need to be part of the key. const generateHeaderKey = function (header: MinimalHeader | Header): string { - return stringify.default.stableStringify(header, (_, value) => { - if (value instanceof RegExp) return value.toString() - return value - }) + return JSON.stringify([ + header.for, + Object.entries(header.values).sort(([keyA], [keyB]) => (keyA < keyB ? -1 : keyA > keyB ? 1 : 0)), + ]) } diff --git a/packages/headers-parser/src/netlify_config_parser.ts b/packages/headers-parser/src/netlify_config_parser.ts index 7500df3e43..1f466a1247 100644 --- a/packages/headers-parser/src/netlify_config_parser.ts +++ b/packages/headers-parser/src/netlify_config_parser.ts @@ -1,7 +1,6 @@ import { promises as fs } from 'fs' import { parse as loadToml } from 'smol-toml' -import { pathExists } from 'path-exists' import { splitResults } from './results.js' import type { MinimalHeader } from './types.js' @@ -10,7 +9,9 @@ import type { MinimalHeader } from './types.js' // This field is already an array of objects, so it only validates and // normalizes it. export const parseConfigHeaders = async function (netlifyConfigPath: string) { - if (!(await pathExists(netlifyConfigPath))) { + try { + await fs.access(netlifyConfigPath) + } catch { return splitResults([]) } diff --git a/packages/headers-parser/src/normalize.ts b/packages/headers-parser/src/normalize.ts index 45aa1081dc..030d75e813 100644 --- a/packages/headers-parser/src/normalize.ts +++ b/packages/headers-parser/src/normalize.ts @@ -1,11 +1,10 @@ -import isPlainObj from 'is-plain-obj' -import mapObj from 'map-obj' -import type { Mapper } from 'map-obj' - import { getForRegExp } from './for_regexp.js' import { splitResults } from './results.js' import type { Header, MinimalHeader } from './types.js' +const isObject = (value: unknown): value is Record => + typeof value === 'object' && value !== null && !Array.isArray(value) + export interface MinimalNormalizedHeaders { headers: MinimalHeader[] errors: Error[] @@ -50,7 +49,7 @@ function parseHeader( index: number, minimal: boolean, ): undefined | Error | MinimalHeader | Header { - if (!isPlainObj(header)) { + if (!isObject(header)) { return new TypeError(`Header must be an object not: ${header}`) } @@ -112,15 +111,15 @@ const normalizePath = function (rawPath?: string): string { // Normalize and validate the `values` field const normalizeValues = function (rawValues: Record): Record { - if (!isPlainObj(rawValues)) { + if (!isObject(rawValues)) { throw new TypeError(`"values" must be an object not: ${rawValues}`) } - return mapObj(rawValues, normalizeValue) + return Object.fromEntries(Object.entries(rawValues).map(([rawKey, rawValue]) => normalizeValue(rawKey, rawValue))) } // Normalize and validate each header `values` -const normalizeValue: Mapper, string, string> = function (rawKey, rawValue) { +const normalizeValue = function (rawKey: string, rawValue: string | string[]): [string, string] { const key: string = rawKey.trim() if (key === '' || key === 'undefined') { throw new Error('Empty header name')