sapper icon indicating copy to clipboard operation
sapper copied to clipboard

make sapper.base optional

Open swyxio opened this issue 6 years ago • 15 comments

Is your feature request related to a problem? Please describe. sapper.base was introduced to solve https://github.com/sveltejs/sapper/issues/180 over a year ago. it is not optional for sapper to compile (i'm not sure why, but im sure there are good reasons). however, this causes screwy things to happen for relative/anchor links.

in discord, rich has voiced willingness:

unindented03/15/2019 is there a way to avoid including %sapper.base% and have prefetching still work? that tag is making me really unhappy it means I can't have relative links anywhere it also fucks up my anchor links

rich_harris03/15/2019 not in the current version. would love to get rid of it, it needs some care though

Describe the solution you'd like make <base> be optional, if not supplied by the user.

Describe alternatives you've considered none

How important is this feature to you? moderate. i'm autogenerating some links and having to account for base is making it pretty tricky.

swyxio avatar Sep 19 '19 05:09 swyxio

updated - workaround is to just monkey patch the relative anchor links for now on mount


  // hack for adding location onto anchor links bc of base element
  import { onMount } from 'svelte'
  onMount(async () => {
    ;[...document.querySelectorAll('a[href^="#"]')].map(
      x => (x.href = document.location + new URL(x.href).hash)
    )
  })

old

workaround: put this in _layout

<script>
  import { onMount } from 'svelte'
  onMount(async () => {
    const base = [...document.head.children].find(x => x.nodeName === 'BASE')
    if (base) base.remove()
  })
</script>

swyxio avatar Sep 19 '19 05:09 swyxio

i found that if i did this it would mostly work but i would occasionally get 500 errors here and there. i dont know how to reliably reproduce them but its frequent enough that clearly something is wrong (requesting /client/foo/bar instead of /foo/bar)

swyxio avatar Sep 19 '19 06:09 swyxio

A <base> also makes url() based refs in SVGs tricksy too fwiw; without converting to absolute urls Safari can't deal.

alexdilley avatar Sep 19 '19 07:09 alexdilley

I came here to repeat what @alexdilley said. Safari doesn't like the <base> tag, it stops the fills from working on the browser and returns fallback fills for SVGs.

danielhaim1 avatar Sep 24 '19 07:09 danielhaim1

@danielhaim1 - i was able to get fills to work. But I had to add style="fill: currentColor;" to the SVG itself. Likewise, I could not use classes within SVGs, I had to apply the stylings inline to the style tag unless it could use something that cascaded, like the fill (with currentColor on the SVG itself).

khrome83 avatar Oct 02 '19 01:10 khrome83

I just spent a few hours trying to get my anchor links to work, only to find out that %sapper.base% was the root cause, but when I remove it Sapper doesn't work then :(

@Rich-Harris From what I've read on #180 this was introduced to help multiple sapper clients on the same server find their own files, but this is now causing a few headaches:

  1. Even if your using a single Sapper client on the server, you are still required to have %sapper.base%
  2. To get relative and anchor links to work, extra work has to be done (both in the server and client)
  3. Any resources with src attribute reference's to relative files are 404'd
# root path
/

# image's real location on live server
/docs/example-1.png

# in `/docs/` (a route), this is where `<img src='example-1.png'>` points to
/example-1.png

# and without `<base>` the same img tag's src attribute points to
/docs/example-1.png

If your like me and only using one instance of Sapper on the server (sapper dev), and then exporting (sapper export), having to go through all these headaches because of one unneeded feature shouldn't even be happening 😢

futagoza avatar Oct 10 '19 08:10 futagoza

Building on @sw-yx's workaround:

<!-- src/components/FixAnchorLinks.svelte -->
<script>
    import { onMount } from "svelte";

    onMount( () => {

        document.querySelectorAll( "a" ).forEach( a => {

            if ( ! a.hash || ! document.querySelectorAll( a.hash ).length ) return;

            a.href = window.location + a.hash;

        } );
    
    } );
</script>

<!-- src/routes/documentation.svelte -->
<script>
    import FixAnchorLinks from "../components/FixAnchorLinks.svelte";
</script>

<FixAnchorLinks />

As a component, this fix's anchor links that navigate to a section on the same page.

NOTE: Only use this component on pages that contain anchor links!

futagoza avatar Oct 10 '19 11:10 futagoza

This line a.href = window.location + a.hash; is better as a.href = window.location.origin + window.location.pathname + a.hash

I was getting an issue where hashes were appended to the same hash, breaking the navigation. This should resolve that for anyone else with that issue, AFAIK.

mikepeiman avatar Feb 25 '20 21:02 mikepeiman

Another bug caused by <base href=/> when using Sapper as a static site generator on Github pages (i.e. gh-pages -d __sapper__/export) is the error disallowed MIME type for the client.js file, see https://medium.com/@ppm1988/fix-disallowed-mime-type-error-angular-github-pages-7a8e9769fe75

Paulmicha avatar Mar 29 '20 14:03 Paulmicha

Tripped over this as well for skip-links. Seems like we can leverage the path store instead of client-side patching:

<script>
	import { stores } from '@sapper/app';

	const { page } = stores();
</script>

<a href={`${$page.path}#maincontent`}>Skip to main content (Press Enter)</a>

<main id="maincontent">
	<slot />
</main>

eps1lon avatar May 16 '20 19:05 eps1lon

My server.js

import sirv from "sirv";
import polka from "polka";
import compression from "compression";
import * as sapper from "@sapper/server";
import path from "path";
import fs from "fs";
const { PORT, NODE_ENV } = process.env;
const dev = NODE_ENV === "development";
const server_path = __dirname;
const root_path = path.resolve(__dirname, "../../../");
const client_path = path.resolve(server_path, "../client");
console.log({ root_path, __dirname, client_path });
function sendFile(filePath, res) {// 这里到时候再完善
  if (filePath.endsWith(".js")) { 
    res.setHeader("content-type", "application/javascript; charset=utf-8");
  }
  fs.createReadStream(filePath).pipe(res);
}

polka() // You can also use Express
  .use(function file_server(req, res, next) {
    const file_path = path.resolve(root_path, "./" + req.url);
    if (req.method === "GET") {
      fs.exists(file_path, (r) => {
        if (r) {
          sendFile(file_path, res);
        } else {
          if (/\/client\//.test(req.url)) {
            const fileName = req.url.replace(/.*\/client\//, "");
            const client_file_path = path.resolve(client_path, "./" + fileName);
            sendFile(client_file_path, res);
          } else {
            next(); // move on
          }
        }
      });
    } else {
      next(); // move on
    }
  })
  .use(compression({ threshold: 0 }), sirv("static", { dev }), sapper.middleware())
  .listen(PORT, (err) => {
    if (err) console.log("error", err);
  });

// 不得不说我在这耗费了一些时间,前面的几个解决方案是对爬虫不怎么友好的。所以只能自己写一段了。 I have to say that I spent some time here, and the first few solutions are not very crawler-friendly.So I have to write a paragraph by myself.

2234839 avatar May 24 '20 07:05 2234839

ah good solution @eps1lon

swyxio avatar May 24 '20 11:05 swyxio

Not the exact functionality that is being looked for here, but maybe a helpful alternate solution if you just want to use a button to navigate to anchor tags inside a page without adding a fragment or mutating the url. just using some super basic vanilla js (can easily make this more robust from here):

<script>
const scrollToAnchor = targetID => {
        const distanceToTop = el => Math.floor(el.getBoundingClientRect().top);

        const targetAnchor = document.querySelector(targetID);
        if (!targetAnchor) return;

        window.scrollBy({ top: distanceToTop(targetAnchor), left: 0 });
    };
</script>

 <!-- somewhere in page -->
<button on:click={()=>scrollToAnchor('#example-anchor')}>Scroll To Anchor</button>

 <!-- somewhere else in page -->
<div id="example-anchor">
	div to scroll to
</div>

colehpage avatar May 28 '20 17:05 colehpage

For SVG users ending up here.

I have a little workaround to deal with broken inline SVG :)

raskyld avatar Jun 27 '20 10:06 raskyld

Not sure if this is relevant, when I open my statically exported site with lynx I get a warning Alert!: HREF in BASE tag is not an absolute URL. I also tried setting it manually with sapper export --basepath / but am still getting the error. Apart from that warning in Lynx the site works fine though.

riccardolardi avatar Sep 15 '20 22:09 riccardolardi