edge-parser
    TypeScript icon, indicating that this package has built-in type declarations

    8.1.0 • Public • Published

    Edge Parser

    Parser to convert edge templates to invokable functions

    gh-workflow-image npm-image license-image synk-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 statePropertyName = 'state'
    const escapeCallPath = 'escape'
    const outputVar = 'out'
    const rethrowCallPath = 'reThrow'
    
    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))
    • 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

    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('', `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

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

    Install

    npm i edge-parser

    DownloadsWeekly Downloads

    7,312

    Version

    8.1.0

    License

    MIT

    Unpacked Size

    76.8 kB

    Total Files

    73

    Last publish

    Collaborators

    • virk