├── example
├── svg.d.ts
├── .dockerignore
├── src
│ ├── main.css
│ ├── assets
│ │ ├── SPA.svg
│ │ ├── SPAwBR.svg
│ │ └── SSR.svg
│ ├── Example.svelte
│ ├── Index.svelte
│ ├── Landing.svelte
│ └── Documentation.svelte
├── Dockerfile.dev
├── Caddyfile
├── Dockerfile
├── tsconfig.json
├── package.json
└── static
│ └── favicon.svg
├── .gitignore
├── default
├── index.ts
└── index.html
├── src
├── index.ts
├── types.d.ts
├── strip.ts
├── cli.ts
├── rsbuild.config.ts
├── router.ts
├── Router.svelte
└── ssr.ts
├── static
├── SPA.svg
├── SPAwBR.svg
└── SSR.svg
├── tsconfig.json
├── LICENSE
├── package.json
├── docs
├── favicon.svg
├── example.html
├── index.html
├── css
│ └── index.e62340e7.css
├── js
│ └── index.22425872.js
└── doc.html
├── release.sh
└── README.md
/example/svg.d.ts:
--------------------------------------------------------------------------------
1 | declare module '*.svg' {
2 | const content: string;
3 | export default content;
4 | }
--------------------------------------------------------------------------------
/example/.dockerignore:
--------------------------------------------------------------------------------
1 | # All dot files in root
2 | /.*
3 |
4 | dist
5 | docs
6 | node_modules
7 |
8 | Dockerfile*
9 | LICENSE
10 | README.md
--------------------------------------------------------------------------------
/example/src/main.css:
--------------------------------------------------------------------------------
1 | body,
2 | html {
3 | font-family: "Roboto", sans-serif;
4 | margin: 0;
5 | padding: 0;
6 | height: 100%;
7 | font-weight: 300;
8 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Local
2 | .DS_Store
3 | *.local
4 | *.log*
5 |
6 | # Dist
7 | node_modules
8 | dist
9 |
10 | # IDE
11 | .vscode/*
12 | !.vscode/extensions.json
13 | .idea
14 |
--------------------------------------------------------------------------------
/default/index.ts:
--------------------------------------------------------------------------------
1 | import { hydrate } from "svelte";
2 | import Index from "./Index.svelte";
3 |
4 | hydrate(Index, {
5 | target: document.getElementById("root")!,
6 | props: {},
7 | });
8 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | export { navigate, link, setupStaticRoutes } from './router.js';
2 | export type { Routes, Component } from './types.js';
3 | export { default as Router } from './Router.svelte';
--------------------------------------------------------------------------------
/default/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | PrevelteKit - default index.html
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/example/Dockerfile.dev:
--------------------------------------------------------------------------------
1 | FROM node:24-alpine AS base
2 | RUN apk add --no-cache pnpm
3 | WORKDIR /app
4 | COPY package.json tsconfig.json ./
5 | RUN pnpm install
6 | ENTRYPOINT ["pnpm", "run", "dev"]
7 |
8 | # run with:
9 | # docker build -f Dockerfile.dev . -t preveltekit-dev
10 | # docker run -p3000:3000 -v./static:/app/static -v./src:/app/src -v./public:/app/public preveltekit-dev
--------------------------------------------------------------------------------
/example/Caddyfile:
--------------------------------------------------------------------------------
1 | {
2 | admin off
3 | auto_https off
4 | }
5 |
6 | #https://caddyserver.com/docs/caddyfile/patterns#single-page-apps-spas
7 | :3000 {
8 | root * /var/www/html
9 | try_files {path} {path}.html /index.html
10 | file_server {
11 | #https://caddyserver.com/docs/caddyfile/directives/file_server#precompressed
12 | precompressed br zstd gzip
13 | }
14 | }
--------------------------------------------------------------------------------
/example/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM node:24-alpine AS base
2 | RUN apk add --no-cache brotli zopfli zstd pnpm
3 | WORKDIR /app
4 | COPY pnpm-lock.yaml package.json tsconfig.json ./
5 | RUN pnpm install --frozen-lockfile
6 | COPY src ./src
7 | RUN pnpm build
8 |
9 | FROM caddy:2-alpine
10 | COPY Caddyfile /etc/caddy/Caddyfile
11 | COPY --from=base /app/dist/ /var/www/html
12 |
13 | # run with:
14 | # docker build . -t preveltekit
15 | # docker run -p3000:3000 preveltekit
16 |
--------------------------------------------------------------------------------
/example/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ES2020",
4 | "module": "ESNext",
5 | "moduleResolution": "bundler",
6 | // svelte-preprocess cannot figure out whether you have a value or a type, so tell TypeScript
7 | // to enforce using `import type` instead of `import` for Types.
8 | "verbatimModuleSyntax": true,
9 | "noEmit": true,
10 | "skipLibCheck": true,
11 | "types": ["svelte"]
12 | },
13 | "include": ["./src/*", "svg.d.ts"],
14 | "exclude": ["node_modules"]
15 | }
--------------------------------------------------------------------------------
/example/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "preveltekit-example",
3 | "version": "1.0.0",
4 | "type": "module",
5 | "scripts": {
6 | "dev": "preveltekit dev",
7 | "build": "NODE_ENV=production preveltekit prod",
8 | "build-no-zip": "NODE_ENV=production preveltekit prod --no-zip",
9 | "stage": "NODE_ENV=production preveltekit stage"
10 | },
11 | "dependencies": {
12 | "svelte": "^5.39.11"
13 | },
14 | "devDependencies": {
15 | "preveltekit": "file:../",
16 | "typescript": "^5.9.3"
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/types.d.ts:
--------------------------------------------------------------------------------
1 | import type { Component as SvelteComponent } from 'svelte';
2 | export type Component = SvelteComponent | null;
3 |
4 | export interface Routes {
5 | dynamicRoutes?: { path: string; component: Component }[];
6 | staticRoutes?: { path: string; htmlFilename: string }[];
7 | }
8 |
9 | // Extend HTMLScriptElement to include readyState for JSDOM compatibility
10 | declare global {
11 | // Extend Window interface for JSDOM and Svelte routes
12 | interface Window {
13 | __isBuildTime?: boolean;
14 | __svelteRoutes?: Routes;
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/static/SPA.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/example/src/assets/SPA.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "lib": ["DOM", "ES2022"],
4 | "target": "ES2022",
5 | "skipLibCheck": true,
6 | // svelte-preprocess cannot figure out whether you have a value or a type, so tell TypeScript
7 | // to enforce using `import type` instead of `import` for Types.
8 | "verbatimModuleSyntax": true,
9 | "useDefineForClassFields": true,
10 |
11 | /* modules */
12 | "module": "ESNext",
13 | "isolatedModules": true,
14 | "resolveJsonModule": true,
15 | "moduleResolution": "Bundler",
16 |
17 | /* type checking */
18 | "strict": true,
19 | "noUnusedLocals": true,
20 | "noUnusedParameters": true,
21 |
22 | "declaration": true,
23 | "outDir": "dist"
24 | },
25 | "include": ["./src/*"],
26 | "exclude": ["node_modules", "./dist/**", "./default/**", "./src/strip.ts"],
27 | }
28 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 Thomas Bocek
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "preveltekit",
3 | "version": "1.2.23",
4 | "description": "PrevelteKit is a lightweight, high-performance web application framework built on Svelte 5, featuring Server-Side Pre Rendering (SSPR) using Rsbuild and jsdom",
5 | "repository": "https://github.com/tbocek/preveltekit",
6 | "homepage": "https://tbocek.github.io/preveltekit",
7 | "author": "Thomas Bocek",
8 | "license": "MIT",
9 | "type": "module",
10 | "exports": {
11 | ".": {
12 | "types": "./dist/index.d.ts",
13 | "import": "./dist/index.js",
14 | "default": "./dist/index.js"
15 | }
16 | },
17 | "bin": {
18 | "preveltekit": "dist/cli.js"
19 | },
20 | "files": [
21 | "dist"
22 | ],
23 | "scripts": {
24 | "build": "rm -rf dist && mkdir -p dist/default && tsx src/strip.ts -i src/Router.svelte -o dist/Router.svelte && tsc && cp src/types.d.ts dist/ && cp -r default dist/",
25 | "prepublishOnly": "pnpm run build",
26 | "docs": "pnpm run build && rm -rf docs/* && cd example && pnpm install && pnpm run build-no-zip && cd dist && cp -r . ../../docs/",
27 | "release": "pnpm publish --access public"
28 | },
29 | "peerDependencies": {
30 | "svelte": "^5.0.0"
31 | },
32 | "dependencies": {
33 | "@rsbuild/core": "^1.6.7",
34 | "@rsbuild/plugin-svelte": "^1.0.11",
35 | "@rspack/core": "^1.6.4",
36 | "express": "^5.1.0",
37 | "jsdom": "^27.2.0"
38 | },
39 | "devDependencies": {
40 | "@types/express": "^5.0.5",
41 | "@types/jsdom": "^27.0.0",
42 | "@types/node": "^24.10.1",
43 | "typescript": "^5.9.3",
44 | "svelte-preprocess": "^6.0.3",
45 | "tsx": "^4.20.6"
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/docs/favicon.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/static/SPAwBR.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/example/src/assets/SPAwBR.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/example/static/favicon.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/strip.ts:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 | import { preprocess } from 'svelte/compiler';
3 | import { sveltePreprocess } from 'svelte-preprocess';
4 | import { readFileSync, writeFileSync } from 'fs';
5 | import { resolve } from 'path';
6 |
7 | // Parse command line arguments
8 | const args: string[] = process.argv.slice(2);
9 |
10 | const getArg = (flag: string): string | null => {
11 | const index = args.indexOf(flag);
12 | return index !== -1 && args[index + 1] ? args[index + 1] : null;
13 | };
14 |
15 | const inputFile: string | null = getArg('--input') || getArg('-i');
16 | const outputFile: string | null = getArg('--output') || getArg('-o');
17 | const showHelp: boolean = args.includes('--help') || args.includes('-h');
18 |
19 | // Show help message
20 | if (showHelp || !inputFile || !outputFile) {
21 | console.log(`
22 | Usage: node compileRouter.js --input --output
23 |
24 | Options:
25 | -i, --input Input TypeScript Svelte file
26 | -o, --output Output JavaScript Svelte file
27 | -h, --help Show this help message
28 |
29 | Example:
30 | node compileRouter.js --input src/Router.svelte --output dist/Router.svelte
31 | node compileRouter.js -i src/Router.svelte -o dist/Router.svelte
32 | `);
33 | process.exit(showHelp ? 0 : 1);
34 | }
35 |
36 | try {
37 | // Read input file
38 | const inputPath: string = resolve(inputFile);
39 | console.log(`Reading: ${inputPath}`);
40 | const source: string = readFileSync(inputPath, 'utf8');
41 |
42 | // Preprocess TypeScript to JavaScript
43 | console.log('Compiling TypeScript...');
44 | const preprocessed = await preprocess(
45 | source,
46 | sveltePreprocess({
47 | typescript: {
48 | tsconfigFile: './tsconfig.json'
49 | }
50 | }),
51 | { filename: inputFile }
52 | );
53 |
54 | // Remove lang="ts" from script tag
55 | let output: string = preprocessed.code.replace(/
5 |
6 | Bitcoin Price Tracker (Server Pre-Rendered)
7 |
8 |