Description
downloadCSV() in web-components/src/utils/index.ts uses four browser-only APIs with no SSR guard:
const link = document.createElement("a") // ❌ ReferenceError in Node.js
const url = URL.createObjectURL(blob) // ❌ Not available in Node.js
document.body.appendChild(link) // ❌ ReferenceError in Node.js
link.click() // ❌ ReferenceError in Node.js
document.body.removeChild(link) // ❌ ReferenceError in Node.js
URL.revokeObjectURL(url) // ❌ Not available in Node.js
When these web components are consumed in an SSR environment (SvelteKit, Nuxt, server middleware), importing any component that triggers downloadCSV at module evaluation time — or calling it during SSR rendering — throws:
ReferenceError: document is not defined
Note that a similar issue was already fixed in the same file for createDebounce (which used window.setTimeout instead of plain setTimeout). This PR applies the same SSR-awareness to downloadCSV.
Fix
Add a typeof document === "undefined" guard at the top of downloadCSV():
export const downloadCSV = (rows: Array<Array<any>>, filename: string, headers: Array<string>) => {
if (rows.length === 0) {
return
}
+ if (typeof document === "undefined" || typeof URL === "undefined") {
+ console.warn("downloadCSV: browser APIs not available (SSR environment), skipping download.")
+ return
+ }
+
const csvContent = [headers.join(","), ...rows.map((row) => row.join(","))].join("\n")
...
Also adds a unit test to the existing describe("downloadCSV") block in index.test.ts to verify the guard fires correctly in a simulated SSR environment.
Checklist
Description
downloadCSV()inweb-components/src/utils/index.tsuses four browser-only APIs with no SSR guard:When these web components are consumed in an SSR environment (SvelteKit, Nuxt, server middleware), importing any component that triggers
downloadCSVat module evaluation time — or calling it during SSR rendering — throws:Note that a similar issue was already fixed in the same file for
createDebounce(which usedwindow.setTimeoutinstead of plainsetTimeout). This PR applies the same SSR-awareness todownloadCSV.Fix
Add a
typeof document === "undefined"guard at the top ofdownloadCSV():export const downloadCSV = (rows: Array<Array<any>>, filename: string, headers: Array<string>) => { if (rows.length === 0) { return } + if (typeof document === "undefined" || typeof URL === "undefined") { + console.warn("downloadCSV: browser APIs not available (SSR environment), skipping download.") + return + } + const csvContent = [headers.join(","), ...rows.map((row) => row.join(","))].join("\n") ...Also adds a unit test to the existing
describe("downloadCSV")block inindex.test.tsto verify the guard fires correctly in a simulated SSR environment.Checklist
createDebouncein the same file