| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150 |
- import { isPlainObject } from 'shared/util'
- const vm = require('vm')
- const path = require('path')
- const resolve = require('resolve')
- const NativeModule = require('module')
- function createSandbox (context) {
- const sandbox = {
- Buffer,
- console,
- process,
- setTimeout,
- setInterval,
- setImmediate,
- clearTimeout,
- clearInterval,
- clearImmediate,
- __VUE_SSR_CONTEXT__: context
- }
- sandbox.global = sandbox
- return sandbox
- }
- function compileModule (files, basedir, runInNewContext) {
- const compiledScripts = {}
- const resolvedModules = {}
- function getCompiledScript (filename) {
- if (compiledScripts[filename]) {
- return compiledScripts[filename]
- }
- const code = files[filename]
- const wrapper = NativeModule.wrap(code)
- const script = new vm.Script(wrapper, {
- filename,
- displayErrors: true
- })
- compiledScripts[filename] = script
- return script
- }
- function evaluateModule (filename, sandbox, evaluatedFiles = {}) {
- if (evaluatedFiles[filename]) {
- return evaluatedFiles[filename]
- }
- const script = getCompiledScript(filename)
- const compiledWrapper = runInNewContext === false
- ? script.runInThisContext()
- : script.runInNewContext(sandbox)
- const m = { exports: {}}
- const r = file => {
- file = path.posix.join('.', file)
- if (files[file]) {
- return evaluateModule(file, sandbox, evaluatedFiles)
- } else if (basedir) {
- return require(
- resolvedModules[file] ||
- (resolvedModules[file] = resolve.sync(file, { basedir }))
- )
- } else {
- return require(file)
- }
- }
- compiledWrapper.call(m.exports, m.exports, r, m)
- const res = Object.prototype.hasOwnProperty.call(m.exports, 'default')
- ? m.exports.default
- : m.exports
- evaluatedFiles[filename] = res
- return res
- }
- return evaluateModule
- }
- function deepClone (val) {
- if (isPlainObject(val)) {
- const res = {}
- for (const key in val) {
- res[key] = deepClone(val[key])
- }
- return res
- } else if (Array.isArray(val)) {
- return val.slice()
- } else {
- return val
- }
- }
- export function createBundleRunner (entry, files, basedir, runInNewContext) {
- const evaluate = compileModule(files, basedir, runInNewContext)
- if (runInNewContext !== false && runInNewContext !== 'once') {
- // new context mode: creates a fresh context and re-evaluate the bundle
- // on each render. Ensures entire application state is fresh for each
- // render, but incurs extra evaluation cost.
- return (userContext = {}) => new Promise(resolve => {
- userContext._registeredComponents = new Set()
- const res = evaluate(entry, createSandbox(userContext))
- resolve(typeof res === 'function' ? res(userContext) : res)
- })
- } else {
- // direct mode: instead of re-evaluating the whole bundle on
- // each render, it simply calls the exported function. This avoids the
- // module evaluation costs but requires the source code to be structured
- // slightly differently.
- let runner // lazy creation so that errors can be caught by user
- let initialContext
- return (userContext = {}) => new Promise(resolve => {
- if (!runner) {
- const sandbox = runInNewContext === 'once'
- ? createSandbox()
- : global
- // the initial context is only used for collecting possible non-component
- // styles injected by vue-style-loader.
- initialContext = sandbox.__VUE_SSR_CONTEXT__ = {}
- runner = evaluate(entry, sandbox)
- // On subsequent renders, __VUE_SSR_CONTEXT__ will not be available
- // to prevent cross-request pollution.
- delete sandbox.__VUE_SSR_CONTEXT__
- if (typeof runner !== 'function') {
- throw new Error(
- 'bundle export should be a function when using ' +
- '{ runInNewContext: false }.'
- )
- }
- }
- userContext._registeredComponents = new Set()
- // vue-style-loader styles imported outside of component lifecycle hooks
- if (initialContext._styles) {
- userContext._styles = deepClone(initialContext._styles)
- // #6353 ensure "styles" is exposed even if no styles are injected
- // in component lifecycles.
- // the renderStyles fn is exposed by vue-style-loader >= 3.0.3
- const renderStyles = initialContext._renderStyles
- if (renderStyles) {
- Object.defineProperty(userContext, 'styles', {
- enumerable: true,
- get () {
- return renderStyles(userContext._styles)
- }
- })
- }
- }
- resolve(runner(userContext))
- })
- }
- }
|