Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(webpack): use compact name for concatenated modules #7639

Merged
merged 10 commits into from Jun 30, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
11 changes: 7 additions & 4 deletions packages/config/src/config/build.js
Expand Up @@ -14,8 +14,8 @@ export default () => ({
serverURLPolyfill: 'url',
filenames: {
// { isDev, isClient, isServer }
app: ({ isDev, isModern }) => isDev ? `${isModern ? 'modern-' : ''}[name].js` : '[name].[contenthash:7].js',
chunk: ({ isDev, isModern }) => isDev ? `${isModern ? 'modern-' : ''}[name].js` : '[name].[contenthash:7].js',
app: ({ isDev, isModern }) => isDev ? `[name]${isModern ? '.modern' : ''}.js` : `[name].[contenthash:7]${isModern ? '.modern' : ''}.js`,
chunk: ({ isDev, isModern }) => isDev ? `[name]${isModern ? '.modern' : ''}.js` : `[name].[contenthash:7]${isModern ? '.modern' : ''}.js`,
css: ({ isDev }) => isDev ? '[name].css' : '[name].[contenthash:7].css',
img: ({ isDev }) => isDev ? '[path][name].[ext]' : 'img/[name].[contenthash:7].[ext]',
font: ({ isDev }) => isDev ? '[path][name].[ext]' : 'fonts/[name].[contenthash:7].[ext]',
Expand Down Expand Up @@ -62,9 +62,12 @@ export default () => ({
minimizer: undefined,
splitChunks: {
chunks: 'all',
automaticNameDelimiter: '.',
name: undefined,
cacheGroups: {}
cacheGroups: {
default: {
name: undefined
}
}
}
},
splitChunks: {
Expand Down
7 changes: 5 additions & 2 deletions packages/config/test/__snapshots__/options.test.js.snap
Expand Up @@ -112,8 +112,11 @@ Object {
"minimizer": undefined,
"runtimeChunk": "single",
"splitChunks": Object {
"automaticNameDelimiter": ".",
"cacheGroups": Object {},
"cacheGroups": Object {
"default": Object {
"name": undefined,
},
},
"chunks": "all",
"name": undefined,
},
Expand Down
14 changes: 10 additions & 4 deletions packages/config/test/config/__snapshots__/index.test.js.snap
Expand Up @@ -88,8 +88,11 @@ Object {
"minimizer": undefined,
"runtimeChunk": "single",
"splitChunks": Object {
"automaticNameDelimiter": ".",
"cacheGroups": Object {},
"cacheGroups": Object {
"default": Object {
"name": undefined,
},
},
"chunks": "all",
"name": undefined,
},
Expand Down Expand Up @@ -458,8 +461,11 @@ Object {
"minimizer": undefined,
"runtimeChunk": "single",
"splitChunks": Object {
"automaticNameDelimiter": ".",
"cacheGroups": Object {},
"cacheGroups": Object {
"default": Object {
"name": undefined,
},
},
"chunks": "all",
"name": undefined,
},
Expand Down
4 changes: 2 additions & 2 deletions packages/config/test/config/build.test.js
Expand Up @@ -26,7 +26,7 @@ describe('config: build', () => {
test('should return modern filenames', () => {
const { filenames } = buildConfig()
const env = { isDev: true, isModern: true }
expect(filenames.app(env)).toEqual('modern-[name].js')
expect(filenames.chunk(env)).toEqual('modern-[name].js')
expect(filenames.app(env)).toEqual('[name].modern.js')
expect(filenames.chunk(env)).toEqual('[name].modern.js')
})
})
5 changes: 2 additions & 3 deletions packages/utils/src/resolve.js
Expand Up @@ -17,10 +17,9 @@ export const wp = function wp (p = '') {
return p
}

// Kept for backward compat (modules may use it from template context)
export const wChunk = function wChunk (p = '') {
// workaround for SplitChunksPlugin that generate names starting from . for catchAll pages _.vue
// consider using https://webpack.js.org/configuration/output/#outputfilename for more robust control over filename generation
return p.replace('_', '[_]')
return p
}

const reqSep = /\//g
Expand Down
32 changes: 19 additions & 13 deletions packages/webpack/src/config/client.js
Expand Up @@ -38,16 +38,6 @@ export default class WebpackClientConfig extends WebpackBaseConfig {
}
}

getFileName (...args) {
if (this.buildContext.buildOptions.analyze) {
const [key] = args
if (['app', 'chunk'].includes(key)) {
return `${this.isModern ? 'modern-' : ''}[name].js`
}
}
return super.getFileName(...args)
}

env () {
return Object.assign(
super.env(),
Expand All @@ -63,18 +53,34 @@ export default class WebpackClientConfig extends WebpackBaseConfig {

optimization () {
const optimization = super.optimization()
const { splitChunks } = optimization
const { cacheGroups } = splitChunks

// Small, known and common modules which are usually used project-wise
// Sum of them may not be more than 244 KiB
if (
this.buildContext.buildOptions.splitChunks.commons === true &&
optimization.splitChunks.cacheGroups.commons === undefined
cacheGroups.commons === undefined
) {
optimization.splitChunks.cacheGroups.commons = {
cacheGroups.commons = {
test: /node_modules[\\/](vue|vue-loader|vue-router|vuex|vue-meta|core-js|@babel\/runtime|axios|webpack|setimmediate|timers-browserify|process|regenerator-runtime|cookie|js-cookie|is-buffer|dotprop|nuxt\.js)[\\/]/,
chunks: 'all',
priority: 10,
name: true
name: true,
automaticNameDelimiter: '/'
}
}

if (!this.dev && cacheGroups.default && cacheGroups.default.name === undefined) {
cacheGroups.default.name = (_module, chunks) => {
// Use default name for single chunks
if (chunks.length === 1) {
return chunks[0].name || ''
}
// Use compact name for concatinated modules
return 'commons/' + chunks.filter(c => c.name).map(c =>
c.name.replace(/\//g, '.').replace(/_/g, '').replace('pages.', '')
).join('~')
}
}

Expand Down
20 changes: 10 additions & 10 deletions test/dev/modern.client.test.js
@@ -1,4 +1,4 @@
import { loadFixture, getPort, Nuxt, rp, wChunk } from '../utils'
import { loadFixture, getPort, Nuxt, rp } from '../utils'

let nuxt, port
const url = route => 'http://localhost:' + port + route
Expand All @@ -16,28 +16,28 @@ describe('modern client mode (SSR)', () => {
test('should contain nomodule legacy resources', async () => {
const { body: response } = await rp(url('/'))
expect(response).toContain('script nomodule crossorigin="use-credentials" src="/_nuxt/app.js')
expect(response).toContain('script nomodule crossorigin="use-credentials" src="/_nuxt/commons.app.js')
expect(response).toContain('script nomodule crossorigin="use-credentials" src="/_nuxt/commons/app.js')
})

test('should contain module modern resources', async () => {
const { body: response } = await rp(url('/'))
expect(response).toContain('<script type="module" crossorigin="use-credentials" src="/_nuxt/modern-app.js"')
expect(response).toContain('<script type="module" crossorigin="use-credentials" src="/_nuxt/modern-commons.app.js"')
expect(response).toContain('<script type="module" crossorigin="use-credentials" src="/_nuxt/app.modern.js"')
expect(response).toContain('<script type="module" crossorigin="use-credentials" src="/_nuxt/commons/app.modern.js"')
})

test('should contain module preload resources', async () => {
const { body: response } = await rp(url('/'))
expect(response).toContain('<link rel="modulepreload" crossorigin="use-credentials" href="/_nuxt/modern-app.js" as="script">')
expect(response).toContain('<link rel="modulepreload" crossorigin="use-credentials" href="/_nuxt/modern-commons.app.js" as="script">')
expect(response).toContain('<link rel="modulepreload" crossorigin="use-credentials" href="/_nuxt/app.modern.js" as="script">')
expect(response).toContain('<link rel="modulepreload" crossorigin="use-credentials" href="/_nuxt/commons/app.modern.js" as="script">')
})

test('should contain module http2 pushed resources', async () => {
const { headers: { link } } = await rp(url('/'))
expect(link).toEqual([
'</_nuxt/modern-runtime.js>; rel=modulepreload; crossorigin=use-credentials; as=script',
'</_nuxt/modern-commons.app.js>; rel=modulepreload; crossorigin=use-credentials; as=script',
'</_nuxt/modern-app.js>; rel=modulepreload; crossorigin=use-credentials; as=script',
`</_nuxt/modern-${wChunk('pages/index.js')}>; rel=modulepreload; crossorigin=use-credentials; as=script`
'</_nuxt/runtime.modern.js>; rel=modulepreload; crossorigin=use-credentials; as=script',
'</_nuxt/commons/app.modern.js>; rel=modulepreload; crossorigin=use-credentials; as=script',
'</_nuxt/app.modern.js>; rel=modulepreload; crossorigin=use-credentials; as=script',
`</_nuxt/pages/index.modern.js>; rel=modulepreload; crossorigin=use-credentials; as=script`
].join(', '))
})

Expand Down
24 changes: 12 additions & 12 deletions test/dev/modern.server.test.js
Expand Up @@ -24,30 +24,30 @@ describe('modern server mode', () => {
test('should use legacy resources by default', async () => {
const { body: response } = await rp(url('/'))
expect(response).toContain('/_nuxt/app.js')
expect(response).toContain('/_nuxt/commons.app.js')
expect(response).toContain('/_nuxt/commons/app.js')
})

test('should use modern resources for modern resources', async () => {
const { body: response } = await rp(url('/'), { headers: { 'user-agent': modernUA } })
expect(response).toContain('/_nuxt/modern-app.js')
expect(response).toContain('/_nuxt/modern-commons.app.js')
expect(response).toContain('/_nuxt/app.modern.js')
expect(response).toContain('/_nuxt/commons/app.modern.js')
})

test('should include es6 syntax in modern resources', async () => {
const { body: response } = await rp(url(`/_nuxt/modern-${wChunk('pages/index.js')}`))
expect(response).toContain('arrow: () => {')
const { body: response } = await rp(url(`/_nuxt/pages/index.modern.js`))
expect(response).toContain('=>')
})

test('should not include es6 syntax in normal resources', async () => {
const { body: response } = await rp(url(`/_nuxt/${wChunk('pages/index.js')}`))
expect(response).toContain('arrow: function arrow() {')
const { body: response } = await rp(url(`/_nuxt/pages/index.js`))
expect(response).not.toContain('=>')
})

test('should contain legacy http2 pushed resources', async () => {
const { headers: { link } } = await rp(url('/'))
expect(link).toEqual([
'</_nuxt/runtime.js>; rel=preload; crossorigin=use-credentials; as=script',
'</_nuxt/commons.app.js>; rel=preload; crossorigin=use-credentials; as=script',
'</_nuxt/commons/app.js>; rel=preload; crossorigin=use-credentials; as=script',
'</_nuxt/app.js>; rel=preload; crossorigin=use-credentials; as=script',
`</_nuxt/${wChunk('pages/index.js')}>; rel=preload; crossorigin=use-credentials; as=script`
].join(', '))
Expand All @@ -58,10 +58,10 @@ describe('modern server mode', () => {
headers: { 'user-agent': modernUA }
})
expect(link).toEqual([
'</_nuxt/modern-runtime.js>; rel=preload; crossorigin=use-credentials; as=script',
'</_nuxt/modern-commons.app.js>; rel=preload; crossorigin=use-credentials; as=script',
'</_nuxt/modern-app.js>; rel=preload; crossorigin=use-credentials; as=script',
`</_nuxt/modern-${wChunk('pages/index.js')}>; rel=preload; crossorigin=use-credentials; as=script`
'</_nuxt/runtime.modern.js>; rel=preload; crossorigin=use-credentials; as=script',
'</_nuxt/commons/app.modern.js>; rel=preload; crossorigin=use-credentials; as=script',
'</_nuxt/app.modern.js>; rel=preload; crossorigin=use-credentials; as=script',
`</_nuxt/pages/index.modern.js>; rel=preload; crossorigin=use-credentials; as=script`
].join(', '))
})

Expand Down
20 changes: 10 additions & 10 deletions test/dev/modern.spa.test.js
Expand Up @@ -24,34 +24,34 @@ describe('modern client mode (SPA)', () => {
test('should contain nomodule legacy resources', async () => {
const { body: response } = await rp(url('/'))
expect(response).toContain('src="/_nuxt/app.js" crossorigin="use-credentials" nomodule')
expect(response).toContain('src="/_nuxt/commons.app.js" crossorigin="use-credentials" nomodule')
expect(response).toContain('src="/_nuxt/commons/app.js" crossorigin="use-credentials" nomodule')
})

test('should contain module modern resources', async () => {
const { body: response } = await rp(url('/'))
expect(response).toContain('<script src="/_nuxt/modern-app.js" type="module" crossorigin="use-credentials"')
expect(response).toContain('<script src="/_nuxt/modern-commons.app.js" type="module" crossorigin="use-credentials"')
expect(response).toContain('<script src="/_nuxt/app.modern.js" type="module" crossorigin="use-credentials"')
expect(response).toContain('<script src="/_nuxt/commons/app.modern.js" type="module" crossorigin="use-credentials"')
})

test('should contain legacy preload resources', async () => {
const { body: response } = await rp(url('/'))
expect(response).toContain('<link rel="preload" crossorigin="use-credentials" href="/_nuxt/app.js" as="script">')
expect(response).toContain('<link rel="preload" crossorigin="use-credentials" href="/_nuxt/commons.app.js" as="script">')
expect(response).toContain('<link rel="preload" crossorigin="use-credentials" href="/_nuxt/commons/app.js" as="script">')
})

test('should contain legacy http2 pushed resources', async () => {
const { headers: { link } } = await rp(url('/'))
expect(link).toEqual([
'</_nuxt/runtime.js>; rel=preload; crossorigin=use-credentials; as=script',
'</_nuxt/commons.app.js>; rel=preload; crossorigin=use-credentials; as=script',
'</_nuxt/commons/app.js>; rel=preload; crossorigin=use-credentials; as=script',
'</_nuxt/app.js>; rel=preload; crossorigin=use-credentials; as=script'
].join(', '))
})

test('should contain modern preload resources', async () => {
const { body: response } = await rp(url('/'), { headers: { 'user-agent': modernUA } })
expect(response).toContain('<link rel="modulepreload" crossorigin="use-credentials" href="/_nuxt/modern-app.js" as="script">')
expect(response).toContain('<link rel="modulepreload" crossorigin="use-credentials" href="/_nuxt/modern-commons.app.js" as="script">')
expect(response).toContain('<link rel="modulepreload" crossorigin="use-credentials" href="/_nuxt/app.modern.js" as="script">')
expect(response).toContain('<link rel="modulepreload" crossorigin="use-credentials" href="/_nuxt/commons/app.modern.js" as="script">')
})

test('should contain safari nomodule fix', async () => {
Expand All @@ -62,9 +62,9 @@ describe('modern client mode (SPA)', () => {
test('should contain modern http2 pushed resources', async () => {
const { headers: { link } } = await rp(url('/'), { headers: { 'user-agent': modernUA } })
expect(link).toEqual([
'</_nuxt/modern-runtime.js>; rel=modulepreload; crossorigin=use-credentials; as=script',
'</_nuxt/modern-commons.app.js>; rel=modulepreload; crossorigin=use-credentials; as=script',
'</_nuxt/modern-app.js>; rel=modulepreload; crossorigin=use-credentials; as=script'
'</_nuxt/runtime.modern.js>; rel=modulepreload; crossorigin=use-credentials; as=script',
'</_nuxt/commons/app.modern.js>; rel=modulepreload; crossorigin=use-credentials; as=script',
'</_nuxt/app.modern.js>; rel=modulepreload; crossorigin=use-credentials; as=script'
].join(', '))
})

Expand Down
2 changes: 1 addition & 1 deletion test/dev/spa.test.js
Expand Up @@ -35,7 +35,7 @@ function spaTests ({ isHashMode }) {
test('/ (include preload and prefetch resources)', async () => {
const { head } = await renderRoute('/')
expect(head).toMatch('<link rel="preload" href="/_nuxt/runtime.js" as="script">')
expect(head).toMatch('<link rel="preload" href="/_nuxt/commons.app.js" as="script">')
expect(head).toMatch('<link rel="preload" href="/_nuxt/commons/app.js" as="script">')
expect(head).toMatch('<link rel="preload" href="/_nuxt/app.js" as="script">')
expect(head).toMatch(`<link rel="prefetch" href="/_nuxt/${wChunk('pages/custom.js')}">`)
expect(head).toMatch(`<link rel="prefetch" href="/_nuxt/${wChunk('pages/error-handler-async.js')}">`)
Expand Down
5 changes: 0 additions & 5 deletions test/dev/with-config.test.js
Expand Up @@ -34,11 +34,6 @@ describe('with-config', () => {
expect(html).toContain('<h1>I have custom configurations</h1>')
})

test('/ (asset name for analyze mode)', async () => {
const { html } = await nuxt.server.renderRoute('/')
expect(html).toContain('<script src="/test/orion/app.js"')
})

test('/ (global styles inlined)', async () => {
const window = await nuxt.server.renderAndGetWindow(url('/test/'))
const html = window.document.head.innerHTML
Expand Down
8 changes: 2 additions & 6 deletions test/fixtures/modern/nuxt.config.js
Expand Up @@ -2,12 +2,8 @@ export default {
modern: true,
build: {
filenames: {
app: ({ isModern }) => {
return `${isModern ? 'modern-' : ''}[name].js`
},
chunk: ({ isModern }) => {
return `${isModern ? 'modern-' : ''}[name].js`
}
app: ({ isModern }) => `[name]${isModern ? '.modern' : ''}.js`,
chunk: ({ isModern }) => `[name]${isModern ? '.modern' : ''}.js`
}
},
render: {
Expand Down
19 changes: 19 additions & 0 deletions test/fixtures/shared-chunk/components/shared-vendor.vue
@@ -0,0 +1,19 @@
<template>
<div>
{{ test }}
</div>
</template>

<script>
import _ from 'lodash'
import $ from 'cheerio'

export default {
data () {
$('<a>A</a>')
return {
test: _.startCase('lodash')
}
}
}
</script>
5 changes: 5 additions & 0 deletions test/fixtures/shared-chunk/components/shared.vue

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions test/fixtures/shared-chunk/nuxt.config.js
@@ -0,0 +1,3 @@
export default {
components: true
}
6 changes: 6 additions & 0 deletions test/fixtures/shared-chunk/pages/_cat/_id/_sub.vue
@@ -0,0 +1,6 @@
<template>
<div>
<Shared />
<SharedVendor />
</div>
</template>
5 changes: 5 additions & 0 deletions test/fixtures/shared-chunk/pages/index.vue
@@ -0,0 +1,5 @@
<template>
<div>
<SharedVendor />
</div>
</template>
6 changes: 6 additions & 0 deletions test/fixtures/shared-chunk/pages/product/_id.vue
@@ -0,0 +1,6 @@
<template>
<div>
<Shared />
<SharedVendor />
</div>
</template>