edge-parser
Advanced tools
Comparing version 6.0.2 to 7.0.0
@@ -31,2 +31,3 @@ export { Stack } from './src/Stack'; | ||
ChainExpression: "ChainExpression"; | ||
SpreadElement: "SpreadElement"; | ||
}; |
@@ -26,2 +26,3 @@ import { TagToken, LexerTagDefinitionContract, MustacheToken } from 'edge-lexer'; | ||
export declare type ClaimTagFn = (name: string) => LexerTagDefinitionContract | null; | ||
export declare type OnLineFn = (line: string) => string; | ||
/** | ||
@@ -31,6 +32,37 @@ * Parser options | ||
export declare type ParserOptions = { | ||
/** | ||
* Is parsing in async mode | ||
*/ | ||
async?: boolean; | ||
/** | ||
* Modify the line before it is being processed by the lexer | ||
*/ | ||
onLine?: OnLineFn; | ||
/** | ||
* Modify the tag before it is being processed by the parser | ||
*/ | ||
onTag?: TagTransformer; | ||
/** | ||
* Modify the mustache block before it is being processed by the parser | ||
*/ | ||
onMustache?: MustacheTransformer; | ||
/** | ||
* Claim un-registered tags | ||
*/ | ||
claimTag?: ClaimTagFn; | ||
/** | ||
* Nested or flat path to the escape method for escaping values. | ||
*/ | ||
escapeCallPath: string | [string, string]; | ||
/** | ||
* Name of the property to be used for accessing the values from | ||
* the template. Leave it to an empty string, if properties | ||
* are available directly (meaning without a subpath). | ||
*/ | ||
statePropertyName: string; | ||
/** | ||
* An array of local variables to be accessible directly. Define these | ||
* so that the parser doesn't access them from the state property. | ||
*/ | ||
localVariables?: string[]; | ||
}; |
@@ -31,4 +31,5 @@ /** | ||
private compiledOutput; | ||
constructor(filename: string, options?: { | ||
outputVar?: string; | ||
constructor(filename: string, options: { | ||
outputVar: string; | ||
rethrowCallPath: string | [string, string]; | ||
}); | ||
@@ -35,0 +36,0 @@ /** |
@@ -26,3 +26,4 @@ "use strict"; | ||
this.options = { | ||
outputVar: 'out', | ||
outputVar: '', | ||
rethrowCallPath: '', | ||
fileNameVar: '$filename', | ||
@@ -48,3 +49,6 @@ lineVar: '$lineNumber', | ||
this.currentFileName = this.filename; | ||
Object.assign(this.options, options); | ||
this.options.outputVar = options.outputVar; | ||
this.options.rethrowCallPath = Array.isArray(options.rethrowCallPath) | ||
? options.rethrowCallPath.join('.') | ||
: options.rethrowCallPath; | ||
} | ||
@@ -90,3 +94,3 @@ /** | ||
*/ | ||
buffer.push(`ctx.reThrow(error, ${this.options.fileNameVar}, ${this.options.lineVar});`); | ||
buffer.push(`${this.options.rethrowCallPath}(error, ${this.options.fileNameVar}, ${this.options.lineVar});`); | ||
/** | ||
@@ -93,0 +97,0 @@ * End catch block |
@@ -12,2 +12,3 @@ "use strict"; | ||
const transformAst_1 = require("../Parser/transformAst"); | ||
const edge_error_1 = require("edge-error"); | ||
exports.default = { | ||
@@ -26,3 +27,8 @@ toStatement(statement, filename, parser) { | ||
else { | ||
throw new Error(`Report this error to the maintainers: Expected Arrow function params to be an identifier. Instead received ${param.type}`); | ||
const { line, col } = parser.utils.getExpressionLoc(param); | ||
throw new edge_error_1.EdgeError(`Report this error to the maintainers: Unexpected arrow function property type ${param.type}`, 'E_PARSER_ERROR', { | ||
line, | ||
col, | ||
filename, | ||
}); | ||
} | ||
@@ -29,0 +35,0 @@ }); |
@@ -12,6 +12,5 @@ "use strict"; | ||
const makeStatePropertyAccessor_1 = require("../Parser/makeStatePropertyAccessor"); | ||
const WHITE_LISTED = ['state', '$filename']; | ||
exports.default = { | ||
toStatement(statement, _, parser) { | ||
if (WHITE_LISTED.indexOf(statement.name) > -1 || | ||
if ((parser.options.localVariables || []).indexOf(statement.name) > -1 || | ||
parser.stack.has(statement.name) || | ||
@@ -21,4 +20,4 @@ global[statement.name] !== undefined) { | ||
} | ||
return makeStatePropertyAccessor_1.makeStatePropertyAccessor(statement); | ||
return makeStatePropertyAccessor_1.makeStatePropertyAccessor(parser.options.statePropertyName, statement); | ||
}, | ||
}; |
@@ -23,1 +23,2 @@ export { default as Identifier } from './Identifier'; | ||
export { default as ChainExpression } from './ChainExpression'; | ||
export { default as SpreadElement } from './SpreadElement'; |
@@ -14,3 +14,3 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.ChainExpression = exports.ThisExpression = exports.ReturnStatement = exports.BlockStatement = exports.NewExpression = exports.AwaitExpression = exports.AssignmentExpression = exports.SequenceExpression = exports.LogicalExpression = exports.ConditionalExpression = exports.FunctionDeclaration = exports.UnaryExpression = exports.ObjectExpression = exports.ArrayExpression = exports.BinaryExpression = exports.TemplateLiteral = exports.Literal = exports.ArrowFunctionExpression = exports.CallExpression = exports.ExpressionStatement = exports.MemberExpression = exports.Identifier = void 0; | ||
exports.SpreadElement = exports.ChainExpression = exports.ThisExpression = exports.ReturnStatement = exports.BlockStatement = exports.NewExpression = exports.AwaitExpression = exports.AssignmentExpression = exports.SequenceExpression = exports.LogicalExpression = exports.ConditionalExpression = exports.FunctionDeclaration = exports.UnaryExpression = exports.ObjectExpression = exports.ArrayExpression = exports.BinaryExpression = exports.TemplateLiteral = exports.Literal = exports.ArrowFunctionExpression = exports.CallExpression = exports.ExpressionStatement = exports.MemberExpression = exports.Identifier = void 0; | ||
var Identifier_1 = require("./Identifier"); | ||
@@ -60,1 +60,3 @@ Object.defineProperty(exports, "Identifier", { enumerable: true, get: function () { return __importDefault(Identifier_1).default; } }); | ||
Object.defineProperty(exports, "ChainExpression", { enumerable: true, get: function () { return __importDefault(ChainExpression_1).default; } }); | ||
var SpreadElement_1 = require("./SpreadElement"); | ||
Object.defineProperty(exports, "SpreadElement", { enumerable: true, get: function () { return __importDefault(SpreadElement_1).default; } }); |
@@ -12,16 +12,28 @@ "use strict"; | ||
const transformAst_1 = require("../Parser/transformAst"); | ||
const edge_error_1 = require("edge-error"); | ||
exports.default = { | ||
toStatement(statement, filename, parser) { | ||
statement.properties = statement.properties.map((node) => { | ||
/** | ||
* Since we change the structure of node.value, we have to | ||
* turnoff shorthand objects, so that the astring outputs | ||
* the key name explicitly | ||
*/ | ||
node.shorthand = false; | ||
if (node.computed === true) { | ||
node.key = transformAst_1.transformAst(node.key, filename, parser); | ||
if (node.type === 'Property') { | ||
/** | ||
* Since we change the structure of node.value, we have to | ||
* turnoff shorthand objects, so that the astring outputs | ||
* the key name explicitly | ||
*/ | ||
node.shorthand = false; | ||
if (node.computed === true) { | ||
node.key = transformAst_1.transformAst(node.key, filename, parser); | ||
} | ||
node.value = transformAst_1.transformAst(node.value, filename, parser); | ||
return node; | ||
} | ||
node.value = transformAst_1.transformAst(node.value, filename, parser); | ||
return node; | ||
if (node.type === 'SpreadElement') { | ||
return transformAst_1.transformAst(node, filename, parser); | ||
} | ||
const { line, col } = parser.utils.getExpressionLoc(node); | ||
throw new edge_error_1.EdgeError(`Report this error to the maintainers: Unexpected object property type "${node.type}"`, 'E_PARSER_ERROR', { | ||
line, | ||
col, | ||
filename, | ||
}); | ||
}); | ||
@@ -28,0 +40,0 @@ return statement; |
@@ -7,3 +7,3 @@ import { Token } from 'edge-lexer'; | ||
import { transformAst } from './transformAst'; | ||
import { makeCtxCallable } from './makeCtxCallable'; | ||
import { makeEscapeCallable } from './makeEscapeCallable'; | ||
import { makeStatePropertyAccessor } from './makeStatePropertyAccessor'; | ||
@@ -46,3 +46,3 @@ import { ParserTagDefinitionContract, ParserOptions } from '../Contracts'; | ||
[key: string]: ParserTagDefinitionContract; | ||
}, stack?: Stack, options?: ParserOptions); | ||
}, stack: Stack, options: ParserOptions); | ||
/** | ||
@@ -55,3 +55,3 @@ * Parser utilities work with the AST | ||
stringify: typeof stringify; | ||
makeCtxCallable: typeof makeCtxCallable; | ||
makeEscapeCallable: typeof makeEscapeCallable; | ||
makeStatePropertyAccessor: typeof makeStatePropertyAccessor; | ||
@@ -65,2 +65,6 @@ collectObjectExpressionProperties: typeof collectObjectExpressionProperties; | ||
/** | ||
* Returns the options to be passed to the tokenizer | ||
*/ | ||
private getTokenizerOptions; | ||
/** | ||
* Process escaped tag token by writing it as it is. However, the children | ||
@@ -71,4 +75,3 @@ * inside a tag are still processed. | ||
/** | ||
* Process escaped tag token by writing it as it is. However, the children | ||
* inside a tag are still processed. | ||
* Process escaped muscahe block by writing it as it is. | ||
*/ | ||
@@ -75,0 +78,0 @@ private processEscapedMustache; |
@@ -18,3 +18,3 @@ "use strict"; | ||
const transformAst_1 = require("./transformAst"); | ||
const makeCtxCallable_1 = require("./makeCtxCallable"); | ||
const makeEscapeCallable_1 = require("./makeEscapeCallable"); | ||
const makeStatePropertyAccessor_1 = require("./makeStatePropertyAccessor"); | ||
@@ -45,3 +45,3 @@ const collectObjectExpressionProperties_1 = require("./collectObjectExpressionProperties"); | ||
class Parser { | ||
constructor(tags, stack = new Stack_1.Stack(), options = {}) { | ||
constructor(tags, stack = new Stack_1.Stack(), options) { | ||
this.tags = tags; | ||
@@ -61,3 +61,3 @@ this.stack = stack; | ||
stringify: stringify_1.stringify, | ||
makeCtxCallable: makeCtxCallable_1.makeCtxCallable, | ||
makeEscapeCallable: makeEscapeCallable_1.makeEscapeCallable, | ||
makeStatePropertyAccessor: makeStatePropertyAccessor_1.makeStatePropertyAccessor, | ||
@@ -76,2 +76,15 @@ collectObjectExpressionProperties: collectObjectExpressionProperties_1.collectObjectExpressionProperties, | ||
/** | ||
* Returns the options to be passed to the tokenizer | ||
*/ | ||
getTokenizerOptions(options) { | ||
if (!this.options) { | ||
return options; | ||
} | ||
return { | ||
claimTag: this.options.claimTag, | ||
onLine: this.options.onLine, | ||
filename: options.filename, | ||
}; | ||
} | ||
/** | ||
* Process escaped tag token by writing it as it is. However, the children | ||
@@ -97,4 +110,3 @@ * inside a tag are still processed. | ||
/** | ||
* Process escaped tag token by writing it as it is. However, the children | ||
* inside a tag are still processed. | ||
* Process escaped muscahe block by writing it as it is. | ||
*/ | ||
@@ -115,3 +127,5 @@ processEscapedMustache(token, buffer) { | ||
*/ | ||
const expression = type === edge_lexer_1.MustacheTypes.MUSTACHE ? makeCtxCallable_1.makeCtxCallable('escape', [node]) : node; | ||
const expression = type === edge_lexer_1.MustacheTypes.MUSTACHE | ||
? makeEscapeCallable_1.makeEscapeCallable(this.options.escapeCallPath, [node]) | ||
: node; | ||
/** | ||
@@ -135,8 +149,3 @@ * Template literal, so there is no need to wrap it inside another | ||
tokenize(template, options) { | ||
const tokenizer = new edge_lexer_1.Tokenizer(template, this.tags, this.options.claimTag | ||
? { | ||
claimTag: this.options.claimTag, | ||
filename: options.filename, | ||
} | ||
: options); | ||
const tokenizer = new edge_lexer_1.Tokenizer(template, this.tags, this.getTokenizerOptions(options)); | ||
tokenizer.parse(); | ||
@@ -143,0 +152,0 @@ return tokenizer.tokens; |
/** | ||
* Returns Acorn complaint AST for a collable expression | ||
*/ | ||
export declare function makeStatePropertyAccessor(args: object): any; | ||
export declare function makeStatePropertyAccessor(propertyName: string, args: object): any; |
@@ -15,3 +15,3 @@ "use strict"; | ||
*/ | ||
function makeStatePropertyAccessor(args) { | ||
function makeStatePropertyAccessor(propertyName, args) { | ||
return { | ||
@@ -21,3 +21,3 @@ type: 'MemberExpression', | ||
type: 'Identifier', | ||
name: 'state', | ||
name: propertyName, | ||
}, | ||
@@ -24,0 +24,0 @@ computed: false, |
{ | ||
"name": "edge-parser", | ||
"version": "6.0.2", | ||
"version": "7.0.0", | ||
"description": "Parser for edge template engine", | ||
@@ -62,6 +62,3 @@ "main": "build/index.js", | ||
"np": "^7.2.0", | ||
"npm-audit-html": "^1.5.0", | ||
"prettier": "^2.2.1", | ||
"typedoc": "^0.20.14", | ||
"typedoc-plugin-markdown": "^3.4.0", | ||
"typescript": "^4.1.3", | ||
@@ -88,3 +85,3 @@ "youch": "^2.1.1" | ||
"hooks": { | ||
"pre-commit": "doctoc README.md --title='## Table of contents' && git add README.md && npm audit --production --json | ./node_modules/.bin/npm-audit-html && git add npm-audit.html", | ||
"pre-commit": "doctoc README.md --title='## Table of contents' && git add README.md", | ||
"commit-msg": "node ./node_modules/@adonisjs/mrm-preset/validateCommit/conventional/validate.js" | ||
@@ -91,0 +88,0 @@ } |
129
README.md
@@ -17,5 +17,4 @@ <div align="center"><img src="https://res.cloudinary.com/adonis-js/image/upload/q_100/v1600679850/edge-banner_wao6ex.png" width="600px"></div> | ||
- [transformAst(acornAst, filename)](#transformastacornast-filename) | ||
- [tokenize (template)](#tokenize-template) | ||
- [tokenize (template, options: { filename })](#tokenize-template-options--filename-) | ||
- [stringify(expression)](#stringifyexpression) | ||
- [parse(template)](#parsetemplate) | ||
- [processToken(token, buffer)](#processtokentoken-buffer) | ||
@@ -38,8 +37,8 @@ - [Supported Expressions](#supported-expressions) | ||
- [FunctionDeclaration](#functiondeclaration) | ||
- [Template expectations](#template-expectations) | ||
- [API Docs](#api-docs) | ||
- [BlockStatement](#blockstatement) | ||
- [ChainExpression](#chainexpression) | ||
<!-- END doctoc generated TOC please keep comment here to allow auto update --> | ||
This repo is the **parser to convert edge templates** to a self invoked Javascript function. Later you can invoke this function by providing a [context](#context-expectations). | ||
This repo is the **parser to convert edge templates** to a self invoked Javascript function. | ||
@@ -60,12 +59,31 @@ ## Usage | ||
```js | ||
import { Parser } from 'edge-parser' | ||
import { Parser, EdgeBuffer, Stack } from 'edge-parser' | ||
const tagsIfAny = {} | ||
const parser = new Parser(tagsIfAny, { filename: 'foo.edge' }) | ||
const filename = 'eval.edge' | ||
const statePropertyName = 'state' | ||
const escapeCallPath = 'escape' | ||
const outputVar = 'out' | ||
const rethrowCallPath = 'reThrow' | ||
parser.parse(`Hello {{ username }}`) | ||
const parser = new Parser({}, new Stack(), { | ||
statePropertyName, | ||
escapeCallPath, | ||
}) | ||
const buffer = new EdgeBuffer(filename, { outputVar, rethrowCallPath }) | ||
parser | ||
.tokenize('Hello {{ username }}', { filename }) | ||
.forEach((token) => parser.processToken(token, buffer)) | ||
``` | ||
**Output** | ||
- All the first set of `const` declarations are the config values that impacts the compiled output. | ||
- `filename` is required to ensure that exceptions stack traces point back to the correct filename. | ||
- `statePropertyName` is the variable name from which the values should be accessed. For example: `{{ username }}` will be compiled as `state.username`. Leave it to empty, if state is not nested inside an object. | ||
- `escapeCallPath` Reference to the `escape` method for escaping interpolation values. For example: `{{ username }}` will be compiled as `escape(state.username)`. The `escape` method should escape only strings and return the other data types as it is. | ||
- `outputVar` is the variable name that holds the output of the compiled template. | ||
- `rethrowCallPath` Reference to the `reThrow` method to raise the template exceptions with the current `$filename` and `$lineNumber`. Check the following compiled output to see how this function is called. | ||
**Compiled output** | ||
```js | ||
@@ -77,5 +95,5 @@ let out = '' | ||
out += 'Hello ' | ||
out += `${ctx.escape(state.username)}` | ||
out += `${escape(state.username)}` | ||
} catch (error) { | ||
ctx.reThrow(error, $filename, $lineNumber) | ||
reThrow(error, $filename, $lineNumber) | ||
} | ||
@@ -85,4 +103,32 @@ return out | ||
> Notice of use of `ctx` in the function body. Parser doesn't provide the implementation of `ctx`, the runtime of template engine should provide it. | ||
You can wrap the compiled output inside a function and invoke it as follows | ||
```ts | ||
/** | ||
* Convert string to a function | ||
*/ | ||
const fn = new Function('', `return function template (state, escape, reThrow) { ${output} }`)() | ||
/** | ||
* Template state | ||
*/ | ||
const state = { username: 'virk' } | ||
/** | ||
* Escape function | ||
*/ | ||
function escape(value: any) { | ||
return value | ||
} | ||
/** | ||
* Rethrow function | ||
*/ | ||
function reThrow(error: Error) { | ||
throw error | ||
} | ||
console.log(fn(state, escape, reThrow)) | ||
``` | ||
## Parser API | ||
@@ -122,3 +168,3 @@ | ||
#### tokenize (template) | ||
#### tokenize (template, options: { filename }) | ||
@@ -128,3 +174,5 @@ Returns an array of [lexer tokens](https://github.com/edge-js/lexer) for the given template. The method is a shortcut to self import the lexer module and then generating tokens. | ||
```ts | ||
const tokens = parser.tokenize('Hello {{ username }}') | ||
const tokens = parser.tokenize('Hello {{ username }}', { | ||
filename: 'eval.edge', | ||
}) | ||
``` | ||
@@ -179,25 +227,2 @@ | ||
#### parse(template) | ||
Parse a template to an `IIFE`. This is what you will use most of the time. | ||
```ts | ||
parser.parse('Hello {{ username }}') | ||
``` | ||
**Output** | ||
```js | ||
let out = '' | ||
let $lineNumber = 1 | ||
let $filename = 'eval.edge' | ||
try { | ||
out += 'Hello ' | ||
out += `${ctx.escape(state.username)}` | ||
} catch (error) { | ||
ctx.reThrow(error, $filename, $lineNumber) | ||
} | ||
return out | ||
``` | ||
#### processToken(token, buffer) | ||
@@ -351,25 +376,19 @@ | ||
## Template expectations | ||
#### BlockStatement | ||
You must define a context object with `escape` and `reThrow` methods when executing the parser compiled function | ||
Here the `map` callback is the block statement | ||
```ts | ||
const ctx = { | ||
escape(value) { | ||
if (typeof value === 'string') { | ||
return escapedValue | ||
} | ||
return value | ||
}, | ||
reThrow(error, fileName, lineNumber) {}, | ||
} | ||
``` | ||
{{ | ||
users.map(() => {}) | ||
}} | ||
``` | ||
## API Docs | ||
#### ChainExpression | ||
Following are the auto generated files via Type doc | ||
Support for optional chaining | ||
- [API](docs/README.md) | ||
``` | ||
{{ user?.username }} | ||
``` | ||
@@ -376,0 +395,0 @@ [circleci-image]: https://img.shields.io/circleci/project/github/edge-js/parser/master.svg?style=for-the-badge&logo=circleci |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
77898
23
74
1738
393