- Can pass functions; while nitro's runtimeConfig cannot. This makes plugins possible
145 lines
3.8 KiB
JavaScript
145 lines
3.8 KiB
JavaScript
import { eventHandler } from 'h3'
|
|
import { useStorage } from 'nitropack/runtime'
|
|
import { createHooks } from 'hookable'
|
|
import { consola } from 'consola'
|
|
import { colorize } from 'consola/utils'
|
|
import { loadConfig } from 'c12'
|
|
|
|
import {
|
|
hooks as mcflyHooks,
|
|
defaultMcflyConfig,
|
|
evaluateServerScripts,
|
|
injectHtmlFragments,
|
|
injectCustomElements,
|
|
} from '@mcflyjs/core/runtime/index.js' // important to import from installed node_module because this script is passed to another context
|
|
|
|
/**
|
|
* @typedef {import('../config').McFlyConfig} Config
|
|
* @typedef {import('unstorage').Storage} Storage
|
|
* @typedef {import('unstorage').StorageValue} StorageValue
|
|
* @typedef {import('h3').EventHandler} EventHandler
|
|
*/
|
|
|
|
/**
|
|
* McFly middleware event handler
|
|
*/
|
|
export default eventHandler(async (event) => {
|
|
const timeStart = performance.now()
|
|
const hooks = createHooks()
|
|
Object.keys(mcflyHooks).forEach((hookName) => hooks.addHooks(hookName))
|
|
const { path } = event
|
|
let { config } = await loadConfig({ name: 'mcfly' })
|
|
const storage = useStorage()
|
|
|
|
// if not page, don't render
|
|
if (event.path.startsWith('/api')) {
|
|
return
|
|
}
|
|
|
|
if (!config || Object.keys(config).length === 0) {
|
|
config = defaultMcflyConfig
|
|
consola.warn(
|
|
`[WARN]: McFly configuration not found, using defaults...`,
|
|
defaultMcflyConfig
|
|
)
|
|
}
|
|
|
|
const plugins = config.plugins ?? []
|
|
|
|
plugins.forEach((plugin) => {
|
|
const pluginHooks = Object.keys(plugin)
|
|
pluginHooks.forEach((pluginHook) => {
|
|
hooks.hook(pluginHook, plugin[pluginHook])
|
|
})
|
|
})
|
|
|
|
const { components: componentType } = config
|
|
let html = await getHtml(path, storage)
|
|
|
|
if (html) {
|
|
const transforms = [
|
|
{
|
|
fn: evaluateServerScripts,
|
|
args: [''],
|
|
hook: mcflyHooks.serverScriptsEvaluated,
|
|
},
|
|
{
|
|
fn: injectHtmlFragments,
|
|
args: [storage],
|
|
hook: mcflyHooks.fragmentsInjected,
|
|
},
|
|
{
|
|
fn: injectCustomElements,
|
|
args: [componentType, storage],
|
|
hook: mcflyHooks.customElementsInjected,
|
|
},
|
|
]
|
|
|
|
if (!!componentType && !!html) {
|
|
for (const transform of transforms) {
|
|
html = await transform.fn(html.toString(), ...transform.args)
|
|
|
|
// call hook
|
|
if (transform.hook) {
|
|
// not sure if we want to await, for now it makes the outcome predictable
|
|
await hooks.callHook(transform.hook)
|
|
}
|
|
}
|
|
} else {
|
|
consola.error('[ERR]: Failed to insert registry', {
|
|
componentType: !componentType ? 'missing' : 'okay',
|
|
html: !html ? 'missing' : 'okay',
|
|
})
|
|
}
|
|
}
|
|
|
|
if (html) {
|
|
await hooks.callHook(mcflyHooks.pageRendered)
|
|
}
|
|
|
|
const timeEnd = performance.now()
|
|
consola.info(
|
|
colorize('green', event.path),
|
|
'rendered in',
|
|
Math.round(timeEnd - timeStart),
|
|
'ms'
|
|
)
|
|
return (
|
|
html ??
|
|
new Response(
|
|
'😱 ERROR 404: Not found. You can put a 404.html on the ./src/pages directory to customize this error page.',
|
|
{ status: 404 }
|
|
)
|
|
)
|
|
})
|
|
/**
|
|
* Gets the storage path for a file
|
|
* @param {string} filename
|
|
* @returns {string}
|
|
*/
|
|
function getPath(filename) {
|
|
return `assets:pages${filename}`
|
|
}
|
|
|
|
function getPurePath(path) {
|
|
return path.split('?')[0]
|
|
}
|
|
/**
|
|
* Gets the correct HTML depending on the path requested
|
|
* @param {string} path
|
|
* @param {Storage} storage
|
|
* @returns {Promise<StorageValue>}
|
|
*/
|
|
async function getHtml(path, storage) {
|
|
const purePath = getPurePath(path)
|
|
const rawPath =
|
|
purePath[purePath.length - 1] === '/' ? purePath.slice(0, -1) : purePath
|
|
const filename = rawPath === '' ? '/index.html' : `${rawPath}.html`
|
|
const fallback = getPath(rawPath + '/index.html')
|
|
const filePath = getPath(filename)
|
|
let html = await storage.getItem(filePath)
|
|
if (!html) html = await storage.getItem(fallback)
|
|
if (!html) html = await storage.getItem(getPath('/404.html'))
|
|
|
|
return html
|
|
}
|