7ffeba540243aa8572c542bde1571336c4d982fe1b765af8d32510269515c21ede6c774c02bf49cd0af681fb56818250a5a3ed0f82ae5a8688752666c3fd6e 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. import { isPlainObject } from 'shared/util'
  2. const vm = require('vm')
  3. const path = require('path')
  4. const resolve = require('resolve')
  5. const NativeModule = require('module')
  6. function createSandbox (context) {
  7. const sandbox = {
  8. Buffer,
  9. console,
  10. process,
  11. setTimeout,
  12. setInterval,
  13. setImmediate,
  14. clearTimeout,
  15. clearInterval,
  16. clearImmediate,
  17. __VUE_SSR_CONTEXT__: context
  18. }
  19. sandbox.global = sandbox
  20. return sandbox
  21. }
  22. function compileModule (files, basedir, runInNewContext) {
  23. const compiledScripts = {}
  24. const resolvedModules = {}
  25. function getCompiledScript (filename) {
  26. if (compiledScripts[filename]) {
  27. return compiledScripts[filename]
  28. }
  29. const code = files[filename]
  30. const wrapper = NativeModule.wrap(code)
  31. const script = new vm.Script(wrapper, {
  32. filename,
  33. displayErrors: true
  34. })
  35. compiledScripts[filename] = script
  36. return script
  37. }
  38. function evaluateModule (filename, sandbox, evaluatedFiles = {}) {
  39. if (evaluatedFiles[filename]) {
  40. return evaluatedFiles[filename]
  41. }
  42. const script = getCompiledScript(filename)
  43. const compiledWrapper = runInNewContext === false
  44. ? script.runInThisContext()
  45. : script.runInNewContext(sandbox)
  46. const m = { exports: {}}
  47. const r = file => {
  48. file = path.posix.join('.', file)
  49. if (files[file]) {
  50. return evaluateModule(file, sandbox, evaluatedFiles)
  51. } else if (basedir) {
  52. return require(
  53. resolvedModules[file] ||
  54. (resolvedModules[file] = resolve.sync(file, { basedir }))
  55. )
  56. } else {
  57. return require(file)
  58. }
  59. }
  60. compiledWrapper.call(m.exports, m.exports, r, m)
  61. const res = Object.prototype.hasOwnProperty.call(m.exports, 'default')
  62. ? m.exports.default
  63. : m.exports
  64. evaluatedFiles[filename] = res
  65. return res
  66. }
  67. return evaluateModule
  68. }
  69. function deepClone (val) {
  70. if (isPlainObject(val)) {
  71. const res = {}
  72. for (const key in val) {
  73. res[key] = deepClone(val[key])
  74. }
  75. return res
  76. } else if (Array.isArray(val)) {
  77. return val.slice()
  78. } else {
  79. return val
  80. }
  81. }
  82. export function createBundleRunner (entry, files, basedir, runInNewContext) {
  83. const evaluate = compileModule(files, basedir, runInNewContext)
  84. if (runInNewContext !== false && runInNewContext !== 'once') {
  85. // new context mode: creates a fresh context and re-evaluate the bundle
  86. // on each render. Ensures entire application state is fresh for each
  87. // render, but incurs extra evaluation cost.
  88. return (userContext = {}) => new Promise(resolve => {
  89. userContext._registeredComponents = new Set()
  90. const res = evaluate(entry, createSandbox(userContext))
  91. resolve(typeof res === 'function' ? res(userContext) : res)
  92. })
  93. } else {
  94. // direct mode: instead of re-evaluating the whole bundle on
  95. // each render, it simply calls the exported function. This avoids the
  96. // module evaluation costs but requires the source code to be structured
  97. // slightly differently.
  98. let runner // lazy creation so that errors can be caught by user
  99. let initialContext
  100. return (userContext = {}) => new Promise(resolve => {
  101. if (!runner) {
  102. const sandbox = runInNewContext === 'once'
  103. ? createSandbox()
  104. : global
  105. // the initial context is only used for collecting possible non-component
  106. // styles injected by vue-style-loader.
  107. initialContext = sandbox.__VUE_SSR_CONTEXT__ = {}
  108. runner = evaluate(entry, sandbox)
  109. // On subsequent renders, __VUE_SSR_CONTEXT__ will not be available
  110. // to prevent cross-request pollution.
  111. delete sandbox.__VUE_SSR_CONTEXT__
  112. if (typeof runner !== 'function') {
  113. throw new Error(
  114. 'bundle export should be a function when using ' +
  115. '{ runInNewContext: false }.'
  116. )
  117. }
  118. }
  119. userContext._registeredComponents = new Set()
  120. // vue-style-loader styles imported outside of component lifecycle hooks
  121. if (initialContext._styles) {
  122. userContext._styles = deepClone(initialContext._styles)
  123. // #6353 ensure "styles" is exposed even if no styles are injected
  124. // in component lifecycles.
  125. // the renderStyles fn is exposed by vue-style-loader >= 3.0.3
  126. const renderStyles = initialContext._renderStyles
  127. if (renderStyles) {
  128. Object.defineProperty(userContext, 'styles', {
  129. enumerable: true,
  130. get () {
  131. return renderStyles(userContext._styles)
  132. }
  133. })
  134. }
  135. }
  136. resolve(runner(userContext))
  137. })
  138. }
  139. }