Skip to content

Commit 2cfe675

Browse files
committed
feat(cli-service): support contenthash in lib build chunkFilename
Allow users to configure contenthash for chunk files via vue.config.js when building in library mode. Fixes #7476
1 parent 7eb93c1 commit 2cfe675

File tree

2 files changed

+63
-1
lines changed

2 files changed

+63
-1
lines changed

packages/@vue/cli-service/__tests__/buildLib.spec.js

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,3 +270,58 @@ test('build as lib with --inline-vue', async () => {
270270
})
271271
expect(divText).toMatch('Hello from Lib')
272272
})
273+
274+
test('build as lib with contenthash in chunkFilename', async () => {
275+
const project = await create('build-lib-with-contenthash', defaultPreset)
276+
277+
// Create main entry with dynamic import to generate chunks
278+
await project.write('src/main.js', `
279+
export default { foo: 1 }
280+
export const bar = 2
281+
282+
// Dynamic import to create a chunk
283+
export const loadAsync = () => import('./async-module.js')
284+
`)
285+
286+
// Create async module
287+
await project.write('src/async-module.js', `
288+
export default { async: true, value: 'test' }
289+
`)
290+
291+
// Configure contenthash in vue.config.js
292+
await project.write('vue.config.js', `
293+
module.exports = {
294+
configureWebpack: {
295+
output: {
296+
chunkFilename: '[name].[contenthash:8].js'
297+
}
298+
}
299+
}
300+
`)
301+
302+
const { stdout } = await project.run('vue-cli-service build --target lib --name testLib src/main.js')
303+
expect(stdout).toMatch('Build complete.')
304+
305+
// Check main files exist
306+
expect(project.has('dist/testLib.common.js')).toBe(true)
307+
expect(project.has('dist/testLib.umd.js')).toBe(true)
308+
expect(project.has('dist/testLib.umd.min.js')).toBe(true)
309+
310+
// Check that chunk files have contenthash
311+
const fs = require('fs')
312+
const distPath = path.join(project.dir, 'dist')
313+
const files = fs.readdirSync(distPath)
314+
315+
// Find chunk files (files with contenthash pattern)
316+
const chunkFiles = files.filter(f => /testLib\.umd\.min\.[^.]+\.[a-f0-9]{8}\.js$/.test(f))
317+
318+
// Should have at least one chunk file with hash
319+
expect(chunkFiles.length).toBeGreaterThan(0)
320+
321+
// Verify hash is 8 characters long
322+
chunkFiles.forEach(file => {
323+
const hashMatch = file.match(/\.([a-f0-9]{8})\.js$/)
324+
expect(hashMatch).toBeTruthy()
325+
expect(hashMatch[1]).toHaveLength(8)
326+
})
327+
})

packages/@vue/cli-service/lib/commands/build/resolveLibConfig.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,13 @@ module.exports = (api, { entry, name, formats, filename, 'inline-vue': inlineVue
114114
[entryName]: realEntry
115115
}
116116

117+
// Allow users to control chunk filename pattern via vue.config.js
118+
// Support contenthash for better cache control
119+
const userChunkFilename = rawConfig.output.chunkFilename
120+
const defaultChunkFilename = userChunkFilename && userChunkFilename.includes('[contenthash')
121+
? userChunkFilename.replace(/\[name\]/g, `${entryName}.[name]`)
122+
: `${entryName}.[name].js`
123+
117124
rawConfig.output = Object.assign({
118125
library: libName,
119126
libraryExport: isVueEntry ? 'default' : undefined,
@@ -125,7 +132,7 @@ module.exports = (api, { entry, name, formats, filename, 'inline-vue': inlineVue
125132
globalObject: `(typeof self !== 'undefined' ? self : this)`
126133
}, rawConfig.output, {
127134
filename: `${entryName}.js`,
128-
chunkFilename: `${entryName}.[name].js`,
135+
chunkFilename: defaultChunkFilename,
129136
// use dynamic publicPath so this can be deployed anywhere
130137
// the actual path will be determined at runtime by checking
131138
// document.currentScript.src.

0 commit comments

Comments
 (0)