npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

edge-parser

v9.0.3

Published

Parser for edge template engine

Downloads

81,113

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] }}