Skip to content

Commit

Permalink
Make sure runtime config is set before any imports for serverless (ve…
Browse files Browse the repository at this point in the history
…rcel#10386)

Co-authored-by: Joe Haddad <timer150@gmail.com>
  • Loading branch information
2 people authored and chibicode committed Feb 11, 2020
1 parent a038a79 commit 836ad75
Show file tree
Hide file tree
Showing 7 changed files with 144 additions and 36 deletions.
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)
}
6 changes: 5 additions & 1 deletion test/integration/serverless-runtime-configs/pages/config.js
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)
})
})

0 comments on commit 836ad75

Please sign in to comment.