Skip to content

Instantly share code, notes, and snippets.

@JamieCurnow
Last active July 29, 2023 20:58
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save JamieCurnow/374e69a2c3dd3b4952f967aa00a02938 to your computer and use it in GitHub Desktop.
Save JamieCurnow/374e69a2c3dd3b4952f967aa00a02938 to your computer and use it in GitHub Desktop.
Using firebase web framework with Nuxt 3
// preset/entry.ts
import '#internal/nitro/virtual/polyfill'
import { toNodeListener } from 'h3'
import { trapUnhandledNodeErrors } from './utils'
const nitroApp = useNitroApp()
export const listener = toNodeListener(nitroApp.h3App)
/** @deprecated use new `listener` export instead */
export const handler = listener
// Trap unhandled errors
trapUnhandledNodeErrors()
{
"hosting": {
"source": ".",
"ignore": ["firebase.json", "**/.*", "**/node_modules/**"],
"frameworksBackend": {
"region": "europe-west1",
"minInstances": 0,
"maxInstances": 4,
"concurrency": 80
}
}
}
// preset/index.ts
import { fileURLToPath } from 'node:url'
import { createRequire } from 'node:module'
import type { NitroPreset } from 'nitropack'
import { join, resolve } from 'pathe'
import { globby } from 'globby'
import { readPackageJSON } from 'pkg-types'
import { writeFile } from './utils'
export default <NitroPreset>{
extends: 'node',
entry: fileURLToPath(new URL('./entry.ts', import.meta.url)),
// serveStatic: true,
hooks: {
async compiled(nitro) {
const _require = createRequire(import.meta.url)
const jsons = await globby(join(nitro.options.output.serverDir, 'node_modules/**/package.json'))
const prefixLength = `${nitro.options.output.serverDir}/node_modules/`.length
const suffixLength = '/package.json'.length
const dependencies = jsons.reduce((obj, packageJson) => {
const dirname = packageJson.slice(prefixLength, -suffixLength)
if (!dirname.includes('node_modules')) {
obj[dirname] = _require(packageJson).version
}
return obj
}, {} as Record<string, string>)
const getPackageVersion = async (id: string) => {
const pkg = await readPackageJSON(id, {
url: nitro.options.nodeModulesDirs
})
return pkg.version
}
await writeFile(
resolve(nitro.options.output.serverDir, 'package.json'),
JSON.stringify(
{
name: 'nitro-output',
version: '0.0.0',
private: true,
dependencies: {
'firebase-functions-test': 'latest',
'firebase-admin': await getPackageVersion('firebase-admin'),
'firebase-functions': await getPackageVersion('firebase-functions'),
...dependencies
}
},
null,
2
)
)
}
}
}
export default defineNuxtConfig({
ssr: true,
nitro: {
preset: resolve(__dirname, 'preset/index.ts')
}
})
// preset/utils.ts
import fsp from 'node:fs/promises'
import { dirname } from 'pathe'
export function trapUnhandledNodeErrors() {
if (process.env.DEBUG) {
process.on('unhandledRejection', (err) => console.error('[nitro] [unhandledRejection]', err))
process.on('uncaughtException', (err) => console.error('[nitro] [uncaughtException]', err))
} else {
process.on('unhandledRejection', (err) => console.error('[nitro] [unhandledRejection] ' + err))
process.on('uncaughtException', (err) => console.error('[nitro] [uncaughtException] ' + err))
}
}
export async function writeFile(file: string, contents: Buffer | string) {
await fsp.mkdir(dirname(file), { recursive: true })
await fsp.writeFile(file, contents, typeof contents === 'string' ? 'utf8' : undefined)
}
@JamieCurnow
Copy link
Author

The index, entry and utils files should all be in the /preset sub dir from the nuxt.config.ts.

I went with the approach of copying the nitro preset file for firebase and using the nitro entry file for plain node.

With this, you should be able to deploy the app to firebase via the webFrameworks

@luc122c
Copy link

luc122c commented Jul 29, 2023

@JamieCurnow Thanks for this! I'm going to try it out. Probably worth adding import { resolve } from "pathe" to the top of nuxt.config.ts :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment