kit
kit copied to clipboard
adapter-static: control “`route.html` or `route/index.html`” output separately from `trailingSlash` option
Describe the problem
I’m generating a static site that I’m putting in an Amazon S3 bucket where it gets served by CDN. My app has a file path like this:
src/routes/foo/+page.js
src/routes/foo/+page.svelte
- When I build the site, I want this output as
build/foo/index.html. - I want it accessible to my users at
/foo, with no trailing slash.
My understanding of the docs is that the only option I have to influence this is export const trailingSlash in my src/routes/+layout.js file. If I use either export const trailingSlash = 'never' or 'ignore', the build command produces build/foo.html. This doesn’t work for me, as my CDN doesn’t resolve /foo to /foo.html.
This leaves export const trailingSlash = 'always', which does cause the build command to produce my desired output of build/foo/index.html. But of course the point of trailingSlash = 'always' is to prevent a path of /foo from working; it must be /foo/. This isn’t the style we use for other URLs, and we want these URLs to be shareable without users needing to know that they shouldn’t strip off this unusual trailing slash. Or to boil down my feature request into a single sentence:
- I want to use
adapter-staticto output “folder-based” routes (likesrc/routes/foo/+page.js) as folders containingindex.htmlfiles (likebuild/foo/index.html) without also needing to make trailing slashes required.
Describe the proposed solution
It seems to me like the simplest solution would be to create a new option to explicitly control HTML file output style (whether to output as foo.html or as foo/index.html), and if present it would override the choice determined by trailingSlash.
Alternatives considered
I read https://github.com/sveltejs/kit/issues/5334 and that issue seemed to get derailed into discussions of redirects and other aspects I’m not concerned with. I agree with the original poster’s feeling that it seems wrong that trailingSlash seems to collapse together two orthogonal options (how to treat trailing slashes in paths, and whether to output foo/+page.js as foo.html or foo/index.html).
After writing all of the above I just discovered https://github.com/sveltejs/kit/pull/3801, where what I’m asking for seems to have been removed because the assumption was that there wouldn’t be static hosts out there that behave the way I’m describing, where a hosted foo.html isn’t accessible via /foo and foo/index.html requires a trailing slash. These are configurable options; SvelteKit shouldn’t assume that no hosts support resolving foo/index.html for /foo(no trailing slash). Amazon S3’s website hosting default behavior is to redirect /foo to /foo/ and serve the index file for /foo/, but this can be changed.
Importance
i cannot use SvelteKit without it
Additional Information
No response
For anyone else using Amazon S3 who wants paths without trailing slashes, I found a workaround:
- In your
src/routes/+layout.jsfile, addexport const trailingSlash = 'never'. This will cause your build to be output with file paths likebuild/foo.html. - When you upload to S3, save these files without an extension and with
Content-Type: text/html. For example,aws s3api put-object --bucket your-bucket --key foo --body foo.html --content-type text/html. Do this for all HTML files except those namedindex.html.
The explicit content type metadata tells S3 how to serve this object, even without an extension. And since the build was configured to not use trailing slashes, the app’s client-side router also expects this slashless path.
I just started using SvelteKit and had the same requirement trailingSlash = 'never', but with <page>/index.html static files. I have build this monstrosity to rename all <page>.html files to <page>/index.html.
@patrick-zippenfenig cheers for this, ran into the same issue. Though this does seem to break the relative paths of css files and other includes done in the html. Modified the script a bit to do what Geoffrey suggested
One workaround is to structure your route such as src/routes/foo/index.html/+page.svelte. This should result in the prerendered page /foo/index.html but you run into the risk of this issue https://github.com/sveltejs/kit/issues/8676
I'm also running into this. @GeoffreyBooth's workaround does get me there, but I wouldn't consider it a solution because it requires saving the files without an extension in the project, e.g. foo instead of foo.html.
I would love to see this addressed, happy to help test a fix (although testing is the straightforward part probably).
I've found a solution that works for me. Unlike what @GeoffreyBooth claims in the original issue, paths without a trailing slash do work when static hosting on AWS, if your static site is built with trailingSlash = 'always'. This is because AWS automatically redirects e.g. /foo to /foo/, and serves /foo/index.html (which is generated by SvelteKit, so long as you set trailingSlash = 'always').
So I guess specifically I disagree with two claims that @GeoffreyBooth makes:
But of course the point of trailingSlash = 'always' is to prevent a path of /foo from working
/foo does work using S3 static hosting -- it redirects to /foo/
and
we want these URLs to be shareable without users needing to know that they shouldn’t strip off this unusual trailing slash.
The URL /foo is shareable because it will automatically redirect. It doesn't matter if users strip the trailing slash.
I hope this is helpful for anyone sharing this issue.
So I guess specifically I disagree with two claims that @GeoffreyBooth makes:
My point is that most websites don't have paths that end in slashes, and such URLs look incorrect to average users. We want paths that don't end in slashes and that don't redirect to paths that end in slashes.
My adapter-static s3 website was working through the direct *.s3-website.amazonaws.com url, but not through cloudfront. Here is what I ended up doing.
In order to use the sveltekit build output without changing filenames, I used
trailingSlash: 'always'
and a cloudfront function on the distribution along the lines of
request.uri = uri.endsWith('/')
? uri + 'index.html'
: uri + '/index.html'