Procházet zdrojové kódy

include ssr webpack plugin in main repo

Evan You před 9 roky
rodič
revize
a2bff57b0d

+ 42 - 28
build/config.js

@@ -1,8 +1,8 @@
 const path = require('path')
-const flow = require('rollup-plugin-flow-no-whitespace')
 const buble = require('rollup-plugin-buble')
-const replace = require('rollup-plugin-replace')
 const alias = require('rollup-plugin-alias')
+const replace = require('rollup-plugin-replace')
+const flow = require('rollup-plugin-flow-no-whitespace')
 const version = process.env.VERSION || require('../package.json').version
 const weexVersion = process.env.WEEX_VERSION || require('../packages/weex-vue-framework/package.json').version
 
@@ -22,18 +22,20 @@ const weexFactoryPlugin = {
   }
 }
 
+const resolve = p => path.resolve(__dirname, '../' + p)
+
 const builds = {
   // Runtime only (CommonJS). Used by bundlers e.g. Webpack & Browserify
   'web-runtime-cjs': {
-    entry: path.resolve(__dirname, '../src/entries/web-runtime.js'),
-    dest: path.resolve(__dirname, '../dist/vue.runtime.common.js'),
+    entry: resolve('src/entries/web-runtime.js'),
+    dest: resolve('dist/vue.runtime.common.js'),
     format: 'cjs',
     banner
   },
   // Runtime+compiler CommonJS build (CommonJS)
   'web-full-cjs': {
-    entry: path.resolve(__dirname, '../src/entries/web-runtime-with-compiler.js'),
-    dest: path.resolve(__dirname, '../dist/vue.common.js'),
+    entry: resolve('src/entries/web-runtime-with-compiler.js'),
+    dest: resolve('dist/vue.common.js'),
     format: 'cjs',
     alias: { he: './entity-decoder' },
     banner
@@ -41,39 +43,39 @@ const builds = {
   // Runtime only (ES Modules). Used by bundlers that support ES Modules,
   // e.g. Rollup & Webpack 2
   'web-runtime-esm': {
-    entry: path.resolve(__dirname, '../src/entries/web-runtime.js'),
-    dest: path.resolve(__dirname, '../dist/vue.runtime.esm.js'),
+    entry: resolve('src/entries/web-runtime.js'),
+    dest: resolve('dist/vue.runtime.esm.js'),
     format: 'es',
     banner
   },
   // Runtime+compiler CommonJS build (ES Modules)
   'web-full-esm': {
-    entry: path.resolve(__dirname, '../src/entries/web-runtime-with-compiler.js'),
-    dest: path.resolve(__dirname, '../dist/vue.esm.js'),
+    entry: resolve('src/entries/web-runtime-with-compiler.js'),
+    dest: resolve('dist/vue.esm.js'),
     format: 'es',
     alias: { he: './entity-decoder' },
     banner
   },
   // runtime-only build (Browser)
   'web-runtime-dev': {
-    entry: path.resolve(__dirname, '../src/entries/web-runtime.js'),
-    dest: path.resolve(__dirname, '../dist/vue.runtime.js'),
+    entry: resolve('src/entries/web-runtime.js'),
+    dest: resolve('dist/vue.runtime.js'),
     format: 'umd',
     env: 'development',
     banner
   },
   // runtime-only production build (Browser)
   'web-runtime-prod': {
-    entry: path.resolve(__dirname, '../src/entries/web-runtime.js'),
-    dest: path.resolve(__dirname, '../dist/vue.runtime.min.js'),
+    entry: resolve('src/entries/web-runtime.js'),
+    dest: resolve('dist/vue.runtime.min.js'),
     format: 'umd',
     env: 'production',
     banner
   },
   // Runtime+compiler development build (Browser)
   'web-full-dev': {
-    entry: path.resolve(__dirname, '../src/entries/web-runtime-with-compiler.js'),
-    dest: path.resolve(__dirname, '../dist/vue.js'),
+    entry: resolve('src/entries/web-runtime-with-compiler.js'),
+    dest: resolve('dist/vue.js'),
     format: 'umd',
     env: 'development',
     alias: { he: './entity-decoder' },
@@ -81,8 +83,8 @@ const builds = {
   },
   // Runtime+compiler production build  (Browser)
   'web-full-prod': {
-    entry: path.resolve(__dirname, '../src/entries/web-runtime-with-compiler.js'),
-    dest: path.resolve(__dirname, '../dist/vue.min.js'),
+    entry: resolve('src/entries/web-runtime-with-compiler.js'),
+    dest: resolve('dist/vue.min.js'),
     format: 'umd',
     env: 'production',
     alias: { he: './entity-decoder' },
@@ -90,38 +92,50 @@ const builds = {
   },
   // Web compiler (CommonJS).
   'web-compiler': {
-    entry: path.resolve(__dirname, '../src/entries/web-compiler.js'),
-    dest: path.resolve(__dirname, '../packages/vue-template-compiler/build.js'),
+    entry: resolve('src/entries/web-compiler.js'),
+    dest: resolve('packages/vue-template-compiler/build.js'),
     format: 'cjs',
     external: Object.keys(require('../packages/vue-template-compiler/package.json').dependencies)
   },
   // Web server renderer (CommonJS).
   'web-server-renderer': {
-    entry: path.resolve(__dirname, '../src/entries/web-server-renderer.js'),
-    dest: path.resolve(__dirname, '../packages/vue-server-renderer/build.js'),
+    entry: resolve('src/entries/web-server-renderer.js'),
+    dest: resolve('packages/vue-server-renderer/build.js'),
+    format: 'cjs',
+    external: Object.keys(require('../packages/vue-server-renderer/package.json').dependencies)
+  },
+  'web-server-renderer-webpack-server-plugin': {
+    entry: resolve('src/server/webpack-plugin/server.js'),
+    dest: resolve('packages/vue-server-renderer/server-plugin.js'),
+    format: 'cjs',
+    external: Object.keys(require('../packages/vue-server-renderer/package.json').dependencies)
+  },
+  'web-server-renderer-webpack-client-plugin': {
+    entry: resolve('src/server/webpack-plugin/client.js'),
+    dest: resolve('packages/vue-server-renderer/client-plugin.js'),
     format: 'cjs',
     external: Object.keys(require('../packages/vue-server-renderer/package.json').dependencies)
   },
   // Weex runtime factory
   'weex-factory': {
     weex: true,
-    entry: path.resolve(__dirname, '../src/entries/weex-factory.js'),
-    dest: path.resolve(__dirname, '../packages/weex-vue-framework/factory.js'),
+    entry: resolve('src/entries/weex-factory.js'),
+    dest: resolve('packages/weex-vue-framework/factory.js'),
     format: 'cjs',
     plugins: [weexFactoryPlugin]
   },
   // Weex runtime framework (CommonJS).
   'weex-framework': {
     weex: true,
-    entry: path.resolve(__dirname, '../src/entries/weex-framework.js'),
-    dest: path.resolve(__dirname, '../packages/weex-vue-framework/index.js'),
+    entry: resolve('src/entries/weex-framework.js'),
+    dest: resolve('packages/weex-vue-framework/index.js'),
     format: 'cjs'
   },
   // Weex compiler (CommonJS). Used by Weex's Webpack loader.
   'weex-compiler': {
     weex: true,
-    entry: path.resolve(__dirname, '../src/entries/weex-compiler.js'),
-    dest: path.resolve(__dirname, '../packages/weex-template-compiler/build.js'),
+    entry: resolve('src/entries/weex-compiler.js'),
+    dest: resolve('packages/weex-template-compiler/build.js'),
     format: 'cjs',
     external: Object.keys(require('../packages/weex-template-compiler/package.json').dependencies)
   }

+ 1 - 0
package.json

@@ -64,6 +64,7 @@
     "babel-preset-es2015": "^6.9.0",
     "babel-preset-flow-vue": "^1.0.0",
     "buble": "^0.15.2",
+    "chalk": "^1.1.3",
     "chromedriver": "^2.21.2",
     "codecov.io": "^0.1.6",
     "cross-spawn": "^5.0.1",

+ 2 - 0
packages/vue-server-renderer/package.json

@@ -18,6 +18,8 @@
     "url": "https://github.com/vuejs/vue/issues"
   },
   "dependencies": {
+    "chalk": "^1.1.3",
+    "hash-sum": "^1.0.2",
     "he": "^1.1.0",
     "de-indent": "^1.0.2",
     "resolve": "^1.2.0",

+ 66 - 0
src/server/webpack-plugin/client.js

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

+ 59 - 0
src/server/webpack-plugin/server.js

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

+ 25 - 0
src/server/webpack-plugin/util.js

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

+ 3 - 3
yarn.lock

@@ -5058,9 +5058,9 @@ void-elements@^2.0.0:
   version "2.0.1"
   resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-2.0.1.tgz#c066afb582bb1cb4128d60ea92392e94d5e9dbec"
 
-vue-ssr-webpack-plugin@^2.0.0:
-  version "2.0.2"
-  resolved "https://registry.yarnpkg.com/vue-ssr-webpack-plugin/-/vue-ssr-webpack-plugin-2.0.2.tgz#80709e7d53e1fe7085ad3dc73afff71c069be715"
+vue-ssr-webpack-plugin@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/vue-ssr-webpack-plugin/-/vue-ssr-webpack-plugin-3.0.0.tgz#db47769ed8e71c8eb53aa9ae7be9ff04baf546fa"
   dependencies:
     chalk "^1.1.3"
     hash-sum "^1.0.2"