diff --git a/.gitignore b/.gitignore
index 7d28d38..08b4391 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,6 +4,7 @@
/vendor
/node_modules
+/.pnpm-store
/backend/vendor
/backend/node_modules
/frontend/node_modules
diff --git a/.zed/settings.json b/.zed/settings.json
index 498db2e..444bf05 100644
--- a/.zed/settings.json
+++ b/.zed/settings.json
@@ -16,13 +16,13 @@
"languages": {
"JavaScript": {
"prettier": { "allowed": false },
- "language_servers": ["oxlint", "oxfmt", "tsgo"],
+ "language_servers": ["oxlint", "oxfmt", "typescript-ls"],
"formatter": [{ "language_server": { "name": "oxfmt" } }],
"code_actions_on_format": { "source.fixAll.oxc": true }
},
"TypeScript": {
"prettier": { "allowed": false },
- "language_servers": ["oxlint", "oxfmt", "tsgo"],
+ "language_servers": ["oxlint", "oxfmt", "typescript-ls"],
"formatter": [{ "language_server": { "name": "oxfmt" } }],
"code_actions_on_format": { "source.fixAll.oxc": true }
},
diff --git a/backend/modules/general/services/Serializer.php b/backend/modules/general/services/Serializer.php
index 7b64f5e..c5d5b15 100644
--- a/backend/modules/general/services/Serializer.php
+++ b/backend/modules/general/services/Serializer.php
@@ -513,7 +513,7 @@ private static function menuItems(mixed $query): array
return [];
}
- return array_values(array_map([static::class, 'menuItem'], $query->all()));
+ return array_values(array_map([static::class, 'menuItem'], $query->eagerly()->all()));
}
/**
diff --git a/backend/modules/general/web/twig/GeneralExtension.php b/backend/modules/general/web/twig/GeneralExtension.php
index b10aa19..caa3b23 100644
--- a/backend/modules/general/web/twig/GeneralExtension.php
+++ b/backend/modules/general/web/twig/GeneralExtension.php
@@ -407,7 +407,7 @@ public static function onlyEnv(?string $markup, string|array $environments): str
return $markup;
}
- return sprintf(' %s ', $markup);
+ return '';
}
public static function plain(mixed $string): string
diff --git a/frontend/.env.example b/frontend/.env.example
index 86be4ce..b45984a 100644
--- a/frontend/.env.example
+++ b/frontend/.env.example
@@ -3,3 +3,4 @@ PUBLIC_CMS_URL=https://cms.craftcms-sveltekit.ddev.site
PUBLIC_OBJECT_STORAGE_URL=
PUBLIC_IMGIX_URL=https://craftcms-sveltekit.imgix.net
CRAFT_INTERNAL_HOST=http://0.0.0.0
+TINYPNG_KEY=
diff --git a/frontend/src/lib/util/image.ts b/frontend/src/lib/util/image.ts
index 3b686e2..e5bcb4f 100644
--- a/frontend/src/lib/util/image.ts
+++ b/frontend/src/lib/util/image.ts
@@ -50,7 +50,11 @@ function attributes(source: ImageSource, loading = 'lazy') {
if (asset && asset.uid && asset.src) {
const src = imgix(asset.src, args || {})
- const src2x = imgix(asset.src, { ...args, width: width * 2, height: height * 2 })
+ const src2x = imgix(asset.src, {
+ ...args,
+ ...(width ? { width: width * 2 } : {}),
+ ...(height ? { height: height * 2 } : {}),
+ })
const alt = asset.alt
if (!width && height) {
@@ -61,6 +65,9 @@ function attributes(source: ImageSource, loading = 'lazy') {
height = Math.floor(Math.min(asset.width, width) * (asset.height / asset.width))
}
+ width = width || asset.width
+ height = height || asset.height
+
Object.assign(attrs, {
width,
height,
diff --git a/frontend/utility/vite-plugin-tinify.ts b/frontend/utility/vite-plugin-tinify.ts
index 8de40a9..9878bb2 100644
--- a/frontend/utility/vite-plugin-tinify.ts
+++ b/frontend/utility/vite-plugin-tinify.ts
@@ -3,7 +3,7 @@ import * as fs from 'node:fs'
import tinify from 'tinify'
import type { Plugin } from 'vite'
-export default function tinifyPlugin(): Plugin {
+export default function tinifyPlugin(options: { key?: string } = {}): Plugin {
return {
name: 'vite-plugin-tinify',
async generateBundle(_, bundler) {
@@ -12,7 +12,7 @@ export default function tinifyPlugin(): Plugin {
continue
}
- const checksum = crypto.createHash('sha1').update(asset.source.toString()).digest('hex')
+ const checksum = crypto.createHash('sha1').update(asset.source).digest('hex')
const checksumfile = `node_modules/.vite/tinify/${checksum}`
let content: Buffer | Uint8Array | undefined
@@ -20,7 +20,7 @@ export default function tinifyPlugin(): Plugin {
if (fs.existsSync(checksumfile)) {
content = fs.readFileSync(checksumfile)
} else {
- if (!process.env.TINYPNG_KEY) {
+ if (!options.key) {
throw new Error('vite-plugin-tinify: TINYPNG_KEY not defined. **Images not optimized**')
}
@@ -42,7 +42,7 @@ export default function tinifyPlugin(): Plugin {
}, process.cwd())
}
- tinify.key = process.env.TINYPNG_KEY
+ tinify.key = options.key
content = await tinify.fromBuffer(asset.source).toBuffer()
fs.writeFile(checksumfile, content, error => error && console.log(error))
diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts
index 1ed262a..6b1377c 100644
--- a/frontend/vite.config.ts
+++ b/frontend/vite.config.ts
@@ -1,58 +1,62 @@
import { join, dirname } from 'node:path'
import { sveltekit } from '@sveltejs/kit/vite'
import tailwindcss from '@tailwindcss/vite'
-import { defineConfig, searchForWorkspaceRoot } from 'vite'
+import { defineConfig, loadEnv, searchForWorkspaceRoot } from 'vite'
import svgo from './utility/vite-plugin-svgo'
import tinify from './utility/vite-plugin-tinify'
-export default defineConfig({
- plugins: [tailwindcss(), sveltekit(), tinify(), svgo()],
- css: {
- transformer: 'lightningcss',
- lightningcss: {
- drafts: { customMedia: true },
- customAtRules: {
- 'reference': { prelude: '', body: null },
- 'utility': { prelude: '*', body: 'style-block' },
- 'theme': { prelude: '*', body: 'style-block' },
- 'custom-variant': { prelude: '*', body: null },
- 'source': { prelude: '*', body: null },
- 'apply': { prelude: '*', body: null },
+export default defineConfig(({ mode }) => {
+ const { TINYPNG_KEY } = loadEnv(mode, import.meta.dirname, '')
+
+ return {
+ plugins: [tailwindcss(), sveltekit(), tinify({ key: TINYPNG_KEY }), svgo()],
+ css: {
+ transformer: 'lightningcss',
+ lightningcss: {
+ drafts: { customMedia: true },
+ customAtRules: {
+ 'reference': { prelude: '', body: null },
+ 'utility': { prelude: '*', body: 'style-block' },
+ 'theme': { prelude: '*', body: 'style-block' },
+ 'custom-variant': { prelude: '*', body: null },
+ 'source': { prelude: '*', body: null },
+ 'apply': { prelude: '*', body: null },
+ },
},
},
- },
- build: {
- cssMinify: 'lightningcss',
- rollupOptions: {
- output: {
- manualChunks(id) {
- if (id.includes('node_modules/svelte')) {
- return 'svelte'
- }
+ build: {
+ cssMinify: 'lightningcss',
+ rollupOptions: {
+ output: {
+ manualChunks(id) {
+ if (id.includes('node_modules/svelte')) {
+ return 'svelte'
+ }
- if (id.includes('node_modules/zod')) {
- return 'zod'
- }
+ if (id.includes('node_modules/zod')) {
+ return 'zod'
+ }
+ },
},
},
},
- },
- resolve: {
- alias: {
- '@fontawesome': join(
- dirname(import.meta.dirname),
- 'backend/vendor/npm-asset/fortawesome--fontawesome-pro/svgs',
- ),
+ resolve: {
+ alias: {
+ '@fontawesome': join(
+ dirname(import.meta.dirname),
+ 'backend/vendor/npm-asset/fortawesome--fontawesome-pro/svgs',
+ ),
+ },
},
- },
- server: {
- allowedHosts: ['.ddev.site'],
- fs: {
- allow: [
- join(import.meta.dirname, 'static'),
- join(dirname(import.meta.dirname), 'backend/vendor/npm-asset/fortawesome--fontawesome-pro/svgs'),
- searchForWorkspaceRoot(import.meta.dirname),
- ],
+ server: {
+ allowedHosts: ['.ddev.site'],
+ fs: {
+ allow: [
+ join(import.meta.dirname, 'static'),
+ join(dirname(import.meta.dirname), 'backend/vendor/npm-asset/fortawesome--fontawesome-pro/svgs'),
+ searchForWorkspaceRoot(import.meta.dirname),
+ ],
+ },
},
- },
+ }
})
diff --git a/utility/install.sh b/utility/install.sh
index c7d99bd..774b300 100755
--- a/utility/install.sh
+++ b/utility/install.sh
@@ -15,8 +15,8 @@ if ! command -v ddev > /dev/null 2>&1; then
fi
write_env() {
- cp "${ROOT_DIR}/backend/.env.example" "${ROOT_DIR}/backend/.env"
- cp "${ROOT_DIR}/frontend/.env.example" "${ROOT_DIR}/frontend/.env"
+ [ -f "${ROOT_DIR}/backend/.env" ] || cp "${ROOT_DIR}/backend/.env.example" "${ROOT_DIR}/backend/.env"
+ [ -f "${ROOT_DIR}/frontend/.env" ] || cp "${ROOT_DIR}/frontend/.env.example" "${ROOT_DIR}/frontend/.env"
ddev dotenv set "${ROOT_DIR}/backend/.env" --primary-site-name="${PROJECT_NAME}"
ddev dotenv set "${ROOT_DIR}/backend/.env" --primary-site-url="${FRONTEND_URL}"
diff --git a/utility/vite-plugin-tinify.ts b/utility/vite-plugin-tinify.ts
index f8333d5..ed224ab 100644
--- a/utility/vite-plugin-tinify.ts
+++ b/utility/vite-plugin-tinify.ts
@@ -3,12 +3,12 @@ import fs from 'node:fs'
import tinify from 'tinify'
import type { Plugin } from 'vite'
-export default (): Plugin => ({
+export default (options: { key?: string } = {}): Plugin => ({
name: 'vite-plugin-tinify',
async generateBundle(_, bundler) {
for (const [path, asset] of Object.entries(bundler)) {
if (/\.(png|jpe?g)$/.test(path) && asset.type === 'asset' && typeof asset.source !== 'undefined') {
- const checksum = crypto.createHash('sha1').update(asset.source.toString()).digest('hex')
+ const checksum = crypto.createHash('sha1').update(asset.source).digest('hex')
const checksumfile = `node_modules/.vite/tinify/${checksum}`
let content: Buffer | Uint8Array | undefined
@@ -16,7 +16,7 @@ export default (): Plugin => ({
if (fs.existsSync(checksumfile)) {
content = fs.readFileSync(checksumfile)
} else {
- if (!process.env.TINYPNG_KEY) {
+ if (!options.key) {
throw new Error('vite-plugin-tinify: TINYPNG_KEY not defined. **Images not optimized**')
}
@@ -40,7 +40,7 @@ export default (): Plugin => ({
}, process.cwd())
}
- tinify.key = process.env.TINYPNG_KEY
+ tinify.key = options.key
content = await tinify.fromBuffer(asset.source).toBuffer()
fs.writeFile(checksumfile, content, error => error && console.log(error))