[
  {
    "path": ".github/workflows/create-release.yml",
    "content": "name: Create release\n\non:\n  push:\n    tags:\n      - 'v*'\n\njobs:\n  create-release:\n    name: Create GitHub Release\n    runs-on: ubuntu-latest\n\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v2\n\n      - name: Get semver number\n        id: get_semver\n        env:\n          TAG_NAME: ${{ github.ref }}\n        run: echo \"::set-output name=num::${TAG_NAME:11}\"\n\n      - name: Create release on GitHub API\n        id: create_release\n        uses: actions/create-release@v1\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n        with:\n          tag_name: ${{ github.ref }}\n          release_name: \"v${{ steps.get_semver.outputs.num }}\"\n          body: |\n            Version ${{ steps.get_semver.outputs.num }}\n          draft: false\n          prerelease: false\n"
  },
  {
    "path": ".gitignore",
    "content": "# If you add anything here, consider also adding to .npmignore\nv8.log\n*.swp\n*.swo\nauth_info.js\ndist\n.idea/\ntags\nnbproject/\nspec/browser/autogen_suite.js\nnode_modules\ntmtags\n*.DS_Store\nexamples/*/log/*\nsite/log/*\n.log\nnpm-debug.log\ndoc/\ntest/tmp\ncoverage/\n/ejs.js\n/ejs.min.js\nout/\npkg/\n/package-lock.json\n"
  },
  {
    "path": ".npmignore",
    "content": "test/\n\n# Copied from .gitignore\nv8.log\n*.swp\n*.swo\nauth_info.js\ndist\n.idea/\ntags\nnbproject/\nspec/browser/autogen_suite.js\ntmtags\n*.DS_Store\nexamples/*/log/*\nsite/log/*\n.log\nnpm-debug.log\ndoc/\ncoverage/\nout/\npkg/\n"
  },
  {
    "path": ".travis.yml",
    "content": "language: node_js\nsudo: false\nnode_js:\n  - \"8\"\n  - \"10\"\n  - \"12\"\n"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "content": "# Contributor Code of Conduct\n\nAs contributors and maintainers of this project, and in the interest of\nfostering an open and welcoming community, we pledge to respect all people who\ncontribute through reporting issues, posting feature requests, updating\ndocumentation, submitting pull requests or patches, and other activities.\n\nWe are committed to making participation in this project a harassment-free\nexperience for everyone, regardless of level of experience, gender, gender\nidentity and expression, sexual orientation, disability, personal appearance,\nbody size, race, ethnicity, age, religion, or nationality.\n\nExamples of unacceptable behavior by participants include:\n\n* The use of sexualized language or imagery\n* Personal attacks\n* Trolling or insulting/derogatory comments\n* Public or private harassment\n* Publishing other's private information, such as physical or electronic\n  addresses, without explicit permission\n* Other unethical or unprofessional conduct\n\nProject maintainers have the right and responsibility to remove, edit, or\nreject comments, commits, code, wiki edits, issues, and other contributions\nthat are not aligned to this Code of Conduct, or to ban temporarily or\npermanently any contributor for other behaviors that they deem inappropriate,\nthreatening, offensive, or harmful.\n\nBy adopting this Code of Conduct, project maintainers commit themselves to\nfairly and consistently applying these principles to every aspect of managing\nthis project. Project maintainers who do not follow or enforce the Code of\nConduct may be permanently removed from the project team.\n\nThis Code of Conduct applies both within project spaces and in public spaces\nwhen an individual is representing the project or its community.\n\nInstances of abusive, harassing, or otherwise unacceptable behavior may be\nreported by contacting a project maintainer at mde@fleegix.org. All\ncomplaints will be reviewed and investigated and will result in a response that\nis deemed necessary and appropriate to the circumstances. Maintainers are\nobligated to maintain confidentiality with regard to the reporter of an\nincident.\n\n\nThis Code of Conduct is adapted from the [Contributor Covenant][homepage],\nversion 1.3.0, available at\n[http://contributor-covenant.org/version/1/3/0/][version]\n\n[homepage]: http://contributor-covenant.org\n[version]: http://contributor-covenant.org/version/1/3/0/\n"
  },
  {
    "path": "LICENSE",
    "content": "\n                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright [yyyy] [name of copyright owner]\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "README.md",
    "content": "Embedded JavaScript templates<br/>\n[![Known Vulnerabilities](https://snyk.io/test/npm/ejs/badge.svg?style=flat)](https://snyk.io/test/npm/ejs)\n=============================\n\n## Security\n\nSecurity professionals, before reporting any security issues, please reference the\n<a href=\"https://github.com/mde/ejs/blob/main/SECURITY.md\">SECURITY.md</a>\nin this project, in particular, the following: \"EJS is effectively a JavaScript runtime.\nIts entire job is to execute JavaScript. If you run the EJS render method without\nchecking the inputs yourself, you are responsible for the results.\"\n\nIn short, DO NOT submit 'vulnerabilities' that include this snippet of code:\n\n```javascript\napp.get('/', (req, res) => {\n  res.render('index', req.query);\n});\n```\n\n## Installation\n\n```bash\n$ npm install ejs\n```\n\n### Import or require\n\nSupports both CommonJS and ES Modules.\n\n```javascript\nimport ejs from 'ejs';\n// Or\nconst ejs = require('ejs');\n```\n\n### Compatibility\n\nServer: CommonJS approach (`require`) supports Node versions at least\nback to v0.12, likely older versions too. ES Modules approach (`import`)\nrequires a Node version that supports ESM.\n\nCLI: Requires Node v8 or newer.\n\nBrowser: EJS supports all modern browsers, but is very likely to work even\nin very, very old browsers. Your mileage may vary.\n\n## Features\n\n  * Control flow with `<% %>`\n  * Escaped output with `<%= %>` (escape function configurable)\n  * Unescaped raw output with `<%- %>`\n  * Newline-trim mode ('newline slurping') with `-%>` ending tag\n  * Whitespace-trim mode (slurp all whitespace) for control flow with `<%_ _%>`\n  * Custom delimiters (e.g. `[? ?]` instead of `<% %>`)\n  * Includes\n  * Client-side support\n  * Static caching of intermediate JavaScript\n  * Static caching of templates\n  * Complies with the [Express](http://expressjs.com) view system\n\n## Example\n\n```ejs\n<% if (user) { %>\n  <h2><%= user.name %></h2>\n<% } %>\n```\n\n## Basic usage\n\n```javascript\nconst template = ejs.compile(str, options);\ntemplate(data);\n// => Rendered HTML string\n\nejs.render(str, data, options);\n// => Rendered HTML string\n\nejs.renderFile(filename, data, options, function(err, str){\n    // str => Rendered HTML string\n});\n```\n\nIt is also possible to use `ejs.render(dataAndOptions);` where you pass\neverything in a single object. In that case, you'll end up with local variables\nfor all the passed options. However, be aware that your code could break if we\nadd an option with the same name as one of your data object's properties.\nTherefore, we do not recommend using this shortcut.\n\n### Important\nYou should never give end-users unfettered access to the EJS render method, If you do so you are using EJS in an inherently un-secure way.\n\n### Options\n\n  - `cache`                 Compiled functions are cached, requires `filename`\n  - `filename`              The name of the file being rendered. Not required if you\n    are using `renderFile()`. Used by `cache` to key caches, and for includes.\n  - `root`                  Set template root(s) for includes with an absolute path (e.g, /file.ejs).\n    Can be array to try to resolve include from multiple directories.\n  - `views`                 An array of paths to use when resolving includes with relative paths.\n  - `context`               Function execution context\n  - `compileDebug`          When `false` no debug instrumentation is compiled\n  - `delimiter`             Character to use for inner delimiter, by default '%'\n  - `openDelimiter`         Character to use for opening delimiter, by default '<'\n  - `closeDelimiter`        Character to use for closing delimiter, by default '>'\n  - `debug`                 Outputs generated function body\n  - `strict`                When set to `true`, generated function is in strict mode\n  - `_with`                 Whether or not to use `with() {}` constructs. If `false`\n    then the locals will be stored in the `locals` object. Set to `false` in strict mode.\n  - `destructuredLocals`    An array of local variables that are always destructured from\n    the locals object, available even in strict mode.\n  - `localsName`            Name to use for the object storing local variables when not using\n    `with` Defaults to `locals`\n  - `rmWhitespace`          Remove all safe-to-remove whitespace, including leading\n    and trailing whitespace. It also enables a safer version of `-%>` line\n    slurping for all scriptlet tags (it does not strip new lines of tags in\n    the middle of a line).\n  - `escape`                The escaping function used with `<%=` construct.\n    (By default escapes XML).\n  - `outputFunctionName`    Set to a string (e.g., 'echo' or 'print') for a function to print\n    output inside scriptlet tags.\n  - `async`                 When `true`, EJS will use an async function for rendering. (Depends\n    on async/await support in the JS runtime).\n  - `includer`              Custom function to handle EJS includes, receives `(originalPath, parsedPath)`\n    parameters, where `originalPath` is the path in include as-is and `parsedPath` is the\n    previously resolved path. Should return an object `{ filename, template }`,\n    you may return only one of the properties, where `filename` is the final parsed path and `template`\n    is the included content.\n\nThis project uses [JSDoc](https://jsdoc.app/). For the full public API\ndocumentation, clone the repository and run `jake doc`. This will run JSDoc\nwith the proper options and output the documentation to `out/`. If you want\nthe both the public & private API docs, run `jake devdoc` instead.\n\n### Tags\n\n  - `<%`              'Scriptlet' tag, for control-flow, no output\n  - `<%_`             'Whitespace Slurping' Scriptlet tag, strips all whitespace before it\n  - `<%=`             Outputs the value into the template (escaped)\n  - `<%-`             Outputs the unescaped value into the template\n  - `<%#`             Comment tag, no execution, no output\n  - `<%%`             Outputs a literal '<%'\n  - `%%>`             Outputs a literal '%>'\n  - `%>`              Plain ending tag\n  - `-%>`             Trim-mode ('newline slurp') tag, trims following newline\n  - `_%>`             'Whitespace Slurping' ending tag, removes all whitespace after it\n\nFor the full syntax documentation, please see [docs/syntax.md](https://github.com/mde/ejs/blob/master/docs/syntax.md).\n\n### Includes\n\nIncludes either have to be an absolute path, or, if not, are assumed as\nrelative to the template with the `include` call. For example if you are\nincluding `./views/user/show.ejs` from `./views/users.ejs` you would\nuse `<%- include('user/show') %>`.\n\nYou must specify the `filename` option for the template with the `include`\ncall unless you are using `renderFile()`.\n\nYou'll likely want to use the raw output tag (`<%-`) with your include to avoid\ndouble-escaping the HTML output.\n\n```ejs\n<ul>\n  <% users.forEach(function(user){ %>\n    <%- include('user/show', {user: user}) %>\n  <% }); %>\n</ul>\n```\n\nIncludes are inserted at runtime, so you can use variables for the path in the\n`include` call (for example `<%- include(somePath) %>`). Variables in your\ntop-level data object are available to all your includes, but local variables\nneed to be passed down.\n\nNOTE: Include preprocessor directives (`<% include user/show %>`) are\nnot supported in v3.0+.\n\n## Custom delimiters\n\nCustom delimiters can be applied on a per-template basis, or globally:\n\n```javascript\nimport ejs from 'ejs';\nconst users = ['geddy', 'neil', 'alex'];\n\n// Just one template\nejs.render('<p>[?= users.join(\" | \"); ?]</p>', {users: users}, {delimiter: '?', openDelimiter: '[', closeDelimiter: ']'});\n// => '<p>geddy | neil | alex</p>'\n\n// Or globally\nejs.delimiter = '?';\nejs.openDelimiter = '[';\nejs.closeDelimiter = ']';\nejs.render('<p>[?= users.join(\" | \"); ?]</p>', {users: users});\n// => '<p>geddy | neil | alex</p>'\n```\n\n### Caching\n\nEJS ships with a basic in-process cache for caching the intermediate JavaScript\nfunctions used to render templates. It's easy to plug in LRU caching using\nNode's `lru-cache` library:\n\n```javascript\nimport ejs from 'ejs';\nimport { LRUCache } from 'lru-cache';\n\nejs.cache = LRUCache({max: 100}); // LRU cache with 100-item limit\n```\n\nIf you want to clear the EJS cache, call `ejs.clearCache`. If you're using the\nLRU cache and need a different limit, simple reset `ejs.cache` to a new instance\nof the LRU.\n\n### Custom file loader\n\nThe default file loader is `fs.readFileSync`, if you want to customize it, you can set ejs.fileLoader.\n\n```javascript\nimport ejs from 'ejs';\n\nconst myFileLoad = function (filePath) {\n  return 'myFileLoad: ' + fs.readFileSync(filePath);\n};\n\nejs.fileLoader = myFileLoad;\n```\n\nWith this feature, you can preprocess the template before reading it.\n\n### Layouts\n\nEJS does not specifically support blocks, but layouts can be implemented by\nincluding headers and footers, like so:\n\n```ejs\n<%- include('header') -%>\n<h1>\n  Title\n</h1>\n<p>\n  My page\n</p>\n<%- include('footer') -%>\n```\n\n## Client-side support\n\nGo to the [Latest Release](https://github.com/mde/ejs/releases/latest), download\n`./ejs.js` or `./ejs.min.js`. Alternately, you can compile it yourself by cloning\nthe repository and running `jake build` (or `npx jake build` if jake is\nnot installed globally).\n\nInclude one of these files on your page, and `ejs` should be available globally.\n\n### Example\n\n```html\n<div id=\"output\"></div>\n<script src=\"ejs.min.js\"></script>\n<script>\n  let people = ['geddy', 'neil', 'alex'],\n      html = ejs.render('<%= people.join(\", \"); %>', {people: people});\n  // With jQuery:\n  $('#output').html(html);\n  // Vanilla JS:\n  document.getElementById('output').innerHTML = html;\n</script>\n```\n\n### Caveats\n\nMost of EJS will work as expected; however, there are a few things to note:\n\n1. Obviously, since you do not have access to the filesystem, `ejs.renderFile()` won't work.\n2. For the same reason, `include`s do not work unless you use an `include callback`. Here is an example:\n  ```javascript\n  let str = \"Hello <%= include('file', {person: 'John'}); %>\",\n      fn = ejs.compile(str);\n\n  fn(data, null, function(path, d){ // include callback\n    // path -> 'file'\n    // d -> {person: 'John'}\n    // Put your code here\n    // Return the contents of file as a string\n  }); // returns rendered string\n  ```\n\nSee the [examples folder](https://github.com/mde/ejs/tree/master/examples) for more details.\n\n## CLI\n\nEJS ships with a full-featured CLI. Options are similar to those used in JavaScript code:\n\n  - `-o / --output-file FILE`            Write the rendered output to FILE rather than stdout.\n  - `-f / --data-file FILE`              Must be JSON-formatted. Use parsed input from FILE as data for rendering.\n  - `-i / --data-input STRING`           Must be JSON-formatted and URI-encoded. Use parsed input from STRING as data for rendering.\n  - `-m / --delimiter CHARACTER`         Use CHARACTER with angle brackets for open/close (defaults to %).\n  - `-p / --open-delimiter CHARACTER`    Use CHARACTER instead of left angle bracket to open.\n  - `-c / --close-delimiter CHARACTER`   Use CHARACTER instead of right angle bracket to close.\n  - `-s / --strict`                      When set to `true`, generated function is in strict mode\n  - `-n / --no-with`                     Use 'locals' object for vars rather than using `with` (implies --strict).\n  - `-l / --locals-name`                 Name to use for the object storing local variables when not using `with`.\n  - `-w / --rm-whitespace`               Remove all safe-to-remove whitespace, including leading and trailing whitespace.\n  - `-d / --debug`                       Outputs generated function body\n  - `-h / --help`                        Display this help message.\n  - `-V/v / --version`                   Display the EJS version.\n\nHere are some examples of usage:\n\n```shell\n$ ejs -p [ -c ] ./template_file.ejs -o ./output.html\n$ ejs ./test/fixtures/user.ejs name=Lerxst\n$ ejs -n -l _ ./some_template.ejs -f ./data_file.json\n```\n\n### Data input\n\nThere is a variety of ways to pass the CLI data for rendering.\n\nStdin:\n\n```shell\n$ ./test/fixtures/user_data.json | ejs ./test/fixtures/user.ejs\n$ ejs ./test/fixtures/user.ejs < test/fixtures/user_data.json\n```\n\nA data file:\n\n```shell\n$ ejs ./test/fixtures/user.ejs -f ./user_data.json\n```\n\nA command-line option (must be URI-encoded):\n\n```shell\n./bin/cli.js -i %7B%22name%22%3A%20%22foo%22%7D ./test/fixtures/user.ejs\n```\n\nOr, passing values directly at the end of the invocation:\n\n```shell\n./bin/cli.js -m $ ./test/fixtures/user.ejs name=foo\n```\n\n### Output\n\nThe CLI by default send output to stdout, but you can use the `-o` or `--output-file`\nflag to specify a target file to send the output to.\n\n## IDE Integration with Syntax Highlighting\n\nVSCode:Javascript EJS by *DigitalBrainstem*\n\n## Related projects\n\nThere are a number of implementations of EJS:\n\n * TJ's implementation, the v1 of this library: https://github.com/tj/ejs\n * EJS Embedded JavaScript Framework on Google Code: https://code.google.com/p/embeddedjavascript/\n * Sam Stephenson's Ruby implementation: https://rubygems.org/gems/ejs\n * Erubis, an ERB implementation which also runs JavaScript: http://www.kuwata-lab.com/erubis/users-guide.04.html#lang-javascript\n * DigitalBrainstem EJS Language support: https://github.com/Digitalbrainstem/ejs-grammar\n\n## License\n\nLicensed under the Apache License, Version 2.0\n(<http://www.apache.org/licenses/LICENSE-2.0>)\n\n- - -\nEJS Embedded JavaScript templates copyright 2112\nmde@fleegix.org.\n"
  },
  {
    "path": "RELEASE_NOTES_v4.md",
    "content": "# EJS Version 4.0.1 Release Notes\n\n## Overview\nEJS version 4.0.1 represents a major release with significant architectural improvements,\nenhanced module support, and improved compatibility. The CommonJS build is now compiled\nusing the TypeScript compiler, ensuring better code quality, maintainability, and\nbackward compatibility.\n\n## Major Changes\n\n### Module System Overhaul\n- **Dual module support**: Added support for both CommonJS (`lib/cjs/ejs.js`) and ES\n  Modules (`lib/esm/ejs.js`)\n- **Package exports**: Implemented proper `exports` field in package.json for better\n  module resolution\n- **Code generation improvements**: Replaced `let` in code-generation strings for\n  CommonJS compatibility\n- **Namespace Node builtins**: Improved isolation and compatibility by namespacing\n  Node.js built-in modules\n\n### Compatibility\n- **Extended Node.js support**: Maintained compatibility with Node.js versions back to\n  0.12.18\n- **Cleaner keyword replacement**: Improved handling of JavaScript keywords in\n  templates\n\n### Build System\n- **Compilation task**: Added new compile task with updated linting configuration\n- **Build improvements**: Enhanced build process to run before tests\n- **Test infrastructure**: Added `testOnly` task for running tests without building\n- **Version string**: Version string is now baked in during packaging process\n\n### Documentation\n- **JSDoc updates**: Complete JSDoc overhaul with updated paths and references\n- **Documentation fixes**:\n  - Fixed missing closing parenthesis in async option description (#766)\n  - Updated JSDoc reference from usejsdoc.org to jsdoc.app (#778)\n- **Removed outdated docs**: Cleaned up old documentation files\n\n### Dependencies\n- **Development dependencies**: Updated various dev dependencies including ESLint,\n  TypeScript, and build tools\n- **Removed lockfiles**: Removed package-lock.json from repository\n\n### Code Quality\n- **Linting**: Updated ESLint configuration for better code quality\n- **Code cleanup**: Removed unused imports and cleaned up codebase\n- **Test fixes**: Fixed failing tests to ensure stability\n\n## Breaking Changes\n\n### Package Structure\n- **Main entry point**: Changed from `./lib/ejs.js` to `./lib/cjs/ejs.js`\n- **Module entry**: New `module` field points to `./lib/esm/ejs.js`\n- **Exports field**: New `exports` field defines import/require paths\n\n## Contributors\n- mde (Matthew Eernisse)\n- Adnan Tahir (#766)\n- Thomas Skardal (#778)\n\n## Migration Guide\n\nIf you're upgrading from version 3.x:\n\n1. **ES Modules**: Standard ESM imports continue to work as before. The `exports` field\n   automatically routes imports to the correct module:\n   ```javascript\n   // Works in Node.js, Deno, and other ESM environments\n   import ejs from 'ejs';\n   ```\n\n   **Deno users**: EJS is now importable via npm specifier:\n   ```javascript\n   import ejs from 'npm:ejs';\n   ```\n\n2. **CommonJS**: CommonJS usage continues to work as before. The `exports` field\n   automatically routes `require()` calls to the CommonJS build:\n   ```javascript\n   const ejs = require('ejs');\n   ```\n\n3. **No code changes required**: The new package structure is transparent to users\n   thanks to the `exports` field. Your existing code should work without modifications.\n\n## Installation\n\n```bash\nnpm install ejs@4.0.1\n```\n\n---\n\n*Generated from git log: v3.1.10..v4.0.1*\n"
  },
  {
    "path": "RELEASE_NOTES_v5.md",
    "content": "# EJS Version 5.0.1 Release Notes\n\n## Overview\nEJS version 5.0.1 is a major release that removes deprecated options, fixes template\nbehavior with custom delimiters, improves the CLI and build pipeline, and simplifies\nthe package by moving Jake to a dev-only dependency.\n\n## Major Changes\n\n### Deprecated Option Removed\n- **Removed `client` option** (Fixes #746): The legacy `client` flag and related\n  code have been removed. This option produced browser-oriented template functions\n  by inlining escape and rethrow helpers; it was unmaintained and broken. Use the\n  standard browser bundle (`ejs.min.js`) or compile templates for the client using\n  your own build setup.\n\n### Bug Fixes\n- **Custom delimiters and whitespace-slurp tags** (Fixed #780): Whitespace-slurp\n  tags (`<%_` and `_%>` by default) now respect custom `openDelimiter`, `delimiter`,\n  and `closeDelimiter`. Previously, the slurp regex was hardcoded to `<%`/`%>`,\n  so custom delimiters did not work correctly with `<%_`/`_%>`-style tags.\n\n### CLI & Build\n- **CLI no longer depends on Jake**: The `ejs` CLI now uses a bundled argument\n  parser (`lib/esm/parseargs.js` / `lib/cjs/parseargs.js`) instead of the Jake\n  program module. Jake remains a devDependency for the build (lint, compile,\n  browserify, minify, test).\n- **Jake moved to devDependencies**: Jake was moved from `dependencies` to\n  `devDependencies`, so installing `ejs` as a dependency no longer pulls in Jake.\n- **Minification fix**: The minify task now minifies the browserified `ejs.js`\n  bundle (output of the browserify task) instead of `lib/cjs/ejs.js`, so the\n  browser bundle is correctly minified.\n\n### Documentation & Examples\n- **JSDoc updates**: Removed references to the `client` option and `ClientFunction`\n  from options and template-function documentation.\n- **Examples**: `examples/client-compilation.html` and `examples/express/app.js`\n  updated to remove use of the `client` option; Express example no longer passes\n  `client: true`.\n- **README**: Removed broken link to the third-party EJS playground.\n\n### Code Quality\n- **Tests**: Removed tests that targeted the removed `client` option behavior.\n- **Utils**: Removed unused `client`-related code from `lib/esm/utils.js`.\n\n## Breaking Changes\n\n### Removed `client` Option\n- **Option removed**: The `client` option is no longer supported. Passing\n  `client: true` (or any value) is ignored; no error is thrown, but no\n  client-specific output is produced.\n- **Migration**: Rely on the standard API and the browser build (`ejs.min.js`) for\n  browser use, or compile templates in your own build pipeline if you need\n  client-side rendering with custom setup.\n\n## Contributors\n- mde (Matthew Eernisse)\n\n## Migration Guide\n\nIf you're upgrading from version 4.x:\n\n1. **If you used the `client` option**: Remove `client: true` (or similar) from\n   your options. Use `ejs.render()` or `ejs.renderFile()` as usual; for browsers,\n   load `ejs.min.js` or bundle the CJS/ESM build. If you depended on the old\n   client output format, you will need to implement your own client compilation\n   or use a different approach.\n\n2. **If you use custom delimiters with whitespace-slurp tags**: Upgrading fixes\n   behavior so that tags like `<%_` and `_%>` are correctly recognized when\n   `openDelimiter`, `delimiter`, or `closeDelimiter` are set. No code changes\n   required.\n\n3. **Install size**: Installing `ejs` as a dependency no longer installs Jake,\n   which may slightly reduce install size and dependency tree depth.\n\n## Installation\n\n```bash\nnpm install ejs@5.0.1\n```\n\n---\n\n*Generated from git log: v4.0.1..v5.0.1*\n"
  },
  {
    "path": "SECURITY.md",
    "content": "# Security Policy\nThis document outlines security procedures and general policies for the EJS template engine project\n\n## Supported Versions\n\nThe current supported version.\n\n| Version | Supported          |\n| ------- | ------------------ |\n| 4.x.x   | :white_check_mark: |\n\n## Reporting a Vulnerability\nThe EJS team and community take all security bugs in EJS seriously. \nWe appreciate your efforts and responsible disclosure and will make every effort to acknowledge your contributions.\n\nReport security bugs by emailing the lead maintainer in the Readme.md file.\nTo ensure the timely response to your report, please ensure that the entirety of the report is contained within the email body and not solely behind a web link or an attachment.\n\nThe EJS team will then evaluate your report and will reply with the next steps in handling your report and may ask for additional information or guidance.\n\n## Out-of-Scope Vulnerabilities\nIf you give end-users unfettered access to the EJS render method, you are using EJS in an inherently un-secure way. Please do not report security issues that stem from doing that.\n\nEJS is effectively a JavaScript runtime. Its entire job is to execute JavaScript. If you run the EJS render method without checking the inputs yourself, you are responsible for the results.\n\nIn short, DO NOT send reports including this snippet of code:\n\n```javascript\nconst express = require('express');\nconst app = express();\nconst PORT = 3000;\napp.set('views', __dirname);\napp.set('view engine', 'ejs');\n\napp.get('/', (req, res) => {\n    res.render('index', req.query);\n});\n\napp.listen(PORT, ()=> {\n    console.log(`Server is running on ${PORT}`);\n});\n```\n"
  },
  {
    "path": "benchmark/bench-ejs.js",
    "content": "'use strict';\n\nvar ejs = require('..');\nvar path = require('path');\n\nejs.fileLoader = function(n) { return files[path.basename(n, '.ejs')]; };\n\nvar loops = 10000;\nvar runs = 9;  // min 4 for median\n\nvar runCompile = false;\nvar runNoCache = false;\nvar runCache = false;\n\nvar i = 1;\nwhile (i < process.argv.length) {\n  var a = process.argv[i];\n  i++;\n  var b;\n  if (i < process.argv.length) b = process.argv[i];\n  switch (a) {\n  case '-r': if(b) runs = b;\n    i++;\n    break;\n  case '-l': if(b) loops = b;\n    i++;\n    break;\n  case '--compile':\n    runCompile = true;\n    break;\n  case '--nocache':\n    runNoCache = true;\n    break;\n  case '--cache':\n    runCache = true;\n    break;\n  }\n}\n\nif (! (runCompile || runNoCache || runCache)) {\n  runCompile = true;\n  runNoCache = true;\n  runCache = true;\n}\n\nvar files = {\n  bench1: `<h1><$= name $></h1>\n    <div><%- num+1 -%></div>\n    <% if(num > 10) { %>\n      <$= cheese $>\n    <% } %>\n    <%# comment #%>\n    <%% literal <$= name $> %%>\n  `,\n\n  bench2: `<h1><$= name $></h1>\n    <div><%- num+1 -%></div>\n    <% if(num > 10) { %>\n      <$= cheese $>\n    <% } %>\n    <%# comment #%>\n    <%% literal <$= name $> %%>\n  `.repeat(100),\n\n  simple1: `<h1><$= name $></h1>\n    <div><%- num+1 -%></div>\n  `,\n\n  locals1: `<h1><$= locals.name $></h1>\n    <div><%- locals.num+1 -%></div>\n    <% if(locals.num > 10) { %>\n      <$= locals.cheese $>\n    <% } %>\n    <%# comment #%>\n    <%% literal <$= locals.name $> %%>\n  `.repeat(10),\n\n  include1: `<h1><$= name $></h1>\n    <div><% include('/simple1') %></div>\n    <div><% include('/simple1') %></div>\n    <div><% include('/simple1') %></div>\n  `,\n\n  include2: `<h1><$= name $></h1>\n    <div><% include /include1 %></div>\n    <div><% include /simple1 %></div>\n  `,\n};\n\nvar data = {\n  name: 'foo',\n  num: 42,\n  cheese: 'out of',\n};\n\nvar sp = '                                                            ';\nfunction fill(s, l) { s=String(s); return s + sp.slice(0,l-s.length); }\nfunction fillR(s, l) { s=String(s); return sp.slice(0,l-s.length)+s; }\n\nfunction log(name, runTimes, totalLoops) {\n  runTimes = runTimes.sort(function(a,b) { return a-b; });\n  var m  = Math.trunc(runs/2);\n  var m2 = (runs % 2 == 0) ? m-1 : m;\n  var med1 = Math.round((runTimes[m]+runTimes[m2])/2);\n  var med2;\n  if (runs % 2 == 0)\n    med2 = Math.round((runTimes[m2-1]+runTimes[m2]+runTimes[m]+runTimes[m+1])/4);\n  else\n    med2 = Math.round((runTimes[m-1]+runTimes[m]+runTimes[m+1])/3);\n  var avg = Math.round(runTimes.reduce(function(a,b) {return a+b;}) / runTimes.length);\n  console.log(fill(name +': ',30), fill(avg/1000,10), fill(med1/1000,10), fill(med2/1000,10), fill(runTimes[0]/1000,10), fill(runTimes[runTimes.length-1]/1000,10),fillR(totalLoops, 15));\n}\n\nfunction benchRender(name, file, data, opts, benchOpts) {\n  ejs.cache.reset();\n  var runTimes = [];\n  opts = opts || {};\n  benchOpts = benchOpts || {};\n  opts.filename = file;\n  var totalLoops = Math.round(loops * (benchOpts.loopFactor || 1));\n  var tmpl = files[file];\n  for (var r = 0; r < runs; r++) {\n    ejs.render(tmpl, data, opts); // one run in advance\n\n    var t = Date.now();\n    for (var i = 0; i < totalLoops; i++) {\n      ejs.render(tmpl, data, opts);\n    }\n    t = Date.now() - t;\n    runTimes.push(t);\n  }\n  log(name, runTimes, totalLoops);\n}\n\nfunction benchCompile(name, file, opts, benchOpts) {\n  ejs.cache.reset();\n  var runTimes = [];\n  opts = opts || {};\n  benchOpts = benchOpts || {};\n  opts.filename = file;\n  var totalLoops = Math.round(loops * (benchOpts.loopFactor || 1));\n  var tmpl = files[file];\n  for (var r = 0; r < runs; r++) {\n    ejs.compile(tmpl, opts); // one run in advance\n\n    var t = Date.now();\n    for (var i = 0; i < totalLoops; i++) {\n      ejs.compile(tmpl, opts);\n    }\n    t = Date.now() - t;\n    runTimes.push(t);\n  }\n  log(name, runTimes, totalLoops);\n}\n\nif (runCompile) {\n  console.log('Running avg accross: ', runs);\n  console.log(fill('name: ',30), fill('avg',10), fill('med',10), fill('med/avg',10), fill('min',10), fill('max',10), fillR('loops',15));\n\n  benchCompile('single tmpl compile',         'bench1', {compileDebug: false}, { loopFactor: 2 });\n  benchCompile('single tmpl compile (debug)', 'bench1', {compileDebug: true}, { loopFactor: 2 });\n\n  benchCompile('large tmpl compile',         'bench2', {compileDebug: false}, { loopFactor: 0.1 });\n\n  benchCompile('include-1 compile',  'include1', {compileDebug: false}, { loopFactor: 2 });\n  console.log('-');\n}\n\n\n\nif (runCache) {\n  benchRender('single tmpl cached',           'bench1', data, {cache:true, compileDebug: false}, { loopFactor: 5 });\n  benchRender('single tmpl cached (debug)',   'bench1', data, {cache:true, compileDebug: true}, { loopFactor: 5 });\n\n  benchRender('large tmpl cached',           'bench2', data, {cache:true, compileDebug: false}, { loopFactor: 0.4 });\n  benchRender('include-1 cached',    'include1', data, {cache:true, compileDebug: false}, { loopFactor: 2 });\n  benchRender('include-2 cached',    'include2', data, {cache:true, compileDebug: false}, { loopFactor: 2 });\n\n\n  benchRender('locals tmpl cached \"with\"',    'locals1', data, {cache:true, compileDebug: false, _with: true}, { loopFactor: 3 });\n  benchRender('locals tmpl cached NO-\"with\"', 'locals1', data, {cache:true, compileDebug: false, _with: false}, { loopFactor: 3 });\n\n  console.log('-');\n}\n\n\nif (runNoCache) {\n  benchRender('single tmpl NO-cache',         'bench1', data, {cache:false, compileDebug: false});\n  benchRender('single tmpl NO-cache (debug)', 'bench1', data, {cache:false, compileDebug: true});\n\n  benchRender('large tmpl NO-cache',         'bench2', data, {cache:false, compileDebug: false}, { loopFactor: 0.1 });\n\n  benchRender('include-1 NO-cache',  'include1', data, {cache:false, compileDebug: false});\n  benchRender('include-2 NO-cache',  'include2', data, {cache:false, compileDebug: false});\n}\n"
  },
  {
    "path": "bin/cli.js",
    "content": "#!/usr/bin/env node\n/*\n * EJS Embedded JavaScript templates\n * Copyright 2112 Matthew Eernisse (mde@fleegix.org)\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *         http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n*/\n\nlet path = require('path');\n\nlet { Parser } = require('../lib/cjs/parseargs');\n\nlet ejs = require('../lib/cjs/ejs');\nlet { hyphenToCamel } = require('../lib/cjs/utils');\nlet fs = require('fs');\nlet args = process.argv.slice(2);\nlet usage = fs.readFileSync(`${__dirname}/../usage.txt`).toString();\n\nfunction die(msg) {\n  console.log(msg);\n  process.stdout.write('', function () {\n    process.stderr.write('', function () {\n      process.exit();\n    });\n  });\n}\n\nconst CLI_OPTS = [\n  { full: 'output-file',\n    abbr: 'o',\n    expectValue: true,\n  },\n  { full: 'data-file',\n    abbr: 'f',\n    expectValue: true,\n  },\n  { full: 'data-input',\n    abbr: 'i',\n    expectValue: true,\n  },\n  { full: 'delimiter',\n    abbr: 'm',\n    expectValue: true,\n    passThrough: true,\n  },\n  { full: 'open-delimiter',\n    abbr: 'p',\n    expectValue: true,\n    passThrough: true,\n  },\n  { full: 'close-delimiter',\n    abbr: 'c',\n    expectValue: true,\n    passThrough: true,\n  },\n  { full: 'strict',\n    abbr: 's',\n    expectValue: false,\n    allowValue: false,\n    passThrough: true,\n  },\n  { full: 'no-with',\n    abbr: 'n',\n    expectValue: false,\n    allowValue: false,\n  },\n  { full: 'locals-name',\n    abbr: 'l',\n    expectValue: true,\n    passThrough: true,\n  },\n  { full: 'rm-whitespace',\n    abbr: 'w',\n    expectValue: false,\n    allowValue: false,\n    passThrough: true,\n  },\n  { full: 'debug',\n    abbr: 'd',\n    expectValue: false,\n    allowValue: false,\n    passThrough: true,\n  },\n  { full: 'help',\n    abbr: 'h',\n    passThrough: true,\n  },\n  { full: 'version',\n    abbr: 'V',\n    passThrough: true,\n  },\n  // Alias lowercase v\n  { full: 'version',\n    abbr: 'v',\n    passThrough: true,\n  },\n];\n\nlet preempts = {\n  version: function () {\n    die(ejs.VERSION);\n  },\n  help: function () {\n    die(usage);\n  }\n};\n\nlet stdin = '';\nprocess.stdin.setEncoding('utf8');\nprocess.stdin.on('readable', () => {\n  let chunk;\n  while ((chunk = process.stdin.read()) !== null) {\n    stdin += chunk;\n  }\n});\n\nfunction run() {\n\n  let parser = new Parser(CLI_OPTS);\n  let result = parser.parse(args);\n\n  let templatePath = result.taskNames[0];\n  let pVals = result.envVars;\n  let pOpts = {};\n\n  for (let p in result.opts) {\n    let name = hyphenToCamel(p);\n    pOpts[name] = result.opts[p];\n  }\n\n  let opts = {};\n  let vals = {};\n\n  // Same-named 'passthrough' opts\n  CLI_OPTS.forEach((opt) => {\n    let optName = hyphenToCamel(opt.full);\n    if (opt.passThrough && typeof pOpts[optName] != 'undefined') {\n      opts[optName] = pOpts[optName];\n    }\n  });\n\n  // Bail out for help/version\n  for (let p in opts) {\n    if (preempts[p]) {\n      return preempts[p]();\n    }\n  }\n\n  // Ensure there's a template to render\n  if (!templatePath) {\n    throw new Error('Please provide a template path. (Run ejs -h for help)');\n  }\n\n  if (opts.strict) {\n    pOpts.noWith = true;\n  }\n  if (pOpts.noWith) {\n    opts._with = false;\n  }\n\n  // Grab and parse any input data, in order of precedence:\n  // 1. Stdin\n  // 2. CLI arg via -i\n  // 3. Data file via -f\n  // Any individual vals passed at the end (e.g., foo=bar) will override\n  // any vals previously set\n  let input;\n  let err = new Error('Please do not pass data multiple ways. Pick one of stdin, -f, or -i.');\n  if (stdin) {\n    input = stdin;\n  }\n  else if (pOpts.dataInput) {\n    if (input) {\n      throw err;\n    }\n    input = decodeURIComponent(pOpts.dataInput);\n  }\n  else if (pOpts.dataFile) {\n    if (input) {\n      throw err;\n    }\n    input = fs.readFileSync(pOpts.dataFile).toString();\n  }\n\n  if (input) {\n    vals = JSON.parse(input);\n  }\n\n  // Override / set any individual values passed from the command line\n  for (let p in pVals) {\n    vals[p] = pVals[p];\n  }\n\n  opts.filename = path.resolve(process.cwd(), templatePath);\n  let template = fs.readFileSync(opts.filename).toString();\n  let output = ejs.render(template, vals, opts);\n  if (pOpts.outputFile) {\n    fs.writeFileSync(pOpts.outputFile, output);\n  }\n  else {\n    process.stdout.write(output);\n  }\n  process.exit();\n}\n\n// Defer execution so that stdin can be read if necessary\nsetImmediate(run);\n"
  },
  {
    "path": "docs/jsdoc/cache.jsdoc",
    "content": "/**\n * A JavaScript function cache. This is implemented by the lru-cache module\n * on NPM, so you can simply do `ejs.cache = LRU(10)` to get a\n * least-recently-used cache.\n *\n * @interface Cache\n * @global\n */\n\n/**\n * Cache the intermediate JavaScript function for a template.\n *\n * @function\n * @name Cache#set\n * @param {String}   key key for caching\n * @param {Function} val cached function\n */\n\n/**\n * Get the cached intermediate JavaScript function for a template.\n *\n * If the cache does not contain the specified key, `null` shall be returned.\n *\n * @function\n * @name Cache#get\n * @param {String}   key key for caching\n * @return {null|Function}\n */\n\n/**\n * Reset the entire cache.\n *\n * Erases the entire cache. Called by {@link module:ejs.clearCache}\n *\n * @function\n * @name Cache#reset\n */\n"
  },
  {
    "path": "docs/jsdoc/callbacks.jsdoc",
    "content": "/**\n * Callback for receiving data from {@link module:ejs.renderFile}.\n *\n * @callback RenderFileCallback\n * @param {?Error}  err error, if any resulted from the rendering process\n * @param {?String} [str] output string, is `null` or `undefined` if there is an error\n * @static\n * @global\n */\n"
  },
  {
    "path": "docs/jsdoc/fileLoader.jsdoc",
    "content": "/**\n * A file read function, similar to fs.readFileSync, this function\n * can be read a file by path, return a string after processing\n *\n * @function\n * @name fileLoader\n * @param {String}   path the path of the file to be read\n * @return {String|Object} the contents of the file as a string or objects that implement the toString() method\n * @global\n */\n"
  },
  {
    "path": "docs/jsdoc/options.jsdoc",
    "content": "/**\n * Compilation and rendering options.\n *\n * @typedef Options\n * @type {Object}\n *\n * @property {Boolean} [debug=false]\n * Log generated JavaScript source for the EJS template to the console.\n *\n * @property {Boolean} [compileDebug=true]\n * Include additional runtime debugging information in generated template\n * functions.\n *\n * @property {Boolean} [_with=true]\n * Whether or not to use `with () {}` construct in the generated template\n * functions. If set to `false`, data is still accessible through the object\n * whose name is specified by {@link module:ejs.localsName} (default to\n * `locals`).\n *\n * @property {Boolean} [strict=false]\n * Whether to run in strict mode or not.\n * Enforces `_with=false`.\n *\n * @property {String[]} [destructuredLocals=[]]\n * An array of local variables that are always destructured from {@link module:ejs.localsName},\n * available even in strict mode.\n *\n * @property {Boolean} [rmWhitespace=false]\n * Remove all safe-to-remove whitespace, including leading and trailing\n * whitespace. It also enables a safer version of `-%>` line slurping for all\n * scriptlet tags (it does not strip new lines of tags in the middle of a\n * line).\n *\n * @property {EscapeCallback} [escape={@link module:utils.escapeXML}]\n * The escaping function used with `<%=` construct.\n *\n * @property {String}  [filename=undefined]\n * The filename of the template. Required for inclusion and caching unless\n * you are using {@link module:ejs.renderFile}. Also used for error reporting.\n *\n * @property {String|String[]}  [root=undefined]\n * The path to the template root(s). When this is set, absolute paths for includes\n * (/filename.ejs) will be relative to the template root(s).\n *\n * @property {String}  [openDelimiter='<']\n * The opening delimiter for all statements. This allows you to clearly delinate\n * the difference between template code and existing delimiters. (It is recommended\n * to synchronize this with the closeDelimiter property.)\n *\n * @property {String}  [closeDelimiter='>']\n * The closing delimiter for all statements. This allows to to clearly delinate\n * the difference between template code and existing delimiters. (It is recommended\n * to synchronize this with the openDelimiter property.)\n *\n * @property {String}  [delimiter='%']\n * The delimiter used in template compilation.\n *\n * @property {Boolean} [cache=false]\n * Whether or not to enable caching of template functions. Beware that\n * the options of compilation are not checked as being the same, so\n * special handling is required if, for example, you want to cache\n * functions compiled with different options for the same file.\n *\n * Requires `filename` to be set. Only works with rendering function.\n *\n * @property {Object}  [context=this]\n * The Object to which `this` is set during rendering.\n *\n * @property {Object}  [scope=this]\n * Alias of `context`. Deprecated.\n *\n * @property {Boolean} [async=false]\n * Whether or not to create an async function instead of a regular function.\n * This requires language support.\n *\n * @static\n * @global\n */\n"
  },
  {
    "path": "docs/jsdoc/template-functions.jsdoc",
    "content": "/**\n * This type of function is returned from {@link module:ejs.compile}.\n *\n * @callback TemplateFunction\n * @param {Object}          [locals={}]\n * an object of data to be passed into the template. The name of this variable\n * is adjustable through {@link module:ejs.localsName}.\n * @return {(String|Promise<String>)}\n * Return type depends on {@link Options}`.async`.\n * @static\n * @global\n */\n\n/**\n * Escapes a string using HTML/XML escaping rules.\n *\n * @callback EscapeCallback\n * @param  {String} markup Input string\n * @return {String}        Escaped string\n * @static\n * @global\n */\n\n/**\n * This type of callback is used when {@link Options}`.compileDebug`\n * is true, and an error in the template is thrown. By default it is used to\n * rethrow an error in a better-formatted way.\n *\n * @callback RethrowCallback\n * @param {Error}  err      Error object\n * @param {String} str      full EJS source\n * @param {String} filename file name of the EJS file\n * @param {Number} lineno   line number of the error\n * @param {EscapeCallback} esc\n * @static\n * @global\n */\n\n/**\n * The callback used to include files at runtime with `include()`\n *\n * @callback IncludeCallback\n * @param  {String} path      Path to be included\n * @param  {Object} [data]    Data passed to the template\n * @return {String}           Contents of the file requested\n * @static\n * @global\n */\n"
  },
  {
    "path": "eslint.config_cjs.mjs",
    "content": "import configDefaults from \"./eslint.config_default.mjs\";\n\nconfigDefaults[0].languageOptions.parserOptions.sourceType = \"script\";\n\nexport default configDefaults;\n"
  },
  {
    "path": "eslint.config_default.mjs",
    "content": "import babelParser from \"@babel/eslint-parser\";\n\nexport default [\n  {\n    languageOptions: {\n      parser: babelParser,\n      globals: {\n        \"suite\": \"readonly\",\n        \"test\": \"readonly\"\n      },\n      \"parserOptions\": {\n        \"ecmaVersion\": 6,\n        \"sourceType\": \"script\",\n        \"requireConfigFile\": false,\n      },\n    },\n    rules: {\n        \"linebreak-style\": [\n            \"error\",\n            \"unix\"\n        ],\n        \"no-trailing-spaces\": 2,\n        \"indent\": [\n          \"error\",\n          2\n        ],\n        \"quotes\": [\n            \"error\",\n            \"single\",\n            {\n              \"avoidEscape\": true,\n              \"allowTemplateLiterals\": true\n            }\n        ],\n        \"semi\": [\n            \"error\",\n            \"always\"\n        ],\n        \"comma-style\": [\n            \"error\",\n            \"last\"\n        ],\n        \"no-console\": 0,\n        \"no-useless-escape\": 0\n    }\n  }\n]\n\n\n"
  },
  {
    "path": "eslint.config_esm.mjs",
    "content": "import configDefaults from \"./eslint.config_default.mjs\";\n\nconfigDefaults[0].languageOptions.parserOptions.sourceType = \"module\";\n\nexport default configDefaults;\n\n"
  },
  {
    "path": "examples/client-compilation.html",
    "content": "<!DOCTYPE html>\n<!--\n  This example demonstrates how to compile EJS templates in the browser\n  from a <textarea>.\n\n  To use this example, first run `jake build` in the EJS root directory to\n  produce the client-side ejs.js and ejs.min.js. Alternatively, you can\n  download the prebuilt client scripts (both minified and not) in GitHub\n  releases.\n  -->\n<html>\n  <head>\n    <title>EJS compilation demo</title>\n    <style>\n      * {\n        box-sizing: border-box;\n      }\n      /* Preformatted <textarea> */\n      textarea.pre {\n        font-family: monospace;\n        font-size: 1em;\n        height: auto;\n        width: 100%;\n      }\n      pre {\n        border: 2px solid gray;\n        border-radius: 2px;\n        padding: 8px;\n        max-height: 600px;\n        min-height: 120px;\n        overflow: auto;\n        width: auto;\n      }\n      /* Prettier button stolen from Bootstrap */\n      button {\n        padding: 8px 16px;\n        font-size: 19px;\n        border: 1px solid transparent;\n        border-radius: 5px;\n        background-color: #fff;\n        border-color: #ccc;\n        cursor: pointer;\n      }\n      button:hover,\n      button:focus,\n      button:active {\n        color: #333;\n        background-color: #e6e6e6;\n        border-color: #adadad;\n      }\n    </style>\n  </head>\n  <body>\n    <h1>\n      <button type=\"submit\" onclick=\"comp()\">\n        Submit\n      </button>\n    </h1>\n\n    <h2>Input template</h2>\n    <textarea rows=\"8\" class=\"pre\" id=\"input\"></textarea>\n\n    <h2>Compilation options (JSON)</h2>\n    <textarea rows=\"8\" class=\"pre\" id=\"options\"></textarea>\n\n    <h2>Locals (JSON)</h2>\n    <textarea rows=\"8\" class=\"pre\" id=\"locals\"></textarea>\n\n    <h2 id=\"output-title\">Output</h2>\n    <pre id=\"output\"></pre>\n\n    <script src=\"../ejs.js\"></script>\n    <script>\n      function comp() {\n        // Get the elements\n        var // input EJS template\n            templ = document.getElementById('input')\n            // JSON locals\n          , locals = document.getElementById('locals')\n            // JSON options\n          , options = document.getElementById('options')\n            // output text area\n          , output = document.getElementById('output')\n            // for dynamic output heading\n          , outputTitle = document.getElementById('output-title')\n          , fn;\n\n        // Handle errors by catching it, print the message to the output HTML\n        // box, and throwing it again so that it's visible on the JS console.\n\n        // `stage` sets the corresponding error context.\n        function handleError(func, stage) {\n          try {\n            // Try executing callback\n            func();\n          } catch (e) {\n            // If there is an exception:\n            // 1. Change output heading to \"Error when ...\"\n            outputTitle.innerHTML = 'Error ' + stage;\n            // 2. Put the stack trace into the output text area\n            output.textContent = e.stack || e.message || e.toString();\n            // 3. Rethrow it\n            throw e;\n          }\n        }\n\n        // Parse options as JSON\n        handleError(function () {\n          options = JSON.parse(options.value || '{}');\n        }, 'when parsing options');\n\n        // Parse locals as JSON\n        handleError(function () {\n          locals = JSON.parse(locals.value || '{}');\n        }, 'when parsing locals');\n\n        // Compile template with specified template and options\n        handleError(function () {\n          fn = ejs.compile(templ.value, options);\n        }, 'when compiling');\n\n        // Put the rendered HTML into the output textarea\n        handleError(function () {\n          output.textContent = fn(locals);\n          outputTitle.innerHTML = 'Output HTML';\n        }, 'when executing template function');\n        \n        // Note: To get the compiled function source code, use fn.toString()\n      }\n    </script>\n  </body>\n</html>\n"
  },
  {
    "path": "examples/client-injection.html",
    "content": "<!DOCTYPE html>\n<!--\n  This example demonstrates how to compile and render EJS templates in the\n  browser. It is usually recommended to always embed a precompiled function\n  in the HTML, rather than compile it on-the-fly, but this demonstrates that\n  it is possible.\n\n  To use this example, first run `jake build` in the EJS root directory to\n  produce the client-side ejs.js and ejs.min.js. Alternatively, you can\n  download the prebuilt client scripts (both minified and not) in GitHub\n  releases.\n  -->\n<html>\n  <head>\n    <script src=\"../ejs.js\"></script>\n    <!-- `type` can be anything but application/javascript -->\n    <script id=\"users\" type=\"text/template\">\n      <% if (names.length) { %>\n        <ul>\n          <% names.forEach(function(name){ %>\n            <li><%= name %></li>\n          <% }) %>\n        </ul>\n      <% } %>\n    </script>\n    <script>\n      onload = function () {\n        // get the EJS template as a string\n        var templ = document.getElementById('users').innerHTML;\n        console.log('EJS template:');\n        console.log(templ);\n        // data to output to the template function\n        var data = { names: ['loki', 'tobi', 'jane'] };\n        // stores the rendered HTML\n        var html = ejs.compile(templ)(data);\n        console.log('Rendered HTML:');\n        console.log(html);\n        // inject the rendered data to <body>\n        document.body.innerHTML = html;\n        console.log('HTML injected to <body>');\n      }\n    </script>\n  </head>\n  <body>\n  </body>\n</html>\n"
  },
  {
    "path": "examples/express/README.md",
    "content": "```\nnpm install\nnpm start\nopen http://localhost:3000\n```\n"
  },
  {
    "path": "examples/express/app.js",
    "content": "var express = require('express');\nvar path = require('path');\nvar ejs = require('ejs');\n\nvar app = express();\napp.set('views', path.join(__dirname, 'views'));\napp.set('view engine', 'ejs');\n\nfunction compileEjsTemplate(name, template) {\n  const compiledTemplate = ejs.compile(template, {\n    outputFunctionName: name\n  });\n  return function compileEjsTemplate(req, res, next) {\n    res.locals.compiledEjsTemplates = res.locals.compiledEjsTemplates || {};\n    res.locals.compiledEjsTemplates[name] = compiledTemplate.toString();\n    return next();\n  };\n}\n\napp.use(compileEjsTemplate('helloTemplate', 'Hello <%= include(\\'messageTemplate\\', { person: \\'John\\' }); %>'));\napp.use(compileEjsTemplate('messageTemplate', '<%= person %> now you know <%= fact %>.'));\napp.use('/', function(req, res) {\n  return res.render('index', {});\n});\n\napp.listen(process.env.PORT || 3000);\n"
  },
  {
    "path": "examples/express/package.json",
    "content": "{\n  \"description\": \"client side ejs compiled with express middleware\",\n  \"main\": \"app.js\",\n  \"scripts\": {\n    \"start\": \"node ./app.js\"\n  },\n  \"dependencies\": {\n    \"ejs\": \"^3.1.8\",\n    \"express\": \"~4.16.0\"\n  }\n}\n"
  },
  {
    "path": "examples/express/views/index.ejs",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <title>client side ejs compiled with express middleware</title>\n  </head>\n  <body>\n    <div id=\"greeting\"></div>\n    <script>\n      const helloTemplateFn = <%- compiledEjsTemplates.helloTemplate %>;\n      const messageTemplateFn = <%- compiledEjsTemplates.messageTemplate %>;\n\n      (function() {\n        const includeFn = function(path, data) {\n          if (path === 'messageTemplate') {\n            return messageTemplateFn({\n              fact: 'how to render a page with compiled ejs templates',\n              person: data.person\n            });\n          }\n        };\n\n        const html = helloTemplateFn({}, null, includeFn, null);\n        const $greeting = document.getElementById('greeting');\n        $greeting.innerHTML = html;\n      })();\n    </script>\n  </body>\n</html>\n"
  },
  {
    "path": "examples/functions.ejs",
    "content": "<h1>Users</h1>\n\n<% function user(user) { %>\n  <li><strong><%= user.name %></strong> is a <%= user.age %> year old <%= user.species %>.</li>\n<% } %>\n\n<ul>\n  <% users.map(user) %>\n</ul>\n"
  },
  {
    "path": "examples/functions.js",
    "content": "/*\n * Believe it or not, you can declare and use functions in EJS templates too.\n */\n\nvar ejs = require('../');\nvar read = require('fs').readFileSync;\nvar join = require('path').join;\nvar path = join(__dirname, '/functions.ejs');\nvar data = {\n  users: [\n    { name: 'Tobi', age: 2, species: 'ferret' },\n    { name: 'Loki', age: 2, species: 'ferret' },\n    { name: 'Jane', age: 6, species: 'ferret' }\n  ]\n};\n\nvar ret = ejs.compile(read(path, 'utf8'), {filename: path})(data);\n\nconsole.log(ret);\n"
  },
  {
    "path": "examples/hello.ejs",
    "content": "<span>Hello EJS!</span>\n"
  },
  {
    "path": "examples/list.ejs",
    "content": "<% if (names.length) { %>\n  <ul>\n    <% names.forEach(function (name) { %>\n      <%# Notice how the single quotation mark is escaped in the output HTML %>\n      <li foo='<%= name + \"'\" %>'><%= name %></li>\n    <% }) %>\n  </ul>\n<% } %>\n"
  },
  {
    "path": "examples/list.js",
    "content": "/*\n * This example demonstrates how to use Array.prototype.forEach() in an EJS\n * template.\n */\n\nvar ejs = require('../');\nvar read = require('fs').readFileSync;\nvar join = require('path').join;\nvar str = read(join(__dirname, '/list.ejs'), 'utf8');\n\nvar ret = ejs.compile(str)({\n  names: ['foo', 'bar', 'baz']\n});\n\nconsole.log(ret);\n"
  },
  {
    "path": "examples/output-function.ejs",
    "content": "<ul>\n<%- include('/hello.ejs') %>\n<%\n    users.forEach(function (user) {\n        echo('<li>');\n            echo('<strong>' + user.name + '</strong>');\n            echo('is a ' + user.age + ' year old ' + user.species);\n        echo('</li>\\n');\n    });\n-%>\n</ul>\n\n<ul>\n<%\n    users.forEach(function (user) {\n        echo(include('./partial.ejs', {user: user}));\n    });\n%>\n</ul>\n"
  },
  {
    "path": "examples/output-function.js",
    "content": "/*\n * Believe it or not, you can declare and use functions in EJS templates too.\n */\n\nvar ejs = require('../');\nvar read = require('fs').readFileSync;\nvar join = require('path').join;\nvar path = join(__dirname, '/output-function.ejs');\nvar data = {\n  users: [\n    {name: 'Tobi', age: 2, species: 'ferret'},\n    {name: 'Loki', age: 2, species: 'ferret'},\n    {name: 'Jane', age: 6, species: 'ferret'}\n  ]\n};\n\nvar ret = ejs.compile(read(path, 'utf8'), {\n  root: [join(__dirname, '..'), __dirname],\n  filename: path,\n  outputFunctionName: 'echo'\n})(data);\n\nconsole.log(ret);\n"
  },
  {
    "path": "examples/partial.ejs",
    "content": "<li>\n    <strong><%= user.name %></strong>\n    is a <%= user.age %> year old <%= user.species %>\n</li>"
  },
  {
    "path": "examples/slot/body.ejs",
    "content": "<div>body</div>\n"
  },
  {
    "path": "examples/slot/footer.ejs",
    "content": "<div>footer</div>\n"
  },
  {
    "path": "examples/slot/index.ejs",
    "content": "\n<div>\n<%-include('./layout.ejs', {\n  body: include('./body.ejs'),\n  footer: include('./footer.ejs')\n})%>\n</div>\n\n<hr>\n\n<div>\n<%-include('./layout.ejs', {\n  footer: include('./footer.ejs')\n})%>\n</div>\n"
  },
  {
    "path": "examples/slot/index.js",
    "content": "/*\n * Advanced use of \"include\", fast layout, and dynamic rendering components.\n */\n\nvar ejs = require('../../lib/ejs');\nvar read = require('fs').readFileSync;\nvar join = require('path').join;\nvar path = join(__dirname, '/index.ejs');\n\nvar ret = ejs.compile(read(path, 'utf8'), {filename: path})({title: 'use slot'});\n\nconsole.log(ret);\n"
  },
  {
    "path": "examples/slot/layout.ejs",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n  <meta charset=\"UTF-8\">\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n  <title>layout</title>\n</head>\n\n<body>\n  <div>\n    <h2><%=title%></h2>\n\n    <% if (typeof body !== 'undefined') { %>\n    <%- body %>\n    <% } else { %>\n    <p>This is the default body content.</p>\n    <% } %>\n\n    <div>description</div>\n\n    <%- footer %>\n  </div>\n\n</body>\n\n</html>\n"
  },
  {
    "path": "jakefile.js",
    "content": "let fs = require('fs');\nlet path = require('path');\nlet execSync = require('child_process').execSync;\nlet exec = function (cmd) {\n  execSync(cmd, {stdio: 'inherit'});\n};\n\n/* global jake, task, desc, publishTask */\n\nconst BUILT_EJS_FILES = [\n  'ejs.js',\n  'ejs.min.js',\n  'lib/esm/ejs.js',\n  'lib/cjs/ejs.js',\n];\n\n// Hook into some of the publish lifecycle events\njake.on('finished', function (ev) {\n\n  switch (ev.name) {\n  case 'publish':\n    console.log('Updating hosted docs...');\n    console.log('If this fails, run jake docPublish to re-try.');\n    jake.Task.docPublish.invoke();\n    break;\n  default:\n      // Do nothing\n  }\n\n});\n\ndesc('Builds the EJS library');\ntask('build', ['lint', 'clean', 'compile', 'browserify', 'minify']);\n\ndesc('Compiles ESM to CJS source files');\ntask('compile', function () {\n  // Compile CJS version\n  exec('npx tsc');\n  let source = fs.readFileSync('lib/cjs/ejs.js', 'utf8').toString();\n  // Browerify chokes on the 'node:' prefix in require statements\n  // Added the 'node:' prefix to ESM for Deno compat\n  ['fs', 'path', 'url'].forEach((mod) => {\n    source = source.replace(`require(\"node:${mod}\")`, `require(\"${mod}\")`);\n    source = source.replace(new RegExp(`node_${mod}_1`, 'g'), `${mod}_1`);\n  });\n  // replace `let` in code-generation strings\n  source = source.replace(\n    \"var DECLARATION_KEYWORD = 'let';\",\n    \"var DECLARATION_KEYWORD = 'var';\"\n  );\n  fs.writeFileSync('lib/cjs/ejs.js', source);\n  fs.writeFileSync('lib/cjs/package.json', '{\"type\":\"commonjs\"}');\n});\n\ndesc('Cleans browerified/minified files and package files');\ntask('clean', ['clobber'], function () {\n  jake.rmRf('./ejs.js');\n  jake.rmRf('./ejs.min.js');\n  jake.rmRf('./lib/cjs');\n  console.log('Cleaned up compiled files.');\n});\n\ndesc('Lints the source code');\ntask('lint', ['clean'], function () {\n  let epath = path.join('./node_modules/.bin/eslint');\n  // Handle both ESM and CJS files in project\n  exec(epath+' --config ./eslint.config_esm.mjs \"lib/esm/*.js\"');\n  exec(epath+' --config ./eslint.config_cjs.mjs \"test/*.js\" \"bin/cli.js\" \"jakefile.js\"');\n  console.log('Linting completed.');\n});\n\ntask('browserify', function () {\n  const currentDir = process.cwd();\n  process.chdir('./lib/cjs');\n  let epath = path.join('../../node_modules/browserify/bin/cmd.js');\n  exec(epath+' --standalone ejs ejs.js > ../../ejs.js');\n  process.chdir(currentDir);\n  console.log('Browserification completed.');\n});\n\ntask('minify', function () {\n  let epath = path.join('./node_modules/uglify-js/bin/uglifyjs');\n  exec(epath+' ./ejs.js > ejs.min.js');\n  console.log('Minification completed.');\n});\n\ndesc('Generates the EJS API docs for the public API');\ntask('doc', function () {\n  jake.rmRf('out');\n  let epath = path.join('./node_modules/.bin/jsdoc');\n  exec(epath+' --verbose -c jsdoc.json lib/esm/* docs/jsdoc/*');\n  console.log('Documentation generated in ./out.');\n});\n\ndesc('Generates the EJS API docs for the public and private API');\ntask('devdoc', function () {\n  jake.rmRf('out');\n  let epath = path.join('./node_modules/.bin/jsdoc');\n  exec(epath+' --verbose -p -c jsdoc.json lib/esm/* docs/jsdoc/*');\n  console.log('Documentation generated in ./out.');\n});\n\ndesc('Publishes the EJS API docs');\ntask('docPublish', ['doc'], function () {\n  fs.writeFileSync('out/CNAME', 'api.ejs.co');\n  console.log('Pushing docs to gh-pages...');\n  let epath = path.join('./node_modules/.bin/git-directory-deploy');\n  exec(epath+' --directory out/');\n  console.log('Docs published to gh-pages.');\n});\n\ndesc('Runs the EJS test suite');\ntask('test', ['build', 'testOnly'], function () {});\n\ntask('testOnly', function () {\n  exec(path.join('./node_modules/.bin/mocha --u tdd'));\n});\n\npublishTask('ejs', ['build'], function () {\n  this.packageFiles.include([\n    'jakefile.js',\n    'README.md',\n    'LICENSE',\n    'package.json',\n    'ejs.js',\n    'ejs.min.js',\n    'lib/**',\n    'bin/**',\n    'usage.txt'\n  ]);\n});\n\n"
  },
  {
    "path": "jsdoc.json",
    "content": "{\n  \"_comment\": \"Configuration file for JSDoc.\"\n, \"tags\": {\n    \"allowUnknownTags\": true\n  }\n, \"source\": {\n      \"includePattern\": \".+\\\\.js(doc)?$\"\n    , \"excludePattern\": \"(^|\\\\/|\\\\\\\\)_\"\n  }\n, \"plugins\": [ \"plugins/markdown\" ]\n, \"templates\": {\n    \"cleverLinks\": true\n  , \"monospaceLinks\": false\n  }\n}\n"
  },
  {
    "path": "lib/esm/ejs.js",
    "content": "/*\n * EJS Embedded JavaScript templates\n * Copyright 2112 Matthew Eernisse (mde@fleegix.org)\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *         http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n*/\n\n'use strict';\n\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport utils from './utils.js';\n\n/**\n * @file Embedded JavaScript templating engine. {@link http://ejs.co}\n * @author Matthew Eernisse <mde@fleegix.org>\n * @project EJS\n * @license {@link http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0}\n */\n\n/**\n * EJS internal functions.\n *\n * Technically this \"module\" lies in the same file as {@link module:ejs}, for\n * the sake of organization all the private functions re grouped into this\n * module.\n *\n * @module ejs-internal\n * @private\n */\n\n/**\n * Embedded JavaScript templating engine.\n *\n * @module ejs\n * @public\n */\n\n// Keyword used in code generation -- updated to 'var' in CJS build\nconst DECLARATION_KEYWORD = 'let';\n\nconst ejs = {};\n\n/** @type {string} */\nlet _DEFAULT_OPEN_DELIMITER = '<';\nlet _DEFAULT_CLOSE_DELIMITER = '>';\nlet _DEFAULT_DELIMITER = '%';\nlet _DEFAULT_LOCALS_NAME = 'locals';\nlet _REGEX_STRING = '(<%%|%%>|<%=|<%-|<%_|<%#|<%|%>|-%>|_%>)';\nlet _OPTS_PASSABLE_WITH_DATA = ['delimiter', 'scope', 'context', 'debug', 'compileDebug',\n  '_with', 'rmWhitespace', 'strict', 'filename', 'async'];\n// We don't allow 'cache' option to be passed in the data obj for\n// the normal `render` call, but this is where Express 2 & 3 put it\n// so we make an exception for `renderFile`\nlet _OPTS_PASSABLE_WITH_DATA_EXPRESS = _OPTS_PASSABLE_WITH_DATA.concat('cache');\nlet _BOM = /^\\uFEFF/;\nlet _JS_IDENTIFIER = /^[a-zA-Z_$][0-9a-zA-Z_$]*$/;\n\n/**\n * EJS template function cache. This can be a LRU object from lru-cache NPM\n * module. By default, it is {@link module:utils.cache}, a simple in-process\n * cache that grows continuously.\n *\n * @type {Cache}\n */\n\nejs.cache = utils.cache;\n\n/**\n * Custom file loader. Useful for template preprocessing or restricting access\n * to a certain part of the filesystem.\n *\n * @type {fileLoader}\n */\n\nejs.fileLoader = fs.readFileSync;\n\n/**\n * Name of the object containing the locals.\n *\n * This variable is overridden by {@link Options}`.localsName` if it is not\n * `undefined`.\n *\n * @type {String}\n * @public\n */\n\nejs.localsName = _DEFAULT_LOCALS_NAME;\n\n/**\n * Promise implementation -- defaults to the native implementation if available\n * This is mostly just for testability\n *\n * @type {PromiseConstructorLike}\n * @public\n */\n\nejs.promiseImpl = (new Function('return this;'))().Promise;\n\n/**\n * Get the path to the included file from the parent file path and the\n * specified path.\n *\n * @param {String}  name     specified path\n * @param {String}  filename parent file path\n * @param {Boolean} [isDir=false] whether the parent file path is a directory\n * @return {String}\n */\nejs.resolveInclude = function(name, filename, isDir) {\n  let dirname = path.dirname;\n  let extname = path.extname;\n  let resolve = path.resolve;\n  let includePath = resolve(isDir ? filename : dirname(filename), name);\n  let ext = extname(name);\n  if (!ext) {\n    includePath += '.ejs';\n  }\n  return includePath;\n};\n\n/**\n * Try to resolve file path on multiple directories\n *\n * @param  {String}        name  specified path\n * @param  {Array<String>} paths list of possible parent directory paths\n * @return {String}\n */\nfunction resolvePaths(name, paths) {\n  let filePath;\n  if (paths.some(function (v) {\n    filePath = ejs.resolveInclude(name, v, true);\n    return fs.existsSync(filePath);\n  })) {\n    return filePath;\n  }\n}\n\n/**\n * Get the path to the included file by Options\n *\n * @param  {String}  path    specified path\n * @param  {Options} options compilation options\n * @return {String}\n */\nfunction getIncludePath(path, options) {\n  let includePath;\n  let filePath;\n  let views = options.views;\n  let match = /^[A-Za-z]+:\\\\|^\\//.exec(path);\n\n  // Abs path\n  if (match && match.length) {\n    path = path.replace(/^\\/*/, '');\n    if (Array.isArray(options.root)) {\n      includePath = resolvePaths(path, options.root);\n    } else {\n      includePath = ejs.resolveInclude(path, options.root || '/', true);\n    }\n  }\n  // Relative paths\n  else {\n    // Look relative to a passed filename first\n    if (options.filename) {\n      filePath = ejs.resolveInclude(path, options.filename);\n      if (fs.existsSync(filePath)) {\n        includePath = filePath;\n      }\n    }\n    // Then look in any views directories\n    if (!includePath && Array.isArray(views)) {\n      includePath = resolvePaths(path, views);\n    }\n    if (!includePath && typeof options.includer !== 'function') {\n      throw new Error('Could not find the include file \"' +\n          options.escapeFunction(path) + '\"');\n    }\n  }\n  return includePath;\n}\n\n/**\n * Get the template from a string or a file, either compiled on-the-fly or\n * read from cache (if enabled), and cache the template if needed.\n *\n * If `template` is not set, the file specified in `options.filename` will be\n * read.\n *\n * If `options.cache` is true, this function reads the file from\n * `options.filename` so it must be set prior to calling this function.\n *\n * @memberof module:ejs-internal\n * @param {Options} options   compilation options\n * @param {String} [template] template source\n * @return {TemplateFunction}\n * @static\n */\n\nfunction handleCache(options, template) {\n  let func;\n  let filename = options.filename;\n  let hasTemplate = arguments.length > 1;\n\n  if (options.cache) {\n    if (!filename) {\n      throw new Error('cache option requires a filename');\n    }\n    func = ejs.cache.get(filename);\n    if (func) {\n      return func;\n    }\n    if (!hasTemplate) {\n      template = fileLoader(filename).toString().replace(_BOM, '');\n    }\n  }\n  else if (!hasTemplate) {\n    // istanbul ignore if: should not happen at all\n    if (!filename) {\n      throw new Error('Internal EJS error: no file name or template '\n                    + 'provided');\n    }\n    template = fileLoader(filename).toString().replace(_BOM, '');\n  }\n  func = ejs.compile(template, options);\n  if (options.cache) {\n    ejs.cache.set(filename, func);\n  }\n  return func;\n}\n\n/**\n * Try calling handleCache with the given options and data and call the\n * callback with the result. If an error occurs, call the callback with\n * the error. Used by renderFile().\n *\n * @memberof module:ejs-internal\n * @param {Options} options    compilation options\n * @param {Object} data        template data\n * @param {RenderFileCallback} cb callback\n * @static\n */\n\nfunction tryHandleCache(options, data, cb) {\n  let result;\n  if (!cb) {\n    if (typeof ejs.promiseImpl == 'function') {\n      return new ejs.promiseImpl(function (resolve, reject) {\n        try {\n          result = handleCache(options)(data);\n          resolve(result);\n        }\n        catch (err) {\n          reject(err);\n        }\n      });\n    }\n    else {\n      throw new Error('Please provide a callback function');\n    }\n  }\n  else {\n    try {\n      result = handleCache(options)(data);\n    }\n    catch (err) {\n      return cb(err);\n    }\n\n    cb(null, result);\n  }\n}\n\n/**\n * fileLoader is independent\n *\n * @param {String} filePath ejs file path.\n * @return {String} The contents of the specified file.\n * @static\n */\n\nfunction fileLoader(filePath){\n  return ejs.fileLoader(filePath);\n}\n\n/**\n * Get the template function.\n *\n * If `options.cache` is `true`, then the template is cached.\n *\n * @memberof module:ejs-internal\n * @param {String}  path    path for the specified file\n * @param {Options} options compilation options\n * @return {TemplateFunction}\n * @static\n */\n\nfunction includeFile(path, options) {\n  let opts = utils.shallowCopy(utils.createNullProtoObjWherePossible(), options);\n  opts.filename = getIncludePath(path, opts);\n  if (typeof options.includer === 'function') {\n    let includerResult = options.includer(path, opts.filename);\n    if (includerResult) {\n      if (includerResult.filename) {\n        opts.filename = includerResult.filename;\n      }\n      if (includerResult.template) {\n        return handleCache(opts, includerResult.template);\n      }\n    }\n  }\n  return handleCache(opts);\n}\n\n/**\n * Re-throw the given `err` in context to the `str` of ejs, `filename`, and\n * `lineno`.\n *\n * @implements {RethrowCallback}\n * @memberof module:ejs-internal\n * @param {Error}  err      Error object\n * @param {String} str      EJS source\n * @param {String} flnm     file name of the EJS file\n * @param {Number} lineno   line number of the error\n * @param {EscapeCallback} esc\n * @static\n */\n\nfunction rethrow(err, str, flnm, lineno, esc) {\n  let lines = str.split('\\n');\n  let start = Math.max(lineno - 3, 0);\n  let end = Math.min(lines.length, lineno + 3);\n  let filename = esc(flnm);\n  // Error context\n  let context = lines.slice(start, end).map(function (line, i){\n    let curr = i + start + 1;\n    return (curr == lineno ? ' >> ' : '    ')\n      + curr\n      + '| '\n      + line;\n  }).join('\\n');\n\n  // Alter exception message\n  err.path = filename;\n  err.message = (filename || 'ejs') + ':'\n    + lineno + '\\n'\n    + context + '\\n\\n'\n    + err.message;\n\n  throw err;\n}\n\nfunction stripSemi(str){\n  return str.replace(/;(\\s*$)/, '$1');\n}\n\n/**\n * Compile the given `str` of ejs into a template function.\n *\n * @param {String}  template EJS template\n *\n * @param {Options} [opts] compilation options\n *\n * @return {TemplateFunction}\n * Note that the return type of the function depends on the value of `opts.async`.\n * @public\n */\n\nejs.compile = function compile(template, opts) {\n  let templ;\n\n  // v1 compat\n  // 'scope' is 'context'\n  // FIXME: Remove this in a future version\n  if (opts && opts.scope) {\n    console.warn('`scope` option is deprecated and will be removed in future EJS');\n    if (!opts.context) {\n      opts.context = opts.scope;\n    }\n    delete opts.scope;\n  }\n  templ = new Template(template, opts);\n  return templ.compile();\n};\n\n/**\n * Render the given `template` of ejs.\n *\n * If you would like to include options but not data, you need to explicitly\n * call this function with `data` being an empty object or `null`.\n *\n * @param {String}   template EJS template\n * @param {Object}  [data={}] template data\n * @param {Options} [opts={}] compilation and rendering options\n * @return {(String|Promise<String>)}\n * Return value type depends on `opts.async`.\n * @public\n */\n\nejs.render = function (template, d, o) {\n  let data = d || utils.createNullProtoObjWherePossible();\n  let opts = o || utils.createNullProtoObjWherePossible();\n\n  // No options object -- if there are optiony names\n  // in the data, copy them to options\n  if (arguments.length == 2) {\n    utils.shallowCopyFromList(opts, data, _OPTS_PASSABLE_WITH_DATA);\n  }\n\n  return handleCache(opts, template)(data);\n};\n\n/**\n * Render an EJS file at the given `path` and callback `cb(err, str)`.\n *\n * If you would like to include options but not data, you need to explicitly\n * call this function with `data` being an empty object or `null`.\n *\n * @param {String}             path     path to the EJS file\n * @param {Object}            [data={}] template data\n * @param {Options}           [opts={}] compilation and rendering options\n * @param {RenderFileCallback} cb callback\n * @public\n */\n\nejs.renderFile = function () {\n  let args = Array.prototype.slice.call(arguments);\n  let filename = args.shift();\n  let cb;\n  let opts = {filename: filename};\n  let data;\n  let viewOpts;\n\n  // Do we have a callback?\n  if (typeof arguments[arguments.length - 1] == 'function') {\n    cb = args.pop();\n  }\n  // Do we have data/opts?\n  if (args.length) {\n    // Should always have data obj\n    data = args.shift();\n    // Normal passed opts (data obj + opts obj)\n    if (args.length) {\n      // Use shallowCopy so we don't pollute passed in opts obj with new vals\n      utils.shallowCopy(opts, args.pop());\n    }\n    // Special casing for Express (settings + opts-in-data)\n    else {\n      // Express 3 and 4\n      if (data.settings) {\n        // Pull a few things from known locations\n        if (data.settings.views) {\n          opts.views = data.settings.views;\n        }\n        if (data.settings['view cache']) {\n          opts.cache = true;\n        }\n        // Undocumented after Express 2, but still usable, esp. for\n        // items that are unsafe to be passed along with data, like `root`\n        viewOpts = data.settings['view options'];\n        if (viewOpts) {\n          utils.shallowCopy(opts, viewOpts);\n        }\n      }\n      // Express 2 and lower, values set in app.locals, or people who just\n      // want to pass options in their data. NOTE: These values will override\n      // anything previously set in settings  or settings['view options']\n      utils.shallowCopyFromList(opts, data, _OPTS_PASSABLE_WITH_DATA_EXPRESS);\n    }\n    opts.filename = filename;\n  }\n  else {\n    data = utils.createNullProtoObjWherePossible();\n  }\n\n  return tryHandleCache(opts, data, cb);\n};\n\n/**\n * Clear intermediate JavaScript cache. Calls {@link Cache#reset}.\n * @public\n */\n\n/**\n * EJS template class\n * @public\n */\nejs.Template = Template;\n\nejs.clearCache = function () {\n  ejs.cache.reset();\n};\n\nfunction Template(text, optsParam) {\n  let opts = utils.hasOwnOnlyObject(optsParam);\n  let options = utils.createNullProtoObjWherePossible();\n  this.templateText = text;\n  /** @type {string | null} */\n  this.mode = null;\n  this.truncate = false;\n  this.currentLine = 1;\n  this.source = '';\n  options.escapeFunction = opts.escape || opts.escapeFunction || utils.escapeXML;\n  options.compileDebug = opts.compileDebug !== false;\n  options.debug = !!opts.debug;\n  options.filename = opts.filename;\n  options.openDelimiter = opts.openDelimiter || ejs.openDelimiter || _DEFAULT_OPEN_DELIMITER;\n  options.closeDelimiter = opts.closeDelimiter || ejs.closeDelimiter || _DEFAULT_CLOSE_DELIMITER;\n  options.delimiter = opts.delimiter || ejs.delimiter || _DEFAULT_DELIMITER;\n  options.strict = opts.strict || false;\n  options.context = opts.context;\n  options.cache = opts.cache || false;\n  options.rmWhitespace = opts.rmWhitespace;\n  options.root = opts.root;\n  options.includer = opts.includer;\n  options.outputFunctionName = opts.outputFunctionName;\n  options.localsName = opts.localsName || ejs.localsName || _DEFAULT_LOCALS_NAME;\n  options.views = opts.views;\n  options.async = opts.async;\n  options.destructuredLocals = opts.destructuredLocals;\n  options.legacyInclude = typeof opts.legacyInclude != 'undefined' ? !!opts.legacyInclude : true;\n\n  if (options.strict) {\n    options._with = false;\n  }\n  else {\n    options._with = typeof opts._with != 'undefined' ? opts._with : true;\n  }\n\n  this.opts = options;\n\n  this.regex = this.createRegex();\n}\n\nTemplate.modes = {\n  EVAL: 'eval',\n  ESCAPED: 'escaped',\n  RAW: 'raw',\n  COMMENT: 'comment',\n  LITERAL: 'literal'\n};\n\nTemplate.prototype = {\n  createRegex: function () {\n    let str = _REGEX_STRING;\n    let delim = utils.escapeRegExpChars(this.opts.delimiter);\n    let open = utils.escapeRegExpChars(this.opts.openDelimiter);\n    let close = utils.escapeRegExpChars(this.opts.closeDelimiter);\n    str = str.replace(/%/g, delim)\n      .replace(/</g, open)\n      .replace(/>/g, close);\n    return new RegExp(str);\n  },\n\n  compile: function () {\n    /** @type {string} */\n    let src;\n    let fn;\n    let opts = this.opts;\n    let prepended = '';\n    let appended = '';\n    /** @type {EscapeCallback} */\n    let escapeFn = opts.escapeFunction;\n    /** @type {FunctionConstructor} */\n    let ctor;\n    /** @type {string} */\n    let sanitizedFilename = opts.filename ? JSON.stringify(opts.filename) : 'undefined';\n\n    if (!this.source) {\n      this.generateSource();\n      prepended +=\n        `  ${DECLARATION_KEYWORD} __output = \"\";\\n` +\n        '  function __append(s) { if (s !== undefined && s !== null) __output += s }\\n';\n      if (opts.outputFunctionName) {\n        if (!_JS_IDENTIFIER.test(opts.outputFunctionName)) {\n          throw new Error('outputFunctionName is not a valid JS identifier.');\n        }\n        prepended += `  ${DECLARATION_KEYWORD} ` + opts.outputFunctionName + ' = __append;' + '\\n';\n      }\n      if (opts.localsName && !_JS_IDENTIFIER.test(opts.localsName)) {\n        throw new Error('localsName is not a valid JS identifier.');\n      }\n      if (opts.destructuredLocals && opts.destructuredLocals.length) {\n        let destructuring = `  ${DECLARATION_KEYWORD} __locals = (` + opts.localsName + ' || {}),\\n';\n        for (let i = 0; i < opts.destructuredLocals.length; i++) {\n          let name = opts.destructuredLocals[i];\n          if (!_JS_IDENTIFIER.test(name)) {\n            throw new Error('destructuredLocals[' + i + '] is not a valid JS identifier.');\n          }\n          if (i > 0) {\n            destructuring += ',\\n  ';\n          }\n          destructuring += name + ' = __locals.' + name;\n        }\n        prepended += destructuring + ';\\n';\n      }\n      if (opts._with !== false) {\n        prepended +=  '  with (' + opts.localsName + ' || {}) {' + '\\n';\n        appended += '  }' + '\\n';\n      }\n      appended += '  return __output;' + '\\n';\n      this.source = prepended + this.source + appended;\n    }\n\n    if (opts.compileDebug) {\n      src = `${DECLARATION_KEYWORD} __line = 1` + '\\n'\n        + '  , __lines = ' + JSON.stringify(this.templateText) + '\\n'\n        + '  , __filename = ' + sanitizedFilename + ';' + '\\n'\n        + 'try {' + '\\n'\n        + this.source\n        + '} catch (e) {' + '\\n'\n        + '  rethrow(e, __lines, __filename, __line, escapeFn);' + '\\n'\n        + '}' + '\\n';\n    }\n    else {\n      src = this.source;\n    }\n\n    if (opts.strict) {\n      src = '\"use strict\";\\n' + src;\n    }\n    if (opts.debug) {\n      console.log(src);\n    }\n    if (opts.compileDebug && opts.filename) {\n      src = src + '\\n'\n        + '//# sourceURL=' + sanitizedFilename + '\\n';\n    }\n\n    try {\n      if (opts.async) {\n        // Have to use generated function for this, since in envs without support,\n        // it breaks in parsing\n        try {\n          ctor = (new Function('return (async function(){}).constructor;'))();\n        }\n        catch(e) {\n          if (e instanceof SyntaxError) {\n            throw new Error('This environment does not support async/await');\n          }\n          else {\n            throw e;\n          }\n        }\n      }\n      else {\n        ctor = Function;\n      }\n      fn = new ctor(opts.localsName + ', escapeFn, include, rethrow', src);\n    }\n    catch(e) {\n      // istanbul ignore else\n      if (e instanceof SyntaxError) {\n        if (opts.filename) {\n          e.message += ' in ' + opts.filename;\n        }\n        e.message += ' while compiling ejs\\n\\n';\n        e.message += 'If the above error is not helpful, you may want to try EJS-Lint:\\n';\n        e.message += 'https://github.com/RyanZim/EJS-Lint';\n        if (!opts.async) {\n          e.message += '\\n';\n          e.message += 'Or, if you meant to create an async function, pass `async: true` as an option.';\n        }\n      }\n      throw e;\n    }\n\n    // Return a callable function which will execute the function\n    // created by the source-code, with the passed data as locals\n    // Adds a local `include` function which allows full recursive include\n    let returnedFn = function anonymous(data) {\n      let include = function (path, includeData) {\n        let d = utils.shallowCopy(utils.createNullProtoObjWherePossible(), data);\n        if (includeData) {\n          d = utils.shallowCopy(d, includeData);\n        }\n        return includeFile(path, opts)(d);\n      };\n      return fn.apply(opts.context,\n        [data || utils.createNullProtoObjWherePossible(), escapeFn, include, rethrow]);\n    };\n    if (opts.filename && typeof Object.defineProperty === 'function') {\n      let filename = opts.filename;\n      let basename = path.basename(filename, path.extname(filename));\n      try {\n        Object.defineProperty(returnedFn, 'name', {\n          value: basename,\n          writable: false,\n          enumerable: false,\n          configurable: true\n        });\n      } catch (e) {/* ignore */}\n    }\n    return returnedFn;\n  },\n\n  generateSource: function () {\n    let opts = this.opts;\n\n    if (opts.rmWhitespace) {\n      // Have to use two separate replace here as `^` and `$` operators don't\n      // work well with `\\r` and empty lines don't work well with the `m` flag.\n      this.templateText =\n        this.templateText.replace(/[\\r\\n]+/g, '\\n').replace(/^\\s+|\\s+$/gm, '');\n    }\n\n    let self = this;\n    let d = this.opts.delimiter;\n    let o = this.opts.openDelimiter;\n    let c = this.opts.closeDelimiter;\n\n    // Slurp spaces and tabs before opening whitespace slurp tag and after closing whitespace slurp tag\n    // Build the tags using custom delimiters: openDelimiter + delimiter + '_' and '_' + delimiter + closeDelimiter\n    let openWhitespaceSlurpTag = utils.escapeRegExpChars(o + d + '_');\n    let closeWhitespaceSlurpTag = utils.escapeRegExpChars('_' + d + c);\n    let openWhitespaceSlurpReplacement = o + d + '_';\n    let closeWhitespaceSlurpReplacement = '_' + d + c;\n    this.templateText =\n      this.templateText.replace(new RegExp('[ \\\\t]*' + openWhitespaceSlurpTag, 'gm'), openWhitespaceSlurpReplacement)\n        .replace(new RegExp(closeWhitespaceSlurpTag + '[ \\\\t]*', 'gm'), closeWhitespaceSlurpReplacement);\n\n    let matches = this.parseTemplateText();\n\n    if (matches && matches.length) {\n      matches.forEach(function (line, index) {\n        let closing;\n        // If this is an opening tag, check for closing tags\n        // FIXME: May end up with some false positives here\n        // Better to store modes as k/v with openDelimiter + delimiter as key\n        // Then this can simply check against the map\n        if ( line.indexOf(o + d) === 0        // If it is a tag\n          && line.indexOf(o + d + d) !== 0) { // and is not escaped\n          closing = matches[index + 2];\n          if (!(closing == d + c || closing == '-' + d + c || closing == '_' + d + c)) {\n            throw new Error('Could not find matching close tag for \"' + line + '\".');\n          }\n        }\n        self.scanLine(line);\n      });\n    }\n\n  },\n\n  parseTemplateText: function () {\n    let str = this.templateText;\n    let pat = this.regex;\n    let result = pat.exec(str);\n    let arr = [];\n    let firstPos;\n\n    while (result) {\n      firstPos = result.index;\n\n      if (firstPos !== 0) {\n        arr.push(str.substring(0, firstPos));\n        str = str.slice(firstPos);\n      }\n\n      arr.push(result[0]);\n      str = str.slice(result[0].length);\n      result = pat.exec(str);\n    }\n\n    if (str) {\n      arr.push(str);\n    }\n\n    return arr;\n  },\n\n  _addOutput: function (line) {\n    if (this.truncate) {\n      // Only replace single leading linebreak in the line after\n      // -%> tag -- this is the single, trailing linebreak\n      // after the tag that the truncation mode replaces\n      // Handle Win / Unix / old Mac linebreaks -- do the \\r\\n\n      // combo first in the regex-or\n      line = line.replace(/^(?:\\r\\n|\\r|\\n)/, '');\n      this.truncate = false;\n    }\n    if (!line) {\n      return line;\n    }\n\n    // Preserve literal slashes\n    line = line.replace(/\\\\/g, '\\\\\\\\');\n\n    // Convert linebreaks\n    line = line.replace(/\\n/g, '\\\\n');\n    line = line.replace(/\\r/g, '\\\\r');\n\n    // Escape double-quotes\n    // - this will be the delimiter during execution\n    line = line.replace(/\"/g, '\\\\\"');\n    this.source += '    ; __append(\"' + line + '\")' + '\\n';\n  },\n\n  scanLine: function (line) {\n    let self = this;\n    let d = this.opts.delimiter;\n    let o = this.opts.openDelimiter;\n    let c = this.opts.closeDelimiter;\n    let newLineCount = 0;\n\n    newLineCount = (line.split('\\n').length - 1);\n\n    switch (line) {\n    case o + d:\n    case o + d + '_':\n      this.mode = Template.modes.EVAL;\n      break;\n    case o + d + '=':\n      this.mode = Template.modes.ESCAPED;\n      break;\n    case o + d + '-':\n      this.mode = Template.modes.RAW;\n      break;\n    case o + d + '#':\n      this.mode = Template.modes.COMMENT;\n      break;\n    case o + d + d:\n      this.mode = Template.modes.LITERAL;\n      this.source += '    ; __append(\"' + line.replace(o + d + d, o + d) + '\")' + '\\n';\n      break;\n    case d + d + c:\n      this.mode = Template.modes.LITERAL;\n      this.source += '    ; __append(\"' + line.replace(d + d + c, d + c) + '\")' + '\\n';\n      break;\n    case d + c:\n    case '-' + d + c:\n    case '_' + d + c:\n      if (this.mode == Template.modes.LITERAL) {\n        this._addOutput(line);\n      }\n\n      this.mode = null;\n      this.truncate = line.indexOf('-') === 0 || line.indexOf('_') === 0;\n      break;\n    default:\n      // In script mode, depends on type of tag\n      if (this.mode) {\n        // If '//' is found without a line break, add a line break.\n        switch (this.mode) {\n        case Template.modes.EVAL:\n        case Template.modes.ESCAPED:\n        case Template.modes.RAW:\n          if (line.lastIndexOf('//') > line.lastIndexOf('\\n')) {\n            line += '\\n';\n          }\n        }\n        switch (this.mode) {\n        // Just executing code\n        case Template.modes.EVAL:\n          this.source += '    ; ' + line + '\\n';\n          break;\n          // Exec, esc, and output\n        case Template.modes.ESCAPED:\n          this.source += '    ; __append(escapeFn(' + stripSemi(line) + '))' + '\\n';\n          break;\n          // Exec and output\n        case Template.modes.RAW:\n          this.source += '    ; __append(' + stripSemi(line) + ')' + '\\n';\n          break;\n        case Template.modes.COMMENT:\n          // Do nothing\n          break;\n          // Literal <%% mode, append as raw output\n        case Template.modes.LITERAL:\n          this._addOutput(line);\n          break;\n        }\n      }\n      // In string mode, just add the output\n      else {\n        this._addOutput(line);\n      }\n    }\n\n    if (self.opts.compileDebug && newLineCount) {\n      this.currentLine += newLineCount;\n      this.source += '    ; __line = ' + this.currentLine + '\\n';\n    }\n  }\n};\n\n/**\n * Escape characters reserved in XML.\n *\n * This is simply an export of {@link module:utils.escapeXML}.\n *\n * If `markup` is `undefined` or `null`, the empty string is returned.\n *\n * @param {String} markup Input string\n * @return {String} Escaped string\n * @public\n * @func\n * */\nejs.escapeXML = utils.escapeXML;\n\n/**\n * Express.js support.\n *\n * This is an alias for {@link module:ejs.renderFile}, in order to support\n * Express.js out-of-the-box.\n *\n * @func\n */\n\nejs.__express = ejs.renderFile;\n\n/* istanbul ignore if */\nif (typeof window != 'undefined') {\n  window.ejs = ejs;\n}\n\nif (typeof module != 'undefined') {\n  module.exports = ejs;\n}\nexport default ejs;\n"
  },
  {
    "path": "lib/esm/package.json",
    "content": "{\n  \"type\": \"module\"\n}\n"
  },
  {
    "path": "lib/esm/parseargs.js",
    "content": "/*\n * EJS Embedded JavaScript templates\n * Copyright 2112 Matthew Eernisse (mde@fleegix.org)\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *         http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n*/\n\n'use strict';\n\nlet parseargs = {};\nlet isOpt = function (arg) { return arg.indexOf('-') === 0; };\nlet removeOptPrefix = function (opt) { return opt.replace(/^--/, '').replace(/^-/, ''); };\n\n/**\n * @constructor\n * Parses a list of command-line args into a key/value object of\n * options and an array of positional commands.\n * @param {Array} opts A list of options in the following format:\n * [{full: 'foo', abbr: 'f'}, {full: 'bar', abbr: 'b'}]]\n */\nparseargs.Parser = function (opts) {\n  // A key/value object of matching options parsed out of the args\n  this.opts = {};\n  this.taskNames = null;\n  this.envVars = null;\n\n  // Data structures used for parsing\n  this.reg = opts;\n  this.shortOpts = {};\n  this.longOpts = {};\n\n  let self = this;\n  [].forEach.call(opts, function (item) {\n    self.shortOpts[item.abbr] = item;\n    self.longOpts[item.full] = item;\n  });\n};\n\nparseargs.Parser.prototype = new function () {\n\n  let _trueOrNextVal = function (argParts, args) {\n    if (argParts[1]) {\n      return argParts[1];\n    }\n    else {\n      return (!args[0] || isOpt(args[0])) ?\n        true : args.shift();\n    }\n  };\n\n  /**\n   * Parses an array of arguments into options and positional commands\n   * @param {Array} args The command-line args to parse\n   */\n  this.parse = function (args) {\n    let cmds = [];\n    let cmd;\n    let envVars = {};\n    let opts = {};\n    let arg;\n    let argItem;\n    let argParts;\n    let cmdItems;\n    let taskNames = [];\n    let preempt;\n\n    while (args.length) {\n      arg = args.shift();\n\n      if (isOpt(arg)) {\n        arg = removeOptPrefix(arg);\n        argParts = arg.split('=');\n        argItem = this.longOpts[argParts[0]] || this.shortOpts[argParts[0]];\n        if (argItem) {\n          // First-encountered preemptive opt takes precedence -- no further opts\n          // or possibility of ambiguity, so just look for a value, or set to\n          // true and then bail\n          if (argItem.preempts) {\n            opts[argItem.full] = _trueOrNextVal(argParts, args);\n            preempt = true;\n            break;\n          }\n          // If the opt requires a value, see if we can get a value from the\n          // next arg, or infer true from no-arg -- if it's followed by another\n          // opt, throw an error\n          if (argItem.expectValue || argItem.allowValue) {\n            opts[argItem.full] = _trueOrNextVal(argParts, args);\n            if (argItem.expectValue && !opts[argItem.full]) {\n              throw new Error(argItem.full + ' option expects a value.');\n            }\n          }\n          else {\n            opts[argItem.full] = true;\n          }\n        }\n      }\n      else {\n        cmds.unshift(arg);\n      }\n    }\n\n    if (!preempt) {\n      // Parse out any env-vars and task-name\n      while ((cmd = cmds.pop())) {\n        cmdItems = cmd.split('=');\n        if (cmdItems.length > 1) {\n          envVars[cmdItems[0]] = cmdItems[1];\n        }\n        else {\n          taskNames.push(cmd);\n        }\n      }\n\n    }\n\n    return {\n      opts: opts,\n      envVars: envVars,\n      taskNames: taskNames\n    };\n  };\n\n};\n\nif (typeof exports != 'undefined') {\n  module.exports = parseargs;\n}\nexport default parseargs;\n"
  },
  {
    "path": "lib/esm/utils.js",
    "content": "/*\n * EJS Embedded JavaScript templates\n * Copyright 2112 Matthew Eernisse (mde@fleegix.org)\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *         http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n*/\n\n/**\n * Private utility functions\n * @module utils\n * @private\n */\n\n'use strict';\n\nconst utils = {};\nvar regExpChars = /[|\\\\{}()[\\]^$+*?.]/g;\nvar hasOwnProperty = Object.prototype.hasOwnProperty;\nvar hasOwn = function (obj, key) { return hasOwnProperty.apply(obj, [key]); };\n\n/**\n * Escape characters reserved in regular expressions.\n *\n * If `string` is `undefined` or `null`, the empty string is returned.\n *\n * @param {String} string Input string\n * @return {String} Escaped string\n * @static\n * @private\n */\nutils.escapeRegExpChars = function (string) {\n  // istanbul ignore if\n  if (!string) {\n    return '';\n  }\n  return String(string).replace(regExpChars, '\\\\$&');\n};\n\nvar _ENCODE_HTML_RULES = {\n  '&': '&amp;',\n  '<': '&lt;',\n  '>': '&gt;',\n  '\"': '&#34;',\n  \"'\": '&#39;'\n};\nvar _MATCH_HTML = /[&<>'\"]/g;\n\nfunction encode_char(c) {\n  return _ENCODE_HTML_RULES[c] || c;\n}\n\n/**\n * Stringified version of constants used by {@link module:utils.escapeXML}.\n *\n * @readonly\n * @type {String}\n */\n\nvar escapeFuncStr =\n  'var _ENCODE_HTML_RULES = {\\n'\n+ '      \"&\": \"&amp;\"\\n'\n+ '    , \"<\": \"&lt;\"\\n'\n+ '    , \">\": \"&gt;\"\\n'\n+ '    , \\'\"\\': \"&#34;\"\\n'\n+ '    , \"\\'\": \"&#39;\"\\n'\n+ '    }\\n'\n+ '  , _MATCH_HTML = /[&<>\\'\"]/g;\\n'\n+ 'function encode_char(c) {\\n'\n+ '  return _ENCODE_HTML_RULES[c] || c;\\n'\n+ '};\\n';\n\n/**\n * Escape characters reserved in XML.\n *\n * If `markup` is `undefined` or `null`, the empty string is returned.\n *\n * @implements {EscapeCallback}\n * @param {String} markup Input string\n * @return {String} Escaped string\n * @static\n * @private\n */\n\nutils.escapeXML = function (markup) {\n  return markup == undefined\n    ? ''\n    : String(markup)\n      .replace(_MATCH_HTML, encode_char);\n};\n\nfunction escapeXMLToString() {\n  return Function.prototype.toString.call(this) + ';\\n' + escapeFuncStr;\n}\n\ntry {\n  if (typeof Object.defineProperty === 'function') {\n  // If the Function prototype is frozen, the \"toString\" property is non-writable. This means that any objects which inherit this property\n  // cannot have the property changed using an assignment. If using strict mode, attempting that will cause an error. If not using strict\n  // mode, attempting that will be silently ignored.\n  // However, we can still explicitly shadow the prototype's \"toString\" property by defining a new \"toString\" property on this object.\n    Object.defineProperty(utils.escapeXML, 'toString', { value: escapeXMLToString });\n  } else {\n    // If Object.defineProperty() doesn't exist, attempt to shadow this property using the assignment operator.\n    utils.escapeXML.toString = escapeXMLToString;\n  }\n} catch (err) {\n  console.warn('Unable to set escapeXML.toString (is the Function prototype frozen?)');\n}\n\n/**\n * Naive copy of properties from one object to another.\n * Does not recurse into non-scalar properties\n * Does not check to see if the property has a value before copying\n *\n * @param  {Object} to   Destination object\n * @param  {Object} from Source object\n * @return {Object}      Destination object\n * @static\n * @private\n */\nutils.shallowCopy = function (to, from) {\n  from = from || {};\n  if ((to !== null) && (to !== undefined)) {\n    for (var p in from) {\n      if (!hasOwn(from, p)) {\n        continue;\n      }\n      if (p === '__proto__' || p === 'constructor') {\n        continue;\n      }\n      to[p] = from[p];\n    }\n  }\n  return to;\n};\n\n/**\n * Naive copy of a list of key names, from one object to another.\n * Only copies property if it is actually defined\n * Does not recurse into non-scalar properties\n *\n * @param  {Object} to   Destination object\n * @param  {Object} from Source object\n * @param  {Array} list List of properties to copy\n * @return {Object}      Destination object\n * @static\n * @private\n */\nutils.shallowCopyFromList = function (to, from, list) {\n  list = list || [];\n  from = from || {};\n  if ((to !== null) && (to !== undefined)) {\n    for (var i = 0; i < list.length; i++) {\n      var p = list[i];\n      if (typeof from[p] != 'undefined') {\n        if (!hasOwn(from, p)) {\n          continue;\n        }\n        if (p === '__proto__' || p === 'constructor') {\n          continue;\n        }\n        to[p] = from[p];\n      }\n    }\n  }\n  return to;\n};\n\n/**\n * Simple in-process cache implementation. Does not implement limits of any\n * sort.\n *\n * @implements {Cache}\n * @static\n * @private\n */\nutils.cache = {\n  _data: {},\n  set: function (key, val) {\n    this._data[key] = val;\n  },\n  get: function (key) {\n    return this._data[key];\n  },\n  remove: function (key) {\n    delete this._data[key];\n  },\n  reset: function () {\n    this._data = {};\n  }\n};\n\n/**\n * Transforms hyphen case variable into camel case.\n *\n * @param {String} string Hyphen case string\n * @return {String} Camel case string\n * @static\n * @private\n */\nutils.hyphenToCamel = function (str) {\n  return str.replace(/-[a-z]/g, function (match) { return match[1].toUpperCase(); });\n};\n\n/**\n * Returns a null-prototype object in runtimes that support it\n *\n * @return {Object} Object, prototype will be set to null where possible\n * @static\n * @private\n */\nutils.createNullProtoObjWherePossible = (function () {\n  if (typeof Object.create == 'function') {\n    return function () {\n      return Object.create(null);\n    };\n  }\n  if (!({__proto__: null} instanceof Object)) {\n    return function () {\n      return {__proto__: null};\n    };\n  }\n  // Not possible, just pass through\n  return function () {\n    return {};\n  };\n})();\n\n/**\n * Copies own-properties from one object to a null-prototype object for basic\n * protection against prototype pollution\n *\n * @return {Object} Object with own-properties of input object\n * @static\n * @private\n */\nutils.hasOwnOnlyObject = function (obj) {\n  var o = utils.createNullProtoObjWherePossible();\n  for (var p in obj) {\n    if (hasOwn(obj, p)) {\n      o[p] = obj[p];\n    }\n  }\n  return o;\n};\n\nif (typeof exports != 'undefined') {\n  module.exports = utils;\n}\nexport default utils;\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"ejs\",\n  \"description\": \"Embedded JavaScript templates\",\n  \"keywords\": [\n    \"template\",\n    \"templating\",\n    \"engine\",\n    \"ejs\"\n  ],\n  \"version\": \"5.0.1\",\n  \"author\": \"Matthew Eernisse <matthew.eernisse@gmail.com>\",\n  \"license\": \"Apache-2.0\",\n  \"bin\": {\n    \"ejs\": \"./bin/cli.js\"\n  },\n  \"main\": \"./lib/cjs/ejs.js\",\n  \"module\": \"./lib/esm/ejs.js\",\n  \"browser\": \"./ejs.min.js\",\n  \"exports\": {\n    \"import\": \"./lib/esm/ejs.js\",\n    \"require\": \"./lib/cjs/ejs.js\"\n  },\n  \"jsdelivr\": \"ejs.min.js\",\n  \"unpkg\": \"ejs.min.js\",\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git://github.com/mde/ejs.git\"\n  },\n  \"bugs\": \"https://github.com/mde/ejs/issues\",\n  \"homepage\": \"https://github.com/mde/ejs\",\n  \"devDependencies\": {\n    \"jake\": \"^10.9.1\",\n    \"typescript\": \"^5.4.5\",\n    \"@babel/eslint-parser\": \"^7.24.5\",\n    \"browserify\": \"^17.0.0\",\n    \"eslint\": \"^9.1.1\",\n    \"git-directory-deploy\": \"^1.5.1\",\n    \"jsdoc\": \"^4.0.2\",\n    \"lru-cache\": \"^4.0.1\",\n    \"mocha\": \"^10.2.0\",\n    \"uglify-js\": \"^3.3.16\"\n  },\n  \"engines\": {\n    \"node\": \">=0.12.18\"\n  },\n  \"scripts\": {\n    \"test\": \"npx jake test\"\n  }\n}\n"
  },
  {
    "path": "test/cli.js",
    "content": "let exec = require('child_process').execSync;\nlet fs = require('fs');\nlet path = require('path');\nlet assert = require('assert');\nlet os = process.platform !== 'win32' ? '' : 'node ';\nlet lf = process.platform !== 'win32' ? '\\n' : '\\r\\n';\n\nfunction run(cmd) {\n  return exec(cmd).toString();\n}\n\nsuite('cli', function () {\n  test('rendering, custom delimiter, passed data', function () {\n    let x = path.join('./bin/cli.js');\n    let u = path.join('./test/fixtures/user.ejs');\n    let o = run(os+x+' -m $ '+u+' name=foo');\n    assert.equal(o, '<h1>foo</h1>'+lf);\n  });\n\n  test('rendering, custom delimiter, data from file with -f', function () {\n    let x = path.join('./bin/cli.js');\n    let u = path.join('./test/fixtures/user.ejs');\n    let o = run(os+x+' -m $ -f ./test/fixtures/user_data.json '+u);\n    assert.equal(o, '<h1>zerb</h1>'+lf);\n  });\n\n  test('rendering, custom delimiter, data from CLI arg with -i', function () {\n    let x = path.join('./bin/cli.js');\n    let u = path.join('./test/fixtures/user.ejs');\n    let o = run(os+x+' -m $ -i %7B%22name%22%3A%20%22foo%22%7D '+u);\n    assert.equal(o, '<h1>foo</h1>'+lf);\n  });\n\n  test('rendering, custom delimiter, data from stdin / pipe', function () {\n    if ( process.platform !== 'win32' ) {\n      let o = run('cat ./test/fixtures/user_data.json | ./bin/cli.js -m $ ./test/fixtures/user.ejs');\n      assert.equal(o, '<h1>zerb</h1>\\n');\n    } // does not work on windows...\n  });\n\n  test('rendering, custom delimiter, passed data overrides file', function () {\n    let x = path.join('./bin/cli.js');\n    let f = path.join('./test/fixtures/user_data.json');\n    let g = path.join('./test/fixtures/user.ejs');\n    let o = run(os+x+' -m $ -f '+f+' '+g+' name=frang');\n    assert.equal(o, '<h1>frang</h1>'+lf);\n  });\n\n  test('rendering, remove whitespace option (hyphen case)', function () {\n    let x = path.join('./bin/cli.js');\n    let f = path.join('./test/fixtures/rmWhitespace.ejs');\n    let o = run(os+x+' --rm-whitespace '+f);\n    let c = fs.readFileSync('test/fixtures/rmWhitespace.html', 'utf-8');\n    assert.equal(o.replace(/\\n/g, lf), c);\n  });\n\n  test('relative path in nested include', function () {\n    let x = path.join('./bin/cli.js');\n    let u = path.join('test/fixtures/include-simple.ejs');\n    let o = run(os+x+' '+u);\n    let c = fs.readFileSync('test/fixtures/include-simple.html', 'utf-8');\n    assert.equal(o, c);\n  });\n\n});\n"
  },
  {
    "path": "test/ejs.js",
    "content": "/* jshint mocha: true */\n/* eslint-env node, mocha */\n\n/**\n * Module dependencies.\n */\n\nvar ejs = require('..');\nvar fs = require('fs');\nvar read = fs.readFileSync;\nvar assert = require('assert');\nvar path = require('path');\nvar LRU = require('lru-cache');\nlet lf = process.platform !== 'win32' ? '\\n' : '\\r\\n';\n\ntry {\n  fs.mkdirSync(__dirname + '/tmp');\n} catch (ex) {\n  if (ex.code !== 'EEXIST') {\n    throw ex;\n  }\n}\n\n// From https://gist.github.com/pguillory/729616\nfunction hook_stdio(stream, callback) {\n  var old_write = stream.write;\n\n  stream.write = (function() {\n    return function(string, encoding, fd) {\n      callback(string, encoding, fd);\n    };\n  })(stream.write);\n\n  return function() {\n    stream.write = old_write;\n  };\n}\n\n/**\n * Load fixture `name`.\n */\n\nfunction fixture(name) {\n  return read('test/fixtures/' + name, 'utf8');\n}\n\n/**\n * User fixtures.\n */\n\nvar users = [];\nusers.push({name: 'geddy'});\nusers.push({name: 'neil'});\nusers.push({name: 'alex'});\n\n/** Used to test code that depends on async functions being supported. */\nvar testAsync = test.skip;\ntry {\n  eval('(async function() {})');\n  testAsync = test;\n} catch (e) {\n  // ignore\n}\n\nsuite('ejs.compile(str, options)', function () {\n  test('compile to a function', function () {\n    var fn = ejs.compile('<p>yay</p>');\n    assert.equal(fn(), '<p>yay</p>');\n  });\n\n  test('empty input works', function () {\n    var fn = ejs.compile('');\n    assert.equal(fn(), '');\n  });\n\n  test('throw if there are syntax errors', function () {\n    try {\n      ejs.compile(fixture('fail.ejs'));\n    }\n    catch (err) {\n      assert.ok(err.message.indexOf('compiling ejs') > -1);\n\n      try {\n        ejs.compile(fixture('fail.ejs'), {filename: 'fail.ejs'});\n      }\n      catch (err) {\n        assert.ok(err.message.indexOf('fail.ejs') > -1);\n        return;\n      }\n    }\n    throw new Error('no error reported when there should be');\n  });\n\n  test('allow customizing delimiter local var', function () {\n    var fn;\n    fn = ejs.compile('<p><?= name ?></p>', {delimiter: '?'});\n    assert.equal(fn({name: 'geddy'}), '<p>geddy</p>');\n\n    fn = ejs.compile('<p><:= name :></p>', {delimiter: ':'});\n    assert.equal(fn({name: 'geddy'}), '<p>geddy</p>');\n\n    fn = ejs.compile('<p><$= name $></p>', {delimiter: '$'});\n    assert.equal(fn({name: 'geddy'}), '<p>geddy</p>');\n  });\n\n  test('allow customizing open and close delimiters', function() {\n    var fn;\n    fn = ejs.compile('<p>[#= name #]</p>', {delimiter: '#', openDelimiter: '[', closeDelimiter: ']'});\n    assert.equal(fn({name: 'geddy'}), '<p>geddy</p>');\n  });\n\n  test('default to using ejs.delimiter', function () {\n    var fn;\n    ejs.delimiter = '&';\n    fn = ejs.compile('<p><&= name &></p>');\n    assert.equal(fn({name: 'geddy'}), '<p>geddy</p>');\n\n    fn = ejs.compile('<p><|= name |></p>', {delimiter: '|'});\n    assert.equal(fn({name: 'geddy'}), '<p>geddy</p>');\n    delete ejs.delimiter;\n  });\n\n  test('support custom escape function', function () {\n    var customEscape;\n    var fn;\n    customEscape = function customEscape(str) {\n      return !str ? '' : str.toUpperCase();\n    };\n    fn = ejs.compile('HELLO <%= name %>', {escape: customEscape});\n    assert.equal(fn({name: 'world'}), 'HELLO WORLD');\n  });\n\n  test('strict mode works', function () {\n    assert.equal(ejs.render(fixture('strict.ejs'), {}, {strict: true}), 'true');\n  });\n\n  test('destructuring works in strict mode as an alternative to `with`', function () {\n    var locals = Object.create(null);\n    locals.foo = 'bar';\n    assert.equal(ejs.render(fixture('strict-destructuring.ejs'), locals, {\n      strict: true,\n      destructuredLocals: Object.keys(locals),\n      _with: true\n    }), locals.foo);\n  });\n\n  testAsync('destructuring works in strict and async mode', function (done) {\n    var locals = Object.create(null);\n    locals.foo = 'bar';\n    ejs.render(fixture('strict-destructuring.ejs'), locals, {\n      strict: true,\n      async: true,\n      destructuredLocals: Object.keys(locals),\n    }).then(function (value) {\n      assert.equal(value, locals.foo);\n    }).then(\n      () => done(),\n      e => done(e)\n    );\n  });\n\n  testAsync('can compile to an async function', function (done) {\n    ejs.compile('<%= await \"Hi\" %>', {async: true})().then(function (value) {\n      try {\n        assert.equal(value, 'Hi');\n      } catch (e) {\n        done(e);\n        return;\n      }\n\n      done();\n    });\n  });\n\n  testAsync('Non-async error message mentions `async: true`', function (done) {\n    try {\n      ejs.compile('<%= await \"Hi\" %>');\n    }\n    catch (err) {\n      if (err instanceof SyntaxError) {\n        assert.ok(err.message.indexOf('async: true') > -1);\n        return done();\n      } else {\n        throw err;\n      }\n    }\n    throw new Error('no error reported when there should be');\n  });\n\n  var testFuncName = typeof Object.defineProperty === 'function' ? test : test.skip;\n\n  testFuncName('Compiled function name matches `filename` without the extension', function (done) {\n    var func = ejs.compile('<%= \"Foo\" %>', {\n      filename: 'foo.ejs'\n    });\n\n    assert.ok(func.name === 'foo');\n    return done();\n  });\n\n  testFuncName('Compiled function name defaults to \"anonymous\" when `filename` is unspecified', function (done) {\n    var func = ejs.compile('<%= \"Foo\" %>');\n\n    assert.ok(func.name === 'anonymous');\n    return done();\n  });\n});\n\n/* Old API -- remove when this shim goes away */\nsuite('ejs.render(str, dataAndOpts)', function () {\n  test('render the template with data/opts passed together', function () {\n    assert.equal(ejs.render('<p><?= foo ?></p>', {foo: 'yay', delimiter: '?'}),\n      '<p>yay</p>');\n  });\n\n  test('disallow unsafe opts passed along in data', function () {\n    assert.equal(ejs.render('<p><?= locals.foo ?></p>',\n      // localsName should not get reset because it's blacklisted\n      {_with: false, foo: 'yay', delimiter: '?', localsName: '_'}),\n    '<p>yay</p>');\n  });\n});\n\nsuite('ejs.render(str, data, opts)', function () {\n  test('render the template', function () {\n    assert.equal(ejs.render('<p>yay</p>'), '<p>yay</p>');\n  });\n\n  test('empty input works', function () {\n    assert.equal(ejs.render(''), '');\n  });\n\n  test('undefined renders nothing escaped', function () {\n    assert.equal(ejs.render('<%= undefined %>'), '');\n  });\n\n  test('undefined renders nothing raw', function () {\n    assert.equal(ejs.render('<%- undefined %>'), '');\n  });\n\n  test('null renders nothing escaped', function () {\n    assert.equal(ejs.render('<%= null %>'), '');\n  });\n\n  test('null renders nothing raw', function () {\n    assert.equal(ejs.render('<%- null %>'), '');\n  });\n\n  test('zero-value data item renders something escaped', function () {\n    assert.equal(ejs.render('<%= 0 %>'), '0');\n  });\n\n  test('zero-value data object renders something raw', function () {\n    assert.equal(ejs.render('<%- 0 %>'), '0');\n  });\n\n  test('accept locals', function () {\n    assert.equal(ejs.render('<p><%= name %></p>', {name: 'geddy'}),\n      '<p>geddy</p>');\n  });\n\n  test('accept locals without using with() {}', function () {\n    assert.equal(ejs.render('<p><%= locals.name %></p>', {name: 'geddy'},\n      {_with: false}),\n    '<p>geddy</p>');\n    assert.throws(function() {\n      ejs.render('<p><%= name %></p>', {name: 'geddy'},\n        {_with: false});\n    }, /name is not defined/);\n  });\n\n  test('accept custom name for locals', function () {\n    ejs.localsName = 'it';\n    assert.equal(ejs.render('<p><%= it.name %></p>', {name: 'geddy'},\n      {_with: false}),\n    '<p>geddy</p>');\n    assert.throws(function() {\n      ejs.render('<p><%= name %></p>', {name: 'geddy'},\n        {_with: false});\n    }, /name is not defined/);\n    ejs.localsName = 'locals';\n  });\n\n  test('support caching', function () {\n    var file = __dirname + '/tmp/render.ejs';\n    var options = {cache: true, filename: file};\n    var out = ejs.render('<p>Old</p>', {}, options);\n    var expected = '<p>Old</p>';\n    assert.equal(out, expected);\n    // Assert no change, still in cache\n    out = ejs.render('<p>New</p>', {}, options);\n    assert.equal(out, expected);\n  });\n\n  test('support LRU caching', function () {\n    var oldCache = ejs.cache;\n    var file = __dirname + '/tmp/render.ejs';\n    var options = {cache: true, filename: file};\n    var out;\n    var expected = '<p>Old</p>';\n\n    // Switch to LRU\n    ejs.cache = LRU();\n\n    out = ejs.render('<p>Old</p>', {}, options);\n    assert.equal(out, expected);\n    // Assert no change, still in cache\n    out = ejs.render('<p>New</p>', {}, options);\n    assert.equal(out, expected);\n\n    // Restore system cache\n    ejs.cache = oldCache;\n  });\n\n  test('opts.context', function () {\n    var ctxt = {foo: 'FOO'};\n    var out = ejs.render('<%= this.foo %>', {}, {context: ctxt});\n    assert.equal(out, ctxt.foo);\n  });\n\n});\n\nsuite('ejs.renderFile(path, [data], [options], [fn])', function () {\n  test('render a file', function(done) {\n    ejs.renderFile('test/fixtures/para.ejs', function(err, html) {\n      if (err) {\n        return done(err);\n      }\n      assert.equal(html, '<p>hey</p>'+lf);\n      done();\n    });\n  });\n\n  test('Promise support for reading files', function(done) {\n    var AsyncCtor;\n    var func;\n    function checkResult(html) {\n      assert.equal(html, '<p>hey</p>'+lf);\n    }\n    // Environments without Promise support -- should throw\n    // when no callback provided\n    function checkNoPromise() {\n      delete ejs.promiseImpl;\n      assert.throws(function () {\n        ejs.renderFile('test/fixtures/para.ejs');\n      });\n      ejs.promiseImpl = global.Promise;\n      done();\n    }\n\n    // Check for async/await support -- have to use eval for check because\n    // envs without async will break at parsing step\n    try {\n      eval('AsyncCtor = (async function () {}).constructor;');\n    }\n    catch (e) {\n      // No-op\n    }\n\n    // Both async and Promise -- in both cases, also check the call\n    // correctly throws in non-Promise envs if no callback provided\n    // -------------------\n    // Async support -- have to use eval for constructing async func for\n    // same reasons as above\n    if (AsyncCtor) {\n      func = new AsyncCtor('ejs', 'checkResult', 'checkNoPromise',\n        \"let res = await ejs.renderFile('test/fixtures/para.ejs'); checkResult(res); checkNoPromise();\");\n      func(ejs, checkResult, checkNoPromise);\n    }\n    // Ordinary Promise support\n    else {\n      ejs.renderFile('test/fixtures/para.ejs').then(function (res) {\n        checkResult(res);\n        checkNoPromise();\n      });\n    }\n  });\n\n  test('accept locals', function(done) {\n    var data =  {name: 'fonebone'};\n    var options = {delimiter: '$'};\n    ejs.renderFile('test/fixtures/user.ejs', data, options, function(err, html) {\n      if (err) {\n        return done(err);\n      }\n      assert.equal(html, '<h1>fonebone</h1>'+lf);\n      done();\n    });\n  });\n\n  test('accept locals without using with() {}', function(done) {\n    var data =  {name: 'fonebone'};\n    var options = {delimiter: '$', _with: false};\n    var doneCount = 0;\n    ejs.renderFile('test/fixtures/user-no-with.ejs', data, options, function(err, html) {\n      if (err) {\n        if (doneCount === 2) {\n          return;\n        }\n        doneCount = 2;\n        return done(err);\n      }\n      assert.equal(html, '<h1>fonebone</h1>'+lf);\n      doneCount++;\n      if (doneCount === 2) {\n        done();\n      }\n    });\n    ejs.renderFile('test/fixtures/user.ejs', data, options, function(err) {\n      if (!err) {\n        if (doneCount === 2) {\n          return;\n        }\n        doneCount = 2;\n        return done(new Error('error not thrown'));\n      }\n      doneCount++;\n      if (doneCount === 2) {\n        done();\n      }\n    });\n  });\n\n  test('not catch err thrown by callback', function(done) {\n    var data =  {name: 'fonebone'};\n    var options = {delimiter: '$'};\n    var counter = 0;\n\n    var d = require('domain').create();\n    d.on('error', function (err) {\n      assert.equal(counter, 1);\n      assert.equal(err.message, 'Exception in callback');\n      done();\n    });\n    d.run(function () {\n      // process.nextTick() needed to work around mochajs/mocha#513\n      //\n      // tl;dr: mocha doesn't support synchronous exception throwing in\n      // domains. Have to make it async. Ticket closed because: \"domains are\n      // deprecated :D\"\n      process.nextTick(function () {\n        ejs.renderFile('test/fixtures/user.ejs', data, options, function(err) {\n          counter++;\n          if (err) {\n            assert.notEqual(err.message, 'Exception in callback');\n            return done(err);\n          }\n          throw new Error('Exception in callback');\n        });\n      });\n    });\n  });\n\n  test('support caching', function (done) {\n    var expected = '<p>Old</p>';\n    var file = __dirname + '/tmp/renderFile.ejs';\n    var options = {cache: true};\n    fs.writeFileSync(file, '<p>Old</p>');\n\n    ejs.renderFile(file, {}, options, function (err, out) {\n      if (err) {\n        done(err);\n      }\n      fs.writeFileSync(file, '<p>New</p>');\n      assert.equal(out, expected);\n\n      ejs.renderFile(file, {}, options, function (err, out) {\n        if (err) {\n          done(err);\n        }\n        // Assert no change, still in cache\n        assert.equal(out, expected);\n        done();\n      });\n    });\n  });\n\n  test('opts.context', function (done) {\n    var ctxt = {foo: 'FOO'};\n    ejs.renderFile('test/fixtures/with-context.ejs', {}, {context: ctxt}, function(err, html) {\n      if (err) {\n        return done(err);\n      }\n      assert.equal(html, ctxt.foo + lf);\n      done();\n    });\n\n  });\n\n  test('support express multiple views folders, falls back to second if first is not available', function (done) {\n    var data = {\n      viewsText: 'test',\n      includePath: 'views-include.ejs',\n      settings: {\n        views: [\n          path.join(__dirname, 'fixtures/nonexistent-folder'),\n          path.join(__dirname, 'fixtures')\n        ]\n      }\n    };\n    ejs.renderFile(path.join(__dirname, 'fixtures/views.ejs'), data, function(error, data){\n      assert.ifError(error);\n      assert.equal('<div><p>global test</p>'+lf+'</div>'+lf, data);\n      done();\n    });\n\n  });\n\n  test('can reference by paths with directory names', function (done) {\n    var data = {\n      viewsText: 'test',\n      includePath: 'views/views-include.ejs',\n      settings: {\n        views: [\n          path.join(__dirname, 'fixtures/views'),\n          path.join(__dirname, 'fixtures')\n        ]\n      }\n    };\n    ejs.renderFile(path.join(__dirname, 'fixtures/views.ejs'), data, function(error, data){\n      assert.ifError(error);\n      assert.equal('<div><p>custom test</p>'+lf+'</div>'+lf, data);\n      done();\n    });\n\n  });\n\n});\n\nsuite('cache specific', function () {\n  test('`clearCache` work properly', function () {\n    var expected = '<p>Old</p>';\n    var file = __dirname + '/tmp/clearCache.ejs';\n    var options = {cache: true, filename: file};\n    var out = ejs.render('<p>Old</p>', {}, options);\n    assert.equal(out, expected);\n\n    ejs.clearCache();\n\n    expected = '<p>New</p>';\n    out = ejs.render('<p>New</p>', {}, options);\n    assert.equal(out, expected);\n  });\n\n  test('`clearCache` work properly, LRU', function () {\n    var expected = '<p>Old</p>';\n    var oldCache = ejs.cache;\n    var file = __dirname + '/tmp/clearCache.ejs';\n    var options = {cache: true, filename: file};\n    var out;\n\n    ejs.cache = LRU();\n\n    out = ejs.render('<p>Old</p>', {}, options);\n    assert.equal(out, expected);\n    ejs.clearCache();\n    expected = '<p>New</p>';\n    out = ejs.render('<p>New</p>', {}, options);\n    assert.equal(out, expected);\n\n    ejs.cache = oldCache;\n  });\n\n  test('LRU with cache-size 1', function () {\n    var oldCache = ejs.cache;\n    var options;\n    var out;\n    var expected;\n    var file;\n\n    ejs.cache = LRU(1);\n\n    file = __dirname + '/tmp/render1.ejs';\n    options = {cache: true, filename: file};\n    out = ejs.render('<p>File1</p>', {}, options);\n    expected = '<p>File1</p>';\n    assert.equal(out, expected);\n\n    // Same filename, different template, but output\n    // should be the same because cache\n    file = __dirname + '/tmp/render1.ejs';\n    options = {cache: true, filename: file};\n    out = ejs.render('<p>ChangedFile1</p>', {}, options);\n    expected = '<p>File1</p>';\n    assert.equal(out, expected);\n\n    // Different filiename -- output should be different,\n    // and previous cache-entry should be evicted\n    file = __dirname + '/tmp/render2.ejs';\n    options = {cache: true, filename: file};\n    out = ejs.render('<p>File2</p>', {}, options);\n    expected = '<p>File2</p>';\n    assert.equal(out, expected);\n\n    // Entry with first filename should now be out of cache,\n    // results should be different\n    file = __dirname + '/tmp/render1.ejs';\n    options = {cache: true, filename: file};\n    out = ejs.render('<p>ChangedFile1</p>', {}, options);\n    expected = '<p>ChangedFile1</p>';\n    assert.equal(out, expected);\n\n    ejs.cache = oldCache;\n  });\n});\n\nsuite('<%', function () {\n  test('without semicolons', function () {\n    assert.equal(ejs.render(fixture('no.semicolons.ejs')),\n      fixture('no.semicolons.html'));\n  });\n});\n\nsuite('<%=', function () {\n  test('should not throw an error with a // comment on the final line', function () {\n    assert.equal(ejs.render('<%=\\n// a comment\\nname\\n// another comment %>', {name: '&nbsp;<script>'}),\n      '&amp;nbsp;&lt;script&gt;');\n  });\n\n  test('escape &amp;<script>', function () {\n    assert.equal(ejs.render('<%= name %>', {name: '&nbsp;<script>'}),\n      '&amp;nbsp;&lt;script&gt;');\n  });\n\n  test('should escape \\'', function () {\n    assert.equal(ejs.render('<%= name %>', {name: 'The Jones\\'s'}),\n      'The Jones&#39;s');\n  });\n\n  test('should escape &foo_bar;', function () {\n    assert.equal(ejs.render('<%= name %>', {name: '&foo_bar;'}),\n      '&amp;foo_bar;');\n  });\n\n  test('should accept custom function', function() {\n\n    var customEscape = function customEscape(str) {\n      return !str ? '' : str.toUpperCase();\n    };\n\n    assert.equal(\n      ejs.render('<%= name %>', {name: 'The Jones\\'s'}, {escape: customEscape}),\n      'THE JONES\\'S'\n    );\n  });\n});\n\nsuite('<%-', function () {\n  test('should not throw an error with a // comment on the final line', function () {\n    assert.equal(ejs.render('<%-\\n// a comment\\nname\\n// another comment %>', {name: '&nbsp;<script>'}),\n      '&nbsp;<script>');\n  });\n\n  test('not escape', function () {\n    assert.equal(ejs.render('<%- name %>', {name: '<script>'}),\n      '<script>');\n  });\n\n  test('terminate gracefully if no close tag is found', function () {\n    try {\n      ejs.compile('<h1>oops</h1><%- name ->');\n      throw new Error('Expected parse failure');\n    }\n    catch (err) {\n      assert.ok(err.message.indexOf('Could not find matching close tag for') > -1);\n    }\n  });\n});\n\nsuite('%>', function () {\n  test('produce newlines', function () {\n    assert.equal(ejs.render(fixture('newlines.ejs'), {users: users}),\n      fixture('newlines.html'));\n  });\n  test('works with `-%>` interspersed', function () {\n    assert.equal(ejs.render(fixture('newlines.mixed.ejs'), {users: users}),\n      fixture('newlines.mixed.html'));\n  });\n  test('consecutive tags work', function () {\n    assert.equal(ejs.render(fixture('consecutive-tags.ejs')),\n      fixture('consecutive-tags.html'));\n  });\n});\n\nsuite('-%>', function () {\n  test('not produce newlines', function () {\n    assert.equal(ejs.render(fixture('no.newlines.ejs'), {users: users}),\n      fixture('no.newlines.html'));\n  });\n  test('stack traces work', function () {\n    try {\n      ejs.render(fixture('no.newlines.error.ejs'));\n    }\n    catch (e) {\n      if (e.message.indexOf('>> 4| <%= qdata %>') > -1) {\n        return;\n      }\n      throw e;\n    }\n    throw new Error('Expected ReferenceError');\n  });\n\n  test('works with unix style', function () {\n    var content = '<ul><% -%>\\n'\n    + '<% users.forEach(function(user){ -%>\\n'\n    + '<li><%= user.name -%></li>\\n'\n    + '<% }) -%>\\n'\n    + '</ul><% -%>\\n';\n\n    var expectedResult = '<ul><li>geddy</li>\\n<li>neil</li>\\n<li>alex</li>\\n</ul>';\n    var fn;\n    fn = ejs.compile(content);\n    assert.equal(fn({users: users}),\n      expectedResult);\n  });\n\n  test('works with windows style', function () {\n    var content = '<ul><% -%>\\r\\n'\n    + '<% users.forEach(function(user){ -%>\\r\\n'\n    + '<li><%= user.name -%></li>\\r\\n'\n    + '<% }) -%>\\r\\n'\n    + '</ul><% -%>\\r\\n';\n\n    var expectedResult = '<ul><li>geddy</li>\\r\\n<li>neil</li>\\r\\n<li>alex</li>\\r\\n</ul>';\n    var fn;\n    fn = ejs.compile(content);\n    assert.equal(fn({users: users}),\n      expectedResult);\n  });\n});\n\nsuite('<%%', function () {\n  test('produce literals', function () {\n    assert.equal(ejs.render('<%%- \"foo\" %>'),\n      '<%- \"foo\" %>');\n  });\n  test('work without an end tag', function () {\n    assert.equal(ejs.render('<%%'), '<%');\n    assert.equal(ejs.render(fixture('literal.ejs'), {}, {delimiter: ' '}),\n      fixture('literal.html'));\n  });\n});\n\nsuite('%%>', function () {\n  test('produce literal', function () {\n    assert.equal(ejs.render('%%>'),\n      '%>');\n    assert.equal(ejs.render('  >', {}, {delimiter: ' '}),\n      ' >');\n  });\n});\n\nsuite('<%_ and _%>', function () {\n  test('slurps spaces and tabs', function () {\n    assert.equal(ejs.render(fixture('space-and-tab-slurp.ejs'), {users: users}),\n      fixture('space-and-tab-slurp.html'));\n  });\n});\n\nsuite('single quotes', function () {\n  test('not mess up the constructed function', function () {\n    assert.equal(ejs.render(fixture('single-quote.ejs')),\n      fixture('single-quote.html'));\n  });\n});\n\nsuite('double quotes', function () {\n  test('not mess up the constructed function', function () {\n    assert.equal(ejs.render(fixture('double-quote.ejs')),\n      fixture('double-quote.html'));\n  });\n});\n\nsuite('backslashes', function () {\n  test('escape', function () {\n    assert.equal(ejs.render(fixture('backslash.ejs')),\n      fixture('backslash.html'));\n  });\n});\n\nsuite('messed up whitespace', function () {\n  test('work', function () {\n    assert.equal(ejs.render(fixture('messed.ejs'), {users: users}),\n      fixture('messed.html'));\n  });\n});\n\nsuite('exceptions', function () {\n  test('produce useful stack traces', function () {\n    try {\n      ejs.render(fixture('error.ejs'), {}, {filename: 'error.ejs'});\n    }\n    catch (err) {\n      assert.equal(err.path, 'error.ejs');\n      var errstck = err.stack.split('\\n').slice(0, 8).join('\\n');\n      errstck = errstck.replace(/\\n/g,lf);\n      errstck = errstck.replace(/\\r\\r\\n/g,lf);\n      assert.equal(errstck, fixture('error.out'));\n      return;\n    }\n    throw new Error('no error reported when there should be');\n  });\n\n  test('not include fancy stack info if compileDebug is false', function () {\n    try {\n      ejs.render(fixture('error.ejs'), {}, {\n        filename: 'error.ejs',\n        compileDebug: false\n      });\n    }\n    catch (err) {\n      assert.ok(!err.path);\n      var errstck = err.stack.split('\\n').slice(0, 8).join('\\n');\n      errstck = errstck.replace(/\\n/g,lf);\n      errstck = errstck.replace(/\\r\\r\\n/g,lf);\n      assert.notEqual(errstck, fixture('error.out'));\n      return;\n    }\n    throw new Error('no error reported when there should be');\n  });\n\n  var unhook = null;\n  test('log JS source when debug is set', function (done) {\n    var out = '';\n    var needToExit = false;\n    unhook = hook_stdio(process.stdout, function (str) {\n      out += str;\n      if (needToExit) {\n        return;\n      }\n      if (out.indexOf('__output')) {\n        needToExit = true;\n        unhook();\n        unhook = null;\n        return done();\n      }\n    });\n    ejs.render(fixture('hello-world.ejs'), {}, {debug: true});\n  });\n\n  test('escape filename in errors', function () {\n    assert.throws(function () {\n      ejs.render('<% throw new Error(\"whoops\"); %>', {}, {filename: '<script>'});\n    }, /Error: &lt;script&gt;/);\n  });\n\n  test('filename in errors uses custom escape', function () {\n    assert.throws(function () {\n      ejs.render('<% throw new Error(\"whoops\"); %>', {}, {\n        filename: '<script>',\n        escape: function () { return 'zooby'; }\n      });\n    }, /Error: zooby/);\n  });\n\n  teardown(function() {\n    if (!unhook) {\n      return;\n    }\n    unhook();\n    unhook = null;\n  });\n});\n\nsuite('rmWhitespace', function () {\n  test('works', function () {\n    var outp = ejs.render(fixture('rmWhitespace.ejs'), {}, {rmWhitespace: true});\n    assert.equal(outp.replace(/\\n/g,lf), fixture('rmWhitespace.html'));\n  });\n});\n\nsuite('include()', function () {\n  test('include ejs', function () {\n    var file = 'test/fixtures/include-simple.ejs';\n    assert.equal(ejs.render(fixture('include-simple.ejs'), {}, {filename: file}),\n      fixture('include-simple.html'));\n  });\n\n  test('include and escape ejs', function () {\n    var file = 'test/fixtures/include-escaped.ejs';\n    assert.equal(ejs.render(fixture('include-escaped.ejs'), {}, {filename: file}),\n      fixture('include-escaped.html'));\n  });\n\n  test('include and escape within included ejs', function () {\n    var escape = function (s) {\n      return s.toUpperCase();\n    };\n\n    var file = 'test/fixtures/include-nested-escape.ejs';\n    assert.equal(ejs.render(fixture('include-nested-escape.ejs'), {}, {filename: file, escape: escape}),\n      fixture('include-nested-escape.html'));\n  });\n\n  test('include in expression ejs', function () {\n    var file = 'test/fixtures/include-expression.ejs';\n    assert.equal(ejs.render(fixture('include-expression.ejs'), {}, {filename: file}),\n      fixture('include-expression.html'));\n  });\n\n  test('include ejs fails without `filename`', function () {\n    try {\n      ejs.render(fixture('include-simple.ejs'));\n    }\n    catch (err) {\n      assert.ok(err.message.indexOf('Could not find') > -1);\n      return;\n    }\n    throw new Error('expected inclusion error');\n  });\n\n  test('show filename when including nonexistent file', function () {\n    try {\n      ejs.render(fixture('include-nonexistent.ejs'));\n    }\n    catch (err) {\n      assert.ok(err.message.indexOf('nonexistent-file') > -1);\n      return;\n    }\n    throw new Error('expected inclusion error containing file name');\n  });\n\n  test('strips BOM', function () {\n    assert.equal(\n      ejs.render('<%- include(\"fixtures/includes/bom.ejs\") %>',\n        {}, {filename: path.join(__dirname, 'f.ejs')}),\n      '<p>This is a file with BOM.</p>'+lf);\n  });\n\n  test('include ejs with locals', function () {\n    var file = 'test/fixtures/include.ejs';\n    assert.equal(ejs.render(fixture('include.ejs'), {pets: users}, {filename: file, delimiter: '@'}),\n      fixture('include.html'));\n  });\n\n  test('include ejs with absolute path and locals', function () {\n    var file = 'test/fixtures/include-abspath.ejs';\n    assert.equal(ejs.render(fixture('include-abspath.ejs'),\n      {dir: path.join(__dirname, 'fixtures'), pets: users, path: path},\n      {filename: file, delimiter: '@'}),\n    fixture('include.html'));\n  });\n\n  test('include ejs with set root path', function () {\n    var file = 'test/fixtures/include-root.ejs';\n    var viewsPath = path.join(__dirname, 'fixtures');\n    assert.equal(ejs.render(fixture('include-root.ejs'), {pets: users}, {filename: file, delimiter: '@',root:viewsPath}),\n      fixture('include.html'));\n  });\n\n  test('include ejs with custom includer function', function () {\n    var file = 'test/fixtures/include-root.ejs';\n    var inc = function (original, prev) {\n      if (original.charAt(0) === '/') {\n        // original: '/include'         (windows)\n        // prev:     'D:\\include.ejs'   (windows)\n        return {\n          filename: path.join(__dirname, 'fixtures', original+'.ejs')\n        };\n      } else {\n        return prev;\n      }\n    };\n    assert.equal(ejs.render(fixture('include-root.ejs'), {pets: users}, {filename: file, delimiter: '@', includer: inc}),\n      fixture('include.html'));\n  });\n\n  test('include ejs with includer returning template', function () {\n    var file = 'test/fixtures/include-root.ejs';\n    var inc = function (original, prev) {\n      // original: '/include'         (windows)\n      // prev:     'D:\\include.ejs'   (windows)\n      if (original === '/include') {\n        return {\n          template: '<p>Hello template!</p>'+lf\n        };\n      } else {\n        return prev;\n      }\n    };\n    assert.equal(ejs.render(fixture('include-root.ejs'), {pets: users}, {filename: file, delimiter: '@', includer: inc}),\n      fixture('hello-template.html'));\n  });\n\n  test('work when nested', function () {\n    var file = 'test/fixtures/menu.ejs';\n    assert.equal(ejs.render(fixture('menu.ejs'), {pets: users}, {filename: file}),\n      fixture('menu.html'));\n  });\n\n  test('work with a variable path', function () {\n    var file = 'test/fixtures/menu_var.ejs';\n    var includePath = 'includes/menu-item';\n    assert.equal(ejs.render(fixture('menu.ejs'), {pets: users, varPath:  includePath}, {filename: file}),\n      fixture('menu.html'));\n  });\n\n  test('include arbitrary files as-is', function () {\n    var file = 'test/fixtures/include.css.ejs';\n    assert.equal(ejs.render(fixture('include.css.ejs'), {pets: users}, {filename: file}),\n      fixture('include.css.html'));\n  });\n\n  test('pass compileDebug to include', function () {\n    var file = 'test/fixtures/include.ejs';\n    var fn;\n    fn = ejs.compile(fixture('include.ejs'), {\n      filename: file,\n      delimiter: '@',\n      compileDebug: false\n    });\n    try {\n      // Render without a required variable reference\n      fn({foo: 'asdf'});\n    }\n    catch(e) {\n      assert.equal(e.message, 'pets is not defined');\n      assert.ok(!e.path);\n      return;\n    }\n    throw new Error('no error reported when there should be');\n  });\n\n  test('is dynamic', function () {\n    fs.writeFileSync(__dirname + '/tmp/include.ejs', '<p>Old</p>');\n    var file = 'test/fixtures/include_cache.ejs';\n    var options = {filename: file};\n    var out = ejs.compile(fixture('include_cache.ejs'), options);\n    assert.equal(out(), '<p>Old</p>'+lf);\n\n    fs.writeFileSync(__dirname + '/tmp/include.ejs', '<p>New</p>');\n    assert.equal(out(), '<p>New</p>'+lf);\n  });\n\n  test('support caching', function () {\n    fs.writeFileSync(__dirname + '/tmp/include.ejs', '<p>Old</p>');\n    var file = 'test/fixtures/include_cache.ejs';\n    var options = {cache: true, filename: file};\n    var out = ejs.render(fixture('include_cache.ejs'), {}, options);\n    var expected = fixture('include_cache.html');\n    assert.equal(out, expected);\n    out = ejs.render(fixture('include_cache.ejs'), {}, options);\n    // No change, still in cache\n    assert.equal(out, expected);\n    fs.writeFileSync(__dirname + '/tmp/include.ejs', '<p>New</p>');\n    out = ejs.render(fixture('include_cache.ejs'), {}, options);\n    assert.equal(out, expected);\n  });\n\n  test('handles errors in included file', function() {\n    try {\n      ejs.render('<%- include(\"fixtures/include-with-error\") %>', {}, {filename: path.join(__dirname, 'f.ejs')});\n    }\n    catch (err) {\n      assert.ok(err.message.indexOf('foobar is not defined') > -1);\n      return;\n    }\n    throw new Error('expected inclusion error');\n  });\n\n});\n\nsuite('comments', function () {\n  test('fully render with comments removed', function () {\n    assert.equal(ejs.render(fixture('comments.ejs')),\n      fixture('comments.html'));\n  });\n});\n\nsuite('test fileloader', function () {\n\n  var myFileLoad = function (filePath) {\n    return 'myFileLoad: ' + fs.readFileSync(filePath);\n  };\n\n  test('test custom fileload', function (done) {\n    ejs.fileLoader = myFileLoad;\n    ejs.renderFile('test/fixtures/para.ejs', function(err, html) {\n      if (err) {\n        return done(err);\n      }\n      assert.equal(html, 'myFileLoad: <p>hey</p>'+lf);\n      done();\n    });\n\n  });\n});\n\nsuite('examples', function () {\n  function noop () {}\n  fs.readdirSync('examples').forEach(function (f) {\n    if (!/\\.js$/.test(f)) {\n      return;\n    }\n    suite(f, function () {\n      test('doesn\\'t throw any errors', function () {\n        var stderr = hook_stdio(process.stderr, noop);\n        var stdout = hook_stdio(process.stdout, noop);\n        try {\n          require('../examples/' + f);\n        }\n        catch (ex) {\n          stdout();\n          stderr();\n          throw ex;\n        }\n        stdout();\n        stderr();\n      });\n    });\n  });\n});\n\nsuite('identifier validation', function () {\n  test('invalid outputFunctionName', function() {\n    assert.throws(function() {\n      ejs.compile('<p>yay</p>', {outputFunctionName: 'x;console.log(1);x'});\n    }, /outputFunctionName is not a valid JS identifier/);\n  });\n\n  test('invalid localsName', function() {\n    var locals = Object.create(null);\n    void(locals); // For linting;\n    assert.throws(function() {\n      ejs.compile('<p>yay</p>', {\n        localsName: 'function(){console.log(1);return locals;}()'});\n    }, /localsName is not a valid JS identifier/);\n  });\n\n  test('invalid destructuredLocals', function() {\n    var locals = {};\n    void(locals); // For linting;\n    assert.throws(function() {\n      ejs.compile('<p>yay</p>', {\n        destructuredLocals: [\n          'console.log(1); //'\n        ]});\n    }, /destructuredLocals\\[0\\] is not a valid JS identifier/);\n  });\n});\n"
  },
  {
    "path": "test/fixtures/backslash.ejs",
    "content": "\\foo\n"
  },
  {
    "path": "test/fixtures/backslash.html",
    "content": "\\foo\n"
  },
  {
    "path": "test/fixtures/comments.ejs",
    "content": "<li><a href=\"foo\"><% // double-slash comment %>foo</li>\n<li><a href=\"bar\"><% /* C-style comment */ %>bar</li>\n<li><a href=\"baz\"><% // double-slash comment with newline\n    %>baz</li>\n<li><a href=\"qux\"><% var x = 'qux'; // double-slash comment @ end of line %><%= x %></li>\n<li><a href=\"fee\"><%# ERB style comment %>fee</li>\n<li><a href=\"bah\"><%= 'not a ' + '//' + ' comment' %></a></li>\n"
  },
  {
    "path": "test/fixtures/comments.html",
    "content": "<li><a href=\"foo\">foo</li>\n<li><a href=\"bar\">bar</li>\n<li><a href=\"baz\">baz</li>\n<li><a href=\"qux\">qux</li>\n<li><a href=\"fee\">fee</li>\n<li><a href=\"bah\">not a // comment</a></li>\n"
  },
  {
    "path": "test/fixtures/consecutive-tags.ejs",
    "content": "<% var a = 'foo' %><% var b = 'bar' %><%= a %>\n"
  },
  {
    "path": "test/fixtures/consecutive-tags.html",
    "content": "foo\n"
  },
  {
    "path": "test/fixtures/double-quote.ejs",
    "content": "<p><%= \"lo\" + 'ki' %>'s \"wheelchair\"</p>\n"
  },
  {
    "path": "test/fixtures/double-quote.html",
    "content": "<p>loki's \"wheelchair\"</p>\n"
  },
  {
    "path": "test/fixtures/error.ejs",
    "content": "<ul>\n  <% if (users) { %>\n    <p>Has users</p>\n  <% } %>\n</ul>\n"
  },
  {
    "path": "test/fixtures/error.out",
    "content": "ReferenceError: error.ejs:2\n    1| <ul>\n >> 2|   <% if (users) { %>\n    3|     <p>Has users</p>\n    4|   <% } %>\n    5| </ul>\n\nusers is not defined"
  },
  {
    "path": "test/fixtures/fail.ejs",
    "content": "<% function foo() return 'foo'; %>\n"
  },
  {
    "path": "test/fixtures/hello-template.html",
    "content": "<p>Hello template!</p>\n"
  },
  {
    "path": "test/fixtures/hello-world.ejs",
    "content": "<p>Hello world!</p>\n"
  },
  {
    "path": "test/fixtures/include-abspath.ejs",
    "content": "<ul>\n  <@ pets.forEach(function(pet){ @>\n    <@- include(path.join(dir, 'pet'), {pet: pet}); @>\n  <@ }); @>\n</ul>\n"
  },
  {
    "path": "test/fixtures/include-escaped.ejs",
    "content": "<ul>\n  <%= include('hello-world'); %>\n</ul>\n"
  },
  {
    "path": "test/fixtures/include-escaped.html",
    "content": "<ul>\n  &lt;p&gt;Hello world!&lt;/p&gt;\n\n</ul>\n"
  },
  {
    "path": "test/fixtures/include-expression.ejs",
    "content": "<ul>\n  <%- 'ping: ' + include('hello-world').replace(/world/, 'planet'); %>\n</ul>\n"
  },
  {
    "path": "test/fixtures/include-expression.html",
    "content": "<ul>\n  ping: <p>Hello planet!</p>\n\n</ul>\n"
  },
  {
    "path": "test/fixtures/include-nested-escape.ejs",
    "content": "<span>\n  <%- include('includes/escape'); %>\n</span>\n"
  },
  {
    "path": "test/fixtures/include-nested-escape.html",
    "content": "<span>\n  FOO\n\n</span>\n"
  },
  {
    "path": "test/fixtures/include-nonexistent.ejs",
    "content": "<%- include('nonexistent-file'); %>\n"
  },
  {
    "path": "test/fixtures/include-root.ejs",
    "content": "<@- include('/include'); @>"
  },
  {
    "path": "test/fixtures/include-simple.ejs",
    "content": "<ul>\n  <%- include('hello-world'); %>\n</ul>\n"
  },
  {
    "path": "test/fixtures/include-simple.html",
    "content": "<ul>\n  <p>Hello world!</p>\n\n</ul>\n"
  },
  {
    "path": "test/fixtures/include-with-error.ejs",
    "content": "<p><%- foobar() %></p>"
  },
  {
    "path": "test/fixtures/include.css.ejs",
    "content": "<style><%- include('style.css', {value: 'bar'}); %></style>\n"
  },
  {
    "path": "test/fixtures/include.css.html",
    "content": "<style>body {\n  foo: 'bar';\n}\n</style>\n"
  },
  {
    "path": "test/fixtures/include.ejs",
    "content": "<ul>\n  <@ pets.forEach(function(pet){ @>\n    <@- include('pet', {pet: pet}); @>\n  <@ }); @>\n</ul>\n"
  },
  {
    "path": "test/fixtures/include.html",
    "content": "<ul>\n  \n    <li>geddy</li>\n\n  \n    <li>neil</li>\n\n  \n    <li>alex</li>\n\n  \n</ul>\n"
  },
  {
    "path": "test/fixtures/include_cache.ejs",
    "content": "<%- include('../tmp/include') %>\n"
  },
  {
    "path": "test/fixtures/include_cache.html",
    "content": "<p>Old</p>\n"
  },
  {
    "path": "test/fixtures/includes/bom.ejs",
    "content": "﻿<p>This is a file with BOM.</p>\n"
  },
  {
    "path": "test/fixtures/includes/escape.ejs",
    "content": "<%= \"foo\" %>\n"
  },
  {
    "path": "test/fixtures/includes/menu/item.ejs",
    "content": "<a href=\"/<%= url %>\"><%= title %></a>\n"
  },
  {
    "path": "test/fixtures/includes/menu-item.ejs",
    "content": "<li><%- include('menu/item', {url: url, title: title}); -%></li>\n"
  },
  {
    "path": "test/fixtures/literal.ejs",
    "content": "<pre>There should be a space followed by a less-than sign and then two more\nspaces in the next line:\n <   .</pre>\n"
  },
  {
    "path": "test/fixtures/literal.html",
    "content": "<pre>There should be a space followed by a less-than sign and then two more\nspaces in the next line:\n <  .</pre>\n"
  },
  {
    "path": "test/fixtures/menu.ejs",
    "content": "<%- include('includes/menu-item', {\n  url: '/foo'\n, title: 'Foo'\n}); -%>\n\n<%- include('includes/menu-item', {\n  url: '/bar'\n, title: 'Bar'\n}); -%>\n\n<%- include('includes/menu-item', {\n  url: '/baz'\n, title: 'Baz'\n}); -%>\n\n"
  },
  {
    "path": "test/fixtures/menu.html",
    "content": "<li><a href=\"//foo\">Foo</a>\n</li>\n\n<li><a href=\"//bar\">Bar</a>\n</li>\n\n<li><a href=\"//baz\">Baz</a>\n</li>\n\n"
  },
  {
    "path": "test/fixtures/menu_var.ejs",
    "content": "<%- include(varPath, {\n  url: '/foo'\n, title: 'Foo'\n}); -%>\n\n<%- include(varPath, {\n  url: '/bar'\n, title: 'Bar'\n}); -%>\n\n<%- include(varPath, {\n  url: '/baz'\n, title: 'Baz'\n}); -%>\n\n"
  },
  {
    "path": "test/fixtures/messed.ejs",
    "content": "<ul><%users.forEach(function(user){%><li><%=user.name%></li><%})%></ul>\n"
  },
  {
    "path": "test/fixtures/messed.html",
    "content": "<ul><li>geddy</li><li>neil</li><li>alex</li></ul>\n"
  },
  {
    "path": "test/fixtures/newlines.ejs",
    "content": "<ul>\n  <% users.forEach(function(user){ %>\n    <li><%= user.name %></li>\n  <% }) %>\n</ul>\n"
  },
  {
    "path": "test/fixtures/newlines.html",
    "content": "<ul>\n  \n    <li>geddy</li>\n  \n    <li>neil</li>\n  \n    <li>alex</li>\n  \n</ul>\n"
  },
  {
    "path": "test/fixtures/newlines.mixed.ejs",
    "content": "<ul>\n  <% var unused1 = 'blah' -%>\n  <% var unused2 = 'bleh'  %>\n  <% var unused3 = 'bloh' -%>\n  <% var unused4 = 'bluh'  %>\n</ul>\n"
  },
  {
    "path": "test/fixtures/newlines.mixed.html",
    "content": "<ul>\n    \n    \n</ul>\n"
  },
  {
    "path": "test/fixtures/no.newlines.ejs",
    "content": "<ul>\n  <% users.forEach(function(user){ -%>\n  <li><%= user.name %></li>\n  <% }) -%>\n</ul>\n"
  },
  {
    "path": "test/fixtures/no.newlines.error.ejs",
    "content": "AAA\n<% var data = \"test\"; -%>\nBBB\n<%= qdata %>\nCCC\n"
  },
  {
    "path": "test/fixtures/no.newlines.html",
    "content": "<ul>\n    <li>geddy</li>\n    <li>neil</li>\n    <li>alex</li>\n  </ul>\n"
  },
  {
    "path": "test/fixtures/no.semicolons.ejs",
    "content": "This document does not use semicolons in scriptlets.\n<%\n  var a = 'b'\n  var b = 'c'\n  var c\n  c = b\n%>\nThe value of c is: <%= c %>\n"
  },
  {
    "path": "test/fixtures/no.semicolons.html",
    "content": "This document does not use semicolons in scriptlets.\n\nThe value of c is: c\n"
  },
  {
    "path": "test/fixtures/para.ejs",
    "content": "<p>hey</p>\n"
  },
  {
    "path": "test/fixtures/pet.ejs",
    "content": "<li><@= pet.name @></li>\n"
  },
  {
    "path": "test/fixtures/rmWhitespace.ejs",
    "content": "   <tag1>  \n<tag2>  \r\nA very long piece of text very long piece of text very long piece of\ntext very long piece <% var f = 'f' %>of text very long piece of\ntex\rt very long piece of<% %>text very long \r\nadsffadsfadsfad<%= f %>   \n   \npiece of text.\n<% var a = 'a' %>  \nText again.\n<% var aa = a + 'a' %>\nRaw output:\n<%- aa %>\nBlank\n\nLine\n<% var b = 'b' %>\n<% var c = 'c'\nvar d = 'd' %>\nAnother text. <%= c %>\nAfter escaped\n<% /* newline slurp */ -%>\nLast line\n"
  },
  {
    "path": "test/fixtures/rmWhitespace.html",
    "content": "<tag1>\n<tag2>\nA very long piece of text very long piece of text very long piece of\ntext very long piece of text very long piece of\ntex\nt very long piece oftext very long\nadsffadsfadsfadf\npiece of text.\n\nText again.\n\nRaw output:\naa\nBlank\nLine\n\n\nAnother text. c\nAfter escaped\nLast line"
  },
  {
    "path": "test/fixtures/single-quote.ejs",
    "content": "<p><%= 'loki' %>'s wheelchair</p>\n"
  },
  {
    "path": "test/fixtures/single-quote.html",
    "content": "<p>loki's wheelchair</p>\n"
  },
  {
    "path": "test/fixtures/space-and-tab-slurp.ejs",
    "content": "<ul>\n\t <%_ users.forEach(function(user){ _%>\t \n    <li><%= user.name %></li>\n \t<%_ }) _%> \t\n</ul>\n"
  },
  {
    "path": "test/fixtures/space-and-tab-slurp.html",
    "content": "<ul>\n    <li>geddy</li>\n    <li>neil</li>\n    <li>alex</li>\n</ul>\n"
  },
  {
    "path": "test/fixtures/strict-destructuring.ejs",
    "content": "<%\n// Unspecified execution context should be `undefined` in strict mode\nvar isReallyStrict = !((function () { return this; })());\n-%>\n<%= isReallyStrict && foo -%>\n"
  },
  {
    "path": "test/fixtures/strict.ejs",
    "content": "<%\n// Unspecified execution context should be `undefined` in strict mode\nvar isReallyStrict = !((function () { return this; })())\n-%>\n<%= isReallyStrict -%>\n"
  },
  {
    "path": "test/fixtures/style.css",
    "content": "body {\n  foo: '<%= value %>';\n}\n"
  },
  {
    "path": "test/fixtures/user-no-with.ejs",
    "content": "<h1><$= locals.name $></h1>\n"
  },
  {
    "path": "test/fixtures/user.ejs",
    "content": "<h1><$= name $></h1>\n"
  },
  {
    "path": "test/fixtures/user_data.json",
    "content": "{\n  \"name\": \"zerb\"\n}\n"
  },
  {
    "path": "test/fixtures/views/views-include.ejs",
    "content": "<p>custom <%= viewsText %></p>\n"
  },
  {
    "path": "test/fixtures/views-include.ejs",
    "content": "<p>global <%= viewsText %></p>\n"
  },
  {
    "path": "test/fixtures/views-old.ejs",
    "content": "<div><%- include views-include.ejs %></div>\n"
  },
  {
    "path": "test/fixtures/views.ejs",
    "content": "<div><%- include(includePath, {viewsText: 'test'}) %></div>\n"
  },
  {
    "path": "test/fixtures/with-context.ejs",
    "content": "<%= this.foo %>\n"
  },
  {
    "path": "test/mocha.opts",
    "content": "--ui tdd\n--reporter spec\n--check-leaks\n"
  },
  {
    "path": "test/parseargs.js",
    "content": "/* jshint mocha: true */\n/* eslint-env node, mocha */\n\nvar parseargs = require('../lib/cjs/parseargs');\nvar assert = require('assert');\n\nvar TEST_OPTS = [\n  { full: 'output-file',\n    abbr: 'o',\n    expectValue: true,\n  },\n  { full: 'debug',\n    abbr: 'd',\n    expectValue: false,\n    allowValue: false,\n  },\n  { full: 'help',\n    abbr: 'h',\n  },\n  { full: 'tasks',\n    abbr: 't',\n    expectValue: false,\n    allowValue: true,\n  },\n  { full: 'version',\n    abbr: 'V',\n  },\n  { full: 'version',\n    abbr: 'v',\n  },\n];\n\nsuite('parseargs', function () {\n\n  test('short opt with value', function () {\n    var parser = new parseargs.Parser(TEST_OPTS);\n    var result = parser.parse(['-o', 'foo.html', 'template.ejs']);\n    assert.equal(result.opts['output-file'], 'foo.html');\n    assert.deepEqual(result.taskNames, ['template.ejs']);\n  });\n\n  test('long opt with value', function () {\n    var parser = new parseargs.Parser(TEST_OPTS);\n    var result = parser.parse(['--output-file', 'foo.html', 'template.ejs']);\n    assert.equal(result.opts['output-file'], 'foo.html');\n    assert.deepEqual(result.taskNames, ['template.ejs']);\n  });\n\n  test('long opt with equals syntax', function () {\n    var parser = new parseargs.Parser(TEST_OPTS);\n    var result = parser.parse(['--output-file=foo.html', 'template.ejs']);\n    assert.equal(result.opts['output-file'], 'foo.html');\n    assert.deepEqual(result.taskNames, ['template.ejs']);\n  });\n\n  test('boolean flag (short)', function () {\n    var parser = new parseargs.Parser(TEST_OPTS);\n    var result = parser.parse(['-d', 'template.ejs']);\n    assert.equal(result.opts['debug'], true);\n    assert.deepEqual(result.taskNames, ['template.ejs']);\n  });\n\n  test('boolean flag (long)', function () {\n    var parser = new parseargs.Parser(TEST_OPTS);\n    var result = parser.parse(['--debug', 'template.ejs']);\n    assert.equal(result.opts['debug'], true);\n    assert.deepEqual(result.taskNames, ['template.ejs']);\n  });\n\n  test('multiple opts combined', function () {\n    var parser = new parseargs.Parser(TEST_OPTS);\n    var result = parser.parse(['-d', '-o', 'out.html', 'template.ejs']);\n    assert.equal(result.opts['debug'], true);\n    assert.equal(result.opts['output-file'], 'out.html');\n    assert.deepEqual(result.taskNames, ['template.ejs']);\n  });\n\n  test('env vars (key=value pairs)', function () {\n    var parser = new parseargs.Parser(TEST_OPTS);\n    var result = parser.parse(['template.ejs', 'name=foo', 'age=30']);\n    assert.deepEqual(result.taskNames, ['template.ejs']);\n    assert.equal(result.envVars['name'], 'foo');\n    assert.equal(result.envVars['age'], '30');\n  });\n\n  test('env vars mixed with opts', function () {\n    var parser = new parseargs.Parser(TEST_OPTS);\n    var result = parser.parse(['-d', 'template.ejs', 'name=foo']);\n    assert.equal(result.opts['debug'], true);\n    assert.deepEqual(result.taskNames, ['template.ejs']);\n    assert.equal(result.envVars['name'], 'foo');\n  });\n\n  test('unknown opts are ignored', function () {\n    var parser = new parseargs.Parser(TEST_OPTS);\n    var result = parser.parse(['--unknown', 'template.ejs']);\n    assert.equal(result.opts['unknown'], undefined);\n    assert.deepEqual(result.taskNames, ['template.ejs']);\n  });\n\n  test('allowValue opt can take a value', function () {\n    var parser = new parseargs.Parser(TEST_OPTS);\n    var result = parser.parse(['-t', 'somevalue']);\n    assert.equal(result.opts['tasks'], 'somevalue');\n  });\n\n  test('allowValue opt defaults to true when no value', function () {\n    var parser = new parseargs.Parser(TEST_OPTS);\n    var result = parser.parse(['-t', '-d']);\n    assert.equal(result.opts['tasks'], true);\n    assert.equal(result.opts['debug'], true);\n  });\n\n  test('multiple abbrs for same full name', function () {\n    var parser = new parseargs.Parser(TEST_OPTS);\n    var result1 = parser.parse(['-V']);\n    var parser2 = new parseargs.Parser(TEST_OPTS);\n    var result2 = parser2.parse(['-v']);\n    assert.equal(result1.opts['version'], true);\n    assert.equal(result2.opts['version'], true);\n  });\n\n  test('no args returns empty results', function () {\n    var parser = new parseargs.Parser(TEST_OPTS);\n    var result = parser.parse([]);\n    assert.deepEqual(result.opts, {});\n    assert.deepEqual(result.envVars, {});\n    assert.deepEqual(result.taskNames, []);\n  });\n\n  test('positional arg before opts', function () {\n    var parser = new parseargs.Parser(TEST_OPTS);\n    var result = parser.parse(['template.ejs', '-d']);\n    assert.equal(result.opts['debug'], true);\n    assert.deepEqual(result.taskNames, ['template.ejs']);\n  });\n\n  test('multiple positional args', function () {\n    var parser = new parseargs.Parser(TEST_OPTS);\n    var result = parser.parse(['first.ejs', 'second.ejs']);\n    assert.deepEqual(result.taskNames, ['first.ejs', 'second.ejs']);\n  });\n\n});\n"
  },
  {
    "path": "test/utils.js",
    "content": "/* jshint mocha: true */\n/* eslint-env node, mocha */\n\n/**\n * Module dependencies.\n */\n\nvar assert = require('assert');\nvar utils = require('../lib/cjs/utils');\n\n/**\n *  Make sure utils exports all it is expected to export...\n */\nsuite('unit testing for completeness of module \\'utils.js\\' exports', function () {\n  test('expect \\'escapeRegExpChars\\' to be exported', function () {\n    assert.ok(typeof(utils.escapeRegExpChars)==='function');\n  });\n  test('expect \\'escapeXML\\' to be exported', function () {\n    assert.ok(typeof(utils.escapeXML)==='function');\n  });\n  test('expect \\'escapeXML.toString\\' to be exported', function () {\n    assert.ok(typeof(utils.escapeXML.toString)==='function');\n  });\n  test('expect \\'shallowCopy\\' to be exported', function () {\n    assert.ok(typeof(utils.shallowCopy)==='function');\n  });\n  test('expect \\'shallowCopyFromList\\' to be exported', function () {\n    assert.ok(typeof(utils.shallowCopyFromList)==='function');\n  });\n  test('expect \\'cache\\' to be exported', function () {\n    assert.ok(typeof(utils.cache)==='object');\n  });\n  test('expect \\'hyphenToCamel\\' to be exported', function () {\n    assert.ok(typeof(utils.hyphenToCamel)==='function');\n  });\n});\n\n/**\n *  Make sure all exported properties behave like they should\n */\nsuite('unit testing exported functions of module \\'utils.js\\'', function () {\n  /**\n   *  Unit testing of exported function 'escapeRegExpChars'\n   */\n  suite('unit testing function \\'escapeRegExpChars\\' of module \\'utils.js\\'', function () {\n    test('it should be callable without parameters', function () {\n      assert.doesNotThrow(() => { utils.escapeRegExpChars(); });\n      assert.ok(utils.escapeRegExpChars()==='');\n    });\n    test('it should be callable with parameter \\'string\\' {null}', function () {\n      assert.doesNotThrow(() => { utils.escapeRegExpChars(null); });\n      assert.ok(utils.escapeRegExpChars(null)==='');\n    });\n    test('it should be callable with parameter \\'string\\' {string}', function () {\n      const str = 'fun()';\n      assert.doesNotThrow(() => { utils.escapeRegExpChars(str); });\n      assert.ok(utils.escapeRegExpChars(str)==='fun\\\\(\\\\)');\n    });\n  });\n\n  /**\n   *  Unit testing of exported function 'escapeXML'\n   */\n  suite('unit testing function \\'escapeXML\\' of module \\'utils.js\\'', function () {\n    test('it should be callable without parameters', function () {\n      assert.doesNotThrow(() => { utils.escapeXML(); });\n      assert.ok(utils.escapeXML()==='');\n    });\n    test('it should be callable with parameter \\'markup\\' {null}', function () {\n      assert.doesNotThrow(() => { utils.escapeXML(null); });\n      assert.ok(utils.escapeXML(null)==='');\n    });\n    test('it should be callable with parameter \\'markup\\' {string}', function () {\n      const markup  = '<a href=\"http://fun.org\">fun.org</a>';\n      const escaped = '&lt;a href=&#34;http://fun.org&#34;&gt;fun.org&lt;/a&gt;';\n      assert.doesNotThrow(() => { utils.escapeXML(markup); });\n      assert.ok(utils.escapeXML(markup)===escaped);\n    });\n  });\n\n  /**\n   *  Unit testing of exported function 'shallowCopy'\n   */\n  suite('unit testing function \\'shallowCopy\\' of module \\'utils.js\\'', function () {\n    test('it should be callable without parameters', function () {\n      assert.doesNotThrow(() => { utils.shallowCopy(); });\n      assert.ok(utils.shallowCopy()===undefined);\n    });\n    test('should be callable with parameters \\'to\\' {undefined} and \\'from\\' {null}', function () {\n      assert.doesNotThrow(() => { utils.shallowCopy(undefined, null); });\n      assert.ok(utils.shallowCopy(undefined, null)===undefined);\n    });\n    test('should be callable with parameters \\'to\\' {null} and \\'from\\' {null}', function () {\n      assert.doesNotThrow(() => { utils.shallowCopy(null, null); });\n      assert.ok(utils.shallowCopy(null, null)===null);\n    });\n    test('should be callable with parameters \\'to\\' {undefined} and \\'from\\' {...}', function () {\n      const to = undefined;\n      const from = { 'foo': 'bar', 'baz': [ '1', '2' ], 'bah': { 'hurz': 'gnarf' }};\n      assert.doesNotThrow(() => { utils.shallowCopy(to, from); });\n      assert.ok(utils.shallowCopy(to, from)===undefined);\n    });\n    test('should be callable with parameters \\'to\\' {undefined} and \\'from\\' {...}', function () {\n      const to = null;\n      const from = { 'foo': 'bar', 'baz': [ '1', '2' ], 'bah': { 'hurz': 'gnarf' }};\n      assert.doesNotThrow(() => { utils.shallowCopy(to, from); });\n      assert.ok(utils.shallowCopy(to, from)===null);\n    });\n    test('should be callable with parameters \\'to\\' { } and \\'from\\' {...}', function () {\n      const to = {};\n      const from = { 'foo': 'bar', 'baz': [ '1', '2' ], 'bah': { 'hurz': 'gnarf' }};\n      assert.doesNotThrow(() => { utils.shallowCopy(to, from); });\n      assert.ok(JSON.stringify(utils.shallowCopy(to, from))===JSON.stringify(from));\n    });\n  });\n\n  /**\n   *  Unit testing of exported function 'shallowCopyFromList'\n   */\n  suite('unit testing function \\'shallowCopyFromList\\' of module \\'utils.js\\'', function () {\n    test('it should be callable without parameters', function () {\n      assert.doesNotThrow(() => { utils.shallowCopyFromList(); });\n      assert.ok(utils.shallowCopyFromList()===undefined);\n    });\n    test('it should be callable parameters \\'to\\' {undefined} and \\'from\\' {null}', function () {\n      assert.doesNotThrow(() => { utils.shallowCopyFromList(undefined, null); });\n      assert.ok(utils.shallowCopyFromList(undefined, null)===undefined);\n    });\n    test('it should be callable parameters \\'to\\' {undefined}, \\'from\\' {null} and \\'list\\' {null}', function () {\n      assert.doesNotThrow(() => { utils.shallowCopyFromList(undefined, null, null); });\n      assert.ok(utils.shallowCopyFromList(undefined, null, null)===undefined);\n    });\n    test('it should be callable parameters \\'to\\' {undefined}, \\'from\\' {null} and \\'list\\' {Array}', function () {\n      const list = [ 'foo', 'bar' ];\n      assert.doesNotThrow(() => { utils.shallowCopyFromList(undefined, null, list); });\n      assert.ok(utils.shallowCopyFromList(undefined, null, list)===undefined);\n    });\n    test('it should be callable parameters \\'to\\' {null} and \\'from\\' {null}', function () {\n      assert.doesNotThrow(() => { utils.shallowCopyFromList(null, null); });\n      assert.ok(utils.shallowCopyFromList(null, null)===null);\n    });\n    test('it should be callable parameters \\'to\\' {undefined}, \\'from\\' {null} and \\'list\\' {null}', function () {\n      assert.doesNotThrow(() => { utils.shallowCopyFromList(null, null, null); });\n      assert.ok(utils.shallowCopyFromList(null, null, null)===null);\n    });\n    test('it should be callable parameters \\'to\\' {null}, \\'from\\' {null} and \\'list\\' {Array}', function () {\n      const list = [ 'foo', 'bar' ];\n      assert.doesNotThrow(() => { utils.shallowCopyFromList(null, null, list); });\n      assert.ok(utils.shallowCopyFromList(null, null, list)===null);\n    });\n    test('it should be callable parameters \\'to\\' { }, \\'from\\' {...} and \\'list\\' {Array}', function () {\n      const list = [ 'foo', 'bar' ];\n      const to = {};\n      const from = { 'foo': 'bar', 'baz': [ '1', '2' ], 'bah': { 'hurz': 'gnarf' }};\n      assert.doesNotThrow(() => { utils.shallowCopyFromList(to, from, list); });\n      assert.ok(JSON.stringify(utils.shallowCopyFromList(to, from, list))===JSON.stringify({ 'foo': 'bar' }));\n    });\n  });\n\n  /**\n   *  Unit testing of exported object 'cache'\n   */\n  suite('unit testing object \\'cache\\' of module \\'utils.js\\'', function () {\n    suite('unit testing function \\'set\\' of object \\'cache\\'', function () {\n      test('it should be callable without parameters', function () {\n        assert.doesNotThrow(() => { utils.cache.set(); });\n        assert.doesNotThrow(() => { utils.cache.set(undefined, null); });\n        assert.doesNotThrow(() => { utils.cache.set(null, undefined); });\n        assert.doesNotThrow(() => { utils.cache.set(null, null); });\n      });\n      test('it should be callable without parameter \\'key\\' {string}', function () {\n        assert.doesNotThrow(() => { utils.cache.set('key', null); });\n        assert.doesNotThrow(() => { utils.cache.set('key', undefined); });\n        assert.doesNotThrow(() => { utils.cache.set('key', { 'foo': 'bar' }); });\n      });\n    });\n    suite('unit testing function \\'get\\' of object \\'cache\\'', function () {\n      test('it should be callable without parameters', function () {\n        assert.doesNotThrow(() => { utils.cache.get(); });\n        // note this depends on setting key {undefined} to {null} - s.a.\n        assert.ok(utils.cache.get()===null);\n      });\n      test('it should be callable parameter \\'key\\' {undefined}', function () {\n        assert.doesNotThrow(() => { utils.cache.get(); });\n        // note this depends on setting key {undefined} to {null} - s.a.\n        assert.ok(utils.cache.get()===null);\n      });\n      test('it should be callable parameter \\'key\\' {null}', function () {\n        assert.doesNotThrow(() => { utils.cache.get(null); });\n        // note this depends on setting key {null} to {null} - s.a.\n        assert.ok(utils.cache.get(null)===null);\n      });\n      test('it should be callable parameter \\'key\\' {string}', function () {\n        assert.doesNotThrow(() => { utils.cache.get('key'); });\n        // note this depends on setting key {string} to {...} - s.a.\n        assert.ok(JSON.stringify(utils.cache.get('key'))===JSON.stringify({ 'foo': 'bar' }));\n      });\n    });\n    suite('unit testing function \\'remove\\' of object \\'cache\\'', function () {\n      test('it should be callable without parameters', function () {\n        assert.doesNotThrow(() => { utils.cache.remove(); });\n        assert.ok(utils.cache.get()===undefined);\n      });\n      test('it should be callable with parameter \\'key\\' {null}', function () {\n        assert.doesNotThrow(() => { utils.cache.remove(null); });\n        assert.ok(utils.cache.get(null)===undefined);\n      });\n      test('it should be callable with parameter \\'key\\' {string}', function () {\n        assert.doesNotThrow(() => { utils.cache.remove('key'); });\n        assert.ok(utils.cache.get('key')===undefined);\n      });\n    });\n    suite('unit testing function \\'reset\\' of object \\'cache\\'', function () {\n      test('it should be callable without parameters', function () {\n        assert.doesNotThrow(() => { utils.cache.reset(); });\n      });\n    });\n  });\n\n  /**\n   *  Unit testing of exported function 'hyphenToCamel'\n   */\n  suite('unit testing function \\'hyphenToCamel\\' of module \\'utils.js\\'', function () {\n    test('it should be callable without parameters', function () {\n      const message = 'Cannot read property \\'replace\\' of undefined';\n      assert.throws(() => { utils.hyphenToCamel(); }, { name: 'TypeError' });\n    });\n    test('it should be callable with parameter \\'str\\' {string}', function () {\n      const str = 'some string';\n      const strCamel = str;\n      assert.doesNotThrow(() => { utils.hyphenToCamel(str); });\n      assert.ok(utils.hyphenToCamel(str)===strCamel);\n\n      const other = 'some-string';\n      const otherCamel = 'someString';\n      assert.doesNotThrow(() => { utils.hyphenToCamel(other); });\n      assert.ok(utils.hyphenToCamel(other)===otherCamel);\n    });\n  });\n\n});\n"
  },
  {
    "path": "tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    /* Visit https://aka.ms/tsconfig to read more about this file */\n\n    /* Projects */\n    // \"incremental\": true,                              /* Save .tsbuildinfo files to allow for incremental compilation of projects. */\n    // \"composite\": true,                                /* Enable constraints that allow a TypeScript project to be used with project references. */\n    // \"tsBuildInfoFile\": \"./.tsbuildinfo\",              /* Specify the path to .tsbuildinfo incremental compilation file. */\n    // \"disableSourceOfProjectReferenceRedirect\": true,  /* Disable preferring source files instead of declaration files when referencing composite projects. */\n    // \"disableSolutionSearching\": true,                 /* Opt a project out of multi-project reference checking when editing. */\n    // \"disableReferencedProjectLoad\": true,             /* Reduce the number of projects loaded automatically by TypeScript. */\n\n    /* Language and Environment */\n    \"target\": \"es5\",                                  /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */\n    // \"lib\": [],                                        /* Specify a set of bundled library declaration files that describe the target runtime environment. */\n    // \"jsx\": \"preserve\",                                /* Specify what JSX code is generated. */\n    // \"experimentalDecorators\": true,                   /* Enable experimental support for legacy experimental decorators. */\n    // \"emitDecoratorMetadata\": true,                    /* Emit design-type metadata for decorated declarations in source files. */\n    // \"jsxFactory\": \"\",                                 /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */\n    // \"jsxFragmentFactory\": \"\",                         /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */\n    // \"jsxImportSource\": \"\",                            /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */\n    // \"reactNamespace\": \"\",                             /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */\n    // \"noLib\": true,                                    /* Disable including any library files, including the default lib.d.ts. */\n    // \"useDefineForClassFields\": true,                  /* Emit ECMAScript-standard-compliant class fields. */\n    // \"moduleDetection\": \"auto\",                        /* Control what method is used to detect module-format JS files. */\n\n    /* Modules */\n    \"module\": \"commonjs\",                                /* Specify what module code is generated. */\n    // \"rootDir\": \"./\",                                  /* Specify the root folder within your source files. */\n    // \"moduleResolution\": \"node10\",                     /* Specify how TypeScript looks up a file from a given module specifier. */\n    // \"baseUrl\": \"./\",                                  /* Specify the base directory to resolve non-relative module names. */\n    // \"paths\": {},                                      /* Specify a set of entries that re-map imports to additional lookup locations. */\n    // \"rootDirs\": [],                                   /* Allow multiple folders to be treated as one when resolving modules. */\n    // \"typeRoots\": [],                                  /* Specify multiple folders that act like './node_modules/@types'. */\n    // \"types\": [],                                      /* Specify type package names to be included without being referenced in a source file. */\n    // \"allowUmdGlobalAccess\": true,                     /* Allow accessing UMD globals from modules. */\n    // \"moduleSuffixes\": [],                             /* List of file name suffixes to search when resolving a module. */\n    // \"allowImportingTsExtensions\": true,               /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */\n    // \"resolvePackageJsonExports\": true,                /* Use the package.json 'exports' field when resolving package imports. */\n    // \"resolvePackageJsonImports\": true,                /* Use the package.json 'imports' field when resolving imports. */\n    // \"customConditions\": [],                           /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */\n    // \"resolveJsonModule\": true,                        /* Enable importing .json files. */\n    // \"allowArbitraryExtensions\": true,                 /* Enable importing files with any extension, provided a declaration file is present. */\n    // \"noResolve\": true,                                /* Disallow 'import's, 'require's or '<reference>'s from expanding the number of files TypeScript should add to a project. */\n\n    /* JavaScript Support */\n    \"allowJs\": true,                                  /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */\n    // \"checkJs\": true,                                  /* Enable error reporting in type-checked JavaScript files. */\n    // \"maxNodeModuleJsDepth\": 1,                        /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */\n\n    /* Emit */\n    // \"declaration\": true,                              /* Generate .d.ts files from TypeScript and JavaScript files in your project. */\n    // \"declarationMap\": true,                           /* Create sourcemaps for d.ts files. */\n    // \"emitDeclarationOnly\": true,                      /* Only output d.ts files and not JavaScript files. */\n    // \"sourceMap\": true,                                /* Create source map files for emitted JavaScript files. */\n    // \"inlineSourceMap\": true,                          /* Include sourcemap files inside the emitted JavaScript. */\n    // \"outFile\": \"./\",                                  /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */\n    \"outDir\": \"./lib/cjs\",                                   /* Specify an output folder for all emitted files. */\n    // \"removeComments\": true,                           /* Disable emitting comments. */\n    // \"noEmit\": true,                                   /* Disable emitting files from a compilation. */\n    // \"importHelpers\": true,                            /* Allow importing helper functions from tslib once per project, instead of including them per-file. */\n    // \"importsNotUsedAsValues\": \"remove\",               /* Specify emit/checking behavior for imports that are only used for types. */\n    // \"downlevelIteration\": true,                       /* Emit more compliant, but verbose and less performant JavaScript for iteration. */\n    // \"sourceRoot\": \"\",                                 /* Specify the root path for debuggers to find the reference source code. */\n    // \"mapRoot\": \"\",                                    /* Specify the location where debugger should locate map files instead of generated locations. */\n    // \"inlineSources\": true,                            /* Include source code in the sourcemaps inside the emitted JavaScript. */\n    // \"emitBOM\": true,                                  /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */\n    // \"newLine\": \"crlf\",                                /* Set the newline character for emitting files. */\n    // \"stripInternal\": true,                            /* Disable emitting declarations that have '@internal' in their JSDoc comments. */\n    // \"noEmitHelpers\": true,                            /* Disable generating custom helper functions like '__extends' in compiled output. */\n    // \"noEmitOnError\": true,                            /* Disable emitting files if any type checking errors are reported. */\n    // \"preserveConstEnums\": true,                       /* Disable erasing 'const enum' declarations in generated code. */\n    // \"declarationDir\": \"./\",                           /* Specify the output directory for generated declaration files. */\n    // \"preserveValueImports\": true,                     /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */\n\n    /* Interop Constraints */\n    // \"isolatedModules\": true,                          /* Ensure that each file can be safely transpiled without relying on other imports. */\n    // \"verbatimModuleSyntax\": true,                     /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */\n    // \"allowSyntheticDefaultImports\": true,             /* Allow 'import x from y' when a module doesn't have a default export. */\n    \"esModuleInterop\": true,                             /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */\n    // \"preserveSymlinks\": true,                         /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */\n    \"forceConsistentCasingInFileNames\": true,            /* Ensure that casing is correct in imports. */\n\n    /* Type Checking */\n    \"strict\": true,                                      /* Enable all strict type-checking options. */\n    // \"noImplicitAny\": true,                            /* Enable error reporting for expressions and declarations with an implied 'any' type. */\n    // \"strictNullChecks\": true,                         /* When type checking, take into account 'null' and 'undefined'. */\n    // \"strictFunctionTypes\": true,                      /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */\n    // \"strictBindCallApply\": true,                      /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */\n    // \"strictPropertyInitialization\": true,             /* Check for class properties that are declared but not set in the constructor. */\n    // \"noImplicitThis\": true,                           /* Enable error reporting when 'this' is given the type 'any'. */\n    // \"useUnknownInCatchVariables\": true,               /* Default catch clause variables as 'unknown' instead of 'any'. */\n    // \"alwaysStrict\": true,                             /* Ensure 'use strict' is always emitted. */\n    // \"noUnusedLocals\": true,                           /* Enable error reporting when local variables aren't read. */\n    // \"noUnusedParameters\": true,                       /* Raise an error when a function parameter isn't read. */\n    // \"exactOptionalPropertyTypes\": true,               /* Interpret optional property types as written, rather than adding 'undefined'. */\n    // \"noImplicitReturns\": true,                        /* Enable error reporting for codepaths that do not explicitly return in a function. */\n    // \"noFallthroughCasesInSwitch\": true,               /* Enable error reporting for fallthrough cases in switch statements. */\n    // \"noUncheckedIndexedAccess\": true,                 /* Add 'undefined' to a type when accessed using an index. */\n    // \"noImplicitOverride\": true,                       /* Ensure overriding members in derived classes are marked with an override modifier. */\n    // \"noPropertyAccessFromIndexSignature\": true,       /* Enforces using indexed accessors for keys declared using an indexed type. */\n    // \"allowUnusedLabels\": true,                        /* Disable error reporting for unused labels. */\n    // \"allowUnreachableCode\": true,                     /* Disable error reporting for unreachable code. */\n\n    /* Completeness */\n    // \"skipDefaultLibCheck\": true,                      /* Skip type checking .d.ts files that are included with TypeScript. */\n    \"skipLibCheck\": true,                                 /* Skip type checking all .d.ts files. */\n    \"ignoreDeprecations\": \"5.0\"\n  },\n  \"include\": [\"./lib/esm/ejs.js\", \"./lib/esm/parseargs.js\"]\n}\n"
  },
  {
    "path": "usage.txt",
    "content": "EJS Embedded JavaScript templates\n{Usage}: ejs [options ...] template-file [data variables ...]\n\n{Options}:\n  -o,     --output-file FILE            Write the rendered output to FILE rather than stdout.\n  -f,     --data-file FILE              Must be JSON-formatted. Use parsed input from FILE as data for rendering.\n  -i,     --data-input STRING           Must be JSON-formatted and URI-encoded. Use parsed input from STRING as data for rendering.\n  -m,     --delimiter CHARACTER         Use CHARACTER with angle brackets for open/close (defaults to %).\n  -p,     --open-delimiter CHARACTER    Use CHARACTER instead of left angle bracket to open.\n  -c,     --close-delimiter CHARACTER   Use CHARACTER instead of right angle bracket to close.\n  -s,     --strict                      When set to `true`, generated function is in strict mode\n  -n      --no-with                     Use 'locals' object for vars rather than using `with` (implies --strict).\n  -l      --locals-name                 Name to use for the object storing local variables when not using `with`.\n  -w      --rm-whitespace               Remove all safe-to-remove whitespace, including leading and trailing whitespace.\n  -d      --debug                       Outputs generated function body\n  -h,     --help                        Display this help message.\n  -V/v,   --version                     Display the EJS version.\n\n{Examples}:\n  ejs -m $ ./test/fixtures/user.ejs -f ./user_data.json\n  ejs -m $ ./test/fixtures/user.ejs name=Lerxst\n  ejs -p [ -c ] ./template_file.ejs -o ./output.html\n  ejs -n -l _ ./some_template.ejs -f ./data_file.json\n  ejs -w ./template_with_whitspace.ejs -o ./output_file.html\n"
  }
]