UNPKG

esbuild-plugin-vue-iii

Version:
298 lines 13.8 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.transformMain = void 0; const querystring_1 = __importDefault(require("querystring")); const path_1 = __importDefault(require("path")); const compiler_sfc_1 = require("@vue/compiler-sfc"); const descriptorCache_1 = require("./utils/descriptorCache"); // import { normalizePath } from '@rollup/pluginutils' const path_2 = require("path"); const script_1 = require("./script"); const template_1 = require("./template"); // import { isOnlyTemplateChanged, isEqualBlock } from './handleHotUpdate' const source_map_1 = require("source-map"); const error_1 = require("./utils/error"); const isEqualBlock = () => { }; const isOnlyTemplateChanged = () => { }; const normalizePath = (filename) => filename.split(path_2.win32.sep).join(path_2.posix.sep); // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types async function transformMain(code, filename, options, pluginContext, ssr, asCustomElement) { var _a; const { root, devServer, isProduction } = options; // prev descriptor is only set and used for hmr const prevDescriptor = descriptorCache_1.getPrevDescriptor(filename); const { descriptor, errors } = descriptorCache_1.createDescriptor(filename, code, root, isProduction); if (errors.length) { errors.forEach((error) => pluginContext.error(error_1.createRollupError(filename, error))); return null; } // feature information const hasScoped = descriptor.styles.some((s) => s.scoped); // script const { code: scriptCode, map } = await genScriptCode(descriptor, options, pluginContext, ssr); // template // Check if we can use compile template as inlined render function // inside <script setup>. This can only be done for build because // inlined template cannot be individually hot updated. const useInlineTemplate = !devServer && descriptor.scriptSetup && !(descriptor.template && descriptor.template.src); const hasTemplateImport = descriptor.template && !useInlineTemplate; let templateCode = ''; let templateMap; if (hasTemplateImport) { ; ({ code: templateCode, map: templateMap } = await genTemplateCode(descriptor, options, pluginContext, ssr)); } let renderReplace = ''; if (hasTemplateImport) { renderReplace = ssr ? `_sfc_main.ssrRender = _sfc_ssrRender` : `_sfc_main.render = _sfc_render`; } else { // #2128 // User may empty the template but we didn't provide rerender function before if (prevDescriptor && !isEqualBlock(descriptor.template, prevDescriptor.template)) { renderReplace = ssr ? `_sfc_main.ssrRender = () => {}` : `_sfc_main.render = () => {}`; } } // styles const stylesCode = await genStyleCode(descriptor, pluginContext, asCustomElement); // custom blocks const customBlocksCode = await genCustomBlockCode(descriptor, pluginContext); const output = [ scriptCode, templateCode, stylesCode, customBlocksCode, renderReplace ]; if (hasScoped) { output.push(`_sfc_main.__scopeId = ${JSON.stringify(`data-v-${descriptor.id}`)}`); } if (devServer && !isProduction) { // expose filename during serve for devtools to pickup output.push(`_sfc_main.__file = ${JSON.stringify(filename)}`); } // HMR if (devServer && devServer.config.server.hmr !== false && !ssr && !isProduction) { output.push(`_sfc_main.__hmrId = ${JSON.stringify(descriptor.id)}`); output.push(`typeof __VUE_HMR_RUNTIME__ !== 'undefined' && ` + `__VUE_HMR_RUNTIME__.createRecord(_sfc_main.__hmrId, _sfc_main)`); // check if the template is the only thing that changed if (prevDescriptor && isOnlyTemplateChanged(prevDescriptor, descriptor)) { output.push(`export const _rerender_only = true`); } output.push(`import.meta.hot.accept(({ default: updated, _rerender_only }) => {`, ` if (_rerender_only) {`, ` __VUE_HMR_RUNTIME__.rerender(updated.__hmrId, updated.render)`, ` } else {`, ` __VUE_HMR_RUNTIME__.reload(updated.__hmrId, updated)`, ` }`, `})`); } // SSR module registration by wrapping user setup if (ssr) { const normalizedFilename = normalizePath(path_1.default.relative(options.root, filename)); output.push(`import { useSSRContext as __vite_useSSRContext } from 'vue'`, `const _sfc_setup = _sfc_main.setup`, `_sfc_main.setup = (props, ctx) => {`, ` const ssrContext = __vite_useSSRContext()`, ` ;(ssrContext.modules || (ssrContext.modules = new Set())).add(${JSON.stringify(normalizedFilename)})`, ` return _sfc_setup ? _sfc_setup(props, ctx) : undefined`, `}`); } // if the template is inlined into the main module (indicated by the presence // of templateMap, we need to concatenate the two source maps. let resolvedMap = map; if (map && templateMap) { const generator = source_map_1.SourceMapGenerator.fromSourceMap(new source_map_1.SourceMapConsumer(map)); const offset = ((_a = scriptCode.match(/\r?\n/g)) === null || _a === void 0 ? void 0 : _a.length) || 1; const templateMapConsumer = new source_map_1.SourceMapConsumer(templateMap); templateMapConsumer.eachMapping((m) => { generator.addMapping({ source: m.source, original: { line: m.originalLine, column: m.originalColumn }, generated: { line: m.generatedLine + offset, column: m.generatedColumn } }); }); resolvedMap = generator.toJSON(); // if this is a template only update, we will be reusing a cached version // of the main module compile result, which has outdated sourcesContent. resolvedMap.sourcesContent = templateMap.sourcesContent; } output.push(`export default _sfc_main`); return { code: output.join('\n'), map: resolvedMap || { mappings: '' } }; } exports.transformMain = transformMain; async function genTemplateCode(descriptor, options, pluginContext, ssr) { const template = descriptor.template; // If the template is not using pre-processor AND is not using external src, // compile and inline it directly in the main module. When served in vite this // saves an extra request per SFC which can improve load performance. if (!template.lang && !template.src) { return template_1.transformTemplateInMain(template.content, descriptor, options, pluginContext, ssr); } else { if (template.src) { await linkSrcToDescriptor(template.src, descriptor, pluginContext); } const src = template.src || descriptor.filename; const srcQuery = template.src ? `&src` : ``; const attrsQuery = attrsToQuery(template.attrs, 'js', true); const query = `?vue&type=template${srcQuery}${attrsQuery}`; const request = JSON.stringify(src + query); const renderFnName = ssr ? 'ssrRender' : 'render'; return { code: `import { ${renderFnName} as _sfc_${renderFnName} } from ${request}`, map: undefined }; } } async function genScriptCode(descriptor, options, pluginContext, ssr) { let scriptCode = `const _sfc_main = {}`; let map; const script = script_1.resolveScript(descriptor, options, ssr); if (script) { // If the script is js/ts and has no external src, it can be directly placed // in the main module. if ((!script.lang || (script.lang === 'ts' && options.devServer)) && !script.src) { scriptCode = script.content; map = script.map; if (script.lang === 'ts') { const result = await options.devServer.transformWithEsbuild(scriptCode, descriptor.filename, { loader: 'ts' }, map); scriptCode = result.code; map = result.map; } scriptCode = compiler_sfc_1.rewriteDefault(scriptCode, `_sfc_main`); } else { if (script.src) { await linkSrcToDescriptor(script.src, descriptor, pluginContext); } const src = script.src || descriptor.filename; const langFallback = (script.src && path_1.default.extname(src).slice(1)) || 'js'; const attrsQuery = attrsToQuery(script.attrs, langFallback); const srcQuery = script.src ? `&src` : ``; const query = `?vue&type=script${srcQuery}${attrsQuery}`; const request = JSON.stringify(src + query); scriptCode = `import _sfc_main from ${request}\n` + `export * from ${request}`; // support named exports } } return { code: scriptCode, map: map }; } async function genStyleCode(descriptor, pluginContext, asCustomElement) { let stylesCode = ``; let hasCSSModules = false; if (descriptor.styles.length) { for (let i = 0; i < descriptor.styles.length; i++) { const style = descriptor.styles[i]; if (style.src) { await linkSrcToDescriptor(style.src, descriptor, pluginContext); } const src = style.src || descriptor.filename; // do not include module in default query, since we use it to indicate // that the module needs to export the modules json const attrsQuery = attrsToQuery(style.attrs, 'css'); const srcQuery = style.src ? `&src` : ``; const directQuery = asCustomElement ? `&inline` : ``; const query = `?vue&type=style&index=${i}${srcQuery}${directQuery}`; const styleRequest = src + query + attrsQuery; if (style.module) { if (asCustomElement) { throw new Error(`<style module> is not supported in custom elements mode.`); } if (!hasCSSModules) { stylesCode += `\nconst cssModules = _sfc_main.__cssModules = {}`; hasCSSModules = true; } stylesCode += genCSSModulesCode(i, styleRequest, style.module); } else { if (asCustomElement) { stylesCode += `\nimport _style_${i} from ${JSON.stringify(styleRequest)}`; } else { stylesCode += `\nimport ${JSON.stringify(styleRequest)}`; } } // TODO SSR critical CSS collection } if (asCustomElement) { stylesCode += `\n_sfc_main.styles = [${descriptor.styles .map((_, i) => `_style_${i}`) .join(',')}]`; } } return stylesCode; } function genCSSModulesCode(index, request, moduleName) { const styleVar = `style${index}`; const exposedName = typeof moduleName === 'string' ? moduleName : '$style'; // inject `.module` before extension so vite handles it as css module const moduleRequest = request.replace(/\.(\w+)$/, '.module.$1'); return (`\nimport ${styleVar} from ${JSON.stringify(moduleRequest)}` + `\ncssModules["${exposedName}"] = ${styleVar}`); } async function genCustomBlockCode(descriptor, pluginContext) { let code = ''; for (let index = 0; index < descriptor.customBlocks.length; index++) { const block = descriptor.customBlocks[index]; if (block.src) { await linkSrcToDescriptor(block.src, descriptor, pluginContext); } const src = block.src || descriptor.filename; const attrsQuery = attrsToQuery(block.attrs, block.type); const srcQuery = block.src ? `&src` : ``; const query = `?vue&type=${block.type}&index=${index}${srcQuery}${attrsQuery}`; const request = JSON.stringify(src + query); code += `import block${index} from ${request}\n`; code += `if (typeof block${index} === 'function') block${index}(_sfc_main)\n`; } return code; } /** * For blocks with src imports, it is important to link the imported file * with its owner SFC descriptor so that we can get the information about * the owner SFC when compiling that file in the transform phase. */ async function linkSrcToDescriptor(src, descriptor, pluginContext) { var _a; const srcFile = ((_a = (await pluginContext.resolve(src, descriptor.filename))) === null || _a === void 0 ? void 0 : _a.id) || src; // #1812 if the src points to a dep file, the resolved id may contain a // version query. descriptorCache_1.setDescriptor(srcFile.replace(/\?.*$/, ''), descriptor); } // these are built-in query parameters so should be ignored // if the user happen to add them as attrs const ignoreList = ['id', 'index', 'src', 'type', 'lang', 'module']; function attrsToQuery(attrs, langFallback, forceLangFallback = false) { let query = ``; for (const name in attrs) { const value = attrs[name]; if (!ignoreList.includes(name)) { query += `&${querystring_1.default.escape(name)}${value ? `=${querystring_1.default.escape(String(value))}` : ``}`; } } if (langFallback || attrs.lang) { query += `lang` in attrs ? forceLangFallback ? `&lang.${langFallback}` : `&lang.${attrs.lang}` : `&lang.${langFallback}`; } return query; } //# sourceMappingURL=main.js.map