Skip to content

Commit

Permalink
Merge branch 'enable/native-url' of github.com:Timer/next.js into ena…
Browse files Browse the repository at this point in the history
…ble/native-url
  • Loading branch information
Timer committed Feb 4, 2020
2 parents c412b57 + f11a806 commit 14e30be
Show file tree
Hide file tree
Showing 11 changed files with 163 additions and 35 deletions.
7 changes: 4 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
"@babel/preset-react": "7.7.0",
"@fullhuman/postcss-purgecss": "1.3.0",
"@mdx-js/loader": "0.18.0",
"@types/http-proxy": "1.17.3",
"@types/jest": "24.0.13",
"@types/string-hash": "1.1.1",
"@typescript-eslint/eslint-plugin": "2.17.0",
Expand All @@ -53,7 +54,9 @@
"babel-core": "7.0.0-bridge.0",
"babel-eslint": "10.0.3",
"babel-jest": "24.9.0",
"browserslist": "^4.8.3",
"browserstack-local": "1.4.0",
"caniuse-lite": "^1.0.30001019",
"cheerio": "0.22.0",
"clone": "2.1.2",
"coveralls": "3.0.3",
Expand Down Expand Up @@ -100,9 +103,7 @@
"tree-kill": "1.2.1",
"typescript": "3.7.3",
"wait-port": "0.2.2",
"webpack-bundle-analyzer": "3.3.2",
"browserslist": "^4.8.3",
"caniuse-lite": "^1.0.30001019"
"webpack-bundle-analyzer": "3.3.2"
},
"resolutions": {
"browserslist": "^4.8.3",
Expand Down
15 changes: 7 additions & 8 deletions packages/next/build/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import chalk from 'chalk'
import ciEnvironment from 'ci-info'
import escapeStringRegexp from 'escape-string-regexp'
import findUp from 'find-up'
import fs from 'fs'
import Worker from 'jest-worker'
Expand All @@ -11,14 +12,14 @@ import { promisify } from 'util'
import formatWebpackMessages from '../client/dev/error-overlay/format-webpack-messages'
import checkCustomRoutes, {
getRedirectStatus,
RouteType,
Header,
Redirect,
Rewrite,
Header,
RouteType,
} from '../lib/check-custom-routes'
import {
PUBLIC_DIR_MIDDLEWARE_CONFLICT,
PAGES_404_GET_INITIAL_PROPS_ERROR,
PUBLIC_DIR_MIDDLEWARE_CONFLICT,
} from '../lib/constants'
import { findPagesDir } from '../lib/find-pages-dir'
import { recursiveDelete } from '../lib/recursive-delete'
Expand All @@ -44,7 +45,7 @@ import loadConfig, {
isTargetLikeServerless,
} from '../next-server/server/config'
import {
eventBuildDuration,
eventBuildCompleted,
eventBuildOptimize,
eventNextPlugins,
eventVersion,
Expand All @@ -56,17 +57,16 @@ import { generateBuildId } from './generate-build-id'
import { isWriteable } from './is-writeable'
import createSpinner from './spinner'
import {
isPageStatic,
collectPages,
getPageSizeInKb,
hasCustomAppGetInitialProps,
isPageStatic,
PageInfo,
printCustomRoutes,
printTreeView,
} from './utils'
import getBaseWebpackConfig from './webpack-config'
import { writeBuildId } from './write-build-id'
import escapeStringRegexp from 'escape-string-regexp'

const fsAccess = promisify(fs.access)
const fsUnlink = promisify(fs.unlink)
Expand Down Expand Up @@ -394,8 +394,7 @@ export default async function build(dir: string, conf = null): Promise<void> {
} else {
console.log(chalk.green('Compiled successfully.\n'))
telemetry.record(
eventBuildDuration({
totalPageCount: pagePaths.length,
eventBuildCompleted(pagePaths, {
durationInSeconds: webpackBuildEnd[0],
})
)
Expand Down
9 changes: 7 additions & 2 deletions packages/next/lib/check-custom-routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,8 +127,13 @@ export default function checkCustomRoutes(
invalidParts.push('`destination` is missing')
} else if (typeof _route.destination !== 'string') {
invalidParts.push('`destination` is not a string')
} else if (type === 'rewrite' && !_route.destination.startsWith('/')) {
invalidParts.push('`destination` does not start with /')
} else if (
type === 'rewrite' &&
!_route.destination.match(/^(\/|https:\/\/|http:\/\/)/)
) {
invalidParts.push(
'`destination` does not start with `/`, `http://`, or `https://`'
)
}
}

Expand Down
40 changes: 30 additions & 10 deletions packages/next/next-server/server/next-server.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import compression from 'compression'
import fs from 'fs'
import Proxy from 'http-proxy'
import { IncomingMessage, ServerResponse } from 'http'
import { join, resolve, sep } from 'path'
import { compile as compilePathToRegex } from 'path-to-regexp'
Expand Down Expand Up @@ -451,7 +452,7 @@ export default class Server {
type: route.type,
statusCode: (route as Redirect).statusCode,
name: `${route.type} ${route.source} route`,
fn: async (_req, res, params, _parsedUrl) => {
fn: async (req, res, params, _parsedUrl) => {
const parsedDestination = parseUrl(route.destination, true)
const destQuery = parsedDestination.query
let destinationCompiler = compilePathToRegex(
Expand Down Expand Up @@ -485,15 +486,15 @@ export default class Server {
throw err
}

if (route.type === 'redirect') {
const parsedNewUrl = parseUrl(newUrl)
const updatedDestination = formatUrl({
...parsedDestination,
pathname: parsedNewUrl.pathname,
hash: parsedNewUrl.hash,
search: undefined,
})
const parsedNewUrl = parseUrl(newUrl)
const updatedDestination = formatUrl({
...parsedDestination,
pathname: parsedNewUrl.pathname,
hash: parsedNewUrl.hash,
search: undefined,
})

if (route.type === 'redirect') {
res.setHeader('Location', updatedDestination)
res.statusCode = getRedirectStatus(route as Redirect)

Expand All @@ -508,7 +509,26 @@ export default class Server {
finished: true,
}
} else {
;(_req as any)._nextDidRewrite = true
// external rewrite, proxy it
if (parsedDestination.protocol) {
const proxy = new Proxy({
target: updatedDestination,
changeOrigin: true,
ignorePath: true,
})
proxy.web(req, res)

proxy.on('error', (err: Error) => {
console.error(
`Error occurred proxying ${updatedDestination}`,
err
)
})
return {
finished: true,
}
}
;(req as any)._nextDidRewrite = true
}

return {
Expand Down
1 change: 1 addition & 0 deletions packages/next/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@
"fork-ts-checker-webpack-plugin": "3.1.1",
"fresh": "0.5.2",
"gzip-size": "5.1.1",
"http-proxy": "1.18.0",
"ignore-loader": "0.1.2",
"is-docker": "2.0.0",
"is-wsl": "2.1.1",
Expand Down
29 changes: 22 additions & 7 deletions packages/next/telemetry/events/build.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,34 @@
const REGEXP_DIRECTORY_DUNDER = /[\\/]__[^\\/]+(?<![\\/]__(?:tests|mocks))__[\\/]/i
const REGEXP_DIRECTORY_TESTS = /[\\/]__(tests|mocks)__[\\/]/i
const REGEXP_FILE_TEST = /\.(?:spec|test)\.[^.]+$/i

const EVENT_BUILD_DURATION = 'NEXT_BUILD_COMPLETED'
type EventBuildCompleted = {
durationInSeconds: number
totalPageCount: number
hasDunderPages: boolean
hasTestPages: boolean
}

export function eventBuildDuration(
event: EventBuildCompleted
export function eventBuildCompleted(
pagePaths: string[],
event: Omit<
EventBuildCompleted,
'totalPageCount' | 'hasDunderPages' | 'hasTestPages'
>
): { eventName: string; payload: EventBuildCompleted } {
return {
eventName: EVENT_BUILD_DURATION,
payload: event,
payload: {
...event,
totalPageCount: pagePaths.length,
hasDunderPages: pagePaths.some(path =>
REGEXP_DIRECTORY_DUNDER.test(path)
),
hasTestPages: pagePaths.some(
path => REGEXP_DIRECTORY_TESTS.test(path) || REGEXP_FILE_TEST.test(path)
),
},
}
}

Expand All @@ -23,10 +42,6 @@ type EventBuildOptimized = {
hasTestPages: boolean
}

const REGEXP_DIRECTORY_DUNDER = /[\\/]__[^\\/]+(?<![\\/]__(?:tests|mocks))__[\\/]/i
const REGEXP_DIRECTORY_TESTS = /[\\/]__(tests|mocks)__[\\/]/i
const REGEXP_FILE_TEST = /\.(?:spec|test)\.[^.]+$/i

export function eventBuildOptimize(
pagePaths: string[],
event: Omit<
Expand Down
4 changes: 4 additions & 0 deletions test/integration/custom-routes/next.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@ module.exports = {
source: '/hidden/_next/:path*',
destination: '/_next/:path*',
},
{
source: '/proxy-me/:path*',
destination: 'http://localhost:__EXTERNAL_PORT__/:path*',
},
{
source: '/api-hello',
destination: '/api/hello',
Expand Down
47 changes: 46 additions & 1 deletion test/integration/custom-routes/test/index.test.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/* eslint-env jest */
/* global jasmine */
import http from 'http'
import url from 'url'
import stripAnsi from 'strip-ansi'
import fs from 'fs-extra'
Expand All @@ -24,9 +25,13 @@ jasmine.DEFAULT_TIMEOUT_INTERVAL = 1000 * 60 * 2

let appDir = join(__dirname, '..')
const nextConfigPath = join(appDir, 'next.config.js')
let externalServerHits = new Set()
let nextConfigRestoreContent
let nextConfigContent
let buildId
let externalServerPort
let externalServer
let stdout = ''
let buildId
let appPort
let app

Expand Down Expand Up @@ -228,6 +233,13 @@ const runTests = (isDev = false) => {
expect(res.headers.get('x-second-header')).toBe('second')
})

it('should support proxying to external resource', async () => {
const res = await fetchViaHTTP(appPort, '/proxy-me/first')
expect(res.status).toBe(200)
expect([...externalServerHits]).toEqual(['/first'])
expect(await res.text()).toContain('hi from external')
})

it('should support unnamed parameters correctly', async () => {
const res = await fetchViaHTTP(appPort, '/unnamed/first/final', undefined, {
redirect: 'manual',
Expand Down Expand Up @@ -493,6 +505,13 @@ const runTests = (isDev = false) => {
),
source: '/hidden/_next/:path*',
},
{
destination: `http://localhost:${externalServerPort}/:path*`,
regex: normalizeRegEx(
'^\\/proxy-me(?:\\/((?:[^\\/]+?)(?:\\/(?:[^\\/]+?))*))?$'
),
source: '/proxy-me/:path*',
},
{
destination: '/api/hello',
regex: normalizeRegEx('^\\/api-hello$'),
Expand Down Expand Up @@ -550,6 +569,32 @@ const runTests = (isDev = false) => {
}

describe('Custom routes', () => {
beforeEach(() => {
externalServerHits = new Set()
})
beforeAll(async () => {
externalServerPort = await findPort()
externalServer = http.createServer((req, res) => {
externalServerHits.add(req.url)
res.end('hi from external')
})
await new Promise((resolve, reject) => {
externalServer.listen(externalServerPort, error => {
if (error) return reject(error)
resolve()
})
})
nextConfigRestoreContent = await fs.readFile(nextConfigPath, 'utf8')
await fs.writeFile(
nextConfigPath,
nextConfigRestoreContent.replace(/__EXTERNAL_PORT__/, externalServerPort)
)
})
afterAll(async () => {
externalServer.close()
await fs.writeFile(nextConfigPath, nextConfigRestoreContent)
})

describe('dev mode', () => {
beforeAll(async () => {
appPort = await findPort()
Expand Down
2 changes: 1 addition & 1 deletion test/integration/invalid-custom-routes/test/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ const runTests = () => {
)

expect(stderr).toContain(
`\`destination\` does not start with / for route {"source":"/hello","destination":"another"}`
`\`destination\` does not start with \`/\`, \`http://\`, or \`https://\` for route {"source":"/hello","destination":"another"}`
)

expect(stderr).toContain(
Expand Down
9 changes: 7 additions & 2 deletions test/integration/telemetry/test/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -108,8 +108,13 @@ describe('Telemetry CLI', () => {
path.join(appDir, 'pages', 'hello.test.skip')
)

expect(stderr).toMatch(/hasDunderPages.*?true/)
expect(stderr).toMatch(/hasTestPages.*?true/)
const event1 = /NEXT_BUILD_COMPLETED[\s\S]+?{([\s\S]+?)}/.exec(stderr).pop()
expect(event1).toMatch(/hasDunderPages.*?true/)
expect(event1).toMatch(/hasTestPages.*?true/)

const event2 = /NEXT_BUILD_OPTIMIZED[\s\S]+?{([\s\S]+?)}/.exec(stderr).pop()
expect(event2).toMatch(/hasDunderPages.*?true/)
expect(event2).toMatch(/hasTestPages.*?true/)
})

it('detects isSrcDir dir correctly for `next dev`', async () => {
Expand Down
Loading

0 comments on commit 14e30be

Please sign in to comment.