| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223 | const qs = require('querystring')const loaderUtils = require('loader-utils')const hash = require('hash-sum')const selfPath = require.resolve('../index')const templateLoaderPath = require.resolve('./templateLoader')const stylePostLoaderPath = require.resolve('./stylePostLoader')const { resolveCompiler } = require('../compiler')const { testWebpack5 } = require('../codegen/utils')const isESLintLoader = (l) => /(\/|\\|@)eslint-loader/.test(l.path)const isNullLoader = (l) => /(\/|\\|@)null-loader/.test(l.path)const isCSSLoader = (l) => /(\/|\\|@)css-loader/.test(l.path)const isCacheLoader = (l) => /(\/|\\|@)cache-loader/.test(l.path)const isPitcher = (l) => l.path !== __filenameconst isPreLoader = (l) => !l.pitchExecutedconst isPostLoader = (l) => l.pitchExecutedconst dedupeESLintLoader = (loaders) => {  const res = []  let seen = false  loaders.forEach((l) => {    if (!isESLintLoader(l)) {      res.push(l)    } else if (!seen) {      seen = true      res.push(l)    }  })  return res}const shouldIgnoreCustomBlock = (loaders) => {  const actualLoaders = loaders.filter((loader) => {    // vue-loader    if (loader.path === selfPath) {      return false    }    // cache-loader    if (isCacheLoader(loader)) {      return false    }    return true  })  return actualLoaders.length === 0}module.exports = (code) => code// This pitching loader is responsible for intercepting all vue block requests// and transform it into appropriate requests.module.exports.pitch = function (remainingRequest) {  const options = loaderUtils.getOptions(this)  const { cacheDirectory, cacheIdentifier } = options  const query = qs.parse(this.resourceQuery.slice(1))  const isWebpack5 = testWebpack5(this._compiler)  let loaders = this.loaders  // if this is a language block request, eslint-loader may get matched  // multiple times  if (query.type) {    // if this is an inline block, since the whole file itself is being linted,    // remove eslint-loader to avoid duplicate linting.    if (/\.vue$/.test(this.resourcePath)) {      loaders = loaders.filter((l) => !isESLintLoader(l))    } else {      // This is a src import. Just make sure there's not more than 1 instance      // of eslint present.      loaders = dedupeESLintLoader(loaders)    }  }  // remove self  loaders = loaders.filter(isPitcher)  // do not inject if user uses null-loader to void the type (#1239)  if (loaders.some(isNullLoader)) {    return  }  const genRequest = (loaders, lang) => {    // Important: dedupe since both the original rule    // and the cloned rule would match a source import request.    // also make sure to dedupe based on loader path.    // assumes you'd probably never want to apply the same loader on the same    // file twice.    // Exception: in Vue CLI we do need two instances of postcss-loader    // for user config and inline minification. So we need to dedupe baesd on    // path AND query to be safe.    const seen = new Map()    const loaderStrings = []    const enableInlineMatchResource =      isWebpack5 && options.experimentalInlineMatchResource    loaders.forEach((loader) => {      const identifier =        typeof loader === 'string' ? loader : loader.path + loader.query      const request = typeof loader === 'string' ? loader : loader.request      if (!seen.has(identifier)) {        seen.set(identifier, true)        // loader.request contains both the resolved loader path and its options        // query (e.g. ??ref-0)        loaderStrings.push(request)      }    })    if (enableInlineMatchResource) {      return loaderUtils.stringifyRequest(        this,        `${this.resourcePath}${lang ? `.${lang}` : ''}${          this.resourceQuery        }!=!-!${[...loaderStrings, this.resourcePath + this.resourceQuery].join('!')}`      )    }    return loaderUtils.stringifyRequest(      this,      '-!' +        [...loaderStrings, this.resourcePath + this.resourceQuery].join('!')    )  }  // Inject style-post-loader before css-loader for scoped CSS and trimming  if (query.type === `style`) {    if (isWebpack5 && this._compiler.options.experiments && this._compiler.options.experiments.css) {      // If user enables `experiments.css`, then we are trying to emit css code directly.      // Although we can target requests like `xxx.vue?type=style` to match `type: "css"`,      // it will make the plugin a mess.      if (!options.experimentalInlineMatchResource) {        this.emitError(          new Error(            '`experimentalInlineMatchResource` should be enabled if `experiments.css` enabled currently'          )        )        return ''      }      if (query.inline || query.module) {        this.emitError(          new Error(            '`inline` or `module` is currently not supported with `experiments.css` enabled'          )        )        return ''      }      const loaderString = [stylePostLoaderPath, ...loaders]        .map((loader) => {          return typeof loader === 'string' ? loader : loader.request        })        .join('!')        const styleRequest = loaderUtils.stringifyRequest(          this,          `${this.resourcePath}${query.lang ? `.${query.lang}` : ''}${            this.resourceQuery          }!=!-!${loaderString}!${this.resourcePath + this.resourceQuery}`        )        return `@import ${styleRequest};`    }    const cssLoaderIndex = loaders.findIndex(isCSSLoader)    if (cssLoaderIndex > -1) {      const afterLoaders = loaders.slice(0, cssLoaderIndex + 1)      const beforeLoaders = loaders.slice(cssLoaderIndex + 1)      const request = genRequest(        [...afterLoaders, stylePostLoaderPath, ...beforeLoaders],        query.lang || 'css'      )      // console.log(request)      return query.module        ? `export { default } from  ${request}; export * from ${request}`        : `export * from ${request}`    }  }  // for templates: inject the template compiler & optional cache  if (query.type === `template`) {    const path = require('path')    const cacheLoader =      cacheDirectory && cacheIdentifier        ? [            `${require.resolve('cache-loader')}?${JSON.stringify({              // For some reason, webpack fails to generate consistent hash if we              // use absolute paths here, even though the path is only used in a              // comment. For now we have to ensure cacheDirectory is a relative path.              cacheDirectory: (path.isAbsolute(cacheDirectory)                ? path.relative(process.cwd(), cacheDirectory)                : cacheDirectory              ).replace(/\\/g, '/'),              cacheIdentifier: hash(cacheIdentifier) + '-vue-loader-template'            })}`          ]        : []    const preLoaders = loaders.filter(isPreLoader)    const postLoaders = loaders.filter(isPostLoader)    const { is27 } = resolveCompiler(this.rootContext, this)    const request = genRequest([      ...cacheLoader,      ...postLoaders,      ...(is27 ? [] : [templateLoaderPath + `??vue-loader-options`]),      ...preLoaders    ])    // the template compiler uses esm exports    return `export * from ${request}`  }  // if a custom block has no other matching loader other than vue-loader itself  // or cache-loader, we should ignore it  if (query.type === `custom` && shouldIgnoreCustomBlock(loaders)) {    return ``  }  // When the user defines a rule that has only resourceQuery but no test,  // both that rule and the cloned rule will match, resulting in duplicated  // loaders. Therefore it is necessary to perform a dedupe here.  const request = genRequest(loaders)  return `import mod from ${request}; export default mod; export * from ${request}`}
 |