UNPKG

@wroud/vite-plugin-ssg

Version:

A Vite plugin for static site generation (SSG) with React. Renders React applications to static HTML for faster load times and improved SEO.

147 lines 6.47 kB
import { readFile } from "node:fs/promises"; import { isMainId, removeMainQuery } from "../modules/mainQuery.js"; import { cleanUrl } from "../utils/cleanUrl.js"; import { cleanSsgAssetId, createSsgAssetId, isSsgAssetId, } from "../modules/isSsgAssetId.js"; import { isUrlId, removeUrlQuery } from "../modules/removeUrlQuery.js"; import { isSsgPageUrlId, removeSsgPageUrlId, } from "../modules/isSsgPageUrlId.js"; import { changePathExt } from "../utils/changePathExt.js"; import { createSsgClientEntryId } from "../modules/isSsgClientEntryId.js"; import { mapBaseToUrl } from "../utils/mapBaseToUrl.js"; import { splitFileAndPostfix } from "../utils/splitFileAndPostfix.js"; import { isGitLfsPlaceholder } from "../utils/isGitLfsPlaceholder.js"; import nodePath from "node:path"; // Vite's regex patterns for inline detection const noInlineRE = /[?&]no-inline\b/; const inlineRE = /[?&]inline\b/; const DEFAULT_ASSETS_INLINE_LIMIT = 4096; export function ssgAssetsResolutionPlugin() { async function shouldInline(id, content, environment) { // Match Vite's shouldInline logic exactly if (noInlineRE.test(id)) return false; if (inlineRE.test(id)) return true; // Check for Git LFS placeholder - don't inline LFS files if (isGitLfsPlaceholder(content)) { return false; } // Build-specific checks if (environment.config.command === "build") { if (environment.config.build.lib) return true; // Note: We can't easily check isEntry here without the module info } // Don't inline HTML files if (id.endsWith(".html")) return false; // Don't inline SVG with fragments (hash in URL) const [file] = splitFileAndPostfix(id); if (file.endsWith(".svg") && id.includes("#")) return false; // Check against assetsInlineLimit let limit; const { assetsInlineLimit } = environment.config.build; if (typeof assetsInlineLimit === "function") { const cleanedId = cleanUrl(id); const userShouldInline = assetsInlineLimit(cleanedId, content); if (userShouldInline != null) return userShouldInline; limit = DEFAULT_ASSETS_INLINE_LIMIT; } else { limit = Number(assetsInlineLimit) || DEFAULT_ASSETS_INLINE_LIMIT; } // Check content length vs limit return content.length < limit; } return { name: "@wroud/vite-plugin-ssg:assets-resolution", applyToEnvironment: (env) => env.name === "ssr", resolveId: { order: "pre", async handler(source, importer, options) { const config = this.environment.config; if (options.custom?.["ssg-assets-resolution"]) { return null; } const isAsset = config?.assetsInclude(cleanUrl(source)); if (isMainId(source) || isUrlId(source) || isSsgPageUrlId(source) || isAsset) { const resolved = await this.resolve(source, importer, { ...options, custom: { ...options.custom, "ssg-assets-resolution": true, }, }); if (!resolved) { return undefined; } if (isSsgAssetId(resolved.id)) { return resolved; } if (isAsset) { // Check if this asset will be inlined by Vite try { const cleanedId = cleanUrl(resolved.id); const content = await readFile(cleanedId); const willBeInlined = await shouldInline(resolved.id, content, this.environment); // If the asset will be inlined, don't wrap it with SSG asset ID // Let Vite handle it naturally if (willBeInlined) { return resolved; } } catch (error) { // If we can't read the file, assume it won't be inlined // This is safer than crashing } } return createSsgAssetId(resolved.id); } return null; }, }, load: { order: "pre", async handler(id) { const config = this.environment.config; if (isSsgAssetId(id)) { if (isSsgPageUrlId(id)) { id = changePathExt(id, ""); if (config.command === "serve") { id = removeSsgPageUrlId(cleanSsgAssetId(id)); id = changePathExt(nodePath.posix.relative(config.root, id), ".html"); return { code: `export default ${JSON.stringify(id)};`, moduleType: "js", }; } } if (isMainId(id)) { id = createSsgClientEntryId(removeMainQuery(id)); } id = removeUrlQuery(cleanSsgAssetId(id)); if (config.command === "serve") { if (id.startsWith(config.root)) { id = nodePath.posix.relative(config.root, id); this.debug(`Transformed to server URL: ${id}`); } else { id = mapBaseToUrl("/@fs" + id, config); this.debug(`Transformed to fs path: ${id}`); } } return { code: `export default ${JSON.stringify(id)};`, moduleType: "js", }; } return null; }, }, }; } //# sourceMappingURL=ssgAssetsResolution.js.map