Evan You
8 years ago
7 changed files with 198 additions and 31 deletions
@ -0,0 +1,66 @@ |
|||
const hash = require('hash-sum') |
|||
import { isJS } from './util' |
|||
|
|||
export default class VueSSRClientPlugin { |
|||
constructor (options = {}) { |
|||
this.options = Object.assign({ |
|||
filename: 'vue-ssr-client-manifest.json' |
|||
}, options) |
|||
} |
|||
|
|||
apply (compiler) { |
|||
compiler.plugin('emit', (compilation, cb) => { |
|||
const stats = compilation.getStats().toJson() |
|||
|
|||
const allFiles = stats.assets |
|||
.map(a => a.name) |
|||
|
|||
const initialScripts = Object.keys(stats.entrypoints) |
|||
.map(name => stats.entrypoints[name].assets) |
|||
.reduce((assets, all) => all.concat(assets), []) |
|||
.filter(isJS) |
|||
|
|||
const asyncScripts = allFiles |
|||
.filter(isJS) |
|||
.filter(file => initialScripts.indexOf(file) < 0) |
|||
|
|||
const manifest = { |
|||
publicPath: stats.publicPath, |
|||
all: allFiles, |
|||
initial: initialScripts, |
|||
async: asyncScripts, |
|||
modules: { /* [identifier: string]: Array<index: number> */ } |
|||
} |
|||
|
|||
const assetModules = stats.modules.filter(m => m.assets.length) |
|||
const fileToIndex = file => manifest.all.indexOf(file) |
|||
stats.modules.forEach(m => { |
|||
// ignore modules duplicated in multiple chunks
|
|||
if (m.chunks.length === 1) { |
|||
const cid = m.chunks[0] |
|||
const chunk = stats.chunks.find(c => c.id === cid) |
|||
const files = manifest.modules[hash(m.identifier)] = chunk.files.map(fileToIndex) |
|||
// find all asset modules associated with the same chunk
|
|||
assetModules.forEach(m => { |
|||
if (m.chunks.some(id => id === cid)) { |
|||
files.push.apply(files, m.assets.map(fileToIndex)) |
|||
} |
|||
}) |
|||
} |
|||
}) |
|||
|
|||
// const debug = (file, obj) => {
|
|||
// require('fs').writeFileSync(__dirname + '/' + file, JSON.stringify(obj, null, 2))
|
|||
// }
|
|||
// debug('stats.json', stats)
|
|||
// debug('client-manifest.json', manifest)
|
|||
|
|||
const json = JSON.stringify(manifest, null, 2) |
|||
compilation.assets[this.options.filename] = { |
|||
source: () => json, |
|||
size: () => json.length |
|||
} |
|||
cb() |
|||
}) |
|||
} |
|||
} |
@ -0,0 +1,59 @@ |
|||
import { validate, isJS } from './util' |
|||
|
|||
export default class VueSSRServerPlugin { |
|||
constructor (options = {}) { |
|||
this.options = Object.assign({ |
|||
filename: 'vue-ssr-server-bundle.json' |
|||
}, options) |
|||
} |
|||
|
|||
apply (compiler) { |
|||
validate(compiler) |
|||
|
|||
compiler.plugin('emit', (compilation, cb) => { |
|||
const stats = compilation.getStats().toJson() |
|||
const entryName = Object.keys(stats.entrypoints)[0] |
|||
const entryAssets = stats.entrypoints[entryName].assets.filter(isJS) |
|||
|
|||
if (entryAssets.length > 1) { |
|||
throw new Error( |
|||
`Server-side bundle should have one single entry file. ` + |
|||
`Avoid using CommonsChunkPlugin in the server config.` |
|||
) |
|||
} |
|||
|
|||
const entry = entryAssets[0] |
|||
if (!entry || typeof entry !== 'string') { |
|||
throw new Error( |
|||
`Entry "${entryName}" not found. Did you specify the correct entry option?` |
|||
) |
|||
} |
|||
|
|||
const bundle = { |
|||
entry, |
|||
files: {}, |
|||
maps: {} |
|||
} |
|||
|
|||
stats.assets.forEach(asset => { |
|||
if (asset.name.match(/\.js$/)) { |
|||
bundle.files[asset.name] = compilation.assets[asset.name].source() |
|||
} else if (asset.name.match(/\.map$/)) { |
|||
bundle.maps[asset.name.replace(/\.map$/, '')] = JSON.parse(compilation.assets[asset.name].source()) |
|||
} |
|||
// do not emit anything else for server
|
|||
delete compilation.assets[asset.name] |
|||
}) |
|||
|
|||
const json = JSON.stringify(bundle, null, 2) |
|||
const filename = this.options.filename |
|||
|
|||
compilation.assets[filename] = { |
|||
source: () => json, |
|||
size: () => json.length |
|||
} |
|||
|
|||
cb() |
|||
}) |
|||
} |
|||
} |
@ -0,0 +1,25 @@ |
|||
const { red, yellow, gray } = require('chalk') |
|||
|
|||
const prefix = `[vue-server-renderer-webpack-plugin]` |
|||
const warn = exports.warn = msg => console.error(red(`${prefix} ${msg}\n`)) |
|||
const tip = exports.tip = msg => console.log(yellow(`${prefix} ${msg}\n`)) |
|||
|
|||
export const validate = compiler => { |
|||
if (compiler.options.target !== 'node') { |
|||
warn('webpack config `target` should be "node".') |
|||
} |
|||
|
|||
if (compiler.options.output && compiler.options.output.libraryTarget !== 'commonjs2') { |
|||
warn('webpack config `output.libraryTarget` should be "commonjs2".') |
|||
} |
|||
|
|||
if (!compiler.options.externals) { |
|||
tip( |
|||
'It is recommended to externalize dependencies for better ssr performance.\n' + |
|||
`See ${gray('https://github.com/vuejs/vue/tree/dev/packages/vue-server-renderer#externals')} ` + |
|||
'for more details.' |
|||
) |
|||
} |
|||
} |
|||
|
|||
export const isJS = file => /\.js($|\?)/.test(file) |
Loading…
Reference in new issue