├── .eslintrc.cjs ├── .gitignore ├── .nojekyll ├── .npmrc ├── .prettierrc ├── README.md ├── docs ├── .nojekyll ├── CNAME ├── app_ │ ├── assets │ │ └── pages │ │ │ ├── articles │ │ │ └── __layout.svelte-984cc1bf.css │ │ │ └── index.svelte-df858b15.css │ ├── chunks │ │ ├── paths-396f020f.js │ │ └── vendor-ba5e527f.js │ ├── error.svelte-5d5d6eed.js │ ├── layout.svelte-eca9fc82.js │ ├── manifest.json │ ├── pages │ │ ├── articles │ │ │ ├── __layout.svelte-f79cd2a5.js │ │ │ ├── migrate-saas-to-sveltekit.svelte-7eec8317.js │ │ │ └── sveltekit-github-pages-guide.svelte-194f2893.js │ │ └── index.svelte-6b511806.js │ └── start-e0df9183.js ├── articles │ ├── migrate-saas-sveltekit │ │ ├── lines_of_code_after.png │ │ ├── lines_of_code_before.png │ │ └── performance.png │ ├── migrate-saas-to-sveltekit │ │ └── index.html │ └── sveltekit-github-pages-guide │ │ ├── github_new_repo_settings.png │ │ ├── github_pages_branch_settings.png │ │ ├── github_pages_dns_settings.png │ │ ├── github_pages_settings.png │ │ ├── github_pages_site_published.png │ │ ├── github_pages_site_ready.png │ │ ├── index.html │ │ └── sveltekit_skeleton_page.png ├── favicon.png ├── index.html ├── mvp.css └── sitemap.xml ├── package-lock.json ├── package.json ├── src ├── app.html ├── global.d.ts └── routes │ ├── articles │ ├── __layout.svelte │ ├── migrate-saas-to-sveltekit.svelte │ └── sveltekit-github-pages-guide.svelte │ └── index.svelte ├── static ├── .nojekyll ├── articles │ ├── migrate-saas-sveltekit │ │ ├── lines_of_code_after.png │ │ ├── lines_of_code_before.png │ │ └── performance.png │ └── sveltekit-github-pages-guide │ │ ├── github_new_repo_settings.png │ │ ├── github_pages_branch_settings.png │ │ ├── github_pages_dns_settings.png │ │ ├── github_pages_settings.png │ │ ├── github_pages_site_published.png │ │ ├── github_pages_site_ready.png │ │ └── sveltekit_skeleton_page.png ├── favicon.png └── mvp.css ├── svelte.config.js └── tsconfig.json /.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | parser: '@typescript-eslint/parser', 4 | extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended', 'prettier'], 5 | plugins: ['svelte3', '@typescript-eslint'], 6 | ignorePatterns: ['*.cjs'], 7 | overrides: [{ files: ['*.svelte'], processor: 'svelte3/svelte3' }], 8 | settings: { 9 | 'svelte3/typescript': () => require('typescript') 10 | }, 11 | parserOptions: { 12 | sourceType: 'module', 13 | ecmaVersion: 2020 14 | }, 15 | env: { 16 | browser: true, 17 | es2017: true, 18 | node: true 19 | } 20 | }; 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /build 4 | /.svelte-kit 5 | /package 6 | .env 7 | .env.* 8 | !.env.example 9 | *.un~ 10 | -------------------------------------------------------------------------------- /.nojekyll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Glench/sveltekit-saas/cd401a996d8199c51962ed3347818d8d51a846f7/.nojekyll -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | engine-strict=true 2 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "useTabs": true, 3 | "singleQuote": true, 4 | "trailingComma": "none", 5 | "printWidth": 100 6 | } 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [The SaaS Template for SvelteKit](https://www.sveltekitsaas.com/) 2 | 3 | NOT READY YET! 4 | 5 | Everything you need to launch your next app. 6 | 7 | User auth, admin dashboards, Stripe payments, and more — all built on top of the best in class SvelteKit framework. 8 | 9 | Stop coding the same features in every app and save weeks of development time. Svelte SaaS is the boilerplate to jump-start your next app. 10 | -------------------------------------------------------------------------------- /docs/.nojekyll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Glench/sveltekit-saas/cd401a996d8199c51962ed3347818d8d51a846f7/docs/.nojekyll -------------------------------------------------------------------------------- /docs/CNAME: -------------------------------------------------------------------------------- 1 | sveltekitsaas.com 2 | -------------------------------------------------------------------------------- /docs/app_/assets/pages/articles/__layout.svelte-984cc1bf.css: -------------------------------------------------------------------------------- 1 | h2.svelte-104jo4w a.svelte-104jo4w{color:#ff3e00}article.svelte-104jo4w.svelte-104jo4w{max-width:700px;margin:0 auto} 2 | -------------------------------------------------------------------------------- /docs/app_/assets/pages/index.svelte-df858b15.css: -------------------------------------------------------------------------------- 1 | body{background-color:#fff;color:#4d4d4d;font-family:Inter,sans-serif;line-height:1.5;margin:0;overflow-x:hidden;padding:0}.btn{flex-grow:0!important}input[type=email]{margin-bottom:0}.mastfoot{display:none!important}.emailoctopus-success-message{color:#3ab73a!important}section.svelte-f8guh1 a.svelte-f8guh1{color:inherit}header.svelte-f8guh1.svelte-f8guh1{max-width:1080px;align-items:center;gap:3rem}header.svelte-f8guh1.svelte-f8guh1,section.svelte-f8guh1.svelte-f8guh1{display:flex;flex-wrap:wrap;justify-content:center;margin:2rem auto;padding:1.5rem}h1.svelte-f8guh1.svelte-f8guh1{font-family:Inter,-apple-system,system-ui,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,sans-serif;font-size:3rem;color:#000;line-height:1.2}.gradient.svelte-f8guh1.svelte-f8guh1{-webkit-background-clip:text;-webkit-box-decoration-break:clone;-webkit-font-smoothing:subpixel-antialiased;-webkit-text-fill-color:transparent;background-image:linear-gradient(180deg,#FF3E00 43.62%,#640000 100%);background-attachment:scroll;background-clip:text;background-color:#0000}p.svelte-f8guh1.svelte-f8guh1{font-size:1.15rem}form.svelte-f8guh1.svelte-f8guh1{border:1px solid #e9e9e9;border-radius:5px;box-shadow:#f4f4f4 2px 2px 10px;box-sizing:border-box;color:#000;display:block;padding:1.7rem;width:361px}.card.svelte-f8guh1.svelte-f8guh1{max-width:280px;color:#c6d3de;background-color:#2a3643;padding:1.75rem;margin:2rem;border-radius:8px;box-shadow:0 2px 4px #00000040}.card.svelte-f8guh1 h2.svelte-f8guh1{padding:0;margin:0;font-size:2rem}.card.svelte-f8guh1 ul.svelte-f8guh1{margin:0 0 0 1rem;padding:0}.card.svelte-f8guh1 li.svelte-f8guh1{margin:1rem 0}.card.svelte-f8guh1 ul.emoji.svelte-f8guh1{list-style:none;color:#fff}.card.svelte-f8guh1 ul.emoji li.svelte-f8guh1{font-size:1.2rem;text-indent:-2rem;padding-left:1rem}.card.svelte-f8guh1 ul.emoji li.svelte-f8guh1:before{content:"\2b50\fe0f";margin-right:.5rem} 2 | -------------------------------------------------------------------------------- /docs/app_/chunks/paths-396f020f.js: -------------------------------------------------------------------------------- 1 | let a="",e="";function t(s){a=s.base,e=s.assets||a}export{e as a,a as b,t as s}; 2 | -------------------------------------------------------------------------------- /docs/app_/chunks/vendor-ba5e527f.js: -------------------------------------------------------------------------------- 1 | function h(){}function I(t,n){for(const e in n)t[e]=n[e];return t}function z(t){return t()}function B(){return Object.create(null)}function m(t){t.forEach(z)}function L(t){return typeof t=="function"}function G(t,n){return t!=t?n==n:t!==n||t&&typeof t=="object"||typeof t=="function"}let b;function lt(t,n){return b||(b=document.createElement("a")),b.href=n,t===b.href}function J(t){return Object.keys(t).length===0}function ut(t,n,e,i){if(t){const c=T(t,n,e,i);return t[0](c)}}function T(t,n,e,i){return t[1]&&i?I(e.ctx.slice(),t[1](i(n))):e.ctx}function st(t,n,e,i){if(t[2]&&i){const c=t[2](i(e));if(n.dirty===void 0)return c;if(typeof c=="object"){const s=[],o=Math.max(n.dirty.length,c.length);for(let l=0;l32){const n=[],e=t.ctx.length/32;for(let i=0;i>1);e(c)<=i?t=c+1:n=c}return t}function R(t){if(t.hydrate_init)return;t.hydrate_init=!0;let n=t.childNodes;if(t.nodeName==="HEAD"){const r=[];for(let u=0;u0&&n[e[c]].claim_order<=u?c+1:Q(1,c,x=>n[e[x]].claim_order,u))-1;i[r]=e[a]+1;const f=a+1;e[f]=r,c=Math.max(f,c)}const s=[],o=[];let l=n.length-1;for(let r=e[c]+1;r!=0;r=i[r-1]){for(s.push(n[r-1]);l>=r;l--)o.push(n[l]);l--}for(;l>=0;l--)o.push(n[l]);s.reverse(),o.sort((r,u)=>r.claim_order-u.claim_order);for(let r=0,u=0;r=s[u].claim_order;)u++;const a=u{for(let o=t.claim_info.last_index;o=0;o--){const l=t[o];if(n(l)){const r=e(l);return r===void 0?t.splice(o,1):t[o]=r,c?r===void 0&&t.claim_info.last_index--:t.claim_info.last_index=o,l}}return i()})();return s.claim_order=t.claim_info.total_claimed,t.claim_info.total_claimed+=1,s}function tt(t,n,e,i){return v(t,c=>c.nodeName===n,c=>{const s=[];for(let o=0;oc.removeAttribute(o))},()=>i(n))}function pt(t,n,e){return tt(t,n,e,X)}function nt(t,n){return v(t,e=>e.nodeType===3,e=>{const i=""+n;if(e.data.startsWith(i)){if(e.data.length!==i.length)return e.splitText(i.length)}else e.data=i},()=>A(n),!0)}function yt(t){return nt(t," ")}function gt(t,n){n=""+n,t.wholeText!==n&&(t.data=n)}function xt(t,n,e,i){e===null?t.style.removeProperty(n):t.style.setProperty(n,e,i?"important":"")}function bt(t,n=document.body){return Array.from(n.querySelectorAll(t))}let p;function y(t){p=t}function S(){if(!p)throw new Error("Function called outside component initialization");return p}function $t(t){S().$$.on_mount.push(t)}function wt(t){S().$$.after_update.push(t)}function Et(t,n){S().$$.context.set(t,n)}const g=[],O=[],w=[],P=[],D=Promise.resolve();let j=!1;function F(){j||(j=!0,D.then(H))}function kt(){return F(),D}function N(t){w.push(t)}const q=new Set;let E=0;function H(){const t=p;do{for(;E{k.delete(t),i&&(e&&t.d(1),i())}),t.o(n)}}function Nt(t,n){const e={},i={},c={$$scope:1};let s=t.length;for(;s--;){const o=t[s],l=n[s];if(l){for(const r in o)r in l||(i[r]=1);for(const r in l)c[r]||(e[r]=l[r],c[r]=1);t[s]=l}else for(const r in o)c[r]=1}for(const o in i)o in e||(e[o]=void 0);return e}function qt(t){return typeof t=="object"&&t!==null?t:{}}function Ct(t){t&&t.c()}function Mt(t,n){t&&t.l(n)}function rt(t,n,e,i){const{fragment:c,on_mount:s,on_destroy:o,after_update:l}=t.$$;c&&c.m(n,e),i||N(()=>{const r=s.map(z).filter(L);o?o.push(...r):m(r),t.$$.on_mount=[]}),l.forEach(N)}function ct(t,n){const e=t.$$;e.fragment!==null&&(m(e.on_destroy),e.fragment&&e.fragment.d(n),e.on_destroy=e.fragment=null,e.ctx=[])}function ot(t,n){t.$$.dirty[0]===-1&&(g.push(t),F(),t.$$.dirty.fill(0)),t.$$.dirty[n/31|0]|=1<{const M=C.length?C[0]:x;return u.ctx&&c(u.ctx[f],u.ctx[f]=M)&&(!u.skip_bound&&u.bound[f]&&u.bound[f](M),a&&ot(t,f)),x}):[],u.update(),a=!0,m(u.before_update),u.fragment=i?i(u.ctx):!1,n.target){if(n.hydrate){K();const f=Y(n.target);u.fragment&&u.fragment.l(f),f.forEach(V)}else u.fragment&&u.fragment.c();n.intro&&it(t.$$.fragment),rt(t,n.target,n.anchor,n.customElement),W(),H()}y(r)}class Bt{$destroy(){ct(this,1),this.$destroy=h}$on(n,e){const i=this.$$.callbacks[n]||(this.$$.callbacks[n]=[]);return i.push(e),()=>{const c=i.indexOf(e);c!==-1&&i.splice(c,1)}}$set(n){this.$$set&&!J(n)&&(this.$$.skip_bound=!0,this.$$set(n),this.$$.skip_bound=!1)}}const _=[];function Tt(t,n=h){let e;const i=new Set;function c(l){if(G(t,l)&&(t=l,e)){const r=!_.length;for(const u of i)u[1](),_.push(u,t);if(r){for(let u=0;u<_.length;u+=2)_[u][0](_[u+1]);_.length=0}}}function s(l){c(l(t))}function o(l,r=h){const u=[l,r];return i.add(u),i.size===1&&(e=n(c)||h),l(t),()=>{i.delete(u),i.size===0&&(e(),e=null)}}return{set:c,update:s,subscribe:o}}export{qt as A,ct as B,I as C,Tt as D,kt as E,ut as F,ft as G,at as H,st as I,U as J,h as K,bt as L,lt as M,Bt as S,Y as a,mt as b,pt as c,V as d,X as e,xt as f,dt as g,nt as h,zt as i,gt as j,_t as k,ht as l,yt as m,At as n,jt as o,St as p,it as q,Et as r,G as s,A as t,wt as u,$t as v,Ct as w,Mt as x,rt as y,Nt as z}; 2 | -------------------------------------------------------------------------------- /docs/app_/error.svelte-5d5d6eed.js: -------------------------------------------------------------------------------- 1 | import{S as K,i as w,s as y,e as v,t as E,c as d,a as b,h as P,d as m,g as n,J as R,j,k as N,l as q,m as S,K as C}from"./chunks/vendor-ba5e527f.js";function H(r){let f,t=r[1].frame+"",a;return{c(){f=v("pre"),a=E(t)},l(l){f=d(l,"PRE",{});var s=b(f);a=P(s,t),s.forEach(m)},m(l,s){n(l,f,s),R(f,a)},p(l,s){s&2&&t!==(t=l[1].frame+"")&&j(a,t)},d(l){l&&m(f)}}}function J(r){let f,t=r[1].stack+"",a;return{c(){f=v("pre"),a=E(t)},l(l){f=d(l,"PRE",{});var s=b(f);a=P(s,t),s.forEach(m)},m(l,s){n(l,f,s),R(f,a)},p(l,s){s&2&&t!==(t=l[1].stack+"")&&j(a,t)},d(l){l&&m(f)}}}function z(r){let f,t,a,l,s=r[1].message+"",c,k,u,p,i=r[1].frame&&H(r),o=r[1].stack&&J(r);return{c(){f=v("h1"),t=E(r[0]),a=N(),l=v("pre"),c=E(s),k=N(),i&&i.c(),u=N(),o&&o.c(),p=q()},l(e){f=d(e,"H1",{});var _=b(f);t=P(_,r[0]),_.forEach(m),a=S(e),l=d(e,"PRE",{});var h=b(l);c=P(h,s),h.forEach(m),k=S(e),i&&i.l(e),u=S(e),o&&o.l(e),p=q()},m(e,_){n(e,f,_),R(f,t),n(e,a,_),n(e,l,_),R(l,c),n(e,k,_),i&&i.m(e,_),n(e,u,_),o&&o.m(e,_),n(e,p,_)},p(e,[_]){_&1&&j(t,e[0]),_&2&&s!==(s=e[1].message+"")&&j(c,s),e[1].frame?i?i.p(e,_):(i=H(e),i.c(),i.m(u.parentNode,u)):i&&(i.d(1),i=null),e[1].stack?o?o.p(e,_):(o=J(e),o.c(),o.m(p.parentNode,p)):o&&(o.d(1),o=null)},i:C,o:C,d(e){e&&m(f),e&&m(a),e&&m(l),e&&m(k),i&&i.d(e),e&&m(u),o&&o.d(e),e&&m(p)}}}function D({error:r,status:f}){return{props:{error:r,status:f}}}function A(r,f,t){let{status:a}=f,{error:l}=f;return r.$$set=s=>{"status"in s&&t(0,a=s.status),"error"in s&&t(1,l=s.error)},[a,l]}class F extends K{constructor(f){super();w(this,f,A,z,y,{status:0,error:1})}}export{F as default,D as load}; 2 | -------------------------------------------------------------------------------- /docs/app_/layout.svelte-eca9fc82.js: -------------------------------------------------------------------------------- 1 | import{S as l,i,s as r,F as u,G as f,H as _,I as c,q as p,o as d}from"./chunks/vendor-ba5e527f.js";function m(n){let s;const o=n[1].default,e=u(o,n,n[0],null);return{c(){e&&e.c()},l(t){e&&e.l(t)},m(t,a){e&&e.m(t,a),s=!0},p(t,[a]){e&&e.p&&(!s||a&1)&&f(e,o,t,t[0],s?c(o,t[0],a,null):_(t[0]),null)},i(t){s||(p(e,t),s=!0)},o(t){d(e,t),s=!1},d(t){e&&e.d(t)}}}function $(n,s,o){let{$$slots:e={},$$scope:t}=s;return n.$$set=a=>{"$$scope"in a&&o(0,t=a.$$scope)},[t,e]}class h extends l{constructor(s){super();i(this,s,$,m,r,{})}}export{h as default}; 2 | -------------------------------------------------------------------------------- /docs/app_/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | ".svelte-kit/runtime/client/start.js": { 3 | "file": "start-e0df9183.js", 4 | "src": ".svelte-kit/runtime/client/start.js", 5 | "isEntry": true, 6 | "imports": [ 7 | "_vendor-ba5e527f.js", 8 | "_paths-396f020f.js" 9 | ], 10 | "dynamicImports": [ 11 | ".svelte-kit/runtime/components/layout.svelte", 12 | ".svelte-kit/runtime/components/error.svelte", 13 | "src/routes/index.svelte", 14 | "src/routes/articles/__layout.svelte", 15 | "src/routes/articles/sveltekit-github-pages-guide.svelte", 16 | "src/routes/articles/migrate-saas-to-sveltekit.svelte" 17 | ] 18 | }, 19 | ".svelte-kit/runtime/components/layout.svelte": { 20 | "file": "layout.svelte-eca9fc82.js", 21 | "src": ".svelte-kit/runtime/components/layout.svelte", 22 | "isEntry": true, 23 | "isDynamicEntry": true, 24 | "imports": [ 25 | "_vendor-ba5e527f.js" 26 | ] 27 | }, 28 | ".svelte-kit/runtime/components/error.svelte": { 29 | "file": "error.svelte-5d5d6eed.js", 30 | "src": ".svelte-kit/runtime/components/error.svelte", 31 | "isEntry": true, 32 | "isDynamicEntry": true, 33 | "imports": [ 34 | "_vendor-ba5e527f.js" 35 | ] 36 | }, 37 | "src/routes/index.svelte": { 38 | "file": "pages/index.svelte-6b511806.js", 39 | "src": "src/routes/index.svelte", 40 | "isEntry": true, 41 | "isDynamicEntry": true, 42 | "imports": [ 43 | "_vendor-ba5e527f.js", 44 | "_paths-396f020f.js" 45 | ], 46 | "css": [ 47 | "assets/pages/index.svelte-df858b15.css" 48 | ] 49 | }, 50 | "src/routes/articles/__layout.svelte": { 51 | "file": "pages/articles/__layout.svelte-f79cd2a5.js", 52 | "src": "src/routes/articles/__layout.svelte", 53 | "isEntry": true, 54 | "isDynamicEntry": true, 55 | "imports": [ 56 | "_vendor-ba5e527f.js", 57 | "_paths-396f020f.js" 58 | ], 59 | "css": [ 60 | "assets/pages/articles/__layout.svelte-984cc1bf.css" 61 | ] 62 | }, 63 | "src/routes/articles/sveltekit-github-pages-guide.svelte": { 64 | "file": "pages/articles/sveltekit-github-pages-guide.svelte-194f2893.js", 65 | "src": "src/routes/articles/sveltekit-github-pages-guide.svelte", 66 | "isEntry": true, 67 | "isDynamicEntry": true, 68 | "imports": [ 69 | "_vendor-ba5e527f.js", 70 | "_paths-396f020f.js" 71 | ] 72 | }, 73 | "src/routes/articles/migrate-saas-to-sveltekit.svelte": { 74 | "file": "pages/articles/migrate-saas-to-sveltekit.svelte-7eec8317.js", 75 | "src": "src/routes/articles/migrate-saas-to-sveltekit.svelte", 76 | "isEntry": true, 77 | "isDynamicEntry": true, 78 | "imports": [ 79 | "_vendor-ba5e527f.js", 80 | "_paths-396f020f.js" 81 | ] 82 | }, 83 | "_vendor-ba5e527f.js": { 84 | "file": "chunks/vendor-ba5e527f.js" 85 | }, 86 | "_paths-396f020f.js": { 87 | "file": "chunks/paths-396f020f.js" 88 | } 89 | } -------------------------------------------------------------------------------- /docs/app_/pages/articles/__layout.svelte-f79cd2a5.js: -------------------------------------------------------------------------------- 1 | import{S as D,i as O,s as P,F as V,e as o,t as $,k as A,L as Y,c as l,a as d,d as e,h as K,m as R,b as s,M as Q,J as a,g as M,G as X,H as Z,I as ee,q as te,o as ae}from"../../chunks/vendor-ba5e527f.js";import{a as ne,b as se}from"../../chunks/paths-396f020f.js";function re(g){let i,p,f,h,c,x,_,N,y,j,I,m,E,v,u,L,C,S,z,k,w;const F=g[2].default,r=V(F,g,g[1],null);return{c(){i=o("link"),p=o("link"),f=o("link"),h=o("link"),c=o("script"),_=o("script"),N=$(`window.dataLayer = window.dataLayer || []; 2 | function gtag(){dataLayer.push(arguments);} 3 | gtag('js', new Date()); 4 | 5 | gtag('config', 'G-FSJW8FC22N'); 6 | `),y=o("style"),j=$(`body { 7 | font-family: "Inter",-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji"; 8 | } 9 | h1 { 10 | font-size: 3rem; 11 | } 12 | article h2 { 13 | font-size: 2rem; 14 | margin-top:3rem; 15 | } 16 | article h3 { 17 | font-size: 1.5rem; 18 | margin-top:3rem; 19 | } 20 | p, li { 21 | max-width: 700px; 22 | margin: 1.5rem auto; 23 | line-height: 2; 24 | } 25 | figure img { 26 | display: block; 27 | margin: 1rem auto; 28 | border: 1px solid #eaeaea; 29 | box-shadow: 0 1px 3px rgba(0,0,0,.07); 30 | } 31 | @media (min-width: 768px) { 32 | p, li { 33 | font-size:18px; 34 | } 35 | pre { 36 | font-size: 17px; 37 | } 38 | }`),I=A(),m=o("main"),E=o("nav"),v=o("h2"),u=o("a"),L=$("Svelte SaaS Starter Kit"),C=A(),S=o("article"),r&&r.c(),z=A(),k=o("footer"),this.h()},l(t){const n=Y('[data-svelte="svelte-vuylx4"]',document.head);i=l(n,"LINK",{rel:!0,href:!0}),p=l(n,"LINK",{rel:!0,href:!0}),f=l(n,"LINK",{rel:!0,href:!0,crossorigin:!0}),h=l(n,"LINK",{href:!0,rel:!0}),c=l(n,"SCRIPT",{src:!0});var W=d(c);W.forEach(e),_=l(n,"SCRIPT",{});var U=d(_);N=K(U,`window.dataLayer = window.dataLayer || []; 39 | function gtag(){dataLayer.push(arguments);} 40 | gtag('js', new Date()); 41 | 42 | gtag('config', 'G-FSJW8FC22N'); 43 | `),U.forEach(e),y=l(n,"STYLE",{});var T=d(y);j=K(T,`body { 44 | font-family: "Inter",-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji"; 45 | } 46 | h1 { 47 | font-size: 3rem; 48 | } 49 | article h2 { 50 | font-size: 2rem; 51 | margin-top:3rem; 52 | } 53 | article h3 { 54 | font-size: 1.5rem; 55 | margin-top:3rem; 56 | } 57 | p, li { 58 | max-width: 700px; 59 | margin: 1.5rem auto; 60 | line-height: 2; 61 | } 62 | figure img { 63 | display: block; 64 | margin: 1rem auto; 65 | border: 1px solid #eaeaea; 66 | box-shadow: 0 1px 3px rgba(0,0,0,.07); 67 | } 68 | @media (min-width: 768px) { 69 | p, li { 70 | font-size:18px; 71 | } 72 | pre { 73 | font-size: 17px; 74 | } 75 | }`),T.forEach(e),n.forEach(e),I=R(t),m=l(t,"MAIN",{});var b=d(m);E=l(b,"NAV",{});var q=d(E);v=l(q,"H2",{class:!0});var G=d(v);u=l(G,"A",{href:!0,class:!0});var H=d(u);L=K(H,"Svelte SaaS Starter Kit"),H.forEach(e),G.forEach(e),q.forEach(e),C=R(b),S=l(b,"ARTICLE",{class:!0});var J=d(S);r&&r.l(J),J.forEach(e),z=R(b),k=l(b,"FOOTER",{});var B=d(k);B.forEach(e),b.forEach(e),this.h()},h(){s(i,"rel","stylesheet"),s(i,"href",""+(ne+"/mvp.css")),s(p,"rel","preconnect"),s(p,"href","https://fonts.googleapis.com"),s(f,"rel","preconnect"),s(f,"href","https://fonts.gstatic.com"),s(f,"crossorigin",""),s(h,"href","https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap"),s(h,"rel","stylesheet"),c.async=!0,Q(c.src,x="https://www.googletagmanager.com/gtag/js?id=G-FSJW8FC22N")||s(c,"src",x),s(u,"href",""+(se+"/")),s(u,"class","svelte-104jo4w"),s(v,"class","svelte-104jo4w"),s(S,"class","svelte-104jo4w")},m(t,n){a(document.head,i),a(document.head,p),a(document.head,f),a(document.head,h),a(document.head,c),a(document.head,_),a(_,N),a(document.head,y),a(y,j),M(t,I,n),M(t,m,n),a(m,E),a(E,v),a(v,u),a(u,L),a(m,C),a(m,S),r&&r.m(S,null),a(m,z),a(m,k),w=!0},p(t,[n]){r&&r.p&&(!w||n&2)&&X(r,F,t,t[1],w?ee(F,t[1],n,null):Z(t[1]),null)},i(t){w||(te(r,t),w=!0)},o(t){ae(r,t),w=!1},d(t){e(i),e(p),e(f),e(h),e(c),e(_),e(y),t&&e(I),t&&e(m),r&&r.d(t)}}}function oe(g,i,p){let{$$slots:f={},$$scope:h}=i;const c=!1;return g.$$set=x=>{"$$scope"in x&&p(1,h=x.$$scope)},[c,h,f]}class ce extends D{constructor(i){super();O(this,i,oe,re,P,{hydrate:0})}get hydrate(){return this.$$.ctx[0]}}export{ce as default}; 76 | -------------------------------------------------------------------------------- /docs/app_/pages/articles/migrate-saas-to-sveltekit.svelte-7eec8317.js: -------------------------------------------------------------------------------- 1 | import{S as Ii,i as Si,s as _i,k as h,e as i,t as o,L as Ei,d as t,m as f,c as s,a as r,h as l,f as H,b as m,M as _a,g as d,J as a,K as Ea}from"../../chunks/vendor-ba5e527f.js";import{a as me}from"../../chunks/paths-396f020f.js";function Ki(gi){let M,E,ue,rt,xe,g,nt,O,dt,ht,Le,I,ft,C,mt,ut,je,S,pt,z,ct,wt,Re,K,B,vt,yt,Te,N,bt,He,F,kt,Me,w,gt,pe,It,St,ce,_t,Et,Oe,D,Kt,Ce,A,J,P,Ka,ze,G,At,Be,x,U,L,Aa,Ne,q,Pt,Fe,W,xt,De,X,Lt,Je,j,Q,R,Pa,Ge,V,jt,Ue,v,Rt,we,Tt,Ht,ve,Mt,Ot,qe,Y,Ct,We,Z,zt,Xe,$,Bt,Qe,ee,Nt,Ve,p,ye,Ft,Dt,be,Jt,Gt,b,Ut,ke,qt,Wt,te,Xt,Qt,Vt,ge,Yt,Zt,T,$t,Ie,ea,ta,Ye,ae,aa,Ze,u,Se,ia,oa,_e,sa,la,Ee,ra,na,Ke,da,ha,Ae,fa,ma,k,ua,ie,pa,ca,Pe,oe,wa,va,$e,se,ya,et,le,ba,tt,_,ka,re,ga,Ia;return{c(){M=h(),E=i("header"),ue=i("h1"),rt=o("Reflections on Migrating my SaaS To SvelteKit"),xe=h(),g=i("p"),nt=o("I run "),O=i("a"),dt=o("ExtensionPay"),ht=o(", a service to monetize Chrome extensions by letting developers easily add payments to their extensions."),Le=h(),I=i("p"),ft=o("Because I seriously enjoy working with "),C=i("a"),mt=o("Svelte"),ut=o(", I built ExtensionPay on top of a custom framework based around Svelte that would take care of all the server-side rendering, server/client communication, code-splitting, file minimizing, stylesheets, etc."),je=h(),S=i("p"),pt=o("The framework was pretty nice for something I cobbled together, but had issues. One big one was startup time. My service would go down for way too long after restarting the app. Another issue was imports. It was difficult or impossible to import libraries client-side so I had to do annoying workarounds and that made me want to work on the code less. A custom framework can also be a businesses liability. So when "),z=i("a"),ct=o("SvelteKit 1.0 was announced"),wt=o(" I really wanted to switch over."),Re=h(),K=i("p"),B=i("a"),vt=o("SvelteKit"),yt=o(" is the framework I\u2019ve dreamed about since I started full stack development in 2010. It not only takes care of all the nitty gritty details of code-splitting, stylesheets, minimization, caching, client-side code, server-side code, server-side rendering, client-side hydration, CSRF, performance, etc, but is basically boilerplate-free. SvelteKit is apparently a lot like Next or Nuxt, but built on top of Svelte so it's blazing fast and a delight to work with."),Te=h(),N=i("p"),bt=o("Working very slowly, it took me a few months to migrate ExtensionPay from my old custom framework to SvelteKit by hand. I deployed the new version on April 2, 2023. Here are some results."),He=h(),F=i("h2"),kt=o("Lines of code"),Me=h(),w=i("p"),gt=o("Here\u2019s a count of the lines of code in my "),pe=i("code"),It=o("routes/"),St=o(" directory using "),ce=i("code"),_t=o("cloc"),Et=o("."),Oe=h(),D=i("p"),Kt=o("Before (my custom framework):"),Ce=h(),A=i("figure"),J=i("a"),P=i("img"),ze=h(),G=i("p"),At=o("After (SvelteKit):"),Be=h(),x=i("figure"),U=i("a"),L=i("img"),Ne=h(),q=i("p"),Pt=o("Around 600 lines removed after the migration. That\u2019s about a 12% reduction in code. My framework was already pretty lean so that's impressive."),Fe=h(),W=i("p"),xt=o("Some of the code reduction was because there was a little duplicate code between pages, but most of it was just because SvelteKit removed the need for a lot of code. This is really great. As a solo founder, fewer lines of code to maintain and understand is a huge help."),De=h(),X=i("h2"),Lt=o("Performance"),Je=h(),j=i("figure"),Q=i("a"),R=i("img"),Ge=h(),V=i("p"),jt=o("I launched the SvelteKit-based app near that black line on the plot. It\u2019s a little hard to see with the spikes, but the overall performance of the SvelteKit-based app is a tiny bit worse. Before, the midday usage would pretty consistently top out around 20% CPU. Now midday usage is typically around 30%. Not a big deal, but will probably mean I need to upgrade the server sooner. So the SvelteKit team owes me some money \u{1F61B} jk"),Ue=h(),v=i("p"),Rt=o("I was actually concerned the performance would be much worse. In my local load testing with the "),we=i("code"),Tt=o("ab"),Ht=o(" command-line utility the SvelteKit app had about "),ve=i("em"),Mt=o("half"),Ot=o(" the performance of my previous app for basic routes. Luckily in practice that didn\u2019t happen. Maybe my test was flawed."),qe=h(),Y=i("h2"),Ct=o("Feelings"),We=h(),Z=i("p"),zt=o("Overall, the migration and deploy went fairly smoothly. It was a lot of work to basically rewrite the app and make sure everything still worked, but I\u2019m happy I did it."),Xe=h(),$=i("p"),Bt=o("Having switched over, I feel like my app is on a lot more solid foundation with SvelteKit. I feel fantastic developing with the framework and my app feels fantastic to use."),Qe=h(),ee=i("p"),Nt=o("A few pain points I ran into working with SvelteKit:"),Ve=h(),p=i("ul"),ye=i("li"),Ft=o("It was surprisingly difficult to get a REPL working nicely with SvelteKit. For business reasons I often have the need to run custom one-off commands in a REPL or script, but because of various import issues, creating a REPL that knew about my app and database was a challenge. I eventually got it to work, but it wasn\u2019t easy."),Dt=h(),be=i("li"),Jt=o("Related to the above, SvelteKit is pretty new so there\u2019s not a lot about it on the internet yet. When I run into issues, there\u2019s less material I can search for that might have fixes. Luckily there was enough and the SvelteKit team was actually crazy responsive to bugs or forum posts I made, and this problem will diminish over time."),Gt=h(),b=i("li"),Ut=o("One small but annoying issue is that when I load 3rd-party libraries in the "),ke=i("code"),qt=o(""),Wt=o(" of a SvelteKit route over http(s), the script loads in a blocking way when rendered from the server and in a non-blocking way when rendered via client-side navigation. This caused my code to fail during client-side navigation but not server-side navigation and it\u2019s only through luck that I caught the failure before launch. "),te=i("a"),Xt=o("I made an issue about this"),Qt=o(" that was closed as \u201Cwell that\u2019s just how it works\u201D and a nice suggestion about how I might achieve my goals instead. Still, It was kind of tricky to figure out how to make a workaround that worked in all cases."),Vt=h(),ge=i("li"),Yt=o("As demonstrated earlier, real-world performance is a little worse than my previous custom framework. I\u2019m not sure why this is. Maybe it's SvelteKit or maybe it's something I'm doing. Either way, it\u2019s not so bad."),Zt=h(),T=i("li"),$t=o("The naming conventions for routes is a little bonkers. Now when I\u2019m working on my app I have like 10 tabs open that all have the name "),Ie=i("code"),ea=o("+page.server.ts"),ta=o(". \u{1F644} Does anyone know how I can make this better in vim?"),Ye=h(),ae=i("p"),aa=o("Some pleasures of working with SvelteKit:"),Ze=h(),u=i("ul"),Se=i("li"),ia=o("The developer experience is phenomenal. Hot reloading is so much nicer than what I was using previously and all the built-in commands for testing work great."),oa=h(),_e=i("li"),sa=o("I\u2019m really happy that SvelteKit allows parts of your app to be pre-rendered and others to be dynamic. I now pre-render my marketing pages (like this post!) which is great for performance. And the client-side routing also still works."),la=h(),Ee=i("li"),ra=o("Speaking of which, the client-side routing was a real unexpected pleasure. I assumed it was just extraneous, but it really does make my app feel a lot snappier and it\u2019s just included automatically in SvelteKit without me having to do anything."),na=h(),Ke=i("li"),da=o("Another unexpected pleasure was using TypeScript for static types and Playwright for end-to-end testing. Both give me an extra layer of confidence that my app is working as intended which is so important as a solo developer. Heck, I didn't set out to use Typescript or Playwright at all, but SvelteKit made them so easy to add that I just did it . And I'm so glad I did because I feel a lot more confident deploying code now."),ha=h(),Ae=i("li"),fa=o("SvelteKit\u2019s documentation is already quite good, and I know the team is also working on an interactive tutorial which is great. Good reference documentation is so helpful."),ma=h(),k=i("li"),ua=o("I really love how forms work in SvelteKit. For whatever reason, working with forms in any other framework always felt so tedious to me. But the "),ie=i("a"),pa=o("form actions API"),ca=o(" means it\u2019s so easy to make boilerlate-free forms that work without client-side JavaScript but can be easily upgraded to AJAX with basically no extra code using "),Pe=i("code"),oe=i("a"),wa=o("enhance"),va=o(". Before the migration, I was getting a surprising number of error notification emails about people submitting forms on my site that didn\u2019t have JavaScript enabled. Now I get none, and all my forms work work with and without client-side JavaScript by default. And I actually enjoy creating them."),$e=h(),se=i("p"),ya=o("Having my app on SvelteKit also comes with some other potential benefits. If I ever want to bring in another developer, it will be much easier to get them up to speed with SvelteKit than my own custom framework. And for the same reason, if I ever want to sell my app I imagine it will be an easier sell that it\u2019s built on something well-supported and documented. Of course, this would be true for any well-known framework, but as I mentioned earlier I really love Svelte and dislike React so for me it\u2019s an easy choice."),et=h(),le=i("p"),ba=o("Overall, I\u2019m really happy with SvelteKit and want to extend my sincere thanks for the SvelteKit team for working so hard on it, taking our feedback, and sharing it with us for free. It really is amazing."),tt=h(),_=i("p"),ka=o("I love it so much, in fact, that I'm taking what I've learned in my own app and developing a "),re=i("a"),ga=o("SvelteKit SaaS starter template"),Ia=o("."),this.h()},l(e){Ei('[data-svelte="svelte-ghkt6g"]',document.head).forEach(t),M=f(e),E=s(e,"HEADER",{style:!0});var xa=r(E);ue=s(xa,"H1",{});var La=r(ue);rt=l(La,"Reflections on Migrating my SaaS To SvelteKit"),La.forEach(t),xa.forEach(t),xe=f(e),g=s(e,"P",{});var at=r(g);nt=l(at,"I run "),O=s(at,"A",{href:!0});var ja=r(O);dt=l(ja,"ExtensionPay"),ja.forEach(t),ht=l(at,", a service to monetize Chrome extensions by letting developers easily add payments to their extensions."),at.forEach(t),Le=f(e),I=s(e,"P",{});var it=r(I);ft=l(it,"Because I seriously enjoy working with "),C=s(it,"A",{href:!0});var Ra=r(C);mt=l(Ra,"Svelte"),Ra.forEach(t),ut=l(it,", I built ExtensionPay on top of a custom framework based around Svelte that would take care of all the server-side rendering, server/client communication, code-splitting, file minimizing, stylesheets, etc."),it.forEach(t),je=f(e),S=s(e,"P",{});var ot=r(S);pt=l(ot,"The framework was pretty nice for something I cobbled together, but had issues. One big one was startup time. My service would go down for way too long after restarting the app. Another issue was imports. It was difficult or impossible to import libraries client-side so I had to do annoying workarounds and that made me want to work on the code less. A custom framework can also be a businesses liability. So when "),z=s(ot,"A",{href:!0});var Ta=r(z);ct=l(Ta,"SvelteKit 1.0 was announced"),Ta.forEach(t),wt=l(ot," I really wanted to switch over."),ot.forEach(t),Re=f(e),K=s(e,"P",{});var Sa=r(K);B=s(Sa,"A",{href:!0});var Ha=r(B);vt=l(Ha,"SvelteKit"),Ha.forEach(t),yt=l(Sa," is the framework I\u2019ve dreamed about since I started full stack development in 2010. It not only takes care of all the nitty gritty details of code-splitting, stylesheets, minimization, caching, client-side code, server-side code, server-side rendering, client-side hydration, CSRF, performance, etc, but is basically boilerplate-free. SvelteKit is apparently a lot like Next or Nuxt, but built on top of Svelte so it's blazing fast and a delight to work with."),Sa.forEach(t),Te=f(e),N=s(e,"P",{});var Ma=r(N);bt=l(Ma,"Working very slowly, it took me a few months to migrate ExtensionPay from my old custom framework to SvelteKit by hand. I deployed the new version on April 2, 2023. Here are some results."),Ma.forEach(t),He=f(e),F=s(e,"H2",{});var Oa=r(F);kt=l(Oa,"Lines of code"),Oa.forEach(t),Me=f(e),w=s(e,"P",{});var ne=r(w);gt=l(ne,"Here\u2019s a count of the lines of code in my "),pe=s(ne,"CODE",{});var Ca=r(pe);It=l(Ca,"routes/"),Ca.forEach(t),St=l(ne," directory using "),ce=s(ne,"CODE",{});var za=r(ce);_t=l(za,"cloc"),za.forEach(t),Et=l(ne,"."),ne.forEach(t),Oe=f(e),D=s(e,"P",{});var Ba=r(D);Kt=l(Ba,"Before (my custom framework):"),Ba.forEach(t),Ce=f(e),A=s(e,"FIGURE",{style:!0});var Na=r(A);J=s(Na,"A",{href:!0});var Fa=r(J);P=s(Fa,"IMG",{style:!0,src:!0,alt:!0}),Fa.forEach(t),Na.forEach(t),ze=f(e),G=s(e,"P",{});var Da=r(G);At=l(Da,"After (SvelteKit):"),Da.forEach(t),Be=f(e),x=s(e,"FIGURE",{style:!0});var Ja=r(x);U=s(Ja,"A",{href:!0});var Ga=r(U);L=s(Ga,"IMG",{style:!0,src:!0,alt:!0}),Ga.forEach(t),Ja.forEach(t),Ne=f(e),q=s(e,"P",{});var Ua=r(q);Pt=l(Ua,"Around 600 lines removed after the migration. That\u2019s about a 12% reduction in code. My framework was already pretty lean so that's impressive."),Ua.forEach(t),Fe=f(e),W=s(e,"P",{});var qa=r(W);xt=l(qa,"Some of the code reduction was because there was a little duplicate code between pages, but most of it was just because SvelteKit removed the need for a lot of code. This is really great. As a solo founder, fewer lines of code to maintain and understand is a huge help."),qa.forEach(t),De=f(e),X=s(e,"H2",{});var Wa=r(X);Lt=l(Wa,"Performance"),Wa.forEach(t),Je=f(e),j=s(e,"FIGURE",{style:!0});var Xa=r(j);Q=s(Xa,"A",{href:!0});var Qa=r(Q);R=s(Qa,"IMG",{src:!0,style:!0,alt:!0}),Qa.forEach(t),Xa.forEach(t),Ge=f(e),V=s(e,"P",{});var Va=r(V);jt=l(Va,"I launched the SvelteKit-based app near that black line on the plot. It\u2019s a little hard to see with the spikes, but the overall performance of the SvelteKit-based app is a tiny bit worse. Before, the midday usage would pretty consistently top out around 20% CPU. Now midday usage is typically around 30%. Not a big deal, but will probably mean I need to upgrade the server sooner. So the SvelteKit team owes me some money \u{1F61B} jk"),Va.forEach(t),Ue=f(e),v=s(e,"P",{});var de=r(v);Rt=l(de,"I was actually concerned the performance would be much worse. In my local load testing with the "),we=s(de,"CODE",{});var Ya=r(we);Tt=l(Ya,"ab"),Ya.forEach(t),Ht=l(de," command-line utility the SvelteKit app had about "),ve=s(de,"EM",{});var Za=r(ve);Mt=l(Za,"half"),Za.forEach(t),Ot=l(de," the performance of my previous app for basic routes. Luckily in practice that didn\u2019t happen. Maybe my test was flawed."),de.forEach(t),qe=f(e),Y=s(e,"H2",{});var $a=r(Y);Ct=l($a,"Feelings"),$a.forEach(t),We=f(e),Z=s(e,"P",{});var ei=r(Z);zt=l(ei,"Overall, the migration and deploy went fairly smoothly. It was a lot of work to basically rewrite the app and make sure everything still worked, but I\u2019m happy I did it."),ei.forEach(t),Xe=f(e),$=s(e,"P",{});var ti=r($);Bt=l(ti,"Having switched over, I feel like my app is on a lot more solid foundation with SvelteKit. I feel fantastic developing with the framework and my app feels fantastic to use."),ti.forEach(t),Qe=f(e),ee=s(e,"P",{});var ai=r(ee);Nt=l(ai,"A few pain points I ran into working with SvelteKit:"),ai.forEach(t),Ve=f(e),p=s(e,"UL",{});var y=r(p);ye=s(y,"LI",{});var ii=r(ye);Ft=l(ii,"It was surprisingly difficult to get a REPL working nicely with SvelteKit. For business reasons I often have the need to run custom one-off commands in a REPL or script, but because of various import issues, creating a REPL that knew about my app and database was a challenge. I eventually got it to work, but it wasn\u2019t easy."),ii.forEach(t),Dt=f(y),be=s(y,"LI",{});var oi=r(be);Jt=l(oi,"Related to the above, SvelteKit is pretty new so there\u2019s not a lot about it on the internet yet. When I run into issues, there\u2019s less material I can search for that might have fixes. Luckily there was enough and the SvelteKit team was actually crazy responsive to bugs or forum posts I made, and this problem will diminish over time."),oi.forEach(t),Gt=f(y),b=s(y,"LI",{});var he=r(b);Ut=l(he,"One small but annoying issue is that when I load 3rd-party libraries in the "),ke=s(he,"CODE",{});var si=r(ke);qt=l(si,""),si.forEach(t),Wt=l(he," of a SvelteKit route over http(s), the script loads in a blocking way when rendered from the server and in a non-blocking way when rendered via client-side navigation. This caused my code to fail during client-side navigation but not server-side navigation and it\u2019s only through luck that I caught the failure before launch. "),te=s(he,"A",{href:!0});var li=r(te);Xt=l(li,"I made an issue about this"),li.forEach(t),Qt=l(he," that was closed as \u201Cwell that\u2019s just how it works\u201D and a nice suggestion about how I might achieve my goals instead. Still, It was kind of tricky to figure out how to make a workaround that worked in all cases."),he.forEach(t),Vt=f(y),ge=s(y,"LI",{});var ri=r(ge);Yt=l(ri,"As demonstrated earlier, real-world performance is a little worse than my previous custom framework. I\u2019m not sure why this is. Maybe it's SvelteKit or maybe it's something I'm doing. Either way, it\u2019s not so bad."),ri.forEach(t),Zt=f(y),T=s(y,"LI",{});var st=r(T);$t=l(st,"The naming conventions for routes is a little bonkers. Now when I\u2019m working on my app I have like 10 tabs open that all have the name "),Ie=s(st,"CODE",{});var ni=r(Ie);ea=l(ni,"+page.server.ts"),ni.forEach(t),ta=l(st,". \u{1F644} Does anyone know how I can make this better in vim?"),st.forEach(t),y.forEach(t),Ye=f(e),ae=s(e,"P",{});var di=r(ae);aa=l(di,"Some pleasures of working with SvelteKit:"),di.forEach(t),Ze=f(e),u=s(e,"UL",{});var c=r(u);Se=s(c,"LI",{});var hi=r(Se);ia=l(hi,"The developer experience is phenomenal. Hot reloading is so much nicer than what I was using previously and all the built-in commands for testing work great."),hi.forEach(t),oa=f(c),_e=s(c,"LI",{});var fi=r(_e);sa=l(fi,"I\u2019m really happy that SvelteKit allows parts of your app to be pre-rendered and others to be dynamic. I now pre-render my marketing pages (like this post!) which is great for performance. And the client-side routing also still works."),fi.forEach(t),la=f(c),Ee=s(c,"LI",{});var mi=r(Ee);ra=l(mi,"Speaking of which, the client-side routing was a real unexpected pleasure. I assumed it was just extraneous, but it really does make my app feel a lot snappier and it\u2019s just included automatically in SvelteKit without me having to do anything."),mi.forEach(t),na=f(c),Ke=s(c,"LI",{});var ui=r(Ke);da=l(ui,"Another unexpected pleasure was using TypeScript for static types and Playwright for end-to-end testing. Both give me an extra layer of confidence that my app is working as intended which is so important as a solo developer. Heck, I didn't set out to use Typescript or Playwright at all, but SvelteKit made them so easy to add that I just did it . And I'm so glad I did because I feel a lot more confident deploying code now."),ui.forEach(t),ha=f(c),Ae=s(c,"LI",{});var pi=r(Ae);fa=l(pi,"SvelteKit\u2019s documentation is already quite good, and I know the team is also working on an interactive tutorial which is great. Good reference documentation is so helpful."),pi.forEach(t),ma=f(c),k=s(c,"LI",{});var fe=r(k);ua=l(fe,"I really love how forms work in SvelteKit. For whatever reason, working with forms in any other framework always felt so tedious to me. But the "),ie=s(fe,"A",{href:!0});var ci=r(ie);pa=l(ci,"form actions API"),ci.forEach(t),ca=l(fe," means it\u2019s so easy to make boilerlate-free forms that work without client-side JavaScript but can be easily upgraded to AJAX with basically no extra code using "),Pe=s(fe,"CODE",{});var wi=r(Pe);oe=s(wi,"A",{href:!0});var vi=r(oe);wa=l(vi,"enhance"),vi.forEach(t),wi.forEach(t),va=l(fe,". Before the migration, I was getting a surprising number of error notification emails about people submitting forms on my site that didn\u2019t have JavaScript enabled. Now I get none, and all my forms work work with and without client-side JavaScript by default. And I actually enjoy creating them."),fe.forEach(t),c.forEach(t),$e=f(e),se=s(e,"P",{});var yi=r(se);ya=l(yi,"Having my app on SvelteKit also comes with some other potential benefits. If I ever want to bring in another developer, it will be much easier to get them up to speed with SvelteKit than my own custom framework. And for the same reason, if I ever want to sell my app I imagine it will be an easier sell that it\u2019s built on something well-supported and documented. Of course, this would be true for any well-known framework, but as I mentioned earlier I really love Svelte and dislike React so for me it\u2019s an easy choice."),yi.forEach(t),et=f(e),le=s(e,"P",{});var bi=r(le);ba=l(bi,"Overall, I\u2019m really happy with SvelteKit and want to extend my sincere thanks for the SvelteKit team for working so hard on it, taking our feedback, and sharing it with us for free. It really is amazing."),bi.forEach(t),tt=f(e),_=s(e,"P",{});var lt=r(_);ka=l(lt,"I love it so much, in fact, that I'm taking what I've learned in my own app and developing a "),re=s(lt,"A",{href:!0});var ki=r(re);ga=l(ki,"SvelteKit SaaS starter template"),ki.forEach(t),Ia=l(lt,"."),lt.forEach(t),this.h()},h(){document.title="Reflections on Migrating my SaaS to SvelteKit",H(E,"padding","0"),m(O,"href","https://extensionpay.com"),m(C,"href","https://svelte.dev/"),m(z,"href","https://svelte.dev/blog/announcing-sveltekit-1.0"),m(B,"href","https://kit.svelte.dev"),H(P,"width","645px"),_a(P.src,Ka=""+(me+"/articles/migrate-saas-sveltekit/lines_of_code_before.png"))||m(P,"src",Ka),m(P,"alt","Screenshot of cloc showing 4975 lines of code total"),m(J,"href",""+(me+"/articles/migrate-saas-sveltekit/lines_of_code_before.png")),H(A,"width","645px"),H(L,"width","647px"),_a(L.src,Aa=""+(me+"/articles/migrate-saas-sveltekit/lines_of_code_after.png"))||m(L,"src",Aa),m(L,"alt","Screenshot of cloc output showing 4397 lines of code total"),m(U,"href",""+(me+"/articles/migrate-saas-sveltekit/lines_of_code_after.png")),H(x,"width","647px"),_a(R.src,Pa=""+(me+"/articles/migrate-saas-sveltekit/performance.png"))||m(R,"src",Pa),H(R,"width","815px"),m(R,"alt","Plot of CPU performance over time. On April 2 there is a black vertical line indicating the time the new SvelteKit was deployed."),m(Q,"href",""+(me+"/articles/migrate-saas-sveltekit/performance.png")),H(j,"width","815px"),m(te,"href","https://github.com/sveltejs/kit/issues/9096"),m(ie,"href","https://kit.svelte.dev/docs/form-actions"),m(oe,"href","https://kit.svelte.dev/docs/form-actions#progressive-enhancement"),m(re,"href","https://sveltekitsaas.com")},m(e,n){d(e,M,n),d(e,E,n),a(E,ue),a(ue,rt),d(e,xe,n),d(e,g,n),a(g,nt),a(g,O),a(O,dt),a(g,ht),d(e,Le,n),d(e,I,n),a(I,ft),a(I,C),a(C,mt),a(I,ut),d(e,je,n),d(e,S,n),a(S,pt),a(S,z),a(z,ct),a(S,wt),d(e,Re,n),d(e,K,n),a(K,B),a(B,vt),a(K,yt),d(e,Te,n),d(e,N,n),a(N,bt),d(e,He,n),d(e,F,n),a(F,kt),d(e,Me,n),d(e,w,n),a(w,gt),a(w,pe),a(pe,It),a(w,St),a(w,ce),a(ce,_t),a(w,Et),d(e,Oe,n),d(e,D,n),a(D,Kt),d(e,Ce,n),d(e,A,n),a(A,J),a(J,P),d(e,ze,n),d(e,G,n),a(G,At),d(e,Be,n),d(e,x,n),a(x,U),a(U,L),d(e,Ne,n),d(e,q,n),a(q,Pt),d(e,Fe,n),d(e,W,n),a(W,xt),d(e,De,n),d(e,X,n),a(X,Lt),d(e,Je,n),d(e,j,n),a(j,Q),a(Q,R),d(e,Ge,n),d(e,V,n),a(V,jt),d(e,Ue,n),d(e,v,n),a(v,Rt),a(v,we),a(we,Tt),a(v,Ht),a(v,ve),a(ve,Mt),a(v,Ot),d(e,qe,n),d(e,Y,n),a(Y,Ct),d(e,We,n),d(e,Z,n),a(Z,zt),d(e,Xe,n),d(e,$,n),a($,Bt),d(e,Qe,n),d(e,ee,n),a(ee,Nt),d(e,Ve,n),d(e,p,n),a(p,ye),a(ye,Ft),a(p,Dt),a(p,be),a(be,Jt),a(p,Gt),a(p,b),a(b,Ut),a(b,ke),a(ke,qt),a(b,Wt),a(b,te),a(te,Xt),a(b,Qt),a(p,Vt),a(p,ge),a(ge,Yt),a(p,Zt),a(p,T),a(T,$t),a(T,Ie),a(Ie,ea),a(T,ta),d(e,Ye,n),d(e,ae,n),a(ae,aa),d(e,Ze,n),d(e,u,n),a(u,Se),a(Se,ia),a(u,oa),a(u,_e),a(_e,sa),a(u,la),a(u,Ee),a(Ee,ra),a(u,na),a(u,Ke),a(Ke,da),a(u,ha),a(u,Ae),a(Ae,fa),a(u,ma),a(u,k),a(k,ua),a(k,ie),a(ie,pa),a(k,ca),a(k,Pe),a(Pe,oe),a(oe,wa),a(k,va),d(e,$e,n),d(e,se,n),a(se,ya),d(e,et,n),d(e,le,n),a(le,ba),d(e,tt,n),d(e,_,n),a(_,ka),a(_,re),a(re,ga),a(_,Ia)},p:Ea,i:Ea,o:Ea,d(e){e&&t(M),e&&t(E),e&&t(xe),e&&t(g),e&&t(Le),e&&t(I),e&&t(je),e&&t(S),e&&t(Re),e&&t(K),e&&t(Te),e&&t(N),e&&t(He),e&&t(F),e&&t(Me),e&&t(w),e&&t(Oe),e&&t(D),e&&t(Ce),e&&t(A),e&&t(ze),e&&t(G),e&&t(Be),e&&t(x),e&&t(Ne),e&&t(q),e&&t(Fe),e&&t(W),e&&t(De),e&&t(X),e&&t(Je),e&&t(j),e&&t(Ge),e&&t(V),e&&t(Ue),e&&t(v),e&&t(qe),e&&t(Y),e&&t(We),e&&t(Z),e&&t(Xe),e&&t($),e&&t(Qe),e&&t(ee),e&&t(Ve),e&&t(p),e&&t(Ye),e&&t(ae),e&&t(Ze),e&&t(u),e&&t($e),e&&t(se),e&&t(et),e&&t(le),e&&t(tt),e&&t(_)}}}class xi extends Ii{constructor(M){super();Si(this,M,null,Ki,_i,{})}}export{xi as default}; 2 | -------------------------------------------------------------------------------- /docs/app_/pages/articles/sveltekit-github-pages-guide.svelte-194f2893.js: -------------------------------------------------------------------------------- 1 | import{S as ac,i as rc,s as uc,k as f,e as a,t as s,L as dc,d as t,m as p,c as r,a as c,h as i,f as y,b as o,M as st,g as d,J as l,K as Kr}from"../../chunks/vendor-ba5e527f.js";import{a as it}from"../../chunks/paths-396f020f.js";function cc(sc){let at,Je,wt,Es,Il,J,ws,Kl,q,_s,Al,B,Ps,Nl,m,Gs,_t,rt,ks,Ss,ut,gs,Cs,xl,C,Os,dt,Ds,Ts,Ll,z,Rs,jl,Q,Is,Hl,V,Ks,Ul,W,ct,As,Ns,Ml,X,qe,Pt,xs,Ls,Yl,Z,E,Gt,js,Hs,kt,Us,Ms,St,Ys,Ws,gt,Fs,Wl,$,Be,Ct,Js,qs,Fl,ee,g,Ot,Bs,zs,Dt,Qs,Vs,Tt,Xs,Zs,Jl,te,ze,Rt,$s,ei,ql,le,Qe,Ar,Bl,O,ti,It,li,oi,zl,D,si,Kt,ii,ai,Ql,oe,ri,Vl,se,ui,Xl,ie,At,di,Zl,ae,ci,re,fi,ue,Nt,pi,$l,de,ni,eo,ce,xt,hi,to,fe,bi,lo,pe,Lt,vi,oo,ne,Ve,Nr,so,he,yi,io,be,mi,ao,h,Ei,jt,wi,_i,Ht,Pi,Gi,Ut,ki,Si,ro,w,gi,Mt,Ci,Oi,Yt,Di,Ti,uo,T,Ri,Wt,Ii,Ki,co,ve,Xe,xr,fo,_,Ai,Ft,Ni,xi,Jt,Li,ji,po,ye,Ze,Lr,no,me,Hi,ho,Ee,Ui,bo,R,Mi,qt,Yi,Wi,vo,I,Fi,ft,Ji,qi,yo,we,Bt,Bi,mo,K,zi,zt,Qi,Vi,Eo,A,Xi,Qt,Zi,$i,wo,_e,Vt,jr=`import adapter from "@sveltejs/adapter-static"; 2 | // was "@sveltejs/adapter-auto" 3 | 4 | const dev = "production" === "development"; 5 | 6 | /** @type {import(""@sveltejs/kit").Config} */ 7 | const config = { 8 | kit: { 9 | adapter: adapter({ 10 | pages: "docs", 11 | assets: "docs" 12 | }), 13 | paths: { 14 | // change below to your repo name 15 | base: dev ? """ : "/your-repo-name", 16 | }, 17 | // hydrate the
element in src/app.html 18 | target: "#svelte" 19 | } 20 | }; 21 | 22 | export default config;`,ea,_o,b,ta,Xt,la,oa,Zt,sa,ia,$t,aa,ra,Po,n,ua,el,da,ca,tl,fa,pa,ll,na,ha,ol,ba,va,sl,ya,ma,Go,N,Ea,il,wa,_a,ko,F,Pa,al,Ga,So,x,ka,rl,Sa,ga,go,Pe,ul,Ca,Co,$e,Oo,Ge,Oa,Do,P,Da,dl,Ta,Ra,cl,Ia,Ka,To,ke,fl,Aa,Ro,G,Na,pl,xa,La,nl,ja,Ha,Io,L,Ua,hl,Ma,Ya,Ko,Se,bl,Wa,Ao,ge,Fa,No,Ce,vl,Ja,xo,Oe,qa,Lo,De,yl,Ba,jo,Te,za,Ho,Re,et,Hr,Uo,Ie,Qa,Mo,Ke,tt,Ur,Yo,Ae,Va,Wo,Ne,Xa,Fo,v,ml,El,Za,$a,wl,_l,er,tr,Pl,Gl,lr,Jo,xe,or,qo,j,sr,kl,ir,ar,Bo,H,rr,pt,ur,dr,zo,U,cr,Sl,fr,pr,Qo,Le,gl,nr,Vo,k,hr,Cl,br,vr,Ol,yr,mr,Xo,je,Er,Zo,He,wr,$o,Ue,Dl,_r,es,M,Pr,Tl,Gr,kr,ts,Me,lt,Mr,ls,Y,Sr,Rl,gr,Cr;return{c(){at=f(),Je=a("header"),wt=a("h1"),Es=s("How to Deploy SvelteKit Apps to Github Pages"),Il=f(),J=a("p"),ws=s("SvelteKit is an app framework based around the popular Svelte component library. SvelteKit is really awesome. You can deploy SvelteKit apps to any server-based, serverless, or even static files host you want!"),Kl=f(),q=a("p"),_s=s("In this guide we\u2019ll show you how you can deploy SvelteKit apps to Github Pages, a popular free way to host static files directly from a Github repository."),Al=f(),B=a("h2"),Ps=s("Getting Started"),Nl=f(),m=a("p"),Gs=s("Want to get up and running right away? Just use "),_t=a("strong"),rt=a("a"),ks=s("this Github template repository"),Ss=s(". It has everything you need already set up. Once you have that you can skip to the "),ut=a("a"),gs=s("Configure Github Pages"),Cs=s(" section below."),xl=f(),C=a("p"),Os=s("Already have a Github repository you want to use but not SvelteKit? Skip to the "),dt=a("a"),Ds=s("Install SvelteKit"),Ts=s(" section below."),Ll=f(),z=a("p"),Rs=s("Otherwise, you can continue with this section and learn how to create a SvelteKit Github repository from scratch. To do that we\u2019ll need two things \u2014 a Github repository and a new SvelteKit project."),jl=f(),Q=a("h2"),Is=s("Create a new Github repository"),Hl=f(),V=a("p"),Ks=s("First, let\u2019s make a new Github repository."),Ul=f(),W=a("p"),ct=a("a"),As=s("Here\u2019s a link to Github\u2019s create new repository page"),Ns=s(" (no repository will be made until you click the \u201CCreate repository\u201D button on that page). The settings you should choose are:"),Ml=f(),X=a("ul"),qe=a("li"),Pt=a("strong"),xs=s("Repository name"),Ls=s(` 23 | : Choose any name you like.`),Yl=f(),Z=a("ul"),E=a("li"),Gt=a("strong"),js=s("Public / Private:"),Hs=s(` 24 | If you have a free Github account, you\u2019ll need to choose `),kt=a("strong"),Us=s("Public"),Ms=s(` 25 | since Github only allows Github Pages hosting from public repositories for free accounts. If you have a paid Github Pro or Team account, you can choose either `),St=a("strong"),Ys=s("Public"),Ws=s(` 26 | or `),gt=a("strong"),Fs=s("Private."),Wl=f(),$=a("ul"),Be=a("li"),Ct=a("strong"),Js=s("Add a README file:"),qs=s(` 27 | Optional. We\u2019ll add a README later, but you can add one now if you like.`),Fl=f(),ee=a("ul"),g=a("li"),Ot=a("strong"),Bs=s("Add .gitignore:"),zs=f(),Dt=a("strong"),Qs=s("Checked"),Vs=s(` 28 | . Choose \u201C`),Tt=a("strong"),Xs=s("Node"),Zs=s(` 29 | \u201D from the \u201C.gitignore template\u201D dropdown. This will make it so that unnecessary files aren\u2019t checked into your repository.`),Jl=f(),te=a("ul"),ze=a("li"),Rt=a("strong"),$s=s("Choose a license"),ei=s(` 30 | : Optional. Choose any license you want, or none. You can always add one later if you\u2019re not sure.`),ql=f(),le=a("figure"),Qe=a("img"),Bl=f(),O=a("p"),ti=s("Once you\u2019re happy with your settings, click the "),It=a("strong"),li=s("Create repository"),oi=s(" button and you\u2019ll have a new blank Github repository."),zl=f(),D=a("p"),si=s("To get the repository on your computer, you can run "),Kt=a("code"),ii=s("git clone "),ai=s(" in a terminal or use your favorite git software."),Ql=f(),oe=a("h2"),ri=s("Install SvelteKit"),Vl=f(),se=a("p"),ui=s("Now we can install SvelteKit. Open a terminal to the Github repository on your computer and run the following command:"),Xl=f(),ie=a("pre"),At=a("code"),di=s("npm create svelte@latest"),Zl=f(),ae=a("p"),ci=s("You\u2019ll be walked through the configuration options for SvelteKit. You can choose any settings you want."),re=a("p"),fi=s("Then we need to install all the necessary SvelteKit packages:"),ue=a("pre"),Nt=a("code"),pi=s("npm install"),$l=f(),de=a("p"),ni=s("Then we can add all these files to our Git repository:"),eo=f(),ce=a("pre"),xt=a("code"),hi=s('git add -A && git commit -m "Initial commit"'),to=f(),fe=a("p"),bi=s("And finally run the development server to test that everything is working:"),lo=f(),pe=a("pre"),Lt=a("code"),vi=s("npm run dev -- --open"),oo=f(),ne=a("figure"),Ve=a("img"),so=f(),he=a("p"),yi=s("Yay!"),io=f(),be=a("h2"),mi=s("Configure Github Pages"),ao=f(),h=a("p"),Ei=s("Github Pages only allows sites to be hosted from either the root of a repository ("),jt=a("code"),wi=s("/"),_i=s(") or a "),Ht=a("code"),Pi=s("/docs"),Gi=s(" folder. Sites can also be hosted from any branch of a repository, but again, only in the root or "),Ut=a("code"),ki=s("/docs"),Si=s(" folder."),ro=f(),w=a("p"),gi=s("To keep things simple and clean, we\u2019re going to put our static site in a "),Mt=a("code"),Ci=s("/docs"),Oi=s(" folder in the "),Yt=a("code"),Di=s("main"),Ti=s(" branch."),uo=f(),T=a("p"),Ri=s("In your Github repository settings, navigate to the "),Wt=a("code"),Ii=s("Pages"),Ki=s(" section. It should look something like this:"),co=f(),ve=a("figure"),Xe=a("img"),fo=f(),_=a("p"),Ai=s("In the \u201CSource\u201D section there\u2019s a dropdown that currently says \u201CNone\u201D. Instead of None, choose the "),Ft=a("code"),Ni=s("main"),xi=s(" branch and then the "),Jt=a("code"),Li=s("/docs"),ji=s(" folder, as shown below."),po=f(),ye=a("figure"),Ze=a("img"),no=f(),me=a("p"),Hi=s("Click \u201CSave\u201D and the page will reload and tell you that your site is ready to be served."),ho=f(),Ee=a("h2"),Ui=s("Configure SvelteKit for Github Pages"),bo=f(),R=a("p"),Mi=s("Now we have to tell SvelteKit that we want our static site to go in the "),qt=a("code"),Yi=s("/docs"),Wi=s(" folder."),vo=f(),I=a("p"),Fi=s("First, we need to install the "),ft=a("a"),Ji=s("SvelteKit static adapter"),qi=s(" which will output our app as a static set of files instead of a dynamic app:"),yo=f(),we=a("pre"),Bt=a("code"),Bi=s("npm install @sveltejs/adapter-static --save-dev"),mo=f(),K=a("p"),zi=s("(You can also run "),zt=a("code"),Qi=s("npm uninstall @sveltejs/adapter-auto --save-dev"),Vi=s(" to remove the unnecessary auto adapter from your package if you want.)"),Eo=f(),A=a("p"),Xi=s("Edit "),Qt=a("code"),Zi=s("svelte.config.js"),$i=s(" to use the static adapter as shown below:"),wo=f(),_e=a("pre"),Vt=a("code"),ea=s(jr),_o=f(),b=a("p"),ta=s("There are a few things we changed from the default sveltekit config file. We imported "),Xt=a("code"),la=s("adapter-static"),oa=s(" instead of "),Zt=a("code"),sa=s("adapter-auto"),ia=s(" at the top of the file. We also configured the adapter to use the "),$t=a("code"),aa=s("docs"),ra=s(" folder as the output target for both our site\u2019s pages as well as static assets like stylesheets, images, and so on."),Po=f(),n=a("p"),ua=s("And finally we also added a "),el=a("code"),da=s("paths"),ca=s(" configuration option which tells SvelteKit the URL path of the SvelteKit site. By default, Github Pages will host your static site at "),tl=a("code"),fa=s("username.github.io/your-repo-name"),pa=s(". So we need to configure SvelteKit so that it knows its URLs should fall under "),ll=a("code"),na=s("/your-repo-name"),ha=s(" when hosted on Github Pages, but not when testing our site on our own computers (it\u2019s hosted at "),ol=a("code"),ba=s("localhost:3000/"),va=s(" during testing with "),sl=a("code"),ya=s("npm run dev"),ma=s(")."),Go=f(),N=a("p"),Ea=s("You can run "),il=a("code"),wa=s("npm run dev"),_a=s(" again to make sure everything is still working before going to the next step."),ko=f(),F=a("h3"),Pa=s("Add a blank .nojekyll file in "),al=a("code"),Ga=s("static"),So=f(),x=a("p"),ka=s("To configure Github Pages so that it doesn\u2019t use the Jekyll static site generator (which it does by default), we simply have to create a blank "),rl=a("code"),Sa=s(".nojekyll"),ga=s(" file in the static folder. You can do that any way you like, but here\u2019s how you can do it from the command line:"),go=f(),Pe=a("pre"),ul=a("code"),Ca=s("touch static/.nojekyll && git add static/.nojekyll"),Co=f(),$e=a("p"),Oo=f(),Ge=a("h2"),Oa=s("Deploy your site on Github Pages"),Do=f(),P=a("p"),Da=s("So we have a demo site that runs fine on our computer with "),dl=a("code"),Ta=s("npm run dev"),Ra=s(" but we want to deploy it on Github pages. To do that, we use SvelteKit\u2019s "),cl=a("code"),Ia=s("build"),Ka=s(" command."),To=f(),ke=a("pre"),fl=a("code"),Aa=s("npm run build"),Ro=f(),G=a("p"),Na=s("This command will create a "),pl=a("code"),xa=s("docs"),La=s(" folder if it doesn\u2019t exist and put all the HTML, CSS, and JavaScript files there. It will also delete and recreate the "),nl=a("code"),ja=s("docs"),Ha=s(" folder every time so make sure not to put any other files in there or they might be deleted."),Io=f(),L=a("p"),Ua=s("Don\u2019t forget to add the "),hl=a("code"),Ma=s("docs"),Ya=s(" folder to your repository:"),Ko=f(),Se=a("pre"),bl=a("code"),Wa=s("git add docs"),Ao=f(),ge=a("p"),Fa=s("Now if you commit your changes..."),No=f(),Ce=a("pre"),vl=a("code"),Ja=s('git commit -m "add /docs"'),xo=f(),Oe=a("p"),qa=s("...and push them to Github..."),Lo=f(),De=a("pre"),yl=a("code"),Ba=s("git push origin main"),jo=f(),Te=a("p"),za=s("Github will automatically deploy your site! In the Github Pages settings you\u2019ll see this right after you push:"),Ho=f(),Re=a("figure"),et=a("img"),Uo=f(),Ie=a("p"),Qa=s("If you wait a little bit, eventually that page will look like this:"),Mo=f(),Ke=a("figure"),tt=a("img"),Yo=f(),Ae=a("p"),Va=s("Click the link to see your new site running with Github Pages!"),Wo=f(),Ne=a("p"),Xa=s("That is generally the process every time you make a change:"),Fo=f(),v=a("ol"),ml=a("li"),El=a("code"),Za=s("npm run build"),$a=f(),wl=a("li"),_l=a("code"),er=s("git add docs && git commit"),tr=f(),Pl=a("li"),Gl=a("code"),lr=s("git push origin main"),Jo=f(),xe=a("h2"),or=s("Custom Domains for Github Pages"),qo=f(),j=a("p"),sr=s("There is one last thing you need to do "),kl=a("strong"),ir=s("only"),ar=s(" if you want to host your SvelteKit site on a custom domain through Github Pages."),Bo=f(),H=a("p"),rr=s("First, read about how to set up custom domains on the "),pt=a("a"),ur=s("Github Pages guide here"),dr=s("."),zo=f(),U=a("p"),cr=s("The main thing you\u2019ll need in your SvelteKit repository is a "),Sl=a("code"),fr=s("CNAME"),pr=s(" file containing the URL of the site you want:"),Qo=f(),Le=a("pre"),gl=a("code"),nr=s("yourcustomdomain.com"),Vo=f(),k=a("p"),hr=s("Put this "),Cl=a("code"),br=s("CNAME"),vr=s(" file in the "),Ol=a("code"),yr=s("static"),mr=s(" directory of your SvelteKit project and you\u2019re good to go!"),Xo=f(),je=a("h3"),Er=s("Addendum: Getting HTTP, HTTPS, and WWW working with custom domains on Github"),Zo=f(),He=a("p"),wr=s("This isn\u2019t a SvelteKit-specific thing but it\u2019s a little confusing how to properly configure DNS for Github Pages so that all the following custom domain URLs will work:"),$o=f(),Ue=a("pre"),Dl=a("code"),_r=s(`example.com 31 | www.example.com 32 | http://example.com 33 | http://www.example.com 34 | https://example.com 35 | https://www.example.com`),es=f(),M=a("p"),Pr=s("To make all those work, it\u2019s pretty simple. Just add \u201CA Records\u201D pointing toward Github\u2019s IP addresses, as well as a \u201CCNAME Record\u201D for www pointed at "),Tl=a("code"),Gr=s("username.github.io"),kr=s(":"),ts=f(),Me=a("figure"),lt=a("img"),ls=f(),Y=a("p"),Sr=s("You should also choose \u201CEnforce HTTPS\u201D from the Github Pages settings page. With this setup, every URL from the list above will automatically go to "),Rl=a("code"),gr=s("https://example.com"),Cr=s("."),this.h()},l(e){dc('[data-svelte="svelte-g9dflq"]',document.head).forEach(t),at=p(e),Je=r(e,"HEADER",{style:!0});var Yr=c(Je);wt=r(Yr,"H1",{});var Wr=c(wt);Es=i(Wr,"How to Deploy SvelteKit Apps to Github Pages"),Wr.forEach(t),Yr.forEach(t),Il=p(e),J=r(e,"P",{id:!0,class:!0});var Fr=c(J);ws=i(Fr,"SvelteKit is an app framework based around the popular Svelte component library. SvelteKit is really awesome. You can deploy SvelteKit apps to any server-based, serverless, or even static files host you want!"),Fr.forEach(t),Kl=p(e),q=r(e,"P",{id:!0,class:!0});var Jr=c(q);_s=i(Jr,"In this guide we\u2019ll show you how you can deploy SvelteKit apps to Github Pages, a popular free way to host static files directly from a Github repository."),Jr.forEach(t),Al=p(e),B=r(e,"H2",{id:!0,class:!0});var qr=c(B);Ps=i(qr,"Getting Started"),qr.forEach(t),Nl=p(e),m=r(e,"P",{id:!0,class:!0});var nt=c(m);Gs=i(nt,"Want to get up and running right away? Just use "),_t=r(nt,"STRONG",{});var Br=c(_t);rt=r(Br,"A",{href:!0});var zr=c(rt);ks=i(zr,"this Github template repository"),zr.forEach(t),Br.forEach(t),Ss=i(nt,". It has everything you need already set up. Once you have that you can skip to the "),ut=r(nt,"A",{href:!0});var Qr=c(ut);gs=i(Qr,"Configure Github Pages"),Qr.forEach(t),Cs=i(nt," section below."),nt.forEach(t),xl=p(e),C=r(e,"P",{id:!0,class:!0});var os=c(C);Os=i(os,"Already have a Github repository you want to use but not SvelteKit? Skip to the "),dt=r(os,"A",{href:!0});var Vr=c(dt);Ds=i(Vr,"Install SvelteKit"),Vr.forEach(t),Ts=i(os," section below."),os.forEach(t),Ll=p(e),z=r(e,"P",{id:!0,class:!0});var Xr=c(z);Rs=i(Xr,"Otherwise, you can continue with this section and learn how to create a SvelteKit Github repository from scratch. To do that we\u2019ll need two things \u2014 a Github repository and a new SvelteKit project."),Xr.forEach(t),jl=p(e),Q=r(e,"H2",{id:!0,class:!0});var Zr=c(Q);Is=i(Zr,"Create a new Github repository"),Zr.forEach(t),Hl=p(e),V=r(e,"P",{id:!0,class:!0});var $r=c(V);Ks=i($r,"First, let\u2019s make a new Github repository."),$r.forEach(t),Ul=p(e),W=r(e,"P",{id:!0,class:!0});var Or=c(W);ct=r(Or,"A",{href:!0});var eu=c(ct);As=i(eu,"Here\u2019s a link to Github\u2019s create new repository page"),eu.forEach(t),Ns=i(Or," (no repository will be made until you click the \u201CCreate repository\u201D button on that page). The settings you should choose are:"),Or.forEach(t),Ml=p(e),X=r(e,"UL",{id:!0,class:!0});var tu=c(X);qe=r(tu,"LI",{style:!0});var Dr=c(qe);Pt=r(Dr,"STRONG",{});var lu=c(Pt);xs=i(lu,"Repository name"),lu.forEach(t),Ls=i(Dr,` 36 | : Choose any name you like.`),Dr.forEach(t),tu.forEach(t),Yl=p(e),Z=r(e,"UL",{id:!0,class:!0});var ou=c(Z);E=r(ou,"LI",{style:!0});var Ye=c(E);Gt=r(Ye,"STRONG",{});var su=c(Gt);js=i(su,"Public / Private:"),su.forEach(t),Hs=i(Ye,` 37 | If you have a free Github account, you\u2019ll need to choose `),kt=r(Ye,"STRONG",{});var iu=c(kt);Us=i(iu,"Public"),iu.forEach(t),Ms=i(Ye,` 38 | since Github only allows Github Pages hosting from public repositories for free accounts. If you have a paid Github Pro or Team account, you can choose either `),St=r(Ye,"STRONG",{});var au=c(St);Ys=i(au,"Public"),au.forEach(t),Ws=i(Ye,` 39 | or `),gt=r(Ye,"STRONG",{});var ru=c(gt);Fs=i(ru,"Private."),ru.forEach(t),Ye.forEach(t),ou.forEach(t),Wl=p(e),$=r(e,"UL",{id:!0,class:!0});var uu=c($);Be=r(uu,"LI",{style:!0});var Tr=c(Be);Ct=r(Tr,"STRONG",{});var du=c(Ct);Js=i(du,"Add a README file:"),du.forEach(t),qs=i(Tr,` 40 | Optional. We\u2019ll add a README later, but you can add one now if you like.`),Tr.forEach(t),uu.forEach(t),Fl=p(e),ee=r(e,"UL",{id:!0,class:!0});var cu=c(ee);g=r(cu,"LI",{style:!0});var ot=c(g);Ot=r(ot,"STRONG",{});var fu=c(Ot);Bs=i(fu,"Add .gitignore:"),fu.forEach(t),zs=p(ot),Dt=r(ot,"STRONG",{});var pu=c(Dt);Qs=i(pu,"Checked"),pu.forEach(t),Vs=i(ot,` 41 | . Choose \u201C`),Tt=r(ot,"STRONG",{});var nu=c(Tt);Xs=i(nu,"Node"),nu.forEach(t),Zs=i(ot,` 42 | \u201D from the \u201C.gitignore template\u201D dropdown. This will make it so that unnecessary files aren\u2019t checked into your repository.`),ot.forEach(t),cu.forEach(t),Jl=p(e),te=r(e,"UL",{id:!0,class:!0});var hu=c(te);ze=r(hu,"LI",{style:!0});var Rr=c(ze);Rt=r(Rr,"STRONG",{});var bu=c(Rt);$s=i(bu,"Choose a license"),bu.forEach(t),ei=i(Rr,` 43 | : Optional. Choose any license you want, or none. You can always add one later if you\u2019re not sure.`),Rr.forEach(t),hu.forEach(t),ql=p(e),le=r(e,"FIGURE",{id:!0,class:!0});var vu=c(le);Qe=r(vu,"IMG",{style:!0,src:!0,alt:!0}),vu.forEach(t),Bl=p(e),O=r(e,"P",{id:!0,class:!0});var ss=c(O);ti=i(ss,"Once you\u2019re happy with your settings, click the "),It=r(ss,"STRONG",{});var yu=c(It);li=i(yu,"Create repository"),yu.forEach(t),oi=i(ss," button and you\u2019ll have a new blank Github repository."),ss.forEach(t),zl=p(e),D=r(e,"P",{id:!0,class:!0});var is=c(D);si=i(is,"To get the repository on your computer, you can run "),Kt=r(is,"CODE",{});var mu=c(Kt);ii=i(mu,"git clone "),mu.forEach(t),ai=i(is," in a terminal or use your favorite git software."),is.forEach(t),Ql=p(e),oe=r(e,"H2",{id:!0,class:!0});var Eu=c(oe);ri=i(Eu,"Install SvelteKit"),Eu.forEach(t),Vl=p(e),se=r(e,"P",{id:!0,class:!0});var wu=c(se);ui=i(wu,"Now we can install SvelteKit. Open a terminal to the Github repository on your computer and run the following command:"),wu.forEach(t),Xl=p(e),ie=r(e,"PRE",{id:!0,class:!0});var _u=c(ie);At=r(_u,"CODE",{});var Pu=c(At);di=i(Pu,"npm create svelte@latest"),Pu.forEach(t),_u.forEach(t),Zl=p(e),ae=r(e,"P",{id:!0,class:!0});var Gu=c(ae);ci=i(Gu,"You\u2019ll be walked through the configuration options for SvelteKit. You can choose any settings you want."),Gu.forEach(t),re=r(e,"P",{id:!0,class:!0});var ku=c(re);fi=i(ku,"Then we need to install all the necessary SvelteKit packages:"),ku.forEach(t),ue=r(e,"PRE",{id:!0,class:!0});var Su=c(ue);Nt=r(Su,"CODE",{});var gu=c(Nt);pi=i(gu,"npm install"),gu.forEach(t),Su.forEach(t),$l=p(e),de=r(e,"P",{id:!0,class:!0});var Cu=c(de);ni=i(Cu,"Then we can add all these files to our Git repository:"),Cu.forEach(t),eo=p(e),ce=r(e,"PRE",{id:!0,class:!0});var Ou=c(ce);xt=r(Ou,"CODE",{});var Du=c(xt);hi=i(Du,'git add -A && git commit -m "Initial commit"'),Du.forEach(t),Ou.forEach(t),to=p(e),fe=r(e,"P",{id:!0,class:!0});var Tu=c(fe);bi=i(Tu,"And finally run the development server to test that everything is working:"),Tu.forEach(t),lo=p(e),pe=r(e,"PRE",{id:!0,class:!0});var Ru=c(pe);Lt=r(Ru,"CODE",{});var Iu=c(Lt);vi=i(Iu,"npm run dev -- --open"),Iu.forEach(t),Ru.forEach(t),oo=p(e),ne=r(e,"FIGURE",{id:!0,class:!0});var Ku=c(ne);Ve=r(Ku,"IMG",{style:!0,src:!0,alt:!0}),Ku.forEach(t),so=p(e),he=r(e,"P",{id:!0,class:!0});var Au=c(he);yi=i(Au,"Yay!"),Au.forEach(t),io=p(e),be=r(e,"H2",{id:!0,class:!0});var Nu=c(be);mi=i(Nu,"Configure Github Pages"),Nu.forEach(t),ao=p(e),h=r(e,"P",{id:!0,class:!0});var We=c(h);Ei=i(We,"Github Pages only allows sites to be hosted from either the root of a repository ("),jt=r(We,"CODE",{});var xu=c(jt);wi=i(xu,"/"),xu.forEach(t),_i=i(We,") or a "),Ht=r(We,"CODE",{});var Lu=c(Ht);Pi=i(Lu,"/docs"),Lu.forEach(t),Gi=i(We," folder. Sites can also be hosted from any branch of a repository, but again, only in the root or "),Ut=r(We,"CODE",{});var ju=c(Ut);ki=i(ju,"/docs"),ju.forEach(t),Si=i(We," folder."),We.forEach(t),ro=p(e),w=r(e,"P",{id:!0,class:!0});var ht=c(w);gi=i(ht,"To keep things simple and clean, we\u2019re going to put our static site in a "),Mt=r(ht,"CODE",{});var Hu=c(Mt);Ci=i(Hu,"/docs"),Hu.forEach(t),Oi=i(ht," folder in the "),Yt=r(ht,"CODE",{});var Uu=c(Yt);Di=i(Uu,"main"),Uu.forEach(t),Ti=i(ht," branch."),ht.forEach(t),uo=p(e),T=r(e,"P",{id:!0,class:!0});var as=c(T);Ri=i(as,"In your Github repository settings, navigate to the "),Wt=r(as,"CODE",{});var Mu=c(Wt);Ii=i(Mu,"Pages"),Mu.forEach(t),Ki=i(as," section. It should look something like this:"),as.forEach(t),co=p(e),ve=r(e,"FIGURE",{id:!0,class:!0});var Yu=c(ve);Xe=r(Yu,"IMG",{style:!0,src:!0,alt:!0}),Yu.forEach(t),fo=p(e),_=r(e,"P",{id:!0,class:!0});var bt=c(_);Ai=i(bt,"In the \u201CSource\u201D section there\u2019s a dropdown that currently says \u201CNone\u201D. Instead of None, choose the "),Ft=r(bt,"CODE",{});var Wu=c(Ft);Ni=i(Wu,"main"),Wu.forEach(t),xi=i(bt," branch and then the "),Jt=r(bt,"CODE",{});var Fu=c(Jt);Li=i(Fu,"/docs"),Fu.forEach(t),ji=i(bt," folder, as shown below."),bt.forEach(t),po=p(e),ye=r(e,"FIGURE",{id:!0,class:!0});var Ju=c(ye);Ze=r(Ju,"IMG",{style:!0,src:!0,alt:!0}),Ju.forEach(t),no=p(e),me=r(e,"P",{id:!0,class:!0});var qu=c(me);Hi=i(qu,"Click \u201CSave\u201D and the page will reload and tell you that your site is ready to be served."),qu.forEach(t),ho=p(e),Ee=r(e,"H2",{id:!0,class:!0});var Bu=c(Ee);Ui=i(Bu,"Configure SvelteKit for Github Pages"),Bu.forEach(t),bo=p(e),R=r(e,"P",{id:!0,class:!0});var rs=c(R);Mi=i(rs,"Now we have to tell SvelteKit that we want our static site to go in the "),qt=r(rs,"CODE",{});var zu=c(qt);Yi=i(zu,"/docs"),zu.forEach(t),Wi=i(rs," folder."),rs.forEach(t),vo=p(e),I=r(e,"P",{id:!0,class:!0});var us=c(I);Fi=i(us,"First, we need to install the "),ft=r(us,"A",{href:!0});var Qu=c(ft);Ji=i(Qu,"SvelteKit static adapter"),Qu.forEach(t),qi=i(us," which will output our app as a static set of files instead of a dynamic app:"),us.forEach(t),yo=p(e),we=r(e,"PRE",{id:!0,class:!0});var Vu=c(we);Bt=r(Vu,"CODE",{});var Xu=c(Bt);Bi=i(Xu,"npm install @sveltejs/adapter-static --save-dev"),Xu.forEach(t),Vu.forEach(t),mo=p(e),K=r(e,"P",{id:!0,class:!0});var ds=c(K);zi=i(ds,"(You can also run "),zt=r(ds,"CODE",{});var Zu=c(zt);Qi=i(Zu,"npm uninstall @sveltejs/adapter-auto --save-dev"),Zu.forEach(t),Vi=i(ds," to remove the unnecessary auto adapter from your package if you want.)"),ds.forEach(t),Eo=p(e),A=r(e,"P",{id:!0,class:!0});var cs=c(A);Xi=i(cs,"Edit "),Qt=r(cs,"CODE",{});var $u=c(Qt);Zi=i($u,"svelte.config.js"),$u.forEach(t),$i=i(cs," to use the static adapter as shown below:"),cs.forEach(t),wo=p(e),_e=r(e,"PRE",{id:!0,class:!0});var ed=c(_e);Vt=r(ed,"CODE",{});var td=c(Vt);ea=i(td,jr),td.forEach(t),ed.forEach(t),_o=p(e),b=r(e,"P",{id:!0,class:!0});var Fe=c(b);ta=i(Fe,"There are a few things we changed from the default sveltekit config file. We imported "),Xt=r(Fe,"CODE",{});var ld=c(Xt);la=i(ld,"adapter-static"),ld.forEach(t),oa=i(Fe," instead of "),Zt=r(Fe,"CODE",{});var od=c(Zt);sa=i(od,"adapter-auto"),od.forEach(t),ia=i(Fe," at the top of the file. We also configured the adapter to use the "),$t=r(Fe,"CODE",{});var sd=c($t);aa=i(sd,"docs"),sd.forEach(t),ra=i(Fe," folder as the output target for both our site\u2019s pages as well as static assets like stylesheets, images, and so on."),Fe.forEach(t),Po=p(e),n=r(e,"P",{id:!0,class:!0});var S=c(n);ua=i(S,"And finally we also added a "),el=r(S,"CODE",{});var id=c(el);da=i(id,"paths"),id.forEach(t),ca=i(S," configuration option which tells SvelteKit the URL path of the SvelteKit site. By default, Github Pages will host your static site at "),tl=r(S,"CODE",{});var ad=c(tl);fa=i(ad,"username.github.io/your-repo-name"),ad.forEach(t),pa=i(S,". So we need to configure SvelteKit so that it knows its URLs should fall under "),ll=r(S,"CODE",{});var rd=c(ll);na=i(rd,"/your-repo-name"),rd.forEach(t),ha=i(S," when hosted on Github Pages, but not when testing our site on our own computers (it\u2019s hosted at "),ol=r(S,"CODE",{});var ud=c(ol);ba=i(ud,"localhost:3000/"),ud.forEach(t),va=i(S," during testing with "),sl=r(S,"CODE",{});var dd=c(sl);ya=i(dd,"npm run dev"),dd.forEach(t),ma=i(S,")."),S.forEach(t),Go=p(e),N=r(e,"P",{id:!0,class:!0});var fs=c(N);Ea=i(fs,"You can run "),il=r(fs,"CODE",{});var cd=c(il);wa=i(cd,"npm run dev"),cd.forEach(t),_a=i(fs," again to make sure everything is still working before going to the next step."),fs.forEach(t),ko=p(e),F=r(e,"H3",{id:!0,class:!0});var Ir=c(F);Pa=i(Ir,"Add a blank .nojekyll file in "),al=r(Ir,"CODE",{});var fd=c(al);Ga=i(fd,"static"),fd.forEach(t),Ir.forEach(t),So=p(e),x=r(e,"P",{id:!0,class:!0});var ps=c(x);ka=i(ps,"To configure Github Pages so that it doesn\u2019t use the Jekyll static site generator (which it does by default), we simply have to create a blank "),rl=r(ps,"CODE",{});var pd=c(rl);Sa=i(pd,".nojekyll"),pd.forEach(t),ga=i(ps," file in the static folder. You can do that any way you like, but here\u2019s how you can do it from the command line:"),ps.forEach(t),go=p(e),Pe=r(e,"PRE",{id:!0,class:!0});var nd=c(Pe);ul=r(nd,"CODE",{});var hd=c(ul);Ca=i(hd,"touch static/.nojekyll && git add static/.nojekyll"),hd.forEach(t),nd.forEach(t),Co=p(e),$e=r(e,"P",{id:!0,class:!0});var ic=c($e);ic.forEach(t),Oo=p(e),Ge=r(e,"H2",{id:!0,class:!0});var bd=c(Ge);Oa=i(bd,"Deploy your site on Github Pages"),bd.forEach(t),Do=p(e),P=r(e,"P",{id:!0,class:!0});var vt=c(P);Da=i(vt,"So we have a demo site that runs fine on our computer with "),dl=r(vt,"CODE",{});var vd=c(dl);Ta=i(vd,"npm run dev"),vd.forEach(t),Ra=i(vt," but we want to deploy it on Github pages. To do that, we use SvelteKit\u2019s "),cl=r(vt,"CODE",{});var yd=c(cl);Ia=i(yd,"build"),yd.forEach(t),Ka=i(vt," command."),vt.forEach(t),To=p(e),ke=r(e,"PRE",{id:!0,class:!0});var md=c(ke);fl=r(md,"CODE",{});var Ed=c(fl);Aa=i(Ed,"npm run build"),Ed.forEach(t),md.forEach(t),Ro=p(e),G=r(e,"P",{id:!0,class:!0});var yt=c(G);Na=i(yt,"This command will create a "),pl=r(yt,"CODE",{});var wd=c(pl);xa=i(wd,"docs"),wd.forEach(t),La=i(yt," folder if it doesn\u2019t exist and put all the HTML, CSS, and JavaScript files there. It will also delete and recreate the "),nl=r(yt,"CODE",{});var _d=c(nl);ja=i(_d,"docs"),_d.forEach(t),Ha=i(yt," folder every time so make sure not to put any other files in there or they might be deleted."),yt.forEach(t),Io=p(e),L=r(e,"P",{id:!0,class:!0});var ns=c(L);Ua=i(ns,"Don\u2019t forget to add the "),hl=r(ns,"CODE",{});var Pd=c(hl);Ma=i(Pd,"docs"),Pd.forEach(t),Ya=i(ns," folder to your repository:"),ns.forEach(t),Ko=p(e),Se=r(e,"PRE",{id:!0,class:!0});var Gd=c(Se);bl=r(Gd,"CODE",{});var kd=c(bl);Wa=i(kd,"git add docs"),kd.forEach(t),Gd.forEach(t),Ao=p(e),ge=r(e,"P",{id:!0,class:!0});var Sd=c(ge);Fa=i(Sd,"Now if you commit your changes..."),Sd.forEach(t),No=p(e),Ce=r(e,"PRE",{id:!0,class:!0});var gd=c(Ce);vl=r(gd,"CODE",{});var Cd=c(vl);Ja=i(Cd,'git commit -m "add /docs"'),Cd.forEach(t),gd.forEach(t),xo=p(e),Oe=r(e,"P",{id:!0,class:!0});var Od=c(Oe);qa=i(Od,"...and push them to Github..."),Od.forEach(t),Lo=p(e),De=r(e,"PRE",{id:!0,class:!0});var Dd=c(De);yl=r(Dd,"CODE",{});var Td=c(yl);Ba=i(Td,"git push origin main"),Td.forEach(t),Dd.forEach(t),jo=p(e),Te=r(e,"P",{id:!0,class:!0});var Rd=c(Te);za=i(Rd,"Github will automatically deploy your site! In the Github Pages settings you\u2019ll see this right after you push:"),Rd.forEach(t),Ho=p(e),Re=r(e,"FIGURE",{id:!0,class:!0});var Id=c(Re);et=r(Id,"IMG",{style:!0,src:!0,alt:!0}),Id.forEach(t),Uo=p(e),Ie=r(e,"P",{id:!0,class:!0});var Kd=c(Ie);Qa=i(Kd,"If you wait a little bit, eventually that page will look like this:"),Kd.forEach(t),Mo=p(e),Ke=r(e,"FIGURE",{id:!0,class:!0});var Ad=c(Ke);tt=r(Ad,"IMG",{style:!0,src:!0,alt:!0}),Ad.forEach(t),Yo=p(e),Ae=r(e,"P",{id:!0,class:!0});var Nd=c(Ae);Va=i(Nd,"Click the link to see your new site running with Github Pages!"),Nd.forEach(t),Wo=p(e),Ne=r(e,"P",{id:!0,class:!0});var xd=c(Ne);Xa=i(xd,"That is generally the process every time you make a change:"),xd.forEach(t),Fo=p(e),v=r(e,"OL",{type:!0,id:!0,class:!0,start:!0});var mt=c(v);ml=r(mt,"LI",{});var Ld=c(ml);El=r(Ld,"CODE",{});var jd=c(El);Za=i(jd,"npm run build"),jd.forEach(t),Ld.forEach(t),$a=p(mt),wl=r(mt,"LI",{});var Hd=c(wl);_l=r(Hd,"CODE",{});var Ud=c(_l);er=i(Ud,"git add docs && git commit"),Ud.forEach(t),Hd.forEach(t),tr=p(mt),Pl=r(mt,"LI",{});var Md=c(Pl);Gl=r(Md,"CODE",{});var Yd=c(Gl);lr=i(Yd,"git push origin main"),Yd.forEach(t),Md.forEach(t),mt.forEach(t),Jo=p(e),xe=r(e,"H2",{id:!0,class:!0});var Wd=c(xe);or=i(Wd,"Custom Domains for Github Pages"),Wd.forEach(t),qo=p(e),j=r(e,"P",{id:!0,class:!0});var hs=c(j);sr=i(hs,"There is one last thing you need to do "),kl=r(hs,"STRONG",{});var Fd=c(kl);ir=i(Fd,"only"),Fd.forEach(t),ar=i(hs," if you want to host your SvelteKit site on a custom domain through Github Pages."),hs.forEach(t),Bo=p(e),H=r(e,"P",{id:!0,class:!0});var bs=c(H);rr=i(bs,"First, read about how to set up custom domains on the "),pt=r(bs,"A",{href:!0});var Jd=c(pt);ur=i(Jd,"Github Pages guide here"),Jd.forEach(t),dr=i(bs,"."),bs.forEach(t),zo=p(e),U=r(e,"P",{id:!0,class:!0});var vs=c(U);cr=i(vs,"The main thing you\u2019ll need in your SvelteKit repository is a "),Sl=r(vs,"CODE",{});var qd=c(Sl);fr=i(qd,"CNAME"),qd.forEach(t),pr=i(vs," file containing the URL of the site you want:"),vs.forEach(t),Qo=p(e),Le=r(e,"PRE",{id:!0,class:!0});var Bd=c(Le);gl=r(Bd,"CODE",{});var zd=c(gl);nr=i(zd,"yourcustomdomain.com"),zd.forEach(t),Bd.forEach(t),Vo=p(e),k=r(e,"P",{id:!0,class:!0});var Et=c(k);hr=i(Et,"Put this "),Cl=r(Et,"CODE",{});var Qd=c(Cl);br=i(Qd,"CNAME"),Qd.forEach(t),vr=i(Et," file in the "),Ol=r(Et,"CODE",{});var Vd=c(Ol);yr=i(Vd,"static"),Vd.forEach(t),mr=i(Et," directory of your SvelteKit project and you\u2019re good to go!"),Et.forEach(t),Xo=p(e),je=r(e,"H3",{id:!0,class:!0});var Xd=c(je);Er=i(Xd,"Addendum: Getting HTTP, HTTPS, and WWW working with custom domains on Github"),Xd.forEach(t),Zo=p(e),He=r(e,"P",{id:!0,class:!0});var Zd=c(He);wr=i(Zd,"This isn\u2019t a SvelteKit-specific thing but it\u2019s a little confusing how to properly configure DNS for Github Pages so that all the following custom domain URLs will work:"),Zd.forEach(t),$o=p(e),Ue=r(e,"PRE",{id:!0,class:!0});var $d=c(Ue);Dl=r($d,"CODE",{});var ec=c(Dl);_r=i(ec,`example.com 44 | www.example.com 45 | http://example.com 46 | http://www.example.com 47 | https://example.com 48 | https://www.example.com`),ec.forEach(t),$d.forEach(t),es=p(e),M=r(e,"P",{id:!0,class:!0});var ys=c(M);Pr=i(ys,"To make all those work, it\u2019s pretty simple. Just add \u201CA Records\u201D pointing toward Github\u2019s IP addresses, as well as a \u201CCNAME Record\u201D for www pointed at "),Tl=r(ys,"CODE",{});var tc=c(Tl);Gr=i(tc,"username.github.io"),tc.forEach(t),kr=i(ys,":"),ys.forEach(t),ts=p(e),Me=r(e,"FIGURE",{id:!0,class:!0});var lc=c(Me);lt=r(lc,"IMG",{style:!0,src:!0,alt:!0}),lc.forEach(t),ls=p(e),Y=r(e,"P",{id:!0,class:!0});var ms=c(Y);Sr=i(ms,"You should also choose \u201CEnforce HTTPS\u201D from the Github Pages settings page. With this setup, every URL from the list above will automatically go to "),Rl=r(ms,"CODE",{});var oc=c(Rl);gr=i(oc,"https://example.com"),oc.forEach(t),Cr=i(ms,"."),ms.forEach(t),this.h()},h(){document.title="How to Deploy SvelteKit Apps to Github Pages",y(Je,"padding","0"),o(J,"id","13fd6e12-4bef-4f0d-bfea-80eea5927559"),o(J,"class",""),o(q,"id","06626e4f-432e-4e4f-8b9e-7ceefc6235de"),o(q,"class",""),o(B,"id","1fe6510c-a9a2-4a90-8eec-745b81648228"),o(B,"class",""),o(rt,"href","https://github.com/Glench/sveltekit-github-pages-template"),o(ut,"href","#80c176b7-13fe-482d-a0ec-ae33c89e983e"),o(m,"id","79e653f5-b792-4cb8-b65f-f697304bca14"),o(m,"class",""),o(dt,"href","#74dca68d-6d21-4e11-b9ed-c790962f55d9"),o(C,"id","49b17068-71ef-4127-80a8-c89f4a436567"),o(C,"class",""),o(z,"id","8ec91792-500c-4dd1-90a9-6d86d8da6cc2"),o(z,"class",""),o(Q,"id","71d350e2-a70f-4fd0-bc65-25e53add7a7c"),o(Q,"class",""),o(V,"id","14f14e08-2261-48f9-8b8c-ece9eeec3a14"),o(V,"class",""),o(ct,"href","https://github.com/new"),o(W,"id","448385a8-7a9d-46b7-8462-44b384899f07"),o(W,"class",""),y(qe,"list-style-type","disc"),o(X,"id","ab7a68cc-223f-486e-b31a-4214cc626342"),o(X,"class","bulleted-list"),y(E,"list-style-type","disc"),o(Z,"id","eef823da-94c7-4418-a3a6-8d7aed616d85"),o(Z,"class","bulleted-list"),y(Be,"list-style-type","disc"),o($,"id","67450d09-1cbc-4a75-82ad-1f5e2ec88048"),o($,"class","bulleted-list"),y(g,"list-style-type","disc"),o(ee,"id","af8dee55-5f84-47d7-82b3-e5cdd8ab438b"),o(ee,"class","bulleted-list"),y(ze,"list-style-type","disc"),o(te,"id","34784b83-23b7-468a-baa1-f937c43f1766"),o(te,"class","bulleted-list"),y(Qe,"width","748px"),st(Qe.src,Ar=""+(it+"/articles/sveltekit-github-pages-guide/github_new_repo_settings.png"))||o(Qe,"src",Ar),o(Qe,"alt","Screenshot of Github's new repository settings page"),o(le,"id","24fd0473-7338-4054-a445-14c8d0fbda52"),o(le,"class","image"),o(O,"id","04252290-fdab-4b91-b78a-eebcedd563b1"),o(O,"class",""),o(D,"id","8acb4fdd-b375-4ffb-bbcb-ad5855d240f3"),o(D,"class",""),o(oe,"id","74dca68d-6d21-4e11-b9ed-c790962f55d9"),o(oe,"class",""),o(se,"id","be06d8e4-5f87-48aa-b11e-c5985c80d9e2"),o(se,"class",""),o(ie,"id","29c34f64-d83f-42c0-b065-98fec160c587"),o(ie,"class","code"),o(ae,"id","93e78da9-3a6d-4261-a1a5-b3c46dd20e2f"),o(ae,"class",""),o(re,"id","3df8d6db-fcd0-44df-a189-0457797f7b91"),o(re,"class",""),o(ue,"id","2dc18833-360e-4050-a9a6-fa1bd261f7f1"),o(ue,"class","code"),o(de,"id","38121c2c-e6a4-401b-9c0c-128ef1ea84b1"),o(de,"class",""),o(ce,"id","788f46e2-c964-4bf6-99b5-6c06f42c822d"),o(ce,"class","code"),o(fe,"id","c5c71222-9dcf-4848-a16f-e4bc1f8114e8"),o(fe,"class",""),o(pe,"id","f9167998-17e5-467a-a883-6e38569894bf"),o(pe,"class","code"),y(Ve,"width","528px"),st(Ve.src,Nr=""+(it+"/articles/sveltekit-github-pages-guide/sveltekit_skeleton_page.png"))||o(Ve,"src",Nr),o(Ve,"alt","Screenshot of SvelteKit's default skeleton page"),o(ne,"id","d493864a-3b47-4a5e-b581-3e72f2b3e074"),o(ne,"class","image"),o(he,"id","28acdcab-862b-49e8-a362-aa573d9a4ed9"),o(he,"class",""),o(be,"id","80c176b7-13fe-482d-a0ec-ae33c89e983e"),o(be,"class",""),o(h,"id","156b17e7-2797-4a0f-8e9d-25a3f1d2c831"),o(h,"class",""),o(w,"id","db8ca798-b74c-431a-82e3-001529bda0cf"),o(w,"class",""),o(T,"id","25d1ec3c-d8d1-4bc0-b025-47b9495af03e"),o(T,"class",""),y(Xe,"width","1137px"),st(Xe.src,xr=""+(it+"/articles/sveltekit-github-pages-guide/github_pages_settings.png"))||o(Xe,"src",xr),o(Xe,"alt","Screenshot of Github Pages' settings"),o(ve,"id","b753dfad-8436-4e5f-80a9-f2b0eaf801ac"),o(ve,"class","image"),o(_,"id","4867b6c2-cbc3-45df-99b0-3cc849d8672a"),o(_,"class",""),y(Ze,"width","396px"),st(Ze.src,Lr=""+(it+"/articles/sveltekit-github-pages-guide/github_pages_branch_settings.png"))||o(Ze,"src",Lr),o(Ze,"alt","Screenshot of Github Pages' branch and folder settings"),o(ye,"id","b7f4648d-760f-41be-a503-1cb6c3e95d92"),o(ye,"class","image"),o(me,"id","eb829a65-531b-4d46-b87e-abc4bf765e20"),o(me,"class",""),o(Ee,"id","35ace530-b5da-41e7-8b7f-58ac7f83983e"),o(Ee,"class",""),o(R,"id","d2522f7b-82a8-4137-a2fe-e9350a474499"),o(R,"class",""),o(ft,"href","https://github.com/sveltejs/kit/tree/master/packages/adapter-static"),o(I,"id","433855e2-ac0b-4335-bc71-76409f771537"),o(I,"class",""),o(we,"id","5deb8b95-8b8c-44bc-837b-b9848cebc7cc"),o(we,"class","code"),o(K,"id","056fe096-d44c-4fbd-87ef-b80503b0aa9f"),o(K,"class",""),o(A,"id","3dfd944b-2358-41c2-85ce-49a85b94c729"),o(A,"class",""),o(_e,"id","84751aaf-8bfb-4976-80b4-1904436c8d13"),o(_e,"class","code"),o(b,"id","94c99b5a-dfee-4e8b-9335-75b7eacd7b31"),o(b,"class",""),o(n,"id","0245312b-2341-41aa-b48e-0e8294ca8106"),o(n,"class",""),o(N,"id","c7f9f442-b4f9-434c-b35d-249c9ba15079"),o(N,"class",""),o(F,"id","953ba726-7c3a-4a6a-a274-5c023f2ada30"),o(F,"class",""),o(x,"id","cd3114b4-3135-4f17-9c56-a9d89e87df95"),o(x,"class",""),o(Pe,"id","c5dd1b17-da2a-4dd6-b597-9e6aea1c0d31"),o(Pe,"class","code"),o($e,"id","7cd8d2d3-a4df-4670-bf91-7559cee650df"),o($e,"class",""),o(Ge,"id","aca314d2-d1ac-4212-a31a-45d30eb67457"),o(Ge,"class",""),o(P,"id","50c13dec-6bd6-4b4c-84a1-9a2077324e41"),o(P,"class",""),o(ke,"id","5794896d-c2d8-4ec3-8ee6-e48bf0d329dc"),o(ke,"class","code"),o(G,"id","16e69038-7fff-4596-bf8b-a84c6131e188"),o(G,"class",""),o(L,"id","ff347bc0-540c-4add-a338-15891c05f1fc"),o(L,"class",""),o(Se,"id","14031abf-5230-4fa3-9ef9-e3acd461c0b4"),o(Se,"class","code"),o(ge,"id","4cd2c9a6-6d42-4a86-8f8b-3b826a509037"),o(ge,"class",""),o(Ce,"id","71fc8bbc-14f3-4448-9e23-939994f3a5e5"),o(Ce,"class","code"),o(Oe,"id","2a015488-106d-427d-b92a-5b83650d604c"),o(Oe,"class",""),o(De,"id","11b05ad5-ee07-41f2-ba63-19f29457082e"),o(De,"class","code"),o(Te,"id","40e3a791-f191-4170-8cde-193958327d50"),o(Te,"class",""),y(et,"width","821px"),st(et.src,Hr=""+(it+"/articles/sveltekit-github-pages-guide/github_pages_site_ready.png"))||o(et,"src",Hr),o(et,"alt","Screenshot of Github Pages' settings with site ready to be published"),o(Re,"id","b60bdc1a-65a8-4fe2-b259-9f167c189573"),o(Re,"class","image"),o(Ie,"id","3e29ec3b-09bb-4e3f-97ec-86cffc405016"),o(Ie,"class",""),y(tt,"width","794px"),st(tt.src,Ur=""+(it+"/articles/sveltekit-github-pages-guide/github_pages_site_published.png"))||o(tt,"src",Ur),o(tt,"alt","Screenshot of Github Pages' successful publish"),o(Ke,"id","324e7a08-fe62-4257-9b62-fb4cb95e55db"),o(Ke,"class","image"),o(Ae,"id","5bd45d46-037c-4c28-a337-b15882a1395e"),o(Ae,"class",""),o(Ne,"id","aa3ab243-fa7e-462b-bd98-977c5b31c220"),o(Ne,"class",""),o(v,"type","1"),o(v,"id","31bc505d-c587-4ffa-89ce-beb6430c2f9a"),o(v,"class","numbered-list"),o(v,"start","1"),o(xe,"id","545577a2-f469-47bb-b33e-233d48a68d71"),o(xe,"class",""),o(j,"id","352fdcb6-faab-416d-aa23-85ea447d41ff"),o(j,"class",""),o(pt,"href","https://docs.github.com/en/pages/configuring-a-custom-domain-for-your-github-pages-site/about-custom-domains-and-github-pages"),o(H,"id","5b29bb52-ca91-47ea-beb1-007db77c31d6"),o(H,"class",""),o(U,"id","0863c72e-a475-442a-b196-e9550fba2af7"),o(U,"class",""),o(Le,"id","165ab509-27a2-4574-b10f-39ca60c6dedf"),o(Le,"class","code"),o(k,"id","3dbc643f-8d0e-408d-8ff8-d0a509e6dd75"),o(k,"class",""),o(je,"id","4fa0fbe3-0293-4b0d-abae-3f4dc391f190"),o(je,"class",""),o(He,"id","2c381a06-3018-469e-a6c1-8cd53b4c9ab3"),o(He,"class",""),o(Ue,"id","1c6756dd-b461-4e81-be9d-66be45d188fb"),o(Ue,"class","code"),o(M,"id","f22f182c-2ff1-47da-8571-1e8842a46c31"),o(M,"class",""),y(lt,"width","542px"),st(lt.src,Mr=""+(it+"/articles/sveltekit-github-pages-guide/github_pages_dns_settings.png"))||o(lt,"src",Mr),o(lt,"alt","Screenshot of DNS settings for Github custom domain"),o(Me,"id","b7650a03-f098-4ab7-adbf-e8317567d736"),o(Me,"class","image"),o(Y,"id","3ec5ffaa-8522-477f-9e13-2a108c9878b6"),o(Y,"class","")},m(e,u){d(e,at,u),d(e,Je,u),l(Je,wt),l(wt,Es),d(e,Il,u),d(e,J,u),l(J,ws),d(e,Kl,u),d(e,q,u),l(q,_s),d(e,Al,u),d(e,B,u),l(B,Ps),d(e,Nl,u),d(e,m,u),l(m,Gs),l(m,_t),l(_t,rt),l(rt,ks),l(m,Ss),l(m,ut),l(ut,gs),l(m,Cs),d(e,xl,u),d(e,C,u),l(C,Os),l(C,dt),l(dt,Ds),l(C,Ts),d(e,Ll,u),d(e,z,u),l(z,Rs),d(e,jl,u),d(e,Q,u),l(Q,Is),d(e,Hl,u),d(e,V,u),l(V,Ks),d(e,Ul,u),d(e,W,u),l(W,ct),l(ct,As),l(W,Ns),d(e,Ml,u),d(e,X,u),l(X,qe),l(qe,Pt),l(Pt,xs),l(qe,Ls),d(e,Yl,u),d(e,Z,u),l(Z,E),l(E,Gt),l(Gt,js),l(E,Hs),l(E,kt),l(kt,Us),l(E,Ms),l(E,St),l(St,Ys),l(E,Ws),l(E,gt),l(gt,Fs),d(e,Wl,u),d(e,$,u),l($,Be),l(Be,Ct),l(Ct,Js),l(Be,qs),d(e,Fl,u),d(e,ee,u),l(ee,g),l(g,Ot),l(Ot,Bs),l(g,zs),l(g,Dt),l(Dt,Qs),l(g,Vs),l(g,Tt),l(Tt,Xs),l(g,Zs),d(e,Jl,u),d(e,te,u),l(te,ze),l(ze,Rt),l(Rt,$s),l(ze,ei),d(e,ql,u),d(e,le,u),l(le,Qe),d(e,Bl,u),d(e,O,u),l(O,ti),l(O,It),l(It,li),l(O,oi),d(e,zl,u),d(e,D,u),l(D,si),l(D,Kt),l(Kt,ii),l(D,ai),d(e,Ql,u),d(e,oe,u),l(oe,ri),d(e,Vl,u),d(e,se,u),l(se,ui),d(e,Xl,u),d(e,ie,u),l(ie,At),l(At,di),d(e,Zl,u),d(e,ae,u),l(ae,ci),d(e,re,u),l(re,fi),d(e,ue,u),l(ue,Nt),l(Nt,pi),d(e,$l,u),d(e,de,u),l(de,ni),d(e,eo,u),d(e,ce,u),l(ce,xt),l(xt,hi),d(e,to,u),d(e,fe,u),l(fe,bi),d(e,lo,u),d(e,pe,u),l(pe,Lt),l(Lt,vi),d(e,oo,u),d(e,ne,u),l(ne,Ve),d(e,so,u),d(e,he,u),l(he,yi),d(e,io,u),d(e,be,u),l(be,mi),d(e,ao,u),d(e,h,u),l(h,Ei),l(h,jt),l(jt,wi),l(h,_i),l(h,Ht),l(Ht,Pi),l(h,Gi),l(h,Ut),l(Ut,ki),l(h,Si),d(e,ro,u),d(e,w,u),l(w,gi),l(w,Mt),l(Mt,Ci),l(w,Oi),l(w,Yt),l(Yt,Di),l(w,Ti),d(e,uo,u),d(e,T,u),l(T,Ri),l(T,Wt),l(Wt,Ii),l(T,Ki),d(e,co,u),d(e,ve,u),l(ve,Xe),d(e,fo,u),d(e,_,u),l(_,Ai),l(_,Ft),l(Ft,Ni),l(_,xi),l(_,Jt),l(Jt,Li),l(_,ji),d(e,po,u),d(e,ye,u),l(ye,Ze),d(e,no,u),d(e,me,u),l(me,Hi),d(e,ho,u),d(e,Ee,u),l(Ee,Ui),d(e,bo,u),d(e,R,u),l(R,Mi),l(R,qt),l(qt,Yi),l(R,Wi),d(e,vo,u),d(e,I,u),l(I,Fi),l(I,ft),l(ft,Ji),l(I,qi),d(e,yo,u),d(e,we,u),l(we,Bt),l(Bt,Bi),d(e,mo,u),d(e,K,u),l(K,zi),l(K,zt),l(zt,Qi),l(K,Vi),d(e,Eo,u),d(e,A,u),l(A,Xi),l(A,Qt),l(Qt,Zi),l(A,$i),d(e,wo,u),d(e,_e,u),l(_e,Vt),l(Vt,ea),d(e,_o,u),d(e,b,u),l(b,ta),l(b,Xt),l(Xt,la),l(b,oa),l(b,Zt),l(Zt,sa),l(b,ia),l(b,$t),l($t,aa),l(b,ra),d(e,Po,u),d(e,n,u),l(n,ua),l(n,el),l(el,da),l(n,ca),l(n,tl),l(tl,fa),l(n,pa),l(n,ll),l(ll,na),l(n,ha),l(n,ol),l(ol,ba),l(n,va),l(n,sl),l(sl,ya),l(n,ma),d(e,Go,u),d(e,N,u),l(N,Ea),l(N,il),l(il,wa),l(N,_a),d(e,ko,u),d(e,F,u),l(F,Pa),l(F,al),l(al,Ga),d(e,So,u),d(e,x,u),l(x,ka),l(x,rl),l(rl,Sa),l(x,ga),d(e,go,u),d(e,Pe,u),l(Pe,ul),l(ul,Ca),d(e,Co,u),d(e,$e,u),d(e,Oo,u),d(e,Ge,u),l(Ge,Oa),d(e,Do,u),d(e,P,u),l(P,Da),l(P,dl),l(dl,Ta),l(P,Ra),l(P,cl),l(cl,Ia),l(P,Ka),d(e,To,u),d(e,ke,u),l(ke,fl),l(fl,Aa),d(e,Ro,u),d(e,G,u),l(G,Na),l(G,pl),l(pl,xa),l(G,La),l(G,nl),l(nl,ja),l(G,Ha),d(e,Io,u),d(e,L,u),l(L,Ua),l(L,hl),l(hl,Ma),l(L,Ya),d(e,Ko,u),d(e,Se,u),l(Se,bl),l(bl,Wa),d(e,Ao,u),d(e,ge,u),l(ge,Fa),d(e,No,u),d(e,Ce,u),l(Ce,vl),l(vl,Ja),d(e,xo,u),d(e,Oe,u),l(Oe,qa),d(e,Lo,u),d(e,De,u),l(De,yl),l(yl,Ba),d(e,jo,u),d(e,Te,u),l(Te,za),d(e,Ho,u),d(e,Re,u),l(Re,et),d(e,Uo,u),d(e,Ie,u),l(Ie,Qa),d(e,Mo,u),d(e,Ke,u),l(Ke,tt),d(e,Yo,u),d(e,Ae,u),l(Ae,Va),d(e,Wo,u),d(e,Ne,u),l(Ne,Xa),d(e,Fo,u),d(e,v,u),l(v,ml),l(ml,El),l(El,Za),l(v,$a),l(v,wl),l(wl,_l),l(_l,er),l(v,tr),l(v,Pl),l(Pl,Gl),l(Gl,lr),d(e,Jo,u),d(e,xe,u),l(xe,or),d(e,qo,u),d(e,j,u),l(j,sr),l(j,kl),l(kl,ir),l(j,ar),d(e,Bo,u),d(e,H,u),l(H,rr),l(H,pt),l(pt,ur),l(H,dr),d(e,zo,u),d(e,U,u),l(U,cr),l(U,Sl),l(Sl,fr),l(U,pr),d(e,Qo,u),d(e,Le,u),l(Le,gl),l(gl,nr),d(e,Vo,u),d(e,k,u),l(k,hr),l(k,Cl),l(Cl,br),l(k,vr),l(k,Ol),l(Ol,yr),l(k,mr),d(e,Xo,u),d(e,je,u),l(je,Er),d(e,Zo,u),d(e,He,u),l(He,wr),d(e,$o,u),d(e,Ue,u),l(Ue,Dl),l(Dl,_r),d(e,es,u),d(e,M,u),l(M,Pr),l(M,Tl),l(Tl,Gr),l(M,kr),d(e,ts,u),d(e,Me,u),l(Me,lt),d(e,ls,u),d(e,Y,u),l(Y,Sr),l(Y,Rl),l(Rl,gr),l(Y,Cr)},p:Kr,i:Kr,o:Kr,d(e){e&&t(at),e&&t(Je),e&&t(Il),e&&t(J),e&&t(Kl),e&&t(q),e&&t(Al),e&&t(B),e&&t(Nl),e&&t(m),e&&t(xl),e&&t(C),e&&t(Ll),e&&t(z),e&&t(jl),e&&t(Q),e&&t(Hl),e&&t(V),e&&t(Ul),e&&t(W),e&&t(Ml),e&&t(X),e&&t(Yl),e&&t(Z),e&&t(Wl),e&&t($),e&&t(Fl),e&&t(ee),e&&t(Jl),e&&t(te),e&&t(ql),e&&t(le),e&&t(Bl),e&&t(O),e&&t(zl),e&&t(D),e&&t(Ql),e&&t(oe),e&&t(Vl),e&&t(se),e&&t(Xl),e&&t(ie),e&&t(Zl),e&&t(ae),e&&t(re),e&&t(ue),e&&t($l),e&&t(de),e&&t(eo),e&&t(ce),e&&t(to),e&&t(fe),e&&t(lo),e&&t(pe),e&&t(oo),e&&t(ne),e&&t(so),e&&t(he),e&&t(io),e&&t(be),e&&t(ao),e&&t(h),e&&t(ro),e&&t(w),e&&t(uo),e&&t(T),e&&t(co),e&&t(ve),e&&t(fo),e&&t(_),e&&t(po),e&&t(ye),e&&t(no),e&&t(me),e&&t(ho),e&&t(Ee),e&&t(bo),e&&t(R),e&&t(vo),e&&t(I),e&&t(yo),e&&t(we),e&&t(mo),e&&t(K),e&&t(Eo),e&&t(A),e&&t(wo),e&&t(_e),e&&t(_o),e&&t(b),e&&t(Po),e&&t(n),e&&t(Go),e&&t(N),e&&t(ko),e&&t(F),e&&t(So),e&&t(x),e&&t(go),e&&t(Pe),e&&t(Co),e&&t($e),e&&t(Oo),e&&t(Ge),e&&t(Do),e&&t(P),e&&t(To),e&&t(ke),e&&t(Ro),e&&t(G),e&&t(Io),e&&t(L),e&&t(Ko),e&&t(Se),e&&t(Ao),e&&t(ge),e&&t(No),e&&t(Ce),e&&t(xo),e&&t(Oe),e&&t(Lo),e&&t(De),e&&t(jo),e&&t(Te),e&&t(Ho),e&&t(Re),e&&t(Uo),e&&t(Ie),e&&t(Mo),e&&t(Ke),e&&t(Yo),e&&t(Ae),e&&t(Wo),e&&t(Ne),e&&t(Fo),e&&t(v),e&&t(Jo),e&&t(xe),e&&t(qo),e&&t(j),e&&t(Bo),e&&t(H),e&&t(zo),e&&t(U),e&&t(Qo),e&&t(Le),e&&t(Vo),e&&t(k),e&&t(Xo),e&&t(je),e&&t(Zo),e&&t(He),e&&t($o),e&&t(Ue),e&&t(es),e&&t(M),e&&t(ts),e&&t(Me),e&&t(ls),e&&t(Y)}}}class nc extends ac{constructor(at){super();rc(this,at,null,cc,uc,{})}}export{nc as default}; 49 | -------------------------------------------------------------------------------- /docs/app_/pages/index.svelte-6b511806.js: -------------------------------------------------------------------------------- 1 | import{S as Ot,i as Ht,s as Kt,e as s,t as o,k as c,L as Pt,c as a,a as l,d as t,h as i,m as n,M as At,b as r,f as y,J as e,g as ee,K as tt}from"../chunks/vendor-ba5e527f.js";import{b as Rt}from"../chunks/paths-396f020f.js";function Tt(jt){let g,st,O,de,le,S,p,A,fe,H,ve,pe,K,ge,me,P,_e,Se,T,w,at,re,d,E,R,Ee,ye,f,C,we,be,U,Ie,Le,G,ke,De,J,Ae,Re,b,M,je,xe,v,N,Fe,Oe,V,He,Ke,q,Pe,Te,z,Ce,Ue,I,W,Ge,Je,m,B,Me,Ne,X,Ve,qe,L,ze,j,We,Be,oe,h,te,Xe,Ze,se,x,Qe,Ye,ae,F,$e;return{c(){g=s("script"),O=s("script"),de=o(`window.dataLayer = window.dataLayer || []; 2 | function gtag(){dataLayer.push(arguments);} 3 | gtag('js', new Date()); 4 | 5 | gtag('config', 'G-FSJW8FC22N');`),le=c(),S=s("header"),p=s("div"),A=s("h1"),fe=o("Launch faster with the "),H=s("span"),ve=o("Svelte SaaS Starter Kit"),pe=c(),K=s("p"),ge=o("A full-stack SaaS template built on SvelteKit that starts your project with all the features that are the same in every app."),me=c(),P=s("p"),_e=o("Focus on the unique parts of your app, save weeks of tedious coding, and launch faster. \u{1F680}"),Se=c(),T=s("form"),w=s("script"),re=c(),d=s("section"),E=s("div"),R=s("h2"),Ee=o("Svelte SaaS \u{1F680}"),ye=c(),f=s("ul"),C=s("li"),we=o("User signup and auth"),be=c(),U=s("li"),Ie=o("Admin dashboards"),Le=c(),G=s("li"),ke=o("Stripe subscriptions"),De=c(),J=s("li"),Ae=o("Database models, forms, REST APIs, and more"),Re=c(),b=s("div"),M=s("h2"),je=o("SvelteKit \u{1F525}"),xe=c(),v=s("ul"),N=s("li"),Fe=o("Optimized full-stack apps with no configuration"),Oe=c(),V=s("li"),He=o("SEO and performance from server-side rendering"),Ke=c(),q=s("li"),Pe=o("Zippy UX from client-side hydration & routing"),Te=c(),z=s("li"),Ce=o("Stay in flow with instant hot-module reloading"),Ue=c(),I=s("div"),W=s("h2"),Ge=o("Svelte \u2764\uFE0F"),Je=c(),m=s("ul"),B=s("li"),Me=o("Blazing fast UI components \u2014 no virtual DOM"),Ne=c(),X=s("li"),Ve=o("Tiny builds \u2014 no bloated JS"),qe=c(),L=s("li"),ze=o("Voted "),j=s("a"),We=o("most-loved framework in the Stack Overflow developer survey"),Be=o(" (beating out React, Rails, Django, and more!)"),oe=c(),h=s("section"),te=s("h3"),Xe=o("Guides"),Ze=c(),se=s("h4"),x=s("a"),Qe=o("How to Deploy SvelteKit Apps to Github Pages"),Ye=c(),ae=s("h4"),F=s("a"),$e=o("Reflections on Migrating my SaaS to Sveltekit"),this.h()},l(u){const _=Pt('[data-svelte="svelte-1c3rwac"]',document.head);g=a(_,"SCRIPT",{src:!0});var xt=l(g);xt.forEach(t),O=a(_,"SCRIPT",{});var lt=l(O);de=i(lt,`window.dataLayer = window.dataLayer || []; 6 | function gtag(){dataLayer.push(arguments);} 7 | gtag('js', new Date()); 8 | 9 | gtag('config', 'G-FSJW8FC22N');`),lt.forEach(t),_.forEach(t),le=n(u),S=a(u,"HEADER",{class:!0});var ie=l(S);p=a(ie,"DIV",{style:!0});var Z=l(p);A=a(Z,"H1",{class:!0});var et=l(A);fe=i(et,"Launch faster with the "),H=a(et,"SPAN",{class:!0});var rt=l(H);ve=i(rt,"Svelte SaaS Starter Kit"),rt.forEach(t),et.forEach(t),pe=n(Z),K=a(Z,"P",{class:!0});var ot=l(K);ge=i(ot,"A full-stack SaaS template built on SvelteKit that starts your project with all the features that are the same in every app."),ot.forEach(t),me=n(Z),P=a(Z,"P",{class:!0});var it=l(P);_e=i(it,"Focus on the unique parts of your app, save weeks of tedious coding, and launch faster. \u{1F680}"),it.forEach(t),Z.forEach(t),Se=n(ie),T=a(ie,"FORM",{class:!0});var ct=l(T);w=a(ct,"SCRIPT",{src:!0,"data-form":!0});var Ft=l(w);Ft.forEach(t),ct.forEach(t),ie.forEach(t),re=n(u),d=a(u,"SECTION",{style:!0,class:!0});var Q=l(d);E=a(Q,"DIV",{class:!0,style:!0});var ce=l(E);R=a(ce,"H2",{style:!0,class:!0});var nt=l(R);Ee=i(nt,"Svelte SaaS \u{1F680}"),nt.forEach(t),ye=n(ce),f=a(ce,"UL",{class:!0});var k=l(f);C=a(k,"LI",{class:!0});var ut=l(C);we=i(ut,"User signup and auth"),ut.forEach(t),be=n(k),U=a(k,"LI",{class:!0});var ht=l(U);Ie=i(ht,"Admin dashboards"),ht.forEach(t),Le=n(k),G=a(k,"LI",{class:!0});var dt=l(G);ke=i(dt,"Stripe subscriptions"),dt.forEach(t),De=n(k),J=a(k,"LI",{class:!0});var ft=l(J);Ae=i(ft,"Database models, forms, REST APIs, and more"),ft.forEach(t),k.forEach(t),ce.forEach(t),Re=n(Q),b=a(Q,"DIV",{class:!0});var ne=l(b);M=a(ne,"H2",{class:!0});var vt=l(M);je=i(vt,"SvelteKit \u{1F525}"),vt.forEach(t),xe=n(ne),v=a(ne,"UL",{class:!0});var D=l(v);N=a(D,"LI",{class:!0});var pt=l(N);Fe=i(pt,"Optimized full-stack apps with no configuration"),pt.forEach(t),Oe=n(D),V=a(D,"LI",{class:!0});var gt=l(V);He=i(gt,"SEO and performance from server-side rendering"),gt.forEach(t),Ke=n(D),q=a(D,"LI",{class:!0});var mt=l(q);Pe=i(mt,"Zippy UX from client-side hydration & routing"),mt.forEach(t),Te=n(D),z=a(D,"LI",{class:!0});var _t=l(z);Ce=i(_t,"Stay in flow with instant hot-module reloading"),_t.forEach(t),D.forEach(t),ne.forEach(t),Ue=n(Q),I=a(Q,"DIV",{class:!0});var ue=l(I);W=a(ue,"H2",{class:!0});var St=l(W);Ge=i(St,"Svelte \u2764\uFE0F"),St.forEach(t),Je=n(ue),m=a(ue,"UL",{class:!0});var Y=l(m);B=a(Y,"LI",{class:!0});var Et=l(B);Me=i(Et,"Blazing fast UI components \u2014 no virtual DOM"),Et.forEach(t),Ne=n(Y),X=a(Y,"LI",{class:!0});var yt=l(X);Ve=i(yt,"Tiny builds \u2014 no bloated JS"),yt.forEach(t),qe=n(Y),L=a(Y,"LI",{class:!0});var he=l(L);ze=i(he,"Voted "),j=a(he,"A",{href:!0,class:!0});var wt=l(j);We=i(wt,"most-loved framework in the Stack Overflow developer survey"),wt.forEach(t),Be=i(he," (beating out React, Rails, Django, and more!)"),he.forEach(t),Y.forEach(t),ue.forEach(t),Q.forEach(t),oe=n(u),h=a(u,"SECTION",{style:!0,class:!0});var $=l(h);te=a($,"H3",{});var bt=l(te);Xe=i(bt,"Guides"),bt.forEach(t),Ze=n($),se=a($,"H4",{});var It=l(se);x=a(It,"A",{href:!0,class:!0});var Lt=l(x);Qe=i(Lt,"How to Deploy SvelteKit Apps to Github Pages"),Lt.forEach(t),It.forEach(t),Ye=n($),ae=a($,"H4",{});var kt=l(ae);F=a(kt,"A",{href:!0,class:!0});var Dt=l(F);$e=i(Dt,"Reflections on Migrating my SaaS to Sveltekit"),Dt.forEach(t),kt.forEach(t),$.forEach(t),this.h()},h(){document.title="Launch Faster with the Full-stack SaaS Template for SvelteKit",g.async=!0,At(g.src,st="https://www.googletagmanager.com/gtag/js?id=G-FSJW8FC22N")||r(g,"src",st),r(H,"class","gradient svelte-f8guh1"),r(A,"class","svelte-f8guh1"),r(K,"class","svelte-f8guh1"),r(P,"class","svelte-f8guh1"),y(p,"max-width","550px"),y(p,"padding","1rem"),w.async=!0,At(w.src,at="https://eomail1.com/form/3690e32d-68d0-11ec-96e5-06b4694bee2a.js")||r(w,"src",at),r(w,"data-form","3690e32d-68d0-11ec-96e5-06b4694bee2a"),r(T,"class","svelte-f8guh1"),r(S,"class","svelte-f8guh1"),y(R,"color","white"),r(R,"class","svelte-f8guh1"),r(C,"class","svelte-f8guh1"),r(U,"class","svelte-f8guh1"),r(G,"class","svelte-f8guh1"),r(J,"class","svelte-f8guh1"),r(f,"class","emoji svelte-f8guh1"),r(E,"class","card svelte-f8guh1"),y(E,"border-radius","6px"),r(M,"class","svelte-f8guh1"),r(N,"class","svelte-f8guh1"),r(V,"class","svelte-f8guh1"),r(q,"class","svelte-f8guh1"),r(z,"class","svelte-f8guh1"),r(v,"class","svelte-f8guh1"),r(b,"class","card svelte-f8guh1"),r(W,"class","svelte-f8guh1"),r(B,"class","svelte-f8guh1"),r(X,"class","svelte-f8guh1"),r(j,"href","https://insights.stackoverflow.com/survey/2021#section-most-loved-dreaded-and-wanted-web-frameworks"),r(j,"class","svelte-f8guh1"),r(L,"class","svelte-f8guh1"),r(m,"class","svelte-f8guh1"),r(I,"class","card svelte-f8guh1"),y(d,"background","radial-gradient(50% 50% at 50% 50%, #6E7176 0%, #515D6A 100%)"),y(d,"padding","4rem 1.5rem"),r(d,"class","svelte-f8guh1"),r(x,"href",""+(Rt+"/articles/sveltekit-github-pages-guide")),r(x,"class","svelte-f8guh1"),r(F,"href",""+(Rt+"/articles/migrate-saas-to-sveltekit")),r(F,"class","svelte-f8guh1"),y(h,"display","block"),y(h,"max-width","800px"),y(h,"font-size","2rem"),r(h,"class","svelte-f8guh1")},m(u,_){e(document.head,g),e(document.head,O),e(O,de),ee(u,le,_),ee(u,S,_),e(S,p),e(p,A),e(A,fe),e(A,H),e(H,ve),e(p,pe),e(p,K),e(K,ge),e(p,me),e(p,P),e(P,_e),e(S,Se),e(S,T),e(T,w),ee(u,re,_),ee(u,d,_),e(d,E),e(E,R),e(R,Ee),e(E,ye),e(E,f),e(f,C),e(C,we),e(f,be),e(f,U),e(U,Ie),e(f,Le),e(f,G),e(G,ke),e(f,De),e(f,J),e(J,Ae),e(d,Re),e(d,b),e(b,M),e(M,je),e(b,xe),e(b,v),e(v,N),e(N,Fe),e(v,Oe),e(v,V),e(V,He),e(v,Ke),e(v,q),e(q,Pe),e(v,Te),e(v,z),e(z,Ce),e(d,Ue),e(d,I),e(I,W),e(W,Ge),e(I,Je),e(I,m),e(m,B),e(B,Me),e(m,Ne),e(m,X),e(X,Ve),e(m,qe),e(m,L),e(L,ze),e(L,j),e(j,We),e(L,Be),ee(u,oe,_),ee(u,h,_),e(h,te),e(te,Xe),e(h,Ze),e(h,se),e(se,x),e(x,Qe),e(h,Ye),e(h,ae),e(ae,F),e(F,$e)},p:tt,i:tt,o:tt,d(u){t(g),t(O),u&&t(le),u&&t(S),u&&t(re),u&&t(d),u&&t(oe),u&&t(h)}}}class Gt extends Ot{constructor(g){super();Ht(this,g,null,Tt,Kt,{})}}export{Gt as default}; 10 | -------------------------------------------------------------------------------- /docs/app_/start-e0df9183.js: -------------------------------------------------------------------------------- 1 | var ne=Object.defineProperty,ae=Object.defineProperties;var oe=Object.getOwnPropertyDescriptors;var B=Object.getOwnPropertySymbols;var M=Object.prototype.hasOwnProperty,Y=Object.prototype.propertyIsEnumerable;var X=(o,e,t)=>e in o?ne(o,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):o[e]=t,E=(o,e)=>{for(var t in e||(e={}))M.call(e,t)&&X(o,t,e[t]);if(B)for(var t of B(e))Y.call(e,t)&&X(o,t,e[t]);return o},K=(o,e)=>ae(o,oe(e));var F=(o,e)=>{var t={};for(var r in o)M.call(o,r)&&e.indexOf(r)<0&&(t[r]=o[r]);if(o!=null&&B)for(var r of B(o))e.indexOf(r)<0&&Y.call(o,r)&&(t[r]=o[r]);return t};import{S as le,i as ce,s as fe,e as ue,c as he,a as de,d as v,b as W,f as T,g as R,t as _e,h as pe,j as ge,k as me,l as _,m as we,n as N,o as p,p as P,q as g,r as be,u as ve,v as J,w as y,x as j,y as k,z as C,A as I,B as $,C as V,D as z,E as H}from"./chunks/vendor-ba5e527f.js";import{s as ye}from"./chunks/paths-396f020f.js";function ke(o){let e,t,r;const l=[o[1]||{}];var n=o[0][0];function a(s){let i={};for(let c=0;c{$(f,1)}),P()}n?(e=new n(a()),y(e.$$.fragment),g(e.$$.fragment,1),k(e,t.parentNode,t)):e=null}else n&&e.$set(c)},i(s){r||(e&&g(e.$$.fragment,s),r=!0)},o(s){e&&p(e.$$.fragment,s),r=!1},d(s){s&&v(t),e&&$(e,s)}}}function $e(o){let e,t,r;const l=[o[1]||{}];var n=o[0][0];function a(s){let i={$$slots:{default:[Ne]},$$scope:{ctx:s}};for(let c=0;c{$(f,1)}),P()}n?(e=new n(a(s)),y(e.$$.fragment),g(e.$$.fragment,1),k(e,t.parentNode,t)):e=null}else n&&e.$set(c)},i(s){r||(e&&g(e.$$.fragment,s),r=!0)},o(s){e&&p(e.$$.fragment,s),r=!1},d(s){s&&v(t),e&&$(e,s)}}}function Ee(o){let e,t,r;const l=[o[2]||{}];var n=o[0][1];function a(s){let i={};for(let c=0;c{$(f,1)}),P()}n?(e=new n(a()),y(e.$$.fragment),g(e.$$.fragment,1),k(e,t.parentNode,t)):e=null}else n&&e.$set(c)},i(s){r||(e&&g(e.$$.fragment,s),r=!0)},o(s){e&&p(e.$$.fragment,s),r=!1},d(s){s&&v(t),e&&$(e,s)}}}function Re(o){let e,t,r;const l=[o[2]||{}];var n=o[0][1];function a(s){let i={$$slots:{default:[Ae]},$$scope:{ctx:s}};for(let c=0;c{$(f,1)}),P()}n?(e=new n(a(s)),y(e.$$.fragment),g(e.$$.fragment,1),k(e,t.parentNode,t)):e=null}else n&&e.$set(c)},i(s){r||(e&&g(e.$$.fragment,s),r=!0)},o(s){e&&p(e.$$.fragment,s),r=!1},d(s){s&&v(t),e&&$(e,s)}}}function Le(o){let e,t,r;const l=[o[3]||{}];var n=o[0][2];function a(s){let i={};for(let c=0;c{$(f,1)}),P()}n?(e=new n(a()),y(e.$$.fragment),g(e.$$.fragment,1),k(e,t.parentNode,t)):e=null}else n&&e.$set(c)},i(s){r||(e&&g(e.$$.fragment,s),r=!0)},o(s){e&&p(e.$$.fragment,s),r=!1},d(s){s&&v(t),e&&$(e,s)}}}function Se(o){let e,t,r;const l=[o[3]||{}];var n=o[0][2];function a(s){let i={$$slots:{default:[Ue]},$$scope:{ctx:s}};for(let c=0;c{$(f,1)}),P()}n?(e=new n(a(s)),y(e.$$.fragment),g(e.$$.fragment,1),k(e,t.parentNode,t)):e=null}else n&&e.$set(c)},i(s){r||(e&&g(e.$$.fragment,s),r=!0)},o(s){e&&p(e.$$.fragment,s),r=!1},d(s){s&&v(t),e&&$(e,s)}}}function Ue(o){let e,t,r;const l=[o[4]||{}];var n=o[0][3];function a(s){let i={};for(let c=0;c{$(f,1)}),P()}n?(e=new n(a()),y(e.$$.fragment),g(e.$$.fragment,1),k(e,t.parentNode,t)):e=null}else n&&e.$set(c)},i(s){r||(e&&g(e.$$.fragment,s),r=!0)},o(s){e&&p(e.$$.fragment,s),r=!1},d(s){s&&v(t),e&&$(e,s)}}}function Ae(o){let e,t,r,l;const n=[Se,Le],a=[];function s(i,c){return i[0][3]?0:1}return e=s(o),t=a[e]=n[e](o),{c(){t.c(),r=_()},l(i){t.l(i),r=_()},m(i,c){a[e].m(i,c),R(i,r,c),l=!0},p(i,c){let f=e;e=s(i),e===f?a[e].p(i,c):(N(),p(a[f],1,1,()=>{a[f]=null}),P(),t=a[e],t?t.p(i,c):(t=a[e]=n[e](i),t.c()),g(t,1),t.m(r.parentNode,r))},i(i){l||(g(t),l=!0)},o(i){p(t),l=!1},d(i){a[e].d(i),i&&v(r)}}}function Ne(o){let e,t,r,l;const n=[Re,Ee],a=[];function s(i,c){return i[0][2]?0:1}return e=s(o),t=a[e]=n[e](o),{c(){t.c(),r=_()},l(i){t.l(i),r=_()},m(i,c){a[e].m(i,c),R(i,r,c),l=!0},p(i,c){let f=e;e=s(i),e===f?a[e].p(i,c):(N(),p(a[f],1,1,()=>{a[f]=null}),P(),t=a[e],t?t.p(i,c):(t=a[e]=n[e](i),t.c()),g(t,1),t.m(r.parentNode,r))},i(i){l||(g(t),l=!0)},o(i){p(t),l=!1},d(i){a[e].d(i),i&&v(r)}}}function Q(o){let e,t=o[6]&&Z(o);return{c(){e=ue("div"),t&&t.c(),this.h()},l(r){e=he(r,"DIV",{id:!0,"aria-live":!0,"aria-atomic":!0,style:!0});var l=de(e);t&&t.l(l),l.forEach(v),this.h()},h(){W(e,"id","svelte-announcer"),W(e,"aria-live","assertive"),W(e,"aria-atomic","true"),T(e,"position","absolute"),T(e,"left","0"),T(e,"top","0"),T(e,"clip","rect(0 0 0 0)"),T(e,"clip-path","inset(50%)"),T(e,"overflow","hidden"),T(e,"white-space","nowrap"),T(e,"width","1px"),T(e,"height","1px")},m(r,l){R(r,e,l),t&&t.m(e,null)},p(r,l){r[6]?t?t.p(r,l):(t=Z(r),t.c(),t.m(e,null)):t&&(t.d(1),t=null)},d(r){r&&v(e),t&&t.d()}}}function Z(o){let e;return{c(){e=_e(o[7])},l(t){e=pe(t,o[7])},m(t,r){R(t,e,r)},p(t,r){r&128&&ge(e,t[7])},d(t){t&&v(e)}}}function Pe(o){let e,t,r,l,n;const a=[$e,ke],s=[];function i(f,d){return f[0][1]?0:1}e=i(o),t=s[e]=a[e](o);let c=o[5]&&Q(o);return{c(){t.c(),r=me(),c&&c.c(),l=_()},l(f){t.l(f),r=we(f),c&&c.l(f),l=_()},m(f,d){s[e].m(f,d),R(f,r,d),c&&c.m(f,d),R(f,l,d),n=!0},p(f,[d]){let u=e;e=i(f),e===u?s[e].p(f,d):(N(),p(s[u],1,1,()=>{s[u]=null}),P(),t=s[e],t?t.p(f,d):(t=s[e]=a[e](f),t.c()),g(t,1),t.m(r.parentNode,r)),f[5]?c?c.p(f,d):(c=Q(f),c.c(),c.m(l.parentNode,l)):c&&(c.d(1),c=null)},i(f){n||(g(t),n=!0)},o(f){p(t),n=!1},d(f){s[e].d(f),f&&v(r),c&&c.d(f),f&&v(l)}}}function Oe(o,e,t){let{stores:r}=e,{page:l}=e,{components:n}=e,{props_0:a=null}=e,{props_1:s=null}=e,{props_2:i=null}=e,{props_3:c=null}=e;be("__svelte__",r),ve(r.page.notify);let f=!1,d=!1,u=null;return J(()=>{const h=r.page.subscribe(()=>{f&&(t(6,d=!0),t(7,u=document.title||"untitled page"))});return t(5,f=!0),h}),o.$$set=h=>{"stores"in h&&t(8,r=h.stores),"page"in h&&t(9,l=h.page),"components"in h&&t(0,n=h.components),"props_0"in h&&t(1,a=h.props_0),"props_1"in h&&t(2,s=h.props_1),"props_2"in h&&t(3,i=h.props_2),"props_3"in h&&t(4,c=h.props_3)},o.$$.update=()=>{o.$$.dirty&768&&r.page.set(l)},[n,a,s,i,c,f,d,u,r,l]}class Te extends le{constructor(e){super();ce(this,e,Oe,Pe,fe,{stores:8,page:9,components:0,props_0:1,props_1:2,props_2:3,props_3:4})}}const je="modulepreload",ee={},Ce="/app_/",D=function(e,t){return!t||t.length===0?e():Promise.all(t.map(r=>{if(r=`${Ce}${r}`,r in ee)return;ee[r]=!0;const l=r.endsWith(".css"),n=l?'[rel="stylesheet"]':"";if(document.querySelector(`link[href="${r}"]${n}`))return;const a=document.createElement("link");if(a.rel=l?"stylesheet":je,l||(a.as="script",a.crossOrigin=""),a.href=r,document.head.appendChild(a),l)return new Promise((s,i)=>{a.addEventListener("load",s),a.addEventListener("error",i)})})).then(()=>e())},L=[()=>D(()=>import("./layout.svelte-eca9fc82.js"),["layout.svelte-eca9fc82.js","chunks/vendor-ba5e527f.js"]),()=>D(()=>import("./error.svelte-5d5d6eed.js"),["error.svelte-5d5d6eed.js","chunks/vendor-ba5e527f.js"]),()=>D(()=>import("./pages/index.svelte-6b511806.js"),["pages/index.svelte-6b511806.js","assets/pages/index.svelte-df858b15.css","chunks/vendor-ba5e527f.js","chunks/paths-396f020f.js"]),()=>D(()=>import("./pages/articles/__layout.svelte-f79cd2a5.js"),["pages/articles/__layout.svelte-f79cd2a5.js","assets/pages/articles/__layout.svelte-984cc1bf.css","chunks/vendor-ba5e527f.js","chunks/paths-396f020f.js"]),()=>D(()=>import("./pages/articles/sveltekit-github-pages-guide.svelte-194f2893.js"),["pages/articles/sveltekit-github-pages-guide.svelte-194f2893.js","chunks/vendor-ba5e527f.js","chunks/paths-396f020f.js"]),()=>D(()=>import("./pages/articles/migrate-saas-to-sveltekit.svelte-7eec8317.js"),["pages/articles/migrate-saas-to-sveltekit.svelte-7eec8317.js","chunks/vendor-ba5e527f.js","chunks/paths-396f020f.js"])],Ie=[[/^\/$/,[L[0],L[2]],[L[1]]],[/^\/articles\/sveltekit-github-pages-guide\/?$/,[L[0],L[3],L[4]],[L[1]]],[/^\/articles\/migrate-saas-to-sveltekit\/?$/,[L[0],L[3],L[5]],[L[1]]]],Ve=[L[0](),L[1]()];function De(o){let e=o.baseURI;if(!e){const t=o.getElementsByTagName("base");e=t.length?t[0].href:o.URL}return e}function G(){return{x:pageXOffset,y:pageYOffset}}function te(o){return o.composedPath().find(t=>t instanceof Node&&t.nodeName.toUpperCase()==="A")}function se(o){return o instanceof SVGAElement?new URL(o.href.baseVal,document.baseURI):new URL(o.href)}class qe{constructor({base:e,routes:t,trailing_slash:r,renderer:l}){var n,a;this.base=e,this.routes=t,this.trailing_slash=r,this.navigating=0,this.renderer=l,l.router=this,this.enabled=!0,document.body.setAttribute("tabindex","-1"),this.current_history_index=(a=(n=history.state)==null?void 0:n["sveltekit:index"])!=null?a:0,this.current_history_index===0&&history.replaceState(K(E({},history.state),{"sveltekit:index":0}),"",location.href),this.callbacks={before_navigate:[],after_navigate:[]}}init_listeners(){"scrollRestoration"in history&&(history.scrollRestoration="manual"),addEventListener("beforeunload",n=>{let a=!1;const s={from:this.renderer.current.url,to:null,cancel:()=>a=!0};this.callbacks.before_navigate.forEach(i=>i(s)),a?(n.preventDefault(),n.returnValue=""):history.scrollRestoration="auto"}),addEventListener("load",()=>{history.scrollRestoration="manual"});let e;addEventListener("scroll",()=>{clearTimeout(e),e=setTimeout(()=>{const n=K(E({},history.state||{}),{"sveltekit:scroll":G()});history.replaceState(n,document.title,window.location.href)},200)});const t=n=>{const a=te(n);a&&a.href&&a.hasAttribute("sveltekit:prefetch")&&this.prefetch(se(a))};let r;const l=n=>{clearTimeout(r),r=setTimeout(()=>{var a;(a=n.target)==null||a.dispatchEvent(new CustomEvent("sveltekit:trigger_prefetch",{bubbles:!0}))},20)};addEventListener("touchstart",t),addEventListener("mousemove",l),addEventListener("sveltekit:trigger_prefetch",t),addEventListener("click",n=>{if(!this.enabled||n.button||n.which!==1||n.metaKey||n.ctrlKey||n.shiftKey||n.altKey||n.defaultPrevented)return;const a=te(n);if(!a||!a.href)return;const s=se(a);if(s.toString()===location.href){location.hash||n.preventDefault();return}const c=(a.getAttribute("rel")||"").split(/\s+/);if(!(a.hasAttribute("download")||c&&c.includes("external"))&&!(a instanceof SVGAElement?a.target.baseVal:a.target)){if(s.href.split("#")[0]===location.href.split("#")[0]){setTimeout(()=>history.pushState({},"",s.href));const f=this.parse(s);return f?this.renderer.update(f,[],!1):void 0}this._navigate({url:s,scroll:a.hasAttribute("sveltekit:noscroll")?G():null,keepfocus:!1,chain:[],details:{state:{},replaceState:!1},accepted:()=>n.preventDefault(),blocked:()=>n.preventDefault()})}}),addEventListener("popstate",n=>{if(n.state&&this.enabled){if(n.state["sveltekit:index"]===this.current_history_index)return;this._navigate({url:new URL(location.href),scroll:n.state["sveltekit:scroll"],keepfocus:!1,chain:[],details:null,accepted:()=>{this.current_history_index=n.state["sveltekit:index"]},blocked:()=>{const a=this.current_history_index-n.state["sveltekit:index"];history.go(a)}})}})}owns(e){return e.origin===location.origin&&e.pathname.startsWith(this.base)}parse(e){if(this.owns(e)){const t=decodeURI(e.pathname.slice(this.base.length)||"/");return{id:e.pathname+e.search,routes:this.routes.filter(([r])=>r.test(t)),url:e,path:t}}}async goto(e,{noscroll:t=!1,replaceState:r=!1,keepfocus:l=!1,state:n={}}={},a){const s=new URL(e,De(document));return this.enabled?this._navigate({url:s,scroll:t?G():null,keepfocus:l,chain:a,details:{state:n,replaceState:r},accepted:()=>{},blocked:()=>{}}):(location.href=s.href,new Promise(()=>{}))}enable(){this.enabled=!0}disable(){this.enabled=!1}async prefetch(e){const t=this.parse(e);if(!t)throw new Error("Attempted to prefetch a URL that does not belong to this app");return this.renderer.load(t)}after_navigate(e){J(()=>(this.callbacks.after_navigate.push(e),()=>{const t=this.callbacks.after_navigate.indexOf(e);this.callbacks.after_navigate.splice(t,1)}))}before_navigate(e){J(()=>(this.callbacks.before_navigate.push(e),()=>{const t=this.callbacks.before_navigate.indexOf(e);this.callbacks.before_navigate.splice(t,1)}))}async _navigate({url:e,scroll:t,keepfocus:r,chain:l,details:n,accepted:a,blocked:s}){const i=this.renderer.current.url;let c=!1;const f={from:i,to:e,cancel:()=>c=!0};if(this.callbacks.before_navigate.forEach(h=>h(f)),c){s();return}const d=this.parse(e);if(!d)return location.href=e.href,new Promise(()=>{});a(),this.navigating||dispatchEvent(new CustomEvent("sveltekit:navigation-start")),this.navigating++;let{pathname:u}=e;if(this.trailing_slash==="never"?u!=="/"&&u.endsWith("/")&&(u=u.slice(0,-1)):this.trailing_slash==="always"&&!e.pathname.split("/").pop().includes(".")&&!u.endsWith("/")&&(u+="/"),d.url=new URL(e.origin+u+e.search+e.hash),n){const h=n.replaceState?0:1;n.state["sveltekit:index"]=this.current_history_index+=h,history[n.replaceState?"replaceState":"pushState"](n.state,"",d.url)}if(await this.renderer.handle_navigation(d,l,!1,{scroll:t,keepfocus:r}),this.navigating--,!this.navigating){dispatchEvent(new CustomEvent("sveltekit:navigation-end"));const h={from:i,to:e};this.callbacks.after_navigate.forEach(S=>S(h))}}}function re(o){return o instanceof Error||o&&o.name&&o.message?o:new Error(JSON.stringify(o))}function xe(o){let e=5381,t=o.length;if(typeof o=="string")for(;t;)e=e*33^o.charCodeAt(--t);else for(;t;)e=e*33^o[--t];return(e>>>0).toString(36)}function Be(o){const e=o.status&&o.status>=400&&o.status<=599&&!o.redirect;if(o.error||e){const t=o.status;if(!o.error&&e)return{status:t||500,error:new Error};const r=typeof o.error=="string"?new Error(o.error):o.error;return r instanceof Error?!t||t<400||t>599?(console.warn('"error" returned from load() without a valid status code \u2014 defaulting to 500'),{status:500,error:r}):{status:t,error:r}:{status:500,error:new Error(`"error" property returned from load() must be a string or instance of Error, received type "${typeof r}"`)}}if(o.redirect){if(!o.status||Math.floor(o.status/100)!==3)return{status:500,error:new Error('"redirect" property returned from load() must be accompanied by a 3xx status code')};if(typeof o.redirect!="string")return{status:500,error:new Error('"redirect" property returned from load() must be a string')}}if(o.context)throw new Error('You are returning "context" from a load function. "context" was renamed to "stuff", please adjust your code accordingly.');return o}function ie(o){const e=z(o);let t=!0;function r(){t=!0,e.update(a=>a)}function l(a){t=!1,e.set(a)}function n(a){let s;return e.subscribe(i=>{(s===void 0||t&&i!==s)&&a(s=i)})}return{notify:r,set:l,subscribe:n}}function Ke(o,e){const t=typeof o=="string"?o:o.url;let r=`script[data-type="svelte-data"][data-url=${JSON.stringify(t)}]`;e&&typeof e.body=="string"&&(r+=`[data-body="${xe(e.body)}"]`);const l=document.querySelector(r);if(l&&l.textContent){const n=JSON.parse(l.textContent),{body:a}=n,s=F(n,["body"]);return Promise.resolve(new Response(a,s))}return fetch(o,e)}class We{constructor({Root:e,fallback:t,target:r,session:l}){this.Root=e,this.fallback=t,this.router,this.target=r,this.started=!1,this.session_id=1,this.invalid=new Set,this.invalidating=null,this.autoscroll=!0,this.updating=!1,this.current={url:null,session_id:0,branch:[]},this.cache=new Map,this.loading={id:null,promise:null},this.stores={url:ie({}),page:ie({}),navigating:z(null),session:z(l)},this.$session=null,this.root=null;let n=!1;this.stores.session.subscribe(async a=>{if(this.$session=a,!n||!this.router)return;this.session_id+=1;const s=this.router.parse(new URL(location.href));s&&this.update(s,[],!0)}),n=!0}disable_scroll_handling(){(this.updating||!this.started)&&(this.autoscroll=!1)}async start({status:e,error:t,nodes:r,url:l,params:n}){const a=[];let s={},i,c;l.hash=window.location.hash;try{for(let f=0;f10||t.includes(e.url.pathname))a=await this._load_error({status:500,error:new Error("Redirect loop"),url:e.url});else{this.router?this.router.goto(new URL(a.redirect,e.url).href,{replaceState:!0},[...t,e.url.pathname]):location.href=new URL(a.redirect,location.href).href;return}if(this.updating=!0,this.started?(this.current=a.state,this.root.$set(a.props),this.stores.navigating.set(null)):this._init(a),l){const{scroll:c,keepfocus:f}=l;if(f||((i=getSelection())==null||i.removeAllRanges(),document.body.focus()),await H(),this.autoscroll){const d=e.url.hash&&document.getElementById(e.url.hash.slice(1));c?scrollTo(c.x,c.y):d?d.scrollIntoView():scrollTo(0,0)}}else await H();if(this.loading.promise=null,this.loading.id=null,this.autoscroll=!0,this.updating=!1,!this.router)return;const s=a.state.branch[a.state.branch.length-1];s&&s.module.router===!1?this.router.disable():this.router.enable()}load(e){return this.loading.promise=this._get_navigation_result(e,!1),this.loading.id=e.id,this.loading.promise}invalidate(e){return this.invalid.add(e),this.invalidating||(this.invalidating=Promise.resolve().then(async()=>{const t=this.router&&this.router.parse(new URL(location.href));t&&await this.update(t,[],!0),this.invalidating=null})),this.invalidating}_init(e){this.current=e.state;const t=document.querySelector("style[data-svelte]");if(t&&t.remove(),this.root=new this.Root({target:this.target,props:E({stores:this.stores},e.props),hydrate:!0}),this.started=!0,this.router){const r={from:null,to:new URL(location.href)};this.router.callbacks.after_navigate.forEach(l=>l(r))}}async _get_navigation_result(e,t){if(this.loading.id===e.id&&this.loading.promise)return this.loading.promise;for(let r=0;ri()),n+=1;else break}const a=await this._load({route:l,info:e},t);if(a)return a}return await this._load_error({status:404,error:new Error(`Not found: ${e.url.pathname}`),url:e.url})}async _get_navigation_result_from_branch({url:e,params:t,stuff:r,branch:l,status:n,error:a}){const s=l.filter(Boolean),i=s.find(u=>u.loaded&&u.loaded.redirect),c={redirect:i&&i.loaded?i.loaded.redirect:void 0,state:{url:e,params:t,branch:l,session_id:this.session_id},props:{components:s.map(u=>u.module.default)}};for(let u=0;u{Object.defineProperty(c.props.page,h,{get:()=>{throw new Error(`$page.${h} has been replaced by $page.url.${S}`)}})};u("origin","origin"),u("path","pathname"),u("query","searchParams")}const f=s[s.length-1],d=f.loaded&&f.loaded.maxage;if(d){const u=e.pathname+e.search;let h=!1;const S=()=>{this.cache.get(u)===c&&this.cache.delete(u),A(),clearTimeout(O)},O=setTimeout(S,d*1e3),A=this.stores.session.subscribe(()=>{h&&S()});h=!0,this.cache.set(u,c)}return c}async _load_node({status:e,error:t,module:r,url:l,params:n,stuff:a}){const s={module:r,uses:{params:new Set,url:!1,session:!1,stuff:!1,dependencies:[]},loaded:null,stuff:a},i={};for(const f in n)Object.defineProperty(i,f,{get(){return s.uses.params.add(f),n[f]},enumerable:!0});const c=this.$session;if(r.load){const{started:f}=this,d={params:i,get url(){return s.uses.url=!0,l},get session(){return s.uses.session=!0,c},get stuff(){return s.uses.stuff=!0,E({},a)},fetch(h,S){const O=typeof h=="string"?h:h.url,{href:A}=new URL(O,l);return s.uses.dependencies.push(A),f?fetch(h,S):Ke(h,S)}};t&&(d.status=e,d.error=t);const u=await r.load.call(null,d);if(!u)throw new Error("load function must return a value");s.loaded=Be(u),s.loaded.stuff&&(s.stuff=s.loaded.stuff)}return s}async _load({route:e,info:{url:t,path:r}},l){const n=t.pathname+t.search;if(!l){const m=this.cache.get(n);if(m)return m}const[a,s,i,c]=e,f=c?c(a.exec(r)):{},d=this.current.url&&{url:n!==this.current.url.pathname+this.current.url.search,params:Object.keys(f).filter(m=>this.current.params[m]!==f[m]),session:this.session_id!==this.current.session_id};let u=[],h={},S=!1,O=200,A;s.forEach(m=>m());e:for(let m=0;mU.uses.params.has(x))||d.session&&U.uses.session||U.uses.dependencies.some(x=>this.invalid.has(x))||S&&U.uses.stuff){if(w=await this._load_node({module:b,url:t,params:f,stuff:h}),w&&w.loaded){if(w.loaded.fallthrough)return;if(w.loaded.error&&(O=w.loaded.status,A=w.loaded.error),w.loaded.redirect)return{redirect:w.loaded.redirect,props:{},state:this.current};w.loaded.stuff&&(S=!0)}}else w=U}catch(b){O=500,A=re(b)}if(A){for(;m--;)if(i[m]){let b,U,q=m;for(;!(U=u[q]);)q-=1;try{if(b=await this._load_node({status:O,error:A,module:await i[m](),url:t,params:f,stuff:U.stuff}),b&&b.loaded&&b.loaded.error)continue;b&&b.loaded&&b.loaded.stuff&&(h=E(E({},h),b.loaded.stuff)),u=u.slice(0,q+1).concat(b);break e}catch{continue}}return await this._load_error({status:O,error:A,url:t})}else w&&w.loaded&&w.loaded.stuff&&(h=E(E({},h),w.loaded.stuff)),u.push(w)}return await this._get_navigation_result_from_branch({url:t,params:f,stuff:h,branch:u,status:O,error:A})}async _load_error({status:e,error:t,url:r}){var c,f;const l={},n=await this._load_node({module:await this.fallback[0],url:r,params:l,stuff:{}}),a=await this._load_node({status:e,error:t,module:await this.fallback[1],url:r,params:l,stuff:n&&n.loaded&&n.loaded.stuff||{}}),s=[n,a],i=E(E({},(c=n==null?void 0:n.loaded)==null?void 0:c.stuff),(f=a==null?void 0:a.loaded)==null?void 0:f.stuff);return await this._get_navigation_result_from_branch({url:r,params:l,stuff:i,branch:s,status:e,error:t})}}async function Me({paths:o,target:e,session:t,route:r,spa:l,trailing_slash:n,hydrate:a}){const s=new We({Root:Te,fallback:Ve,target:e,session:t}),i=r?new qe({base:o.base,routes:Ie,trailing_slash:n,renderer:s}):null;ye(o),a&&await s.start(a),i&&(l&&i.goto(location.href,{replaceState:!0},[]),i.init_listeners()),dispatchEvent(new CustomEvent("sveltekit:start"))}export{Me as start}; 2 | -------------------------------------------------------------------------------- /docs/articles/migrate-saas-sveltekit/lines_of_code_after.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Glench/sveltekit-saas/cd401a996d8199c51962ed3347818d8d51a846f7/docs/articles/migrate-saas-sveltekit/lines_of_code_after.png -------------------------------------------------------------------------------- /docs/articles/migrate-saas-sveltekit/lines_of_code_before.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Glench/sveltekit-saas/cd401a996d8199c51962ed3347818d8d51a846f7/docs/articles/migrate-saas-sveltekit/lines_of_code_before.png -------------------------------------------------------------------------------- /docs/articles/migrate-saas-sveltekit/performance.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Glench/sveltekit-saas/cd401a996d8199c51962ed3347818d8d51a846f7/docs/articles/migrate-saas-sveltekit/performance.png -------------------------------------------------------------------------------- /docs/articles/migrate-saas-to-sveltekit/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Reflections on Migrating my SaaS to SvelteKit 48 | 49 | 50 | 51 | 52 | 53 | 54 | 76 | 77 | 78 |
79 | 80 | 81 | 82 | 83 |
84 | 85 |
86 | 87 |

Reflections on Migrating my SaaS To SvelteKit

88 | 89 |

I run ExtensionPay, a service to monetize Chrome extensions by letting developers easily add payments to their extensions.

90 | 91 |

Because I seriously enjoy working with Svelte, I built ExtensionPay on top of a custom framework based around Svelte that would take care of all the server-side rendering, server/client communication, code-splitting, file minimizing, stylesheets, etc.

92 | 93 |

The framework was pretty nice for something I cobbled together, but had issues. One big one was startup time. My service would go down for way too long after restarting the app. Another issue was imports. It was difficult or impossible to import libraries client-side so I had to do annoying workarounds and that made me want to work on the code less. A custom framework can also be a businesses liability. So when SvelteKit 1.0 was announced I really wanted to switch over.

94 | 95 |

SvelteKit is the framework I’ve dreamed about since I started full stack development in 2010. It not only takes care of all the nitty gritty details of code-splitting, stylesheets, minimization, caching, client-side code, server-side code, server-side rendering, client-side hydration, CSRF, performance, etc, but is basically boilerplate-free. SvelteKit is apparently a lot like Next or Nuxt, but built on top of Svelte so it's blazing fast and a delight to work with.

96 | 97 |

Working very slowly, it took me a few months to migrate ExtensionPay from my old custom framework to SvelteKit by hand. I deployed the new version on April 2, 2023. Here are some results.

98 | 99 |

Lines of code

100 | 101 |

Here’s a count of the lines of code in my routes/ directory using cloc.

102 | 103 |

Before (my custom framework):

104 | 105 |
Screenshot of cloc showing 4975 lines of code total
106 | 107 |

After (SvelteKit):

108 | 109 |
Screenshot of cloc output showing 4397 lines of code total
110 | 111 |

Around 600 lines removed after the migration. That’s about a 12% reduction in code. My framework was already pretty lean so that's impressive.

112 | 113 |

Some of the code reduction was because there was a little duplicate code between pages, but most of it was just because SvelteKit removed the need for a lot of code. This is really great. As a solo founder, fewer lines of code to maintain and understand is a huge help.

114 | 115 |

Performance

116 | 117 |
Plot of CPU performance over time. On April 2 there is a black vertical line indicating the time the new SvelteKit was deployed.
118 | 119 |

I launched the SvelteKit-based app near that black line on the plot. It’s a little hard to see with the spikes, but the overall performance of the SvelteKit-based app is a tiny bit worse. Before, the midday usage would pretty consistently top out around 20% CPU. Now midday usage is typically around 30%. Not a big deal, but will probably mean I need to upgrade the server sooner. So the SvelteKit team owes me some money 😛 jk

120 | 121 |

I was actually concerned the performance would be much worse. In my local load testing with the ab command-line utility the SvelteKit app had about half the performance of my previous app for basic routes. Luckily in practice that didn’t happen. Maybe my test was flawed.

122 | 123 |

Feelings

124 | 125 |

Overall, the migration and deploy went fairly smoothly. It was a lot of work to basically rewrite the app and make sure everything still worked, but I’m happy I did it.

126 | 127 |

Having switched over, I feel like my app is on a lot more solid foundation with SvelteKit. I feel fantastic developing with the framework and my app feels fantastic to use.

128 | 129 |

A few pain points I ran into working with SvelteKit:

130 | 131 |
  • It was surprisingly difficult to get a REPL working nicely with SvelteKit. For business reasons I often have the need to run custom one-off commands in a REPL or script, but because of various import issues, creating a REPL that knew about my app and database was a challenge. I eventually got it to work, but it wasn’t easy.
  • 132 | 133 |
  • Related to the above, SvelteKit is pretty new so there’s not a lot about it on the internet yet. When I run into issues, there’s less material I can search for that might have fixes. Luckily there was enough and the SvelteKit team was actually crazy responsive to bugs or forum posts I made, and this problem will diminish over time.
  • 134 | 135 |
  • One small but annoying issue is that when I load 3rd-party libraries in the <head> of a SvelteKit route over http(s), the script loads in a blocking way when rendered from the server and in a non-blocking way when rendered via client-side navigation. This caused my code to fail during client-side navigation but not server-side navigation and it’s only through luck that I caught the failure before launch. I made an issue about this that was closed as “well that’s just how it works” and a nice suggestion about how I might achieve my goals instead. Still, It was kind of tricky to figure out how to make a workaround that worked in all cases.
  • 136 | 137 |
  • As demonstrated earlier, real-world performance is a little worse than my previous custom framework. I’m not sure why this is. Maybe it's SvelteKit or maybe it's something I'm doing. Either way, it’s not so bad.
  • 138 | 139 |
  • The naming conventions for routes is a little bonkers. Now when I’m working on my app I have like 10 tabs open that all have the name +page.server.ts. 🙄 Does anyone know how I can make this better in vim?
140 | 141 |

Some pleasures of working with SvelteKit:

142 | 143 |
  • The developer experience is phenomenal. Hot reloading is so much nicer than what I was using previously and all the built-in commands for testing work great.
  • 144 | 145 |
  • I’m really happy that SvelteKit allows parts of your app to be pre-rendered and others to be dynamic. I now pre-render my marketing pages (like this post!) which is great for performance. And the client-side routing also still works.
  • 146 | 147 |
  • Speaking of which, the client-side routing was a real unexpected pleasure. I assumed it was just extraneous, but it really does make my app feel a lot snappier and it’s just included automatically in SvelteKit without me having to do anything.
  • 148 | 149 |
  • Another unexpected pleasure was using TypeScript for static types and Playwright for end-to-end testing. Both give me an extra layer of confidence that my app is working as intended which is so important as a solo developer. Heck, I didn't set out to use Typescript or Playwright at all, but SvelteKit made them so easy to add that I just did it . And I'm so glad I did because I feel a lot more confident deploying code now.
  • 150 | 151 |
  • SvelteKit’s documentation is already quite good, and I know the team is also working on an interactive tutorial which is great. Good reference documentation is so helpful.
  • 152 | 153 |
  • I really love how forms work in SvelteKit. For whatever reason, working with forms in any other framework always felt so tedious to me. But the form actions API means it’s so easy to make boilerlate-free forms that work without client-side JavaScript but can be easily upgraded to AJAX with basically no extra code using enhance. Before the migration, I was getting a surprising number of error notification emails about people submitting forms on my site that didn’t have JavaScript enabled. Now I get none, and all my forms work work with and without client-side JavaScript by default. And I actually enjoy creating them.
154 | 155 |

Having my app on SvelteKit also comes with some other potential benefits. If I ever want to bring in another developer, it will be much easier to get them up to speed with SvelteKit than my own custom framework. And for the same reason, if I ever want to sell my app I imagine it will be an easier sell that it’s built on something well-supported and documented. Of course, this would be true for any well-known framework, but as I mentioned earlier I really love Svelte and dislike React so for me it’s an easy choice.

156 | 157 |

Overall, I’m really happy with SvelteKit and want to extend my sincere thanks for the SvelteKit team for working so hard on it, taking our feedback, and sharing it with us for free. It really is amazing.

158 | 159 |

I love it so much, in fact, that I'm taking what I've learned in my own app and developing a SvelteKit SaaS starter template.

160 | 161 |
162 |
163 | 164 |
165 | 166 | 167 | -------------------------------------------------------------------------------- /docs/articles/sveltekit-github-pages-guide/github_new_repo_settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Glench/sveltekit-saas/cd401a996d8199c51962ed3347818d8d51a846f7/docs/articles/sveltekit-github-pages-guide/github_new_repo_settings.png -------------------------------------------------------------------------------- /docs/articles/sveltekit-github-pages-guide/github_pages_branch_settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Glench/sveltekit-saas/cd401a996d8199c51962ed3347818d8d51a846f7/docs/articles/sveltekit-github-pages-guide/github_pages_branch_settings.png -------------------------------------------------------------------------------- /docs/articles/sveltekit-github-pages-guide/github_pages_dns_settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Glench/sveltekit-saas/cd401a996d8199c51962ed3347818d8d51a846f7/docs/articles/sveltekit-github-pages-guide/github_pages_dns_settings.png -------------------------------------------------------------------------------- /docs/articles/sveltekit-github-pages-guide/github_pages_settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Glench/sveltekit-saas/cd401a996d8199c51962ed3347818d8d51a846f7/docs/articles/sveltekit-github-pages-guide/github_pages_settings.png -------------------------------------------------------------------------------- /docs/articles/sveltekit-github-pages-guide/github_pages_site_published.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Glench/sveltekit-saas/cd401a996d8199c51962ed3347818d8d51a846f7/docs/articles/sveltekit-github-pages-guide/github_pages_site_published.png -------------------------------------------------------------------------------- /docs/articles/sveltekit-github-pages-guide/github_pages_site_ready.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Glench/sveltekit-saas/cd401a996d8199c51962ed3347818d8d51a846f7/docs/articles/sveltekit-github-pages-guide/github_pages_site_ready.png -------------------------------------------------------------------------------- /docs/articles/sveltekit-github-pages-guide/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | How to Deploy SvelteKit Apps to Github Pages 48 | 49 | 50 | 51 | 52 | 53 | 54 | 76 | 77 | 78 |
79 | 80 | 81 | 82 | 83 |
84 | 85 |
86 | 87 |

How to Deploy SvelteKit Apps to Github Pages

88 | 89 |

SvelteKit is an app framework based around the popular Svelte component library. SvelteKit is really awesome. You can deploy SvelteKit apps to any server-based, serverless, or even static files host you want!

90 |

In this guide we’ll show you how you can deploy SvelteKit apps to Github Pages, a popular free way to host static files directly from a Github repository.

91 | 92 | 93 |

Getting Started

94 | 95 |

Want to get up and running right away? Just use this Github template repository. It has everything you need already set up. Once you have that you can skip to the Configure Github Pages section below.

96 |

Already have a Github repository you want to use but not SvelteKit? Skip to the Install SvelteKit section below.

97 |

Otherwise, you can continue with this section and learn how to create a SvelteKit Github repository from scratch. To do that we’ll need two things — a Github repository and a new SvelteKit project.

98 | 99 | 100 |

Create a new Github repository

101 | 102 |

First, let’s make a new Github repository.

103 |

Here’s a link to Github’s create new repository page (no repository will be made until you click the “Create repository” button on that page). The settings you should choose are:

104 |
  • Repository name 105 | : Choose any name you like.
106 |
  • Public / Private: 107 | If you have a free Github account, you’ll need to choose Public 108 | since Github only allows Github Pages hosting from public repositories for free accounts. If you have a paid Github Pro or Team account, you can choose either Public 109 | or Private.
110 |
  • Add a README file: 111 | Optional. We’ll add a README later, but you can add one now if you like.
112 |
  • Add .gitignore: 113 | Checked 114 | . Choose “Node 115 | ” from the “.gitignore template” dropdown. This will make it so that unnecessary files aren’t checked into your repository.
116 |
  • Choose a license 117 | : Optional. Choose any license you want, or none. You can always add one later if you’re not sure.
118 |
Screenshot of Github's new repository settings page
119 |

Once you’re happy with your settings, click the Create repository button and you’ll have a new blank Github repository.

120 |

To get the repository on your computer, you can run git clone <repository url> in a terminal or use your favorite git software.

121 | 122 | 123 |

Install SvelteKit

124 |

Now we can install SvelteKit. Open a terminal to the Github repository on your computer and run the following command:

125 | 126 |
npm create svelte@latest
127 | 128 |

You’ll be walked through the configuration options for SvelteKit. You can choose any settings you want.

Then we need to install all the necessary SvelteKit packages:

npm install
129 |

Then we can add all these files to our Git repository:

130 |
git add -A && git commit -m "Initial commit"
131 |

And finally run the development server to test that everything is working:

132 |
npm run dev -- --open
133 | 134 |
Screenshot of SvelteKit's default skeleton page
135 | 136 |

Yay!

137 |

Configure Github Pages

138 |

Github Pages only allows sites to be hosted from either the root of a repository (/) or a /docs folder. Sites can also be hosted from any branch of a repository, but again, only in the root or /docs folder.

139 |

To keep things simple and clean, we’re going to put our static site in a /docs folder in the main branch.

140 |

In your Github repository settings, navigate to the Pages section. It should look something like this:

141 | 142 |
Screenshot of Github Pages' settings
143 | 144 |

In the “Source” section there’s a dropdown that currently says “None”. Instead of None, choose the main branch and then the /docs folder, as shown below.

145 | 146 |
Screenshot of Github Pages' branch and folder settings
147 | 148 |

Click “Save” and the page will reload and tell you that your site is ready to be served.

149 |

Configure SvelteKit for Github Pages

150 |

Now we have to tell SvelteKit that we want our static site to go in the /docs folder.

151 |

First, we need to install the SvelteKit static adapter which will output our app as a static set of files instead of a dynamic app:

152 |
npm install @sveltejs/adapter-static --save-dev
153 |

(You can also run npm uninstall @sveltejs/adapter-auto --save-dev to remove the unnecessary auto adapter from your package if you want.)

154 |

Edit svelte.config.js to use the static adapter as shown below:

155 |
import adapter from "@sveltejs/adapter-static"; 
156 | // was "@sveltejs/adapter-auto"
157 | 
158 | const dev = "production" === "development";
159 | 
160 | /** @type {import(""@sveltejs/kit").Config} */
161 | const config = {
162 |     kit: {
163 |         adapter: adapter({
164 |             pages: "docs",
165 |             assets: "docs"
166 |         }),
167 |         paths: {
168 |             // change below to your repo name
169 |             base: dev ? """ : "/your-repo-name",
170 |         },
171 |         // hydrate the <div id="svelte"> element in src/app.html
172 |         target: "#svelte"
173 |     }
174 | };
175 | 
176 | export default config;
177 |

There are a few things we changed from the default sveltekit config file. We imported adapter-static instead of adapter-auto at the top of the file. We also configured the adapter to use the docs folder as the output target for both our site’s pages as well as static assets like stylesheets, images, and so on.

178 |

And finally we also added a paths configuration option which tells SvelteKit the URL path of the SvelteKit site. By default, Github Pages will host your static site at username.github.io/your-repo-name. So we need to configure SvelteKit so that it knows its URLs should fall under /your-repo-name when hosted on Github Pages, but not when testing our site on our own computers (it’s hosted at localhost:3000/ during testing with npm run dev).

179 |

You can run npm run dev again to make sure everything is still working before going to the next step.

180 |

Add a blank .nojekyll file in static

181 |

To configure Github Pages so that it doesn’t use the Jekyll static site generator (which it does by default), we simply have to create a blank .nojekyll file in the static folder. You can do that any way you like, but here’s how you can do it from the command line:

182 |
touch static/.nojekyll && git add static/.nojekyll
183 |

184 |

Deploy your site on Github Pages

185 |

So we have a demo site that runs fine on our computer with npm run dev but we want to deploy it on Github pages. To do that, we use SvelteKit’s build command.

186 |
npm run build
187 |

This command will create a docs folder if it doesn’t exist and put all the HTML, CSS, and JavaScript files there. It will also delete and recreate the docs folder every time so make sure not to put any other files in there or they might be deleted.

188 |

Don’t forget to add the docs folder to your repository:

189 |
git add docs
190 |

Now if you commit your changes...

191 |
git commit -m "add /docs"
192 |

...and push them to Github...

193 |
git push origin main
194 |

Github will automatically deploy your site! In the Github Pages settings you’ll see this right after you push:

195 | 196 |
Screenshot of Github Pages' settings with site ready to be published
197 |

If you wait a little bit, eventually that page will look like this:

198 | 199 |
Screenshot of Github Pages' successful publish
200 | 201 |

Click the link to see your new site running with Github Pages!

202 |

That is generally the process every time you make a change:

203 |
  1. npm run build
  2. 204 |
  3. git add docs && git commit
  4. 205 |
  5. git push origin main
206 |

Custom Domains for Github Pages

207 |

There is one last thing you need to do only if you want to host your SvelteKit site on a custom domain through Github Pages.

208 |

First, read about how to set up custom domains on the Github Pages guide here.

209 |

The main thing you’ll need in your SvelteKit repository is a CNAME file containing the URL of the site you want:

210 |
yourcustomdomain.com
211 |

Put this CNAME file in the static directory of your SvelteKit project and you’re good to go!

212 | 213 |

Addendum: Getting HTTP, HTTPS, and WWW working with custom domains on Github

214 | 215 |

This isn’t a SvelteKit-specific thing but it’s a little confusing how to properly configure DNS for Github Pages so that all the following custom domain URLs will work:

216 | 217 |
example.com
218 | www.example.com
219 | http://example.com
220 | http://www.example.com
221 | https://example.com
222 | https://www.example.com
223 |

To make all those work, it’s pretty simple. Just add “A Records” pointing toward Github’s IP addresses, as well as a “CNAME Record” for www pointed at username.github.io:

224 |
Screenshot of DNS settings for Github custom domain
225 |

You should also choose “Enforce HTTPS” from the Github Pages settings page. With this setup, every URL from the list above will automatically go to https://example.com.

226 | 227 |
228 |
229 | 230 |
231 | 232 | 233 | -------------------------------------------------------------------------------- /docs/articles/sveltekit-github-pages-guide/sveltekit_skeleton_page.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Glench/sveltekit-saas/cd401a996d8199c51962ed3347818d8d51a846f7/docs/articles/sveltekit-github-pages-guide/sveltekit_skeleton_page.png -------------------------------------------------------------------------------- /docs/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Glench/sveltekit-saas/cd401a996d8199c51962ed3347818d8d51a846f7/docs/favicon.png -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Launch Faster with the Full-stack SaaS Template for SvelteKit 15 | 16 | 17 | 18 | 19 | 20 | 41 | 42 | 43 |
44 | 45 | 46 | 47 | 48 |

Launch faster with the Svelte SaaS Starter Kit

49 | 50 |

A full-stack SaaS template built on SvelteKit that starts your project with all the features that are the same in every app.

51 | 52 |

Focus on the unique parts of your app, save weeks of tedious coding, and launch faster. 🚀

53 | 54 | 55 |
56 |

Svelte SaaS 🚀

57 |
  • User signup and auth
  • 58 |
  • Admin dashboards
  • 59 |
  • Stripe subscriptions
  • 60 |
  • Database models, forms, REST APIs, and more
61 | 62 |

SvelteKit 🔥

63 |
  • Optimized full-stack apps with no configuration
  • 64 |
  • SEO and performance from server-side rendering
  • 65 |
  • Zippy UX from client-side hydration & routing
  • 66 |
  • Stay in flow with instant hot-module reloading
67 | 68 | 69 |

Svelte ❤️

70 |
73 | 74 |

Guides

75 |

How to Deploy SvelteKit Apps to Github Pages

76 |

Reflections on Migrating my SaaS to Sveltekit

77 |
78 | 79 |
80 | 81 | 82 | -------------------------------------------------------------------------------- /docs/mvp.css: -------------------------------------------------------------------------------- 1 | /* MVP.css v1.8 - https://github.com/andybrewer/mvp */ 2 | 3 | :root { 4 | --active-brightness: 0.85; 5 | --border-radius: 5px; 6 | --box-shadow: 2px 2px 10px; 7 | --color: #118bee; 8 | --color-accent: #118bee15; 9 | --color-bg: #fff; 10 | --color-bg-secondary: #e9e9e9; 11 | --color-link: #118bee; 12 | --color-secondary: #920de9; 13 | --color-secondary-accent: #920de90b; 14 | --color-shadow: #f4f4f4; 15 | --color-table: #118bee; 16 | --color-text: #000; 17 | --color-text-secondary: #999; 18 | --font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif; 19 | --hover-brightness: 1.2; 20 | --justify-important: center; 21 | --justify-normal: left; 22 | --line-height: 1.5; 23 | --width-card: 285px; 24 | --width-card-medium: 460px; 25 | --width-card-wide: 800px; 26 | --width-content: 1080px; 27 | } 28 | 29 | @media (prefers-color-scheme: dark) { 30 | :root { 31 | --color: #0097fc; 32 | --color-accent: #0097fc4f; 33 | --color-bg: #333; 34 | --color-bg-secondary: #555; 35 | --color-link: #0097fc; 36 | --color-secondary: #e20de9; 37 | --color-secondary-accent: #e20de94f; 38 | --color-shadow: #bbbbbb20; 39 | --color-table: #0097fc; 40 | --color-text: #f7f7f7; 41 | --color-text-secondary: #aaa; 42 | } 43 | } 44 | 45 | /* Layout */ 46 | article aside { 47 | background: var(--color-secondary-accent); 48 | border-left: 4px solid var(--color-secondary); 49 | padding: 0.01rem 0.8rem; 50 | } 51 | 52 | body { 53 | background: var(--color-bg); 54 | color: var(--color-text); 55 | font-family: var(--font-family); 56 | line-height: var(--line-height); 57 | margin: 0; 58 | overflow-x: hidden; 59 | padding: 0; 60 | } 61 | 62 | footer, 63 | header, 64 | main { 65 | margin: 0 auto; 66 | max-width: var(--width-content); 67 | padding: 3rem 1rem; 68 | } 69 | 70 | hr { 71 | background-color: var(--color-bg-secondary); 72 | border: none; 73 | height: 1px; 74 | margin: 4rem 0; 75 | width: 100%; 76 | } 77 | 78 | section { 79 | display: flex; 80 | flex-wrap: wrap; 81 | justify-content: var(--justify-important); 82 | } 83 | 84 | section img, 85 | article img { 86 | max-width: 100%; 87 | } 88 | 89 | section pre { 90 | overflow: auto; 91 | } 92 | 93 | section aside { 94 | border: 1px solid var(--color-bg-secondary); 95 | border-radius: var(--border-radius); 96 | box-shadow: var(--box-shadow) var(--color-shadow); 97 | margin: 1rem; 98 | padding: 1.25rem; 99 | width: var(--width-card); 100 | } 101 | 102 | section aside:hover { 103 | box-shadow: var(--box-shadow) var(--color-bg-secondary); 104 | } 105 | 106 | [hidden] { 107 | display: none; 108 | } 109 | 110 | /* Headers */ 111 | article header, 112 | div header, 113 | main header { 114 | padding-top: 0; 115 | } 116 | 117 | header { 118 | text-align: var(--justify-important); 119 | } 120 | 121 | header a b, 122 | header a em, 123 | header a i, 124 | header a strong { 125 | margin-left: 0.5rem; 126 | margin-right: 0.5rem; 127 | } 128 | 129 | header nav img { 130 | margin: 1rem 0; 131 | } 132 | 133 | section header { 134 | padding-top: 0; 135 | width: 100%; 136 | } 137 | 138 | /* Nav */ 139 | nav { 140 | align-items: center; 141 | display: flex; 142 | font-weight: bold; 143 | justify-content: space-between; 144 | margin-bottom: 7rem; 145 | } 146 | 147 | nav ul { 148 | list-style: none; 149 | padding: 0; 150 | } 151 | 152 | nav ul li { 153 | display: inline-block; 154 | margin: 0 0.5rem; 155 | position: relative; 156 | text-align: left; 157 | } 158 | 159 | /* Nav Dropdown */ 160 | nav ul li:hover ul { 161 | display: block; 162 | } 163 | 164 | nav ul li ul { 165 | background: var(--color-bg); 166 | border: 1px solid var(--color-bg-secondary); 167 | border-radius: var(--border-radius); 168 | box-shadow: var(--box-shadow) var(--color-shadow); 169 | display: none; 170 | height: auto; 171 | left: -2px; 172 | padding: .5rem 1rem; 173 | position: absolute; 174 | top: 1.7rem; 175 | white-space: nowrap; 176 | width: auto; 177 | z-index: 1; 178 | } 179 | 180 | nav ul li ul::before { 181 | /* fill gap above to make mousing over them easier */ 182 | content: ""; 183 | position: absolute; 184 | left: 0; 185 | right: 0; 186 | top: -0.5rem; 187 | height: 0.5rem; 188 | } 189 | 190 | nav ul li ul li, 191 | nav ul li ul li a { 192 | display: block; 193 | } 194 | 195 | /* Typography */ 196 | code, 197 | samp { 198 | background-color: var(--color-accent); 199 | border-radius: var(--border-radius); 200 | color: var(--color-text); 201 | display: inline-block; 202 | margin: 0 0.1rem; 203 | padding: 0 0.5rem; 204 | } 205 | 206 | details { 207 | margin: 1.3rem 0; 208 | } 209 | 210 | details summary { 211 | font-weight: bold; 212 | cursor: pointer; 213 | } 214 | 215 | h1, 216 | h2, 217 | h3, 218 | h4, 219 | h5, 220 | h6 { 221 | line-height: var(--line-height); 222 | } 223 | 224 | mark { 225 | padding: 0.1rem; 226 | } 227 | 228 | ol li, 229 | ul li { 230 | padding: 0.2rem 0; 231 | } 232 | 233 | p { 234 | margin: 0.75rem 0; 235 | padding: 0; 236 | width: 100%; 237 | } 238 | 239 | pre { 240 | margin: 1rem 0; 241 | max-width: var(--width-card-wide); 242 | padding: 1rem 0; 243 | } 244 | 245 | pre code, 246 | pre samp { 247 | display: block; 248 | max-width: var(--width-card-wide); 249 | padding: 0.5rem 2rem; 250 | white-space: pre-wrap; 251 | } 252 | 253 | small { 254 | color: var(--color-text-secondary); 255 | } 256 | 257 | sup { 258 | background-color: var(--color-secondary); 259 | border-radius: var(--border-radius); 260 | color: var(--color-bg); 261 | font-size: xx-small; 262 | font-weight: bold; 263 | margin: 0.2rem; 264 | padding: 0.2rem 0.3rem; 265 | position: relative; 266 | top: -2px; 267 | } 268 | 269 | /* Links */ 270 | a { 271 | color: var(--color-link); 272 | display: inline-block; 273 | font-weight: bold; 274 | text-decoration: none; 275 | } 276 | 277 | a:active { 278 | filter: brightness(var(--active-brightness)); 279 | text-decoration: underline; 280 | } 281 | 282 | a:hover { 283 | filter: brightness(var(--hover-brightness)); 284 | text-decoration: underline; 285 | } 286 | 287 | a b, 288 | a em, 289 | a i, 290 | a strong, 291 | button { 292 | border-radius: var(--border-radius); 293 | display: inline-block; 294 | font-size: medium; 295 | font-weight: bold; 296 | line-height: var(--line-height); 297 | margin: 0.5rem 0; 298 | padding: 1rem 2rem; 299 | } 300 | 301 | button { 302 | font-family: var(--font-family); 303 | } 304 | 305 | button:active { 306 | filter: brightness(var(--active-brightness)); 307 | } 308 | 309 | button:hover { 310 | cursor: pointer; 311 | filter: brightness(var(--hover-brightness)); 312 | } 313 | 314 | a b, 315 | a strong, 316 | button { 317 | background-color: var(--color-link); 318 | border: 2px solid var(--color-link); 319 | color: var(--color-bg); 320 | } 321 | 322 | a em, 323 | a i { 324 | border: 2px solid var(--color-link); 325 | border-radius: var(--border-radius); 326 | color: var(--color-link); 327 | display: inline-block; 328 | padding: 1rem 2rem; 329 | } 330 | 331 | article aside a { 332 | color: var(--color-secondary); 333 | } 334 | 335 | /* Images */ 336 | figure { 337 | margin: 0; 338 | padding: 0; 339 | } 340 | 341 | figure img { 342 | max-width: 100%; 343 | } 344 | 345 | figure figcaption { 346 | color: var(--color-text-secondary); 347 | } 348 | 349 | /* Forms */ 350 | 351 | button:disabled, 352 | input:disabled { 353 | background: var(--color-bg-secondary); 354 | border-color: var(--color-bg-secondary); 355 | color: var(--color-text-secondary); 356 | cursor: not-allowed; 357 | } 358 | 359 | button[disabled]:hover { 360 | filter: none; 361 | } 362 | 363 | form { 364 | border: 1px solid var(--color-bg-secondary); 365 | border-radius: var(--border-radius); 366 | box-shadow: var(--box-shadow) var(--color-shadow); 367 | display: block; 368 | max-width: var(--width-card-wide); 369 | min-width: var(--width-card); 370 | padding: 1.5rem; 371 | text-align: var(--justify-normal); 372 | } 373 | 374 | form header { 375 | margin: 1.5rem 0; 376 | padding: 1.5rem 0; 377 | } 378 | 379 | input, 380 | label, 381 | select, 382 | textarea { 383 | display: block; 384 | font-size: inherit; 385 | max-width: var(--width-card-wide); 386 | } 387 | 388 | input[type="checkbox"], 389 | input[type="radio"] { 390 | display: inline-block; 391 | } 392 | 393 | input[type="checkbox"]+label, 394 | input[type="radio"]+label { 395 | display: inline-block; 396 | font-weight: normal; 397 | position: relative; 398 | top: 1px; 399 | } 400 | 401 | input, 402 | select, 403 | textarea { 404 | border: 1px solid var(--color-bg-secondary); 405 | border-radius: var(--border-radius); 406 | margin-bottom: 1rem; 407 | padding: 0.4rem 0.8rem; 408 | } 409 | 410 | input[readonly], 411 | textarea[readonly] { 412 | background-color: var(--color-bg-secondary); 413 | } 414 | 415 | label { 416 | font-weight: bold; 417 | margin-bottom: 0.2rem; 418 | } 419 | 420 | /* Tables */ 421 | table { 422 | border: 1px solid var(--color-bg-secondary); 423 | border-radius: var(--border-radius); 424 | border-spacing: 0; 425 | display: inline-block; 426 | max-width: 100%; 427 | overflow-x: auto; 428 | padding: 0; 429 | white-space: nowrap; 430 | } 431 | 432 | table td, 433 | table th, 434 | table tr { 435 | padding: 0.4rem 0.8rem; 436 | text-align: var(--justify-important); 437 | } 438 | 439 | table thead { 440 | background-color: var(--color-table); 441 | border-collapse: collapse; 442 | border-radius: var(--border-radius); 443 | color: var(--color-bg); 444 | margin: 0; 445 | padding: 0; 446 | } 447 | 448 | table thead th:first-child { 449 | border-top-left-radius: var(--border-radius); 450 | } 451 | 452 | table thead th:last-child { 453 | border-top-right-radius: var(--border-radius); 454 | } 455 | 456 | table thead th:first-child, 457 | table tr td:first-child { 458 | text-align: var(--justify-normal); 459 | } 460 | 461 | table tr:nth-child(even) { 462 | background-color: var(--color-accent); 463 | } 464 | 465 | /* Quotes */ 466 | blockquote { 467 | display: block; 468 | font-size: x-large; 469 | line-height: var(--line-height); 470 | margin: 1rem auto; 471 | max-width: var(--width-card-medium); 472 | padding: 1.5rem 1rem; 473 | text-align: var(--justify-important); 474 | } 475 | 476 | blockquote footer { 477 | color: var(--color-text-secondary); 478 | display: block; 479 | font-size: small; 480 | line-height: var(--line-height); 481 | padding: 1.5rem 0; 482 | } 483 | -------------------------------------------------------------------------------- /docs/sitemap.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | https://sveltekitsaas.com 6 | 7 | 8 | https://sveltekitsaas.com/articles/migrate-saas-to-sveltekit 9 | 10 | 11 | https://sveltekitsaas.com/articles/sveltekit-github-pages-guide 12 | 13 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "svelte-saas", 3 | "version": "0.0.1", 4 | "scripts": { 5 | "dev": "svelte-kit dev", 6 | "build": "svelte-kit build && echo 'sveltekitsaas.com' > docs/CNAME && npm run sitemap", 7 | "sitemap": "svelte-sitemap --domain https://sveltekitsaas.com -o docs", 8 | "package": "svelte-kit package", 9 | "preview": "svelte-kit preview", 10 | "check": "svelte-check --tsconfig ./tsconfig.json", 11 | "check:watch": "svelte-check --tsconfig ./tsconfig.json --watch", 12 | "lint": "prettier --ignore-path .gitignore --check --plugin-search-dir=. . && eslint --ignore-path .gitignore .", 13 | "format": "prettier --ignore-path .gitignore --write --plugin-search-dir=. ." 14 | }, 15 | "devDependencies": { 16 | "@sveltejs/adapter-auto": "next", 17 | "@sveltejs/adapter-static": "next", 18 | "@sveltejs/kit": "next", 19 | "@typescript-eslint/eslint-plugin": "^4.31.1", 20 | "@typescript-eslint/parser": "^4.31.1", 21 | "eslint": "^7.32.0", 22 | "eslint-config-prettier": "^8.3.0", 23 | "eslint-plugin-svelte3": "^3.2.1", 24 | "prettier": "^2.4.1", 25 | "prettier-plugin-svelte": "^2.4.0", 26 | "svelte": "^3.44.0", 27 | "svelte-check": "^2.2.6", 28 | "svelte-preprocess": "^4.9.4", 29 | "svelte-sitemap": "^2.3.0", 30 | "tslib": "^2.3.1", 31 | "typescript": "^4.4.3" 32 | }, 33 | "type": "module" 34 | } 35 | -------------------------------------------------------------------------------- /src/app.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | %svelte.head% 10 | 11 | 12 |
%svelte.body%
13 | 14 | 15 | -------------------------------------------------------------------------------- /src/global.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /src/routes/articles/__layout.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 20 | 55 | 56 | 57 |
58 | 61 | 62 |
63 | 64 |
65 | 66 |
67 | 68 |
69 |
70 | 71 | 80 | 81 | -------------------------------------------------------------------------------- /src/routes/articles/migrate-saas-to-sveltekit.svelte: -------------------------------------------------------------------------------- 1 | 4 | 5 | Reflections on Migrating my SaaS to SvelteKit 6 | 7 | 8 |
9 |

Reflections on Migrating my SaaS To SvelteKit

10 |
11 | 12 |

I run ExtensionPay, a service to monetize Chrome extensions by letting developers easily add payments to their extensions.

13 | 14 |

Because I seriously enjoy working with Svelte, I built ExtensionPay on top of a custom framework based around Svelte that would take care of all the server-side rendering, server/client communication, code-splitting, file minimizing, stylesheets, etc.

15 | 16 |

The framework was pretty nice for something I cobbled together, but had issues. One big one was startup time. My service would go down for way too long after restarting the app. Another issue was imports. It was difficult or impossible to import libraries client-side so I had to do annoying workarounds and that made me want to work on the code less. A custom framework can also be a businesses liability. So when SvelteKit 1.0 was announced I really wanted to switch over.

17 | 18 |

SvelteKit is the framework I’ve dreamed about since I started full stack development in 2010. It not only takes care of all the nitty gritty details of code-splitting, stylesheets, minimization, caching, client-side code, server-side code, server-side rendering, client-side hydration, CSRF, performance, etc, but is basically boilerplate-free. SvelteKit is apparently a lot like Next or Nuxt, but built on top of Svelte so it's blazing fast and a delight to work with.

19 | 20 |

Working very slowly, it took me a few months to migrate ExtensionPay from my old custom framework to SvelteKit by hand. I deployed the new version on April 2, 2023. Here are some results.

21 | 22 |

Lines of code

23 | 24 |

Here’s a count of the lines of code in my routes/ directory using cloc.

25 | 26 |

Before (my custom framework):

27 | 28 |
Screenshot of cloc showing 4975 lines of code total
29 | 30 |

After (SvelteKit):

31 | 32 |
Screenshot of cloc output showing 4397 lines of code total
33 | 34 |

Around 600 lines removed after the migration. That’s about a 12% reduction in code. My framework was already pretty lean so that's impressive.

35 | 36 |

Some of the code reduction was because there was a little duplicate code between pages, but most of it was just because SvelteKit removed the need for a lot of code. This is really great. As a solo founder, fewer lines of code to maintain and understand is a huge help.

37 | 38 |

Performance

39 | 40 |
Plot of CPU performance over time. On April 2 there is a black vertical line indicating the time the new SvelteKit was deployed.
41 | 42 |

I launched the SvelteKit-based app near that black line on the plot. It’s a little hard to see with the spikes, but the overall performance of the SvelteKit-based app is a tiny bit worse. Before, the midday usage would pretty consistently top out around 20% CPU. Now midday usage is typically around 30%. Not a big deal, but will probably mean I need to upgrade the server sooner. So the SvelteKit team owes me some money 😛 jk

43 | 44 |

I was actually concerned the performance would be much worse. In my local load testing with the ab command-line utility the SvelteKit app had about half the performance of my previous app for basic routes. Luckily in practice that didn’t happen. Maybe my test was flawed.

45 | 46 |

Feelings

47 | 48 |

Overall, the migration and deploy went fairly smoothly. It was a lot of work to basically rewrite the app and make sure everything still worked, but I’m happy I did it.

49 | 50 |

Having switched over, I feel like my app is on a lot more solid foundation with SvelteKit. I feel fantastic developing with the framework and my app feels fantastic to use.

51 | 52 |

A few pain points I ran into working with SvelteKit:

53 | 54 |
    55 |
  • It was surprisingly difficult to get a REPL working nicely with SvelteKit. For business reasons I often have the need to run custom one-off commands in a REPL or script, but because of various import issues, creating a REPL that knew about my app and database was a challenge. I eventually got it to work, but it wasn’t easy.
  • 56 | 57 |
  • Related to the above, SvelteKit is pretty new so there’s not a lot about it on the internet yet. When I run into issues, there’s less material I can search for that might have fixes. Luckily there was enough and the SvelteKit team was actually crazy responsive to bugs or forum posts I made, and this problem will diminish over time.
  • 58 | 59 |
  • One small but annoying issue is that when I load 3rd-party libraries in the <head> of a SvelteKit route over http(s), the script loads in a blocking way when rendered from the server and in a non-blocking way when rendered via client-side navigation. This caused my code to fail during client-side navigation but not server-side navigation and it’s only through luck that I caught the failure before launch. I made an issue about this that was closed as “well that’s just how it works” and a nice suggestion about how I might achieve my goals instead. Still, It was kind of tricky to figure out how to make a workaround that worked in all cases.
  • 60 | 61 |
  • As demonstrated earlier, real-world performance is a little worse than my previous custom framework. I’m not sure why this is. Maybe it's SvelteKit or maybe it's something I'm doing. Either way, it’s not so bad.
  • 62 | 63 |
  • The naming conventions for routes is a little bonkers. Now when I’m working on my app I have like 10 tabs open that all have the name +page.server.ts. 🙄 Does anyone know how I can make this better in vim?
  • 64 |
65 | 66 |

Some pleasures of working with SvelteKit:

67 | 68 |
    69 |
  • The developer experience is phenomenal. Hot reloading is so much nicer than what I was using previously and all the built-in commands for testing work great.
  • 70 | 71 |
  • I’m really happy that SvelteKit allows parts of your app to be pre-rendered and others to be dynamic. I now pre-render my marketing pages (like this post!) which is great for performance. And the client-side routing also still works.
  • 72 | 73 |
  • Speaking of which, the client-side routing was a real unexpected pleasure. I assumed it was just extraneous, but it really does make my app feel a lot snappier and it’s just included automatically in SvelteKit without me having to do anything.
  • 74 | 75 |
  • Another unexpected pleasure was using TypeScript for static types and Playwright for end-to-end testing. Both give me an extra layer of confidence that my app is working as intended which is so important as a solo developer. Heck, I didn't set out to use Typescript or Playwright at all, but SvelteKit made them so easy to add that I just did it . And I'm so glad I did because I feel a lot more confident deploying code now.
  • 76 | 77 |
  • SvelteKit’s documentation is already quite good, and I know the team is also working on an interactive tutorial which is great. Good reference documentation is so helpful.
  • 78 | 79 |
  • I really love how forms work in SvelteKit. For whatever reason, working with forms in any other framework always felt so tedious to me. But the form actions API means it’s so easy to make boilerlate-free forms that work without client-side JavaScript but can be easily upgraded to AJAX with basically no extra code using enhance. Before the migration, I was getting a surprising number of error notification emails about people submitting forms on my site that didn’t have JavaScript enabled. Now I get none, and all my forms work work with and without client-side JavaScript by default. And I actually enjoy creating them.
  • 80 |
81 | 82 |

Having my app on SvelteKit also comes with some other potential benefits. If I ever want to bring in another developer, it will be much easier to get them up to speed with SvelteKit than my own custom framework. And for the same reason, if I ever want to sell my app I imagine it will be an easier sell that it’s built on something well-supported and documented. Of course, this would be true for any well-known framework, but as I mentioned earlier I really love Svelte and dislike React so for me it’s an easy choice.

83 | 84 |

Overall, I’m really happy with SvelteKit and want to extend my sincere thanks for the SvelteKit team for working so hard on it, taking our feedback, and sharing it with us for free. It really is amazing.

85 | 86 |

I love it so much, in fact, that I'm taking what I've learned in my own app and developing a SvelteKit SaaS starter template.

87 | 88 | -------------------------------------------------------------------------------- /src/routes/articles/sveltekit-github-pages-guide.svelte: -------------------------------------------------------------------------------- 1 | 4 | 5 | How to Deploy SvelteKit Apps to Github Pages 6 | 7 | 8 |
9 |

How to Deploy SvelteKit Apps to Github Pages

10 |
11 | 12 |

SvelteKit is an app framework based around the popular Svelte component library. SvelteKit is really awesome. You can deploy SvelteKit apps to any server-based, serverless, or even static files host you want!

13 |

In this guide we’ll show you how you can deploy SvelteKit apps to Github Pages, a popular free way to host static files directly from a Github repository.

14 | 15 | 16 |

Getting Started

17 | 18 |

Want to get up and running right away? Just use this Github template repository. It has everything you need already set up. Once you have that you can skip to the Configure Github Pages section below.

19 |

Already have a Github repository you want to use but not SvelteKit? Skip to the Install SvelteKit section below.

20 |

Otherwise, you can continue with this section and learn how to create a SvelteKit Github repository from scratch. To do that we’ll need two things — a Github repository and a new SvelteKit project.

21 | 22 | 23 |

Create a new Github repository

24 | 25 |

First, let’s make a new Github repository.

26 |

Here’s a link to Github’s create new repository page (no repository will be made until you click the “Create repository” button on that page). The settings you should choose are:

27 |
  • 28 | Repository name 29 | : Choose any name you like.
30 |
  • 31 | Public / Private: 32 | If you have a free Github account, you’ll need to choose Public 33 | since Github only allows Github Pages hosting from public repositories for free accounts. If you have a paid Github Pro or Team account, you can choose either Public 34 | or Private. 35 |
36 |
  • 37 | Add a README file: 38 | Optional. We’ll add a README later, but you can add one now if you like.
39 |
  • 40 | Add .gitignore: 41 | Checked 42 | . Choose “Node 43 | ” from the “.gitignore template” dropdown. This will make it so that unnecessary files aren’t checked into your repository.
44 |
  • 45 | Choose a license 46 | : Optional. Choose any license you want, or none. You can always add one later if you’re not sure.
47 |
48 | Screenshot of Github's new repository settings page 49 |
50 |

Once you’re happy with your settings, click the Create repository button and you’ll have a new blank Github repository.

51 |

To get the repository on your computer, you can run git clone <repository url> in a terminal or use your favorite git software.

52 | 53 | 54 |

Install SvelteKit

55 |

Now we can install SvelteKit. Open a terminal to the Github repository on your computer and run the following command:

56 | 57 |
 58 |         npm create svelte@latest
 59 |     
60 | 61 |

62 | You’ll be walked through the configuration options for SvelteKit. You can choose any settings you want.

63 | Then we need to install all the necessary SvelteKit packages:

 64 |     npm install
 65 |     
66 |

Then we can add all these files to our Git repository:

67 |
 68 |         git add -A && git commit -m "Initial commit"
 69 |     
70 |

And finally run the development server to test that everything is working:

71 |
 72 |         npm run dev -- --open
 73 |     
74 | 75 |
Screenshot of SvelteKit's default skeleton page
76 | 77 |

Yay!

78 |

Configure Github Pages

79 |

Github Pages only allows sites to be hosted from either the root of a repository (/) or a /docs folder. Sites can also be hosted from any branch of a repository, but again, only in the root or /docs folder.

80 |

To keep things simple and clean, we’re going to put our static site in a /docs folder in the main branch.

81 |

In your Github repository settings, navigate to the Pages section. It should look something like this:

82 | 83 |
84 | Screenshot of Github Pages' settings 85 |
86 | 87 |

In the “Source” section there’s a dropdown that currently says “None”. Instead of None, choose the main branch and then the /docs folder, as shown below.

88 | 89 |
90 | Screenshot of Github Pages' branch and folder settings 91 |
92 | 93 |

Click “Save” and the page will reload and tell you that your site is ready to be served.

94 |

Configure SvelteKit for Github Pages

95 |

Now we have to tell SvelteKit that we want our static site to go in the /docs folder.

96 |

First, we need to install the SvelteKit static adapter which will output our app as a static set of files instead of a dynamic app:

97 |
npm install @sveltejs/adapter-static --save-dev
98 |

(You can also run npm uninstall @sveltejs/adapter-auto --save-dev to remove the unnecessary auto adapter from your package if you want.)

99 |

Edit svelte.config.js to use the static adapter as shown below:

100 |
{`import adapter from "@sveltejs/adapter-static"; 
101 | // was "@sveltejs/adapter-auto"
102 | 
103 | const dev = process.env.NODE_ENV === "development";
104 | 
105 | /** @type {import(""@sveltejs/kit").Config} */
106 | const config = {
107 |     kit: {
108 |         adapter: adapter({
109 |             pages: "docs",
110 |             assets: "docs"
111 |         }),
112 |         paths: {
113 |             // change below to your repo name
114 |             base: dev ? """ : "/your-repo-name",
115 |         },
116 |         // hydrate the 
element in src/app.html 117 | target: "#svelte" 118 | } 119 | }; 120 | 121 | export default config;`}
122 |

There are a few things we changed from the default sveltekit config file. We imported adapter-static instead of adapter-auto at the top of the file. We also configured the adapter to use the docs folder as the output target for both our site’s pages as well as static assets like stylesheets, images, and so on.

123 |

And finally we also added a paths configuration option which tells SvelteKit the URL path of the SvelteKit site. By default, Github Pages will host your static site at username.github.io/your-repo-name. So we need to configure SvelteKit so that it knows its URLs should fall under /your-repo-name when hosted on Github Pages, but not when testing our site on our own computers (it’s hosted at localhost:3000/ during testing with npm run dev).

124 |

You can run npm run dev again to make sure everything is still working before going to the next step.

125 |

Add a blank .nojekyll file in static

126 |

To configure Github Pages so that it doesn’t use the Jekyll static site generator (which it does by default), we simply have to create a blank .nojekyll file in the static folder. You can do that any way you like, but here’s how you can do it from the command line:

127 |
touch static/.nojekyll && git add static/.nojekyll
128 |

129 |

130 |

Deploy your site on Github Pages

131 |

So we have a demo site that runs fine on our computer with npm run dev but we want to deploy it on Github pages. To do that, we use SvelteKit’s build command.

132 |
npm run build
133 |

This command will create a docs folder if it doesn’t exist and put all the HTML, CSS, and JavaScript files there. It will also delete and recreate the docs folder every time so make sure not to put any other files in there or they might be deleted.

134 |

Don’t forget to add the docs folder to your repository:

135 |
git add docs
136 |

Now if you commit your changes...

137 |
git commit -m "add /docs"
138 |

...and push them to Github...

139 |
git push origin main
140 |

Github will automatically deploy your site! In the Github Pages settings you’ll see this right after you push:

141 | 142 |
143 | Screenshot of Github Pages' settings with site ready to be published 144 |
145 |

If you wait a little bit, eventually that page will look like this:

146 | 147 |
Screenshot of Github Pages' successful publish
148 | 149 |

Click the link to see your new site running with Github Pages!

150 |

That is generally the process every time you make a change:

151 |
    152 |
  1. npm run build
  2. 153 |
  3. git add docs && git commit
  4. 154 |
  5. git push origin main
  6. 155 |
156 |

Custom Domains for Github Pages

157 |

There is one last thing you need to do only if you want to host your SvelteKit site on a custom domain through Github Pages.

158 |

First, read about how to set up custom domains on the Github Pages guide here.

159 |

The main thing you’ll need in your SvelteKit repository is a CNAME file containing the URL of the site you want:

160 |
yourcustomdomain.com
161 |

Put this CNAME file in the static directory of your SvelteKit project and you’re good to go!

162 | 163 |

Addendum: Getting HTTP, HTTPS, and WWW working with custom domains on Github

164 | 165 |

This isn’t a SvelteKit-specific thing but it’s a little confusing how to properly configure DNS for Github Pages so that all the following custom domain URLs will work:

166 | 167 |
example.com
168 | www.example.com
169 | http://example.com
170 | http://www.example.com
171 | https://example.com
172 | https://www.example.com
173 |

To make all those work, it’s pretty simple. Just add “A Records” pointing toward Github’s IP addresses, as well as a “CNAME Record” for www pointed at username.github.io:

174 |
Screenshot of DNS settings for Github custom domain
175 |

You should also choose “Enforce HTTPS” from the Github Pages settings page. With this setup, every URL from the list above will automatically go to https://example.com.

176 | 177 | -------------------------------------------------------------------------------- /src/routes/index.svelte: -------------------------------------------------------------------------------- 1 | 4 | 5 | Launch Faster with the Full-stack SaaS Template for SvelteKit 6 | 7 | 8 | 15 | 16 | 17 |
18 |
19 |

Launch faster with the Svelte SaaS Starter Kit

20 | 21 |

A full-stack SaaS template built on SvelteKit that starts your project with all the features that are the same in every app.

22 | 23 |

Focus on the unique parts of your app, save weeks of tedious coding, and launch faster. 🚀

24 |
25 | 26 | 27 |
28 | 29 |
30 | 31 |
32 |
33 |
34 |

Svelte SaaS 🚀

35 |
    36 |
  • User signup and auth
  • 37 |
  • Admin dashboards
  • 38 |
  • Stripe subscriptions
  • 39 |
  • Database models, forms, REST APIs, and more
  • 40 |
41 |
42 | 43 |
44 |

SvelteKit 🔥

45 |
    46 |
  • Optimized full-stack apps with no configuration
  • 47 |
  • SEO and performance from server-side rendering
  • 48 |
  • Zippy UX from client-side hydration & routing
  • 49 |
  • Stay in flow with instant hot-module reloading
  • 50 |
51 |
52 | 53 | 54 |
55 |

Svelte ❤️

56 | 61 |
62 |
63 | 64 |
65 |

Guides

66 |

How to Deploy SvelteKit Apps to Github Pages

67 |

Reflections on Migrating my SaaS to Sveltekit

68 |
69 | 70 | 71 | 173 | -------------------------------------------------------------------------------- /static/.nojekyll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Glench/sveltekit-saas/cd401a996d8199c51962ed3347818d8d51a846f7/static/.nojekyll -------------------------------------------------------------------------------- /static/articles/migrate-saas-sveltekit/lines_of_code_after.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Glench/sveltekit-saas/cd401a996d8199c51962ed3347818d8d51a846f7/static/articles/migrate-saas-sveltekit/lines_of_code_after.png -------------------------------------------------------------------------------- /static/articles/migrate-saas-sveltekit/lines_of_code_before.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Glench/sveltekit-saas/cd401a996d8199c51962ed3347818d8d51a846f7/static/articles/migrate-saas-sveltekit/lines_of_code_before.png -------------------------------------------------------------------------------- /static/articles/migrate-saas-sveltekit/performance.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Glench/sveltekit-saas/cd401a996d8199c51962ed3347818d8d51a846f7/static/articles/migrate-saas-sveltekit/performance.png -------------------------------------------------------------------------------- /static/articles/sveltekit-github-pages-guide/github_new_repo_settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Glench/sveltekit-saas/cd401a996d8199c51962ed3347818d8d51a846f7/static/articles/sveltekit-github-pages-guide/github_new_repo_settings.png -------------------------------------------------------------------------------- /static/articles/sveltekit-github-pages-guide/github_pages_branch_settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Glench/sveltekit-saas/cd401a996d8199c51962ed3347818d8d51a846f7/static/articles/sveltekit-github-pages-guide/github_pages_branch_settings.png -------------------------------------------------------------------------------- /static/articles/sveltekit-github-pages-guide/github_pages_dns_settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Glench/sveltekit-saas/cd401a996d8199c51962ed3347818d8d51a846f7/static/articles/sveltekit-github-pages-guide/github_pages_dns_settings.png -------------------------------------------------------------------------------- /static/articles/sveltekit-github-pages-guide/github_pages_settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Glench/sveltekit-saas/cd401a996d8199c51962ed3347818d8d51a846f7/static/articles/sveltekit-github-pages-guide/github_pages_settings.png -------------------------------------------------------------------------------- /static/articles/sveltekit-github-pages-guide/github_pages_site_published.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Glench/sveltekit-saas/cd401a996d8199c51962ed3347818d8d51a846f7/static/articles/sveltekit-github-pages-guide/github_pages_site_published.png -------------------------------------------------------------------------------- /static/articles/sveltekit-github-pages-guide/github_pages_site_ready.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Glench/sveltekit-saas/cd401a996d8199c51962ed3347818d8d51a846f7/static/articles/sveltekit-github-pages-guide/github_pages_site_ready.png -------------------------------------------------------------------------------- /static/articles/sveltekit-github-pages-guide/sveltekit_skeleton_page.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Glench/sveltekit-saas/cd401a996d8199c51962ed3347818d8d51a846f7/static/articles/sveltekit-github-pages-guide/sveltekit_skeleton_page.png -------------------------------------------------------------------------------- /static/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Glench/sveltekit-saas/cd401a996d8199c51962ed3347818d8d51a846f7/static/favicon.png -------------------------------------------------------------------------------- /static/mvp.css: -------------------------------------------------------------------------------- 1 | /* MVP.css v1.8 - https://github.com/andybrewer/mvp */ 2 | 3 | :root { 4 | --active-brightness: 0.85; 5 | --border-radius: 5px; 6 | --box-shadow: 2px 2px 10px; 7 | --color: #118bee; 8 | --color-accent: #118bee15; 9 | --color-bg: #fff; 10 | --color-bg-secondary: #e9e9e9; 11 | --color-link: #118bee; 12 | --color-secondary: #920de9; 13 | --color-secondary-accent: #920de90b; 14 | --color-shadow: #f4f4f4; 15 | --color-table: #118bee; 16 | --color-text: #000; 17 | --color-text-secondary: #999; 18 | --font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif; 19 | --hover-brightness: 1.2; 20 | --justify-important: center; 21 | --justify-normal: left; 22 | --line-height: 1.5; 23 | --width-card: 285px; 24 | --width-card-medium: 460px; 25 | --width-card-wide: 800px; 26 | --width-content: 1080px; 27 | } 28 | 29 | @media (prefers-color-scheme: dark) { 30 | :root { 31 | --color: #0097fc; 32 | --color-accent: #0097fc4f; 33 | --color-bg: #333; 34 | --color-bg-secondary: #555; 35 | --color-link: #0097fc; 36 | --color-secondary: #e20de9; 37 | --color-secondary-accent: #e20de94f; 38 | --color-shadow: #bbbbbb20; 39 | --color-table: #0097fc; 40 | --color-text: #f7f7f7; 41 | --color-text-secondary: #aaa; 42 | } 43 | } 44 | 45 | /* Layout */ 46 | article aside { 47 | background: var(--color-secondary-accent); 48 | border-left: 4px solid var(--color-secondary); 49 | padding: 0.01rem 0.8rem; 50 | } 51 | 52 | body { 53 | background: var(--color-bg); 54 | color: var(--color-text); 55 | font-family: var(--font-family); 56 | line-height: var(--line-height); 57 | margin: 0; 58 | overflow-x: hidden; 59 | padding: 0; 60 | } 61 | 62 | footer, 63 | header, 64 | main { 65 | margin: 0 auto; 66 | max-width: var(--width-content); 67 | padding: 3rem 1rem; 68 | } 69 | 70 | hr { 71 | background-color: var(--color-bg-secondary); 72 | border: none; 73 | height: 1px; 74 | margin: 4rem 0; 75 | width: 100%; 76 | } 77 | 78 | section { 79 | display: flex; 80 | flex-wrap: wrap; 81 | justify-content: var(--justify-important); 82 | } 83 | 84 | section img, 85 | article img { 86 | max-width: 100%; 87 | } 88 | 89 | section pre { 90 | overflow: auto; 91 | } 92 | 93 | section aside { 94 | border: 1px solid var(--color-bg-secondary); 95 | border-radius: var(--border-radius); 96 | box-shadow: var(--box-shadow) var(--color-shadow); 97 | margin: 1rem; 98 | padding: 1.25rem; 99 | width: var(--width-card); 100 | } 101 | 102 | section aside:hover { 103 | box-shadow: var(--box-shadow) var(--color-bg-secondary); 104 | } 105 | 106 | [hidden] { 107 | display: none; 108 | } 109 | 110 | /* Headers */ 111 | article header, 112 | div header, 113 | main header { 114 | padding-top: 0; 115 | } 116 | 117 | header { 118 | text-align: var(--justify-important); 119 | } 120 | 121 | header a b, 122 | header a em, 123 | header a i, 124 | header a strong { 125 | margin-left: 0.5rem; 126 | margin-right: 0.5rem; 127 | } 128 | 129 | header nav img { 130 | margin: 1rem 0; 131 | } 132 | 133 | section header { 134 | padding-top: 0; 135 | width: 100%; 136 | } 137 | 138 | /* Nav */ 139 | nav { 140 | align-items: center; 141 | display: flex; 142 | font-weight: bold; 143 | justify-content: space-between; 144 | margin-bottom: 7rem; 145 | } 146 | 147 | nav ul { 148 | list-style: none; 149 | padding: 0; 150 | } 151 | 152 | nav ul li { 153 | display: inline-block; 154 | margin: 0 0.5rem; 155 | position: relative; 156 | text-align: left; 157 | } 158 | 159 | /* Nav Dropdown */ 160 | nav ul li:hover ul { 161 | display: block; 162 | } 163 | 164 | nav ul li ul { 165 | background: var(--color-bg); 166 | border: 1px solid var(--color-bg-secondary); 167 | border-radius: var(--border-radius); 168 | box-shadow: var(--box-shadow) var(--color-shadow); 169 | display: none; 170 | height: auto; 171 | left: -2px; 172 | padding: .5rem 1rem; 173 | position: absolute; 174 | top: 1.7rem; 175 | white-space: nowrap; 176 | width: auto; 177 | z-index: 1; 178 | } 179 | 180 | nav ul li ul::before { 181 | /* fill gap above to make mousing over them easier */ 182 | content: ""; 183 | position: absolute; 184 | left: 0; 185 | right: 0; 186 | top: -0.5rem; 187 | height: 0.5rem; 188 | } 189 | 190 | nav ul li ul li, 191 | nav ul li ul li a { 192 | display: block; 193 | } 194 | 195 | /* Typography */ 196 | code, 197 | samp { 198 | background-color: var(--color-accent); 199 | border-radius: var(--border-radius); 200 | color: var(--color-text); 201 | display: inline-block; 202 | margin: 0 0.1rem; 203 | padding: 0 0.5rem; 204 | } 205 | 206 | details { 207 | margin: 1.3rem 0; 208 | } 209 | 210 | details summary { 211 | font-weight: bold; 212 | cursor: pointer; 213 | } 214 | 215 | h1, 216 | h2, 217 | h3, 218 | h4, 219 | h5, 220 | h6 { 221 | line-height: var(--line-height); 222 | } 223 | 224 | mark { 225 | padding: 0.1rem; 226 | } 227 | 228 | ol li, 229 | ul li { 230 | padding: 0.2rem 0; 231 | } 232 | 233 | p { 234 | margin: 0.75rem 0; 235 | padding: 0; 236 | width: 100%; 237 | } 238 | 239 | pre { 240 | margin: 1rem 0; 241 | max-width: var(--width-card-wide); 242 | padding: 1rem 0; 243 | } 244 | 245 | pre code, 246 | pre samp { 247 | display: block; 248 | max-width: var(--width-card-wide); 249 | padding: 0.5rem 2rem; 250 | white-space: pre-wrap; 251 | } 252 | 253 | small { 254 | color: var(--color-text-secondary); 255 | } 256 | 257 | sup { 258 | background-color: var(--color-secondary); 259 | border-radius: var(--border-radius); 260 | color: var(--color-bg); 261 | font-size: xx-small; 262 | font-weight: bold; 263 | margin: 0.2rem; 264 | padding: 0.2rem 0.3rem; 265 | position: relative; 266 | top: -2px; 267 | } 268 | 269 | /* Links */ 270 | a { 271 | color: var(--color-link); 272 | display: inline-block; 273 | font-weight: bold; 274 | text-decoration: none; 275 | } 276 | 277 | a:active { 278 | filter: brightness(var(--active-brightness)); 279 | text-decoration: underline; 280 | } 281 | 282 | a:hover { 283 | filter: brightness(var(--hover-brightness)); 284 | text-decoration: underline; 285 | } 286 | 287 | a b, 288 | a em, 289 | a i, 290 | a strong, 291 | button { 292 | border-radius: var(--border-radius); 293 | display: inline-block; 294 | font-size: medium; 295 | font-weight: bold; 296 | line-height: var(--line-height); 297 | margin: 0.5rem 0; 298 | padding: 1rem 2rem; 299 | } 300 | 301 | button { 302 | font-family: var(--font-family); 303 | } 304 | 305 | button:active { 306 | filter: brightness(var(--active-brightness)); 307 | } 308 | 309 | button:hover { 310 | cursor: pointer; 311 | filter: brightness(var(--hover-brightness)); 312 | } 313 | 314 | a b, 315 | a strong, 316 | button { 317 | background-color: var(--color-link); 318 | border: 2px solid var(--color-link); 319 | color: var(--color-bg); 320 | } 321 | 322 | a em, 323 | a i { 324 | border: 2px solid var(--color-link); 325 | border-radius: var(--border-radius); 326 | color: var(--color-link); 327 | display: inline-block; 328 | padding: 1rem 2rem; 329 | } 330 | 331 | article aside a { 332 | color: var(--color-secondary); 333 | } 334 | 335 | /* Images */ 336 | figure { 337 | margin: 0; 338 | padding: 0; 339 | } 340 | 341 | figure img { 342 | max-width: 100%; 343 | } 344 | 345 | figure figcaption { 346 | color: var(--color-text-secondary); 347 | } 348 | 349 | /* Forms */ 350 | 351 | button:disabled, 352 | input:disabled { 353 | background: var(--color-bg-secondary); 354 | border-color: var(--color-bg-secondary); 355 | color: var(--color-text-secondary); 356 | cursor: not-allowed; 357 | } 358 | 359 | button[disabled]:hover { 360 | filter: none; 361 | } 362 | 363 | form { 364 | border: 1px solid var(--color-bg-secondary); 365 | border-radius: var(--border-radius); 366 | box-shadow: var(--box-shadow) var(--color-shadow); 367 | display: block; 368 | max-width: var(--width-card-wide); 369 | min-width: var(--width-card); 370 | padding: 1.5rem; 371 | text-align: var(--justify-normal); 372 | } 373 | 374 | form header { 375 | margin: 1.5rem 0; 376 | padding: 1.5rem 0; 377 | } 378 | 379 | input, 380 | label, 381 | select, 382 | textarea { 383 | display: block; 384 | font-size: inherit; 385 | max-width: var(--width-card-wide); 386 | } 387 | 388 | input[type="checkbox"], 389 | input[type="radio"] { 390 | display: inline-block; 391 | } 392 | 393 | input[type="checkbox"]+label, 394 | input[type="radio"]+label { 395 | display: inline-block; 396 | font-weight: normal; 397 | position: relative; 398 | top: 1px; 399 | } 400 | 401 | input, 402 | select, 403 | textarea { 404 | border: 1px solid var(--color-bg-secondary); 405 | border-radius: var(--border-radius); 406 | margin-bottom: 1rem; 407 | padding: 0.4rem 0.8rem; 408 | } 409 | 410 | input[readonly], 411 | textarea[readonly] { 412 | background-color: var(--color-bg-secondary); 413 | } 414 | 415 | label { 416 | font-weight: bold; 417 | margin-bottom: 0.2rem; 418 | } 419 | 420 | /* Tables */ 421 | table { 422 | border: 1px solid var(--color-bg-secondary); 423 | border-radius: var(--border-radius); 424 | border-spacing: 0; 425 | display: inline-block; 426 | max-width: 100%; 427 | overflow-x: auto; 428 | padding: 0; 429 | white-space: nowrap; 430 | } 431 | 432 | table td, 433 | table th, 434 | table tr { 435 | padding: 0.4rem 0.8rem; 436 | text-align: var(--justify-important); 437 | } 438 | 439 | table thead { 440 | background-color: var(--color-table); 441 | border-collapse: collapse; 442 | border-radius: var(--border-radius); 443 | color: var(--color-bg); 444 | margin: 0; 445 | padding: 0; 446 | } 447 | 448 | table thead th:first-child { 449 | border-top-left-radius: var(--border-radius); 450 | } 451 | 452 | table thead th:last-child { 453 | border-top-right-radius: var(--border-radius); 454 | } 455 | 456 | table thead th:first-child, 457 | table tr td:first-child { 458 | text-align: var(--justify-normal); 459 | } 460 | 461 | table tr:nth-child(even) { 462 | background-color: var(--color-accent); 463 | } 464 | 465 | /* Quotes */ 466 | blockquote { 467 | display: block; 468 | font-size: x-large; 469 | line-height: var(--line-height); 470 | margin: 1rem auto; 471 | max-width: var(--width-card-medium); 472 | padding: 1.5rem 1rem; 473 | text-align: var(--justify-important); 474 | } 475 | 476 | blockquote footer { 477 | color: var(--color-text-secondary); 478 | display: block; 479 | font-size: small; 480 | line-height: var(--line-height); 481 | padding: 1.5rem 0; 482 | } 483 | -------------------------------------------------------------------------------- /svelte.config.js: -------------------------------------------------------------------------------- 1 | import adapter from '@sveltejs/adapter-static'; 2 | import preprocess from 'svelte-preprocess'; 3 | 4 | const dev = process.env.NODE_ENV === 'development'; 5 | 6 | /** @type {import('@sveltejs/kit').Config} */ 7 | const config = { 8 | // Consult https://github.com/sveltejs/svelte-preprocess 9 | // for more information about preprocessors 10 | preprocess: preprocess(), 11 | 12 | kit: { 13 | // paths: { 14 | // base: dev ? '' : '/svelte-saas', 15 | // }, 16 | appDir: 'app_', 17 | adapter: adapter({ 18 | pages: 'docs', 19 | assets: 'docs', 20 | }), 21 | 22 | // hydrate the
element in src/app.html 23 | target: '#svelte' 24 | } 25 | }; 26 | 27 | export default config; 28 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "moduleResolution": "node", 4 | "module": "es2020", 5 | "lib": ["es2020", "DOM"], 6 | "target": "es2020", 7 | /** 8 | svelte-preprocess cannot figure out whether you have a value or a type, so tell TypeScript 9 | to enforce using \`import type\` instead of \`import\` for Types. 10 | */ 11 | "importsNotUsedAsValues": "error", 12 | "isolatedModules": true, 13 | "resolveJsonModule": true, 14 | /** 15 | To have warnings/errors of the Svelte compiler at the correct position, 16 | enable source maps by default. 17 | */ 18 | "sourceMap": true, 19 | "esModuleInterop": true, 20 | "skipLibCheck": true, 21 | "forceConsistentCasingInFileNames": true, 22 | "baseUrl": ".", 23 | "allowJs": true, 24 | "checkJs": true, 25 | "paths": { 26 | "$lib": ["src/lib"], 27 | "$lib/*": ["src/lib/*"] 28 | } 29 | }, 30 | "include": ["src/**/*.d.ts", "src/**/*.js", "src/**/*.ts", "src/**/*.svelte"] 31 | } 32 | --------------------------------------------------------------------------------