Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
ffe825c
Add automatic exporting of pages with no getInitialProps
ijjk May 9, 2019
89a3ad3
Add support for exporting serverless to static
ijjk May 9, 2019
4ae98f8
Fix missing runtimeEnv when requiring page, re-add warning
ijjk May 9, 2019
f53b31c
Update flying-shuttle test
ijjk May 9, 2019
9a325a2
Merge remote-tracking branch 'upstream/canary' into add/auto-static
ijjk May 10, 2019
20c3e87
revert un-used pagesManifest change
ijjk May 10, 2019
6a96aff
remove query.amp RegExp test
ijjk May 10, 2019
4757537
Fix windows backslashes not being replaced
ijjk May 10, 2019
89b2da3
Merge remote-tracking branch 'upstream/canary' into add/auto-static
ijjk May 10, 2019
ceba225
Merge remote-tracking branch 'upstream/canary' into add/auto-static
ijjk May 13, 2019
9973273
Re-enable serverless support for next start
ijjk May 13, 2019
2df95ab
Merge branch 'canary' into add/auto-static
ijjk May 13, 2019
68927e3
bump
ijjk May 13, 2019
6ccd760
Merge remote-tracking branch 'upstream/canary' into add/auto-static
ijjk May 14, 2019
4e2e958
Fix getInitialProps check
ijjk May 14, 2019
7d33924
Fix incorrect error check
ijjk May 14, 2019
70cfd06
Re-add check for reserved pages
ijjk May 14, 2019
a547bec
Fix static check
ijjk May 14, 2019
333430a
Merge remote-tracking branch 'upstream/canary' into add/auto-static
ijjk May 15, 2019
a30f548
Update to ignore /api pages and clean up some tests
ijjk May 15, 2019
e5aada9
Re-add needed next.config for test and correct behavior
ijjk May 15, 2019
6f85821
Merge remote-tracking branch 'upstream/canary' into add/auto-static
ijjk May 16, 2019
7517a99
Update RegExp for ignored pages for auto-static
ijjk May 16, 2019
5ae7cf0
Add checking for custom getInitialProps in pages/_app
ijjk May 17, 2019
9672558
Merge branch 'canary' into add/auto-static
ijjk May 17, 2019
ad999e9
Update isPageStatic logic to only use default export
ijjk May 17, 2019
3a971e3
Re-add retrying to CircleCi
ijjk May 17, 2019
56b873a
Update query during dev to only have values
ijjk May 17, 2019
4a66457
Fix test
ijjk May 17, 2019
07e2605
Merge branch 'canary' into add/auto-static
ijjk May 17, 2019
c7710b0
Merge remote-tracking branch 'upstream/canary' into add/auto-static
ijjk May 18, 2019
b6414ab
Merge branch 'add/auto-static' of github.com:ijjk/next.js into add/au…
ijjk May 18, 2019
b188864
Add warning when page without default export is
ijjk May 18, 2019
e377e3f
Merge remote-tracking branch 'upstream/canary' into add/auto-static
ijjk May 18, 2019
99ca34e
Fix backslashes not being replaced
ijjk May 18, 2019
e29b946
Merge remote-tracking branch 'upstream/canary' into add/auto-static
ijjk May 21, 2019
e80a694
Merge remote-tracking branch 'upstream/canary' into add/auto-static
ijjk May 22, 2019
7be354c
Integrate auto-static with flying-shuttle
ijjk May 22, 2019
f2748ce
Add autoExport for opting in
ijjk May 22, 2019
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
2 changes: 2 additions & 0 deletions packages/next-server/lib/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export const BUILD_MANIFEST = 'build-manifest.json'
export const REACT_LOADABLE_MANIFEST = 'react-loadable-manifest.json'
export const CHUNK_GRAPH_MANIFEST = 'compilation-modules.json'
export const SERVER_DIRECTORY = 'server'
export const SERVERLESS_DIRECTORY = 'serverless'
export const CONFIG_FILE = 'next.config.js'
export const BUILD_ID_FILE = 'BUILD_ID'
export const BLOCKED_PAGES = [
Expand All @@ -27,4 +28,5 @@ export const CLIENT_STATIC_FILES_RUNTIME_WEBPACK = `${CLIENT_STATIC_FILES_RUNTIM
export const IS_BUNDLED_PAGE_REGEX = /^static[/\\][^/\\]+[/\\]pages.*\.js$/
// matches static/<buildid>/pages/:page*.js
export const ROUTE_NAME_REGEX = /^static[/\\][^/\\]+[/\\]pages[/\\](.*)\.js$/
export const SERVERLESS_ROUTE_NAME_REGEX = /^pages[/\\](.*)\.js$/
export const HEAD_BUILD_ID_FILE = `${CLIENT_STATIC_FILES_PATH}/HEAD_BUILD_ID`
1 change: 1 addition & 0 deletions packages/next-server/server/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ const defaultConfig: {[key: string]: any} = {
(Number(process.env.CIRCLE_NODE_TOTAL) ||
(os.cpus() || { length: 1 }).length) - 1,
),
autoExport: false,
ampBindInitData: false,
exportTrailingSlash: true,
terserLoader: false,
Expand Down
10 changes: 7 additions & 3 deletions packages/next-server/server/load-components.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {BUILD_MANIFEST, CLIENT_STATIC_FILES_PATH, REACT_LOADABLE_MANIFEST, SERVER_DIRECTORY} from '../lib/constants';
import {BUILD_MANIFEST, CLIENT_STATIC_FILES_PATH, REACT_LOADABLE_MANIFEST, SERVER_DIRECTORY, SERVERLESS_DIRECTORY} from '../lib/constants';
import { join } from 'path';

import { requirePage } from './require';
Expand All @@ -7,14 +7,18 @@ export function interopDefault(mod: any) {
return mod.default || mod
}

export async function loadComponents(distDir: string, buildId: string, pathname: string) {
export async function loadComponents(distDir: string, buildId: string, pathname: string, serverless: boolean) {
if (serverless) {
const Component = await requirePage(pathname, distDir, serverless)
return { Component }
}
const documentPath = join(distDir, SERVER_DIRECTORY, CLIENT_STATIC_FILES_PATH, buildId, 'pages', '_document')
const appPath = join(distDir, SERVER_DIRECTORY, CLIENT_STATIC_FILES_PATH, buildId, 'pages', '_app')

const [buildManifest, reactLoadableManifest, Component, Document, App] = await Promise.all([
require(join(distDir, BUILD_MANIFEST)),
require(join(distDir, REACT_LOADABLE_MANIFEST)),
interopDefault(requirePage(pathname, distDir)),
interopDefault(requirePage(pathname, distDir, serverless)),
interopDefault(require(documentPath)),
interopDefault(require(appPath)),
])
Expand Down
28 changes: 20 additions & 8 deletions packages/next-server/server/next-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,14 +72,8 @@ export default class Server {
publicRuntimeConfig,
assetPrefix,
generateEtags,
target,
} = this.nextConfig

if (process.env.NODE_ENV === 'production' && target !== 'server')
throw new Error(
'Cannot start server when target is not server. https://err.sh/zeit/next.js/next-start-serverless',
)

this.buildId = this.readBuildId()
this.renderOpts = {
ampBindInitData: this.nextConfig.experimental.ampBindInitData,
Expand Down Expand Up @@ -257,7 +251,7 @@ export default class Server {
* @param pathname path of request
*/
private resolveApiRequest(pathname: string) {
return getPagePath(pathname, this.distDir)
return getPagePath(pathname, this.distDir, this.nextConfig.target === 'serverless')
}

private generatePublicRoutes(): Route[] {
Expand Down Expand Up @@ -353,7 +347,25 @@ export default class Server {
query: ParsedUrlQuery = {},
opts: any,
) {
const result = await loadComponents(this.distDir, this.buildId, pathname)
const serverless = this.nextConfig.target === 'serverless'
// try serving a static AMP version first
if (query.amp) {
try {
const result = await loadComponents(this.distDir, this.buildId, (pathname === '/' ? '/index' : pathname) + '.amp', serverless)
if (typeof result.Component === 'string') return result.Component
} catch (err) {
if (err.code !== 'ENOENT') throw err
}
}
const result = await loadComponents(this.distDir, this.buildId, pathname, serverless)
// handle static page
if (typeof result.Component === 'string') return result.Component
// handle serverless
if (typeof result.Component === 'object' &&
typeof result.Component.renderReqToHTML === 'function'
) {
return result.Component.renderReqToHTML(req, res)
}
return renderToHTML(req, res, pathname, query, { ...result, ...opts })
}

Expand Down
12 changes: 11 additions & 1 deletion packages/next-server/server/render.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,16 @@ export async function renderToHTML(
`The default export is not a React Component in page: "/_document"`,
)
}

const isStaticPage = typeof (Component as any).getInitialProps !== 'function'
const defaultAppGetInitialProps = App.getInitialProps === (App as any).origGetInitialProps

if (isStaticPage && defaultAppGetInitialProps) {
// remove query values except ones that will be set during export
query = {
amp: query.amp,
}
}
}

// @ts-ignore url will always be set
Expand Down Expand Up @@ -304,7 +314,7 @@ export async function renderToHTML(

const ampMode = {
enabled: false,
hasQuery: Boolean(query.amp && /^(y|yes|true|1)/i.test(query.amp.toString())),
hasQuery: Boolean(query.amp),
}

if (ampBindInitData) {
Expand Down
17 changes: 12 additions & 5 deletions packages/next-server/server/require.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
import fs from 'fs'
import {join} from 'path'
import {PAGES_MANIFEST, SERVER_DIRECTORY} from '../lib/constants'
import {promisify} from 'util'
import {PAGES_MANIFEST, SERVER_DIRECTORY, SERVERLESS_DIRECTORY} from '../lib/constants'
import { normalizePagePath } from './normalize-page-path'

const readFile = promisify(fs.readFile)

export function pageNotFoundError(page: string): Error {
const err: any = new Error(`Cannot find module for page: ${page}`)
err.code = 'ENOENT'
return err
}

export function getPagePath(page: string, distDir: string): string {
const serverBuildPath = join(distDir, SERVER_DIRECTORY)
export function getPagePath(page: string, distDir: string, serverless: boolean): string {
const serverBuildPath = join(distDir, serverless ? SERVERLESS_DIRECTORY : SERVER_DIRECTORY)
const pagesManifest = require(join(serverBuildPath, PAGES_MANIFEST))

try {
Expand All @@ -32,7 +36,10 @@ export function getPagePath(page: string, distDir: string): string {
return join(serverBuildPath, pagesManifest[page])
}

export function requirePage(page: string, distDir: string): any {
const pagePath = getPagePath(page, distDir)
export function requirePage(page: string, distDir: string, serverless: boolean): any {
const pagePath = getPagePath(page, distDir, serverless)
if (pagePath.endsWith('.html')) {
return readFile(pagePath, 'utf8')
}
return require(pagePath)
}
88 changes: 82 additions & 6 deletions packages/next/build/flying-shuttle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { promisify } from 'util'

import { recursiveDelete } from '../lib/recursive-delete'
import * as Log from './output/log'
import { PageInfo } from './utils';

const FILE_BUILD_ID = 'HEAD_BUILD_ID'
const FILE_UPDATED_AT = 'UPDATED_AT'
Expand Down Expand Up @@ -220,6 +221,29 @@ export class FlyingShuttle {
return (this._shuttleBuildId = contents)
}

getPageInfos = async (): Promise<Map<string, PageInfo>> => {
const pageInfos: Map<string, PageInfo> = new Map()
const pagesManifest = JSON.parse(await fsReadFile(
path.join(
this.shuttleDirectory, DIR_FILES_NAME, 'serverless/pages-manifest.json'
),
'utf8'
))
Object.keys(pagesManifest).forEach(pg => {
const path = pagesManifest[pg]
const isStatic: boolean = path.endsWith('html')
let isAmp = Boolean(pagesManifest[pg + '.amp'])
if (pg === '/') isAmp = Boolean(pagesManifest['/index.amp'])
pageInfos.set(pg, {
isAmp,
size: 0,
static: isStatic,
serverBundle: path
})
})
return pageInfos
}

getUnchangedPages = async () => {
const manifestPath = path.join(this.shuttleDirectory, CHUNK_GRAPH_MANIFEST)
const manifest = require(manifestPath) as ChunkGraphManifest
Expand Down Expand Up @@ -276,8 +300,36 @@ export class FlyingShuttle {
return unchangedPages
}

restorePage = async (page: string): Promise<boolean> => {
mergePagesManifest = async (): Promise<void> => {
const savedPagesManifest = path.join(
this.shuttleDirectory, DIR_FILES_NAME, 'serverless/pages-manifest.json'
)
if (!(await fsExists(savedPagesManifest))) return

const saved = JSON.parse(await fsReadFile(
savedPagesManifest,
'utf8'
))
const currentPagesManifest = path.join(
this.distDirectory, 'serverless/pages-manifest.json'
)
const current = JSON.parse(await fsReadFile(
currentPagesManifest,
'utf8'
))

await fsWriteFile(currentPagesManifest, JSON.stringify({
...saved,
...current,
}))
}

restorePage = async (
page: string,
pageInfo: PageInfo = {} as PageInfo
): Promise<boolean> => {
await this._restoreSema.acquire()

try {
const manifestPath = path.join(
this.shuttleDirectory,
Expand All @@ -293,10 +345,9 @@ export class FlyingShuttle {

const serverless = path.join(
'serverless/pages',
`${page === '/' ? 'index' : page}.js`
`${page === '/' ? 'index' : page}.${pageInfo.static ? 'html' : 'js'}`
)
const files = [serverless, ...pageChunks[page]]

const filesExists = await Promise.all(
files
.map(f => path.join(this.shuttleDirectory, DIR_FILES_NAME, f))
Expand Down Expand Up @@ -366,7 +417,7 @@ export class FlyingShuttle {
}
}

save = async () => {
save = async (staticPages: Set<string>, pageInfos: Map<string, PageInfo>) => {
Log.wait('docking flying shuttle')

await recursiveDelete(this.shuttleDirectory)
Expand Down Expand Up @@ -419,10 +470,30 @@ export class FlyingShuttle {
const usedChunks = new Set()
const pages = Object.keys(storeManifest.pageChunks)
pages.forEach(page => {
storeManifest.pageChunks[page].forEach(file => usedChunks.add(file))
const info = pageInfos.get(page) || {} as PageInfo

storeManifest.pageChunks[page].forEach((file, idx) => {
if (info.isAmp) {
// AMP pages don't have client bundles
storeManifest.pageChunks[page] = []
return
}
usedChunks.add(file)
})
usedChunks.add(
path.join('serverless/pages', `${page === '/' ? 'index' : page}.js`)
path.join('serverless/pages', `${
page === '/' ? 'index' : page
}.${staticPages.has(page) ? 'html' : 'js'}`)
)
const ampPage = (page === '/' ? '/index' : page) + '.amp'

if (staticPages.has(ampPage)) {
storeManifest.pages[ampPage] = []
storeManifest.pageChunks[ampPage] = []
usedChunks.add(
path.join('serverless/pages', `${ampPage}.html`)
)
}
})

await fsWriteFile(
Expand All @@ -442,6 +513,11 @@ export class FlyingShuttle {
})
)

await fsCopyFile(
path.join(this.distDirectory, 'serverless/pages-manifest.json'),
path.join(this.shuttleDirectory, DIR_FILES_NAME, 'serverless/pages-manifest.json')
)

Log.info(`flying shuttle payload: ${usedChunks.size + 2} files`)
Log.ready('flying shuttle docked')

Expand Down
Loading