mirror of
https://github.com/BradNut/personal-website-sveltekit
synced 2025-09-08 23:20:18 +00:00
commit
96bea41a06
9 changed files with 417 additions and 762 deletions
46
.github/workflows/node.js.yml
vendored
Normal file
46
.github/workflows/node.js.yml
vendored
Normal file
|
|
@ -0,0 +1,46 @@
|
||||||
|
# This workflow will do a clean installation of node dependencies, cache/restore them, build the source code and run tests across different versions of node
|
||||||
|
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-nodejs
|
||||||
|
|
||||||
|
name: Node.js CI
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: ['master', 'development']
|
||||||
|
pull_request:
|
||||||
|
branches: ['master']
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
node-version: [18.x, 20.x]
|
||||||
|
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
|
||||||
|
|
||||||
|
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
|
||||||
|
name: Install pnpm
|
||||||
|
with:
|
||||||
|
version: 8
|
||||||
|
run_install: false
|
||||||
|
- name: Get pnpm store directory
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
|
||||||
|
- uses: actions/cache@v3
|
||||||
|
name: Setup pnpm cache
|
||||||
|
with:
|
||||||
|
path: ${{ env.STORE_PATH }}
|
||||||
|
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-pnpm-store-
|
||||||
|
- name: Install dependencies
|
||||||
|
run: pnpm install
|
||||||
|
- run: pnpm run build
|
||||||
|
- run: pnpm test
|
||||||
18
package.json
18
package.json
|
|
@ -6,22 +6,21 @@
|
||||||
"dev": "vite dev",
|
"dev": "vite dev",
|
||||||
"build": "vite build",
|
"build": "vite build",
|
||||||
"preview": "vite preview",
|
"preview": "vite preview",
|
||||||
"test": "playwright test",
|
"test": "npm run test:integration && npm run test:unit",
|
||||||
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
||||||
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
|
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
|
||||||
"test:unit": "vitest",
|
|
||||||
"lint": "prettier --plugin-search-dir . --check . && eslint .",
|
"lint": "prettier --plugin-search-dir . --check . && eslint .",
|
||||||
"format": "prettier --plugin-search-dir . --write .",
|
"format": "prettier --plugin-search-dir . --write .",
|
||||||
"site:update": "pnpm update -i -L"
|
"test:integration": "playwright test",
|
||||||
|
"test:unit": "vitest"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@iconify-icons/material-symbols": "^1.2.56",
|
"@iconify-icons/material-symbols": "^1.2.56",
|
||||||
"@iconify-icons/mdi": "^1.2.47",
|
"@iconify-icons/mdi": "^1.2.47",
|
||||||
"@iconify-icons/radix-icons": "^1.2.9",
|
"@iconify-icons/radix-icons": "^1.2.9",
|
||||||
"@iconify-icons/simple-icons": "^1.2.70",
|
"@iconify-icons/simple-icons": "^1.2.70",
|
||||||
"@leveluptuts/svelte-side-menu": "^1.0.5",
|
"@melt-ui/pp": "^0.1.2",
|
||||||
"@leveluptuts/svelte-toy": "^2.0.3",
|
"@playwright/test": "^1.28.1",
|
||||||
"@playwright/test": "^1.36.2",
|
|
||||||
"@sveltejs/adapter-static": "^2.0.3",
|
"@sveltejs/adapter-static": "^2.0.3",
|
||||||
"@sveltejs/adapter-vercel": "^1.0.6",
|
"@sveltejs/adapter-vercel": "^1.0.6",
|
||||||
"@sveltejs/kit": "^1.25.1",
|
"@sveltejs/kit": "^1.25.1",
|
||||||
|
|
@ -43,22 +42,23 @@
|
||||||
"prettier": "^2.8.8",
|
"prettier": "^2.8.8",
|
||||||
"prettier-plugin-svelte": "^2.10.1",
|
"prettier-plugin-svelte": "^2.10.1",
|
||||||
"sass": "^1.65.1",
|
"sass": "^1.65.1",
|
||||||
"scrape-it": "^5.3.2",
|
"scrape-it": "^6.0.1",
|
||||||
"sharp": "^0.32.6",
|
"sharp": "^0.32.6",
|
||||||
"svelte": "^4.2.1",
|
"svelte": "^4.2.1",
|
||||||
"svelte-check": "^3.4.6",
|
"svelte-check": "^3.4.6",
|
||||||
"svelte-lazy-loader": "^1.0.0",
|
"svelte-lazy-loader": "^1.0.0",
|
||||||
"svelte-preprocess": "^5.0.4",
|
"svelte-preprocess": "^5.0.4",
|
||||||
|
"svelte-sequential-preprocessor": "^2.0.1",
|
||||||
"tslib": "^2.6.2",
|
"tslib": "^2.6.2",
|
||||||
"typescript": "^5.2.2",
|
"typescript": "^5.2.2",
|
||||||
"vanilla-lazyload": "^17.8.4",
|
"vanilla-lazyload": "^17.8.4",
|
||||||
"vite": "^4.4.9",
|
"vite": "^4.4.9",
|
||||||
"vite-imagetools": "^5.0.8",
|
"vite-imagetools": "^5.0.8",
|
||||||
"vitest": "^0.25.3"
|
"vitest": "^0.32.2"
|
||||||
},
|
},
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@melt-ui/svelte": "^0.19.1",
|
"@melt-ui/svelte": "^0.50.0",
|
||||||
"@types/nprogress": "^0.2.1",
|
"@types/nprogress": "^0.2.1",
|
||||||
"ioredis": "^5.3.2",
|
"ioredis": "^5.3.2",
|
||||||
"nprogress": "^0.2.0"
|
"nprogress": "^0.2.0"
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,11 @@ import type { PlaywrightTestConfig } from '@playwright/test';
|
||||||
|
|
||||||
const config: PlaywrightTestConfig = {
|
const config: PlaywrightTestConfig = {
|
||||||
webServer: {
|
webServer: {
|
||||||
command: 'npm run build && npm run preview',
|
command: 'pnpm run build && pnpm run preview',
|
||||||
port: 4173
|
port: 4173
|
||||||
},
|
},
|
||||||
testDir: 'tests'
|
testDir: 'tests',
|
||||||
|
testMatch: /(.+\.)?(test|spec)\.[jt]s/
|
||||||
};
|
};
|
||||||
|
|
||||||
export default config;
|
export default config;
|
||||||
|
|
|
||||||
983
pnpm-lock.yaml
983
pnpm-lock.yaml
File diff suppressed because it is too large
Load diff
|
|
@ -4,13 +4,8 @@
|
||||||
import linkedin from '@iconify-icons/radix-icons/linkedin-logo';
|
import linkedin from '@iconify-icons/radix-icons/linkedin-logo';
|
||||||
import twitter from '@iconify-icons/radix-icons/twitter-logo';
|
import twitter from '@iconify-icons/radix-icons/twitter-logo';
|
||||||
|
|
||||||
export let showFacebook: boolean = false;
|
|
||||||
export let showInstagram: boolean = false;
|
|
||||||
export let showTwitter: boolean = false;
|
export let showTwitter: boolean = false;
|
||||||
export let showLinkedIn: boolean = false;
|
export let showLinkedIn: boolean = false;
|
||||||
export let showReddit: boolean = false;
|
|
||||||
export let showGithub: boolean = false;
|
|
||||||
export let showYoutube: boolean = false;
|
|
||||||
export let showEmail: boolean = false;
|
export let showEmail: boolean = false;
|
||||||
export let userNames: Record<string, string>;
|
export let userNames: Record<string, string>;
|
||||||
export let showText: boolean = false;
|
export let showText: boolean = false;
|
||||||
|
|
@ -30,7 +25,7 @@
|
||||||
aria-label="Contact through Twitter"
|
aria-label="Contact through Twitter"
|
||||||
rel="noreferrer"
|
rel="noreferrer"
|
||||||
>
|
>
|
||||||
<iconify-icon icon={github} class="twitter-contact" width="24" height="24" />
|
<iconify-icon icon={twitter} class="twitter-contact" width="24" height="24" />
|
||||||
</a>
|
</a>
|
||||||
</span>
|
</span>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
@ -60,7 +55,7 @@
|
||||||
</a>
|
</a>
|
||||||
</span>
|
</span>
|
||||||
{/if}
|
{/if}
|
||||||
{#if email && userNames?.email}
|
{#if showEmail && userNames?.email}
|
||||||
<span>
|
<span>
|
||||||
<a
|
<a
|
||||||
href={`mailto:${userNames.email}`}
|
href={`mailto:${userNames.email}`}
|
||||||
|
|
@ -79,19 +74,14 @@
|
||||||
div {
|
div {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
/* margin-top: 1rem; */
|
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
&.justifyCenter {
|
&.justifyCenter {
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
--facebookColor: #3b5999;
|
|
||||||
--instagramColor: #8e37b8;
|
|
||||||
--redditColor: #ff4500;
|
|
||||||
--twitterColor: #1da1f2;
|
--twitterColor: #1da1f2;
|
||||||
--linkedInColor: #0a66c2;
|
--linkedInColor: #0a66c2;
|
||||||
--youTubeColor: #ff0000;
|
|
||||||
--githubColor: #72757e;
|
--githubColor: #72757e;
|
||||||
--emailColor: var(--linkHover);
|
--emailColor: var(--linkHover);
|
||||||
}
|
}
|
||||||
|
|
@ -112,24 +102,6 @@
|
||||||
font-size: 3.55rem;
|
font-size: 3.55rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.facebook-contact {
|
|
||||||
color: var(--textColor);
|
|
||||||
&:hover {
|
|
||||||
color: var(--facebookColor);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.instagram-contact {
|
|
||||||
color: var(--textColor);
|
|
||||||
&:hover {
|
|
||||||
color: var(--instagramColor);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.reddit-contact {
|
|
||||||
color: var(--textColor);
|
|
||||||
&:hover {
|
|
||||||
color: var(--redditColor);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.twitter-contact {
|
.twitter-contact {
|
||||||
color: var(--textColor);
|
color: var(--textColor);
|
||||||
&:hover {
|
&:hover {
|
||||||
|
|
@ -142,12 +114,6 @@
|
||||||
color: var(--linkedInColor);
|
color: var(--linkedInColor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.youtube-contact {
|
|
||||||
color: var(--textColor);
|
|
||||||
&:hover {
|
|
||||||
color: var(--youTubeColor);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.github-contact {
|
.github-contact {
|
||||||
color: var(--textColor);
|
color: var(--textColor);
|
||||||
&:hover {
|
&:hover {
|
||||||
|
|
|
||||||
19
src/lib/util/fetchBandcampAlbums.test.ts
Normal file
19
src/lib/util/fetchBandcampAlbums.test.ts
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
import { describe, it, expect } from 'vitest';
|
||||||
|
import { fetchBandcampAlbums } from './fetchBandcampAlbums';
|
||||||
|
|
||||||
|
describe('test fetchBandcampAlbums', () => {
|
||||||
|
it('fetches bandcamp albums', async () => {
|
||||||
|
const albums = await fetchBandcampAlbums();
|
||||||
|
console.log('albums');
|
||||||
|
expect(albums).not.toBeNull();
|
||||||
|
expect(albums).toBeTruthy();
|
||||||
|
expect(albums?.length).toBeGreaterThan(0);
|
||||||
|
for (const album of albums) {
|
||||||
|
expect(album?.artist).toHaveLength;
|
||||||
|
expect(album?.artwork).toHaveLength;
|
||||||
|
expect(album?.src).toHaveLength;
|
||||||
|
expect(album?.title).toHaveLength;
|
||||||
|
expect(album?.url).toHaveLength;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { createTabs } from '@melt-ui/svelte';
|
import { createTabs, melt } from '@melt-ui/svelte';
|
||||||
import GitHub from '@iconify-icons/simple-icons/github';
|
import GitHub from '@iconify-icons/simple-icons/github';
|
||||||
import SEO from '$lib/components/SEO.svelte';
|
import SEO from '$lib/components/SEO.svelte';
|
||||||
import Portfolio from '$lib/components/Portfolio.svelte';
|
import Portfolio from '$lib/components/Portfolio.svelte';
|
||||||
|
|
@ -13,24 +13,30 @@
|
||||||
import OldWebsite from '$lib/content/portfolio/personal/old-website.md';
|
import OldWebsite from '$lib/content/portfolio/personal/old-website.md';
|
||||||
import ExternalLink from '$lib/components/ExternalLink.svelte';
|
import ExternalLink from '$lib/components/ExternalLink.svelte';
|
||||||
|
|
||||||
const { root, list, content, trigger } = createTabs({
|
const {
|
||||||
value: 'personal',
|
elements: { root, list, content, trigger }
|
||||||
});
|
} = createTabs({
|
||||||
|
defaultValue: 'personal'
|
||||||
|
});
|
||||||
|
|
||||||
|
const triggers = [
|
||||||
|
{ id: 'personal', title: 'Personal Sites' },
|
||||||
|
{ id: 'professional', title: 'Professional Sites'}
|
||||||
|
];
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<SEO title="Portfolio" />
|
<SEO title="Portfolio" />
|
||||||
|
|
||||||
<h1>Portfolio!</h1>
|
<h1>Portfolio!</h1>
|
||||||
<div {...$root} class="root tab-group">
|
<div use:melt={$root} class="root tab-group">
|
||||||
<div {...$list} aria-label="tabs portfolios" class="list tab-list">
|
<div use:melt={$list} aria-label="tabs portfolios" class="list tab-list">
|
||||||
<button {...$trigger('personal')} use:trigger class="trigger">
|
{#each triggers as triggerItem}
|
||||||
<h2>Personal Sites</h2>
|
<button use:melt={$trigger(triggerItem.id)} class="trigger" type="button">
|
||||||
</button>
|
<h2>{triggerItem.title}</h2>
|
||||||
<button {...$trigger('professional')} use:trigger value="professional-sites" class="trigger">
|
</button>
|
||||||
<h2>Professional Sites</h2>
|
{/each}
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
<div {...$content('personal')} class="content">
|
<div use:melt={$content('personal')} class="content">
|
||||||
<Portfolio name="Personal Website"
|
<Portfolio name="Personal Website"
|
||||||
style="max-height: 640px;"
|
style="max-height: 640px;"
|
||||||
src={personalSite}
|
src={personalSite}
|
||||||
|
|
@ -69,7 +75,7 @@
|
||||||
<OldWebsite slot="portfolio-details" />
|
<OldWebsite slot="portfolio-details" />
|
||||||
</Portfolio>
|
</Portfolio>
|
||||||
</div>
|
</div>
|
||||||
<div {...$content('professional')} class="content">
|
<div use:melt={$content('professional')} class="content">
|
||||||
<Portfolio name="Mark Shellnut Architect"
|
<Portfolio name="Mark Shellnut Architect"
|
||||||
style="max-height: 640px;"
|
style="max-height: 640px;"
|
||||||
src={shellnutArchitectWebsite}
|
src={shellnutArchitectWebsite}
|
||||||
|
|
@ -91,7 +97,7 @@
|
||||||
/* overflow: hidden; */
|
/* overflow: hidden; */
|
||||||
/* border-radius: var(--border-radius); */
|
/* border-radius: var(--border-radius); */
|
||||||
|
|
||||||
& [data-orientation="vertical"] {
|
&[data-orientation="vertical"] {
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -111,15 +117,17 @@
|
||||||
user-select: none;
|
user-select: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.trigger[data-state='active'] {
|
.trigger {
|
||||||
& h2 {
|
&[data-state='active'] {
|
||||||
border-bottom: 2px solid var(--shellYellow);
|
h2 {
|
||||||
|
border-bottom: 2px solid var(--shellYellow);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
.trigger[data-state='inactive'] {
|
&[data-state='inactive'] {
|
||||||
& h2 {
|
h2 {
|
||||||
border-bottom: 2px solid var(--white);
|
border-bottom: 2px solid var(--white);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import adapter from '@sveltejs/adapter-vercel';
|
import adapter from '@sveltejs/adapter-vercel';
|
||||||
import { vitePreprocess } from '@sveltejs/kit/vite';
|
import { vitePreprocess } from '@sveltejs/kit/vite';
|
||||||
|
import { preprocessMeltUI } from '@melt-ui/pp';
|
||||||
import { mdsvex } from 'mdsvex';
|
import { mdsvex } from 'mdsvex';
|
||||||
import mdsvexConfig from './mdsvex.config.js';
|
import mdsvexConfig from './mdsvex.config.js';
|
||||||
import relativeImages from 'mdsvex-relative-images';
|
import relativeImages from 'mdsvex-relative-images';
|
||||||
|
|
@ -10,19 +11,16 @@ const config = {
|
||||||
// Consult https://kit.svelte.dev/docs/integrations#preprocessors
|
// Consult https://kit.svelte.dev/docs/integrations#preprocessors
|
||||||
// for more information about preprocessors
|
// for more information about preprocessors
|
||||||
extensions: ['.svelte', ...mdsvexConfig.extensions],
|
extensions: ['.svelte', ...mdsvexConfig.extensions],
|
||||||
preprocess: [vitePreprocess(),mdsvex(mdsvexConfig)],
|
preprocess: [vitePreprocess(), mdsvex(mdsvexConfig), preprocessMeltUI()],
|
||||||
|
vitePlugin: {
|
||||||
|
inspector: true,
|
||||||
|
toggleKeyCombo: 'control-alt-shift'
|
||||||
|
},
|
||||||
kit: {
|
kit: {
|
||||||
adapter: adapter(),
|
adapter: adapter(),
|
||||||
alias: {
|
alias: {
|
||||||
$root: './src'
|
$root: './src'
|
||||||
}
|
}
|
||||||
},
|
|
||||||
vitePlugin: {
|
|
||||||
experimental: {
|
|
||||||
inspector: {
|
|
||||||
toggleKeyCombo: 'control-alt-shift'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,5 +2,5 @@ import { expect, test } from '@playwright/test';
|
||||||
|
|
||||||
test('index page has expected h1', async ({ page }) => {
|
test('index page has expected h1', async ({ page }) => {
|
||||||
await page.goto('/');
|
await page.goto('/');
|
||||||
expect(await page.textContent('h1')).toBe('Welcome to SvelteKit');
|
expect(await page.textContent('h1')).toBe("Hello! I'm Bradley Shellnut.");
|
||||||
});
|
});
|
||||||
Loading…
Reference in a new issue