diff --git a/lib/loader.js b/lib/loader.js index 762842597..b61ee8b62 100644 --- a/lib/loader.js +++ b/lib/loader.js @@ -1,18 +1,16 @@ var path = require('path') -var hash = require('hash-sum') -var parse = require('./parser') var genId = require('./utils/gen-id') var querystring = require('querystring') var loaderUtils = require('loader-utils') var normalize = require('./utils/normalize') var tryRequire = require('./utils/try-require') +var compiler = require('vue-component-compiler') // internal lib loaders var selectorPath = normalize.lib('selector') var styleCompilerPath = normalize.lib('style-compiler/index') var templateCompilerPath = normalize.lib('template-compiler/index') var templatePreprocessorPath = normalize.lib('template-compiler/preprocessor') -var componentNormalizerPath = normalize.lib('component-normalizer') // dep loaders var styleLoaderPath = normalize.dep('vue-style-loader') @@ -50,15 +48,17 @@ module.exports = function (content) { var loaderContext = this var query = loaderUtils.getOptions(this) || {} var options = Object.assign({ - esModule: true + esModule: true, + isHot: !isServer && !isProduction, + isServer, + isProduction, + isInjectable: query.inject, + hasStyleInjectFn: true, + require: { + vueHotReloadAPI: hotReloadAPIPath + } }, this.options.vue, this.vue, query) - // disable esModule in inject mode - // because import/export must be top-level - if (query.inject) { - options.esModule = false - } - // #824 avoid multiple webpack runs complaining about unknown option Object.defineProperty(this.options, '__vueOptions__', { value: options, @@ -74,6 +74,8 @@ module.exports = function (content) { var moduleId = 'data-v-' + genId(filePath, context, options.hashKey) var shortFilePath = path.relative(context, filePath).replace(/^(\.\.\/)+/, '') + options.moduleIdentifier = options.scopeId = moduleId + var cssLoaderOptions = '' if (!isProduction && this.sourceMap && options.cssSourceMap !== false) { cssLoaderOptions += '?sourceMap' @@ -111,16 +113,6 @@ module.exports = function (content) { var preLoaders = options.preLoaders || {} var postLoaders = options.postLoaders || {} - function getRequire (type, part, index, scoped) { - return 'require(' + - getRequireString(type, part, index, scoped) + - ')' - } - - function getImport (type, part, index, scoped) { - return 'import __vue_' + type + '__ from ' + getRequireString(type, part, index, scoped) - } - function getRequireString (type, part, index, scoped) { return loaderUtils.stringifyRequest(loaderContext, // disable all configuration loaders @@ -134,16 +126,6 @@ module.exports = function (content) { ) } - function getRequireForImport (type, impt, scoped) { - return 'require(' + - getRequireForImportString(type, impt, scoped) + - ')' - } - - function getImportForImport (type, impt, scoped) { - return 'import __vue_' + type + '__ from ' + getRequireForImportString(type, impt, scoped) - } - function getRequireForImportString (type, impt, scoped) { return loaderUtils.stringifyRequest(loaderContext, '!!' + @@ -152,6 +134,14 @@ module.exports = function (content) { ) } + function unwrap (any) { + if (typeof any === 'string') { + return JSON.parse(any) + } + + return any + } + function addCssModulesToLoader (loader, part, index) { if (!part.module) return loader var option = options.cssModules || {} @@ -208,14 +198,14 @@ module.exports = function (content) { function getLangString (type, part) { if (type === 'script' || type === 'template' || type === 'styles') { - return part.lang || defaultLang[type] + return (part && part.lang) || defaultLang[type] } else { return type } } function getRawLoaderString (type, part, index, scoped) { - var lang = part.lang || defaultLang[type] + var lang = (part && part.lang) || defaultLang[type] var styleCompiler = '' if (type === 'styles') { @@ -225,6 +215,7 @@ module.exports = function (content) { vue: true, id: moduleId, scoped: !!scoped, + module: part && part.module, hasInlineConfig: !!query.postcss }) + '!' // normalize scss/sass if no specific loaders have been provided @@ -337,266 +328,40 @@ module.exports = function (content) { }) } - var output = '' - var parts = parse(content, fileName, this.sourceMap) - var hasScoped = parts.styles.some(function (s) { return s.scoped }) - var needsHotReload = ( - !isServer && - !isProduction && - (parts.script || parts.template) + const parts = compiler.parse(content, fileName, { needsMap: this.sourceMap }) + + const output = compiler.assemble( + { + script: { + id: parts.script && unwrap(parts.script.src + ? getRequireForImportString('script', parts.script) + : getRequireString('script', parts.script)), + descriptor: parts.script + }, + render: { + id: parts.template && unwrap(parts.template.src + ? getRequireForImportString('template', parts.template) + : getRequireString('template', parts.template)), + descriptor: parts.template + }, + styles: parts.styles && parts.styles.map( + (style, i) => ({ + id: style && unwrap(style.src + ? getRequireForImportString('styles', style, i, style.scoped) + : getRequireString('styles', style, i, style.scoped)), + descriptor: style + }) + ), + customBlocks: parts.customBlocks && parts.customBlocks.map( + (block, i) => ({ + id: block && unwrap(block.src + ? getRequireForImportString(block.type, block) + : getRequireString(block.type, block, i)), + descriptor: block + }) + ) + }, shortFilePath, options ) - if (needsHotReload) { - output += 'var disposed = false\n' - } - - // add requires for styles - var cssModules - if (parts.styles.length) { - var styleInjectionCode = 'function injectStyle (ssrContext) {\n' - if (needsHotReload) { - styleInjectionCode += ` if (disposed) return\n` - } - if (isServer) { - styleInjectionCode += `var i\n` - } - parts.styles.forEach(function (style, i) { - // require style - var requireString = style.src - ? getRequireForImport('styles', style, style.scoped) - : getRequire('styles', style, i, style.scoped) - - var hasStyleLoader = requireString.indexOf('style-loader') > -1 - var hasVueStyleLoader = requireString.indexOf('vue-style-loader') > -1 - // vue-style-loader exposes inject functions during SSR so they are - // always called - var invokeStyle = isServer && hasVueStyleLoader - ? code => `;(i=${code},i.__inject__&&i.__inject__(ssrContext),i)\n` - : code => ` ${code}\n` - - var moduleName = (style.module === true) ? '$style' : style.module - // setCssModule - if (moduleName) { - if (!cssModules) { - cssModules = {} - if (needsHotReload) { - output += `var cssModules = {}\n` - } - } - if (moduleName in cssModules) { - loaderContext.emitError('CSS module name "' + moduleName + '" is not unique!') - styleInjectionCode += invokeStyle(requireString) - } else { - cssModules[moduleName] = true - - // `(vue-)style-loader` exposes the name-to-hash map directly - // `css-loader` exposes it in `.locals` - // add `.locals` if the user configured to not use style-loader. - if (!hasStyleLoader) { - requireString += '.locals' - } - - if (!needsHotReload) { - styleInjectionCode += invokeStyle('this["' + moduleName + '"] = ' + requireString) - } else { - // handle hot reload for CSS modules. - // we store the exported locals in an object and proxy to it by - // defining getters inside component instances' lifecycle hook. - styleInjectionCode += - invokeStyle(`cssModules["${moduleName}"] = ${requireString}`) + - `Object.defineProperty(this, "${moduleName}", { get: function () { return cssModules["${moduleName}"] }})\n` - - var requirePath = getRequireString('styles', style, i, style.scoped) - output += - `module.hot && module.hot.accept([${requirePath}], function () {\n` + - // 1. check if style has been injected - ` var oldLocals = cssModules["${moduleName}"]\n` + - ` if (!oldLocals) return\n` + - // 2. re-import (side effect: updates the