I'm trying to rewrite an old HTML and plain JS frontend project using Vite and Preact. I created the project as suggested in the Getting Started docs via:
npm init preact
The basics seem to work fine. But currently I'm stumbling over a problem with downloading a file via ajax. The typical snippet you find out there somehow looks like this (in my development environment the backend is running at http://localhost:8080 the frontend at http://localhost:5173)
let response = await fetch("http://localhost:8080/api/entries/template");
if (response.ok) {
let content = await response.text(); //I know this request always returns a plain text file
let type = "text/plain"; //and sets the respective content-type header
let downloadLink = document.createElement('a');
downloadLink.href = window.URL.createObjectURL(new Blob([content], { type }));
downloadLink.download = 'filename.txt';
document.body.appendChild(downloadLink);
downloadLink.click();
}
This is (more or less) copy and pasted from the old project, where it works fine. But it doesn't work with the new project anymore and throws the following security error
Uncaught SecurityError: Failed to execute 'pushState' on 'History': A history state object with URL 'blob:/26cd13a0-b444-4ff4-9dd5-f3b79d04b2ec' cannot be created in a document with origin 'http://localhost:5173' and URL 'http://localhost:5173/entries'
The stacktrace points to preact-iso and it thows at history.pushState in the following snippet:
function handleNav(state, action) {
...
url = link.href.replace(location.origin, "");
...
if (push === true) history.pushState(null, "", url);
...
}
with push being true of course, and url being the blob:/... mentioned in the error. It seems noteworthy that the link.href generated by window.URL.createObjectURL looks bit different, i.e. is something like blob:http://localhost:5173/26cd13a0-b444-4ff4-9dd5-f3b79d04b2ec
What am I doing wrong here? Does this method not work with the Preact Router any more? If so, what is the correct way of doing this in a Preact web app?
Yes, I know the default way would be just creating a <a href=... download='filenane.txt'> to download the file. But for reasons of how the api is currently defined (and I cannot change at the moment) I need to set some headers with this request, in order for the file to be generated correctly. Thus a simple link won't work.
As @Yogi discovered, yup, this was due to the router intercepting the link (which it shouldn't). Just a bug in the package, not in your app.
PR to correct it & close the issue you had opened.
Thanks for reporting it!
Edit: For preact-iso v2.11.0 and older, you can work around this by setting a target different from _self to the download link:
const downloadLink = document.createElement('a');
downloadLink.href = window.URL.createObjectURL(new Blob([content], { type }));
downloadLink.download = 'filename.txt';
downloadLink.target = '_top';
document.body.appendChild(downloadLink);
downloadLink.click();