UNPKG

@vue/component-compiler

Version:
396 lines (365 loc) 15.5 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const source_map_1 = require("source-map"); const source_map_2 = require("./source-map"); const path = require("path"); function assemble(compiler, filename, result, options = {}) { return assembleFromSource(compiler, options, { filename, scopeId: result.scopeId, script: result.script && { source: result.script.code, map: result.script.map }, template: result.template && Object.assign({}, result.template, { source: result.template.code, functional: result.template.functional }), styles: result.styles.map(style => { if (style.errors.length) { console.error(style.errors); } return Object.assign({}, style, { source: style.code, media: style.media, scoped: style.scoped, moduleName: style.moduleName, module: style.module }); }) }); } exports.assemble = assemble; function assembleFromSource(compiler, options, { filename, script, template, styles, scopeId }) { script = script || { source: 'export default {}' }; template = template || { source: '' }; let map = undefined; const mapGenerator = new source_map_1.SourceMapGenerator({ file: filename.replace(/\\/g, '/') }); const hasScopedStyle = styles.some(style => style.scoped === true); const hasStyle = styles.some(style => style.source || style.module); const e = (any) => JSON.stringify(any); const createImport = (name, value) => value.startsWith('~') ? `import ${name} from '${value.substr(1)}'` : `const ${name} = ${value}`; const o = e; const IDENTIFIER = /^[a-z0-9]+$/i; // language=JavaScript const inlineCreateInjector = `function __vue_create_injector__() { const styles = __vue_create_injector__.styles || (__vue_create_injector__.styles = {}) const isOldIE = typeof navigator !== 'undefined' && /msie [6-9]\\\\b/.test(navigator.userAgent.toLowerCase()) return function addStyle(id, css) { if (document.querySelector('style[data-vue-ssr-id~="' + id + '"]')) return // SSR styles are present. const group = isOldIE ? css.media || 'default' : id const style = styles[group] || (styles[group] = { ids: [], parts: [], element: undefined }) if (!style.ids.includes(id)) { let code = css.source let index = style.ids.length style.ids.push(id) if (${e(compiler.template.isProduction)} && css.map) { // https://developer.chrome.com/devtools/docs/javascript-debugging // this makes source maps inside style tags work properly in Chrome code += '\\n/*# sourceURL=' + css.map.sources[0] + ' */' // http://stackoverflow.com/a/26603875 code += '\\n/*# sourceMappingURL=data:application/json;base64,' + btoa(unescape(encodeURIComponent(JSON.stringify(css.map)))) + ' */' } if (isOldIE) { style.element = style.element || document.querySelector('style[data-group=' + group + ']') } if (!style.element) { const head = document.head || document.getElementsByTagName('head')[0] const el = style.element = document.createElement('style') el.type = 'text/css' if (css.media) el.setAttribute('media', css.media) if (isOldIE) { el.setAttribute('data-group', group) el.setAttribute('data-next-index', '0') } head.appendChild(el) } if (isOldIE) { index = parseInt(style.element.getAttribute('data-next-index')) style.element.setAttribute('data-next-index', index + 1) } if (style.element.styleSheet) { style.parts.push(code) style.element.styleSheet.cssText = style.parts .filter(Boolean) .join('\\n') } else { const textNode = document.createTextNode(code) const nodes = style.element.childNodes if (nodes[index]) style.element.removeChild(nodes[index]) if (nodes.length) style.element.insertBefore(textNode, nodes[index]) else style.element.appendChild(textNode) } } } }`; const createInjector = options.styleInjector ? createImport('__vue_create_injector__', options.styleInjector) : inlineCreateInjector; // language=JavaScript const inlineCreateInjectorSSR = `function __vue_create_injector_ssr__(context) { if (!context && typeof __VUE_SSR_CONTEXT__ !== 'undefined') { context = __VUE_SSR_CONTEXT__ } if (!context) return function () {} if (!context.hasOwnProperty('styles')) { Object.defineProperty(context, 'styles', { enumerable: true, get: () => context._styles }) context._renderStyles = renderStyles } function renderStyles(styles) { let css = '' for (const {ids, media, parts} of styles) { css += '<style data-vue-ssr-id="' + ids.join(' ') + '"' + (media ? ' media="' + media + '"' : '') + '>' + parts.join('\\n') + '</style>' } return css } return function addStyle(id, css) { const group = ${e(compiler.template.isProduction)} ? css.media || 'default' : id const style = context._styles[group] || (context._styles[group] = { ids: [], parts: [] }) if (!style.ids.includes(id)) { style.media = css.media style.ids.push(id) let code = css.source if (${e(!compiler.template.isProduction)} && css.map) { // https://developer.chrome.com/devtools/docs/javascript-debugging // this makes source maps inside style tags work properly in Chrome code += '\\n/*# sourceURL=' + css.map.sources[0] + ' */' // http://stackoverflow.com/a/26603875 code += '\\n/*# sourceMappingURL=data:application/json;base64,' + btoa(unescape(encodeURIComponent(JSON.stringify(css.map)))) + ' */' } style.parts.push(code) } } }`; const createInjectorSSR = options.styleInjectorSSR ? createImport('__vue_create_injector_ssr__', options.styleInjectorSSR) : inlineCreateInjectorSSR; const inlineCreateInjectorShadow = `function __vue_create_injector_shadow__(__, shadowRoot) { function createStyleElement(shadowRoot) { var styleElement = document.createElement('style') styleElement.type = 'text/css' shadowRoot.appendChild(styleElement) return styleElement } return function addStyle(id, css) { const styleElement = createStyleElement(shadowRoot) if (css.media) styleElement.setAttribute('media', css.media) let code = css.source if (${e(compiler.template.isProduction)} && css.map) { // https://developer.chrome.com/devtools/docs/javascript-debugging // this makes source maps inside style tags work properly in Chrome code += '\\n/*# sourceURL=' + css.map.sources[0] + ' */' // http://stackoverflow.com/a/26603875 code += '\\n/*# sourceMappingURL=data:application/json;base64,' + btoa(unescape(encodeURIComponent(JSON.stringify(css.map)))) + ' */' } if ('styleSheet' in styleElement) { styleElement.styleSheet.cssText = code } else { while (styleElement.firstChild) { styleElement.removeChild(styleElement.firstChild) } styleElement.appendChild(document.createTextNode(code)) } } }`; const createInjectorShadow = options.styleInjectorShadow ? createImport('__vue_create_injector_shadow__', options.styleInjectorShadow) : inlineCreateInjectorShadow; // language=JavaScript const inlineNormalizeComponent = `function __vue_normalize__( template, style, script, scope, functional, moduleIdentifier, shadowMode, createInjector, createInjectorSSR, createInjectorShadow ) { const component = (typeof script === 'function' ? script.options : script) || {} // For security concerns, we use only base name in production mode. component.__file = ${compiler.template.isProduction ? e(path.basename(filename)) : e(filename)} if (!component.render) { component.render = template.render component.staticRenderFns = template.staticRenderFns component._compiled = true if (functional) component.functional = true } component._scopeId = scope if (${e(hasStyle)}) { let hook if (${e(compiler.template.optimizeSSR)}) { // In SSR. hook = function(context) { // 2.3 injection context = context || // cached call (this.$vnode && this.$vnode.ssrContext) || // stateful (this.parent && this.parent.$vnode && this.parent.$vnode.ssrContext) // functional // 2.2 with runInNewContext: true if (!context && typeof __VUE_SSR_CONTEXT__ !== 'undefined') { context = __VUE_SSR_CONTEXT__ } // inject component styles if (style) { style.call(this, createInjectorSSR(context)) } // register component module identifier for async chunk inference if (context && context._registeredComponents) { context._registeredComponents.add(moduleIdentifier) } } // used by ssr in case component is cached and beforeCreate // never gets called component._ssrRegister = hook } else if (style) { hook = shadowMode ? function(context) { style.call(this, createInjectorShadow(context, this.$root.$options.shadowRoot)) } : function(context) { style.call(this, createInjector(context)) } } if (hook !== undefined) { if (component.functional) { // register for functional component in vue file const originalRender = component.render component.render = function renderWithStyleInjection(h, context) { hook.call(context) return originalRender(h, context) } } else { // inject component registration as beforeCreate hook const existing = component.beforeCreate component.beforeCreate = existing ? [].concat(existing, hook) : [hook] } } } return component }`; const normalizeComponent = options.normalizer ? createImport('__vue_normalize__', options.normalizer) : inlineNormalizeComponent; const DEFAULT_EXPORT = 'const __vue_script__ ='; // language=JavaScript let code = `/* script */\n${script.source.replace(/export\s+default/, DEFAULT_EXPORT)}` + `\n/* template */\n${template.source .replace('var render =', 'var __vue_render__ =') .replace('var staticRenderFns =', 'var __vue_staticRenderFns__ =') .replace('render._withStripped =', '__vue_render__._withStripped =')} /* style */ const __vue_inject_styles__ = ${hasStyle ? `function (inject) { if (!inject) return ${styles.map((style, index) => { const source = IDENTIFIER.test(style.source) ? style.source : e(style.source); const map = !compiler.template.isProduction ? typeof style.map === 'string' && IDENTIFIER.test(style.map) ? style.map : o(style.map) : undefined; const tokens = typeof style.module === 'string' && IDENTIFIER.test(style.module) ? style.module : o(style.module); return ((source ? `inject("${scopeId + '_' + index}", { source: ${source}, map: ${map}, media: ${e(style.media)} })\n` : '') + (style.moduleName ? `Object.defineProperty(this, "${style.moduleName}", { value: ${tokens} })` + '\n' : '')); })} }` : 'undefined'} /* scoped */ const __vue_scope_id__ = ${hasScopedStyle ? e(scopeId) : 'undefined'} /* module identifier */ const __vue_module_identifier__ = ${compiler.template.optimizeSSR ? e(scopeId) : 'undefined'} /* functional template */ const __vue_is_functional_template__ = ${e(template.functional)} /* component normalizer */ ${normalizeComponent} /* style inject */ ${hasStyle && !compiler.template.optimizeSSR && !options.isWebComponent ? createInjector : ''} /* style inject SSR */ ${hasStyle && compiler.template.optimizeSSR ? createInjectorSSR : ''} /* style inject shadow dom */ ${hasStyle && options.isWebComponent ? createInjectorShadow : ''} `; // generate source map. { const input = script.source.split('\n'); input.forEach((sourceLine, index) => { if (!sourceLine) return; const matches = /export\s+default/.exec(sourceLine); if (matches) { const pos = sourceLine.indexOf(matches[0]); if (pos > 0) { mapGenerator.addMapping({ source: filename, original: { line: index + 1, column: 0 }, generated: { line: index + 2, column: 0 } }); } mapGenerator.addMapping({ source: filename, original: { line: index + 1, column: pos }, generated: { line: index + 2, column: pos } }); if (sourceLine.substr(pos + matches[0].length)) { mapGenerator.addMapping({ source: filename, original: { line: index + 1, column: pos + matches[0].length }, generated: { line: index + 2, column: pos + DEFAULT_EXPORT.length } }); } } else { mapGenerator.addMapping({ source: filename, original: { line: index + 1, column: 0 }, generated: { line: index + 2, column: 0 } }); } }); } code += ` const __vue_component__ = /*#__PURE__*/__vue_normalize__( ${code.indexOf('__vue_render__') > -1 ? '{ render: __vue_render__, staticRenderFns: __vue_staticRenderFns__ }' : '{}'}, __vue_inject_styles__, ${code.indexOf('__vue_script__') > -1 ? '__vue_script__' : '{}'}, __vue_scope_id__, __vue_is_functional_template__, __vue_module_identifier__, ${options.isWebComponent ? 'true' : 'false'}, ${code.indexOf('__vue_create_injector__') > -1 ? '__vue_create_injector__' : 'undefined'}, ${code.indexOf('__vue_create_injector_ssr__') > -1 ? '__vue_create_injector_ssr__' : 'undefined'}, ${code.indexOf('__vue_create_injector_shadow__') > -1 ? '__vue_create_injector_shadow__' : 'undefined'} )\n export default __vue_component__`; if (script.map) { map = source_map_2.merge(script.map, JSON.parse(mapGenerator.toString())); } else { map = JSON.parse(mapGenerator.toString()); } return { code, map }; } exports.assembleFromSource = assembleFromSource;