[
  {
    "path": ".eslintignore",
    "content": ".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\npnpm-lock.yaml\npackage-lock.json\nyarn.lock\n"
  },
  {
    "path": ".eslintrc.cjs",
    "content": "module.exports = {\n\troot: true,\n\tparser: '@typescript-eslint/parser',\n\textends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended', 'prettier'],\n\tplugins: ['svelte3', '@typescript-eslint'],\n\tignorePatterns: ['*.cjs'],\n\toverrides: [{ files: ['*.svelte'], processor: 'svelte3/svelte3' }],\n\tsettings: {\n\t\t'svelte3/typescript': () => require('typescript')\n\t},\n\tparserOptions: {\n\t\tsourceType: 'module',\n\t\tecmaVersion: 2020\n\t},\n\tenv: {\n\t\tbrowser: true,\n\t\tes2017: true,\n\t\tnode: true\n\t}\n};\n"
  },
  {
    "path": ".github/workflows/test.yaml",
    "content": "name: Node.js tests\n\non:\n  push:\n    branches: [ main ]\n  pull_request:\n    branches: [ main ]\n\njobs:\n  build:\n\n    runs-on: ubuntu-latest\n\n    strategy:\n      matrix:\n        node-version: [16.x, 18.x]\n\n    steps:\n      - uses: actions/checkout@v3\n      - name: Use Node.js ${{ matrix.node-version }}\n        uses: actions/setup-node@v3\n        with:\n          node-version: ${{ matrix.node-version }}\n      - uses: pnpm/action-setup@v2\n        with:\n          version: 7\n      - name: Install packages\n        run: pnpm install\n      - name: Install Playwright\n        run: pnpx playwright install\n      - name: Build\n        run: pnpm build\n      - name: Test\n        run: pnpm test"
  },
  {
    "path": ".gitignore",
    "content": ".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.timestamp-*\n"
  },
  {
    "path": ".npmrc",
    "content": "engine-strict=true\n"
  },
  {
    "path": ".prettierignore",
    "content": ".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\npnpm-lock.yaml\npackage-lock.json\nyarn.lock\n"
  },
  {
    "path": ".prettierrc",
    "content": "{\n\t\"useTabs\": true,\n\t\"tabWidth\": 2,\n\t\"singleQuote\": true,\n\t\"trailingComma\": \"none\",\n\t\"printWidth\": 100,\n\t\"plugins\": [\"prettier-plugin-svelte\"],\n\t\"pluginSearchDirs\": [\".\"],\n\t\"overrides\": [{ \"files\": \"*.svelte\", \"options\": { \"parser\": \"svelte\" } }]\n}\n"
  },
  {
    "path": ".vscode/settings.json",
    "content": "{\n\t\"explorer.sortOrder\": \"type\"\n}\n"
  },
  {
    "path": "LICENSE",
    "content": "Copyright 2021 Mehdi Vasigh\n\nPermission 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:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE 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.\n"
  },
  {
    "path": "README.md",
    "content": "# `sveltekit-mdsvex-blog`\n\nMinimalistic blog site template, built with [TypeScript](https://www.typescriptlang.org/), [Svelte](https://svelte.dev), [SvelteKit](https://kit.svelte.dev) and [MDsveX](https://mdsvex.com).\n\n[Live demo](https://sveltekit-mdsvex-blog.netlify.app)\n\n## Getting started\n\nFirst, clone the repository and navigate to the project directory:\n\n```bash\ngit clone https://github.com/mvasigh/sveltekit-mdsvex-blog.git my-blog\ncd my-blog\n```\n\nIf you want to have a fresh commit history, blow away the `.git` directory and re-initialize the repository locally:\n\n```bash\nrm -rf .git\ngit init\n```\n\nNext, install dependencies with NPM:\n\n```bash\nnpm install # or `pnpm i`\n```\n\nFinally, run the local development server:\n\n```bash\nnpm run dev # or `pnpm dev`\n```\n\n## Building for production\n\nThis 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`.\n\n## Starting from scratch\n\nYou 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`:\n\n1. Create a new project per the [SvelteKit docs](https://kit.svelte.dev/docs#introduction-getting-started)\n2. [Add MDsveX to your project](https://github.com/svelte-add/mdsvex#-adding-to-sveltekit) using svelte-add\n3. Configure your site to your liking; files with the `.svelte.md`, `.md` and `.svx` extensions will be picked up by MDsveX by default\n\n## Questions?\n\nFeel 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).\n"
  },
  {
    "path": "mdsvex.config.js",
    "content": "import remarkGithub from 'remark-github';\nimport remarkAbbr from 'remark-abbr';\nimport rehypeSlug from 'rehype-slug';\nimport rehypeAutolinkHeadings from 'rehype-autolink-headings';\nimport { defineMDSveXConfig as defineConfig } from 'mdsvex';\n\nconst config = defineConfig({\n\textensions: ['.svelte.md', '.md', '.svx'],\n\n\tsmartypants: {\n\t\tdashes: 'oldschool'\n\t},\n\n\tremarkPlugins: [\n\t\t[\n\t\t\tremarkGithub,\n\t\t\t{\n\t\t\t\t// TODO: Replace with your own repository\n\t\t\t\trepository: 'https://github.com/mvasigh/sveltekit-mdsvex-blog.git'\n\t\t\t}\n\t\t],\n\t\tremarkAbbr\n\t],\n\trehypePlugins: [\n\t\trehypeSlug,\n\t\t[\n\t\t\trehypeAutolinkHeadings,\n\t\t\t{\n\t\t\t\tbehavior: 'wrap'\n\t\t\t}\n\t\t]\n\t]\n});\n\nexport default config;\n"
  },
  {
    "path": "package.json",
    "content": "{\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\": \"vite build\",\n\t\t\"preview\": \"vite preview\",\n\t\t\"check\": \"svelte-kit sync && svelte-check --tsconfig ./tsconfig.json\",\n\t\t\"check:watch\": \"svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch\",\n\t\t\"test:unit\": \"vitest\",\n\t\t\"test:playwright\": \"playwright test\",\n\t\t\"test\": \"pnpm test:unit run && pnpm test:playwright\",\n\t\t\"lint\": \"prettier --plugin-search-dir . --check . && eslint .\",\n\t\t\"format\": \"prettier --plugin-search-dir . --write .\"\n\t},\n\t\"devDependencies\": {\n\t\t\"@playwright/test\": \"^1.28.1\",\n\t\t\"@sveltejs/adapter-static\": \"^1.0.0\",\n\t\t\"@sveltejs/kit\": \"^1.0.0\",\n\t\t\"@typescript-eslint/eslint-plugin\": \"^5.45.0\",\n\t\t\"@typescript-eslint/parser\": \"^5.45.0\",\n\t\t\"eslint\": \"^8.28.0\",\n\t\t\"eslint-config-prettier\": \"^8.5.0\",\n\t\t\"eslint-plugin-svelte3\": \"^4.0.0\",\n\t\t\"mdsvex\": \"^0.10.6\",\n\t\t\"prettier\": \"^2.8.0\",\n\t\t\"prettier-plugin-svelte\": \"^2.8.1\",\n\t\t\"rehype-autolink-headings\": \"^6.1.1\",\n\t\t\"rehype-slug\": \"^5.1.0\",\n\t\t\"remark-abbr\": \"^1.4.1\",\n\t\t\"remark-github\": \"^11.2.4\",\n\t\t\"svelte\": \"^3.54.0\",\n\t\t\"svelte-check\": \"^2.9.2\",\n\t\t\"tslib\": \"^2.4.1\",\n\t\t\"typescript\": \"^4.9.3\",\n\t\t\"vite\": \"^4.0.0\",\n\t\t\"vitest\": \"^0.25.3\"\n\t},\n\t\"type\": \"module\"\n}\n"
  },
  {
    "path": "playwright.config.ts",
    "content": "import type { PlaywrightTestConfig } from '@playwright/test';\n\nconst config: PlaywrightTestConfig = {\n\twebServer: {\n\t\tcommand: 'npm run build && npm run preview',\n\t\tport: 4173\n\t},\n\ttestDir: 'tests'\n};\n\nexport default config;\n"
  },
  {
    "path": "src/app.d.ts",
    "content": "// See https://kit.svelte.dev/docs/types#app\n// for information about these interfaces\n// and what to do when importing types\ndeclare namespace App {\n\t// interface Error {}\n\t// interface Locals {}\n\t// interface PageData {}\n\t// interface Platform {}\n\n\tinterface MdsvexFile {\n\t\tdefault: import('svelte/internal').SvelteComponent;\n\t\tmetadata: Record<string, string>;\n\t}\n\n\ttype MdsvexResolver = () => Promise<MdsvexFile>;\n\n\tinterface BlogPost {\n\t\tslug: string;\n\t\ttitle: string;\n\t\tauthor: string;\n\t\tdescription: string;\n\t\tdate: string;\n\t\tpublished: boolean;\n\t}\n}\n"
  },
  {
    "path": "src/app.html",
    "content": "<!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.png\" />\n\t\t<meta name=\"viewport\" content=\"width=device-width\" />\n\t\t%sveltekit.head%\n\t</head>\n\t<body data-sveltekit-preload-data=\"hover\">\n\t\t<div style=\"display: contents\">%sveltekit.body%</div>\n\t</body>\n</html>\n"
  },
  {
    "path": "src/lib/components/Article.svelte",
    "content": "<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",
    "content": "<script lang=\"ts\">\n\texport let description: string;\n\texport let slug = '';\n\n\tconst href = slug && `/posts/${slug}`;\n</script>\n\n<p>\n\t{description}\n\n\t{#if slug}\n\t\t<a {href}>Read More &rarr;</a>\n\t{/if}\n</p>\n\n<style>\n\tp {\n\t\tmargin: 0;\n\t\tmargin-bottom: calc(var(--spacing-unit) * 8);\n\t}\n</style>\n"
  },
  {
    "path": "src/lib/components/ArticleMeta.svelte",
    "content": "<script lang=\"ts\">\n\texport let author: string;\n\texport let date: string;\n\n\tconst formattedDate = new Date(date).toDateString();\n</script>\n\n<p>\n\t<span class=\"author\">{author}</span>\n\t<span class=\"date\">{formattedDate}</span>\n</p>\n\n<style>\n  p {\n    margin: 0;\n    margin-bottom: calc(var(--spacing-unit) * 4);\n  }\n\n\t.author {\n    font-weight: bold;\n\t\tmargin-right: calc(var(--spacing-unit) * 2);\n\t}\n\n\t.date {\n\t\tcolor: var(--color-text-secondary);\n\t}\n</style>\n"
  },
  {
    "path": "src/lib/components/ArticleTitle.svelte",
    "content": "<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-zA-Z ]/g, '')\n\t\t.replace(/\\s/g, '-');\n\n\tconst href = slug ? `/posts/${slug}` : '#' + id;\n</script>\n\n{#if slug}\n\t<h3 class=\"heading\" class:large={!slug} {id}>\n\t\t<a {href}>\n\t\t\t{title}\n\t\t</a>\n\t</h3>\n{:else}\n\t<h2 class=\"heading\" class:large={!slug} {id}>\n\t\t<a {href}>\n\t\t\t{title}\n\t\t</a>\n\t</h2>\n{/if}\n\n<style>\n\th2 {\n\t\tmargin: 0;\n\t}\n\n\t.heading {\n\t\tmargin: 0;\n\t\tfont-size: 1.8rem;\n\t}\n\n\t.large {\n\t\tmargin-top: calc(var(--spacing-unit) * 12);\n\t\tfont-size: 2.2rem;\n\t}\n</style>\n"
  },
  {
    "path": "src/lib/components/Counter.svelte",
    "content": "<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={() => (count += 1)}>Increment</button>\n</div>\n\n<style>\n  .counter {\n    text-align: center;\n  }\n\n  .count {\n    font-size: 2rem;\n    margin-bottom: calc(var(--spacing-unit) * 2);\n  }\n\n  .counter {\n    margin-top: calc(var(--spacing-unit) * 4);\n    margin-bottom: calc(var(--spacing-unit) * 4);\n  }\n</style>"
  },
  {
    "path": "src/lib/components/PageHead.svelte",
    "content": "<script lang=\"ts\">\n\texport let title: string;\n\texport let description: string;\n\n\tconst siteTitle = 'SvelteKit + MDsveX Blog';\n\tconst formattedTitle = title ? `${title} | ${siteTitle}` : siteTitle;\n</script>\n\n<svelte:head>\n\t<title>{formattedTitle}</title>\n\n\t<meta property=\"og:site_name\" content={siteTitle} />\n\t<meta property=\"og:title\" content={title} />\n\t<meta property=\"og:description\" content={description} />\n</svelte:head>\n"
  },
  {
    "path": "src/lib/slugFromPath.test.ts",
    "content": "import { describe, it, expect } from 'vitest';\nimport { slugFromPath } from './slugFromPath';\n\ndescribe('slugFromPath', () => {\n\tit('extracts slug from paths correctly', () => {\n\t\tconst cases = [\n\t\t\t{\n\t\t\t\tpath: '/foo/bar/test-slug.md',\n\t\t\t\texpected: 'test-slug'\n\t\t\t},\n\t\t\t{\n\t\t\t\tpath: '/foo/bar/test-slug.svx',\n\t\t\t\texpected: 'test-slug'\n\t\t\t},\n\t\t\t{\n\t\t\t\tpath: '/foo/bar/test-slug.svelte.md',\n\t\t\t\texpected: 'test-slug'\n\t\t\t}\n\t\t];\n\n\t\tcases.forEach(({ path, expected }) => expect(slugFromPath(path)).toBe(expected));\n\t});\n\n\tit('returns null for unknown extension', () => {\n\t\tconst path = '/foo/bar/test-slug.abc';\n\n\t\texpect(slugFromPath(path)).toBeNull();\n\t});\n\n\tit('returns null for no extension', () => {\n\t\tconst path = '/foo/bar/test-slug';\n\n\t\texpect(slugFromPath(path)).toBeNull();\n\t});\n});\n"
  },
  {
    "path": "src/lib/slugFromPath.ts",
    "content": "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",
    "content": "---\ntitle: 'A second blog post'\ndescription: \"The first blog post wasn't enough; I had to come back and write more about Svelte and SvelteKit.\"\nauthor: 'Mehdi Vasigh'\ndate: '2021-05-03'\npublished: true\n---\n\n<script>\n  import Counter from '$lib/components/Counter.svelte'\n</script>\n\nLorem 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.\n\nHere's a random Svelte component thrown into my MDsveX markdown:\n\n<Counter />\n\n### Heading\n\nIn 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.\n\n### Heading\n\nNullam 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.\n\nUt 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.\n"
  },
  {
    "path": "src/posts/unpublished-draft-example.svelte.md",
    "content": "---\ntitle: 'Unpublished draft example'\ndescription: 'This blog post is not yet ready to be seen by the world'\nauthor: 'Mehdi Vasigh'\ndate: '2021-05-10'\npublished: false\n---\n\nThis is an example of an unpublished post! Your site will display a 404 page until you set your `published` flag to `true`.\n"
  },
  {
    "path": "src/posts/welcome-to-my-blog.svelte.md",
    "content": "---\ntitle: 'Welcome to my blog!'\ndescription: 'I love to write about Svelte and all the cool things that you can build with it.'\nauthor: 'Mehdi Vasigh'\ndate: '2021-04-21'\npublished: true\n---\n\n<script>\n  import Counter from '$lib/components/Counter.svelte'\n</script>\n\nLorem 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.\n\nHere's a random Svelte component thrown into my MDsveX markdown:\n\n<Counter />\n\n## Link to other page\n\n[A second blog post](/posts/a-second-post)\n\n## Example heading\n\nIn 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.\n\n## Another example heading\n\nNullam 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.\n\nUt 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.\n"
  },
  {
    "path": "src/posts/yet-another-blog-post.svelte.md",
    "content": "---\ntitle: 'Yet another article'\ndescription: \"I know, by now you've probably had enough, but this template looks more full with three posts, and here we are.\"\nauthor: 'Mehdi Vasigh'\ndate: '2021-05-05'\npublished: true\n---\n\n<script>\n  import Counter from '$lib/components/Counter.svelte'\n</script>\n\nLorem 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.\n\nHere's a random Svelte component thrown into my MDsveX markdown:\n\n<Counter />\n\n### Heading\n\nIn 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.\n\n### Heading\n\nNullam 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.\n\nUt 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.\n"
  },
  {
    "path": "src/routes/+error.svelte",
    "content": "<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\tdisplay: flex;\n\t\tflex-direction: column;\n\t\tjustify-content: center;\n\t\talign-items: center;\n\t\tfont-weight: 400;\n\t\tmin-height: 400px;\n\t}\n\n\t.errorCode {\n\t\tfont-size: 8rem;\n\t}\n\n\t.errorMessage {\n\t\tfont-size: 1rem;\n\t}\n</style>\n"
  },
  {
    "path": "src/routes/+layout.svelte",
    "content": "<script lang=\"ts\">\n\timport { page } from '$app/stores';\n</script>\n\n<header>\n\t<a href=\"/\"><h1 class:small={$page.url.pathname !== '/'}>SvelteKit + MDsveX Blog</h1></a>\n</header>\n\n<main>\n\t<slot />\n</main>\n\n<footer>\n\t<p>\n\t\tCopyright &#169; <a href=\"https://twitter.com/mehdi_vasigh\">Mehdi Vasigh</a>, {new Date().getFullYear()}\n\t</p>\n</footer>\n\n<style>\n\t:global(:root) {\n\t\t--spacing-unit: 4px;\n\t\t--color-background: #e5e5e5;\n\t\t--color-text-primary: #212121;\n\t\t--color-text-secondary: #5a5a5a;\n\t}\n\n\t:global(body) {\n\t\tmargin: 0 auto;\n\t\tmax-width: 75ch;\n\t\tpadding: calc(var(--spacing-unit) * 8);\n\t\tfont-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu',\n\t\t\t'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif;\n\t\t-webkit-font-smoothing: antialiased;\n\t\t-moz-osx-font-smoothing: grayscale;\n\n\t\tbackground-color: var(--color-background);\n\t\tcolor: var(--color-text-primary);\n\t\tline-height: 1.51;\n\t\tfont-size: 18px;\n\t}\n\n\t:global(a, a:visited, a:active) {\n\t\ttext-decoration: none;\n\t\tcolor: var(--color-text-primary);\n\t\tfont-weight: 700;\n\t}\n\n\t:global(a:hover) {\n\t\ttext-decoration: underline;\n\t}\n\n\t.small {\n\t\tfont-size: 1.6rem;\n\t}\n\n\tfooter {\n\t\tmargin-top: calc(var(--spacing-unit) * 8);\n\t}\n</style>\n"
  },
  {
    "path": "src/routes/+layout.ts",
    "content": "export const prerender = true;\n"
  },
  {
    "path": "src/routes/+page.server.ts",
    "content": "import type { PageServerLoad } from './$types';\nimport { slugFromPath } from '$lib/slugFromPath';\n\nconst MAX_POSTS = 10;\n\nexport const load: PageServerLoad = async ({ url }) => {\n\tconst modules = import.meta.glob(`/src/posts/*.{md,svx,svelte.md}`);\n\n\tconst postPromises = Object.entries(modules).map(([path, resolver]) =>\n\t\tresolver().then(\n\t\t\t(post) =>\n\t\t\t\t({\n\t\t\t\t\tslug: slugFromPath(path),\n\t\t\t\t\t...(post as unknown as App.MdsvexFile).metadata\n\t\t\t\t} as App.BlogPost)\n\t\t)\n\t);\n\n\tconst posts = await Promise.all(postPromises);\n\tconst publishedPosts = posts.filter((post) => post.published).slice(0, MAX_POSTS);\n\n\tpublishedPosts.sort((a, b) => (new Date(a.date) > new Date(b.date) ? -1 : 1));\n\n\treturn { posts: publishedPosts };\n};\n"
  },
  {
    "path": "src/routes/+page.svelte",
    "content": "<script lang=\"ts\">\n\timport type { PageData } from './$types';\n\n\timport PageHead from '$lib/components/PageHead.svelte';\n\timport Article from '$lib/components/Article.svelte';\n\timport ArticleTitle from '$lib/components/ArticleTitle.svelte';\n\timport ArticleMeta from '$lib/components/ArticleMeta.svelte';\n\timport ArticleDescription from '$lib/components/ArticleDescription.svelte';\n\n\texport let data: PageData;\n</script>\n\n<PageHead title=\"Home\" description=\"An awesome blog about development with Svelte\" />\n\n<p>\n\tThis is a minimalistic example of a blog built with <a href=\"https://kit.svelte.dev\">SvelteKit</a>\n\tand <a href=\"https://mdsvex.com/\">MDsveX</a>.\n\t<a href=\"https://github.com/mvasigh/sveltekit-mdsvex-blog\">View source code on Github.</a>\n</p>\n\n{#each data.posts as { slug, title, author, description, date }}\n\t<Article>\n\t\t<ArticleTitle {slug} {title} />\n\t\t<ArticleMeta {author} {date} />\n\t\t<ArticleDescription {description} {slug} />\n\t</Article>\n{/each}\n\n<slot />\n"
  },
  {
    "path": "src/routes/posts/[slug]/+page.svelte",
    "content": "<script lang=\"ts\">\n\timport type { PageData } from './$types';\n\timport type { SvelteComponentTyped } from 'svelte/internal';\n\n\timport PageHead from '$lib/components/PageHead.svelte';\n\timport ArticleTitle from '$lib/components/ArticleTitle.svelte';\n\timport ArticleMeta from '$lib/components/ArticleMeta.svelte';\n\n\texport let data: PageData;\n\n\ttype C = $$Generic<typeof SvelteComponentTyped<any, any, any>>;\n\t$: component = data.component as unknown as C;\n</script>\n\n<PageHead title={data.frontmatter.title} description={data.frontmatter.description} />\n<ArticleTitle title={data.frontmatter.title} />\n<ArticleMeta author={data.frontmatter.author} date={data.frontmatter.date} />\n\n<svelte:component this={component} />\n"
  },
  {
    "path": "src/routes/posts/[slug]/+page.ts",
    "content": "import type { PageLoad } from './$types';\nimport { slugFromPath } from '$lib/slugFromPath';\nimport { error } from '@sveltejs/kit';\n\nexport const load: PageLoad = async ({ params }) => {\n\tconst modules = import.meta.glob(`/src/posts/*.{md,svx,svelte.md}`);\n\n\tlet match: { path?: string; resolver?: App.MdsvexResolver } = {};\n\tfor (const [path, resolver] of Object.entries(modules)) {\n\t\tif (slugFromPath(path) === params.slug) {\n\t\t\tmatch = { path, resolver: resolver as unknown as App.MdsvexResolver };\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tconst post = await match?.resolver?.();\n\n\tif (!post || !post.metadata.published) {\n\t\tthrow error(404); // Couldn't resolve the post\n\t}\n\n\treturn {\n\t\tcomponent: post.default,\n\t\tfrontmatter: post.metadata\n\t};\n};\n"
  },
  {
    "path": "svelte.config.js",
    "content": "import { mdsvex } from 'mdsvex';\nimport mdsvexConfig from './mdsvex.config.js';\nimport adapter from '@sveltejs/adapter-static';\nimport { vitePreprocess } from '@sveltejs/kit/vite';\n\n/** @type {import('@sveltejs/kit').Config} */\nconst config = {\n\textensions: ['.svelte', ...mdsvexConfig.extensions],\n\n\t// Consult https://kit.svelte.dev/docs/integrations#preprocessors\n\t// for more information about preprocessors\n\tpreprocess: [vitePreprocess(), mdsvex(mdsvexConfig)],\n\n\tkit: {\n\t\tadapter: adapter({ strict: false })\n\t}\n};\n\nexport default config;\n"
  },
  {
    "path": "tests/test.ts",
    "content": "import { expect, test } from '@playwright/test';\n\ntest('index page has expected content', async ({ page }) => {\n\tawait page.goto('/');\n\n\tconst articles = await page.$$('article');\n\n\texpect(await page.textContent('h1')).toBe('SvelteKit + MDsveX Blog');\n\texpect(articles.length).toBeGreaterThan(0);\n\texpect(articles.length).not.toBeGreaterThan(10);\n\tfor (const article of articles) {\n\t\texpect(await article.$('a')).not.toBeFalsy();\n\t}\n});\n\ntest('clicking on article title in home page navigates to the article', async ({ page }) => {\n\tawait page.goto('/');\n\tconst title = await page.textContent('article h3');\n\n\tawait page.getByText(title || '').click();\n\n\texpect(await page.textContent('h2')).toBe(title);\n});\n"
  },
  {
    "path": "tsconfig.json",
    "content": "{\n\t\"extends\": \"./.svelte-kit/tsconfig.json\",\n\t\"compilerOptions\": {\n\t\t\"allowJs\": true,\n\t\t\"checkJs\": true,\n\t\t\"esModuleInterop\": true,\n\t\t\"forceConsistentCasingInFileNames\": true,\n\t\t\"resolveJsonModule\": true,\n\t\t\"skipLibCheck\": true,\n\t\t\"sourceMap\": true,\n\t\t\"strict\": true\n\t}\n\t// Path aliases are handled by https://kit.svelte.dev/docs/configuration#alias\n\t//\n\t// If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes\n\t// from the referenced tsconfig.json - TypeScript does not merge them in\n}\n"
  },
  {
    "path": "vite.config.js",
    "content": "import { sveltekit } from '@sveltejs/kit/vite';\n\n/** @type {import('vite').UserConfig} */\nconst config = {\n\tplugins: [sveltekit()],\n\ttest: {\n\t\tinclude: ['src/**/*.{test,spec}.{js,ts}']\n\t}\n};\n\nexport default config;\n"
  }
]