Skip to content
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

Replace pages-plugin with loader #5994

Merged
merged 26 commits into from
Jan 8, 2019
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
f4c6dde
Remove unused argument
timneutkens Jan 5, 2019
38cf34d
Replace pages-plugin with loader
timneutkens Jan 5, 2019
63e6a5e
Add loader-utils types
timneutkens Jan 5, 2019
8036af1
Remove logs
timneutkens Jan 5, 2019
a343679
Bring back previous deposal behavior
timneutkens Jan 5, 2019
71eaed0
Remove console.log
timneutkens Jan 5, 2019
555cf9e
Remove webpack/utils as it’s no longer in use
timneutkens Jan 5, 2019
3ec971a
Remove hot-self-accept-loader
timneutkens Jan 6, 2019
85b19c8
Error Recovery tests
timneutkens Jan 7, 2019
cb0590c
Make hotSelfAccept a noop default loader
timneutkens Jan 7, 2019
b82fbfb
Fix windows deleted/added
timneutkens Jan 7, 2019
3a8888e
Remove logging
timneutkens Jan 7, 2019
4ef30fa
Remove unused variables
timneutkens Jan 7, 2019
4ed7b69
Remove log
timneutkens Jan 7, 2019
27889aa
Simplify entrypoint generation
timneutkens Jan 7, 2019
9bd040c
Don’t return the function
timneutkens Jan 7, 2019
143aab1
Fix _app test
timneutkens Jan 7, 2019
6303cba
Remove code that’s always true
timneutkens Jan 8, 2019
89dec37
Move aliases to constants
timneutkens Jan 8, 2019
025422d
Use alias
timneutkens Jan 8, 2019
418ee0c
Join pages alias in reduce
timneutkens Jan 8, 2019
a013927
Default pages differently
timneutkens Jan 8, 2019
6fdeba9
Loop over pages instead of manually defining
timneutkens Jan 8, 2019
fa58135
Move entry generation into common function
timneutkens Jan 8, 2019
a7f0076
Update packages/next/build/webpack/loaders/next-client-pages-loader.ts
lucleray Jan 8, 2019
e902c55
Update packages/next/build/webpack/loaders/next-client-pages-loader.ts
timneutkens Jan 8, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
42 changes: 30 additions & 12 deletions packages/next/build/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,17 +37,19 @@ export default async function build (dir: string, conf = null): Promise<void> {
return result
}, {})

let entrypoints
// Because on Windows absolute paths in the generated code can break because of numbers, eg 1 in the path,
// we have to use a private alias
const pagesDirAlias = 'private-next-pages'
const dotNextDirAlias = 'private-dot-next'

const absoluteAppPath = pages['/_app'] ? join(pagesDirAlias, pages['/_app']).replace(/\\/g, '/') : 'next/dist/pages/_app'
const absoluteDocumentPath = pages['/_document'] ? join(pagesDirAlias, pages['/_document']).replace(/\\/g, '/') : 'next/dist/pages/_document'
const absoluteErrorPath = pages['/_error'] ? join(pagesDirAlias, pages['/_error']).replace(/\\/g, '/') : 'next/dist/pages/_error'

let serverEntrypoints

if (config.target === 'serverless') {
const serverlessEntrypoints: any = {}
// Because on Windows absolute paths in the generated code can break because of numbers, eg 1 in the path,
// we have to use a private alias
const pagesDirAlias = 'private-next-pages'
const dotNextDirAlias = 'private-dot-next'
const absoluteAppPath = pages['/_app'] ? join(pagesDirAlias, pages['/_app']).replace(/\\/g, '/') : 'next/dist/pages/_app'
const absoluteDocumentPath = pages['/_document'] ? join(pagesDirAlias, pages['/_document']).replace(/\\/g, '/') : 'next/dist/pages/_document'
const absoluteErrorPath = pages['/_error'] ? join(pagesDirAlias, pages['/_error']).replace(/\\/g, '/') : 'next/dist/pages/_error'

const defaultOptions = {
absoluteAppPath,
absoluteDocumentPath,
Expand Down Expand Up @@ -75,12 +77,28 @@ export default async function build (dir: string, conf = null): Promise<void> {
serverlessEntrypoints[errorPage] = `next-serverless-loader?${stringify(serverlessLoaderOptions)}!`
}

entrypoints = serverlessEntrypoints
serverEntrypoints = serverlessEntrypoints
}

const clientEntrypoints: any = {}

Object.keys(pages).forEach(async (page) => {
timneutkens marked this conversation as resolved.
Show resolved Hide resolved
if (page === '/_app' || page === '/_document' || page === '/_error') {
return
}

const absolutePagePath = join(pagesDirAlias, pages[page]).replace(/\\/g, '/')
const bundleFile = page === '/' ? '/index.js' : `${page}.js`
const clientPagesLoaderOptions = {page, absolutePagePath}
clientEntrypoints[join('static', buildId, 'pages', bundleFile)] = `next-client-pages-loader?${stringify(clientPagesLoaderOptions)}!`
})

clientEntrypoints[join('static', buildId, 'pages', '/_app.js')] = `next-client-pages-loader?${stringify({page: '/_app', absolutePagePath: absoluteAppPath})}!`
clientEntrypoints[join('static', buildId, 'pages', '/_error.js')] = `next-client-pages-loader?${stringify({page: '/_error', absolutePagePath: absoluteErrorPath})}!`

const configs: any = await Promise.all([
getBaseWebpackConfig(dir, { buildId, isServer: false, config, target: config.target }),
getBaseWebpackConfig(dir, { buildId, isServer: true, config, target: config.target, entrypoints })
getBaseWebpackConfig(dir, { buildId, isServer: false, config, target: config.target, entrypoints: clientEntrypoints }),
getBaseWebpackConfig(dir, { buildId, isServer: true, config, target: config.target, entrypoints: serverEntrypoints })
])

let result: CompilerResult = {warnings: [], errors: []}
Expand Down
12 changes: 9 additions & 3 deletions packages/next/build/webpack-config.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import CaseSensitivePathPlugin from 'case-sensitive-paths-webpack-plugin'
import FriendlyErrorsWebpackPlugin from 'friendly-errors-webpack-plugin'
import WebpackBar from 'webpackbar'
import {getPages} from './webpack/utils'
import PagesPlugin from './webpack/plugins/pages-plugin'
import NextJsSsrImportPlugin from './webpack/plugins/nextjs-ssr-import'
import NextJsSSRModuleCachePlugin from './webpack/plugins/nextjs-ssr-module-cache'
import NextJsRequireCacheHotReloader from './webpack/plugins/nextjs-require-cache-hot-reloader'
Expand Down Expand Up @@ -205,8 +204,16 @@ export default async function getBaseWebpackConfig (dir, {dev = false, isServer
context: dir,
// Kept as function to be backwards compatible
entry: async () => {
console.log({
entrypoints,
clientEntries,
pagesEntries
})
if (entrypoints) {
return entrypoints
return {
...clientEntries,
...entrypoints
}
}
return {
...clientEntries,
Expand Down Expand Up @@ -309,7 +316,6 @@ export default async function getBaseWebpackConfig (dir, {dev = false, isServer
}),
target !== 'serverless' && isServer && new PagesManifestPlugin(),
!isServer && new BuildManifestPlugin(),
!isServer && new PagesPlugin(),
isServer && new NextJsSsrImportPlugin(),
target !== 'serverless' && isServer && new NextJsSSRModuleCachePlugin({outputPath}),
!isServer && !dev && new AssetsSizePlugin({buildId, distDir})
Expand Down
19 changes: 19 additions & 0 deletions packages/next/build/webpack/loaders/next-client-pages-loader.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import {loader} from 'webpack'
import loaderUtils from 'loader-utils'

export type ClientPagesLoaderOptions = {
absolutePagePath: string,
page: string
}

const nextServerlessLoader: loader.Loader = function () {
timneutkens marked this conversation as resolved.
Show resolved Hide resolved
timneutkens marked this conversation as resolved.
Show resolved Hide resolved
const {absolutePagePath, page}: any = loaderUtils.getOptions(this)
return `
(window.__NEXT_P=window.__NEXT_P||[]).push(['${page}', function() {
const page = require('${absolutePagePath}')
return { page: page.default || page }
}]);
`
}

export default nextServerlessLoader
timneutkens marked this conversation as resolved.
Show resolved Hide resolved
53 changes: 0 additions & 53 deletions packages/next/build/webpack/plugins/pages-plugin.js

This file was deleted.

62 changes: 51 additions & 11 deletions packages/next/server/hot-reloader.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ import WebSocket from 'ws'
import getBaseWebpackConfig from '../build/webpack-config'
import {IS_BUNDLED_PAGE_REGEX, ROUTE_NAME_REGEX, BLOCKED_PAGES, CLIENT_STATIC_FILES_PATH} from 'next-server/constants'
import {route} from 'next-server/dist/server/router'
import globModule from 'glob'
import {promisify} from 'util'
import {stringify} from 'querystring'
const glob = promisify(globModule)

export async function renderScriptError (res, error) {
// Asks CDNs and others to not to cache the errored page
Expand Down Expand Up @@ -169,6 +173,36 @@ export default class HotReloader {
}))
}

async getWebpackConfig () {
const extensions = this.config.pageExtensions.join('|')
const pagePaths = await glob(`pages/+(_app|_document|_error).+(${extensions})`, {cwd: this.dir})
const pages = pagePaths.reduce((result, pagePath) => {
let page = `/${pagePath.replace(new RegExp(`\\.+(${extensions})$`), '').replace(/\\/g, '/')}`.replace(/\/index$/, '')
page = page === '' ? '/' : page
result[page] = pagePath
return result
}, {})
const pagesDirAlias = 'private-next-pages'
const absoluteAppPath = pages['/_app'] ? join(pagesDirAlias, pages['/_app']).replace(/\\/g, '/') : 'next/dist/pages/_app'
const absoluteErrorPath = pages['/_error'] ? join(pagesDirAlias, pages['/_error']).replace(/\\/g, '/') : 'next/dist/pages/_error'
const absoluteDocumentPath = pages['/_document'] ? join(pagesDirAlias, pages['/_document']).replace(/\\/g, '/') : 'next/dist/pages/_document'
const clientEntrypoints = {}
clientEntrypoints[join('static', this.buildId, 'pages', '/_app.js')] = `next-client-pages-loader?${stringify({page: '/_app', absolutePagePath: absoluteAppPath})}!`
clientEntrypoints[join('static', this.buildId, 'pages', '/_error.js')] = `next-client-pages-loader?${stringify({page: '/_error', absolutePagePath: absoluteErrorPath})}!`

const serverEntryPoints = {}
serverEntryPoints[join('static', this.buildId, 'pages', '/_app.js')] = [absoluteAppPath]
serverEntryPoints[join('static', this.buildId, 'pages', '/_error.js')] = [absoluteErrorPath]
serverEntryPoints[join('static', this.buildId, 'pages', '/_document.js')] = [absoluteDocumentPath]

console.log({clientEntrypoints, serverEntryPoints})

return Promise.all([
getBaseWebpackConfig(this.dir, { dev: true, isServer: false, config: this.config, buildId: this.buildId, entrypoints: clientEntrypoints }),
getBaseWebpackConfig(this.dir, { dev: true, isServer: true, config: this.config, buildId: this.buildId, entrypoints: serverEntryPoints })
])
}

async start () {
await this.clean()

Expand All @@ -188,10 +222,7 @@ export default class HotReloader {
})
})

const configs = await Promise.all([
getBaseWebpackConfig(this.dir, { dev: true, isServer: false, config: this.config, buildId: this.buildId }),
getBaseWebpackConfig(this.dir, { dev: true, isServer: true, config: this.config, buildId: this.buildId })
])
const configs = await this.getWebpackConfig()
this.addWsPort(configs)

const multiCompiler = webpack(configs)
Expand Down Expand Up @@ -220,10 +251,7 @@ export default class HotReloader {

await this.clean()

const configs = await Promise.all([
getBaseWebpackConfig(this.dir, { dev: true, isServer: false, config: this.config, buildId: this.buildId }),
getBaseWebpackConfig(this.dir, { dev: true, isServer: true, config: this.config, buildId: this.buildId })
])
const configs = await this.getWebpackConfig()
this.addWsPort(configs)

const compiler = webpack(configs)
Expand Down Expand Up @@ -306,6 +334,7 @@ export default class HotReloader {
// e.g, pages/index.js <-> pages/_error.js
const added = diff(chunkNames, this.prevChunkNames)
const removed = diff(this.prevChunkNames, chunkNames)
console.log('REMOVED', removed)
const succeeded = diff(this.prevFailedChunkNames, failedChunkNames)

// reload all failed chunks to replace the templace to the error ones,
Expand All @@ -314,11 +343,21 @@ export default class HotReloader {

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, ...failed])) {
const route = toRoute(relative(rootDir, n))
this.send('reload', route)
}

const disposedRoutes = new Set()

if (removed.size !== 0) {
for (const n of removed) {
const route = toRoute(relative(rootDir, n))
this.send('dispose', route)
disposedRoutes.add(route)
}
}

let changedPageRoutes = []

for (const [n, hash] of chunkHashes) {
Expand All @@ -327,12 +366,14 @@ export default class HotReloader {

const route = toRoute(relative(rootDir, n))

if (disposedRoutes.get(route)) continue

changedPageRoutes.push(route)
}

// This means `/_app` is most likely included in the list, or a page was added/deleted in this compilation run.
// This means we should filter out `/_app` because `/_app` will be re-rendered with the page reload.
if (added.size !== 0 || removed.size !== 0 || changedPageRoutes.length > 1) {
if (added.size !== 0 || changedPageRoutes.length > 1) {
changedPageRoutes = changedPageRoutes.filter((route) => route !== '/_app' && route !== '/_document')
}

Expand Down Expand Up @@ -381,7 +422,6 @@ export default class HotReloader {
const onDemandEntries = onDemandEntryHandler(webpackDevMiddleware, multiCompiler, {
dir: this.dir,
buildId: this.buildId,
dev: true,
reload: this.reload.bind(this),
pageExtensions: this.config.pageExtensions,
wsPort: this.wsPort,
Expand Down
40 changes: 19 additions & 21 deletions packages/next/server/on-demand-entry-handler.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import promisify from '../lib/promisify'
import globModule from 'glob'
import {pageNotFoundError} from 'next-server/dist/server/require'
import {normalizePagePath} from 'next-server/dist/server/normalize-page-path'
import {createEntry} from '../build/webpack/utils'
import { ROUTE_NAME_REGEX, IS_BUNDLED_PAGE_REGEX } from 'next-server/constants'
import {stringify} from 'querystring'

const ADDED = Symbol('added')
const BUILDING = Symbol('building')
Expand All @@ -30,7 +30,6 @@ function addEntry (compilation, context, name, entry) {
export default function onDemandEntryHandler (devMiddleware, multiCompiler, {
buildId,
dir,
dev,
reload,
pageExtensions,
maxInactiveAge,
Expand All @@ -51,20 +50,17 @@ export default function onDemandEntryHandler (devMiddleware, multiCompiler, {
invalidator.startBuilding()

const allEntries = Object.keys(entries).map(async (page) => {
const { name, entry } = entries[page]
const files = Array.isArray(entry) ? entry : [entry]
// Is just one item. But it's passed as an array.
for (const file of files) {
try {
await access(join(dir, file), (fs.constants || fs).W_OK)
} catch (err) {
console.warn('Page was removed', page)
delete entries[page]
return
}
const { name, absolutePagePath } = entries[page]
try {
await access(absolutePagePath, (fs.constants || fs).W_OK)
} catch (err) {
console.warn('Page was removed', page)
delete entries[page]
return
}

entries[page].status = BUILDING
return addEntry(compilation, compiler.context, name, entry)
return addEntry(compilation, compiler.context, name, [compiler.name === 'client' ? `next-client-pages-loader?${stringify({page, absolutePagePath})}!` : absolutePagePath])
})

return Promise.all(allEntries)
Expand Down Expand Up @@ -178,17 +174,19 @@ export default function onDemandEntryHandler (devMiddleware, multiCompiler, {
}

const extensions = pageExtensions.join('|')
const paths = await glob(`pages/{${normalizedPagePath}/index,${normalizedPagePath}}.+(${extensions})`, {cwd: dir})
const pagesDir = join(dir, 'pages')
const paths = await glob(`{${normalizedPagePath.slice(1)}/index,${normalizedPagePath.slice(1)}}.+(${extensions})`, {cwd: pagesDir})

if (paths.length === 0) {
throw pageNotFoundError(normalizedPagePath)
}

const relativePathToPage = paths[0]

const pathname = join(dir, relativePathToPage)

const {name, files} = createEntry(relativePathToPage, {buildId, pageExtensions: extensions})
const pagePath = paths[0]
let pageUrl = `/${pagePath.replace(new RegExp(`\\.+(${extensions})$`), '').replace(/\\/g, '/')}`.replace(/\/index$/, '')
pageUrl = pageUrl === '' ? '/' : pageUrl
const bundleFile = pageUrl === '/' ? '/index.js' : `${pageUrl}.js`
const name = join('static', buildId, 'pages', bundleFile)
const absolutePagePath = join(pagesDir, pagePath)

await new Promise((resolve, reject) => {
const entryInfo = entries[page]
Expand All @@ -207,7 +205,7 @@ export default function onDemandEntryHandler (devMiddleware, multiCompiler, {

console.log(`> Building page: ${page}`)

entries[page] = { name, entry: files, pathname, status: ADDED }
entries[page] = { name, absolutePagePath, status: ADDED }
doneCallbacks.once(page, handleCallback)

invalidator.invalidate()
Expand Down