diff --git a/.env.development b/.env.development
index c41c4c5c6..6b499e60d 100644
--- a/.env.development
+++ b/.env.development
@@ -1,3 +1,4 @@
EP_SESSIONS_API="https://static.europython.eu/programme/ep2025/releases/current/sessions.json"
EP_SPEAKERS_API="https://static.europython.eu/programme/ep2025/releases/current/speakers.json"
EP_SCHEDULE_API="https://static.europython.eu/programme/ep2025/releases/current/schedule.json"
+EP_FAST_BUILD="true"
diff --git a/astro.config.mjs b/astro.config.mjs
index db8338cfe..31e38d733 100644
--- a/astro.config.mjs
+++ b/astro.config.mjs
@@ -1,4 +1,5 @@
import path from "path";
+import { loadEnv } from "vite";
import { defineConfig } from "astro/config";
import mdx from "@astrojs/mdx";
import sitemap from "@astrojs/sitemap";
@@ -27,6 +28,15 @@ if (!gitVersion) {
}
}
+const mode =
+ process.argv.find((arg) =>
+ ["development", "production", "preview"].includes(arg)
+ ) || "production";
+const fastBuild = loadEnv(mode, process.cwd(), "").EP_FAST_BUILD === "true";
+console.log(
+ `\x1b[35m[EP]\x1b[0m Fast Build: \x1b[1m\x1b[34m${fastBuild}\x1b[0m`
+);
+
function dontDie() {
return {
name: "dont-die",
@@ -114,23 +124,27 @@ export default defineConfig({
},
integrations: [
mdx(),
- sitemap(),
- metaTags(),
- deleteUnusedImages(),
svelte(),
serviceWorker({
workbox: { inlineWorkboxRuntime: true },
}),
- compress({
- HTML: false,
- CSS: false,
- SVG: false,
- }),
- dontDie(),
+ ...(fastBuild
+ ? []
+ : [
+ sitemap(),
+ metaTags(),
+ deleteUnusedImages(),
+ compress({
+ HTML: false,
+ CSS: false,
+ SVG: false,
+ }),
+ dontDie(),
+ ]),
],
output: "static",
build: {
- minify: true,
+ ...(fastBuild ? {} : { minify: true }),
},
image: {
remotePatterns: [{ protocol: "https" }],
diff --git a/package.json b/package.json
index 145d1f004..6e511eca0 100644
--- a/package.json
+++ b/package.json
@@ -33,6 +33,7 @@
"date-fns-tz": "^3.2.0",
"hastscript": "^9.0.1",
"js-yaml": "^4.1.0",
+ "lite-youtube-embed": "^0.3.3",
"marked": "^15.0.12",
"nanostores": "^1.0.1",
"pagefind": "^1.3.0",
@@ -50,7 +51,8 @@
"prettier-plugin-astro": "^0.14.1",
"puppeteer": "^24.9.0",
"tsx": "^4.19.4",
- "typescript": "^5.8.3"
+ "typescript": "^5.8.3",
+ "vite": "^6.3.5"
},
"prettier": {
"proseWrap": "always"
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 057eb94ea..7cec73640 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -37,7 +37,7 @@ importers:
version: 0.5.16(tailwindcss@4.1.8)
'@tailwindcss/vite':
specifier: ^4.1.8
- version: 4.1.8(vite@6.3.4(@types/node@22.13.14)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.39.0)(tsx@4.19.4)(yaml@2.7.1))
+ version: 4.1.8(vite@6.3.5(@types/node@22.13.14)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.39.0)(tsx@4.19.4)(yaml@2.7.1))
astro:
specifier: ^5.8.0
version: 5.8.0(@types/node@22.13.14)(jiti@2.4.2)(lightningcss@1.30.1)(rollup@2.79.2)(terser@5.39.0)(tsx@4.19.4)(typescript@5.8.3)(yaml@2.7.1)
@@ -74,6 +74,9 @@ importers:
js-yaml:
specifier: ^4.1.0
version: 4.1.0
+ lite-youtube-embed:
+ specifier: ^0.3.3
+ version: 0.3.3
marked:
specifier: ^15.0.12
version: 15.0.12
@@ -120,6 +123,9 @@ importers:
tsx:
specifier: ^4.19.4
version: 4.19.4
+ vite:
+ specifier: ^6.3.5
+ version: 6.3.5(@types/node@22.13.14)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.39.0)(tsx@4.19.4)(yaml@2.7.1)
packages:
@@ -4046,46 +4052,6 @@ packages:
vfile@6.0.3:
resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==}
- vite@6.3.4:
- resolution: {integrity: sha512-BiReIiMS2fyFqbqNT/Qqt4CVITDU9M9vE+DKcVAsB+ZV0wvTKd+3hMbkpxz1b+NmEDMegpVbisKiAZOnvO92Sw==}
- engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
- hasBin: true
- peerDependencies:
- '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0
- jiti: '>=1.21.0'
- less: '*'
- lightningcss: ^1.21.0
- sass: '*'
- sass-embedded: '*'
- stylus: '*'
- sugarss: '*'
- terser: ^5.16.0
- tsx: ^4.8.1
- yaml: ^2.4.2
- peerDependenciesMeta:
- '@types/node':
- optional: true
- jiti:
- optional: true
- less:
- optional: true
- lightningcss:
- optional: true
- sass:
- optional: true
- sass-embedded:
- optional: true
- stylus:
- optional: true
- sugarss:
- optional: true
- terser:
- optional: true
- tsx:
- optional: true
- yaml:
- optional: true
-
vite@6.3.5:
resolution: {integrity: sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==}
engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
@@ -4540,7 +4506,7 @@ snapshots:
'@astrojs/svelte@7.1.0(@types/node@22.13.14)(astro@5.8.0(@types/node@22.13.14)(jiti@2.4.2)(lightningcss@1.30.1)(rollup@2.79.2)(terser@5.39.0)(tsx@4.19.4)(typescript@5.8.3)(yaml@2.7.1))(jiti@2.4.2)(lightningcss@1.30.1)(svelte@5.33.12)(terser@5.39.0)(tsx@4.19.4)(typescript@5.8.3)(yaml@2.7.1)':
dependencies:
- '@sveltejs/vite-plugin-svelte': 5.0.3(svelte@5.33.12)(vite@6.3.4(@types/node@22.13.14)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.39.0)(tsx@4.19.4)(yaml@2.7.1))
+ '@sveltejs/vite-plugin-svelte': 5.0.3(svelte@5.33.12)(vite@6.3.5(@types/node@22.13.14)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.39.0)(tsx@4.19.4)(yaml@2.7.1))
astro: 5.8.0(@types/node@22.13.14)(jiti@2.4.2)(lightningcss@1.30.1)(rollup@2.79.2)(terser@5.39.0)(tsx@4.19.4)(typescript@5.8.3)(yaml@2.7.1)
svelte: 5.33.12
svelte2tsx: 0.7.39(svelte@5.33.12)(typescript@5.8.3)
@@ -5754,14 +5720,14 @@ snapshots:
'@sveltejs/vite-plugin-svelte-inspector@4.0.1(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.33.12)(vite@6.3.5(@types/node@22.13.14)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.39.0)(tsx@4.19.4)(yaml@2.7.1)))(svelte@5.33.12)(vite@6.3.5(@types/node@22.13.14)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.39.0)(tsx@4.19.4)(yaml@2.7.1))':
dependencies:
- '@sveltejs/vite-plugin-svelte': 5.0.3(svelte@5.33.12)(vite@6.3.4(@types/node@22.13.14)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.39.0)(tsx@4.19.4)(yaml@2.7.1))
+ '@sveltejs/vite-plugin-svelte': 5.0.3(svelte@5.33.12)(vite@6.3.5(@types/node@22.13.14)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.39.0)(tsx@4.19.4)(yaml@2.7.1))
debug: 4.4.1
svelte: 5.33.12
vite: 6.3.5(@types/node@22.13.14)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.39.0)(tsx@4.19.4)(yaml@2.7.1)
transitivePeerDependencies:
- supports-color
- '@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.33.12)(vite@6.3.4(@types/node@22.13.14)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.39.0)(tsx@4.19.4)(yaml@2.7.1))':
+ '@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.33.12)(vite@6.3.5(@types/node@22.13.14)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.39.0)(tsx@4.19.4)(yaml@2.7.1))':
dependencies:
'@sveltejs/vite-plugin-svelte-inspector': 4.0.1(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.33.12)(vite@6.3.5(@types/node@22.13.14)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.39.0)(tsx@4.19.4)(yaml@2.7.1)))(svelte@5.33.12)(vite@6.3.5(@types/node@22.13.14)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.39.0)(tsx@4.19.4)(yaml@2.7.1))
debug: 4.4.1
@@ -5769,7 +5735,7 @@ snapshots:
kleur: 4.1.5
magic-string: 0.30.17
svelte: 5.33.12
- vite: 6.3.4(@types/node@22.13.14)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.39.0)(tsx@4.19.4)(yaml@2.7.1)
+ vite: 6.3.5(@types/node@22.13.14)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.39.0)(tsx@4.19.4)(yaml@2.7.1)
vitefu: 1.0.6(vite@6.3.5(@types/node@22.13.14)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.39.0)(tsx@4.19.4)(yaml@2.7.1))
transitivePeerDependencies:
- supports-color
@@ -5850,12 +5816,12 @@ snapshots:
postcss-selector-parser: 6.0.10
tailwindcss: 4.1.8
- '@tailwindcss/vite@4.1.8(vite@6.3.4(@types/node@22.13.14)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.39.0)(tsx@4.19.4)(yaml@2.7.1))':
+ '@tailwindcss/vite@4.1.8(vite@6.3.5(@types/node@22.13.14)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.39.0)(tsx@4.19.4)(yaml@2.7.1))':
dependencies:
'@tailwindcss/node': 4.1.8
'@tailwindcss/oxide': 4.1.8
tailwindcss: 4.1.8
- vite: 6.3.4(@types/node@22.13.14)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.39.0)(tsx@4.19.4)(yaml@2.7.1)
+ vite: 6.3.5(@types/node@22.13.14)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.39.0)(tsx@4.19.4)(yaml@2.7.1)
'@tootallnate/quickjs-emscripten@0.23.0': {}
@@ -6164,8 +6130,8 @@ snapshots:
unist-util-visit: 5.0.0
unstorage: 1.16.0
vfile: 6.0.3
- vite: 6.3.4(@types/node@22.13.14)(jiti@2.4.2)(lightningcss@1.29.3)(terser@5.39.0)(tsx@4.19.4)(yaml@2.7.1)
- vitefu: 1.0.6(vite@6.3.4(@types/node@22.13.14)(jiti@2.4.2)(lightningcss@1.29.3)(terser@5.39.0)(tsx@4.19.4)(yaml@2.7.1))
+ vite: 6.3.5(@types/node@22.13.14)(jiti@2.4.2)(lightningcss@1.29.3)(terser@5.39.0)(tsx@4.19.4)(yaml@2.7.1)
+ vitefu: 1.0.6(vite@6.3.5(@types/node@22.13.14)(jiti@2.4.2)(lightningcss@1.29.3)(terser@5.39.0)(tsx@4.19.4)(yaml@2.7.1))
xxhash-wasm: 1.1.0
yargs-parser: 21.1.1
yocto-spinner: 0.2.2
@@ -6264,8 +6230,8 @@ snapshots:
unist-util-visit: 5.0.0
unstorage: 1.16.0
vfile: 6.0.3
- vite: 6.3.4(@types/node@22.13.14)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.39.0)(tsx@4.19.4)(yaml@2.7.1)
- vitefu: 1.0.6(vite@6.3.4(@types/node@22.13.14)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.39.0)(tsx@4.19.4)(yaml@2.7.1))
+ vite: 6.3.5(@types/node@22.13.14)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.39.0)(tsx@4.19.4)(yaml@2.7.1)
+ vitefu: 1.0.6(vite@6.3.5(@types/node@22.13.14)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.39.0)(tsx@4.19.4)(yaml@2.7.1))
xxhash-wasm: 1.1.0
yargs-parser: 21.1.1
yocto-spinner: 0.2.2
@@ -9332,7 +9298,7 @@ snapshots:
'@types/unist': 3.0.3
vfile-message: 4.0.2
- vite@6.3.4(@types/node@22.13.14)(jiti@2.4.2)(lightningcss@1.29.3)(terser@5.39.0)(tsx@4.19.4)(yaml@2.7.1):
+ vite@6.3.5(@types/node@22.13.14)(jiti@2.4.2)(lightningcss@1.29.3)(terser@5.39.0)(tsx@4.19.4)(yaml@2.7.1):
dependencies:
esbuild: 0.25.3
fdir: 6.4.4(picomatch@4.0.2)
@@ -9349,23 +9315,6 @@ snapshots:
tsx: 4.19.4
yaml: 2.7.1
- vite@6.3.4(@types/node@22.13.14)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.39.0)(tsx@4.19.4)(yaml@2.7.1):
- dependencies:
- esbuild: 0.25.3
- fdir: 6.4.4(picomatch@4.0.2)
- picomatch: 4.0.2
- postcss: 8.5.3
- rollup: 4.40.1
- tinyglobby: 0.2.13
- optionalDependencies:
- '@types/node': 22.13.14
- fsevents: 2.3.3
- jiti: 2.4.2
- lightningcss: 1.30.1
- terser: 5.39.0
- tsx: 4.19.4
- yaml: 2.7.1
-
vite@6.3.5(@types/node@22.13.14)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.39.0)(tsx@4.19.4)(yaml@2.7.1):
dependencies:
esbuild: 0.25.3
@@ -9383,13 +9332,9 @@ snapshots:
tsx: 4.19.4
yaml: 2.7.1
- vitefu@1.0.6(vite@6.3.4(@types/node@22.13.14)(jiti@2.4.2)(lightningcss@1.29.3)(terser@5.39.0)(tsx@4.19.4)(yaml@2.7.1)):
- optionalDependencies:
- vite: 6.3.4(@types/node@22.13.14)(jiti@2.4.2)(lightningcss@1.29.3)(terser@5.39.0)(tsx@4.19.4)(yaml@2.7.1)
-
- vitefu@1.0.6(vite@6.3.4(@types/node@22.13.14)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.39.0)(tsx@4.19.4)(yaml@2.7.1)):
+ vitefu@1.0.6(vite@6.3.5(@types/node@22.13.14)(jiti@2.4.2)(lightningcss@1.29.3)(terser@5.39.0)(tsx@4.19.4)(yaml@2.7.1)):
optionalDependencies:
- vite: 6.3.4(@types/node@22.13.14)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.39.0)(tsx@4.19.4)(yaml@2.7.1)
+ vite: 6.3.5(@types/node@22.13.14)(jiti@2.4.2)(lightningcss@1.29.3)(terser@5.39.0)(tsx@4.19.4)(yaml@2.7.1)
vitefu@1.0.6(vite@6.3.5(@types/node@22.13.14)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.39.0)(tsx@4.19.4)(yaml@2.7.1)):
optionalDependencies:
diff --git a/src/components/InstallPWA.astro b/src/components/InstallPWA.astro
index bbcf7b131..df48f0750 100644
--- a/src/components/InstallPWA.astro
+++ b/src/components/InstallPWA.astro
@@ -29,11 +29,12 @@
+
+
diff --git a/src/env.d.ts b/src/env.d.ts
index db4bfb864..1140d0fac 100644
--- a/src/env.d.ts
+++ b/src/env.d.ts
@@ -5,6 +5,7 @@ interface ImportMetaEnv {
readonly EP_SESSIONS_API: string;
readonly EP_SPEAKERS_API: string;
readonly EP_SCHEDULE_API: string;
+ readonly EP_FAST_BUILD: boolean;
}
interface ImportMeta {
diff --git a/src/utils/markdown.ts b/src/utils/markdown.ts
new file mode 100644
index 000000000..1a3fe4e52
--- /dev/null
+++ b/src/utils/markdown.ts
@@ -0,0 +1,30 @@
+import { experimental_AstroContainer } from "astro/container";
+import YouTube from "@ui/YouTube.astro";
+
+export async function replaceYouTubeLinks(
+ markdownContent: string
+): Promise {
+ const youtubeRegex =
+ /(?:https?:\/\/)?(?:www\.)?(?:youtube\.com\/watch\?v=|youtu\.be\/)([^\s'"()[\]<>]+)/gi;
+
+ const matches = [...markdownContent.matchAll(youtubeRegex)];
+ if (matches.length === 0) return markdownContent;
+
+ const container = await experimental_AstroContainer.create();
+ let updatedContent = markdownContent;
+
+ for (const match of matches) {
+ const fullUrl = match[0];
+ const id = match[1];
+
+ if (!id) continue;
+
+ const html = await container.renderToString(YouTube, {
+ props: { id, alt: "Embedded YouTube video" },
+ });
+
+ updatedContent = updatedContent.replace(fullUrl, html);
+ }
+
+ return updatedContent;
+}