{"id":14156353,"url":"https://github.com/edge-js/parser","last_synced_at":"2025-03-20T04:14:24.733Z","repository":{"id":33233495,"uuid":"134853604","full_name":"edge-js/parser","owner":"edge-js","description":"Parser for edge template engine","archived":false,"fork":false,"pushed_at":"2025-01-18T10:34:46.000Z","size":2051,"stargazers_count":19,"open_issues_count":0,"forks_count":1,"subscribers_count":4,"default_branch":"9.x","last_synced_at":"2025-02-21T13:39:10.907Z","etag":null,"topics":["acorn","ast","estree","parser"],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/edge-js.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":"thetutlage"}},"created_at":"2018-05-25T12:30:55.000Z","updated_at":"2025-01-18T10:34:48.000Z","dependencies_parsed_at":"2024-03-18T21:01:03.825Z","dependency_job_id":"ed5587c0-27cd-4dcf-877a-2f4a37716e20","html_url":"https://github.com/edge-js/parser","commit_stats":{"total_commits":291,"total_committers":4,"mean_commits":72.75,"dds":0.03436426116838487,"last_synced_commit":"358510d61202e24b5b00c6effa21bbf5b617d50c"},"previous_names":[],"tags_count":92,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/edge-js%2Fparser","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/edge-js%2Fparser/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/edge-js%2Fparser/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/edge-js%2Fparser/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/edge-js","download_url":"https://codeload.github.com/edge-js/parser/tar.gz/refs/heads/9.x","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244547600,"owners_count":20470104,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["acorn","ast","estree","parser"],"created_at":"2024-08-17T08:05:24.118Z","updated_at":"2025-03-20T04:14:24.707Z","avatar_url":"https://github.com/edge-js.png","language":"TypeScript","funding_links":["https://github.com/sponsors/thetutlage"],"categories":["others"],"sub_categories":[],"readme":"# edge-parser\n\u003e Parser to convert edge templates to invokable functions\n\n[![gh-workflow-image]][gh-workflow-url] [![npm-image]][npm-url] ![][typescript-image] [![license-image]][license-url]\n\n\u003c!-- START doctoc generated TOC please keep comment here to allow auto update --\u003e\n\u003c!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE --\u003e\n\n## Table of contents\n\n- [Table of contents](#table-of-contents)\n- [Usage](#usage)\n- [Parser API](#parser-api)\n  - [generateAST(jsExpression, lexerLoc, filename)](#generateastjsexpression-lexerloc-filename)\n  - [transformAst(acornAst, filename)](#transformastacornast-filename)\n  - [tokenize (template, options: { filename })](#tokenize-template-options--filename-)\n  - [stringify(expression)](#stringifyexpression)\n  - [processToken(token, buffer)](#processtokentoken-buffer)\n- [Supported Expressions](#supported-expressions)\n  - [Identifier](#identifier)\n  - [Literal](#literal)\n  - [ArrayExpression](#arrayexpression)\n  - [ObjectExpression](#objectexpression)\n  - [UnaryExpression](#unaryexpression)\n  - [BinaryExpression](#binaryexpression)\n  - [LogicalExpression](#logicalexpression)\n  - [MemberExpression](#memberexpression)\n  - [ConditionalExpression](#conditionalexpression)\n  - [CallExpression](#callexpression)\n  - [SequenceExpression](#sequenceexpression)\n  - [TemplateLiteral](#templateliteral)\n  - [ArrowFunctionExpression](#arrowfunctionexpression)\n  - [AwaitExpression](#awaitexpression)\n  - [FunctionDeclaration](#functiondeclaration)\n  - [BlockStatement](#blockstatement)\n  - [ChainExpression](#chainexpression)\n\n\u003c!-- END doctoc generated TOC please keep comment here to allow auto update --\u003e\n\nThis repo is the **parser to convert edge templates** to a self invoked Javascript function.\n\n## Usage\n\nInstall the package from npm registry as follows:\n\n```sh\nnpm i edge-parser\n\n# yarn\nyarn add edge-parser\n```\n\nand then use it as follows\n\n```js\nimport { Parser, EdgeBuffer, Stack } from 'edge-parser'\n\nconst filename = 'eval.edge'\n\nconst parser = new Parser({}, new Stack(), {\n  statePropertyName: 'state',\n  escapeCallPath: 'escape',\n  toAttributesCallPath: 'toAttributes',\n})\n\nconst buffer = new EdgeBuffer(filename, {\n  outputVar: 'out',\n  rethrowCallPath: 'reThrow'\n})\n\nparser\n  .tokenize('Hello {{ username }}', { filename })\n  .forEach((token) =\u003e parser.processToken(token, buffer))\n\nconst output = buffer.flush()\nconsole.log(output)\n```\n\n- `filename` is required to ensure that exceptions stack traces point back to the correct filename.\n- `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.\n- `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.\n- `toAttributesCallPath`: Reference to the function that will convert an object to HTML attributes.\n- `outputVar` is the variable name that holds the output of the compiled template.\n- `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.\n\n**Compiled output**\n\n```js\nlet out = ''\nlet $lineNumber = 1\nlet $filename = 'eval.edge'\ntry {\n  out += 'Hello '\n  out += `${escape(state.username)}`\n} catch (error) {\n  reThrow(error, $filename, $lineNumber)\n}\nreturn out\n```\n\nYou can wrap the compiled output inside a function and invoke it as follows\n\n```ts\n/**\n * Convert string to a function\n */\nconst fn = new Function('state, escape, reThrow', output)\n\n/**\n * Template state\n */\nconst state = { username: 'virk' }\n\n/**\n * Escape function\n */\nfunction escape(value: any) {\n  return value\n}\n\n/**\n * Rethrow function\n */\nfunction reThrow(error: Error) {\n  throw error\n}\n\nconsole.log(fn(state, escape, reThrow))\n```\n\n## Parser API\n\nAlong with parsing the main template, the parser also exposes the API, that tags can use to selectively parse the content of a tag.\n\n#### generateAST(jsExpression, lexerLoc, filename)\n\nParses a string as a Javascript expression. The output is a valid [Estree expression](https://github.com/estree/estree)\n\nThe following example returns a [BinaryExpression](https://astexplorer.net/#/gist/0b6250a81804270a026fe39e3bc33fb6/latest)\n\n```ts\nconst loc = {\n  start: { line: 1, col: 1 },\n  end: { line: 1, col: 1 },\n}\nconst filename = 'eval.edge'\n\nparser.utils.generateAST('2 + 2', loc, filename)\n```\n\n#### transformAst(acornAst, filename)\n\nTransform the acorn AST and make it compatible with Edge runtime. This method mutates the inner nodes of the original AST.\n\n```ts\nconst loc = {\n  start: { line: 1, col: 1 },\n  end: { line: 1, col: 1 },\n}\nconst filename = 'eval.edge'\n\nparser.utils.transformAst(parser.utils.generateAST('2 + 2', loc, filename), filename)\n```\n\n#### tokenize (template, options: { filename })\n\nReturns 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.\n\n```ts\nconst tokens = parser.tokenize('Hello {{ username }}', {\n  filename: 'eval.edge',\n})\n```\n\n**Output**\n\n```json\n[\n  {\n    \"type\": \"raw\",\n    \"line\": 1,\n    \"value\": \"Hello \"\n  },\n  {\n    \"type\": \"mustache\",\n    \"filename\": \"eval.edge\",\n    \"loc\": {\n      \"start\": {\n        \"line\": 1,\n        \"col\": 8\n      },\n      \"end\": {\n        \"line\": 1,\n        \"col\": 20\n      }\n    },\n    \"properties\": {\n      \"jsArg\": \" username \"\n    }\n  }\n]\n```\n\n#### stringify(expression)\n\nConvert 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.\n\n```ts\nconst expression = parser.utils.generateAST(\n  '2 + 2',\n  {\n    start: { line: 1, col: 1 },\n    end: { line: 1, col: 1 },\n  },\n  'eval.edge'\n)\n\nexpression.left.value = 3\nparser.utils.stringify(expression) // returns 3 + 2\n```\n\n#### processToken(token, buffer)\n\nYou will often find yourself using this method as a tag author, when you want to recursively process all children of your tag\n\n```ts\nconst byPass = {\n  block: true,\n  seekable: false,\n  name: 'bypass',\n\n  compile(parser, buffer, token) {\n    token.children.forEach((child) =\u003e parser.processToken(child, buffer))\n  },\n}\n```\n\nand then use it as\n\n```edge\n@bypass\n  Hello {{ username }}\n@endbypass\n```\n\n## Supported Expressions\n\nThe following expressions are supported by the parser. Can you also access the list of supported expressions as\n\n```js\nimport { expressions } from 'edge-parser'\n```\n\n#### Identifier\n\nThe identifier are prefixed with `state.` In following statement `username` is the identifier\n\n```\nHello {{ username }}\n```\n\n#### Literal\n\nA string literal\n\n```\nHello {{ 'Guest' }}\n```\n\n#### ArrayExpression\n\nThe `[1, 2, 3, 4]` is an array expression.\n\n```\nEvens are {{\n  [1, 2, 3, 4].filter((num) =\u003e num % 2 === 0)\n}}\n```\n\n#### ObjectExpression\n\nThe `{ username: 'virk' }` is an Object expression\n\n```\n{{ toJSON({ username: 'virk' })  }}\n```\n\n#### UnaryExpression\n\nFollowing are examples of `UnaryExpression`.\n\n```\n{{ typeof(username) }}\n\n{{ !!username }}\n```\n\n#### BinaryExpression\n\nHere `{{ 2 + 2 }}` is the binary expression\n\n```\n{{ 2 + 2 }} = 4\n```\n\n#### LogicalExpression\n\nFollowing is the example of `LogicalExpression`.\n\n```\n{{ username || admin.username }}\n```\n\n#### MemberExpression\n\n```\n{{ username.toUpperCase() }}\n```\n\n#### ConditionalExpression\n\n```\n{{ username ? username : 'Guest' }}\n```\n\n#### CallExpression\n\n```\n{{ upper(username) }}\n```\n\n#### SequenceExpression\n\nSequence is not supported in mustache blocks and instead used inside tags. For example:\n\nEverything inside `()` is a sequence expression.\n\n```\n@component('button', text = 'Submit', type = 'Primary')\n```\n\n#### TemplateLiteral\n\n```\n{{ Hello `${username}` }}\n```\n\n#### ArrowFunctionExpression\n\n```\n{{\n  users.map((user) =\u003e {\n    return user.username\n  })\n}}\n```\n\n#### AwaitExpression\n\n```\n{{ await foo() }}\n```\n\n#### FunctionDeclaration\n\n```\n{{ function foo () {} }}\n```\n\n#### BlockStatement\n\nHere the `map` callback is the block statement\n\n```\n{{\n  users.map(() =\u003e {})\n}}\n```\n\n#### ChainExpression\n\nSupport for optional chaining\n\n```\n{{ user?.username }}\n```\n\n#### NewExpression\n\n```js\n{{ new User() }}\n```\n\n#### ReturnStatement\nIn the following example `return` keyword is a return statement\n\n```js\nusers.map((user) =\u003e {\n  return user.username\n})\n```\n\n#### ThisExpression\nSupport for the this keyword\n\n```js\n{{ this.state }}\n```\n\n#### SpreadElement\nSupport for the spread element\n\n```js\n{{ [...users] }}\n```\n\n[gh-workflow-image]: https://img.shields.io/github/actions/workflow/status/edge-js/parser/checks.yml?style=for-the-badge\n[gh-workflow-url]: https://github.com/edge-js/parser/actions/workflows/checks.yml \"Github action\"\n\n[npm-image]: https://img.shields.io/npm/v/edge-parser.svg?style=for-the-badge\u0026logo=npm\n[npm-url]: https://npmjs.org/package/edge-parser 'npm'\n\n[typescript-image]: https://img.shields.io/badge/Typescript-294E80.svg?style=for-the-badge\u0026logo=typescript\n\n[license-url]: LICENSE.md\n[license-image]: https://img.shields.io/github/license/edge-js/lexer?style=for-the-badge\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fedge-js%2Fparser","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fedge-js%2Fparser","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fedge-js%2Fparser/lists"}