templateLoader.js 2.9 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889
  1. const qs = require('querystring')
  2. const loaderUtils = require('loader-utils')
  3. const { compileTemplate } = require('@vue/component-compiler-utils')
  4. // Loader that compiles raw template into JavaScript functions.
  5. // This is injected by the global pitcher (../pitch) for template
  6. // selection requests initiated from vue files.
  7. module.exports = function (source) {
  8. const loaderContext = this
  9. const query = qs.parse(this.resourceQuery.slice(1))
  10. // although this is not the main vue-loader, we can get access to the same
  11. // vue-loader options because we've set an ident in the plugin and used that
  12. // ident to create the request for this loader in the pitcher.
  13. const options = loaderUtils.getOptions(loaderContext) || {}
  14. const { id } = query
  15. const isServer = loaderContext.target === 'node'
  16. const isProduction = options.productionMode || loaderContext.minimize || process.env.NODE_ENV === 'production'
  17. const isFunctional = query.functional
  18. // allow using custom compiler via options
  19. const compiler = options.compiler || require('vue-template-compiler')
  20. const compilerOptions = Object.assign({
  21. outputSourceRange: true
  22. }, options.compilerOptions, {
  23. scopeId: query.scoped ? `data-v-${id}` : null,
  24. comments: query.comments
  25. })
  26. // for vue-component-compiler
  27. const finalOptions = {
  28. source,
  29. filename: this.resourcePath,
  30. compiler,
  31. compilerOptions,
  32. // allow customizing behavior of vue-template-es2015-compiler
  33. transpileOptions: options.transpileOptions,
  34. transformAssetUrls: options.transformAssetUrls || true,
  35. isProduction,
  36. isFunctional,
  37. optimizeSSR: isServer && options.optimizeSSR !== false,
  38. prettify: options.prettify
  39. }
  40. const compiled = compileTemplate(finalOptions)
  41. // tips
  42. if (compiled.tips && compiled.tips.length) {
  43. compiled.tips.forEach(tip => {
  44. loaderContext.emitWarning(typeof tip === 'object' ? tip.msg : tip)
  45. })
  46. }
  47. // errors
  48. if (compiled.errors && compiled.errors.length) {
  49. // 2.6 compiler outputs errors as objects with range
  50. if (compiler.generateCodeFrame && finalOptions.compilerOptions.outputSourceRange) {
  51. // TODO account for line offset in case template isn't placed at top
  52. // of the file
  53. loaderContext.emitError(
  54. `\n\n Errors compiling template:\n\n` +
  55. compiled.errors.map(({ msg, start, end }) => {
  56. const frame = compiler.generateCodeFrame(source, start, end)
  57. return ` ${msg}\n\n${pad(frame)}`
  58. }).join(`\n\n`) +
  59. '\n'
  60. )
  61. } else {
  62. loaderContext.emitError(
  63. `\n Error compiling template:\n${pad(compiled.source)}\n` +
  64. compiled.errors.map(e => ` - ${e}`).join('\n') +
  65. '\n'
  66. )
  67. }
  68. }
  69. const { code } = compiled
  70. // finish with ESM exports
  71. return code + `\nexport { render, staticRenderFns }`
  72. }
  73. function pad (source) {
  74. return source
  75. .split(/\r?\n/)
  76. .map(line => ` ${line}`)
  77. .join('\n')
  78. }