@life-and-dev/mdsite 0.1.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +40 -22
- package/dist/commands/commands.test.js +103 -31
- package/dist/commands/commands.test.js.map +1 -1
- package/dist/commands/init.d.ts +1 -1
- package/dist/commands/init.js +64 -2
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/prepare.js +22 -14
- package/dist/commands/prepare.js.map +1 -1
- package/dist/commands/prepare.test.js +33 -39
- package/dist/commands/prepare.test.js.map +1 -1
- package/dist/commands/preview.d.ts +1 -0
- package/dist/commands/preview.js +11 -10
- package/dist/commands/preview.js.map +1 -1
- package/dist/commands/start.d.ts +1 -0
- package/dist/commands/start.js +23 -10
- package/dist/commands/start.js.map +1 -1
- package/dist/commands/stop.js +6 -3
- package/dist/commands/stop.js.map +1 -1
- package/dist/commands/workflows.test.js +81 -21
- package/dist/commands/workflows.test.js.map +1 -1
- package/dist/config/mdsite-config.js +1 -1
- package/dist/config/mdsite-config.js.map +1 -1
- package/dist/config/mdsite-config.test.js +1 -1
- package/dist/config/mdsite-config.test.js.map +1 -1
- package/dist/config/menu.js +0 -1
- package/dist/config/menu.js.map +1 -1
- package/dist/index.js +44 -10
- package/dist/index.js.map +1 -1
- package/dist/index.test.js +40 -0
- package/dist/index.test.js.map +1 -1
- package/dist/process/child-process.test.js +3 -3
- package/dist/process/child-process.test.js.map +1 -1
- package/dist/process/runtime-state.d.ts +6 -5
- package/dist/process/runtime-state.js +13 -17
- package/dist/process/runtime-state.js.map +1 -1
- package/dist/process/runtime-state.test.js +21 -13
- package/dist/process/runtime-state.test.js.map +1 -1
- package/dist/renderer/mdsite-nuxt.d.ts +2 -0
- package/dist/renderer/mdsite-nuxt.js +29 -32
- package/dist/renderer/mdsite-nuxt.js.map +1 -1
- package/dist/renderer/mdsite-nuxt.test.js +37 -48
- package/dist/renderer/mdsite-nuxt.test.js.map +1 -1
- package/mdsite-nuxt/app/composables/useAppTheme.ts +22 -3
- package/mdsite-nuxt/app/config/themes.ts +38 -0
- package/mdsite-nuxt/nuxt.config.ts +11 -1
- package/mdsite-nuxt/scripts/generate-favicons.ts +24 -55
- package/mdsite-nuxt/scripts/renderer-hooks.test.ts +0 -8
- package/mdsite-nuxt/scripts/renderer-hooks.ts +1 -2
- package/mdsite-nuxt/scripts/sync-content.ts +5 -79
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// https://nuxt.com/docs/api/configuration/nuxt-config
|
|
2
2
|
import path from 'path'
|
|
3
|
-
import { getDomainThemes } from './app/config/themes'
|
|
3
|
+
import { buildDarkOverrideCss, getDomainThemes } from './app/config/themes'
|
|
4
4
|
import { createBibleReferencePatterns } from './app/utils/bible-book-names'
|
|
5
5
|
import { runBuildFallbackHooks } from './scripts/renderer-hooks'
|
|
6
6
|
import { withBasePath } from './utils/base-url'
|
|
@@ -59,6 +59,16 @@ export default defineNuxtConfig({
|
|
|
59
59
|
app: {
|
|
60
60
|
baseURL: appBaseURL,
|
|
61
61
|
head: {
|
|
62
|
+
style: [
|
|
63
|
+
{ innerHTML: buildDarkOverrideCss() }
|
|
64
|
+
],
|
|
65
|
+
script: [
|
|
66
|
+
{
|
|
67
|
+
tagPosition: 'head',
|
|
68
|
+
tagPriority: 'critical',
|
|
69
|
+
innerHTML: `(function(){try{var t=localStorage.getItem('theme-preference');if(t!=='light'&&t!=='dark'){t=(window.matchMedia&&window.matchMedia('(prefers-color-scheme: dark)').matches)?'dark':'light';}document.documentElement.setAttribute('data-mdsite-theme',t);}catch(e){document.documentElement.setAttribute('data-mdsite-theme','light');}})();`
|
|
70
|
+
}
|
|
71
|
+
],
|
|
62
72
|
link: [
|
|
63
73
|
{ rel: 'icon', type: 'image/svg+xml', href: withBasePath('/favicon.svg', appBaseURL), sizes: 'any' },
|
|
64
74
|
{ rel: 'icon', type: 'image/x-icon', href: withBasePath('/favicon.ico', appBaseURL), sizes: '32x32' },
|
|
@@ -19,45 +19,49 @@ const FAVICON_SIZES = {
|
|
|
19
19
|
/**
|
|
20
20
|
* Generate favicons from the active mdsite config
|
|
21
21
|
*/
|
|
22
|
-
export async function generateFavicons() {
|
|
22
|
+
export async function generateFavicons(): Promise<boolean> {
|
|
23
23
|
const { contentDir, config } = loadMdsiteConfigSync()
|
|
24
|
-
const logoPath = path.join(contentDir, 'logo.svg')
|
|
25
|
-
const faviconDir = path.join(contentDir, 'favicon')
|
|
26
24
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
console.error(`❌ Logo not found: ${logoPath}`)
|
|
25
|
+
if (!config.favicon || !config.favicon.trim()) {
|
|
26
|
+
console.warn(`⚠️ No favicon source configured (config.favicon is empty). Skipping favicon generation.`)
|
|
30
27
|
return false
|
|
31
28
|
}
|
|
32
29
|
|
|
33
|
-
|
|
30
|
+
const sourcePath = path.resolve(contentDir, config.favicon)
|
|
31
|
+
|
|
32
|
+
if (!await fs.pathExists(sourcePath)) {
|
|
33
|
+
console.error(`❌ Favicon source not found: ${sourcePath}`)
|
|
34
|
+
return false
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const publicDir = path.resolve(__dirname, '..', 'public')
|
|
38
|
+
await fs.ensureDir(publicDir)
|
|
34
39
|
|
|
35
40
|
console.log(`🎨 Generating favicons for site: ${config.site.name}`)
|
|
36
|
-
console.log(` Source: ${
|
|
37
|
-
console.log(` Output: ${
|
|
41
|
+
console.log(` Source: ${sourcePath}`)
|
|
42
|
+
console.log(` Output: ${publicDir}`)
|
|
38
43
|
|
|
39
44
|
try {
|
|
40
45
|
// Copy SVG as-is (for modern browsers)
|
|
41
|
-
const svgTargetPath = path.join(
|
|
42
|
-
await fs.copy(
|
|
46
|
+
const svgTargetPath = path.join(publicDir, 'favicon.svg')
|
|
47
|
+
await fs.copy(sourcePath, svgTargetPath)
|
|
43
48
|
console.log(` ✓ SVG: favicon.svg`)
|
|
44
49
|
|
|
45
50
|
// Generate ICO (32x32 with transparent padding)
|
|
46
|
-
const icoTargetPath = path.join(
|
|
47
|
-
const png32Buffer = await sharp(
|
|
51
|
+
const icoTargetPath = path.join(publicDir, 'favicon.ico')
|
|
52
|
+
const png32Buffer = await sharp(sourcePath)
|
|
48
53
|
.resize(32, 32, {
|
|
49
54
|
fit: 'contain',
|
|
50
55
|
background: { r: 255, g: 255, b: 255, alpha: 0 }
|
|
51
56
|
})
|
|
52
57
|
.png()
|
|
53
58
|
.toBuffer()
|
|
54
|
-
|
|
55
59
|
await fs.writeFile(icoTargetPath, png32Buffer)
|
|
56
60
|
console.log(` ✓ ICO: favicon.ico`)
|
|
57
61
|
|
|
58
62
|
// Generate Apple Touch Icon (180x180 with transparent padding)
|
|
59
|
-
const appleTouchPath = path.join(
|
|
60
|
-
await sharp(
|
|
63
|
+
const appleTouchPath = path.join(publicDir, 'apple-touch-icon.png')
|
|
64
|
+
await sharp(sourcePath)
|
|
61
65
|
.resize(FAVICON_SIZES.appleTouchIcon, FAVICON_SIZES.appleTouchIcon, {
|
|
62
66
|
fit: 'contain',
|
|
63
67
|
background: { r: 255, g: 255, b: 255, alpha: 0 }
|
|
@@ -67,8 +71,8 @@ export async function generateFavicons() {
|
|
|
67
71
|
console.log(` ✓ Apple Touch Icon: apple-touch-icon.png`)
|
|
68
72
|
|
|
69
73
|
// Generate PWA Icon 192x192
|
|
70
|
-
const icon192Path = path.join(
|
|
71
|
-
await sharp(
|
|
74
|
+
const icon192Path = path.join(publicDir, 'icon-192.png')
|
|
75
|
+
await sharp(sourcePath)
|
|
72
76
|
.resize(FAVICON_SIZES.pwaIcon192, FAVICON_SIZES.pwaIcon192, {
|
|
73
77
|
fit: 'contain',
|
|
74
78
|
background: { r: 255, g: 255, b: 255, alpha: 0 }
|
|
@@ -78,8 +82,8 @@ export async function generateFavicons() {
|
|
|
78
82
|
console.log(` ✓ PWA Icon 192: icon-192.png`)
|
|
79
83
|
|
|
80
84
|
// Generate PWA Icon 512x512
|
|
81
|
-
const icon512Path = path.join(
|
|
82
|
-
await sharp(
|
|
85
|
+
const icon512Path = path.join(publicDir, 'icon-512.png')
|
|
86
|
+
await sharp(sourcePath)
|
|
83
87
|
.resize(FAVICON_SIZES.pwaIcon512, FAVICON_SIZES.pwaIcon512, {
|
|
84
88
|
fit: 'contain',
|
|
85
89
|
background: { r: 255, g: 255, b: 255, alpha: 0 }
|
|
@@ -96,40 +100,6 @@ export async function generateFavicons() {
|
|
|
96
100
|
}
|
|
97
101
|
}
|
|
98
102
|
|
|
99
|
-
/**
|
|
100
|
-
* Copy favicon files from content submodule to public directory
|
|
101
|
-
*/
|
|
102
|
-
export async function copyFaviconsToPublic() {
|
|
103
|
-
const projectRoot = path.resolve(__dirname, '..')
|
|
104
|
-
const { contentDir, config } = loadMdsiteConfigSync()
|
|
105
|
-
const faviconDir = path.join(contentDir, 'favicon')
|
|
106
|
-
const publicDir = path.join(projectRoot, 'public')
|
|
107
|
-
|
|
108
|
-
console.log(`📋 Copying ${config.site.name} favicons to public...`)
|
|
109
|
-
|
|
110
|
-
const files = [
|
|
111
|
-
'favicon.svg',
|
|
112
|
-
'favicon.ico',
|
|
113
|
-
'apple-touch-icon.png',
|
|
114
|
-
'icon-192.png',
|
|
115
|
-
'icon-512.png'
|
|
116
|
-
]
|
|
117
|
-
|
|
118
|
-
for (const file of files) {
|
|
119
|
-
const sourcePath = path.join(faviconDir, file)
|
|
120
|
-
const targetPath = path.join(publicDir, file)
|
|
121
|
-
|
|
122
|
-
if (await fs.pathExists(sourcePath)) {
|
|
123
|
-
await fs.copy(sourcePath, targetPath)
|
|
124
|
-
console.log(` ✓ ${file}`)
|
|
125
|
-
} else {
|
|
126
|
-
console.warn(` ⚠ Missing: ${file}`)
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
console.log(`✅ Favicon copy complete\n`)
|
|
131
|
-
}
|
|
132
|
-
|
|
133
103
|
/**
|
|
134
104
|
* Generate web manifest for PWA support
|
|
135
105
|
*/
|
|
@@ -183,7 +153,6 @@ if (import.meta.url === `file://${process.argv[1]}`) {
|
|
|
183
153
|
; (async () => {
|
|
184
154
|
const success = await generateFavicons()
|
|
185
155
|
if (success) {
|
|
186
|
-
await copyFaviconsToPublic()
|
|
187
156
|
await generateWebManifest(config.site.name)
|
|
188
157
|
} else {
|
|
189
158
|
process.exit(1)
|
|
@@ -4,7 +4,6 @@ import { beforeEach, describe, expect, it, vi } from 'vitest'
|
|
|
4
4
|
|
|
5
5
|
const {
|
|
6
6
|
buildContentDataMock,
|
|
7
|
-
copyFaviconsToPublicMock,
|
|
8
7
|
existsSyncMock,
|
|
9
8
|
generateFaviconsMock,
|
|
10
9
|
generateWebManifestMock,
|
|
@@ -20,7 +19,6 @@ const {
|
|
|
20
19
|
writeFileSyncMock,
|
|
21
20
|
} = vi.hoisted(() => ({
|
|
22
21
|
buildContentDataMock: vi.fn(),
|
|
23
|
-
copyFaviconsToPublicMock: vi.fn(),
|
|
24
22
|
existsSyncMock: vi.fn(),
|
|
25
23
|
generateFaviconsMock: vi.fn(),
|
|
26
24
|
generateWebManifestMock: vi.fn(),
|
|
@@ -60,7 +58,6 @@ vi.mock('./generate-indices.js', () => ({
|
|
|
60
58
|
}))
|
|
61
59
|
|
|
62
60
|
vi.mock('./generate-favicons.js', () => ({
|
|
63
|
-
copyFaviconsToPublic: copyFaviconsToPublicMock,
|
|
64
61
|
generateFavicons: generateFaviconsMock,
|
|
65
62
|
generateWebManifest: generateWebManifestMock,
|
|
66
63
|
}))
|
|
@@ -99,7 +96,6 @@ describe('renderer hooks orchestration', () => {
|
|
|
99
96
|
stringifyYamlMock.mockReturnValue('compat-config')
|
|
100
97
|
generateFaviconsMock.mockResolvedValue(true)
|
|
101
98
|
buildContentDataMock.mockResolvedValue(undefined)
|
|
102
|
-
copyFaviconsToPublicMock.mockResolvedValue(undefined)
|
|
103
99
|
generateWebManifestMock.mockResolvedValue(undefined)
|
|
104
100
|
startWatcherMock.mockResolvedValue(undefined)
|
|
105
101
|
syncContentMock.mockResolvedValue(undefined)
|
|
@@ -255,7 +251,6 @@ describe('renderer hooks orchestration', () => {
|
|
|
255
251
|
expect(syncContentMock).toHaveBeenCalledTimes(1)
|
|
256
252
|
expect(buildContentDataMock).toHaveBeenCalledTimes(1)
|
|
257
253
|
expect(generateFaviconsMock).toHaveBeenCalledTimes(1)
|
|
258
|
-
expect(copyFaviconsToPublicMock).toHaveBeenCalledTimes(1)
|
|
259
254
|
expect(generateWebManifestMock).toHaveBeenCalledWith('Docs')
|
|
260
255
|
expect(syncContentMock.mock.invocationCallOrder[0]).toBeLessThan(buildContentDataMock.mock.invocationCallOrder[0])
|
|
261
256
|
expect(buildContentDataMock.mock.invocationCallOrder[0]).toBeLessThan(generateFaviconsMock.mock.invocationCallOrder[0])
|
|
@@ -268,7 +263,6 @@ describe('renderer hooks orchestration', () => {
|
|
|
268
263
|
expect(syncContentMock).toHaveBeenCalledTimes(1)
|
|
269
264
|
expect(buildContentDataMock).toHaveBeenCalledTimes(1)
|
|
270
265
|
expect(generateFaviconsMock).toHaveBeenCalledTimes(1)
|
|
271
|
-
expect(copyFaviconsToPublicMock).toHaveBeenCalledTimes(1)
|
|
272
266
|
expect(generateWebManifestMock).toHaveBeenCalledWith('Docs')
|
|
273
267
|
expect(startWatcherMock).not.toHaveBeenCalled()
|
|
274
268
|
expect(rmMock).not.toHaveBeenCalled()
|
|
@@ -292,7 +286,6 @@ describe('renderer hooks orchestration', () => {
|
|
|
292
286
|
|
|
293
287
|
expect(buildContentDataMock).toHaveBeenCalledTimes(1)
|
|
294
288
|
expect(generateFaviconsMock).toHaveBeenCalledTimes(1)
|
|
295
|
-
expect(copyFaviconsToPublicMock).not.toHaveBeenCalled()
|
|
296
289
|
expect(generateWebManifestMock).not.toHaveBeenCalled()
|
|
297
290
|
})
|
|
298
291
|
|
|
@@ -304,7 +297,6 @@ describe('renderer hooks orchestration', () => {
|
|
|
304
297
|
expect(syncContentMock).toHaveBeenCalledTimes(1)
|
|
305
298
|
expect(buildContentDataMock).toHaveBeenCalledTimes(1)
|
|
306
299
|
expect(generateFaviconsMock).toHaveBeenCalledTimes(1)
|
|
307
|
-
expect(copyFaviconsToPublicMock).not.toHaveBeenCalled()
|
|
308
300
|
expect(generateWebManifestMock).not.toHaveBeenCalled()
|
|
309
301
|
expect(process.env.MDSITE_RENDERER_ORCHESTRATED).toBe('1')
|
|
310
302
|
})
|
|
@@ -3,7 +3,7 @@ import path from 'path'
|
|
|
3
3
|
import YAML from 'yaml'
|
|
4
4
|
|
|
5
5
|
import { buildContentData } from './generate-indices.js'
|
|
6
|
-
import { generateFavicons,
|
|
6
|
+
import { generateFavicons, generateWebManifest } from './generate-favicons.js'
|
|
7
7
|
import { startWatcher, syncContent } from './sync-content.js'
|
|
8
8
|
import { loadMdsiteConfigSync, resolveMdsiteConfigPath } from '../utils/mdsite-config.js'
|
|
9
9
|
|
|
@@ -95,7 +95,6 @@ async function generateFaviconAssets(siteName: string): Promise<void> {
|
|
|
95
95
|
const success = await generateFavicons()
|
|
96
96
|
|
|
97
97
|
if (success) {
|
|
98
|
-
await copyFaviconsToPublic()
|
|
99
98
|
await generateWebManifest(siteName)
|
|
100
99
|
console.log(`✅ Favicons ready for ${siteName}\n`)
|
|
101
100
|
}
|
|
@@ -18,15 +18,6 @@ const STATIC_FILES = [
|
|
|
18
18
|
'site.webmanifest',
|
|
19
19
|
'robots.txt'
|
|
20
20
|
]
|
|
21
|
-
const LOGO_FILE = 'logo.svg'
|
|
22
|
-
const FAVICON_DIR = 'favicon'
|
|
23
|
-
const FAVICON_FILES = [
|
|
24
|
-
'favicon.svg',
|
|
25
|
-
'favicon.ico',
|
|
26
|
-
'apple-touch-icon.png',
|
|
27
|
-
'icon-192.png',
|
|
28
|
-
'icon-512.png'
|
|
29
|
-
]
|
|
30
21
|
|
|
31
22
|
// Debounce timers for JSON regeneration (5 second delay)
|
|
32
23
|
let navigationDebounceTimer: NodeJS.Timeout | null = null
|
|
@@ -118,45 +109,26 @@ export async function generateJsonFiles() {
|
|
|
118
109
|
}
|
|
119
110
|
|
|
120
111
|
/**
|
|
121
|
-
* Copy all images
|
|
112
|
+
* Copy all images from content to public directory (one-time)
|
|
122
113
|
*/
|
|
123
114
|
export async function copyAllImages() {
|
|
124
115
|
const sourceDir = getSourceDir()
|
|
125
116
|
const targetDir = getTargetDir()
|
|
126
117
|
|
|
127
|
-
console.log(`📦 Copying images
|
|
118
|
+
console.log(`📦 Copying images from: ${sourceDir}`)
|
|
128
119
|
console.log(`📦 Target directory: ${targetDir}`)
|
|
129
120
|
|
|
130
121
|
let copiedCount = 0
|
|
131
122
|
|
|
132
|
-
// Copy images (excluding logo.svg and favicon directory - handled separately)
|
|
133
123
|
for (const ext of IMAGE_EXTS) {
|
|
134
124
|
const files = await getAllFiles(sourceDir, ext)
|
|
135
125
|
|
|
136
126
|
for (const sourcePath of files) {
|
|
137
|
-
|
|
138
|
-
// Skip files in favicon directory - handled separately
|
|
139
|
-
if (sourcePath.includes(`${path.sep}${FAVICON_DIR}${path.sep}`)) {
|
|
140
|
-
continue
|
|
141
|
-
}
|
|
142
|
-
|
|
143
127
|
await copyImage(sourcePath, false)
|
|
144
128
|
copiedCount++
|
|
145
129
|
}
|
|
146
130
|
}
|
|
147
131
|
|
|
148
|
-
// Copy favicon files
|
|
149
|
-
const faviconDir = path.join(sourceDir, FAVICON_DIR)
|
|
150
|
-
if (await fs.pathExists(faviconDir)) {
|
|
151
|
-
for (const faviconFile of FAVICON_FILES) {
|
|
152
|
-
const sourcePath = path.join(faviconDir, faviconFile)
|
|
153
|
-
if (await fs.pathExists(sourcePath)) {
|
|
154
|
-
await copyFaviconFile(sourcePath, false)
|
|
155
|
-
copiedCount++
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
|
|
160
132
|
console.log(`✓ Copied ${copiedCount} file(s)\n`)
|
|
161
133
|
}
|
|
162
134
|
|
|
@@ -221,9 +193,7 @@ export async function startWatcher() {
|
|
|
221
193
|
watcher
|
|
222
194
|
.on('add', (filePath) => {
|
|
223
195
|
const fileName = path.basename(filePath)
|
|
224
|
-
if (fileName
|
|
225
|
-
handleLogoChange(filePath, 'added')
|
|
226
|
-
} else if (fileName.endsWith('.md')) {
|
|
196
|
+
if (fileName.endsWith('.md')) {
|
|
227
197
|
// Markdown file added - regenerate both navigation and search
|
|
228
198
|
console.log(`📝 Markdown added: ${fileName}`)
|
|
229
199
|
regenerateNavigation()
|
|
@@ -234,9 +204,7 @@ export async function startWatcher() {
|
|
|
234
204
|
})
|
|
235
205
|
.on('change', (filePath) => {
|
|
236
206
|
const fileName = path.basename(filePath)
|
|
237
|
-
if (fileName
|
|
238
|
-
handleLogoChange(filePath, 'updated')
|
|
239
|
-
} else if (fileName.endsWith('.md')) {
|
|
207
|
+
if (fileName.endsWith('.md')) {
|
|
240
208
|
// Markdown file changed - regenerate both navigation and search
|
|
241
209
|
console.log(`📝 Markdown updated: ${fileName}`)
|
|
242
210
|
regenerateNavigation()
|
|
@@ -247,9 +215,7 @@ export async function startWatcher() {
|
|
|
247
215
|
})
|
|
248
216
|
.on('unlink', (filePath) => {
|
|
249
217
|
const fileName = path.basename(filePath)
|
|
250
|
-
if (fileName
|
|
251
|
-
console.log(`🗑️ Logo removed: ${fileName}`)
|
|
252
|
-
} else if (fileName.endsWith('.md')) {
|
|
218
|
+
if (fileName.endsWith('.md')) {
|
|
253
219
|
// Markdown file deleted - regenerate both navigation and search
|
|
254
220
|
console.log(`📝 Markdown deleted: ${fileName}`)
|
|
255
221
|
regenerateNavigation()
|
|
@@ -363,46 +329,6 @@ async function isDraftOnlyImage(imagePath: string): Promise<boolean> {
|
|
|
363
329
|
return !hasPublishedVersion && hasDraftVersion
|
|
364
330
|
}
|
|
365
331
|
|
|
366
|
-
/**
|
|
367
|
-
* Copy a single favicon file from content to public
|
|
368
|
-
*/
|
|
369
|
-
async function copyFaviconFile(sourcePath: string, log: boolean = true, action: string = 'copied') {
|
|
370
|
-
try {
|
|
371
|
-
const targetDir = getTargetDir()
|
|
372
|
-
const fileName = path.basename(sourcePath)
|
|
373
|
-
const targetPath = path.join(targetDir, fileName)
|
|
374
|
-
|
|
375
|
-
await fs.ensureDir(targetDir)
|
|
376
|
-
await fs.copy(sourcePath, targetPath)
|
|
377
|
-
|
|
378
|
-
if (log) {
|
|
379
|
-
console.log(`✓ Favicon ${action}: ${fileName}`)
|
|
380
|
-
}
|
|
381
|
-
} catch (error) {
|
|
382
|
-
console.error(`❌ Failed to copy favicon ${sourcePath}:`, error)
|
|
383
|
-
}
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
/**
|
|
387
|
-
* Handle logo.svg changes - regenerate favicons
|
|
388
|
-
*/
|
|
389
|
-
async function handleLogoChange(logoPath: string, action: string) {
|
|
390
|
-
const domain = getContentDomain()
|
|
391
|
-
console.log(`🎨 Logo ${action}, regenerating favicons for ${domain}...`)
|
|
392
|
-
|
|
393
|
-
try {
|
|
394
|
-
const { generateFavicons, copyFaviconsToPublic } = await import('./generate-favicons.js')
|
|
395
|
-
const success = await generateFavicons(domain)
|
|
396
|
-
|
|
397
|
-
if (success) {
|
|
398
|
-
await copyFaviconsToPublic(domain)
|
|
399
|
-
console.log(`✓ Favicons regenerated for ${domain}\n`)
|
|
400
|
-
}
|
|
401
|
-
} catch (error) {
|
|
402
|
-
console.error(`❌ Failed to regenerate favicons:`, error)
|
|
403
|
-
}
|
|
404
|
-
}
|
|
405
|
-
|
|
406
332
|
/**
|
|
407
333
|
* Get all files with specific extension recursively
|
|
408
334
|
*/
|