Merge remote-tracking branch 'upstream/master' into dynamic-elements-implementation

pull/6898/head
Yuichiro Yamashita 5 years ago
commit a6a8ffd0f8

@ -6,7 +6,7 @@ jobs:
timeout-minutes: 15
strategy:
matrix:
node-version: [8, 10, 12, 14]
node-version: [8, 10, 12, 14, 16]
os: [ubuntu-latest, windows-latest, macOS-latest]
steps:
- uses: actions/checkout@v1

12
.gitignore vendored

@ -24,13 +24,13 @@ _actual*.*
_output
/types
/site/cypress/screenshots/
/site/__sapper__/
/site/.svelte-kit/
/site/build/
/site/.env
/site/.sessions
/site/static/svelte-app.json
/site/static/contributors.jpg
/site/static/workers
/site/scripts/svelte-app
/site/scripts/community
/site/static/donors.jpg
/site/static/workers/
/site/scripts/svelte-app/
/site/src/routes/_contributors.js
/site/src/routes/_donors.js

@ -1,5 +1,17 @@
# Svelte changelog
## 3.44.0
* Add `enableSourcemap` compiler option ([#6835](https://github.com/sveltejs/svelte/pull/6835))
## 3.43.2
* Fix regression where user-specified `import`s were not rewritten according to the `sveltePath` option ([#6834](https://github.com/sveltejs/svelte/issues/6834))
## 3.43.1
* Prevent a rejecting promise used in `{#await}` during SSR from appearing as an unhandled rejection ([#6789](https://github.com/sveltejs/svelte/issues/6789))
## 3.43.0
* Use export map to expose no-op versions of lifecycle functions for SSR ([#6743](https://github.com/sveltejs/svelte/pull/6743))

1076
package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -1,6 +1,6 @@
{
"name": "svelte",
"version": "3.43.0",
"version": "3.44.0",
"description": "Cybernetically enhanced web apps",
"module": "index.mjs",
"main": "index",
@ -111,7 +111,7 @@
"@rollup/plugin-sucrase": "^3.1.0",
"@rollup/plugin-typescript": "^2.0.1",
"@rollup/plugin-virtual": "^2.0.0",
"@sveltejs/eslint-config": "github:sveltejs/eslint-config#v5.7.0",
"@sveltejs/eslint-config": "github:sveltejs/eslint-config#v5.8.0",
"@types/mocha": "^7.0.0",
"@types/node": "^8.10.53",
"@typescript-eslint/eslint-plugin": "^4.31.2",
@ -133,7 +133,7 @@
"magic-string": "^0.25.3",
"mocha": "^7.0.0",
"periscopic": "^3.0.4",
"puppeteer": "^2.1.1",
"puppeteer": "^2.0.0",
"rollup": "^1.27.14",
"source-map": "^0.7.3",
"source-map-support": "^0.5.13",

@ -2,8 +2,6 @@
!/Dockerfile
!/package.json
!/package-lock.json
!/__sapper__
/__sapper__/*
!/__sapper__/build
!/build
!/static
!/content

@ -1,13 +1,13 @@
NODE_ENV=
PORT=
BASEURL=
PORT=3000
BASEURL=http://localhost:3000
GITHUB_CLIENT_ID=
GITHUB_CLIENT_SECRET=
MAPBOX_ACCESS_TOKEN=
VITE_MAPBOX_ACCESS_TOKEN=
PGHOST=hostname
PGPORT=port
PGHOST=localhost
PGPORT=5432
PGUSER=username
PGPASSWORD=password
PGDATABASE=database_name

@ -1,11 +0,0 @@
sudo: false
language: node_js
node_js:
- "stable"
env:
global:
- BUILD_TIMEOUT=10000
install:
- npm install
- npm install cypress

@ -1,7 +1,7 @@
# IMPORTANT: Don't use this Dockerfile in your own Sapper projects without also looking at the .dockerignore file.
# IMPORTANT: Don't use this Dockerfile in your own projects without also looking at the .dockerignore file.
# Without an appropriate .dockerignore, this Dockerfile will copy a large number of unneeded files into your image.
FROM mhart/alpine-node:12
FROM mhart/alpine-node:14
# install dependencies
WORKDIR /app
@ -12,11 +12,11 @@ RUN npm ci --production
# Only copy over the Node pieces we need
# ~> Saves 35MB
###
FROM mhart/alpine-node:slim-12
FROM mhart/alpine-node:slim-14
WORKDIR /app
COPY --from=0 /app .
COPY . .
EXPOSE 3000
CMD ["node", "__sapper__/build"]
CMD ["node", "build"]

@ -5,10 +5,10 @@ PROJECT := svelte-dev
IMAGE := gcr.io/$(PROJECT)/$(SERVICE):$(HASH)
sapper:
sveltekit:
@echo "\n~> updating template & contributors list"
@npm run update
@echo "\n~> building Sapper app"
@echo "\n~> building SvelteKit app"
@npm run build
@ -17,6 +17,6 @@ docker:
@gcloud builds submit --project $(PROJECT) -t $(IMAGE)
deploy: sapper docker
deploy: sveltekit docker
@echo "\n~> deploying $(SERVICE) to Cloud Run servers"
@gcloud run deploy $(SERVICE) --project $(PROJECT) --allow-unauthenticated --platform managed --region us-central1 --image $(IMAGE) --memory=512Mi

@ -51,7 +51,7 @@ In order for the REPL's GitHub integration to work properly when running locally
## Building the site
To build the website, run `npm run build`. The output can be found in `__sapper__/build`.
To build the website, run `npm run build`. The output can be found in `build`.
## Testing

@ -1,17 +0,0 @@
version: "{build}"
shallow_clone: true
init:
- git config --global core.autocrlf false
build: off
environment:
matrix:
# node.js
- nodejs_version: stable
install:
- ps: Install-Product node $env:nodejs_version
- npm install

@ -80,7 +80,7 @@ Again, you don't need to make this change manually — just run svelte-upgrade o
Svelte v1 was careful to only emit ES5 code, so that you wouldn't be forced to faff around with transpilers in order to use it. But it's 2018 now, and almost all browsers support modern JavaScript. By ditching the ES5 constraint, we can generate leaner code.
If you need to support IE11 and friends, you will need to use a transpiler like [Babel](http://babeljs.io/repl) or [Bublé](http://buble.surge.sh/).
If you need to support IE11 and friends, you will need to use a transpiler like [Babel](https://babeljs.io/repl) or [Bublé](https://buble.surge.sh/).
## New lifecycle hooks

@ -40,7 +40,7 @@ Aside from all that, we've been tempted by the thought of rewriting Sapper for a
## How is this new thing different?
The first of those foundational assumptions is that you need to use a module bundler like [webpack](https://webpack.js.org/) or [Rollup](http://rollupjs.org/) to build apps. These tools trace the dependency graph of your application, analysing and transforming code along the way (turning Svelte components to JS modules, for example), in order to create bundles of code that can run anywhere. As the original creator of Rollup, I can attest that it is a surprisingly complex problem with fiendish edge cases.
The first of those foundational assumptions is that you need to use a module bundler like [webpack](https://webpack.js.org/) or [Rollup](https://rollupjs.org/) to build apps. These tools trace the dependency graph of your application, analysing and transforming code along the way (turning Svelte components to JS modules, for example), in order to create bundles of code that can run anywhere. As the original creator of Rollup, I can attest that it is a surprisingly complex problem with fiendish edge cases.
You certainly needed a bundler several years ago, because browsers didn't natively support the `import` keyword, but it's much less true today. Right now, we're seeing the rise of the *unbundled development* workflow, which is radically simpler: instead of eagerly bundling your app, a dev server can serve modules (converted to JavaScript, if necessary) *on-demand*, meaning startup is essentially instantaneous however large your app becomes.

@ -0,0 +1,99 @@
---
title: What's new in Svelte: October 2021
description: A whole year of "What's new in Svelte"
author: Daniel Sandoval
authorURL: https://desandoval.net
---
Hey y'all 👋 It's been 1 year since "What's new in Svelte" started being cross-posted to the Svelte blog. I wanted to take this moment at the top to thank all of you for reading and for all the contributors every month. From the maintainers to everyone who posts their work in the Discord and Reddit, it's amazing to witness all the effort that goes into making the Svelte community great.
Keep up the good work, everyone! Now, let's dive into this month's news...
## New around Svelte
- New additions to Svelte's export map now expose no-op versions of lifecycle functions for SSR (Svelte **3.43.0**)
- Custom components with a `src` attribute no longer break `svelte-native` builds (Svelte **3.42.4**)
- Svelte plugin users without [the TypeScript plugin](https://www.npmjs.com/package/typescript-svelte-plugin) enabled will now be prompted to enable it. It enhances TypeScript and JavaScript files with additional intellisense to interact with Svelte files. [Please leave feedback](https://github.com/sveltejs/language-tools/issues/580) if you are using it (Svelte extensions **105.4.0**)
- Event modifiers have been added to intellisense as autocomplete and hover info (Svelte extensions **105.4.0**)
- TypeScript users no longer have to strictly separate type imports and value imports when using Svelte version 3.39 or higher and `svelte-preprocess` version 4.9.5 or higher. This means you can now write `import { MyInterface, myValue } from './somewhere'` instead of `import type { MyInterface } from './somewhere'; import { myValue } from './somewhere'`. Huge thanks to community member [@SomaticIT](https://github.com/SomaticIT) who mainly implemented this!
For a full list of features and bug fixes, check out the [Svelte changelog](https://github.com/sveltejs/svelte/blob/master/CHANGELOG.md).
## SvelteKit Updates
Nearly 100 PRs committed again this past month, but there's still lots to do and Svelte maintainers are [looking for help getting SvelteKit to 1.0](https://github.com/sveltejs/kit/issues/2100). Antony said it well in a [recent comment](https://github.com/sveltejs/kit/issues/2100#issuecomment-895446285) on the issue:
> If you think you are too n00b to contribute (you're not), then add tests, or write tests for the feature you want to add, before you add it! Start small, and learn the codebase that way.
If you'd like to help, please consider working on any of the [1.0 milestone issues labeled with "help wanted"](https://github.com/sveltejs/kit/issues?q=is%3Aopen+is%3Aissue+milestone%3A1.0+label%3A%22help+wanted%22).
Notable SvelteKit improvements this month include...
- Service workers are now allowed to access files using the `$lib` alias ([#2326](https://github.com/sveltejs/kit/pull/2326))
- Svelte libraries should now work out-of-the-box without any Vite configuration ([#2343](https://github.com/sveltejs/kit/pull/2343))
- Improvements to package exports field ([#2345](https://github.com/sveltejs/kit/pull/2345) and [#2327](https://github.com/sveltejs/kit/pull/2327))
- [breaking] The `prerender.pages` config option has been renamed to `prerender.entries` ([#2380](https://github.com/sveltejs/kit/pull/2380))
- A new generic argument has beend added to allow typing Body from hooks ([#2413](https://github.com/sveltejs/kit/pull/2413))
- The `svelte` field will be added to package.json when running the package command ([#2431](https://github.com/sveltejs/kit/pull/2431))
- [breaking] The `context` parameter of the load function was renamed to `stuff` ([#2439](https://github.com/sveltejs/kit/pull/2439))
- Added an `entryPoint` option for building a custom server with `adapter-node` ([#2414](https://github.com/sveltejs/kit/pull/2414))
- `vite-plugin-svelte` improved support for [useVitePreprocess](https://github.com/sveltejs/vite-plugin-svelte/blob/main/docs/config.md#usevitepreprocess), which uses Vite to automatically preprocess TypeScript, PostCSS, Scss, etc in Svelte components ([#173](https://github.com/sveltejs/vite-plugin-svelte/pull/173))
To see all updates to SvelteKit, check out the [SvelteKit changelog](https://github.com/sveltejs/kit/blob/master/packages/kit/CHANGELOG.md).
---
## Community Showcase
**Apps & Sites**
- [radiofrance](https://www.radiofrance.fr/) just migrated their website to SvelteKit
- [FLAYKS](https://flayks.com/) is the portfolio site for Félix Péault made with SvelteKit, Sanity, and Anime.js
- [hirehive](https://www.hirehive.com/) is a candidate and job tracking site
- [Microsocial](https://microsocial.xyz/) is an experimental Peer-to-Peer Social Platform
- [Dylan Ipsum](https://www.dylanlyrics.app/) is a random text generator to replace lorem ipsum with Bob Dylan lyrics
- [Chip8 Svelte](https://github.com/mikeyhogarth/chip8-svelte) is a CHIP-8 emulator frontend, built on top of CHIP8 Typescript
**Looking for a Svelte project to work on? Interested in helping make Svelte's presence on the web better?** Check out [the list of open issues](https://github.com/svelte-society/sveltesociety-2021/issues) if you'd like to contribute to the Svelte Society rewrite in SvelteKit.
**Podcasts Featuring Svelte**
- [Syntax Podast: From React to SvelteKit](https://podcasts.apple.com/us/podcast/from-react-to-sveltekit/id1253186678?i=1000536276106) Scott talks with Wes about moving Level Up Tutorials from React to SvelteKit — why he did it, how, benefits, things to watch out for, and more!
- [Web Rush Podcast: Svelte Tools and Svelte Society](https://www.webrush.io/episodes/episode-150-svelte-tools-and-svelte-society) Kevin Åberg Kultalahti talks about what Svelte Society is, what he's excited about with Svelte, how important documentation is to any product, and much _much_ more
- [Svelte: The Compiled Future of Front End](https://www.arahansen.com/the-compiled-future-of-front-end/) details the history of component-based frontends and how a compiler changes everything
- [Svelte Radio: Contributing to Svelte with Martin 'Grygrflzr' Krisnanto Putra](https://share.transistor.fm/s/10aa305c) Grygrflzr shares his journey to becoming a maintainer and his views on React, Vite and a host of other things
- [Svelte Radio: Routify 3 with Jake and Willow](https://share.transistor.fm/s/10aa305c) the Svelte Radio crew sits down with the maintainers of Routify and discusses the just-released Routify 3
- [JS Party: 1Password](https://twitter.com/geoffrich_/status/1441816829853253640?s=20) mentioned on the latest episode of The Changelog's JS Party that they use Svelte to power their in-page suggestions
**Educational Content**
- [How I built a blog with Svelte and SvelteKit](https://fantinel.dev/blog-development-sveltekit/) is an introduction to Svelte, SvelteKit and Progressive Enhancement with code examples
- [I built a decentralized chat dapp](https://www.youtube.com/watch?v=J5x3OMXjgMc) is a tutorial on how to use popular web3 technologies like GUN to build a decentralized web app (dapp)
- [Writing a Svelte Store with TypeScript](https://javascript.plainenglish.io/writing-a-svelte-store-with-typescript-22fa1c901a4) is a deep dive into writing Svelte stores with TypeScript
- [How Svelte scopes component styles](https://geoffrich.net/posts/svelte-scoped-styles/) explains scoping using classes and more complex CSS specifiers
- [SvelteKit Hooks](https://www.youtube.com/watch?v=RarufLoEL08) shows how to use hooks.js in Sveltekit.. When you're done, check out [Part 2](https://www.youtube.com/watch?v=RmIBG3G0-VY)
- [An early look at SvelteKit](https://www.infoworld.com/article/3630395/an-early-look-at-sveltekit.html) is a post from Infoworld walking through the features and onboarding of SvelteKit
**Libraries, Tools & Components**
- [sveltekit-netlify-cms](https://github.com/buhrmi/sveltekit-netlify-cms) is a SvelteKit skeleton app configured for use with Netlify CMS
- [SvelteFireTS](https://github.com/jacobbowdoin/sveltefirets) is a SvelteKit + Typescript + Firebase library inspired by Fireship.io
- [stores-x](https://github.com/Anyass3/stores-x) lets you use Svelte stores just like vueX
- [sveltekit-snippets](https://github.com/stordahl/sveltekit-snippets) is a VSCode extension that provides snippets for common patterns in SvelteKit & Vanilla Svelte
- [svelte-xactor](https://github.com/wobsoriano/svelte-xactor) is a middleware that allows you to easily convert your xactor machines into a global store that implements the store contract
- [vite-plugin-pages-svelte](https://github.com/aldy505/vite-plugin-pages-svelte) is a vite plugin for automatic filesystem-based routing
- [sveltio](https://www.npmjs.com/package/sveltio) is a Svelte wrapper for valtio - a proxy-state library
- [svelte-transition-classes](https://github.com/rmarscher/svelte-transition-classes) is a custom Svelte transition for adding and swapping CSS classes
- [Svelte-Boring-Avatars](https://github.com/paolotiu/svelte-boring-avatars) is Svelte port of the popular [Boring Avatars](https://github.com/boringdesigners/boring-avatars) React project
- [Svelte DataTables](https://github.com/homescriptone/svelte-datatables) brings DataTable, a popular JavaScript library allowing you to easily display your data in a user-friendly table, into your Svelte project.
- [focus-svelte](https://github.com/chanced/focus-svelte) is a focus trap for Svelte with zero dependencies
- [filedrop-svelte](https://github.com/chanced/filedrop-svelte) is a file dropzone action & component for Svelte
Check out the community site [sveltesociety.dev](https://sveltesociety.dev/templates/) for more templates, adders and adapters from across the Svelte ecosystem.
## Before you go, answer the call for speakers!
Svelte Summit Fall 2021 (happening 20 November 2021) is looking for speakers. Submit your talk propsoal before 30 October... all are welcome to present and attend.
### More info on the [sessionize site](https://sessionize.com/svelte-summit-fall-2021/)
Can't wait for the summit? Join us on [Reddit](https://www.reddit.com/r/sveltejs/) or [Discord](https://discord.com/invite/yy75DKs)!

@ -309,7 +309,7 @@ An each block can also have an `{:else}` clause, which is rendered if the list i
---
Await blocks allow you to branch on the three possible states of a Promise — pending, fulfilled or rejected.
Await blocks allow you to branch on the three possible states of a Promise — pending, fulfilled or rejected. In SSR mode, only the pending state will be rendered on the server.
```sv
{#await promise}
@ -1132,7 +1132,7 @@ DOMRect {
---
An animation is triggered when the contents of a [keyed each block](docs#each) are re-ordered. Animations do not run when an element is removed, only when the each block's data is reordered. Animate directives must be on an element that is an *immediate* child of a keyed each block.
An animation is triggered when the contents of a [keyed each block](docs#each) are re-ordered. Animations do not run when an element is added or removed, only when the index of an existing data item within the each block changes. Animate directives must be on an element that is an *immediate* child of a keyed each block.
Animations can be used with Svelte's [built-in animation functions](docs#svelte_animate) or [custom animation functions](docs#Custom_animation_functions).

@ -533,7 +533,7 @@ A `spring` store gradually changes to its target value based on its `stiffness`
* `stiffness` (`number`, default `0.15`) — a value between 0 and 1 where higher means a 'tighter' spring
* `damping` (`number`, default `0.8`) — a value between 0 and 1 where lower means a 'springier' spring
* `precision` (`number`, default `0.001`) — determines the threshold at which the spring is considered to have 'settled', where lower means more precise
* `precision` (`number`, default `0.01`) — determines the threshold at which the spring is considered to have 'settled', where lower means more precise
---

@ -82,6 +82,8 @@ The following options can be passed to the compiler. None are required:
| `loopGuardTimeout` | 0 | A `number` that tells Svelte to break the loop if it blocks the thread for more than `loopGuardTimeout` ms. This is useful to prevent infinite loops. **Only available when `dev: true`**
| `preserveComments` | `false` | If `true`, your HTML comments will be preserved during server-side rendering. By default, they are stripped out.
| `preserveWhitespace` | `false` | If `true`, whitespace inside and between elements is kept as you typed it, rather than removed or collapsed to a single space where possible.
| `sourcemap` | `object \| string` | An initial sourcemap that will be merged into the final output sourcemap. This is usually the preprocessor sourcemap.
| `enableSourcemap` | `boolean \| { js: boolean; css: boolean; }` | If `true`, Svelte generate sourcemaps for components. Use an object with `js` or `css` for more granular control of sourcemap generation. By default, this is `true`.
| `outputFilename` | `null` | A `string` used for your JavaScript sourcemap.
| `cssOutputFilename` | `null` | A `string` used for your CSS sourcemap.
| `sveltePath` | `"svelte"` | The location of the `svelte` package. Any imports from `svelte` or `svelte/[module]` will be modified accordingly.

@ -30,7 +30,7 @@
</label>
{#if visible}
<p in:typewriter out:typewriter>
<p transition:typewriter>
The quick brown fox jumps over the lazy dog
</p>
{/if}

@ -14,3 +14,8 @@
</label>
<p>{a} + {b} = {a + b}</p>
<style>
label { display: flex }
input, p { margin: 6px }
</style>

@ -14,3 +14,8 @@
</label>
<p>{a} + {b} = {a + b}</p>
<style>
label { display: flex }
input, p { margin: 6px }
</style>

@ -12,7 +12,7 @@ Now we can programmatically interact with this component using `field`.
```html
<button on:click="{() => field.focus()}">
Focus field
Focus field
</button>
```

@ -6,7 +6,7 @@
let count_value;
const unsubscribe = count.subscribe(value => {
count.subscribe(value => {
count_value = value;
});
</script>

@ -16,7 +16,7 @@
</label>
{#if visible}
<p in:typewriter>
<p transition:typewriter>
The quick brown fox jumps over the lazy dog
</p>
{/if}

@ -30,7 +30,7 @@
</label>
{#if visible}
<p in:typewriter out:typewriter>
<p transition:typewriter>
The quick brown fox jumps over the lazy dog
</p>
{/if}

@ -0,0 +1,15 @@
version: "3.0"
services:
postgres:
image: postgres:13.4
ports:
- "5432:5432"
environment:
- POSTGRES_DB=${PGDATABASE}
- POSTGRES_USER=${PGUSER}
- POSTGRES_PASSWORD=${PGPASSWORD}
adminer:
image: adminer
restart: always
ports:
- "4000:8080"

4968
site/package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -2,62 +2,42 @@
"name": "svelte.dev",
"version": "1.0.0",
"description": "Docs and examples for Svelte",
"type": "module",
"scripts": {
"dev": "node scripts/update.js && npm run copy-workers && sapper dev",
"dev": "node scripts/update.js && npm run copy-workers && svelte-kit dev",
"copy-workers": "node scripts/copy-workers.js",
"migrate": "node-pg-migrate -r dotenv/config",
"build": "node scripts/update.js && npm run copy-workers && sapper build --legacy",
"build": "node scripts/update.js && npm run copy-workers && svelte-kit build",
"update": "node scripts/update.js --force=true",
"start": "node __sapper__/build",
"test": "mocha -r esm test/**",
"start": "node build",
"test": "uvu test",
"deploy": "make deploy"
},
"dependencies": {
"@polka/redirect": "^1.0.0-next.7",
"@polka/send": "^1.0.0-next.7",
"cookie": "^0.4.0",
"devalue": "^2.0.0",
"do-not-zip": "^1.0.0",
"flru": "^1.0.2",
"httpie": "^1.1.2",
"jsonwebtoken": "^8.5.1",
"marked": "^1.0.0",
"pg": "^7.12.1",
"polka": "^1.0.0-next.9",
"marked": "^3.0.5",
"pg": "^8.7.1",
"prism-svelte": "^0.4.3",
"prismjs": "^1.25.0",
"sirv": "^1.0.0",
"yootils": "0.0.16"
"prismjs": "^1.25.0"
},
"devDependencies": {
"@babel/core": "^7.6.0",
"@babel/plugin-syntax-dynamic-import": "^7.2.0",
"@babel/plugin-transform-runtime": "^7.6.0",
"@babel/preset-env": "^7.6.0",
"@babel/runtime": "^7.6.0",
"@rollup/plugin-babel": "^5.0.0",
"@rollup/plugin-commonjs": "^17.0.0",
"@rollup/plugin-json": "^4.1.0",
"@rollup/plugin-node-resolve": "^11.0.0",
"@rollup/plugin-replace": "^2.2.0",
"@sindresorhus/slugify": "^0.9.1",
"@sveltejs/adapter-node": "next",
"@sveltejs/kit": "next",
"@sveltejs/site-kit": "^1.4.0",
"@sveltejs/svelte-repl": "^0.2.1",
"@sveltejs/svelte-repl": "^0.3.0",
"degit": "^2.1.4",
"dotenv": "^8.1.0",
"esm": "^3.2.25",
"dotenv": "^10.0.0",
"jimp": "^0.8.0",
"mocha": "^6.2.0",
"node-fetch": "^2.6.1",
"node-pg-migrate": "^3.22.0",
"rollup": "^2.30.0",
"rollup-plugin-svelte": "^7.0.0",
"rollup-plugin-terser": "^7.0.0",
"sapper": "^0.28.10",
"node-pg-migrate": "^6.0.0",
"shelljs": "^0.8.3",
"svelte": "^3.39.0"
},
"engines": {
"node": ">=10.0.0"
"svelte": "^3.39.0",
"uvu": "^0.5.2"
}
}

@ -1,122 +0,0 @@
import 'dotenv/config';
import babel from '@rollup/plugin-babel';
import commonjs from '@rollup/plugin-commonjs';
import json from '@rollup/plugin-json';
import replace from '@rollup/plugin-replace';
import resolve from '@rollup/plugin-node-resolve';
import svelte from 'rollup-plugin-svelte';
import { terser } from 'rollup-plugin-terser';
import config from 'sapper/config/rollup.js';
import pkg from './package.json';
const mode = process.env.NODE_ENV;
const dev = mode === 'development';
const legacy = !!process.env.SAPPER_LEGACY_BUILD;
if (!dev && !process.env.MAPBOX_ACCESS_TOKEN) {
throw new Error('MAPBOX_ACCESS_TOKEN is missing. Please add the token in the .env file before generating the production build.');
}
const onwarn = (warning, onwarn) => (warning.code === 'CIRCULAR_DEPENDENCY' && /[/\\]@sapper[/\\]/.test(warning.message)) || onwarn(warning);
const dedupe = importee => importee === 'svelte' || importee.startsWith('svelte/');
export default {
client: {
input: config.client.input(),
output: config.client.output(),
plugins: [
replace({
'process.browser': true,
'process.env.NODE_ENV': JSON.stringify(mode),
'process.env.MAPBOX_ACCESS_TOKEN': JSON.stringify(process.env.MAPBOX_ACCESS_TOKEN)
}),
svelte({
compilerOptions: {
dev,
hydratable: true
}
}),
resolve({
browser: true,
dedupe
}),
commonjs(),
json(),
legacy && babel({
extensions: ['.js', '.mjs', '.html', '.svelte'],
babelHelpers: 'runtime',
exclude: ['node_modules/@babel/**'],
presets: [
['@babel/preset-env', {
targets: '> 0.25%, not dead'
}]
],
plugins: [
'@babel/plugin-syntax-dynamic-import',
['@babel/plugin-transform-runtime', {
useESModules: true
}]
]
}),
!dev && terser({
module: true
})
],
preserveEntrySignatures: false,
onwarn
},
server: {
input: config.server.input(),
output: config.server.output(),
plugins: [
replace({
'process.browser': false,
'process.env.NODE_ENV': JSON.stringify(mode)
}),
svelte({
compilerOptions: {
dev,
generate: 'ssr',
hydratable: true
},
emitCss: false
}),
resolve({
dedupe
}),
commonjs(),
json()
],
external: [
'yootils',
'codemirror',
...Object.keys(pkg.dependencies || {}).concat(
require('module').builtinModules || Object.keys(process.binding('natives'))
)
],
preserveEntrySignatures: 'strict',
onwarn
},
serviceworker: {
input: config.serviceworker.input(),
output: config.serviceworker.output(),
plugins: [
resolve(),
replace({
'process.browser': true,
'process.env.NODE_ENV': JSON.stringify(mode)
}),
commonjs(),
!dev && terser()
],
preserveEntrySignatures: false,
onwarn
}
};

@ -1,4 +1,4 @@
const sh = require('shelljs');
import sh from 'shelljs';
sh.rm('-rf', 'static/workers');
sh.cp('-r', 'node_modules/@sveltejs/svelte-repl/workers', 'static');

@ -1,7 +1,7 @@
const fs = require('fs');
const puppeteer = require('puppeteer');
const Jimp = require('jimp');
const c = require('kleur');
import fs from 'fs';
import puppeteer from 'puppeteer';
import Jimp from 'jimp';
import c from 'kleur';
const slugs = [];

@ -1,5 +1,8 @@
const sander = require('sander');
import sander from 'sander';
import { dirname } from 'path';
import { fileURLToPath } from 'url';
const __dirname = dirname(fileURLToPath(import.meta.url));
process.chdir(`${__dirname}/../..`);
function extract_frontmatter(markdown) {

@ -1,10 +1,14 @@
require('dotenv/config');
const fs = require('fs');
const fetch = require('node-fetch');
const Jimp = require('jimp');
import 'dotenv/config';
import fs from 'fs';
import fetch from 'node-fetch';
import Jimp from 'jimp';
import { dirname } from 'path';
import { fileURLToPath } from 'url';
const force = process.env.FORCE_UPDATE === 'true';
const __dirname = dirname(fileURLToPath(import.meta.url));
process.chdir(__dirname);
const outputFile = `../src/routes/_contributors.js`;

@ -0,0 +1,63 @@
import 'dotenv/config';
import fs from 'fs';
import fetch from 'node-fetch';
import Jimp from 'jimp';
import { dirname } from 'path';
import { fileURLToPath } from 'url';
const force = process.env.FORCE_UPDATE === 'true';
const __dirname = dirname(fileURLToPath(import.meta.url));
process.chdir(__dirname);
const outputFile = `../src/routes/_donors.js`;
if (!force && fs.existsSync(outputFile)) {
console.info(`[update/donors] ${outputFile} exists. Skipping`);
process.exit(0);
}
const SIZE = 64;
async function main() {
const res = await fetch('https://opencollective.com/svelte/members/all.json');
const donors = await res.json();
const unique = new Map();
donors.forEach(d => unique.set(d.profile, d));
let backers = [...unique.values()]
.filter(({ role }) => role === 'BACKER')
.sort((a, b) => b.totalAmountDonated - a.totalAmountDonated);
const included = [];
for (let i = 0; i < backers.length; i += 1) {
const backer = backers[i];
console.log(`${i} / ${backers.length}: ${backer.name}`);
try {
const image_data = await fetch(backer.image);
const buffer = await image_data.arrayBuffer();
const image = await Jimp.read(buffer);
image.resize(SIZE, SIZE);
included.push({ backer, image });
} catch( err) {
console.log(`Skipping ${backer.name}: no image data`);
}
}
const sprite = new Jimp(SIZE * included.length, SIZE);
for (let i = 0; i < included.length; i += 1) {
sprite.composite(included[i].image, i * SIZE, 0);
}
await sprite.quality(80).write(`../static/donors.jpg`);
// TODO: Optimizing the static/donors.jpg image should probably get automated as well
console.log('remember to additionally optimize the resulting /static/donors.jpg image file via e.g. https://squoosh.app ');
const str = `[\n\t${included.map(a => `${JSON.stringify(a.backer.name)}`).join(',\n\t')}\n]`;
fs.writeFileSync(outputFile, `export default ${str};`);
}
main();

@ -1,8 +1,9 @@
const sh = require('shelljs');
import sh from 'shelljs';
sh.env['FORCE_UPDATE'] = process.argv.includes('--force=true');
Promise.all([
sh.exec('node ./scripts/get_contributors.js'),
sh.exec('node ./scripts/get_donors.js'),
sh.exec('node ./scripts/update_template.js')
]);

@ -1,9 +1,11 @@
const sh = require('shelljs');
const fs = require('fs');
const path = require('path');
import sh from 'shelljs';
import fs from 'fs';
import path, { dirname } from 'path';
import { fileURLToPath } from 'url';
const force = process.env.FORCE_UPDATE === 'true';
const __dirname = dirname(fileURLToPath(import.meta.url));
sh.cd(path.join(__dirname, '..'));
const outputFile = 'static/svelte-app.json';

@ -0,0 +1,26 @@
<!doctype html>
<html lang='en' class="theme-default typo-default">
<head>
<meta charset='utf-8'>
<meta name='viewport' content='width=device-width,initial-scale=1'>
<meta name='theme-color' content='#ff3e00'>
<base href='/'>
<link rel='stylesheet' href='global.css'>
<link rel='stylesheet' href='prism.css'>
<link rel='manifest' href='manifest.json'>
<link rel='icon' type='image/png' href='favicon.png'>
<meta name='twitter:card' content='summary_large_image'>
<meta name='twitter:site' content='@sveltejs'>
<meta name='twitter:creator' content='@sveltejs'>
<meta name='twitter:image' content='https://svelte.dev/images/twitter-card.png'>
<meta name='og:image' content='https://svelte.dev/images/twitter-card.png'>
%svelte.head%
</head>
<body>
<div id='svelte'>%svelte.body%</div>
</body>
</html>

@ -1,6 +0,0 @@
import '@sveltejs/site-kit/base.css';
import * as sapper from '@sapper/app';
sapper.start({
target: document.querySelector('#sapper')
});

@ -1,6 +1,7 @@
<script>
import Repl from '@sveltejs/svelte-repl';
import { onMount } from 'svelte';
import { browser } from '$app/env';
import { process_example } from '../../utils/examples';
import InputOutputToggle from './InputOutputToggle.svelte';
@ -12,7 +13,7 @@
let repl;
let name = 'loading...';
let width = process.browser
let width = browser
? window.innerWidth - 32
: 1000;
@ -73,7 +74,7 @@
$: if (embedded) document.title = `${name} • Svelte REPL`;
$: svelteUrl = process.browser && version === 'local' ?
$: svelteUrl = browser && version === 'local' ?
`${location.origin}/repl/local` :
`https://unpkg.com/svelte@${version}`;
@ -116,7 +117,7 @@
<div class="repl-outer" bind:clientWidth={width} class:mobile>
<div class="viewport" class:offset={checked}>
{#if process.browser}
{#if browser}
<Repl
bind:this={repl}
workersUrl="workers"

@ -2,4 +2,4 @@
export const svelteUrl = `https://unpkg.com/svelte@3`;
export const rollupUrl = `https://unpkg.com/rollup@1/dist/rollup.browser.js`;
export const mapbox_setup = `window.MAPBOX_ACCESS_TOKEN = process.env.MAPBOX_ACCESS_TOKEN;`;
export const mapbox_setup = `window.MAPBOX_ACCESS_TOKEN = '${import.meta.env.VITE_MAPBOX_ACCESS_TOKEN}';`;

@ -0,0 +1,31 @@
// need to do this first before importing database, etc.
import dotenv from 'dotenv';
dotenv.config();
import * as cookie from 'cookie';
import { get_user, sanitize_user } from './utils/auth';
import { query } from './utils/db';
/** @type {import('@sveltejs/kit').Handle} */
export async function handle({ request, resolve }) {
if (process.env['PGHOST']) {
// this is a convenient time to clear out expired sessions
query('delete from sessions where expiry < now()');
request.locals.cookies = cookie.parse(request.headers.cookie || '');
request.locals.user = await get_user(request.locals.cookies.sid);
}
const response = await resolve(request);
return response;
}
/** @type {import('@sveltejs/kit').GetSession} */
export function getSession(request) {
return request.locals.user
? {
user: sanitize_user(request.locals.user)
}
: {};
}

@ -1,6 +1,13 @@
<script>
const dev = process.env.NODE_ENV === 'development';
<script context="module">
/** @type {import('@sveltejs/kit').ErrorLoad} */
export function load({ error, status }) {
return {
props: { error, status }
};
}
</script>
<script>
export let status;
export let error;
@ -54,7 +61,7 @@
<p class="error">Encountered a {status} error</p>
{/if}
{#if dev && error.stack}
{#if import.meta.env.DEV && error.stack}
<pre>{error.stack}</pre>
{:else}
{#if status >= 500}

@ -1,13 +1,12 @@
<script>
import '@sveltejs/site-kit/base.css';
import { setContext } from 'svelte';
import { stores } from '@sapper/app';
import { page, navigating, session } from '$app/stores';
import { Icon, Icons, Nav, NavItem } from '@sveltejs/site-kit';
import PreloadingIndicator from '../components/PreloadingIndicator.svelte';
export let segment;
const { page, preloading, session } = stores();
setContext('app', {
login: () => {
const login_window = window.open(`${window.location.origin}/auth/login`, 'login', 'width=600,height=400');
@ -31,7 +30,7 @@
<Icons/>
{#if $preloading}
{#if $navigating && $navigating.to}
<PreloadingIndicator/>
{/if}

@ -0,0 +1,27 @@
<script>
import donors from '../_donors.js';
</script>
<style>
.donor {
width: 2.4em;
height: 2.4em;
border-radius: 50%;
text-indent: -9999px;
display: inline-block;
background: no-repeat url(/donors.jpg);
background-size: auto 102%;
margin: 0 0.5em 0.5em 0;
border: 2px solid var(--second);
}
</style>
{#each donors as donor, i}
<a
class="donor"
style="background-position: {(100 * i) / (donors.length - 1)}% 0"
alt="{donor}"
href="https://opencollective.com/svelte">
{donor}
</a>
{/each}

@ -46,6 +46,7 @@
height: 100%;
padding: 5px 10px;
transition: transform 0.2s;
min-width: 0; /* Avoid image overflow in Safari */
}
picture:hover,
img:hover {

@ -1,26 +1,25 @@
import send from '@polka/send';
import { query } from '../../utils/db';
import { query as db_query } from '../../utils/db';
export async function get(req, res) {
if (req.user) {
export async function get({ query, locals }) {
if (locals.user) {
const page_size = 100;
const offset = req.query.offset ? parseInt(req.query.offset) : 0;
const rows = await query(`
const offset = query.get('offset') ? parseInt(query.get('offset')) : 0;
const rows = await db_query(`
select g.uid, g.name, coalesce(g.updated_at, g.created_at) as updated_at
from gists g
where g.user_id = $1
order by id desc
limit ${page_size + 1}
offset $2
`, [req.user.id, offset]);
`, [locals.user.id, offset]);
rows.forEach(row => {
row.uid = row.uid.replace(/-/g, '');
});
const more = rows.length > page_size;
send(res, 200, { apps: rows.slice(0, page_size), offset: more ? offset + page_size : null });
return { body: { apps: rows.slice(0, page_size), offset: more ? offset + page_size : null }};
} else {
send(res, 401);
return { status: 401 };
}
}

@ -1,22 +1,22 @@
<script context="module">
export async function preload(page, { user }) {
export async function load({ fetch, page, session: { user }}) {
let apps = [];
let offset = null;
if (user) {
let url = 'apps.json';
if (page.query.offset) {
url += `?offset=${encodeURIComponent(page.query.offset)}`;
if (page.query.get('offset')) {
url += `?offset=${encodeURIComponent(page.query.get('offset'))}`;
}
const r = await this.fetch(url, {
const r = await fetch(url, {
credentials: 'include'
});
if (!r.ok) return this.error(r.status, await r.text());
if (!r.ok) return { status: r.status, body: await r.text() };
({ apps, offset } = await r.json());
}
return { user, apps, offset };
return { props: { user, apps, offset }};
}
</script>

@ -1,6 +1,6 @@
export const oauth = 'https://github.com/login/oauth';
export const baseurl = process.env.BASEURL;
export const baseurl = process.env['BASEURL'];
export const secure = baseurl && baseurl.startsWith('https:');
export const client_id = process.env.GITHUB_CLIENT_ID;
export const client_secret = process.env.GITHUB_CLIENT_SECRET;
export const client_id = process.env['GITHUB_CLIENT_ID'];
export const client_secret = process.env['GITHUB_CLIENT_SECRET'];

@ -1,4 +1,3 @@
import send from '@polka/send';
import devalue from 'devalue';
import * as cookie from 'cookie';
import * as httpie from 'httpie';
@ -6,11 +5,11 @@ import { parse, stringify } from 'querystring';
import { sanitize_user, create_user, create_session } from '../../utils/auth';
import { oauth, secure, client_id, client_secret } from './_config.js';
export async function get(req, res) {
export async function get({ query }) {
try {
// Trade "code" for "access_token"
const r1 = await httpie.post(`${oauth}/access_token?` + stringify({
code: req.query.code,
code: query.get('code'),
client_id,
client_secret,
}));
@ -27,28 +26,29 @@ export async function get(req, res) {
const user = await create_user(r2.data, access_token);
const session = await create_session(user);
res.writeHead(200, {
'Set-Cookie': cookie.serialize('sid', session.uid, {
maxAge: 31536000,
path: '/',
httpOnly: true,
secure
}),
'Content-Type': 'text/html; charset=utf-8'
});
res.end(`
return {
headers: {
'Set-Cookie': cookie.serialize('sid', session.uid, {
maxAge: 31536000,
path: '/',
httpOnly: true,
secure
}),
'Content-Type': 'text/html; charset=utf-8'
},
body: `
<script>
window.opener.postMessage({
user: ${devalue(sanitize_user(user))}
}, window.location.origin);
</script>
`);
`
};
} catch (err) {
console.error('GET /auth/callback', err);
send(res, 500, err.data, {
'Content-Type': err.headers['content-type'],
'Content-Length': err.headers['content-length']
});
return {
status: 500,
body: err.data
};
}
}

@ -1,19 +1,28 @@
import send from '@polka/send';
import { stringify } from 'querystring';
import { oauth, baseurl, client_id } from './_config.js';
export const get = client_id
? (req, res) => {
? () => {
const Location = `${oauth}/authorize?` + stringify({
scope: 'read:user',
client_id,
redirect_uri: `${baseurl}/auth/callback`,
});
send(res, 302, Location, { Location });
return {
status: 302,
headers: {
Location
}
};
}
: (req, res) => {
send(res, 500, `
: () => {
return {
status: 500,
headers: {
'Content-Type': 'text/html; charset=utf-8'
},
body: `
<body style="font-family: sans-serif; background: rgb(255,215,215); border: 2px solid red; margin: 0; padding: 1em;">
<h1>Missing .env file</h1>
<p>In order to use GitHub authentication, you will need to <a target="_blank" href="https://github.com/settings/developers">register an OAuth application</a> and create a local .env file:</p>
@ -21,7 +30,6 @@ export const get = client_id
<p>The <code>BASEURL</code> variable should match the callback URL specified for your app.</p>
<p>See also <a target="_blank" href="https://github.com/sveltejs/svelte/tree/master/site#repl-github-integration">here</a></p>
</body>
`, {
'Content-Type': 'text/html; charset=utf-8'
});
`
};
};

@ -1,17 +1,18 @@
import send from '@polka/send';
import * as cookie from 'cookie';
import { secure } from './_config.js';
import { delete_session } from '../../utils/auth.js';
export async function get(req, res) {
await delete_session(req.cookies.sid);
export async function get(request) {
await delete_session(request.locals.cookies.sid);
send(res, 200, '', {
'Set-Cookie': cookie.serialize('sid', '', {
maxAge: -1,
path: '/',
httpOnly: true,
secure
})
});
return {
headers: {
'Set-Cookie': cookie.serialize('sid', '', {
maxAge: -1,
path: '/',
httpOnly: true,
secure
})
}
};
}

@ -1,9 +1,8 @@
import send from '@polka/send';
import get_posts from './_posts.js';
let lookup;
export function get(req, res) {
export function get({ params }) {
if (!lookup || process.env.NODE_ENV !== 'production') {
lookup = new Map();
get_posts().forEach(post => {
@ -11,12 +10,14 @@ export function get(req, res) {
});
}
const post = lookup.get(req.params.slug);
const post = lookup.get(params.slug);
if (post) {
res.setHeader('Cache-Control', `max-age=${5 * 60 * 1e3}`); // 5 minutes
send(res, 200, post);
} else {
send(res, 404, { message: 'not found' });
return {
body: post,
headers: {
'Cache-Control': `max-age=${5 * 60 * 1e3}` // 5 minutes
}
};
}
}

@ -1,7 +1,9 @@
<script context="module">
export async function preload({ params }) {
const res = await this.fetch(`blog/${params.slug}.json`);
return res.ok ? { post: await res.json() } : this.error(404, 'Not found');
export async function load({ fetch, page: { params } }) {
const res = await fetch(`/blog/${params.slug}.json`);
if (res.ok) {
return { props: { post: await res.json() }};
}
}
</script>

@ -1,9 +1,8 @@
import send from '@polka/send';
import get_posts from './_posts.js';
let json;
export function get(req, res) {
export function get() {
if (!json || process.env.NODE_ENV !== 'production') {
const posts = get_posts()
.filter(post => !post.metadata.draft)
@ -17,8 +16,11 @@ export function get(req, res) {
json = JSON.stringify(posts);
}
send(res, 200, json, {
'Content-Type': 'application/json',
'Cache-Control': `max-age=${5 * 60 * 1e3}` // 5 minutes
});
return {
body: json,
headers: {
'Content-Type': 'application/json',
'Cache-Control': `max-age=${5 * 60 * 1e3}` // 5 minutes
}
}
}

@ -1,7 +1,11 @@
<script context="module">
export async function preload() {
const posts = await this.fetch(`blog.json`).then(r => r.json());
return { posts };
export async function load({ fetch }) {
const posts = await fetch(`blog.json`).then(r => r.json());
return {
props: {
posts
}
};
}
</script>
@ -22,7 +26,7 @@
<div class='posts stretch'>
{#each posts as post}
<article class='post' data-pubdate={post.metadata.dateString}>
<a class="no-underline" rel='prefetch' href='blog/{post.slug}' title='Read the article »'>
<a class="no-underline" sveltekit:prefetch href='blog/{post.slug}' title='Read the article »'>
<h2>{post.metadata.title}</h2>
<p>{post.metadata.description}</p>
</a>

@ -1,4 +1,3 @@
import send from '@polka/send';
import get_posts from '../blog/_posts.js';
const months = ',Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec'.split(',');
@ -46,9 +45,12 @@ const rss = `
</rss>
`.replace(/>[^\S]+/gm, '>').replace(/[^\S]+</gm, '<').trim();
export function get(req, res) {
send(res, 200, rss, {
'Cache-Control': `max-age=${30 * 60 * 1e3}`,
'Content-Type': 'application/rss+xml'
});
export function get() {
return {
body: rss,
headers: {
'Cache-Control': `max-age=${30 * 60 * 1e3}`,
'Content-Type': 'application/rss+xml'
}
};
}

@ -1,4 +1,6 @@
export function get(req, res) {
res.writeHead(302, { Location: 'https://discord.gg/yy75DKs' });
res.end();
export function get() {
return {
status: 302,
headers: { Location: 'https://discord.gg/yy75DKs' },
};
}

@ -1,12 +1,13 @@
import send from '@polka/send';
import get_sections from './_sections.js';
let json;
export function get(req, res) {
export function get() {
if (!json || process.env.NODE_ENV !== 'production') {
json = get_sections();
}
send(res, 200, json);
return {
body: json
};
}

@ -6,8 +6,8 @@
'4_Prefix_stores_with_$_to_access_their_values': 'accessing stores ($)'
};
export async function preload() {
const sections = await this.fetch(`docs.json`).then(r => r.json());
export async function load({ fetch }) {
const sections = await fetch(`docs.json`).then(r => r.json());
for (const section of sections) {
for (const subsection of section.subsections) {
const { slug } = subsection;
@ -18,7 +18,11 @@
}
}
return { sections };
return {
props: {
sections
}
};
}
</script>

@ -1,10 +1,9 @@
import send from '@polka/send';
import { get_example } from './_examples.js';
const cache = new Map();
export function get(req, res) {
const { slug } = req.params;
export function get({ params }) {
const { slug } = params;
let example = cache.get(slug);
@ -14,10 +13,8 @@ export function get(req, res) {
}
if (example) {
send(res, 200, example);
} else {
send(res, 404, {
error: 'not found'
});
return {
body: example
};
}
}

@ -1,18 +1,22 @@
import send from '@polka/send';
import { get_examples } from './_examples.js';
let cached;
export function get(req, res) {
try {
if (!cached || process.env.NODE_ENV !== 'production') {
cached = get_examples().filter(section => section.title);
}
export function get() {
if (!cached || process.env.NODE_ENV !== 'production') {
cached = get_examples().filter(section => section.title);
}
send(res, 200, cached);
} catch (e) {
send(res, e.status || 500, {
message: e.message
});
try {
return {
body: cached
};
} catch(err) {
return {
status: e.status || 500,
body: {
message: e.message
}
};
}
}

@ -1,7 +1,7 @@
<!-- FIXME sometimes it adds a trailing slash when landing -->
<script context="module">
export async function preload() {
const sections = await this.fetch(`examples.json`).then(r => r.json());
export async function load({ fetch }) {
const sections = await fetch(`examples.json`).then(r => r.json());
const title_by_slug = sections.reduce((acc, {examples}) => {
examples.forEach(({slug, title}) => {
acc[slug] = title;
@ -10,13 +10,18 @@
return acc;
}, {});
return {sections, title_by_slug};
return {
props: {
sections,
title_by_slug
}
};
}
</script>
<script>
import { onMount } from 'svelte';
import { goto } from '@sapper/app';
import { goto } from '$app/navigation';
import Repl from '@sveltejs/svelte-repl';
import ScreenToggle from '../../components/ScreenToggle.svelte';

@ -1,9 +1,8 @@
import send from '@polka/send';
import get_faqs from './_faqs.js';
let json;
export function get(req, res) {
export function get() {
if (!json || process.env.NODE_ENV !== 'production') {
const faqs = get_faqs()
.map(faq => {
@ -17,8 +16,11 @@ export function get(req, res) {
json = JSON.stringify(faqs);
}
send(res, 200, json, {
'Content-Type': 'application/json',
'Cache-Control': `max-age=${5 * 60 * 1e3}` // 5 minutes
});
return {
body: json,
headers: {
'Content-Type': 'application/json',
'Cache-Control': `max-age=${5 * 60 * 1e3}` // 5 minutes
}
};
}

@ -1,7 +1,11 @@
<script context="module">
export async function preload() {
const faqs = await this.fetch(`faq.json`).then(r => r.json());
return { faqs };
export async function load({ fetch }) {
const faqs = await fetch(`faq.json`).then(r => r.json());
return {
props: {
faqs
}
};
}
</script>
@ -26,7 +30,7 @@
<article class='faq'>
<h2>
<span id={faq.fragment} class="offset-anchor"></span>
<a class="anchor" rel='prefetch' href='faq#{faq.fragment}' title='{faq.question}'>&nbsp;</a>
<a class="anchor" href='faq#{faq.fragment}' title='{faq.question}'>&nbsp;</a>
{faq.metadata.question}
</h2>
<p>{@html faq.answer}</p>

@ -1,6 +1,7 @@
<script>
import { Blurb, Hero, Section } from '@sveltejs/site-kit';
import Contributors from './_components/Contributors.svelte';
import Donors from './_components/Donors.svelte';
import Example from './_components/Example.svelte';
import WhosUsingSvelte from './_components/WhosUsingSvelte.svelte';
// import Lazy from '../components/Lazy.svelte';
@ -69,7 +70,7 @@
<p>Instead of using techniques like virtual DOM diffing, Svelte writes code that surgically updates the DOM when the state of your app changes.</p>
<p><a href="blog/svelte-3-rethinking-reactivity">Read the introductory blog post</a> to learn more.</p>
<p>We're proud that Svelte was recently voted the <a href="https://insights.stackoverflow.com/survey/2021#section-most-loved-dreaded-and-wanted-web-frameworks">most loved web framework</a> with the <a href="https://2020.stateofjs.com/en-US/technologies/front-end-frameworks/">most satisfied developers</a> in a pair of industry surveys. We think you'll love it too. <a href="blog/svelte-3-rethinking-reactivity">Read the introductory blog post</a> to learn more.</p>
</div>
<div style="grid-area: start; display: flex; flex-direction: column; min-width: 0" slot="how">
@ -86,7 +87,7 @@ npm run dev
<p style="flex: 1">See the <a href="blog/the-easiest-way-to-get-started">quickstart guide</a> for more information.</p>
<p class="cta"><a rel="prefetch" href="tutorial">Learn Svelte</a></p>
<p class="cta"><a sveltekit:prefetch href="tutorial">Learn Svelte</a></p>
</div>
</Blurb>
@ -115,9 +116,17 @@ npm run dev
</Section>
<Section>
<h3>Contributors</h3>
<h3>Supporters</h3>
<p>Svelte is free and open source software, made possible by the work of hundreds of volunteers and donors. <a href="https://github.com/sveltejs/svelte">Join us</a> or <a href="https://opencollective.com/svelte">give</a>!</p>
<p>Svelte is free and open source software, made possible by the work of dozens of volunteers. <a href="https://github.com/sveltejs/svelte">Join us!</a></p>
<h3>Contributors</h3>
<Contributors/>
<p></p>
<h3>Donors</h3>
<Donors/>
</Section>

@ -1,7 +1,6 @@
<script>
import { getContext } from 'svelte';
import { stores } from '@sapper/app';
const { session } = stores();
import { session } from '$app/stores';
const { logout } = getContext('app');

@ -1,6 +1,6 @@
<script>
import { createEventDispatcher, getContext } from 'svelte';
import { stores } from '@sapper/app';
import { session } from '$app/stores';
import UserMenu from './UserMenu.svelte';
import { Icon } from '@sveltejs/site-kit';
import * as doNotZip from 'do-not-zip';
@ -9,7 +9,6 @@
import { isMac } from '../../../../../utils/compat.js';
const dispatch = createEventDispatcher();
const { session } = stores();
const { login } = getContext('app');
export let repl;
@ -44,6 +43,9 @@
const r = await fetch(`repl/create.json`, {
method: 'POST',
credentials: 'include',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
name,
files: components.map(component => ({
@ -99,6 +101,9 @@
const r = await fetch(`repl/${gist.uid}.json`, {
method: 'PATCH',
credentials: 'include',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
name,
files: components.map(component => ({

@ -1,127 +1,146 @@
import send from '@polka/send';
import body from '../_utils/body.js';
import * as httpie from 'httpie';
import { query, find } from '../../../utils/db';
import { get_example } from '../../examples/_examples.js';
const { GITHUB_CLIENT_ID, GITHUB_CLIENT_SECRET } = process.env;
const GITHUB_CLIENT_ID = process.env['GITHUB_CLIENT_ID'];
const GITHUB_CLIENT_SECRET = process.env['GITHUB_CLIENT_SECRET'];
async function import_gist(req, res) {
const base = `https://api.github.com/gists/${req.params.id}`;
const url = `${base}?client_id=${GITHUB_CLIENT_ID}&client_secret=${GITHUB_CLIENT_SECRET}`;
try {
const { data } = await httpie.get(url, {
headers: {
'User-Agent': 'https://svelte.dev'
}
});
// create owner if necessary...
let user = await find(`select * from users where uid = $1`, [data.owner.id]);
if (!user) {
const { id, name, login, avatar_url } = data.owner;
user = await find(`
insert into users(uid, name, username, avatar)
values ($1, $2, $3, $4)
returning *
`, [id, name, login, avatar_url]);
}
delete data.files['README.md'];
delete data.files['meta.json'];
const files = Object.keys(data.files).map(key => {
const name = key.replace(/\.html$/, '.svelte');
return {
name,
source: data.files[key].content
};
});
// add gist to database...
await query(`
insert into gists(uid, user_id, name, files)
values ($1, $2, $3, $4)
`, [req.params.id, user.id, data.description, JSON.stringify(files)]);
send(res, 200, {
uid: req.params.id,
name: data.description,
files,
owner: data.owner.id
});
} catch (err) {
send(res, err.statusCode, { error: err.message });
}
}
export async function get(req, res) {
export async function get({ path, params }) {
// is this an example?
const example = get_example(req.params.id);
const example = get_example(params.id);
if (example) {
return send(res, 200, {
relaxed: true,
uid: req.params.id,
name: example.title,
files: example.files,
owner: null
});
return {
body: {
relaxed: true,
uid: params.id,
name: example.title,
files: example.files,
owner: null
}
};
}
if (process.env.NODE_ENV === 'development') {
// In dev, proxy requests to load particular REPLs to the real server.
// This avoids needing to connect to the real database server.
req.pipe(
require('https').request({ host: 'svelte.dev', path: req.url })
).once('response', res_proxy => {
res_proxy.pipe(res);
res.writeHead(res_proxy.statusCode, res_proxy.headers);
}).once('error', () => res.end());
return;
try {
const res_proxy = await httpie.get(`https://svelte.dev${path}`);
return {
body: res_proxy.data,
status: res_proxy.statusCode,
headers: res_proxy.headers
};
} catch (err) {
return {
status: err.statusCode,
body: { error: err.message }
};
}
}
const [row] = await query(`
select g.*, u.uid as owner from gists g
left join users u on g.user_id = u.id
where g.uid = $1 limit 1
`, [req.params.id]); // via filename pattern
`, [params.id]); // via filename pattern
if (!row) {
return import_gist(req, res);
const base = `https://api.github.com/gists/${params.id}`;
const url = `${base}?client_id=${GITHUB_CLIENT_ID}&client_secret=${GITHUB_CLIENT_SECRET}`;
try {
const { data } = await httpie.get(url, {
headers: {
'User-Agent': 'https://svelte.dev'
}
});
// create owner if necessary...
let user = await find(`select * from users where uid = $1`, [data.owner.id]);
if (!user) {
const { id, name, login, avatar_url } = data.owner;
user = await find(`
insert into users(uid, name, username, avatar)
values ($1, $2, $3, $4)
returning *
`, [id, name, login, avatar_url]);
}
delete data.files['README.md'];
delete data.files['meta.json'];
const files = Object.keys(data.files).map(key => {
const name = key.replace(/\.html$/, '.svelte');
return {
name,
source: data.files[key].content
};
});
// add gist to database...
await query(`
insert into gists(uid, user_id, name, files)
values ($1, $2, $3, $4)
`, [params.id, user.id, data.description, JSON.stringify(files)]);
return {
body: {
uid: params.id,
name: data.description,
files,
owner: data.owner.id
}
};
} catch (err) {
return {
status: err.statusCode,
body: { error: err.message }
};
}
}
send(res, 200, {
uid: row.uid.replace(/-/g, ''),
name: row.name,
files: row.files,
owner: row.owner
});
return {
body: {
uid: row.uid.replace(/-/g, ''),
name: row.name,
files: row.files,
owner: row.owner
}
};
}
export async function patch(req, res) {
const { user } = req;
export async function patch({ params, locals, body }) {
const { user } = locals;
if (!user) return;
let id;
const uid = req.params.id;
const uid = params.id;
try {
const [row] = await query(`select * from gists where uid = $1 limit 1`, [uid]);
if (!row) return send(res, 404, { error: 'Gist not found' });
if (row.user_id !== user.id) return send(res, 403, { error: 'Item does not belong to you' });
if (!row) {
return {
status: 404,
body: {
error: 'Gist not found'
}
};
}
if (row.user_id !== user.id) {
return { status: 403, body: { error: 'Item does not belong to you' }};
}
id = row.id;
} catch (err) {
console.error('PATCH /gists @ select', err);
return send(res, 500);
return { status: 500 };
}
try {
const obj = await body(req);
const obj = body;
obj.updated_at = 'now()';
let k;
const cols = [];
@ -136,14 +155,19 @@ export async function patch(req, res) {
const [row] = await query(`update gists ${set} where id = ${id} returning *`, vals);
send(res, 200, {
uid: row.uid.replace(/-/g, ''),
name: row.name,
files: row.files,
owner: user.uid,
});
return {
body: {
uid: row.uid.replace(/-/g, ''),
name: row.name,
files: row.files,
owner: user.uid,
}
};
} catch (err) {
console.error('PATCH /gists @ update', err);
send(res, 500, { error: err.message });
return {
status: 500,
body: { error: err.message }
};
}
}

@ -1,8 +1,10 @@
<script context="module">
export function preload({ params, query }) {
export function load({ page: { params, query }}) {
return {
version: query.version || '3',
id: params.id
props: {
version: query.get('version') || '3',
id: params.id
}
};
}
</script>
@ -10,21 +12,22 @@
<script>
import Repl from '@sveltejs/svelte-repl';
import { onMount } from 'svelte';
import { goto, stores } from '@sapper/app';
import { browser } from '$app/env';
import { goto } from '$app/navigation';
import { session } from '$app/stores';
import { mapbox_setup } from '../../../config';
import InputOutputToggle from '../../../components/Repl/InputOutputToggle.svelte';
import AppControls from './_components/AppControls/index.svelte';
export let version;
export let id;
const { session } = stores();
let repl;
let gist;
let name = 'Loading...';
let zen_mode = false;
let is_relaxed_gist = false;
let width = process.browser ? window.innerWidth : 1000;
let width = browser ? window.innerWidth : 1000;
let checked = false;
function update_query_string(version) {
@ -33,8 +36,8 @@
if (version !== 'latest') params.push(`version=${version}`);
const url = params.length > 0
? `repl/${id}?${params.join('&')}`
: `repl/${id}`;
? `/repl/${id}?${params.join('&')}`
: `/repl/${id}`;
history.replaceState({}, 'x', url);
}
@ -48,7 +51,7 @@
}
// TODO handle `relaxed` logic
fetch(`repl/${id}.json`).then(r => {
fetch(`/repl/${id}.json`).then(r => {
if (r.ok) {
r.json().then(data => {
gist = data;
@ -82,7 +85,7 @@
});
}
$: if (process.browser) fetch_gist(id);
$: if (browser) fetch_gist(id);
onMount(() => {
if (version !== 'local') {
@ -100,15 +103,12 @@
goto(`/repl/${gist.uid}?version=${version}`);
}
$: svelteUrl = process.browser && version === 'local' ?
$: svelteUrl = browser && version === 'local' ?
`${location.origin}/repl/local` :
`https://unpkg.com/svelte@${version}`;
const rollupUrl = `https://unpkg.com/rollup@1/dist/rollup.browser.js`;
// needed for context API example
const mapbox_setup = `window.MAPBOX_ACCESS_TOKEN = process.env.MAPBOX_ACCESS_TOKEN;`;
$: mobile = width < 540;
$: relaxed = is_relaxed_gist || ($session.user && gist && $session.user.uid === gist.owner);
@ -192,7 +192,7 @@
on:forked={handle_fork}
/>
{#if process.browser}
{#if browser}
<div class="viewport" class:offset={checked}>
<Repl
bind:this={repl}

@ -1,27 +1,31 @@
import send from '@polka/send';
import body from './_utils/body.js';
import { query } from '../../utils/db';
export async function post(req, res) {
const { user } = req;
export async function post({ locals, body }) {
const { user } = locals;
if (!user) return; // response already sent
try {
const { name, files } = await body(req);
const { name, files } = body;
const [row] = await query(`
insert into gists(user_id, name, files)
values ($1, $2, $3) returning *`, [user.id, name, JSON.stringify(files)]);
send(res, 201, {
uid: row.uid.replace(/-/g, ''),
name: row.name,
files: row.files,
owner: user.uid,
});
return {
status: 201,
body: {
uid: row.uid.replace(/-/g, ''),
name: row.name,
files: row.files,
owner: user.uid,
}
};
} catch (err) {
send(res, 500, {
error: err.message
});
return {
status: 500,
body: {
error: err.message
}
};
}
}

@ -1,17 +1,20 @@
<script context="module">
export function preload({ query }) {
export function load({ page: { query }}) {
return {
version: query.version,
gist: query.gist,
example: query.example
props: {
version: query.get('version') || '3',
gist: query.get('gist'),
example: query.get('example')
}
};
}
</script>
<script>
import { browser } from '$app/env';
import ReplWidget from '../../components/Repl/ReplWidget.svelte';
export let version = '3';
export let version;
export let gist;
export let example;
</script>
@ -41,7 +44,7 @@
</svelte:head>
<div class="repl-outer">
{#if process.browser}
{#if browser}
<ReplWidget {version} {gist} {example} embedded={true}/>
{/if}
</div>

@ -1,16 +1,22 @@
<script context="module">
export function preload({ query }) {
export function load({ page: { query }}) {
const { gist, example, version } = query;
// redirect to v2 REPL if appropriate
if (/^[^>]?[12]/.test(version)) {
const q = Object.keys(query).map(key => `${key}=${query[key]}`).join('&');
return this.redirect(302, `https://v2.svelte.dev/repl?${q}`);
return {
status: 302,
redirect: `https://v2.svelte.dev/repl?${q}`
};
}
const id = gist || example || 'hello-world';
const q = version ? `?version=${version}` : ``;
this.redirect(301, `repl/${id}${q}`);
return {
status: 301,
redirect: `/repl/${id}${q}`
};
}
</script>

@ -1,17 +0,0 @@
import { createReadStream } from 'fs';
export function get(req, res) {
const path = req.params.file.join('/');
if (process.env.NODE_ENV !== 'development' || ('/' + path).includes('/.')) {
res.writeHead(403);
res.end();
return;
}
createReadStream('../' + path)
.on('error', () => {
res.writeHead(403);
res.end();
})
.pipe(res);
res.writeHead(200, { 'Content-Type': 'text/javascript' });
}

@ -0,0 +1,11 @@
import { readFileSync } from 'fs';
export function get({ params: { path } }) {
if (process.env.NODE_ENV !== 'development' || ('/' + path).includes('/.')) {
return { status: 403 };
}
return {
headers: { 'Content-Type': 'text/javascript' },
body: readFileSync('../' + path)
};
}

@ -1,5 +1,5 @@
<script>
import { goto } from '@sapper/app';
import { goto } from '$app/navigation';
import { Icon } from '@sveltejs/site-kit';
export let sections;
@ -64,7 +64,7 @@
</style>
<nav>
<a rel="prefetch" aria-label="Previous tutorial step" class="no-underline" href="tutorial/{(selected.prev || selected).slug}" class:disabled={!selected.prev}>
<a sveltekit:prefetch aria-label="Previous tutorial step" class="no-underline" href="tutorial/{(selected.prev || selected).slug}" class:disabled={!selected.prev}>
<Icon name="arrow-left" />
</a>
@ -88,7 +88,7 @@
</select>
</div>
<a rel="prefetch" aria-label="Next tutorial step" class="no-underline" href="tutorial/{(selected.next || selected).slug}" class:disabled={!selected.next}>
<a sveltekit:prefetch aria-label="Next tutorial step" class="no-underline" href="tutorial/{(selected.next || selected).slug}" class:disabled={!selected.next}>
<Icon name="arrow-right" />
</a>
</nav>

@ -1,7 +1,6 @@
import * as fs from 'fs';
import * as path from 'path';
import marked from 'marked';
import send from '@polka/send';
import { extract_frontmatter, extract_metadata, link_renderer } from '@sveltejs/site-kit/utils/markdown';
import { highlight } from '../../../utils/highlight';
@ -85,8 +84,8 @@ function get_tutorial(slug) {
};
}
export function get(req, res) {
const { slug } = req.params;
export function get({ params }) {
const { slug } = params;
let tut = cache.get(slug);
if (!tut || process.env.NODE_ENV !== 'production') {
@ -95,8 +94,8 @@ export function get(req, res) {
}
if (tut) {
send(res, 200, tut);
} else {
send(res, 404, { message: 'not found' });
return {
body: tut
};
}
}

@ -1,14 +1,19 @@
<script context="module">
export async function preload({ params }) {
const res = await this.fetch(`tutorial/${params.slug}.json`);
export async function load({ fetch, page: { params }}) {
const res = await fetch(`/tutorial/${params.slug}.json`);
if (!res.ok) {
return this.redirect(301, `tutorial/basics`);
return {
status: 301,
redirect: '/tutorial/basics'
}
}
return {
slug: params.slug,
chapter: await res.json()
props: {
slug: params.slug,
chapter: await res.json()
}
};
}
</script>
@ -16,6 +21,7 @@
<script>
import Repl from '@sveltejs/svelte-repl';
import { getContext } from 'svelte';
import { browser } from '$app/env';
import ScreenToggle from '../../../components/ScreenToggle.svelte';
import TableOfContents from './_TableOfContents.svelte';
@ -36,7 +42,7 @@
let scrollable;
const lookup = new Map();
let width = process.browser ? window.innerWidth : 1000;
let width = browser ? window.innerWidth : 1000;
let offset = 0;
sections.forEach(section => {
@ -50,7 +56,7 @@
lookup.set(chapter.slug, obj);
if (process.browser) { // pending https://github.com/sveltejs/svelte/issues/2135
if (browser) { // pending https://github.com/sveltejs/svelte/issues/2135
if (prev) prev.next = obj;
prev = obj;
}

@ -0,0 +1,19 @@
<script context="module">
export async function load({ fetch }) {
const sections = await fetch(`/tutorial.json`).then(r => r.json());
return {
props: {
sections
}
};
}
</script>
<script>
import { setContext } from 'svelte';
export let sections;
setContext('tutorial', { sections });
</script>
<slot></slot>

@ -1,15 +0,0 @@
<script context="module">
export async function preload() {
const sections = await this.fetch(`tutorial.json`).then(r => r.json());
return { sections };
}
</script>
<script>
import { setContext } from 'svelte';
export let sections;
setContext('tutorial', { sections });
</script>
<slot></slot>

@ -1,5 +1,4 @@
import * as fs from 'fs';
import send from '@polka/send';
import { extract_frontmatter } from '@sveltejs/site-kit/utils/markdown';
let json;
@ -48,16 +47,21 @@ function get_sections() {
return sections;
}
export function get(req, res) {
export function get() {
try {
if (!json || process.env.NODE_ENV !== 'production') {
json = get_sections();
}
send(res, 200, json);
return {
body: json
};
} catch (err) {
send(res, 500, {
message: err.message
});
return {
status: 500,
body: {
message: err.message
}
};
}
}

@ -1,5 +1,8 @@
<script context="module">
export function preload() {
this.redirect(301, 'tutorial/basics');
export function load() {
return {
status: 301,
redirect: '/tutorial/basics'
};
}
</script>

@ -1,20 +1,23 @@
export function get(req, res) {
export async function get(req) {
let { min = '0', max = '100' } = req.query;
min = +min;
max = +max;
res.setHeader('Access-Control-Allow-Origin', '*');
// simulate a long delay
setTimeout(() => {
// fail sometimes
if (Math.random() < 0.333) {
res.statusCode = 400;
res.end(`Failed to generate random number. Please try again`);
return;
}
await new Promise((res) => setTimeout(res, 1000));
// fail sometimes
if (Math.random() < 0.333) {
return {
status: 400,
headers: { 'Access-Control-Allow-Origin': '*' },
body: `Failed to generate random number. Please try again`
};
}
const num = min + Math.round(Math.random() * (max - min));
res.end(String(num));
}, 1000);
const num = min + Math.round(Math.random() * (max - min));
return {
headers: { 'Access-Control-Allow-Origin': '*' },
body: String(num)
};
}

@ -1,43 +0,0 @@
import polka from 'polka';
import send from '@polka/send';
import sirv from 'sirv';
import * as sapper from '@sapper/server';
import { sanitize_user, authenticate } from './utils/auth';
if (!process.env.PORT) {
process.env.PORT = 3000;
}
const { PORT } = process.env;
const app = polka({
onError: (err, req, res) => {
const error = err.message || err;
const code = err.code || err.status || 500;
res.headersSent || send(res, code, { error }, {
'content-type': 'text/plain'
});
}
});
if (process.env.PGHOST) {
app.use(authenticate());
}
app.use(
sirv('static', {
dev: process.env.NODE_ENV === 'development',
setHeaders(res) {
res.setHeader('Access-Control-Allow-Origin', '*');
res.hasHeader('Cache-Control') || res.setHeader('Cache-Control', 'max-age=600'); // 10min default
}
}),
sapper.middleware({
session: req => ({
user: sanitize_user(req.user)
})
})
);
app.listen(PORT);

@ -1,87 +0,0 @@
import { timestamp, files, shell } from '@sapper/service-worker';
const ASSETS = `cache${timestamp}`;
// `shell` is an array of all the files generated by Rollup,
// `files` is an array of everything in the `static` directory
const to_cache = shell.concat(files.filter(file => {
const basename = file.split('/').pop();
if (basename[0] === '.') return false; // .DS_Store and friends
if (basename.endsWith('.mp3')) return false; // TODO others?
return true;
}));
const cached = new Set(to_cache);
self.addEventListener('install', event => {
event.waitUntil(
caches
.open(ASSETS)
.then(cache => cache.addAll(to_cache))
.then(() => {
self.skipWaiting();
})
);
});
self.addEventListener('activate', event => {
event.waitUntil(
caches.keys().then(async keys => {
// delete old caches
for (const key of keys) {
if (key !== ASSETS) await caches.delete(key);
}
self.clients.claim();
})
);
});
self.addEventListener('fetch', event => {
if (event.request.method !== 'GET' || event.request.headers.has('range')) return;
const url = new URL(event.request.url);
// don't try to handle e.g. data: URIs
if (!url.protocol.startsWith('http')) return;
// ignore dev server requests
if (url.hostname === self.location.hostname && url.port !== self.location.port) return;
// always serve static files and Rollup-generated assets from cache
if (url.host === self.location.host && cached.has(url.pathname)) {
event.respondWith(caches.match(event.request));
return;
}
// for pages, you might want to serve a shell `index.html` file,
// which Sapper has generated for you. It's not right for every
// app, but if it's right for yours then uncomment this section
/*
if (url.origin === self.origin && routes.find(route => route.pattern.test(url.pathname))) {
event.respondWith(caches.match('/index.html'));
return;
}
*/
if (event.request.cache === 'only-if-cached') return;
// for everything else, try the network first, falling back to
// cache if the user is offline. (If the pages never change, you
// might prefer a cache-first approach to a network-first one.)
event.respondWith(
caches
.open(`offline${timestamp}`)
.then(async cache => {
try {
const response = await fetch(event.request);
cache.put(event.request, response.clone());
return response;
} catch (err) {
const response = await cache.match(event.request);
if (response) return response;
throw err;
}
})
);
});

@ -1,40 +0,0 @@
<!doctype html>
<html lang='en' class="theme-default typo-default">
<head>
<meta charset='utf-8'>
<meta name='viewport' content='width=device-width,initial-scale=1'>
<meta name='theme-color' content='#ff3e00'>
%sapper.base%
<link href=global.css rel=stylesheet>
<link href=prism.css rel=stylesheet>
<link rel='manifest' href='manifest.json'>
<link rel='icon' type='image/png' href='favicon.png'>
<meta name='twitter:card' content='summary_large_image'>
<meta name='twitter:site' content='@sveltejs'>
<meta name='twitter:creator' content='@sveltejs'>
<meta name='twitter:image' content='https://svelte.dev/images/twitter-card.png'>
<meta name='og:image' content='https://svelte.dev/images/twitter-card.png'>
<!-- Sapper generates a <style> tag containing critical CSS
for the current page. CSS for the rest of the app is
lazily loaded when it precaches secondary pages -->
%sapper.styles%
<!-- This contains the contents of the <svelte:head> component, if
the current page has one -->
%sapper.head%
</head>
<body>
<!-- The application will be rendered inside this element,
because `app/client.js` references it -->
<div id='sapper'>%sapper.html%</div>
<!-- Sapper creates a <script> tag containing `app/client.js`
and anything else it needs to hydrate the app and
initialise the router -->
%sapper.scripts%
</body>
</html>

@ -1,4 +1,3 @@
import * as cookie from 'cookie';
import flru from 'flru';
import { find, query } from './db';
@ -37,7 +36,7 @@ export const delete_session = async sid => {
session_cache.set(sid, null);
};
const get_user = async sid => {
export const get_user = async sid => {
if (!sid) return null;
if (!session_cache.has(sid)) {
@ -51,15 +50,3 @@ const get_user = async sid => {
return session_cache.get(sid);
};
export const authenticate = () => {
// this is a convenient time to clear out expired sessions
query(`delete from sessions where expiry < now()`);
return async (req, res, next) => {
req.cookies = cookie.parse(req.headers.cookie || '');
req.user = await get_user(req.cookies.sid);
next();
};
};

@ -1,7 +1,7 @@
import { Pool } from 'pg';
import pg from 'pg';
// Uses `PG*` ENV vars
export const DB = process.env.PGHOST ? new Pool() : null;
export const DB = process.env['PGHOST'] ? new pg.Pool() : null;
export function query(text, values=[]) {
return DB.query(text, values).then(r => r.rows);

@ -1,7 +1,7 @@
import { langs } from '@sveltejs/site-kit/utils/markdown.js';
import PrismJS from 'prismjs';
import 'prismjs/components/prism-bash';
import 'prismjs/components/prism-diff';
import 'prismjs/components/prism-bash.js';
import 'prismjs/components/prism-diff.js';
import 'prism-svelte';
export function highlight(source, lang) {

@ -1,5 +1,5 @@
import slugify from '@sindresorhus/slugify';
import {SLUG_SEPARATOR} from '../../config';
import {SLUG_SEPARATOR} from '../../config.js';
/* url-safe processor */

@ -0,0 +1,36 @@
import adapter from '@sveltejs/adapter-node';
/** @type {import('@sveltejs/kit').Config} */
export default {
kit: {
adapter: adapter(),
target: '#svelte',
prerender: {
enabled: false
},
vite: () => ({
optimizeDeps: {
include: [
'codemirror',
'codemirror/mode/javascript/javascript.js',
'codemirror/mode/handlebars/handlebars.js',
'codemirror/mode/htmlmixed/htmlmixed.js',
'codemirror/mode/xml/xml.js',
'codemirror/mode/css/css.js',
'codemirror/mode/markdown/markdown.js',
'codemirror/addon/edit/closebrackets.js',
'codemirror/addon/edit/closetag.js',
'codemirror/addon/edit/continuelist.js',
'codemirror/addon/comment/comment.js',
'codemirror/addon/fold/foldcode.js',
'codemirror/addon/fold/foldgutter.js',
'codemirror/addon/fold/brace-fold.js',
'codemirror/addon/fold/xml-fold.js',
'codemirror/addon/fold/indent-fold.js',
'codemirror/addon/fold/markdown-fold.js',
'codemirror/addon/fold/comment-fold.js'
]
}
})
}
};

@ -1,423 +1,430 @@
import {strict as assert} from 'assert';
import {urlsafeSlugProcessor, unicodeSafeProcessor} from '../../src/utils/slug';
import {SLUG_SEPARATOR as _} from '../../config';
import * as uvu from 'uvu';
import * as assert from 'uvu/assert';
import {urlsafeSlugProcessor, unicodeSafeProcessor} from '../../src/utils/slug.js';
import {SLUG_SEPARATOR as _} from '../../config.js';
describe('slug', () => {
describe('urlsafeSlugProcessor', () => {
describe('ascii', () => {
it('space separated words', () => {
assert.equal(
urlsafeSlugProcessor('Text expressions'),
`Text${_}expressions`
);
});
it('numbered text', () => {
assert.equal(
urlsafeSlugProcessor('1. export creates'),
`1${_}export${_}creates`
);
});
it('punctuated text', () => {
assert.equal(
urlsafeSlugProcessor('svelte.VERSION'),
`svelte${_}VERSION`
);
});
it('text starting with the dollar sign', () => {
assert.equal(
urlsafeSlugProcessor('$destroy method'),
`$destroy${_}method`
);
});
it('numbered text containing the dollar sign', () => {
assert.equal(
urlsafeSlugProcessor('1. export $destroy'),
`1${_}export${_}$destroy`
);
});
it('text containing the equal char', () => {
assert.equal(
urlsafeSlugProcessor('script context=module'),
`script${_}context${_}module`
);
});
it('text containing the colon char', () => {
assert.equal(
urlsafeSlugProcessor('svelte:body'),
`svelte${_}body`
);
});
it('text containing the slash char', () => {
assert.equal(
urlsafeSlugProcessor('svelte/motion'),
`svelte${_}motion`
);
});
it('text containing the comma char', () => {
assert.equal(
urlsafeSlugProcessor('svelte, motion'),
`svelte${_}motion`
);
});
});
describe('unicode', () => {
it('should translate symbols to English', () => {
assert.equal(
urlsafeSlugProcessor('Ich ♥ Deutsch'),
`Ich${_}love${_}Deutsch`
);
});
it('should remove emoji', () => {
assert.equal(
urlsafeSlugProcessor('Ich 😍 Deutsch'),
`Ich${_}Deutsch`
);
});
});
describe('cyricllic', () => {
it('space separated words', () => {
assert.equal(
urlsafeSlugProcessor('Всплытие и перехват событий'),
`Vsplytie${_}i${_}perehvat${_}sobytij`
);
});
it('numbered text', () => {
assert.equal(
urlsafeSlugProcessor('1 Всплытие и перехват событий'),
`1${_}Vsplytie${_}i${_}perehvat${_}sobytij`
);
});
it('punctuated text', () => {
assert.equal(
urlsafeSlugProcessor('.Всплытие.и.перехват событий'),
`Vsplytie${_}i${_}perehvat${_}sobytij`
);
});
it('text starting with the dollar sign', () => {
assert.equal(
urlsafeSlugProcessor('$Всплытие $ перехват событий'),
`$Vsplytie${_}$${_}perehvat${_}sobytij`
);
});
it('text containing the dollar sign', () => {
assert.equal(
urlsafeSlugProcessor('Всплытие$перехват'),
`Vsplytie$perehvat`
);
});
it('text containing the equal char', () => {
assert.equal(
urlsafeSlugProcessor('Всплытие = перехват=событий'),
`Vsplytie${_}perehvat${_}sobytij`
);
});
it('text containing the colon char', () => {
assert.equal(
urlsafeSlugProcessor('Всплытие : перехват:событий'),
`Vsplytie${_}perehvat${_}sobytij`
);
});
it('text containing the slash char', () => {
assert.equal(
urlsafeSlugProcessor('Всплытие / перехват/событий'),
`Vsplytie${_}perehvat${_}sobytij`
);
});
it('text containing the comma char', () => {
assert.equal(
urlsafeSlugProcessor('Всплытие, перехват'),
`Vsplytie${_}perehvat`
);
});
});
describe('ascii + cyricllic', () => {
it('space separated words', () => {
assert.equal(
urlsafeSlugProcessor('Всплытие и export перехват событий'),
`Vsplytie${_}i${_}export${_}perehvat${_}sobytij`
);
});
it('ascii word concatenated to a cyricllic word', () => {
assert.equal(
urlsafeSlugProcessor('exportВсплытие'),
'exportVsplytie'
);
});
it('cyricllic word concatenated to an ascii word', () => {
assert.equal(
urlsafeSlugProcessor('Всплытиеexport'),
`Vsplytieexport`
);
});
it('numbered text', () => {
assert.equal(
urlsafeSlugProcessor('1 export Всплытие и перехват событий'),
`1${_}export${_}Vsplytie${_}i${_}perehvat${_}sobytij`
);
});
it('punctuated text', () => {
assert.equal(
urlsafeSlugProcessor('.Всплытие.export.и.перехват событий'),
`Vsplytie${_}export${_}i${_}perehvat${_}sobytij`
);
});
it('text starting with the dollar sign, followed by ascii char', () => {
assert.equal(
urlsafeSlugProcessor('$exportВсплытие перехват событий'),
`$exportVsplytie${_}perehvat${_}sobytij`
);
});
it('text starting with the dollar sign, followed by unicode char', () => {
assert.equal(
urlsafeSlugProcessor('$Всплытие export перехват событий'),
`$Vsplytie${_}export${_}perehvat${_}sobytij`
);
});
it('text containing the dollar sign, followed by ascii char', () => {
assert.equal(
urlsafeSlugProcessor('export $destroy a component prop Всплытие и перехват событий'),
`export${_}$destroy${_}a${_}component${_}prop${_}Vsplytie${_}i${_}perehvat${_}sobytij`
);
});
it('text containing the dollar sign, followed by unicode char', () => {
assert.equal(
urlsafeSlugProcessor('Всплытие export $Всплытие a component prop Всплытие и перехват событий'),
`Vsplytie${_}export${_}$Vsplytie${_}a${_}component${_}prop${_}Vsplytie${_}i${_}perehvat${_}sobytij`
);
});
it('text containing the equal char', () => {
assert.equal(
urlsafeSlugProcessor('script context=module Всплытие=и перехват событий'),
`script${_}context${_}module${_}Vsplytie${_}i${_}perehvat${_}sobytij`
);
});
it('text containing the colon char', () => {
assert.equal(
urlsafeSlugProcessor('svelte:body Всплытие и:перехват событий'),
`svelte${_}body${_}Vsplytie${_}i${_}perehvat${_}sobytij`
);
});
it('text containing the slash char', () => {
assert.equal(
urlsafeSlugProcessor('svelte/motion Всплытие и / перехват/событий'),
`svelte${_}motion${_}Vsplytie${_}i${_}perehvat${_}sobytij`
);
});
it('text containing the comma char', () => {
assert.equal(
urlsafeSlugProcessor('Всплытие, export'),
`Vsplytie${_}export`
);
});
});
function run(name, func) {
const suite = uvu.suite(name);
func(suite);
suite.run();
}
run('urlsafeSlugProcessor -> ascii', it => {
it('space separated words', () => {
assert.equal(
urlsafeSlugProcessor('Text expressions'),
`Text${_}expressions`
);
});
it('numbered text', () => {
assert.equal(
urlsafeSlugProcessor('1. export creates'),
`1${_}export${_}creates`
);
});
it('punctuated text', () => {
assert.equal(
urlsafeSlugProcessor('svelte.VERSION'),
`svelte${_}VERSION`
);
});
it('text starting with the dollar sign', () => {
assert.equal(
urlsafeSlugProcessor('$destroy method'),
`$destroy${_}method`
);
});
it('numbered text containing the dollar sign', () => {
assert.equal(
urlsafeSlugProcessor('1. export $destroy'),
`1${_}export${_}$destroy`
);
});
it('text containing the equal char', () => {
assert.equal(
urlsafeSlugProcessor('script context=module'),
`script${_}context${_}module`
);
});
it('text containing the colon char', () => {
assert.equal(
urlsafeSlugProcessor('svelte:body'),
`svelte${_}body`
);
});
it('text containing the slash char', () => {
assert.equal(
urlsafeSlugProcessor('svelte/motion'),
`svelte${_}motion`
);
});
it('text containing the comma char', () => {
assert.equal(
urlsafeSlugProcessor('svelte, motion'),
`svelte${_}motion`
);
});
});
run('urlsafeSlugProcessor - unicode', it => {
it('should translate symbols to English', () => {
assert.equal(
urlsafeSlugProcessor('Ich ♥ Deutsch'),
`Ich${_}love${_}Deutsch`
);
});
it('should remove emoji', () => {
assert.equal(
urlsafeSlugProcessor('Ich 😍 Deutsch'),
`Ich${_}Deutsch`
);
});
});
run('urlsafeSlugProcessor -> cyrillic', it => {
it('space separated words', () => {
assert.equal(
urlsafeSlugProcessor('Всплытие и перехват событий'),
`Vsplytie${_}i${_}perehvat${_}sobytij`
);
});
it('numbered text', () => {
assert.equal(
urlsafeSlugProcessor('1 Всплытие и перехват событий'),
`1${_}Vsplytie${_}i${_}perehvat${_}sobytij`
);
});
it('punctuated text', () => {
assert.equal(
urlsafeSlugProcessor('.Всплытие.и.перехват событий'),
`Vsplytie${_}i${_}perehvat${_}sobytij`
);
});
it('text starting with the dollar sign', () => {
assert.equal(
urlsafeSlugProcessor('$Всплытие $ перехват событий'),
`$Vsplytie${_}$${_}perehvat${_}sobytij`
);
});
it('text containing the dollar sign', () => {
assert.equal(
urlsafeSlugProcessor('Всплытие$перехват'),
`Vsplytie$perehvat`
);
});
it('text containing the equal char', () => {
assert.equal(
urlsafeSlugProcessor('Всплытие = перехват=событий'),
`Vsplytie${_}perehvat${_}sobytij`
);
});
it('text containing the colon char', () => {
assert.equal(
urlsafeSlugProcessor('Всплытие : перехват:событий'),
`Vsplytie${_}perehvat${_}sobytij`
);
});
it('text containing the slash char', () => {
assert.equal(
urlsafeSlugProcessor('Всплытие / перехват/событий'),
`Vsplytie${_}perehvat${_}sobytij`
);
});
it('text containing the comma char', () => {
assert.equal(
urlsafeSlugProcessor('Всплытие, перехват'),
`Vsplytie${_}perehvat`
);
});
});
run('urlsafeSlugProcessor -> ascii + cyrillic', it => {
it('space separated words', () => {
assert.equal(
urlsafeSlugProcessor('Всплытие и export перехват событий'),
`Vsplytie${_}i${_}export${_}perehvat${_}sobytij`
);
});
it('ascii word concatenated to a cyrillic word', () => {
assert.equal(
urlsafeSlugProcessor('exportВсплытие'),
'exportVsplytie'
);
});
it('cyrillic word concatenated to an ascii word', () => {
assert.equal(
urlsafeSlugProcessor('Всплытиеexport'),
`Vsplytieexport`
);
});
it('numbered text', () => {
assert.equal(
urlsafeSlugProcessor('1 export Всплытие и перехват событий'),
`1${_}export${_}Vsplytie${_}i${_}perehvat${_}sobytij`
);
});
it('punctuated text', () => {
assert.equal(
urlsafeSlugProcessor('.Всплытие.export.и.перехват событий'),
`Vsplytie${_}export${_}i${_}perehvat${_}sobytij`
);
});
it('text starting with the dollar sign, followed by ascii char', () => {
assert.equal(
urlsafeSlugProcessor('$exportВсплытие перехват событий'),
`$exportVsplytie${_}perehvat${_}sobytij`
);
});
it('text starting with the dollar sign, followed by unicode char', () => {
assert.equal(
urlsafeSlugProcessor('$Всплытие export перехват событий'),
`$Vsplytie${_}export${_}perehvat${_}sobytij`
);
});
it('text containing the dollar sign, followed by ascii char', () => {
assert.equal(
urlsafeSlugProcessor('export $destroy a component prop Всплытие и перехват событий'),
`export${_}$destroy${_}a${_}component${_}prop${_}Vsplytie${_}i${_}perehvat${_}sobytij`
);
});
it('text containing the dollar sign, followed by unicode char', () => {
assert.equal(
urlsafeSlugProcessor('Всплытие export $Всплытие a component prop Всплытие и перехват событий'),
`Vsplytie${_}export${_}$Vsplytie${_}a${_}component${_}prop${_}Vsplytie${_}i${_}perehvat${_}sobytij`
);
});
it('text containing the equal char', () => {
assert.equal(
urlsafeSlugProcessor('script context=module Всплытие=и перехват событий'),
`script${_}context${_}module${_}Vsplytie${_}i${_}perehvat${_}sobytij`
);
});
it('text containing the colon char', () => {
assert.equal(
urlsafeSlugProcessor('svelte:body Всплытие и:перехват событий'),
`svelte${_}body${_}Vsplytie${_}i${_}perehvat${_}sobytij`
);
});
it('text containing the slash char', () => {
assert.equal(
urlsafeSlugProcessor('svelte/motion Всплытие и / перехват/событий'),
`svelte${_}motion${_}Vsplytie${_}i${_}perehvat${_}sobytij`
);
});
it('text containing the comma char', () => {
assert.equal(
urlsafeSlugProcessor('Всплытие, export'),
`Vsplytie${_}export`
);
});
});
run('unicodeSafeProcessor (preserve unicode) -> ascii', it => {
it('space separated words', () => {
assert.equal(
unicodeSafeProcessor('Text expressions'),
`Text${_}expressions`
);
});
it('numbered text', () => {
assert.equal(
unicodeSafeProcessor('1. export creates'),
`1${_}export${_}creates`
);
});
it('punctuated text', () => {
assert.equal(
unicodeSafeProcessor('svelte.VERSION'),
`svelte${_}VERSION`
);
});
it('text starting with the dollar sign', () => {
assert.equal(
unicodeSafeProcessor('$destroy method'),
`$destroy${_}method`
);
});
it('numbered text containing the dollar sign', () => {
assert.equal(
unicodeSafeProcessor('1. export $destroy'),
`1${_}export${_}$destroy`
);
});
it('text containing the equal char', () => {
assert.equal(
unicodeSafeProcessor('script context=module'),
`script${_}context${_}module`
);
});
it('text containing the colon char', () => {
assert.equal(
unicodeSafeProcessor('svelte:body'),
`svelte${_}body`
);
});
it('text containing the slash char', () => {
assert.equal(
unicodeSafeProcessor('svelte/motion'),
`svelte${_}motion`
);
});
it('text containing the comma char', () => {
assert.equal(
unicodeSafeProcessor('svelte, motion'),
`svelte${_}motion`
);
});
});
run('unicodeSafeProcessor (preserve unicode) -> unicode', it => {
it('should preserve symbols', () => {
assert.equal(
unicodeSafeProcessor('Ich ♥ Deutsch'),
`Ich${_}love${_}Deutsch`
);
});
it('should remove emoji', () => {
assert.equal(
unicodeSafeProcessor('Ich 😍 Deutsch'),
`Ich${_}Deutsch`
);
});
});
describe('unicodeSafeProcessor (preserve unicode)', () => {
describe('ascii', () => {
it('space separated words', () => {
assert.equal(
unicodeSafeProcessor('Text expressions'),
`Text${_}expressions`
);
});
it('numbered text', () => {
assert.equal(
unicodeSafeProcessor('1. export creates'),
`1${_}export${_}creates`
);
});
it('punctuated text', () => {
assert.equal(
unicodeSafeProcessor('svelte.VERSION'),
`svelte${_}VERSION`
);
});
it('text starting with the dollar sign', () => {
assert.equal(
unicodeSafeProcessor('$destroy method'),
`$destroy${_}method`
);
});
it('numbered text containing the dollar sign', () => {
assert.equal(
unicodeSafeProcessor('1. export $destroy'),
`1${_}export${_}$destroy`
);
});
it('text containing the equal char', () => {
assert.equal(
unicodeSafeProcessor('script context=module'),
`script${_}context${_}module`
);
});
it('text containing the colon char', () => {
assert.equal(
unicodeSafeProcessor('svelte:body'),
`svelte${_}body`
);
});
it('text containing the slash char', () => {
assert.equal(
unicodeSafeProcessor('svelte/motion'),
`svelte${_}motion`
);
});
it('text containing the comma char', () => {
assert.equal(
unicodeSafeProcessor('svelte, motion'),
`svelte${_}motion`
);
});
});
describe('unicode', () => {
it('should preserve symbols', () => {
assert.equal(
unicodeSafeProcessor('Ich ♥ Deutsch'),
`Ich${_}love${_}Deutsch`
);
});
it('should remove emoji', () => {
assert.equal(
unicodeSafeProcessor('Ich 😍 Deutsch'),
`Ich${_}Deutsch`
);
});
});
describe('cyricllic', () => {
it('space separated words', () => {
assert.equal(
unicodeSafeProcessor('Всплытие и перехват событий'),
`Всплытие${_}и${_}перехват${_}событий`
);
});
it('numbered text', () => {
assert.equal(
unicodeSafeProcessor('1 Всплытие и перехват событий'),
`1${_}Всплытие${_}и${_}перехват${_}событий`
);
});
it('punctuated text', () => {
assert.equal(
unicodeSafeProcessor('.Всплытие.и.перехват событий'),
`Всплытие${_}и${_}перехват${_}событий`
);
});
it('text starting with the dollar sign', () => {
assert.equal(
unicodeSafeProcessor('$Всплытие $ перехват событий'),
`$${_}Всплытие${_}$${_}перехват${_}событий`
);
});
it('text containing the dollar sign', () => {
assert.equal(
unicodeSafeProcessor('Всплытие$перехват'),
`Всплытие${_}$${_}перехват`
);
});
it('text containing the equal char', () => {
assert.equal(
unicodeSafeProcessor('Всплытие = перехват=событий'),
`Всплытие${_}перехват${_}событий`
);
});
it('text containing the colon char', () => {
assert.equal(
unicodeSafeProcessor('Всплытие : перехват:событий'),
`Всплытие${_}перехват${_}событий`
);
});
it('text containing the slash char', () => {
assert.equal(
unicodeSafeProcessor('Всплытие / перехват/событий'),
`Всплытие${_}перехват${_}событий`
);
});
it('text containing the comma char', () => {
assert.equal(
unicodeSafeProcessor('Всплытие, перехват'),
`Всплытие${_}перехват`
);
});
});
describe('ascii + cyricllic', () => {
it('space separated words', () => {
assert.equal(
unicodeSafeProcessor('Всплытие и export перехват событий'),
`Всплытие${_}и${_}export${_}перехват${_}событий`
);
});
it('ascii word concatenated to a cyricllic word', () => {
assert.equal(
unicodeSafeProcessor('exportВсплытие'),
`export${_}Всплытие`
);
});
it('cyricllic word concatenated to an ascii word', () => {
assert.equal(
unicodeSafeProcessor('Всплытиеexport'),
`Всплытие${_}export`
);
});
it('numbered text', () => {
assert.equal(
unicodeSafeProcessor('1 export Всплытие и перехват событий'),
`1${_}export${_}Всплытие${_}и${_}перехват${_}событий`
);
});
it('punctuated text', () => {
assert.equal(
unicodeSafeProcessor('.Всплытие.export.и.перехват событий'),
`Всплытие${_}export${_}и${_}перехват${_}событий`
);
});
it('text starting with the dollar sign, followed by ascii char', () => {
assert.equal(
unicodeSafeProcessor('$exportВсплытие перехват событий'),
`$export${_}Всплытие${_}перехват${_}событий`
);
});
it('text starting with the dollar sign, followed by unicode char', () => {
assert.equal(
unicodeSafeProcessor('$Всплытие export перехват событий'),
`$${_}Всплытие${_}export${_}перехват${_}событий`
);
});
it('text containing the dollar sign, followed by ascii char', () => {
assert.equal(
unicodeSafeProcessor('export $destroy a component prop Всплытие и перехват событий'),
`export${_}$destroy${_}a${_}component${_}prop${_}Всплытие${_}и${_}перехват${_}событий`
);
});
it('text containing the dollar sign, followed by unicode char', () => {
assert.equal(
unicodeSafeProcessor('Всплытие export $Всплытие a component prop Всплытие и перехват событий'),
`Всплытие${_}export${_}$${_}Всплытие${_}a${_}component${_}prop${_}Всплытие${_}и${_}перехват${_}событий`
);
});
it('text containing the equal char', () => {
assert.equal(
unicodeSafeProcessor('script context=module Всплытие=и перехват событий'),
`script${_}context${_}module${_}Всплытие${_}и${_}перехват${_}событий`
);
});
it('text containing the colon char', () => {
assert.equal(
unicodeSafeProcessor('svelte:body Всплытие и:перехват событий'),
`svelte${_}body${_}Всплытие${_}и${_}перехват${_}событий`
);
});
it('text containing the slash char', () => {
assert.equal(
unicodeSafeProcessor('svelte/motion Всплытие и / перехват/событий'),
`svelte${_}motion${_}Всплытие${_}и${_}перехват${_}событий`
);
});
it('text containing the comma char', () => {
assert.equal(
unicodeSafeProcessor('Всплытие, export'),
`Всплытие${_}export`
);
});
});
run('unicodeSafeProcessor (preserve unicode) -> cyrillic', it => {
it('space separated words', () => {
assert.equal(
unicodeSafeProcessor('Всплытие и перехват событий'),
`Всплытие${_}и${_}перехват${_}событий`
);
});
it('numbered text', () => {
assert.equal(
unicodeSafeProcessor('1 Всплытие и перехват событий'),
`1${_}Всплытие${_}и${_}перехват${_}событий`
);
});
it('punctuated text', () => {
assert.equal(
unicodeSafeProcessor('.Всплытие.и.перехват событий'),
`Всплытие${_}и${_}перехват${_}событий`
);
});
it('text starting with the dollar sign', () => {
assert.equal(
unicodeSafeProcessor('$Всплытие $ перехват событий'),
`$${_}Всплытие${_}$${_}перехват${_}событий`
);
});
it('text containing the dollar sign', () => {
assert.equal(
unicodeSafeProcessor('Всплытие$перехват'),
`Всплытие${_}$${_}перехват`
);
});
it('text containing the equal char', () => {
assert.equal(
unicodeSafeProcessor('Всплытие = перехват=событий'),
`Всплытие${_}перехват${_}событий`
);
});
it('text containing the colon char', () => {
assert.equal(
unicodeSafeProcessor('Всплытие : перехват:событий'),
`Всплытие${_}перехват${_}событий`
);
});
it('text containing the slash char', () => {
assert.equal(
unicodeSafeProcessor('Всплытие / перехват/событий'),
`Всплытие${_}перехват${_}событий`
);
});
it('text containing the comma char', () => {
assert.equal(
unicodeSafeProcessor('Всплытие, перехват'),
`Всплытие${_}перехват`
);
});
});
run('unicodeSafeProcessor (preserve unicode) -> ascii + cyrillic', it => {
it('space separated words', () => {
assert.equal(
unicodeSafeProcessor('Всплытие и export перехват событий'),
`Всплытие${_}и${_}export${_}перехват${_}событий`
);
});
it('ascii word concatenated to a cyrillic word', () => {
assert.equal(
unicodeSafeProcessor('exportВсплытие'),
`export${_}Всплытие`
);
});
it('cyrillic word concatenated to an ascii word', () => {
assert.equal(
unicodeSafeProcessor('Всплытиеexport'),
`Всплытие${_}export`
);
});
it('numbered text', () => {
assert.equal(
unicodeSafeProcessor('1 export Всплытие и перехват событий'),
`1${_}export${_}Всплытие${_}и${_}перехват${_}событий`
);
});
it('punctuated text', () => {
assert.equal(
unicodeSafeProcessor('.Всплытие.export.и.перехват событий'),
`Всплытие${_}export${_}и${_}перехват${_}событий`
);
});
it('text starting with the dollar sign, followed by ascii char', () => {
assert.equal(
unicodeSafeProcessor('$exportВсплытие перехват событий'),
`$export${_}Всплытие${_}перехват${_}событий`
);
});
it('text starting with the dollar sign, followed by unicode char', () => {
assert.equal(
unicodeSafeProcessor('$Всплытие export перехват событий'),
`$${_}Всплытие${_}export${_}перехват${_}событий`
);
});
it('text containing the dollar sign, followed by ascii char', () => {
assert.equal(
unicodeSafeProcessor('export $destroy a component prop Всплытие и перехват событий'),
`export${_}$destroy${_}a${_}component${_}prop${_}Всплытие${_}и${_}перехват${_}событий`
);
});
it('text containing the dollar sign, followed by unicode char', () => {
assert.equal(
unicodeSafeProcessor('Всплытие export $Всплытие a component prop Всплытие и перехват событий'),
`Всплытие${_}export${_}$${_}Всплытие${_}a${_}component${_}prop${_}Всплытие${_}и${_}перехват${_}событий`
);
});
it('text containing the equal char', () => {
assert.equal(
unicodeSafeProcessor('script context=module Всплытие=и перехват событий'),
`script${_}context${_}module${_}Всплытие${_}и${_}перехват${_}событий`
);
});
it('text containing the colon char', () => {
assert.equal(
unicodeSafeProcessor('svelte:body Всплытие и:перехват событий'),
`svelte${_}body${_}Всплытие${_}и${_}перехват${_}событий`
);
});
it('text containing the slash char', () => {
assert.equal(
unicodeSafeProcessor('svelte/motion Всплытие и / перехват/событий'),
`svelte${_}motion${_}Всплытие${_}и${_}перехват${_}событий`
);
});
it('text containing the comma char', () => {
assert.equal(
unicodeSafeProcessor('Всплытие, export'),
`Всплытие${_}export`
);
});
});

@ -36,6 +36,7 @@ import { clone } from '../utils/clone';
import compiler_warnings from './compiler_warnings';
import compiler_errors from './compiler_errors';
import { extract_ignores_above_position, extract_svelte_ignore_from_comments } from '../utils/extract_svelte_ignore';
import check_enable_sourcemap from './utils/check_enable_sourcemap';
interface ComponentOptions {
namespace?: string;
@ -343,21 +344,28 @@ export default class Component {
? { code: null, map: null }
: result.css;
const sourcemap_source_filename = get_sourcemap_source_filename(compile_options);
const js_sourcemap_enabled = check_enable_sourcemap(compile_options.enableSourcemap, 'js');
js = print(program, {
sourceMapSource: sourcemap_source_filename
});
if (!js_sourcemap_enabled) {
js = print(program);
js.map = null;
} else {
const sourcemap_source_filename = get_sourcemap_source_filename(compile_options);
js.map.sources = [
sourcemap_source_filename
];
js = print(program, {
sourceMapSource: sourcemap_source_filename
});
js.map.sourcesContent = [
this.source
];
js.map.sources = [
sourcemap_source_filename
];
js.map = apply_preprocessor_sourcemap(sourcemap_source_filename, js.map, compile_options.sourcemap as (string | RawSourceMap | DecodedSourceMap));
js.map.sourcesContent = [
this.source
];
js.map = apply_preprocessor_sourcemap(sourcemap_source_filename, js.map, compile_options.sourcemap as (string | RawSourceMap | DecodedSourceMap));
}
}
return {

@ -93,12 +93,15 @@ function esm(
const internal_globals = get_internal_globals(globals, helpers);
// edit user imports
imports.forEach(node => {
node.source.value = edit_source(node.source.value, sveltePath);
});
exports_from.forEach(node => {
node.source!.value = edit_source(node.source!.value, sveltePath);
});
function rewrite_import(node) {
const value = edit_source(node.source.value, sveltePath);
if (node.source.value !== value) {
node.source.value = value;
node.source.raw = null;
}
}
imports.forEach(rewrite_import);
exports_from.forEach(rewrite_import);
const exports = module_exports.length > 0 && {
type: 'ExportNamedDeclaration',

@ -13,6 +13,7 @@ const valid_options = [
'name',
'filename',
'sourcemap',
'enableSourcemap',
'generate',
'errorMode',
'varsReport',
@ -82,7 +83,7 @@ function validate_options(options: CompileOptions, warnings: Warning[]) {
}
export default function compile(source: string, options: CompileOptions = {}) {
options = Object.assign({ generate: 'dom', dev: false }, options);
options = Object.assign({ generate: 'dom', dev: false, enableSourcemap: true }, options);
const stats = new Stats();
const warnings = [];

@ -10,6 +10,7 @@ import { ImportDeclaration, ClassDeclaration, FunctionExpression, Node, Statemen
import { apply_preprocessor_sourcemap } from '../../utils/mapped_code';
import { RawSourceMap, DecodedSourceMap } from '@ampproject/remapping/dist/types/types';
import { flatten } from '../../utils/flatten';
import check_enable_sourcemap from '../utils/check_enable_sourcemap';
export default function dom(
component: Component,
@ -34,9 +35,15 @@ export default function dom(
const css = component.stylesheet.render(options.filename, !options.customElement);
css.map = apply_preprocessor_sourcemap(options.filename, css.map, options.sourcemap as string | RawSourceMap | DecodedSourceMap);
const css_sourcemap_enabled = check_enable_sourcemap(options.enableSourcemap, 'css');
const styles = component.stylesheet.has_styles && options.dev
if (css_sourcemap_enabled) {
css.map = apply_preprocessor_sourcemap(options.filename, css.map, options.sourcemap as string | RawSourceMap | DecodedSourceMap);
} else {
css.map = null;
}
const styles = css_sourcemap_enabled && component.stylesheet.has_styles && options.dev
? `${css.code}\n/*# sourceMappingURL=${css.map.toUrl()} */`
: css.code;
@ -521,7 +528,7 @@ export default function dom(
constructor(options) {
super();
${css.code && b`this.shadowRoot.innerHTML = \`<style>${css.code.replace(/\\/g, '\\\\')}${options.dev ? `\n/*# sourceMappingURL=${css.map.toUrl()} */` : ''}</style>\`;`}
${css.code && b`this.shadowRoot.innerHTML = \`<style>${css.code.replace(/\\/g, '\\\\')}${css_sourcemap_enabled && options.dev ? `\n/*# sourceMappingURL=${css.map.toUrl()} */` : ''}</style>\`;`}
@init(this, { target: this.shadowRoot, props: ${init_props}, customElement: true }, ${definition}, ${has_create_fragment ? 'create_fragment' : 'null'}, ${not_equal}, ${prop_indexes}, null, ${dirty});

@ -13,7 +13,10 @@ export default function(node: AwaitBlock, renderer: Renderer, options: RenderOpt
renderer.add_expression(x`
function(__value) {
if (@is_promise(__value)) return ${pending};
if (@is_promise(__value)) {
__value.then(null, @noop);
return ${pending};
}
return (function(${node.then_node ? node.then_node : ''}) { return ${then}; }(__value));
}(${node.expression.node})
`);

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save