commit 5c76852bbb59b518a191012c941a1184bf0c8965 Author: Sidharth Vinod Date: Sun May 9 17:30:34 2021 +0530 Initial diff --git a/.eslintrc.cjs b/.eslintrc.cjs new file mode 100644 index 00000000..fba38619 --- /dev/null +++ b/.eslintrc.cjs @@ -0,0 +1,20 @@ +module.exports = { + root: true, + parser: '@typescript-eslint/parser', + extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended', 'prettier'], + plugins: ['svelte3', '@typescript-eslint'], + ignorePatterns: ['*.cjs'], + overrides: [{ files: ['*.svelte'], processor: 'svelte3/svelte3' }], + settings: { + 'svelte3/typescript': () => require('typescript') + }, + parserOptions: { + sourceType: 'module', + ecmaVersion: 2019 + }, + env: { + browser: true, + es2017: true, + node: true + } +}; diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..0a462318 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +.DS_Store +node_modules +/.svelte-kit +/build +/functions diff --git a/.npmrc b/.npmrc new file mode 100644 index 00000000..b6f27f13 --- /dev/null +++ b/.npmrc @@ -0,0 +1 @@ +engine-strict=true diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 00000000..d26fa1cf --- /dev/null +++ b/.prettierignore @@ -0,0 +1,4 @@ +.svelte-kit/** +static/** +build/** +node_modules/** diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 00000000..ff2677ef --- /dev/null +++ b/.prettierrc @@ -0,0 +1,6 @@ +{ + "useTabs": true, + "singleQuote": true, + "trailingComma": "none", + "printWidth": 100 +} diff --git a/README.md b/README.md new file mode 100644 index 00000000..82510ca0 --- /dev/null +++ b/README.md @@ -0,0 +1,38 @@ +# create-svelte + +Everything you need to build a Svelte project, powered by [`create-svelte`](https://github.com/sveltejs/kit/tree/master/packages/create-svelte); + +## Creating a project + +If you're seeing this, you've probably already done this step. Congrats! + +```bash +# create a new project in the current directory +npm init svelte@next + +# create a new project in my-app +npm init svelte@next my-app +``` + +> Note: the `@next` is temporary + +## Developing + +Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server: + +```bash +npm run dev + +# or start the server and open the app in a new browser tab +npm run dev -- --open +``` + +## Building + +Before creating a production version of your app, install an [adapter](https://kit.svelte.dev/docs#adapters) for your target environment. Then: + +```bash +npm run build +``` + +> You can preview the built app with `npm run preview`, regardless of whether you installed an adapter. This should _not_ be used to serve your app in production. diff --git a/package.json b/package.json new file mode 100644 index 00000000..bd6f5344 --- /dev/null +++ b/package.json @@ -0,0 +1,31 @@ +{ + "name": "~TODO~", + "version": "0.0.1", + "scripts": { + "dev": "svelte-kit dev", + "build": "svelte-kit build", + "preview": "svelte-kit preview", + "lint": "prettier --check --plugin-search-dir=. . && eslint --ignore-path .gitignore .", + "format": "prettier --write --plugin-search-dir=. ." + }, + "devDependencies": { + "@sveltejs/kit": "next", + "@typescript-eslint/eslint-plugin": "^4.19.0", + "@typescript-eslint/parser": "^4.19.0", + "eslint": "^7.22.0", + "eslint-config-prettier": "^8.1.0", + "eslint-plugin-svelte3": "^3.2.0", + "prettier": "~2.2.1", + "prettier-plugin-svelte": "^2.2.0", + "svelte": "^3.34.0", + "svelte-preprocess": "^4.0.0", + "tslib": "^2.0.0", + "typescript": "^4.0.0" + }, + "type": "module", + "dependencies": { + "@fontsource/fira-mono": "^4.2.2", + "@lukeed/uuid": "^2.0.0", + "cookie": "^0.4.1" + } +} \ No newline at end of file diff --git a/src/app.css b/src/app.css new file mode 100644 index 00000000..426f60f8 --- /dev/null +++ b/src/app.css @@ -0,0 +1,108 @@ +@import '@fontsource/fira-mono'; + +:root { + font-family: Arial, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, + Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; + --font-mono: 'Fira Mono', monospace; + --pure-white: #ffffff; + --primary-color: #b9c6d2; + --secondary-color: #d0dde9; + --tertiary-color: #edf0f8; + --accent-color: #ff3e00; + --heading-color: rgba(0, 0, 0, 0.7); + --text-color: #444444; + --background-without-opacity: rgba(255, 255, 255, 0.7); + --column-width: 42rem; + --column-margin-top: 4rem; +} + +body { + min-height: 100vh; + margin: 0; + background-color: var(--primary-color); + background: linear-gradient( + 180deg, + var(--primary-color) 0%, + var(--secondary-color) 10.45%, + var(--tertiary-color) 41.35% + ); +} + +body::before { + content: ''; + width: 80vw; + height: 100vh; + position: absolute; + top: 0; + left: 10vw; + z-index: -1; + background: radial-gradient( + 50% 50% at 50% 50%, + var(--pure-white) 0%, + rgba(255, 255, 255, 0) 100% + ); + opacity: 0.05; +} + +#svelte { + min-height: 100vh; + display: flex; + flex-direction: column; +} + +h1, +h2, +p { + font-weight: 400; + color: var(--heading-color); +} + +p { + line-height: 1.5; +} + +a { + color: var(--accent-color); + text-decoration: none; +} + +a:hover { + text-decoration: underline; +} + +h1 { + font-size: 2rem; + margin-bottom: 0 0 1em 0; + text-align: center; +} + +h2 { + font-size: 1rem; +} + +pre { + font-size: 16px; + font-family: var(--font-mono); + background-color: rgba(255, 255, 255, 0.45); + border-radius: 3px; + box-shadow: 2px 2px 6px rgb(255 255 255 / 25%); + padding: 0.5em; + overflow-x: auto; + color: var(--text-color); +} + +input, +button { + font-size: inherit; + font-family: inherit; +} + +button:focus:not(:focus-visible) { + outline: none; +} + +@media (min-width: 720px) { + h1 { + font-size: 2.4rem; + } +} diff --git a/src/app.html b/src/app.html new file mode 100644 index 00000000..83011ca6 --- /dev/null +++ b/src/app.html @@ -0,0 +1,13 @@ + + + + + + + + %svelte.head% + + +
%svelte.body%
+ + diff --git a/src/global.d.ts b/src/global.d.ts new file mode 100644 index 00000000..63908c66 --- /dev/null +++ b/src/global.d.ts @@ -0,0 +1 @@ +/// diff --git a/src/hooks.ts b/src/hooks.ts new file mode 100644 index 00000000..6fc783eb --- /dev/null +++ b/src/hooks.ts @@ -0,0 +1,23 @@ +import cookie from 'cookie'; +import { v4 as uuid } from '@lukeed/uuid'; +import type { Handle } from '@sveltejs/kit'; + +export const handle: Handle = async ({ request, render }) => { + const cookies = cookie.parse(request.headers.cookie || ''); + request.locals.userid = cookies.userid || uuid(); + + // TODO https://github.com/sveltejs/kit/issues/1046 + if (request.query.has('_method')) { + request.method = request.query.get('_method').toUpperCase(); + } + + const response = await render(request); + + if (!cookies.userid) { + // if this is the first time the user has visited this app, + // set a cookie so that we recognise them when they return + response.headers['set-cookie'] = `userid=${request.locals.userid}; Path=/; HttpOnly`; + } + + return response; +}; diff --git a/src/lib/Counter/index.svelte b/src/lib/Counter/index.svelte new file mode 100644 index 00000000..00997318 --- /dev/null +++ b/src/lib/Counter/index.svelte @@ -0,0 +1,98 @@ + + +
+ + +
+
+ + {Math.floor($displayed_count)} +
+
+ + +
+ + diff --git a/src/lib/Header/index.svelte b/src/lib/Header/index.svelte new file mode 100644 index 00000000..bab8050a --- /dev/null +++ b/src/lib/Header/index.svelte @@ -0,0 +1,120 @@ + + +
+
+ + SvelteKit + +
+ + + +
+ +
+
+ + diff --git a/src/lib/Header/svelte-logo.svg b/src/lib/Header/svelte-logo.svg new file mode 100644 index 00000000..49492a83 --- /dev/null +++ b/src/lib/Header/svelte-logo.svg @@ -0,0 +1 @@ +svelte-logo \ No newline at end of file diff --git a/src/lib/form.ts b/src/lib/form.ts new file mode 100644 index 00000000..c52e9230 --- /dev/null +++ b/src/lib/form.ts @@ -0,0 +1,60 @@ +// this action (https://svelte.dev/tutorial/actions) allows us to +// progressively enhance a
that already works without JS +export function enhance( + form: HTMLFormElement, + { + pending, + error, + result + }: { + pending?: (data: FormData, form: HTMLFormElement) => void; + error?: (res: Response, error: Error, form: HTMLFormElement) => void; + result: (res: Response, form: HTMLFormElement) => void; + } +) { + let current_token: {}; + + async function handle_submit(e: Event) { + const token = (current_token = {}); + + e.preventDefault(); + + const body = new FormData(form); + + if (pending) pending(body, form); + + try { + const res = await fetch(form.action, { + method: form.method, + headers: { + accept: 'application/json' + }, + body + }); + + if (token !== current_token) return; + + if (res.ok) { + result(res, form); + } else if (error) { + error(res, null, form); + } else { + console.error(await res.text()); + } + } catch (e) { + if (error) { + error(null, e, form); + } else { + throw e; + } + } + } + + form.addEventListener('submit', handle_submit); + + return { + destroy() { + form.removeEventListener('submit', handle_submit); + } + }; +} diff --git a/src/lib/types.d.ts b/src/lib/types.d.ts new file mode 100644 index 00000000..6edddd1d --- /dev/null +++ b/src/lib/types.d.ts @@ -0,0 +1,7 @@ +/** + * Can be made globally available by placing this + * inside `global.d.ts` and removing `export` keyword + */ +export interface Locals { + userid: string; +} diff --git a/src/routes/__layout.svelte b/src/routes/__layout.svelte new file mode 100644 index 00000000..029f8191 --- /dev/null +++ b/src/routes/__layout.svelte @@ -0,0 +1,45 @@ + + +
+ +
+ +
+ + + + diff --git a/src/routes/about.svelte b/src/routes/about.svelte new file mode 100644 index 00000000..569d3e17 --- /dev/null +++ b/src/routes/about.svelte @@ -0,0 +1,50 @@ + + + + About + + +
+

About this app

+ +

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

+ + +
npm init svelte@next
+ +

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

+ +

+ The TODOs page illustrates SvelteKit's data loading and form handling. Try using + it with JavaScript disabled! +

+
+ + diff --git a/src/routes/index.svelte b/src/routes/index.svelte new file mode 100644 index 00000000..7594f18f --- /dev/null +++ b/src/routes/index.svelte @@ -0,0 +1,59 @@ + + + + + + Home + + +
+

+
+ + + Welcome + +
+ + to your new
SvelteKit app +

+ +

+ try editing src/routes/index.svelte +

+ + +
+ + diff --git a/src/routes/todos/[uid].json.ts b/src/routes/todos/[uid].json.ts new file mode 100644 index 00000000..17891faf --- /dev/null +++ b/src/routes/todos/[uid].json.ts @@ -0,0 +1,16 @@ +import { api } from './_api'; +import type { RequestHandler } from '@sveltejs/kit'; +import type { Locals } from '$lib/types'; + +// PATCH /todos/:uid.json +export const patch: RequestHandler = async (request) => { + return api(request, `todos/${request.locals.userid}/${request.params.uid}`, { + text: request.body.get('text'), + done: request.body.has('done') ? !!request.body.get('done') : undefined + }); +}; + +// DELETE /todos/:uid.json +export const del: RequestHandler = async (request) => { + return api(request, `todos/${request.locals.userid}/${request.params.uid}`); +}; diff --git a/src/routes/todos/_api.ts b/src/routes/todos/_api.ts new file mode 100644 index 00000000..2319c187 --- /dev/null +++ b/src/routes/todos/_api.ts @@ -0,0 +1,48 @@ +import type { Request } from '@sveltejs/kit'; +import type { Locals } from '$lib/types'; + +/* + This module is used by the /todos.json and /todos/[uid].json + endpoints to make calls to api.svelte.dev, which stores todos + for each user. The leading underscore indicates that this is + a private module, _not_ an endpoint — visiting /todos/_api + will net you a 404 response. + + (The data on the todo app will expire periodically; no + guarantees are made. Don't use it to organise your life.) +*/ + +const base = 'https://api.svelte.dev'; + +export async function api(request: Request, resource: string, data?: {}) { + // user must have a cookie set + if (!request.locals.userid) { + return { status: 401 }; + } + + const res = await fetch(`${base}/${resource}`, { + method: request.method, + headers: { + 'content-type': 'application/json' + }, + body: data && JSON.stringify(data) + }); + + // if the request came from a submission, the browser's default + // behaviour is to show the URL corresponding to the form's "action" + // attribute. in those cases, we want to redirect them back to the + // /todos page, rather than showing the response + if (res.ok && request.method !== 'GET' && request.headers.accept !== 'application/json') { + return { + status: 303, + headers: { + location: '/todos' + } + }; + } + + return { + status: res.status, + body: await res.json() + }; +} diff --git a/src/routes/todos/index.json.ts b/src/routes/todos/index.json.ts new file mode 100644 index 00000000..57d68a91 --- /dev/null +++ b/src/routes/todos/index.json.ts @@ -0,0 +1,30 @@ +import { api } from './_api'; +import type { RequestHandler } from '@sveltejs/kit'; +import type { Locals } from '$lib/types'; + +// GET /todos.json +export const get: RequestHandler = async (request) => { + // request.locals.userid comes from src/hooks.js + const response = await api(request, `todos/${request.locals.userid}`); + + if (response.status === 404) { + // user hasn't created a todo list. + // start with an empty array + return { body: [] }; + } + + return response; +}; + +// POST /todos.json +export const post: RequestHandler = async (request) => { + const response = await api(request, `todos/${request.locals.userid}`, { + // because index.svelte posts a FormData object, + // request.body is _also_ a (readonly) FormData + // object, which allows us to get form data + // with the `body.get(key)` method + text: request.body.get('text') + }); + + return response; +}; diff --git a/src/routes/todos/index.svelte b/src/routes/todos/index.svelte new file mode 100644 index 00000000..224e1ef7 --- /dev/null +++ b/src/routes/todos/index.svelte @@ -0,0 +1,227 @@ + + + + + + Todos + + +
+

Todos

+ + { + const created = await res.json(); + todos = [...todos, created]; + + form.reset(); + } + }} + > + + + + {#each todos as todo (todo.uid)} +
+
{ + todo.done = !!data.get('done'); + }, + result: patch + }} + > + +
+ {/each} +
+ + diff --git a/static/favicon.ico b/static/favicon.ico new file mode 100644 index 00000000..d75d248e Binary files /dev/null and b/static/favicon.ico differ diff --git a/static/robots.txt b/static/robots.txt new file mode 100644 index 00000000..e9e57dc4 --- /dev/null +++ b/static/robots.txt @@ -0,0 +1,3 @@ +# https://www.robotstxt.org/robotstxt.html +User-agent: * +Disallow: diff --git a/static/svelte-welcome.png b/static/svelte-welcome.png new file mode 100644 index 00000000..fe7d2d6b Binary files /dev/null and b/static/svelte-welcome.png differ diff --git a/static/svelte-welcome.webp b/static/svelte-welcome.webp new file mode 100644 index 00000000..6ec1a28d Binary files /dev/null and b/static/svelte-welcome.webp differ diff --git a/svelte.config.js b/svelte.config.js new file mode 100644 index 00000000..5eb051a0 --- /dev/null +++ b/svelte.config.js @@ -0,0 +1,15 @@ +import preprocess from 'svelte-preprocess'; + +/** @type {import('@sveltejs/kit').Config} */ +const config = { + // Consult https://github.com/sveltejs/svelte-preprocess + // for more information about preprocessors + preprocess: preprocess(), + + kit: { + // hydrate the
element in src/app.html + target: '#svelte' + } +}; + +export default config; diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 00000000..d826f6ef --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,30 @@ +{ + "compilerOptions": { + "moduleResolution": "node", + "module": "es2020", + "lib": ["es2020"], + "target": "es2019", + /** + 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, + "baseUrl": ".", + "allowJs": true, + "checkJs": true, + "paths": { + "$lib/*": ["src/lib/*"] + } + }, + "include": ["src/**/*.d.ts", "src/**/*.js", "src/**/*.ts", "src/**/*.svelte"] +}