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

build single js file and single css file like Svelte does. #3882

Closed
kauaicreative opened this issue Feb 13, 2022 · 56 comments
Closed

build single js file and single css file like Svelte does. #3882

kauaicreative opened this issue Feb 13, 2022 · 56 comments
Assignees
Labels
feature / enhancement New feature or request
Milestone

Comments

@kauaicreative
Copy link

Describe the problem

Is there an option or an adaptor that will build the app as svelte does - with a single js file and single css file?

Describe the proposed solution

This makes embedding into wordpress much simpler.

Alternatives considered

I am using the adapter from '@sveltejs/adapter-static' but this builds each component into many individual js and css files.
Also tried the wordpress adapter. It is not compatible with the latest version of Svelte Kit. And I do not believe it will create a single js file and single css file.

Importance

i cannot use SvelteKit without it

Additional Information

No response

@kauaicreative
Copy link
Author

kauaicreative commented Feb 13, 2022

I see that using adapter( { fallback: 'index.html' }) get's me most the way there - 2 js file and 1 css file. But the file names change after each compilation (/_app/assets/vendor-4ff902fe.css") which makes linking to these files impossible without another build step. Is there a way to set the file name much like we can set the pages and assets directory names?

	<link rel="stylesheet" href="/_app/assets/vendor-4ff902fe.css">
	<link rel="modulepreload" href="/_app/start-7cbc46f1.js">
	<link rel="modulepreload" href="/_app/chunks/vendor-878788bc.js">

@benmccann
Copy link
Member

You may be interested in the inlineStyleThreshold option: https://kit.svelte.dev/docs/configuration#inlinestylethreshold

The name is based on the file contents. This allows the file to be cached. Can you explain more about why you would want to link to these files?

@kauaicreative
Copy link
Author

kauaicreative commented Feb 13, 2022

@benblazak , thanks for the idea but I don't think it will work in this case.

I have several svelte apps which I am dynamically inserting into wordpress content via shortcodes. In wordpress/php I wp_register_script() and wp_enqueue_script() to add the css and js assets to the wordpress page render process. These filenames can not change. I then insert the svelte startup markup into the content. e.g. svelte: <div id="app"></div> , or sveltekit: start({...})

I was hoping to use sveltekit moving forward but I may have to go back to svelte for these embedded apps.

@kauaicreative
Copy link
Author

kauaicreative commented Feb 13, 2022

Wondering if I should be looking at component packaging instead #518 #1499

@gevera
Copy link

gevera commented Jul 12, 2022

Encountered same need, we are transitioning from a React SPA. Hopefully this will get some love

@cubic3d
Copy link

cubic3d commented Aug 12, 2022

@gevera looks like you can use vite config to influence file names: sveltejs/vite-plugin-svelte#375 (comment)

@paolodina
Copy link

@cubic3d

I tried and doesn't work.

The following Vite config options will be overridden by SvelteKit:
  - build.rollupOptions.output.entryFileNames
  - build.rollupOptions.output.chunkFileNames
  - build.rollupOptions.output.assetFileNames

@edwinspire
Copy link

I have the same problem with the latest version of Svelte.

@d-velopment
Copy link

While there's no solution or adapter itself hasn't been improved, I've created the "hacky" way of re-wrapping the project files to the bundle.js: https://github.com/d-velopment/SvelteKit-One-Bundle

It's not solving the problem of thousand chunks but gives the bundle.js to be loaded from a custom index.html or another project to render the application.

@benmccann
Copy link
Member

Can folks share their use cases for this? If we implement it, I'd like to ensure that we document when to use it vs when not to. I believe users should mostly avoid this option, but I'd like to hear about valid use cases so that we can guide users appropriately.

@wozzashek
Copy link

My use case is for an enterprise platform. In truth I don't need all the JS to be in a single file. Rather I need all the JS to be built to the /dist folder, no JS or CSS in subfolders.

So the enterprise platform I use allows me to serve a single html page at an endpoint with all JS & CSS file requests coming from that endpoint e.g.

Index html: platform.com/webapp/my_app
All JS/CSS files: platform.com/webapp/my_app/{filename}

Although that forces me to use hashed routing as well which I guess I'll have to implement myself unless Sveltekit has one I can turn on.

@khromov
Copy link

khromov commented Apr 17, 2023

@benmccann I'm making a Capacitor application for iOS/Android. Capacitor works by starting a local web server and then serving your application like a static host on your phone. This local server does not have SSL or HTTP/2, so while some requests load in parallel, it takes quite a while to load 50+ resources over HTTP/1. It's hard to measure raw numbers but in our project it takes around ~1 second for loading up the app until interactable and about half that on our PWA version. So it takes twice as long to load a local application on your phone than going to servers in a different country.

@edwinspire
Copy link

In my case I am using svelte to create a web interface inside an ESP32 (microcontroller), for the microcontroller, the fewer requests it has to attend to, the better, for this reason having everything in one file is ideal, or at least the html, js and css are each in a single file.

@ChristianSch
Copy link

This is also a common problem highlighted by SEO tools. 'reduce number of css files'. Would be good to be able to combine them.

@anakinjay
Copy link

I have this need for importing components into other systems like wordpress or drupal.

In react, we've always handled this by reading the assets manifest.json file from webpack to figure out which file names we need to load, which vite DOES support under the options build.manifest, but when I try and enable that settings I get the error:

The following Vite config options will be overridden by SvelteKit:
  - build.manifest

@mdocter
Copy link

mdocter commented May 13, 2023

Like @wozzashek mentioned, I also work with an enterprise system, Microsoft Dynamics/Power Platform.

  1. In their case Microsoft prepends a cache busting version value (=hash/guid) in front of my web files in the url path. So a hash in the js/css filenames is unnecessary.

<Microsoft Dynamics URL>/%7B<version value>%7D/WebResources/index.html

  1. Those web resources are part of a solution zip file which is used to deploy and redeploy updates. So I want to have the same file names, otherwise my solution grows and grows with each build being added.

I tried all suggestions to disable the [hash] in the file names, but nothing works. Having multiple chunks is not a problem in my use case, as long as they have the same file names.

Any suggestions on how to disable the hash in the file names?

@tystol
Copy link

tystol commented Jun 9, 2023

My use case is for packaging up an admin front-end UI within a back-end library. The devs consuming the library will ultimately choose which paths the UI will be served under, and it needs to be dynamic at runtime.
eg. The Hangfire dashboard UI (not my project, but a close analogy to what I need to do):
https://docs.hangfire.io/en/latest/configuration/using-dashboard.html
Note the end of the article, I'd like to support things like this too:

Multiple Dashboards
You can also map multiple dashboards that show information about different storages.

var storage1 = new SqlServerStorage("Connection1");
var storage2 = new SqlServerStorage("Connection2");

app.UseHangfireDashboard("/hangfire1", new DashboardOptions(), storage1);
app.UseHangfireDashboard("/hangfire2", new DashboardOptions(), storage2);

@captainhusaynpenguin
Copy link

My most obvious usecase is developing a CSS framework and using SvelteKit as a wrapper for documentations and also partially custom implementation of the CSS style for one off svelte components.

So, such a feature would be extremely, extremely appreciated;

PS. Stuff like these seemed to be the focus/concern of the dev team at some point in time? see: Svelte.Dev Blog CSS-in-JS

@mdocter

This comment was marked as off-topic.

@dsbaars
Copy link

dsbaars commented Nov 17, 2023

In my case I am using svelte to create a web interface inside an ESP32 (microcontroller), for the microcontroller, the fewer requests it has to attend to, the better, for this reason having everything in one file is ideal, or at least the html, js and css are each in a single file.

Similar use case here, also writing a web interface for a ESP32. As it uses SPIFFS/LittleFS, the filenames also need to be shorter than 31 characters.

@AndrewPenry
Copy link

My use case: The new PCI DSS 4.0 - 11.6.1 requires anyone with a payment page to monitor the page (as rendered by the client) weekly for changes, to make sure no one has modified the code. Writing synthetic user monitoring services (in Playwright or whatever) will be much easier if there are less files to compare. Note that the requirement as written prevents you from checking the integrity of your files just on the server side; it must monitor it on the client side to ensure it catches attacks injected by malicious 3rd parties.

@craigkai
Copy link

In my case I am using svelte to create a web interface inside an ESP32 (microcontroller), for the microcontroller, the fewer requests it has to attend to, the better, for this reason having everything in one file is ideal, or at least the html, js and css are each in a single file.

Similar use case here, also writing a web interface for a ESP32. As it uses SPIFFS/LittleFS, the filenames also need to be shorter than 31 characters.

Did you ever end up figuring this out?

@dsbaars
Copy link

dsbaars commented Jan 22, 2024

Did you ever end up figuring this out?

I solved this in a quite ugly way. I created a patch that's applied automatically by patch-package when doing yarn install.

@theelims
Copy link

@craigkai My project contains a small plugin for vite to shorten the filenames: https://github.com/theelims/ESP32-sveltekit/blob/main/interface/vite-plugin-littlefs.ts which must then be included into https://github.com/theelims/ESP32-sveltekit/blob/main/interface/vite.config.ts

@flo-at
Copy link

flo-at commented Feb 14, 2024

Our use case: SPA that acts as the GUI to our software. SEO isn't a concern here at all. It works fine with many small files. But I'd prefer a longer initial loading time (for the bundles) and snappier navigation (fewer requests) over the current situation.

The websever serves precompressed files (br and gz) and large/merged files typically have a lot better compression ratio than small ones.

Ideally we could get this:

index.html
bundle.[hash].js
bundle.[hash].css
favicon.png
...

I'm overall pretty happy with Svelte(Kit). Thanks a lot for the awesome work!

@katriellucas
Copy link

katriellucas commented Feb 19, 2024

Adding to the comment above: https://csswizardry.com/2023/10/the-three-c-concatenate-compress-cache/

This article explains how one file is sometimes better than a many files setup even in HTTP/2 scenarios, which is due to how compression works by comparing the file against itself. A big file has more "historical data" to use and ends up finding way more repetitions thus compressing better.

I think it makes sense to be able to controls this.

@timoanttila
Copy link

timoanttila commented Mar 24, 2024

My use case for this would be admin area inside ProcessWire CMS. I want to import SvelteKit app with multiple pages to one PHP page. I have tried copying all the scripts and divs from the build folder's index.html and later tried using iframe. It would be perfect if there would be only one JS and CSS file. There is no need for SEO.

I want this to be fully client side because some of the information is dynamic and need to be fetched from backend after checking who user is and what rights (s)he has.

import adapter from '@sveltejs/adapter-static'
const vitePreprocess = import('@sveltejs/vite-plugin-svelte').then(m => m.vitePreprocess())

/** @type {import('@sveltejs/kit').Config} */
const config = {
  preprocess: {
    script: async options => (await vitePreprocess).script(options),
    style: async options => (await vitePreprocess).style(options)
  },
  kit: {
    adapter: adapter({
      fallback: 'index.html'
    }),
    prerender: {
      crawl: false,
      entries: []
    },
    alias: {src: './src'}
  }
}

export default config

I have used Vue before but I would really want to change to SvelteKit since it is much simpler and my frontend is made with it.

@DougAnderson444
Copy link

I'd like to use SRI on the Svelte app code, so I need one file in order to hash it all and provide the integrity-hash on that one script.

@gregg-cbs
Copy link

Im not looking for a single file but im looking for less files. On my home page which does not have much i am seeing 75 js file requests. Some are 500bytes. I would like to be able to group components/imports/files into chunks so there arent all these bitty requests.

The issue I see is that my burger menu code is being loaded last and so the burger menu does not have any functionality until the whole page is completely loaded - on a slow network this is a horrible experience. If i could optimize this by grouping the burger menu code with other code/components into 1 file then my problem is alleviated.

Webpack has this functionality with their chunkName:
import( /* webpackChunkName: "login" */ '../views/LoginView.vue'),

@fortserious
Copy link

Adding another use case for this feature.

In our company, our app is bundled into an WebView2 portal that's limited to displaying a single html file. We can inject file contents into this file, but we can't read these files from a subdirectory.

We are currently using Svelte + adapter-static + vite-plugin-singlefile to create a single index.html file and hand-rolling our own routing and that's working well for our purposes, but we'd ideally like to move to SvelteKit's native routing.

We can work with either a single .html file or a single .js, .css and .html file (as long as it could be expected that they all share the same root directory).

@khromov
Copy link

khromov commented Aug 16, 2024

After discussion with a couple core maintainers, there are two solutions that can be used today to reduce the number of built JS bundles:

Option 1: Configure experimentalMinChunkSize

This is an Experimental Vite option that does some smart optimisation of joining multiple chunks together. This will cause a slight overlap in chunks (so some code might be loaded that isn't used for certain routes), but it seems to bring down the number of JS files loaded by ~30% (so for example, from 30 loaded JS files to 20).

A vite.config.ts example:

import { sveltekit } from '@sveltejs/kit/vite';
import { defineConfig } from 'vitest/config';

export default defineConfig({
	plugins: [sveltekit(),],
	test: { include: ['src/**/*.{test,spec}.{js,ts}']},
	build: {
		rollupOptions: {
			output: {
				experimentalMinChunkSize: 50000, // add this
			}
		}
	}
});

Option 2: Configure manualChunks

Configuring manualChunks is the best option to reduce the number of JS files. It can reduce the total number of files down to approximately 3 JS files. To set this option:

import { sveltekit } from '@sveltejs/kit/vite';
import { defineConfig } from 'vitest/config';

export default defineConfig({
	plugins: [sveltekit(),],
	test: { include: ['src/**/*.{test,spec}.{js,ts}']},
	build: {
		rollupOptions: {
			output: {
				manualChunks: (id) => {
					return 'my-app';
				}
			}
		}
	}
});

While this does not bring the number of built files to just one, it does fix a lot of the mentioned cases above where you have many dozens or even 100+ JS files being loaded for a single page load, or you are working in a constrained environment.

@ImXirvin

This comment was marked as duplicate.

@gregg-cbs
Copy link

gregg-cbs commented Sep 6, 2024

Yeah so i did manual chunking @ImXirvin and I got performance improvements so i do recommend.

I looked at the network requests in the browser, noted down which js files i wanted to group. I then did the console log in rollups chunk generator to see the path it uses for those files and added them to an array to group.

The code is self explanatory and simple:

vite.config.ts

import { sveltekit } from '@sveltejs/kit/vite';
import { defineConfig } from 'vitest/config';

// the chunks you want to group - common things around your site
const group_chunks = [
  "src/constants.ts",
  
  "src/lib/utils/util_fetch.ts",
  "src/lib/utils/util_date_get_time.ts",
  "src/lib/utils/util_price_to_rands.ts",
  "src/lib/utils/util_date_to_friendly_date.ts",
  
  "flowbite-svelte/dist/typography/A.svelte", // node package
  "flowbite-svelte/dist/typography/Button.svelte",
  "flowbite-svelte/dist/toast/Toast.svelte",
  
  "svelte-hero-icons/dist/Icon.svelte",
  
  "src/lib/components/metadata.svelte",
  "src/lib/components/logo.svelte",
  "src/lib/components/form/input.svelte",
  "src/lib/components/form/field.svelte",
  "src/lib/components/header/header.svelte",
  "src/lib/components/header/headerNavbar.svelte",
  "src/lib/components/general/loader.svelte",
]

export default defineConfig({
  plugins: [sveltekit()],
  test: {
    include: ['src/**/*.{test,spec}.{js,ts}']
  },
  server: {
    host: true
  },
  resolve: {
    alias: {
      '$types': "../types"
    }
  },
  build: {
    rollupOptions: {
      output: {
        manualChunks: (id) => {
          // console.log(id) // -> do this to see all the files/components being used when rollup creates the chunks
          const is_group_chunk = group_chunks.some(partialPath => id.includes(partialPath));
          
          if (is_group_chunk) {
            return "general" // the chunk name they will be grouped under
          }
        },
      },
    },
  },
});

@kran6a
Copy link

kran6a commented Sep 7, 2024

I build svelte-based widgets. Currently they are tiny (~20kb - 100kb) self-contained svelte apps. They include their own css, fonts, images, svgs and js and are then bundled to a single js file using an internal tool I hacked together. Those widgets are then stored on a database along with their configuration/theming metadata so they are available for the end users.

I would like to be able to use sveltekit + adapter static to build them mainly to benefit from svelte-kit routing and layout system. Maybe we should have an official "adapter-component" which outputs either a single .js file file ready to be mounted on any DOM node or a .js file and a .css file.

@khromov
Copy link

khromov commented Sep 7, 2024

@kran6a We already have this, it's the Vite Svelte preset.

SvelteKit can't be mounted in a DOM node, as it needs to take over routing for the whole page.

Where I work we've had success using Kit for widgets by using iframes and Pym.js to handle the resizing.

@kran6a
Copy link

kran6a commented Sep 7, 2024

@kran6a We already have this, it's the Vite Svelte preset.

SvelteKit can't be mounted in a DOM node, as it needs to take over routing for the whole page.

Where I work we've had success using Kit for widgets by using iframes and Pym.js to handle the resizing.

The default preset does not inline fonts and images in CSS as base64 data urls nor does it inline CSS into JS. Svgs also have their own inlining process since it is possible to inline them without base64 encoding, resulting in a few less bytes per svg.

@benmccann
Copy link
Member

Leaving some notes for when we hopefully come back to this before too long. Here's an old branch I found for this: https://github.com/sveltejs/kit/tree/iife

The main reason we hadn't moved forward with it was trying to nail down the API. I ended up with only a single option. I'm not sure that JS and CSS are separately controllable. We get the JS and CSS out of the client manifest:

const entry = find_deps(client_manifest, node.component, true);
If there's only a single JS entry in the client manifest then there's only a single CSS entry in the client manifest as well I believe and so I don't think you could turn off code-splitting for JS, but leave it enabled for CSS.

@flashblocks
Copy link

What is the solution? Is this completed? Thank.

@brunnerh
Copy link
Member

There is a bundle strategy option now, see release notes:

@Rich-Harris
Copy link
Member

Yep — add output.bundleStrategy: 'single' to your svelte.config.js:

import adapter from '@sveltejs/adapter-auto';

/** @type {import('@sveltejs/kit').Config} */
const config = {
  kit: {
    adapter: adapter(),

+   output: {
+     bundleStrategy: 'single'
+   }
  }
};

export default config;

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature / enhancement New feature or request
Projects
None yet
Development

No branches or pull requests