mirror of
https://github.com/terribleplan/next.js.git
synced 2024-01-19 02:48:18 +00:00
Improved next/asset support (#3664)
* Allow next/asset to work properly with dynamic assetPrefix Now we use webpack's publicPath via client side. * Add test cases for dynamic assetPrefix and next/asset.
This commit is contained in:
parent
f046c0f6c2
commit
60cb06c1ba
|
@ -30,7 +30,10 @@ const {
|
||||||
location
|
location
|
||||||
} = window
|
} = window
|
||||||
|
|
||||||
// With this, static assets will work across zones
|
// With dynamic assetPrefix it's no longer possible to set assetPrefix at the build time
|
||||||
|
// So, this is how we do it in the client side at runtime
|
||||||
|
__webpack_public_path__ = `${assetPrefix}/_next/webpack/` //eslint-disable-line
|
||||||
|
// Initialize next/asset with the assetPrefix
|
||||||
asset.setAssetPrefix(assetPrefix)
|
asset.setAssetPrefix(assetPrefix)
|
||||||
|
|
||||||
const asPath = getURL()
|
const asPath = getURL()
|
||||||
|
|
|
@ -1,6 +1,11 @@
|
||||||
let assetPrefix
|
let assetPrefix
|
||||||
|
|
||||||
export default function asset (path) {
|
export default function asset (path) {
|
||||||
|
// If the URL starts with http, we assume it's an
|
||||||
|
if (/^https?:\/\//.test(path)) {
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
|
||||||
const pathWithoutSlash = path.replace(/^\//, '')
|
const pathWithoutSlash = path.replace(/^\//, '')
|
||||||
return `${assetPrefix}/static/${pathWithoutSlash}`
|
return `${assetPrefix}/static/${pathWithoutSlash}`
|
||||||
}
|
}
|
||||||
|
|
|
@ -126,7 +126,6 @@ export default async function getBaseWebpackConfig (dir, {dev = false, isServer
|
||||||
path: path.join(dir, config.distDir, isServer ? 'dist' : ''), // server compilation goes to `.next/dist`
|
path: path.join(dir, config.distDir, isServer ? 'dist' : ''), // server compilation goes to `.next/dist`
|
||||||
filename: '[name]',
|
filename: '[name]',
|
||||||
libraryTarget: 'commonjs2',
|
libraryTarget: 'commonjs2',
|
||||||
publicPath: `${config.assetPrefix}/_next/webpack/`,
|
|
||||||
// This saves chunks with the name given via require.ensure()
|
// This saves chunks with the name given via require.ensure()
|
||||||
chunkFilename: '[name]-[chunkhash].js',
|
chunkFilename: '[name]-[chunkhash].js',
|
||||||
strictModuleExceptionHandling: true,
|
strictModuleExceptionHandling: true,
|
||||||
|
|
10
test/integration/basic/pages/using-asset/asset.js
Normal file
10
test/integration/basic/pages/using-asset/asset.js
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
import asset from 'next/asset'
|
||||||
|
|
||||||
|
export default () => (
|
||||||
|
<div id='asset-page'>
|
||||||
|
<img id='img1' src={asset('/the-image')} />
|
||||||
|
<img id='img2' src={asset('the-image')} />
|
||||||
|
<img id='img3' src={asset('http://the-image.com/the-image')} />
|
||||||
|
<img id='img4' src={asset('https://the-image.com/the-image')} />
|
||||||
|
</div>
|
||||||
|
)
|
9
test/integration/basic/pages/using-asset/index.js
Normal file
9
test/integration/basic/pages/using-asset/index.js
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
import Link from 'next/link'
|
||||||
|
|
||||||
|
export default () => (
|
||||||
|
<div>
|
||||||
|
<Link href='/using-asset/asset'>
|
||||||
|
<a id='go-asset'>Asset</a>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
)
|
57
test/integration/basic/test/asset.js
Normal file
57
test/integration/basic/test/asset.js
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
/* global describe, it, expect */
|
||||||
|
import cheerio from 'cheerio'
|
||||||
|
import {
|
||||||
|
renderViaHTTP
|
||||||
|
} from 'next-test-utils'
|
||||||
|
import webdriver from 'next-webdriver'
|
||||||
|
|
||||||
|
export default (context) => {
|
||||||
|
async function get$ (path) {
|
||||||
|
const html = await renderViaHTTP(context.appPort, path)
|
||||||
|
return cheerio.load(html)
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('With next/asset', () => {
|
||||||
|
describe('with SSR', () => {
|
||||||
|
it('should handle beginning slash properly', async () => {
|
||||||
|
const $ = await get$('/using-asset/asset')
|
||||||
|
expect($('#img1').attr('src')).toBe('/static/the-image')
|
||||||
|
expect($('#img2').attr('src')).toBe('/static/the-image')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should handle http(s) properly', async () => {
|
||||||
|
const $ = await get$('/using-asset/asset')
|
||||||
|
expect($('#img3').attr('src')).toBe('http://the-image.com/the-image')
|
||||||
|
expect($('#img4').attr('src')).toBe('https://the-image.com/the-image')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('with client navigation', () => {
|
||||||
|
it('should handle beginning slash properly', async () => {
|
||||||
|
const browser = await webdriver(context.appPort, '/using-asset')
|
||||||
|
await browser
|
||||||
|
.elementByCss('#go-asset').click()
|
||||||
|
.waitForElementByCss('#asset-page')
|
||||||
|
|
||||||
|
expect(await browser.elementByCss('#img1').getAttribute('src'))
|
||||||
|
.toBe(`http://localhost:${context.appPort}/static/the-image`)
|
||||||
|
expect(await browser.elementByCss('#img2').getAttribute('src'))
|
||||||
|
.toBe(`http://localhost:${context.appPort}/static/the-image`)
|
||||||
|
browser.close()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should handle http(s) properly', async () => {
|
||||||
|
const browser = await webdriver(context.appPort, '/using-asset')
|
||||||
|
await browser
|
||||||
|
.elementByCss('#go-asset').click()
|
||||||
|
.waitForElementByCss('#asset-page')
|
||||||
|
|
||||||
|
expect(await browser.elementByCss('#img3').getAttribute('src'))
|
||||||
|
.toBe('http://the-image.com/the-image')
|
||||||
|
expect(await browser.elementByCss('#img4').getAttribute('src'))
|
||||||
|
.toBe('https://the-image.com/the-image')
|
||||||
|
browser.close()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
|
@ -14,6 +14,7 @@ import rendering from './rendering'
|
||||||
import clientNavigation from './client-navigation'
|
import clientNavigation from './client-navigation'
|
||||||
import hmr from './hmr'
|
import hmr from './hmr'
|
||||||
import dynamic from './dynamic'
|
import dynamic from './dynamic'
|
||||||
|
import asset from './asset'
|
||||||
|
|
||||||
const context = {}
|
const context = {}
|
||||||
jasmine.DEFAULT_TIMEOUT_INTERVAL = 1000 * 60 * 5
|
jasmine.DEFAULT_TIMEOUT_INTERVAL = 1000 * 60 * 5
|
||||||
|
@ -60,4 +61,5 @@ describe('Basic Features', () => {
|
||||||
clientNavigation(context, (p, q) => renderViaHTTP(context.appPort, p, q))
|
clientNavigation(context, (p, q) => renderViaHTTP(context.appPort, p, q))
|
||||||
dynamic(context, (p, q) => renderViaHTTP(context.appPort, p, q))
|
dynamic(context, (p, q) => renderViaHTTP(context.appPort, p, q))
|
||||||
hmr(context, (p, q) => renderViaHTTP(context.appPort, p, q))
|
hmr(context, (p, q) => renderViaHTTP(context.appPort, p, q))
|
||||||
|
asset(context)
|
||||||
})
|
})
|
||||||
|
|
7
test/integration/custom-server/pages/asset.js
Normal file
7
test/integration/custom-server/pages/asset.js
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
import asset from 'next/asset'
|
||||||
|
|
||||||
|
export default () => (
|
||||||
|
<div id='asset-page'>
|
||||||
|
<img src={asset('/myimage.png')} />
|
||||||
|
</div>
|
||||||
|
)
|
|
@ -1,3 +1,9 @@
|
||||||
|
import Link from 'next/link'
|
||||||
|
|
||||||
export default () => (
|
export default () => (
|
||||||
<div>My Homepage</div>
|
<div>
|
||||||
|
<Link href='/asset'>
|
||||||
|
<a id='go-asset'>Asset</a>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
)
|
)
|
||||||
|
|
|
@ -11,7 +11,7 @@ const handleNextRequests = app.getRequestHandler()
|
||||||
app.prepare().then(() => {
|
app.prepare().then(() => {
|
||||||
const server = micro((req, res) => {
|
const server = micro((req, res) => {
|
||||||
if (/setAssetPrefix/.test(req.url)) {
|
if (/setAssetPrefix/.test(req.url)) {
|
||||||
app.setAssetPrefix('https://cdn.com/myapp')
|
app.setAssetPrefix(`http://127.0.0.1:${port}`)
|
||||||
} else {
|
} else {
|
||||||
// This is to support multi-zones support in localhost
|
// This is to support multi-zones support in localhost
|
||||||
// and may be in staging deployments
|
// and may be in staging deployments
|
||||||
|
|
|
@ -3,11 +3,13 @@
|
||||||
import { join } from 'path'
|
import { join } from 'path'
|
||||||
import getPort from 'get-port'
|
import getPort from 'get-port'
|
||||||
import clone from 'clone'
|
import clone from 'clone'
|
||||||
|
import cheerio from 'cheerio'
|
||||||
import {
|
import {
|
||||||
initNextServerScript,
|
initNextServerScript,
|
||||||
killApp,
|
killApp,
|
||||||
renderViaHTTP
|
renderViaHTTP
|
||||||
} from 'next-test-utils'
|
} from 'next-test-utils'
|
||||||
|
import webdriver from 'next-webdriver'
|
||||||
|
|
||||||
const appDir = join(__dirname, '../')
|
const appDir = join(__dirname, '../')
|
||||||
let appPort
|
let appPort
|
||||||
|
@ -29,23 +31,51 @@ describe('Custom Server', () => {
|
||||||
|
|
||||||
describe('with dynamic assetPrefix', () => {
|
describe('with dynamic assetPrefix', () => {
|
||||||
it('should set the assetPrefix dynamically', async () => {
|
it('should set the assetPrefix dynamically', async () => {
|
||||||
const normalUsage = await renderViaHTTP(appPort, '/')
|
const normalUsage = await renderViaHTTP(appPort, '/asset')
|
||||||
expect(normalUsage).not.toMatch(/cdn\.com\/myapp/)
|
expect(normalUsage).not.toMatch(/127\.0\.0\.1/)
|
||||||
|
|
||||||
const dynamicUsage = await renderViaHTTP(appPort, '/?setAssetPrefix=1')
|
const dynamicUsage = await renderViaHTTP(appPort, '/asset?setAssetPrefix=1')
|
||||||
expect(dynamicUsage).toMatch(/cdn\.com\/myapp/)
|
expect(dynamicUsage).toMatch(/127\.0\.0\.1/)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should set the assetPrefix to a given request', async () => {
|
it('should set the assetPrefix to a given request', async () => {
|
||||||
for (let lc = 0; lc < 1000; lc++) {
|
for (let lc = 0; lc < 1000; lc++) {
|
||||||
const [normalUsage, dynamicUsage] = await Promise.all([
|
const [normalUsage, dynamicUsage] = await Promise.all([
|
||||||
await renderViaHTTP(appPort, '/'),
|
await renderViaHTTP(appPort, '/asset'),
|
||||||
await renderViaHTTP(appPort, '/?setAssetPrefix=1')
|
await renderViaHTTP(appPort, '/asset?setAssetPrefix=1')
|
||||||
])
|
])
|
||||||
|
|
||||||
expect(normalUsage).not.toMatch(/cdn\.com\/myapp/)
|
expect(normalUsage).not.toMatch(/127\.0\.0\.1/)
|
||||||
expect(dynamicUsage).toMatch(/cdn\.com\/myapp/)
|
expect(dynamicUsage).toMatch(/127\.0\.0\.1/)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('should support next/asset in server side', async () => {
|
||||||
|
const $normal = cheerio.load(await renderViaHTTP(appPort, '/asset'))
|
||||||
|
expect($normal('img').attr('src')).toBe('/static/myimage.png')
|
||||||
|
|
||||||
|
const $dynamic = cheerio.load(await renderViaHTTP(appPort, '/asset?setAssetPrefix=1'))
|
||||||
|
expect($dynamic('img').attr('src')).toBe(`http://127.0.0.1:${context.appPort}/static/myimage.png`)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should support next/asset in client side', async() => {
|
||||||
|
const browser = await webdriver(context.appPort, '/')
|
||||||
|
await browser
|
||||||
|
.elementByCss('#go-asset').click()
|
||||||
|
.waitForElementByCss('#asset-page')
|
||||||
|
|
||||||
|
expect(await browser.elementByCss('img').getAttribute('src'))
|
||||||
|
.toBe(`http://localhost:${context.appPort}/static/myimage.png`)
|
||||||
|
browser.close()
|
||||||
|
|
||||||
|
const browser2 = await webdriver(context.appPort, '/?setAssetPrefix=1')
|
||||||
|
await browser2
|
||||||
|
.elementByCss('#go-asset').click()
|
||||||
|
.waitForElementByCss('#asset-page')
|
||||||
|
|
||||||
|
expect(await browser2.elementByCss('img').getAttribute('src'))
|
||||||
|
.toBe(`http://127.0.0.1:${context.appPort}/static/myimage.png`)
|
||||||
|
browser2.close()
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
Loading…
Reference in a new issue