You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
parttimejob/node_modules/@vue/vue-loader-v15/lib/index.js

232 lines
7.1 KiB

const path = require('path')
const hash = require('hash-sum')
const qs = require('querystring')
const plugin = require('./plugin')
const selectBlock = require('./select')
const loaderUtils = require('loader-utils')
const {
attrsToQuery,
testWebpack5,
genMatchResource
} = require('./codegen/utils')
const genStylesCode = require('./codegen/styleInjection')
const { genHotReloadCode } = require('./codegen/hotReload')
const genCustomBlocksCode = require('./codegen/customBlocks')
const componentNormalizerPath = require.resolve('./runtime/componentNormalizer')
const { NS } = require('./plugin')
const { resolveCompiler } = require('./compiler')
const { setDescriptor } = require('./descriptorCache')
let errorEmitted = false
module.exports = function (source) {
const loaderContext = this
if (!errorEmitted && !loaderContext['thread-loader'] && !loaderContext[NS]) {
loaderContext.emitError(
new Error(
`vue-loader was used without the corresponding plugin. ` +
`Make sure to include VueLoaderPlugin in your webpack config.`
)
)
errorEmitted = true
}
const stringifyRequest = (r) => loaderUtils.stringifyRequest(loaderContext, r)
const {
mode,
target,
request,
minimize,
sourceMap,
rootContext,
resourcePath,
resourceQuery: _resourceQuery = '',
_compiler
} = loaderContext
const isWebpack5 = testWebpack5(_compiler)
const rawQuery = _resourceQuery.slice(1)
const resourceQuery = rawQuery ? `&${rawQuery}` : ''
const incomingQuery = qs.parse(rawQuery)
const options = loaderUtils.getOptions(loaderContext) || {}
const enableInlineMatchResource =
isWebpack5 && Boolean(options.experimentalInlineMatchResource)
const isServer = target === 'node'
const isShadow = !!options.shadowMode
const isProduction =
mode === 'production' ||
options.productionMode ||
minimize ||
process.env.NODE_ENV === 'production'
const filename = path.basename(resourcePath)
const context = rootContext || process.cwd()
const sourceRoot = path.dirname(path.relative(context, resourcePath))
const { compiler, templateCompiler } = resolveCompiler(context, loaderContext)
const descriptor = compiler.parse({
source,
compiler: options.compiler || templateCompiler,
filename,
sourceRoot,
needMap: sourceMap
})
// cache descriptor
setDescriptor(resourcePath, descriptor)
// module id for scoped CSS & hot-reload
const rawShortFilePath = path
.relative(context, resourcePath)
.replace(/^(\.\.[\/\\])+/, '')
const shortFilePath = rawShortFilePath.replace(/\\/g, '/')
const id = hash(
isProduction
? shortFilePath + '\n' + source.replace(/\r\n/g, '\n')
: shortFilePath
)
// if the query has a type field, this is a language block request
// e.g. foo.vue?type=template&id=xxxxx
// and we will return early
if (incomingQuery.type) {
return selectBlock(
descriptor,
id,
options,
loaderContext,
incomingQuery,
!!options.appendExtension
)
}
// feature information
const hasScoped = descriptor.styles.some((s) => s.scoped)
const hasFunctional =
descriptor.template && descriptor.template.attrs.functional
const needsHotReload =
!isServer &&
!isProduction &&
(descriptor.script || descriptor.scriptSetup || descriptor.template) &&
options.hotReload !== false
// script
let scriptImport = `var script = {}`
// let isTS = false
const { script, scriptSetup } = descriptor
if (script || scriptSetup) {
const lang = (script && script.lang) || (scriptSetup && scriptSetup.lang)
// isTS = !!(lang && /tsx?/.test(lang))
const externalQuery =
script && !scriptSetup && script.src ? `&external` : ``
const src = (script && !scriptSetup && script.src) || resourcePath
const attrsQuery = attrsToQuery((scriptSetup || script).attrs, 'js')
const query = `?vue&type=script${attrsQuery}${resourceQuery}${externalQuery}`
let scriptRequest
if (enableInlineMatchResource) {
scriptRequest = stringifyRequest(
genMatchResource(loaderContext, src, query, lang || 'js')
)
} else {
scriptRequest = stringifyRequest(src + query)
}
scriptImport =
`import script from ${scriptRequest}\n` + `export * from ${scriptRequest}` // support named exports
}
// template
let templateImport = `var render, staticRenderFns`
let templateRequest
if (descriptor.template) {
const src = descriptor.template.src || resourcePath
const externalQuery = descriptor.template.src ? `&external` : ``
const idQuery = `&id=${id}`
const scopedQuery = hasScoped ? `&scoped=true` : ``
const attrsQuery = attrsToQuery(descriptor.template.attrs)
// const tsQuery =
// options.enableTsInTemplate !== false && isTS ? `&ts=true` : ``
const query = `?vue&type=template${idQuery}${scopedQuery}${attrsQuery}${resourceQuery}${externalQuery}`
if (enableInlineMatchResource) {
templateRequest = stringifyRequest(
// TypeScript syntax in template expressions is not supported in Vue 2, so the lang is always 'js'
genMatchResource(loaderContext, src, query, 'js')
)
} else {
templateRequest = stringifyRequest(src + query)
}
templateImport = `import { render, staticRenderFns } from ${templateRequest}`
}
// styles
let stylesCode = ``
if (descriptor.styles.length) {
stylesCode = genStylesCode(
loaderContext,
descriptor.styles,
id,
resourcePath,
stringifyRequest,
needsHotReload,
isServer || isShadow, // needs explicit injection?
isProduction,
enableInlineMatchResource
)
}
let code =
`
${templateImport}
${scriptImport}
${stylesCode}
/* normalize component */
import normalizer from ${stringifyRequest(`!${componentNormalizerPath}`)}
var component = normalizer(
script,
render,
staticRenderFns,
${hasFunctional ? `true` : `false`},
${/injectStyles/.test(stylesCode) ? `injectStyles` : `null`},
${hasScoped ? JSON.stringify(id) : `null`},
${isServer ? JSON.stringify(hash(request)) : `null`}
${isShadow ? `,true` : ``}
)
`.trim() + `\n`
if (descriptor.customBlocks && descriptor.customBlocks.length) {
code += genCustomBlocksCode(
loaderContext,
descriptor.customBlocks,
resourcePath,
resourceQuery,
stringifyRequest,
enableInlineMatchResource
)
}
if (needsHotReload) {
code += `\n` + genHotReloadCode(id, hasFunctional, templateRequest)
}
// Expose filename. This is used by the devtools and Vue runtime warnings.
if (!isProduction) {
// Expose the file's full path in development, so that it can be opened
// from the devtools.
code += `\ncomponent.options.__file = ${JSON.stringify(
rawShortFilePath.replace(/\\/g, '/')
)}`
} else if (options.exposeFilename) {
// Libraries can opt-in to expose their components' filenames in production builds.
// For security reasons, only expose the file's basename in production.
code += `\ncomponent.options.__file = ${JSON.stringify(filename)}`
}
code += `\nexport default component.exports`
return code
}
module.exports.VueLoaderPlugin = plugin