sapper
sapper copied to clipboard
Add XML generation
It would be good if we could generate XML in the same way that works for HTML. My current use case is for generating a sitemap.xml, which was talked about a bit in #9. I want to export the site but have some urls which aren't directly linked to (or perhaps non-deterministically) so it's nice to know they can be spidered.
Currently when I add a .xml
to src/routes/
I get this and error trying to start the server.
✗ server
Unexpected token
Here's how I'm generating an RSS feed for Svelte Hacker News — https://github.com/sveltejs/sapper-hacker-news/blob/master/src/routes/%5Blist%5D/rss.js. If I wanted the .xml file extension I'd just call it rss.xml.js
. Not exactly what you're after, but it's a solution.
I guess you could try doing sitemap.xml.html
to use component syntax? Not sure if that'd work or not
Ah cool, this is a good solution, thanks!
I had a play around with sitemap.xml.html
and it looks promising, I think it just needs a way to change the template.html
and ideally _layout.html
, it didn't look like they configurable at the moment.
Good point. I don't think that's likely to change. But you could maybe do it this way:
// src/routes/sitemap.xml.js
import Sitemap from './_Sitemap.html';
export function get(req, res) {
const sitemap = Sitemap.render(...);
res.writeHead(200, {
'Content-Type': 'application/rss+xml'
});
res.end(sitemap);
}
I'm also running into the specific problem of wanting to generate a sitemap for a static site.
In the discussion on #9 there was talk of allowing sitemap generation as an option to exporting. @Rich-Harris would you accept a PR to add that option?
@kball any news on this ?
@rcauquil I did not end up implementing an option to export, and instead used a more manual solution similar to @Rich-Harris's example. You can see how it came out here: https://github.com/kball/speakwritelisten.com/blob/master/src/routes/sitemap.xml.js and then to make sure it rendered during export I fetch it in prefetch from index.svelte
(https://github.com/kball/speakwritelisten.com/blob/master/src/routes/index.svelte)
Good point. I don't think that's likely to change. But you could maybe do it this way:
// src/routes/sitemap.xml.js import Sitemap from './_Sitemap.html'; export function get(req, res) { const sitemap = Sitemap.render(...); res.writeHead(200, { 'Content-Type': 'application/rss+xml' }); res.end(sitemap); }
This worked well... the only catch (for me at least) was
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>http://www.example.com/</loc>
<lastmod>2005-01-01</lastmod>
<changefreq>monthly</changefreq>
<priority>0.8</priority>
</url>
</urlset>
</xml>
that the import / render procedure didn't like the "?" in the Doc title <?xml portion. This was causing problem. I removed the "?" and just left it as <xml....
<xml version="1.0" encoding="UTF-8">
I just scraped the above XML snippet from a sitemap.xml example off the net. I'm not that familiar with XML to know if the missing ? will cause problems - but an online xml validator I tested against didn't complain. Any thoughts?
I got close to getting this working using @IwateKyle's suggestion, but no matter what I do, tags have their self closing part stripped off, producing invalid XML.
Like I would write this:
<link href={post.url} />
or this:
<link href={post.url}></link>
but it would always get mangled into
<link href="abcd">
and produce invalid XML.
The DTD issue can be fixed by doing this:
res.end(`<?xml version="1.0" encoding="UTF-8"?>${content.html}`);
It feels pretty bad that I can't use Svelte templates for producing XML. I'll probably have to piece them together an Atom feed with template strings...
@tiffany352 , did you try removing the [?] (question mark) from <?xml version....> and just use < xml version... > That is what finally worked for me.
I think there is an html minifier that is enabled by default, you probably want to disable it in this case.
Also there is another bit "dirty" way to create sitemap
import {routes} from '@sapper/internal/manifest-client.mjs';
routes.forEach(el => {
data.push( el.pattern.source.toString().replace(/\\/g, '').split('/').filter(el => /[a-zA-Z-]/.exec(el)).join('/'))
})
data = [...new Set(data)]
https://github.com/MailCheck-co/mailcheck.site/commit/beedbbe1da08b0dee784c5596d7c045ddcda8d0f#diff-72ecb24b600de219094b2c6c2a5879b260efddeda513e29c883589e121f90083R1
it depends on internal realization, and not recommended in production, but it's compatible with sapper export