fixes for svelte 5 and hetzner deployment
This commit is contained in:
parent
2b48498f72
commit
d7c2ad8ea0
11 changed files with 704 additions and 275 deletions
2
.npmrc
Normal file
2
.npmrc
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
legacy-peer-deps=true
|
||||||
|
|
||||||
798
package-lock.json
generated
798
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -6,6 +6,7 @@
|
||||||
"dev": "vite dev",
|
"dev": "vite dev",
|
||||||
"build": "vite build",
|
"build": "vite build",
|
||||||
"preview": "vite preview",
|
"preview": "vite preview",
|
||||||
|
"start": "node build",
|
||||||
"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",
|
||||||
"lint": "prettier --plugin-search-dir . --check . && eslint .",
|
"lint": "prettier --plugin-search-dir . --check . && eslint .",
|
||||||
|
|
@ -22,7 +23,7 @@
|
||||||
"@typescript-eslint/parser": "^5.45.0",
|
"@typescript-eslint/parser": "^5.45.0",
|
||||||
"eslint": "^8.28.0",
|
"eslint": "^8.28.0",
|
||||||
"eslint-config-prettier": "^8.5.0",
|
"eslint-config-prettier": "^8.5.0",
|
||||||
"eslint-plugin-svelte": "^2.30.0",
|
"eslint-plugin-svelte": "^3.0.0",
|
||||||
"prettier": "^3.0.0",
|
"prettier": "^3.0.0",
|
||||||
"prettier-plugin-svelte": "^3.0.0",
|
"prettier-plugin-svelte": "^3.0.0",
|
||||||
"sass": "^1.64.2",
|
"sass": "^1.64.2",
|
||||||
|
|
|
||||||
|
|
@ -1,41 +1,58 @@
|
||||||
type Post = {
|
type Post = {
|
||||||
metadata: {
|
metadata: {
|
||||||
title: string
|
title: string
|
||||||
date: string
|
date?: string
|
||||||
description: string
|
description: string
|
||||||
tags: string[]
|
tags?: string[]
|
||||||
header_bg_image: string
|
header_bg_image?: string
|
||||||
svg: string
|
svg?: string
|
||||||
video: string
|
video?: string
|
||||||
order: number
|
order: number
|
||||||
}
|
}
|
||||||
default: {
|
default: unknown // Component, but we don't need to render it for the list page
|
||||||
render: () => string
|
|
||||||
}
|
|
||||||
path: string
|
|
||||||
Content: string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const fetchMarkdownPosts = async () => {
|
export const fetchMarkdownPosts = async () => {
|
||||||
// eslint-disable-next-line no-useless-escape
|
try {
|
||||||
const allPostFiles = import.meta.glob('/src/routes/work/md/\*.md')
|
const allPostFiles = import.meta.glob('/src/routes/work/md/*.md', { eager: false })
|
||||||
|
|
||||||
const iterablePostFiles = Object.entries(allPostFiles)
|
const iterablePostFiles = Object.entries(allPostFiles)
|
||||||
|
|
||||||
const allPosts = await Promise.all(
|
if (iterablePostFiles.length === 0) {
|
||||||
iterablePostFiles.map(async ([path, resolver]) => {
|
console.warn('No markdown files found')
|
||||||
const postPath = path.slice(11, -3).replace('work/md/','work/')
|
return []
|
||||||
const data: unknown = await resolver()
|
}
|
||||||
const postData = data as Post
|
|
||||||
const content = postData.default.render() as unknown as { html: string }
|
const allPosts = await Promise.all(
|
||||||
|
iterablePostFiles.map(async ([path, resolver]) => {
|
||||||
|
try {
|
||||||
|
// Path will be like '/src/routes/work/md/adidas.md'
|
||||||
|
// We want '/work/adidas'
|
||||||
|
const pathWithoutExt = path.slice(0, -3) // Remove '.md'
|
||||||
|
const pathParts = pathWithoutExt.split('/')
|
||||||
|
const slug = pathParts[pathParts.length - 1] // Get 'adidas'
|
||||||
|
const postPath = `/work/${slug}`
|
||||||
|
|
||||||
|
const data: unknown = await resolver()
|
||||||
|
const postData = data as Post
|
||||||
|
|
||||||
return {
|
// Only extract metadata - we don't need to render the content for the list page
|
||||||
meta: postData.metadata,
|
// The work page only uses metadata (title, svg, tags) and path
|
||||||
path: postPath,
|
return {
|
||||||
Content: content.html,
|
meta: postData.metadata,
|
||||||
}
|
path: postPath,
|
||||||
})
|
}
|
||||||
)
|
} catch (error) {
|
||||||
|
console.error(`Error loading post from ${path}:`, error)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
return allPosts
|
// Filter out any null results from failed imports
|
||||||
|
return allPosts.filter((post): post is NonNullable<typeof post> => post !== null)
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error in fetchMarkdownPosts:', error)
|
||||||
|
return []
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -3,11 +3,21 @@
|
||||||
import Header from '$lib/components/Header.svelte';
|
import Header from '$lib/components/Header.svelte';
|
||||||
import Loader from '$lib/components/Loader.svelte';
|
import Loader from '$lib/components/Loader.svelte';
|
||||||
import { navigating, page } from '$app/stores';
|
import { navigating, page } from '$app/stores';
|
||||||
|
import { onMount } from 'svelte';
|
||||||
import type { Snippet } from 'svelte';
|
import type { Snippet } from 'svelte';
|
||||||
interface LayoutData {
|
interface LayoutData {
|
||||||
pathname: string;
|
pathname: string;
|
||||||
}
|
}
|
||||||
let { data, children }: { data: LayoutData, children: Snippet } = $props();
|
let { data, children }: { data: LayoutData, children: Snippet } = $props();
|
||||||
|
|
||||||
|
// Track pathname changes - if pathname changes but navigating is still true,
|
||||||
|
// navigation might be stuck (though this shouldn't happen normally)
|
||||||
|
let lastPathname = $state(data.pathname);
|
||||||
|
$effect(() => {
|
||||||
|
if (data.pathname !== lastPathname) {
|
||||||
|
lastPathname = data.pathname;
|
||||||
|
}
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:head>
|
<svelte:head>
|
||||||
|
|
|
||||||
|
|
@ -142,8 +142,10 @@
|
||||||
bulgefilter.center = new PIXI.Point(mouse.x, 0.5);
|
bulgefilter.center = new PIXI.Point(mouse.x, 0.5);
|
||||||
})
|
})
|
||||||
|
|
||||||
|
let scrollTriggerTweens: Array<GSAPTween> = [];
|
||||||
|
|
||||||
function createScrollTrigger() {
|
function createScrollTrigger() {
|
||||||
gsap.to([textRows[0], textRows[1]], {
|
const tween1 = gsap.to([textRows[0], textRows[1]], {
|
||||||
y: '+=' + -textRows[0][0].height * 0.9,
|
y: '+=' + -textRows[0][0].height * 0.9,
|
||||||
duration: 1,
|
duration: 1,
|
||||||
ease: 'none',
|
ease: 'none',
|
||||||
|
|
@ -155,7 +157,7 @@
|
||||||
// markers: true,
|
// markers: true,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
gsap.to([textRows[2], textRows[3]], {
|
const tween2 = gsap.to([textRows[2], textRows[3]], {
|
||||||
y: '+=' + textRows[0][0].height * 0.9,
|
y: '+=' + textRows[0][0].height * 0.9,
|
||||||
duration: 1,
|
duration: 1,
|
||||||
ease: 'none',
|
ease: 'none',
|
||||||
|
|
@ -167,11 +169,22 @@
|
||||||
// markers: true,
|
// markers: true,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
scrollTriggerTweens.push(tween1, tween2);
|
||||||
}
|
}
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
app.destroy(true, { children: true, texture: true, baseTexture: true });
|
// IMPORTANT: Kill ScrollTrigger and GSAP animations FIRST,
|
||||||
|
// before destroying PixiJS objects they reference
|
||||||
|
scrollTriggerTweens.forEach( tween => {
|
||||||
|
if (tween.scrollTrigger) {
|
||||||
|
tween.scrollTrigger.kill();
|
||||||
|
}
|
||||||
|
tween.kill();
|
||||||
|
});
|
||||||
|
// Kill all other tweens
|
||||||
tweens.forEach( tween => tween.kill() );
|
tweens.forEach( tween => tween.kill() );
|
||||||
|
// Now safe to destroy PixiJS app
|
||||||
|
app.destroy(true, { children: true, texture: true, baseTexture: true });
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,28 @@
|
||||||
import { fetchMarkdownPosts } from '../../lib/importMarkdown'
|
import { fetchMarkdownPosts } from '../../lib/importMarkdown'
|
||||||
|
|
||||||
export async function load() {
|
export async function load() {
|
||||||
const posts = await fetchMarkdownPosts()
|
try {
|
||||||
if (!posts) console.error('No posts found')
|
const posts = await fetchMarkdownPosts()
|
||||||
|
if (!posts) {
|
||||||
|
console.error('No posts found')
|
||||||
|
return {
|
||||||
|
posts: [],
|
||||||
|
title: 'Work References',
|
||||||
|
description: 'A few of the projects I have worked on'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
posts,
|
posts,
|
||||||
title: 'Work References',
|
title: 'Work References',
|
||||||
description: 'A few of the projects I have worked on'
|
description: 'A few of the projects I have worked on'
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error loading work posts:', error)
|
||||||
|
return {
|
||||||
|
posts: [],
|
||||||
|
title: 'Work References',
|
||||||
|
description: 'A few of the projects I have worked on'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,9 +4,9 @@
|
||||||
import { SplitText } from 'gsap/SplitText';
|
import { SplitText } from 'gsap/SplitText';
|
||||||
let { data } = $props();
|
let { data } = $props();
|
||||||
|
|
||||||
const orderedPosts = data.posts.sort((a, b) => {
|
const orderedPosts = $derived.by(() => (data.posts || []).sort((a, b) => {
|
||||||
return a.meta.order - b.meta.order;
|
return a.meta.order - b.meta.order;
|
||||||
});
|
}));
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
document.querySelectorAll('.workclone')?.forEach(clone => {
|
document.querySelectorAll('.workclone')?.forEach(clone => {
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,8 @@ export async function load( { params }: { params: { slug: string }} ){
|
||||||
const post = await import(`../md/${params.slug}.md`)
|
const post = await import(`../md/${params.slug}.md`)
|
||||||
const { title = '', date = '', header_bg_image = '', svg = '', video = '', tags = [], reference = '', referenceName = '', tasks = [], description = [], images = [], agency = '', agencyName = '' } = post.metadata
|
const { title = '', date = '', header_bg_image = '', svg = '', video = '', tags = [], reference = '', referenceName = '', tasks = [], description = [], images = [], agency = '', agencyName = '' } = post.metadata
|
||||||
|
|
||||||
const Content = post.default.render()
|
// Don't pass the component - it's not serializable
|
||||||
|
// Import it directly in the page component instead
|
||||||
|
|
||||||
return {
|
return {
|
||||||
title,
|
title,
|
||||||
|
|
@ -12,7 +13,7 @@ export async function load( { params }: { params: { slug: string }} ){
|
||||||
svg,
|
svg,
|
||||||
video,
|
video,
|
||||||
tags,
|
tags,
|
||||||
Content,
|
slug: params.slug,
|
||||||
reference,
|
reference,
|
||||||
referenceName,
|
referenceName,
|
||||||
tasks,
|
tasks,
|
||||||
|
|
@ -23,5 +24,6 @@ export async function load( { params }: { params: { slug: string }} ){
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error)
|
console.error(error)
|
||||||
|
throw error
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -5,9 +5,18 @@
|
||||||
import { CldImage } from 'svelte-cloudinary';
|
import { CldImage } from 'svelte-cloudinary';
|
||||||
let { data } = $props();
|
let { data } = $props();
|
||||||
|
|
||||||
let visible = false;
|
let visible = $state(false);
|
||||||
|
let Content = $state<any>(null);
|
||||||
|
|
||||||
onMount(() => {
|
// Import the markdown component dynamically and set up animations
|
||||||
|
onMount(async () => {
|
||||||
|
// Load the markdown component
|
||||||
|
try {
|
||||||
|
const post = await import(`../md/${data.slug}.md`);
|
||||||
|
Content = post.default;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error loading markdown component:', error);
|
||||||
|
}
|
||||||
|
|
||||||
gsap.registerPlugin(ScrollToPlugin);
|
gsap.registerPlugin(ScrollToPlugin);
|
||||||
|
|
||||||
|
|
@ -136,7 +145,7 @@
|
||||||
{:else}
|
{:else}
|
||||||
<CldImage
|
<CldImage
|
||||||
src={image}
|
src={image}
|
||||||
alt="{data.title}"
|
alt={data.title}
|
||||||
sizes="(min-width: 768px) 67vw, 90vw"
|
sizes="(min-width: 768px) 67vw, 90vw"
|
||||||
width={1400}
|
width={1400}
|
||||||
height={840}
|
height={840}
|
||||||
|
|
@ -178,7 +187,9 @@
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
<div class="work-content-text">
|
<div class="work-content-text">
|
||||||
{@html data.Content.html}
|
{#if Content}
|
||||||
|
<svelte:component this={Content} />
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
// import adapter from '@sveltejs/adapter-auto';
|
// import adapter from '@sveltejs/adapter-auto';
|
||||||
// import adapter from '@sveltejs/adapter-node';
|
import adapter from '@sveltejs/adapter-node';
|
||||||
import adapter from '@sveltejs/adapter-netlify';
|
// import adapter from '@sveltejs/adapter-netlify';
|
||||||
import preprocess from 'svelte-preprocess';
|
import preprocess from 'svelte-preprocess';
|
||||||
import { mdsvex } from 'mdsvex'
|
import { mdsvex } from 'mdsvex'
|
||||||
|
|
||||||
|
|
@ -20,13 +20,12 @@ const config = {
|
||||||
// If your environment is not supported or you settled on a specific environment, switch out the adapter.
|
// If your environment is not supported or you settled on a specific environment, switch out the adapter.
|
||||||
// See https://kit.svelte.dev/docs/adapters for more information about adapters.
|
// See https://kit.svelte.dev/docs/adapters for more information about adapters.
|
||||||
adapter: adapter({
|
adapter: adapter({
|
||||||
// if true, will create a Netlify Edge Function rather
|
// Output directory for the built server
|
||||||
// than using standard Node-based functions
|
out: 'build',
|
||||||
edge: false,
|
// Precompress output files (gzip and brotli)
|
||||||
// if true, will split your app into multiple functions
|
precompress: true,
|
||||||
// instead of creating a single one for the entire app.
|
// Enable polyfills for Node.js built-in modules
|
||||||
// if `edge` is true, this option cannot be used
|
polyfills: true
|
||||||
split: false
|
|
||||||
}),
|
}),
|
||||||
csp: {
|
csp: {
|
||||||
mode: 'auto',
|
mode: 'auto',
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue