Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Paths #229

Closed
Rich-Harris opened this issue Dec 5, 2020 · 9 comments
Closed

Paths #229

Rich-Harris opened this issue Dec 5, 2020 · 9 comments

Comments

@Rich-Harris
Copy link
Member

We need to support at least two, maybe three, path configurations:

  • a basePath that is used for routing, so that a request for /foo/bar on a site with a basePath of /foo is handled by the /bar route. It defaults to ''
  • a staticPath for static assets, in cases where assets are deployed to somewhere other than the site itself. It defaults to '' and is resolved against basePath, meaning that https://example.com/my/things/and/stuff, /my/things/and/stuff and my/things/and/stuff would all be valid (and would resolve to the same thing in the case where the app was deployed to https://example.com and had an empty basePath).
  • (possibly) an additional assetPath for generated assets, which might conceivably need to live somewhere other than alongside regular static assets. This defaults to '_app' (🐃 ) and is resolved against staticPath.

All these values would be declared the config file:

// svelte.config.js
module.exports = {
  adapter: '@sveltejs/adapter-node',
  basePath: '/subdirectory',
  staticPath: '/static-assets', // *not* relative to `/subdirectory`, because leading `/`
  assetPath: 'svelte-kit-stuff' // *is* relative to `/static-assets`, i.e. `/static-assets/svelte-kit-stuff`
};

Does it matter that basePath is intended to be a path, but staticPath and assetPath could potentially be fully-qualified URLs?

Given the problems they caused in Sapper, we should probably try and avoid relying on <base> elements (I quite like the idea of per-page <base> elements to normalise between /foo and /foo/, but they do cause merry havoc with things like SVG url(...) properties). In other words, if something needs to use basePath, it should presumably be importable from somewhere. I propose $app/env:

import { basePath } from '$app/env';
import { goto } from '$app/navigation';

function handle_click() {
  goto(`${basePath}/other-page`);
}

Alternatively, should goto automatically add the basePath prefix?

Similarly, staticPath would be available to JS modules and components:

<script>
  import { staticPath } from '$app/env'; // fully resolved, if the version in the config was relative
</script>

<img alt="a potato" src="${staticPath}/images/potato.jpg">

If the stars align, these constants will get folded into the source code.

One thing I haven't thought about is whether CSS should be able to access these values somehow. But I don't feel like solving that problem right now.

Any thoughts?

@Rich-Harris Rich-Harris added this to the public beta milestone Dec 5, 2020
@Rich-Harris
Copy link
Member Author

I did wonder if basePath et al should in fact be environment variables, but then I remembered that with Snowpack the variables have to be prefixed, and this...

import { basePath, staticPath } from '$app/env';

...is arguably somewhat nicer than this:

const basePath = import.meta.env.SNOWPACK_PUBLIC_BASEPATH;
const staticPath = import.meta.env.SNOWPACK_PUBLIC_STATICPATH;

In fact I now think we should probably put all env-related stuff in there:

import {
  basePath,
  staticPath,
  assetPath, // not that you'd ever need this one
  browser, // boolean
  dev // boolean
} from '$app/env';

@Rich-Harris
Copy link
Member Author

Ugh I just realised that test/apps/options/svelte.config.js looks like this:

module.exports = {
  // TODO adapterless builds
  adapter: '@sveltejs/adapter-node',

  paths: {
    static: 'public',
    routes: 'source/pages',
    template: 'source/template.html'
  },

  target: '#content-goes-here',

  // this creates `window.start` which starts the app, instead of
  // it starting automatically — allows test runner to control
  // when hydration occurs
  startGlobal: 'start'
};

basePath, staticPath and assetPath would look really odd next to paths. Need to come up with better names for everything

@dummdidumm
Copy link
Member

General note: I think it would be better to have all svelte-kit related config within a property called svelteKitOptions or something to not pollute the global config namespace.

@Rich-Harris
Copy link
Member Author

Yeah, I reluctantly agree. maybe kitOptions, the svelte is a bit redundant (and e.g. it's compilerOptions not svelteCompilerOptions).

Thought about the shape of this a little more and I'm now in favour of this:

module.exports = {
  kitOptions: {
    // renamed from current 'paths'
    files: {
      static: 'public',
      routes: 'source/pages',
      template: 'source/template.html'
    },

    // this maps to `$app/paths`, e.g. 
    //   import * as paths from '$app/paths';
    //   console.log(paths.base, paths.static);
    paths: {
      base: basePath: '/subdirectory',
      static: '/static-assets', // *not* relative to `/subdirectory`, because leading `/`
      assets: 'svelte-kit-stuff' // *is* relative to `/static-assets`, i.e. `/static-assets/svelte-kit-stuff`
    }
  }
};

It's annoying that static is a reserved word because it means you have to do

import * as paths from '$app/paths';
console.log(paths.static);

rather than

import { static } from '$app/paths';
console.log(static);

but c'est la vie. Any feedback before i go ahead and implement this?

@pngwn
Copy link
Member

pngwn commented Dec 7, 2020

Have we considered being able to pass in these paths at runtime in addition to/ instead of reading from config at build time? Specifically the basePath.

@Rich-Harris
Copy link
Member Author

I hadn't, no. What's the use case?

@pngwn
Copy link
Member

pngwn commented Dec 7, 2020

Microfrontends is my main usecase (I'll create an issue for this, explaining what we'd need to support that usecase, it isn't a lot really). But a frontend that had internal routing doesn't know where it will live (the basePath) until it is mounted, and nor should it. So this would need to be passed in when it is initialised. This was also why I mentioned being able to pass a router instance into apps for it to use. This isn't essential but makes routing between different standalone apps simpler. Nested routers can be tricky.

Another use case is is deploy previews for pull requests (although this could be solved in other ways). In the case of static apps you may use a single url for these kinds of deploys. dev.my-product.com with each deploy on some subpath maybe the PR slug or commit hash: dev.product.com/implement-some-feature/ even though when deployed they'll live somewhere else. You could modify the config before the build but then you'll need to build multiple times and can't pass artifacts around. This introduces some risk as the build you tested against in dev is not the same one you are using in production, but this isn't a deal breaker really just makes things a bit simpler in some cases.

@benmccann
Copy link
Member

Instead of static and assets you might call it assets and generated (or processed, etc.). This would solve the problem with static being a keyword. Also, I think that static and assets are almost synonyms (I frequently say "static assets") and it's hard to tell what goes where, but generated would help clarify this.

I also somewhat wonder if it should be kit instead of kitOptions because it seems like everything in this file is an option, so the "options" part might be a bit redundant

@Rich-Harris
Copy link
Member Author

Yeah, I like assets and generated. Take the point re kitOptions vs kit, was mainly thinking about symmetry with compilerOptions

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

No branches or pull requests

4 participants