Skip to content

Commit

Permalink
Merge branch 'canary' into hotfix/fix-css-paths
Browse files Browse the repository at this point in the history
  • Loading branch information
Timer authored Jan 3, 2020
2 parents e95ddad + 2f1b15c commit c17e48b
Show file tree
Hide file tree
Showing 10 changed files with 232 additions and 2 deletions.
4 changes: 3 additions & 1 deletion packages/next/next-server/server/load-components.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ export async function loadComponents(
const DocumentMod = require(documentPath)
const { middleware: DocumentMiddleware } = DocumentMod

const AppMod = require(appPath)

const ComponentMod = requirePage(pathname, distDir, serverless)

const [
Expand All @@ -77,7 +79,7 @@ export async function loadComponents(
require(join(distDir, REACT_LOADABLE_MANIFEST)),
interopDefault(ComponentMod),
interopDefault(DocumentMod),
interopDefault(require(appPath)),
interopDefault(AppMod),
])

return {
Expand Down
2 changes: 1 addition & 1 deletion packages/next/next-server/server/render.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -499,8 +499,8 @@ export async function renderToHTML(
const devFiles = buildManifest.devFiles
const files = [
...new Set([
...getPageFiles(buildManifest, pathname),
...getPageFiles(buildManifest, '/_app'),
...getPageFiles(buildManifest, pathname),
]),
]
const polyfillFiles = getPageFiles(buildManifest, '/_polyfills')
Expand Down
35 changes: 35 additions & 0 deletions test/integration/app-document-import-order/next.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
module.exports = {
webpack: (config, { isServer }) => {
if (!isServer) {
const optimization = config.optimization || {}
const splitChunks = optimization.splitChunks || {}
const cacheGroups = splitChunks.cacheGroups || {}

config.optimization = {
...optimization,
splitChunks: {
...splitChunks,
cacheGroups: {
...cacheGroups,
requiredByApp: {
test: /requiredByApp.js/,
name: 'requiredByApp',
enforce: true,
priority: 10,
chunks: 'all',
},
requiredByPage: {
test: /requiredByPage.js/,
name: 'requiredByPage',
enforce: true,
priority: 10,
chunks: 'all',
},
},
},
}
}

return config
},
}
16 changes: 16 additions & 0 deletions test/integration/app-document-import-order/pages/_app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import React from 'react'
import RequiredByApp from '../requiredByApp'
import sideEffect from '../sideEffectModule'

sideEffect('_app')

function MyApp({ Component, pageProps }) {
return (
<React.Fragment>
<RequiredByApp />
<Component {...pageProps} />
</React.Fragment>
)
}

export default MyApp
28 changes: 28 additions & 0 deletions test/integration/app-document-import-order/pages/_document.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import Document, { Html, Head, Main, NextScript } from 'next/document'
import sideEffect from '../sideEffectModule'

sideEffect('_document')

class MyDocument extends Document {
static async getInitialProps(ctx) {
const initialProps = await Document.getInitialProps(ctx)

return {
...initialProps,
}
}

render() {
return (
<Html>
<Head />
<body>
<Main />
<NextScript />
</body>
</Html>
)
}
}

export default MyDocument
22 changes: 22 additions & 0 deletions test/integration/app-document-import-order/pages/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import sideEffect from '../sideEffectModule'
import RequiredByPage from '../requiredByPage'

const sideEffects = sideEffect('page')

function Hi() {
return (
<div>
<RequiredByPage />
<p>Hello world!</p>
{sideEffects.map((arg, index) => (
<p key={arg} className="side-effect-calls">
{arg}
</p>
))}
</div>
)
}

Hi.getInitialProps = () => ({})

export default Hi
9 changes: 9 additions & 0 deletions test/integration/app-document-import-order/requiredByApp.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
function RequiredByApp() {
return (
<div>
<p>RequiredByApp!</p>
</div>
)
}

export default RequiredByApp
9 changes: 9 additions & 0 deletions test/integration/app-document-import-order/requiredByPage.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
function RequiredByPage() {
return (
<div>
<p>RequiredByPage</p>
</div>
)
}

export default RequiredByPage
10 changes: 10 additions & 0 deletions test/integration/app-document-import-order/sideEffectModule.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
const sideEffect = arg => {
if (!sideEffect.callArguments) {
sideEffect.callArguments = []
}
sideEffect.callArguments.push(arg)

return sideEffect.callArguments
}

export default sideEffect
99 changes: 99 additions & 0 deletions test/integration/app-document-import-order/test/index.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
/* eslint-env jest */
/* global jasmine */
import { join } from 'path'
import cheerio from 'cheerio'
import {
stopApp,
startApp,
nextBuild,
nextServer,
fetchViaHTTP,
findPort,
launchApp,
killApp,
} from 'next-test-utils'

jasmine.DEFAULT_TIMEOUT_INTERVAL = 1000 * 60
const appDir = join(__dirname, '../')
let appPort
let server
let app

describe('Root components import order', () => {
beforeAll(async () => {
await nextBuild(appDir)
app = nextServer({
dir: join(__dirname, '../'),
dev: false,
quiet: true,
})

server = await startApp(app)
appPort = server.address().port
})
afterAll(() => stopApp(server))

const respectsSideEffects = async () => {
const res = await fetchViaHTTP(appPort, '/')
const html = await res.text()
const $ = cheerio.load(html)

const expectSideEffectsOrder = ['_document', '_app', 'page']

const sideEffectCalls = $('.side-effect-calls')

Array.from(sideEffectCalls).forEach((sideEffectCall, index) => {
expect($(sideEffectCall).text()).toEqual(expectSideEffectsOrder[index])
})
}

it(
'root components should be imported in this order _document > _app > page in order to respect side effects',
respectsSideEffects
)

const respectsChunkAttachmentOrder = async () => {
const res = await fetchViaHTTP(appPort, '/')
const html = await res.text()
const $ = cheerio.load(html)

const requiredByRegex = /^\/_next\/static\/chunks\/(requiredBy\w*).*\.js/
const chunks = Array.from($('head').contents())
.filter(
child =>
child.type === 'tag' &&
child.name === 'link' &&
child.attribs.href.match(requiredByRegex)
)
.map(child => child.attribs.href.match(requiredByRegex)[1])

const requiredByAppIndex = chunks.indexOf('requiredByApp')
const requiredByPageIndex = chunks.indexOf('requiredByPage')

expect(requiredByAppIndex).toBeLessThan(requiredByPageIndex)
}

it(
'_app chunks should be attached to de dom before page chunks',
respectsChunkAttachmentOrder
)

describe('on dev server', () => {
beforeAll(async () => {
appPort = await findPort()
app = await launchApp(join(__dirname, '../'), appPort)
})

afterAll(() => killApp(app))

it(
'root components should be imported in this order _document > _app > page in order to respect side effects',
respectsSideEffects
)

it(
'_app chunks should be attached to de dom before page chunks',
respectsChunkAttachmentOrder
)
})
})

0 comments on commit c17e48b

Please sign in to comment.