Skip to content
Permalink
Browse files

fix: port bundle renderer features to the basic renderer

  • Loading branch information...
l5x committed Oct 20, 2019
1 parent d7d8ff0 commit 2a461f7ccd12fe0ee301a8123c6731e5e7ade3fa
@@ -1,13 +1,21 @@
/* @flow */

import { extend } from 'shared/util'
import modules from './server/modules/index'
import directives from './server/directives/index'
import baseDirectives from './server/directives/index'
import { isUnaryTag, canBeLeftOpenTag } from './compiler/util'
import { createBasicRenderer } from 'server/create-basic-renderer'

export default createBasicRenderer({
modules,
directives,
isUnaryTag,
canBeLeftOpenTag
})
import { createBasicRenderer as _createRenderer } from 'server/create-basic-renderer'

export function createRenderer (options?: Object = {}): {
renderToString: Function,
} {
return _createRenderer(extend(extend({}, options), {
isUnaryTag,
canBeLeftOpenTag,
modules,
// user can provide server-side implementations for custom directives
// when creating the renderer.
directives: extend(baseDirectives, options.directives)
}))
}
@@ -2,36 +2,114 @@

import { createWriteFunction } from './write'
import { createRenderFunction } from './render'
import type { RenderOptions } from './create-renderer'
import { createPromiseCallback } from './util'
import TemplateRenderer from './template-renderer/index'
import type { ClientManifest } from './template-renderer/index'

export type Renderer = {
renderToString: (component: Component, context: any, cb: any) => ?Promise<string>;
};

type RenderCache = {
get: (key: string, cb?: Function) => string | void;
set: (key: string, val: string) => void;
has?: (key: string, cb?: Function) => boolean | void;
};

export type RenderOptions = {
modules?: Array<(vnode: VNode) => ?string>;
directives?: Object;
isUnaryTag?: Function;
cache?: RenderCache;
template?: string | (content: string, context: any) => string;
inject?: boolean;
basedir?: string;
shouldPreload?: Function;
shouldPrefetch?: Function;
clientManifest?: ClientManifest;
serializer?: Function;
};

export function createBasicRenderer ({
modules = [],
directives = {},
isUnaryTag = (() => false),
cache
}: RenderOptions = {}) {
template,
inject,
cache,
shouldPreload,
shouldPrefetch,
clientManifest,
serializer
}: RenderOptions = {}): Renderer {
const render = createRenderFunction(modules, directives, isUnaryTag, cache)

return function renderToString (
component: Component,
context: any,
done: any
): void {
if (typeof context === 'function') {
done = context
context = {}
}
let result = ''
const write = createWriteFunction(text => {
result += text
return false
}, done)
try {
render(component, write, context, () => {
done(null, result)
})
} catch (e) {
done(e)
const templateRenderer = new TemplateRenderer({
template,
inject,
shouldPreload,
shouldPrefetch,
clientManifest,
serializer
})

return {
renderToString (
component: Component,
context: any,
cb: any
): ?Promise<string> {
if (typeof context === 'function') {
cb = context
context = {}
}

if (context) {
templateRenderer.bindRenderFns(context)
}

// no callback, return Promise
let promise
if (!cb) {
({ promise, cb } = createPromiseCallback())
}

let result = ''
const write = createWriteFunction(text => {
result += text
return false
}, cb)
try {
render(component, write, context, err => {
if (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)
}
})
} catch (e) {
cb(e)
}

return promise
}
}
}
@@ -1,10 +1,10 @@
/* @flow */

const path = require('path')
// const path = require('path')
const serialize = require('serialize-javascript')

import { isJS, isCSS } from '../util'
import TemplateStream from './template-stream'
import { isJS, isCSS, extname } from '../util'
// import TemplateStream from './template-stream'
import { parseTemplate } from './parse-template'
import { createMapper } from './create-async-file-mapper'
import type { ParsedTemplate } from './parse-template'
@@ -55,7 +55,7 @@ export default class TemplateRenderer {
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.

const { template } = options
this.parsedTemplate = template
? typeof template === 'string'
@@ -241,18 +241,19 @@ export default class TemplateRenderer {
return context._mappedFiles
}

// create a transform stream
createStream (context: ?Object): TemplateStream {
if (!this.parsedTemplate) {
throw new Error('createStream cannot be called without a template.')
}
return new TemplateStream(this, this.parsedTemplate, context || {})
}
// // create a transform stream
// createStream (context: ?Object): TemplateStream {
// if (!this.parsedTemplate) {
// throw new Error('createStream cannot be called without a template.')
// }
// return new TemplateStream(this, this.parsedTemplate, context || {})
// }
}

function normalizeFile (file: string): Resource {
const withoutQuery = file.replace(/\?.*/, '')
const extension = path.extname(withoutQuery).slice(1)
const extension = extname(withoutQuery).slice(1)

return {
file,
extension,
@@ -16,3 +16,52 @@ export function createPromiseCallback () {
}
return { promise, cb }
}

export function extname(path) {
var startDot = -1;
var startPart = 0;
var end = -1;
var matchedSlash = true;
// Track the state of characters (if any) we see before our first dot and
// after any path separator we find
var preDotState = 0;
for (var i = path.length - 1; i >= 0; --i) {
var code = path.charCodeAt(i);
if (code === 47 /*/*/) {
// If we reached a path separator that was not part of a set of path
// separators at the end of the string, stop now
if (!matchedSlash) {
startPart = i + 1;
break;
}
continue;
}
if (end === -1) {
// We saw the first non-path separator, mark this as the end of our
// extension
matchedSlash = false;
end = i + 1;
}
if (code === 46 /*.*/) {
// If this is our first dot, mark it as the start of our extension
if (startDot === -1) startDot = i;
else if (preDotState !== 1) preDotState = 1;
} else if (startDot !== -1) {
// We saw a non-dot and non-path separator before our dot, so we should
// have a good chance at having a non-empty extension
preDotState = -1;
}
}

if (
startDot === -1 ||
end === -1 ||
// We saw a non-dot character immediately before the dot
preDotState === 0 ||
// The (right-most) trimmed path component is exactly '..'
(preDotState === 1 && startDot === end - 1 && startDot === startPart + 1)
) {
return "";
}
return path.slice(startDot, end);
}

0 comments on commit 2a461f7

Please sign in to comment.
You can’t perform that action at this time.