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

Run runtime config side-effect before other serverless imports #10386

Merged
merged 3 commits into from Feb 3, 2020
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
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
53 changes: 27 additions & 26 deletions packages/next/build/webpack/loaders/next-serverless-loader.ts
Expand Up @@ -54,7 +54,7 @@ const nextServerlessLoader: loader.Loader = function() {

const runtimeConfigImports = runtimeConfig
? `
import { setConfig } from 'next/dist/next-server/lib/runtime-config'
const { setConfig } = require('next/dist/next-server/lib/runtime-config')
`
: ''

Expand All @@ -67,8 +67,8 @@ const nextServerlessLoader: loader.Loader = function() {

const dynamicRouteImports = pageIsDynamicRoute
? `
import { getRouteMatcher } from 'next/dist/next-server/lib/router/utils/route-matcher';
import { getRouteRegex } from 'next/dist/next-server/lib/router/utils/route-regex';
const { getRouteMatcher } = require('next/dist/next-server/lib/router/utils/route-matcher');
const { getRouteRegex } = require('next/dist/next-server/lib/router/utils/route-regex');
`
: ''

Expand All @@ -79,8 +79,8 @@ const nextServerlessLoader: loader.Loader = function() {
: ''

const rewriteImports = `
import { rewrites } from '${routesManifest}'
import pathMatch, { pathToRegexp } from 'next/dist/next-server/server/lib/path-match'
const { rewrites } = require('${routesManifest}')
const { pathToRegexp, default: pathMatch } = require('next/dist/next-server/server/lib/path-match')
`

const handleRewrites = `
Expand Down Expand Up @@ -132,20 +132,22 @@ const nextServerlessLoader: loader.Loader = function() {

if (page.match(API_ROUTE)) {
return `
${dynamicRouteImports}
import { parse } from 'url'
import { apiResolver } from 'next/dist/next-server/server/api-utils'
import initServer from 'next-plugin-loader?middleware=on-init-server!'
import onError from 'next-plugin-loader?middleware=on-error-server!'
${rewriteImports}
${runtimeConfigImports}

${dynamicRouteMatcher}
${handleRewrites}
${
/* this needs to be called before importing the API method */
/*
this needs to be called first so its available for any other imports
*/
runtimeConfigSetter
}
${dynamicRouteImports}
const { parse } = require('url')
const { apiResolver } = require('next/dist/next-server/server/api-utils')
${rewriteImports}

${dynamicRouteMatcher}
${handleRewrites}

export default async (req, res) => {
try {
Expand Down Expand Up @@ -186,25 +188,24 @@ const nextServerlessLoader: loader.Loader = function() {
`
} else {
return `
import {parse} from 'url'
import {parse as parseQs} from 'querystring'
import {renderToHTML} from 'next/dist/next-server/server/render';
import {sendHTML} from 'next/dist/next-server/server/send-html';
import initServer from 'next-plugin-loader?middleware=on-init-server!'
import onError from 'next-plugin-loader?middleware=on-error-server!'
import buildManifest from '${buildManifest}';
import reactLoadableManifest from '${reactLoadableManifest}';
import Document from '${absoluteDocumentPath}';
import Error from '${absoluteErrorPath}';
import App from '${absoluteAppPath}';
${dynamicRouteImports}
${rewriteImports}
${runtimeConfigImports}

${
/* this needs to be called before importing the component */
// this needs to be called first so its available for any other imports
runtimeConfigSetter
}
const {parse} = require('url')
const {parse: parseQs} = require('querystring')
const {renderToHTML} =require('next/dist/next-server/server/render');
const {sendHTML} = require('next/dist/next-server/server/send-html');
const buildManifest = require('${buildManifest}');
const reactLoadableManifest = require('${reactLoadableManifest}');
const Document = require('${absoluteDocumentPath}').default;
const Error = require('${absoluteErrorPath}').default;
const App = require('${absoluteAppPath}').default;
${dynamicRouteImports}
${rewriteImports}

const ComponentInfo = require('${absolutePagePath}')

Expand Down
10 changes: 10 additions & 0 deletions test/integration/serverless-runtime-configs/pages/_app.js
@@ -0,0 +1,10 @@
import getConfig from 'next/config'

const config = getConfig()

export default ({ Component, pageProps }) => (
<>
<p id="app-config">{JSON.stringify(config)}</p>
<Component {...pageProps} />
</>
)
26 changes: 26 additions & 0 deletions test/integration/serverless-runtime-configs/pages/_document.js
@@ -0,0 +1,26 @@
import Document, { Html, Head, Main, NextScript } from 'next/document'
import getConfig from 'next/config'

const config = getConfig()

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

render() {
return (
<Html>
<Head />
<body>
<div id="doc-config">{JSON.stringify(config)}</div>
<Main />
<NextScript />
</body>
</Html>
)
}
}

export default MyDocument
@@ -0,0 +1,7 @@
import getConfig from 'next/config'

const config = getConfig()

export default (req, res) => {
res.json(config)
}
Expand Up @@ -2,4 +2,8 @@ import getConfig from 'next/config'

const config = getConfig()

export default () => <p id="config">{JSON.stringify(config)}</p>
const page = () => <p id="config">{JSON.stringify(config)}</p>

page.getInitialProps = () => ({ a: 'b' })

export default page
31 changes: 31 additions & 0 deletions test/integration/serverless-runtime-configs/server.js
@@ -0,0 +1,31 @@
const path = require('path')
const http = require('http')
const send = require('send')

const server = http.createServer((req, res) => {
if (req.url === '/config') {
return require('./.next/serverless/pages/config.js').render(req, res)
}

if (req.url === '/') {
return send(
req,
path.join(__dirname, '.next/serverless/pages/index.html')
).pipe(res)
}

if (req.url === '/api/config') {
return require('./.next/serverless/pages/api/config.js').default(req, res)
}

if (req.url.startsWith('/_next')) {
send(
req,
path.join(__dirname, '.next', req.url.split('/_next').pop())
).pipe(res)
}
})

server.listen(process.env.PORT, () => {
console.log('ready on', process.env.PORT)
})
47 changes: 38 additions & 9 deletions test/integration/serverless-runtime-configs/test/index.test.js
Expand Up @@ -5,9 +5,9 @@ import { join } from 'path'
import {
nextBuild,
findPort,
nextStart,
killApp,
renderViaHTTP,
initNextServerScript,
} from 'next-test-utils'
import cheerio from 'cheerio'
import webdriver from 'next-webdriver'
Expand All @@ -18,6 +18,18 @@ jasmine.DEFAULT_TIMEOUT_INTERVAL = 1000 * 60 * 2

const cleanUp = () => fs.remove(nextConfigPath)

const nextStart = async (appDir, appPort) => {
const scriptPath = join(appDir, 'server.js')
const env = Object.assign({ ...process.env }, { PORT: `${appPort}` })

return initNextServerScript(
scriptPath,
/ready on/i,
env,
/ReferenceError: options is not defined/
)
}

describe('Serverless runtime configs', () => {
beforeAll(() => cleanUp())
afterAll(() => cleanUp())
Expand Down Expand Up @@ -78,15 +90,13 @@ describe('Serverless runtime configs', () => {

await nextBuild(appDir, [], { stderr: true, stdout: true })
const appPort = await findPort()
const app = await nextStart(appDir, appPort, {
onStdout: console.log,
onStderr: console.log,
})
const app = await nextStart(appDir, appPort)

const browser = await webdriver(appPort, '/config')

const clientHTML = await browser.eval(`document.documentElement.innerHTML`)
const ssrHTML = await renderViaHTTP(appPort, '/config')
const apiJson = await renderViaHTTP(appPort, '/api/config')

await killApp(app)
await fs.remove(nextConfigPath)
Expand All @@ -97,19 +107,38 @@ describe('Serverless runtime configs', () => {
const ssrConfig = ssr$('#config').text()
const clientConfig = client$('#config').text()

expect(JSON.parse(ssrConfig)).toEqual({
const expectedSsrConfig = {
publicRuntimeConfig: {
another: 'thing',
},
serverRuntimeConfig: {
hello: 'world',
},
})
expect(JSON.parse(clientConfig)).toEqual({
}

const expectedClientConfig = {
publicRuntimeConfig: {
another: 'thing',
},
serverRuntimeConfig: {},
})
}

expect(JSON.parse(ssrConfig)).toEqual(expectedSsrConfig)
expect(JSON.parse(clientConfig)).toEqual(expectedClientConfig)

const appSsrConfig = ssr$('#app-config').text()
const appClientConfig = client$('#app-config').text()

expect(JSON.parse(appSsrConfig)).toEqual(expectedSsrConfig)
expect(JSON.parse(appClientConfig)).toEqual(expectedClientConfig)

const docSsrConfig = ssr$('#doc-config').text()
const docClientConfig = client$('#doc-config').text()

// _document doesn't update on client so should be the same
expect(JSON.parse(docSsrConfig)).toEqual(expectedSsrConfig)
expect(JSON.parse(docClientConfig)).toEqual(expectedSsrConfig)

expect(JSON.parse(apiJson)).toEqual(expectedSsrConfig)
})
})