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') // 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') var hotReloadAPIPath = normalize.dep('vue-hot-reload-api') // check whether default js loader exists var hasBabel = !!tryRequire('babel-loader') var hasBuble = !!tryRequire('buble-loader') // for mp js var { compileMP, compileMPScript } = require('./mp-compiler') var { defaultStylePart } = require('./mp-compiler/util') var rewriterInjectRE = /\b(css(?:-loader)?(?:\?[^!]+)?)(?:!|$)/ var defaultLang = { template: 'html', styles: 'css', script: 'js' } // When extracting parts from the source vue file, we want to apply the // loaders chained before vue-loader, but exclude some loaders that simply // produces side effects such as linting. function getRawRequest (context, excludedPreLoaders) { excludedPreLoaders = excludedPreLoaders || /eslint-loader/ return loaderUtils.getRemainingRequest({ resource: context.resource, loaderIndex: context.loaderIndex, loaders: context.loaders.filter(loader => !excludedPreLoaders.test(loader.path)) }) } module.exports = function (content) { // for mp // 针对 entry 的 main.js 处理 page 和 app 的入口文件和配置等 const mpOptions = loaderUtils.getOptions(this) || {} if (mpOptions.checkMPEntry) { return compileMP.call(this, content, mpOptions) } this.cacheable() var isServer = this.target === 'node' var isProduction = this.minimize || process.env.NODE_ENV === 'production' var loaderContext = this var query = loaderUtils.getOptions(this) || {} var options = Object.assign({ esModule: true }, 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, enumerable: false, configurable: true }) var rawRequest = getRawRequest(this, options.excludedPreLoaders) var filePath = this.resourcePath var fileName = path.basename(filePath) var context = (this._compiler && this._compiler.context) || this.options.context || process.cwd() var moduleId = 'data-v-' + genId(filePath, context, options.hashKey) var shortFilePath = path.relative(context, filePath).replace(/^(\.\.\/)+/, '') var cssLoaderOptions = '' if (!isProduction && this.sourceMap && options.cssSourceMap !== false) { cssLoaderOptions += '?sourceMap' } if (isProduction) { cssLoaderOptions += (cssLoaderOptions ? '&' : '?') + 'minimize' } var bubleOptions = hasBuble && options.buble ? '?' + JSON.stringify(options.buble) : '' var output = '' var parts = parse(content, fileName, this.sourceMap) // fix #153: 根组件没有 style 模块,不生成页面的 wxss,补齐内容方便加载 vendor.wxss if (!parts.styles.length) { parts.styles.push(defaultStylePart) } var hasScoped = parts.styles.some(function (s) { return s.scoped }) var hasComment = parts.template && parts.template.attrs && parts.template.attrs.comments var templateCompilerOptions = '?' + JSON.stringify({ id: moduleId, hasScoped: hasScoped, hasComment: hasComment, transformToRequire: options.transformToRequire, preserveWhitespace: options.preserveWhitespace, buble: options.buble, // only pass compilerModules if it's a path string compilerModules: typeof options.compilerModules === 'string' ? options.compilerModules : undefined }) // mp compiler 全局模式下注入 babelrc const babelLoaderOptions = mpOptions.globalBabelrc ? { loader: 'babel-loader', options: { extends: mpOptions.globalBabelrc } } : 'babel-loader' var defaultLoaders = { html: templateCompilerPath + templateCompilerOptions, css: options.extractCSS ? getCSSExtractLoader() : styleLoaderPath + '!' + 'css-loader' + cssLoaderOptions, js: hasBuble ? ('buble-loader' + bubleOptions) : hasBabel ? babelLoaderOptions : '' } // check if there are custom loaders specified via // webpack config, otherwise use defaults var loaders = Object.assign({}, defaultLoaders, options.loaders) var preLoaders = options.preLoaders || {} var postLoaders = options.postLoaders || {} var needsHotReload = ( !isServer && !isProduction && (parts.script || parts.template) ) 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 = style.src ? getRequireForImportString('styles', style, style.scoped) : 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