mirror of
https://github.com/terribleplan/next.js.git
synced 2024-01-19 02:48:18 +00:00
Set assetPrefix dynamically (#3661)
* Introduce the setAssetPrefix API for dynamic assetPrefix. * Remove unwanted node_modules dir in a test dir. * Change test main description.
This commit is contained in:
parent
d373b893d1
commit
4c7f6cc76a
|
@ -120,13 +120,16 @@
|
|||
"benchmark": "2.1.4",
|
||||
"cheerio": "0.22.0",
|
||||
"chromedriver": "2.32.3",
|
||||
"clone": "2.1.1",
|
||||
"coveralls": "2.13.1",
|
||||
"cross-env": "5.0.5",
|
||||
"express": "4.15.5",
|
||||
"fkill": "5.1.0",
|
||||
"get-port": "3.2.0",
|
||||
"husky": "0.14.3",
|
||||
"jest-cli": "21.2.0",
|
||||
"lint-staged": "4.2.3",
|
||||
"micro": "9.1.0",
|
||||
"mkdirp": "0.5.1",
|
||||
"node-fetch": "1.7.3",
|
||||
"node-notifier": "5.1.2",
|
||||
|
|
45
readme.md
45
readme.md
|
@ -1,6 +1,6 @@
|
|||
<img width="112" alt="screen shot 2016-10-25 at 2 37 27 pm" src="https://cloud.githubusercontent.com/assets/13041/19686250/971bf7f8-9ac0-11e6-975c-188defd82df1.png">
|
||||
|
||||
[![NPM version](https://img.shields.io/npm/v/next.svg)](https://www.npmjs.com/package/next)
|
||||
[![NPM version](https://img.shields.io/npm/v/next.svg)](https://www.npmjs.com/package/next)
|
||||
[![Build Status](https://travis-ci.org/zeit/next.js.svg?branch=master)](https://travis-ci.org/zeit/next.js)
|
||||
[![Build status](https://ci.appveyor.com/api/projects/status/gqp5hs71l3ebtx1r/branch/master?svg=true)](https://ci.appveyor.com/project/arunoda/next-js/branch/master)
|
||||
[![Coverage Status](https://coveralls.io/repos/zeit/next.js/badge.svg?branch=master)](https://coveralls.io/r/zeit/next.js?branch=master)
|
||||
|
@ -224,7 +224,7 @@ export default () => (
|
|||
```
|
||||
|
||||
In this case only the second `<meta name="viewport" />` is rendered.
|
||||
|
||||
|
||||
_Note: The contents of `<head>` get cleared upon unmounting the component, so make sure each page completely defines what it needs in `<head>`, without making assumptions about what other pages added_
|
||||
|
||||
### Fetching data and component lifecycle
|
||||
|
@ -744,9 +744,9 @@ Then, change your `start` script to `NODE_ENV=production node server.js`.
|
|||
#### Disabling file-system routing
|
||||
By default, `Next` will serve each file in `/pages` under a pathname matching the filename (eg, `/pages/some-file.js` is served at `site.com/some-file`.
|
||||
|
||||
If your project uses custom routing, this behavior may result in the same content being served from multiple paths, which can present problems with SEO and UX.
|
||||
If your project uses custom routing, this behavior may result in the same content being served from multiple paths, which can present problems with SEO and UX.
|
||||
|
||||
To disable this behavior & prevent routing based on files in `/pages`, simply set the following option in your `next.config.js`:
|
||||
To disable this behavior & prevent routing based on files in `/pages`, simply set the following option in your `next.config.js`:
|
||||
|
||||
```js
|
||||
// next.config.js
|
||||
|
@ -755,6 +755,43 @@ module.exports = {
|
|||
}
|
||||
```
|
||||
|
||||
#### Dynamic assetPrefix
|
||||
|
||||
Sometimes we need to set the `assetPrefix` dynamically. This is useful when changing the `assetPrefix` based on incoming requests.
|
||||
For that, we can use `app.setAssetPrefix`.
|
||||
|
||||
Here's an example usage of it:
|
||||
|
||||
```js
|
||||
const next = require('next')
|
||||
const micro = require('micro')
|
||||
|
||||
const dev = process.env.NODE_ENV !== 'production'
|
||||
const app = next({ dev })
|
||||
const handle = app.getRequestHandler()
|
||||
|
||||
app.prepare().then(() => {
|
||||
const server = micro((req, res) => {
|
||||
// Add assetPrefix support based on the hostname
|
||||
if (req.headers.host === 'my-app.com') {
|
||||
app.setAssetPrefix('http://cdn.com/myapp')
|
||||
} else {
|
||||
app.setAssetPrefix('')
|
||||
}
|
||||
|
||||
handleNextRequests(req, res)
|
||||
})
|
||||
|
||||
server.listen(port, (err) => {
|
||||
if (err) {
|
||||
throw err
|
||||
}
|
||||
|
||||
console.log(`> Ready on http://localhost:${port}`)
|
||||
})
|
||||
})
|
||||
|
||||
```
|
||||
|
||||
### Dynamic Import
|
||||
|
||||
|
|
|
@ -48,13 +48,10 @@ export default class Server {
|
|||
hotReloader: this.hotReloader,
|
||||
buildStats: this.buildStats,
|
||||
buildId: this.buildId,
|
||||
assetPrefix: this.config.assetPrefix.replace(/\/$/, ''),
|
||||
availableChunks: dev ? {} : getAvailableChunks(this.dir, this.dist)
|
||||
}
|
||||
|
||||
// With this, static assets will work across zones
|
||||
asset.setAssetPrefix(this.config.assetPrefix)
|
||||
|
||||
this.setAssetPrefix(this.config.assetPrefix)
|
||||
this.defineRoutes()
|
||||
}
|
||||
|
||||
|
@ -87,6 +84,11 @@ export default class Server {
|
|||
return this.handleRequest.bind(this)
|
||||
}
|
||||
|
||||
setAssetPrefix (prefix) {
|
||||
this.renderOpts.assetPrefix = prefix.replace(/\/$/, '')
|
||||
asset.setAssetPrefix(this.renderOpts.assetPrefix)
|
||||
}
|
||||
|
||||
async prepare () {
|
||||
if (this.hotReloader) {
|
||||
await this.hotReloader.start()
|
||||
|
|
3
test/integration/custom-server/pages/index.js
Normal file
3
test/integration/custom-server/pages/index.js
Normal file
|
@ -0,0 +1,3 @@
|
|||
export default () => (
|
||||
<div>My Homepage</div>
|
||||
)
|
31
test/integration/custom-server/server.js
Normal file
31
test/integration/custom-server/server.js
Normal file
|
@ -0,0 +1,31 @@
|
|||
const micro = require('micro')
|
||||
const next = require('next')
|
||||
|
||||
const dev = process.env.NODE_ENV !== 'production'
|
||||
const dir = __dirname
|
||||
const port = process.env.PORT || 3000
|
||||
|
||||
const app = next({ dev, dir })
|
||||
const handleNextRequests = app.getRequestHandler()
|
||||
|
||||
app.prepare().then(() => {
|
||||
const server = micro((req, res) => {
|
||||
if (/setAssetPrefix/.test(req.url)) {
|
||||
app.setAssetPrefix('https://cdn.com/myapp')
|
||||
} else {
|
||||
// This is to support multi-zones support in localhost
|
||||
// and may be in staging deployments
|
||||
app.setAssetPrefix('')
|
||||
}
|
||||
|
||||
handleNextRequests(req, res)
|
||||
})
|
||||
|
||||
server.listen(port, (err) => {
|
||||
if (err) {
|
||||
throw err
|
||||
}
|
||||
|
||||
console.log(`> Ready on http://localhost:${port}`)
|
||||
})
|
||||
})
|
51
test/integration/custom-server/test/index.test.js
Normal file
51
test/integration/custom-server/test/index.test.js
Normal file
|
@ -0,0 +1,51 @@
|
|||
/* global jasmine, describe, it, expect, beforeAll, afterAll */
|
||||
|
||||
import { join } from 'path'
|
||||
import getPort from 'get-port'
|
||||
import clone from 'clone'
|
||||
import {
|
||||
initNextServerScript,
|
||||
killApp,
|
||||
renderViaHTTP
|
||||
} from 'next-test-utils'
|
||||
|
||||
const appDir = join(__dirname, '../')
|
||||
let appPort
|
||||
let server
|
||||
jasmine.DEFAULT_TIMEOUT_INTERVAL = 1000 * 60 * 2
|
||||
|
||||
const context = {}
|
||||
|
||||
describe('Custom Server', () => {
|
||||
beforeAll(async () => {
|
||||
const scriptPath = join(appDir, 'server.js')
|
||||
context.appPort = appPort = await getPort()
|
||||
const env = clone(process.env)
|
||||
env.PORT = `${appPort}`
|
||||
|
||||
server = await initNextServerScript(scriptPath, /Ready on/, env)
|
||||
})
|
||||
afterAll(() => killApp(server))
|
||||
|
||||
describe('with dynamic assetPrefix', () => {
|
||||
it('should set the assetPrefix dynamically', async () => {
|
||||
const normalUsage = await renderViaHTTP(appPort, '/')
|
||||
expect(normalUsage).not.toMatch(/cdn\.com\/myapp/)
|
||||
|
||||
const dynamicUsage = await renderViaHTTP(appPort, '/?setAssetPrefix=1')
|
||||
expect(dynamicUsage).toMatch(/cdn\.com\/myapp/)
|
||||
})
|
||||
|
||||
it('should set the assetPrefix to a given request', async () => {
|
||||
for (let lc = 0; lc < 1000; lc++) {
|
||||
const [normalUsage, dynamicUsage] = await Promise.all([
|
||||
await renderViaHTTP(appPort, '/'),
|
||||
await renderViaHTTP(appPort, '/?setAssetPrefix=1')
|
||||
])
|
||||
|
||||
expect(normalUsage).not.toMatch(/cdn\.com\/myapp/)
|
||||
expect(dynamicUsage).toMatch(/cdn\.com\/myapp/)
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
|
@ -17,6 +17,36 @@ export const nextBuild = build
|
|||
export const nextExport = _export
|
||||
export const pkg = _pkg
|
||||
|
||||
export function initNextServerScript (scriptPath, successRegexp, env) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const instance = spawn('node', [scriptPath], { env })
|
||||
|
||||
function handleStdout (data) {
|
||||
const message = data.toString()
|
||||
if (successRegexp.test(message)) {
|
||||
resolve(instance)
|
||||
}
|
||||
process.stdout.write(message)
|
||||
}
|
||||
|
||||
function handleStderr (data) {
|
||||
process.stderr.write(data.toString())
|
||||
}
|
||||
|
||||
instance.stdout.on('data', handleStdout)
|
||||
instance.stderr.on('data', handleStderr)
|
||||
|
||||
instance.on('close', () => {
|
||||
instance.stdout.removeListener('data', handleStdout)
|
||||
instance.stderr.removeListener('data', handleStderr)
|
||||
})
|
||||
|
||||
instance.on('error', (err) => {
|
||||
reject(err)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
export function renderViaAPI (app, pathname, query) {
|
||||
const url = `${pathname}${query ? `?${qs.stringify(query)}` : ''}`
|
||||
return app.renderToHTML({ url }, {}, pathname, query)
|
||||
|
|
40
yarn.lock
40
yarn.lock
|
@ -1259,6 +1259,10 @@ builtin-status-codes@^3.0.0:
|
|||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8"
|
||||
|
||||
bytes@3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048"
|
||||
|
||||
cacache@^10.0.1:
|
||||
version "10.0.2"
|
||||
resolved "https://registry.yarnpkg.com/cacache/-/cacache-10.0.2.tgz#105a93a162bbedf3a25da42e1939ed99ffb145f8"
|
||||
|
@ -1458,6 +1462,10 @@ cliui@^3.2.0:
|
|||
strip-ansi "^3.0.1"
|
||||
wrap-ansi "^2.0.0"
|
||||
|
||||
clone@2.1.1:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.1.tgz#d217d1e961118e3ac9a4b8bba3285553bf647cdb"
|
||||
|
||||
clor@^5.1.0:
|
||||
version "5.2.0"
|
||||
resolved "https://registry.yarnpkg.com/clor/-/clor-5.2.0.tgz#9ddc74e7e86728cfcd05a80546ba58d317b81035"
|
||||
|
@ -1541,7 +1549,7 @@ content-type-parser@^1.0.1:
|
|||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/content-type-parser/-/content-type-parser-1.0.1.tgz#c3e56988c53c65127fb46d4032a3a900246fdc94"
|
||||
|
||||
content-type@~1.0.2:
|
||||
content-type@1.0.4, content-type@~1.0.2:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b"
|
||||
|
||||
|
@ -2657,6 +2665,10 @@ get-own-enumerable-property-symbols@^2.0.1:
|
|||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-2.0.1.tgz#5c4ad87f2834c4b9b4e84549dc1e0650fb38c24b"
|
||||
|
||||
get-port@3.2.0:
|
||||
version "3.2.0"
|
||||
resolved "https://registry.yarnpkg.com/get-port/-/get-port-3.2.0.tgz#dd7ce7de187c06c8bf353796ac71e099f0980ebc"
|
||||
|
||||
get-stdin@^5.0.1:
|
||||
version "5.0.1"
|
||||
resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-5.0.1.tgz#122e161591e21ff4c52530305693f20e6393a398"
|
||||
|
@ -2957,7 +2969,7 @@ iconv-lite@0.4.13:
|
|||
version "0.4.13"
|
||||
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.13.tgz#1f88aba4ab0b1508e8312acc39345f36e992e2f2"
|
||||
|
||||
iconv-lite@~0.4.13:
|
||||
iconv-lite@0.4.19, iconv-lite@~0.4.13:
|
||||
version "0.4.19"
|
||||
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.19.tgz#f7468f60135f5e5dad3399c0a81be9a1603a082b"
|
||||
|
||||
|
@ -3203,7 +3215,7 @@ is-resolvable@^1.0.0:
|
|||
dependencies:
|
||||
tryit "^1.0.1"
|
||||
|
||||
is-stream@^1.0.1, is-stream@^1.1.0:
|
||||
is-stream@1.1.0, is-stream@^1.0.1, is-stream@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44"
|
||||
|
||||
|
@ -3961,6 +3973,15 @@ methods@~1.1.2:
|
|||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee"
|
||||
|
||||
micro@9.1.0:
|
||||
version "9.1.0"
|
||||
resolved "https://registry.yarnpkg.com/micro/-/micro-9.1.0.tgz#f2effba306639076e994c007c327dfc36a5185e9"
|
||||
dependencies:
|
||||
content-type "1.0.4"
|
||||
is-stream "1.1.0"
|
||||
mri "1.1.0"
|
||||
raw-body "2.3.2"
|
||||
|
||||
micromatch@^2.1.5, micromatch@^2.3.11:
|
||||
version "2.3.11"
|
||||
resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-2.3.11.tgz#86677c97d1720b363431d04d0d15293bd38c1565"
|
||||
|
@ -4093,7 +4114,7 @@ move-concurrently@^1.0.1:
|
|||
rimraf "^2.5.4"
|
||||
run-queue "^1.0.3"
|
||||
|
||||
mri@^1.1.0:
|
||||
mri@1.1.0, mri@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/mri/-/mri-1.1.0.tgz#5c0a3f29c8ccffbbb1ec941dcec09d71fa32f36a"
|
||||
|
||||
|
@ -4769,6 +4790,15 @@ range-parser@^1.0.3, range-parser@~1.2.0:
|
|||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.0.tgz#f49be6b487894ddc40dcc94a322f611092e00d5e"
|
||||
|
||||
raw-body@2.3.2:
|
||||
version "2.3.2"
|
||||
resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.3.2.tgz#bcd60c77d3eb93cde0050295c3f379389bc88f89"
|
||||
dependencies:
|
||||
bytes "3.0.0"
|
||||
http-errors "1.6.2"
|
||||
iconv-lite "0.4.19"
|
||||
unpipe "1.0.0"
|
||||
|
||||
rc@^1.1.7:
|
||||
version "1.2.2"
|
||||
resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.2.tgz#d8ce9cb57e8d64d9c7badd9876c7c34cbe3c7077"
|
||||
|
@ -5865,7 +5895,7 @@ unique-slug@^2.0.0:
|
|||
dependencies:
|
||||
imurmurhash "^0.1.4"
|
||||
|
||||
unpipe@~1.0.0:
|
||||
unpipe@1.0.0, unpipe@~1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec"
|
||||
|
||||
|
|
Loading…
Reference in a new issue