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

[WIP] Webpack 4 #4108

Closed
wants to merge 3 commits into from
Closed
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
105 changes: 55 additions & 50 deletions lib/router/index.js
Original file line number Diff line number Diff line change
@@ -1,70 +1,75 @@
/* global window */
import _Router from './router'
import { execOnce } from '../utils'

const SingletonRouter = {
router: null, // holds the actual router instance
readyCallbacks: [],
ready (cb) {
if (this.router) return cb()
if (typeof window !== 'undefined') {
this.readyCallbacks.push(cb)
}
}
}

let SingletonRouter

console.trace('routerr')

// Create public properties and methods of the router in the SingletonRouter
const propertyFields = ['components', 'pathname', 'route', 'query', 'asPath']
const routerEvents = ['routeChangeStart', 'beforeHistoryChange', 'routeChangeComplete', 'routeChangeError']
const coreMethodFields = ['push', 'replace', 'reload', 'back', 'prefetch', 'beforePopState']

propertyFields.forEach((field) => {
// Here we need to use Object.defineProperty because, we need to return
// the property assigned to the actual router
// The value might get changed as we change routes and this is the
// proper way to access it
Object.defineProperty(SingletonRouter, field, {
get () {
function initialRouter () {
SingletonRouter = {
router: null, // holds the actual router instance
readyCallbacks: [],
ready (cb) {
if (this.router) return cb()
if (typeof window !== 'undefined') {
this.readyCallbacks.push(cb)
}
}
}

propertyFields.forEach((field) => {
// Here we need to use Object.defineProperty because, we need to return
// the property assigned to the actual router
// The value might get changed as we change routes and this is the
// proper way to access it
Object.defineProperty(SingletonRouter, field, {
get () {
throwIfNoRouter()
return SingletonRouter.router[field]
}
})
})

coreMethodFields.forEach((field) => {
SingletonRouter[field] = (...args) => {
throwIfNoRouter()
return SingletonRouter.router[field]
return SingletonRouter.router[field](...args)
}
})
})

coreMethodFields.forEach((field) => {
SingletonRouter[field] = (...args) => {
throwIfNoRouter()
return SingletonRouter.router[field](...args)
}
})

routerEvents.forEach((event) => {
SingletonRouter.ready(() => {
SingletonRouter.router.events.on(event, (...args) => {
const eventField = `on${event.charAt(0).toUpperCase()}${event.substring(1)}`
if (SingletonRouter[eventField]) {
try {
SingletonRouter[eventField](...args)
} catch (err) {
console.error(`Error when running the Router event: ${eventField}`)
console.error(`${err.message}\n${err.stack}`)
routerEvents.forEach((event) => {
SingletonRouter.ready(() => {
SingletonRouter.router.events.on(event, (...args) => {
const eventField = `on${event.charAt(0).toUpperCase()}${event.substring(1)}`
if (SingletonRouter[eventField]) {
try {
SingletonRouter[eventField](...args)
} catch (err) {
console.error(`Error when running the Router event: ${eventField}`)
console.error(`${err.message}\n${err.stack}`)
}
}
}
})
})
})
})

const warnAboutRouterOnAppUpdated = execOnce(() => {
console.warn(`Router.onAppUpdated is removed - visit https://err.sh/next.js/no-on-app-updated-hook for more information.`)
})

Object.defineProperty(SingletonRouter, 'onAppUpdated', {
get () { return null },
set () {
warnAboutRouterOnAppUpdated()
return null
if (typeof window !== 'undefined') {
window.__NEXT_ROUTER__ = SingletonRouter
}
})
}

// If SSR or window.__NEXT_ROUTER__ is not set yet, run initialRouter()
// This is to share state across all router requires
if (typeof window === 'undefined' || (typeof window !== 'undefined' && !window.__NEXT_ROUTER__)) {
initialRouter()
} else {
SingletonRouter = window.__NEXT_ROUTER__
}

function throwIfNoRouter () {
if (!SingletonRouter.router) {
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@
"url": "0.11.0",
"uuid": "3.1.0",
"walk": "2.3.9",
"webpack": "3.10.0",
"webpack": "4.5.0",
"webpack-dev-middleware": "1.12.0",
"webpack-hot-middleware": "2.19.1",
"webpack-sources": "1.1.0",
Expand Down
2 changes: 1 addition & 1 deletion server/build/plugins/pages-plugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ class PageChunkTemplatePlugin {
export default class PagesPlugin {
apply (compiler) {
compiler.plugin('compilation', (compilation) => {
compilation.chunkTemplate.apply(new PageChunkTemplatePlugin())
compilation.mainTemplate.apply(new PageChunkTemplatePlugin())
})
}
}
93 changes: 57 additions & 36 deletions server/build/webpack.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import path, {sep} from 'path'
import path from 'path'
import webpack from 'webpack'
import resolve from 'resolve'
import UglifyJSPlugin from 'uglifyjs-webpack-plugin'
Expand Down Expand Up @@ -115,7 +115,28 @@ export default async function getBaseWebpackConfig (dir, {dev = false, isServer
].filter(Boolean)
} : {}

function optimization ({dev, isServer}) {
if (isServer) {
return {}
}

return {
splitChunks: {
cacheGroups: {
commons: {
test: /(node_modules\/(react|react-dom|core-js|next)|next\.js\/dist)/,
chunks: 'all',
priority: 10,
name: 'vendor.js'
}
}
}
}
}

let webpackConfig = {
optimization: optimization({dev, isServer}),
mode: dev ? 'development' : 'production',
devtool: dev ? 'source-map' : false,
name: isServer ? 'server' : 'client',
cache: true,
Expand All @@ -135,7 +156,7 @@ export default async function getBaseWebpackConfig (dir, {dev = false, isServer
filename: '[name]',
libraryTarget: 'commonjs2',
// This saves chunks with the name given via require.ensure()
chunkFilename: '[name]-[chunkhash].js',
chunkFilename: dev ? '[name]' : '[name]-[chunkhash].js',
strictModuleExceptionHandling: true,
devtoolModuleFilenameTemplate (info) {
if (dev) {
Expand Down Expand Up @@ -261,45 +282,45 @@ export default async function getBaseWebpackConfig (dir, {dev = false, isServer
isServer && new PagesManifestPlugin(),
!isServer && new PagesPlugin(),
!isServer && new DynamicChunksPlugin(),
isServer && new NextJsSsrImportPlugin(),
isServer && new NextJsSsrImportPlugin()
// In dev mode, we don't move anything to the commons bundle.
// In production we move common modules into the existing main.js bundle
!isServer && new webpack.optimize.CommonsChunkPlugin({
name: 'main.js',
filename: 'main.js',
minChunks (module, count) {
// React and React DOM are used everywhere in Next.js. So they should always be common. Even in development mode, to speed up compilation.
if (module.resource && module.resource.includes(`${sep}react-dom${sep}`) && count >= 0) {
return true
}
// !isServer && new webpack.optimize.CommonsChunkPlugin({
// name: 'main.js',
// filename: 'main.js',
// minChunks (module, count) {
// // React and React DOM are used everywhere in Next.js. So they should always be common. Even in development mode, to speed up compilation.
// if (module.resource && module.resource.includes(`${sep}react-dom${sep}`) && count >= 0) {
// return true
// }

if (module.resource && module.resource.includes(`${sep}react${sep}`) && count >= 0) {
return true
}
// if (module.resource && module.resource.includes(`${sep}react${sep}`) && count >= 0) {
// return true
// }

// In the dev we use on-demand-entries.
// So, it makes no sense to use commonChunks based on the minChunks count.
// Instead, we move all the code in node_modules into each of the pages.
if (dev) {
return false
}
// // In the dev we use on-demand-entries.
// // So, it makes no sense to use commonChunks based on the minChunks count.
// // Instead, we move all the code in node_modules into each of the pages.
// if (dev) {
// return false
// }

// commons
// If there are one or two pages, only move modules to common if they are
// used in all of the pages. Otherwise, move modules used in at-least
// 1/2 of the total pages into commons.
if (totalPages <= 2) {
return count >= totalPages
}
return count >= totalPages * 0.5
// commons end
}
}),
// We use a manifest file in development to speed up HMR
dev && !isServer && new webpack.optimize.CommonsChunkPlugin({
name: 'manifest',
filename: 'manifest.js'
})
// // commons
// // If there are one or two pages, only move modules to common if they are
// // used in all of the pages. Otherwise, move modules used in at-least
// // 1/2 of the total pages into commons.
// if (totalPages <= 2) {
// return count >= totalPages
// }
// return count >= totalPages * 0.5
// // commons end
// }
// }),
// // We use a manifest file in development to speed up HMR
// dev && !isServer && new webpack.optimize.CommonsChunkPlugin({
// name: 'manifest',
// filename: 'manifest.js'
// })
].filter(Boolean)
}

Expand Down
4 changes: 2 additions & 2 deletions server/document.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ export class Head extends Component {
const { dev } = this.context._documentProps
if (dev) {
return [
this.getChunkPreloadLink('manifest.js'),
this.getChunkPreloadLink('vendor.js'),
this.getChunkPreloadLink('main.js')
]
}
Expand Down Expand Up @@ -142,7 +142,7 @@ export class NextScript extends Component {
const { dev } = this.context._documentProps
if (dev) {
return [
this.getChunkScript('manifest.js'),
this.getChunkScript('vendor.js'),
this.getChunkScript('main.js')
]
}
Expand Down
12 changes: 4 additions & 8 deletions server/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -162,19 +162,15 @@ export default class Server {
await this.serveStatic(req, res, p)
},

'/_next/:buildId/manifest.js': async (req, res, params) => {
if (!this.dev) return this.send404(res)

'/_next/:buildId/vendor.js': async (req, res, params) => {
this.handleBuildId(params.buildId, res)
const p = join(this.dir, this.dist, 'manifest.js')
const p = join(this.dir, this.dist, 'vendor.js')
await this.serveStatic(req, res, p)
},

'/_next/:buildId/manifest.js.map': async (req, res, params) => {
if (!this.dev) return this.send404(res)

'/_next/:buildId/vendor.js.map': async (req, res, params) => {
this.handleBuildId(params.buildId, res)
const p = join(this.dir, this.dist, 'manifest.js.map')
const p = join(this.dir, this.dist, 'vendor.js.map')
await this.serveStatic(req, res, p)
},

Expand Down
Loading