@uniweb/build 0.14.32 → 0.14.34
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/package.json +6 -6
- package/src/prerender.js +14 -3
- package/src/site/advanced-processors.js +11 -3
- package/src/site/asset-processor.js +27 -5
- package/src/site/build-site-data.js +3 -1
- package/src/site/content-collector.js +27 -4
- package/src/site/head-markers.js +16 -0
- package/src/site/plugin.js +19 -19
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@uniweb/build",
|
|
3
|
-
"version": "0.14.
|
|
3
|
+
"version": "0.14.34",
|
|
4
4
|
"description": "Build tooling for the Uniweb Component Web Platform",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -59,12 +59,12 @@
|
|
|
59
59
|
"js-yaml": "^4.1.0",
|
|
60
60
|
"sharp": "^0.33.2",
|
|
61
61
|
"yaml": "^2.5.0",
|
|
62
|
-
"@uniweb/
|
|
63
|
-
"@uniweb/
|
|
62
|
+
"@uniweb/content-writer": "0.2.6",
|
|
63
|
+
"@uniweb/theming": "0.1.9"
|
|
64
64
|
},
|
|
65
65
|
"optionalDependencies": {
|
|
66
|
-
"@uniweb/
|
|
67
|
-
"@uniweb/
|
|
66
|
+
"@uniweb/content-reader": "1.1.13",
|
|
67
|
+
"@uniweb/runtime": "0.8.27",
|
|
68
68
|
"@uniweb/schemas": "0.2.4"
|
|
69
69
|
},
|
|
70
70
|
"peerDependencies": {
|
|
@@ -74,7 +74,7 @@
|
|
|
74
74
|
"@tailwindcss/vite": "^4.0.0",
|
|
75
75
|
"@vitejs/plugin-react": "^4.0.0 || ^5.0.0",
|
|
76
76
|
"vite-plugin-svgr": "^4.0.0",
|
|
77
|
-
"@uniweb/core": "0.7.
|
|
77
|
+
"@uniweb/core": "0.7.21"
|
|
78
78
|
},
|
|
79
79
|
"peerDependenciesMeta": {
|
|
80
80
|
"vite": {
|
package/src/prerender.js
CHANGED
|
@@ -13,6 +13,7 @@ import { join, dirname, resolve } from 'node:path'
|
|
|
13
13
|
import { pathToFileURL } from 'node:url'
|
|
14
14
|
import { executeFetch, mergeDataIntoContent } from './site/data-fetcher.js'
|
|
15
15
|
import { shouldSplitContent } from './site/split-content.js'
|
|
16
|
+
import { FONT_LINKS_MARKER } from './site/head-markers.js'
|
|
16
17
|
import { getAdapter } from './hosts/index.js'
|
|
17
18
|
import { detectCiContext } from './hosts/detect-ci-context.js'
|
|
18
19
|
|
|
@@ -376,9 +377,18 @@ export function scopeFetchedData(fetchedData, scopeRoutes) {
|
|
|
376
377
|
* @param {string|null} [options.currentRoute=null] - Route of the page this HTML is for
|
|
377
378
|
* @returns {string} HTML with build-specific data injected
|
|
378
379
|
*/
|
|
379
|
-
function injectBuildData(html, siteContent, { splitContent = false, currentRoute = null, scopeRoutes = null } = {}) {
|
|
380
|
+
export function injectBuildData(html, siteContent, { splitContent = false, currentRoute = null, scopeRoutes = null } = {}) {
|
|
380
381
|
let result = html
|
|
381
382
|
|
|
383
|
+
// Inject the theme's font <link> tags if not already present (the vite
|
|
384
|
+
// plugin normally puts them there when it builds index.html)
|
|
385
|
+
if (siteContent?.theme?.links && !result.includes(FONT_LINKS_MARKER)) {
|
|
386
|
+
result = result.replace(
|
|
387
|
+
'</head>',
|
|
388
|
+
` ${FONT_LINKS_MARKER}\n${siteContent.theme.links}\n </head>`
|
|
389
|
+
)
|
|
390
|
+
}
|
|
391
|
+
|
|
382
392
|
// Inject theme CSS if not already present
|
|
383
393
|
if (siteContent?.theme?.css && !result.includes('id="uniweb-theme"')) {
|
|
384
394
|
result = result.replace(
|
|
@@ -388,11 +398,12 @@ function injectBuildData(html, siteContent, { splitContent = false, currentRoute
|
|
|
388
398
|
}
|
|
389
399
|
|
|
390
400
|
// Inject site content as JSON for hydration
|
|
391
|
-
// Strip CSS from theme (
|
|
401
|
+
// Strip CSS and font links from theme (both are already in <head>)
|
|
392
402
|
let contentForJson = { ...siteContent }
|
|
393
|
-
if (contentForJson.theme?.css) {
|
|
403
|
+
if (contentForJson.theme?.css || contentForJson.theme?.links) {
|
|
394
404
|
contentForJson.theme = { ...contentForJson.theme }
|
|
395
405
|
delete contentForJson.theme.css
|
|
406
|
+
delete contentForJson.theme.links
|
|
396
407
|
}
|
|
397
408
|
|
|
398
409
|
// Split mode: strip sections from all pages except the current one.
|
|
@@ -258,6 +258,7 @@ export async function generatePdfThumbnail(pdfPath, outputPath, options = {}) {
|
|
|
258
258
|
*
|
|
259
259
|
* @param {Object} asset - Asset info
|
|
260
260
|
* @param {Object} options - Processing options
|
|
261
|
+
* @param {string} [options.basePath='/'] - Site base path for subdirectory deployments
|
|
261
262
|
* @returns {Promise<Object>} Processing result with poster/thumbnail info
|
|
262
263
|
*/
|
|
263
264
|
export async function processAdvancedAsset(asset, options = {}) {
|
|
@@ -266,9 +267,16 @@ export async function processAdvancedAsset(asset, options = {}) {
|
|
|
266
267
|
assetsSubdir = 'assets',
|
|
267
268
|
videoPosters = true,
|
|
268
269
|
pdfThumbnails = true,
|
|
269
|
-
quality = 80
|
|
270
|
+
quality = 80,
|
|
271
|
+
basePath = '/'
|
|
270
272
|
} = options
|
|
271
273
|
|
|
274
|
+
// Emitted URLs are site-root-absolute and must carry the deployment base,
|
|
275
|
+
// same as processAsset() — see the withBase() note in asset-processor.js.
|
|
276
|
+
const prefix = basePath && basePath !== '/'
|
|
277
|
+
? (basePath.endsWith('/') ? basePath.slice(0, -1) : basePath)
|
|
278
|
+
: ''
|
|
279
|
+
|
|
272
280
|
const { resolved } = asset
|
|
273
281
|
|
|
274
282
|
if (!existsSync(resolved)) {
|
|
@@ -293,7 +301,7 @@ export async function processAdvancedAsset(asset, options = {}) {
|
|
|
293
301
|
return {
|
|
294
302
|
processed: true,
|
|
295
303
|
type: 'video',
|
|
296
|
-
poster:
|
|
304
|
+
poster: `${prefix}/${assetsSubdir}/${posterFilename}`
|
|
297
305
|
}
|
|
298
306
|
} else if (result.skipped) {
|
|
299
307
|
// ffmpeg not available - not an error, just skip
|
|
@@ -314,7 +322,7 @@ export async function processAdvancedAsset(asset, options = {}) {
|
|
|
314
322
|
return {
|
|
315
323
|
processed: true,
|
|
316
324
|
type: 'pdf',
|
|
317
|
-
thumbnail:
|
|
325
|
+
thumbnail: `${prefix}/${assetsSubdir}/${thumbFilename}`,
|
|
318
326
|
pageCount: result.pageCount,
|
|
319
327
|
placeholder: result.placeholder
|
|
320
328
|
}
|
|
@@ -21,6 +21,24 @@ const CONVERTIBLE_FORMATS = ['.png', '.jpg', '.jpeg', '.gif']
|
|
|
21
21
|
// Image formats to pass through without conversion
|
|
22
22
|
const PASSTHROUGH_FORMATS = ['.svg', '.webp', '.avif', '.ico']
|
|
23
23
|
|
|
24
|
+
/**
|
|
25
|
+
* Prefix a root-relative URL with the site's base path.
|
|
26
|
+
*
|
|
27
|
+
* Emitted asset URLs are absolute-from-site-root (`/assets/hero-ab12cd34.webp`).
|
|
28
|
+
* Under a subdirectory deployment (GitHub Pages project sites, `/docs/`, ...)
|
|
29
|
+
* the served root moves, so the URL baked into site-content.json has to carry
|
|
30
|
+
* the base — components render `content.images[]` as a raw `<img src>` and have
|
|
31
|
+
* no chance to resolve it themselves. This mirrors what the collection
|
|
32
|
+
* processor already does for collection asset paths.
|
|
33
|
+
*/
|
|
34
|
+
function withBase(url, basePath) {
|
|
35
|
+
if (!url || !basePath || basePath === '/') return url
|
|
36
|
+
if (!url.startsWith('/') || url.startsWith('//')) return url
|
|
37
|
+
const prefix = basePath.endsWith('/') ? basePath.slice(0, -1) : basePath
|
|
38
|
+
if (url === prefix || url.startsWith(prefix + '/')) return url // already based
|
|
39
|
+
return prefix + url
|
|
40
|
+
}
|
|
41
|
+
|
|
24
42
|
/**
|
|
25
43
|
* Generate a content hash for a file
|
|
26
44
|
*/
|
|
@@ -56,7 +74,8 @@ export async function processAsset(asset, options = {}) {
|
|
|
56
74
|
outputDir,
|
|
57
75
|
assetsSubdir = 'assets',
|
|
58
76
|
convertToWebp: shouldConvert = true,
|
|
59
|
-
quality = 80
|
|
77
|
+
quality = 80,
|
|
78
|
+
basePath = '/'
|
|
60
79
|
} = options
|
|
61
80
|
|
|
62
81
|
const { original, resolved, isImage } = asset
|
|
@@ -66,7 +85,9 @@ export async function processAsset(asset, options = {}) {
|
|
|
66
85
|
console.warn(`[asset-processor] Source not found: ${resolved}`)
|
|
67
86
|
return {
|
|
68
87
|
original,
|
|
69
|
-
|
|
88
|
+
// Keep the authored path as fallback — still based, since an unprocessed
|
|
89
|
+
// public/ asset is served under the base too.
|
|
90
|
+
output: withBase(original, basePath),
|
|
70
91
|
processed: false,
|
|
71
92
|
error: 'Source not found'
|
|
72
93
|
}
|
|
@@ -107,8 +128,8 @@ export async function processAsset(asset, options = {}) {
|
|
|
107
128
|
// Write processed file
|
|
108
129
|
await writeFile(outputPath, outputBuffer)
|
|
109
130
|
|
|
110
|
-
// Return the URL path (
|
|
111
|
-
const outputUrl = `/${assetsSubdir}/${outputFilename}
|
|
131
|
+
// Return the URL path (site-root-absolute, carrying the deployment base)
|
|
132
|
+
const outputUrl = withBase(`/${assetsSubdir}/${outputFilename}`, basePath)
|
|
112
133
|
|
|
113
134
|
return {
|
|
114
135
|
original,
|
|
@@ -123,7 +144,7 @@ export async function processAsset(asset, options = {}) {
|
|
|
123
144
|
console.warn(`[asset-processor] Failed to process ${resolved}:`, error.message)
|
|
124
145
|
return {
|
|
125
146
|
original,
|
|
126
|
-
output: original,
|
|
147
|
+
output: withBase(original, basePath),
|
|
127
148
|
processed: false,
|
|
128
149
|
error: error.message
|
|
129
150
|
}
|
|
@@ -135,6 +156,7 @@ export async function processAsset(asset, options = {}) {
|
|
|
135
156
|
*
|
|
136
157
|
* @param {Object} assetManifest - Asset manifest from content collector
|
|
137
158
|
* @param {Object} options - Processing options
|
|
159
|
+
* @param {string} [options.basePath='/'] - Site base path for subdirectory deployments
|
|
138
160
|
* @returns {Promise<Object>} Mapping of original paths to output URLs
|
|
139
161
|
*/
|
|
140
162
|
export async function processAssets(assetManifest, options = {}) {
|
|
@@ -97,7 +97,7 @@ export async function buildSiteData({
|
|
|
97
97
|
// No vite needed — collectSiteContent is a plain async function.
|
|
98
98
|
// dropUnpublished: link mode is always a published deploy — prune hidden
|
|
99
99
|
// pages + their subtree so drafts never reach the served site.
|
|
100
|
-
let siteContent = await collectSiteContent(resolvedSiteRoot, { foundationPath, dropUnpublished: true })
|
|
100
|
+
let siteContent = await collectSiteContent(resolvedSiteRoot, { foundationPath, dropUnpublished: true, base: basePath })
|
|
101
101
|
|
|
102
102
|
// 2. Compile content collections (file-based markdown/yaml/json).
|
|
103
103
|
// `writeCollectionFiles` lands them under `<siteRoot>/public/data/`;
|
|
@@ -138,6 +138,7 @@ export async function buildSiteData({
|
|
|
138
138
|
assetsSubdir: assetsOpts.outputDir,
|
|
139
139
|
convertToWebp: assetsOpts.convertToWebp,
|
|
140
140
|
quality: assetsOpts.quality,
|
|
141
|
+
basePath,
|
|
141
142
|
})
|
|
142
143
|
|
|
143
144
|
const advancedEnabled = assetsOpts.videoPosters || assetsOpts.pdfThumbnails
|
|
@@ -150,6 +151,7 @@ export async function buildSiteData({
|
|
|
150
151
|
videoPosters: assetsOpts.videoPosters,
|
|
151
152
|
pdfThumbnails: assetsOpts.pdfThumbnails,
|
|
152
153
|
quality: assetsOpts.quality,
|
|
154
|
+
basePath,
|
|
153
155
|
hasExplicitPoster: siteContent.hasExplicitPoster || new Set(),
|
|
154
156
|
hasExplicitPreview: siteContent.hasExplicitPreview || new Set(),
|
|
155
157
|
}
|
|
@@ -2013,11 +2013,26 @@ async function collectLayouts(layoutDir, siteRoot, layoutNames = new Set()) {
|
|
|
2013
2013
|
* @returns {Promise<Object>} Site content object with assets manifest
|
|
2014
2014
|
*/
|
|
2015
2015
|
export async function collectSiteContent(sitePath, options = {}) {
|
|
2016
|
-
const { foundationPath, configFile = 'site.yml', profile: profileName, dropUnpublished = false } = options
|
|
2016
|
+
const { foundationPath, configFile = 'site.yml', profile: profileName, dropUnpublished = false, base = '/' } = options
|
|
2017
2017
|
|
|
2018
2018
|
// Read site config and raw theme config
|
|
2019
2019
|
const siteConfig = await readYamlFile(join(sitePath, configFile))
|
|
2020
2020
|
|
|
2021
|
+
// Record the RESOLVED base (--base > UNIWEB_BASE > site.yml::base) on the
|
|
2022
|
+
// config so every consumer reads one value. Prerender sets website.basePath
|
|
2023
|
+
// from `config.base` alone — it has no access to Vite's BASE_URL — so a base
|
|
2024
|
+
// that arrived via UNIWEB_BASE (what the generated GitHub Pages workflow
|
|
2025
|
+
// uses) was invisible to it, and prerendered <a href>s came out with no
|
|
2026
|
+
// base prefix while the hydrated browser routes were fine.
|
|
2027
|
+
//
|
|
2028
|
+
// Only a real base is written. At '/' the field stays absent, because in
|
|
2029
|
+
// shell mode `config.base` is the SERVING layer's channel (it injects the
|
|
2030
|
+
// served subpath, e.g. /gateway/site/<uuid>/) and a build-time '/' would
|
|
2031
|
+
// be a meaningless value sitting in its slot.
|
|
2032
|
+
if (base && base !== '/') {
|
|
2033
|
+
siteConfig.base = base
|
|
2034
|
+
}
|
|
2035
|
+
|
|
2021
2036
|
// Profile selects workspace-root defaults: site.yml → pages/ + page mode +
|
|
2022
2037
|
// pages: ordering; document.yml → content/ + folder mode + content: ordering.
|
|
2023
2038
|
const profile = (profileName && PROFILE_ALIASES[profileName]) || getContentProfile(configFile)
|
|
@@ -2047,7 +2062,10 @@ export async function collectSiteContent(sitePath, options = {}) {
|
|
|
2047
2062
|
|
|
2048
2063
|
// Load foundation info (vars + layout names) and process theme
|
|
2049
2064
|
const { vars: foundationVars, layoutNames: layoutNames } = await loadFoundationInfo(foundationPath)
|
|
2050
|
-
|
|
2065
|
+
// `base` reaches the theme because self-hosted font faces are authored
|
|
2066
|
+
// root-relative (`/fonts/x.woff2`) and the emitted @font-face lives in an
|
|
2067
|
+
// inline <style> — under a subdirectory deployment it must carry the base.
|
|
2068
|
+
const { config: processedTheme, css: themeCSS, links: themeLinks, warnings } = buildTheme(rawThemeConfig, { foundationVars, base })
|
|
2051
2069
|
|
|
2052
2070
|
// Log theme warnings
|
|
2053
2071
|
if (warnings?.length > 0) {
|
|
@@ -2060,7 +2078,8 @@ export async function collectSiteContent(sitePath, options = {}) {
|
|
|
2060
2078
|
config: siteConfig,
|
|
2061
2079
|
theme: {
|
|
2062
2080
|
...processedTheme,
|
|
2063
|
-
css: themeCSS
|
|
2081
|
+
css: themeCSS,
|
|
2082
|
+
links: themeLinks
|
|
2064
2083
|
},
|
|
2065
2084
|
pages: [],
|
|
2066
2085
|
assets: {}
|
|
@@ -2208,7 +2227,11 @@ export async function collectSiteContent(sitePath, options = {}) {
|
|
|
2208
2227
|
},
|
|
2209
2228
|
theme: {
|
|
2210
2229
|
...processedTheme,
|
|
2211
|
-
css: themeCSS
|
|
2230
|
+
css: themeCSS,
|
|
2231
|
+
// Font <link> tags (Google Fonts stylesheet + preconnects, preload hints
|
|
2232
|
+
// for self-hosted faces). The theme CSS stopped carrying an @import for
|
|
2233
|
+
// these, so they only reach the page if a consumer injects `links`.
|
|
2234
|
+
links: themeLinks
|
|
2212
2235
|
},
|
|
2213
2236
|
// Reachability axis: on the published build paths, drop `hidden` pages and
|
|
2214
2237
|
// their whole subtree (cascade). Dev keeps them so drafts stay previewable.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stable markers for head content the build injects.
|
|
3
|
+
*
|
|
4
|
+
* Two stages can write the same block: the vite plugin's `transformIndexHtml`
|
|
5
|
+
* (which produces `dist/index.html`) and the prerenderer (which post-processes
|
|
6
|
+
* that HTML per page). The marker lets the second stage tell "already injected"
|
|
7
|
+
* from "never injected" instead of guessing, so a page rendered through both
|
|
8
|
+
* paths gets exactly one copy.
|
|
9
|
+
*
|
|
10
|
+
* The theme CSS uses `id="uniweb-theme"` on its <style> for the same purpose;
|
|
11
|
+
* <link> tags have no natural id to hang that on, hence the comment marker.
|
|
12
|
+
*
|
|
13
|
+
* @module @uniweb/build/site
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
export const FONT_LINKS_MARKER = '<!--uniweb-fonts-->'
|
package/src/site/plugin.js
CHANGED
|
@@ -39,6 +39,7 @@ import { processAdvancedAssets } from './advanced-processors.js'
|
|
|
39
39
|
import { processCollections, writeCollectionFiles } from './collection-processor.js'
|
|
40
40
|
import { executeFetch, mergeDataIntoContent } from './data-fetcher.js'
|
|
41
41
|
import { shouldSplitContent } from './split-content.js'
|
|
42
|
+
import { FONT_LINKS_MARKER } from './head-markers.js'
|
|
42
43
|
|
|
43
44
|
// BCP 47 locale code pattern: en, zh-CN, zh-Hant, pt-BR, fr-CA, sr-Latn, etc.
|
|
44
45
|
const LOCALE_RE = '[a-z]{2,3}(?:-[A-Za-z]{2,4})?'
|
|
@@ -621,7 +622,7 @@ export function siteContentPlugin(options = {}) {
|
|
|
621
622
|
try {
|
|
622
623
|
// dropUnpublished only on a production build — in dev (serve) hidden
|
|
623
624
|
// pages stay in the graph so in-progress drafts remain previewable.
|
|
624
|
-
siteContent = await collectSiteContent(resolvedSitePath, { foundationPath, dropUnpublished: isProduction })
|
|
625
|
+
siteContent = await collectSiteContent(resolvedSitePath, { foundationPath, dropUnpublished: isProduction, base: basePath })
|
|
625
626
|
headHtml = await loadHeadHtml()
|
|
626
627
|
console.log(`[site-content] Collected ${siteContent.pages?.length || 0} pages`)
|
|
627
628
|
|
|
@@ -669,7 +670,7 @@ export function siteContentPlugin(options = {}) {
|
|
|
669
670
|
rebuildTimeout = setTimeout(async () => {
|
|
670
671
|
console.log('[site-content] Content changed, rebuilding...')
|
|
671
672
|
try {
|
|
672
|
-
siteContent = await collectSiteContent(resolvedSitePath, { foundationPath })
|
|
673
|
+
siteContent = await collectSiteContent(resolvedSitePath, { foundationPath, base: basePath })
|
|
673
674
|
headHtml = await loadHeadHtml()
|
|
674
675
|
// Execute fetches for the updated content
|
|
675
676
|
await executeDevFetches(siteContent, resolvedSitePath)
|
|
@@ -989,22 +990,19 @@ export function siteContentPlugin(options = {}) {
|
|
|
989
990
|
headInjection += headHtml + '\n'
|
|
990
991
|
}
|
|
991
992
|
|
|
992
|
-
// Inject font
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
if (origins.has('https://fonts.googleapis.com')) {
|
|
1006
|
-
headInjection += ` <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>\n`
|
|
1007
|
-
}
|
|
993
|
+
// Inject the theme's font <link> tags (before theme CSS so the browser
|
|
994
|
+
// starts DNS/fetch early). This is every font link in one place:
|
|
995
|
+
// preconnects, the merged Google Fonts stylesheet, and preload hints for
|
|
996
|
+
// self-hosted faces.
|
|
997
|
+
//
|
|
998
|
+
// These used to be dropped. The theme CSS once carried `@import
|
|
999
|
+
// url(<google>)` and this spot only had to add a preconnect in front of
|
|
1000
|
+
// it; when @uniweb/theming replaced that @import with a `links` string,
|
|
1001
|
+
// nothing here started consuming it — so the build preconnected to
|
|
1002
|
+
// Google Fonts and then never requested the stylesheet, leaving every
|
|
1003
|
+
// imported family undefined and silently falling back.
|
|
1004
|
+
if (contentToInject.theme?.links) {
|
|
1005
|
+
headInjection += ` ${FONT_LINKS_MARKER}\n${contentToInject.theme.links}\n`
|
|
1008
1006
|
}
|
|
1009
1007
|
|
|
1010
1008
|
// Inject theme CSS
|
|
@@ -1063,7 +1061,8 @@ export function siteContentPlugin(options = {}) {
|
|
|
1063
1061
|
outputDir: resolvedOutDir,
|
|
1064
1062
|
assetsSubdir: assetsOptions.outputDir,
|
|
1065
1063
|
convertToWebp: assetsOptions.convertToWebp,
|
|
1066
|
-
quality: assetsOptions.quality
|
|
1064
|
+
quality: assetsOptions.quality,
|
|
1065
|
+
basePath
|
|
1067
1066
|
})
|
|
1068
1067
|
|
|
1069
1068
|
// Process advanced assets (videos, PDFs)
|
|
@@ -1079,6 +1078,7 @@ export function siteContentPlugin(options = {}) {
|
|
|
1079
1078
|
videoPosters: assetsOptions.videoPosters,
|
|
1080
1079
|
pdfThumbnails: assetsOptions.pdfThumbnails,
|
|
1081
1080
|
quality: assetsOptions.quality,
|
|
1081
|
+
basePath,
|
|
1082
1082
|
// Pass explicit poster/preview sets to skip auto-generation
|
|
1083
1083
|
hasExplicitPoster: siteContent.hasExplicitPoster || new Set(),
|
|
1084
1084
|
hasExplicitPreview: siteContent.hasExplicitPreview || new Set()
|