1
0
Fork 0
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:
Arunoda Susiripala 2018-02-03 01:39:24 +05:30 committed by Tim Neutkens
parent f046c0f6c2
commit 60cb06c1ba
11 changed files with 140 additions and 12 deletions

View file

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

View file

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

View file

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

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

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

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

View file

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

View file

@ -0,0 +1,7 @@
import asset from 'next/asset'
export default () => (
<div id='asset-page'>
<img src={asset('/myimage.png')} />
</div>
)

View file

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

View file

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

View file

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