From 7ca7655e40190d905853767a9f4f005a5f45c2e6 Mon Sep 17 00:00:00 2001 From: Gbanyan Date: Wed, 19 Nov 2025 17:31:18 +0800 Subject: [PATCH] Add asset sync script --- .gitignore | 3 +++ README.md | 16 ++++++++++++---- package.json | 5 ++++- public/assets | 1 - scripts/sync-assets.mjs | 27 +++++++++++++++++++++++++++ 5 files changed, 46 insertions(+), 6 deletions(-) delete mode 120000 public/assets create mode 100644 scripts/sync-assets.mjs diff --git a/.gitignore b/.gitignore index cd70e1c..c410c09 100644 --- a/.gitignore +++ b/.gitignore @@ -34,5 +34,8 @@ pnpm-debug.log* # Vercel .vercel +# Generated assets mirror +/public/assets + # TypeScript *.tsbuildinfo diff --git a/README.md b/README.md index 3a6c3d0..a9da9b8 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,7 @@ Recent iterations focused on migrating every image to `next/image`, refreshing t - `posts/` – Blog posts (`.md`) - `pages/` – Static pages (`.md`) - `assets/` – Images referenced from markdown -- `public/assets` – Symlink to `content/assets` for serving images at `/assets/...` +- `public/assets` – Copy of `content/assets` that is refreshed via `npm run sync-assets` (and automatically before `npm run build`) so Next.js can serve `/assets/...` without relying on symlinks. - `contentlayer.config.ts` – Contentlayer document types and markdown pipeline ## UI Overview @@ -199,9 +199,17 @@ Recent iterations focused on migrating every image to `next/image`, refreshing t echo -n 'your-email@example.com' | md5 # macOS # or echo -n 'your-email@example.com' | md5sum | cut -d' ' -f1 # Linux - ``` +``` -5. **Run the development server** +5. **Mirror markdown assets** + + ```bash + npm run sync-assets + ``` + + This copies `content/assets` into `public/assets` so `/assets/...` continues to work; the build script already runs it before `next build`, but running it locally keeps your previews in sync. + +6. **Run the development server** ```bash npm run dev @@ -242,7 +250,7 @@ Contentlayer is configured in `contentlayer.config.ts` to read from the `content ``` - At build time, a rehype plugin rewrites these to `/assets/my-image.jpg`. -- `public/assets` is a symlink to `content/assets`, so Next.js serves them as static files. +- `public/assets` is populated from `content/assets` before each build (and via `npm run sync-assets`) so `/assets/...` stays available without symlinks. - `feature_image` fields are also mapped from `../assets/...` → `/assets/...` and rendered above the article content via `next/image`. - All component-level imagery (list thumbnails, related posts, sidebar avatar, about page hero, etc.) now uses `next/image` for responsive sizing, blur placeholders, and better LCP. diff --git a/package.json b/package.json index a1285cf..6e1883f 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,8 @@ "main": "index.js", "scripts": { "dev": "next dev", - "build": "next build", + "sync-assets": "node scripts/sync-assets.mjs", + "build": "npm run sync-assets && next build", "start": "next start", "lint": "next lint", "contentlayer": "contentlayer build" @@ -20,6 +21,7 @@ "@fortawesome/react-fontawesome": "^3.1.0", "clsx": "^2.1.1", "contentlayer": "^0.3.4", + "framer-motion": "^12.23.24", "gray-matter": "^4.0.3", "markdown-wasm": "^1.2.0", "next": "^13.5.11", @@ -30,6 +32,7 @@ "rehype-autolink-headings": "^7.1.0", "rehype-slug": "^6.0.0", "remark-gfm": "^4.0.1", + "tailwind-merge": "^3.4.0", "unist-util-visit": "^5.0.0" }, "devDependencies": { diff --git a/public/assets b/public/assets deleted file mode 120000 index 5d1357b..0000000 --- a/public/assets +++ /dev/null @@ -1 +0,0 @@ -../content/assets \ No newline at end of file diff --git a/scripts/sync-assets.mjs b/scripts/sync-assets.mjs new file mode 100644 index 0000000..1c54132 --- /dev/null +++ b/scripts/sync-assets.mjs @@ -0,0 +1,27 @@ +import { cp, mkdir, rm, stat } from 'node:fs/promises'; +import path from 'node:path'; + +async function syncAssets() { + const root = process.cwd(); + const sourceDir = path.join(root, 'content', 'assets'); + const targetDir = path.join(root, 'public', 'assets'); + + await rm(targetDir, { recursive: true, force: true }); + await mkdir(targetDir, { recursive: true }); + + try { + await stat(sourceDir); + } catch { + // Nothing to copy yet; leave the empty directory in place. + console.info('sync-assets: no content/assets directory found yet.'); + return; + } + + await cp(sourceDir, targetDir, { recursive: true, force: true }); + console.info('sync-assets: copied content/assets → public/assets.'); +} + +syncAssets().catch((error) => { + console.error('sync-assets: failed to mirror assets.', error); + process.exit(1); +});