mirror of
https://github.com/harttle/liquidjs.git
synced 2026-09-15 04:10:40 -07:00
perf: improve performance by 4x by simplified parseFile
- previously deprecated `getTemplate()` and `getTemplateSync()` not no longer supported - `opts` no longer support dynamic set in `parseFile()`, `renderFile()` arguments
This commit is contained in:
@@ -37,7 +37,7 @@ export default {
|
||||
ctx.setRegister('blockMode', BlockMode.OUTPUT)
|
||||
const scope = yield hash.render(ctx)
|
||||
if (withVar) scope[filepath] = evalToken(withVar, ctx)
|
||||
const templates = yield liquid._parseFile(filepath, ctx.opts, ctx.sync)
|
||||
const templates = yield liquid.parseFileImpl(filepath, ctx.sync)
|
||||
ctx.push(scope)
|
||||
yield renderer.renderTemplates(templates, ctx, emitter)
|
||||
ctx.pop()
|
||||
|
||||
@@ -26,7 +26,7 @@ export default {
|
||||
: evalToken(this.file, ctx))
|
||||
: file.getText()
|
||||
assert(filepath, () => `file "${file.getText()}"("${filepath}") not available`)
|
||||
const templates = yield liquid._parseFile(filepath, ctx.opts, ctx.sync)
|
||||
const templates = yield liquid.parseFileImpl(filepath, ctx.sync)
|
||||
|
||||
// render remaining contents and store rendered results
|
||||
ctx.setRegister('blockMode', BlockMode.STORE)
|
||||
|
||||
@@ -64,12 +64,12 @@ export default {
|
||||
scope['forloop'] = new ForloopDrop(collection.length)
|
||||
for (const item of collection) {
|
||||
scope[alias] = item
|
||||
const templates = yield liquid._parseFile(filepath, childCtx.opts, childCtx.sync)
|
||||
const templates = yield liquid.parseFileImpl(filepath, childCtx.sync)
|
||||
yield renderer.renderTemplates(templates, childCtx, emitter)
|
||||
scope.forloop.next()
|
||||
}
|
||||
} else {
|
||||
const templates = yield liquid._parseFile(filepath, childCtx.opts, childCtx.sync)
|
||||
const templates = yield liquid.parseFileImpl(filepath, childCtx.sync)
|
||||
yield renderer.renderTemplates(templates, childCtx, emitter)
|
||||
}
|
||||
}
|
||||
|
||||
+58
-56
@@ -24,6 +24,7 @@ export class Liquid {
|
||||
public parser: Parser
|
||||
public filters: FilterMap
|
||||
public tags: TagMap
|
||||
private parseFileImpl: (file: string, sync?: boolean) => Iterator<Template[]>
|
||||
|
||||
public constructor (opts: LiquidOptions = {}) {
|
||||
this.options = applyDefault(normalize(opts))
|
||||
@@ -31,6 +32,7 @@ export class Liquid {
|
||||
this.renderer = new Render()
|
||||
this.filters = new FilterMap(this.options.strictFilters, this)
|
||||
this.tags = new TagMap()
|
||||
this.parseFileImpl = this.options.cache ? this._parseFileCached : this._parseFile
|
||||
|
||||
forOwn(builtinTags, (conf: TagImplOptions, name: string) => this.registerTag(snakeCase(name), conf))
|
||||
forOwn(builtinFilters, (handler: FilterImplOptions, name: string) => this.registerFilter(snakeCase(name), handler))
|
||||
@@ -41,64 +43,61 @@ export class Liquid {
|
||||
return this.parser.parse(tokens)
|
||||
}
|
||||
|
||||
public _render (tpl: Template[], scope?: object, opts?: LiquidOptions, sync?: boolean): IterableIterator<any> {
|
||||
const options = { ...this.options, ...normalize(opts) }
|
||||
const ctx = new Context(scope, options, sync)
|
||||
const emitter = new Emitter(options.keepOutputType)
|
||||
public _render (tpl: Template[], scope?: object, sync?: boolean): IterableIterator<any> {
|
||||
const ctx = new Context(scope, this.options, sync)
|
||||
const emitter = new Emitter(this.options.keepOutputType)
|
||||
return this.renderer.renderTemplates(tpl, ctx, emitter)
|
||||
}
|
||||
public async render (tpl: Template[], scope?: object, opts?: LiquidOptions): Promise<any> {
|
||||
return toPromise(this._render(tpl, scope, opts, false))
|
||||
public async render (tpl: Template[], scope?: object): Promise<any> {
|
||||
return toPromise(this._render(tpl, scope, false))
|
||||
}
|
||||
public renderSync (tpl: Template[], scope?: object, opts?: LiquidOptions): any {
|
||||
return toValue(this._render(tpl, scope, opts, true))
|
||||
public renderSync (tpl: Template[], scope?: object): any {
|
||||
return toValue(this._render(tpl, scope, true))
|
||||
}
|
||||
|
||||
public _parseAndRender (html: string, scope?: object, opts?: LiquidOptions, sync?: boolean): IterableIterator<any> {
|
||||
public _parseAndRender (html: string, scope?: object, sync?: boolean): IterableIterator<any> {
|
||||
const tpl = this.parse(html)
|
||||
return this._render(tpl, scope, opts, sync)
|
||||
return this._render(tpl, scope, sync)
|
||||
}
|
||||
public async parseAndRender (html: string, scope?: object, opts?: LiquidOptions): Promise<any> {
|
||||
return toPromise(this._parseAndRender(html, scope, opts, false))
|
||||
public async parseAndRender (html: string, scope?: object): Promise<any> {
|
||||
return toPromise(this._parseAndRender(html, scope, false))
|
||||
}
|
||||
public parseAndRenderSync (html: string, scope?: object, opts?: LiquidOptions): any {
|
||||
return toValue(this._parseAndRender(html, scope, opts, true))
|
||||
public parseAndRenderSync (html: string, scope?: object): any {
|
||||
return toValue(this._parseAndRender(html, scope, true))
|
||||
}
|
||||
|
||||
public * _parseFile (file: string, opts?: LiquidOptions, sync?: boolean) {
|
||||
const options = { ...this.options, ...normalize(opts) }
|
||||
const paths = options.root.map(root => options.fs.resolve(root, file, options.extname))
|
||||
if (options.fs.fallback !== undefined) {
|
||||
const filepath = options.fs.fallback(file)
|
||||
if (filepath !== undefined) paths.push(filepath)
|
||||
}
|
||||
private * _parseFileCached (file: string, sync?: boolean) {
|
||||
const cache = this.options.cache!
|
||||
let tpls = yield cache.read(file)
|
||||
if (tpls) return tpls
|
||||
|
||||
for (const filepath of paths) {
|
||||
const { cache } = options
|
||||
if (cache) {
|
||||
const tpls = yield cache.read(filepath)
|
||||
if (tpls) return tpls
|
||||
}
|
||||
if (!(sync ? options.fs.existsSync(filepath) : yield options.fs.exists(filepath))) continue
|
||||
const tpl = this.parse(sync ? options.fs.readFileSync(filepath) : yield options.fs.readFile(filepath), filepath)
|
||||
if (cache) cache.write(filepath, tpl)
|
||||
tpls = yield this._parseFile(file, sync)
|
||||
cache.write(file, tpls)
|
||||
return tpls
|
||||
}
|
||||
private * _parseFile (file: string, sync?: boolean) {
|
||||
const { fs, root } = this.options
|
||||
|
||||
for (const filepath of this.lookupFiles(file, this.options)) {
|
||||
if (!(sync ? fs.existsSync(filepath) : yield fs.exists(filepath))) continue
|
||||
const tpl = this.parse(sync ? fs.readFileSync(filepath) : yield fs.readFile(filepath), filepath)
|
||||
return tpl
|
||||
}
|
||||
throw this.lookupError(file, options.root)
|
||||
throw this.lookupError(file, root)
|
||||
}
|
||||
public async parseFile (file: string, opts?: LiquidOptions): Promise<Template[]> {
|
||||
return toPromise(this._parseFile(file, opts, false))
|
||||
public async parseFile (file: string): Promise<Template[]> {
|
||||
return toPromise(this.parseFileImpl(file, false))
|
||||
}
|
||||
public parseFileSync (file: string, opts?: LiquidOptions): Template[] {
|
||||
return toValue(this._parseFile(file, opts, true))
|
||||
public parseFileSync (file: string): Template[] {
|
||||
return toValue(this.parseFileImpl(file, true))
|
||||
}
|
||||
public async renderFile (file: string, ctx?: object, opts?: LiquidOptions) {
|
||||
const templates = await this.parseFile(file, opts)
|
||||
return this.render(templates, ctx, opts)
|
||||
public async renderFile (file: string, ctx?: object) {
|
||||
const templates = await this.parseFile(file)
|
||||
return this.render(templates, ctx)
|
||||
}
|
||||
public renderFileSync (file: string, ctx?: object, opts?: LiquidOptions) {
|
||||
const templates = this.parseFileSync(file, opts)
|
||||
return this.renderSync(templates, ctx, opts)
|
||||
public renderFileSync (file: string, ctx?: object) {
|
||||
const templates = this.parseFileSync(file)
|
||||
return this.renderSync(templates, ctx)
|
||||
}
|
||||
|
||||
public _evalValue (str: string, ctx: Context): IterableIterator<any> {
|
||||
@@ -123,9 +122,25 @@ export class Liquid {
|
||||
}
|
||||
public express () {
|
||||
const self = this // eslint-disable-line
|
||||
let firstCall = true
|
||||
|
||||
return function (this: any, filePath: string, ctx: object, callback: (err: Error | null, rendered: string) => void) {
|
||||
const opts = { root: [...normalizeStringArray(this.root), ...self.options.root] }
|
||||
self.renderFile(filePath, ctx, opts).then(html => callback(null, html) as any, callback as any)
|
||||
if (firstCall) {
|
||||
firstCall = false
|
||||
self.options.root.unshift(...normalizeStringArray(this.root))
|
||||
}
|
||||
self.renderFile(filePath, ctx).then(html => callback(null, html) as any, callback as any)
|
||||
}
|
||||
}
|
||||
|
||||
private * lookupFiles (file: string, options: NormalizedFullOptions) {
|
||||
const { root, fs, extname } = options
|
||||
for (const dir of root) {
|
||||
yield fs.resolve(dir, file, extname)
|
||||
}
|
||||
if (fs.fallback !== undefined) {
|
||||
const filepath = fs.fallback(file)
|
||||
if (filepath !== undefined) yield filepath
|
||||
}
|
||||
}
|
||||
|
||||
@@ -135,17 +150,4 @@ export class Liquid {
|
||||
err.code = 'ENOENT'
|
||||
return err
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use parseFile instead
|
||||
*/
|
||||
public async getTemplate (file: string, opts?: LiquidOptions): Promise<Template[]> {
|
||||
return this.parseFile(file, opts)
|
||||
}
|
||||
/**
|
||||
* @deprecated use parseFileSync instead
|
||||
*/
|
||||
public getTemplateSync (file: string, opts?: LiquidOptions): Template[] {
|
||||
return this.parseFileSync(file, opts)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user