Skip to content

Commit 9390f8a

Browse files
committed
refactor: render with filepath
1 parent 345597e commit 9390f8a

22 files changed

Lines changed: 122 additions & 148 deletions

README-zh-CN.md

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -25,20 +25,13 @@
2525

2626
```javascript
2727
const engine = new Engine()
28-
const { render } = await engine.compile('{{= name }} 苍苍,白露为霜')
29-
const html = await render({ name: '蒹葭' })
28+
const html = await engine.render('{{= name }} 苍苍,白露为霜', { name: '蒹葭' })
29+
// or
30+
// const html = await engine.renderFile('./template.html', { name: '蒹葭' })
3031

3132
document.body.innerHTML = html
3233
```
3334

34-
或,更简单的方式:
35-
36-
```javascript
37-
document.body.innerHTML = await template('{{= name }} 苍苍,白露为霜', {
38-
name: '蒹葭',
39-
})
40-
```
41-
4235
[使用文档](./documentation-zh-CN.md)
4336

4437
## 许可证

README.md

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -25,20 +25,13 @@
2525

2626
```javascript
2727
const engine = new Engine()
28-
const { render } = await engine.compile('Hello, {{= name }}!')
29-
const html = await render({ name: 'World' })
28+
const html = await engine.render('Hello, {{= name }}!', { name: 'World' })
29+
// or
30+
// const html = await engine.renderFile('./template.html', { name: 'World' })
3031

3132
document.body.innerHTML = html
3233
```
3334

34-
Or, in a simpler way:
35-
36-
```javascript
37-
document.body.innerHTML = await template('Hello, {{= name }}!', {
38-
name: 'World',
39-
})
40-
```
41-
4235
[Documentation](./documentation.md)
4336

4437
## License

benchmark/engines/jianjia.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,6 @@ export default {
66
ext: 'jianjia',
77
render: async function (templatePath, data) {
88
const template = fs.readFileSync(templatePath, 'utf-8');
9-
return (await new Engine().compile(template)).render(data);
9+
return new Engine().render(template, data);
1010
},
1111
};

benchmark/readme.md

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@
22

33
| Template \ Engine | ejs | eta | handlebars | jianjia | liquidjs |
44
|-------------------|:--------:|:--------:|:--------:|:--------:|:--------:|
5-
| **friends** | 🟡 0.81 | 🟢 0.14 | 🟡 0.66 | 🟡 0.53 |4.32 |
6-
| **if-expression** | 🟡 0.05 | 🟢 0.03 | - | 🟡 0.05 | ⚫ 0.16 |
7-
| **projects-escaped** | 🟡 0.06 | 🟢 0.04 | ⚫ 0.19 | 🟡 0.05 | 🔴 0.15 |
8-
| **projects-unescaped** | 🟡 0.06 | 🟢 0.05 | ⚫ 0.17 | 🟢 0.05 | 🔴 0.15 |
9-
| **search-results** | 🟡 0.14 | 🟢 0.05 | 🟠 0.29 | 🟡 0.10 | ⚫ 0.49 |
10-
| **simple-0** | 🟢 0.02 | 🟢 0.02 | 🟠 0.05 | 🟡 0.03 | ⚫ 0.07 |
11-
| **simple-1** | 🟡 0.05 | 🟢 0.03 | ⚫ 0.18 | 🟡 0.05 | 🔴 0.15 |
12-
| **simple-2** | 🟡 0.04 | 🟢 0.03 | ⚫ 0.18 | 🟡 0.05 | 🔴 0.13 |
5+
| **friends** | 🟡 1.04 | 🟢 0.18 | 🟡 0.80 | 🟡 0.66 |5.16 |
6+
| **if-expression** | 🟡 0.06 | 🟢 0.04 | - | 🟡 0.06 | ⚫ 0.20 |
7+
| **projects-escaped** | 🟡 0.07 | 🟢 0.05 | ⚫ 0.24 | 🟡 0.06 | 🔴 0.20 |
8+
| **projects-unescaped** | 🟡 0.07 | 🟢 0.04 | ⚫ 0.22 | 🟡 0.06 | 🔴 0.19 |
9+
| **search-results** | 🟡 0.19 | 🟢 0.06 | 🟠 0.38 | 🟡 0.13 | ⚫ 0.61 |
10+
| **simple-0** | 🟢 0.03 | 🟢 0.03 | 🔴 0.07 | 🟡 0.04 | ⚫ 0.09 |
11+
| **simple-1** | 🟡 0.07 | 🟢 0.04 | ⚫ 0.23 | 🟡 0.07 | 🔴 0.18 |
12+
| **simple-2** | 🟡 0.06 | 🟢 0.04 | ⚫ 0.22 | 🟡 0.06 | 🔴 0.16 |

packages/template/src/compile-error.test.ts

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,28 @@ it('compile error', () => {
1717
" JianJia if tag must have a value
1818
1919
{{ #if }}
20-
"
20+
21+
0:9"
22+
`)
23+
})
24+
25+
it('compile error w/ filepath', () => {
26+
const error = new CompileError('if tag must have a value', {
27+
name: 'if',
28+
value: '',
29+
raw: '{{ #if }}',
30+
previous: null,
31+
next: null,
32+
start: 0,
33+
end: 9,
34+
}, '/path/to/file.jianjia')
35+
expect(error.name).toBe('CompileError')
36+
expect(error.message).toBe('if tag must have a value')
37+
expect(error.details).toMatchInlineSnapshot(`
38+
" JianJia if tag must have a value
39+
40+
{{ #if }}
41+
42+
at /path/to/file.jianjia:0:9"
2143
`)
2244
})

packages/template/src/compile-error.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ export class CompileError extends Error {
55
constructor(
66
message: string,
77
private token: Token,
8+
private filepath?: string,
89
) {
910
super(message)
1011
this.name = 'CompileError'
@@ -15,6 +16,7 @@ export class CompileError extends Error {
1516
return `${c.bgRed(c.bold(' JianJia '))} ${c.red(this.message)}
1617
1718
${this.token.raw}
18-
`
19+
20+
${this.filepath ? `at ${this.filepath}:` : ''}${this.token.start}:${this.token.end}`
1921
}
2022
}

packages/template/src/compiler.test.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,8 @@ it('invalid', async () => {
7373
" JianJia Unexpected /if
7474
7575
{{ /if }}
76-
"
76+
77+
0:9"
7778
`,
7879
)
7980
})

packages/template/src/compiler.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { Validator } from './validator'
88
export class Compiler {
99
constructor(public options: Required<EngineOptions>) {}
1010

11-
async compile(token: Token | null) {
11+
async compile(token: Token | null, filepath?: string) {
1212
const ctx = new Context(this.options)
1313
const out = new OutScript(this.options)
1414
const sourcemap = new SourceMap(this.options)
@@ -42,7 +42,7 @@ export class Compiler {
4242
}
4343
catch (error: any) {
4444
if (this.options.debug) {
45-
throw new CompileError(error.message, token)
45+
throw new CompileError(error.message, token, filepath)
4646
}
4747

4848
return { value: '', script: (async () => 'invalid template') as unknown as Script, sourcemap }

packages/template/src/engine.test.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,8 @@ it('compile error', async () => {
209209
" JianJia Unexpected /if
210210
211211
{{ /if }}
212-
"
212+
213+
24:33"
213214
`)
214215
})
215216

packages/template/src/engine.ts

Lines changed: 28 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import type { SourceMap } from './source-map'
2-
import type { EngineOptions, Filters, Globals, Render, Script, Tag } from './types'
2+
import type { EngineOptions, Filters, Globals, Script, Tag } from './types'
33
import { Compiler } from './compiler'
44
import { escape } from './escape'
55
import * as filters from './filters'
@@ -25,7 +25,11 @@ export const defaultOptions: Required<EngineOptions> = {
2525
cache: false,
2626
}
2727

28-
const cache = new Map<string, Render>()
28+
const cache = new Map<string, {
29+
template: string
30+
script: Script
31+
sourcemap: SourceMap
32+
}>()
2933

3034
export class Engine {
3135
protected options: Required<EngineOptions>
@@ -49,46 +53,43 @@ export class Engine {
4953
Object.assign(this.options.tags, tags)
5054
}
5155

52-
async load(filepath: string) {
56+
async render(
57+
template: string,
58+
globals: Globals,
59+
) {
60+
const { script, sourcemap } = await this._compile(template)
61+
return this._render(template, globals, script, sourcemap)
62+
}
63+
64+
async renderFile(filepath: string, globals: Globals) {
5365
if (this.options.cache && cache.has(filepath)) {
54-
return cache.get(filepath)
66+
const { template, script, sourcemap } = cache.get(filepath)!
67+
return this._render(template, globals, script, sourcemap)
5568
}
5669

5770
const template = await this.options.loader!(filepath)
58-
59-
return {
60-
template,
61-
compile: async () => this.compile(template, filepath),
71+
const { script, sourcemap } = await this._compile(template)
72+
if (this.options.cache) {
73+
cache.set(filepath, { template, script, sourcemap })
6274
}
75+
return this._render(template, globals, script, sourcemap)
6376
}
6477

65-
async compile(template: string, filepath?: string) {
66-
const { value, script, sourcemap } = await new Compiler(this.options).compile(
78+
private async _compile(template: string, filepath?: string) {
79+
return new Compiler(this.options).compile(
6780
await new Tokenizer(this.options).parse(template),
81+
filepath,
6882
)
69-
70-
const render = async (globals: Globals) => this.render(globals, script, template, sourcemap)
71-
72-
if (this.options.cache && filepath) {
73-
cache.set(filepath, render)
74-
}
75-
76-
return {
77-
value,
78-
script,
79-
sourcemap,
80-
render,
81-
}
8283
}
8384

84-
async render(
85-
globals: Globals,
86-
func: Script,
85+
private async _render(
8786
template: string,
87+
globals: Globals,
88+
script: Script,
8889
sourcemap: SourceMap,
8990
) {
9091
try {
91-
return await func(
92+
return await script(
9293
{ ...this.options.globals, ...globals },
9394
{ ...this.options.filters, ...filters },
9495
(v: unknown) => {

0 commit comments

Comments
 (0)