aboutsummaryrefslogtreecommitdiff
path: root/node_modules/vue/src/server/optimizing-compiler
diff options
context:
space:
mode:
authorruki <waruqi@gmail.com>2018-11-08 00:38:48 +0800
committerruki <waruqi@gmail.com>2018-11-07 21:53:09 +0800
commit26105034da4fcce7ac883c899d781f016559310d (patch)
treec459a5dc4e3aa0972d9919033ece511ce76dd129 /node_modules/vue/src/server/optimizing-compiler
parent2c77f00f1a7ecb6c8192f9c16d3b2001b254a107 (diff)
downloadxmake-docs-26105034da4fcce7ac883c899d781f016559310d.tar.gz
xmake-docs-26105034da4fcce7ac883c899d781f016559310d.zip
switch to vuepress
Diffstat (limited to 'node_modules/vue/src/server/optimizing-compiler')
-rw-r--r--node_modules/vue/src/server/optimizing-compiler/codegen.js260
-rw-r--r--node_modules/vue/src/server/optimizing-compiler/index.js20
-rw-r--r--node_modules/vue/src/server/optimizing-compiler/modules.js126
-rw-r--r--node_modules/vue/src/server/optimizing-compiler/optimizer.js140
-rw-r--r--node_modules/vue/src/server/optimizing-compiler/runtime-helpers.js150
5 files changed, 696 insertions, 0 deletions
diff --git a/node_modules/vue/src/server/optimizing-compiler/codegen.js b/node_modules/vue/src/server/optimizing-compiler/codegen.js
new file mode 100644
index 00000000..26097c07
--- /dev/null
+++ b/node_modules/vue/src/server/optimizing-compiler/codegen.js
@@ -0,0 +1,260 @@
+/* @flow */
+
+// The SSR codegen is essentially extending the default codegen to handle
+// SSR-optimizable nodes and turn them into string render fns. In cases where
+// a node is not optimizable it simply falls back to the default codegen.
+
+import {
+ genIf,
+ genFor,
+ genData,
+ genText,
+ genElement,
+ genChildren,
+ CodegenState
+} from 'compiler/codegen/index'
+
+import {
+ genAttrSegments,
+ genDOMPropSegments,
+ genClassSegments,
+ genStyleSegments,
+ applyModelTransform
+} from './modules'
+
+import { escape } from 'web/server/util'
+import { optimizability } from './optimizer'
+import type { CodegenResult } from 'compiler/codegen/index'
+
+export type StringSegment = {
+ type: number;
+ value: string;
+};
+
+// segment types
+export const RAW = 0
+export const INTERPOLATION = 1
+export const EXPRESSION = 2
+
+export function generate (
+ ast: ASTElement | void,
+ options: CompilerOptions
+): CodegenResult {
+ const state = new CodegenState(options)
+ const code = ast ? genSSRElement(ast, state) : '_c("div")'
+ return {
+ render: `with(this){return ${code}}`,
+ staticRenderFns: state.staticRenderFns
+ }
+}
+
+function genSSRElement (el: ASTElement, state: CodegenState): string {
+ if (el.for && !el.forProcessed) {
+ return genFor(el, state, genSSRElement)
+ } else if (el.if && !el.ifProcessed) {
+ return genIf(el, state, genSSRElement)
+ } else if (el.tag === 'template' && !el.slotTarget) {
+ return el.ssrOptimizability === optimizability.FULL
+ ? genChildrenAsStringNode(el, state)
+ : genSSRChildren(el, state) || 'void 0'
+ }
+
+ switch (el.ssrOptimizability) {
+ case optimizability.FULL:
+ // stringify whole tree
+ return genStringElement(el, state)
+ case optimizability.SELF:
+ // stringify self and check children
+ return genStringElementWithChildren(el, state)
+ case optimizability.CHILDREN:
+ // generate self as VNode and stringify children
+ return genNormalElement(el, state, true)
+ case optimizability.PARTIAL:
+ // generate self as VNode and check children
+ return genNormalElement(el, state, false)
+ default:
+ // bail whole tree
+ return genElement(el, state)
+ }
+}
+
+function genNormalElement (el, state, stringifyChildren) {
+ const data = el.plain ? undefined : genData(el, state)
+ const children = stringifyChildren
+ ? `[${genChildrenAsStringNode(el, state)}]`
+ : genSSRChildren(el, state, true)
+ return `_c('${el.tag}'${
+ data ? `,${data}` : ''
+ }${
+ children ? `,${children}` : ''
+ })`
+}
+
+function genSSRChildren (el, state, checkSkip) {
+ return genChildren(el, state, checkSkip, genSSRElement, genSSRNode)
+}
+
+function genSSRNode (el, state) {
+ return el.type === 1
+ ? genSSRElement(el, state)
+ : genText(el)
+}
+
+function genChildrenAsStringNode (el, state) {
+ return el.children.length
+ ? `_ssrNode(${flattenSegments(childrenToSegments(el, state))})`
+ : ''
+}
+
+function genStringElement (el, state) {
+ return `_ssrNode(${elementToString(el, state)})`
+}
+
+function genStringElementWithChildren (el, state) {
+ const children = genSSRChildren(el, state, true)
+ return `_ssrNode(${
+ flattenSegments(elementToOpenTagSegments(el, state))
+ },"</${el.tag}>"${
+ children ? `,${children}` : ''
+ })`
+}
+
+function elementToString (el, state) {
+ return `(${flattenSegments(elementToSegments(el, state))})`
+}
+
+function elementToSegments (el, state): Array<StringSegment> {
+ // v-for / v-if
+ if (el.for && !el.forProcessed) {
+ el.forProcessed = true
+ return [{
+ type: EXPRESSION,
+ value: genFor(el, state, elementToString, '_ssrList')
+ }]
+ } else if (el.if && !el.ifProcessed) {
+ el.ifProcessed = true
+ return [{
+ type: EXPRESSION,
+ value: genIf(el, state, elementToString, '"<!---->"')
+ }]
+ } else if (el.tag === 'template') {
+ return childrenToSegments(el, state)
+ }
+
+ const openSegments = elementToOpenTagSegments(el, state)
+ const childrenSegments = childrenToSegments(el, state)
+ const { isUnaryTag } = state.options
+ const close = (isUnaryTag && isUnaryTag(el.tag))
+ ? []
+ : [{ type: RAW, value: `</${el.tag}>` }]
+ return openSegments.concat(childrenSegments, close)
+}
+
+function elementToOpenTagSegments (el, state): Array<StringSegment> {
+ applyModelTransform(el, state)
+ let binding
+ const segments = [{ type: RAW, value: `<${el.tag}` }]
+ // attrs
+ if (el.attrs) {
+ segments.push.apply(segments, genAttrSegments(el.attrs))
+ }
+ // domProps
+ if (el.props) {
+ segments.push.apply(segments, genDOMPropSegments(el.props, el.attrs))
+ }
+ // v-bind="object"
+ if ((binding = el.attrsMap['v-bind'])) {
+ segments.push({ type: EXPRESSION, value: `_ssrAttrs(${binding})` })
+ }
+ // v-bind.prop="object"
+ if ((binding = el.attrsMap['v-bind.prop'])) {
+ segments.push({ type: EXPRESSION, value: `_ssrDOMProps(${binding})` })
+ }
+ // class
+ if (el.staticClass || el.classBinding) {
+ segments.push.apply(
+ segments,
+ genClassSegments(el.staticClass, el.classBinding)
+ )
+ }
+ // style & v-show
+ if (el.staticStyle || el.styleBinding || el.attrsMap['v-show']) {
+ segments.push.apply(
+ segments,
+ genStyleSegments(
+ el.attrsMap.style,
+ el.staticStyle,
+ el.styleBinding,
+ el.attrsMap['v-show']
+ )
+ )
+ }
+ // _scopedId
+ if (state.options.scopeId) {
+ segments.push({ type: RAW, value: ` ${state.options.scopeId}` })
+ }
+ segments.push({ type: RAW, value: `>` })
+ return segments
+}
+
+function childrenToSegments (el, state): Array<StringSegment> {
+ let binding
+ if ((binding = el.attrsMap['v-html'])) {
+ return [{ type: EXPRESSION, value: `_s(${binding})` }]
+ }
+ if ((binding = el.attrsMap['v-text'])) {
+ return [{ type: INTERPOLATION, value: `_s(${binding})` }]
+ }
+ if (el.tag === 'textarea' && (binding = el.attrsMap['v-model'])) {
+ return [{ type: INTERPOLATION, value: `_s(${binding})` }]
+ }
+ return el.children
+ ? nodesToSegments(el.children, state)
+ : []
+}
+
+function nodesToSegments (
+ children: Array<ASTNode>,
+ state: CodegenState
+): Array<StringSegment> {
+ const segments = []
+ for (let i = 0; i < children.length; i++) {
+ const c = children[i]
+ if (c.type === 1) {
+ segments.push.apply(segments, elementToSegments(c, state))
+ } else if (c.type === 2) {
+ segments.push({ type: INTERPOLATION, value: c.expression })
+ } else if (c.type === 3) {
+ segments.push({ type: RAW, value: escape(c.text) })
+ }
+ }
+ return segments
+}
+
+function flattenSegments (segments: Array<StringSegment>): string {
+ const mergedSegments = []
+ let textBuffer = ''
+
+ const pushBuffer = () => {
+ if (textBuffer) {
+ mergedSegments.push(JSON.stringify(textBuffer))
+ textBuffer = ''
+ }
+ }
+
+ for (let i = 0; i < segments.length; i++) {
+ const s = segments[i]
+ if (s.type === RAW) {
+ textBuffer += s.value
+ } else if (s.type === INTERPOLATION) {
+ pushBuffer()
+ mergedSegments.push(`_ssrEscape(${s.value})`)
+ } else if (s.type === EXPRESSION) {
+ pushBuffer()
+ mergedSegments.push(`(${s.value})`)
+ }
+ }
+ pushBuffer()
+
+ return mergedSegments.join('+')
+}
diff --git a/node_modules/vue/src/server/optimizing-compiler/index.js b/node_modules/vue/src/server/optimizing-compiler/index.js
new file mode 100644
index 00000000..893630d6
--- /dev/null
+++ b/node_modules/vue/src/server/optimizing-compiler/index.js
@@ -0,0 +1,20 @@
+/* @flow */
+
+import { parse } from 'compiler/parser/index'
+import { generate } from './codegen'
+import { optimize } from './optimizer'
+import { createCompilerCreator } from 'compiler/create-compiler'
+
+export const createCompiler = createCompilerCreator(function baseCompile (
+ template: string,
+ options: CompilerOptions
+): CompiledResult {
+ const ast = parse(template.trim(), options)
+ optimize(ast, options)
+ const code = generate(ast, options)
+ return {
+ ast,
+ render: code.render,
+ staticRenderFns: code.staticRenderFns
+ }
+})
diff --git a/node_modules/vue/src/server/optimizing-compiler/modules.js b/node_modules/vue/src/server/optimizing-compiler/modules.js
new file mode 100644
index 00000000..69a7be87
--- /dev/null
+++ b/node_modules/vue/src/server/optimizing-compiler/modules.js
@@ -0,0 +1,126 @@
+/* @flow */
+
+import {
+ RAW,
+ // INTERPOLATION,
+ EXPRESSION
+} from './codegen'
+
+import {
+ propsToAttrMap,
+ isRenderableAttr
+} from 'web/server/util'
+
+import {
+ isBooleanAttr,
+ isEnumeratedAttr
+} from 'web/util/attrs'
+
+import type { StringSegment } from './codegen'
+import type { CodegenState } from 'compiler/codegen/index'
+
+type Attr = { name: string; value: string };
+
+const plainStringRE = /^"(?:[^"\\]|\\.)*"$|^'(?:[^'\\]|\\.)*'$/
+
+// let the model AST transform translate v-model into appropriate
+// props bindings
+export function applyModelTransform (el: ASTElement, state: CodegenState) {
+ if (el.directives) {
+ for (let i = 0; i < el.directives.length; i++) {
+ const dir = el.directives[i]
+ if (dir.name === 'model') {
+ state.directives.model(el, dir, state.warn)
+ // remove value for textarea as its converted to text
+ if (el.tag === 'textarea' && el.props) {
+ el.props = el.props.filter(p => p.name !== 'value')
+ }
+ break
+ }
+ }
+ }
+}
+
+export function genAttrSegments (
+ attrs: Array<Attr>
+): Array<StringSegment> {
+ return attrs.map(({ name, value }) => genAttrSegment(name, value))
+}
+
+export function genDOMPropSegments (
+ props: Array<Attr>,
+ attrs: ?Array<Attr>
+): Array<StringSegment> {
+ const segments = []
+ props.forEach(({ name, value }) => {
+ name = propsToAttrMap[name] || name.toLowerCase()
+ if (isRenderableAttr(name) &&
+ !(attrs && attrs.some(a => a.name === name))
+ ) {
+ segments.push(genAttrSegment(name, value))
+ }
+ })
+ return segments
+}
+
+function genAttrSegment (name: string, value: string): StringSegment {
+ if (plainStringRE.test(value)) {
+ // force double quote
+ value = value.replace(/^'|'$/g, '"')
+ // force enumerated attr to "true"
+ if (isEnumeratedAttr(name) && value !== `"false"`) {
+ value = `"true"`
+ }
+ return {
+ type: RAW,
+ value: isBooleanAttr(name)
+ ? ` ${name}="${name}"`
+ : value === '""'
+ ? ` ${name}`
+ : ` ${name}="${JSON.parse(value)}"`
+ }
+ } else {
+ return {
+ type: EXPRESSION,
+ value: `_ssrAttr(${JSON.stringify(name)},${value})`
+ }
+ }
+}
+
+export function genClassSegments (
+ staticClass: ?string,
+ classBinding: ?string
+): Array<StringSegment> {
+ if (staticClass && !classBinding) {
+ return [{ type: RAW, value: ` class=${staticClass}` }]
+ } else {
+ return [{
+ type: EXPRESSION,
+ value: `_ssrClass(${staticClass || 'null'},${classBinding || 'null'})`
+ }]
+ }
+}
+
+export function genStyleSegments (
+ staticStyle: ?string,
+ parsedStaticStyle: ?string,
+ styleBinding: ?string,
+ vShowExpression: ?string
+): Array<StringSegment> {
+ if (staticStyle && !styleBinding && !vShowExpression) {
+ return [{ type: RAW, value: ` style=${JSON.stringify(staticStyle)}` }]
+ } else {
+ return [{
+ type: EXPRESSION,
+ value: `_ssrStyle(${
+ parsedStaticStyle || 'null'
+ },${
+ styleBinding || 'null'
+ }, ${
+ vShowExpression
+ ? `{ display: (${vShowExpression}) ? '' : 'none' }`
+ : 'null'
+ })`
+ }]
+ }
+}
diff --git a/node_modules/vue/src/server/optimizing-compiler/optimizer.js b/node_modules/vue/src/server/optimizing-compiler/optimizer.js
new file mode 100644
index 00000000..d24acc55
--- /dev/null
+++ b/node_modules/vue/src/server/optimizing-compiler/optimizer.js
@@ -0,0 +1,140 @@
+/* @flow */
+
+/**
+ * In SSR, the vdom tree is generated only once and never patched, so
+ * we can optimize most element / trees into plain string render functions.
+ * The SSR optimizer walks the AST tree to detect optimizable elements and trees.
+ *
+ * The criteria for SSR optimizability is quite a bit looser than static tree
+ * detection (which is designed for client re-render). In SSR we bail only for
+ * components/slots/custom directives.
+ */
+
+import { no, makeMap, isBuiltInTag } from 'shared/util'
+
+// optimizability constants
+export const optimizability = {
+ FALSE: 0, // whole sub tree un-optimizable
+ FULL: 1, // whole sub tree optimizable
+ SELF: 2, // self optimizable but has some un-optimizable children
+ CHILDREN: 3, // self un-optimizable but have fully optimizable children
+ PARTIAL: 4 // self un-optimizable with some un-optimizable children
+}
+
+let isPlatformReservedTag
+
+export function optimize (root: ?ASTElement, options: CompilerOptions) {
+ if (!root) return
+ isPlatformReservedTag = options.isReservedTag || no
+ walk(root, true)
+}
+
+function walk (node: ASTNode, isRoot?: boolean) {
+ if (isUnOptimizableTree(node)) {
+ node.ssrOptimizability = optimizability.FALSE
+ return
+ }
+ // root node or nodes with custom directives should always be a VNode
+ const selfUnoptimizable = isRoot || hasCustomDirective(node)
+ const check = child => {
+ if (child.ssrOptimizability !== optimizability.FULL) {
+ node.ssrOptimizability = selfUnoptimizable
+ ? optimizability.PARTIAL
+ : optimizability.SELF
+ }
+ }
+ if (selfUnoptimizable) {
+ node.ssrOptimizability = optimizability.CHILDREN
+ }
+ if (node.type === 1) {
+ for (let i = 0, l = node.children.length; i < l; i++) {
+ const child = node.children[i]
+ walk(child)
+ check(child)
+ }
+ if (node.ifConditions) {
+ for (let i = 1, l = node.ifConditions.length; i < l; i++) {
+ const block = node.ifConditions[i].block
+ walk(block, isRoot)
+ check(block)
+ }
+ }
+ if (node.ssrOptimizability == null ||
+ (!isRoot && (node.attrsMap['v-html'] || node.attrsMap['v-text']))
+ ) {
+ node.ssrOptimizability = optimizability.FULL
+ } else {
+ node.children = optimizeSiblings(node)
+ }
+ } else {
+ node.ssrOptimizability = optimizability.FULL
+ }
+}
+
+function optimizeSiblings (el) {
+ const children = el.children
+ const optimizedChildren = []
+
+ let currentOptimizableGroup = []
+ const pushGroup = () => {
+ if (currentOptimizableGroup.length) {
+ optimizedChildren.push({
+ type: 1,
+ parent: el,
+ tag: 'template',
+ attrsList: [],
+ attrsMap: {},
+ children: currentOptimizableGroup,
+ ssrOptimizability: optimizability.FULL
+ })
+ }
+ currentOptimizableGroup = []
+ }
+
+ for (let i = 0; i < children.length; i++) {
+ const c = children[i]
+ if (c.ssrOptimizability === optimizability.FULL) {
+ currentOptimizableGroup.push(c)
+ } else {
+ // wrap fully-optimizable adjacent siblings inside a template tag
+ // so that they can be optimized into a single ssrNode by codegen
+ pushGroup()
+ optimizedChildren.push(c)
+ }
+ }
+ pushGroup()
+ return optimizedChildren
+}
+
+function isUnOptimizableTree (node: ASTNode): boolean {
+ if (node.type === 2 || node.type === 3) { // text or expression
+ return false
+ }
+ return (
+ isBuiltInTag(node.tag) || // built-in (slot, component)
+ !isPlatformReservedTag(node.tag) || // custom component
+ !!node.component || // "is" component
+ isSelectWithModel(node) // <select v-model> requires runtime inspection
+ )
+}
+
+const isBuiltInDir = makeMap('text,html,show,on,bind,model,pre,cloak,once')
+
+function hasCustomDirective (node: ASTNode): ?boolean {
+ return (
+ node.type === 1 &&
+ node.directives &&
+ node.directives.some(d => !isBuiltInDir(d.name))
+ )
+}
+
+// <select v-model> cannot be optimized because it requires a runtime check
+// to determine proper selected option
+function isSelectWithModel (node: ASTNode): boolean {
+ return (
+ node.type === 1 &&
+ node.tag === 'select' &&
+ node.directives != null &&
+ node.directives.some(d => d.name === 'model')
+ )
+}
diff --git a/node_modules/vue/src/server/optimizing-compiler/runtime-helpers.js b/node_modules/vue/src/server/optimizing-compiler/runtime-helpers.js
new file mode 100644
index 00000000..6f65de04
--- /dev/null
+++ b/node_modules/vue/src/server/optimizing-compiler/runtime-helpers.js
@@ -0,0 +1,150 @@
+/* @flow */
+
+import { escape, isSSRUnsafeAttr } from 'web/server/util'
+import { isObject, extend } from 'shared/util'
+import { renderAttr } from 'web/server/modules/attrs'
+import { renderClass } from 'web/util/class'
+import { genStyle } from 'web/server/modules/style'
+import { normalizeStyleBinding } from 'web/util/style'
+
+import {
+ normalizeChildren,
+ simpleNormalizeChildren
+} from 'core/vdom/helpers/normalize-children'
+
+import {
+ propsToAttrMap,
+ isRenderableAttr
+} from 'web/server/util'
+
+const ssrHelpers = {
+ _ssrEscape: escape,
+ _ssrNode: renderStringNode,
+ _ssrList: renderStringList,
+ _ssrAttr: renderAttr,
+ _ssrAttrs: renderAttrs,
+ _ssrDOMProps: renderDOMProps,
+ _ssrClass: renderSSRClass,
+ _ssrStyle: renderSSRStyle
+}
+
+export function installSSRHelpers (vm: Component) {
+ if (vm._ssrNode) {
+ return
+ }
+ let Vue = vm.constructor
+ while (Vue.super) {
+ Vue = Vue.super
+ }
+ extend(Vue.prototype, ssrHelpers)
+ if (Vue.FunctionalRenderContext) {
+ extend(Vue.FunctionalRenderContext.prototype, ssrHelpers)
+ }
+}
+
+class StringNode {
+ isString: boolean;
+ open: string;
+ close: ?string;
+ children: ?Array<any>;
+
+ constructor (
+ open: string,
+ close?: string,
+ children?: Array<any>,
+ normalizationType?: number
+ ) {
+ this.isString = true
+ this.open = open
+ this.close = close
+ if (children) {
+ this.children = normalizationType === 1
+ ? simpleNormalizeChildren(children)
+ : normalizationType === 2
+ ? normalizeChildren(children)
+ : children
+ } else {
+ this.children = void 0
+ }
+ }
+}
+
+function renderStringNode (
+ open: string,
+ close?: string,
+ children?: Array<any>,
+ normalizationType?: number
+): StringNode {
+ return new StringNode(open, close, children, normalizationType)
+}
+
+function renderStringList (
+ val: any,
+ render: (
+ val: any,
+ keyOrIndex: string | number,
+ index?: number
+ ) => string
+): string {
+ let ret = ''
+ let i, l, keys, key
+ if (Array.isArray(val) || typeof val === 'string') {
+ for (i = 0, l = val.length; i < l; i++) {
+ ret += render(val[i], i)
+ }
+ } else if (typeof val === 'number') {
+ for (i = 0; i < val; i++) {
+ ret += render(i + 1, i)
+ }
+ } else if (isObject(val)) {
+ keys = Object.keys(val)
+ for (i = 0, l = keys.length; i < l; i++) {
+ key = keys[i]
+ ret += render(val[key], key, i)
+ }
+ }
+ return ret
+}
+
+function renderAttrs (obj: Object): string {
+ let res = ''
+ for (const key in obj) {
+ if (isSSRUnsafeAttr(key)) {
+ continue
+ }
+ res += renderAttr(key, obj[key])
+ }
+ return res
+}
+
+function renderDOMProps (obj: Object): string {
+ let res = ''
+ for (const key in obj) {
+ const attr = propsToAttrMap[key] || key.toLowerCase()
+ if (isRenderableAttr(attr)) {
+ res += renderAttr(attr, obj[key])
+ }
+ }
+ return res
+}
+
+function renderSSRClass (
+ staticClass: ?string,
+ dynamic: any
+): string {
+ const res = renderClass(staticClass, dynamic)
+ return res === '' ? res : ` class="${escape(res)}"`
+}
+
+function renderSSRStyle (
+ staticStyle: ?Object,
+ dynamic: any,
+ extra: ?Object
+): string {
+ const style = {}
+ if (staticStyle) extend(style, staticStyle)
+ if (dynamic) extend(style, normalizeStyleBinding(dynamic))
+ if (extra) extend(style, extra)
+ const res = genStyle(style)
+ return res === '' ? res : ` style=${JSON.stringify(escape(res))}`
+}