nav tabs on admin dashboard

This commit is contained in:
2019-03-07 00:20:34 -06:00
parent f73d6ae228
commit e4f473f376
11661 changed files with 216240 additions and 1544253 deletions
+39 -7
View File
@@ -23,12 +23,13 @@ export type RenderOptions = {
directives?: Object;
isUnaryTag?: Function;
cache?: RenderCache;
template?: string;
template?: string | (content: string, context: any) => string;
inject?: boolean;
basedir?: string;
shouldPreload?: Function;
shouldPrefetch?: Function;
clientManifest?: ClientManifest;
serializer?: Function;
runInNewContext?: boolean | 'once';
};
@@ -41,7 +42,8 @@ export function createRenderer ({
cache,
shouldPreload,
shouldPrefetch,
clientManifest
clientManifest,
serializer
}: RenderOptions = {}): Renderer {
const render = createRenderFunction(modules, directives, isUnaryTag, cache)
const templateRenderer = new TemplateRenderer({
@@ -49,7 +51,8 @@ export function createRenderer ({
inject,
shouldPreload,
shouldPrefetch,
clientManifest
clientManifest,
serializer
})
return {
@@ -79,11 +82,26 @@ export function createRenderer ({
}, cb)
try {
render(component, write, context, err => {
if (template) {
result = templateRenderer.renderSync(result, context)
}
if (err) {
cb(err)
return cb(err)
}
if (context && context.rendered) {
context.rendered(context)
}
if (template) {
try {
const res = templateRenderer.render(result, context)
if (typeof res !== 'string') {
// function template returning promise
res
.then(html => cb(null, html))
.catch(cb)
} else {
cb(null, res)
}
} catch (e) {
cb(e)
}
} else {
cb(null, result)
}
@@ -106,13 +124,27 @@ export function createRenderer ({
render(component, write, context, done)
})
if (!template) {
if (context && context.rendered) {
const rendered = context.rendered
renderStream.once('beforeEnd', () => {
rendered(context)
})
}
return renderStream
} else if (typeof template === 'function') {
throw new Error(`function template is only supported in renderToString.`)
} else {
const templateStream = templateRenderer.createStream(context)
renderStream.on('error', err => {
templateStream.emit('error', err)
})
renderStream.pipe(templateStream)
if (context && context.rendered) {
const rendered = context.rendered
renderStream.once('beforeEnd', () => {
rendered(context)
})
}
return templateStream
}
}
+5 -1
View File
@@ -225,7 +225,11 @@ function nodesToSegments (
} 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) })
let text = escape(c.text)
if (c.isComment) {
text = '<!--' + text + '-->'
}
segments.push({ type: RAW, value: text })
}
}
return segments
+4 -6
View File
@@ -19,8 +19,6 @@ import {
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
@@ -42,14 +40,14 @@ export function applyModelTransform (el: ASTElement, state: CodegenState) {
}
export function genAttrSegments (
attrs: Array<Attr>
attrs: Array<ASTAttr>
): Array<StringSegment> {
return attrs.map(({ name, value }) => genAttrSegment(name, value))
}
export function genDOMPropSegments (
props: Array<Attr>,
attrs: ?Array<Attr>
props: Array<ASTAttr>,
attrs: ?Array<ASTAttr>
): Array<StringSegment> {
const segments = []
props.forEach(({ name, value }) => {
@@ -92,7 +90,7 @@ export function genClassSegments (
classBinding: ?string
): Array<StringSegment> {
if (staticClass && !classBinding) {
return [{ type: RAW, value: ` class=${staticClass}` }]
return [{ type: RAW, value: ` class="${JSON.parse(staticClass)}"` }]
} else {
return [{
type: EXPRESSION,
+1
View File
@@ -84,6 +84,7 @@ function optimizeSiblings (el) {
tag: 'template',
attrsList: [],
attrsMap: {},
rawAttrsMap: {},
children: currentOptimizableGroup,
ssrOptimizability: optimizability.FULL
})
+46 -46
View File
@@ -66,54 +66,54 @@ export class RenderContext {
}
next () {
const lastState = this.renderStates[this.renderStates.length - 1]
if (isUndef(lastState)) {
return this.done()
}
switch (lastState.type) {
case 'Element':
case 'Fragment':
const { children, total } = lastState
const rendered = lastState.rendered++
if (rendered < total) {
this.renderNode(children[rendered], false, this)
} else {
this.renderStates.pop()
if (lastState.type === 'Element') {
this.write(lastState.endTag, this.next)
// eslint-disable-next-line
while (true) {
const lastState = this.renderStates[this.renderStates.length - 1]
if (isUndef(lastState)) {
return this.done()
}
/* eslint-disable no-case-declarations */
switch (lastState.type) {
case 'Element':
case 'Fragment':
const { children, total } = lastState
const rendered = lastState.rendered++
if (rendered < total) {
return this.renderNode(children[rendered], false, this)
} else {
this.next()
this.renderStates.pop()
if (lastState.type === 'Element') {
return this.write(lastState.endTag, this.next)
}
}
}
break
case 'Component':
this.renderStates.pop()
this.activeInstance = lastState.prevActive
this.next()
break
case 'ComponentWithCache':
this.renderStates.pop()
const { buffer, bufferIndex, componentBuffer, key } = lastState
const result = {
html: buffer[bufferIndex],
components: componentBuffer[bufferIndex]
}
this.cache.set(key, result)
if (bufferIndex === 0) {
// this is a top-level cached component,
// exit caching mode.
this.write.caching = false
} else {
// parent component is also being cached,
// merge self into parent's result
buffer[bufferIndex - 1] += result.html
const prev = componentBuffer[bufferIndex - 1]
result.components.forEach(c => prev.add(c))
}
buffer.length = bufferIndex
componentBuffer.length = bufferIndex
this.next()
break
break
case 'Component':
this.renderStates.pop()
this.activeInstance = lastState.prevActive
break
case 'ComponentWithCache':
this.renderStates.pop()
const { buffer, bufferIndex, componentBuffer, key } = lastState
const result = {
html: buffer[bufferIndex],
components: componentBuffer[bufferIndex]
}
this.cache.set(key, result)
if (bufferIndex === 0) {
// this is a top-level cached component,
// exit caching mode.
this.write.caching = false
} else {
// parent component is also being cached,
// merge self into parent's result
buffer[bufferIndex - 1] += result.html
const prev = componentBuffer[bufferIndex - 1]
result.components.forEach(c => prev.add(c))
}
buffer.length = bufferIndex
componentBuffer.length = bufferIndex
break
}
}
}
}
+1
View File
@@ -42,6 +42,7 @@ export default class RenderStream extends stream.Readable {
})
this.end = () => {
this.emit('beforeEnd')
// the rendering is finished; we should push out the last of the buffer.
this.done = true
this.push(this.buffer)
+55 -14
View File
@@ -3,6 +3,7 @@
import { escape } from 'web/server/util'
import { SSR_ATTR } from 'shared/constants'
import { RenderContext } from './render-context'
import { resolveAsset } from 'core/util/options'
import { generateComponentTrace } from 'core/util/debug'
import { ssrCompileToFunctions } from 'web/server/compiler'
import { installSSRHelpers } from './optimizing-compiler/runtime-helpers'
@@ -18,6 +19,7 @@ let warned = Object.create(null)
const warnOnce = msg => {
if (!warned[msg]) {
warned[msg] = true
// eslint-disable-next-line no-console
console.warn(`\n\u001b[31m${msg}\u001b[39m\n`)
}
}
@@ -48,6 +50,27 @@ const normalizeRender = vm => {
}
}
function waitForServerPrefetch (vm, resolve, reject) {
let handlers = vm.$options.serverPrefetch
if (isDef(handlers)) {
if (!Array.isArray(handlers)) handlers = [handlers]
try {
const promises = []
for (let i = 0, j = handlers.length; i < j; i++) {
const result = handlers[i].call(vm, vm)
if (result && typeof result.then === 'function') {
promises.push(result)
}
}
Promise.all(promises).then(resolve).catch(reject)
return
} catch (e) {
reject(e)
}
}
resolve()
}
function renderNode (node, isRoot, context) {
if (node.isString) {
renderStringNode(node, context)
@@ -91,7 +114,12 @@ function renderComponent (node, isRoot, context) {
const registerComponent = registerComponentForCache(Ctor.options, write)
if (isDef(getKey) && isDef(cache) && isDef(name)) {
const key = name + '::' + getKey(node.componentOptions.propsData)
const rawKey = getKey(node.componentOptions.propsData)
if (rawKey === false) {
renderComponentInner(node, isRoot, context)
return
}
const key = name + '::' + rawKey
const { has, get } = context
if (isDef(has)) {
has(key, hit => {
@@ -165,13 +193,20 @@ function renderComponentInner (node, isRoot, context) {
context.activeInstance
)
normalizeRender(child)
const childNode = child._render()
childNode.parent = node
context.renderStates.push({
type: 'Component',
prevActive
})
renderNode(childNode, isRoot, context)
const resolve = () => {
const childNode = child._render()
childNode.parent = node
context.renderStates.push({
type: 'Component',
prevActive
})
renderNode(childNode, isRoot, context)
}
const reject = context.done
waitForServerPrefetch(child, resolve, reject)
}
function renderAsyncComponent (node, isRoot, context) {
@@ -324,11 +359,13 @@ function renderStartingTag (node: VNode, context) {
if (dirs) {
for (let i = 0; i < dirs.length; i++) {
const name = dirs[i].name
const dirRenderer = directives[name]
if (dirRenderer && name !== 'show') {
// directives mutate the node's data
// which then gets rendered by modules
dirRenderer(node, dirs[i])
if (name !== 'show') {
const dirRenderer = resolveAsset(context, 'directives', name)
if (dirRenderer) {
// directives mutate the node's data
// which then gets rendered by modules
dirRenderer(node, dirs[i])
}
}
}
}
@@ -391,6 +428,10 @@ export function createRenderFunction (
})
installSSRHelpers(component)
normalizeRender(component)
renderNode(component._render(), true, context)
const resolve = () => {
renderNode(component._render(), true, context)
}
waitForServerPrefetch(component, resolve, done)
}
}
+40 -20
View File
@@ -11,11 +11,12 @@ import type { ParsedTemplate } from './parse-template'
import type { AsyncFileMapper } from './create-async-file-mapper'
type TemplateRendererOptions = {
template: ?string;
template?: string | (content: string, context: any) => string;
inject?: boolean;
clientManifest?: ClientManifest;
shouldPreload?: (file: string, type: string) => boolean;
shouldPrefetch?: (file: string, type: string) => boolean;
serializer?: Function;
};
export type ClientManifest = {
@@ -41,26 +42,39 @@ type Resource = {
export default class TemplateRenderer {
options: TemplateRendererOptions;
inject: boolean;
parsedTemplate: ParsedTemplate | null;
parsedTemplate: ParsedTemplate | Function | null;
publicPath: string;
clientManifest: ClientManifest;
preloadFiles: Array<Resource>;
prefetchFiles: Array<Resource>;
mapFiles: AsyncFileMapper;
serialize: Function;
constructor (options: TemplateRendererOptions) {
this.options = options
this.inject = options.inject !== false
// if no template option is provided, the renderer is created
// as a utility object for rendering assets like preload links and scripts.
this.parsedTemplate = options.template
? parseTemplate(options.template)
const { template } = options
this.parsedTemplate = template
? typeof template === 'string'
? parseTemplate(template)
: template
: null
// function used to serialize initial state JSON
this.serialize = options.serializer || (state => {
return serialize(state, { isJSON: true })
})
// extra functionality with client manifest
if (options.clientManifest) {
const clientManifest = this.clientManifest = options.clientManifest
this.publicPath = clientManifest.publicPath.replace(/\/$/, '')
// ensure publicPath ends with /
this.publicPath = clientManifest.publicPath === ''
? ''
: clientManifest.publicPath.replace(/([^\/])$/, '$1/')
// preload/prefetch directives
this.preloadFiles = (clientManifest.initial || []).map(normalizeFile)
this.prefetchFiles = (clientManifest.async || []).map(normalizeFile)
@@ -79,12 +93,17 @@ export default class TemplateRenderer {
}
// render synchronously given rendered app content and render context
renderSync (content: string, context: ?Object) {
render (content: string, context: ?Object): string | Promise<string> {
const template = this.parsedTemplate
if (!template) {
throw new Error('renderSync cannot be called without a template.')
throw new Error('render cannot be called without a template.')
}
context = context || {}
if (typeof template === 'function') {
return template(content, context)
}
if (this.inject) {
return (
template.head(context) +
@@ -108,13 +127,13 @@ export default class TemplateRenderer {
}
renderStyles (context: Object): string {
const cssFiles = this.clientManifest
? this.clientManifest.all.filter(isCSS)
: []
const initial = this.preloadFiles || []
const async = this.getUsedAsyncFiles(context) || []
const cssFiles = initial.concat(async).filter(({ file }) => isCSS(file))
return (
// render links for css files
(cssFiles.length
? cssFiles.map(file => `<link rel="stylesheet" href="${this.publicPath}/${file}">`).join('')
? cssFiles.map(({ file }) => `<link rel="stylesheet" href="${this.publicPath}${file}">`).join('')
: '') +
// context.styles is a getter exposed by vue-style-loader which contains
// the inline component styles collected during SSR
@@ -153,7 +172,7 @@ export default class TemplateRenderer {
extra = ` type="font/${extension}" crossorigin`
}
return `<link rel="preload" href="${
this.publicPath}/${file
this.publicPath}${file
}"${
asType !== '' ? ` as="${asType}"` : ''
}${
@@ -179,7 +198,7 @@ export default class TemplateRenderer {
if (alreadyRendered(file)) {
return ''
}
return `<link rel="prefetch" href="${this.publicPath}/${file}">`
return `<link rel="prefetch" href="${this.publicPath}${file}">`
}).join('')
} else {
return ''
@@ -191,22 +210,23 @@ export default class TemplateRenderer {
contextKey = 'state',
windowKey = '__INITIAL_STATE__'
} = options || {}
const state = serialize(context[contextKey], { isJSON: true })
const state = this.serialize(context[contextKey])
const autoRemove = process.env.NODE_ENV === 'production'
? ';(function(){var s;(s=document.currentScript||document.scripts[document.scripts.length-1]).parentNode.removeChild(s);}());'
: ''
const nonceAttr = context.nonce ? ` nonce="${context.nonce}"` : ''
return context[contextKey]
? `<script>window.${windowKey}=${state}${autoRemove}</script>`
? `<script${nonceAttr}>window.${windowKey}=${state}${autoRemove}</script>`
: ''
}
renderScripts (context: Object): string {
if (this.clientManifest) {
const initial = this.preloadFiles
const async = this.getUsedAsyncFiles(context)
const needed = [initial[0]].concat(async || [], initial.slice(1))
return needed.filter(({ file }) => isJS(file)).map(({ file }) => {
return `<script src="${this.publicPath}/${file}" defer></script>`
const initial = this.preloadFiles.filter(({ file }) => isJS(file))
const async = (this.getUsedAsyncFiles(context) || []).filter(({ file }) => isJS(file))
const needed = [initial[0]].concat(async, initial.slice(1))
return needed.map(({ file }) => {
return `<script src="${this.publicPath}${file}" defer></script>`
}).join('')
} else {
return ''
+6 -11
View File
@@ -1,6 +1,6 @@
const hash = require('hash-sum')
const uniq = require('lodash.uniq')
import { isJS } from './util'
import { isJS, isCSS, onEmit } from './util'
export default class VueSSRClientPlugin {
constructor (options = {}) {
@@ -10,7 +10,7 @@ export default class VueSSRClientPlugin {
}
apply (compiler) {
compiler.plugin('emit', (compilation, cb) => {
onEmit(compiler, 'vue-client-plugin', (compilation, cb) => {
const stats = compilation.getStats().toJson()
const allFiles = uniq(stats.assets
@@ -19,10 +19,10 @@ export default class VueSSRClientPlugin {
const initialFiles = uniq(Object.keys(stats.entrypoints)
.map(name => stats.entrypoints[name].assets)
.reduce((assets, all) => all.concat(assets), [])
.filter(isJS))
.filter((file) => isJS(file) || isCSS(file)))
const asyncFiles = allFiles
.filter(isJS)
.filter((file) => isJS(file) || isCSS(file))
.filter(file => initialFiles.indexOf(file) < 0)
const manifest = {
@@ -43,7 +43,8 @@ export default class VueSSRClientPlugin {
if (!chunk || !chunk.files) {
return
}
const files = manifest.modules[hash(m.identifier)] = chunk.files.map(fileToIndex)
const id = m.identifier.replace(/\s\w+$/, '') // remove appended hash
const files = manifest.modules[hash(id)] = chunk.files.map(fileToIndex)
// find all asset modules associated with the same chunk
assetModules.forEach(m => {
if (m.chunks.some(id => id === cid)) {
@@ -53,12 +54,6 @@ export default class VueSSRClientPlugin {
}
})
// const debug = (file, obj) => {
// require('fs').writeFileSync(__dirname + '/' + file, JSON.stringify(obj, null, 2))
// }
// debug('stats.json', stats)
// debug('client-manifest.json', manifest)
const json = JSON.stringify(manifest, null, 2)
compilation.assets[this.options.filename] = {
source: () => json,
+3 -3
View File
@@ -1,4 +1,4 @@
import { validate, isJS } from './util'
import { validate, isJS, onEmit } from './util'
export default class VueSSRServerPlugin {
constructor (options = {}) {
@@ -10,7 +10,7 @@ export default class VueSSRServerPlugin {
apply (compiler) {
validate(compiler)
compiler.plugin('emit', (compilation, cb) => {
onEmit(compiler, 'vue-server-plugin', (compilation, cb) => {
const stats = compilation.getStats().toJson()
const entryName = Object.keys(stats.entrypoints)[0]
const entryInfo = stats.entrypoints[entryName]
@@ -43,7 +43,7 @@ export default class VueSSRServerPlugin {
}
stats.assets.forEach(asset => {
if (asset.name.match(/\.js$/)) {
if (isJS(asset.name)) {
bundle.files[asset.name] = compilation.assets[asset.name].source()
} else if (asset.name.match(/\.js\.map$/)) {
bundle.maps[asset.name.replace(/\.map$/, '')] = JSON.parse(compilation.assets[asset.name].source())
+10
View File
@@ -21,4 +21,14 @@ export const validate = compiler => {
}
}
export const onEmit = (compiler, name, hook) => {
if (compiler.hooks) {
// Webpack >= 4.0.0
compiler.hooks.emit.tapAsync(name, hook)
} else {
// Webpack < 4.0.0
compiler.plugin('emit', hook)
}
}
export { isJS, isCSS } from '../util'
+1 -1
View File
@@ -1,6 +1,6 @@
/* @flow */
const MAX_STACK_DEPTH = 1000
const MAX_STACK_DEPTH = 800
const noop = _ => _
const defer = typeof process !== 'undefined' && process.nextTick