kit icon indicating copy to clipboard operation
kit copied to clipboard

Build an output that is usable from local disk (file:// URL)

Open jzillmann opened this issue 3 years ago • 14 comments

Is your feature request related to a problem? Please describe. Quite often i code tools which take some input (like a JSON file) and generate a HTML report out of it. This HTML report might be served by a server (e.g. Github Pages) but often just sits somewhere on the disk and should be openable/consumable from there (e.g. open file:///Users/x/projects/y/build/test/index.html).

Describe the solution you'd like From initial experiments (with adapter-static) I found 2 things I would need for the above to work which I failed to accomplish with svelte-kit so far:

  • Links/Includes (from index.html) need to be relative instead of absolute
    • paths/assets||base were quite resistant setting up anything relative
  • The JS field need to be in IIFE format (since module script imports will raise CORS issues)
    • vite.build: { lib: { name: "my-app", entry: "src/main.ts", formats: ["iife"] }} produced UMD and IIFE output formats are not supported for code-splitting builds

A solution would be to make the 2 things possible. Perfect would be a simple way to achieve that. Like an specialized adapter.

Describe alternatives you've considered Maybe what I'm trying to achieve is dump and using svelte-kit for such a thing doesn't make much sense? Just stick to Svelte Classic ?

With Svelte Classic and Vite I can accomplish the above by tweaking vite.config.js in the following way:

  • Set base to '' (makes the links relative)
  • Configure IIEF format:
build: {
  lib: { name: "my-app", entry: "src/main.ts", formats: ["iife"] },
}
  • Move the index.html to public/ and add appropriate script and css includes

How important is this feature to you? This is the first thing I usually set up for the majority of apps I'm crafting.

jzillmann avatar Apr 06 '21 17:04 jzillmann

I understand your pain as I have the same problem. See also issues https://github.com/sveltejs/kit/issues/1553 and https://github.com/sveltejs/kit/issues/1154.

KonradHoeffner avatar May 25 '21 15:05 KonradHoeffner

@jzillmann Re your current Svelte Classic & Vite solution: could you go more into the routing?

btakita avatar Jul 20 '21 11:07 btakita

@btakita Not sure what you mean...

jzillmann avatar Jul 21 '21 18:07 jzillmann

@jzillmann How are you currently routing to the different "page" components in the SPA? I wonder if it's feasible to extract only the Sveltekit router to use with Classic Svelte & Vite.

--- Edit

I'm going to try to add use rollup & @sveltejs/kit as a git submodule to directly output all of the JS & CSS into index.html. Will expose some core functions such as create_manifest_data and whatever else is useful to handle the routing. Fortunately kit is well factored. Hopefully it will work out.

btakita avatar Jul 22 '21 01:07 btakita

Oh, I guess i just do customized routing, nothing special there!

jzillmann avatar Jul 25 '21 22:07 jzillmann

This issue is essential for Electron apps, which use the filesystem.

All the links and scripts in the page are absolute, so it doesn't work.

maximedupre avatar Jul 27 '21 16:07 maximedupre

Just a heads-up. I'm working on a fork of Sapper to support file:// hash-based routing. My project needs to continue supporting sapper until some SSR issues are fixed (https://github.com/vitejs/vite/issues/4306).

My current work in progress is at https://github.com/btakita/sapper/tree/hbr I presume that SvelteKit & Sapper have similar routing. I'm using conditionals checking the window.location.origin === 'file://' to use the Hash Based Routing strategy.

I had to attend to some other priorities & will continue with the hbr branch over the next couple of days.

btakita avatar Jul 28 '21 23:07 btakita

@benmccann I was able to modify the routing to switch between hash-based routing when the origin is file:// & otherwise the default path-based-routing. https://github.com/btakita/sapper/tree/hbr

When the time comes to migrate my project over to Svelte Kit, I'll work on a PR. The conditionals are spread out in several places in https://github.com/btakita/sapper/blob/hbr/runtime/src/app/router/index.ts & https://github.com/btakita/sapper/blob/hbr/runtime/src/app/goto/index.ts.

I didn't want to do any major refactorings, lest the hbr branch diverge too far from the master branch.

btakita avatar Aug 02 '21 22:08 btakita

Also hoping this can get added to sveltekit.

IMO This is essential for running native apps with svelte running in WebView as a GUI

Is there a workaround available?

I'd like to keep using SvelteKit for creating the full blown app with a server. But also need the SPA app to be run from file on client if no connection is available.

AlsDevShack avatar Aug 31 '21 12:08 AlsDevShack

I've tried installing the vite-plugin-singlefile and putting the default config in the svelte.config.js, but the resulting bundle never shows up. I am not familiar with vite.

TJKoury avatar Oct 08 '21 15:10 TJKoury

any solution for now? I want to use sveltekit and adapter-static to generate static html files use on cordova, but there is no luck for now.

easyfrog avatar Oct 11 '21 06:10 easyfrog

After a lot of tinkering I have managed to build a script that can take an html-file generated by adapter-static and inline all javascript in the file. It is very hacky and runs code in the browser to make the imports work, but it works for my use-case which is to build simple html-files with a bit of interactivity in them that I can send in an email without hosting them anywhere. Might be useful as inspiration for someone.

The trickiest part is that the only way I could use import was if the the module was added as a blob to the page. But adding it as a blob generates a new url for the module, which means that all import statements must be rewritten after the blob has been added.

https://github.com/mattiash/svelte-notebook/blob/master/util/inlinejs.cjs

mattiash avatar Feb 09 '22 20:02 mattiash

I also came up with a hacky workaround for a similar issue including hash routing, not sure if it's specific to file:/// but as an index.html being loaded from within an extension which I think is treated similarly.

  1. Create an index.html/+page.svelte route that is not prerendered, which is ran when the extension loads the index.html. This index.html route then redirects to the root route via goto('/');
  2. Hash routes are swapped in after route transitions (handleRouteChange with afterNavigate on root +layout.svelte)
  3. On app initialization any initial hash routes are navigated to the correct route (handleInitialHashRoute with onMount on root +layout.svelte) and subsequently replaced with the hash route again.

chadian avatar Sep 11 '22 19:09 chadian

This might what ya all are looking for: https://github.com/richardtallent/vite-plugin-singlefile

from https://github.com/sveltejs/kit/issues/2042#issuecomment-889818690 (has example)

MentalGear avatar Sep 18 '22 18:09 MentalGear

I hope this is the correct issue to comment on.

My use-case is similar, even though I don't need it to work standalone with file://.

I am trying to make a my application work within an electron app. For that, I found that if I was not using SvelteKit, I could configure vite with base: './' and it would work as expected. With SvelteKit this currently seems impossible as all paths have to empty or absolute.

When I leave kit.paths.base empty, it won't use what I put in vite either. Seems like the configuration for SvelteKit wins either way.

till avatar Jan 20 '23 19:01 till

@till it would seem that SvelteKit always sets base to ./ when building the client:

https://github.com/sveltejs/kit/blob/cad0389e5f5f6d407e1c48f45d84c28c744d73aa/packages/kit/src/exports/vite/index.js#L431

Is there anything you notice being different in the output between a normal Vite build with ./ and a SvelteKit build?

benmccann avatar Jan 22 '23 17:01 benmccann

@benmccann Maybe I am using it wrong, or using not the latest? I just upgraded to a 1.x.

When I set base in vite's config nothing happens, when I leave paths.base empty in SvelteKit's configuration, all paths in the build are / and then my app fails to run.

till avatar Jan 22 '23 17:01 till

Hmm, looking at the code, maybe paths.base vs paths.assets?

till avatar Jan 22 '23 17:01 till

Links/Includes (from index.html) need to be relative instead of absolute

The file references are relative now:

Screenshot from 2023-01-23 15-36-27

The JS field need to be in IIFE format (since module script imports will raise CORS issues)

Yep. It looks like this is the main blocker:

Screenshot from 2023-01-23 15-36-43

IIFE requires that we have only a single input, so in IIFE mode we would need to tweak that:

https://github.com/sveltejs/kit/blob/74798e94ab16db33887a2229ae03f6ec419d40f3/packages/kit/src/exports/vite/index.js#L407

This is highly related to https://github.com/sveltejs/kit/issues/3882. I'm not entirely sure what will be required to support IIFE beyond that. Probably we should try to use IIFE only on the client.

benmccann avatar Jan 23 '23 23:01 benmccann

@benmccann is there anything to check on my end?

This is the code that gets generated currently:

<!DOCTYPE html>
<html lang="en">
	<head>
		<meta charset="utf-8" />
		<meta name="description" content="" />
		<link rel="icon" href="/favicon.png" />
		<meta name="viewport" content="width=device-width, initial-scale=1" />
		<meta http-equiv="content-security-policy" content="">
		<link rel="modulepreload" href="/_app/immutable/start-029f2991.js">
		<link rel="modulepreload" href="/_app/immutable/chunks/index-1e690e6a.js">
		<link rel="modulepreload" href="/_app/immutable/chunks/singletons-89169131.js">
		<link rel="modulepreload" href="/_app/immutable/chunks/index-e3c86715.js">
	</head>
	<body class="flex flex-col items-center min-h-screen">
		
		<script type="module" data-sveltekit-hydrate="45h">
			import { start } from "/_app/immutable/start-029f2991.js";

			start({
				env: {},
				paths: {"assets":"","base":""},
				target: document.querySelector('[data-sveltekit-hydrate="45h"]').parentNode,
				version: "1674548434903"
			});
		</script>
	</body>
</html>

My svelte.config.js:

// import adapter from '@sveltejs/adapter-auto';
import adapter from '@sveltejs/adapter-static'
import preprocess from 'svelte-preprocess'

/** @type {import('@sveltejs/kit').Config} */
export default {
	extensions: ['.svelte'],
	kit: {
		adapter: adapter({
			// default options are shown
			pages: 'build',
			assets: 'build',
			fallback: 'index.html',
			precompress: false,
		}),
	},
	preprocess: [
		preprocess({
			postcss: true,
		}),
	],
}

My vite.config.js:

const config = {
	build: {
		minify: true,
	},
	resolve: {
		alias: {
			$routes: path.resolve('./src/routes'),
			$store: path.resolve('./src/store'),
		},
	},
	plugins: [sveltekit()],
}
export default config

till avatar Jan 24 '23 12:01 till