|
1 | 1 | #! /usr/bin/env node
|
2 | 2 | import { spawnSync } from "child_process";
|
| 3 | +import { join } from "path"; |
| 4 | +import fsExtra from "fs-extra"; |
| 5 | +import { stringify as yamlStringify } from "yaml"; |
| 6 | + |
| 7 | +const { copy, mkdirp, exists, readJson, writeFile } = fsExtra; |
| 8 | + |
| 9 | +import type { RoutesManifest } from "../interfaces.js"; |
3 | 10 |
|
4 | 11 | const cwd = process.cwd();
|
5 | 12 |
|
6 |
| -spawnSync("next", ["build"], { cwd, stdio: "inherit" }); |
| 13 | +// TODO don't await these here, clean up these imports |
| 14 | +// for the constants we should probably keep them the same way we do in firebase-tools |
| 15 | +const { default: nextServerConfig }: { default: typeof import("next/dist/server/config.js") } = await import(`${cwd}/node_modules/next/dist/server/config.js`); |
| 16 | +const { PHASE_PRODUCTION_BUILD, ROUTES_MANIFEST }: typeof import("next/constants.js") = await import(`${cwd}/node_modules/next/constants.js`); |
| 17 | + |
| 18 | +const loadConfig = nextServerConfig.default; |
| 19 | + |
| 20 | +// Since this tool can be executed with NPX maybe we should just call `npm run build`? |
| 21 | +spawnSync("next", ["build"], { cwd, stdio: "inherit", env: { ...process.env, NEXT_PRIVATE_STANDALONE: 'true' } }); |
| 22 | + |
| 23 | +const { distDir, basePath } = await loadConfig(PHASE_PRODUCTION_BUILD, cwd); |
| 24 | + |
| 25 | +const destination = join(cwd, '.apphosting_build'); |
| 26 | + |
| 27 | +const staticDestination = join(destination, "static", basePath); |
| 28 | +const nextStaticDestination = join(staticDestination, "_next", "static"); |
| 29 | +const publicDestination = join(destination, "public"); |
| 30 | + |
| 31 | +const standaloneDir = join(cwd, distDir, "standalone"); |
| 32 | +const publicDir = join(cwd, "public"); |
| 33 | +const nextStaticDir = join(cwd, distDir, "static"); |
| 34 | + |
| 35 | +await mkdirp(nextStaticDestination); |
| 36 | + |
| 37 | +const copyPublicDir = async () => { |
| 38 | + const publicDirExists = await exists(publicDir); |
| 39 | + if (!publicDirExists) return; |
| 40 | + await Promise.all([ |
| 41 | + copy(publicDir, publicDestination), |
| 42 | + copy(publicDir, staticDestination) |
| 43 | + ]); |
| 44 | +}; |
| 45 | + |
| 46 | +const writeBundleYaml = async () => { |
| 47 | + const manifest: RoutesManifest = await readJson(join(cwd, distDir, ROUTES_MANIFEST)); |
| 48 | + const headers = manifest.headers.map(it => ({ ...it, regex: undefined })); |
| 49 | + const redirects = manifest.redirects.filter(it => !it.internal).map(it => ({ ...it, regex: undefined })); |
| 50 | + const beforeFileRewrites = Array.isArray(manifest.rewrites) ? manifest.rewrites : manifest.rewrites?.beforeFiles || []; |
| 51 | + const rewrites = beforeFileRewrites.map(it => ({ ...it, regex: undefined })); |
| 52 | + await writeFile(join(destination, "bundle.yaml"), yamlStringify({ headers, redirects, rewrites })); |
| 53 | +} |
| 54 | + |
| 55 | +await Promise.all([ |
| 56 | + copy(standaloneDir, destination), |
| 57 | + copy(nextStaticDir, nextStaticDestination), |
| 58 | + copyPublicDir(), |
| 59 | + writeBundleYaml(), |
| 60 | +]); |
0 commit comments