├── .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 | [](https://github.com/mourner/yeahjs/actions/workflows/node.yml)
6 | [](https://packagephobia.now.sh/result?p=yeahjs)
7 | [](https://bundlephobia.com/result?p=yeahjs)
8 | [](https://github.com/mourner/projects)
9 |
10 | ## Example
11 |
12 | ```ejs
13 |
14 | <% for (let word of locals.items) { -%>
15 | - <%= word %>
16 | <% } -%>
17 |
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 |
29 | - flour
30 | - water
31 | - salt
32 |
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: '