new svelte framework demo

This commit is contained in:
Rolands 2022-11-01 18:34:30 +02:00
parent a941eba154
commit 5683e7fe50
24 changed files with 3940 additions and 14 deletions

View file

@ -32,7 +32,7 @@ export class WSClient {
this.#ws = new WebSocket(url)
if (keep_alive && reconnect_tries)
this.#ws.addEventListener("close", () => {
if (this.#verbose) info(this.name, `WebSocket closed. Retrying...`);
if (this.#verbose) info(`WebSocket closed. Retrying...`, this.name);
this.#connect(url, keep_alive, verbose, reconnect_tries - 1)
}); // <- rise from your grave!
}
@ -42,7 +42,7 @@ export class WSClient {
if (this.#verbose) info(kind, data)
switch(kind){
case 'CON': this.ses_id = data; this.#is_ready = true; if (this.#verbose) info(this.name, `WebSocket connected.`); break;
case 'CON': this.ses_id = data; this.#is_ready = true; if (this.#verbose) info(`WebSocket connected.`, this.name); break;
case 'UPD':
if (data.sql in this.#queries)
this.#queries[data.sql].forEach(f => f(data.result));

View file

@ -26,6 +26,6 @@ document.getElementById('insert').addEventListener('click', async (e) => {
//queries with dynamic data - via params (optional):
//subscribe to the changes of this query - whenever the table is altered on the backend. And run the callback with the new received data
//this will also run the sql query to get the initial value, then indefinitely receive updates and rerun this callback.
ws.subscribe({ sql: 'SELECT COUNT(*) FROM users WHERE name = :name;', params: { name: 'Bob' } }, (res) => {
document.getElementById('3').innerText = res[0]['COUNT(*)'] //res is whatever object your particular DB interface lib returns from a raw query
ws.subscribe({ sql: 'SELECT COUNT(*) AS RESULT FROM users WHERE name = :name;', params: { name: 'Bob' } }, (res) => {
document.getElementById('3').innerText = res[0].RESULT //res is whatever object your particular DB interface lib returns from a raw query
})

View file

@ -1,5 +1,5 @@
{
"name": "basic",
"name": "Socio demo - Basic",
"version": "1.0.0",
"description": "",
"main": "server.js",

View file

@ -1,19 +1,13 @@
# This is a super simple locally runnable demonstration of this lib and how to use it. Only depends on having Node installed.
# This is a super simple locally runnable demonstration of the Socio lib and how to use it. Only depends on having Node installed.
#### I saw the angel in the marble and carved until I set him free. /Michelangelo/
* Download or clone this repo
* ```cd demo```
* ```cd demos/basic```
* ```npm i```
* ```npm run demo```
You might need to link the core lib to the demo manually, since it is not yet published (or setup) to npm:
```bash
cd core
npm link
cd ../demos/basic
npm link ../../core
```
_if it prints an import error, that might be bcs locally i test by npm linking the local package and have commented out the import of the released npm package. You can find the import and switch the commented lines._
Should start the express webserver and print out its url, that you can visit on your fav browser to begin the interactive demo.

198
demos/framework/.gitignore vendored Normal file
View file

@ -0,0 +1,198 @@
#mine
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
# Created by https://www.toptal.com/developers/gitignore/api/node,visualstudiocode,svelte
# Edit at https://www.toptal.com/developers/gitignore?templates=node,visualstudiocode,svelte
### Node ###
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
.pnpm-debug.log*
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
*.lcov
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# Snowpack dependency directory (https://snowpack.dev/)
web_modules/
# TypeScript cache
*.tsbuildinfo
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional stylelint cache
.stylelintcache
# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variable files
.env
.env.development.local
.env.test.local
.env.production.local
.env.local
# parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache
# Next.js build output
.next
out
# Nuxt.js build / generate output
.nuxt
dist
# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and not Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public
# vuepress build output
.vuepress/dist
# vuepress v2.x temp and cache directory
.temp
# Docusaurus cache and generated files
.docusaurus
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# TernJS port file
.tern-port
# Stores VSCode versions used for testing VSCode extensions
.vscode-test
# yarn v2
.yarn/cache
.yarn/unplugged
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*
### Node Patch ###
# Serverless Webpack directories
.webpack/
# Optional stylelint cache
# SvelteKit build / generate output
.svelte-kit
### Svelte ###
# gitignore template for the SvelteKit, frontend web component framework
# website: https://kit.svelte.dev/
.svelte-kit/
package
### VisualStudioCode ###
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
!.vscode/*.code-snippets
# Local History for Visual Studio Code
.history/
# Built Visual Studio Code Extensions
*.vsix
### VisualStudioCode Patch ###
# Ignore all local history of files
.history
.ionide
# End of https://www.toptal.com/developers/gitignore/api/node,visualstudiocode,svelte

View file

@ -0,0 +1,3 @@
{
"recommendations": ["svelte.svelte-vscode"]
}

21
demos/framework/README.md Normal file
View file

@ -0,0 +1,21 @@
# This is a simple locally runnable demonstration of the Socio lib and how to use it within a frontend framework like Svelte. Only depends on having Node installed.
* Download or clone this repo
* ```cd demos/framework```
* ```npm i```
In 2 paralel terminals:
* ```npm run dev``` for the Svelte-Vite dev server
* ```npm run soc``` for the WebSocketServer
_if it prints an import error, that might be bcs locally i test by npm linking the local package and have commented out the import of the released npm package. You can find the import and switch the commented lines._
* Visi the Svelte dev server URL on one or multiple tabs or browser instances.
* Then press the big INSERT button, which will insert a new row into the DB on that table.
* Then you should see the subscribed queries update their values to whatever the queries returned.
As you will notice, all instances of the browsers and their tabs update their values. This is because the API isnt built with the REST method, but rather with WebSockets, which means the server can push its updates to the clients, if they have registered to receive them. Instead of the traditional way of pooling resquests.
This is powerful because you nolonger need to write a REST API middle layer between front and back end and manually sync states and data. This is all done automatically for you. As well as no need to write DB query interfacing middle layers, since your SQL queries can just sit in one place - the front end.
## Next check out the ``App.svelte`` file to see how the magic is done on the frontend - its super simple ;)

View file

@ -0,0 +1,20 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<!-- <link rel="icon" type="image/svg+xml" href="/vite.svg" /> -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link
href="https://fonts.googleapis.com/css2?family=Josefin+Sans:wght@200;400;700&family=Trispace:wght@100;200&display=swap"
rel="stylesheet">
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Socio demo - framework - Svelte</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.js"></script>
</body>
</html>

View file

@ -0,0 +1,33 @@
{
"compilerOptions": {
"moduleResolution": "Node",
"target": "ESNext",
"module": "ESNext",
/**
* svelte-preprocess cannot figure out whether you have
* a value or a type, so tell TypeScript to enforce using
* `import type` instead of `import` for Types.
*/
"importsNotUsedAsValues": "error",
"isolatedModules": true,
"resolveJsonModule": true,
/**
* To have warnings / errors of the Svelte compiler at the
* correct position, enable source maps by default.
*/
"sourceMap": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
/**
* Typecheck JS in `.svelte` and `.js` files by default.
* Disable this if you'd like to use dynamic types.
*/
"checkJs": true
},
/**
* Use global.d.ts instead of compilerOptions.types
* to avoid limiting type declarations.
*/
"include": ["src/**/*.d.ts", "src/**/*.js", "src/**/*.svelte"]
}

3307
demos/framework/package-lock.json generated Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,23 @@
{
"name": "Socio demo - framework",
"private": true,
"version": "0.0.1",
"type": "module",
"scripts": {
"soc": "node server.js",
"dev": "vite --port 5000",
"build": "vite build",
"preview": "vite preview"
},
"devDependencies": {
"@sveltejs/vite-plugin-svelte": "^1.1.0",
"svelte": "^3.52.0",
"vite": "^3.2.0"
},
"dependencies": {
"@rolands/log": "^1.1.1",
"sequelize": "^6.25.3",
"socio": "^0.0.1",
"sqlite3": "^5.1.2"
}
}

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

23
demos/framework/server.js Normal file
View file

@ -0,0 +1,23 @@
// import {SessionManager} from 'socio/core.js'
import { SessionManager } from '../../core/core.js'
import { Sequelize } from 'sequelize';
import { log, info, setPrefix, setShowTime } from '@rolands/log'; setPrefix('SERVER'); setShowTime(false);
//constants
const ws_port = 3000 //can be set up that the websockets run on the same port as the http server
//init local RAM DB with 1 table - "users" and 2 rows.
const sequelize = new Sequelize('sqlite::memory:');
await sequelize.query('CREATE TABLE Users(name varchar(50), num INTEGER NOT NULL DEFAULT 0);')
await sequelize.query('INSERT INTO Users VALUES("Jane", 42);')
await sequelize.query('INSERT INTO Users VALUES("John", 69);')
//set up the WebSocket manager and give it the DB querying function that comes from whatever your DB interface lib provides.
//it needs the raw sql string, which can contain formatting parameters - insert dynamic data into the string.
//Either you in a wrapper function or your DB interface lib should do the sql validation and sanitization, as this lib does not!
const QueryWrap = async (sql = '', params = {}) => (await sequelize.query(sql, { logging: false, raw: true, replacements: params }))[0]
const manager = new SessionManager({ port: ws_port }, QueryWrap, { verbose: true })
info(`Created SessionManager on port`, ws_port)
//init
// const sec = Secure({})

View file

@ -0,0 +1,130 @@
<script>
//imports:
// import { WSClient } from 'socio/core-client.js'
import { WSClient } from '../../../core/core-client.js'
import {onMount} from 'svelte'
import {slide} from 'svelte/transition'
//svelte components:
import Nav from "./nav.svelte";
import Code from './code.svelte'
import Spinner from './spinner.svelte'
import Button from './button.svelte'
let ws = null
let clienID = false
const static_queries = [{text:'once:', sql:'SELECT 42+69 AS RESULT;'}, {text:'once:', sql:'SELECT COUNT(*) AS RESULT FROM users;'}]
let users = [], bob_count=0;
const insert_fields = {name:'Bob', num:420}
onMount(async ()=>{
ws = new WSClient('ws://localhost:3000', {verbose:true, name:'Main'})
await ws.ready()
clienID = ws.ses_id
ws.subscribe({ sql: 'SELECT COUNT(*) AS RESULT FROM users WHERE name = :name;', params: { name: 'Bob' } }, (res) => {
bob_count = res[0].RESULT //res is whatever object your particular DB interface lib returns from a raw query
})
ws.subscribe({ sql: 'SELECT * FROM users;'}, (res) => {
users = res //res is whatever object your particular DB interface lib returns from a raw query
})
})
</script>
<Nav></Nav>
<main>
<h1>Socio framework use demonstration - Svelte</h1>
{#if clienID}
<div class="horiz">
<h2 id="ready" class="status">Ready.</h2>
<h3>client ID: {clienID}</h3>
</div>
{#each static_queries as q}
<h2 class="row horiz">
<h3>{q.text}</h3><Code on:click={() => q.sql = q.sql}>{q.sql}</Code> =
{#await ws.query(q.sql)}
<Spinner></Spinner>
{:then res}
<span class="num grad_clip">{res[0].RESULT}</span>
{/await}
</h2>
{/each}
<div class="horiz">
<Button on:click={async () => await ws.query('INSERT INTO users VALUES(:name, :num);', insert_fields)} bind:name={insert_fields.name} bind:num={insert_fields.num}></Button>
<input type="text" bind:value={insert_fields.name}>
<input type="number" bind:value={insert_fields.num}>
</div>
<h2 class="row horiz">
<h3>subscribed:</h3>
<Code >SELECT COUNT(*) AS RESULT FROM users WHERE name = :name; && :name = 'Bob'</Code>
=
<span class="num grad_clip">{bob_count}</span>
</h2>
{:else}
<h2 id="ready" class="status">Not Ready!</h2>
{/if}
</main>
<section>
<div class="users">
<div class="horiz"><h3>subscribed:</h3> <Code>SELECT * AS RESULT FROM users;</Code>=</div>
<h2 class="grad_clip">{'{'}</h2>
{#each users as u}
<div class="horiz user_row" transition:slide>
<h2>name: <span class="grad_clip">{u.name}</span></h2>
<h2>num: <span class="grad_clip">{u.num}</span></h2>
</div>
{/each}
<h2 class="grad_clip">{'}'}</h2>
</div>
<h3>Check the dev console for verbose logs and the network panel for websocket connection messages ;)</h3>
</section>
<style lang="css">
main {
height: 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: space-evenly;
}
section{
padding: 32px;
display: flex;
flex-direction: column;
align-items: center;
gap: 24px;
}
.horiz{
display: flex;
align-items: baseline;
gap: 12px;
}
.users{
display: flex;
flex-direction: column;
gap: 16px;
}
.user_row{
justify-content: center;
}
input{
background-color: #292929;
border: 1px solid #9c9c9c;
border-radius: 4px;
width: 150px;
font-size: 25px;
padding: 4px 8px;
transition: var(--trans);
}
input:hover{
background-color: #383838;
}
</style>

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="26.6" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 308"><path fill="#FF3E00" d="M239.682 40.707C211.113-.182 154.69-12.301 113.895 13.69L42.247 59.356a82.198 82.198 0 0 0-37.135 55.056a86.566 86.566 0 0 0 8.536 55.576a82.425 82.425 0 0 0-12.296 30.719a87.596 87.596 0 0 0 14.964 66.244c28.574 40.893 84.997 53.007 125.787 27.016l71.648-45.664a82.182 82.182 0 0 0 37.135-55.057a86.601 86.601 0 0 0-8.53-55.577a82.409 82.409 0 0 0 12.29-30.718a87.573 87.573 0 0 0-14.963-66.244"></path><path fill="#FFF" d="M106.889 270.841c-23.102 6.007-47.497-3.036-61.103-22.648a52.685 52.685 0 0 1-9.003-39.85a49.978 49.978 0 0 1 1.713-6.693l1.35-4.115l3.671 2.697a92.447 92.447 0 0 0 28.036 14.007l2.663.808l-.245 2.659a16.067 16.067 0 0 0 2.89 10.656a17.143 17.143 0 0 0 18.397 6.828a15.786 15.786 0 0 0 4.403-1.935l71.67-45.672a14.922 14.922 0 0 0 6.734-9.977a15.923 15.923 0 0 0-2.713-12.011a17.156 17.156 0 0 0-18.404-6.832a15.78 15.78 0 0 0-4.396 1.933l-27.35 17.434a52.298 52.298 0 0 1-14.553 6.391c-23.101 6.007-47.497-3.036-61.101-22.649a52.681 52.681 0 0 1-9.004-39.849a49.428 49.428 0 0 1 22.34-33.114l71.664-45.677a52.218 52.218 0 0 1 14.563-6.398c23.101-6.007 47.497 3.036 61.101 22.648a52.685 52.685 0 0 1 9.004 39.85a50.559 50.559 0 0 1-1.713 6.692l-1.35 4.116l-3.67-2.693a92.373 92.373 0 0 0-28.037-14.013l-2.664-.809l.246-2.658a16.099 16.099 0 0 0-2.89-10.656a17.143 17.143 0 0 0-18.398-6.828a15.786 15.786 0 0 0-4.402 1.935l-71.67 45.674a14.898 14.898 0 0 0-6.73 9.975a15.9 15.9 0 0 0 2.709 12.012a17.156 17.156 0 0 0 18.404 6.832a15.841 15.841 0 0 0 4.402-1.935l27.345-17.427a52.147 52.147 0 0 1 14.552-6.397c23.101-6.006 47.497 3.037 61.102 22.65a52.681 52.681 0 0 1 9.003 39.848a49.453 49.453 0 0 1-22.34 33.12l-71.664 45.673a52.218 52.218 0 0 1-14.563 6.398"></path></svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

View file

@ -0,0 +1,30 @@
<script>
export let name, num;
</script>
<button on:click>
INSERT INTO users VALUES("{name}", {num});
</button>
<style>
button{
font-weight: 700;
font-size: 24px;
background: var(--grad);
background-position: 50% 0px;
background-size: 150%;
color: #fff;
border: none;
outline: none;
border-radius: 12px;
padding: 12px;
filter: var(--shad);
transition: var(--trans);
}
button:hover{
cursor: pointer;
border-radius: 6px;
background-position: 0px 0px;
}
</style>

View file

@ -0,0 +1,21 @@
<span class="code" on:click title="Click to refresh!">
<slot></slot>
</span>
<style>
.code{
font-family: 'Trispace', monospace;
font-weight: 100;
font-size: 19.20px;
padding: 8px;
border-radius: 4px;
background-color: #292929;
border: 1px solid #9c9c9c;
transition: var(--trans);
filter: var(--shad);
}
.code:hover{
background-color: #383838;
cursor: pointer;
}
</style>

View file

@ -0,0 +1,61 @@
:root {
font-family: 'Josefin Sans', 'Open Sans', sans-serif;
font-size: 20px;
font-weight: 400;
color-scheme: dark;
color: white;
background-color: #16161b;
--accent1: #ee3a46;
--accent2: #6418a3;
--grad: linear-gradient(45deg, var(--accent1) 0%, var(--accent1) 40%, var(--accent2) 90%, var(--accent2) 100%);
--shad: drop-shadow(0px 5px 5px rgba(0, 0, 0, 0.192));
--trans: 150ms ease-in-out;
}
html, body, #app {
margin: 0;
height: 100%;
}
#app{padding: 32px;}
h1,h2,h3,h4,a{
margin: 0;
padding: 0;
filter: var(--shad);
}
h1 {
font-size: 48.83px;
font-weight: 700;
}
h2 {
font-size: 31.25px;
font-weight: 700;
line-height: 1.3;
}
h3 {
font-size: 20px;
font-weight: 300;
font-style: italic;
color: #5a5a5f;
}
a {
font-size: 25px;
}
.grad_clip {
background: var(--grad);
background-clip: text;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-position: 30% 0px;
background-size: 130%;
transition: var(--trans);
}
.grad_clip:hover {
background-position: 0px 0px;
}

View file

@ -0,0 +1,8 @@
import './global.css'
import App from './App.svelte'
const app = new App({
target: document.getElementById('app')
})
export default app

View file

@ -0,0 +1,16 @@
<nav>
<h2 class="grad_clip">Socio</h2>
<a href="https://github.com/Rolands-Laucis/Socio.js" style="color: var(--accent2);">github</a>
<a href="https://www.npmjs.com/package/socio" style="color: var(--accent2);">npm</a>
</nav>
<style>
nav {
position: absolute;
top: 16px;
left: 16px;
display: flex;
align-items: center;
gap: 16px;
}
</style>

View file

@ -0,0 +1,24 @@
<script>
export let style=''
</script>
<div class="spinner" {style}></div>
<style>
.spinner{
--thicc: 5px;
height: 32px;
aspect-ratio: 1/1;
border: 5px solid;
border-color: var(--accent1) transparent var(--accent2) transparent;
border-radius: 50%;
display: inline-block;
box-sizing: border-box;
animation: spin 1s ease-in-out infinite forwards;
}
@keyframes spin{
from{transform: rotate(0deg);}
to{transform: rotate(720deg);}
}
</style>

View file

@ -0,0 +1,3 @@
import { writable } from 'svelte/store'
export const count = writable(0)

2
demos/framework/src/vite-env.d.ts vendored Normal file
View file

@ -0,0 +1,2 @@
/// <reference types="svelte" />
/// <reference types="vite/client" />

View file

@ -0,0 +1,7 @@
import { defineConfig } from 'vite'
import { svelte } from '@sveltejs/vite-plugin-svelte'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [svelte()]
})