120 lines
3.1 KiB
TypeScript
120 lines
3.1 KiB
TypeScript
|
import fs from 'fs/promises'
|
||
|
|
||
|
import serveStatic from 'serve-static'
|
||
|
import { Plugin } from 'vite'
|
||
|
|
||
|
type MultipleSPAOptions = {
|
||
|
defaultSite: string
|
||
|
templatePath: string
|
||
|
sites: Record<string, Record<string, string>>
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* A custom plugin to create multiple virtual html files from a template. Will
|
||
|
* generate distinct entry points and single-page application outputs.
|
||
|
*/
|
||
|
export function multipleSPA(options: MultipleSPAOptions): Plugin {
|
||
|
const fillTemplate = async (siteName: string) => {
|
||
|
const siteData = options.sites[siteName]
|
||
|
const template = await fs.readFile(options.templatePath, 'utf-8')
|
||
|
const filledTemplate = template
|
||
|
.toString()
|
||
|
.replace(/\{\{(.+)\}\}/g, (_match, p1) => siteData[(p1 as string).trim()])
|
||
|
|
||
|
return filledTemplate
|
||
|
}
|
||
|
|
||
|
return {
|
||
|
name: 'multiple-spa',
|
||
|
enforce: 'pre',
|
||
|
|
||
|
configureServer(vite) {
|
||
|
// TODO: this middleware is specific to the "mon-entreprise" app and
|
||
|
// shouldn't be in the "multipleSPA" plugin. It allows to serve the
|
||
|
// iframe integration script (already built) from the same server as the app.
|
||
|
vite.middlewares.use(
|
||
|
'/simulateur-iframe-integration.js',
|
||
|
serveStatic(new URL('./dist', import.meta.url).pathname, {
|
||
|
index: 'simulateur-iframe-integration.js',
|
||
|
})
|
||
|
)
|
||
|
|
||
|
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
||
|
vite.middlewares.use(async (req, res, next) => {
|
||
|
const url = req.originalUrl?.replace(/^\/%2F/, '/')
|
||
|
|
||
|
const firstLevelDir = url?.slice(1).split('/')[0]
|
||
|
|
||
|
if (url && /\?.*html-proxy/.test(url)) {
|
||
|
return next()
|
||
|
}
|
||
|
|
||
|
if (url && ['/', '/index.html'].includes(url)) {
|
||
|
res.writeHead(302, { Location: '/' + options.defaultSite }).end()
|
||
|
}
|
||
|
// this condition is for the start:netlify script to match /mon-entreprise or /infrance
|
||
|
else if (
|
||
|
firstLevelDir &&
|
||
|
url &&
|
||
|
Object.keys(options.sites)
|
||
|
.map((site) => `/${site}.html`)
|
||
|
.includes(url)
|
||
|
) {
|
||
|
const siteName = firstLevelDir.replace('.html', '')
|
||
|
const content = await vite.transformIndexHtml(
|
||
|
'/' + siteName,
|
||
|
await fillTemplate(siteName)
|
||
|
)
|
||
|
res.end(content)
|
||
|
} else if (
|
||
|
firstLevelDir &&
|
||
|
Object.keys(options.sites).some((name) => firstLevelDir === name)
|
||
|
) {
|
||
|
const siteName = firstLevelDir
|
||
|
const content = await vite.transformIndexHtml(
|
||
|
url,
|
||
|
await fillTemplate(siteName)
|
||
|
)
|
||
|
res.end(content)
|
||
|
} else {
|
||
|
next()
|
||
|
}
|
||
|
})
|
||
|
},
|
||
|
|
||
|
config(config, { command }) {
|
||
|
if (command === 'build' && !config.build?.ssr) {
|
||
|
config.build = {
|
||
|
...config.build,
|
||
|
rollupOptions: {
|
||
|
...config.build?.rollupOptions,
|
||
|
input: Object.fromEntries(
|
||
|
Object.keys(options.sites).map((name) => [
|
||
|
name,
|
||
|
`virtual:${name}.html`,
|
||
|
])
|
||
|
),
|
||
|
},
|
||
|
}
|
||
|
}
|
||
|
},
|
||
|
|
||
|
resolveId(id) {
|
||
|
const pathname = id.split('/').slice(-1)[0]
|
||
|
if (pathname?.startsWith('virtual:')) {
|
||
|
return pathname.replace('virtual:', '')
|
||
|
}
|
||
|
|
||
|
return null
|
||
|
},
|
||
|
|
||
|
async load(id) {
|
||
|
if (
|
||
|
Object.keys(options.sites).some((name) => id.endsWith(name + '.html'))
|
||
|
) {
|
||
|
return await fillTemplate(id.replace(/\.html$/, ''))
|
||
|
}
|
||
|
},
|
||
|
}
|
||
|
}
|