├── .github └── workflows │ └── node.yml ├── .gitignore ├── LICENSE ├── README.md ├── eslint.config.js ├── index.js ├── package-lock.json ├── package.json └── test ├── fixtures ├── _header.ejs ├── index.ejs └── posts │ ├── _foo.ejs │ └── post.ejs └── test.js /.github/workflows/node.yml: -------------------------------------------------------------------------------- 1 | name: Node 2 | 3 | on: 4 | push: 5 | branches: ['*'] 6 | pull_request: 7 | branches: [master] 8 | 9 | jobs: 10 | test: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Checkout 14 | uses: actions/checkout@v4 15 | 16 | - name: Setup Node 17 | uses: actions/setup-node@v4 18 | with: 19 | node-version: 22 20 | 21 | - name: Install dependencies 22 | run: npm install 23 | 24 | - name: Run tests 25 | run: npm test 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.log 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | ISC License 2 | 3 | Copyright (c) 2025, Vladimir Agafonkin 4 | 5 | Permission to use, copy, modify, and/or distribute this software for any purpose 6 | with or without fee is hereby granted, provided that the above copyright notice 7 | and this permission notice appear in all copies. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 10 | REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND 11 | FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 12 | INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS 13 | OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 14 | TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF 15 | THIS SOFTWARE. 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # yeahjs 2 | 3 | A tiny, modern, fast implementation of EJS (Embedded JavaScript Templates). A nearly drop-in replacement for [`ejs`](https://ejs.co/) with a few [intentional limitations](#compared-to-ejs). 4 | 5 | [![Node](https://github.com/mourner/yeahjs/actions/workflows/node.yml/badge.svg)](https://github.com/mourner/yeahjs/actions/workflows/node.yml) 6 | [![Install Size](https://packagephobia.now.sh/badge?p=yeahjs)](https://packagephobia.now.sh/result?p=yeahjs) 7 | [![Min-zipped Size](https://badgen.net/bundlephobia/minzip/yeahjs)](https://bundlephobia.com/result?p=yeahjs) 8 | [![Simply Awesome](https://img.shields.io/badge/simply-awesome-brightgreen.svg)](https://github.com/mourner/projects) 9 | 10 | ## Example 11 | 12 | ```ejs 13 | 18 | ``` 19 | 20 | ```js 21 | import {compile} from 'yeahjs'; 22 | 23 | const template = compile(ejs); 24 | const output = template({items: ['flour', 'water', 'salt']}); 25 | ``` 26 | 27 | ```html 28 | 33 | ``` 34 | 35 | ## Compared to `ejs` 36 | 37 | There are a few key differences that allow `yeahjs` to be so small and fast: 38 | 39 | - **Strict mode** only (no `with` keyword in compiled functions). 40 | - Only **static path includes** (`include('header.ejs')`, but not `include(dir + file)`). 41 | - File handling **not included** — provide it with `read` and `resolve` options (see [example](#file-handling)). 42 | 43 | Otherwise `yeahjs` produces identical output to `ejs`. 44 | 45 | ### Strict mode only 46 | 47 | The `with` keyword has a very significant impact on performance in JavaScript, in addition to introducing hard to debug issues. Limiting `yeahjs` to strict mode makes sure it's always as fast and predictable as possible. 48 | 49 | ### Static path includes 50 | 51 | Static path includes make sure `yeahjs` can fully compile templates with includes at `compile` time, avoiding lazy compilation during template evaluation. This makes evaluation faster and more predictable. 52 | 53 | ### Custom file handling 54 | 55 | Not including any file-system-specific code makes `yeahjs` environment-agnostic, having the same bundle for both Node and the browsers and giving full control over how includes get read and resolved. 56 | 57 | ## Usage 58 | 59 | ```js 60 | import {compile} from 'yeahjs'; 61 | 62 | const template = compile(ejs, options); 63 | ```` 64 | 65 | Returns a function of the form `(data) => content`. Options: 66 | 67 | - `localsName`: the namespace to use for accessing template data (`locals` by default for `<%= locals.foo %>`). 68 | - `locals`: an array of variables to access directly (e.g. `['foo']` will allow `<%= foo %>` instead of `<%= locals.foo %>`). 69 | - `context`: an object to use as `this` in templates (`null` by default). 70 | - `escape`: a custom escaping function for values inside `<%= ... %>` (escapes XML by default). 71 | - `async`: if `true`, generates an async function to make it possible to use `await` inside templates (`false` by default). 72 | - `filename`: the file name of the template if present (used for resolving includes). 73 | - `read`: a function of the form `(filename) => content` for reading includes (e.g. from file system in Node). 74 | - `resolve`: a function of the form `(parentPath, includePath) => path` for resolving include paths. 75 | - `cache`: an object to cache compiled includes in for faster compilation; reuse between `compile` runs for best performance (`{}` by default). 76 | 77 | ## EJS cheatsheet 78 | 79 | - `<%= value %>`: output the value (escaped). 80 | - `<%- value %>`: output the value (raw). 81 | - `<% code %>`: use arbitrary JavaScript. 82 | - `<%_ code %>`: use arbitrary JavaScript and strip whitespace on the same line before the tag. 83 | - `... _%>`: strip whitespace and a single line break on the same line after the tag. 84 | - `... -%>`: strip a single line break immediately after the tag. 85 | - `<%%`, `%%>`: output literal `<%` or `%>`. 86 | - `<%# comment %>`: comment (ignored). 87 | - `<%- include('path/to/template.ejs', {foo: bar}) %>`: include another template, optionally passing data. 88 | 89 | ## File handling 90 | 91 | An example of using `read`, `resolve` and `filename` options in Node.js to process includes: 92 | 93 | ```js 94 | import {readFileSync} from 'fs'; 95 | import {join, dirname} from 'path'; 96 | 97 | const template = yeahjs.compile(`<%- include('../bar.html') %>`, { 98 | filename: 'foo/foo.html', 99 | resolve: (parent, filename) => join(dirname(parent), filename), 100 | read: filename => readFileSync(filename, 'utf8') 101 | }); 102 | ``` 103 | -------------------------------------------------------------------------------- /eslint.config.js: -------------------------------------------------------------------------------- 1 | export {default} from 'eslint-config-mourner'; 2 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 2 | const RE = /(<%%|%%>|<%=|<%-|<%_|<%#|<%|%>|-%>|_%>)/g; 3 | const ESCAPE_RE = /[&<>'"]/g; 4 | const BREAK_RE = /^(\r\n|\r|\n)/; 5 | const W_LEFT_RE = /^[ \t]+(\r\n|\r|\n)/; 6 | const W_RIGHT_RE = /[ \t]+$/; 7 | const INCLUDE_RE = /include\(\s*(['"])(.+?)\1\s*(,\s*({.+?})\s*)?\)/g; 8 | 9 | const defaultOptions = { 10 | escape: escapeXML, 11 | localsName: 'locals', 12 | resolve: (parent, path) => path 13 | }; 14 | 15 | let AsyncFunction; 16 | 17 | export function compile(ejs, options) { 18 | options = Object.assign({cache: {}}, defaultOptions, options); 19 | const {escape, localsName, context, filename, async} = options; 20 | 21 | const code = `'use strict'; ${compilePart(ejs, filename, options)}`; 22 | 23 | if (async && !AsyncFunction) { 24 | try { 25 | AsyncFunction = (new Function('return (async () => {}).constructor'))(); 26 | } catch { 27 | throw new Error('This environment does not support async/await.'); 28 | } 29 | } 30 | const fn = new (async ? AsyncFunction : Function)(localsName, '_esc', '_str', code); 31 | 32 | return data => fn.call(context, data || null, escape, stringify); 33 | } 34 | 35 | function compilePart(ejs, filename, options) { 36 | const {locals, localsName} = options; 37 | let code = locals && locals.length ? `const {${locals.join(', ')}} = ${localsName}; ` : ''; 38 | code += 'let _out = `'; 39 | 40 | const originalLastIndex = RE.lastIndex; 41 | let lastIndex = RE.lastIndex = 0; 42 | let match, prev, open; 43 | 44 | do { 45 | match = RE.exec(ejs); 46 | const token = match && match[0]; 47 | 48 | if (prev !== '<%#') { 49 | let str = ejs.slice(lastIndex, match ? match.index : undefined); 50 | if (!open) { // text data 51 | if (token === '<%_') str = str.replace(W_RIGHT_RE, ''); 52 | if (prev === '_%>') str = str.replace(W_LEFT_RE, ''); 53 | else if (prev === '-%>') str = str.replace(BREAK_RE, ''); 54 | 55 | code += str.replace('\\', '\\\\').replace('\r', '\\r'); 56 | 57 | } else { // JS 58 | code += compileIncludes(str, filename, options); 59 | } 60 | } 61 | 62 | if (!token || token[0] === '<' && token[2] !== '%') { 63 | if (open) throw new Error(`Could not find matching close tag for ${open}.`); 64 | open = token; 65 | } 66 | 67 | switch (token) { 68 | case '%>': 69 | case '_%>': 70 | case '-%>': code += 71 | prev === '<%=' || prev === '<%-' ? '\n)) + `' : 72 | prev === '<%' || prev === '<%_' ? '\n_out += `' : 73 | prev === '<%#' ? '' : token; 74 | open = null; 75 | break; 76 | case '<%': 77 | case '<%_': code += '`;'; break; 78 | case '<%=': code += '` + _esc(_str('; break; 79 | case '<%-': code += '` + _str(('; break; 80 | case '<%%': code += '<%'; break; 81 | case '%%>': code += '%>'; 82 | } 83 | 84 | prev = token; 85 | lastIndex = RE.lastIndex; 86 | 87 | } while (match); 88 | 89 | code += '`; return _out;'; 90 | RE.lastIndex = originalLastIndex; 91 | 92 | return code; 93 | } 94 | 95 | function compileIncludes(js, filename, options) { 96 | const {read, resolve, cache, localsName} = options; 97 | let code = ''; 98 | 99 | const originalLastIndex = INCLUDE_RE.lastIndex; 100 | let lastIndex = INCLUDE_RE.lastIndex = 0; 101 | let match; 102 | 103 | while ((match = INCLUDE_RE.exec(js)) !== null) { 104 | const includePath = match[2]; 105 | const includeData = match[4]; 106 | if (!read) throw new Error(`Found an include but read option missing: ${includePath}`); 107 | 108 | const before = js.slice(lastIndex, match.index); 109 | const key = resolve(filename, includePath); 110 | const includeCode = cache[key] = cache[key] || compilePart(read(key), key, options); 111 | const includeLocals = includeData ? `Object.assign(Object.create(${localsName}), ${includeData})` : ''; 112 | 113 | code += `${before}((${includeLocals ? localsName : ''}) => { ${includeCode} })(${includeLocals})`; 114 | 115 | lastIndex = INCLUDE_RE.lastIndex; 116 | } 117 | 118 | code += js.slice(lastIndex); 119 | INCLUDE_RE.lastIndex = originalLastIndex; 120 | 121 | return code; 122 | } 123 | 124 | function stringify(v) { 125 | return v === null || v === undefined ? '' : String(v); 126 | } 127 | 128 | const escapeChar = c => ( 129 | c === '&' ? '&' : 130 | c === '<' ? '<' : 131 | c === '>' ? '>' : 132 | c === '\'' ? ''' : 133 | c === '"' ? '"' : c); 134 | 135 | function escapeXML(xml) { 136 | return xml && xml.replace(ESCAPE_RE, escapeChar); 137 | } 138 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "yeahjs", 3 | "version": "0.3.0", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "yeahjs", 9 | "version": "0.3.0", 10 | "license": "ISC", 11 | "devDependencies": { 12 | "eslint": "^9.20.1", 13 | "eslint-config-mourner": "^4.0.2" 14 | } 15 | }, 16 | "node_modules/@eslint-community/eslint-utils": { 17 | "version": "4.4.1", 18 | "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz", 19 | "integrity": "sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==", 20 | "dev": true, 21 | "license": "MIT", 22 | "dependencies": { 23 | "eslint-visitor-keys": "^3.4.3" 24 | }, 25 | "engines": { 26 | "node": "^12.22.0 || ^14.17.0 || >=16.0.0" 27 | }, 28 | "funding": { 29 | "url": "https://opencollective.com/eslint" 30 | }, 31 | "peerDependencies": { 32 | "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" 33 | } 34 | }, 35 | "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { 36 | "version": "3.4.3", 37 | "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", 38 | "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", 39 | "dev": true, 40 | "license": "Apache-2.0", 41 | "engines": { 42 | "node": "^12.22.0 || ^14.17.0 || >=16.0.0" 43 | }, 44 | "funding": { 45 | "url": "https://opencollective.com/eslint" 46 | } 47 | }, 48 | "node_modules/@eslint-community/regexpp": { 49 | "version": "4.12.1", 50 | "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", 51 | "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", 52 | "dev": true, 53 | "license": "MIT", 54 | "engines": { 55 | "node": "^12.0.0 || ^14.0.0 || >=16.0.0" 56 | } 57 | }, 58 | "node_modules/@eslint/config-array": { 59 | "version": "0.19.2", 60 | "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.19.2.tgz", 61 | "integrity": "sha512-GNKqxfHG2ySmJOBSHg7LxeUx4xpuCoFjacmlCoYWEbaPXLwvfIjixRI12xCQZeULksQb23uiA8F40w5TojpV7w==", 62 | "dev": true, 63 | "license": "Apache-2.0", 64 | "dependencies": { 65 | "@eslint/object-schema": "^2.1.6", 66 | "debug": "^4.3.1", 67 | "minimatch": "^3.1.2" 68 | }, 69 | "engines": { 70 | "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 71 | } 72 | }, 73 | "node_modules/@eslint/core": { 74 | "version": "0.11.0", 75 | "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.11.0.tgz", 76 | "integrity": "sha512-DWUB2pksgNEb6Bz2fggIy1wh6fGgZP4Xyy/Mt0QZPiloKKXerbqq9D3SBQTlCRYOrcRPu4vuz+CGjwdfqxnoWA==", 77 | "dev": true, 78 | "license": "Apache-2.0", 79 | "dependencies": { 80 | "@types/json-schema": "^7.0.15" 81 | }, 82 | "engines": { 83 | "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 84 | } 85 | }, 86 | "node_modules/@eslint/eslintrc": { 87 | "version": "3.2.0", 88 | "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.2.0.tgz", 89 | "integrity": "sha512-grOjVNN8P3hjJn/eIETF1wwd12DdnwFDoyceUJLYYdkpbwq3nLi+4fqrTAONx7XDALqlL220wC/RHSC/QTI/0w==", 90 | "dev": true, 91 | "license": "MIT", 92 | "dependencies": { 93 | "ajv": "^6.12.4", 94 | "debug": "^4.3.2", 95 | "espree": "^10.0.1", 96 | "globals": "^14.0.0", 97 | "ignore": "^5.2.0", 98 | "import-fresh": "^3.2.1", 99 | "js-yaml": "^4.1.0", 100 | "minimatch": "^3.1.2", 101 | "strip-json-comments": "^3.1.1" 102 | }, 103 | "engines": { 104 | "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 105 | }, 106 | "funding": { 107 | "url": "https://opencollective.com/eslint" 108 | } 109 | }, 110 | "node_modules/@eslint/js": { 111 | "version": "9.20.0", 112 | "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.20.0.tgz", 113 | "integrity": "sha512-iZA07H9io9Wn836aVTytRaNqh00Sad+EamwOVJT12GTLw1VGMFV/4JaME+JjLtr9fiGaoWgYnS54wrfWsSs4oQ==", 114 | "dev": true, 115 | "license": "MIT", 116 | "engines": { 117 | "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 118 | } 119 | }, 120 | "node_modules/@eslint/object-schema": { 121 | "version": "2.1.6", 122 | "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", 123 | "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", 124 | "dev": true, 125 | "license": "Apache-2.0", 126 | "engines": { 127 | "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 128 | } 129 | }, 130 | "node_modules/@eslint/plugin-kit": { 131 | "version": "0.2.5", 132 | "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.5.tgz", 133 | "integrity": "sha512-lB05FkqEdUg2AA0xEbUz0SnkXT1LcCTa438W4IWTUh4hdOnVbQyOJ81OrDXsJk/LSiJHubgGEFoR5EHq1NsH1A==", 134 | "dev": true, 135 | "license": "Apache-2.0", 136 | "dependencies": { 137 | "@eslint/core": "^0.10.0", 138 | "levn": "^0.4.1" 139 | }, 140 | "engines": { 141 | "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 142 | } 143 | }, 144 | "node_modules/@eslint/plugin-kit/node_modules/@eslint/core": { 145 | "version": "0.10.0", 146 | "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.10.0.tgz", 147 | "integrity": "sha512-gFHJ+xBOo4G3WRlR1e/3G8A6/KZAH6zcE/hkLRCZTi/B9avAG365QhFA8uOGzTMqgTghpn7/fSnscW++dpMSAw==", 148 | "dev": true, 149 | "license": "Apache-2.0", 150 | "dependencies": { 151 | "@types/json-schema": "^7.0.15" 152 | }, 153 | "engines": { 154 | "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 155 | } 156 | }, 157 | "node_modules/@humanfs/core": { 158 | "version": "0.19.1", 159 | "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", 160 | "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", 161 | "dev": true, 162 | "license": "Apache-2.0", 163 | "engines": { 164 | "node": ">=18.18.0" 165 | } 166 | }, 167 | "node_modules/@humanfs/node": { 168 | "version": "0.16.6", 169 | "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", 170 | "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", 171 | "dev": true, 172 | "license": "Apache-2.0", 173 | "dependencies": { 174 | "@humanfs/core": "^0.19.1", 175 | "@humanwhocodes/retry": "^0.3.0" 176 | }, 177 | "engines": { 178 | "node": ">=18.18.0" 179 | } 180 | }, 181 | "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { 182 | "version": "0.3.1", 183 | "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", 184 | "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", 185 | "dev": true, 186 | "license": "Apache-2.0", 187 | "engines": { 188 | "node": ">=18.18" 189 | }, 190 | "funding": { 191 | "type": "github", 192 | "url": "https://github.com/sponsors/nzakas" 193 | } 194 | }, 195 | "node_modules/@humanwhocodes/module-importer": { 196 | "version": "1.0.1", 197 | "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", 198 | "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", 199 | "dev": true, 200 | "license": "Apache-2.0", 201 | "engines": { 202 | "node": ">=12.22" 203 | }, 204 | "funding": { 205 | "type": "github", 206 | "url": "https://github.com/sponsors/nzakas" 207 | } 208 | }, 209 | "node_modules/@humanwhocodes/retry": { 210 | "version": "0.4.1", 211 | "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.1.tgz", 212 | "integrity": "sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA==", 213 | "dev": true, 214 | "license": "Apache-2.0", 215 | "engines": { 216 | "node": ">=18.18" 217 | }, 218 | "funding": { 219 | "type": "github", 220 | "url": "https://github.com/sponsors/nzakas" 221 | } 222 | }, 223 | "node_modules/@stylistic/eslint-plugin-js": { 224 | "version": "2.13.0", 225 | "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin-js/-/eslint-plugin-js-2.13.0.tgz", 226 | "integrity": "sha512-GPPDK4+fcbsQD58a3abbng2Dx+jBoxM5cnYjBM4T24WFZRZdlNSKvR19TxP8CPevzMOodQ9QVzNeqWvMXzfJRA==", 227 | "dev": true, 228 | "license": "MIT", 229 | "dependencies": { 230 | "eslint-visitor-keys": "^4.2.0", 231 | "espree": "^10.3.0" 232 | }, 233 | "engines": { 234 | "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 235 | }, 236 | "peerDependencies": { 237 | "eslint": ">=8.40.0" 238 | } 239 | }, 240 | "node_modules/@types/estree": { 241 | "version": "1.0.6", 242 | "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", 243 | "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", 244 | "dev": true, 245 | "license": "MIT" 246 | }, 247 | "node_modules/@types/json-schema": { 248 | "version": "7.0.15", 249 | "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", 250 | "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", 251 | "dev": true, 252 | "license": "MIT" 253 | }, 254 | "node_modules/acorn": { 255 | "version": "8.14.0", 256 | "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", 257 | "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", 258 | "dev": true, 259 | "license": "MIT", 260 | "bin": { 261 | "acorn": "bin/acorn" 262 | }, 263 | "engines": { 264 | "node": ">=0.4.0" 265 | } 266 | }, 267 | "node_modules/acorn-jsx": { 268 | "version": "5.3.2", 269 | "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", 270 | "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", 271 | "dev": true, 272 | "license": "MIT", 273 | "peerDependencies": { 274 | "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" 275 | } 276 | }, 277 | "node_modules/ajv": { 278 | "version": "6.12.6", 279 | "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", 280 | "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", 281 | "dev": true, 282 | "license": "MIT", 283 | "dependencies": { 284 | "fast-deep-equal": "^3.1.1", 285 | "fast-json-stable-stringify": "^2.0.0", 286 | "json-schema-traverse": "^0.4.1", 287 | "uri-js": "^4.2.2" 288 | }, 289 | "funding": { 290 | "type": "github", 291 | "url": "https://github.com/sponsors/epoberezkin" 292 | } 293 | }, 294 | "node_modules/ansi-styles": { 295 | "version": "4.3.0", 296 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", 297 | "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", 298 | "dev": true, 299 | "license": "MIT", 300 | "dependencies": { 301 | "color-convert": "^2.0.1" 302 | }, 303 | "engines": { 304 | "node": ">=8" 305 | }, 306 | "funding": { 307 | "url": "https://github.com/chalk/ansi-styles?sponsor=1" 308 | } 309 | }, 310 | "node_modules/argparse": { 311 | "version": "2.0.1", 312 | "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", 313 | "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", 314 | "dev": true, 315 | "license": "Python-2.0" 316 | }, 317 | "node_modules/balanced-match": { 318 | "version": "1.0.2", 319 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", 320 | "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", 321 | "dev": true, 322 | "license": "MIT" 323 | }, 324 | "node_modules/brace-expansion": { 325 | "version": "1.1.11", 326 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 327 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 328 | "dev": true, 329 | "license": "MIT", 330 | "dependencies": { 331 | "balanced-match": "^1.0.0", 332 | "concat-map": "0.0.1" 333 | } 334 | }, 335 | "node_modules/callsites": { 336 | "version": "3.1.0", 337 | "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", 338 | "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", 339 | "dev": true, 340 | "license": "MIT", 341 | "engines": { 342 | "node": ">=6" 343 | } 344 | }, 345 | "node_modules/chalk": { 346 | "version": "4.1.2", 347 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", 348 | "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", 349 | "dev": true, 350 | "license": "MIT", 351 | "dependencies": { 352 | "ansi-styles": "^4.1.0", 353 | "supports-color": "^7.1.0" 354 | }, 355 | "engines": { 356 | "node": ">=10" 357 | }, 358 | "funding": { 359 | "url": "https://github.com/chalk/chalk?sponsor=1" 360 | } 361 | }, 362 | "node_modules/color-convert": { 363 | "version": "2.0.1", 364 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", 365 | "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", 366 | "dev": true, 367 | "license": "MIT", 368 | "dependencies": { 369 | "color-name": "~1.1.4" 370 | }, 371 | "engines": { 372 | "node": ">=7.0.0" 373 | } 374 | }, 375 | "node_modules/color-name": { 376 | "version": "1.1.4", 377 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", 378 | "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", 379 | "dev": true, 380 | "license": "MIT" 381 | }, 382 | "node_modules/concat-map": { 383 | "version": "0.0.1", 384 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 385 | "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", 386 | "dev": true, 387 | "license": "MIT" 388 | }, 389 | "node_modules/cross-spawn": { 390 | "version": "7.0.6", 391 | "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", 392 | "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", 393 | "dev": true, 394 | "license": "MIT", 395 | "dependencies": { 396 | "path-key": "^3.1.0", 397 | "shebang-command": "^2.0.0", 398 | "which": "^2.0.1" 399 | }, 400 | "engines": { 401 | "node": ">= 8" 402 | } 403 | }, 404 | "node_modules/debug": { 405 | "version": "4.4.0", 406 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", 407 | "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", 408 | "dev": true, 409 | "license": "MIT", 410 | "dependencies": { 411 | "ms": "^2.1.3" 412 | }, 413 | "engines": { 414 | "node": ">=6.0" 415 | }, 416 | "peerDependenciesMeta": { 417 | "supports-color": { 418 | "optional": true 419 | } 420 | } 421 | }, 422 | "node_modules/deep-is": { 423 | "version": "0.1.4", 424 | "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", 425 | "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", 426 | "dev": true, 427 | "license": "MIT" 428 | }, 429 | "node_modules/escape-string-regexp": { 430 | "version": "4.0.0", 431 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", 432 | "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", 433 | "dev": true, 434 | "license": "MIT", 435 | "engines": { 436 | "node": ">=10" 437 | }, 438 | "funding": { 439 | "url": "https://github.com/sponsors/sindresorhus" 440 | } 441 | }, 442 | "node_modules/eslint": { 443 | "version": "9.20.1", 444 | "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.20.1.tgz", 445 | "integrity": "sha512-m1mM33o6dBUjxl2qb6wv6nGNwCAsns1eKtaQ4l/NPHeTvhiUPbtdfMyktxN4B3fgHIgsYh1VT3V9txblpQHq+g==", 446 | "dev": true, 447 | "license": "MIT", 448 | "dependencies": { 449 | "@eslint-community/eslint-utils": "^4.2.0", 450 | "@eslint-community/regexpp": "^4.12.1", 451 | "@eslint/config-array": "^0.19.0", 452 | "@eslint/core": "^0.11.0", 453 | "@eslint/eslintrc": "^3.2.0", 454 | "@eslint/js": "9.20.0", 455 | "@eslint/plugin-kit": "^0.2.5", 456 | "@humanfs/node": "^0.16.6", 457 | "@humanwhocodes/module-importer": "^1.0.1", 458 | "@humanwhocodes/retry": "^0.4.1", 459 | "@types/estree": "^1.0.6", 460 | "@types/json-schema": "^7.0.15", 461 | "ajv": "^6.12.4", 462 | "chalk": "^4.0.0", 463 | "cross-spawn": "^7.0.6", 464 | "debug": "^4.3.2", 465 | "escape-string-regexp": "^4.0.0", 466 | "eslint-scope": "^8.2.0", 467 | "eslint-visitor-keys": "^4.2.0", 468 | "espree": "^10.3.0", 469 | "esquery": "^1.5.0", 470 | "esutils": "^2.0.2", 471 | "fast-deep-equal": "^3.1.3", 472 | "file-entry-cache": "^8.0.0", 473 | "find-up": "^5.0.0", 474 | "glob-parent": "^6.0.2", 475 | "ignore": "^5.2.0", 476 | "imurmurhash": "^0.1.4", 477 | "is-glob": "^4.0.0", 478 | "json-stable-stringify-without-jsonify": "^1.0.1", 479 | "lodash.merge": "^4.6.2", 480 | "minimatch": "^3.1.2", 481 | "natural-compare": "^1.4.0", 482 | "optionator": "^0.9.3" 483 | }, 484 | "bin": { 485 | "eslint": "bin/eslint.js" 486 | }, 487 | "engines": { 488 | "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 489 | }, 490 | "funding": { 491 | "url": "https://eslint.org/donate" 492 | }, 493 | "peerDependencies": { 494 | "jiti": "*" 495 | }, 496 | "peerDependenciesMeta": { 497 | "jiti": { 498 | "optional": true 499 | } 500 | } 501 | }, 502 | "node_modules/eslint-config-mourner": { 503 | "version": "4.0.2", 504 | "resolved": "https://registry.npmjs.org/eslint-config-mourner/-/eslint-config-mourner-4.0.2.tgz", 505 | "integrity": "sha512-gaXR2jyDbu51PhUNQXuLZmyHYgFztdLfAz7EtfdmBXw0Ok97H6/39hKb1nLBwQa+8+QHXCCgqG8Sviu/Z5GSzQ==", 506 | "dev": true, 507 | "license": "ISC", 508 | "dependencies": { 509 | "@eslint/js": "^9.6.0", 510 | "@stylistic/eslint-plugin-js": "^2.3.0", 511 | "globals": "^15.8.0" 512 | } 513 | }, 514 | "node_modules/eslint-config-mourner/node_modules/globals": { 515 | "version": "15.15.0", 516 | "resolved": "https://registry.npmjs.org/globals/-/globals-15.15.0.tgz", 517 | "integrity": "sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==", 518 | "dev": true, 519 | "license": "MIT", 520 | "engines": { 521 | "node": ">=18" 522 | }, 523 | "funding": { 524 | "url": "https://github.com/sponsors/sindresorhus" 525 | } 526 | }, 527 | "node_modules/eslint-scope": { 528 | "version": "8.2.0", 529 | "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.2.0.tgz", 530 | "integrity": "sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A==", 531 | "dev": true, 532 | "license": "BSD-2-Clause", 533 | "dependencies": { 534 | "esrecurse": "^4.3.0", 535 | "estraverse": "^5.2.0" 536 | }, 537 | "engines": { 538 | "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 539 | }, 540 | "funding": { 541 | "url": "https://opencollective.com/eslint" 542 | } 543 | }, 544 | "node_modules/eslint-visitor-keys": { 545 | "version": "4.2.0", 546 | "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", 547 | "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", 548 | "dev": true, 549 | "license": "Apache-2.0", 550 | "engines": { 551 | "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 552 | }, 553 | "funding": { 554 | "url": "https://opencollective.com/eslint" 555 | } 556 | }, 557 | "node_modules/espree": { 558 | "version": "10.3.0", 559 | "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz", 560 | "integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==", 561 | "dev": true, 562 | "license": "BSD-2-Clause", 563 | "dependencies": { 564 | "acorn": "^8.14.0", 565 | "acorn-jsx": "^5.3.2", 566 | "eslint-visitor-keys": "^4.2.0" 567 | }, 568 | "engines": { 569 | "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 570 | }, 571 | "funding": { 572 | "url": "https://opencollective.com/eslint" 573 | } 574 | }, 575 | "node_modules/esquery": { 576 | "version": "1.6.0", 577 | "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", 578 | "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", 579 | "dev": true, 580 | "license": "BSD-3-Clause", 581 | "dependencies": { 582 | "estraverse": "^5.1.0" 583 | }, 584 | "engines": { 585 | "node": ">=0.10" 586 | } 587 | }, 588 | "node_modules/esrecurse": { 589 | "version": "4.3.0", 590 | "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", 591 | "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", 592 | "dev": true, 593 | "license": "BSD-2-Clause", 594 | "dependencies": { 595 | "estraverse": "^5.2.0" 596 | }, 597 | "engines": { 598 | "node": ">=4.0" 599 | } 600 | }, 601 | "node_modules/estraverse": { 602 | "version": "5.3.0", 603 | "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", 604 | "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", 605 | "dev": true, 606 | "license": "BSD-2-Clause", 607 | "engines": { 608 | "node": ">=4.0" 609 | } 610 | }, 611 | "node_modules/esutils": { 612 | "version": "2.0.3", 613 | "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", 614 | "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", 615 | "dev": true, 616 | "license": "BSD-2-Clause", 617 | "engines": { 618 | "node": ">=0.10.0" 619 | } 620 | }, 621 | "node_modules/fast-deep-equal": { 622 | "version": "3.1.3", 623 | "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", 624 | "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", 625 | "dev": true, 626 | "license": "MIT" 627 | }, 628 | "node_modules/fast-json-stable-stringify": { 629 | "version": "2.1.0", 630 | "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", 631 | "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", 632 | "dev": true, 633 | "license": "MIT" 634 | }, 635 | "node_modules/fast-levenshtein": { 636 | "version": "2.0.6", 637 | "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", 638 | "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", 639 | "dev": true, 640 | "license": "MIT" 641 | }, 642 | "node_modules/file-entry-cache": { 643 | "version": "8.0.0", 644 | "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", 645 | "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", 646 | "dev": true, 647 | "license": "MIT", 648 | "dependencies": { 649 | "flat-cache": "^4.0.0" 650 | }, 651 | "engines": { 652 | "node": ">=16.0.0" 653 | } 654 | }, 655 | "node_modules/find-up": { 656 | "version": "5.0.0", 657 | "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", 658 | "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", 659 | "dev": true, 660 | "license": "MIT", 661 | "dependencies": { 662 | "locate-path": "^6.0.0", 663 | "path-exists": "^4.0.0" 664 | }, 665 | "engines": { 666 | "node": ">=10" 667 | }, 668 | "funding": { 669 | "url": "https://github.com/sponsors/sindresorhus" 670 | } 671 | }, 672 | "node_modules/flat-cache": { 673 | "version": "4.0.1", 674 | "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", 675 | "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", 676 | "dev": true, 677 | "license": "MIT", 678 | "dependencies": { 679 | "flatted": "^3.2.9", 680 | "keyv": "^4.5.4" 681 | }, 682 | "engines": { 683 | "node": ">=16" 684 | } 685 | }, 686 | "node_modules/flatted": { 687 | "version": "3.3.2", 688 | "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.2.tgz", 689 | "integrity": "sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA==", 690 | "dev": true, 691 | "license": "ISC" 692 | }, 693 | "node_modules/glob-parent": { 694 | "version": "6.0.2", 695 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", 696 | "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", 697 | "dev": true, 698 | "license": "ISC", 699 | "dependencies": { 700 | "is-glob": "^4.0.3" 701 | }, 702 | "engines": { 703 | "node": ">=10.13.0" 704 | } 705 | }, 706 | "node_modules/globals": { 707 | "version": "14.0.0", 708 | "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", 709 | "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", 710 | "dev": true, 711 | "license": "MIT", 712 | "engines": { 713 | "node": ">=18" 714 | }, 715 | "funding": { 716 | "url": "https://github.com/sponsors/sindresorhus" 717 | } 718 | }, 719 | "node_modules/has-flag": { 720 | "version": "4.0.0", 721 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", 722 | "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", 723 | "dev": true, 724 | "license": "MIT", 725 | "engines": { 726 | "node": ">=8" 727 | } 728 | }, 729 | "node_modules/ignore": { 730 | "version": "5.3.2", 731 | "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", 732 | "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", 733 | "dev": true, 734 | "license": "MIT", 735 | "engines": { 736 | "node": ">= 4" 737 | } 738 | }, 739 | "node_modules/import-fresh": { 740 | "version": "3.3.1", 741 | "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", 742 | "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", 743 | "dev": true, 744 | "license": "MIT", 745 | "dependencies": { 746 | "parent-module": "^1.0.0", 747 | "resolve-from": "^4.0.0" 748 | }, 749 | "engines": { 750 | "node": ">=6" 751 | }, 752 | "funding": { 753 | "url": "https://github.com/sponsors/sindresorhus" 754 | } 755 | }, 756 | "node_modules/imurmurhash": { 757 | "version": "0.1.4", 758 | "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", 759 | "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", 760 | "dev": true, 761 | "license": "MIT", 762 | "engines": { 763 | "node": ">=0.8.19" 764 | } 765 | }, 766 | "node_modules/is-extglob": { 767 | "version": "2.1.1", 768 | "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", 769 | "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", 770 | "dev": true, 771 | "license": "MIT", 772 | "engines": { 773 | "node": ">=0.10.0" 774 | } 775 | }, 776 | "node_modules/is-glob": { 777 | "version": "4.0.3", 778 | "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", 779 | "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", 780 | "dev": true, 781 | "license": "MIT", 782 | "dependencies": { 783 | "is-extglob": "^2.1.1" 784 | }, 785 | "engines": { 786 | "node": ">=0.10.0" 787 | } 788 | }, 789 | "node_modules/isexe": { 790 | "version": "2.0.0", 791 | "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", 792 | "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", 793 | "dev": true, 794 | "license": "ISC" 795 | }, 796 | "node_modules/js-yaml": { 797 | "version": "4.1.0", 798 | "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", 799 | "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", 800 | "dev": true, 801 | "license": "MIT", 802 | "dependencies": { 803 | "argparse": "^2.0.1" 804 | }, 805 | "bin": { 806 | "js-yaml": "bin/js-yaml.js" 807 | } 808 | }, 809 | "node_modules/json-buffer": { 810 | "version": "3.0.1", 811 | "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", 812 | "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", 813 | "dev": true, 814 | "license": "MIT" 815 | }, 816 | "node_modules/json-schema-traverse": { 817 | "version": "0.4.1", 818 | "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", 819 | "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", 820 | "dev": true, 821 | "license": "MIT" 822 | }, 823 | "node_modules/json-stable-stringify-without-jsonify": { 824 | "version": "1.0.1", 825 | "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", 826 | "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", 827 | "dev": true, 828 | "license": "MIT" 829 | }, 830 | "node_modules/keyv": { 831 | "version": "4.5.4", 832 | "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", 833 | "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", 834 | "dev": true, 835 | "license": "MIT", 836 | "dependencies": { 837 | "json-buffer": "3.0.1" 838 | } 839 | }, 840 | "node_modules/levn": { 841 | "version": "0.4.1", 842 | "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", 843 | "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", 844 | "dev": true, 845 | "license": "MIT", 846 | "dependencies": { 847 | "prelude-ls": "^1.2.1", 848 | "type-check": "~0.4.0" 849 | }, 850 | "engines": { 851 | "node": ">= 0.8.0" 852 | } 853 | }, 854 | "node_modules/locate-path": { 855 | "version": "6.0.0", 856 | "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", 857 | "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", 858 | "dev": true, 859 | "license": "MIT", 860 | "dependencies": { 861 | "p-locate": "^5.0.0" 862 | }, 863 | "engines": { 864 | "node": ">=10" 865 | }, 866 | "funding": { 867 | "url": "https://github.com/sponsors/sindresorhus" 868 | } 869 | }, 870 | "node_modules/lodash.merge": { 871 | "version": "4.6.2", 872 | "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", 873 | "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", 874 | "dev": true, 875 | "license": "MIT" 876 | }, 877 | "node_modules/minimatch": { 878 | "version": "3.1.2", 879 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", 880 | "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", 881 | "dev": true, 882 | "license": "ISC", 883 | "dependencies": { 884 | "brace-expansion": "^1.1.7" 885 | }, 886 | "engines": { 887 | "node": "*" 888 | } 889 | }, 890 | "node_modules/ms": { 891 | "version": "2.1.3", 892 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", 893 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", 894 | "dev": true, 895 | "license": "MIT" 896 | }, 897 | "node_modules/natural-compare": { 898 | "version": "1.4.0", 899 | "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", 900 | "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", 901 | "dev": true, 902 | "license": "MIT" 903 | }, 904 | "node_modules/optionator": { 905 | "version": "0.9.4", 906 | "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", 907 | "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", 908 | "dev": true, 909 | "license": "MIT", 910 | "dependencies": { 911 | "deep-is": "^0.1.3", 912 | "fast-levenshtein": "^2.0.6", 913 | "levn": "^0.4.1", 914 | "prelude-ls": "^1.2.1", 915 | "type-check": "^0.4.0", 916 | "word-wrap": "^1.2.5" 917 | }, 918 | "engines": { 919 | "node": ">= 0.8.0" 920 | } 921 | }, 922 | "node_modules/p-limit": { 923 | "version": "3.1.0", 924 | "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", 925 | "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", 926 | "dev": true, 927 | "license": "MIT", 928 | "dependencies": { 929 | "yocto-queue": "^0.1.0" 930 | }, 931 | "engines": { 932 | "node": ">=10" 933 | }, 934 | "funding": { 935 | "url": "https://github.com/sponsors/sindresorhus" 936 | } 937 | }, 938 | "node_modules/p-locate": { 939 | "version": "5.0.0", 940 | "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", 941 | "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", 942 | "dev": true, 943 | "license": "MIT", 944 | "dependencies": { 945 | "p-limit": "^3.0.2" 946 | }, 947 | "engines": { 948 | "node": ">=10" 949 | }, 950 | "funding": { 951 | "url": "https://github.com/sponsors/sindresorhus" 952 | } 953 | }, 954 | "node_modules/parent-module": { 955 | "version": "1.0.1", 956 | "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", 957 | "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", 958 | "dev": true, 959 | "license": "MIT", 960 | "dependencies": { 961 | "callsites": "^3.0.0" 962 | }, 963 | "engines": { 964 | "node": ">=6" 965 | } 966 | }, 967 | "node_modules/path-exists": { 968 | "version": "4.0.0", 969 | "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", 970 | "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", 971 | "dev": true, 972 | "license": "MIT", 973 | "engines": { 974 | "node": ">=8" 975 | } 976 | }, 977 | "node_modules/path-key": { 978 | "version": "3.1.1", 979 | "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", 980 | "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", 981 | "dev": true, 982 | "license": "MIT", 983 | "engines": { 984 | "node": ">=8" 985 | } 986 | }, 987 | "node_modules/prelude-ls": { 988 | "version": "1.2.1", 989 | "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", 990 | "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", 991 | "dev": true, 992 | "license": "MIT", 993 | "engines": { 994 | "node": ">= 0.8.0" 995 | } 996 | }, 997 | "node_modules/punycode": { 998 | "version": "2.3.1", 999 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", 1000 | "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", 1001 | "dev": true, 1002 | "license": "MIT", 1003 | "engines": { 1004 | "node": ">=6" 1005 | } 1006 | }, 1007 | "node_modules/resolve-from": { 1008 | "version": "4.0.0", 1009 | "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", 1010 | "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", 1011 | "dev": true, 1012 | "license": "MIT", 1013 | "engines": { 1014 | "node": ">=4" 1015 | } 1016 | }, 1017 | "node_modules/shebang-command": { 1018 | "version": "2.0.0", 1019 | "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", 1020 | "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", 1021 | "dev": true, 1022 | "license": "MIT", 1023 | "dependencies": { 1024 | "shebang-regex": "^3.0.0" 1025 | }, 1026 | "engines": { 1027 | "node": ">=8" 1028 | } 1029 | }, 1030 | "node_modules/shebang-regex": { 1031 | "version": "3.0.0", 1032 | "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", 1033 | "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", 1034 | "dev": true, 1035 | "license": "MIT", 1036 | "engines": { 1037 | "node": ">=8" 1038 | } 1039 | }, 1040 | "node_modules/strip-json-comments": { 1041 | "version": "3.1.1", 1042 | "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", 1043 | "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", 1044 | "dev": true, 1045 | "license": "MIT", 1046 | "engines": { 1047 | "node": ">=8" 1048 | }, 1049 | "funding": { 1050 | "url": "https://github.com/sponsors/sindresorhus" 1051 | } 1052 | }, 1053 | "node_modules/supports-color": { 1054 | "version": "7.2.0", 1055 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", 1056 | "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", 1057 | "dev": true, 1058 | "license": "MIT", 1059 | "dependencies": { 1060 | "has-flag": "^4.0.0" 1061 | }, 1062 | "engines": { 1063 | "node": ">=8" 1064 | } 1065 | }, 1066 | "node_modules/type-check": { 1067 | "version": "0.4.0", 1068 | "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", 1069 | "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", 1070 | "dev": true, 1071 | "license": "MIT", 1072 | "dependencies": { 1073 | "prelude-ls": "^1.2.1" 1074 | }, 1075 | "engines": { 1076 | "node": ">= 0.8.0" 1077 | } 1078 | }, 1079 | "node_modules/uri-js": { 1080 | "version": "4.4.1", 1081 | "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", 1082 | "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", 1083 | "dev": true, 1084 | "license": "BSD-2-Clause", 1085 | "dependencies": { 1086 | "punycode": "^2.1.0" 1087 | } 1088 | }, 1089 | "node_modules/which": { 1090 | "version": "2.0.2", 1091 | "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", 1092 | "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", 1093 | "dev": true, 1094 | "license": "ISC", 1095 | "dependencies": { 1096 | "isexe": "^2.0.0" 1097 | }, 1098 | "bin": { 1099 | "node-which": "bin/node-which" 1100 | }, 1101 | "engines": { 1102 | "node": ">= 8" 1103 | } 1104 | }, 1105 | "node_modules/word-wrap": { 1106 | "version": "1.2.5", 1107 | "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", 1108 | "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", 1109 | "dev": true, 1110 | "license": "MIT", 1111 | "engines": { 1112 | "node": ">=0.10.0" 1113 | } 1114 | }, 1115 | "node_modules/yocto-queue": { 1116 | "version": "0.1.0", 1117 | "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", 1118 | "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", 1119 | "dev": true, 1120 | "license": "MIT", 1121 | "engines": { 1122 | "node": ">=10" 1123 | }, 1124 | "funding": { 1125 | "url": "https://github.com/sponsors/sindresorhus" 1126 | } 1127 | } 1128 | } 1129 | } 1130 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "yeahjs", 3 | "version": "0.3.0", 4 | "description": "A tiny, modern implementation of EJS templates", 5 | "author": "Vladimir Agafonkin", 6 | "license": "ISC", 7 | "type": "module", 8 | "main": "index.js", 9 | "exports": "./index.js", 10 | "devDependencies": { 11 | "eslint": "^9.20.1", 12 | "eslint-config-mourner": "^4.0.2" 13 | }, 14 | "scripts": { 15 | "pretest": "eslint index.js test/test.js", 16 | "test": "node --test --test-reporter spec test/test.js" 17 | }, 18 | "files": [ 19 | "index.js" 20 | ], 21 | "keywords": [ 22 | "ejs", 23 | "template", 24 | "engine" 25 | ], 26 | "repository": { 27 | "type": "git", 28 | "url": "https://github.com/mourner/yeahjs" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /test/fixtures/_header.ejs: -------------------------------------------------------------------------------- 1 | <%- include('posts/_foo.ejs', {name: 'Vladimir Agafonkin'}) %> 2 |

Oh my

3 | -------------------------------------------------------------------------------- /test/fixtures/index.ejs: -------------------------------------------------------------------------------- 1 | <%- include('_header.ejs') %> 2 |

Hello

3 | -------------------------------------------------------------------------------- /test/fixtures/posts/_foo.ejs: -------------------------------------------------------------------------------- 1 |

(c) <%= locals.name %>

2 | -------------------------------------------------------------------------------- /test/fixtures/posts/post.ejs: -------------------------------------------------------------------------------- 1 | <%- include('../_header.ejs') %> 2 |

Lorem ipsum

3 | -------------------------------------------------------------------------------- /test/test.js: -------------------------------------------------------------------------------- 1 | 2 | import fs from 'fs'; 3 | import test from 'node:test'; 4 | import assert from 'node:assert/strict'; 5 | 6 | import {compile} from '../index.js'; 7 | 8 | const users = [{name: 'Vlad'}, {name: 'Masha'}, {name: 'Dasha'}]; 9 | 10 | test('empty', () => { 11 | assert.equal(compile('')(), '', 'empty'); 12 | assert.equal(compile('

')(), '

', 'no tags'); 13 | }); 14 | 15 | test('<%= %>', () => { 16 | assert.equal(compile('

<%= locals.foo %>

')({foo: 'bar'}), '

bar

', 'locals'); 17 | assert.equal(compile('<%= locals.name %>')({name: '