UNPKG

rollup-plugin-vue

Version:
261 lines (257 loc) 11.3 kB
import { createFilter } from 'rollup-pluginutils'; import { stringify, parse } from 'querystring'; import { basename, resolve, dirname } from 'path'; import { createDefaultCompiler, assemble } from '@vue/component-compiler'; import { parse as parse$1 } from '@vue/component-compiler-utils'; import debug from 'debug'; const GET_QUERY = /\.vue(\.[a-z]+?)?\?(.+)$/i; const PARAM_NAME = 'rollup-plugin-vue'; function createVueFilter(include = [/\.vue$/i], exclude = []) { const filter = createFilter(include, exclude); return id => filter(id); } function getVueMetaFromQuery(id) { const match = GET_QUERY.exec(id); if (match) { const query = parse(match[2]); if (PARAM_NAME in query) { const data = (Array.isArray(query[PARAM_NAME]) ? query[PARAM_NAME][0] : query[PARAM_NAME]); const [type, index, lang] = data.split('.'); return (lang ? { type, lang, index: parseInt(index) } // styles.0.css : { type, lang: index }); // script.js } } return null; } function isVuePartRequest(id) { return getVueMetaFromQuery(id) !== null; } const createVuePartRequest = ((filename, lang, type, index) => { lang = lang || createVuePartRequest.defaultLang[type]; const match = GET_QUERY.exec(filename); const query = match ? parse(match[2]) : {}; query[PARAM_NAME] = [type, index, lang] .filter(it => it !== undefined) .join('.'); return `${basename(filename)}?${stringify(query)}`; }); createVuePartRequest.defaultLang = { template: 'html', styles: 'css', script: 'js' }; function parseVuePartRequest(id) { if (!id.includes('.vue')) return; const filename = id.substr(0, id.lastIndexOf('.vue') + 4); const params = getVueMetaFromQuery(id); if (params === null) return; return { filename, meta: params }; } function resolveVuePart(descriptors, { filename, meta }) { const descriptor = descriptors.get(filename); if (!descriptor) throw Error('File not processed yet, ' + filename); const blocks = descriptor[meta.type]; const block = Array.isArray(blocks) ? blocks[meta.index] : blocks; if (!block) throw Error(`Requested (type=${meta.type} & index=${meta.index}) block not found in ${filename}`); return block; } function transformRequireToImport(code) { const imports = {}; let strImports = ''; code = code.replace(/require\(("(?:[^"\\]|\\.)+"|'(?:[^'\\]|\\.)+')\)/g, (_, name) => { if (!(name in imports)) { imports[name] = `__$_require_${name.replace(/[^a-z0-9]/g, '_').replace(/_{2,}/g, '_').replace(/^_|_$/g, '')}__`; strImports += 'import ' + imports[name] + ' from ' + name + '\n'; } return imports[name]; }); return strImports + code; } const templateCompiler = require('vue-template-compiler'); const hash = require('hash-sum'); const { version } = require('../package.json'); const d = debug('rollup-plugin-vue'); const dR = debug('rollup-plugin-vue:resolve'); const dL = debug('rollup-plugin-vue:load'); const dT = debug('rollup-plugin-vue:transform'); /** * Rollup plugin for handling .vue files. */ function vue(opts = {}) { const isVue = createVueFilter(opts.include, opts.exclude); const isProduction = process.env.NODE_ENV === 'production' || process.env.BUILD === 'production'; d('Version ' + version); d(`Build environment: ${isProduction ? 'production' : 'development'}`); d(`Build target: ${process.env.VUE_ENV || 'browser'}`); if (!opts.normalizer) opts.normalizer = '~' + 'vue-runtime-helpers/dist/normalize-component.js'; if (!opts.styleInjector) opts.styleInjector = '~' + 'vue-runtime-helpers/dist/inject-style/browser.js'; if (!opts.styleInjectorSSR) opts.styleInjectorSSR = '~' + 'vue-runtime-helpers/dist/inject-style/server.js'; createVuePartRequest.defaultLang = { ...createVuePartRequest.defaultLang, ...opts.defaultLang, }; const shouldExtractCss = opts.css === false; const blacklisted = new Set(opts.blackListCustomBlocks || ['*']); const whitelisted = new Set(opts.whiteListCustomBlocks || []); const isAllowed = (customBlockType) => (!blacklisted.has('*') || !blacklisted.has(customBlockType)) && (whitelisted.has('*') || whitelisted.has(customBlockType)); const beforeAssemble = opts.beforeAssemble || ((d) => d); delete opts.beforeAssemble; delete opts.css; delete opts.blackListCustomBlocks; delete opts.whiteListCustomBlocks; delete opts.defaultLang; delete opts.include; delete opts.exclude; opts.template = { transformAssetUrls: { video: ['src', 'poster'], source: 'src', img: 'src', image: 'xlink:href', }, ...opts.template, }; if (opts.template && typeof opts.template.isProduction === 'undefined') { opts.template.isProduction = isProduction; } const compiler = createDefaultCompiler(opts); const descriptors = new Map(); if (opts.css === false) d('Running in CSS extract mode'); return { name: 'VuePlugin', resolveId(id, importer) { const request = id; if (id.startsWith('vue-runtime-helpers/')) { id = require.resolve(id); dR(`form: ${request} \nto: ${id}\n`); return id; } if (!isVuePartRequest(id)) return; id = resolve(dirname(importer), id); const ref = parseVuePartRequest(id); if (ref) { const element = resolveVuePart(descriptors, ref); const src = element.src; if (ref.meta.type !== 'styles' && typeof src === 'string') { if (src.startsWith('.')) { return resolve(dirname(ref.filename), src); } else { return require.resolve(src, { paths: [dirname(ref.filename)] }); } } dR(`from: ${request} \nto: ${id}\n`); return id; } }, load(id) { const request = parseVuePartRequest(id); if (!request) return null; const element = resolveVuePart(descriptors, request); const code = 'code' in element ? element.code // .code is set when extract styles is used. { css: false } : element.content; const map = element.map; dL(`id: ${id}\ncode: \n${code}\nmap: ${JSON.stringify(map, null, 2)}\n\n`); return { code, map }; }, async transform(source, filename) { if (isVue(filename)) { const descriptor = JSON.parse(JSON.stringify(parse$1({ filename, source, compiler: opts.compiler || templateCompiler, compilerParseOptions: opts.compilerParseOptions, sourceRoot: opts.sourceRoot, needMap: true, }))); const scopeId = 'data-v-' + (isProduction ? hash(basename(filename) + source) : hash(filename + source)); descriptors.set(filename, descriptor); const styles = await Promise.all(descriptor.styles.map(async (style) => { const compiled = await compiler.compileStyleAsync(filename, scopeId, style); if (compiled.errors.length > 0) throw Error(compiled.errors[0]); return compiled; })); const input = { scopeId, styles, customBlocks: [], }; if (descriptor.template) { input.template = compiler.compileTemplate(filename, descriptor.template); input.template.code = transformRequireToImport(input.template.code); if (input.template.errors && input.template.errors.length) { input.template.errors.map((error) => this.error(error)); } if (input.template.tips && input.template.tips.length) { input.template.tips.map((message) => this.warn({ message })); } } input.script = descriptor.script ? { code: ` export * from '${createVuePartRequest(filename, descriptor.script.lang || 'js', 'script')}' import script from '${createVuePartRequest(filename, descriptor.script.lang || 'js', 'script')}' export default script // For security concerns, we use only base name in production mode. See https://github.com/vuejs/rollup-plugin-vue/issues/258 script.__file = ${isProduction ? JSON.stringify(basename(filename)) : JSON.stringify(filename)} `, } : { code: '' }; if (shouldExtractCss) { input.styles = input.styles .map((style, index) => { descriptor.styles[index].code = style.code; input.script.code += '\n' + `import '${createVuePartRequest(filename, 'css', 'styles', index)}'`; if (style.module || descriptor.styles[index].scoped) { return { ...style, code: '', map: undefined }; } }) .filter(Boolean); } input.script.code = input.script.code.replace(/^\s+/gm, ''); const result = assemble(compiler, filename, beforeAssemble(input), opts); descriptor.customBlocks.forEach((block, index) => { if (!isAllowed(block.type)) return; result.code += '\n' + `export * from '${createVuePartRequest(filename, (typeof block.attrs.lang === 'string' && block.attrs.lang) || createVuePartRequest.defaultLang[block.type] || block.type, 'customBlocks', index)}'`; }); dT(`id: ${filename}\ncode:\n${result.code}\n\nmap:\n${JSON.stringify(result.map, null, 2)}\n`); result.map = result.map || { mappings: '' }; return result; } }, }; } export default vue; //# sourceMappingURL=rollup-plugin-vue.mjs.map