polen
Version:
A framework for delightful GraphQL developer portals
177 lines (165 loc) • 5.94 kB
text/typescript
import type { Config } from '#api/config/index'
import type { PolenBuildManifest } from '#api/static/manifest'
import { Vite } from '#dep/vite/index'
import { ViteVirtual } from '#lib/vite-virtual/index'
import { debugPolen } from '#singletons/debug'
import { Fs, Path } from '@wollybeard/kit'
import packageJson from '../../../../package.json' with { type: 'json' }
import { isKitUnusedExternalImport, isRadixModuleLevelDirective } from '../log-filters.js'
import { polenVirtual } from '../vi.js'
export const Build = (config: Config.Config): Vite.Plugin[] => {
const debug = debugPolen.sub(`vite-build`)
debug(`construct`)
// let viteConfigResolved: Vite.ResolvedConfig
// const outDir = Path.join(config.paths.project.rootDir, `dist`)
return [
Manifest(config),
BuildManifest(config),
{
name: `polen:build-client`,
apply: `build`,
applyToEnvironment: Vite.isEnvironmentClient,
// HACK: For some reason the ?url import doesn't lead to a rewrite in the build.
// Furthermore we need to rely on the manifest to get its final name because it is
// generated by the client build before the server build.
// However, we still need the asset in development.
// But we cannot exclude the import in build.
// So this does that for us but it is really hacky.
// FIXME
// 1. Raise issue about having ?url lead to expected build path rewrite?
// 2. And: Move asset generation to server build?
// 3. And/or: Use Vite Environments API?
generateBundle(_, bundle, isWrite) {
if (isWrite) {
for (const chunkOrAsset of Object.values(bundle)) {
if (chunkOrAsset.type === `asset` && chunkOrAsset.names.includes(`entry.client.jsx`)) {
delete bundle[chunkOrAsset.fileName]
}
}
}
},
onLog(_, message) {
if (isRadixModuleLevelDirective(message)) return
if (isKitUnusedExternalImport(message)) return
},
config() {
return {
environments: {
client: {
build: {
manifest: true,
rollupOptions: {
input: [config.paths.framework.template.absolute.client.entrypoint],
external: id => id.startsWith(`node:`),
onwarn(message) {
if (isKitUnusedExternalImport(message)) return
},
},
},
},
},
}
},
},
{
name: `polen-ssr-build`,
apply: `build`,
applyToEnvironment: Vite.isEnvironmentSsr,
config() {
return {
// Have to configure this here??
// @see https://github.com/vitejs/vite/issues/20098
ssr: {
noExternal: true,
},
environments: {
ssr: {
build: {
// NO EFFECT (see above)
// Bundle all dependencies instead of externalizing them
// noExternal: true,
// The SSR build will follow the client build, and emptying the dir would lose the output of the client build.
emptyOutDir: false,
rollupOptions: {
input: [config.paths.framework.template.absolute.server.entrypoint],
output: {
entryFileNames: config.paths.project.relative.build.relative.serverEntrypoint,
},
},
},
},
},
}
},
onLog(_, message) {
if (isKitUnusedExternalImport(message)) return
},
// generateBundle(_, bundle, isWrite) {
// if (isWrite) {
// for (const chunkOrAsset of Object.values(bundle)) {
// console.log(chunkOrAsset)
// if (chunkOrAsset.type === `chunk`) {
// if (chunkOrAsset.facadeModuleId === viClientManifest.resolved) {
// delete bundle[chunkOrAsset.fileName]
// }
// }
// }
// }
// },
async closeBundle() {
/**
* clean up the manifest. Was generated by client. For server build. Not needed after (unless debugging).
*/
if (!config.advanced.debug) {
await Fs.remove(Path.join(config.paths.project.absolute.build.root, `.vite`))
}
},
},
]
}
const viClientManifest = polenVirtual([`vite`, `client`, `manifest`])
const Manifest = (config: Config.Config): Vite.Plugin => {
let configEnv: Vite.ConfigEnv
return {
name: `polen-manifest`,
config(_, configEnv_) {
configEnv = configEnv_
},
...ViteVirtual.IdentifiedLoader.toHooks(
{
identifier: viClientManifest,
loader: async () => {
// In development just return an empty manifest
if (configEnv.mode === Vite.ModeName.development) {
return `export default {}`
}
const manifestPath = Path.join(config.paths.project.absolute.build.root, `.vite`, `manifest.json`)
const module = await import(manifestPath, { with: { type: `json` } }) as {
default: Vite.Manifest
}
return `export default ${JSON.stringify(module.default)}`
},
},
),
}
}
const BuildManifest = (config: Config.Config): Vite.Plugin => {
return {
name: `polen:build-manifest`,
apply: `build`,
applyToEnvironment: Vite.isEnvironmentClient,
async generateBundle() {
const manifest: PolenBuildManifest = {
type: config.build.architecture === `ssr` ? `ssr` : `ssg`,
version: packageJson.version,
basePath: config.build.base ?? `/`,
}
// Emit the manifest as an asset
this.emitFile({
type: `asset`,
fileName: `.polen/build.json`,
source: JSON.stringify(manifest, null, 2),
})
},
}
}