Last active
April 20, 2022 18:57
-
-
Save jthegedus/8e820d37e1f3768f991886fb65de154f to your computer and use it in GitHub Desktop.
Next.js static asset hoisting for Firebase Hosting CDN
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var shell = require("shelljs"); | |
var nextjsConfig = require("../next.config"); | |
var distDir = nextjsConfig.distDir || ".next"; | |
var BUILD_ID = shell.cat(`${distDir}/BUILD_ID`); | |
function hoistPages(fileExt, outputPath) { | |
console.log( | |
`${distDir}/server/static/${BUILD_ID}/pages/**/*${fileExt} -> ${outputPath}/` | |
); | |
shell.mkdir("-p", outputPath); | |
var match = new RegExp("\\" + `${fileExt}`); | |
var filesToHoist = shell | |
.find(`${distDir}/server/static/${BUILD_ID}/pages/`) | |
.filter(function (file) { | |
// ensure the file has the required extension and is not a dynamic route (/blog/[pid]) | |
return file.match(match) && file.match(/^((?!\[|\]).)*$/); | |
}); | |
filesToHoist.forEach((filePath) => { | |
var outPath = filePath.split("pages/")[1]; | |
if (outPath.includes("/")) { | |
shell.mkdir( | |
"-p", | |
`${outputPath}/${outPath.substring(0, outPath.lastIndexOf("/"))}` | |
); | |
} | |
shell.cp("-f", filePath, `${outputPath}/${outPath}`); | |
}); | |
} | |
console.log( | |
"next export doesn't support getServerSideProps() so we perform our own copy of static assets to prepare our Firebase Hosting upload" | |
); | |
console.log( | |
"Hoist public/ Next.js runtime and optimised chunks, computed .html and .json data\n" | |
); | |
console.log("public/ -> out/"); | |
shell.mkdir("-p", "out/"); | |
shell.cp("-Rf", "public/*", "out/"); | |
console.log(`${distDir}/static/ -> out/_next/static/`); | |
shell.mkdir("-p", "out/_next/static/"); | |
shell.cp("-Rf", `${distDir}/static/`, "out/_next/"); | |
hoistPages(".html", "out"); | |
hoistPages(".json", `out/_next/data/${BUILD_ID}`); |
Makes sense! I'm going to look more into Cloud Run when I get a chance. Looking forward to your blog post.
The SSG
hack isn't really viable after testing it. Non-cached requests to a cold start app are still insanely slow, like 18 seconds:
Cold
± curl -w "@curl-format.txt" -I "https://dev.converge.is/"
HTTP/2 200
cache-control: s-maxage=31536000, stale-while-revalidate
content-type: text/html; charset=utf-8
etag: "c29f-ueawi3pUlnGm86tZic53Ydjm4r4"
function-execution-id: lw8l0koc8idb
server: Google Frontend
x-cloud-trace-context: e5840d6192499cffb974a6c1f5aa8505;o=1
x-country-code: US
x-powered-by: Next.js
accept-ranges: bytes
date: Mon, 10 May 2021 14:30:17 GMT
x-served-by: cache-sea4446-SEA
x-cache: MISS
x-cache-hits: 0
x-timer: S1620657000.692516,VS0,VE17493
vary: Accept-Encoding,cookie,need-authorization, x-fh-requested-host, accept-encodin
g
content-length: 49823
time_namelookup: 0.277409s
time_connect: 0.336877s
time_appconnect: 0.520884s
time_pretransfer: 0.521728s
time_redirect: 0.000000s
time_starttransfer: 18.091856s
----------
time_total: 18.092007s
Warm
± curl -w "@curl-format.txt" -I "https://dev.converge.is/"
HTTP/2 200
cache-control: s-maxage=31536000, stale-while-revalidate
content-type: text/html; charset=utf-8
etag: "c29f-ueawi3pUlnGm86tZic53Ydjm4r4"
function-execution-id: lw8l0koc8idb
server: Google Frontend
x-cloud-trace-context: e5840d6192499cffb974a6c1f5aa8505;o=1
x-country-code: US
x-powered-by: Next.js
accept-ranges: bytes
date: Mon, 10 May 2021 14:40:52 GMT
x-served-by: cache-sea4437-SEA
x-cache: HIT
x-cache-hits: 1
x-timer: S1620657653.725299,VS0,VE1
vary: Accept-Encoding,cookie,need-authorization, x-fh-requested-host, accept-encodin
g
content-length: 49823
time_namelookup: 0.002825s
time_connect: 0.077373s
time_appconnect: 0.237598s
time_pretransfer: 0.237705s
time_redirect: 0.000000s
time_starttransfer: 0.321439s
----------
time_total: 0.321567s
18s!!! :O I haven't seen a cold start over 2s!
Yeah, doesn't seem right. Guessing it's either because I'm not paying enough money yet or I have too many deps. (I'm in the process of removing redux everywhere but not there yet).
At this point I'm dropping SSR almost everywhere, running a next export
and trying to cover as many getStaticPaths
as I can, while relying on /api
routes continuing to be served by Cloud Functions.
Maybe some day I'll migrate to Cloud Run, just don't have bandwidth atm.
package.json deps
"@date-io/date-fns": "1.x",
"@google-cloud/bigquery": "^5.5.0",
"@google/maps": "^1.0.1",
"@material-ui/core": "^4.11.0",
"@material-ui/icons": "^4.9.1",
"@material-ui/lab": "^4.0.0-alpha.56",
"@material-ui/pickers": "^3.2.10",
"@mui-treasury/styles": "^0.5.0",
"@react-hook/window-size": "^3.0.6",
"autosuggest-highlight": "^3.1.1",
"axios": "^0.19.2",
"browser-or-node": "^1.2.1",
"clsx": "^1.0.4",
"common-tags": "^1.8.0",
"core-js": "^3.2.1",
"date-fns": "^2.4.1",
"date-fns-tz": "^1.0.12",
"email-addresses": "^3.1.0",
"firebase": "^8.2.7",
"firebase-admin": "^9.5.0",
"firebase-functions": "^3.13.1",
"ical-generator": "^1.15.1",
"integrify": "^3.0.1",
"isomorphic-unfetch": "^3.0.0",
"js-cookie": "^2.2.1",
"lodash": "^4.17.15",
"next": "^10.0.6",
"next-cookies": "^2.0.3",
"next-images": "^1.1.2",
"next-redux-wrapper": "^6.0.2",
"node": "^14.2.0",
"nodemailer": "^6.2.1",
"nookies": "^2.5.2",
"papaparse": "^5.2.0",
"pluralize": "^8.0.0",
"prop-types": "^15.7.2",
"react": "^17.0.1",
"react-dom": "^17.0.1",
"react-dropzone": "^10.2.2",
"react-flip-move": "^3.0.3",
"react-ga": "^2.5.7",
"react-redux": "^7.2.1",
"react-redux-firebase": "^3.10.0",
"react-responsive-modal": "^4.0.1",
"react-scroll-parallax": "^2.4.0",
"reactfire": "^3.0.0-rc.0",
"recompose": "^0.30.0",
"redux": "^4.0.5",
"redux-devtools-extension": "^2.13.8",
"redux-firestore": "^0.13.0",
"redux-thunk": "^2.3.0",
"rrule": "^2.6.6",
"uuid": "^8.1.0"
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@devth Glad someone else found this useful! 😅
I am in the process of updating my Next.js example yet again and actually publishing my new blog post.
I am intending on recommending Cloud Run, I would use this personally. It has far fewer concessions than Functions.
I think the
SSG
hack is sufficient for those who must use Cloud Functions, but thestale-while-revalidate
on what should be static content is terribly annoying. I might call for others to help build out Automatic Static Optimization. There may already be a solution contained within https://github.com/serverless-nextjs/serverless-next.js we could extract.TBH the effort involved circumventing the SSG hack doesn't seem worth it when Cloud Run is so easily usable and available by Firebase users. Just look at the Issues and effort to maintain the aws-serverless plugin linked above.