feat: support Jekyll-like include syntax, see #441

This commit is contained in:
Harttle
2022-01-19 23:07:25 +08:00
committed by Harttle
parent d71d0a0c20
commit 388d0fbbc4
7 changed files with 141 additions and 11 deletions
+2 -2
View File
@@ -20,7 +20,7 @@ export default {
} else tokenizer.p = begin
} else tokenizer.p = begin
this.hash = new Hash(tokenizer.remaining())
this.hash = new Hash(tokenizer.remaining(), this.liquid.options.jekyllInclude)
},
render: function * (ctx: Context, emitter: Emitter) {
const { liquid, hash, withVar } = this
@@ -34,7 +34,7 @@ export default {
const scope = yield hash.render(ctx)
if (withVar) scope[filepath] = evalToken(withVar, ctx)
const templates = yield liquid._parsePartialFile(filepath, ctx.sync, this['currentFile'])
ctx.push(scope)
ctx.push(ctx.opts.jekyllInclude ? { include: scope } : scope)
yield renderer.renderTemplates(templates, ctx, emitter)
ctx.pop()
ctx.restoreRegister(saved)
+5 -1
View File
@@ -17,6 +17,8 @@ export interface LiquidOptions {
layouts?: string | string[];
/** Allow refer to layouts/partials by relative pathname. To avoid arbitrary filesystem read, paths been referenced also need to be within corresponding root, partials, layouts. Defaults to `true`. */
relativeReference?: boolean;
/** Use jekyll style include, pass parameters to `include` variable of current scope. Defaults to `false`. */
jekyllInclude?: boolean;
/** Add a extname (if filepath doesn't include one) before template file lookup. Eg: setting to `".html"` will allow including file by basename. Defaults to `""`. */
extname?: string;
/** Whether or not to cache resolved templates. Defaults to `false`. */
@@ -93,6 +95,7 @@ export interface NormalizedFullOptions extends NormalizedOptions {
partials: string[];
layouts: string[];
relativeReference: boolean;
jekyllInclude: boolean;
extname: string;
cache: undefined | Cache<Thenable<Template[]>>;
jsTruthy: boolean;
@@ -122,6 +125,7 @@ export const defaultOptions: NormalizedFullOptions = {
layouts: ['.'],
partials: ['.'],
relativeReference: true,
jekyllInclude: false,
cache: undefined,
extname: '',
fs: fs,
@@ -161,7 +165,7 @@ export function normalize (options: LiquidOptions): NormalizedFullOptions {
else cache = options.cache ? new LRU(1024) : undefined
options.cache = cache
}
options = { ...defaultOptions, ...options }
options = { ...defaultOptions, ...(options.jekyllInclude ? { dynamicPartials: false } : {}), ...options }
if (!options.fs!.dirname && options.relativeReference) {
console.warn('[LiquidJS] `fs.dirname` is required for relativeReference, set relativeReference to `false` to suppress this warning, or provide implementation for `fs.dirname`')
options.relativeReference = false
+5 -4
View File
@@ -233,16 +233,16 @@ export class Tokenizer {
return new IdentifierToken(this.input, begin, this.p, this.file)
}
readHashes () {
readHashes (jekyllStyle?: boolean) {
const hashes = []
while (true) {
const hash = this.readHash()
const hash = this.readHash(jekyllStyle)
if (!hash) return hashes
hashes.push(hash)
}
}
readHash (): HashToken | undefined {
readHash (jekyllStyle?: boolean): HashToken | undefined {
this.skipBlank()
if (this.peek() === ',') ++this.p
const begin = this.p
@@ -251,7 +251,8 @@ export class Tokenizer {
let value
this.skipBlank()
if (this.peek() === ':') {
const sep = jekyllStyle ? '=' : ':'
if (this.peek() === sep) {
++this.p
value = this.readValue()
}
+2 -2
View File
@@ -16,9 +16,9 @@ export interface HashValue {
*/
export class Hash {
hash: HashValue = {}
constructor (markup: string) {
constructor (markup: string, jekyllStyle?: boolean) {
const tokenizer = new Tokenizer(markup, {})
for (const hash of tokenizer.readHashes()) {
for (const hash of tokenizer.readHashes(jekyllStyle)) {
this.hash[hash.name.content] = hash.value
}
}