feat: add Vite plugins for enhanced functionality and localization support

- Introduced multiple Vite plugins including `ogImagePlugin`, `feedSitemapPlugin`, `localesJsonPlugin`, and `manifestInjectPlugin` to enhance the build process and support dynamic content generation.
- Implemented a custom hot module replacement (HMR) for JSON localization files to improve development experience.
- Added a new `createDependencyChunksPlugin` for better chunk management in the build process.
- Established a structure for handling localization files and generating corresponding assets, improving internationalization support.

Signed-off-by: Innei <tukon479@gmail.com>
This commit is contained in:
Innei
2025-06-27 16:58:12 +08:00
parent 854ee1df8d
commit 6668425fba
10 changed files with 32 additions and 41 deletions

View File

@@ -0,0 +1,9 @@
import path from 'node:path'
const dirname = path.dirname(new URL(import.meta.url).pathname)
export const MANIFEST_PATH = path.resolve(
dirname,
'../../../../../packages/data/src/photos-manifest.json',
)
export const MONOREPO_ROOT_PATH = path.resolve(dirname, '../../../../..')

View File

@@ -1,11 +1,11 @@
import { readFileSync } from 'node:fs' import { readFileSync } from 'node:fs'
import { resolve } from 'node:path'
import { fileURLToPath } from 'node:url' import { fileURLToPath } from 'node:url'
import type { PhotoManifestItem } from '@afilmory/builder' import type { PhotoManifestItem } from '@afilmory/builder'
import type { Plugin } from 'vite' import type { Plugin } from 'vite'
import type { SiteConfig } from '../../site.config' import type { SiteConfig } from '../../../../site.config'
import { MANIFEST_PATH } from './__internal__/constants'
const __dirname = fileURLToPath(new URL('.', import.meta.url)) const __dirname = fileURLToPath(new URL('.', import.meta.url))
@@ -15,13 +15,8 @@ export function createFeedSitemapPlugin(siteConfig: SiteConfig): Plugin {
apply: 'build', apply: 'build',
generateBundle() { generateBundle() {
try { try {
// Read photos manifest
const manifestPath = resolve(
__dirname,
'../../packages/data/src/photos-manifest.json',
)
const photosData: PhotoManifestItem[] = JSON.parse( const photosData: PhotoManifestItem[] = JSON.parse(
readFileSync(manifestPath, 'utf-8'), readFileSync(MANIFEST_PATH, 'utf-8'),
).data ).data
// Sort photos by date taken (newest first) // Sort photos by date taken (newest first)

View File

@@ -1,11 +1,8 @@
import path from 'node:path'
import { fileURLToPath } from 'node:url'
import { set } from 'es-toolkit/compat' import { set } from 'es-toolkit/compat'
import type { Plugin } from 'vite' import type { Plugin } from 'vite'
const __dirname = fileURLToPath(new URL('.', import.meta.url)) import { MONOREPO_ROOT_PATH } from './__internal__/constants'
const localesDir = path.resolve(__dirname, '../../locales')
export function localesJsonPlugin(): Plugin { export function localesJsonPlugin(): Plugin {
return { return {
@@ -13,7 +10,7 @@ export function localesJsonPlugin(): Plugin {
enforce: 'pre', enforce: 'pre',
async transform(code, id) { async transform(code, id) {
if (!id.includes(localesDir) || !id.endsWith('.json')) { if (!id.includes(MONOREPO_ROOT_PATH) || !id.endsWith('.json')) {
return null return null
} }

View File

@@ -1,26 +1,23 @@
import fs from 'node:fs' import fs from 'node:fs'
import path, { dirname } from 'node:path' import path from 'node:path'
import { fileURLToPath } from 'node:url'
import { set } from 'es-toolkit/compat' import { set } from 'es-toolkit/compat'
import type { Plugin } from 'vite' import type { Plugin } from 'vite'
import { MONOREPO_ROOT_PATH } from './__internal__/constants'
export function localesPlugin(): Plugin { export function localesPlugin(): Plugin {
return { return {
name: 'locales-merge', name: 'locales-merge',
enforce: 'post', enforce: 'post',
generateBundle(_options, bundle) { generateBundle(_options, bundle) {
const __dirname = dirname(fileURLToPath(import.meta.url))
const localesDir = path.resolve(__dirname, '../../locales')
const namespaces = fs const namespaces = fs
.readdirSync(localesDir) .readdirSync(MONOREPO_ROOT_PATH)
.filter((dir) => dir !== '.DS_Store') .filter((dir) => dir !== '.DS_Store')
const languageResources = {} as any const languageResources = {} as any
namespaces.forEach((namespace) => { namespaces.forEach((namespace) => {
const namespacePath = path.join(localesDir, namespace) const namespacePath = path.join(MONOREPO_ROOT_PATH, namespace)
const files = fs const files = fs
.readdirSync(namespacePath) .readdirSync(namespacePath)
.filter((file) => file.endsWith('.json')) .filter((file) => file.endsWith('.json'))

View File

@@ -1,19 +1,13 @@
import { readFileSync } from 'node:fs' import { readFileSync } from 'node:fs'
import path from 'node:path'
import type { Plugin } from 'vite' import type { Plugin } from 'vite'
const dirname = path.dirname(new URL(import.meta.url).pathname) import { MANIFEST_PATH } from './__internal__/constants'
export function manifestInjectPlugin(): Plugin {
// 定位到 manifest 文件的实际位置
const manifestPath = path.resolve(
dirname,
'../../packages/data/src/photos-manifest.json',
)
export function manifestInjectPlugin(): Plugin {
function getManifestContent(): string { function getManifestContent(): string {
try { try {
const content = readFileSync(manifestPath, 'utf-8') const content = readFileSync(MANIFEST_PATH, 'utf-8')
return content return content
} catch (error) { } catch (error) {
console.warn('Failed to read manifest file:', error) console.warn('Failed to read manifest file:', error)
@@ -26,10 +20,10 @@ export function manifestInjectPlugin(): Plugin {
configureServer(server) { configureServer(server) {
// 监听 manifest 文件变化 // 监听 manifest 文件变化
server.watcher.add(manifestPath) server.watcher.add(MANIFEST_PATH)
server.watcher.on('change', (file) => { server.watcher.on('change', (file) => {
if (file === manifestPath) { if (file === MANIFEST_PATH) {
console.info( console.info(
'[manifest-inject] Manifest file changed, triggering HMR...', '[manifest-inject] Manifest file changed, triggering HMR...',
) )

View File

@@ -1,8 +1,8 @@
import type { Plugin } from 'vite' import type { Plugin } from 'vite'
import { cleanupOldOGImages } from '../scripts/cleanup-og-images.js' import { cleanupOldOGImages } from '../../../../scripts/cleanup-og-images.js'
import { generateFavicons } from '../scripts/generate-favicon.js' import { generateFavicons } from '../../../../scripts/generate-favicon.js'
import { generateOGImage } from '../scripts/generate-og-image.js' import { generateOGImage } from '../../../../scripts/generate-og-image.js'
interface OGImagePluginOptions { interface OGImagePluginOptions {
title?: string title?: string

View File

@@ -13,13 +13,13 @@ import { createHtmlPlugin } from 'vite-plugin-html'
import tsconfigPaths from 'vite-tsconfig-paths' import tsconfigPaths from 'vite-tsconfig-paths'
import PKG from '../../package.json' import PKG from '../../package.json'
import { ogImagePlugin } from '../../plugins/og-image-plugin'
import { createDependencyChunksPlugin } from '../../plugins/vite/deps'
import { createFeedSitemapPlugin } from '../../plugins/vite/feed-sitemap'
import { localesJsonPlugin } from '../../plugins/vite/locales-json'
import { manifestInjectPlugin } from '../../plugins/vite/manifest-inject'
import { siteConfig } from '../../site.config' import { siteConfig } from '../../site.config'
import { astPlugin } from './plugins/vite/ast' import { astPlugin } from './plugins/vite/ast'
import { createDependencyChunksPlugin } from './plugins/vite/deps'
import { createFeedSitemapPlugin } from './plugins/vite/feed-sitemap'
import { localesJsonPlugin } from './plugins/vite/locales-json'
import { manifestInjectPlugin } from './plugins/vite/manifest-inject'
import { ogImagePlugin } from './plugins/vite/og-image-plugin'
const __dirname = path.dirname(fileURLToPath(import.meta.url)) const __dirname = path.dirname(fileURLToPath(import.meta.url))

View File

@@ -224,7 +224,6 @@
"loading.heic.main": "HEIC", "loading.heic.main": "HEIC",
"loading.webgl.building": "高品質テクスチャを構築中...", "loading.webgl.building": "高品質テクスチャを構築中...",
"loading.webgl.main": "WebGL テクスチャの読み込み", "loading.webgl.main": "WebGL テクスチャの読み込み",
"photo.conversion.transmux": "トランスマックス",
"photo.conversion.webcodecs": "WebCodecs", "photo.conversion.webcodecs": "WebCodecs",
"photo.copy.error": "画像のコピーに失敗しました。後でもう一度お試しください。", "photo.copy.error": "画像のコピーに失敗しました。後でもう一度お試しください。",
"photo.copy.image": "画像をコピー", "photo.copy.image": "画像をコピー",