sveltejs / sapper

The next small thing in web development, powered by Svelte
https://sapper.svelte.dev
MIT License
7k stars 434 forks source link

Add XML generation #461

Open aubergene opened 5 years ago

aubergene commented 5 years ago

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
Rich-Harris commented 5 years ago

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

aubergene commented 5 years ago

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.

Rich-Harris commented 5 years ago

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);
}
kball commented 5 years ago

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?

rcauquil commented 4 years ago

@kball any news on this ?

kball commented 4 years ago

@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)

IwateKyle commented 4 years ago

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?

tiffany352 commented 4 years ago

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

IwateKyle commented 4 years ago

@tiffany352 , did you try removing the [?] (question mark) from <?xml version....> and just use < xml version... > That is what finally worked for me.

thgh commented 4 years ago

I think there is an html minifier that is enabled by default, you probably want to disable it in this case.

nosovk commented 3 years ago

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