From 30464a8f505428c832ffdcf7d7d3f9a6aae1cfd0 Mon Sep 17 00:00:00 2001 From: Evan You Date: Tue, 14 Jun 2022 22:21:15 +0800 Subject: [PATCH] feat: apply js loaders to compiled template code when used with 2.7 --- lib/compiler.js | 32 ++++--- lib/index.js | 34 ++++--- lib/loaders/pitcher.js | 44 +++++---- lib/loaders/stylePostLoader.js | 2 +- lib/loaders/templateLoader.js | 5 +- lib/plugin-webpack4.js | 77 +++++++++++++-- lib/plugin-webpack5.js | 166 ++++++++++++++++++++++----------- lib/resolveScript.js | 2 +- package.json | 2 +- 9 files changed, 252 insertions(+), 112 deletions(-) diff --git a/lib/compiler.js b/lib/compiler.js index e83df169b..645c6eedf 100644 --- a/lib/compiler.js +++ b/lib/compiler.js @@ -2,18 +2,18 @@ let cached -exports.resolveCompiler = function (loaderContext) { +exports.resolveCompiler = function (ctx, loaderContext) { if (cached) { return cached } - const ctx = loaderContext.rootContext // check 2.7 try { const pkg = loadFromContext('vue/package.json', ctx) const [major, minor] = pkg.version.split('.') - if (major === '2' && minor === '7') { + if (major === '2' && Number(minor) >= 7) { return (cached = { + is27: true, compiler: loadFromContext('vue/compiler-sfc', ctx), templateCompiler: undefined }) @@ -22,7 +22,7 @@ exports.resolveCompiler = function (loaderContext) { return (cached = { compiler: require('@vue/component-compiler-utils'), - templateCompiler: loadTemplateCompiler(loaderContext) + templateCompiler: loadTemplateCompiler(ctx, loaderContext) }) } @@ -32,19 +32,23 @@ function loadFromContext (path, ctx) { })) } -function loadTemplateCompiler (loaderContext) { +function loadTemplateCompiler (ctx, loaderContext) { try { - return loadFromContext('vue-template-compiler', loaderContext.rootContext) + return loadFromContext('vue-template-compiler', ctx) } catch (e) { - if (/version mismatch/.test(e.toString())) { - loaderContext.emitError(e) - } else { - loaderContext.emitError( - new Error( - `[vue-loader] vue-template-compiler must be installed as a peer dependency, ` + - `or a compatible compiler implementation must be passed via options.` + if (loaderContext) { + if (/version mismatch/.test(e.toString())) { + loaderContext.emitError(e) + } else { + loaderContext.emitError( + new Error( + `[vue-loader] vue-template-compiler must be installed as a peer dependency, ` + + `or a compatible compiler implementation must be passed via options.` + ) ) - ) + } + } else { + throw e } } } diff --git a/lib/index.js b/lib/index.js index 853d2738d..d6e96edd5 100644 --- a/lib/index.js +++ b/lib/index.js @@ -52,7 +52,10 @@ module.exports = function (source) { const context = rootContext || process.cwd() const sourceRoot = path.dirname(path.relative(context, resourcePath)) - const { compiler, templateCompiler } = resolveCompiler(loaderContext) + const { compiler, templateCompiler } = resolveCompiler( + rootContext, + loaderContext + ) const descriptor = compiler.parse({ source, @@ -98,6 +101,21 @@ module.exports = function (source) { (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?.lang || scriptSetup?.lang + // isTS = !!(lang && /tsx?/.test(lang)) + const src = (script && !scriptSetup && script.src) || resourcePath + const attrsQuery = attrsToQuery((scriptSetup || script).attrs, 'js') + const query = `?vue&type=script${attrsQuery}${inheritQuery}` + const request = stringifyRequest(src + query) + scriptImport = + `import script from ${request}\n` + `export * from ${request}` // support named exports + } + // template let templateImport = `var render, staticRenderFns` let templateRequest @@ -106,23 +124,13 @@ module.exports = function (source) { 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}${inheritQuery}` const request = (templateRequest = stringifyRequest(src + query)) templateImport = `import { render, staticRenderFns } from ${request}` } - // script - let scriptImport = `var script = {}` - const { script, scriptSetup } = descriptor - if (script || scriptSetup) { - const src = (script && !scriptSetup && script.src) || resourcePath - const attrsQuery = attrsToQuery((scriptSetup || script).attrs, 'js') - const query = `?vue&type=script${attrsQuery}${inheritQuery}` - const request = stringifyRequest(src + query) - scriptImport = - `import script from ${request}\n` + `export * from ${request}` // support named exports - } - // styles let stylesCode = `` if (descriptor.styles.length) { diff --git a/lib/loaders/pitcher.js b/lib/loaders/pitcher.js index 2d7947f86..5bf28c674 100644 --- a/lib/loaders/pitcher.js +++ b/lib/loaders/pitcher.js @@ -4,6 +4,7 @@ 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 isESLintLoader = l => /(\/|\\|@)eslint-loader/.test(l.path) const isNullLoader = l => /(\/|\\|@)null-loader/.test(l.path) @@ -90,9 +91,8 @@ module.exports.pitch = function (remainingRequest) { const loaderStrings = [] loaders.forEach(loader => { - const identifier = typeof loader === 'string' - ? loader - : (loader.path + loader.query) + 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) @@ -102,10 +102,11 @@ module.exports.pitch = function (remainingRequest) { } }) - return loaderUtils.stringifyRequest(this, '-!' + [ - ...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 @@ -129,25 +130,30 @@ module.exports.pitch = function (remainingRequest) { // 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 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, - templateLoaderPath + `??vue-loader-options`, + ...(is27 ? [] : [templateLoaderPath + `??vue-loader-options`]), ...preLoaders ]) // console.log(request) diff --git a/lib/loaders/stylePostLoader.js b/lib/loaders/stylePostLoader.js index 949fe0a5a..b6390a52c 100644 --- a/lib/loaders/stylePostLoader.js +++ b/lib/loaders/stylePostLoader.js @@ -6,7 +6,7 @@ const { resolveCompiler } = require('../compiler') // for any