├── demo ├── .npmrc ├── static │ ├── robots.txt │ └── favicon.png ├── src │ ├── lib │ │ └── images │ │ │ ├── svelte-welcome.png │ │ │ ├── svelte-welcome.webp │ │ │ ├── github.svg │ │ │ └── svelte-logo.svg │ ├── routes │ │ ├── +page.js │ │ ├── about │ │ │ ├── +page.js │ │ │ └── +page.svelte │ │ ├── sverdle │ │ │ ├── how-to-play │ │ │ │ ├── +page.js │ │ │ │ └── +page.svelte │ │ │ ├── reduced-motion.js │ │ │ ├── +page.server.js │ │ │ ├── game.js │ │ │ └── +page.svelte │ │ ├── +layout.svelte │ │ ├── +page.svelte │ │ ├── Counter.svelte │ │ └── Header.svelte │ ├── app.d.ts │ ├── app.html │ └── app.css ├── vite.config.js ├── .gitignore ├── svelte.config.js ├── jsconfig.json ├── package.json └── README.md ├── files ├── server.js └── mod.ts ├── .gitignore ├── index.d.ts ├── .github └── workflows │ └── ci.yml ├── package.json ├── LICENSE ├── index.js └── README.md /demo/.npmrc: -------------------------------------------------------------------------------- 1 | engine-strict=true 2 | -------------------------------------------------------------------------------- /demo/static/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /demo/static/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dbushell/sveltekit-adapter-deno/HEAD/demo/static/favicon.png -------------------------------------------------------------------------------- /demo/src/lib/images/svelte-welcome.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dbushell/sveltekit-adapter-deno/HEAD/demo/src/lib/images/svelte-welcome.png -------------------------------------------------------------------------------- /demo/src/lib/images/svelte-welcome.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dbushell/sveltekit-adapter-deno/HEAD/demo/src/lib/images/svelte-welcome.webp -------------------------------------------------------------------------------- /demo/src/routes/+page.js: -------------------------------------------------------------------------------- 1 | // since there's no dynamic data here, we can prerender 2 | // it so that it gets served as a static asset in production 3 | export const prerender = true; 4 | -------------------------------------------------------------------------------- /demo/vite.config.js: -------------------------------------------------------------------------------- 1 | import { sveltekit } from "@sveltejs/kit/vite"; 2 | import { defineConfig } from "vite"; 3 | 4 | export default defineConfig({ 5 | plugins: [sveltekit()], 6 | }); 7 | -------------------------------------------------------------------------------- /files/server.js: -------------------------------------------------------------------------------- 1 | import { Server } from "./server/index.js"; 2 | import { manifest } from "./server/manifest.js"; 3 | 4 | const server = new Server(manifest); 5 | 6 | export default server; 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /* 2 | !.github 3 | !.gitignore 4 | !index.d.ts 5 | !index.js 6 | !package.json 7 | !README.md 8 | !LICENSE 9 | !files 10 | files/* 11 | !files/*.json 12 | !files/*.js 13 | !files/*.ts 14 | !demo 15 | -------------------------------------------------------------------------------- /demo/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | 3 | # Output 4 | .output 5 | .vercel 6 | /.svelte-kit 7 | /build 8 | 9 | # OS 10 | .DS_Store 11 | Thumbs.db 12 | 13 | # Env 14 | .env 15 | .env.* 16 | !.env.example 17 | !.env.test 18 | 19 | # Vite 20 | vite.config.js.timestamp-* 21 | vite.config.ts.timestamp-* 22 | -------------------------------------------------------------------------------- /index.d.ts: -------------------------------------------------------------------------------- 1 | import { Adapter } from "@sveltejs/kit"; 2 | import { BuildOptions } from "esbuild"; 3 | 4 | interface AdapterOptions { 5 | out?: string; 6 | buildOptions?: BuildOptions; 7 | usage?: "deno" | "deno-compile"; 8 | } 9 | 10 | export default function plugin(options?: AdapterOptions): Adapter; 11 | -------------------------------------------------------------------------------- /demo/src/app.d.ts: -------------------------------------------------------------------------------- 1 | // See https://svelte.dev/docs/kit/types#app 2 | // for information about these interfaces 3 | declare global { 4 | namespace App { 5 | // interface Error {} 6 | // interface Locals {} 7 | // interface PageData {} 8 | // interface PageState {} 9 | // interface Platform {} 10 | } 11 | } 12 | 13 | export {}; 14 | -------------------------------------------------------------------------------- /demo/src/routes/about/+page.js: -------------------------------------------------------------------------------- 1 | import { dev } from "$app/environment"; 2 | 3 | // we don't need any JS on this page, though we'll load 4 | // it in dev so that we get hot module replacement 5 | export const csr = dev; 6 | 7 | // since there's no dynamic data here, we can prerender 8 | // it so that it gets served as a static asset in production 9 | export const prerender = true; 10 | -------------------------------------------------------------------------------- /demo/src/routes/sverdle/how-to-play/+page.js: -------------------------------------------------------------------------------- 1 | import { dev } from "$app/environment"; 2 | 3 | // we don't need any JS on this page, though we'll load 4 | // it in dev so that we get hot module replacement 5 | export const csr = dev; 6 | 7 | // since there's no dynamic data here, we can prerender 8 | // it so that it gets served as a static asset in production 9 | export const prerender = true; 10 | -------------------------------------------------------------------------------- /demo/src/app.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | %sveltekit.head% 8 | 9 | 10 |
%sveltekit.body%
11 | 12 | 13 | -------------------------------------------------------------------------------- /demo/svelte.config.js: -------------------------------------------------------------------------------- 1 | import adapter from "sveltekit-adapter-deno"; 2 | 3 | /** @type {import('@sveltejs/kit').Config} */ 4 | const config = { 5 | kit: { 6 | // adapter-auto only supports some environments, see https://svelte.dev/docs/kit/adapter-auto for a list. 7 | // If your environment is not supported, or you settled on a specific environment, switch out the adapter. 8 | // See https://svelte.dev/docs/kit/adapters for more information about adapters. 9 | adapter: adapter({ 10 | buildOptions: { 11 | banner: { 12 | js: `/* Build time: ${new Date().toISOString()} */`, 13 | }, 14 | }, 15 | }), 16 | }, 17 | }; 18 | 19 | export default config; 20 | -------------------------------------------------------------------------------- /demo/jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./.svelte-kit/tsconfig.json", 3 | "compilerOptions": { 4 | "allowJs": true, 5 | "checkJs": true, 6 | "esModuleInterop": true, 7 | "forceConsistentCasingInFileNames": true, 8 | "resolveJsonModule": true, 9 | "skipLibCheck": true, 10 | "sourceMap": true, 11 | "strict": true, 12 | "moduleResolution": "bundler" 13 | } 14 | // Path aliases are handled by https://svelte.dev/docs/kit/configuration#alias 15 | // except $lib which is handled by https://svelte.dev/docs/kit/configuration#files 16 | // 17 | // If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes 18 | // from the referenced tsconfig.json - TypeScript does not merge them in 19 | } 20 | -------------------------------------------------------------------------------- /demo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "demo", 3 | "version": "0.0.1", 4 | "type": "module", 5 | "scripts": { 6 | "dev": "vite dev", 7 | "build": "vite build", 8 | "preview": "vite preview", 9 | "check": "svelte-kit sync && svelte-check --tsconfig ./jsconfig.json", 10 | "check:watch": "svelte-kit sync && svelte-check --tsconfig ./jsconfig.json --watch" 11 | }, 12 | "devDependencies": { 13 | "@fontsource/fira-mono": "^5.0.0", 14 | "@neoconfetti/svelte": "^2.0.0", 15 | "@sveltejs/adapter-auto": "^3.0.0", 16 | "@sveltejs/kit": "^2.0.0", 17 | "@sveltejs/vite-plugin-svelte": "^4.0.0", 18 | "svelte": "^5.0.0", 19 | "svelte-check": "^4.0.0", 20 | "sveltekit-adapter-deno": "^0.16.0", 21 | "typescript": "^5.0.0", 22 | "vite": "^5.0.3" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: Deploy 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | jobs: 9 | deploy: 10 | name: Deploy 11 | runs-on: ubuntu-latest 12 | 13 | permissions: 14 | id-token: write 15 | contents: read 16 | 17 | steps: 18 | - name: Clone Repository 19 | uses: actions/checkout@v4 20 | 21 | - name: Install Node 22 | uses: actions/setup-node@v4 23 | with: 24 | node-version: 22.x 25 | 26 | - name: Build 27 | run: npm install --include=dev && npx vite build 28 | working-directory: demo 29 | env: 30 | NODE_ENV: production 31 | 32 | - name: Deploy 33 | uses: denoland/deployctl@v1 34 | with: 35 | project: sveltekit-adapter-deno 36 | entrypoint: mod.ts 37 | root: demo/build 38 | -------------------------------------------------------------------------------- /demo/src/routes/sverdle/reduced-motion.js: -------------------------------------------------------------------------------- 1 | import { readable } from "svelte/store"; 2 | import { browser } from "$app/environment"; 3 | 4 | const reducedMotionQuery = "(prefers-reduced-motion: reduce)"; 5 | 6 | const getInitialMotionPreference = () => { 7 | if (!browser) return false; 8 | return window.matchMedia(reducedMotionQuery).matches; 9 | }; 10 | 11 | export const reducedMotion = readable(getInitialMotionPreference(), (set) => { 12 | if (browser) { 13 | /** 14 | * @param {MediaQueryListEvent} event 15 | */ 16 | const setReducedMotion = (event) => { 17 | set(event.matches); 18 | }; 19 | const mediaQueryList = window.matchMedia(reducedMotionQuery); 20 | mediaQueryList.addEventListener("change", setReducedMotion); 21 | 22 | return () => { 23 | mediaQueryList.removeEventListener("change", setReducedMotion); 24 | }; 25 | } 26 | }); 27 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sveltekit-adapter-deno", 3 | "version": "0.16.1", 4 | "repository": { 5 | "type": "git", 6 | "url": "https://github.com/dbushell/sveltekit-adapter-deno" 7 | }, 8 | "description": "Adapter for SvelteKit apps that generates a standalone Deno or Deno Deploy server", 9 | "license": "MIT", 10 | "type": "module", 11 | "main": "index.js", 12 | "types": "index.d.ts", 13 | "exports": { 14 | ".": { 15 | "types": "./index.d.ts", 16 | "import": "./index.js" 17 | }, 18 | "./package.json": "./package.json" 19 | }, 20 | "files": [ 21 | "files", 22 | "index.js", 23 | "index.d.ts", 24 | "package.json" 25 | ], 26 | "devDependencies": { 27 | "@sveltejs/kit": "^2.0.6" 28 | }, 29 | "peerDependencies": { 30 | "@sveltejs/kit": "^2.0.0" 31 | }, 32 | "dependencies": { 33 | "esbuild": "^0.24.0" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /demo/src/routes/about/+page.svelte: -------------------------------------------------------------------------------- 1 | 2 | About 3 | 4 | 5 | 6 |
7 |

About this app

8 | 9 |

10 | This is a SvelteKit app. You can make your own by typing 11 | the following into your command line and following the prompts: 12 |

13 | 14 |
npx sv create
15 | 16 |

17 | The page you're looking at is purely static HTML, with no client-side interactivity needed. 18 | Because of that, we don't need to load any JavaScript. Try viewing the page's source, or opening 19 | the devtools network panel and reloading. 20 |

21 | 22 |

23 | The Sverdle page illustrates SvelteKit's data loading and form handling. Try 24 | using it with JavaScript disabled! 25 |

26 |
27 | -------------------------------------------------------------------------------- /demo/README.md: -------------------------------------------------------------------------------- 1 | # create-svelte 2 | 3 | Everything you need to build a Svelte project, powered by 4 | [`create-svelte`](https://github.com/sveltejs/kit/tree/main/packages/create-svelte). 5 | 6 | ## Creating a project 7 | 8 | If you're seeing this, you've probably already done this step. Congrats! 9 | 10 | ```bash 11 | # create a new project in the current directory 12 | npx sv create 13 | 14 | # create a new project in my-app 15 | npx sv create my-app 16 | ``` 17 | 18 | ## Developing 19 | 20 | Once you've created a project and installed dependencies with `npm install` (or 21 | `pnpm install` or `yarn`), start a development server: 22 | 23 | ```bash 24 | npm run dev 25 | 26 | # or start the server and open the app in a new browser tab 27 | npm run dev -- --open 28 | ``` 29 | 30 | ## Building 31 | 32 | To create a production version of your app: 33 | 34 | ```bash 35 | npm run build 36 | ``` 37 | 38 | You can preview the production build with `npm run preview`. 39 | 40 | > To deploy your app, you may need to install an 41 | > [adapter](https://svelte.dev/docs/kit/adapters) for your target environment. 42 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 David Bushell 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 | -------------------------------------------------------------------------------- /demo/src/routes/+layout.svelte: -------------------------------------------------------------------------------- 1 | 8 | 9 |
10 |
11 | 12 |
13 | {@render children()} 14 |
15 | 16 | 21 |
22 | 23 | 59 | -------------------------------------------------------------------------------- /demo/src/routes/+page.svelte: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | Home 9 | 10 | 11 | 12 |
13 |

14 | 15 | 16 | 17 | Welcome 18 | 19 | 20 | 21 | to your new
SvelteKit app 22 |

23 | 24 |

25 | try editing src/routes/+page.svelte 26 |

27 | 28 | 29 |
30 | 31 | 60 | -------------------------------------------------------------------------------- /demo/src/lib/images/github.svg: -------------------------------------------------------------------------------- 1 | 2 | 9 | 16 | -------------------------------------------------------------------------------- /demo/src/lib/images/svelte-logo.svg: -------------------------------------------------------------------------------- 1 | svelte-logo -------------------------------------------------------------------------------- /demo/src/routes/sverdle/+page.server.js: -------------------------------------------------------------------------------- 1 | import { fail } from "@sveltejs/kit"; 2 | import { Game } from "./game"; 3 | 4 | /** @satisfies {import('./$types').PageServerLoad} */ 5 | export const load = ({ cookies }) => { 6 | const game = new Game(cookies.get("sverdle")); 7 | 8 | return { 9 | /** 10 | * The player's guessed words so far 11 | */ 12 | guesses: game.guesses, 13 | 14 | /** 15 | * An array of strings like '__x_c' corresponding to the guesses, where 'x' means 16 | * an exact match, and 'c' means a close match (right letter, wrong place) 17 | */ 18 | answers: game.answers, 19 | 20 | /** 21 | * The correct answer, revealed if the game is over 22 | */ 23 | answer: game.answers.length >= 6 ? game.answer : null, 24 | }; 25 | }; 26 | 27 | /** @satisfies {import('./$types').Actions} */ 28 | export const actions = { 29 | /** 30 | * Modify game state in reaction to a keypress. If client-side JavaScript 31 | * is available, this will happen in the browser instead of here 32 | */ 33 | update: async ({ request, cookies }) => { 34 | const game = new Game(cookies.get("sverdle")); 35 | 36 | const data = await request.formData(); 37 | const key = data.get("key"); 38 | 39 | const i = game.answers.length; 40 | 41 | if (key === "backspace") { 42 | game.guesses[i] = game.guesses[i].slice(0, -1); 43 | } else { 44 | game.guesses[i] += key; 45 | } 46 | 47 | cookies.set("sverdle", game.toString(), { path: "/" }); 48 | }, 49 | 50 | /** 51 | * Modify game state in reaction to a guessed word. This logic always runs on 52 | * the server, so that people can't cheat by peeking at the JavaScript 53 | */ 54 | enter: async ({ request, cookies }) => { 55 | const game = new Game(cookies.get("sverdle")); 56 | 57 | const data = await request.formData(); 58 | const guess = /** @type {string[]} */ (data.getAll("guess")); 59 | 60 | if (!game.enter(guess)) { 61 | return fail(400, { badGuess: true }); 62 | } 63 | 64 | cookies.set("sverdle", game.toString(), { path: "/" }); 65 | }, 66 | 67 | restart: async ({ cookies }) => { 68 | cookies.delete("sverdle", { path: "/" }); 69 | }, 70 | }; 71 | -------------------------------------------------------------------------------- /demo/src/routes/sverdle/game.js: -------------------------------------------------------------------------------- 1 | import { allowed, words } from "./words.server"; 2 | 3 | export class Game { 4 | /** 5 | * Create a game object from the player's cookie, or initialise a new game 6 | * @param {string | undefined} serialized 7 | */ 8 | constructor(serialized = undefined) { 9 | if (serialized) { 10 | const [index, guesses, answers] = serialized.split("-"); 11 | 12 | this.index = +index; 13 | this.guesses = guesses ? guesses.split(" ") : []; 14 | this.answers = answers ? answers.split(" ") : []; 15 | } else { 16 | this.index = Math.floor(Math.random() * words.length); 17 | this.guesses = ["", "", "", "", "", ""]; 18 | this.answers = /** @type {string[]} */ ([]); 19 | } 20 | 21 | this.answer = words[this.index]; 22 | } 23 | 24 | /** 25 | * Update game state based on a guess of a five-letter word. Returns 26 | * true if the guess was valid, false otherwise 27 | * @param {string[]} letters 28 | */ 29 | enter(letters) { 30 | const word = letters.join(""); 31 | const valid = allowed.has(word); 32 | 33 | if (!valid) return false; 34 | 35 | this.guesses[this.answers.length] = word; 36 | 37 | const available = Array.from(this.answer); 38 | const answer = Array(5).fill("_"); 39 | 40 | // first, find exact matches 41 | for (let i = 0; i < 5; i += 1) { 42 | if (letters[i] === available[i]) { 43 | answer[i] = "x"; 44 | available[i] = " "; 45 | } 46 | } 47 | 48 | // then find close matches (this has to happen 49 | // in a second step, otherwise an early close 50 | // match can prevent a later exact match) 51 | for (let i = 0; i < 5; i += 1) { 52 | if (answer[i] === "_") { 53 | const index = available.indexOf(letters[i]); 54 | if (index !== -1) { 55 | answer[i] = "c"; 56 | available[index] = " "; 57 | } 58 | } 59 | } 60 | 61 | this.answers.push(answer.join("")); 62 | 63 | return true; 64 | } 65 | 66 | /** 67 | * Serialize game state so it can be set as a cookie 68 | */ 69 | toString() { 70 | return `${this.index}-${this.guesses.join(" ")}-${this.answers.join(" ")}`; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /files/mod.ts: -------------------------------------------------------------------------------- 1 | import { serveDir, serveFile } from "jsr:@std/http@1/file-server"; 2 | import { dirname, extname, fromFileUrl, join } from "jsr:@std/path@1"; 3 | 4 | import server from "SERVER"; 5 | 6 | const initialized = server.init({ env: Deno.env.toObject() }); 7 | 8 | const prerendered: Set = new Set(PRERENDERED); 9 | 10 | const appDir = "APP_DIR"; 11 | const baseDir = dirname(CURRENT_DIRNAME); 12 | const rootDir = join(baseDir, "static"); 13 | 14 | Deno.serve( 15 | { 16 | port: Number.parseInt(Deno.env.get("PORT") ?? "8000"), 17 | hostname: Deno.env.get("HOST") ?? "0.0.0.0", 18 | }, 19 | async (request: Request, info: Deno.ServeHandlerInfo): Promise => { 20 | // Get client IP address 21 | const clientAddress = request.headers.get("x-forwarded-for") ?? 22 | info.remoteAddr.hostname; 23 | 24 | const { pathname } = new URL(request.url); 25 | 26 | // Path has trailing slash 27 | const slashed = pathname.at(-1) === "/"; 28 | 29 | // Handle trailing slash redirects for prerendered routes 30 | const location = slashed ? pathname.slice(0, -1) : `${pathname}/`; 31 | if (prerendered.has(location)) { 32 | return new Response(null, { 33 | status: 308, 34 | statusText: "Permanent Redirect", 35 | headers: { 36 | location, 37 | }, 38 | }); 39 | } 40 | 41 | // Try prerendered route with html extension 42 | if (!slashed && !extname(pathname) && prerendered.has(pathname)) { 43 | const response = await serveFile( 44 | request, 45 | join(rootDir, `${pathname}.html`), 46 | ); 47 | if (response.ok || response.status === 304) { 48 | return response; 49 | } 50 | } 51 | 52 | // Try static files (ignore redirects and errors) 53 | const response = await serveDir(request, { 54 | fsRoot: rootDir, 55 | quiet: true, 56 | }); 57 | if (response.ok || response.status === 304) { 58 | if ( 59 | pathname.startsWith(`/${appDir}/immutable/`) && 60 | response.status === 200 61 | ) { 62 | response.headers.set( 63 | "cache-control", 64 | "public, max-age=31536000, immutable", 65 | ); 66 | } 67 | return response; 68 | } 69 | 70 | // Pass to the SvelteKit server 71 | await initialized; 72 | 73 | return server.respond(request, { 74 | getClientAddress: () => clientAddress, 75 | }); 76 | }, 77 | ); 78 | -------------------------------------------------------------------------------- /demo/src/app.css: -------------------------------------------------------------------------------- 1 | @import "@fontsource/fira-mono"; 2 | 3 | :root { 4 | --font-body: 5 | Arial, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, 6 | Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif; 7 | --font-mono: "Fira Mono", monospace; 8 | --color-bg-0: rgb(202, 216, 228); 9 | --color-bg-1: hsl(209, 36%, 86%); 10 | --color-bg-2: hsl(224, 44%, 95%); 11 | --color-theme-1: #ff3e00; 12 | --color-theme-2: #4075a6; 13 | --color-text: rgba(0, 0, 0, 0.7); 14 | --column-width: 42rem; 15 | --column-margin-top: 4rem; 16 | font-family: var(--font-body); 17 | color: var(--color-text); 18 | } 19 | 20 | body { 21 | min-height: 100vh; 22 | margin: 0; 23 | background-attachment: fixed; 24 | background-color: var(--color-bg-1); 25 | background-size: 100vw 100vh; 26 | background-image: 27 | radial-gradient( 28 | 50% 50% at 50% 50%, 29 | rgba(255, 255, 255, 0.75) 0%, 30 | rgba(255, 255, 255, 0) 100% 31 | ), 32 | 33 | linear-gradient( 34 | 180deg, 35 | var(--color-bg-0) 0%, 36 | var(--color-bg-1) 15%, 37 | var(--color-bg-2) 50% 38 | ); 39 | } 40 | 41 | h1, 42 | h2, 43 | p { 44 | font-weight: 400; 45 | } 46 | 47 | p { 48 | line-height: 1.5; 49 | } 50 | 51 | a { 52 | color: var(--color-theme-1); 53 | text-decoration: none; 54 | } 55 | 56 | a:hover { 57 | text-decoration: underline; 58 | } 59 | 60 | h1 { 61 | font-size: 2rem; 62 | text-align: center; 63 | } 64 | 65 | h2 { 66 | font-size: 1rem; 67 | } 68 | 69 | pre { 70 | font-size: 16px; 71 | font-family: var(--font-mono); 72 | background-color: rgba(255, 255, 255, 0.45); 73 | border-radius: 3px; 74 | box-shadow: 2px 2px 6px rgb(255 255 255 / 25%); 75 | padding: 0.5em; 76 | overflow-x: auto; 77 | color: var(--color-text); 78 | } 79 | 80 | .text-column { 81 | display: flex; 82 | max-width: 48rem; 83 | flex: 0.6; 84 | flex-direction: column; 85 | justify-content: center; 86 | margin: 0 auto; 87 | } 88 | 89 | input, 90 | button { 91 | font-size: inherit; 92 | font-family: inherit; 93 | } 94 | 95 | button:focus:not(:focus-visible) { 96 | outline: none; 97 | } 98 | 99 | @media (min-width: 720px) { 100 | h1 { 101 | font-size: 2.4rem; 102 | } 103 | } 104 | 105 | .visually-hidden { 106 | border: 0; 107 | clip: rect(0 0 0 0); 108 | height: auto; 109 | margin: 0; 110 | overflow: hidden; 111 | padding: 0; 112 | position: absolute; 113 | width: 1px; 114 | white-space: nowrap; 115 | } 116 | -------------------------------------------------------------------------------- /demo/src/routes/sverdle/how-to-play/+page.svelte: -------------------------------------------------------------------------------- 1 | 2 | How to play Sverdle 3 | 4 | 5 | 6 |
7 |

How to play Sverdle

8 | 9 |

10 | Sverdle is a clone of Wordle, the 11 | word guessing game. To play, enter a five-letter English word. For example: 12 |

13 | 14 |
15 | r 16 | i 17 | t 18 | z 19 | y 20 |
21 | 22 |

23 | The y is in the right place. r and 24 | t 25 | are the right letters, but in the wrong place. The other letters are wrong, and can be discarded. 26 | Let's make another guess: 27 |

28 | 29 |
30 | p 31 | a 32 | r 33 | t 34 | y 35 |
36 | 37 |

This time we guessed right! You have six guesses to get the word.

38 | 39 |

40 | Unlike the original Wordle, Sverdle runs on the server instead of in the browser, making it 41 | impossible to cheat. It uses <form> and cookies to submit data, meaning you can 42 | even play with JavaScript disabled! 43 |

44 |
45 | 46 | 96 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | import { fileURLToPath } from "node:url"; 2 | import { build } from "esbuild"; 3 | 4 | const usageOptions = { 5 | deno: "deno", 6 | compile: "deno-compile", 7 | }; 8 | 9 | /** @type {import('.').default} */ 10 | export default function (opts = {}) { 11 | const { out = "build", buildOptions = {}, usage = usageOptions.deno } = opts; 12 | 13 | return { 14 | name: "deno-deploy-adapter", 15 | 16 | async adapt(builder) { 17 | const tmp = builder.getBuildDirectory("deno-deploy-adapter"); 18 | 19 | builder.rimraf(out); 20 | builder.rimraf(tmp); 21 | builder.mkdirp(tmp); 22 | 23 | const outDir = `${out}/static/${builder.config.kit.paths.base}`; 24 | builder.writeClient(outDir); 25 | builder.writePrerendered(outDir); 26 | 27 | builder.writeServer(`${out}/server`); 28 | 29 | const denoPath = fileURLToPath(new URL("./files", import.meta.url).href); 30 | builder.copy(denoPath, `${out}`, {}); 31 | 32 | const modPath = fileURLToPath( 33 | new URL("./files/mod.ts", import.meta.url).href, 34 | ); 35 | builder.copy(modPath, `${out}/mod.ts`, { 36 | replace: { 37 | SERVER: "./server.js", 38 | APP_DIR: builder.getAppPath(), 39 | PRERENDERED: JSON.stringify(builder.prerendered.paths), 40 | CURRENT_DIRNAME: usage === usageOptions.deno 41 | ? "fromFileUrl(import.meta.url)" 42 | : "Deno.execPath()", 43 | }, 44 | }); 45 | 46 | const defaultOptions = { 47 | entryPoints: [`${out}/server.js`], 48 | outfile: `${out}/server.js`, 49 | bundle: true, 50 | format: "esm", 51 | target: "esnext", 52 | platform: "node", 53 | allowOverwrite: true, 54 | }; 55 | 56 | for (const key of Object.keys(buildOptions)) { 57 | if (Object.hasOwn(defaultOptions, key)) { 58 | console.warn( 59 | `Warning: "buildOptions" has override for default "${key}" this may break deployment.`, 60 | ); 61 | } 62 | } 63 | 64 | try { 65 | await build({ 66 | ...defaultOptions, 67 | ...buildOptions, 68 | }); 69 | } catch (err) { 70 | console.error(err); 71 | process.exit(1); 72 | } finally { 73 | builder.rimraf(`${out}/server`); 74 | console.warn(` 75 | PLEASE NOTE: This adapter is no longer maintained. 76 | Deno is now compatible with the official Node adapters: 77 | https://svelte.dev/docs/kit/adapters 78 | Use adapter v0.16.0 to hide this message. 79 | `); 80 | } 81 | }, 82 | }; 83 | } 84 | -------------------------------------------------------------------------------- /demo/src/routes/Counter.svelte: -------------------------------------------------------------------------------- 1 | 23 | 24 |
25 | 30 | 31 |
32 |
33 | 34 | {Math.floor($displayedCount)} 35 |
36 |
37 | 38 | 43 |
44 | 45 | 111 | -------------------------------------------------------------------------------- /demo/src/routes/Header.svelte: -------------------------------------------------------------------------------- 1 | 6 | 7 |
8 |
9 | 10 | SvelteKit 11 | 12 |
13 | 14 | 33 | 34 |
35 | 36 | GitHub 37 | 38 |
39 |
40 | 41 | 130 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # sveltekit-adapter-deno 2 | 3 | ⚠️ This project is no longer maintained! ⚠️ 4 | 5 | Deno is now mostly compatible with SvelteKit and the [official adapters](https://svelte.dev/docs/kit/adapters) should be preferred. 6 | 7 | This adapter still works for Deno and Deno Deploy but I no longer use nor maintain it. 8 | 9 | I would **not** recommend Deno Deploy for website hosting due to unresolved [isolate start-up time issues](https://github.com/denoland/deploy_feedback/issues/505) and increased latency due to the [reduction in regions](https://www.reddit.com/r/Deno/comments/19f5xg7/deno_deploy_dropped_from_35_regions_to_just_12/). 10 | 11 | * * * 12 | 13 | [Adapter](https://kit.svelte.dev/docs/adapters) for 14 | [SvelteKit](https://kit.svelte.dev/) apps that generates a standalone 15 | [Deno](https://deno.com/runtime) or [Deno Deploy](https://deno.com/deploy) 16 | server. 17 | 18 | ## Usage 19 | 20 | Install in your SvelteKit project: 21 | 22 | ```sh 23 | npm install --save-dev sveltekit-adapter-deno 24 | ``` 25 | 26 | Add the adapter to your 27 | [SvelteKit configuration](https://kit.svelte.dev/docs/configuration). 28 | 29 | ```js 30 | // svelte.config.js 31 | import adapter from "sveltekit-adapter-deno"; 32 | 33 | /** @type {import('@sveltejs/kit').Config} */ 34 | const config = { 35 | kit: { 36 | adapter: adapter(), 37 | }, 38 | }; 39 | 40 | export default config; 41 | ``` 42 | 43 | Build the app for production (`npm run build`). 44 | 45 | Serve with Deno from the build directory: 46 | 47 | ```sh 48 | deno run --allow-env --allow-read --allow-net mod.ts 49 | ``` 50 | 51 | For Deno Deploy set the entry point to `mod.ts`. 52 | 53 | See the [GitHub Action workflow](/.github/workflows/ci.yml) for automated 54 | deployment. 55 | 56 | Using [deployctl](https://deno.com/deploy/docs/deployctl): 57 | 58 | ```sh 59 | deployctl deploy --project=demo --import-map=import_map.json mod.ts 60 | ``` 61 | 62 | ### Change the port or hostname 63 | 64 | The following environment variables can be used to change the port and hostname: 65 | 66 | ```sh 67 | PORT=5678 HOST=0.0.0.0 deno run --allow-env --allow-read --allow-net mod.ts 68 | ``` 69 | 70 | The default port is `8000` and the default hostname is `0.0.0.0`. 71 | 72 | ## Adapter options 73 | 74 | See the [TypeScript definition](/index.d.ts) for `AdapterOptions`. You can 75 | specify the build output directory and provide additional esbuild options. 76 | 77 | The `usage` option is used to determine where the current directory is (this is 78 | needed for the static and prerendered files). The default is `usage: 'deno'` 79 | which uses the `import.meta.url` to get the current directory. If you want to 80 | compile the result with `deno compile` you should use `usage: 'deno-compile'` 81 | which uses `Deno.execPath()` to get the current directory. 82 | 83 | ## Node and NPM modules 84 | 85 | Import Node modules in server routes with the `node:` prefix: 86 | 87 | ```js 88 | import * as fs from "node:fs"; 89 | import { Buffer } from "node:buffer"; 90 | ``` 91 | 92 | Import NPM modules as if coding for Node: 93 | 94 | ```js 95 | import slugify from "@sindresorhus/slugify"; 96 | console.log(slugify("I ♥ Deno")); 97 | ``` 98 | 99 | ## Demo App 100 | 101 | This repo publishes a SvelteKit demo app to Deno Deploy at: 102 | 103 | [sveltekit-adapter-deno.deno.dev](https://sveltekit-adapter-deno.deno.dev/) 104 | 105 | --- 106 | 107 | [MIT License](/LICENSE) | Copyright © 2023 [David Bushell](https://dbushell.com) 108 | -------------------------------------------------------------------------------- /demo/src/routes/sverdle/+page.svelte: -------------------------------------------------------------------------------- 1 | 96 | 97 | 98 | 99 | 100 | Sverdle 101 | 102 | 103 | 104 |

Sverdle

105 | 106 |
{ 110 | // prevent default callback from resetting the form 111 | return ({ update }) => { 112 | update({ reset: false }); 113 | }; 114 | }} 115 | > 116 | How to play 117 | 118 |
119 | {#each Array.from(Array(6).keys()) as row (row)} 120 | {@const current = row === i} 121 |

Row {row + 1}

122 |
123 | {#each Array.from(Array(5).keys()) as column (column)} 124 | {@const guess = current ? currentGuess : data.guesses[row]} 125 | {@const answer = data.answers[row]?.[column]} 126 | {@const value = guess?.[column] ?? ''} 127 | {@const selected = current && column === guess.length} 128 | {@const exact = answer === 'x'} 129 | {@const close = answer === 'c'} 130 | {@const missing = answer === '_'} 131 |
132 | {value} 133 | 134 | {#if exact} 135 | (correct) 136 | {:else if close} 137 | (present) 138 | {:else if missing} 139 | (absent) 140 | {:else} 141 | empty 142 | {/if} 143 | 144 | 145 |
146 | {/each} 147 |
148 | {/each} 149 |
150 | 151 |
152 | {#if won || data.answers.length >= 6} 153 | {#if !won && data.answer} 154 |

the answer was "{data.answer}"

155 | {/if} 156 | 159 | {:else} 160 |
161 | 162 | 163 | 172 | 173 | {#each ['qwertyuiop', 'asdfghjkl', 'zxcvbnm'] as row} 174 |
175 | {#each row as letter} 176 | 188 | {/each} 189 |
190 | {/each} 191 |
192 | {/if} 193 |
194 |
195 | 196 | {#if won} 197 |
207 | {/if} 208 | 209 | 421 | --------------------------------------------------------------------------------