diff --git a/packages/fontless/README.md b/packages/fontless/README.md
index a4fedd6f..13e731ab 100644
--- a/packages/fontless/README.md
+++ b/packages/fontless/README.md
@@ -9,7 +9,7 @@
## Features
-- 🚀 **Optimized font loading**: Automatically loads and configures fonts with proper fallbacks
+- 🚀 **Optimized font loading**: Automatically loads and configures fonts with proper fallbacks and preload links.
- 🔤 **Multiple provider support**: Google Fonts, Bunny Fonts, FontShare, FontSource, and more using [unifont](https://github.com/unjs/unifont)
- 📦 **Zero runtime overhead**: Pure CSS solution with no JavaScript required at runtime
- 📏 **Metric-based fallbacks**: Reduces Cumulative Layout Shift (CLS) by using font metrics from [fontaine](https://github.com/unjs/fontaine)
@@ -115,6 +115,95 @@ fontless({
})
```
+## Preloading Fonts
+
+Fontless provides an option to select fonts to preload via `preload` option. For Vite SPA, the selected preload fonts are automatically injected into the HTML.
+
+For SSR meta-frameworks which don't rely on [`transformIndexHtml` plugin hook](https://vite.dev/guide/api-plugin.html#transformindexhtml), you need to manually render preload links on the server. Fontless provides `fontless/runtime` module for server to access the necessary data for preload links generation, for example:
+
+- Vanilla
+
+```tsx
+import { preloads } from "fontless/runtime";
+
+function renderHtml() {
+ const renderedPreloads = preloads
+ .map(
+ (attrs) =>
+ ` `,
+ )
+ .join("\n");
+ return `\
+
+
+ ${renderedPreloads}
+
+
+ ...
+
+
+`;
+}
+```
+
+- [Qwik](./examples/qwik-app)
+
+```tsx
+import { preloads } from "fontless/runtime"
+
+export const RouterHead = component$(() => {
+ return (
+ <>
+ {preloads.map((l) => (
+
+ ))}
+ ...
+ >
+ )
+})
+```
+
+- [React](./examples/react-router-app)
+
+```tsx
+import { preloads } from 'fontless/runtime'
+
+function Layout() {
+ return (
+
+
+ {preloads.map(({crossorigin, ...attrs}) => (
+
+ ))}
+ ...
+
+
+ ...
+
+
+ )
+}
+```
+
+- [SvelteKit]./examples/sveltekit-app)
+
+```svelte
+
+
+
+
+ {#each preloads as attrs}
+
+ {/each}
+
+```
+
## How It Works
Fontless works by:
diff --git a/packages/fontless/build.config.ts b/packages/fontless/build.config.ts
index 55adfa7d..b8143cc0 100644
--- a/packages/fontless/build.config.ts
+++ b/packages/fontless/build.config.ts
@@ -5,5 +5,9 @@ export default defineBuildConfig({
rollup: {
dts: { respectExternal: false },
},
+ entries: [
+ 'src/index.ts',
+ 'src/runtime.ts',
+ ],
externals: ['vite'],
})
diff --git a/packages/fontless/e2e/basic.test.ts b/packages/fontless/e2e/basic.test.ts
index 5e1f2b03..7b4a74f1 100644
--- a/packages/fontless/e2e/basic.test.ts
+++ b/packages/fontless/e2e/basic.test.ts
@@ -38,3 +38,86 @@ test.describe('dev vanilla', () => {
)
})
})
+
+test.describe('build react-rotuer', () => {
+ let cli: ReturnType
+ let baseURL: string
+
+ test.beforeAll(async () => {
+ cli = runCli({ command: 'pnpm build', cwd: 'examples/react-router-app' })
+ await cli.done
+ cli = runCli({ command: 'pnpm start', cwd: 'examples/react-router-app' })
+ const port = await cli.findPort()
+ baseURL = `http://localhost:${port}`
+ })
+
+ test.afterAll(async () => {
+ if (!cli)
+ return
+ cli.kill()
+ await cli.done
+ })
+
+ test('basic', async ({ page }) => {
+ await page.goto(baseURL)
+ const fonts = await page.evaluate(async () => {
+ const fonts = await (globalThis as any).document.fonts.ready
+ return [...fonts].map((f: any) => ({ family: f.family, status: f.status }))
+ })
+ expect(fonts).toEqual(
+ expect.arrayContaining([
+ { family: 'Poppins', status: 'loaded' },
+ ]),
+ )
+ })
+
+ test.describe('no js', () => {
+ test.use({ javaScriptEnabled: false })
+
+ test('ssr preload links', async ({ page }) => {
+ await page.goto(baseURL)
+ await expect(page.locator('head > link[rel="preload"][as="font"]').first()).toBeAttached()
+ })
+ })
+})
+
+test.describe('build sveltekit', () => {
+ let cli: ReturnType
+ let baseURL: string
+
+ test.beforeAll(async () => {
+ const build = runCli({ command: 'pnpm build', cwd: 'examples/sveltekit-app' })
+ await build.done
+ cli = runCli({ command: 'pnpm preview', cwd: 'examples/sveltekit-app' })
+ const port = await cli.findPort()
+ baseURL = `http://localhost:${port}`
+ })
+
+ test.afterAll(async () => {
+ if (!cli)
+ return
+ cli.kill()
+ await cli.done
+ })
+
+ test('basic', async ({ page }) => {
+ await page.goto(baseURL)
+ const fonts = await page.evaluate(async () => {
+ const fonts = await (globalThis as any).document.fonts.ready
+ return [...fonts].map((f: any) => ({ family: f.family, status: f.status }))
+ })
+ expect(fonts).toEqual(
+ expect.arrayContaining([
+ { family: 'Fira Sans', status: 'loaded' },
+ ]),
+ )
+ })
+
+ test.describe('no js', () => {
+ test.use({ javaScriptEnabled: false })
+ test('ssr preload links', async ({ page }) => {
+ await page.goto(baseURL)
+ await expect(page.locator('head > link[rel="preload"][as="font"]').first()).toBeAttached()
+ })
+ })
+})
diff --git a/packages/fontless/examples/qwik-app/package.json b/packages/fontless/examples/qwik-app/package.json
index fe212c69..e0d3b530 100644
--- a/packages/fontless/examples/qwik-app/package.json
+++ b/packages/fontless/examples/qwik-app/package.json
@@ -15,11 +15,11 @@
"build": "qwik build",
"build.client": "vite build",
"build.preview": "vite build --ssr src/entry.preview.tsx",
- "build.types": "tsc --incremental --noEmit",
+ "x-build.types": "tsc --incremental --noEmit",
"deploy": "echo 'Run \"npm run qwik add\" to install a server adapter'",
"dev": "vite --mode ssr --port 5173",
"dev.debug": "node --inspect-brk ./node_modules/vite/bin/vite.js --mode ssr --force",
- "lint": "eslint \"src/**/*.ts*\"",
+ "x-lint": "eslint \"src/**/*.ts*\"",
"preview": "qwik build preview && vite preview --open --port 5173",
"start": "vite --open --mode ssr",
"qwik": "qwik"
diff --git a/packages/fontless/examples/qwik-app/src/components/router-head/router-head.tsx b/packages/fontless/examples/qwik-app/src/components/router-head/router-head.tsx
index 8a35f4e1..f057fba2 100644
--- a/packages/fontless/examples/qwik-app/src/components/router-head/router-head.tsx
+++ b/packages/fontless/examples/qwik-app/src/components/router-head/router-head.tsx
@@ -1,5 +1,6 @@
import { component$ } from '@qwik.dev/core'
import { useDocumentHead, useLocation } from '@qwik.dev/router'
+import { preloads } from "fontless/runtime"
/**
* The RouterHead component is placed inside of the document `` element.
@@ -24,6 +25,10 @@ export const RouterHead = component$(() => {
))}
+ {preloads.map((l) => (
+
+ ))}
+
{head.styles.map(s => (
))}
diff --git a/packages/fontless/examples/qwik-app/src/routes/index.tsx b/packages/fontless/examples/qwik-app/src/routes/index.tsx
index 676ada74..a58d9fdd 100644
--- a/packages/fontless/examples/qwik-app/src/routes/index.tsx
+++ b/packages/fontless/examples/qwik-app/src/routes/index.tsx
@@ -1,3 +1,4 @@
+///
import type { DocumentHead } from '@qwik.dev/router'
import { component$, useStyles$ } from '@qwik.dev/core'
import styles from './index.css?inline'
diff --git a/packages/fontless/examples/qwik-app/vite.config.mts b/packages/fontless/examples/qwik-app/vite.config.mts
index e6bd591d..e73abc4f 100644
--- a/packages/fontless/examples/qwik-app/vite.config.mts
+++ b/packages/fontless/examples/qwik-app/vite.config.mts
@@ -23,7 +23,21 @@ errorOnDuplicatesPkgDeps(devDependencies, dependencies)
*/
export default defineConfig(({ command, mode }): UserConfig => {
return {
- plugins: [qwikRouter(), qwikVite(), tsconfigPaths(), fontless()],
+ plugins: [
+ qwikRouter(),
+ qwikVite({
+ disableFontPreload: true,
+ }),
+ tsconfigPaths(),
+ fontless({
+ families: [
+ {
+ name: 'Poppins',
+ preload: true,
+ }
+ ]
+ }),
+ ],
// This tells Vite which dependencies to pre-build in dev mode.
optimizeDeps: {
// Put problematic deps that break bundling here, mostly those with binaries.
diff --git a/packages/fontless/examples/react-router-app/app/root.tsx b/packages/fontless/examples/react-router-app/app/root.tsx
index 6d124d9f..f32a9d95 100644
--- a/packages/fontless/examples/react-router-app/app/root.tsx
+++ b/packages/fontless/examples/react-router-app/app/root.tsx
@@ -2,12 +2,21 @@ import { Links, Meta, Outlet, Scripts, ScrollRestoration } from 'react-router'
import './index.css'
+import { preloads } from 'fontless/runtime'
+
export function Layout({ children }: { children: React.ReactNode }) {
return (
+ {preloads.map(({crossorigin, ...attrs}) => (
+
+ ))}
diff --git a/packages/fontless/examples/react-router-app/package.json b/packages/fontless/examples/react-router-app/package.json
index db417178..e1976c0b 100644
--- a/packages/fontless/examples/react-router-app/package.json
+++ b/packages/fontless/examples/react-router-app/package.json
@@ -9,7 +9,7 @@
"scripts": {
"build": "react-router build",
"dev": "react-router dev",
- "start": "react-router-serve ./build/server/index.js",
+ "start": "PORT=3000 react-router-serve ./build/server/index.js",
"typecheck": "tsc"
},
"dependencies": {
diff --git a/packages/fontless/examples/react-router-app/vite.config.ts b/packages/fontless/examples/react-router-app/vite.config.ts
index 427d1d51..c4089f92 100644
--- a/packages/fontless/examples/react-router-app/vite.config.ts
+++ b/packages/fontless/examples/react-router-app/vite.config.ts
@@ -5,6 +5,13 @@ import { defineConfig } from 'vite'
export default defineConfig({
plugins: [
reactRouter(),
- fontless(),
+ fontless({
+ families: [
+ {
+ name: 'Poppins',
+ preload: true,
+ },
+ ],
+ }),
],
})
diff --git a/packages/fontless/examples/svelte-app/.gitignore b/packages/fontless/examples/svelte-app/.gitignore
deleted file mode 100644
index a547bf36..00000000
--- a/packages/fontless/examples/svelte-app/.gitignore
+++ /dev/null
@@ -1,24 +0,0 @@
-# Logs
-logs
-*.log
-npm-debug.log*
-yarn-debug.log*
-yarn-error.log*
-pnpm-debug.log*
-lerna-debug.log*
-
-node_modules
-dist
-dist-ssr
-*.local
-
-# Editor directories and files
-.vscode/*
-!.vscode/extensions.json
-.idea
-.DS_Store
-*.suo
-*.ntvs*
-*.njsproj
-*.sln
-*.sw?
diff --git a/packages/fontless/examples/svelte-app/index.html b/packages/fontless/examples/svelte-app/index.html
deleted file mode 100644
index b6c5f0af..00000000
--- a/packages/fontless/examples/svelte-app/index.html
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
-
-
-
-
- Vite + Svelte + TS
-
-
-
-
-
-
diff --git a/packages/fontless/examples/svelte-app/package.json b/packages/fontless/examples/svelte-app/package.json
deleted file mode 100644
index fb6afba3..00000000
--- a/packages/fontless/examples/svelte-app/package.json
+++ /dev/null
@@ -1,21 +0,0 @@
-{
- "name": "svelte-app",
- "type": "module",
- "version": "0.0.0",
- "private": true,
- "scripts": {
- "dev": "vite",
- "build": "vite build",
- "preview": "vite preview",
- "check": "svelte-check --tsconfig ./tsconfig.app.json && tsc -p tsconfig.node.json"
- },
- "devDependencies": {
- "@sveltejs/vite-plugin-svelte": "^5.1.1",
- "@tsconfig/svelte": "^5.0.5",
- "fontless": "latest",
- "svelte": "^5.39.5",
- "svelte-check": "^4.3.2",
- "typescript": "~5.9.2",
- "vite": "^7.1.7"
- }
-}
diff --git a/packages/fontless/examples/svelte-app/public/vite.svg b/packages/fontless/examples/svelte-app/public/vite.svg
deleted file mode 100644
index e7b8dfb1..00000000
--- a/packages/fontless/examples/svelte-app/public/vite.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/packages/fontless/examples/svelte-app/src/App.svelte b/packages/fontless/examples/svelte-app/src/App.svelte
deleted file mode 100644
index 51d5888d..00000000
--- a/packages/fontless/examples/svelte-app/src/App.svelte
+++ /dev/null
@@ -1,59 +0,0 @@
-
-
-
Google
-
Poppins
-
Press Start 2P
-
-
Bunny
-
Aclonica
-
Allan
-
-
FontShare
-
Panchang
-
-
FontSource
-
Luckiest
-
-
Local
-
Local font
-
-
-
diff --git a/packages/fontless/examples/svelte-app/src/app.css b/packages/fontless/examples/svelte-app/src/app.css
deleted file mode 100644
index c54ee7d1..00000000
--- a/packages/fontless/examples/svelte-app/src/app.css
+++ /dev/null
@@ -1,4 +0,0 @@
-:root {
- color: rgba(255, 255, 255, 0.87);
- background-color: #242424;
-}
diff --git a/packages/fontless/examples/svelte-app/src/black-fox.ttf b/packages/fontless/examples/svelte-app/src/black-fox.ttf
deleted file mode 100644
index 9497048b..00000000
Binary files a/packages/fontless/examples/svelte-app/src/black-fox.ttf and /dev/null differ
diff --git a/packages/fontless/examples/svelte-app/src/main.ts b/packages/fontless/examples/svelte-app/src/main.ts
deleted file mode 100644
index 702d4b59..00000000
--- a/packages/fontless/examples/svelte-app/src/main.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-import { mount } from 'svelte'
-import App from './App.svelte'
-import './app.css'
-
-const app = mount(App, {
- target: document.getElementById('app')!,
-})
-
-export default app
diff --git a/packages/fontless/examples/svelte-app/src/vite-env.d.ts b/packages/fontless/examples/svelte-app/src/vite-env.d.ts
deleted file mode 100644
index 4078e747..00000000
--- a/packages/fontless/examples/svelte-app/src/vite-env.d.ts
+++ /dev/null
@@ -1,2 +0,0 @@
-///
-///
diff --git a/packages/fontless/examples/svelte-app/svelte.config.js b/packages/fontless/examples/svelte-app/svelte.config.js
deleted file mode 100644
index b0683fd2..00000000
--- a/packages/fontless/examples/svelte-app/svelte.config.js
+++ /dev/null
@@ -1,7 +0,0 @@
-import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'
-
-export default {
- // Consult https://svelte.dev/docs#compile-time-svelte-preprocess
- // for more information about preprocessors
- preprocess: vitePreprocess(),
-}
diff --git a/packages/fontless/examples/svelte-app/tsconfig.app.json b/packages/fontless/examples/svelte-app/tsconfig.app.json
deleted file mode 100644
index cde3b9d3..00000000
--- a/packages/fontless/examples/svelte-app/tsconfig.app.json
+++ /dev/null
@@ -1,20 +0,0 @@
-{
- "extends": "@tsconfig/svelte/tsconfig.json",
- "compilerOptions": {
- "target": "ESNext",
- "moduleDetection": "force",
- "useDefineForClassFields": true,
- "module": "ESNext",
- "resolveJsonModule": true,
- /**
- * Typecheck JS in `.svelte` and `.js` files by default.
- * Disable checkJs if you'd like to use dynamic types in JS.
- * Note that setting allowJs false does not prevent the use
- * of JS in `.svelte` files.
- */
- "allowJs": true,
- "checkJs": true,
- "isolatedModules": true
- },
- "include": ["src/**/*.ts", "src/**/*.js", "src/**/*.svelte"]
-}
diff --git a/packages/fontless/examples/svelte-app/tsconfig.json b/packages/fontless/examples/svelte-app/tsconfig.json
deleted file mode 100644
index a2efd469..00000000
--- a/packages/fontless/examples/svelte-app/tsconfig.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "references": [
- { "path": "./tsconfig.app.json" },
- { "path": "./tsconfig.node.json" }
- ],
- "files": []
-}
diff --git a/packages/fontless/examples/svelte-app/tsconfig.node.json b/packages/fontless/examples/svelte-app/tsconfig.node.json
deleted file mode 100644
index d6fbfae8..00000000
--- a/packages/fontless/examples/svelte-app/tsconfig.node.json
+++ /dev/null
@@ -1,24 +0,0 @@
-{
- "compilerOptions": {
- "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
- "target": "ES2022",
- "lib": ["ES2023"],
- "moduleDetection": "force",
- "module": "ESNext",
-
- /* Bundler mode */
- "moduleResolution": "bundler",
- "allowImportingTsExtensions": true,
-
- /* Linting */
- "strict": true,
- "noFallthroughCasesInSwitch": true,
- "noUnusedLocals": true,
- "noUnusedParameters": true,
- "noEmit": true,
- "isolatedModules": true,
- "skipLibCheck": true,
- "noUncheckedSideEffectImports": true
- },
- "include": ["vite.config.ts"]
-}
diff --git a/packages/fontless/examples/svelte-app/vite.config.ts b/packages/fontless/examples/svelte-app/vite.config.ts
deleted file mode 100644
index 15731a80..00000000
--- a/packages/fontless/examples/svelte-app/vite.config.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-import { svelte } from '@sveltejs/vite-plugin-svelte'
-import { fontless } from 'fontless'
-import { defineConfig } from 'vite'
-
-// https://vite.dev/config/
-export default defineConfig({
- plugins: [svelte(), fontless()],
-})
diff --git a/packages/fontless/examples/sveltekit-app/.gitignore b/packages/fontless/examples/sveltekit-app/.gitignore
new file mode 100644
index 00000000..3b462cb0
--- /dev/null
+++ b/packages/fontless/examples/sveltekit-app/.gitignore
@@ -0,0 +1,23 @@
+node_modules
+
+# Output
+.output
+.vercel
+.netlify
+.wrangler
+/.svelte-kit
+/build
+
+# OS
+.DS_Store
+Thumbs.db
+
+# Env
+.env
+.env.*
+!.env.example
+!.env.test
+
+# Vite
+vite.config.js.timestamp-*
+vite.config.ts.timestamp-*
diff --git a/packages/fontless/examples/sveltekit-app/.npmrc b/packages/fontless/examples/sveltekit-app/.npmrc
new file mode 100644
index 00000000..b6f27f13
--- /dev/null
+++ b/packages/fontless/examples/sveltekit-app/.npmrc
@@ -0,0 +1 @@
+engine-strict=true
diff --git a/packages/fontless/examples/sveltekit-app/README.md b/packages/fontless/examples/sveltekit-app/README.md
new file mode 100644
index 00000000..75842c40
--- /dev/null
+++ b/packages/fontless/examples/sveltekit-app/README.md
@@ -0,0 +1,38 @@
+# sv
+
+Everything you need to build a Svelte project, powered by [`sv`](https://github.com/sveltejs/cli).
+
+## Creating a project
+
+If you're seeing this, you've probably already done this step. Congrats!
+
+```sh
+# create a new project in the current directory
+npx sv create
+
+# create a new project in my-app
+npx sv create my-app
+```
+
+## Developing
+
+Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server:
+
+```sh
+npm run dev
+
+# or start the server and open the app in a new browser tab
+npm run dev -- --open
+```
+
+## Building
+
+To create a production version of your app:
+
+```sh
+npm run build
+```
+
+You can preview the production build with `npm run preview`.
+
+> To deploy your app, you may need to install an [adapter](https://svelte.dev/docs/kit/adapters) for your target environment.
diff --git a/packages/fontless/examples/sveltekit-app/package.json b/packages/fontless/examples/sveltekit-app/package.json
new file mode 100644
index 00000000..2012509c
--- /dev/null
+++ b/packages/fontless/examples/sveltekit-app/package.json
@@ -0,0 +1,24 @@
+{
+ "name": "sveltekit-app",
+ "private": true,
+ "version": "0.0.1",
+ "type": "module",
+ "scripts": {
+ "dev": "vite dev",
+ "build": "vite build",
+ "preview": "vite preview",
+ "prepare": "svelte-kit sync || echo ''",
+ "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
+ "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch"
+ },
+ "devDependencies": {
+ "@sveltejs/adapter-auto": "^6.0.0",
+ "@sveltejs/kit": "^2.22.0",
+ "@sveltejs/vite-plugin-svelte": "^6.0.0",
+ "fontless": "latest",
+ "svelte": "^5.0.0",
+ "svelte-check": "^4.0.0",
+ "typescript": "^5.0.0",
+ "vite": "^7.0.4"
+ }
+}
diff --git a/packages/fontless/examples/sveltekit-app/src/app.css b/packages/fontless/examples/sveltekit-app/src/app.css
new file mode 100644
index 00000000..ce9adc67
--- /dev/null
+++ b/packages/fontless/examples/sveltekit-app/src/app.css
@@ -0,0 +1,3 @@
+:root {
+ font-family: "Fira Sans", sans-serif;
+}
diff --git a/packages/fontless/examples/sveltekit-app/src/app.d.ts b/packages/fontless/examples/sveltekit-app/src/app.d.ts
new file mode 100644
index 00000000..da08e6da
--- /dev/null
+++ b/packages/fontless/examples/sveltekit-app/src/app.d.ts
@@ -0,0 +1,13 @@
+// See https://svelte.dev/docs/kit/types#app.d.ts
+// for information about these interfaces
+declare global {
+ namespace App {
+ // interface Error {}
+ // interface Locals {}
+ // interface PageData {}
+ // interface PageState {}
+ // interface Platform {}
+ }
+}
+
+export {};
diff --git a/packages/fontless/examples/sveltekit-app/src/app.html b/packages/fontless/examples/sveltekit-app/src/app.html
new file mode 100644
index 00000000..f273cc58
--- /dev/null
+++ b/packages/fontless/examples/sveltekit-app/src/app.html
@@ -0,0 +1,11 @@
+
+
+
+
+
+ %sveltekit.head%
+
+
+ %sveltekit.body%
+
+
diff --git a/packages/fontless/examples/sveltekit-app/src/lib/assets/favicon.svg b/packages/fontless/examples/sveltekit-app/src/lib/assets/favicon.svg
new file mode 100644
index 00000000..cc5dc66a
--- /dev/null
+++ b/packages/fontless/examples/sveltekit-app/src/lib/assets/favicon.svg
@@ -0,0 +1 @@
+svelte-logo
\ No newline at end of file
diff --git a/packages/fontless/examples/sveltekit-app/src/routes/+layout.svelte b/packages/fontless/examples/sveltekit-app/src/routes/+layout.svelte
new file mode 100644
index 00000000..03fc2096
--- /dev/null
+++ b/packages/fontless/examples/sveltekit-app/src/routes/+layout.svelte
@@ -0,0 +1,16 @@
+
+
+
+
+ {#each preloads as attrs}
+
+ {/each}
+
+
+{@render children?.()}
diff --git a/packages/fontless/examples/sveltekit-app/src/routes/+page.svelte b/packages/fontless/examples/sveltekit-app/src/routes/+page.svelte
new file mode 100644
index 00000000..cc88df0e
--- /dev/null
+++ b/packages/fontless/examples/sveltekit-app/src/routes/+page.svelte
@@ -0,0 +1,2 @@
+Welcome to SvelteKit
+Visit svelte.dev/docs/kit to read the documentation
diff --git a/packages/fontless/examples/sveltekit-app/static/robots.txt b/packages/fontless/examples/sveltekit-app/static/robots.txt
new file mode 100644
index 00000000..b6dd6670
--- /dev/null
+++ b/packages/fontless/examples/sveltekit-app/static/robots.txt
@@ -0,0 +1,3 @@
+# allow crawling everything by default
+User-agent: *
+Disallow:
diff --git a/packages/fontless/examples/sveltekit-app/svelte.config.js b/packages/fontless/examples/sveltekit-app/svelte.config.js
new file mode 100644
index 00000000..1295460d
--- /dev/null
+++ b/packages/fontless/examples/sveltekit-app/svelte.config.js
@@ -0,0 +1,18 @@
+import adapter from '@sveltejs/adapter-auto';
+import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
+
+/** @type {import('@sveltejs/kit').Config} */
+const config = {
+ // Consult https://svelte.dev/docs/kit/integrations
+ // for more information about preprocessors
+ preprocess: vitePreprocess(),
+
+ kit: {
+ // adapter-auto only supports some environments, see https://svelte.dev/docs/kit/adapter-auto for a list.
+ // If your environment is not supported, or you settled on a specific environment, switch out the adapter.
+ // See https://svelte.dev/docs/kit/adapters for more information about adapters.
+ adapter: adapter()
+ }
+};
+
+export default config;
diff --git a/packages/fontless/examples/sveltekit-app/tsconfig.json b/packages/fontless/examples/sveltekit-app/tsconfig.json
new file mode 100644
index 00000000..a5567ee6
--- /dev/null
+++ b/packages/fontless/examples/sveltekit-app/tsconfig.json
@@ -0,0 +1,19 @@
+{
+ "extends": "./.svelte-kit/tsconfig.json",
+ "compilerOptions": {
+ "allowJs": true,
+ "checkJs": true,
+ "esModuleInterop": true,
+ "forceConsistentCasingInFileNames": true,
+ "resolveJsonModule": true,
+ "skipLibCheck": true,
+ "sourceMap": true,
+ "strict": true,
+ "moduleResolution": "bundler"
+ }
+ // Path aliases are handled by https://svelte.dev/docs/kit/configuration#alias
+ // except $lib which is handled by https://svelte.dev/docs/kit/configuration#files
+ //
+ // To make changes to top-level options such as include and exclude, we recommend extending
+ // the generated config; see https://svelte.dev/docs/kit/configuration#typescript
+}
diff --git a/packages/fontless/examples/sveltekit-app/vite.config.ts b/packages/fontless/examples/sveltekit-app/vite.config.ts
new file mode 100644
index 00000000..e80b7224
--- /dev/null
+++ b/packages/fontless/examples/sveltekit-app/vite.config.ts
@@ -0,0 +1,15 @@
+import { sveltekit } from '@sveltejs/kit/vite';
+import { defineConfig } from 'vite';
+import { fontless } from "fontless"
+
+export default defineConfig({
+ plugins: [
+ sveltekit(),
+ fontless({
+ provider: 'google',
+ defaults: {
+ preload: true,
+ }
+ })
+ ]
+});
diff --git a/packages/fontless/package.json b/packages/fontless/package.json
index cd210e73..9be48329 100644
--- a/packages/fontless/package.json
+++ b/packages/fontless/package.json
@@ -23,7 +23,8 @@
],
"sideEffects": false,
"exports": {
- ".": "./dist/index.mjs"
+ ".": "./dist/index.mjs",
+ "./runtime": "./dist/runtime.mjs"
},
"main": "./dist/index.mjs",
"module": "./dist/index.mjs",
diff --git a/packages/fontless/src/runtime.ts b/packages/fontless/src/runtime.ts
new file mode 100644
index 00000000..e35555f4
--- /dev/null
+++ b/packages/fontless/src/runtime.ts
@@ -0,0 +1,9 @@
+/** the runtime value is generated via plugin */
+export const preloads: LinkAttributes[] = []
+
+export interface LinkAttributes {
+ rel: 'preload'
+ as: 'font'
+ href: string
+ crossorigin: 'anonymous' | 'use-credentials' | '' | undefined
+}
diff --git a/packages/fontless/src/vite.ts b/packages/fontless/src/vite.ts
index a3dffd27..4046cb5a 100644
--- a/packages/fontless/src/vite.ts
+++ b/packages/fontless/src/vite.ts
@@ -1,10 +1,12 @@
-import type { Plugin } from 'vite'
+import type { Plugin, ResolvedConfig, ViteDevServer } from 'vite'
import type { NormalizeFontDataContext } from './assets'
+import type { LinkAttributes } from './runtime'
import type { FontlessOptions } from './types'
-import type { FontFamilyInjectionPluginOptions } from './utils'
+import type { FontFamilyInjectionPluginOptions } from './utils'
import { Buffer } from 'node:buffer'
import { defu } from 'defu'
+import MagicString from 'magic-string'
import { joinURL } from 'ufo'
import { normalizeFontData } from './assets'
import { defaultOptions } from './defaults'
@@ -19,16 +21,30 @@ const INLINE_STYLE_ID_RE = /[?&]index=\d+\.css$/
// Copied from vue-bundle-renderer utils
const CSS_EXTENSIONS_RE = /\.(?:css|scss|sass|postcss|pcss|less|stylus|styl)(?:\?[^.]+)?$/
-export function fontless(_options?: FontlessOptions): Plugin {
+export function fontless(_options?: FontlessOptions): Plugin[] {
const options = defu(_options, defaultOptions satisfies FontlessOptions) as FontlessOptions
let cssTransformOptions: FontFamilyInjectionPluginOptions
let assetContext: NormalizeFontDataContext
+ let resolvedConfig: ResolvedConfig
+ let server: ViteDevServer | undefined
+ const RUNTIME_NAME = 'fontless/runtime'
+
+ function getPreloads(): LinkAttributes[] {
+ const hrefs = [...cssTransformOptions.fontsToPreload.values()].flatMap(v => [...v])
+ return hrefs.map(href => ({
+ rel: 'preload',
+ as: 'font',
+ href,
+ crossorigin: '',
+ }))
+ }
- return {
+ const mainPlugin: Plugin = {
name: 'vite-plugin-fontless',
apply: (_config, env) => !env.isPreview,
async configResolved(config) {
+ resolvedConfig = config
assetContext = {
dev: config.mode === 'development',
renderedFontURLs: new Map(),
@@ -74,9 +90,10 @@ export function fontless(_options?: FontlessOptions): Plugin {
}
}
},
- configureServer(server) {
+ configureServer(server_) {
// serve font assets via middleware during dev
// based on https://github.com/nuxt/fonts/blob/e7f537a0357896d34be9c17031b3178fb4e79042/src/assets.ts#L30
+ server = server_
server.middlewares.use(assetContext.assetsBaseURL, async (req, res, next) => {
try {
const filename = req.url!.slice(1)
@@ -113,6 +130,10 @@ export function fontless(_options?: FontlessOptions): Plugin {
const s = await transformCSS(cssTransformOptions, code, id)
if (s.hasChanged()) {
+ // invalidate virtual module to ensure fresh preloads list during dev
+ if (server) {
+ invalidateModuleByid(server, `\0${RUNTIME_NAME}`)
+ }
return {
code: s.toString(),
map: s.generateMap({ hires: true }),
@@ -143,17 +164,79 @@ export function fontless(_options?: FontlessOptions): Plugin {
handler() {
// Preload doesn't work on initial rendering during dev since `fontsToPreload`
// is empty before css is transformed.
- const hrefs = [...cssTransformOptions.fontsToPreload.values()].flatMap(v => [...v])
- return hrefs.map(href => ({
+ return getPreloads().map(attrs => ({
tag: 'link',
- attrs: {
- rel: 'preload',
- as: 'font',
- href,
- crossorigin: '',
- },
+ attrs: attrs as unknown as Record,
}))
},
},
}
+
+ const RUNTIME_PLACEHOLDER = '__FONTLESS_RUNTIME_BUILD_PLACEHOLDER__'
+ const runtimePlugin: Plugin = {
+ name: 'fontless-runtime',
+ config() {
+ return {
+ ssr: {
+ // ensure 'fontless/runtime' is loaded through vite
+ noExternal: ['fontless'],
+ },
+ }
+ },
+ configEnvironment() {
+ return {
+ resolve: {
+ noExternal: ['fontless'],
+ },
+ }
+ },
+ resolveId: {
+ // override Vite's node resolution
+ order: 'pre',
+ handler(source) {
+ if (source === RUNTIME_NAME) {
+ return `\0${RUNTIME_NAME}`
+ }
+ },
+ },
+ load: {
+ handler(id) {
+ if (id === `\0${RUNTIME_NAME}`) {
+ // during build, postpone replacement until `renderChunk`
+ // to ensure fonts are collected through css transform
+ if (resolvedConfig.command === 'build') {
+ return `export const { preloads } = ${RUNTIME_PLACEHOLDER}`
+ }
+ return `export const { preloads } = ${JSON.stringify({ preloads: getPreloads() })}`
+ }
+ },
+ },
+ renderChunk(code, _chunk) {
+ if (code.includes(RUNTIME_PLACEHOLDER)) {
+ const s = new MagicString(code)
+ s.replaceAll(
+ RUNTIME_PLACEHOLDER,
+ JSON.stringify({ preloads: getPreloads() }),
+ )
+ return {
+ code: s.toString(),
+ map: s.generateMap({ hires: 'boundary' }),
+ }
+ }
+ },
+ }
+
+ return [
+ mainPlugin,
+ runtimePlugin,
+ ]
+}
+
+function invalidateModuleByid(server: ViteDevServer, id: string) {
+ for (const environment of Object.values(server.environments)) {
+ const mod = environment.moduleGraph.getModuleById(id)
+ if (mod) {
+ environment.moduleGraph.invalidateModule(mod)
+ }
+ }
}
diff --git a/packages/fontless/test/e2e.spec.ts b/packages/fontless/test/e2e.spec.ts
index 5c7ec3fe..b26857f5 100644
--- a/packages/fontless/test/e2e.spec.ts
+++ b/packages/fontless/test/e2e.spec.ts
@@ -6,10 +6,13 @@ import { join, resolve } from 'pathe'
import { build } from 'vite'
import { describe, expect, it } from 'vitest'
-const fixtures = await Array.fromAsync(fsp.glob('*', {
+let fixtures = await Array.fromAsync(fsp.glob('*', {
cwd: fileURLToPath(new URL('../examples', import.meta.url)),
}))
+// tested via playwright
+fixtures = fixtures.filter(i => !['react-router-app', 'sveltekit-app'].includes(i))
+
describe.each(fixtures)('e2e %s', (fixture) => {
it('should compile', { timeout: 20_000 }, async () => {
const root = fileURLToPath(new URL(`../examples/${fixture}`, import.meta.url))
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 62139fda..bc7e6d96 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -452,28 +452,31 @@ importers:
specifier: ^2.11.8
version: 2.11.8(solid-js@1.9.9)(vite@7.1.7(@types/node@22.18.7)(jiti@2.6.0)(less@4.2.2)(lightningcss@1.30.2)(sass@1.85.0)(terser@5.44.0)(yaml@2.8.1))
- packages/fontless/examples/svelte-app:
+ packages/fontless/examples/sveltekit-app:
devDependencies:
+ '@sveltejs/adapter-auto':
+ specifier: ^6.0.0
+ version: 6.1.0(@sveltejs/kit@2.43.5(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.39.5)(vite@7.1.7(@types/node@22.18.7)(jiti@2.6.0)(less@4.2.2)(lightningcss@1.30.2)(sass@1.85.0)(terser@5.44.0)(yaml@2.8.1)))(svelte@5.39.5)(vite@7.1.7(@types/node@22.18.7)(jiti@2.6.0)(less@4.2.2)(lightningcss@1.30.2)(sass@1.85.0)(terser@5.44.0)(yaml@2.8.1)))
+ '@sveltejs/kit':
+ specifier: ^2.22.0
+ version: 2.43.5(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.39.5)(vite@7.1.7(@types/node@22.18.7)(jiti@2.6.0)(less@4.2.2)(lightningcss@1.30.2)(sass@1.85.0)(terser@5.44.0)(yaml@2.8.1)))(svelte@5.39.5)(vite@7.1.7(@types/node@22.18.7)(jiti@2.6.0)(less@4.2.2)(lightningcss@1.30.2)(sass@1.85.0)(terser@5.44.0)(yaml@2.8.1))
'@sveltejs/vite-plugin-svelte':
- specifier: ^5.1.1
- version: 5.1.1(svelte@5.39.5)(vite@7.1.7(@types/node@22.18.7)(jiti@2.6.0)(less@4.2.2)(lightningcss@1.30.2)(sass@1.85.0)(terser@5.44.0)(yaml@2.8.1))
- '@tsconfig/svelte':
- specifier: ^5.0.5
- version: 5.0.5
+ specifier: ^6.0.0
+ version: 6.2.1(svelte@5.39.5)(vite@7.1.7(@types/node@22.18.7)(jiti@2.6.0)(less@4.2.2)(lightningcss@1.30.2)(sass@1.85.0)(terser@5.44.0)(yaml@2.8.1))
fontless:
specifier: workspace:*
version: link:../..
svelte:
- specifier: ^5.39.5
+ specifier: ^5.0.0
version: 5.39.5
svelte-check:
- specifier: ^4.3.2
+ specifier: ^4.0.0
version: 4.3.2(picomatch@4.0.3)(svelte@5.39.5)(typescript@5.9.2)
typescript:
- specifier: ~5.9.2
+ specifier: ^5.0.0
version: 5.9.2
vite:
- specifier: ^7.1.7
+ specifier: ^7.0.4
version: 7.1.7(@types/node@22.18.7)(jiti@2.6.0)(less@4.2.2)(lightningcss@1.30.2)(sass@1.85.0)(terser@5.44.0)(yaml@2.8.1)
packages/fontless/examples/tailwind:
@@ -4214,6 +4217,9 @@ packages:
'@speed-highlight/core@1.2.7':
resolution: {integrity: sha512-0dxmVj4gxg3Jg879kvFS/msl4s9F3T9UXC1InxgOf7t5NvcPD97u/WTA5vL/IxWHMn7qSxBozqrnnE2wvl1m8g==}
+ '@standard-schema/spec@1.0.0':
+ resolution: {integrity: sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==}
+
'@stylistic/eslint-plugin@5.4.0':
resolution: {integrity: sha512-UG8hdElzuBDzIbjG1QDwnYH0MQ73YLXDFHgZzB4Zh/YJfnw8XNsloVtytqzx0I2Qky9THSdpTmi8Vjn/pf/Lew==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
@@ -4225,20 +4231,38 @@ packages:
peerDependencies:
acorn: ^8.9.0
- '@sveltejs/vite-plugin-svelte-inspector@4.0.1':
- resolution: {integrity: sha512-J/Nmb2Q2y7mck2hyCX4ckVHcR5tu2J+MtBEQqpDrrgELZ2uvraQcK/ioCV61AqkdXFgriksOKIceDcQmqnGhVw==}
- engines: {node: ^18.0.0 || ^20.0.0 || >=22}
+ '@sveltejs/adapter-auto@6.1.0':
+ resolution: {integrity: sha512-shOuLI5D2s+0zTv2ab5M5PqfknXqWbKi+0UwB9yLTRIdzsK1R93JOO8jNhIYSHdW+IYXIYnLniu+JZqXs7h9Wg==}
+ peerDependencies:
+ '@sveltejs/kit': ^2.0.0
+
+ '@sveltejs/kit@2.43.5':
+ resolution: {integrity: sha512-44Mm5csR4mesKx2Eyhtk8UVrLJ4c04BT2wMTfYGKJMOkUqpHP5KLL2DPV0hXUA4t4+T3ZYe0aBygd42lVYv2cA==}
+ engines: {node: '>=18.13'}
+ hasBin: true
peerDependencies:
- '@sveltejs/vite-plugin-svelte': ^5.0.0
+ '@opentelemetry/api': ^1.0.0
+ '@sveltejs/vite-plugin-svelte': ^3.0.0 || ^4.0.0-next.1 || ^5.0.0 || ^6.0.0-next.0
+ svelte: ^4.0.0 || ^5.0.0-next.0
+ vite: ^5.0.3 || ^6.0.0 || ^7.0.0-beta.0
+ peerDependenciesMeta:
+ '@opentelemetry/api':
+ optional: true
+
+ '@sveltejs/vite-plugin-svelte-inspector@5.0.1':
+ resolution: {integrity: sha512-ubWshlMk4bc8mkwWbg6vNvCeT7lGQojE3ijDh3QTR6Zr/R+GXxsGbyH4PExEPpiFmqPhYiVSVmHBjUcVc1JIrA==}
+ engines: {node: ^20.19 || ^22.12 || >=24}
+ peerDependencies:
+ '@sveltejs/vite-plugin-svelte': ^6.0.0-next.0
svelte: ^5.0.0
- vite: ^6.0.0
+ vite: ^6.3.0 || ^7.0.0
- '@sveltejs/vite-plugin-svelte@5.1.1':
- resolution: {integrity: sha512-Y1Cs7hhTc+a5E9Va/xwKlAJoariQyHY+5zBgCZg4PFWNYQ1nMN9sjK1zhw1gK69DuqVP++sht/1GZg1aRwmAXQ==}
- engines: {node: ^18.0.0 || ^20.0.0 || >=22}
+ '@sveltejs/vite-plugin-svelte@6.2.1':
+ resolution: {integrity: sha512-YZs/OSKOQAQCnJvM/P+F1URotNnYNeU3P2s4oIpzm1uFaqUEqRxUB0g5ejMjEb5Gjb9/PiBI5Ktrq4rUUF8UVQ==}
+ engines: {node: ^20.19 || ^22.12 || >=24}
peerDependencies:
svelte: ^5.0.0
- vite: ^6.0.0
+ vite: ^6.3.0 || ^7.0.0
'@swc/core-darwin-arm64@1.13.5':
resolution: {integrity: sha512-lKNv7SujeXvKn16gvQqUQI5DdyY8v7xcoO3k06/FJbHJS90zEwZdQiMNRiqpYw/orU543tPaWgz7cIYWhbopiQ==}
@@ -4427,9 +4451,6 @@ packages:
'@ts-morph/common@0.22.0':
resolution: {integrity: sha512-HqNBuV/oIlMKdkLshXd1zKBqNQCsuPEsgQOkfFQ/eUKjRlwndXW1AjN9LVkBEIukm00gGXSRmfkl0Wv5VXLnlw==}
- '@tsconfig/svelte@5.0.5':
- resolution: {integrity: sha512-48fAnUjKye38FvMiNOj0J9I/4XlQQiZlpe9xaNPfe8vy2Y1hFBt8g1yqf2EGjVvHavo4jf2lC+TQyENCr4BJBQ==}
-
'@tufjs/canonical-json@2.0.0':
resolution: {integrity: sha512-yVtV8zsdo8qFHe+/3kw81dSLyF7D576A5cCFCi4X7B39tWT7SekaEFUnvnWJHz+9qO7qJTah1JbrDjWKqFtdWA==}
engines: {node: ^16.14.0 || >=18.0.0}
@@ -4468,6 +4489,9 @@ packages:
'@types/connect@3.4.38':
resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==}
+ '@types/cookie@0.6.0':
+ resolution: {integrity: sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==}
+
'@types/css-tree@2.3.10':
resolution: {integrity: sha512-WcaBazJ84RxABvRttQjjFWgTcHvZR9jGr0Y3hccPkHjFyk/a3N8EuxjKr+QfrwjoM5b1yI1Uj1i7EzOAAwBwag==}
@@ -5893,6 +5917,10 @@ packages:
cookie-signature@1.0.6:
resolution: {integrity: sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==}
+ cookie@0.6.0:
+ resolution: {integrity: sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==}
+ engines: {node: '>= 0.6'}
+
cookie@0.7.1:
resolution: {integrity: sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==}
engines: {node: '>= 0.6'}
@@ -15661,6 +15689,8 @@ snapshots:
'@speed-highlight/core@1.2.7': {}
+ '@standard-schema/spec@1.0.0': {}
+
'@stylistic/eslint-plugin@5.4.0(eslint@9.36.0(jiti@2.6.0))':
dependencies:
'@eslint-community/eslint-utils': 4.9.0(eslint@9.36.0(jiti@2.6.0))
@@ -15675,21 +15705,43 @@ snapshots:
dependencies:
acorn: 8.15.0
- '@sveltejs/vite-plugin-svelte-inspector@4.0.1(@sveltejs/vite-plugin-svelte@5.1.1(svelte@5.39.5)(vite@7.1.7(@types/node@22.18.7)(jiti@2.6.0)(less@4.2.2)(lightningcss@1.30.2)(sass@1.85.0)(terser@5.44.0)(yaml@2.8.1)))(svelte@5.39.5)(vite@7.1.7(@types/node@22.18.7)(jiti@2.6.0)(less@4.2.2)(lightningcss@1.30.2)(sass@1.85.0)(terser@5.44.0)(yaml@2.8.1))':
+ '@sveltejs/adapter-auto@6.1.0(@sveltejs/kit@2.43.5(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.39.5)(vite@7.1.7(@types/node@22.18.7)(jiti@2.6.0)(less@4.2.2)(lightningcss@1.30.2)(sass@1.85.0)(terser@5.44.0)(yaml@2.8.1)))(svelte@5.39.5)(vite@7.1.7(@types/node@22.18.7)(jiti@2.6.0)(less@4.2.2)(lightningcss@1.30.2)(sass@1.85.0)(terser@5.44.0)(yaml@2.8.1)))':
+ dependencies:
+ '@sveltejs/kit': 2.43.5(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.39.5)(vite@7.1.7(@types/node@22.18.7)(jiti@2.6.0)(less@4.2.2)(lightningcss@1.30.2)(sass@1.85.0)(terser@5.44.0)(yaml@2.8.1)))(svelte@5.39.5)(vite@7.1.7(@types/node@22.18.7)(jiti@2.6.0)(less@4.2.2)(lightningcss@1.30.2)(sass@1.85.0)(terser@5.44.0)(yaml@2.8.1))
+
+ '@sveltejs/kit@2.43.5(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.39.5)(vite@7.1.7(@types/node@22.18.7)(jiti@2.6.0)(less@4.2.2)(lightningcss@1.30.2)(sass@1.85.0)(terser@5.44.0)(yaml@2.8.1)))(svelte@5.39.5)(vite@7.1.7(@types/node@22.18.7)(jiti@2.6.0)(less@4.2.2)(lightningcss@1.30.2)(sass@1.85.0)(terser@5.44.0)(yaml@2.8.1))':
dependencies:
- '@sveltejs/vite-plugin-svelte': 5.1.1(svelte@5.39.5)(vite@7.1.7(@types/node@22.18.7)(jiti@2.6.0)(less@4.2.2)(lightningcss@1.30.2)(sass@1.85.0)(terser@5.44.0)(yaml@2.8.1))
+ '@standard-schema/spec': 1.0.0
+ '@sveltejs/acorn-typescript': 1.0.5(acorn@8.15.0)
+ '@sveltejs/vite-plugin-svelte': 6.2.1(svelte@5.39.5)(vite@7.1.7(@types/node@22.18.7)(jiti@2.6.0)(less@4.2.2)(lightningcss@1.30.2)(sass@1.85.0)(terser@5.44.0)(yaml@2.8.1))
+ '@types/cookie': 0.6.0
+ acorn: 8.15.0
+ cookie: 0.6.0
+ devalue: 5.3.2
+ esm-env: 1.2.2
+ kleur: 4.1.5
+ magic-string: 0.30.19
+ mrmime: 2.0.1
+ sade: 1.8.1
+ set-cookie-parser: 2.7.1
+ sirv: 3.0.2
+ svelte: 5.39.5
+ vite: 7.1.7(@types/node@22.18.7)(jiti@2.6.0)(less@4.2.2)(lightningcss@1.30.2)(sass@1.85.0)(terser@5.44.0)(yaml@2.8.1)
+
+ '@sveltejs/vite-plugin-svelte-inspector@5.0.1(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.39.5)(vite@7.1.7(@types/node@22.18.7)(jiti@2.6.0)(less@4.2.2)(lightningcss@1.30.2)(sass@1.85.0)(terser@5.44.0)(yaml@2.8.1)))(svelte@5.39.5)(vite@7.1.7(@types/node@22.18.7)(jiti@2.6.0)(less@4.2.2)(lightningcss@1.30.2)(sass@1.85.0)(terser@5.44.0)(yaml@2.8.1))':
+ dependencies:
+ '@sveltejs/vite-plugin-svelte': 6.2.1(svelte@5.39.5)(vite@7.1.7(@types/node@22.18.7)(jiti@2.6.0)(less@4.2.2)(lightningcss@1.30.2)(sass@1.85.0)(terser@5.44.0)(yaml@2.8.1))
debug: 4.4.3
svelte: 5.39.5
vite: 7.1.7(@types/node@22.18.7)(jiti@2.6.0)(less@4.2.2)(lightningcss@1.30.2)(sass@1.85.0)(terser@5.44.0)(yaml@2.8.1)
transitivePeerDependencies:
- supports-color
- '@sveltejs/vite-plugin-svelte@5.1.1(svelte@5.39.5)(vite@7.1.7(@types/node@22.18.7)(jiti@2.6.0)(less@4.2.2)(lightningcss@1.30.2)(sass@1.85.0)(terser@5.44.0)(yaml@2.8.1))':
+ '@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.39.5)(vite@7.1.7(@types/node@22.18.7)(jiti@2.6.0)(less@4.2.2)(lightningcss@1.30.2)(sass@1.85.0)(terser@5.44.0)(yaml@2.8.1))':
dependencies:
- '@sveltejs/vite-plugin-svelte-inspector': 4.0.1(@sveltejs/vite-plugin-svelte@5.1.1(svelte@5.39.5)(vite@7.1.7(@types/node@22.18.7)(jiti@2.6.0)(less@4.2.2)(lightningcss@1.30.2)(sass@1.85.0)(terser@5.44.0)(yaml@2.8.1)))(svelte@5.39.5)(vite@7.1.7(@types/node@22.18.7)(jiti@2.6.0)(less@4.2.2)(lightningcss@1.30.2)(sass@1.85.0)(terser@5.44.0)(yaml@2.8.1))
+ '@sveltejs/vite-plugin-svelte-inspector': 5.0.1(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.39.5)(vite@7.1.7(@types/node@22.18.7)(jiti@2.6.0)(less@4.2.2)(lightningcss@1.30.2)(sass@1.85.0)(terser@5.44.0)(yaml@2.8.1)))(svelte@5.39.5)(vite@7.1.7(@types/node@22.18.7)(jiti@2.6.0)(less@4.2.2)(lightningcss@1.30.2)(sass@1.85.0)(terser@5.44.0)(yaml@2.8.1))
debug: 4.4.3
deepmerge: 4.3.1
- kleur: 4.1.5
magic-string: 0.30.19
svelte: 5.39.5
vite: 7.1.7(@types/node@22.18.7)(jiti@2.6.0)(less@4.2.2)(lightningcss@1.30.2)(sass@1.85.0)(terser@5.44.0)(yaml@2.8.1)
@@ -15843,8 +15895,6 @@ snapshots:
mkdirp: 3.0.1
path-browserify: 1.0.1
- '@tsconfig/svelte@5.0.5': {}
-
'@tufjs/canonical-json@2.0.0': {}
'@tufjs/models@3.0.1':
@@ -15900,6 +15950,8 @@ snapshots:
dependencies:
'@types/node': 22.18.7
+ '@types/cookie@0.6.0': {}
+
'@types/css-tree@2.3.10': {}
'@types/debug@4.1.12':
@@ -17766,6 +17818,8 @@ snapshots:
cookie-signature@1.0.6: {}
+ cookie@0.6.0: {}
+
cookie@0.7.1: {}
cookie@1.0.2: {}