Repository: mvasigh/sveltekit-mdsvex-blog
Branch: main
Commit: d71a0b489117
Files: 38
Total size: 26.6 KB
Directory structure:
gitextract_zi2r1xca/
├── .eslintignore
├── .eslintrc.cjs
├── .github/
│ └── workflows/
│ └── test.yaml
├── .gitignore
├── .npmrc
├── .prettierignore
├── .prettierrc
├── .vscode/
│ └── settings.json
├── LICENSE
├── README.md
├── mdsvex.config.js
├── package.json
├── playwright.config.ts
├── src/
│ ├── app.d.ts
│ ├── app.html
│ ├── lib/
│ │ ├── components/
│ │ │ ├── Article.svelte
│ │ │ ├── ArticleDescription.svelte
│ │ │ ├── ArticleMeta.svelte
│ │ │ ├── ArticleTitle.svelte
│ │ │ ├── Counter.svelte
│ │ │ └── PageHead.svelte
│ │ ├── slugFromPath.test.ts
│ │ └── slugFromPath.ts
│ ├── posts/
│ │ ├── a-second-post.svelte.md
│ │ ├── unpublished-draft-example.svelte.md
│ │ ├── welcome-to-my-blog.svelte.md
│ │ └── yet-another-blog-post.svelte.md
│ └── routes/
│ ├── +error.svelte
│ ├── +layout.svelte
│ ├── +layout.ts
│ ├── +page.server.ts
│ ├── +page.svelte
│ └── posts/
│ └── [slug]/
│ ├── +page.svelte
│ └── +page.ts
├── svelte.config.js
├── tests/
│ └── test.ts
├── tsconfig.json
└── vite.config.js
================================================
FILE CONTENTS
================================================
================================================
FILE: .eslintignore
================================================
.DS_Store
node_modules
/build
/.svelte-kit
/package
.env
.env.*
!.env.example
# Ignore files for PNPM, NPM and YARN
pnpm-lock.yaml
package-lock.json
yarn.lock
================================================
FILE: .eslintrc.cjs
================================================
module.exports = {
root: true,
parser: '@typescript-eslint/parser',
extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended', 'prettier'],
plugins: ['svelte3', '@typescript-eslint'],
ignorePatterns: ['*.cjs'],
overrides: [{ files: ['*.svelte'], processor: 'svelte3/svelte3' }],
settings: {
'svelte3/typescript': () => require('typescript')
},
parserOptions: {
sourceType: 'module',
ecmaVersion: 2020
},
env: {
browser: true,
es2017: true,
node: true
}
};
================================================
FILE: .github/workflows/test.yaml
================================================
name: Node.js tests
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [16.x, 18.x]
steps:
- uses: actions/checkout@v3
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
- uses: pnpm/action-setup@v2
with:
version: 7
- name: Install packages
run: pnpm install
- name: Install Playwright
run: pnpx playwright install
- name: Build
run: pnpm build
- name: Test
run: pnpm test
================================================
FILE: .gitignore
================================================
.DS_Store
node_modules
/build
/.svelte-kit
/package
.env
.env.*
!.env.example
vite.config.js.timestamp-*
vite.config.ts.timestamp-*
================================================
FILE: .npmrc
================================================
engine-strict=true
================================================
FILE: .prettierignore
================================================
.DS_Store
node_modules
/build
/.svelte-kit
/package
.env
.env.*
!.env.example
# Ignore files for PNPM, NPM and YARN
pnpm-lock.yaml
package-lock.json
yarn.lock
================================================
FILE: .prettierrc
================================================
{
"useTabs": true,
"tabWidth": 2,
"singleQuote": true,
"trailingComma": "none",
"printWidth": 100,
"plugins": ["prettier-plugin-svelte"],
"pluginSearchDirs": ["."],
"overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }]
}
================================================
FILE: .vscode/settings.json
================================================
{
"explorer.sortOrder": "type"
}
================================================
FILE: LICENSE
================================================
Copyright 2021 Mehdi Vasigh
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
================================================
FILE: README.md
================================================
# `sveltekit-mdsvex-blog`
Minimalistic blog site template, built with [TypeScript](https://www.typescriptlang.org/), [Svelte](https://svelte.dev), [SvelteKit](https://kit.svelte.dev) and [MDsveX](https://mdsvex.com).
[Live demo](https://sveltekit-mdsvex-blog.netlify.app)
## Getting started
First, clone the repository and navigate to the project directory:
```bash
git clone https://github.com/mvasigh/sveltekit-mdsvex-blog.git my-blog
cd my-blog
```
If you want to have a fresh commit history, blow away the `.git` directory and re-initialize the repository locally:
```bash
rm -rf .git
git init
```
Next, install dependencies with NPM:
```bash
npm install # or `pnpm i`
```
Finally, run the local development server:
```bash
npm run dev # or `pnpm dev`
```
## Building for production
This project is pre-configured with [`@sveltejs/adapter-static`](https://github.com/sveltejs/kit/tree/master/packages/adapter-static). This makes deploying to static site hosts, such as Netlify, super easy. Simply add your site's repository to your static site host of choice, configure the build command to be `npm run build` and the build output directory to be `build`.
## Starting from scratch
You may want to create your own project from scratch using `create-svelte`. You can do so easily and add MDsveX support to your project using `svelte-add`:
1. Create a new project per the [SvelteKit docs](https://kit.svelte.dev/docs#introduction-getting-started)
2. [Add MDsveX to your project](https://github.com/svelte-add/mdsvex#-adding-to-sveltekit) using svelte-add
3. Configure your site to your liking; files with the `.svelte.md`, `.md` and `.svx` extensions will be picked up by MDsveX by default
## Questions?
Feel free to ask any questions that you have! I am happy to try and answer them. The best way to reach me is by Mastodon, [@mehdi@mastodon.gamedev.place](https://mastodon.gamedev.place/@mehdi), but you can also [open an issue in this repository](https://github.com/mvasigh/sveltekit-mdsvex-blog/issues/new).
================================================
FILE: mdsvex.config.js
================================================
import remarkGithub from 'remark-github';
import remarkAbbr from 'remark-abbr';
import rehypeSlug from 'rehype-slug';
import rehypeAutolinkHeadings from 'rehype-autolink-headings';
import { defineMDSveXConfig as defineConfig } from 'mdsvex';
const config = defineConfig({
extensions: ['.svelte.md', '.md', '.svx'],
smartypants: {
dashes: 'oldschool'
},
remarkPlugins: [
[
remarkGithub,
{
// TODO: Replace with your own repository
repository: 'https://github.com/mvasigh/sveltekit-mdsvex-blog.git'
}
],
remarkAbbr
],
rehypePlugins: [
rehypeSlug,
[
rehypeAutolinkHeadings,
{
behavior: 'wrap'
}
]
]
});
export default config;
================================================
FILE: package.json
================================================
{
"name": "sveltekit-mdsvex-blog",
"version": "0.0.1",
"private": true,
"scripts": {
"dev": "vite dev",
"build": "vite build",
"preview": "vite preview",
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
"test:unit": "vitest",
"test:playwright": "playwright test",
"test": "pnpm test:unit run && pnpm test:playwright",
"lint": "prettier --plugin-search-dir . --check . && eslint .",
"format": "prettier --plugin-search-dir . --write ."
},
"devDependencies": {
"@playwright/test": "^1.28.1",
"@sveltejs/adapter-static": "^1.0.0",
"@sveltejs/kit": "^1.0.0",
"@typescript-eslint/eslint-plugin": "^5.45.0",
"@typescript-eslint/parser": "^5.45.0",
"eslint": "^8.28.0",
"eslint-config-prettier": "^8.5.0",
"eslint-plugin-svelte3": "^4.0.0",
"mdsvex": "^0.10.6",
"prettier": "^2.8.0",
"prettier-plugin-svelte": "^2.8.1",
"rehype-autolink-headings": "^6.1.1",
"rehype-slug": "^5.1.0",
"remark-abbr": "^1.4.1",
"remark-github": "^11.2.4",
"svelte": "^3.54.0",
"svelte-check": "^2.9.2",
"tslib": "^2.4.1",
"typescript": "^4.9.3",
"vite": "^4.0.0",
"vitest": "^0.25.3"
},
"type": "module"
}
================================================
FILE: playwright.config.ts
================================================
import type { PlaywrightTestConfig } from '@playwright/test';
const config: PlaywrightTestConfig = {
webServer: {
command: 'npm run build && npm run preview',
port: 4173
},
testDir: 'tests'
};
export default config;
================================================
FILE: src/app.d.ts
================================================
// See https://kit.svelte.dev/docs/types#app
// for information about these interfaces
// and what to do when importing types
declare namespace App {
// interface Error {}
// interface Locals {}
// interface PageData {}
// interface Platform {}
interface MdsvexFile {
default: import('svelte/internal').SvelteComponent;
metadata: Record<string, string>;
}
type MdsvexResolver = () => Promise<MdsvexFile>;
interface BlogPost {
slug: string;
title: string;
author: string;
description: string;
date: string;
published: boolean;
}
}
================================================
FILE: src/app.html
================================================
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
<meta name="viewport" content="width=device-width" />
%sveltekit.head%
</head>
<body data-sveltekit-preload-data="hover">
<div style="display: contents">%sveltekit.body%</div>
</body>
</html>
================================================
FILE: src/lib/components/Article.svelte
================================================
<article>
<slot />
</article>
<style>
article {
margin-bottom: calc(var(--spacing-unit) * 8);
}
</style>
================================================
FILE: src/lib/components/ArticleDescription.svelte
================================================
<script lang="ts">
export let description: string;
export let slug = '';
const href = slug && `/posts/${slug}`;
</script>
<p>
{description}
{#if slug}
<a {href}>Read More →</a>
{/if}
</p>
<style>
p {
margin: 0;
margin-bottom: calc(var(--spacing-unit) * 8);
}
</style>
================================================
FILE: src/lib/components/ArticleMeta.svelte
================================================
<script lang="ts">
export let author: string;
export let date: string;
const formattedDate = new Date(date).toDateString();
</script>
<p>
<span class="author">{author}</span>
<span class="date">{formattedDate}</span>
</p>
<style>
p {
margin: 0;
margin-bottom: calc(var(--spacing-unit) * 4);
}
.author {
font-weight: bold;
margin-right: calc(var(--spacing-unit) * 2);
}
.date {
color: var(--color-text-secondary);
}
</style>
================================================
FILE: src/lib/components/ArticleTitle.svelte
================================================
<script lang="ts">
export let slug = '';
export let title: string;
const id = title
.toLowerCase()
.replace(/[^a-zA-Z ]/g, '')
.replace(/\s/g, '-');
const href = slug ? `/posts/${slug}` : '#' + id;
</script>
{#if slug}
<h3 class="heading" class:large={!slug} {id}>
<a {href}>
{title}
</a>
</h3>
{:else}
<h2 class="heading" class:large={!slug} {id}>
<a {href}>
{title}
</a>
</h2>
{/if}
<style>
h2 {
margin: 0;
}
.heading {
margin: 0;
font-size: 1.8rem;
}
.large {
margin-top: calc(var(--spacing-unit) * 12);
font-size: 2.2rem;
}
</style>
================================================
FILE: src/lib/components/Counter.svelte
================================================
<script lang="ts">
let count = 0;
</script>
<div class="counter">
<p class="count">{count}</p>
<button on:click={() => (count += 1)}>Increment</button>
</div>
<style>
.counter {
text-align: center;
}
.count {
font-size: 2rem;
margin-bottom: calc(var(--spacing-unit) * 2);
}
.counter {
margin-top: calc(var(--spacing-unit) * 4);
margin-bottom: calc(var(--spacing-unit) * 4);
}
</style>
================================================
FILE: src/lib/components/PageHead.svelte
================================================
<script lang="ts">
export let title: string;
export let description: string;
const siteTitle = 'SvelteKit + MDsveX Blog';
const formattedTitle = title ? `${title} | ${siteTitle}` : siteTitle;
</script>
<svelte:head>
<title>{formattedTitle}</title>
<meta property="og:site_name" content={siteTitle} />
<meta property="og:title" content={title} />
<meta property="og:description" content={description} />
</svelte:head>
================================================
FILE: src/lib/slugFromPath.test.ts
================================================
import { describe, it, expect } from 'vitest';
import { slugFromPath } from './slugFromPath';
describe('slugFromPath', () => {
it('extracts slug from paths correctly', () => {
const cases = [
{
path: '/foo/bar/test-slug.md',
expected: 'test-slug'
},
{
path: '/foo/bar/test-slug.svx',
expected: 'test-slug'
},
{
path: '/foo/bar/test-slug.svelte.md',
expected: 'test-slug'
}
];
cases.forEach(({ path, expected }) => expect(slugFromPath(path)).toBe(expected));
});
it('returns null for unknown extension', () => {
const path = '/foo/bar/test-slug.abc';
expect(slugFromPath(path)).toBeNull();
});
it('returns null for no extension', () => {
const path = '/foo/bar/test-slug';
expect(slugFromPath(path)).toBeNull();
});
});
================================================
FILE: src/lib/slugFromPath.ts
================================================
export const slugFromPath = (path: string) =>
path.match(/([\w-]+)\.(svelte\.md|md|svx)/i)?.[1] ?? null;
================================================
FILE: src/posts/a-second-post.svelte.md
================================================
---
title: 'A second blog post'
description: "The first blog post wasn't enough; I had to come back and write more about Svelte and SvelteKit."
author: 'Mehdi Vasigh'
date: '2021-05-03'
published: true
---
<script>
import Counter from '$lib/components/Counter.svelte'
</script>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris pellentesque orci nec facilisis iaculis. Sed accumsan placerat dolor. Donec sollicitudin nisi sit amet sodales molestie. Maecenas sit amet dolor nulla. Fusce sed elit et erat consequat dignissim. Nunc eu erat felis. Mauris pretium, arcu eu dapibus tempor, mauris eros tempor tortor, eu tincidunt erat libero sit amet mi. Phasellus eu libero mollis, finibus lacus eget, sollicitudin nulla.
Here's a random Svelte component thrown into my MDsveX markdown:
<Counter />
### Heading
In lectus erat, maximus sed pulvinar eu, elementum vehicula mi. Maecenas nec dui urna. Nunc at magna purus. Cras facilisis, purus in dignissim egestas, ante ligula malesuada leo, quis malesuada dolor tortor vitae mauris. Morbi auctor mauris nibh, ut sodales tortor volutpat in. Donec aliquet ex eget ullamcorper semper. Proin vel libero at nulla gravida blandit. Maecenas odio massa, pretium blandit lectus ac, vehicula ultrices justo. Suspendisse libero dolor, tristique quis laoreet vel, placerat vel nisl. Nunc et venenatis nunc, vitae fringilla risus. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Phasellus consectetur pellentesque ipsum, in hendrerit lectus ultricies quis. Morbi ultrices, elit nec lacinia vulputate, dolor nibh commodo justo, quis dictum nisi nulla vel mi.
### Heading
Nullam nisl risus, pellentesque at enim id, molestie tristique purus. Aliquam ultricies, sem eu rutrum posuere, augue libero commodo nisi, et aliquam massa mi vitae nunc. In sodales, lorem vitae pellentesque lacinia, est velit condimentum orci, in vestibulum lorem est eget nisi. Quisque varius eget risus mattis rutrum. Sed tellus nisl, egestas a ligula et, finibus sollicitudin mauris. Ut sit amet scelerisque purus, luctus auctor leo. Nullam enim velit, tincidunt sed venenatis non, fringilla ut lacus. Morbi diam nulla, luctus non orci at, maximus rhoncus est.
Ut quis leo rhoncus, aliquet sapien at, venenatis lectus. Nunc mattis vestibulum sapien. Donec quis vestibulum ex. Vivamus condimentum dui gravida pulvinar feugiat. Duis posuere, lacus eu cursus gravida, magna ex lobortis sapien, non finibus orci justo at orci. Nulla sit amet ligula a lorem aliquam consequat. Sed vehicula lacus nec ipsum efficitur, vel volutpat sem blandit. Curabitur nunc nunc, commodo sed ultrices id, dignissim ac orci. Nunc semper lectus et orci faucibus, et suscipit nisl consequat. Phasellus malesuada nisl a risus ultricies, vitae pretium libero vulputate. Nullam at neque ut enim mattis dapibus porttitor vitae dui. Cras erat libero, porta a eros ac, tempus consectetur est.
================================================
FILE: src/posts/unpublished-draft-example.svelte.md
================================================
---
title: 'Unpublished draft example'
description: 'This blog post is not yet ready to be seen by the world'
author: 'Mehdi Vasigh'
date: '2021-05-10'
published: false
---
This is an example of an unpublished post! Your site will display a 404 page until you set your `published` flag to `true`.
================================================
FILE: src/posts/welcome-to-my-blog.svelte.md
================================================
---
title: 'Welcome to my blog!'
description: 'I love to write about Svelte and all the cool things that you can build with it.'
author: 'Mehdi Vasigh'
date: '2021-04-21'
published: true
---
<script>
import Counter from '$lib/components/Counter.svelte'
</script>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris pellentesque orci nec facilisis iaculis. Sed accumsan placerat dolor. Donec sollicitudin nisi sit amet sodales molestie. Maecenas sit amet dolor nulla. Fusce sed elit et erat consequat dignissim. Nunc eu erat felis. Mauris pretium, arcu eu dapibus tempor, mauris eros tempor tortor, eu tincidunt erat libero sit amet mi. Phasellus eu libero mollis, finibus lacus eget, sollicitudin nulla.
Here's a random Svelte component thrown into my MDsveX markdown:
<Counter />
## Link to other page
[A second blog post](/posts/a-second-post)
## Example heading
In lectus erat, maximus sed pulvinar eu, elementum vehicula mi. Maecenas nec dui urna. Nunc at magna purus. Cras facilisis, purus in dignissim egestas, ante ligula malesuada leo, quis malesuada dolor tortor vitae mauris. Morbi auctor mauris nibh, ut sodales tortor volutpat in. Donec aliquet ex eget ullamcorper semper. Proin vel libero at nulla gravida blandit. Maecenas odio massa, pretium blandit lectus ac, vehicula ultrices justo. Suspendisse libero dolor, tristique quis laoreet vel, placerat vel nisl. Nunc et venenatis nunc, vitae fringilla risus. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Phasellus consectetur pellentesque ipsum, in hendrerit lectus ultricies quis. Morbi ultrices, elit nec lacinia vulputate, dolor nibh commodo justo, quis dictum nisi nulla vel mi.
## Another example heading
Nullam nisl risus, pellentesque at enim id, molestie tristique purus. Aliquam ultricies, sem eu rutrum posuere, augue libero commodo nisi, et aliquam massa mi vitae nunc. In sodales, lorem vitae pellentesque lacinia, est velit condimentum orci, in vestibulum lorem est eget nisi. Quisque varius eget risus mattis rutrum. Sed tellus nisl, egestas a ligula et, finibus sollicitudin mauris. Ut sit amet scelerisque purus, luctus auctor leo. Nullam enim velit, tincidunt sed venenatis non, fringilla ut lacus. Morbi diam nulla, luctus non orci at, maximus rhoncus est.
Ut quis leo rhoncus, aliquet sapien at, venenatis lectus. Nunc mattis vestibulum sapien. Donec quis vestibulum ex. Vivamus condimentum dui gravida pulvinar feugiat. Duis posuere, lacus eu cursus gravida, magna ex lobortis sapien, non finibus orci justo at orci. Nulla sit amet ligula a lorem aliquam consequat. Sed vehicula lacus nec ipsum efficitur, vel volutpat sem blandit. Curabitur nunc nunc, commodo sed ultrices id, dignissim ac orci. Nunc semper lectus et orci faucibus, et suscipit nisl consequat. Phasellus malesuada nisl a risus ultricies, vitae pretium libero vulputate. Nullam at neque ut enim mattis dapibus porttitor vitae dui. Cras erat libero, porta a eros ac, tempus consectetur est.
================================================
FILE: src/posts/yet-another-blog-post.svelte.md
================================================
---
title: 'Yet another article'
description: "I know, by now you've probably had enough, but this template looks more full with three posts, and here we are."
author: 'Mehdi Vasigh'
date: '2021-05-05'
published: true
---
<script>
import Counter from '$lib/components/Counter.svelte'
</script>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris pellentesque orci nec facilisis iaculis. Sed accumsan placerat dolor. Donec sollicitudin nisi sit amet sodales molestie. Maecenas sit amet dolor nulla. Fusce sed elit et erat consequat dignissim. Nunc eu erat felis. Mauris pretium, arcu eu dapibus tempor, mauris eros tempor tortor, eu tincidunt erat libero sit amet mi. Phasellus eu libero mollis, finibus lacus eget, sollicitudin nulla.
Here's a random Svelte component thrown into my MDsveX markdown:
<Counter />
### Heading
In lectus erat, maximus sed pulvinar eu, elementum vehicula mi. Maecenas nec dui urna. Nunc at magna purus. Cras facilisis, purus in dignissim egestas, ante ligula malesuada leo, quis malesuada dolor tortor vitae mauris. Morbi auctor mauris nibh, ut sodales tortor volutpat in. Donec aliquet ex eget ullamcorper semper. Proin vel libero at nulla gravida blandit. Maecenas odio massa, pretium blandit lectus ac, vehicula ultrices justo. Suspendisse libero dolor, tristique quis laoreet vel, placerat vel nisl. Nunc et venenatis nunc, vitae fringilla risus. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Phasellus consectetur pellentesque ipsum, in hendrerit lectus ultricies quis. Morbi ultrices, elit nec lacinia vulputate, dolor nibh commodo justo, quis dictum nisi nulla vel mi.
### Heading
Nullam nisl risus, pellentesque at enim id, molestie tristique purus. Aliquam ultricies, sem eu rutrum posuere, augue libero commodo nisi, et aliquam massa mi vitae nunc. In sodales, lorem vitae pellentesque lacinia, est velit condimentum orci, in vestibulum lorem est eget nisi. Quisque varius eget risus mattis rutrum. Sed tellus nisl, egestas a ligula et, finibus sollicitudin mauris. Ut sit amet scelerisque purus, luctus auctor leo. Nullam enim velit, tincidunt sed venenatis non, fringilla ut lacus. Morbi diam nulla, luctus non orci at, maximus rhoncus est.
Ut quis leo rhoncus, aliquet sapien at, venenatis lectus. Nunc mattis vestibulum sapien. Donec quis vestibulum ex. Vivamus condimentum dui gravida pulvinar feugiat. Duis posuere, lacus eu cursus gravida, magna ex lobortis sapien, non finibus orci justo at orci. Nulla sit amet ligula a lorem aliquam consequat. Sed vehicula lacus nec ipsum efficitur, vel volutpat sem blandit. Curabitur nunc nunc, commodo sed ultrices id, dignissim ac orci. Nunc semper lectus et orci faucibus, et suscipit nisl consequat. Phasellus malesuada nisl a risus ultricies, vitae pretium libero vulputate. Nullam at neque ut enim mattis dapibus porttitor vitae dui. Cras erat libero, porta a eros ac, tempus consectetur est.
================================================
FILE: src/routes/+error.svelte
================================================
<h2>
<span class="errorCode">404</span>
<span class="errorMessage">Page Not Found</span>
</h2>
<style>
h2 {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
font-weight: 400;
min-height: 400px;
}
.errorCode {
font-size: 8rem;
}
.errorMessage {
font-size: 1rem;
}
</style>
================================================
FILE: src/routes/+layout.svelte
================================================
<script lang="ts">
import { page } from '$app/stores';
</script>
<header>
<a href="/"><h1 class:small={$page.url.pathname !== '/'}>SvelteKit + MDsveX Blog</h1></a>
</header>
<main>
<slot />
</main>
<footer>
<p>
Copyright © <a href="https://twitter.com/mehdi_vasigh">Mehdi Vasigh</a>, {new Date().getFullYear()}
</p>
</footer>
<style>
:global(:root) {
--spacing-unit: 4px;
--color-background: #e5e5e5;
--color-text-primary: #212121;
--color-text-secondary: #5a5a5a;
}
:global(body) {
margin: 0 auto;
max-width: 75ch;
padding: calc(var(--spacing-unit) * 8);
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu',
'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
background-color: var(--color-background);
color: var(--color-text-primary);
line-height: 1.51;
font-size: 18px;
}
:global(a, a:visited, a:active) {
text-decoration: none;
color: var(--color-text-primary);
font-weight: 700;
}
:global(a:hover) {
text-decoration: underline;
}
.small {
font-size: 1.6rem;
}
footer {
margin-top: calc(var(--spacing-unit) * 8);
}
</style>
================================================
FILE: src/routes/+layout.ts
================================================
export const prerender = true;
================================================
FILE: src/routes/+page.server.ts
================================================
import type { PageServerLoad } from './$types';
import { slugFromPath } from '$lib/slugFromPath';
const MAX_POSTS = 10;
export const load: PageServerLoad = async ({ url }) => {
const modules = import.meta.glob(`/src/posts/*.{md,svx,svelte.md}`);
const postPromises = Object.entries(modules).map(([path, resolver]) =>
resolver().then(
(post) =>
({
slug: slugFromPath(path),
...(post as unknown as App.MdsvexFile).metadata
} as App.BlogPost)
)
);
const posts = await Promise.all(postPromises);
const publishedPosts = posts.filter((post) => post.published).slice(0, MAX_POSTS);
publishedPosts.sort((a, b) => (new Date(a.date) > new Date(b.date) ? -1 : 1));
return { posts: publishedPosts };
};
================================================
FILE: src/routes/+page.svelte
================================================
<script lang="ts">
import type { PageData } from './$types';
import PageHead from '$lib/components/PageHead.svelte';
import Article from '$lib/components/Article.svelte';
import ArticleTitle from '$lib/components/ArticleTitle.svelte';
import ArticleMeta from '$lib/components/ArticleMeta.svelte';
import ArticleDescription from '$lib/components/ArticleDescription.svelte';
export let data: PageData;
</script>
<PageHead title="Home" description="An awesome blog about development with Svelte" />
<p>
This is a minimalistic example of a blog built with <a href="https://kit.svelte.dev">SvelteKit</a>
and <a href="https://mdsvex.com/">MDsveX</a>.
<a href="https://github.com/mvasigh/sveltekit-mdsvex-blog">View source code on Github.</a>
</p>
{#each data.posts as { slug, title, author, description, date }}
<Article>
<ArticleTitle {slug} {title} />
<ArticleMeta {author} {date} />
<ArticleDescription {description} {slug} />
</Article>
{/each}
<slot />
================================================
FILE: src/routes/posts/[slug]/+page.svelte
================================================
<script lang="ts">
import type { PageData } from './$types';
import type { SvelteComponentTyped } from 'svelte/internal';
import PageHead from '$lib/components/PageHead.svelte';
import ArticleTitle from '$lib/components/ArticleTitle.svelte';
import ArticleMeta from '$lib/components/ArticleMeta.svelte';
export let data: PageData;
type C = $$Generic<typeof SvelteComponentTyped<any, any, any>>;
$: component = data.component as unknown as C;
</script>
<PageHead title={data.frontmatter.title} description={data.frontmatter.description} />
<ArticleTitle title={data.frontmatter.title} />
<ArticleMeta author={data.frontmatter.author} date={data.frontmatter.date} />
<svelte:component this={component} />
================================================
FILE: src/routes/posts/[slug]/+page.ts
================================================
import type { PageLoad } from './$types';
import { slugFromPath } from '$lib/slugFromPath';
import { error } from '@sveltejs/kit';
export const load: PageLoad = async ({ params }) => {
const modules = import.meta.glob(`/src/posts/*.{md,svx,svelte.md}`);
let match: { path?: string; resolver?: App.MdsvexResolver } = {};
for (const [path, resolver] of Object.entries(modules)) {
if (slugFromPath(path) === params.slug) {
match = { path, resolver: resolver as unknown as App.MdsvexResolver };
break;
}
}
const post = await match?.resolver?.();
if (!post || !post.metadata.published) {
throw error(404); // Couldn't resolve the post
}
return {
component: post.default,
frontmatter: post.metadata
};
};
================================================
FILE: svelte.config.js
================================================
import { mdsvex } from 'mdsvex';
import mdsvexConfig from './mdsvex.config.js';
import adapter from '@sveltejs/adapter-static';
import { vitePreprocess } from '@sveltejs/kit/vite';
/** @type {import('@sveltejs/kit').Config} */
const config = {
extensions: ['.svelte', ...mdsvexConfig.extensions],
// Consult https://kit.svelte.dev/docs/integrations#preprocessors
// for more information about preprocessors
preprocess: [vitePreprocess(), mdsvex(mdsvexConfig)],
kit: {
adapter: adapter({ strict: false })
}
};
export default config;
================================================
FILE: tests/test.ts
================================================
import { expect, test } from '@playwright/test';
test('index page has expected content', async ({ page }) => {
await page.goto('/');
const articles = await page.$$('article');
expect(await page.textContent('h1')).toBe('SvelteKit + MDsveX Blog');
expect(articles.length).toBeGreaterThan(0);
expect(articles.length).not.toBeGreaterThan(10);
for (const article of articles) {
expect(await article.$('a')).not.toBeFalsy();
}
});
test('clicking on article title in home page navigates to the article', async ({ page }) => {
await page.goto('/');
const title = await page.textContent('article h3');
await page.getByText(title || '').click();
expect(await page.textContent('h2')).toBe(title);
});
================================================
FILE: tsconfig.json
================================================
{
"extends": "./.svelte-kit/tsconfig.json",
"compilerOptions": {
"allowJs": true,
"checkJs": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"skipLibCheck": true,
"sourceMap": true,
"strict": true
}
// Path aliases are handled by https://kit.svelte.dev/docs/configuration#alias
//
// If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes
// from the referenced tsconfig.json - TypeScript does not merge them in
}
================================================
FILE: vite.config.js
================================================
import { sveltekit } from '@sveltejs/kit/vite';
/** @type {import('vite').UserConfig} */
const config = {
plugins: [sveltekit()],
test: {
include: ['src/**/*.{test,spec}.{js,ts}']
}
};
export default config;
gitextract_zi2r1xca/ ├── .eslintignore ├── .eslintrc.cjs ├── .github/ │ └── workflows/ │ └── test.yaml ├── .gitignore ├── .npmrc ├── .prettierignore ├── .prettierrc ├── .vscode/ │ └── settings.json ├── LICENSE ├── README.md ├── mdsvex.config.js ├── package.json ├── playwright.config.ts ├── src/ │ ├── app.d.ts │ ├── app.html │ ├── lib/ │ │ ├── components/ │ │ │ ├── Article.svelte │ │ │ ├── ArticleDescription.svelte │ │ │ ├── ArticleMeta.svelte │ │ │ ├── ArticleTitle.svelte │ │ │ ├── Counter.svelte │ │ │ └── PageHead.svelte │ │ ├── slugFromPath.test.ts │ │ └── slugFromPath.ts │ ├── posts/ │ │ ├── a-second-post.svelte.md │ │ ├── unpublished-draft-example.svelte.md │ │ ├── welcome-to-my-blog.svelte.md │ │ └── yet-another-blog-post.svelte.md │ └── routes/ │ ├── +error.svelte │ ├── +layout.svelte │ ├── +layout.ts │ ├── +page.server.ts │ ├── +page.svelte │ └── posts/ │ └── [slug]/ │ ├── +page.svelte │ └── +page.ts ├── svelte.config.js ├── tests/ │ └── test.ts ├── tsconfig.json └── vite.config.js
SYMBOL INDEX (4 symbols across 2 files)
FILE: src/app.d.ts
type MdsvexFile (line 10) | interface MdsvexFile {
type MdsvexResolver (line 15) | type MdsvexResolver = () => Promise<MdsvexFile>;
type BlogPost (line 17) | interface BlogPost {
FILE: src/routes/+page.server.ts
constant MAX_POSTS (line 4) | const MAX_POSTS = 10;
Condensed preview — 38 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (31K chars).
[
{
"path": ".eslintignore",
"chars": 160,
"preview": ".DS_Store\nnode_modules\n/build\n/.svelte-kit\n/package\n.env\n.env.*\n!.env.example\n\n# Ignore files for PNPM, NPM and YARN\npnp"
},
{
"path": ".eslintrc.cjs",
"chars": 494,
"preview": "module.exports = {\n\troot: true,\n\tparser: '@typescript-eslint/parser',\n\textends: ['eslint:recommended', 'plugin:@typescri"
},
{
"path": ".github/workflows/test.yaml",
"chars": 685,
"preview": "name: Node.js tests\n\non:\n push:\n branches: [ main ]\n pull_request:\n branches: [ main ]\n\njobs:\n build:\n\n runs"
},
{
"path": ".gitignore",
"chars": 132,
"preview": ".DS_Store\nnode_modules\n/build\n/.svelte-kit\n/package\n.env\n.env.*\n!.env.example\nvite.config.js.timestamp-*\nvite.config.ts."
},
{
"path": ".npmrc",
"chars": 19,
"preview": "engine-strict=true\n"
},
{
"path": ".prettierignore",
"chars": 160,
"preview": ".DS_Store\nnode_modules\n/build\n/.svelte-kit\n/package\n.env\n.env.*\n!.env.example\n\n# Ignore files for PNPM, NPM and YARN\npnp"
},
{
"path": ".prettierrc",
"chars": 249,
"preview": "{\n\t\"useTabs\": true,\n\t\"tabWidth\": 2,\n\t\"singleQuote\": true,\n\t\"trailingComma\": \"none\",\n\t\"printWidth\": 100,\n\t\"plugins\": [\"pr"
},
{
"path": ".vscode/settings.json",
"chars": 34,
"preview": "{\n\t\"explorer.sortOrder\": \"type\"\n}\n"
},
{
"path": "LICENSE",
"chars": 1052,
"preview": "Copyright 2021 Mehdi Vasigh\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this softwa"
},
{
"path": "README.md",
"chars": 2031,
"preview": "# `sveltekit-mdsvex-blog`\n\nMinimalistic blog site template, built with [TypeScript](https://www.typescriptlang.org/), [S"
},
{
"path": "mdsvex.config.js",
"chars": 678,
"preview": "import remarkGithub from 'remark-github';\nimport remarkAbbr from 'remark-abbr';\nimport rehypeSlug from 'rehype-slug';\nim"
},
{
"path": "package.json",
"chars": 1256,
"preview": "{\n\t\"name\": \"sveltekit-mdsvex-blog\",\n\t\"version\": \"0.0.1\",\n\t\"private\": true,\n\t\"scripts\": {\n\t\t\"dev\": \"vite dev\",\n\t\t\"build\":"
},
{
"path": "playwright.config.ts",
"chars": 225,
"preview": "import type { PlaywrightTestConfig } from '@playwright/test';\n\nconst config: PlaywrightTestConfig = {\n\twebServer: {\n\t\tco"
},
{
"path": "src/app.d.ts",
"chars": 558,
"preview": "// See https://kit.svelte.dev/docs/types#app\n// for information about these interfaces\n// and what to do when importing "
},
{
"path": "src/app.html",
"chars": 329,
"preview": "<!DOCTYPE html>\n<html lang=\"en\">\n\t<head>\n\t\t<meta charset=\"utf-8\" />\n\t\t<link rel=\"icon\" href=\"%sveltekit.assets%/favicon."
},
{
"path": "src/lib/components/Article.svelte",
"chars": 111,
"preview": "<article>\n\t<slot />\n</article>\n\n<style>\n\tarticle {\n\t\tmargin-bottom: calc(var(--spacing-unit) * 8);\n\t}\n</style>\n"
},
{
"path": "src/lib/components/ArticleDescription.svelte",
"chars": 291,
"preview": "<script lang=\"ts\">\n\texport let description: string;\n\texport let slug = '';\n\n\tconst href = slug && `/posts/${slug}`;\n</sc"
},
{
"path": "src/lib/components/ArticleMeta.svelte",
"chars": 458,
"preview": "<script lang=\"ts\">\n\texport let author: string;\n\texport let date: string;\n\n\tconst formattedDate = new Date(date).toDateSt"
},
{
"path": "src/lib/components/ArticleTitle.svelte",
"chars": 587,
"preview": "<script lang=\"ts\">\n\texport let slug = '';\n\texport let title: string;\n\n\tconst id = title\n\t\t.toLowerCase()\n\t\t.replace(/[^a"
},
{
"path": "src/lib/components/Counter.svelte",
"chars": 423,
"preview": "<script lang=\"ts\">\n\tlet count = 0;\n</script>\n\n<div class=\"counter\">\n\t<p class=\"count\">{count}</p>\n\t<button on:click={() "
},
{
"path": "src/lib/components/PageHead.svelte",
"chars": 429,
"preview": "<script lang=\"ts\">\n\texport let title: string;\n\texport let description: string;\n\n\tconst siteTitle = 'SvelteKit + MDsveX B"
},
{
"path": "src/lib/slugFromPath.test.ts",
"chars": 787,
"preview": "import { describe, it, expect } from 'vitest';\nimport { slugFromPath } from './slugFromPath';\n\ndescribe('slugFromPath', "
},
{
"path": "src/lib/slugFromPath.ts",
"chars": 106,
"preview": "export const slugFromPath = (path: string) =>\n\tpath.match(/([\\w-]+)\\.(svelte\\.md|md|svx)/i)?.[1] ?? null;\n"
},
{
"path": "src/posts/a-second-post.svelte.md",
"chars": 2933,
"preview": "---\ntitle: 'A second blog post'\ndescription: \"The first blog post wasn't enough; I had to come back and write more about"
},
{
"path": "src/posts/unpublished-draft-example.svelte.md",
"chars": 298,
"preview": "---\ntitle: 'Unpublished draft example'\ndescription: 'This blog post is not yet ready to be seen by the world'\nauthor: 'M"
},
{
"path": "src/posts/welcome-to-my-blog.svelte.md",
"chars": 3007,
"preview": "---\ntitle: 'Welcome to my blog!'\ndescription: 'I love to write about Svelte and all the cool things that you can build w"
},
{
"path": "src/posts/yet-another-blog-post.svelte.md",
"chars": 2949,
"preview": "---\ntitle: 'Yet another article'\ndescription: \"I know, by now you've probably had enough, but this template looks more f"
},
{
"path": "src/routes/+error.svelte",
"chars": 335,
"preview": "<h2>\n\t<span class=\"errorCode\">404</span>\n\t<span class=\"errorMessage\">Page Not Found</span>\n</h2>\n\n<style>\n\th2 {\n\t\tdispla"
},
{
"path": "src/routes/+layout.svelte",
"chars": 1234,
"preview": "<script lang=\"ts\">\n\timport { page } from '$app/stores';\n</script>\n\n<header>\n\t<a href=\"/\"><h1 class:small={$page.url.path"
},
{
"path": "src/routes/+layout.ts",
"chars": 31,
"preview": "export const prerender = true;\n"
},
{
"path": "src/routes/+page.server.ts",
"chars": 729,
"preview": "import type { PageServerLoad } from './$types';\nimport { slugFromPath } from '$lib/slugFromPath';\n\nconst MAX_POSTS = 10;"
},
{
"path": "src/routes/+page.svelte",
"chars": 976,
"preview": "<script lang=\"ts\">\n\timport type { PageData } from './$types';\n\n\timport PageHead from '$lib/components/PageHead.svelte';\n"
},
{
"path": "src/routes/posts/[slug]/+page.svelte",
"chars": 716,
"preview": "<script lang=\"ts\">\n\timport type { PageData } from './$types';\n\timport type { SvelteComponentTyped } from 'svelte/interna"
},
{
"path": "src/routes/posts/[slug]/+page.ts",
"chars": 729,
"preview": "import type { PageLoad } from './$types';\nimport { slugFromPath } from '$lib/slugFromPath';\nimport { error } from '@svel"
},
{
"path": "svelte.config.js",
"chars": 544,
"preview": "import { mdsvex } from 'mdsvex';\nimport mdsvexConfig from './mdsvex.config.js';\nimport adapter from '@sveltejs/adapter-s"
},
{
"path": "tests/test.ts",
"chars": 709,
"preview": "import { expect, test } from '@playwright/test';\n\ntest('index page has expected content', async ({ page }) => {\n\tawait p"
},
{
"path": "tsconfig.json",
"chars": 532,
"preview": "{\n\t\"extends\": \"./.svelte-kit/tsconfig.json\",\n\t\"compilerOptions\": {\n\t\t\"allowJs\": true,\n\t\t\"checkJs\": true,\n\t\t\"esModuleInte"
},
{
"path": "vite.config.js",
"chars": 215,
"preview": "import { sveltekit } from '@sveltejs/kit/vite';\n\n/** @type {import('vite').UserConfig} */\nconst config = {\n\tplugins: [sv"
}
]
About this extraction
This page contains the full source code of the mvasigh/sveltekit-mdsvex-blog GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 38 files (26.6 KB), approximately 8.3k tokens, and a symbol index with 4 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.