1
0
Fork 0
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:
Arunoda Susiripala 2018-02-02 20:13:36 +05:30 committed by Tim Neutkens
parent d373b893d1
commit 4c7f6cc76a
8 changed files with 200 additions and 13 deletions

View file

@ -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",

View file

@ -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

View file

@ -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()

View file

@ -0,0 +1,3 @@
export default () => (
<div>My Homepage</div>
)

View 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}`)
})
})

View 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/)
}
})
})
})

View file

@ -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)

View file

@ -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"