-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
3f2d3bc
commit e708553
Showing
5 changed files
with
181 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
import handlebars from 'handlebars'; | ||
|
||
/** | ||
* Parse AST for variables | ||
* | ||
* @param statement - Statement to parse | ||
* @returns Variables | ||
*/ | ||
function parseHandlebarsAst(statement: hbs.AST.Statement): { | ||
[k in string]: unknown; | ||
} { | ||
// No variables | ||
if (statement.type === 'ContentStatement') { | ||
return {}; | ||
} | ||
|
||
if (statement.type === 'PartialStatement') { | ||
const moustacheStatement = statement as hbs.AST.PartialStatement; | ||
const pathStatement = moustacheStatement.name as hbs.AST.PathExpression; | ||
return { | ||
[pathStatement.original]: 'partial', | ||
}; | ||
} | ||
|
||
// Parse variables from {{ var }} | ||
if (statement.type === 'MustacheStatement') { | ||
const moustacheStatement = statement as hbs.AST.MustacheStatement; | ||
const paramsExpressionList = | ||
moustacheStatement.params as hbs.AST.PathExpression[]; | ||
const pathExpression = moustacheStatement.path as hbs.AST.PathExpression; | ||
const vars = [ | ||
...paramsExpressionList.map(({ original }) => original), | ||
pathExpression.original, | ||
].filter((x) => !!x); | ||
return vars.reduce((acc, x) => Object.assign(acc, { [x]: null }), {}); | ||
} | ||
|
||
// Parse from {{#each}} or {{#with}} | ||
if (statement.type === 'BlockStatement' && statement) { | ||
const moustacheStatement = statement as hbs.AST.MustacheStatement; | ||
const paramsExpressionList = | ||
moustacheStatement.params as hbs.AST.PathExpression[]; | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
const program = (moustacheStatement as any).program as hbs.AST.Program; | ||
const param = paramsExpressionList[0] as unknown as | ||
| hbs.AST.PathExpression | ||
| hbs.AST.SubExpression; | ||
const pathExpression = moustacheStatement.path as hbs.AST.PathExpression; | ||
if (param.type === 'SubExpression') { | ||
return program.body | ||
.map(parseHandlebarsAst) | ||
.reduce((acc, obj) => Object.assign(acc, obj), {}); | ||
} | ||
|
||
if (pathExpression.original === 'each') { | ||
return { | ||
[param.original]: [ | ||
program.body | ||
.map(parseHandlebarsAst) | ||
.reduce((acc, obj) => Object.assign(acc, obj), {}), | ||
], | ||
}; | ||
} | ||
|
||
return { | ||
[param.original]: program.body | ||
.map(parseHandlebarsAst) | ||
.reduce((acc, obj) => Object.assign(acc, obj), {}), | ||
}; | ||
} | ||
throw new Error(`Unknown statement: ${statement.type}`); | ||
} | ||
|
||
/** | ||
* Get variables from handlebars template | ||
* | ||
* @param template - Template | ||
* @returns Variables | ||
*/ | ||
export function getVariablesFromHandlebarsTemplate(template: string): { | ||
[k in string]: unknown; | ||
} { | ||
const ast = handlebars.parseWithoutProcessing(template); | ||
|
||
const results = ast.body.map(parseHandlebarsAst); | ||
return results.reduce((acc, data) => { | ||
Object.entries(data).forEach(([k, v]) => { | ||
const existing = acc[k]; | ||
if (!existing) { | ||
return Object.assign(acc, { [k]: v }); | ||
} | ||
if (Array.isArray(existing) && Array.isArray(v)) { | ||
return Object.assign(acc, { | ||
[k]: [ | ||
{ | ||
...existing[0], | ||
...v[0], | ||
}, | ||
], | ||
}); | ||
} | ||
if (typeof existing === 'object' && typeof v === 'object') { | ||
return Object.assign(acc, { | ||
[k]: { | ||
...existing, | ||
...v, | ||
}, | ||
}); | ||
} | ||
return Object.assign(acc, { | ||
[k]: v, | ||
}); | ||
}); | ||
return acc; | ||
}, {}); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,3 @@ | ||
export * from './change-case'; | ||
export * from './createHandlebars'; | ||
export * from './getVariablesFromHandlebarsTemplate'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
import { expect } from 'chai'; | ||
|
||
import { getVariablesFromHandlebarsTemplate } from '../index'; | ||
|
||
const TEST_HBS = ` | ||
<p> | ||
You are an experienced project manager, constantly juggling a variety of different tasks. | ||
Other employees at your company are very busy. | ||
They quickly record notes in slack of things that they want to look into later. | ||
You are tasked with reviewing those raw notes, and turning them into a structured | ||
form that will make the notes more easily indexable by the employee when they have more time to review the information. | ||
{{{ description }}}. Return a JSON object in the following format: | ||
({{#each parameters}}{{ name }} - {{slug}}, {{/each}}clarification). | ||
</p> | ||
{{> promptPartialTodaysDate }} | ||
{{> promptPartialTranscendProducts }} | ||
{{#with dog}}{{cat}} - {{fish}}{{/with}} | ||
{{#if (listLen extraContext)}} | ||
{{#each extraContext}} | ||
{{ name }} | ||
{{/each}} | ||
{{/if}} | ||
<p> | ||
The following rules define each of the input parameters: | ||
<ul> | ||
{{#each parameters}} | ||
<li>{{ name }}: {{{ description }}}</li> | ||
{{/each}} | ||
</ul> | ||
</p> | ||
{{#with dog}} {{meow}}{{/with}} | ||
<p> | ||
If any of the parameters are not known, it should be set to null. | ||
If anything is not clear, return a prompt in the clarification key of the response asking for further detail | ||
</p>`; | ||
|
||
describe('getVariablesFromHandlebars', () => { | ||
it('should merge together', () => { | ||
expect(getVariablesFromHandlebarsTemplate(TEST_HBS)).to.deep.equal({ | ||
description: null, | ||
parameters: [{ name: null, slug: null, description: null }], | ||
dog: { cat: null, fish: null, meow: null }, | ||
extraContext: [ | ||
{ | ||
name: null, | ||
}, | ||
], | ||
promptPartialTodaysDate: 'partial', | ||
promptPartialTranscendProducts: 'partial', | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters