mirror of
https://github.com/terribleplan/next.js.git
synced 2024-01-19 02:48:18 +00:00
Compile pages to .next/static directory (#4828)
* Compile pages to .next/static/<buildid>/pages/<page> * Fix test * Export class instead of using exports * Use constant for static directory * Add comment about what the middleware does
This commit is contained in:
parent
c090a57e77
commit
475b426ed1
|
@ -128,7 +128,7 @@ export default async function getBaseWebpackConfig (dir: string, {dev = false, i
|
||||||
.filter((p) => !!p)
|
.filter((p) => !!p)
|
||||||
|
|
||||||
const outputPath = path.join(dir, config.distDir, isServer ? SERVER_DIRECTORY : '')
|
const outputPath = path.join(dir, config.distDir, isServer ? SERVER_DIRECTORY : '')
|
||||||
const pagesEntries = await getPages(dir, {nextPagesDir: DEFAULT_PAGES_DIR, dev, isServer, pageExtensions: config.pageExtensions.join('|')})
|
const pagesEntries = await getPages(dir, {nextPagesDir: DEFAULT_PAGES_DIR, dev, buildId, isServer, pageExtensions: config.pageExtensions.join('|')})
|
||||||
const totalPages = Object.keys(pagesEntries).length
|
const totalPages = Object.keys(pagesEntries).length
|
||||||
const clientEntries = !isServer ? {
|
const clientEntries = !isServer ? {
|
||||||
// Backwards compatibility
|
// Backwards compatibility
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { RawSource } from 'webpack-sources'
|
||||||
import {PAGES_MANIFEST, ROUTE_NAME_REGEX} from '../../../lib/constants'
|
import {PAGES_MANIFEST, ROUTE_NAME_REGEX} from '../../../lib/constants'
|
||||||
|
|
||||||
// This plugin creates a pages-manifest.json from page entrypoints.
|
// This plugin creates a pages-manifest.json from page entrypoints.
|
||||||
// This is used for mapping paths like `/` to `.next/dist/bundles/pages/index.js` when doing SSR
|
// This is used for mapping paths like `/` to `.next/server/static/<buildid>/pages/index.js` when doing SSR
|
||||||
// It's also used by next export to provide defaultPathMap
|
// It's also used by next export to provide defaultPathMap
|
||||||
export default class PagesManifestPlugin {
|
export default class PagesManifestPlugin {
|
||||||
apply (compiler: any) {
|
apply (compiler: any) {
|
||||||
|
|
|
@ -33,7 +33,7 @@ function buildManifest (compiler, compilation) {
|
||||||
return manifest
|
return manifest
|
||||||
}
|
}
|
||||||
|
|
||||||
class ReactLoadablePlugin {
|
export class ReactLoadablePlugin {
|
||||||
constructor (opts = {}) {
|
constructor (opts = {}) {
|
||||||
this.filename = opts.filename
|
this.filename = opts.filename
|
||||||
}
|
}
|
||||||
|
@ -54,5 +54,3 @@ class ReactLoadablePlugin {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.ReactLoadablePlugin = ReactLoadablePlugin
|
|
||||||
|
|
|
@ -1,13 +1,14 @@
|
||||||
import path from 'path'
|
import path from 'path'
|
||||||
import promisify from '../../lib/promisify'
|
import promisify from '../../lib/promisify'
|
||||||
import globModule from 'glob'
|
import globModule from 'glob'
|
||||||
|
import {CLIENT_STATIC_FILES_PATH} from '../../lib/constants'
|
||||||
|
|
||||||
const glob = promisify(globModule)
|
const glob = promisify(globModule)
|
||||||
|
|
||||||
export async function getPages (dir, {nextPagesDir, dev, isServer, pageExtensions}) {
|
export async function getPages (dir, {nextPagesDir, dev, buildId, isServer, pageExtensions}) {
|
||||||
const pageFiles = await getPagePaths(dir, {dev, isServer, pageExtensions})
|
const pageFiles = await getPagePaths(dir, {dev, isServer, pageExtensions})
|
||||||
|
|
||||||
return getPageEntries(pageFiles, {nextPagesDir, isServer, pageExtensions})
|
return getPageEntries(pageFiles, {nextPagesDir, buildId, isServer, pageExtensions})
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getPagePaths (dir, {dev, isServer, pageExtensions}) {
|
export async function getPagePaths (dir, {dev, isServer, pageExtensions}) {
|
||||||
|
@ -25,7 +26,7 @@ export async function getPagePaths (dir, {dev, isServer, pageExtensions}) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert page path into single entry
|
// Convert page path into single entry
|
||||||
export function createEntry (filePath, {name, pageExtensions} = {}) {
|
export function createEntry (filePath, {buildId = '', name, pageExtensions} = {}) {
|
||||||
const parsedPath = path.parse(filePath)
|
const parsedPath = path.parse(filePath)
|
||||||
let entryName = name || filePath
|
let entryName = name || filePath
|
||||||
|
|
||||||
|
@ -41,35 +42,35 @@ export function createEntry (filePath, {name, pageExtensions} = {}) {
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
name: path.join('bundles', entryName),
|
name: path.join(CLIENT_STATIC_FILES_PATH, buildId, entryName),
|
||||||
files: [parsedPath.root ? filePath : `./${filePath}`] // The entry always has to be an array.
|
files: [parsedPath.root ? filePath : `./${filePath}`] // The entry always has to be an array.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert page paths into entries
|
// Convert page paths into entries
|
||||||
export function getPageEntries (pagePaths, {nextPagesDir, isServer = false, pageExtensions} = {}) {
|
export function getPageEntries (pagePaths, {nextPagesDir, buildId, isServer = false, pageExtensions} = {}) {
|
||||||
const entries = {}
|
const entries = {}
|
||||||
|
|
||||||
for (const filePath of pagePaths) {
|
for (const filePath of pagePaths) {
|
||||||
const entry = createEntry(filePath, {pageExtensions})
|
const entry = createEntry(filePath, {pageExtensions, buildId})
|
||||||
entries[entry.name] = entry.files
|
entries[entry.name] = entry.files
|
||||||
}
|
}
|
||||||
|
|
||||||
const appPagePath = path.join(nextPagesDir, '_app.js')
|
const appPagePath = path.join(nextPagesDir, '_app.js')
|
||||||
const appPageEntry = createEntry(appPagePath, {name: 'pages/_app.js'}) // default app.js
|
const appPageEntry = createEntry(appPagePath, {buildId, name: 'pages/_app.js'}) // default app.js
|
||||||
if (!entries[appPageEntry.name]) {
|
if (!entries[appPageEntry.name]) {
|
||||||
entries[appPageEntry.name] = appPageEntry.files
|
entries[appPageEntry.name] = appPageEntry.files
|
||||||
}
|
}
|
||||||
|
|
||||||
const errorPagePath = path.join(nextPagesDir, '_error.js')
|
const errorPagePath = path.join(nextPagesDir, '_error.js')
|
||||||
const errorPageEntry = createEntry(errorPagePath, {name: 'pages/_error.js'}) // default error.js
|
const errorPageEntry = createEntry(errorPagePath, {buildId, name: 'pages/_error.js'}) // default error.js
|
||||||
if (!entries[errorPageEntry.name]) {
|
if (!entries[errorPageEntry.name]) {
|
||||||
entries[errorPageEntry.name] = errorPageEntry.files
|
entries[errorPageEntry.name] = errorPageEntry.files
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isServer) {
|
if (isServer) {
|
||||||
const documentPagePath = path.join(nextPagesDir, '_document.js')
|
const documentPagePath = path.join(nextPagesDir, '_document.js')
|
||||||
const documentPageEntry = createEntry(documentPagePath, {name: 'pages/_document.js'}) // default _document.js
|
const documentPageEntry = createEntry(documentPagePath, {buildId, name: 'pages/_document.js'}) // default _document.js
|
||||||
if (!entries[documentPageEntry.name]) {
|
if (!entries[documentPageEntry.name]) {
|
||||||
entries[documentPageEntry.name] = documentPageEntry.files
|
entries[documentPageEntry.name] = documentPageEntry.files
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,9 +14,12 @@ export const BLOCKED_PAGES = [
|
||||||
'/_app',
|
'/_app',
|
||||||
'/_error'
|
'/_error'
|
||||||
]
|
]
|
||||||
export const IS_BUNDLED_PAGE_REGEX = /^bundles[/\\]pages.*\.js$/
|
// matches static/<buildid>/pages/<page>.js
|
||||||
export const ROUTE_NAME_REGEX = /^bundles[/\\]pages[/\\](.*)\.js$/
|
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 NEXT_PROJECT_ROOT = join(__dirname, '..', '..')
|
export const NEXT_PROJECT_ROOT = join(__dirname, '..', '..')
|
||||||
export const NEXT_PROJECT_ROOT_DIST = join(NEXT_PROJECT_ROOT, 'dist')
|
export const NEXT_PROJECT_ROOT_DIST = join(NEXT_PROJECT_ROOT, 'dist')
|
||||||
export const NEXT_PROJECT_ROOT_NODE_MODULES = join(NEXT_PROJECT_ROOT, 'node_modules')
|
export const NEXT_PROJECT_ROOT_NODE_MODULES = join(NEXT_PROJECT_ROOT, 'node_modules')
|
||||||
export const DEFAULT_PAGES_DIR = join(NEXT_PROJECT_ROOT_DIST, 'pages')
|
export const DEFAULT_PAGES_DIR = join(NEXT_PROJECT_ROOT_DIST, 'pages')
|
||||||
|
export const CLIENT_STATIC_FILES_PATH = 'static'
|
||||||
|
|
|
@ -69,7 +69,7 @@ export default class PageLoader {
|
||||||
const scriptRoute = route === '/' ? '/index.js' : `${route}.js`
|
const scriptRoute = route === '/' ? '/index.js' : `${route}.js`
|
||||||
|
|
||||||
const script = document.createElement('script')
|
const script = document.createElement('script')
|
||||||
const url = `${this.assetPrefix}/_next/${encodeURIComponent(this.buildId)}/page${scriptRoute}`
|
const url = `${this.assetPrefix}/_next/static/${encodeURIComponent(this.buildId)}/pages${scriptRoute}`
|
||||||
script.src = url
|
script.src = url
|
||||||
script.onerror = () => {
|
script.onerror = () => {
|
||||||
const error = new Error(`Error when loading route: ${route}`)
|
const error = new Error(`Error when loading route: ${route}`)
|
||||||
|
|
|
@ -99,7 +99,6 @@
|
||||||
"unfetch": "3.0.0",
|
"unfetch": "3.0.0",
|
||||||
"url": "0.11.0",
|
"url": "0.11.0",
|
||||||
"uuid": "3.1.0",
|
"uuid": "3.1.0",
|
||||||
"walk": "2.3.9",
|
|
||||||
"webpack": "4.16.1",
|
"webpack": "4.16.1",
|
||||||
"webpack-dev-middleware": "3.1.3",
|
"webpack-dev-middleware": "3.1.3",
|
||||||
"webpack-hot-middleware": "2.22.2",
|
"webpack-hot-middleware": "2.22.2",
|
||||||
|
|
|
@ -80,9 +80,9 @@ export class Head extends Component {
|
||||||
|
|
||||||
return <head {...this.props}>
|
return <head {...this.props}>
|
||||||
{(head || []).map((h, i) => React.cloneElement(h, { key: h.key || i }))}
|
{(head || []).map((h, i) => React.cloneElement(h, { key: h.key || i }))}
|
||||||
{page !== '/_error' && <link rel='preload' href={`${assetPrefix}/_next/${buildId}/page${pagePathname}`} as='script' nonce={this.props.nonce} />}
|
{page !== '/_error' && <link rel='preload' href={`${assetPrefix}/_next/static/${buildId}/pages${pagePathname}`} as='script' nonce={this.props.nonce} />}
|
||||||
<link rel='preload' href={`${assetPrefix}/_next/${buildId}/page/_app.js`} as='script' nonce={this.props.nonce} />
|
<link rel='preload' href={`${assetPrefix}/_next/static/${buildId}/pages/_app.js`} as='script' nonce={this.props.nonce} />
|
||||||
<link rel='preload' href={`${assetPrefix}/_next/${buildId}/page/_error.js`} as='script' nonce={this.props.nonce} />
|
<link rel='preload' href={`${assetPrefix}/_next/static/${buildId}/pages/_error.js`} as='script' nonce={this.props.nonce} />
|
||||||
{this.getPreloadDynamicChunks()}
|
{this.getPreloadDynamicChunks()}
|
||||||
{this.getPreloadMainLinks()}
|
{this.getPreloadMainLinks()}
|
||||||
{styles || null}
|
{styles || null}
|
||||||
|
@ -168,9 +168,9 @@ export class NextScript extends Component {
|
||||||
})`: ''}
|
})`: ''}
|
||||||
`
|
`
|
||||||
}} />}
|
}} />}
|
||||||
{page !== '/_error' && <script async id={`__NEXT_PAGE__${pathname}`} src={`${assetPrefix}/_next/${buildId}/page${pagePathname}`} nonce={this.props.nonce} />}
|
{page !== '/_error' && <script async id={`__NEXT_PAGE__${pathname}`} src={`${assetPrefix}/_next/static/${buildId}/pages${pagePathname}`} nonce={this.props.nonce} />}
|
||||||
<script async id={`__NEXT_PAGE__/_app`} src={`${assetPrefix}/_next/${buildId}/page/_app.js`} nonce={this.props.nonce} />
|
<script async id={`__NEXT_PAGE__/_app`} src={`${assetPrefix}/_next/static/${buildId}/pages/_app.js`} nonce={this.props.nonce} />
|
||||||
<script async id={`__NEXT_PAGE__/_error`} src={`${assetPrefix}/_next/${buildId}/page/_error.js`} nonce={this.props.nonce} />
|
<script async id={`__NEXT_PAGE__/_error`} src={`${assetPrefix}/_next/static/${buildId}/pages/_error.js`} nonce={this.props.nonce} />
|
||||||
{staticMarkup ? null : this.getDynamicChunks()}
|
{staticMarkup ? null : this.getDynamicChunks()}
|
||||||
{staticMarkup ? null : this.getScripts()}
|
{staticMarkup ? null : this.getScripts()}
|
||||||
</Fragment>
|
</Fragment>
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
import del from 'del'
|
import del from 'del'
|
||||||
import cp from 'recursive-copy'
|
import cp from 'recursive-copy'
|
||||||
import mkdirp from 'mkdirp-then'
|
import mkdirp from 'mkdirp-then'
|
||||||
import walk from 'walk'
|
|
||||||
import { extname, resolve, join, dirname, sep } from 'path'
|
import { extname, resolve, join, dirname, sep } from 'path'
|
||||||
import { existsSync, readFileSync, writeFileSync } from 'fs'
|
import { existsSync, readFileSync, writeFileSync } from 'fs'
|
||||||
import loadConfig from './config'
|
import loadConfig from './config'
|
||||||
import {PHASE_EXPORT, SERVER_DIRECTORY, PAGES_MANIFEST, CONFIG_FILE, BUILD_ID_FILE} from '../lib/constants'
|
import {PHASE_EXPORT, SERVER_DIRECTORY, PAGES_MANIFEST, CONFIG_FILE, BUILD_ID_FILE, CLIENT_STATIC_FILES_PATH} from '../lib/constants'
|
||||||
import { renderToHTML } from './render'
|
import { renderToHTML } from './render'
|
||||||
import { setAssetPrefix } from '../lib/asset'
|
import { setAssetPrefix } from '../lib/asset'
|
||||||
import * as envConfig from '../lib/runtime-config'
|
import * as envConfig from '../lib/runtime-config'
|
||||||
|
@ -51,11 +50,11 @@ export default async function (dir, options, configuration) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy .next/static directory
|
// Copy .next/static directory
|
||||||
if (existsSync(join(distDir, 'static'))) {
|
if (existsSync(join(distDir, CLIENT_STATIC_FILES_PATH))) {
|
||||||
log(' copying "static build" directory')
|
log(' copying "static build" directory')
|
||||||
await cp(
|
await cp(
|
||||||
join(distDir, 'static'),
|
join(distDir, CLIENT_STATIC_FILES_PATH),
|
||||||
join(outDir, '_next', 'static')
|
join(outDir, '_next', CLIENT_STATIC_FILES_PATH)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,8 +69,6 @@ export default async function (dir, options, configuration) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
await copyPages(distDir, outDir, buildId)
|
|
||||||
|
|
||||||
// Get the exportPathMap from the config file
|
// Get the exportPathMap from the config file
|
||||||
if (typeof nextConfig.exportPathMap !== 'function') {
|
if (typeof nextConfig.exportPathMap !== 'function') {
|
||||||
console.log(`> No "exportPathMap" found in "${CONFIG_FILE}". Generating map from "./pages"`)
|
console.log(`> No "exportPathMap" found in "${CONFIG_FILE}". Generating map from "./pages"`)
|
||||||
|
@ -149,40 +146,3 @@ export default async function (dir, options, configuration) {
|
||||||
console.log(message)
|
console.log(message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function copyPages (distDir, outDir, buildId) {
|
|
||||||
// TODO: do some proper error handling
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
const nextBundlesDir = join(distDir, 'bundles', 'pages')
|
|
||||||
const walker = walk.walk(nextBundlesDir, { followLinks: false })
|
|
||||||
|
|
||||||
walker.on('file', (root, stat, next) => {
|
|
||||||
const filename = stat.name
|
|
||||||
const fullFilePath = `${root}${sep}${filename}`
|
|
||||||
const relativeFilePath = fullFilePath.replace(nextBundlesDir, '')
|
|
||||||
|
|
||||||
// We should not expose this page to the client side since
|
|
||||||
// it has no use in the client side.
|
|
||||||
if (relativeFilePath === `${sep}_document.js`) {
|
|
||||||
next()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
let destFilePath = null
|
|
||||||
if (relativeFilePath === `${sep}index.js`) {
|
|
||||||
destFilePath = join(outDir, '_next', buildId, 'page', relativeFilePath)
|
|
||||||
} else if (/index\.js$/.test(filename)) {
|
|
||||||
const newRelativeFilePath = relativeFilePath.replace(`${sep}index.js`, '.js')
|
|
||||||
destFilePath = join(outDir, '_next', buildId, 'page', newRelativeFilePath)
|
|
||||||
} else {
|
|
||||||
destFilePath = join(outDir, '_next', buildId, 'page', relativeFilePath)
|
|
||||||
}
|
|
||||||
|
|
||||||
cp(fullFilePath, destFilePath)
|
|
||||||
.then(next)
|
|
||||||
.catch(reject)
|
|
||||||
})
|
|
||||||
|
|
||||||
walker.on('end', resolve)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
|
@ -8,7 +8,12 @@ import getBaseWebpackConfig from '../build/webpack'
|
||||||
import {
|
import {
|
||||||
addCorsSupport
|
addCorsSupport
|
||||||
} from './utils'
|
} from './utils'
|
||||||
import {IS_BUNDLED_PAGE_REGEX, ROUTE_NAME_REGEX} from '../lib/constants'
|
import {IS_BUNDLED_PAGE_REGEX, ROUTE_NAME_REGEX, BLOCKED_PAGES, CLIENT_STATIC_FILES_PATH} from '../lib/constants'
|
||||||
|
import pathMatch from './lib/path-match'
|
||||||
|
import {renderScriptError} from './render'
|
||||||
|
|
||||||
|
const route = pathMatch()
|
||||||
|
const matchNextPageBundleRequest = route('/_next/static/:buildId/pages/:path*.js(.map)?')
|
||||||
|
|
||||||
// Recursively look up the issuer till it ends up at the root
|
// Recursively look up the issuer till it ends up at the root
|
||||||
function findEntryModule (issuer) {
|
function findEntryModule (issuer) {
|
||||||
|
@ -46,7 +51,7 @@ function erroredPages (compilation, options = {enhanceName: (name) => name}) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class HotReloader {
|
export default class HotReloader {
|
||||||
constructor (dir, { quiet, config, buildId } = {}) {
|
constructor (dir, { config, buildId } = {}) {
|
||||||
this.buildId = buildId
|
this.buildId = buildId
|
||||||
this.dir = dir
|
this.dir = dir
|
||||||
this.middlewares = []
|
this.middlewares = []
|
||||||
|
@ -63,7 +68,7 @@ export default class HotReloader {
|
||||||
this.config = config
|
this.config = config
|
||||||
}
|
}
|
||||||
|
|
||||||
async run (req, res) {
|
async run (req, res, parsedUrl) {
|
||||||
// Usually CORS support is not needed for the hot-reloader (this is dev only feature)
|
// Usually CORS support is not needed for the hot-reloader (this is dev only feature)
|
||||||
// With when the app runs for multi-zones support behind a proxy,
|
// With when the app runs for multi-zones support behind a proxy,
|
||||||
// the current page is trying to access this URL via assetPrefix.
|
// the current page is trying to access this URL via assetPrefix.
|
||||||
|
@ -73,6 +78,42 @@ export default class HotReloader {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// When a request comes in that is a page bundle, e.g. /_next/static/<buildid>/pages/index.js
|
||||||
|
// we have to compile the page using on-demand-entries, this middleware will handle doing that
|
||||||
|
// by adding the page to on-demand-entries, waiting till it's done
|
||||||
|
// and then the bundle will be served like usual by the actual route in server/index.js
|
||||||
|
const handlePageBundleRequest = async (req, res, parsedUrl) => {
|
||||||
|
const {pathname} = parsedUrl
|
||||||
|
const params = matchNextPageBundleRequest(pathname)
|
||||||
|
if (!params) {
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (params.buildId !== this.buildId) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const page = `/${params.path.join('/')}`
|
||||||
|
if (BLOCKED_PAGES.indexOf(page) === -1) {
|
||||||
|
try {
|
||||||
|
await this.ensurePage(page)
|
||||||
|
} catch (error) {
|
||||||
|
await renderScriptError(req, res, page, error)
|
||||||
|
return {finished: true}
|
||||||
|
}
|
||||||
|
|
||||||
|
const errors = await this.getCompilationErrors(page)
|
||||||
|
if (errors.length > 0) {
|
||||||
|
await renderScriptError(req, res, page, errors[0])
|
||||||
|
return {finished: true}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
|
||||||
|
const {finished} = await handlePageBundleRequest(req, res, parsedUrl)
|
||||||
|
|
||||||
for (const fn of this.middlewares) {
|
for (const fn of this.middlewares) {
|
||||||
await new Promise((resolve, reject) => {
|
await new Promise((resolve, reject) => {
|
||||||
fn(req, res, (err) => {
|
fn(req, res, (err) => {
|
||||||
|
@ -81,6 +122,8 @@ export default class HotReloader {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return {finished}
|
||||||
}
|
}
|
||||||
|
|
||||||
async clean () {
|
async clean () {
|
||||||
|
@ -121,8 +164,8 @@ export default class HotReloader {
|
||||||
await this.clean()
|
await this.clean()
|
||||||
|
|
||||||
const configs = await Promise.all([
|
const configs = await Promise.all([
|
||||||
getBaseWebpackConfig(this.dir, { dev: true, isServer: false, config: this.config }),
|
getBaseWebpackConfig(this.dir, { dev: true, isServer: false, config: this.config, buildId: this.buildId }),
|
||||||
getBaseWebpackConfig(this.dir, { dev: true, isServer: true, config: this.config })
|
getBaseWebpackConfig(this.dir, { dev: true, isServer: true, config: this.config, buildId: this.buildId })
|
||||||
])
|
])
|
||||||
|
|
||||||
const compiler = webpack(configs)
|
const compiler = webpack(configs)
|
||||||
|
@ -158,7 +201,7 @@ export default class HotReloader {
|
||||||
|
|
||||||
// We only watch `_document` for changes on the server compilation
|
// We only watch `_document` for changes on the server compilation
|
||||||
// the rest of the files will be triggered by the client compilation
|
// the rest of the files will be triggered by the client compilation
|
||||||
const documentChunk = compilation.chunks.find(c => c.name === normalize('bundles/pages/_document.js'))
|
const documentChunk = compilation.chunks.find(c => c.name === normalize(`static/${this.buildId}/pages/_document.js`))
|
||||||
// If the document chunk can't be found we do nothing
|
// If the document chunk can't be found we do nothing
|
||||||
if (!documentChunk) {
|
if (!documentChunk) {
|
||||||
console.warn('_document.js chunk not found')
|
console.warn('_document.js chunk not found')
|
||||||
|
@ -209,7 +252,7 @@ export default class HotReloader {
|
||||||
// and to update error content
|
// and to update error content
|
||||||
const failed = failedChunkNames
|
const failed = failedChunkNames
|
||||||
|
|
||||||
const rootDir = join('bundles', 'pages')
|
const rootDir = join(CLIENT_STATIC_FILES_PATH, this.buildId, 'pages')
|
||||||
|
|
||||||
for (const n of new Set([...added, ...succeeded, ...removed, ...failed])) {
|
for (const n of new Set([...added, ...succeeded, ...removed, ...failed])) {
|
||||||
const route = toRoute(relative(rootDir, n))
|
const route = toRoute(relative(rootDir, n))
|
||||||
|
@ -274,6 +317,7 @@ export default class HotReloader {
|
||||||
|
|
||||||
const onDemandEntries = onDemandEntryHandler(webpackDevMiddleware, multiCompiler.compilers, {
|
const onDemandEntries = onDemandEntryHandler(webpackDevMiddleware, multiCompiler.compilers, {
|
||||||
dir: this.dir,
|
dir: this.dir,
|
||||||
|
buildId: this.buildId,
|
||||||
dev: true,
|
dev: true,
|
||||||
reload: this.reload.bind(this),
|
reload: this.reload.bind(this),
|
||||||
pageExtensions: this.config.pageExtensions,
|
pageExtensions: this.config.pageExtensions,
|
||||||
|
|
|
@ -4,18 +4,16 @@ import { parse as parseUrl } from 'url'
|
||||||
import { parse as parseQs } from 'querystring'
|
import { parse as parseQs } from 'querystring'
|
||||||
import fs from 'fs'
|
import fs from 'fs'
|
||||||
import http, { STATUS_CODES } from 'http'
|
import http, { STATUS_CODES } from 'http'
|
||||||
import promisify from '../lib/promisify'
|
|
||||||
import {
|
import {
|
||||||
renderToHTML,
|
renderToHTML,
|
||||||
renderErrorToHTML,
|
renderErrorToHTML,
|
||||||
sendHTML,
|
sendHTML,
|
||||||
serveStatic,
|
serveStatic
|
||||||
renderScriptError
|
|
||||||
} from './render'
|
} from './render'
|
||||||
import Router from './router'
|
import Router from './router'
|
||||||
import { isInternalUrl } from './utils'
|
import { isInternalUrl } from './utils'
|
||||||
import loadConfig from './config'
|
import loadConfig from './config'
|
||||||
import {PHASE_PRODUCTION_SERVER, PHASE_DEVELOPMENT_SERVER, BLOCKED_PAGES, BUILD_ID_FILE} from '../lib/constants'
|
import {PHASE_PRODUCTION_SERVER, PHASE_DEVELOPMENT_SERVER, BLOCKED_PAGES, BUILD_ID_FILE, CLIENT_STATIC_FILES_PATH} from '../lib/constants'
|
||||||
import * as asset from '../lib/asset'
|
import * as asset from '../lib/asset'
|
||||||
import * as envConfig from '../lib/runtime-config'
|
import * as envConfig from '../lib/runtime-config'
|
||||||
import { isResSent } from '../lib/utils'
|
import { isResSent } from '../lib/utils'
|
||||||
|
@ -23,8 +21,6 @@ import { isResSent } from '../lib/utils'
|
||||||
// We need to go up one more level since we are in the `dist` directory
|
// We need to go up one more level since we are in the `dist` directory
|
||||||
import pkg from '../../package'
|
import pkg from '../../package'
|
||||||
|
|
||||||
const access = promisify(fs.access)
|
|
||||||
|
|
||||||
export default class Server {
|
export default class Server {
|
||||||
constructor ({ dir = '.', dev = false, staticMarkup = false, quiet = false, conf = null } = {}) {
|
constructor ({ dir = '.', dev = false, staticMarkup = false, quiet = false, conf = null } = {}) {
|
||||||
this.dir = resolve(dir)
|
this.dir = resolve(dir)
|
||||||
|
@ -44,8 +40,8 @@ export default class Server {
|
||||||
console.error(`> Could not find a valid build in the '${this.distDir}' directory! Try building your app with 'next build' before starting the server.`)
|
console.error(`> Could not find a valid build in the '${this.distDir}' directory! Try building your app with 'next build' before starting the server.`)
|
||||||
process.exit(1)
|
process.exit(1)
|
||||||
}
|
}
|
||||||
this.buildId = !dev ? this.readBuildId() : '-'
|
this.buildId = this.readBuildId(dev)
|
||||||
this.hotReloader = dev ? this.getHotReloader(this.dir, { quiet, config: this.nextConfig, buildId: this.buildId }) : null
|
this.hotReloader = dev ? this.getHotReloader(this.dir, { config: this.nextConfig, buildId: this.buildId }) : null
|
||||||
this.renderOpts = {
|
this.renderOpts = {
|
||||||
dev,
|
dev,
|
||||||
staticMarkup,
|
staticMarkup,
|
||||||
|
@ -128,69 +124,19 @@ export default class Server {
|
||||||
|
|
||||||
async defineRoutes () {
|
async defineRoutes () {
|
||||||
const routes = {
|
const routes = {
|
||||||
'/_next/:buildId/page/:path*.js.map': async (req, res, params) => {
|
|
||||||
const paths = params.path || ['']
|
|
||||||
const page = `/${paths.join('/')}`
|
|
||||||
|
|
||||||
if (this.dev) {
|
|
||||||
try {
|
|
||||||
await this.hotReloader.ensurePage(page)
|
|
||||||
} catch (err) {
|
|
||||||
await this.render404(req, res)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const path = join(this.distDir, 'bundles', 'pages', `${page}.js.map`)
|
|
||||||
await serveStatic(req, res, path)
|
|
||||||
},
|
|
||||||
|
|
||||||
'/_next/:buildId/page/:path*.js': async (req, res, params) => {
|
|
||||||
const paths = params.path || ['']
|
|
||||||
const page = `/${paths.join('/')}`
|
|
||||||
|
|
||||||
if (!this.handleBuildId(params.buildId, res)) {
|
|
||||||
const error = new Error('INVALID_BUILD_ID')
|
|
||||||
return await renderScriptError(req, res, page, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.dev && page !== '/_error' && page !== '/_app') {
|
|
||||||
try {
|
|
||||||
await this.hotReloader.ensurePage(page)
|
|
||||||
} catch (error) {
|
|
||||||
return await renderScriptError(req, res, page, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
const compilationErr = await this.getCompilationError(page)
|
|
||||||
if (compilationErr) {
|
|
||||||
return await renderScriptError(req, res, page, compilationErr)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const p = join(this.distDir, 'bundles', 'pages', `${page}.js`)
|
|
||||||
|
|
||||||
// [production] If the page is not exists, we need to send a proper Next.js style 404
|
|
||||||
// Otherwise, it'll affect the multi-zones feature.
|
|
||||||
try {
|
|
||||||
await access(p, (fs.constants || fs).R_OK)
|
|
||||||
} catch (err) {
|
|
||||||
return await renderScriptError(req, res, page, { code: 'ENOENT' })
|
|
||||||
}
|
|
||||||
|
|
||||||
await this.serveStatic(req, res, p)
|
|
||||||
},
|
|
||||||
|
|
||||||
'/_next/static/:path*': async (req, res, params) => {
|
'/_next/static/:path*': async (req, res, params) => {
|
||||||
// The commons folder holds commonschunk files
|
// The commons folder holds commonschunk files
|
||||||
// The chunks folder holds dynamic entries
|
// The chunks folder holds dynamic entries
|
||||||
|
// The buildId folder holds pages and potentially other assets. As buildId changes per build it can be long-term cached.
|
||||||
// In development they don't have a hash, and shouldn't be cached by the browser.
|
// In development they don't have a hash, and shouldn't be cached by the browser.
|
||||||
if (params.path[0] === 'commons' || params.path[0] === 'chunks') {
|
if (params.path[0] === 'commons' || params.path[0] === 'chunks' || params.path[0] === this.buildId) {
|
||||||
if (this.dev) {
|
if (this.dev) {
|
||||||
res.setHeader('Cache-Control', 'no-store, must-revalidate')
|
res.setHeader('Cache-Control', 'no-store, must-revalidate')
|
||||||
} else {
|
} else {
|
||||||
res.setHeader('Cache-Control', 'public, max-age=31536000, immutable')
|
res.setHeader('Cache-Control', 'public, max-age=31536000, immutable')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const p = join(this.distDir, 'static', ...(params.path || []))
|
const p = join(this.distDir, CLIENT_STATIC_FILES_PATH, ...(params.path || []))
|
||||||
await this.serveStatic(req, res, p)
|
await this.serveStatic(req, res, p)
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -269,7 +215,10 @@ export default class Server {
|
||||||
|
|
||||||
async run (req, res, parsedUrl) {
|
async run (req, res, parsedUrl) {
|
||||||
if (this.hotReloader) {
|
if (this.hotReloader) {
|
||||||
await this.hotReloader.run(req, res)
|
const {finished} = await this.hotReloader.run(req, res, parsedUrl)
|
||||||
|
if (finished) {
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const fn = this.router.match(req, res, parsedUrl)
|
const fn = this.router.match(req, res, parsedUrl)
|
||||||
|
@ -392,7 +341,10 @@ export default class Server {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
readBuildId () {
|
readBuildId (dev) {
|
||||||
|
if (dev) {
|
||||||
|
return 'development'
|
||||||
|
}
|
||||||
const buildIdPath = join(this.distDir, BUILD_ID_FILE)
|
const buildIdPath = join(this.distDir, BUILD_ID_FILE)
|
||||||
const buildId = fs.readFileSync(buildIdPath, 'utf8')
|
const buildId = fs.readFileSync(buildIdPath, 'utf8')
|
||||||
return buildId.trim()
|
return buildId.trim()
|
||||||
|
|
|
@ -17,6 +17,7 @@ const glob = promisify(globModule)
|
||||||
const access = promisify(fs.access)
|
const access = promisify(fs.access)
|
||||||
|
|
||||||
export default function onDemandEntryHandler (devMiddleware, compilers, {
|
export default function onDemandEntryHandler (devMiddleware, compilers, {
|
||||||
|
buildId,
|
||||||
dir,
|
dir,
|
||||||
dev,
|
dev,
|
||||||
reload,
|
reload,
|
||||||
|
@ -163,7 +164,7 @@ export default function onDemandEntryHandler (devMiddleware, compilers, {
|
||||||
|
|
||||||
const pathname = join(dir, relativePathToPage)
|
const pathname = join(dir, relativePathToPage)
|
||||||
|
|
||||||
const {name, files} = createEntry(relativePathToPage, {pageExtensions: extensions})
|
const {name, files} = createEntry(relativePathToPage, {buildId, pageExtensions: extensions})
|
||||||
|
|
||||||
await new Promise((resolve, reject) => {
|
await new Promise((resolve, reject) => {
|
||||||
const entryInfo = entries[page]
|
const entryInfo = entries[page]
|
||||||
|
|
|
@ -10,10 +10,10 @@ import { loadGetInitialProps, isResSent } from '../lib/utils'
|
||||||
import Head, { defaultHead } from '../lib/head'
|
import Head, { defaultHead } from '../lib/head'
|
||||||
import ErrorDebug from '../lib/error-debug'
|
import ErrorDebug from '../lib/error-debug'
|
||||||
import Loadable from 'react-loadable'
|
import Loadable from 'react-loadable'
|
||||||
import { BUILD_MANIFEST, REACT_LOADABLE_MANIFEST, SERVER_DIRECTORY } from '../lib/constants'
|
import { BUILD_MANIFEST, REACT_LOADABLE_MANIFEST, SERVER_DIRECTORY, CLIENT_STATIC_FILES_PATH } from '../lib/constants'
|
||||||
|
|
||||||
// Based on https://github.com/jamiebuilds/react-loadable/pull/132
|
// Based on https://github.com/jamiebuilds/react-loadable/pull/132
|
||||||
function getBundles (manifest, moduleIds) {
|
function getDynamicImportBundles (manifest, moduleIds) {
|
||||||
return moduleIds.reduce((bundles, moduleId) => {
|
return moduleIds.reduce((bundles, moduleId) => {
|
||||||
if (typeof manifest[moduleId] === 'undefined') {
|
if (typeof manifest[moduleId] === 'undefined') {
|
||||||
return bundles
|
return bundles
|
||||||
|
@ -62,8 +62,8 @@ async function doRender (req, res, pathname, query, {
|
||||||
await ensurePage(page, { dir, hotReloader })
|
await ensurePage(page, { dir, hotReloader })
|
||||||
}
|
}
|
||||||
|
|
||||||
const documentPath = join(distDir, SERVER_DIRECTORY, 'bundles', 'pages', '_document')
|
const documentPath = join(distDir, SERVER_DIRECTORY, CLIENT_STATIC_FILES_PATH, buildId, 'pages', '_document')
|
||||||
const appPath = join(distDir, SERVER_DIRECTORY, 'bundles', 'pages', '_app')
|
const appPath = join(distDir, SERVER_DIRECTORY, CLIENT_STATIC_FILES_PATH, buildId, 'pages', '_app')
|
||||||
let [buildManifest, reactLoadableManifest, Component, Document, App] = await Promise.all([
|
let [buildManifest, reactLoadableManifest, Component, Document, App] = await Promise.all([
|
||||||
require(join(distDir, BUILD_MANIFEST)),
|
require(join(distDir, BUILD_MANIFEST)),
|
||||||
require(join(distDir, REACT_LOADABLE_MANIFEST)),
|
require(join(distDir, REACT_LOADABLE_MANIFEST)),
|
||||||
|
@ -144,7 +144,7 @@ async function doRender (req, res, pathname, query, {
|
||||||
}
|
}
|
||||||
|
|
||||||
const docProps = await loadGetInitialProps(Document, { ...ctx, renderPage })
|
const docProps = await loadGetInitialProps(Document, { ...ctx, renderPage })
|
||||||
const dynamicImports = getBundles(reactLoadableManifest, reactLoadableModules)
|
const dynamicImports = getDynamicImportBundles(reactLoadableManifest, reactLoadableModules)
|
||||||
|
|
||||||
if (isResSent(res)) return
|
if (isResSent(res)) return
|
||||||
|
|
||||||
|
|
|
@ -30,22 +30,22 @@ describe('On Demand Entries', () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should compile pages for JSON page requests', async () => {
|
it('should compile pages for JSON page requests', async () => {
|
||||||
const pageContent = await renderViaHTTP(context.appPort, '/_next/-/page/about.js')
|
const pageContent = await renderViaHTTP(context.appPort, '/_next/static/development/pages/about.js')
|
||||||
expect(pageContent.includes('About Page')).toBeTruthy()
|
expect(pageContent.includes('About Page')).toBeTruthy()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should dispose inactive pages', async () => {
|
it('should dispose inactive pages', async () => {
|
||||||
const indexPagePath = resolve(__dirname, '../.next/bundles/pages/index.js')
|
const indexPagePath = resolve(__dirname, '../.next/static/development/pages/index.js')
|
||||||
expect(existsSync(indexPagePath)).toBeTruthy()
|
expect(existsSync(indexPagePath)).toBeTruthy()
|
||||||
|
|
||||||
// Render two pages after the index, since the server keeps at least two pages
|
// Render two pages after the index, since the server keeps at least two pages
|
||||||
await renderViaHTTP(context.appPort, '/about')
|
await renderViaHTTP(context.appPort, '/about')
|
||||||
await renderViaHTTP(context.appPort, '/_next/on-demand-entries-ping', {page: '/about'})
|
await renderViaHTTP(context.appPort, '/_next/on-demand-entries-ping', {page: '/about'})
|
||||||
const aboutPagePath = resolve(__dirname, '../.next/bundles/pages/about.js')
|
const aboutPagePath = resolve(__dirname, '../.next/static/development/pages/about.js')
|
||||||
|
|
||||||
await renderViaHTTP(context.appPort, '/third')
|
await renderViaHTTP(context.appPort, '/third')
|
||||||
await renderViaHTTP(context.appPort, '/_next/on-demand-entries-ping', {page: '/third'})
|
await renderViaHTTP(context.appPort, '/_next/on-demand-entries-ping', {page: '/third'})
|
||||||
const thirdPagePath = resolve(__dirname, '../.next/bundles/pages/third.js')
|
const thirdPagePath = resolve(__dirname, '../.next/static/development/pages/third.js')
|
||||||
|
|
||||||
// Wait maximum of jasmine.DEFAULT_TIMEOUT_INTERVAL checking
|
// Wait maximum of jasmine.DEFAULT_TIMEOUT_INTERVAL checking
|
||||||
// for disposing /about
|
// for disposing /about
|
||||||
|
|
|
@ -63,7 +63,7 @@ describe('Production Usage', () => {
|
||||||
const resources = []
|
const resources = []
|
||||||
|
|
||||||
// test a regular page
|
// test a regular page
|
||||||
resources.push(`${url}${buildId}/page/index.js`)
|
resources.push(`${url}static/${buildId}/pages/index.js`)
|
||||||
|
|
||||||
// test dynamic chunk
|
// test dynamic chunk
|
||||||
resources.push(url + reactLoadableManifest['../../components/hello1'][0].publicPath)
|
resources.push(url + reactLoadableManifest['../../components/hello1'][0].publicPath)
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"/index": "bundles/pages/index.js",
|
"/index": "static/development/pages/index.js",
|
||||||
"/world": "bundles/pages/world.js",
|
"/world": "static/development/pages/world.js",
|
||||||
"/_error": "bundles/pages/_error.js",
|
"/_error": "static/development/pages/_error.js",
|
||||||
"/non-existent-child": "bundles/pages/non-existent-child.js"
|
"/non-existent-child": "static/development/pages/non-existent-child.js"
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
/* global describe, it, expect */
|
/* global describe, it, expect */
|
||||||
|
|
||||||
import { join } from 'path'
|
import { join } from 'path'
|
||||||
import {SERVER_DIRECTORY} from 'next/constants'
|
import {SERVER_DIRECTORY, CLIENT_STATIC_FILES_PATH} from 'next/constants'
|
||||||
import requirePage, {getPagePath, normalizePagePath, pageNotFoundError} from '../../dist/server/require'
|
import requirePage, {getPagePath, normalizePagePath, pageNotFoundError} from '../../dist/server/require'
|
||||||
|
|
||||||
const sep = '/'
|
const sep = '/'
|
||||||
const distDir = join(__dirname, '_resolvedata')
|
const distDir = join(__dirname, '_resolvedata')
|
||||||
const pathToBundles = join(distDir, SERVER_DIRECTORY, 'bundles', 'pages')
|
const pathToBundles = join(distDir, SERVER_DIRECTORY, CLIENT_STATIC_FILES_PATH, 'development', 'pages')
|
||||||
|
|
||||||
describe('pageNotFoundError', () => {
|
describe('pageNotFoundError', () => {
|
||||||
it('Should throw error with ENOENT code', () => {
|
it('Should throw error with ENOENT code', () => {
|
||||||
|
|
|
@ -3,46 +3,54 @@
|
||||||
import {normalize, join} from 'path'
|
import {normalize, join} from 'path'
|
||||||
import {getPageEntries, createEntry} from '../../dist/build/webpack/utils'
|
import {getPageEntries, createEntry} from '../../dist/build/webpack/utils'
|
||||||
|
|
||||||
|
const buildId = 'development'
|
||||||
|
|
||||||
describe('createEntry', () => {
|
describe('createEntry', () => {
|
||||||
it('Should turn a path into a page entry', () => {
|
it('Should turn a path into a page entry', () => {
|
||||||
const entry = createEntry('pages/index.js')
|
const entry = createEntry('pages/index.js')
|
||||||
expect(entry.name).toBe(normalize('bundles/pages/index.js'))
|
expect(entry.name).toBe(normalize(`static/pages/index.js`))
|
||||||
expect(entry.files[0]).toBe('./pages/index.js')
|
expect(entry.files[0]).toBe('./pages/index.js')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Should have a custom name', () => {
|
it('Should have a custom name', () => {
|
||||||
const entry = createEntry('pages/index.js', {name: 'something-else.js'})
|
const entry = createEntry('pages/index.js', {name: 'something-else.js'})
|
||||||
expect(entry.name).toBe(normalize('bundles/something-else.js'))
|
expect(entry.name).toBe(normalize(`static/something-else.js`))
|
||||||
expect(entry.files[0]).toBe('./pages/index.js')
|
expect(entry.files[0]).toBe('./pages/index.js')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Should allow custom extension like .ts to be turned into .js', () => {
|
it('Should allow custom extension like .ts to be turned into .js', () => {
|
||||||
const entry = createEntry('pages/index.ts', {pageExtensions: ['js', 'ts'].join('|')})
|
const entry = createEntry('pages/index.ts', {pageExtensions: ['js', 'ts'].join('|')})
|
||||||
expect(entry.name).toBe(normalize('bundles/pages/index.js'))
|
expect(entry.name).toBe(normalize('static/pages/index.js'))
|
||||||
expect(entry.files[0]).toBe('./pages/index.ts')
|
expect(entry.files[0]).toBe('./pages/index.ts')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Should allow custom extension like .jsx to be turned into .js', () => {
|
it('Should allow custom extension like .jsx to be turned into .js', () => {
|
||||||
const entry = createEntry('pages/index.jsx', {pageExtensions: ['jsx', 'js'].join('|')})
|
const entry = createEntry('pages/index.jsx', {pageExtensions: ['jsx', 'js'].join('|')})
|
||||||
expect(entry.name).toBe(normalize('bundles/pages/index.js'))
|
expect(entry.name).toBe(normalize('static/pages/index.js'))
|
||||||
expect(entry.files[0]).toBe('./pages/index.jsx')
|
expect(entry.files[0]).toBe('./pages/index.jsx')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Should allow custom extension like .tsx to be turned into .js', () => {
|
it('Should allow custom extension like .tsx to be turned into .js', () => {
|
||||||
const entry = createEntry('pages/index.tsx', {pageExtensions: ['tsx', 'ts'].join('|')})
|
const entry = createEntry('pages/index.tsx', {pageExtensions: ['tsx', 'ts'].join('|')})
|
||||||
expect(entry.name).toBe(normalize('bundles/pages/index.js'))
|
expect(entry.name).toBe(normalize('static/pages/index.js'))
|
||||||
expect(entry.files[0]).toBe('./pages/index.tsx')
|
expect(entry.files[0]).toBe('./pages/index.tsx')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Should allow custom extension like .tsx to be turned into .js with another order', () => {
|
it('Should allow custom extension like .tsx to be turned into .js with another order', () => {
|
||||||
const entry = createEntry('pages/index.tsx', {pageExtensions: ['ts', 'tsx'].join('|')})
|
const entry = createEntry('pages/index.tsx', {pageExtensions: ['ts', 'tsx'].join('|')})
|
||||||
expect(entry.name).toBe(normalize('bundles/pages/index.js'))
|
expect(entry.name).toBe(normalize('static/pages/index.js'))
|
||||||
expect(entry.files[0]).toBe('./pages/index.tsx')
|
expect(entry.files[0]).toBe('./pages/index.tsx')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Should turn pages/blog/index.js into pages/blog.js', () => {
|
it('Should turn pages/blog/index.js into pages/blog.js', () => {
|
||||||
const entry = createEntry('pages/blog/index.js')
|
const entry = createEntry('pages/blog/index.js')
|
||||||
expect(entry.name).toBe(normalize('bundles/pages/blog.js'))
|
expect(entry.name).toBe(normalize('static/pages/blog.js'))
|
||||||
|
expect(entry.files[0]).toBe('./pages/blog/index.js')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('Should add buildId when provided', () => {
|
||||||
|
const entry = createEntry('pages/blog/index.js', {buildId})
|
||||||
|
expect(entry.name).toBe(normalize(`static/${buildId}/pages/blog.js`))
|
||||||
expect(entry.files[0]).toBe('./pages/blog/index.js')
|
expect(entry.files[0]).toBe('./pages/blog/index.js')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -53,30 +61,30 @@ describe('getPageEntries', () => {
|
||||||
it('Should return paths', () => {
|
it('Should return paths', () => {
|
||||||
const pagePaths = ['pages/index.js']
|
const pagePaths = ['pages/index.js']
|
||||||
const pageEntries = getPageEntries(pagePaths, {nextPagesDir})
|
const pageEntries = getPageEntries(pagePaths, {nextPagesDir})
|
||||||
expect(pageEntries[normalize('bundles/pages/index.js')][0]).toBe('./pages/index.js')
|
expect(pageEntries[normalize('static/pages/index.js')][0]).toBe('./pages/index.js')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Should include default _error', () => {
|
it('Should include default _error', () => {
|
||||||
const pagePaths = ['pages/index.js']
|
const pagePaths = ['pages/index.js']
|
||||||
const pageEntries = getPageEntries(pagePaths, {nextPagesDir})
|
const pageEntries = getPageEntries(pagePaths, {nextPagesDir})
|
||||||
expect(pageEntries[normalize('bundles/pages/_error.js')][0]).toMatch(/dist[/\\]pages[/\\]_error\.js/)
|
expect(pageEntries[normalize('static/pages/_error.js')][0]).toMatch(/dist[/\\]pages[/\\]_error\.js/)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Should not include default _error when _error.js is inside the pages directory', () => {
|
it('Should not include default _error when _error.js is inside the pages directory', () => {
|
||||||
const pagePaths = ['pages/index.js', 'pages/_error.js']
|
const pagePaths = ['pages/index.js', 'pages/_error.js']
|
||||||
const pageEntries = getPageEntries(pagePaths, {nextPagesDir})
|
const pageEntries = getPageEntries(pagePaths, {nextPagesDir})
|
||||||
expect(pageEntries[normalize('bundles/pages/_error.js')][0]).toBe('./pages/_error.js')
|
expect(pageEntries[normalize('static/pages/_error.js')][0]).toBe('./pages/_error.js')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Should include default _document when isServer is true', () => {
|
it('Should include default _document when isServer is true', () => {
|
||||||
const pagePaths = ['pages/index.js']
|
const pagePaths = ['pages/index.js']
|
||||||
const pageEntries = getPageEntries(pagePaths, {nextPagesDir, isServer: true})
|
const pageEntries = getPageEntries(pagePaths, {nextPagesDir, isServer: true})
|
||||||
expect(pageEntries[normalize('bundles/pages/_document.js')][0]).toMatch(/dist[/\\]pages[/\\]_document\.js/)
|
expect(pageEntries[normalize('static/pages/_document.js')][0]).toMatch(/dist[/\\]pages[/\\]_document\.js/)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Should not include default _document when _document.js is inside the pages directory', () => {
|
it('Should not include default _document when _document.js is inside the pages directory', () => {
|
||||||
const pagePaths = ['pages/index.js', 'pages/_document.js']
|
const pagePaths = ['pages/index.js', 'pages/_document.js']
|
||||||
const pageEntries = getPageEntries(pagePaths, {nextPagesDir, isServer: true})
|
const pageEntries = getPageEntries(pagePaths, {nextPagesDir, isServer: true})
|
||||||
expect(pageEntries[normalize('bundles/pages/_document.js')][0]).toBe('./pages/_document.js')
|
expect(pageEntries[normalize('static/pages/_document.js')][0]).toBe('./pages/_document.js')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
10
yarn.lock
10
yarn.lock
|
@ -3352,10 +3352,6 @@ foreach@^2.0.5:
|
||||||
version "2.0.5"
|
version "2.0.5"
|
||||||
resolved "https://registry.yarnpkg.com/foreach/-/foreach-2.0.5.tgz#0bee005018aeb260d0a3af3ae658dd0136ec1b99"
|
resolved "https://registry.yarnpkg.com/foreach/-/foreach-2.0.5.tgz#0bee005018aeb260d0a3af3ae658dd0136ec1b99"
|
||||||
|
|
||||||
foreachasync@^3.0.0:
|
|
||||||
version "3.0.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/foreachasync/-/foreachasync-3.0.0.tgz#5502987dc8714be3392097f32e0071c9dee07cf6"
|
|
||||||
|
|
||||||
foreground-child@^1.5.3, foreground-child@^1.5.6:
|
foreground-child@^1.5.3, foreground-child@^1.5.6:
|
||||||
version "1.5.6"
|
version "1.5.6"
|
||||||
resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-1.5.6.tgz#4fd71ad2dfde96789b980a5c0a295937cb2f5ce9"
|
resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-1.5.6.tgz#4fd71ad2dfde96789b980a5c0a295937cb2f5ce9"
|
||||||
|
@ -7651,12 +7647,6 @@ vm-browserify@0.0.4:
|
||||||
dependencies:
|
dependencies:
|
||||||
indexof "0.0.1"
|
indexof "0.0.1"
|
||||||
|
|
||||||
walk@2.3.9:
|
|
||||||
version "2.3.9"
|
|
||||||
resolved "https://registry.yarnpkg.com/walk/-/walk-2.3.9.tgz#31b4db6678f2ae01c39ea9fb8725a9031e558a7b"
|
|
||||||
dependencies:
|
|
||||||
foreachasync "^3.0.0"
|
|
||||||
|
|
||||||
walkdir@^0.0.11:
|
walkdir@^0.0.11:
|
||||||
version "0.0.11"
|
version "0.0.11"
|
||||||
resolved "https://registry.yarnpkg.com/walkdir/-/walkdir-0.0.11.tgz#a16d025eb931bd03b52f308caed0f40fcebe9532"
|
resolved "https://registry.yarnpkg.com/walkdir/-/walkdir-0.0.11.tgz#a16d025eb931bd03b52f308caed0f40fcebe9532"
|
||||||
|
|
Loading…
Reference in a new issue