Skip to content
This repository has been archived by the owner on Jan 11, 2023. It is now read-only.

make sapper.base optional #904

Open
swyxio opened this issue Sep 19, 2019 · 15 comments
Open

make sapper.base optional #904

swyxio opened this issue Sep 19, 2019 · 15 comments
Labels

Comments

@swyxio
Copy link

swyxio commented Sep 19, 2019

Is your feature request related to a problem? Please describe.
sapper.base was introduced to solve #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
Copy link
Author

swyxio commented Sep 19, 2019

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
Copy link
Author

swyxio commented Sep 19, 2019

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)

@alexdilley
Copy link

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

@danielhaim1
Copy link

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.

@khrome83
Copy link

khrome83 commented Oct 2, 2019

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

@futagoza
Copy link

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
Copy link

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!

@mikepeiman
Copy link

mikepeiman commented Feb 25, 2020

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 added a commit to mikepeiman/super-tic-tac-toe that referenced this issue Mar 6, 2020
@Paulmicha
Copy link

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

@eps1lon
Copy link

eps1lon commented May 16, 2020

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>

@2234839
Copy link

2234839 commented May 24, 2020

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.

@swyxio
Copy link
Author

swyxio commented May 24, 2020

ah good solution @eps1lon

@colehpage
Copy link

colehpage commented May 28, 2020

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>

@raskyld
Copy link

raskyld commented Jun 27, 2020

For SVG users ending up here.

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

@riccardolardi
Copy link
Contributor

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.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.