chore(tooling): add vitest + svelte-check, drop yarn.lock, CI gates, typed env imports

Toolchain hygiene:
  - svelte-check is now a pinned devDep (was npx-fetched per CI run)
  - vitest + coverage as devDeps; vitest.config.ts is separate from
    vite.config.ts so vite build does not pull vitest
  - $env stub for vitest (alias resolves $env/dynamic/{public,private}
    to a process.env proxy) — keeps server modules testable without
    booting SvelteKit
  - npm scripts: check / check:watch / typecheck / test / test:watch
  - drop yarn.lock + packageManager field; CI uses npm ci, single source
    of truth is package-lock.json
  - CI runs generate:icons + svelte-check + tsc --noEmit before deploy

Switch from raw process.env.X to $env/dynamic/{public,private} in
file-cache, image-cache, transform-cache and rusty-image.server. The
typed imports get tree-shaken correctly per server/client and fail
loudly at build time on accidental client-side use of private vars.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Peter Meier
2026-05-04 16:09:02 +02:00
parent d0dede92fe
commit 615d9cdbd1
10 changed files with 2446 additions and 1460 deletions
+18
View File
@@ -0,0 +1,18 @@
/**
* Stub für `$env/dynamic/{public,private}`. Wird ausschließlich von Vitest
* via Alias aufgelöst (siehe `vitest.config.ts`). Im Produktivbuild verwendet
* SvelteKit das echte Modul.
*
* Tests können `process.env` direkt setzen — Proxy reicht alle Reads
* unverändert durch.
*/
export const env = new Proxy({} as Record<string, string | undefined>, {
get(_target, prop) {
if (typeof prop !== 'string') return undefined;
return process.env[prop];
},
has(_target, prop) {
if (typeof prop !== 'string') return false;
return prop in process.env;
},
});
+3 -2
View File
@@ -8,9 +8,10 @@ import { createHash } from 'node:crypto';
import { existsSync, mkdirSync } from 'node:fs';
import { readFile, readdir, writeFile } from 'node:fs/promises';
import { extname, join } from 'node:path';
import { env } from '$env/dynamic/private';
const CACHE_DIR = process.env.FILE_CACHE_DIR?.trim() || '.cache/files';
const MAX_MEMORY_ENTRIES = Number(process.env.FILE_CACHE_MAX_MEMORY) || 50;
const CACHE_DIR = env.FILE_CACHE_DIR?.trim() || '.cache/files';
const MAX_MEMORY_ENTRIES = Number(env.FILE_CACHE_MAX_MEMORY) || 50;
export interface CachedFile {
buffer: Buffer;
+3 -2
View File
@@ -11,9 +11,10 @@ import { createHash } from 'node:crypto';
import { existsSync, mkdirSync } from 'node:fs';
import { readFile, readdir, unlink, writeFile } from 'node:fs/promises';
import { join } from 'node:path';
import { env } from '$env/dynamic/private';
const CACHE_DIR = process.env.IMAGE_CACHE_DIR?.trim() || '.cache/images';
const MAX_MEMORY_ENTRIES = Number(process.env.IMAGE_CACHE_MAX_MEMORY) || 200;
const CACHE_DIR = env.IMAGE_CACHE_DIR?.trim() || '.cache/images';
const MAX_MEMORY_ENTRIES = Number(env.IMAGE_CACHE_MAX_MEMORY) || 200;
export interface CachedImage {
buffer: Buffer;
+2 -5
View File
@@ -1,6 +1,6 @@
/**
* Server-only Bild-Helfer: Asset-Meta vom CMS, Blurhash-LQIP, Responsive-Srcset.
* Nur in `+page.server.ts` / `+layout.server.ts` importieren — nutzt sharp + blurhash + process.env.
* Nur in `+page.server.ts` / `+layout.server.ts` importieren — nutzt sharp + blurhash.
*/
import { env } from '$env/dynamic/public';
@@ -11,10 +11,7 @@ import {
} from './rusty-image';
function getCmsBaseUrl(): string {
return (env.PUBLIC_CMS_URL || process.env.PUBLIC_CMS_URL || 'http://localhost:3000').replace(
/\/$/,
'',
);
return (env.PUBLIC_CMS_URL || 'http://localhost:3000').replace(/\/$/, '');
}
export interface AssetMeta {
+4 -2
View File
@@ -6,8 +6,10 @@
* - Optional Datei-Cache: TRANSFORM_CACHE_DIR setzen (z. B. .cache/transform).
*/
const MAX_ENTRIES = Number(process.env.TRANSFORM_CACHE_MAX_ENTRIES) || 100;
const CACHE_DIR = process.env.TRANSFORM_CACHE_DIR?.trim() || '';
import { env } from '$env/dynamic/private';
const MAX_ENTRIES = Number(env.TRANSFORM_CACHE_MAX_ENTRIES) || 100;
const CACHE_DIR = env.TRANSFORM_CACHE_DIR?.trim() || '';
interface CachedImage {
buffer: ArrayBuffer;