Skip to content

Commit

Permalink
Make fetch polyfill work with ky-universal (vercel#12804)
Browse files Browse the repository at this point in the history
  • Loading branch information
timneutkens committed May 12, 2020
1 parent 9cb75d5 commit 93c8149
Show file tree
Hide file tree
Showing 15 changed files with 286 additions and 36 deletions.
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@
"get-port": "5.1.1",
"isomorphic-unfetch": "3.0.0",
"jest-cli": "24.9.0",
"ky": "0.19.1",
"ky-universal": "0.6.0",
"lerna": "3.14.1",
"lint-staged": "10.1.7",
"moment": "^2.24.0",
Expand Down
14 changes: 4 additions & 10 deletions packages/next/build/webpack/loaders/next-serverless-loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,11 +139,8 @@ const nextServerlessLoader: loader.Loader = function() {
return `
import initServer from 'next-plugin-loader?middleware=on-init-server!'
import onError from 'next-plugin-loader?middleware=on-error-server!'
import fetch from 'next/dist/compiled/node-fetch'
if(!global.fetch) {
global.fetch = fetch
}
import 'next/dist/next-server/server/node-polyfill-fetch'
${runtimeConfigImports}
${
/*
Expand Down Expand Up @@ -208,11 +205,8 @@ const nextServerlessLoader: loader.Loader = function() {
return `
import initServer from 'next-plugin-loader?middleware=on-init-server!'
import onError from 'next-plugin-loader?middleware=on-error-server!'
import fetch from 'next/dist/compiled/node-fetch'
if(!global.fetch) {
global.fetch = fetch
}
import 'next/dist/next-server/server/node-polyfill-fetch'
${runtimeConfigImports}
${
// this needs to be called first so its available for any other imports
Expand Down
9 changes: 1 addition & 8 deletions packages/next/export/worker.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,7 @@ import { getRouteMatcher } from '../next-server/lib/router/utils/route-matcher'
import { getRouteRegex } from '../next-server/lib/router/utils/route-regex'
import { normalizePagePath } from '../next-server/server/normalize-page-path'
import { SERVER_PROPS_EXPORT_ERROR } from '../lib/constants'
import fetch from 'next/dist/compiled/node-fetch'

// @ts-ignore fetch exists globally
if (!global.fetch) {
// Polyfill fetch() in the Node.js environment
// @ts-ignore fetch exists globally
global.fetch = fetch
}
import 'next/dist/next-server/server/node-polyfill-fetch'

const envConfig = require('../next-server/lib/runtime-config')

Expand Down
9 changes: 1 addition & 8 deletions packages/next/next-server/server/next-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,14 +64,7 @@ import { execOnce } from '../lib/utils'
import { isBlockedPage } from './utils'
import { compile as compilePathToRegex } from 'next/dist/compiled/path-to-regexp'
import { loadEnvConfig } from '../../lib/load-env-config'
import fetch from 'next/dist/compiled/node-fetch'

// @ts-ignore fetch exists globally
if (!global.fetch) {
// Polyfill fetch() in the Node.js environment
// @ts-ignore fetch exists globally
global.fetch = fetch
}
import './node-polyfill-fetch'

const getCustomRouteMatcher = pathMatch(true)

Expand Down
13 changes: 13 additions & 0 deletions packages/next/next-server/server/node-polyfill-fetch.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import fetch, {
Headers,
Request,
Response,
} from 'next/dist/compiled/node-fetch'

// Polyfill fetch() in the Node.js environment
if (!global.fetch) {
global.fetch = fetch
global.Headers = Headers
global.Request = Request
global.Response = Response
}
9 changes: 1 addition & 8 deletions packages/next/server/static-paths-worker.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,6 @@
import { buildStaticPaths } from '../build/utils'
import { loadComponents } from '../next-server/server/load-components'
import fetch from 'next/dist/compiled/node-fetch'

// @ts-ignore fetch exists globally
if (!global.fetch) {
// Polyfill fetch() in the Node.js environment
// @ts-ignore fetch exists globally
global.fetch = fetch
}
import '../next-server/server/node-polyfill-fetch'

let workerWasUsed = false

Expand Down
14 changes: 14 additions & 0 deletions test/integration/fetch-polyfill-ky-universal/api-server.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
const http = require('http')
const port = process.env.PORT || 3000

const server = new http.Server(async (req, res) => {
res.end(JSON.stringify({ foo: 'bar' }))
})

server.listen(port, err => {
if (err) {
throw err
}

console.log(`> Ready on http://localhost:${port}`)
})
7 changes: 7 additions & 0 deletions test/integration/fetch-polyfill-ky-universal/api/api-route.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import ky from 'ky-universal'

export default async function ApiRoute(_req, res) {
const port = process.env.NEXT_PUBLIC_API_PORT
const json = await ky.get(`http://localhost:${port}/`).json()
res.json(json)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import ky from 'ky-universal'

export default function SSRPageWithGetInitialProps({ data }) {
return <div>{data.foo}</div>
}

SSRPageWithGetInitialProps.getInitialProps = async () => {
const port = process.env.NEXT_PUBLIC_API_PORT
const json = await ky.get(`http://localhost:${port}/`).json()
return {
data: json,
}
}
15 changes: 15 additions & 0 deletions test/integration/fetch-polyfill-ky-universal/pages/ssr.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import ky from 'ky-universal'

export default function SSRPage({ data }) {
return <div>{data.foo}</div>
}

export async function getServerSideProps() {
const port = process.env.NEXT_PUBLIC_API_PORT
const json = await ky.get(`http://localhost:${port}/`).json()
return {
props: {
data: json,
},
}
}
15 changes: 15 additions & 0 deletions test/integration/fetch-polyfill-ky-universal/pages/static.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import ky from 'ky-universal'

export default function StaticPage({ data }) {
return <div>{data.foo}</div>
}

export async function getStaticProps() {
const port = process.env.NEXT_PUBLIC_API_PORT
const json = await ky.get(`http://localhost:${port}/`).json()
return {
props: {
data: json,
},
}
}
44 changes: 44 additions & 0 deletions test/integration/fetch-polyfill-ky-universal/serverless-server.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// This is not the correct way to implement the Next.js serverless target for production traffic
// It is only used for testing cases of rendering specific pages in the integration test suite

const path = require('path')
const http = require('http')
const send = require('send')

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

if (req.url === '/getinitialprops') {
return require('./.next/serverless/pages/getinitialprops.js').render(
req,
res
)
}

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

if (req.url === '/static') {
return send(
req,
path.join(__dirname, '.next/serverless/pages/static.html')
).pipe(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)
})
138 changes: 138 additions & 0 deletions test/integration/fetch-polyfill-ky-universal/test/index.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
/* eslint-env jest */
/* global jasmine */
import fs from 'fs-extra'
import { join } from 'path'
import {
killApp,
findPort,
launchApp,
initNextServerScript,
renderViaHTTP,
nextBuild,
nextStart,
} from 'next-test-utils'
import clone from 'clone'

jasmine.DEFAULT_TIMEOUT_INTERVAL = 1000 * 60 * 2
const appDir = join(__dirname, '../')
const nextConfig = join(appDir, 'next.config.js')
let appPort
let app
let apiServerPort
let apiServer

const startApiServer = async (optEnv = {}, opts) => {
const scriptPath = join(appDir, 'api-server.js')
apiServerPort = await findPort()
const env = Object.assign(
{},
clone(process.env),
{ PORT: `${apiServerPort}` },
optEnv
)

apiServer = await initNextServerScript(
scriptPath,
/ready on/i,
env,
/ReferenceError: options is not defined/,
opts
)
}

const startServerlessServer = async (optEnv = {}, opts) => {
const scriptPath = join(appDir, 'serverless-server.js')
appPort = await findPort()
const env = Object.assign(
{},
clone(process.env),
{ PORT: `${appPort}` },
optEnv
)

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

function runTests() {
it('includes polyfilled fetch when using getStaticProps', async () => {
const html = await renderViaHTTP(appPort, '/static')
expect(html).toMatch(/bar/)
})
it('includes polyfilled fetch when using getServerSideProps', async () => {
const html = await renderViaHTTP(appPort, '/ssr')
expect(html).toMatch(/bar/)
})
it('includes polyfilled fetch when using getInitialProps', async () => {
const html = await renderViaHTTP(appPort, '/getinitialprops')
expect(html).toMatch(/bar/)
})
}

describe('Fetch polyfill with ky-universal', () => {
describe('dev support', () => {
beforeAll(async () => {
appPort = await findPort()
await startApiServer()
app = await launchApp(appDir, appPort, {
env: {
NEXT_PUBLIC_API_PORT: apiServerPort,
},
})
})
afterAll(async () => {
await killApp(app)
await killApp(apiServer)
})

runTests()
})

describe('Server support', () => {
beforeAll(async () => {
await startApiServer()
await nextBuild(appDir, [], {
env: {
NEXT_PUBLIC_API_PORT: apiServerPort,
},
})
appPort = await findPort()
app = await nextStart(appDir, appPort)
})
afterAll(async () => {
await killApp(app)
await killApp(apiServer)
})

runTests()
})

describe('Serverless support', () => {
beforeAll(async () => {
await fs.writeFile(
nextConfig,
`module.exports = { target: 'serverless' }`
)
await startApiServer()
await nextBuild(appDir, [], {
env: {
NEXT_PUBLIC_API_PORT: apiServerPort,
},
})
appPort = await findPort()
app = await startServerlessServer()
})
afterAll(async () => {
await killApp(app)
await fs.remove(nextConfig)
await killApp(apiServer)
})

runTests()
})
})
3 changes: 3 additions & 0 deletions test/integration/fetch-polyfill/serverless-server.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
// This is not the correct way to implement the Next.js serverless target for production traffic
// It is only used for testing cases of rendering specific pages in the integration test suite

const path = require('path')
const http = require('http')
const send = require('send')
Expand Down
17 changes: 15 additions & 2 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3353,7 +3353,7 @@ abbrev@1:
resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8"
integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==

abort-controller@3.0.0:
abort-controller@3.0.0, abort-controller@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392"
integrity sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==
Expand Down Expand Up @@ -10035,6 +10035,19 @@ kleur@^3.0.2, kleur@^3.0.3:
resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e"
integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==

ky-universal@0.6.0:
version "0.6.0"
resolved "https://registry.yarnpkg.com/ky-universal/-/ky-universal-0.6.0.tgz#a91c265c80c38f750e65bbc6d72df16f5b58691f"
integrity sha512-GvKOzAO5Ec4LiI2jCYRib/7FJwirI3xUFcxUQi9SY3kPZflLY8N289UlsYekwg7gtiLPeU1abYnTFA9N6IO9ig==
dependencies:
abort-controller "^3.0.0"
node-fetch "^2.6.0"

ky@0.19.1:
version "0.19.1"
resolved "https://registry.yarnpkg.com/ky/-/ky-0.19.1.tgz#c533884028c83f78167d12905dfecaf6dd760bec"
integrity sha512-ZwciYrfaWpDI72U2HAruuGYGFW3PCfGNdWWSANGGssg9BGm4rRJ9s/sApiiRpj+8Y245/hlZW9c60zudLr6iwA==

last-call-webpack-plugin@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/last-call-webpack-plugin/-/last-call-webpack-plugin-3.0.0.tgz#9742df0e10e3cf46e5c0381c2de90d3a7a2d7555"
Expand Down Expand Up @@ -11391,7 +11404,7 @@ node-fetch-npm@^2.0.2:
json-parse-better-errors "^1.0.0"
safe-buffer "^5.1.1"

node-fetch@2.6.0, node-fetch@^2.1.1, node-fetch@^2.2.0, node-fetch@^2.3.0:
node-fetch@2.6.0, node-fetch@^2.1.1, node-fetch@^2.2.0, node-fetch@^2.3.0, node-fetch@^2.6.0:
version "2.6.0"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.0.tgz#e633456386d4aa55863f676a7ab0daa8fdecb0fd"
integrity sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==
Expand Down

0 comments on commit 93c8149

Please sign in to comment.