Important: This documentation covers Yarn 1 (Classic).
For Yarn 2+ docs and migration guide, see yarnpkg.com.

Package detail

edge-parser

edge-js91.6kMIT9.0.4TypeScript support: included

Parser for edge template engine

edge, template, template-engine

readme

edge-parser

Parser to convert edge templates to invokable functions

gh-workflow-image npm-image license-image

Table of contents

This repo is the parser to convert edge templates to a self invoked Javascript function.

Usage

Install the package from npm registry as follows:

npm i edge-parser

# yarn
yarn add edge-parser

and then use it as follows

import { Parser, EdgeBuffer, Stack } from 'edge-parser'

const filename = 'eval.edge'

const parser = new Parser({}, new Stack(), {
  statePropertyName: 'state',
  escapeCallPath: 'escape',
  toAttributesCallPath: 'toAttributes',
})

const buffer = new EdgeBuffer(filename, {
  outputVar: 'out',
  rethrowCallPath: 'reThrow'
})

parser
  .tokenize('Hello {{ username }}', { filename })
  .forEach((token) => parser.processToken(token, buffer))

const output = buffer.flush()
console.log(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.
  • toAttributesCallPath: Reference to the function that will convert an object to HTML attributes.
  • 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

let out = ''
let $lineNumber = 1
let $filename = 'eval.edge'
try {
  out += 'Hello '
  out += `${escape(state.username)}`
} catch (error) {
  reThrow(error, $filename, $lineNumber)
}
return out

You can wrap the compiled output inside a function and invoke it as follows

/**
 * Convert string to a function
 */
const fn = new Function('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

Along with parsing the main template, the parser also exposes the API, that tags can use to selectively parse the content of a tag.

generateAST(jsExpression, lexerLoc, filename)

Parses a string as a Javascript expression. The output is a valid Estree expression

The following example returns a BinaryExpression

const loc = {
  start: { line: 1, col: 1 },
  end: { line: 1, col: 1 },
}
const filename = 'eval.edge'

parser.utils.generateAST('2 + 2', loc, filename)

transformAst(acornAst, filename)

Transform the acorn AST and make it compatible with Edge runtime. This method mutates the inner nodes of the original AST.

const loc = {
  start: { line: 1, col: 1 },
  end: { line: 1, col: 1 },
}
const filename = 'eval.edge'

parser.utils.transformAst(parser.utils.generateAST('2 + 2', loc, filename), filename)

tokenize (template, options: { filename })

Returns an array of lexer tokens for the given template. The method is a shortcut to self import the lexer module and then generating tokens.

const tokens = parser.tokenize('Hello {{ username }}', {
  filename: 'eval.edge',
})

Output

[
  {
    "type": "raw",
    "line": 1,
    "value": "Hello "
  },
  {
    "type": "mustache",
    "filename": "eval.edge",
    "loc": {
      "start": {
        "line": 1,
        "col": 8
      },
      "end": {
        "line": 1,
        "col": 20
      }
    },
    "properties": {
      "jsArg": " username "
    }
  }
]

stringify(expression)

Convert edge or acorn expression back to a string. This is helpful, when you mutate some nodes inside the expression and now want a valid Javascript string out of it.

const expression = parser.utils.generateAST(
  '2 + 2',
  {
    start: { line: 1, col: 1 },
    end: { line: 1, col: 1 },
  },
  'eval.edge'
)

expression.left.value = 3
parser.utils.stringify(expression) // returns 3 + 2

processToken(token, buffer)

You will often find yourself using this method as a tag author, when you want to recursively process all children of your tag

const byPass = {
  block: true,
  seekable: false,
  name: 'bypass',

  compile(parser, buffer, token) {
    token.children.forEach((child) => parser.processToken(child, buffer))
  },
}

and then use it as

@bypass
  Hello {{ username }}
@endbypass

Supported Expressions

The following expressions are supported by the parser. Can you also access the list of supported expressions as

import { expressions } from 'edge-parser'

Identifier

The identifier are prefixed with state. In following statement username is the identifier

Hello {{ username }}

Literal

A string literal

Hello {{ 'Guest' }}

ArrayExpression

The [1, 2, 3, 4] is an array expression.

Evens are {{
  [1, 2, 3, 4].filter((num) => num % 2 === 0)
}}

ObjectExpression

The { username: 'virk' } is an Object expression

{{ toJSON({ username: 'virk' })  }}

UnaryExpression

Following are examples of UnaryExpression.

{{ typeof(username) }}

{{ !!username }}

BinaryExpression

Here {{ 2 + 2 }} is the binary expression

{{ 2 + 2 }} = 4

LogicalExpression

Following is the example of LogicalExpression.

{{ username || admin.username }}

MemberExpression

{{ username.toUpperCase() }}

ConditionalExpression

{{ username ? username : 'Guest' }}

CallExpression

{{ upper(username) }}

SequenceExpression

Sequence is not supported in mustache blocks and instead used inside tags. For example:

Everything inside () is a sequence expression.

@component('button', text = 'Submit', type = 'Primary')

TemplateLiteral

{{ Hello `${username}` }}

ArrowFunctionExpression

{{
  users.map((user) => {
    return user.username
  })
}}

AwaitExpression

{{ await foo() }}

FunctionDeclaration

{{ function foo () {} }}

BlockStatement

Here the map callback is the block statement

{{
  users.map(() => {})
}}

ChainExpression

Support for optional chaining

{{ user?.username }}

NewExpression

{{ new User() }}

ReturnStatement

In the following example return keyword is a return statement

users.map((user) => {
  return user.username
})

ThisExpression

Support for the this keyword

{{ this.state }}

SpreadElement

Support for the spread element

{{ [...users] }}

changelog

2.0.3 (2018-11-09)

2.0.2 (2018-11-09)

Bug Fixes

  • parser: report correct column number (98b06b5)

2.0.1 (2018-11-09)

Bug Fixes

  • parser: report correct column number (98b06b5)

2.0.0 (2018-11-05)

1.0.16 (2018-07-12)

Features

  • parser: expose parseTokens method and make options public (3a30ff4)

1.0.15 (2018-07-10)

Bug Fixes

  • parser: patch loc for all the tokens of acorn (1c4c24e)

1.0.14 (2018-07-10)

1.0.13 (2018-07-09)

Features

  • parser: expose generateTokens method to generate lexer tokens (9c5073f)

1.0.12 (2018-07-09)

1.0.11 (2018-07-07)

1.0.10 (2018-07-07)

Features

  • buffer: add support for wrapping statements (5242f7d)

1.0.9 (2018-07-05)

Bug Fixes

  • objectexpression: turn off shorthand objects (c43444f)

Features

  • AssignmentExpression: add support (c920387)

1.0.8 (2018-07-04)

1.0.7 (2018-07-04)

Code Refactoring

  • parser: patch the loc node of ast in place (4314ded)

BREAKING CHANGES

  • parser: removed patchNodeLine

1.0.5 (2018-07-02)

Features

  • buffer: add support to return unwrapped lines (da27c99)
  • parser: add support for unwrapped templates (853ba21)

1.0.3 (2018-07-01)

Bug Fixes

  • newline: use os.EOL convert \n to new lines (4fa76a9)

1.0.2 (2018-06-01)

Bug Fixes

  • buffer: escape quotes inside raw strings (cd659d0)

1.0.1 (2018-06-01)

1.0.0 (2018-05-31)

Bug Fixes

  • buffer: append new line with writeLine statement (d4852f5)
  • parser: export must use es6 export syntax (b6a1e61)
  • parser: wrap mustache nodes inside escape call (432cfed)

Features

  • cover all allowed expression (5a415d8)
  • initial commit (971bbd9)
  • buffer: add writeStatement method (be705fd)
  • buffer: expose edge buffer interface (b01d34b)
  • errors: patch lineno in errors (ca7e93f)
  • parser: handle block tokens too (08a4ded)
  • parser: parse eacaped and safe mustache tokens (2dafb5e)