diff --git a/src/constants.js b/src/constants.ts similarity index 66% rename from src/constants.js rename to src/constants.ts index 6225993605..5058fd7257 100644 --- a/src/constants.js +++ b/src/constants.ts @@ -1,9 +1,9 @@ -const HANDLER_FUNCTION_NAME = '___netlify-handler' -const ODB_FUNCTION_NAME = '___netlify-odb-handler' -const IMAGE_FUNCTION_NAME = '_ipx' +export const HANDLER_FUNCTION_NAME = '___netlify-handler' +export const ODB_FUNCTION_NAME = '___netlify-odb-handler' +export const IMAGE_FUNCTION_NAME = '_ipx' // These are paths in .next that shouldn't be publicly accessible -const HIDDEN_PATHS = [ +export const HIDDEN_PATHS = [ '/cache/*', '/server/*', '/serverless/*', diff --git a/src/helpers/cache.js b/src/helpers/cache.ts similarity index 50% rename from src/helpers/cache.js rename to src/helpers/cache.ts index 5f6e4248b4..d2a61d100d 100644 --- a/src/helpers/cache.js +++ b/src/helpers/cache.ts @@ -1,9 +1,8 @@ -const { - posix: { join }, -} = require('path') +import { posix } from 'path' + +export const restoreCache = async ({ cache, publish }) => { + const cacheDir = posix.join(publish, 'cache') -exports.restoreCache = async ({ cache, publish }) => { - const cacheDir = join(publish, 'cache') if (await cache.restore(cacheDir)) { console.log('Next.js cache restored.') } else { @@ -11,13 +10,13 @@ exports.restoreCache = async ({ cache, publish }) => { } } -exports.saveCache = async ({ cache, publish }) => { - const cacheDir = join(publish, 'cache') +export const saveCache = async ({ cache, publish }) => { + const cacheDir = posix.join(publish, 'cache') - const buildManifest = join(publish, 'build-manifest.json') + const buildManifest = posix.join(publish, 'build-manifest.json') if (await cache.save(cacheDir, { digests: [buildManifest] })) { console.log('Next.js cache saved.') } else { console.log('No Next.js cache to save.') } -} +} \ No newline at end of file diff --git a/src/helpers/config.js b/src/helpers/config.ts similarity index 84% rename from src/helpers/config.js rename to src/helpers/config.ts index 87a9693d50..b7c5a19a16 100644 --- a/src/helpers/config.js +++ b/src/helpers/config.ts @@ -1,17 +1,21 @@ /* eslint-disable max-lines */ -const { yellowBright } = require('chalk') -const { readJSON, existsSync } = require('fs-extra') -const { outdent } = require('outdent') -const { join, dirname, relative } = require('pathe') -const slash = require('slash') +import { NetlifyConfig } from '@netlify/build' +import { yellowBright } from 'chalk' +import { readJSON } from 'fs-extra' +import { PrerenderManifest } from 'next/dist/build' +import { outdent } from 'outdent' +import { join, dirname, relative } from 'pathe' +import slash from 'slash' -const defaultFailBuild = (message, { error }) => { +import { HANDLER_FUNCTION_NAME, ODB_FUNCTION_NAME, HIDDEN_PATHS } from '../constants' + +import { RequiredServerFiles } from './requiredServerFilesType' + +const defaultFailBuild = (message: string, { error } ): never => { throw new Error(`${message}\n${error && error.stack}`) } -const { HANDLER_FUNCTION_NAME, ODB_FUNCTION_NAME, HIDDEN_PATHS } = require('../constants') - const ODB_FUNCTION_PATH = `/.netlify/builders/${ODB_FUNCTION_NAME}` const HANDLER_FUNCTION_PATH = `/.netlify/functions/${HANDLER_FUNCTION_NAME}` @@ -19,7 +23,7 @@ const CATCH_ALL_REGEX = /\/\[\.{3}(.*)](.json)?$/ const OPTIONAL_CATCH_ALL_REGEX = /\/\[{2}\.{3}(.*)]{2}(.json)?$/ const DYNAMIC_PARAMETER_REGEX = /\/\[(.*?)]/g -const getNetlifyRoutes = (nextRoute) => { +const getNetlifyRoutes = (nextRoute: string): Array => { let netlifyRoutes = [nextRoute] // If the route is an optional catch-all route, we need to add a second @@ -54,8 +58,12 @@ const getNetlifyRoutes = (nextRoute) => { return netlifyRoutes } -exports.generateRedirects = async ({ netlifyConfig, basePath, i18n }) => { - const { dynamicRoutes, routes: staticRoutes } = await readJSON( +export const generateRedirects = async ({ netlifyConfig, basePath, i18n }: { + netlifyConfig: NetlifyConfig, + basePath: string, + i18n +}) => { + const { dynamicRoutes, routes: staticRoutes }: PrerenderManifest = await readJSON( join(netlifyConfig.build.publish, 'prerender-manifest.json'), ) @@ -123,6 +131,8 @@ exports.generateRedirects = async ({ netlifyConfig, basePath, i18n }) => { from: `${basePath}/*`, to: HANDLER_FUNCTION_PATH, status: 200, + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore conditions: { Cookie: ['__prerender_bypass', '__next_preview_data'] }, force: true, }, @@ -158,14 +168,16 @@ exports.generateRedirects = async ({ netlifyConfig, basePath, i18n }) => { } } -exports.getNextConfig = async function getNextConfig({ publish, failBuild = defaultFailBuild }) { +export const getNextConfig = async function getNextConfig({ publish, failBuild = defaultFailBuild }) { try { - const { config, appDir, ignore } = await readJSON(join(publish, 'required-server-files.json')) + const { config, appDir, ignore }: RequiredServerFiles = await readJSON(join(publish, 'required-server-files.json')) if (!config) { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore return failBuild('Error loading your Next config') } return { ...config, appDir, ignore } - } catch (error) { + } catch (error: unknown) { return failBuild('Error loading your Next config', { error }) } } @@ -180,7 +192,7 @@ const resolveModuleRoot = (moduleName) => { const DEFAULT_EXCLUDED_MODULES = ['sharp', 'electron'] -exports.configureHandlerFunctions = ({ netlifyConfig, publish, ignore = [] }) => { +export const configureHandlerFunctions = ({ netlifyConfig, publish, ignore = [] }) => { /* eslint-disable no-underscore-dangle */ netlifyConfig.functions._ipx ||= {} netlifyConfig.functions._ipx.node_bundler = 'nft' diff --git a/src/helpers/requiredServerFilesType.ts b/src/helpers/requiredServerFilesType.ts new file mode 100644 index 0000000000..486331804e --- /dev/null +++ b/src/helpers/requiredServerFilesType.ts @@ -0,0 +1,129 @@ +/* eslint-disable eslint-comments/disable-enable-pair, @typescript-eslint/no-empty-interface, no-use-before-define */ +/** + * This was generated using @see {@link https://quicktype.io/}, and using the + * demo's {@code demos/default/.next/required-server-files.json} file. I was + * unable to find any types for this file so instead this was manually generated + */ + +export interface RequiredServerFiles { + version?: number; + config?: Config; + appDir?: string; + files?: string[]; + ignore?: string[]; +} + +export interface Env { +} + +export interface Config { + env?: Env; + webpack?: null; + webpackDevMiddleware?: null; + eslint?: Eslint; + typescript?: Typescript; + distDir?: string; + cleanDistDir?: boolean; + assetPrefix?: string; + configOrigin?: string; + useFileSystemPublicRoutes?: boolean; + generateEtags?: boolean; + pageExtensions?: string[]; + target?: string; + poweredByHeader?: boolean; + compress?: boolean; + analyticsId?: string; + images?: Images; + devIndicators?: DevIndicators; + onDemandEntries?: OnDemandEntries; + amp?: Amp; + basePath?: string; + sassOptions?: Env; + trailingSlash?: boolean; + i18n?: I18N; + productionBrowserSourceMaps?: boolean; + optimizeFonts?: boolean; + excludeDefaultMomentLocales?: boolean; + serverRuntimeConfig?: Env; + publicRuntimeConfig?: Env; + reactStrictMode?: boolean; + httpAgentOptions?: HTTPAgentOptions; + outputFileTracing?: boolean; + staticPageGenerationTimeout?: number; + swcMinify?: boolean; + experimental?: Experimental; + future?: Future; + configFileName?: string; +} + +export interface Amp { + canonicalBase?: string; +} + +export interface DevIndicators { + buildActivity?: boolean; + buildActivityPosition?: string; +} + +export interface Eslint { + ignoreDuringBuilds?: boolean; +} + +export interface Experimental { + cpus?: number; + sharedPool?: boolean; + plugins?: boolean; + profiling?: boolean; + isrFlushToDisk?: boolean; + workerThreads?: boolean; + pageEnv?: boolean; + optimizeImages?: boolean; + optimizeCss?: boolean; + scrollRestoration?: boolean; + externalDir?: boolean; + reactRoot?: boolean; + disableOptimizedLoading?: boolean; + gzipSize?: boolean; + craCompat?: boolean; + esmExternals?: boolean; + isrMemoryCacheSize?: number; + concurrentFeatures?: boolean; + serverComponents?: boolean; + fullySpecified?: boolean; + outputFileTracingRoot?: string; + outputStandalone?: boolean; +} + +export interface Future { + strictPostcssConfiguration?: boolean; +} + +export interface HTTPAgentOptions { + keepAlive?: boolean; +} + +export interface I18N { + defaultLocale?: string; + locales?: string[]; +} + +export interface Images { + deviceSizes?: number[]; + imageSizes?: number[]; + path?: string; + loader?: string; + domains?: any[]; + disableStaticImages?: boolean; + minimumCacheTTL?: number; + formats?: string[]; +} + +export interface OnDemandEntries { + maxInactiveAge?: number; + pagesBufferLength?: number; +} + +export interface Typescript { + ignoreBuildErrors?: boolean; + tsconfigPath?: string; +}