Skip to content

feat: add API cache + 404 page #7189

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,9 @@
"scripts": {
"prepare": "is-ci || husky install node_modules/@netlify/eslint-config-node/.husky/",
"start": "node ./bin/run.js",
"build": "tsc",
"build": "run-s build:ts build:post",
"build:ts": "tsc",
"build:post": "node scripts/postbuild.mjs",
"dev": "tsc --watch",
"test": "run-s format test:dev",
"format": "run-s format:check-fix:*",
Expand Down
10 changes: 10 additions & 0 deletions scripts/postbuild.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import fs from 'fs'
import path from 'path'
import { fileURLToPath } from 'url'

const cwd = path.dirname(fileURLToPath(import.meta.url))

const srcPath = path.resolve(cwd, '../src/lib/templates')
const destPath = path.resolve(cwd, '../dist/lib/templates')

fs.cpSync(srcPath, destPath, { recursive: true })
13 changes: 11 additions & 2 deletions src/commands/base-command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,10 @@ import inquirer from 'inquirer'
// @ts-expect-error TS(7016) FIXME: Could not find a declaration file for module 'inqu... Remove this comment to see the full error message
import inquirerAutocompletePrompt from 'inquirer-autocomplete-prompt'
import merge from 'lodash/merge.js'
import { NetlifyAPI } from 'netlify'
import { NetlifyAPI, APICache } from 'netlify'

import { getAgent } from '../lib/http-agent.js'
import { getPathInHome } from '../lib/settings.js'
import {
NETLIFY_CYAN,
USER_AGENT,
Expand Down Expand Up @@ -77,6 +78,13 @@ const COMMANDS_WITHOUT_WORKSPACE_OPTIONS = new Set(['api', 'recipes', 'completio
*/
const COMMANDS_WITH_FEATURE_FLAGS = new Set(['build', 'dev', 'deploy'])

const apiCache = new APICache({
fsPath: getPathInHome(['api-cache']),
ttl: 30_000,
// swr: 3_600_000,
swr: Infinity,
})

/** Formats a help list correctly with the correct indent */
const formatHelpList = (textArray: string[]) => textArray.join('\n').replace(/^/gm, ' '.repeat(HELP_INDENT_WIDTH))

Expand Down Expand Up @@ -566,7 +574,7 @@ export default class BaseCommand extends Command {
httpProxy: flags.httpProxy,
certificateFile: flags.httpProxyCertificateFilename,
})
const apiOpts = { ...apiUrlOpts, agent }
const apiOpts = { ...apiUrlOpts, agent, cache: apiCache }
// TODO: remove typecast once we have proper types for the API
const api = new NetlifyAPI(token || '', apiOpts) as NetlifyOptions['api']

Expand Down Expand Up @@ -697,6 +705,7 @@ export default class BaseCommand extends Command {
try {
return await resolveConfig({
accountId: this.accountId,
apiCache,
config: config.configFilePath,
packagePath: config.packagePath,
repositoryRoot: config.repositoryRoot,
Expand Down
103 changes: 103 additions & 0 deletions src/lib/templates/404.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
<!DOCTYPE html>
<html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8">

<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">

<title>Page Not Found</title>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
background: rgb(52, 56, 60);
color: white;
overflow: hidden;
margin: 0;
padding: 0;
}

h1 {
margin: 0;
font-size: 22px;
line-height: 24px;
}

.main {
position: relative;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100vh;
width: 100vw;
}

.card {
position: relative;
display: flex;
flex-direction: column;
width: 75%;
max-width: 364px;
padding: 24px;
background: white;
color: rgb(14, 30, 37);
border-radius: 8px;
box-shadow: 0 2px 4px 0 rgba(14, 30, 37, .16);
}

a {
margin: 0;
font-weight: 600;
line-height: 24px;
color: #054861;
}

a svg {
position: relative;
top: 2px;
}

a:hover,
a:focus {
text-decoration: none;
}

a:hover svg path{
fill: #007067;
}

p:last-of-type {
margin-bottom: 0;
}

</style>
</head>
<body>
<div class="main">
<div class="card">
<div class="header">
<h1>Page Not Found</h1>
</div>
<div class="body">
<p>Looks like you've followed a broken link or entered a URL that doesn't exist on this site.</p>
<!--p>
<a id="back-link" href="/">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<path fill="#007067" d="M11.9998836,4.09370803 L8.55809517,7.43294953 C8.23531459,7.74611298 8.23531459,8.25388736 8.55809517,8.56693769 L12,11.9062921 L9.84187871,14 L4.24208544,8.56693751 C3.91930485,8.25388719 3.91930485,7.74611281 4.24208544,7.43294936 L9.84199531,2 L11.9998836,4.09370803 Z"></path>
</svg>
Back to our site
</a>
</p-->
<hr><p>If this is a brand new site, you can add files to the <code>netlify/publish</code> directory or <a href="#">configure a custom publish directory</a>.
</p>
</div>
</div>
</div>
<script>
(function() {
if (document.referrer && document.location.host && document.referrer.match(new RegExp("^https?://" + document.location.host))) {
document.getElementById("back-link").setAttribute("href", document.referrer);
}
})();
</script>


</body></html>
22 changes: 20 additions & 2 deletions src/utils/detect-server-settings.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { statSync } from 'fs'
import { readFile } from 'fs/promises'
import { EOL } from 'os'
import { dirname, relative, resolve } from 'path'
Expand Down Expand Up @@ -92,9 +93,26 @@ const DEFAULT_STATIC_PORT = 3999
* Logs a message that it was unable to determine the dist directory and falls back to the workingDir
*/
const getDefaultDist = (workingDir: string) => {
const netlifyPublishPath = resolve(workingDir, 'netlify', 'publish')

try {
const stat = statSync(netlifyPublishPath)

if (stat.isDirectory()) {
log(`${NETLIFYDEVWARN} Unable to determine public folder to serve files from. Using current working directory`)
log(` Setup a netlify.toml file with a [dev] section to specify your dev server settings.`)
log(` https://docs.netlify.com/cli/local-development/#project-detection`)

return netlifyPublishPath
}
} catch {
// no-op
}

log(`${NETLIFYDEVWARN} Unable to determine public folder to serve files from. Using current working directory`)
log(`${NETLIFYDEVWARN} Setup a netlify.toml file with a [dev] section to specify your dev server settings.`)
log(`${NETLIFYDEVWARN} See docs at: https://docs.netlify.com/cli/local-development/#project-detection`)
log(` Setup a netlify.toml file with a [dev] section to specify your dev server settings.`)
log(` https://docs.netlify.com/cli/local-development/#project-detection`)

return workingDir
}

Expand Down
20 changes: 19 additions & 1 deletion src/utils/static-server.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
import { statSync } from 'fs'
import path from 'path'
import { fileURLToPath } from 'url'

import fastifyStatic from '@fastify/static'
import Fastify from 'fastify'

import { log, NETLIFYDEVLOG } from './command-helpers.js'

const cwd = path.dirname(fileURLToPath(import.meta.url))
const default404Template = path.resolve(cwd, '../lib/templates/404.html')

/**
* @param {object} config
* @param {import('./types.js').ServerSettings} config.settings
Expand All @@ -21,7 +26,20 @@ export const startStaticServer = async ({ settings }) => {
})

server.setNotFoundHandler((_req, res) => {
res.code(404).sendFile('404.html', rootPath)
let pagePath = default404Template

try {
const custom404Path = path.join(settings.dist, '404.html')
const stats = statSync(custom404Path)

if (stats.isFile()) {
pagePath = custom404Path
}
} catch {
// no-op
}

res.code(404).sendFile(path.basename(pagePath), path.dirname(pagePath))
})

server.addHook('onRequest', (req, reply, done) => {
Expand Down