close enough for a parse fix
This commit is contained in:
parent
10003a1167
commit
435103a304
2 changed files with 169 additions and 42 deletions
|
|
@ -1,17 +1,3 @@
|
|||
|
||||
const example =
|
||||
`Dear {{.Name}},
|
||||
{{if .Attended}}
|
||||
It was a pleasure to see you at the wedding.
|
||||
{{- else}}
|
||||
It is a shame you couldn't make it to the wedding.
|
||||
{{- end}}
|
||||
{{with .Gift -}}
|
||||
Thank you for the lovely {{.}}.
|
||||
{{end}}
|
||||
Best wishes,
|
||||
Josie`;
|
||||
|
||||
type Node = {
|
||||
type: 'Text' | 'Data' | 'Conditional' | 'Marker' | 'Container' | 'Empty'
|
||||
|
||||
|
|
@ -39,13 +25,11 @@ type Node = {
|
|||
parent?: Node;
|
||||
};
|
||||
|
||||
function parse(text: string): Node {
|
||||
let root: Node = {
|
||||
type: 'Container',
|
||||
placeholders: []
|
||||
};
|
||||
return _parse(text, root);
|
||||
}
|
||||
type Template = {
|
||||
name: string;
|
||||
root: Node;
|
||||
};
|
||||
|
||||
|
||||
function _parse(text: string, root: Node): Node {
|
||||
if (!text || text.length < 1) {
|
||||
|
|
@ -130,7 +114,7 @@ function _parse(text: string, root: Node): Node {
|
|||
|
||||
if (textNode.startIndex! < textNode.endIndex!) {
|
||||
textNode.parent = scope;
|
||||
textNode.text = text.substring(textNode.startIndex!, textNode.endIndex!)
|
||||
textNode.text = text.substring(textNode.startIndex!, textNode.endIndex! + 1)
|
||||
scope.placeholders!.push(textNode);
|
||||
}
|
||||
|
||||
|
|
@ -223,7 +207,65 @@ function parseDataText(text: string): Node {
|
|||
};
|
||||
}
|
||||
|
||||
function printNode(node: Node, offset='') {
|
||||
function _execute(node: Node, data: Record<string, any> | string, build: string): string {
|
||||
if (!node) {
|
||||
return build;
|
||||
}
|
||||
|
||||
switch (node.type) {
|
||||
case 'Container':
|
||||
return build + node.placeholders!.map(p => _execute(p, data, '')).join('');
|
||||
case 'Conditional':
|
||||
const condEval = _execute(node.condition!, data, '');
|
||||
let value = condEval;
|
||||
try {
|
||||
value = JSON.parse(value);
|
||||
} catch (e) {}
|
||||
if (node.conditionType == 'if') {
|
||||
// BREH JS BS (without the parenthesis it includes build in its ternary condition -.-)
|
||||
return build + (!!value ?
|
||||
_execute(node.consequent!, data, '') :
|
||||
_execute(node.alternative!, data, ''))
|
||||
} else {
|
||||
// BREH JS BS
|
||||
return build + (!!value ?
|
||||
_execute(node.consequent!, value, '') :
|
||||
_execute(node.alternative!, value, ''))
|
||||
}
|
||||
case 'Data':
|
||||
if (node.key == ".") {
|
||||
if (typeof data === 'string') {
|
||||
return build + data
|
||||
} else {
|
||||
return build + JSON.stringify(data);
|
||||
}
|
||||
} else {
|
||||
if (typeof data === 'object') {
|
||||
const inner = data[node.key!.substring(1)]
|
||||
if (typeof inner === 'string') {
|
||||
return build + inner
|
||||
}
|
||||
return build + JSON.stringify(inner);
|
||||
}
|
||||
throw new Error('huh?')
|
||||
}
|
||||
case 'Text':
|
||||
return node.text!;
|
||||
default:
|
||||
return build + '[NOT_IMPLEMENTED]'
|
||||
}
|
||||
}
|
||||
|
||||
export namespace Gin {
|
||||
export function parse(text: string): Node {
|
||||
let root: Node = {
|
||||
type: 'Container',
|
||||
placeholders: []
|
||||
};
|
||||
return _parse(text, root);
|
||||
};
|
||||
|
||||
export function printNode(node: Node, offset='') {
|
||||
switch (node.type) {
|
||||
case 'Conditional':
|
||||
console.log(`${offset}${node.conditionType}`);
|
||||
|
|
@ -249,9 +291,13 @@ function printNode(node: Node, offset='') {
|
|||
default:
|
||||
console.log(`${offset}${node.type}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function execute(template: Template, data: Record<string, any>) {
|
||||
return _execute(template.root, data, '');
|
||||
}
|
||||
};
|
||||
|
||||
printNode(parse(example));
|
||||
/**
|
||||
* Stopgap :(
|
||||
*/
|
||||
|
|
|
|||
81
test/util/gin.test.ts
Normal file
81
test/util/gin.test.ts
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
import { describe, it, expect } from 'bun:test'
|
||||
import { Gin } from '../../src/util/gin'
|
||||
|
||||
describe('Gin template porting test', () => {
|
||||
it('parse', async () => {
|
||||
const example =
|
||||
`Dear {{.Name}},
|
||||
{{if .Attended}}
|
||||
It was a pleasure to see you at the wedding.
|
||||
{{- else}}
|
||||
It is a shame you couldn't make it to the wedding.
|
||||
{{- end}}
|
||||
{{with .Gift -}}
|
||||
Thank you for the lovely {{.}}.
|
||||
{{end}}
|
||||
Best wishes,
|
||||
Josie`;
|
||||
|
||||
const template = {
|
||||
name: 'example',
|
||||
root: Gin.parse(example)
|
||||
};
|
||||
|
||||
const data = [
|
||||
{
|
||||
Name: 'Aunt Mildred',
|
||||
Gift: 'bone china tea set',
|
||||
Attended: true,
|
||||
},
|
||||
{
|
||||
Name: 'Uncle John',
|
||||
Gift: 'moleskin pants',
|
||||
Attended: false,
|
||||
},
|
||||
{
|
||||
Name: 'Cousin Rodney',
|
||||
Gift: '',
|
||||
Attended: false,
|
||||
},
|
||||
]
|
||||
|
||||
const expected =
|
||||
`Dear Aunt Mildred,
|
||||
|
||||
It was a pleasure to see you at the wedding.
|
||||
Thank you for the lovely bone china tea set.
|
||||
|
||||
Best wishes,
|
||||
Josie
|
||||
|
||||
Dear Uncle John,
|
||||
|
||||
It is a shame you couldn't make it to the wedding.
|
||||
Thank you for the lovely moleskin pants.
|
||||
|
||||
Best wishes,
|
||||
Josie
|
||||
|
||||
Dear Cousin Rodney,
|
||||
|
||||
It is a shame you couldn't make it to the wedding.
|
||||
|
||||
Best wishes,
|
||||
Josie`
|
||||
|
||||
const actual = data.map(r => Gin.execute(template, r)).join('\n');
|
||||
console.log(actual)
|
||||
expect(actual).toEqual(expected);
|
||||
})
|
||||
|
||||
// it('parseGlob', async () => {
|
||||
// const gin = new Gin();
|
||||
// const paths = await gin.parseGlob('public/**/*html');
|
||||
// })
|
||||
|
||||
// it('parseHTML', async () => {
|
||||
// const gin = new Gin();
|
||||
// const text = await gin.parseHTML('public/pages/movie.html');
|
||||
// console.log(text);
|
||||
// })
|
||||
})
|
||||
Loading…
Add table
Add a link
Reference in a new issue